@tokenoftrust/cli 1.4.0-rc.0 → 1.4.0-rc.1

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/README.md CHANGED
@@ -46,18 +46,24 @@ The same `tot` does the right thing wherever you run it (walks up like `git`):
46
46
  - Not signed in? On a terminal, `tot start` / `tot checkout` **offer to sign you in right there** and continue in-flow — no "run `tot login`, then re-run".
47
47
  - The old operator env-triple (`TOT_API_KEY` / `TOT_SECRET_KEY` / `TOT_APP_DOMAIN`) **no longer signs the CLI in** — tot-mcp went OAuth-first on 2026-07-23. If those vars are set, `tot` prints a one-line advisory and uses your `tot login` session anyway; it never reads them for auth.
48
48
 
49
- ### Multiple identities at once (`TOT_PROFILE`)
49
+ ### Optional: multiple identities at once (`TOT_PROFILE`)
50
50
 
51
- One credential file = one active identity, so a plain `tot login` replaces the previous session. To keep **different identities live in different terminals** e.g. a staff `@tokenoftrust.com` sign-in in one shell and a plain developer identity in another set a profile per shell:
51
+ **Most people never set this** the single default session is all you need, and a plain `tot login` just replaces it. Reach for a profile only when you want **more than one identity live at the same time**: a staff `@tokenoftrust.com` sign-in alongside a plain developer one, or many parallel test identities. Set it per shell and each gets its own credential file under `~/.tot` (the renderer cache and everything else stay shared):
52
52
 
53
53
  ```
54
- # terminal A
55
- export TOT_PROFILE=staff && tot login
56
- # terminal B
57
- export TOT_PROFILE=dev && tot login
54
+ # terminal A # terminal B
55
+ export TOT_PROFILE=staff export TOT_PROFILE=dev
56
+ tot login tot login
58
57
  ```
59
58
 
60
- Each profile gets its own `~/.tot/credentials.<profile>.json` (the renderer cache and everything else stay shared). Unset → the default session, unchanged. `tot whoami` shows the active profile.
59
+ The value is an **opaque label** any string works, so it's easy to script parallel identities:
60
+
61
+ ```
62
+ export TOT_PROFILE=$(uuidgen) # a fresh isolated identity per terminal / test worker
63
+ tot login
64
+ ```
65
+
66
+ A clean short name (`staff`, `dev`, `test-7`) becomes a readable `credentials.<name>.json`; any other value (symbols, uppercase, long) is hashed to a stable `credentials.h<hash>.json` — so an arbitrary id never collides or escapes `~/.tot`. Unset → the default session, unchanged. `tot whoami` shows the active profile.
61
67
 
62
68
  ## Design notes
63
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "1.4.0-rc.0",
3
+ "version": "1.4.0-rc.1",
4
4
  "description": "Token of Trust developer CLI — check out 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",
@@ -30,36 +30,59 @@
30
30
  *
31
31
  * `TOT_HOME` overrides the home dir (used by tests to point at a temp dir).
32
32
  *
33
- * PROFILES (`TOT_PROFILE`): single-plane auth means one active identity per
34
- * credential file, so to keep DIFFERENT identities live at once e.g. a staff
35
- * `@tokenoftrust.com` sign-in in one terminal and a plain developer identity in
36
- * another export a profile per shell (`export TOT_PROFILE=staff` / `=dev`).
37
- * Each profile gets its OWN `credentials.<profile>.json` under the same `~/.tot`
38
- * (the renderer cache, last-tenant, etc. stay shared — only the identity splits).
39
- * Unset the default `credentials.json`, so existing sessions are untouched.
33
+ * PROFILES (`TOT_PROFILE`) OPTIONAL, for parallel work. Almost every developer
34
+ * leaves this UNSET and uses the single default `credentials.json`that path is
35
+ * unchanged and is the norm. It exists only when you want MORE THAN ONE identity
36
+ * live at once (a staff `@tokenoftrust.com` sign-in and a plain developer one, or
37
+ * many parallel test identities): export a profile per shell and each gets its OWN
38
+ * credential file under the same `~/.tot` (the renderer cache, last-tenant, etc.
39
+ * stay shared — only the identity splits). The value is an OPAQUE label — any
40
+ * string works, so `export TOT_PROFILE=$(uuidgen)` per terminal/test is fine:
41
+ * - a clean short token (`staff`, `dev`, `test-7`) is used verbatim for a
42
+ * readable `credentials.<profile>.json`;
43
+ * - any other value (symbols, uppercase, long) is HASHED to a stable, safe
44
+ * `credentials.h<hash>.json` — so an arbitrary opaque id can never collide
45
+ * with another or escape `~/.tot`, while `tot whoami` still shows what you set.
40
46
  */
41
47
  import {
42
48
  readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, existsSync, rmSync,
43
49
  } from "node:fs";
44
50
  import { homedir } from "node:os";
51
+ import { createHash } from "node:crypto";
45
52
  import { join, dirname } from "node:path";
46
53
 
54
+ /** A short token safe to drop straight into a filename (readable profiles). */
55
+ const CLEAN_PROFILE_RE = /^[a-z0-9_-]{1,64}$/;
56
+
47
57
  /**
48
- * The active credential profile from `TOT_PROFILE`, sanitized to a safe filename
49
- * token (`[a-z0-9_-]`, lowercased, ≤64 chars), or null for the default
50
- * (unnamespaced) session. Sanitizing not just trusting the value — keeps a
51
- * stray `/` or `..` from ever escaping the `~/.tot` dir.
58
+ * The active profile's DISPLAY label — the raw `TOT_PROFILE`, trimmed or null
59
+ * when unset. This is what `tot whoami` shows; it is NOT the filename (see
60
+ * profileSlug, which makes any value filesystem-safe).
52
61
  */
53
62
  export function activeProfile(env = process.env) {
54
- const safe = String(env.TOT_PROFILE || "").trim().toLowerCase().replace(/[^a-z0-9_-]/g, "").slice(0, 64);
55
- return safe || null;
63
+ return String(env.TOT_PROFILE || "").trim() || null;
64
+ }
65
+
66
+ /**
67
+ * The active profile's safe filename token, or null for the default session. A
68
+ * clean short label passes through verbatim (readable files); ANY other opaque
69
+ * value is hashed — collision-resistant and incapable of a `/` or `..` path
70
+ * escape, so `TOT_PROFILE` can be a literally arbitrary identifier. Hashing the
71
+ * RAW value (not a stripped form) keeps distinct tokens distinct.
72
+ */
73
+ export function profileSlug(env = process.env) {
74
+ const raw = String(env.TOT_PROFILE || "").trim();
75
+ if (!raw) return null;
76
+ const lower = raw.toLowerCase();
77
+ if (CLEAN_PROFILE_RE.test(lower)) return lower;
78
+ return `h${createHash("sha256").update(raw).digest("hex").slice(0, 16)}`;
56
79
  }
57
80
 
58
81
  /** Absolute path to the credential file for this environment (+ TOT_PROFILE). */
59
82
  export function defaultCredentialsPath(env = process.env) {
60
83
  const home = env.TOT_HOME || homedir();
61
- const profile = activeProfile(env);
62
- return join(home, ".tot", profile ? `credentials.${profile}.json` : "credentials.json");
84
+ const slug = profileSlug(env);
85
+ return join(home, ".tot", slug ? `credentials.${slug}.json` : "credentials.json");
63
86
  }
64
87
 
65
88
  /**