@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 +110 -34
- package/dist/bin/exe-support.js +106 -30
- package/package.json +1 -1
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] : "
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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_${
|
|
11000
|
-
|
|
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
|
-
|
|
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",
|
|
11031
|
+
checks.push({ check: "local_report_file", level: "pass", detail: reportPath });
|
|
11022
11032
|
if (!licenseKey && !licenseToken) {
|
|
11023
|
-
checks.push({
|
|
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
|
-
|
|
11057
|
-
detail: res.ok ? `sent id=${String(data?.id ?? id)}` :
|
|
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({
|
|
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",
|
|
11102
|
+
return { check: "local_bug_report_dir_writable", level: "pass", detail: dir };
|
|
11082
11103
|
} catch (err) {
|
|
11083
|
-
return {
|
|
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",
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
11214
|
+
console.log(JSON.stringify({ ok: !hasFailures(rows), checks: rows }, null, 2));
|
|
11151
11215
|
return;
|
|
11152
11216
|
}
|
|
11153
|
-
console.log(
|
|
11217
|
+
console.log(`
|
|
11218
|
+
exe-os support ${command}
|
|
11219
|
+
`);
|
|
11154
11220
|
for (const row of rows) {
|
|
11155
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
20548
|
+
printHelp3();
|
|
20473
20549
|
return;
|
|
20474
20550
|
}
|
|
20475
20551
|
await runRegistryProxy(registryProxyOptionsFromEnv());
|
package/dist/bin/exe-support.js
CHANGED
|
@@ -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] : "
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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_${
|
|
300
|
-
|
|
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
|
-
|
|
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",
|
|
331
|
+
checks.push({ check: "local_report_file", level: "pass", detail: reportPath });
|
|
322
332
|
if (!licenseKey && !licenseToken) {
|
|
323
|
-
checks.push({
|
|
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
|
-
|
|
357
|
-
detail: res.ok ? `sent id=${String(data?.id ?? id)}` :
|
|
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({
|
|
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",
|
|
402
|
+
return { check: "local_bug_report_dir_writable", level: "pass", detail: dir };
|
|
382
403
|
} catch (err) {
|
|
383
|
-
return {
|
|
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",
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
514
|
+
console.log(JSON.stringify({ ok: !hasFailures(rows), checks: rows }, null, 2));
|
|
451
515
|
return;
|
|
452
516
|
}
|
|
453
|
-
console.log(
|
|
517
|
+
console.log(`
|
|
518
|
+
exe-os support ${command}
|
|
519
|
+
`);
|
|
454
520
|
for (const row of rows) {
|
|
455
|
-
|
|
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.
|
|
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",
|