opencode-wyvern 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/server.js +29 -8
- package/src/setup.js +9 -1
- package/templates/client-bash.sh +26 -8
- package/templates/client-pwsh.ps1 +22 -9
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npm install -g opencode-wyvern
|
|
|
20
20
|
Il comando si chiama **`oc-setup`** (non `oc-help`, non `oc`). Verifica:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
oc-setup --version # → 0.2.
|
|
23
|
+
oc-setup --version # → 0.2.3
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Se compare `oc-setup: command not found` subito dopo l'installazione, il
|
|
@@ -149,4 +149,4 @@ node bin/oc-setup.js --help
|
|
|
149
149
|
- Nessuna chiave/token/password nel pacchetto o in `config.json` locale.
|
|
150
150
|
- Le API key sono scritte dal bootstrap remoto in `~/.config/opencode/.env` (`chmod 600`).
|
|
151
151
|
- I file remoti viaggiano come base64 dentro lo script; con server non raggiungibile
|
|
152
|
-
lo script non viene stampato a video (evita leak delle chiavi).
|
|
152
|
+
lo script non viene stampato a video (evita leak delle chiavi).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-wyvern",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Setup modulare per OpenCode: SSH, client PowerShell/bash (comandi oc-*), configurazione server opencode, provider, plugin e comandi custom. Installi tutto, attivi quello che vuoi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/server.js
CHANGED
|
@@ -244,11 +244,12 @@ export function ensureLocalKey() {
|
|
|
244
244
|
export function installKeyOnServer(cfg) {
|
|
245
245
|
const { pub } = keyPaths()
|
|
246
246
|
const pubContent = fs.readFileSync(pub, "utf8").trim()
|
|
247
|
+
const pubB64 = Buffer.from(pubContent, "utf8").toString("base64")
|
|
247
248
|
const target = sshTargetArgs(cfg)
|
|
248
249
|
const remoteCmd =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
"
|
|
250
|
+
`umask 077; mkdir -p ~/.ssh; touch ~/.ssh/authorized_keys; ` +
|
|
251
|
+
`PUB="$(printf '%s' '${pubB64}' | base64 -d)"; ` +
|
|
252
|
+
`grep -qxF "$PUB" ~/.ssh/authorized_keys 2>/dev/null || printf '%s\\n' "$PUB" >> ~/.ssh/authorized_keys; ` +
|
|
252
253
|
"chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys; echo OK"
|
|
253
254
|
|
|
254
255
|
console.log(c.cyan(ui(
|
|
@@ -278,6 +279,8 @@ export function hostAliasBlock(cfg) {
|
|
|
278
279
|
if (cfg.host) parts.push(` HostName ${cfg.host}`)
|
|
279
280
|
if (cfg.user) parts.push(` User ${cfg.user}`)
|
|
280
281
|
if (cfg.port && String(cfg.port) !== "22") parts.push(` Port ${cfg.port}`)
|
|
282
|
+
parts.push(" IdentityFile ~/.ssh/id_ed25519")
|
|
283
|
+
parts.push(" IdentitiesOnly yes")
|
|
281
284
|
parts.push(" ServerAliveInterval 30")
|
|
282
285
|
parts.push(" ServerAliveCountMax 5")
|
|
283
286
|
parts.push(" StrictHostKeyChecking accept-new")
|
|
@@ -289,19 +292,37 @@ export function ensureSshConfigAlias(cfg) {
|
|
|
289
292
|
const p = sshConfigPath()
|
|
290
293
|
const target = `Host ${cfg.server}`
|
|
291
294
|
let content = ""
|
|
295
|
+
const nextBlock = hostAliasBlock(cfg)
|
|
292
296
|
if (fs.existsSync(p)) {
|
|
293
297
|
content = fs.readFileSync(p, "utf8")
|
|
294
|
-
|
|
298
|
+
const re = new RegExp(`(^|\\n)Host\\s+${cfg.server.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n(?:[ \\t].*(?:\\n|$))*`, "m")
|
|
299
|
+
if (re.test(content)) {
|
|
300
|
+
const updated = content.replace(re, (m, lead = "") => `${lead}${nextBlock}\n`)
|
|
301
|
+
if (updated === content) return false
|
|
302
|
+
fs.writeFileSync(p, updated, "utf8")
|
|
303
|
+
return true
|
|
304
|
+
}
|
|
295
305
|
if (content && !content.endsWith("\n")) content += "\n"
|
|
296
306
|
}
|
|
297
307
|
ensureDir(path.dirname(p))
|
|
298
|
-
fs.writeFileSync(p, content +
|
|
308
|
+
fs.writeFileSync(p, content + nextBlock + "\n", "utf8")
|
|
299
309
|
return true
|
|
300
310
|
}
|
|
301
311
|
|
|
302
312
|
/** Verifica la connessione SSH senza password (BatchMode). */
|
|
303
313
|
export function verifyConnection(cfg) {
|
|
304
|
-
const
|
|
314
|
+
const { key } = keyPaths()
|
|
315
|
+
const target = cfg.server || sshTargetArgs(cfg).at(-1)
|
|
316
|
+
const targetArgs = cfg.server ? [] : sshTargetArgs(cfg).slice(0, -1)
|
|
317
|
+
const keyArgs = fs.existsSync(key) ? ["-i", key, "-o", "IdentitiesOnly=yes"] : []
|
|
318
|
+
const res = run("ssh", [
|
|
319
|
+
...targetArgs,
|
|
320
|
+
...keyArgs,
|
|
321
|
+
"-o", "BatchMode=yes",
|
|
322
|
+
"-o", "ConnectTimeout=8",
|
|
323
|
+
target,
|
|
324
|
+
"echo CONN_OK",
|
|
325
|
+
], {
|
|
305
326
|
silent: true,
|
|
306
327
|
timeout: 20000,
|
|
307
328
|
})
|
|
@@ -335,7 +356,7 @@ export function buildRemoteScript({ sections = new Set(), providers = new Set(),
|
|
|
335
356
|
"if [ -z \"$OPENCODE_BIN\" ]; then",
|
|
336
357
|
" log 'opencode mancante: lo installo (npm i -g opencode-ai)'",
|
|
337
358
|
" npm install -g opencode-ai >/dev/null 2>&1 || true",
|
|
338
|
-
" OPENCODE_BIN=\"$(command -v opencode 2>/dev/null)\"",
|
|
359
|
+
" OPENCODE_BIN=\"$(command -v opencode 2>/dev/null || ls -t $HOME/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)\"",
|
|
339
360
|
"fi",
|
|
340
361
|
'[ -f package.json ] || printf \'{\\n "private": true,\\n "dependencies": {}\\n}\\n\' > package.json',
|
|
341
362
|
"[ -f .gitignore ] || printf 'node_modules\\n.env\\n' > .gitignore",
|
|
@@ -448,4 +469,4 @@ export function runScriptLocal(script) {
|
|
|
448
469
|
|
|
449
470
|
export function isPortValid(v) {
|
|
450
471
|
return v === "" || (/^\d+$/.test(v) && Number(v) >= 1 && Number(v) <= 65535)
|
|
451
|
-
}
|
|
472
|
+
}
|
package/src/setup.js
CHANGED
|
@@ -323,6 +323,14 @@ if (!localOnly) {
|
|
|
323
323
|
if (doInstall) {
|
|
324
324
|
try {
|
|
325
325
|
installKeyOnServer(entry)
|
|
326
|
+
if (verifyConnection(entry)) {
|
|
327
|
+
console.log(c.green(ui(" autenticazione SSH a chiave verificata.", " SSH key authentication verified.")))
|
|
328
|
+
} else {
|
|
329
|
+
console.log(c.yellow(ui(
|
|
330
|
+
" attenzione: la chiave e' stata copiata, ma l'accesso senza password non e' stato verificato. Controlla sshd e ~/.ssh/authorized_keys sul server.",
|
|
331
|
+
" warning: the key was copied, but passwordless login could not be verified. Check sshd and ~/.ssh/authorized_keys on the server.",
|
|
332
|
+
)))
|
|
333
|
+
}
|
|
326
334
|
} catch (err) {
|
|
327
335
|
console.log(c.yellow(` ${ui("attenzione:", "warning:")} ${err.message}`))
|
|
328
336
|
}
|
|
@@ -447,4 +455,4 @@ function fail(msg) {
|
|
|
447
455
|
export async function runPrintClient(kind) {
|
|
448
456
|
const id = kind === "bash" ? "client-bash" : "client-pwsh"
|
|
449
457
|
await cmdPrint(id)
|
|
450
|
-
}
|
|
458
|
+
}
|
package/templates/client-bash.sh
CHANGED
|
@@ -28,11 +28,10 @@ oc-sync-env
|
|
|
28
28
|
|
|
29
29
|
oc-path() {
|
|
30
30
|
oc-sync-env
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fi
|
|
35
|
-
echo "$OC_OPENCODE"
|
|
31
|
+
# Risolve opencode SUL SERVER al momento dell'esecuzione: funziona anche
|
|
32
|
+
# senza chiave SSH configurata (BatchMode non tenuto) e senza opencode nel
|
|
33
|
+
# PATH del server (cade sulle installazioni nvm `~/.nvm/versions/node/*/bin`).
|
|
34
|
+
printf '%s' '__oc_wyvern(){ OPENCODE_BIN="$(command -v opencode 2>/dev/null || ls -t $HOME/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)"; if [ -z "$OPENCODE_BIN" ]; then echo "[oc] ERRORE: opencode non trovato sul server. Esegui oc-setup scegliendo Client + Server, oppure installa sul server: npm install -g opencode-ai" >&2; return 127; fi; "$OPENCODE_BIN" "$@"; }; __oc_wyvern'
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
oc-connect() {
|
|
@@ -201,10 +200,23 @@ oc-delete() {
|
|
|
201
200
|
|
|
202
201
|
oc-go() {
|
|
203
202
|
oc-sync-env
|
|
204
|
-
local id="$1" dir="${2
|
|
203
|
+
local id="$1" dir="${2:-}" norecap=0 oc exit_code session_dir
|
|
205
204
|
[ "$3" = "--no-recap" ] && norecap=1
|
|
205
|
+
if [ -z "$id" ]; then
|
|
206
|
+
echo -e "${RED}[FAIL] Specifica l'ID della sessione.${NC}"
|
|
207
|
+
return 2
|
|
208
|
+
fi
|
|
209
|
+
case "$id" in
|
|
210
|
+
*[![:alnum:]_-]*) echo -e "${RED}[FAIL] ID sessione non valido.${NC}"; return 2 ;;
|
|
211
|
+
esac
|
|
206
212
|
oc="$(oc-path)"
|
|
207
|
-
|
|
213
|
+
if [ -z "$dir" ]; then
|
|
214
|
+
session_dir="$(oc-cache-load | awk -F '\t' -v wanted="$id" '$2 == wanted { print $4; exit }')"
|
|
215
|
+
if [ -z "$session_dir" ]; then
|
|
216
|
+
session_dir="$(oc-sessions-all | awk -F '\t' -v wanted="$id" '$2 == wanted { print $4; exit }')"
|
|
217
|
+
fi
|
|
218
|
+
dir="${session_dir:-~}"
|
|
219
|
+
fi
|
|
208
220
|
mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/opencode-wyvern"
|
|
209
221
|
printf '%s\t%s\n' "$id" "${dir:-~}" > "${XDG_CACHE_HOME:-$HOME/.cache}/opencode-wyvern/last.tsv"
|
|
210
222
|
if [ "$dir" = "~" ] || [ -z "$dir" ]; then
|
|
@@ -229,6 +241,12 @@ oc-recap() {
|
|
|
229
241
|
live="$(oc-sessions-today 2>/dev/null)"
|
|
230
242
|
if [ -z "$live" ]; then
|
|
231
243
|
cached="$(oc-cache-load)"
|
|
244
|
+
if [ "$last_exit" = "127" ]; then
|
|
245
|
+
echo -e "${RED} [!] Comando remoto non trovato (SSH exit 127).${NC}"
|
|
246
|
+
echo -e "${YELLOW} Probabile causa: opencode non e' installato sul server o non e' nel PATH della shell SSH.${NC}"
|
|
247
|
+
echo -e "${DARKGRAY:-} Risolvi con: oc-setup scegliendo Client + Server, oppure sul server: npm install -g opencode-ai${NC}"
|
|
248
|
+
return 127
|
|
249
|
+
fi
|
|
232
250
|
if [ -n "$cached" ]; then
|
|
233
251
|
m="$(stat -c %y "${XDG_CACHE_HOME:-$HOME/.cache}/opencode-wyvern/sessions.tsv" 2>/dev/null | cut -d'.' -f1)"
|
|
234
252
|
echo -e "${RED} [!] Connessione persa alle $err_time (SSH exit $last_exit).${NC}"
|
|
@@ -448,4 +466,4 @@ oc-help() {
|
|
|
448
466
|
echo ""
|
|
449
467
|
echo -e "${DARKGRAY:-}Nota: oc-ssh, oc-sessions, oc-resume richiedono SSH key configurata"
|
|
450
468
|
echo ""
|
|
451
|
-
}
|
|
469
|
+
}
|
|
@@ -26,13 +26,10 @@ function Sync-OcEnv {
|
|
|
26
26
|
Sync-OcEnv
|
|
27
27
|
|
|
28
28
|
function Get-OcPath {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
else { $global:OC_OPENCODE = 'opencode' }
|
|
34
|
-
}
|
|
35
|
-
return $global:OC_OPENCODE
|
|
29
|
+
# Risolve opencode SUL SERVER al momento dell'esecuzione: funziona anche
|
|
30
|
+
# senza chiave SSH configurata (niente BatchMode) e senza opencode nel PATH
|
|
31
|
+
# del server (fallback sulle installazioni nvm ~/.nvm/versions/node/*/bin).
|
|
32
|
+
return '__oc_wyvern(){ OPENCODE_BIN="$(command -v opencode 2>/dev/null || ls -t $HOME/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)"; if [ -z "$OPENCODE_BIN" ]; then echo "[oc] ERRORE: opencode non trovato sul server. Esegui oc-setup scegliendo Client + Server, oppure installa sul server: npm install -g opencode-ai" >&2; return 127; fi; "$OPENCODE_BIN" "$@"; }; __oc_wyvern'
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
function oc-connect {
|
|
@@ -300,11 +297,21 @@ function oc-go {
|
|
|
300
297
|
param(
|
|
301
298
|
[Parameter(Mandatory)]
|
|
302
299
|
[string]$Id,
|
|
303
|
-
[string]$Dir = "
|
|
300
|
+
[string]$Dir = "",
|
|
304
301
|
[switch]$NoRecap
|
|
305
302
|
)
|
|
306
303
|
Sync-OcEnv
|
|
307
|
-
$
|
|
304
|
+
if ($Id -notmatch '^[A-Za-z0-9_-]+$') {
|
|
305
|
+
Write-Host "ID sessione non valido." -ForegroundColor Red
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
if ([string]::IsNullOrWhiteSpace($Dir)) {
|
|
309
|
+
$found = @(Get-OcCachedSessions | Where-Object { $_.Id -eq $Id } | Select-Object -First 1)
|
|
310
|
+
if (-not $found.Count) {
|
|
311
|
+
$found = @(Get-OcAllSessions | Where-Object { $_.Id -eq $Id } | Select-Object -First 1)
|
|
312
|
+
}
|
|
313
|
+
$Dir = if ($found.Count) { $found[0].Dir } else { "~" }
|
|
314
|
+
}
|
|
308
315
|
$oc = Get-OcPath
|
|
309
316
|
if ($Dir -eq '~' -or [string]::IsNullOrEmpty($Dir)) {
|
|
310
317
|
$remoteCmd = "cd ~ && $oc -s $Id"
|
|
@@ -334,6 +341,12 @@ function Show-OcRecap {
|
|
|
334
341
|
$sessions = @(Get-OcRecentSessions)
|
|
335
342
|
$fromCache = $false
|
|
336
343
|
if ($sessions.Count -eq 0) {
|
|
344
|
+
if ($LastExit -eq 127) {
|
|
345
|
+
Write-Host " [!] Comando remoto non trovato (SSH exit 127)." -ForegroundColor Red
|
|
346
|
+
Write-Host " Probabile causa: opencode non e' installato sul server o non e' nel PATH della shell SSH." -ForegroundColor Yellow
|
|
347
|
+
Write-Host " Risolvi con: oc-setup scegliendo Client + Server, oppure sul server: npm install -g opencode-ai" -ForegroundColor DarkGray
|
|
348
|
+
return
|
|
349
|
+
}
|
|
337
350
|
$status = Get-OcServerStatus
|
|
338
351
|
if ($status.Up) {
|
|
339
352
|
Write-Host " Server raggiungibile ma nessuna sessione nelle ultime 24 ore." -ForegroundColor Yellow
|