@aicraftalchemy/aca 0.0.6 → 0.0.8

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.
Files changed (2) hide show
  1. package/cli-wrapper.cjs +49 -8
  2. package/package.json +1 -1
package/cli-wrapper.cjs CHANGED
@@ -1,18 +1,59 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * ACA launcher. This is the `bin` entry npm links as `aca`. It just execs the
4
- * compiled binary that install.cjs downloaded into bin/, forwarding all args
5
- * and stdio, and exits with the binary's exit code.
3
+ * ACA launcher. This is the `bin` entry npm links as `aca`. It applies any
4
+ * staged auto-update, then execs the compiled binary that install.cjs
5
+ * downloaded into bin/, forwarding all args and stdio, and exits with the
6
+ * binary's exit code.
6
7
  */
7
8
  const { spawnSync } = require('node:child_process')
8
9
  const path = require('node:path')
9
10
  const fs = require('node:fs')
10
11
 
11
- const bin = path.join(
12
- __dirname,
13
- 'bin',
14
- process.platform === 'win32' ? 'aca.exe' : 'aca',
15
- )
12
+ const binDir = path.join(__dirname, 'bin')
13
+ const isWin = process.platform === 'win32'
14
+ const bin = path.join(binDir, isWin ? 'aca.exe' : 'aca')
15
+
16
+ /**
17
+ * Apply a staged auto-update. The running binary downloads a new version to
18
+ * `<name>.pending` in the background (see autoUpdater.stagePendingUpdate); here
19
+ * — BEFORE the binary is spawned, so nothing this launch has open is locked —
20
+ * we swap it in. On Windows a running file (a concurrent session) can still be
21
+ * RENAMED aside even while locked, so the swap is safe; deleting the parked
22
+ * `.old` is best-effort and retried on the next launch. The `.aca-pending-version`
23
+ * marker gates the swap, so a half-downloaded binary is never applied.
24
+ */
25
+ function applyStagedUpdate() {
26
+ const marker = path.join(binDir, '.aca-pending-version')
27
+ if (!fs.existsSync(marker)) return
28
+ const acaPending = path.join(binDir, isWin ? 'aca.exe.pending' : 'aca.pending')
29
+ if (!fs.existsSync(acaPending)) {
30
+ // Marker without a binary — stale/partial. Clear it and move on.
31
+ try { fs.rmSync(marker, { force: true }) } catch {}
32
+ return
33
+ }
34
+ const swap = name => {
35
+ const pending = path.join(binDir, name + '.pending')
36
+ if (!fs.existsSync(pending)) return
37
+ const real = path.join(binDir, name)
38
+ const old = path.join(binDir, name + '.old')
39
+ try { fs.rmSync(old, { force: true }) } catch {}
40
+ try { if (fs.existsSync(real)) fs.renameSync(real, old) } catch {}
41
+ fs.renameSync(pending, real)
42
+ if (!isWin) { try { fs.chmodSync(real, 0o755) } catch {} }
43
+ try { fs.rmSync(old, { force: true }) } catch {}
44
+ }
45
+ try {
46
+ swap(isWin ? 'aca.exe' : 'aca')
47
+ swap(isWin ? 'rg.exe' : 'rg')
48
+ try { fs.rmSync(marker, { force: true }) } catch {}
49
+ } catch (err) {
50
+ // Swap failed (e.g. transient FS error) — leave the pending files in place
51
+ // so the next launch retries; fall through and run the current binary.
52
+ console.error(`ACA: could not apply staged update (${err.message}); continuing.`)
53
+ }
54
+ }
55
+
56
+ applyStagedUpdate()
16
57
 
17
58
  if (!fs.existsSync(bin)) {
18
59
  console.error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicraftalchemy/aca",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "ACA — agentic coding CLI. Any model, any provider (Claude, Gemini, OpenAI, NVIDIA, Groq, Cerebras, OpenRouter, Hugging Face). Developed by Aicraftalchemy.",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {