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 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.1
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.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
- "umask 077; mkdir -p ~/.ssh && grep -qF '" +
250
- pubContent.split(" ")[0] +
251
- "' ~/.ssh/authorized_keys 2>/dev/null || (cat >> ~/.ssh/authorized_keys); " +
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
- if (content.includes(target)) return false
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 + hostAliasBlock(cfg) + "\n", "utf8")
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 res = run("ssh", [...sshTargetArgs(cfg), "-o", "BatchMode=yes", "-o", "ConnectTimeout=8", "echo CONN_OK"], {
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
+ }
@@ -28,11 +28,10 @@ oc-sync-env
28
28
 
29
29
  oc-path() {
30
30
  oc-sync-env
31
- if [ -z "$OC_OPENCODE" ]; then
32
- OC_OPENCODE=$(ssh -o BatchMode=yes "$OC_SERVER" "(command -v opencode || ls -t \$HOME/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)" 2>/dev/null | head -n1)
33
- [ -n "$OC_OPENCODE" ] || OC_OPENCODE='opencode'
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:-~}" norecap=0 oc exit_code
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
- oc-sessions-all >/dev/null 2>&1
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
- if (-not $global:OC_OPENCODE) {
30
- $remotePath = "(command -v opencode || ls -t ~/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)"
31
- $found = ssh -o BatchMode=yes $env:OC_SERVER $remotePath 2>$null | Select-Object -First 1
32
- if ($found) { $global:OC_OPENCODE = $found.Trim() }
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
- $null = @(Get-OcAllSessions)
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