@askexenow/exe-os 0.9.80 → 0.9.82

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin/stack-update.ts
4
- import { readFileSync as readFileSync2 } from "fs";
4
+ import { readFileSync as readFileSync4 } from "fs";
5
5
 
6
6
  // src/lib/is-main.ts
7
7
  import { realpathSync } from "fs";
@@ -21,10 +21,132 @@ function isMainModule(importMetaUrl) {
21
21
  // src/lib/stack-update.ts
22
22
  import { execFileSync } from "child_process";
23
23
  import { createVerify, verify as verifySignature } from "crypto";
24
- import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "fs";
24
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
25
25
  import http from "http";
26
26
  import https from "https";
27
+ import path3 from "path";
28
+
29
+ // src/lib/license.ts
30
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
31
+ import { randomUUID } from "crypto";
32
+ import { createRequire } from "module";
33
+ import { pathToFileURL } from "url";
34
+ import os2 from "os";
35
+ import path2 from "path";
36
+ import { jwtVerify, importSPKI } from "jose";
37
+
38
+ // src/lib/config.ts
39
+ import { readFile, writeFile } from "fs/promises";
40
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
27
41
  import path from "path";
42
+ import os from "os";
43
+
44
+ // src/lib/secure-files.ts
45
+ import { chmodSync, existsSync, mkdirSync } from "fs";
46
+ import { chmod, mkdir } from "fs/promises";
47
+
48
+ // src/lib/config.ts
49
+ function resolveDataDir() {
50
+ if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
51
+ if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
52
+ const newDir = path.join(os.homedir(), ".exe-os");
53
+ const legacyDir = path.join(os.homedir(), ".exe-mem");
54
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
55
+ try {
56
+ renameSync(legacyDir, newDir);
57
+ process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
58
+ `);
59
+ } catch {
60
+ return legacyDir;
61
+ }
62
+ }
63
+ return newDir;
64
+ }
65
+ var EXE_AI_DIR = resolveDataDir();
66
+ var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
67
+ var MODELS_DIR = path.join(EXE_AI_DIR, "models");
68
+ var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
69
+ var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
70
+ var CURRENT_CONFIG_VERSION = 1;
71
+ var DEFAULT_CONFIG = {
72
+ config_version: CURRENT_CONFIG_VERSION,
73
+ dbPath: DB_PATH,
74
+ modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
75
+ embeddingDim: 1024,
76
+ batchSize: 20,
77
+ flushIntervalMs: 1e4,
78
+ autoIngestion: true,
79
+ autoRetrieval: true,
80
+ searchMode: "hybrid",
81
+ hookSearchMode: "hybrid",
82
+ fileGrepEnabled: true,
83
+ splashEffect: true,
84
+ consolidationEnabled: true,
85
+ consolidationIntervalMs: 6 * 60 * 60 * 1e3,
86
+ consolidationModel: "claude-haiku-4-5-20251001",
87
+ consolidationMaxCallsPerRun: 20,
88
+ selfQueryRouter: true,
89
+ selfQueryModel: "claude-haiku-4-5-20251001",
90
+ rerankerEnabled: true,
91
+ scalingRoadmap: {
92
+ rerankerAutoTrigger: {
93
+ enabled: true,
94
+ broadQueryMinCardinality: 5e4,
95
+ fetchTopK: 200,
96
+ returnTopK: 20
97
+ }
98
+ },
99
+ graphRagEnabled: true,
100
+ wikiEnabled: false,
101
+ wikiUrl: "",
102
+ wikiApiKey: "",
103
+ wikiSyncIntervalMs: 30 * 60 * 1e3,
104
+ wikiWorkspaceMapping: {},
105
+ wikiAutoUpdate: true,
106
+ wikiAutoUpdateThreshold: 0.5,
107
+ wikiAutoUpdateCreateNew: true,
108
+ skillLearning: true,
109
+ skillThreshold: 3,
110
+ skillModel: "claude-haiku-4-5-20251001",
111
+ exeHeartbeat: {
112
+ enabled: true,
113
+ intervalSeconds: 60,
114
+ staleInProgressThresholdHours: 2
115
+ },
116
+ sessionLifecycle: {
117
+ idleKillEnabled: true,
118
+ idleKillTicksRequired: 3,
119
+ idleKillIntercomAckWindowMs: 1e4,
120
+ maxAutoInstances: 10
121
+ },
122
+ autoUpdate: {
123
+ checkOnBoot: true,
124
+ autoInstall: false,
125
+ checkIntervalMs: 24 * 60 * 60 * 1e3
126
+ },
127
+ support: {
128
+ bugReportEndpoint: "https://askexe.com/v1/support/bug-reports"
129
+ },
130
+ orchestration: {
131
+ phase: "phase_1_coo",
132
+ phaseSetBy: "default"
133
+ }
134
+ };
135
+
136
+ // src/lib/license.ts
137
+ var LICENSE_PATH = path2.join(EXE_AI_DIR, "license.key");
138
+ var CACHE_PATH = path2.join(EXE_AI_DIR, "license-cache.json");
139
+ var DEVICE_ID_PATH = path2.join(EXE_AI_DIR, "device-id");
140
+ function loadLicense() {
141
+ try {
142
+ if (!existsSync3(LICENSE_PATH)) return null;
143
+ return readFileSync2(LICENSE_PATH, "utf8").trim();
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ // src/lib/stack-update.ts
28
150
  function isSignedEnvelope(value) {
29
151
  return !!value && typeof value === "object" && "manifest" in value && "signature" in value;
30
152
  }
@@ -58,21 +180,21 @@ function stableJson(value) {
58
180
  return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableJson(obj[key])}`).join(",")}}`;
59
181
  }
60
182
  function findLatestBackupEnvFile(envFile) {
61
- const backupDir = path.join(path.dirname(envFile), ".exe-stack-backups");
62
- if (!existsSync(backupDir)) return null;
183
+ const backupDir = path3.join(path3.dirname(envFile), ".exe-stack-backups");
184
+ if (!existsSync4(backupDir)) return null;
63
185
  const backups = readdirSync(backupDir).filter((name) => name.startsWith("env-") && name.endsWith(".bak")).sort();
64
186
  const latest = backups.at(-1);
65
- return latest ? path.join(backupDir, latest) : null;
187
+ return latest ? path3.join(backupDir, latest) : null;
66
188
  }
67
189
  async function rollbackStackUpdate(options) {
68
190
  const exec = options.exec ?? defaultExec;
69
- const backupEnvFile = options.lockFile && existsSync(options.lockFile) ? JSON.parse(readFileSync(options.lockFile, "utf8")).backupEnvFile : void 0;
70
- const rollbackEnv = backupEnvFile && existsSync(backupEnvFile) ? backupEnvFile : findLatestBackupEnvFile(options.envFile);
191
+ const backupEnvFile = options.lockFile && existsSync4(options.lockFile) ? JSON.parse(readFileSync3(options.lockFile, "utf8")).backupEnvFile : void 0;
192
+ const rollbackEnv = backupEnvFile && existsSync4(backupEnvFile) ? backupEnvFile : findLatestBackupEnvFile(options.envFile);
71
193
  if (!rollbackEnv) throw new Error(`No stack backup env found beside ${options.envFile}`);
72
- writeFileSync(options.envFile, readFileSync(rollbackEnv), { mode: 384 });
194
+ writeFileSync2(options.envFile, readFileSync3(rollbackEnv), { mode: 384 });
73
195
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
74
196
  exec("docker", [...composeArgs, "up", "-d"]);
75
- return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path.join(path.dirname(options.envFile), ".exe-stack-lock.json") };
197
+ return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path3.join(path3.dirname(options.envFile), ".exe-stack-lock.json") };
76
198
  }
77
199
  function parseStackManifest(raw, publicKey) {
78
200
  const parsedRaw = JSON.parse(raw);
@@ -97,7 +219,7 @@ function parseStackManifest(raw, publicKey) {
97
219
  }
98
220
  async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey, authToken) {
99
221
  if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchTextWithAuth(ref, fetchText, authToken), publicKey);
100
- return parseStackManifest(readFileSync(ref, "utf8"), publicKey);
222
+ return parseStackManifest(readFileSync3(ref, "utf8"), publicKey);
101
223
  }
102
224
  async function fetchTextWithAuth(ref, fetchText, authToken) {
103
225
  if (!authToken || fetchText !== defaultFetchText) return fetchText(ref);
@@ -227,9 +349,9 @@ Emergency override requires --break-glass <reason> and writes an audit file.`
227
349
  function writeBreakGlassAudit(plan, issues, options) {
228
350
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
229
351
  const stamp = now().toISOString().replace(/[:.]/g, "-");
230
- const defaultDir = existsSync("exe/output") ? "exe/output" : path.dirname(options.envFile ?? ".");
231
- const auditFile = options.breakGlassAuditFile ?? path.join(defaultDir, `stack-update-break-glass-${stamp}.md`);
232
- mkdirSync(path.dirname(auditFile), { recursive: true });
352
+ const defaultDir = existsSync4("exe/output") ? "exe/output" : path3.dirname(options.envFile ?? ".");
353
+ const auditFile = options.breakGlassAuditFile ?? path3.join(defaultDir, `stack-update-break-glass-${stamp}.md`);
354
+ mkdirSync3(path3.dirname(auditFile), { recursive: true });
233
355
  const body = [
234
356
  `# Stack Update Break-Glass Audit \u2014 ${now().toISOString()}`,
235
357
  "",
@@ -243,7 +365,7 @@ function writeBreakGlassAudit(plan, issues, options) {
243
365
  "Return this deployment to the standard pinned GHCR image path immediately after the emergency is resolved.",
244
366
  ""
245
367
  ].join("\n");
246
- writeFileSync(auditFile, body, { mode: 384 });
368
+ writeFileSync2(auditFile, body, { mode: 384 });
247
369
  console.warn(`[stack-update] BREAK-GLASS deploy override recorded: ${auditFile}`);
248
370
  }
249
371
  function assertBreakingChangesAllowed(plan, allowedIds) {
@@ -265,34 +387,34 @@ async function runStackUpdate(options) {
265
387
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
266
388
  if (options.rollback) return rollbackStackUpdate(options);
267
389
  const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
268
- const envRaw = readFileSync(options.envFile, "utf8");
390
+ const envRaw = readFileSync3(options.envFile, "utf8");
269
391
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
270
392
  assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
271
393
  assertDeploymentScopeAllowed(plan, options.deploymentPersona ?? "customer");
272
394
  const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
273
- const composeRaw = readFileSync(options.composeFile, "utf8");
395
+ const composeRaw = readFileSync3(options.composeFile, "utf8");
274
396
  assertProductionDeployGate(plan, plannedEnvRaw, composeRaw, {
275
397
  breakGlassReason: options.breakGlassReason,
276
398
  breakGlassAuditFile: options.breakGlassAuditFile,
277
399
  now,
278
400
  envFile: options.envFile
279
401
  });
280
- const lockFile = options.lockFile ?? path.join(path.dirname(options.envFile), ".exe-stack-lock.json");
402
+ const lockFile = options.lockFile ?? path3.join(path3.dirname(options.envFile), ".exe-stack-lock.json");
281
403
  const previousVersion = readCurrentStackVersion(lockFile);
282
404
  if (options.dryRun || plan.changes.length === 0) {
283
405
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
284
406
  }
285
407
  await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
286
- const backupDir = path.join(path.dirname(options.envFile), ".exe-stack-backups");
287
- mkdirSync(backupDir, { recursive: true });
408
+ const backupDir = path3.join(path3.dirname(options.envFile), ".exe-stack-backups");
409
+ mkdirSync3(backupDir, { recursive: true });
288
410
  const stamp = now().toISOString().replace(/[:.]/g, "-");
289
- const backupEnvFile = path.join(backupDir, `env-${stamp}.bak`);
290
- writeFileSync(backupEnvFile, envRaw, { mode: 384 });
411
+ const backupEnvFile = path3.join(backupDir, `env-${stamp}.bak`);
412
+ writeFileSync2(backupEnvFile, envRaw, { mode: 384 });
291
413
  const updates = Object.fromEntries(plan.changes.map((c) => [c.key, c.after]));
292
414
  const patched = patchEnv(envRaw, updates);
293
415
  const tmp = `${options.envFile}.tmp-${process.pid}`;
294
- writeFileSync(tmp, patched, { mode: 384 });
295
- renameSync(tmp, options.envFile);
416
+ writeFileSync2(tmp, patched, { mode: 384 });
417
+ renameSync2(tmp, options.envFile);
296
418
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
297
419
  let registryForLogout;
298
420
  try {
@@ -304,11 +426,11 @@ async function runStackUpdate(options) {
304
426
  exec("docker", [...composeArgs, "pull"]);
305
427
  exec("docker", [...composeArgs, "up", "-d"]);
306
428
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
307
- writeFileSync(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
429
+ writeFileSync2(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
308
430
  await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
309
431
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
310
432
  } catch (err) {
311
- writeFileSync(options.envFile, envRaw, { mode: 384 });
433
+ writeFileSync2(options.envFile, envRaw, { mode: 384 });
312
434
  try {
313
435
  exec("docker", [...composeArgs, "up", "-d"]);
314
436
  } catch {
@@ -339,9 +461,9 @@ async function fetchImageCredentials(options) {
339
461
  return await res.json();
340
462
  }
341
463
  function readCurrentStackVersion(lockFile) {
342
- if (!existsSync(lockFile)) return void 0;
464
+ if (!existsSync4(lockFile)) return void 0;
343
465
  try {
344
- const parsed = JSON.parse(readFileSync(lockFile, "utf8"));
466
+ const parsed = JSON.parse(readFileSync3(lockFile, "utf8"));
345
467
  return parsed.stackVersion;
346
468
  } catch {
347
469
  return void 0;
@@ -429,22 +551,22 @@ async function defaultPostJson(url, body, authToken) {
429
551
  if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
430
552
  }
431
553
  function defaultStackPaths() {
432
- const cwdCompose = path.resolve("docker-compose.yml");
433
- const cwdEnv = path.resolve(".env");
554
+ const cwdCompose = path3.resolve("docker-compose.yml");
555
+ const cwdEnv = path3.resolve(".env");
434
556
  return {
435
- composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
436
- envFile: process.env.EXE_STACK_ENV_FILE || (existsSync(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
557
+ composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync4(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
558
+ envFile: process.env.EXE_STACK_ENV_FILE || (existsSync4(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
437
559
  manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json",
438
560
  auditUrl: process.env.EXE_STACK_AUDIT_URL || "https://update.askexe.com/v1/deploy-audits",
439
561
  imageCredentialsUrl: process.env.EXE_STACK_IMAGE_CREDENTIALS_URL || "https://update.askexe.com/v1/image-credentials",
440
- manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN,
562
+ manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN || process.env.EXE_LICENSE_KEY || loadLicense() || void 0,
441
563
  manifestPublicKey: loadDefaultPublicKey()
442
564
  };
443
565
  }
444
566
  function loadDefaultPublicKey() {
445
567
  if (process.env.EXE_STACK_PUBLIC_KEY) return process.env.EXE_STACK_PUBLIC_KEY;
446
- if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
447
- return readFileSync(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
568
+ if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync4(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
569
+ return readFileSync3(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
448
570
  }
449
571
  return void 0;
450
572
  }
@@ -481,8 +603,8 @@ function parseArgs(args) {
481
603
  else if (arg === "--env-file") opts.envFile = next();
482
604
  else if (arg.startsWith("--env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
483
605
  else if (arg === "--lock-file") opts.lockFile = next();
484
- else if (arg === "--public-key") opts.manifestPublicKey = readFileSync2(next(), "utf8");
485
- else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync2(arg.split("=").slice(1).join("="), "utf8");
606
+ else if (arg === "--public-key") opts.manifestPublicKey = readFileSync4(next(), "utf8");
607
+ else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync4(arg.split("=").slice(1).join("="), "utf8");
486
608
  else if (arg === "--auth-token") opts.manifestAuthToken = next();
487
609
  else if (arg.startsWith("--auth-token=")) opts.manifestAuthToken = arg.split("=").slice(1).join("=");
488
610
  else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
@@ -573,8 +695,8 @@ function printBreaking(changes) {
573
695
  if (c.expectedDowntimeMinutes) console.log(` Expected downtime: ${c.expectedDowntimeMinutes} minutes`);
574
696
  }
575
697
  }
576
- async function main() {
577
- const opts = parseArgs(process.argv.slice(2));
698
+ async function main(args = process.argv.slice(2)) {
699
+ const opts = parseArgs(args);
578
700
  if (opts.rollback) {
579
701
  if (!opts.yes) {
580
702
  console.error("Refusing to rollback without --yes.");
@@ -585,11 +707,11 @@ async function main() {
585
707
  return;
586
708
  }
587
709
  const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
588
- const envRaw = readFileSync2(opts.envFile, "utf8");
710
+ const envRaw = readFileSync4(opts.envFile, "utf8");
589
711
  const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
590
712
  assertDeploymentScopeAllowed(plan, opts.deploymentPersona);
591
713
  const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
592
- assertProductionDeployGate(plan, plannedEnvRaw, readFileSync2(opts.composeFile, "utf8"), {
714
+ assertProductionDeployGate(plan, plannedEnvRaw, readFileSync4(opts.composeFile, "utf8"), {
593
715
  breakGlassReason: opts.breakGlassReason,
594
716
  breakGlassAuditFile: opts.breakGlassAuditFile,
595
717
  envFile: opts.envFile
@@ -612,7 +734,7 @@ async function main() {
612
734
  if (result.backupEnvFile) console.log(`Backup env: ${result.backupEnvFile}`);
613
735
  console.log(`Lock file: ${result.lockFile}`);
614
736
  }
615
- if (isMainModule(import.meta.url)) {
737
+ if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("stack-update")) {
616
738
  main().catch((err) => {
617
739
  console.error(err instanceof Error ? err.message : String(err));
618
740
  process.exit(1);