@cybedefend/vibedefend 1.1.1 → 1.2.0

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 (40) hide show
  1. package/README.md +13 -4
  2. package/dist/clients/claude-code.js +6 -8
  3. package/dist/clients/claude-code.js.map +1 -1
  4. package/dist/clients/codex.js +6 -1
  5. package/dist/clients/codex.js.map +1 -1
  6. package/dist/clients/detect.js +5 -1
  7. package/dist/clients/detect.js.map +1 -1
  8. package/dist/config.js +1 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/diagnostics.js +5 -0
  11. package/dist/diagnostics.js.map +1 -1
  12. package/dist/doctor.js +13 -0
  13. package/dist/doctor.js.map +1 -1
  14. package/dist/hook-runner.js +537 -101
  15. package/dist/hooks/install.js +54 -9
  16. package/dist/hooks/install.js.map +1 -1
  17. package/dist/hooks/runtime/guard-violations-buffer.js +65 -15
  18. package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
  19. package/dist/hooks/runtime/self-update-check.js +196 -0
  20. package/dist/hooks/runtime/self-update-check.js.map +1 -0
  21. package/dist/hooks/runtime/semver.js +34 -0
  22. package/dist/hooks/runtime/semver.js.map +1 -0
  23. package/dist/hooks/runtime/session-review.js +126 -40
  24. package/dist/hooks/runtime/session-review.js.map +1 -1
  25. package/dist/hooks/runtime/session-start.js +24 -0
  26. package/dist/hooks/runtime/session-start.js.map +1 -1
  27. package/dist/hooks/shim-entry.js +10 -0
  28. package/dist/hooks/shim-entry.js.map +1 -0
  29. package/dist/hooks/shim.js +66 -0
  30. package/dist/hooks/shim.js.map +1 -0
  31. package/dist/login.js +25 -5
  32. package/dist/login.js.map +1 -1
  33. package/dist/prompts.js +14 -2
  34. package/dist/prompts.js.map +1 -1
  35. package/dist/self-update.js +46 -21
  36. package/dist/self-update.js.map +1 -1
  37. package/dist/shim.js +40 -0
  38. package/dist/utils.js +5 -1
  39. package/dist/utils.js.map +1 -1
  40. package/package.json +1 -1
@@ -17,6 +17,7 @@
17
17
  import { copyFileSync, existsSync, rmSync } from 'node:fs';
18
18
  import { dirname, join } from 'node:path';
19
19
  import { fileURLToPath } from 'node:url';
20
+ import { detectInstallMode, resolveScriptPath, GLOBAL_MODES, } from '../self-update.js';
20
21
  import { ensureDirFor, home, log, writeJson } from '../utils.js';
21
22
  /** Top-level directory we own. */
22
23
  export const VIBEDEFEND_DIR = home('.cybedefend');
@@ -48,6 +49,23 @@ function locateBundledRunner() {
48
49
  }
49
50
  return null;
50
51
  }
52
+ /**
53
+ * Resolve the bundled `shim.js` shipped alongside `hook-runner.js`.
54
+ * Same search strategy as `locateBundledRunner`. Returns null if absent.
55
+ */
56
+ function locateBundledShim() {
57
+ const here = dirname(fileURLToPath(import.meta.url));
58
+ const candidates = [
59
+ join(here, 'shim.js'),
60
+ join(here, '..', 'dist', 'shim.js'),
61
+ join(here, '..', '..', 'dist', 'shim.js'),
62
+ ];
63
+ for (const c of candidates) {
64
+ if (existsSync(c))
65
+ return c;
66
+ }
67
+ return null;
68
+ }
51
69
  /**
52
70
  * Drop any legacy `~/.cybedefend/hooks/` directory (V1 bash scripts).
53
71
  * Idempotent — silent no-op if the directory doesn't exist.
@@ -68,21 +86,36 @@ function removeLegacyHooks() {
68
86
  *
69
87
  * Steps:
70
88
  * 1. Sweep legacy `~/.cybedefend/hooks/` if present.
71
- * 2. Copy `dist/hook-runner.js` → `~/.cybedefend/hook-runner.js`.
89
+ * 2. For global installs: copy `dist/shim.js` → `~/.cybedefend/hook-runner.js`
90
+ * and stamp `runnerSource` in the config so updates are live immediately.
91
+ * For non-global installs: copy the full runner (legacy behaviour) and
92
+ * force `autoUpdate: false`.
72
93
  * 3. Write `~/.cybedefend/runtime-config.json` with the user's
73
94
  * region + hook settings.
74
95
  *
75
96
  * Returns the destination paths so the caller can `log.hint(...)` them.
76
97
  */
77
- export function installHookRuntime(opts) {
98
+ export function installHookRuntime(opts, deps = {}) {
78
99
  removeLegacyHooks();
79
- const source = locateBundledRunner();
80
- if (source === null) {
100
+ const installMode = deps.installMode ?? detectInstallMode(resolveScriptPath());
101
+ const locateRunner = deps.locateRunner ?? locateBundledRunner;
102
+ const locateShim = deps.locateShim ?? locateBundledShim;
103
+ const runnerSource = locateRunner();
104
+ if (runnerSource === null) {
81
105
  throw new Error('Could not locate the bundled hook-runner.js. Did `pnpm run build` finish? ' +
82
106
  'Expected to find it under <package>/dist/.');
83
107
  }
84
- ensureDirFor(HOOK_RUNNER_PATH);
85
- copyFileSync(source, HOOK_RUNNER_PATH);
108
+ const dir = deps.cybeDir ?? VIBEDEFEND_DIR;
109
+ const hookRunnerDest = join(dir, 'hook-runner.js');
110
+ const configDest = join(dir, 'runtime-config.json');
111
+ // Global installs get the stable shim + a stamped runnerSource so a later
112
+ // `npm i -g` is immediately live with no recopy. Non-global installs have
113
+ // no stable package path, so we keep the legacy full-copy behaviour and
114
+ // disable background auto-update.
115
+ const shimPath = locateShim();
116
+ const useShim = GLOBAL_MODES.has(installMode) && shimPath !== null;
117
+ ensureDirFor(hookRunnerDest);
118
+ copyFileSync(useShim ? shimPath : runnerSource, hookRunnerDest);
86
119
  const runtimeConfig = {
87
120
  region: {
88
121
  id: opts.region.id,
@@ -95,11 +128,23 @@ export function installHookRuntime(opts) {
95
128
  enableSessionReview: opts.hooks.enableSessionReview,
96
129
  reviewThreshold: opts.hooks.reviewThreshold,
97
130
  autoProposeMode: opts.hooks.autoProposeMode,
131
+ autoUpdate: useShim ? opts.hooks.autoUpdate : false,
98
132
  },
99
133
  installedVersion: opts.installedVersion,
134
+ ...(useShim ? { runnerSource } : {}),
100
135
  };
101
- writeJson(RUNTIME_CONFIG_PATH, runtimeConfig);
102
- return { runnerPath: HOOK_RUNNER_PATH, configPath: RUNTIME_CONFIG_PATH };
136
+ writeJson(configDest, runtimeConfig);
137
+ return { runnerPath: hookRunnerDest, configPath: configDest };
138
+ }
139
+ /**
140
+ * Quote a path for embedding in a hook `command` string, but ONLY when it
141
+ * contains whitespace. Both POSIX `sh` and Windows `cmd.exe` (which run the
142
+ * hook command) accept a double-quoted path, so a home directory with a
143
+ * space (e.g. `C:\\Users\\First Last\\...`) no longer splits mid-argument.
144
+ * Space-free paths are left untouched so the common command is unchanged.
145
+ */
146
+ export function quoteCommandPath(p) {
147
+ return /\s/.test(p) ? `"${p}"` : p;
103
148
  }
104
149
  /**
105
150
  * Compose the `command` field for a settings.json hook entry, targeting
@@ -107,7 +152,7 @@ export function installHookRuntime(opts) {
107
152
  * touching the filesystem.
108
153
  */
109
154
  export function hookCommand(subcommand) {
110
- return `node ${HOOK_RUNNER_PATH} ${subcommand}`;
155
+ return `node ${quoteCommandPath(HOOK_RUNNER_PATH)} ${subcommand}`;
111
156
  }
112
157
  /**
113
158
  * Marker used by every adapter's `dropOurs` to identify legacy AND new
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAIzC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGjE,kCAAkC;AAClC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;AAElD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;AAEtE,iEAAiE;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CACrC,aAAa,EACb,qBAAqB,CACtB,CAAC;AAEF,kEAAkE;AAClE,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;;GAOG;AACH,SAAS,mBAAmB;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,iEAAiE;IACjE,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC;QAC1C,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC;KACjD,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QACrB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC;QACnC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC;KAC1C,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB;IACxB,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,kCAAkC,gBAAgB,EAAE,CAAC,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;AACH,CAAC;AAaD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAIC,EACD,OAA+B,EAAE;IAEjC,iBAAiB,EAAE,CAAC;IAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,iBAAiB,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,mBAAmB,CAAC;IAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;IAExD,MAAM,YAAY,GAAG,YAAY,EAAE,CAAC;IACpC,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,4EAA4E;YAC1E,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC;IAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;IAEpD,0EAA0E;IAC1E,0EAA0E;IAC1E,wEAAwE;IACxE,kCAAkC;IAClC,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC;IAEnE,YAAY,CAAC,cAAc,CAAC,CAAC;IAC7B,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,QAAS,CAAC,CAAC,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAEjE,MAAM,aAAa,GAAkB;QACnC,MAAM,EAAE;YACN,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE;YAClB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;SAC7B;QACD,KAAK,EAAE;YACL,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,mBAAmB;YACnD,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe;YAC3C,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe;YAC3C,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK;SACpD;QACD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrC,CAAC;IACF,SAAS,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAErC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,UAMiB;IAEjB,OAAO,QAAQ,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,UAAU,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AAErD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,mBAAmB,GAAsB;IAC7C,yBAAyB;CAC1B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAe;IAC1D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,KAAK,MAAM,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -3,8 +3,7 @@
3
3
  *
4
4
  * Violations (deny/warn outcomes) are appended as JSON lines to a local
5
5
  * buffer file (`~/.cybedefend/guards-violations-buffer.jsonl`) and flushed
6
- * to the gateway on demand (typically on hook process exit via
7
- * `process.on('beforeExit')`).
6
+ * to the gateway on demand by the hook before it returns.
8
7
  *
9
8
  * Why JSONL + append semantics?
10
9
  * - Append to a file is atomic at the OS level for small writes.
@@ -13,6 +12,17 @@
13
12
  * - The hook NEVER blocks on the audit write — `bufferViolation` is
14
13
  * synchronous (sync appendFileSync with minimal data).
15
14
  *
15
+ * Per-project semantics:
16
+ * - Each buffered record carries its own `projectId`. When the buffer is
17
+ * flushed, records are grouped by `projectId` and each group is POSTed
18
+ * to its own project's endpoint. This is required because a developer
19
+ * may switch projects between hook invocations, and the user's Logto
20
+ * token (same across all their projects) can authorise calls to any
21
+ * project the Permify policy allows.
22
+ * - On partial failure (e.g. permission denied for one project, success
23
+ * for another) only the failed groups are kept in the buffer; the
24
+ * successful ones are purged.
25
+ *
16
26
  * Violation semantics:
17
27
  * - `deny` → outcome = 'denied'
18
28
  * - `warn` → outcome = 'warned'
@@ -53,7 +63,7 @@ export function makeViolationsBuffer(opts = {}) {
53
63
  content = readFileSync(bufferFile, 'utf8').trim();
54
64
  }
55
65
  catch {
56
- return; // can't read — skip
66
+ return;
57
67
  }
58
68
  if (!content)
59
69
  return;
@@ -63,30 +73,70 @@ export function makeViolationsBuffer(opts = {}) {
63
73
  if (!trimmed)
64
74
  continue;
65
75
  try {
66
- records.push(JSON.parse(trimmed));
76
+ const parsed = JSON.parse(trimmed);
77
+ // Drop records that pre-date the projectId field or are malformed
78
+ // beyond recovery — keeping them would block the flush forever
79
+ // since they cannot be addressed to a project.
80
+ if (typeof parsed.projectId === 'string' && parsed.projectId) {
81
+ records.push(parsed);
82
+ }
67
83
  }
68
84
  catch {
69
- // Corrupt line — skip it
85
+ // Corrupt line — drop it
70
86
  }
71
87
  }
72
- if (records.length === 0)
88
+ if (records.length === 0) {
89
+ // Nothing salvageable — clear the file so corrupt content doesn't
90
+ // grow unboundedly.
91
+ try {
92
+ writeFileSync(bufferFile, '', 'utf8');
93
+ }
94
+ catch {
95
+ // Best-effort
96
+ }
73
97
  return;
98
+ }
99
+ // Group by projectId, preserving original insertion order within each group.
100
+ const groups = new Map();
101
+ for (const r of records) {
102
+ const arr = groups.get(r.projectId);
103
+ if (arr)
104
+ arr.push(r);
105
+ else
106
+ groups.set(r.projectId, [r]);
107
+ }
74
108
  const BATCH_SIZE = 100;
75
- try {
76
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
77
- const batch = records.slice(i, i + BATCH_SIZE);
78
- await postFn(ctx, batch);
109
+ const failed = [];
110
+ for (const [projectId, group] of groups) {
111
+ let groupFailed = false;
112
+ for (let i = 0; i < group.length; i += BATCH_SIZE) {
113
+ const batch = group.slice(i, i + BATCH_SIZE);
114
+ try {
115
+ await postFn(ctx, projectId, batch);
116
+ }
117
+ catch {
118
+ groupFailed = true;
119
+ break;
120
+ }
79
121
  }
80
- // Truncate on success
81
- try {
122
+ if (groupFailed) {
123
+ failed.push(...group);
124
+ }
125
+ }
126
+ // Rewrite the file with only the failed groups (or wipe it on full success).
127
+ try {
128
+ if (failed.length === 0) {
82
129
  writeFileSync(bufferFile, '', 'utf8');
83
130
  }
84
- catch {
85
- // Best-effort truncate
131
+ else {
132
+ const serialised = failed.map((r) => JSON.stringify(r)).join('\n') + '\n';
133
+ writeFileSync(bufferFile, serialised, 'utf8');
86
134
  }
87
135
  }
88
136
  catch {
89
- // POST failed preserve buffer for next flush attempt
137
+ // Best-effortif we can't rewrite, the worst case is that successful
138
+ // groups are re-flushed on the next attempt (the API push is idempotent
139
+ // at the (project_id, rule_id, target_hash, session_id) level).
90
140
  }
91
141
  }
92
142
  return { bufferViolation, flushViolations };
@@ -1 +1 @@
1
- {"version":3,"file":"guard-violations-buffer.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-violations-buffer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard-violations-buffer.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-violations-buffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Throttled, opt-out background self-update check. Runs once per session
3
+ * from the SessionStart hook. RETURNS a one-line notice to fold into the
4
+ * bootstrap body (or null) — it never writes to stdout itself (the caller
5
+ * emits once, so Codex's JSON envelope stays intact). Side effects: writes
6
+ * the throttle stamp and, when an update is due + enabled for a global
7
+ * install, spawns a DETACHED wrapper that runs the package-manager update
8
+ * and records the outcome to last-update.log.
9
+ *
10
+ * Source of truth: the npm registry (`/<pkg>/latest`). No gateway call.
11
+ */
12
+ import { spawn } from 'node:child_process';
13
+ import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ import { ensureDirFor } from '../../utils.js';
17
+ import { isNewer } from './semver.js';
18
+ import { detectInstallMode, resolveScriptPath, GLOBAL_MODES, } from '../../self-update.js';
19
+ const PACKAGE_NAME = '@cybedefend/vibedefend';
20
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
21
+ const THROTTLE_MS = 24 * 60 * 60 * 1000;
22
+ const FETCH_TIMEOUT_MS = 3000;
23
+ export async function selfUpdateCheck(deps) {
24
+ const now = deps.now ?? (() => Date.now());
25
+ const dir = deps.cybeDir ?? join(process.env.CYBEDEFEND_HOME ?? homedir(), '.cybedefend');
26
+ const throttlePath = join(dir, 'update-check.json');
27
+ const logPath = join(dir, 'last-update.log');
28
+ // Derive the install mode from the stable global runner path
29
+ // (`runnerSource`) when available — NOT from `resolveScriptPath()`, which at
30
+ // runtime returns the shim (`~/.cybedefend/hook-runner.js` → `'unknown'`,
31
+ // never a global mode, so the background update would never spawn).
32
+ const installMode = deps.installMode ??
33
+ (deps.runnerSource
34
+ ? detectInstallMode(deps.runnerSource)
35
+ : detectInstallMode(resolveScriptPath()));
36
+ // Read + consume last-update.log (a prior background update's outcome),
37
+ // regardless of throttle — surface a failure as soon as possible.
38
+ const priorFailure = takeUpdateLogNotice(logPath);
39
+ const throttle = readThrottle(throttlePath);
40
+ if (throttle && now() - throttle.lastCheckedAt < THROTTLE_MS) {
41
+ return priorFailure;
42
+ }
43
+ const fetchLatest = deps.fetchLatest ?? defaultFetchLatest;
44
+ const latest = await fetchLatest();
45
+ // Stamp the check time regardless of fetch outcome so a flaky registry
46
+ // doesn't make every session hammer npm.
47
+ writeThrottle(throttlePath, { lastCheckedAt: now() });
48
+ if (!latest || !isNewer(latest, deps.installedVersion)) {
49
+ return priorFailure;
50
+ }
51
+ const updateNotice = applyOrNotify(deps, installMode, latest, logPath);
52
+ // Surface BOTH signals when present: a prior failure (its log was already
53
+ // consumed on read) AND the new pending update. They're independent.
54
+ return [priorFailure, updateNotice].filter(Boolean).join('\n') || null;
55
+ }
56
+ function applyOrNotify(deps, installMode, latest, logPath) {
57
+ const from = deps.installedVersion;
58
+ if (!deps.autoUpdate || !GLOBAL_MODES.has(installMode)) {
59
+ return `\u{1F6E1}️ vibedefend ${from} → ${latest} available — run \`vibedefend update --self\` to upgrade.`;
60
+ }
61
+ const spawnUpdate = deps.spawnUpdate ?? defaultSpawnUpdate;
62
+ spawnUpdate({
63
+ mode: installMode,
64
+ spec: `${PACKAGE_NAME}@${latest}`,
65
+ logPath,
66
+ from,
67
+ to: latest,
68
+ });
69
+ return `\u{1F6E1}️ vibedefend: updating in background ${from} → ${latest} (active next session).`;
70
+ }
71
+ function readThrottle(path) {
72
+ try {
73
+ if (!existsSync(path))
74
+ return null;
75
+ const data = JSON.parse(readFileSync(path, 'utf8'));
76
+ if (typeof data.lastCheckedAt !== 'number')
77
+ return null;
78
+ return { lastCheckedAt: data.lastCheckedAt };
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ function writeThrottle(path, throttle) {
85
+ try {
86
+ ensureDirFor(path);
87
+ writeFileSync(path, JSON.stringify(throttle) + '\n');
88
+ }
89
+ catch {
90
+ /* best-effort */
91
+ }
92
+ }
93
+ /**
94
+ * Read and CONSUME last-update.log (the prior background update's outcome).
95
+ * The log is always deleted on read — a failure is surfaced once, a stale
96
+ * success (or garbage) is simply dropped so it can't accumulate or re-nag.
97
+ * Returns a one-line notice only when the prior update failed.
98
+ */
99
+ function takeUpdateLogNotice(logPath) {
100
+ try {
101
+ if (!existsSync(logPath))
102
+ return null;
103
+ let parsed = {};
104
+ try {
105
+ parsed = JSON.parse(readFileSync(logPath, 'utf8'));
106
+ }
107
+ catch {
108
+ parsed = {};
109
+ }
110
+ rmSync(logPath, { force: true }); // consume once, whatever the content
111
+ if (typeof parsed.exitCode === 'number' && parsed.exitCode !== 0) {
112
+ return `⚠ vibedefend: last auto-update to ${parsed.to ?? 'latest'} failed (exit ${parsed.exitCode}) — run \`vibedefend update --self\`.`;
113
+ }
114
+ return null;
115
+ }
116
+ catch {
117
+ return null;
118
+ }
119
+ }
120
+ async function defaultFetchLatest() {
121
+ try {
122
+ const res = await fetch(REGISTRY_URL, {
123
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
124
+ });
125
+ if (!res.ok)
126
+ return null;
127
+ const body = (await res.json());
128
+ return typeof body.version === 'string' ? body.version : null;
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ /**
135
+ * Build the `node -e` wrapper script that runs the package-manager update and
136
+ * records the outcome (`{ from, to, exitCode, finishedAt }`) to last-update.log.
137
+ * Exported for tests. `shell: process.platform === 'win32'` so the
138
+ * `npm`/`pnpm`/`yarn` `.cmd` shims resolve on Windows; POSIX resolves the
139
+ * binaries directly (no shell layer). `node -e` runs as CommonJS, so `require`
140
+ * is available.
141
+ */
142
+ export function buildUpdateWrapperScript(args) {
143
+ return `
144
+ const { spawnSync } = require('node:child_process');
145
+ const { writeFileSync } = require('node:fs');
146
+ const r = spawnSync(${JSON.stringify(args.pm)}, ${JSON.stringify(args.pmArgs)}, {
147
+ stdio: 'ignore',
148
+ shell: process.platform === 'win32',
149
+ });
150
+ try {
151
+ writeFileSync(${JSON.stringify(args.logPath)}, JSON.stringify({
152
+ from: ${JSON.stringify(args.from)},
153
+ to: ${JSON.stringify(args.to)},
154
+ exitCode: r.status == null ? 1 : r.status,
155
+ finishedAt: Date.now(),
156
+ }) + "\\n");
157
+ } catch {}
158
+ `;
159
+ }
160
+ /**
161
+ * Spawn a DETACHED, unref'd wrapper that runs the package-manager update and
162
+ * records its outcome. `windowsHide` prevents a console window flashing on
163
+ * Windows.
164
+ */
165
+ function defaultSpawnUpdate(args) {
166
+ const { pm, pmArgs } = pmCommand(args.mode, args.spec);
167
+ const script = buildUpdateWrapperScript({
168
+ pm,
169
+ pmArgs,
170
+ logPath: args.logPath,
171
+ from: args.from,
172
+ to: args.to,
173
+ });
174
+ try {
175
+ const child = spawn(process.execPath, ['-e', script], {
176
+ detached: true,
177
+ stdio: 'ignore',
178
+ windowsHide: true,
179
+ });
180
+ child.unref();
181
+ }
182
+ catch {
183
+ /* best-effort — failure is invisible; the 24h retry covers it */
184
+ }
185
+ }
186
+ function pmCommand(mode, spec) {
187
+ switch (mode) {
188
+ case 'pnpm-global':
189
+ return { pm: 'pnpm', pmArgs: ['add', '-g', spec] };
190
+ case 'yarn-global':
191
+ return { pm: 'yarn', pmArgs: ['global', 'add', spec] };
192
+ default:
193
+ return { pm: 'npm', pmArgs: ['install', '-g', spec] };
194
+ }
195
+ }
196
+ //# sourceMappingURL=self-update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-update-check.js","sourceRoot":"","sources":["../../../src/hooks/runtime/self-update-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Minimal semver comparison for `x.y.z` version strings — no dependency,
3
+ * the CLI ships only what it needs. Tolerant: a leading `v` is stripped,
4
+ * prerelease/build suffixes are ignored, and any missing or non-numeric
5
+ * segment counts as 0, so a malformed input never throws (it simply
6
+ * compares as a low version).
7
+ */
8
+ export function parseVersion(input) {
9
+ const cleaned = input.trim().replace(/^v/i, '');
10
+ const core = cleaned.split('-')[0].split('+')[0];
11
+ const parts = core.split('.');
12
+ const seg = (i) => {
13
+ const v = Number.parseInt(parts[i] ?? '0', 10);
14
+ return Number.isFinite(v) && v >= 0 ? v : 0;
15
+ };
16
+ return [seg(0), seg(1), seg(2)];
17
+ }
18
+ /** -1 if a<b, 0 if equal, 1 if a>b — by major, then minor, then patch. */
19
+ export function compareVersions(a, b) {
20
+ const pa = parseVersion(a);
21
+ const pb = parseVersion(b);
22
+ for (let i = 0; i < 3; i++) {
23
+ if (pa[i] < pb[i])
24
+ return -1;
25
+ if (pa[i] > pb[i])
26
+ return 1;
27
+ }
28
+ return 0;
29
+ }
30
+ /** True iff `latest` is strictly newer than `current`. */
31
+ export function isNewer(latest, current) {
32
+ return compareVersions(latest, current) > 0;
33
+ }
34
+ //# sourceMappingURL=semver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver.js","sourceRoot":"","sources":["../../../src/hooks/runtime/semver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE;QAChC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,OAAe;IACrD,OAAO,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAC9C,CAAC"}