@askexenow/exe-os 0.9.81 → 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.
- package/deploy/stack-manifests/v0.9.json +138 -5
- package/dist/bin/cli.js +7 -6
- package/dist/bin/exe-gateway.js +123 -10
- package/dist/bin/registry-proxy.js +1 -1
- package/dist/bin/stack-update.js +163 -41
- package/dist/lib/exe-daemon.js +167 -76
- package/dist/mcp/server.js +159 -76
- package/package.json +1 -1
- package/stack.release.json +5 -4
package/dist/bin/stack-update.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin/stack-update.ts
|
|
4
|
-
import { readFileSync as
|
|
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 =
|
|
62
|
-
if (!
|
|
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 ?
|
|
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 &&
|
|
70
|
-
const rollbackEnv = backupEnvFile &&
|
|
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
|
-
|
|
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 ??
|
|
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(
|
|
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 =
|
|
231
|
-
const auditFile = options.breakGlassAuditFile ??
|
|
232
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
287
|
-
|
|
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 =
|
|
290
|
-
|
|
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
|
-
|
|
295
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
464
|
+
if (!existsSync4(lockFile)) return void 0;
|
|
343
465
|
try {
|
|
344
|
-
const parsed = JSON.parse(
|
|
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 =
|
|
433
|
-
const cwdEnv =
|
|
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 || (
|
|
436
|
-
envFile: process.env.EXE_STACK_ENV_FILE || (
|
|
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 &&
|
|
447
|
-
return
|
|
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 =
|
|
485
|
-
else if (arg.startsWith("--public-key=")) opts.manifestPublicKey =
|
|
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(
|
|
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 =
|
|
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,
|
|
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);
|