@askexenow/exe-os 0.9.62 → 0.9.63

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/dist/bin/cli.js CHANGED
@@ -820,7 +820,7 @@ function isLegacySplitPostToolCommand(command) {
820
820
  function textHasLegacySplitPostToolHook(text) {
821
821
  return [EXE_HOOK_FILES.ingest, EXE_HOOK_FILES.errorRecall, EXE_HOOK_FILES.ingestWorker].some((file) => text.includes(file));
822
822
  }
823
- var EXE_HOOK_FILES, EXE_HOOKS, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
823
+ var EXE_HOOK_FILES, EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
824
824
  var init_runtime_hook_manifest = __esm({
825
825
  "src/adapters/runtime-hook-manifest.ts"() {
826
826
  "use strict";
@@ -844,6 +844,116 @@ var init_runtime_hook_manifest = __esm({
844
844
  notification: "dist/hooks/notification.js",
845
845
  instructionsLoaded: "dist/hooks/instructions-loaded.js"
846
846
  };
847
+ EXE_HOOK_MANIFEST = [
848
+ {
849
+ key: "postToolCombined",
850
+ event: "PostToolUse",
851
+ commandMarker: EXE_HOOKS.postToolCombined,
852
+ owner: "exe-os",
853
+ purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
854
+ runtimes: ["claude", "codex", "opencode"],
855
+ checkpointRole: "none"
856
+ },
857
+ {
858
+ key: "sessionStart",
859
+ event: "SessionStart",
860
+ commandMarker: EXE_HOOKS.sessionStart,
861
+ owner: "exe-os",
862
+ purpose: "Loads agent identity, procedures, and boot context.",
863
+ runtimes: ["claude", "codex", "opencode"],
864
+ checkpointRole: "none"
865
+ },
866
+ {
867
+ key: "promptSubmit",
868
+ event: "UserPromptSubmit",
869
+ commandMarker: EXE_HOOKS.promptSubmit,
870
+ owner: "exe-os",
871
+ purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
872
+ runtimes: ["claude", "codex", "opencode"],
873
+ checkpointRole: "none"
874
+ },
875
+ {
876
+ key: "heartbeat",
877
+ event: "UserPromptSubmit",
878
+ commandMarker: EXE_HOOKS.heartbeat,
879
+ owner: "exe-os",
880
+ purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
881
+ runtimes: ["claude"],
882
+ checkpointRole: "none"
883
+ },
884
+ {
885
+ key: "stop",
886
+ event: "Stop",
887
+ commandMarker: EXE_HOOKS.stop,
888
+ owner: "exe-os",
889
+ purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
890
+ runtimes: ["claude", "codex", "opencode"],
891
+ checkpointRole: "capacity_checkpoint"
892
+ },
893
+ {
894
+ key: "preToolUse",
895
+ event: "PreToolUse",
896
+ commandMarker: EXE_HOOKS.preToolUse,
897
+ owner: "exe-os",
898
+ purpose: "Preflight guardrails before shell/tool execution.",
899
+ runtimes: ["claude", "codex", "opencode"],
900
+ checkpointRole: "none"
901
+ },
902
+ {
903
+ key: "subagentStop",
904
+ event: "SubagentStop",
905
+ commandMarker: EXE_HOOKS.subagentStop,
906
+ owner: "exe-os",
907
+ purpose: "Captures subagent completion context.",
908
+ runtimes: ["claude"],
909
+ checkpointRole: "none"
910
+ },
911
+ {
912
+ key: "preCompact",
913
+ event: "PreCompact",
914
+ commandMarker: EXE_HOOKS.preCompact,
915
+ owner: "exe-os",
916
+ purpose: "Writes active-task snapshot and compaction recovery context.",
917
+ runtimes: ["claude"],
918
+ checkpointRole: "recovery_context"
919
+ },
920
+ {
921
+ key: "postCompact",
922
+ event: "PostCompact",
923
+ commandMarker: EXE_HOOKS.postCompact,
924
+ owner: "exe-os",
925
+ purpose: "Rehydrates recovery context after compaction.",
926
+ runtimes: ["claude"],
927
+ checkpointRole: "recovery_context"
928
+ },
929
+ {
930
+ key: "sessionEnd",
931
+ event: "SessionEnd",
932
+ commandMarker: EXE_HOOKS.sessionEnd,
933
+ owner: "exe-os",
934
+ purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
935
+ runtimes: ["claude"],
936
+ checkpointRole: "session_summary"
937
+ },
938
+ {
939
+ key: "notification",
940
+ event: "Notification",
941
+ commandMarker: EXE_HOOKS.notification,
942
+ owner: "exe-os",
943
+ purpose: "Captures runtime notifications and nudges.",
944
+ runtimes: ["claude"],
945
+ checkpointRole: "none"
946
+ },
947
+ {
948
+ key: "instructionsLoaded",
949
+ event: "InstructionsLoaded",
950
+ commandMarker: EXE_HOOKS.instructionsLoaded,
951
+ owner: "exe-os",
952
+ purpose: "Applies runtime instruction post-processing.",
953
+ runtimes: ["claude"],
954
+ checkpointRole: "none"
955
+ }
956
+ ];
847
957
  LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
848
958
  "dist/hooks/ingest.js",
849
959
  "dist/hooks/error-recall.js",
@@ -17181,10 +17291,14 @@ function parseStackManifest(raw, publicKey) {
17181
17291
  }
17182
17292
  return parsed;
17183
17293
  }
17184
- async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey) {
17185
- if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref), publicKey);
17294
+ async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey, authToken) {
17295
+ if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchTextWithAuth(ref, fetchText, authToken), publicKey);
17186
17296
  return parseStackManifest(readFileSync25(ref, "utf8"), publicKey);
17187
17297
  }
17298
+ async function fetchTextWithAuth(ref, fetchText, authToken) {
17299
+ if (!authToken || fetchText !== defaultFetchText) return fetchText(ref);
17300
+ return defaultFetchText(ref, authToken);
17301
+ }
17188
17302
  function parseEnv(raw) {
17189
17303
  const env = /* @__PURE__ */ new Map();
17190
17304
  for (const line of raw.split(/\r?\n/)) {
@@ -17250,14 +17364,16 @@ async function runStackUpdate(options) {
17250
17364
  const exec2 = options.exec ?? defaultExec;
17251
17365
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
17252
17366
  if (options.rollback) return rollbackStackUpdate(options);
17253
- const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey);
17367
+ const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
17254
17368
  const envRaw = readFileSync25(options.envFile, "utf8");
17255
17369
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
17256
17370
  assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
17257
17371
  const lockFile = options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json");
17372
+ const previousVersion = readCurrentStackVersion(lockFile);
17258
17373
  if (options.dryRun || plan.changes.length === 0) {
17259
17374
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
17260
17375
  }
17376
+ await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
17261
17377
  const backupDir = path37.join(path37.dirname(options.envFile), ".exe-stack-backups");
17262
17378
  mkdirSync19(backupDir, { recursive: true });
17263
17379
  const stamp = now().toISOString().replace(/[:.]/g, "-");
@@ -17274,6 +17390,7 @@ async function runStackUpdate(options) {
17274
17390
  exec2("docker", [...composeArgs, "up", "-d"]);
17275
17391
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
17276
17392
  writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
17393
+ await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
17277
17394
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
17278
17395
  } catch (err) {
17279
17396
  writeFileSync20(options.envFile, envRaw, { mode: 384 });
@@ -17282,9 +17399,37 @@ async function runStackUpdate(options) {
17282
17399
  } catch {
17283
17400
  }
17284
17401
  const reason = err instanceof Error ? err.message : String(err);
17402
+ await postDeployAudit(options, "failed", plan.targetVersion, previousVersion, reason, { rollbackAttempted: true });
17285
17403
  throw new Error(`Stack update failed and rollback was attempted: ${reason}`);
17286
17404
  }
17287
17405
  }
17406
+ function readCurrentStackVersion(lockFile) {
17407
+ if (!existsSync30(lockFile)) return void 0;
17408
+ try {
17409
+ const parsed = JSON.parse(readFileSync25(lockFile, "utf8"));
17410
+ return parsed.stackVersion;
17411
+ } catch {
17412
+ return void 0;
17413
+ }
17414
+ }
17415
+ async function postDeployAudit(options, status, stackVersion, previousVersion, error, metadata) {
17416
+ if (!options.auditUrl) return;
17417
+ const postJson = options.postJson ?? defaultPostJson;
17418
+ try {
17419
+ await postJson(options.auditUrl, {
17420
+ stackVersion,
17421
+ previousVersion,
17422
+ status,
17423
+ error,
17424
+ metadata,
17425
+ deviceId: options.deviceId,
17426
+ licenseKey: options.licenseKey
17427
+ }, options.manifestAuthToken);
17428
+ } catch (err) {
17429
+ const reason = err instanceof Error ? err.message : String(err);
17430
+ console.warn(`[stack-update] deploy audit failed: ${reason}`);
17431
+ }
17432
+ }
17288
17433
  async function verifyReleaseHealth(release, retries, delayMs) {
17289
17434
  for (const [serviceName, service] of Object.entries(release.services)) {
17290
17435
  if (!service.healthUrl) continue;
@@ -17321,20 +17466,43 @@ function httpStatus(urlString) {
17321
17466
  function defaultExec(cmd, args2, opts) {
17322
17467
  execFileSync3(cmd, args2, { stdio: "inherit", cwd: opts?.cwd });
17323
17468
  }
17324
- async function defaultFetchText(ref) {
17325
- const res = await fetch(ref);
17469
+ async function defaultFetchText(ref, authToken) {
17470
+ const res = await fetch(ref, {
17471
+ headers: authToken ? { authorization: `Bearer ${authToken}` } : void 0
17472
+ });
17326
17473
  if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
17327
17474
  return res.text();
17328
17475
  }
17476
+ async function defaultPostJson(url, body, authToken) {
17477
+ const res = await fetch(url, {
17478
+ method: "POST",
17479
+ headers: {
17480
+ "content-type": "application/json",
17481
+ ...authToken ? { authorization: `Bearer ${authToken}` } : {}
17482
+ },
17483
+ body: JSON.stringify(body)
17484
+ });
17485
+ if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
17486
+ }
17329
17487
  function defaultStackPaths() {
17330
17488
  const cwdCompose = path37.resolve("docker-compose.yml");
17331
17489
  const cwdEnv = path37.resolve(".env");
17332
17490
  return {
17333
17491
  composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
17334
17492
  envFile: process.env.EXE_STACK_ENV_FILE || (existsSync30(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
17335
- manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json"
17493
+ manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json",
17494
+ auditUrl: process.env.EXE_STACK_AUDIT_URL || "https://update.askexe.com/v1/deploy-audits",
17495
+ manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN,
17496
+ manifestPublicKey: loadDefaultPublicKey()
17336
17497
  };
17337
17498
  }
17499
+ function loadDefaultPublicKey() {
17500
+ if (process.env.EXE_STACK_PUBLIC_KEY) return process.env.EXE_STACK_PUBLIC_KEY;
17501
+ if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync30(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
17502
+ return readFileSync25(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
17503
+ }
17504
+ return void 0;
17505
+ }
17338
17506
  var init_stack_update = __esm({
17339
17507
  "src/lib/stack-update.ts"() {
17340
17508
  "use strict";
@@ -17353,6 +17521,11 @@ function parseArgs4(args2) {
17353
17521
  manifestRef: defaults.manifestRef,
17354
17522
  composeFile: defaults.composeFile,
17355
17523
  envFile: defaults.envFile,
17524
+ auditUrl: defaults.auditUrl,
17525
+ manifestAuthToken: defaults.manifestAuthToken,
17526
+ manifestPublicKey: defaults.manifestPublicKey,
17527
+ deviceId: process.env.EXE_DEVICE_ID,
17528
+ licenseKey: process.env.EXE_LICENSE_KEY,
17356
17529
  dryRun: false,
17357
17530
  check: false,
17358
17531
  rollback: false,
@@ -17373,6 +17546,15 @@ function parseArgs4(args2) {
17373
17546
  else if (arg === "--lock-file") opts.lockFile = next();
17374
17547
  else if (arg === "--public-key") opts.manifestPublicKey = readFileSync26(next(), "utf8");
17375
17548
  else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync26(arg.split("=").slice(1).join("="), "utf8");
17549
+ else if (arg === "--auth-token") opts.manifestAuthToken = next();
17550
+ else if (arg.startsWith("--auth-token=")) opts.manifestAuthToken = arg.split("=").slice(1).join("=");
17551
+ else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
17552
+ else if (arg === "--audit-url") opts.auditUrl = next();
17553
+ else if (arg.startsWith("--audit-url=")) opts.auditUrl = arg.split("=").slice(1).join("=");
17554
+ else if (arg === "--device-id") opts.deviceId = next();
17555
+ else if (arg.startsWith("--device-id=")) opts.deviceId = arg.split("=").slice(1).join("=");
17556
+ else if (arg === "--license-key") opts.licenseKey = next();
17557
+ else if (arg.startsWith("--license-key=")) opts.licenseKey = arg.split("=").slice(1).join("=");
17376
17558
  else if (arg === "--rollback") opts.rollback = true;
17377
17559
  else if (arg === "--dry-run") opts.dryRun = true;
17378
17560
  else if (arg === "--check") opts.check = true;
@@ -17403,6 +17585,11 @@ Options:
17403
17585
  --check Print available changes only
17404
17586
  --dry-run Plan only; do not run Docker
17405
17587
  --public-key <path> PEM public key for signed manifest verification
17588
+ --auth-token <token> Bearer token for private update manifest/audit API
17589
+ --auth-token-env <name> Read bearer token from an environment variable
17590
+ --audit-url <url> Deploy-audit endpoint (default: EXE_STACK_AUDIT_URL/update.askexe.com)
17591
+ --device-id <id> Device ID to include in deploy audit
17592
+ --license-key <key> License key to include in deploy audit
17406
17593
  --rollback Restore latest backed-up .env and restart stack
17407
17594
  --allow-breaking <ids> Confirm breaking changes, comma-separated
17408
17595
  -y, --yes Non-interactive confirmation
@@ -17440,7 +17627,7 @@ async function main3() {
17440
17627
  console.log(`\u2705 Stack rollback attempted using backup: ${result2.backupEnvFile ?? "latest backup"}`);
17441
17628
  return;
17442
17629
  }
17443
- const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey);
17630
+ const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
17444
17631
  const envRaw = readFileSync26(opts.envFile, "utf8");
17445
17632
  const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
17446
17633
  console.log(`Exe OS stack target: ${plan.targetVersion}`);