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

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.
@@ -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
  /**