@askexenow/exe-os 0.9.62 → 0.9.64

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
@@ -17181,10 +17181,14 @@ function parseStackManifest(raw, publicKey) {
17181
17181
  }
17182
17182
  return parsed;
17183
17183
  }
17184
- async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey) {
17185
- if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref), publicKey);
17184
+ async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey, authToken) {
17185
+ if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchTextWithAuth(ref, fetchText, authToken), publicKey);
17186
17186
  return parseStackManifest(readFileSync25(ref, "utf8"), publicKey);
17187
17187
  }
17188
+ async function fetchTextWithAuth(ref, fetchText, authToken) {
17189
+ if (!authToken || fetchText !== defaultFetchText) return fetchText(ref);
17190
+ return defaultFetchText(ref, authToken);
17191
+ }
17188
17192
  function parseEnv(raw) {
17189
17193
  const env = /* @__PURE__ */ new Map();
17190
17194
  for (const line of raw.split(/\r?\n/)) {
@@ -17250,14 +17254,16 @@ async function runStackUpdate(options) {
17250
17254
  const exec2 = options.exec ?? defaultExec;
17251
17255
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
17252
17256
  if (options.rollback) return rollbackStackUpdate(options);
17253
- const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey);
17257
+ const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
17254
17258
  const envRaw = readFileSync25(options.envFile, "utf8");
17255
17259
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
17256
17260
  assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
17257
17261
  const lockFile = options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json");
17262
+ const previousVersion = readCurrentStackVersion(lockFile);
17258
17263
  if (options.dryRun || plan.changes.length === 0) {
17259
17264
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
17260
17265
  }
17266
+ await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
17261
17267
  const backupDir = path37.join(path37.dirname(options.envFile), ".exe-stack-backups");
17262
17268
  mkdirSync19(backupDir, { recursive: true });
17263
17269
  const stamp = now().toISOString().replace(/[:.]/g, "-");
@@ -17269,11 +17275,18 @@ async function runStackUpdate(options) {
17269
17275
  writeFileSync20(tmp, patched, { mode: 384 });
17270
17276
  renameSync7(tmp, options.envFile);
17271
17277
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
17278
+ let registryForLogout;
17272
17279
  try {
17280
+ const creds = await fetchImageCredentials(options);
17281
+ if (creds) {
17282
+ (options.dockerLogin ?? defaultDockerLogin)(creds);
17283
+ registryForLogout = creds.registry;
17284
+ }
17273
17285
  exec2("docker", [...composeArgs, "pull"]);
17274
17286
  exec2("docker", [...composeArgs, "up", "-d"]);
17275
17287
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
17276
17288
  writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
17289
+ await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
17277
17290
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
17278
17291
  } catch (err) {
17279
17292
  writeFileSync20(options.envFile, envRaw, { mode: 384 });
@@ -17282,7 +17295,55 @@ async function runStackUpdate(options) {
17282
17295
  } catch {
17283
17296
  }
17284
17297
  const reason = err instanceof Error ? err.message : String(err);
17298
+ await postDeployAudit(options, "failed", plan.targetVersion, previousVersion, reason, { rollbackAttempted: true });
17285
17299
  throw new Error(`Stack update failed and rollback was attempted: ${reason}`);
17300
+ } finally {
17301
+ if (registryForLogout) {
17302
+ try {
17303
+ (options.dockerLogout ?? defaultDockerLogout)(registryForLogout);
17304
+ } catch {
17305
+ }
17306
+ }
17307
+ }
17308
+ }
17309
+ async function fetchImageCredentials(options) {
17310
+ if (!options.imageCredentialsUrl) return null;
17311
+ const res = await fetch(options.imageCredentialsUrl, {
17312
+ method: "POST",
17313
+ headers: {
17314
+ "content-type": "application/json",
17315
+ ...options.manifestAuthToken ? { authorization: `Bearer ${options.manifestAuthToken}` } : {}
17316
+ },
17317
+ body: JSON.stringify({ deviceId: options.deviceId, licenseKey: options.licenseKey })
17318
+ });
17319
+ if (!res.ok) throw new Error(`Failed to fetch image credentials: HTTP ${res.status}`);
17320
+ return await res.json();
17321
+ }
17322
+ function readCurrentStackVersion(lockFile) {
17323
+ if (!existsSync30(lockFile)) return void 0;
17324
+ try {
17325
+ const parsed = JSON.parse(readFileSync25(lockFile, "utf8"));
17326
+ return parsed.stackVersion;
17327
+ } catch {
17328
+ return void 0;
17329
+ }
17330
+ }
17331
+ async function postDeployAudit(options, status, stackVersion, previousVersion, error, metadata) {
17332
+ if (!options.auditUrl) return;
17333
+ const postJson = options.postJson ?? defaultPostJson;
17334
+ try {
17335
+ await postJson(options.auditUrl, {
17336
+ stackVersion,
17337
+ previousVersion,
17338
+ status,
17339
+ error,
17340
+ metadata,
17341
+ deviceId: options.deviceId,
17342
+ licenseKey: options.licenseKey
17343
+ }, options.manifestAuthToken);
17344
+ } catch (err) {
17345
+ const reason = err instanceof Error ? err.message : String(err);
17346
+ console.warn(`[stack-update] deploy audit failed: ${reason}`);
17286
17347
  }
17287
17348
  }
17288
17349
  async function verifyReleaseHealth(release, retries, delayMs) {
@@ -17321,20 +17382,53 @@ function httpStatus(urlString) {
17321
17382
  function defaultExec(cmd, args2, opts) {
17322
17383
  execFileSync3(cmd, args2, { stdio: "inherit", cwd: opts?.cwd });
17323
17384
  }
17324
- async function defaultFetchText(ref) {
17325
- const res = await fetch(ref);
17385
+ function defaultDockerLogin(creds) {
17386
+ execFileSync3("docker", ["login", creds.registry, "-u", creds.username, "--password-stdin"], {
17387
+ input: creds.password,
17388
+ stdio: ["pipe", "inherit", "inherit"]
17389
+ });
17390
+ }
17391
+ function defaultDockerLogout(registry) {
17392
+ execFileSync3("docker", ["logout", registry], { stdio: "ignore" });
17393
+ }
17394
+ async function defaultFetchText(ref, authToken) {
17395
+ const res = await fetch(ref, {
17396
+ headers: authToken ? { authorization: `Bearer ${authToken}` } : void 0
17397
+ });
17326
17398
  if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
17327
17399
  return res.text();
17328
17400
  }
17401
+ async function defaultPostJson(url, body, authToken) {
17402
+ const res = await fetch(url, {
17403
+ method: "POST",
17404
+ headers: {
17405
+ "content-type": "application/json",
17406
+ ...authToken ? { authorization: `Bearer ${authToken}` } : {}
17407
+ },
17408
+ body: JSON.stringify(body)
17409
+ });
17410
+ if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
17411
+ }
17329
17412
  function defaultStackPaths() {
17330
17413
  const cwdCompose = path37.resolve("docker-compose.yml");
17331
17414
  const cwdEnv = path37.resolve(".env");
17332
17415
  return {
17333
17416
  composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
17334
17417
  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"
17418
+ manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json",
17419
+ auditUrl: process.env.EXE_STACK_AUDIT_URL || "https://update.askexe.com/v1/deploy-audits",
17420
+ imageCredentialsUrl: process.env.EXE_STACK_IMAGE_CREDENTIALS_URL || "https://update.askexe.com/v1/image-credentials",
17421
+ manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN,
17422
+ manifestPublicKey: loadDefaultPublicKey()
17336
17423
  };
17337
17424
  }
17425
+ function loadDefaultPublicKey() {
17426
+ if (process.env.EXE_STACK_PUBLIC_KEY) return process.env.EXE_STACK_PUBLIC_KEY;
17427
+ if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync30(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
17428
+ return readFileSync25(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
17429
+ }
17430
+ return void 0;
17431
+ }
17338
17432
  var init_stack_update = __esm({
17339
17433
  "src/lib/stack-update.ts"() {
17340
17434
  "use strict";
@@ -17353,6 +17447,12 @@ function parseArgs4(args2) {
17353
17447
  manifestRef: defaults.manifestRef,
17354
17448
  composeFile: defaults.composeFile,
17355
17449
  envFile: defaults.envFile,
17450
+ auditUrl: defaults.auditUrl,
17451
+ imageCredentialsUrl: defaults.imageCredentialsUrl,
17452
+ manifestAuthToken: defaults.manifestAuthToken,
17453
+ manifestPublicKey: defaults.manifestPublicKey,
17454
+ deviceId: process.env.EXE_DEVICE_ID,
17455
+ licenseKey: process.env.EXE_LICENSE_KEY,
17356
17456
  dryRun: false,
17357
17457
  check: false,
17358
17458
  rollback: false,
@@ -17373,6 +17473,18 @@ function parseArgs4(args2) {
17373
17473
  else if (arg === "--lock-file") opts.lockFile = next();
17374
17474
  else if (arg === "--public-key") opts.manifestPublicKey = readFileSync26(next(), "utf8");
17375
17475
  else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync26(arg.split("=").slice(1).join("="), "utf8");
17476
+ else if (arg === "--auth-token") opts.manifestAuthToken = next();
17477
+ else if (arg.startsWith("--auth-token=")) opts.manifestAuthToken = arg.split("=").slice(1).join("=");
17478
+ else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
17479
+ else if (arg === "--audit-url") opts.auditUrl = next();
17480
+ else if (arg.startsWith("--audit-url=")) opts.auditUrl = arg.split("=").slice(1).join("=");
17481
+ else if (arg === "--image-credentials-url") opts.imageCredentialsUrl = next();
17482
+ else if (arg.startsWith("--image-credentials-url=")) opts.imageCredentialsUrl = arg.split("=").slice(1).join("=");
17483
+ else if (arg === "--no-image-credentials") opts.imageCredentialsUrl = void 0;
17484
+ else if (arg === "--device-id") opts.deviceId = next();
17485
+ else if (arg.startsWith("--device-id=")) opts.deviceId = arg.split("=").slice(1).join("=");
17486
+ else if (arg === "--license-key") opts.licenseKey = next();
17487
+ else if (arg.startsWith("--license-key=")) opts.licenseKey = arg.split("=").slice(1).join("=");
17376
17488
  else if (arg === "--rollback") opts.rollback = true;
17377
17489
  else if (arg === "--dry-run") opts.dryRun = true;
17378
17490
  else if (arg === "--check") opts.check = true;
@@ -17403,6 +17515,13 @@ Options:
17403
17515
  --check Print available changes only
17404
17516
  --dry-run Plan only; do not run Docker
17405
17517
  --public-key <path> PEM public key for signed manifest verification
17518
+ --auth-token <token> Bearer token for private update manifest/audit API
17519
+ --auth-token-env <name> Read bearer token from an environment variable
17520
+ --audit-url <url> Deploy-audit endpoint (default: EXE_STACK_AUDIT_URL/update.askexe.com)
17521
+ --image-credentials-url <url> Registry credential broker endpoint
17522
+ --no-image-credentials Skip registry credential broker / Docker login
17523
+ --device-id <id> Device ID to include in deploy audit
17524
+ --license-key <key> License key to include in deploy audit
17406
17525
  --rollback Restore latest backed-up .env and restart stack
17407
17526
  --allow-breaking <ids> Confirm breaking changes, comma-separated
17408
17527
  -y, --yes Non-interactive confirmation
@@ -17440,7 +17559,7 @@ async function main3() {
17440
17559
  console.log(`\u2705 Stack rollback attempted using backup: ${result2.backupEnvFile ?? "latest backup"}`);
17441
17560
  return;
17442
17561
  }
17443
- const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey);
17562
+ const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
17444
17563
  const envRaw = readFileSync26(opts.envFile, "utf8");
17445
17564
  const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
17446
17565
  console.log(`Exe OS stack target: ${plan.targetVersion}`);
@@ -95,10 +95,14 @@ function parseStackManifest(raw, publicKey) {
95
95
  }
96
96
  return parsed;
97
97
  }
98
- async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey) {
99
- if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref), publicKey);
98
+ async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey, authToken) {
99
+ if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchTextWithAuth(ref, fetchText, authToken), publicKey);
100
100
  return parseStackManifest(readFileSync(ref, "utf8"), publicKey);
101
101
  }
102
+ async function fetchTextWithAuth(ref, fetchText, authToken) {
103
+ if (!authToken || fetchText !== defaultFetchText) return fetchText(ref);
104
+ return defaultFetchText(ref, authToken);
105
+ }
102
106
  function parseEnv(raw) {
103
107
  const env = /* @__PURE__ */ new Map();
104
108
  for (const line of raw.split(/\r?\n/)) {
@@ -164,14 +168,16 @@ async function runStackUpdate(options) {
164
168
  const exec = options.exec ?? defaultExec;
165
169
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
166
170
  if (options.rollback) return rollbackStackUpdate(options);
167
- const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey);
171
+ const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
168
172
  const envRaw = readFileSync(options.envFile, "utf8");
169
173
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
170
174
  assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
171
175
  const lockFile = options.lockFile ?? path.join(path.dirname(options.envFile), ".exe-stack-lock.json");
176
+ const previousVersion = readCurrentStackVersion(lockFile);
172
177
  if (options.dryRun || plan.changes.length === 0) {
173
178
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
174
179
  }
180
+ await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
175
181
  const backupDir = path.join(path.dirname(options.envFile), ".exe-stack-backups");
176
182
  mkdirSync(backupDir, { recursive: true });
177
183
  const stamp = now().toISOString().replace(/[:.]/g, "-");
@@ -183,11 +189,18 @@ async function runStackUpdate(options) {
183
189
  writeFileSync(tmp, patched, { mode: 384 });
184
190
  renameSync(tmp, options.envFile);
185
191
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
192
+ let registryForLogout;
186
193
  try {
194
+ const creds = await fetchImageCredentials(options);
195
+ if (creds) {
196
+ (options.dockerLogin ?? defaultDockerLogin)(creds);
197
+ registryForLogout = creds.registry;
198
+ }
187
199
  exec("docker", [...composeArgs, "pull"]);
188
200
  exec("docker", [...composeArgs, "up", "-d"]);
189
201
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
190
202
  writeFileSync(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
203
+ await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
191
204
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
192
205
  } catch (err) {
193
206
  writeFileSync(options.envFile, envRaw, { mode: 384 });
@@ -196,7 +209,55 @@ async function runStackUpdate(options) {
196
209
  } catch {
197
210
  }
198
211
  const reason = err instanceof Error ? err.message : String(err);
212
+ await postDeployAudit(options, "failed", plan.targetVersion, previousVersion, reason, { rollbackAttempted: true });
199
213
  throw new Error(`Stack update failed and rollback was attempted: ${reason}`);
214
+ } finally {
215
+ if (registryForLogout) {
216
+ try {
217
+ (options.dockerLogout ?? defaultDockerLogout)(registryForLogout);
218
+ } catch {
219
+ }
220
+ }
221
+ }
222
+ }
223
+ async function fetchImageCredentials(options) {
224
+ if (!options.imageCredentialsUrl) return null;
225
+ const res = await fetch(options.imageCredentialsUrl, {
226
+ method: "POST",
227
+ headers: {
228
+ "content-type": "application/json",
229
+ ...options.manifestAuthToken ? { authorization: `Bearer ${options.manifestAuthToken}` } : {}
230
+ },
231
+ body: JSON.stringify({ deviceId: options.deviceId, licenseKey: options.licenseKey })
232
+ });
233
+ if (!res.ok) throw new Error(`Failed to fetch image credentials: HTTP ${res.status}`);
234
+ return await res.json();
235
+ }
236
+ function readCurrentStackVersion(lockFile) {
237
+ if (!existsSync(lockFile)) return void 0;
238
+ try {
239
+ const parsed = JSON.parse(readFileSync(lockFile, "utf8"));
240
+ return parsed.stackVersion;
241
+ } catch {
242
+ return void 0;
243
+ }
244
+ }
245
+ async function postDeployAudit(options, status, stackVersion, previousVersion, error, metadata) {
246
+ if (!options.auditUrl) return;
247
+ const postJson = options.postJson ?? defaultPostJson;
248
+ try {
249
+ await postJson(options.auditUrl, {
250
+ stackVersion,
251
+ previousVersion,
252
+ status,
253
+ error,
254
+ metadata,
255
+ deviceId: options.deviceId,
256
+ licenseKey: options.licenseKey
257
+ }, options.manifestAuthToken);
258
+ } catch (err) {
259
+ const reason = err instanceof Error ? err.message : String(err);
260
+ console.warn(`[stack-update] deploy audit failed: ${reason}`);
200
261
  }
201
262
  }
202
263
  async function verifyReleaseHealth(release, retries, delayMs) {
@@ -235,20 +296,53 @@ function httpStatus(urlString) {
235
296
  function defaultExec(cmd, args, opts) {
236
297
  execFileSync(cmd, args, { stdio: "inherit", cwd: opts?.cwd });
237
298
  }
238
- async function defaultFetchText(ref) {
239
- const res = await fetch(ref);
299
+ function defaultDockerLogin(creds) {
300
+ execFileSync("docker", ["login", creds.registry, "-u", creds.username, "--password-stdin"], {
301
+ input: creds.password,
302
+ stdio: ["pipe", "inherit", "inherit"]
303
+ });
304
+ }
305
+ function defaultDockerLogout(registry) {
306
+ execFileSync("docker", ["logout", registry], { stdio: "ignore" });
307
+ }
308
+ async function defaultFetchText(ref, authToken) {
309
+ const res = await fetch(ref, {
310
+ headers: authToken ? { authorization: `Bearer ${authToken}` } : void 0
311
+ });
240
312
  if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
241
313
  return res.text();
242
314
  }
315
+ async function defaultPostJson(url, body, authToken) {
316
+ const res = await fetch(url, {
317
+ method: "POST",
318
+ headers: {
319
+ "content-type": "application/json",
320
+ ...authToken ? { authorization: `Bearer ${authToken}` } : {}
321
+ },
322
+ body: JSON.stringify(body)
323
+ });
324
+ if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
325
+ }
243
326
  function defaultStackPaths() {
244
327
  const cwdCompose = path.resolve("docker-compose.yml");
245
328
  const cwdEnv = path.resolve(".env");
246
329
  return {
247
330
  composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
248
331
  envFile: process.env.EXE_STACK_ENV_FILE || (existsSync(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
249
- manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json"
332
+ manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json",
333
+ auditUrl: process.env.EXE_STACK_AUDIT_URL || "https://update.askexe.com/v1/deploy-audits",
334
+ imageCredentialsUrl: process.env.EXE_STACK_IMAGE_CREDENTIALS_URL || "https://update.askexe.com/v1/image-credentials",
335
+ manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN,
336
+ manifestPublicKey: loadDefaultPublicKey()
250
337
  };
251
338
  }
339
+ function loadDefaultPublicKey() {
340
+ if (process.env.EXE_STACK_PUBLIC_KEY) return process.env.EXE_STACK_PUBLIC_KEY;
341
+ if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
342
+ return readFileSync(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
343
+ }
344
+ return void 0;
345
+ }
252
346
 
253
347
  // src/bin/stack-update.ts
254
348
  function parseArgs(args) {
@@ -257,6 +351,12 @@ function parseArgs(args) {
257
351
  manifestRef: defaults.manifestRef,
258
352
  composeFile: defaults.composeFile,
259
353
  envFile: defaults.envFile,
354
+ auditUrl: defaults.auditUrl,
355
+ imageCredentialsUrl: defaults.imageCredentialsUrl,
356
+ manifestAuthToken: defaults.manifestAuthToken,
357
+ manifestPublicKey: defaults.manifestPublicKey,
358
+ deviceId: process.env.EXE_DEVICE_ID,
359
+ licenseKey: process.env.EXE_LICENSE_KEY,
260
360
  dryRun: false,
261
361
  check: false,
262
362
  rollback: false,
@@ -277,6 +377,18 @@ function parseArgs(args) {
277
377
  else if (arg === "--lock-file") opts.lockFile = next();
278
378
  else if (arg === "--public-key") opts.manifestPublicKey = readFileSync2(next(), "utf8");
279
379
  else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync2(arg.split("=").slice(1).join("="), "utf8");
380
+ else if (arg === "--auth-token") opts.manifestAuthToken = next();
381
+ else if (arg.startsWith("--auth-token=")) opts.manifestAuthToken = arg.split("=").slice(1).join("=");
382
+ else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
383
+ else if (arg === "--audit-url") opts.auditUrl = next();
384
+ else if (arg.startsWith("--audit-url=")) opts.auditUrl = arg.split("=").slice(1).join("=");
385
+ else if (arg === "--image-credentials-url") opts.imageCredentialsUrl = next();
386
+ else if (arg.startsWith("--image-credentials-url=")) opts.imageCredentialsUrl = arg.split("=").slice(1).join("=");
387
+ else if (arg === "--no-image-credentials") opts.imageCredentialsUrl = void 0;
388
+ else if (arg === "--device-id") opts.deviceId = next();
389
+ else if (arg.startsWith("--device-id=")) opts.deviceId = arg.split("=").slice(1).join("=");
390
+ else if (arg === "--license-key") opts.licenseKey = next();
391
+ else if (arg.startsWith("--license-key=")) opts.licenseKey = arg.split("=").slice(1).join("=");
280
392
  else if (arg === "--rollback") opts.rollback = true;
281
393
  else if (arg === "--dry-run") opts.dryRun = true;
282
394
  else if (arg === "--check") opts.check = true;
@@ -307,6 +419,13 @@ Options:
307
419
  --check Print available changes only
308
420
  --dry-run Plan only; do not run Docker
309
421
  --public-key <path> PEM public key for signed manifest verification
422
+ --auth-token <token> Bearer token for private update manifest/audit API
423
+ --auth-token-env <name> Read bearer token from an environment variable
424
+ --audit-url <url> Deploy-audit endpoint (default: EXE_STACK_AUDIT_URL/update.askexe.com)
425
+ --image-credentials-url <url> Registry credential broker endpoint
426
+ --no-image-credentials Skip registry credential broker / Docker login
427
+ --device-id <id> Device ID to include in deploy audit
428
+ --license-key <key> License key to include in deploy audit
310
429
  --rollback Restore latest backed-up .env and restart stack
311
430
  --allow-breaking <ids> Confirm breaking changes, comma-separated
312
431
  -y, --yes Non-interactive confirmation
@@ -344,7 +463,7 @@ async function main() {
344
463
  console.log(`\u2705 Stack rollback attempted using backup: ${result2.backupEnvFile ?? "latest backup"}`);
345
464
  return;
346
465
  }
347
- const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey);
466
+ const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
348
467
  const envRaw = readFileSync2(opts.envFile, "utf8");
349
468
  const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
350
469
  console.log(`Exe OS stack target: ${plan.targetVersion}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.62",
3
+ "version": "0.9.64",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",