chore: isolate shared dsh plugins into independent monorepo
This commit is contained in:
54
scripts/check-published.mjs
Normal file
54
scripts/check-published.mjs
Normal file
@@ -0,0 +1,54 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
/**
|
||||
* Print the names of packages/* whose current version is NOT yet published
|
||||
* to the Gitea npm registry. Makes a publish workflow idempotent.
|
||||
*
|
||||
* Reads publishConfig.registry from each package.json; auth token from env
|
||||
* GITEA_NPM_TOKEN. Prints one bare package name per line (suitable for the
|
||||
* `pnpm --filter <name> publish` loop). Never prints the token.
|
||||
*/
|
||||
const token = process.env.GITEA_NPM_TOKEN
|
||||
if (!token) {
|
||||
console.error('GITEA_NPM_TOKEN not set')
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
const packagesDir = path.resolve('packages')
|
||||
const unpublished = []
|
||||
|
||||
for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue
|
||||
const pkgPath = path.join(packagesDir, entry.name, 'package.json')
|
||||
if (!fs.existsSync(pkgPath)) continue
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
||||
if (pkg.private) continue
|
||||
const registry = pkg.publishConfig?.registry
|
||||
if (!registry) {
|
||||
console.error(`skipping ${pkg.name}: no publishConfig.registry`)
|
||||
continue
|
||||
}
|
||||
// Gitea npm registry URL form: .../api/packages/{owner}/npm/<name>
|
||||
const esc = (pkg.name).replace('/', '%2F')
|
||||
const url = `${registry}${esc}`
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `token ${token}` },
|
||||
})
|
||||
if (res.status === 404) {
|
||||
unpublished.push(pkg.name)
|
||||
} else if (res.ok) {
|
||||
// registry returns 200 when the package exists
|
||||
console.error(`already published: ${pkg.name}`)
|
||||
} else {
|
||||
console.error(`registry check ${pkg.name}: HTTP ${res.status}`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`registry check ${pkg.name} failed: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (unpublished.length) {
|
||||
process.stdout.write(unpublished.join('\n') + '\n')
|
||||
}
|
||||
74
scripts/dsh-plugin-install.ps1
Normal file
74
scripts/dsh-plugin-install.ps1
Normal file
@@ -0,0 +1,74 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs shared @deepseek-ai/* plugins from the Gitea npm registry into a
|
||||
DSH profile and wires them into cordis.patch.yml.
|
||||
|
||||
.DESCRIPTION
|
||||
Ensures the profile .npmrc maps the @deepseek-ai scope to the Gitea npm
|
||||
registry, installs the three shared plugins into the profile, removes stale
|
||||
link: dependencies, and idempotently appends insert rows to cordis.patch.yml.
|
||||
|
||||
.PARAMETER Profile
|
||||
Profile name (directory under $DshHome\profiles). Default: 'web'.
|
||||
|
||||
.PARAMETER DshHome
|
||||
DSH config root. Default: $env:DSH_HOME, else ~/.dsh.
|
||||
|
||||
.PARAMETER Token
|
||||
Gitea token for the private npm registry. Default: $env:GITEA_NPM_TOKEN.
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\dsh-plugin-install.ps1 -Token <GITEA_NPM_TOKEN>
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Profile = 'web',
|
||||
[string]$DshHome,
|
||||
[string]$Token = $env:GITEA_NPM_TOKEN
|
||||
)
|
||||
|
||||
$registry = 'https://git.byte-mate.ru/api/packages/Coder/npm/'
|
||||
$plugins = @(
|
||||
@{ Name = '@deepseek-ai/dsh-tool-lab'; Version = '0.1.0-rc.7'; Id = 'tool-lab' },
|
||||
@{ Name = '@deepseek-ai/dsh-web-search-searxng'; Version = '0.1.0-rc.7'; Id = 'web-search-searxng' },
|
||||
@{ Name = '@deepseek-ai/dsh-telegram-remote'; Version = '0.1.0'; Id = 'telegram-remote' }
|
||||
)
|
||||
|
||||
if (-not $DshHome) { $DshHome = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $HOME '.dsh' } }
|
||||
$profileDir = Join-Path $DshHome "profiles\$Profile"
|
||||
if (-not (Test-Path $profileDir)) {
|
||||
Write-Error "Profile dir not found: $profileDir"
|
||||
exit 1
|
||||
}
|
||||
Write-Host "Profile dir: $profileDir"
|
||||
|
||||
# 1. Ensure .npmrc scoped registry + token.
|
||||
$npmrc = Join-Path $profileDir '.npmrc'
|
||||
$npmrcLines = if (Test-Path $npmrc) { Get-Content $npmrc } else { @() }
|
||||
$scopeLine = "@deepseek-ai:registry=$registry"
|
||||
$authLine = "//$($registry -replace 'https://','')/:_authToken=$token"
|
||||
if ($npmrcLines -notcontains $scopeLine) { $npmrcLines += $scopeLine }
|
||||
if ($Token) {
|
||||
$npmrcLines = ($npmrcLines | Where-Object { $_ -notlike '//*:_authToken=*' })
|
||||
$npmrcLines += $authLine
|
||||
}
|
||||
Set-Content -Path $npmrc -Value $npmrcLines -Encoding ascii
|
||||
Write-Host "Wrote $npmrc"
|
||||
|
||||
# 2. Remove stale link: dependencies.
|
||||
& pnpm --dir $profileDir remove @deepseek-ai/dsh-web-search-searxng dsh-telegram-remote 2>$null | Out-Null
|
||||
Write-Host "Removed stale link dependencies (if any)"
|
||||
|
||||
# 3. Install the shared plugins from the Gitea registry.
|
||||
$specs = $plugins | ForEach-Object { "$($_.Name)@$($_.Version)" }
|
||||
& pnpm --dir $profileDir add $specs
|
||||
|
||||
# 4. Idempotently append insert blocks to cordis.patch.yml.
|
||||
$patchFile = Join-Path $profileDir 'cordis.patch.yml'
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
foreach ($p in $plugins) {
|
||||
& node (Join-Path $scriptDir 'patch-cordis.mjs') --file $patchFile --name $p.Name --id $p.Id
|
||||
}
|
||||
|
||||
Write-Host "`nDone. Plugins installed into $Profile profile. Restart dsh to load them."
|
||||
exit 0
|
||||
51
scripts/dsh-plugin-install.sh
Normal file
51
scripts/dsh-plugin-install.sh
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# dsh-plugin-install.sh — install shared @deepseek-ai/* plugins from the Gitea
|
||||
# npm registry into a DSH profile and wire them into cordis.patch.yml.
|
||||
set -euo pipefail
|
||||
|
||||
PROFILE="${1:-web}"
|
||||
DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
|
||||
TOKEN="${GITEA_NPM_TOKEN:-}"
|
||||
|
||||
REGISTRY="https://git.byte-mate.ru/api/packages/Coder/npm/"
|
||||
|
||||
PROFILE_DIR="$DSH_HOME/profiles/$PROFILE"
|
||||
if [ ! -d "$PROFILE_DIR" ]; then
|
||||
echo "Profile dir not found: $PROFILE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Profile dir: $PROFILE_DIR"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# 1. Ensure .npmrc scoped registry + token.
|
||||
NPMRC="$PROFILE_DIR/.npmrc"
|
||||
touch "$NPMRC"
|
||||
SCOPE_LINE="@deepseek-ai:registry=$REGISTRY"
|
||||
if ! grep -qF "$SCOPE_LINE" "$NPMRC"; then
|
||||
echo "$SCOPE_LINE" >> "$NPMRC"
|
||||
fi
|
||||
if [ -n "$TOKEN" ]; then
|
||||
sed -i.bak "/^\/\/.*:_authToken=/d" "$NPMRC" && rm -f "$NPMRC.bak"
|
||||
printf '//%s/:_authToken=%s\n' "$(printf '%s' "$REGISTRY" | sed 's#^https://##; s#/$##')" "$TOKEN" >> "$NPMRC"
|
||||
fi
|
||||
echo "Wrote $NPMRC"
|
||||
|
||||
# 2. Remove stale link: dependencies.
|
||||
pnpm --dir "$PROFILE_DIR" remove @deepseek-ai/dsh-web-search-searxng dsh-telegram-remote >/dev/null 2>&1 || true
|
||||
echo "Removed stale link dependencies (if any)"
|
||||
|
||||
# 3. Install the shared plugins from the Gitea registry.
|
||||
pnpm --dir "$PROFILE_DIR" add \
|
||||
"@deepseek-ai/dsh-tool-lab@0.1.0-rc.7" \
|
||||
"@deepseek-ai/dsh-web-search-searxng@0.1.0-rc.7" \
|
||||
"@deepseek-ai/dsh-telegram-remote@0.1.0"
|
||||
|
||||
# 4. Idempotently append insert blocks to cordis.patch.yml.
|
||||
PATCH_FILE="$PROFILE_DIR/cordis.patch.yml"
|
||||
node "$SCRIPT_DIR/patch-cordis.mjs" --file "$PATCH_FILE" --name "@deepseek-ai/dsh-tool-lab" --id tool-lab
|
||||
node "$SCRIPT_DIR/patch-cordis.mjs" --file "$PATCH_FILE" --name "@deepseek-ai/dsh-web-search-searxng" --id web-search-searxng
|
||||
node "$SCRIPT_DIR/patch-cordis.mjs" --file "$PATCH_FILE" --name "@deepseek-ai/dsh-telegram-remote" --id telegram-remote
|
||||
|
||||
echo ""
|
||||
echo "Done. Plugins installed into $PROFILE profile. Restart dsh to load them."
|
||||
49
scripts/patch-cordis.mjs
Normal file
49
scripts/patch-cordis.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import fs from 'node:fs'
|
||||
|
||||
/**
|
||||
* Idempotently append an `- insert:` patch block to a DSH cordis.patch.yml
|
||||
* file unless a row with the same full package name already exists.
|
||||
*
|
||||
* Usage: node patch-cordis.mjs --file <path> --name <full pkg name> [--id <id>]
|
||||
*/
|
||||
function parseArg(key) {
|
||||
const i = process.argv.indexOf(key)
|
||||
return i >= 0 ? process.argv[i + 1] : undefined
|
||||
}
|
||||
|
||||
const file = parseArg('--file')
|
||||
const name = parseArg('--name')
|
||||
const id = parseArg('--id') || (name ? name.split('/').pop() : undefined)
|
||||
|
||||
if (!file || !name || !id) {
|
||||
console.error('usage: node patch-cordis.mjs --file <path> --name <full-pkg-name> [--id <id>]')
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
let text
|
||||
try {
|
||||
text = fs.readFileSync(file, 'utf8')
|
||||
} catch (err) {
|
||||
console.error(`cannot read ${file}: ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Idempotency: if a row with this name already exists, nothing to do.
|
||||
if (text.includes(`name: '${name}'`) || text.includes(`name: "${name}"`)) {
|
||||
console.log(`already present: ${name} — no change`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const block = [
|
||||
'',
|
||||
'- insert:',
|
||||
' - id: ' + id,
|
||||
` name: '${name}'`,
|
||||
' config: {}',
|
||||
'',
|
||||
].join('\n')
|
||||
|
||||
// Append preserving a single trailing newline.
|
||||
const trimmed = text.endsWith('\n') ? text.slice(0, -1) : text
|
||||
fs.writeFileSync(file, trimmed + '\n' + block + '\n')
|
||||
console.log(`appended insert for ${name} (id=${id}) to ${file}`)
|
||||
Reference in New Issue
Block a user