@tokenoftrust/cli 2.0.27 → 2.0.28

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/bin/tot.mjs CHANGED
@@ -27,6 +27,10 @@ import {
27
27
  recordDiagnostic,
28
28
  resetInvocationDiagnostics,
29
29
  } from "../src/diagnostics.mjs";
30
+ import {
31
+ nativeWindowsRefuseMessage,
32
+ shouldRefuseNativeWindows,
33
+ } from "../src/platform.mjs";
30
34
 
31
35
  const BUILD_ORDER = ["clone", "validate", "dev", "preview"];
32
36
 
@@ -99,6 +103,14 @@ async function dispatch(cmd, rest, ctx) {
99
103
  return cmd ? 0 : 2;
100
104
  }
101
105
 
106
+ // Native Windows: refuse before login/--device/start burn cycles on a host the
107
+ // local loop cannot run on. doctor/feedback/ideas stay available so the gate
108
+ // itself is discoverable. WSL reports as linux and passes through.
109
+ if (shouldRefuseNativeWindows(cmd)) {
110
+ process.stderr.write(nativeWindowsRefuseMessage(cmd));
111
+ return 2;
112
+ }
113
+
102
114
  if (cmd === "start") {
103
115
  const { run } = await import("../src/commands/start.mjs");
104
116
  return run(rest, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "2.0.27",
3
+ "version": "2.0.28",
4
4
  "description": "Token of Trust developer CLI — clone a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Token of Trust",
@@ -25,6 +25,11 @@ import { join } from "node:path";
25
25
  import { hasLegacyOperatorEnv, legacyOperatorEnvAdvisory } from "../auth.mjs";
26
26
  import { MIN_NODE, nodeMeetsFloor } from "../ensure-node.mjs";
27
27
  import { clientPackages, osLabel } from "../mcp.mjs";
28
+ import {
29
+ isNativeWindows,
30
+ nativeWindowsDetail,
31
+ shouldAttemptDoctorAutoLogin,
32
+ } from "../platform.mjs";
28
33
  import { defaultCredentialsPath, readCredentials, isExpired } from "../token-store.mjs";
29
34
  import { dockerAvailable, tryStartDocker, RENDERER_CACHE_ROOT, rendererArtifactComplete } from "./dev.mjs";
30
35
  import { loginAndCache } from "./login.mjs";
@@ -68,14 +73,12 @@ export function collectChecks(_ctx, env = process.env) {
68
73
 
69
74
  // Native Windows isn't a supported host for the local loop (the runner install
70
75
  // + tar extraction assume a POSIX toolchain) — WSL is, and reports as linux
71
- // here. Say so up front instead of letting `tot dev` fail with an opaque ENOENT.
72
- const nativeWindows = process.platform === "win32";
76
+ // here. Say so up front instead of letting `tot login` / `tot dev` fail opaquely.
77
+ const nativeWindows = isNativeWindows();
73
78
  checks.push({
74
79
  name: "supported platform",
75
80
  pass: !nativeWindows,
76
- detail: nativeWindows
77
- ? "native Windows isn't supported yet — run tot inside WSL (https://learn.microsoft.com/windows/wsl/install)"
78
- : "macOS / Linux / WSL",
81
+ detail: nativeWindows ? nativeWindowsDetail() : "macOS / Linux / WSL",
79
82
  blocking: true,
80
83
  });
81
84
 
@@ -178,13 +181,17 @@ async function applyFixes(checks, env) {
178
181
 
179
182
  const auth = checks.find((c) => c.name === "auth identity");
180
183
  if (auth && !auth.pass) {
181
- const mcpUrl = env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
182
- console.log(" ~ not signed in — opening the browser to sign in…");
183
- try {
184
- await loginAndCache(mcpUrl, env, { log: (m) => console.log(` ${m}`) });
185
- console.log(" ✓ signed in");
186
- } catch (e) {
187
- console.log(` ~ couldn't sign in automatically (${e?.message || e}) — run \`tot login\``);
184
+ if (!shouldAttemptDoctorAutoLogin()) {
185
+ console.log(` ~ not signed in — skipping auto-login (${nativeWindowsDetail()})`);
186
+ } else {
187
+ const mcpUrl = env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
188
+ console.log(" ~ not signed in — opening the browser to sign in…");
189
+ try {
190
+ await loginAndCache(mcpUrl, env, { log: (m) => console.log(` ${m}`) });
191
+ console.log(" ✓ signed in");
192
+ } catch (e) {
193
+ console.log(` ~ couldn't sign in automatically (${e?.message || e}) — run \`tot login\``);
194
+ }
188
195
  }
189
196
  }
190
197
  }
@@ -24,6 +24,7 @@ import { defaultCredentialsPath, readCredentials, writeCredentials } from "../to
24
24
  import { openBrowser } from "../open.mjs";
25
25
  import { fail } from "../errors.mjs";
26
26
  import { cockpitRecoveryUrl, normalizeEmailHint, redactEmailForHint, emailFromJwt } from "../auth.mjs";
27
+ import { loginFailureNext } from "../platform.mjs";
27
28
  import { isInteractive, promptYesNo } from "../prompt.mjs";
28
29
  import { versionStamp } from "../mcp.mjs";
29
30
 
@@ -293,9 +294,7 @@ export async function run(argv, _ctx) {
293
294
  console.error(
294
295
  fail(
295
296
  `sign-in didn't complete: ${e?.message || e}`,
296
- args.device
297
- ? "re-run `tot login --device` and approve the code."
298
- : "re-run `tot login` and finish the browser sign-in (approve the request) — or try `tot login --device`.",
297
+ loginFailureNext({ device: args.device }),
299
298
  ),
300
299
  );
301
300
  return 1;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Host-platform readiness for the local loop.
3
+ *
4
+ * Native Windows is unsupported (runner install + tar extraction assume a POSIX
5
+ * toolchain). WSL reports as `linux` and is the supported path on a Windows box.
6
+ * Keep the check + copy in ONE place so doctor, the CLI gate, and login recovery
7
+ * hints cannot drift.
8
+ */
9
+
10
+ export const WSL_INSTALL_URL = "https://learn.microsoft.com/windows/wsl/install";
11
+
12
+ /**
13
+ * True when this process is native Windows (not WSL).
14
+ * @param {string} [platform] Node `process.platform` value (defaults to current).
15
+ */
16
+ export function isNativeWindows(platform = process.platform) {
17
+ return platform === "win32";
18
+ }
19
+
20
+ /**
21
+ * One-line detail used by `tot doctor` and CliError footers.
22
+ * @param {{ url?: string }} [opts]
23
+ */
24
+ export function nativeWindowsDetail(opts = {}) {
25
+ const url = opts.url ?? WSL_INSTALL_URL;
26
+ return `native Windows isn't supported yet — run tot inside WSL (${url})`;
27
+ }
28
+
29
+ /**
30
+ * Multi-line refuse message printed before exit. Names the attempted command so
31
+ * a pasted invite (`tot login --code … && tot start`) still points at WSL.
32
+ * @param {string} [command] e.g. "login", "start"
33
+ */
34
+ export function nativeWindowsRefuseMessage(command) {
35
+ const verb = command ? `tot ${command}` : "tot";
36
+ return (
37
+ `✗ ${nativeWindowsDetail()}\n` +
38
+ ` → next: install WSL, open an Ubuntu (or other Linux) terminal, install Node 24 LTS + git there,\n` +
39
+ ` then re-run \`${verb}\` inside WSL — don't keep retrying login/--device on native Windows.\n`
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Commands that are safe / useful to run on native Windows so the developer can
45
+ * learn the gate (`doctor`) or report friction (`feedback`) without a false start.
46
+ */
47
+ export const NATIVE_WINDOWS_ALLOWED_COMMANDS = new Set([
48
+ "help",
49
+ "--help",
50
+ "-h",
51
+ "--version",
52
+ "-v",
53
+ "version",
54
+ "doctor",
55
+ "feedback",
56
+ "ideas",
57
+ ]);
58
+
59
+ /**
60
+ * @param {string | null | undefined} command
61
+ * @param {string} [platform] Node `process.platform` value (defaults to current).
62
+ * @returns {boolean} true when this command must refuse on native Windows
63
+ */
64
+ export function shouldRefuseNativeWindows(command, platform = process.platform) {
65
+ if (!isNativeWindows(/** @type {string} */ (platform))) return false;
66
+ const cmd = String(command ?? "").trim();
67
+ if (!cmd) return false; // bare `tot` → help
68
+ return !NATIVE_WINDOWS_ALLOWED_COMMANDS.has(cmd);
69
+ }
70
+
71
+ /**
72
+ * Recovery hint after a failed `tot login`. Never steers native Windows at
73
+ * `--device` — that path still can't run the local loop on win32.
74
+ * @param {{ device?: boolean, platform?: string }} [opts]
75
+ */
76
+ export function loginFailureNext(opts = {}) {
77
+ const platform = /** @type {string} */ (opts.platform ?? process.platform);
78
+ if (isNativeWindows(platform)) return nativeWindowsDetail();
79
+ return opts.device
80
+ ? "re-run `tot login --device` and approve the code."
81
+ : "re-run `tot login` and finish the browser sign-in (approve the request) — or try `tot login --device`.";
82
+ }
83
+
84
+ /**
85
+ * `tot doctor --fix` must not open OAuth on native Windows — the session
86
+ * would be unusable for the local loop, and the browser hop burns a cycle.
87
+ * @param {string} [platform] Node `process.platform` value (defaults to current).
88
+ */
89
+ export function shouldAttemptDoctorAutoLogin(platform = process.platform) {
90
+ return !isNativeWindows(/** @type {string} */ (platform));
91
+ }