@askexenow/exe-os 0.9.63 → 0.9.65

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.
@@ -189,7 +189,13 @@ async function runStackUpdate(options) {
189
189
  writeFileSync(tmp, patched, { mode: 384 });
190
190
  renameSync(tmp, options.envFile);
191
191
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
192
+ let registryForLogout;
192
193
  try {
194
+ const creds = await fetchImageCredentials(options);
195
+ if (creds) {
196
+ (options.dockerLogin ?? defaultDockerLogin)(creds);
197
+ registryForLogout = creds.registry;
198
+ }
193
199
  exec("docker", [...composeArgs, "pull"]);
194
200
  exec("docker", [...composeArgs, "up", "-d"]);
195
201
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
@@ -205,8 +211,28 @@ async function runStackUpdate(options) {
205
211
  const reason = err instanceof Error ? err.message : String(err);
206
212
  await postDeployAudit(options, "failed", plan.targetVersion, previousVersion, reason, { rollbackAttempted: true });
207
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
+ }
208
221
  }
209
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
+ }
210
236
  function readCurrentStackVersion(lockFile) {
211
237
  if (!existsSync(lockFile)) return void 0;
212
238
  try {
@@ -270,6 +296,15 @@ function httpStatus(urlString) {
270
296
  function defaultExec(cmd, args, opts) {
271
297
  execFileSync(cmd, args, { stdio: "inherit", cwd: opts?.cwd });
272
298
  }
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
+ }
273
308
  async function defaultFetchText(ref, authToken) {
274
309
  const res = await fetch(ref, {
275
310
  headers: authToken ? { authorization: `Bearer ${authToken}` } : void 0
@@ -296,6 +331,7 @@ function defaultStackPaths() {
296
331
  envFile: process.env.EXE_STACK_ENV_FILE || (existsSync(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
297
332
  manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json",
298
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",
299
335
  manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN,
300
336
  manifestPublicKey: loadDefaultPublicKey()
301
337
  };
@@ -316,6 +352,7 @@ function parseArgs(args) {
316
352
  composeFile: defaults.composeFile,
317
353
  envFile: defaults.envFile,
318
354
  auditUrl: defaults.auditUrl,
355
+ imageCredentialsUrl: defaults.imageCredentialsUrl,
319
356
  manifestAuthToken: defaults.manifestAuthToken,
320
357
  manifestPublicKey: defaults.manifestPublicKey,
321
358
  deviceId: process.env.EXE_DEVICE_ID,
@@ -345,6 +382,9 @@ function parseArgs(args) {
345
382
  else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
346
383
  else if (arg === "--audit-url") opts.auditUrl = next();
347
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;
348
388
  else if (arg === "--device-id") opts.deviceId = next();
349
389
  else if (arg.startsWith("--device-id=")) opts.deviceId = arg.split("=").slice(1).join("=");
350
390
  else if (arg === "--license-key") opts.licenseKey = next();
@@ -382,6 +422,8 @@ Options:
382
422
  --auth-token <token> Bearer token for private update manifest/audit API
383
423
  --auth-token-env <name> Read bearer token from an environment variable
384
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
385
427
  --device-id <id> Device ID to include in deploy audit
386
428
  --license-key <key> License key to include in deploy audit
387
429
  --rollback Restore latest backed-up .env and restart stack
@@ -8892,98 +8892,54 @@ async function fastDbInit() {
8892
8892
  // src/adapters/claude/hooks/bug-report-worker.ts
8893
8893
  init_database();
8894
8894
  init_tasks();
8895
-
8896
- // src/lib/bug-intake.ts
8897
- import { createHash as createHash3, randomUUID as randomUUID4 } from "crypto";
8898
- var BUG_INTAKE_SCHEMA_VERSION = 1;
8899
- function firstMeaningfulLine(text) {
8900
- return text.split("\n").find((line) => line.trim().length > 0)?.trim().slice(0, 80) ?? "unknown error";
8901
- }
8902
- function stableFingerprint(input) {
8903
- const basis = [
8904
- input.source,
8905
- input.toolName ?? "unknown",
8906
- firstMeaningfulLine(input.errorText ?? ""),
8907
- input.projectName ?? "unknown"
8908
- ].join("|");
8909
- return createHash3("sha256").update(basis).digest("hex").slice(0, 16);
8910
- }
8911
- function hashLicense(licenseKey) {
8912
- if (!licenseKey) return void 0;
8913
- return createHash3("sha256").update(licenseKey).digest("hex").slice(0, 16);
8914
- }
8915
- function buildBugIntake(input) {
8916
- const toolName = input.toolName ?? "unknown";
8917
- const errorText = input.errorText ?? "";
8918
- const summary = firstMeaningfulLine(errorText);
8919
- const fingerprint = input.fingerprint && input.fingerprint.trim().length > 0 ? input.fingerprint.trim() : stableFingerprint(input);
8920
- return {
8921
- schemaVersion: BUG_INTAKE_SCHEMA_VERSION,
8922
- id: randomUUID4(),
8923
- source: input.source,
8924
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
8925
- fingerprint,
8926
- severity: input.severity ?? "p1",
8927
- title: `[auto-bug] ${toolName}: ${summary.slice(0, 60)}`,
8928
- summary,
8929
- reporterAgentId: input.reporterAgentId ?? "unknown",
8930
- reporterAgentRole: input.reporterAgentRole ?? "employee",
8931
- projectName: input.projectName ?? "unknown",
8932
- toolName,
8933
- errorText,
8934
- toolInput: input.toolInput ?? "{}",
8935
- runtime: input.runtime,
8936
- repo: input.repo,
8937
- licenseKeyHash: hashLicense(input.licenseKey),
8938
- labels: ["auto-bug", input.source, toolName].filter(Boolean)
8939
- };
8940
- }
8941
- function buildBugIntakeFromEnv(env = process.env) {
8942
- return buildBugIntake({
8943
- source: "hook",
8944
- toolName: env.BUG_TOOL_NAME,
8945
- errorText: env.BUG_ERROR_TEXT,
8946
- toolInput: env.BUG_TOOL_INPUT,
8947
- fingerprint: env.BUG_FINGERPRINT,
8948
- reporterAgentId: env.BUG_AGENT_ID,
8949
- reporterAgentRole: env.BUG_AGENT_ROLE,
8950
- projectName: env.BUG_PROJECT_NAME,
8951
- runtime: env.EXE_RUNTIME,
8952
- repo: env.EXE_REPO,
8953
- licenseKey: env.EXE_LICENSE_KEY
8895
+ async function main() {
8896
+ const toolName = process.env.BUG_TOOL_NAME ?? "unknown";
8897
+ const errorText = process.env.BUG_ERROR_TEXT ?? "";
8898
+ const toolInput = process.env.BUG_TOOL_INPUT ?? "{}";
8899
+ const fingerprint = process.env.BUG_FINGERPRINT ?? "";
8900
+ const agentId = process.env.BUG_AGENT_ID ?? "unknown";
8901
+ const agentRole = process.env.BUG_AGENT_ROLE ?? "employee";
8902
+ const projectName = process.env.BUG_PROJECT_NAME ?? "unknown";
8903
+ await fastDbInit();
8904
+ const fpPrefix = fingerprint.slice(0, 8);
8905
+ const client = getClient();
8906
+ const { loadEmployeesSync: loadEmployeesSync2, getEmployeeByRole: getEmployeeByRole2, getCoordinatorName: getCoordinatorName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
8907
+ const employees = loadEmployeesSync2();
8908
+ const coordinatorName = getCoordinatorName2(employees);
8909
+ const ctoName = getEmployeeByRole2(employees, "CTO")?.name ?? coordinatorName;
8910
+ const existing = await client.execute({
8911
+ sql: `SELECT id FROM tasks
8912
+ WHERE assigned_to = ?
8913
+ AND status IN ('open', 'in_progress')
8914
+ AND title LIKE '[auto-bug]%'
8915
+ AND task_file LIKE ?
8916
+ LIMIT 1`,
8917
+ args: [ctoName, `%${fpPrefix}%`]
8954
8918
  });
8955
- }
8956
- function formatBugIntakeTaskContext(record) {
8957
- return [
8919
+ if (existing.rows.length > 0) {
8920
+ process.stderr.write(`[bug-report-worker] Duplicate found for fingerprint ${fingerprint}, skipping
8921
+ `);
8922
+ return;
8923
+ }
8924
+ const errorSummary = errorText.split("\n").find((line) => line.trim().length > 0)?.trim().slice(0, 60) ?? "unknown error";
8925
+ const context = [
8958
8926
  "## Auto-detected system bug",
8959
8927
  "",
8960
- `**Schema:** bug-intake/v${record.schemaVersion}`,
8961
- `**Source:** ${record.source}`,
8962
- `**Detected by:** ${record.reporterAgentId} (${record.reporterAgentRole})`,
8963
- `**Tool:** ${record.toolName}`,
8964
- `**Timestamp:** ${record.createdAt}`,
8965
- `**Fingerprint:** ${record.fingerprint}`,
8966
- `**Severity:** ${record.severity}`,
8967
- record.runtime ? `**Runtime:** ${record.runtime}` : void 0,
8968
- record.repo ? `**Repo:** ${record.repo}` : void 0,
8969
- record.licenseKeyHash ? `**License hash:** ${record.licenseKeyHash}` : void 0,
8928
+ `**Detected by:** ${agentId} (${agentRole})`,
8929
+ `**Tool:** ${toolName}`,
8930
+ `**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
8931
+ `**Fingerprint:** ${fingerprint}`,
8970
8932
  "",
8971
8933
  "## Error output",
8972
8934
  "",
8973
8935
  "```",
8974
- record.errorText.slice(0, 2e3),
8936
+ errorText.slice(0, 1e3),
8975
8937
  "```",
8976
8938
  "",
8977
8939
  "## Tool input (reproduction context)",
8978
8940
  "",
8979
8941
  "```json",
8980
- record.toolInput.slice(0, 1e3),
8981
- "```",
8982
- "",
8983
- "## Normalized intake JSON",
8984
- "",
8985
- "```json",
8986
- JSON.stringify(record, null, 2),
8942
+ toolInput.slice(0, 500),
8987
8943
  "```",
8988
8944
  "",
8989
8945
  "## Triage notes",
@@ -8991,44 +8947,19 @@ function formatBugIntakeTaskContext(record) {
8991
8947
  "- Classification: system bug (auto-detected)",
8992
8948
  "- Review this error \u2014 fix if real, close if false positive",
8993
8949
  "- If false positive: add the error pattern to USER_ERROR_PATTERNS in error-detector.ts"
8994
- ].filter((line) => line !== void 0).join("\n");
8995
- }
8996
-
8997
- // src/adapters/claude/hooks/bug-report-worker.ts
8998
- async function main() {
8999
- const intake = buildBugIntakeFromEnv(process.env);
9000
- await fastDbInit();
9001
- const client = getClient();
9002
- const { loadEmployeesSync: loadEmployeesSync2, getEmployeeByRole: getEmployeeByRole2, getCoordinatorName: getCoordinatorName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
9003
- const employees = loadEmployeesSync2();
9004
- const coordinatorName = getCoordinatorName2(employees);
9005
- const ctoName = getEmployeeByRole2(employees, "CTO")?.name ?? coordinatorName;
9006
- const existing = await client.execute({
9007
- sql: `SELECT id FROM tasks
9008
- WHERE assigned_to = ?
9009
- AND status IN ('open', 'in_progress')
9010
- AND title LIKE '[auto-bug]%'
9011
- AND (context LIKE ? OR task_file LIKE ?)
9012
- LIMIT 1`,
9013
- args: [ctoName, `%${intake.fingerprint}%`, `%${intake.fingerprint.slice(0, 8)}%`]
9014
- });
9015
- if (existing.rows.length > 0) {
9016
- process.stderr.write(`[bug-report-worker] Duplicate found for fingerprint ${intake.fingerprint}, skipping
9017
- `);
9018
- return;
9019
- }
8950
+ ].join("\n");
9020
8951
  await createTask({
9021
- title: intake.title,
8952
+ title: `[auto-bug] ${toolName}: ${errorSummary}`,
9022
8953
  assignedTo: ctoName,
9023
8954
  assignedBy: "system",
9024
- projectName: intake.projectName,
9025
- priority: intake.severity,
9026
- context: formatBugIntakeTaskContext(intake),
8955
+ projectName,
8956
+ priority: "p1",
8957
+ context,
9027
8958
  baseDir: process.cwd(),
9028
8959
  skipDispatch: true,
9029
8960
  reviewer: coordinatorName
9030
8961
  });
9031
- process.stderr.write(`[bug-report-worker] Created auto-bug task for ${intake.toolName}: ${intake.summary}
8962
+ process.stderr.write(`[bug-report-worker] Created auto-bug task for ${toolName}: ${errorSummary}
9032
8963
  `);
9033
8964
  }
9034
8965
  main().catch((err) => {