@askexenow/exe-os 0.9.78 → 0.9.79

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
@@ -10954,19 +10954,23 @@ import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, unlinkSync as
10954
10954
  import path20 from "path";
10955
10955
  import { randomUUID as randomUUID4 } from "crypto";
10956
10956
  async function main4(argv = process.argv.slice(2)) {
10957
- const command = argv[0] && !argv[0].startsWith("--") ? argv[0] : "test";
10957
+ const command = argv[0] && !argv[0].startsWith("--") ? argv[0] : "health";
10958
10958
  const json = argv.includes("--json");
10959
10959
  const project = getArg(argv, "--project") ?? "support-smoke";
10960
+ if (command === "help" || argv.includes("--help") || argv.includes("-h")) {
10961
+ printHelp();
10962
+ return;
10963
+ }
10960
10964
  if (command === "health") {
10961
10965
  const result = await runHealth();
10962
- output(result, json);
10963
- process.exitCode = result.some((row) => !row.ok) ? 1 : 0;
10966
+ output(result, json, "health");
10967
+ process.exitCode = hasFailures(result) ? 1 : 0;
10964
10968
  return;
10965
10969
  }
10966
10970
  if (command === "test") {
10967
10971
  const result = await runTest(project);
10968
- output(result, json);
10969
- process.exitCode = result.some((row) => !row.ok) ? 1 : 0;
10972
+ output(result, json, "test");
10973
+ process.exitCode = hasFailures(result) ? 1 : 0;
10970
10974
  return;
10971
10975
  }
10972
10976
  console.error("Usage: exe-os support health|test [--project <name>] [--json]");
@@ -10978,34 +10982,40 @@ async function runHealth() {
10978
10982
  checks.push(checkLocalWrite());
10979
10983
  checks.push({
10980
10984
  check: "license_key_present",
10981
- ok: Boolean(loadLicense()),
10982
- detail: loadLicense() ? "license.key found" : "missing ~/.exe-os/license.key; run exe-os setup or exe-os cloud setup"
10985
+ level: loadLicense() ? "pass" : "fail",
10986
+ detail: loadLicense() ? "license.key found" : "missing ~/.exe-os/license.key; run exe-os setup or exe-os cloud setup",
10987
+ next: loadLicense() ? void 0 : "Run `exe-os setup` or ask AskExe for the customer license key."
10983
10988
  });
10984
10989
  checks.push({
10985
10990
  check: "license_token_cached",
10986
- ok: Boolean(readCachedLicenseToken()),
10987
- detail: readCachedLicenseToken() ? "cached license token found" : "no cached token yet; support can still use license.key"
10991
+ level: readCachedLicenseToken() ? "pass" : "warn",
10992
+ detail: readCachedLicenseToken() ? "cached license token found" : "no cached token yet; support can still use license.key",
10993
+ next: readCachedLicenseToken() ? void 0 : "This is OK if license.key exists. It refreshes after cloud/license validation."
10988
10994
  });
10989
10995
  try {
10990
10996
  const res = await fetch(endpoints.healthEndpoint, { method: "GET", signal: AbortSignal.timeout(1e4) });
10991
10997
  const body = await safeJson2(res);
10992
10998
  checks.push({
10993
10999
  check: "support_health_endpoint",
10994
- ok: res.ok && body?.status !== "down",
10995
- detail: `${res.status} ${body?.status ?? res.statusText}`
11000
+ level: res.ok && body?.status !== "down" ? "pass" : "fail",
11001
+ detail: `${res.status} ${body?.status ?? res.statusText}`,
11002
+ next: res.ok ? void 0 : "Check internet access, DNS, or AskExe support status."
10996
11003
  });
10997
11004
  for (const remote of body?.checks ?? []) {
11005
+ const name = remote.name ?? "unknown";
10998
11006
  checks.push({
10999
- check: `server_${remote.name ?? "unknown"}`,
11000
- ok: remote.ok === true,
11001
- detail: remote.detail ?? (remote.ok ? "ok" : "failed")
11007
+ check: `server_${name}`,
11008
+ level: remote.ok === true ? "pass" : name === "admin_inbox_configured" ? "warn" : "fail",
11009
+ detail: remote.detail ?? (remote.ok ? "ok" : "failed"),
11010
+ next: remote.ok ? void 0 : "AskExe must fix this server-side."
11002
11011
  });
11003
11012
  }
11004
11013
  } catch (err) {
11005
11014
  checks.push({
11006
11015
  check: "support_health_endpoint",
11007
- ok: false,
11008
- detail: err instanceof Error ? err.message : String(err)
11016
+ level: "fail",
11017
+ detail: err instanceof Error ? err.message : String(err),
11018
+ next: "Check internet access, then run `exe-os support health` again."
11009
11019
  });
11010
11020
  }
11011
11021
  return checks;
@@ -11018,9 +11028,14 @@ async function runTest(project) {
11018
11028
  const id = randomUUID4();
11019
11029
  const version = readPackageVersion();
11020
11030
  const reportPath = writeLocalTestReport(id, project, version);
11021
- checks.push({ check: "local_report_file", ok: true, detail: reportPath });
11031
+ checks.push({ check: "local_report_file", level: "pass", detail: reportPath });
11022
11032
  if (!licenseKey && !licenseToken) {
11023
- checks.push({ check: "upstream_post", ok: false, detail: "missing license key/token" });
11033
+ checks.push({
11034
+ check: "upstream_post",
11035
+ level: "fail",
11036
+ detail: "not sent because this device has no license key/token",
11037
+ next: "Run `exe-os setup` or ask AskExe to provision this customer device."
11038
+ });
11024
11039
  return checks;
11025
11040
  }
11026
11041
  const payload = {
@@ -11053,14 +11068,20 @@ async function runTest(project) {
11053
11068
  const data = await safeJson2(res);
11054
11069
  checks.push({
11055
11070
  check: "upstream_post",
11056
- ok: res.ok,
11057
- detail: res.ok ? `sent id=${String(data?.id ?? id)}` : `${res.status} ${JSON.stringify(data)}`
11071
+ level: res.ok ? "pass" : "fail",
11072
+ detail: res.ok ? `sent id=${String(data?.id ?? id)}` : summarizeHttpFailure(res.status, data),
11073
+ next: res.ok ? void 0 : nextForPostFailure(res.status)
11058
11074
  });
11059
11075
  if (res.ok) {
11060
11076
  checks.push(await maybeCloseAdmin(String(data?.id ?? id), endpoints.adminEndpoint, version));
11061
11077
  }
11062
11078
  } catch (err) {
11063
- checks.push({ check: "upstream_post", ok: false, detail: err instanceof Error ? err.message : String(err) });
11079
+ checks.push({
11080
+ check: "upstream_post",
11081
+ level: "fail",
11082
+ detail: err instanceof Error ? err.message : String(err),
11083
+ next: "Check internet access, then run `exe-os support test` again."
11084
+ });
11064
11085
  }
11065
11086
  return checks;
11066
11087
  }
@@ -11078,9 +11099,14 @@ function checkLocalWrite() {
11078
11099
  mkdirSync13(dir, { recursive: true, mode: 448 });
11079
11100
  writeFileSync13(testPath, "ok\n", { mode: 384 });
11080
11101
  unlinkSync7(testPath);
11081
- return { check: "local_bug_report_dir_writable", ok: true, detail: dir };
11102
+ return { check: "local_bug_report_dir_writable", level: "pass", detail: dir };
11082
11103
  } catch (err) {
11083
- return { check: "local_bug_report_dir_writable", ok: false, detail: err instanceof Error ? err.message : String(err) };
11104
+ return {
11105
+ check: "local_bug_report_dir_writable",
11106
+ level: "fail",
11107
+ detail: err instanceof Error ? err.message : String(err),
11108
+ next: "Fix permissions on ~/.exe-os or rerun setup on a writable user account."
11109
+ };
11084
11110
  }
11085
11111
  }
11086
11112
  function writeLocalTestReport(id, project, version) {
@@ -11100,7 +11126,7 @@ Synthetic smoke test from \`exe-os support test\`. Safe to close.
11100
11126
  async function maybeCloseAdmin(id, adminEndpoint, version) {
11101
11127
  const token = process.env.ASKEXE_SUPPORT_ADMIN_TOKEN ?? process.env.EXE_SUPPORT_ADMIN_TOKEN;
11102
11128
  if (!token) {
11103
- return { check: "askexe_admin_autoclose", ok: true, detail: "skipped; admin token is AskExe-only" };
11129
+ return { check: "askexe_admin_autoclose", level: "warn", detail: "skipped; admin token is AskExe-only" };
11104
11130
  }
11105
11131
  try {
11106
11132
  const res = await fetch(`${adminEndpoint}/${encodeURIComponent(id)}`, {
@@ -11113,9 +11139,19 @@ async function maybeCloseAdmin(id, adminEndpoint, version) {
11113
11139
  }),
11114
11140
  signal: AbortSignal.timeout(1e4)
11115
11141
  });
11116
- return { check: "askexe_admin_autoclose", ok: res.ok, detail: res.ok ? "closed" : `${res.status} ${await res.text()}` };
11142
+ return {
11143
+ check: "askexe_admin_autoclose",
11144
+ level: res.ok ? "pass" : "warn",
11145
+ detail: res.ok ? "closed" : `${res.status} ${await res.text()}`,
11146
+ next: res.ok ? void 0 : "Report was sent; AskExe can close the smoke report manually."
11147
+ };
11117
11148
  } catch (err) {
11118
- return { check: "askexe_admin_autoclose", ok: false, detail: err instanceof Error ? err.message : String(err) };
11149
+ return {
11150
+ check: "askexe_admin_autoclose",
11151
+ level: "warn",
11152
+ detail: err instanceof Error ? err.message : String(err),
11153
+ next: "Report was sent; AskExe can close the smoke report manually."
11154
+ };
11119
11155
  }
11120
11156
  }
11121
11157
  function readPackageVersion() {
@@ -11145,14 +11181,54 @@ function getArg(argv, name) {
11145
11181
  const found = argv.find((arg) => arg.startsWith(prefix));
11146
11182
  return found?.slice(prefix.length);
11147
11183
  }
11148
- function output(rows, json) {
11184
+ function hasFailures(rows) {
11185
+ return rows.some((row) => row.level === "fail");
11186
+ }
11187
+ function summarizeHttpFailure(status, data) {
11188
+ const error = data?.error;
11189
+ return `${status} ${error?.message ?? JSON.stringify(data)}`;
11190
+ }
11191
+ function nextForPostFailure(status) {
11192
+ if (status === 401) return "License credentials were not sent. Run `exe-os support health` and check license_key_present.";
11193
+ if (status === 403) return "License is valid locally but not accepted by support intake. AskExe must check server-side license provisioning.";
11194
+ if (status >= 500) return "AskExe support intake is unhealthy server-side. Try again shortly and report the local file path.";
11195
+ return "Run `exe-os support health`; if it passes, send this output to AskExe.";
11196
+ }
11197
+ function printHelp() {
11198
+ console.log(`
11199
+ exe-os support
11200
+
11201
+ Customer-safe diagnostics for AskExe bug-report intake.
11202
+
11203
+ Commands:
11204
+ exe-os support health Check local setup + AskExe support server health
11205
+ exe-os support test --project hygo Send a safe smoke report end-to-end
11206
+
11207
+ What success means:
11208
+ health = this device looks ready
11209
+ test = AskExe received a real report from this device
11210
+ `);
11211
+ }
11212
+ function output(rows, json, command) {
11149
11213
  if (json) {
11150
- console.log(JSON.stringify({ ok: rows.every((row) => row.ok), checks: rows }, null, 2));
11214
+ console.log(JSON.stringify({ ok: !hasFailures(rows), checks: rows }, null, 2));
11151
11215
  return;
11152
11216
  }
11153
- console.log("\nexe-os support diagnostics\n");
11217
+ console.log(`
11218
+ exe-os support ${command}
11219
+ `);
11154
11220
  for (const row of rows) {
11155
- console.log(`${row.ok ? "\u2705" : "\u274C"} ${row.check}: ${row.detail}`);
11221
+ const icon = row.level === "pass" ? "\u2705" : row.level === "warn" ? "\u26A0\uFE0F " : "\u274C";
11222
+ console.log(`${icon} ${row.check}: ${row.detail}`);
11223
+ if (row.next) console.log(` \u2192 ${row.next}`);
11224
+ }
11225
+ console.log("");
11226
+ if (hasFailures(rows)) {
11227
+ console.log("Result: not ready. Fix the failed item(s), then rerun this command.");
11228
+ } else if (rows.some((row) => row.level === "warn")) {
11229
+ console.log("Result: ready with warnings. Bug reports can be sent; warnings are informational unless AskExe asked for stricter validation.");
11230
+ } else {
11231
+ console.log(command === "test" ? "Result: ready. AskExe received the test report." : "Result: ready. Run `exe-os support test --project <customer>` for an end-to-end send test.");
11156
11232
  }
11157
11233
  console.log("");
11158
11234
  }
@@ -20172,7 +20248,7 @@ function parseArgs4(args2) {
20172
20248
  else if (arg === "--allow-breaking") opts.allowedBreakingChangeIds.push(...next().split(",").map((s) => s.trim()).filter(Boolean));
20173
20249
  else if (arg.startsWith("--allow-breaking=")) opts.allowedBreakingChangeIds.push(...arg.split("=")[1].split(",").map((s) => s.trim()).filter(Boolean));
20174
20250
  else if (arg === "--help" || arg === "-h") {
20175
- printHelp();
20251
+ printHelp2();
20176
20252
  process.exit(0);
20177
20253
  } else {
20178
20254
  throw new Error(`Unknown option: ${arg}`);
@@ -20180,7 +20256,7 @@ function parseArgs4(args2) {
20180
20256
  }
20181
20257
  return opts;
20182
20258
  }
20183
- function printHelp() {
20259
+ function printHelp2() {
20184
20260
  console.log(`exe-os stack-update \u2014 update a self-hosted Exe OS stack from a pinned manifest
20185
20261
 
20186
20262
  Usage:
@@ -20450,7 +20526,7 @@ var registry_proxy_exports = {};
20450
20526
  __export(registry_proxy_exports, {
20451
20527
  main: () => main8
20452
20528
  });
20453
- function printHelp2() {
20529
+ function printHelp3() {
20454
20530
  console.log(`exe-os registry-proxy \u2014 authenticated pull-through proxy for AskExe customer images
20455
20531
 
20456
20532
  Environment:
@@ -20469,7 +20545,7 @@ Docker image shape for clients:
20469
20545
  }
20470
20546
  async function main8(args2 = process.argv.slice(2)) {
20471
20547
  if (args2.includes("--help") || args2.includes("-h")) {
20472
- printHelp2();
20548
+ printHelp3();
20473
20549
  return;
20474
20550
  }
20475
20551
  await runRegistryProxy(registryProxyOptionsFromEnv());
@@ -254,19 +254,23 @@ function readCachedLicenseToken() {
254
254
  var DEFAULT_BUG_ENDPOINT = "https://askexe.com/v1/support/bug-reports";
255
255
  var DEFAULT_ADMIN_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
256
256
  async function main(argv = process.argv.slice(2)) {
257
- const command = argv[0] && !argv[0].startsWith("--") ? argv[0] : "test";
257
+ const command = argv[0] && !argv[0].startsWith("--") ? argv[0] : "health";
258
258
  const json = argv.includes("--json");
259
259
  const project = getArg(argv, "--project") ?? "support-smoke";
260
+ if (command === "help" || argv.includes("--help") || argv.includes("-h")) {
261
+ printHelp();
262
+ return;
263
+ }
260
264
  if (command === "health") {
261
265
  const result = await runHealth();
262
- output(result, json);
263
- process.exitCode = result.some((row) => !row.ok) ? 1 : 0;
266
+ output(result, json, "health");
267
+ process.exitCode = hasFailures(result) ? 1 : 0;
264
268
  return;
265
269
  }
266
270
  if (command === "test") {
267
271
  const result = await runTest(project);
268
- output(result, json);
269
- process.exitCode = result.some((row) => !row.ok) ? 1 : 0;
272
+ output(result, json, "test");
273
+ process.exitCode = hasFailures(result) ? 1 : 0;
270
274
  return;
271
275
  }
272
276
  console.error("Usage: exe-os support health|test [--project <name>] [--json]");
@@ -278,34 +282,40 @@ async function runHealth() {
278
282
  checks.push(checkLocalWrite());
279
283
  checks.push({
280
284
  check: "license_key_present",
281
- ok: Boolean(loadLicense()),
282
- detail: loadLicense() ? "license.key found" : "missing ~/.exe-os/license.key; run exe-os setup or exe-os cloud setup"
285
+ level: loadLicense() ? "pass" : "fail",
286
+ detail: loadLicense() ? "license.key found" : "missing ~/.exe-os/license.key; run exe-os setup or exe-os cloud setup",
287
+ next: loadLicense() ? void 0 : "Run `exe-os setup` or ask AskExe for the customer license key."
283
288
  });
284
289
  checks.push({
285
290
  check: "license_token_cached",
286
- ok: Boolean(readCachedLicenseToken()),
287
- detail: readCachedLicenseToken() ? "cached license token found" : "no cached token yet; support can still use license.key"
291
+ level: readCachedLicenseToken() ? "pass" : "warn",
292
+ detail: readCachedLicenseToken() ? "cached license token found" : "no cached token yet; support can still use license.key",
293
+ next: readCachedLicenseToken() ? void 0 : "This is OK if license.key exists. It refreshes after cloud/license validation."
288
294
  });
289
295
  try {
290
296
  const res = await fetch(endpoints.healthEndpoint, { method: "GET", signal: AbortSignal.timeout(1e4) });
291
297
  const body = await safeJson(res);
292
298
  checks.push({
293
299
  check: "support_health_endpoint",
294
- ok: res.ok && body?.status !== "down",
295
- detail: `${res.status} ${body?.status ?? res.statusText}`
300
+ level: res.ok && body?.status !== "down" ? "pass" : "fail",
301
+ detail: `${res.status} ${body?.status ?? res.statusText}`,
302
+ next: res.ok ? void 0 : "Check internet access, DNS, or AskExe support status."
296
303
  });
297
304
  for (const remote of body?.checks ?? []) {
305
+ const name = remote.name ?? "unknown";
298
306
  checks.push({
299
- check: `server_${remote.name ?? "unknown"}`,
300
- ok: remote.ok === true,
301
- detail: remote.detail ?? (remote.ok ? "ok" : "failed")
307
+ check: `server_${name}`,
308
+ level: remote.ok === true ? "pass" : name === "admin_inbox_configured" ? "warn" : "fail",
309
+ detail: remote.detail ?? (remote.ok ? "ok" : "failed"),
310
+ next: remote.ok ? void 0 : "AskExe must fix this server-side."
302
311
  });
303
312
  }
304
313
  } catch (err) {
305
314
  checks.push({
306
315
  check: "support_health_endpoint",
307
- ok: false,
308
- detail: err instanceof Error ? err.message : String(err)
316
+ level: "fail",
317
+ detail: err instanceof Error ? err.message : String(err),
318
+ next: "Check internet access, then run `exe-os support health` again."
309
319
  });
310
320
  }
311
321
  return checks;
@@ -318,9 +328,14 @@ async function runTest(project) {
318
328
  const id = randomUUID2();
319
329
  const version = readPackageVersion();
320
330
  const reportPath = writeLocalTestReport(id, project, version);
321
- checks.push({ check: "local_report_file", ok: true, detail: reportPath });
331
+ checks.push({ check: "local_report_file", level: "pass", detail: reportPath });
322
332
  if (!licenseKey && !licenseToken) {
323
- checks.push({ check: "upstream_post", ok: false, detail: "missing license key/token" });
333
+ checks.push({
334
+ check: "upstream_post",
335
+ level: "fail",
336
+ detail: "not sent because this device has no license key/token",
337
+ next: "Run `exe-os setup` or ask AskExe to provision this customer device."
338
+ });
324
339
  return checks;
325
340
  }
326
341
  const payload = {
@@ -353,14 +368,20 @@ async function runTest(project) {
353
368
  const data = await safeJson(res);
354
369
  checks.push({
355
370
  check: "upstream_post",
356
- ok: res.ok,
357
- detail: res.ok ? `sent id=${String(data?.id ?? id)}` : `${res.status} ${JSON.stringify(data)}`
371
+ level: res.ok ? "pass" : "fail",
372
+ detail: res.ok ? `sent id=${String(data?.id ?? id)}` : summarizeHttpFailure(res.status, data),
373
+ next: res.ok ? void 0 : nextForPostFailure(res.status)
358
374
  });
359
375
  if (res.ok) {
360
376
  checks.push(await maybeCloseAdmin(String(data?.id ?? id), endpoints.adminEndpoint, version));
361
377
  }
362
378
  } catch (err) {
363
- checks.push({ check: "upstream_post", ok: false, detail: err instanceof Error ? err.message : String(err) });
379
+ checks.push({
380
+ check: "upstream_post",
381
+ level: "fail",
382
+ detail: err instanceof Error ? err.message : String(err),
383
+ next: "Check internet access, then run `exe-os support test` again."
384
+ });
364
385
  }
365
386
  return checks;
366
387
  }
@@ -378,9 +399,14 @@ function checkLocalWrite() {
378
399
  mkdirSync3(dir, { recursive: true, mode: 448 });
379
400
  writeFileSync2(testPath, "ok\n", { mode: 384 });
380
401
  unlinkSync(testPath);
381
- return { check: "local_bug_report_dir_writable", ok: true, detail: dir };
402
+ return { check: "local_bug_report_dir_writable", level: "pass", detail: dir };
382
403
  } catch (err) {
383
- return { check: "local_bug_report_dir_writable", ok: false, detail: err instanceof Error ? err.message : String(err) };
404
+ return {
405
+ check: "local_bug_report_dir_writable",
406
+ level: "fail",
407
+ detail: err instanceof Error ? err.message : String(err),
408
+ next: "Fix permissions on ~/.exe-os or rerun setup on a writable user account."
409
+ };
384
410
  }
385
411
  }
386
412
  function writeLocalTestReport(id, project, version) {
@@ -400,7 +426,7 @@ Synthetic smoke test from \`exe-os support test\`. Safe to close.
400
426
  async function maybeCloseAdmin(id, adminEndpoint, version) {
401
427
  const token = process.env.ASKEXE_SUPPORT_ADMIN_TOKEN ?? process.env.EXE_SUPPORT_ADMIN_TOKEN;
402
428
  if (!token) {
403
- return { check: "askexe_admin_autoclose", ok: true, detail: "skipped; admin token is AskExe-only" };
429
+ return { check: "askexe_admin_autoclose", level: "warn", detail: "skipped; admin token is AskExe-only" };
404
430
  }
405
431
  try {
406
432
  const res = await fetch(`${adminEndpoint}/${encodeURIComponent(id)}`, {
@@ -413,9 +439,19 @@ async function maybeCloseAdmin(id, adminEndpoint, version) {
413
439
  }),
414
440
  signal: AbortSignal.timeout(1e4)
415
441
  });
416
- return { check: "askexe_admin_autoclose", ok: res.ok, detail: res.ok ? "closed" : `${res.status} ${await res.text()}` };
442
+ return {
443
+ check: "askexe_admin_autoclose",
444
+ level: res.ok ? "pass" : "warn",
445
+ detail: res.ok ? "closed" : `${res.status} ${await res.text()}`,
446
+ next: res.ok ? void 0 : "Report was sent; AskExe can close the smoke report manually."
447
+ };
417
448
  } catch (err) {
418
- return { check: "askexe_admin_autoclose", ok: false, detail: err instanceof Error ? err.message : String(err) };
449
+ return {
450
+ check: "askexe_admin_autoclose",
451
+ level: "warn",
452
+ detail: err instanceof Error ? err.message : String(err),
453
+ next: "Report was sent; AskExe can close the smoke report manually."
454
+ };
419
455
  }
420
456
  }
421
457
  function readPackageVersion() {
@@ -445,14 +481,54 @@ function getArg(argv, name) {
445
481
  const found = argv.find((arg) => arg.startsWith(prefix));
446
482
  return found?.slice(prefix.length);
447
483
  }
448
- function output(rows, json) {
484
+ function hasFailures(rows) {
485
+ return rows.some((row) => row.level === "fail");
486
+ }
487
+ function summarizeHttpFailure(status, data) {
488
+ const error = data?.error;
489
+ return `${status} ${error?.message ?? JSON.stringify(data)}`;
490
+ }
491
+ function nextForPostFailure(status) {
492
+ if (status === 401) return "License credentials were not sent. Run `exe-os support health` and check license_key_present.";
493
+ if (status === 403) return "License is valid locally but not accepted by support intake. AskExe must check server-side license provisioning.";
494
+ if (status >= 500) return "AskExe support intake is unhealthy server-side. Try again shortly and report the local file path.";
495
+ return "Run `exe-os support health`; if it passes, send this output to AskExe.";
496
+ }
497
+ function printHelp() {
498
+ console.log(`
499
+ exe-os support
500
+
501
+ Customer-safe diagnostics for AskExe bug-report intake.
502
+
503
+ Commands:
504
+ exe-os support health Check local setup + AskExe support server health
505
+ exe-os support test --project hygo Send a safe smoke report end-to-end
506
+
507
+ What success means:
508
+ health = this device looks ready
509
+ test = AskExe received a real report from this device
510
+ `);
511
+ }
512
+ function output(rows, json, command) {
449
513
  if (json) {
450
- console.log(JSON.stringify({ ok: rows.every((row) => row.ok), checks: rows }, null, 2));
514
+ console.log(JSON.stringify({ ok: !hasFailures(rows), checks: rows }, null, 2));
451
515
  return;
452
516
  }
453
- console.log("\nexe-os support diagnostics\n");
517
+ console.log(`
518
+ exe-os support ${command}
519
+ `);
454
520
  for (const row of rows) {
455
- console.log(`${row.ok ? "\u2705" : "\u274C"} ${row.check}: ${row.detail}`);
521
+ const icon = row.level === "pass" ? "\u2705" : row.level === "warn" ? "\u26A0\uFE0F " : "\u274C";
522
+ console.log(`${icon} ${row.check}: ${row.detail}`);
523
+ if (row.next) console.log(` \u2192 ${row.next}`);
524
+ }
525
+ console.log("");
526
+ if (hasFailures(rows)) {
527
+ console.log("Result: not ready. Fix the failed item(s), then rerun this command.");
528
+ } else if (rows.some((row) => row.level === "warn")) {
529
+ console.log("Result: ready with warnings. Bug reports can be sent; warnings are informational unless AskExe asked for stricter validation.");
530
+ } else {
531
+ console.log(command === "test" ? "Result: ready. AskExe received the test report." : "Result: ready. Run `exe-os support test --project <customer>` for an end-to-end send test.");
456
532
  }
457
533
  console.log("");
458
534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.78",
3
+ "version": "0.9.79",
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",