@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 +13 -7
- package/package.json +1 -1
- package/src/token-store.mjs +38 -15
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
|
-
###
|
|
49
|
+
### Optional: multiple identities at once (`TOT_PROFILE`)
|
|
50
50
|
|
|
51
|
-
|
|
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
|
|
56
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|
package/src/token-store.mjs
CHANGED
|
@@ -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`)
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* (the renderer cache, last-tenant, etc.
|
|
39
|
-
*
|
|
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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|
62
|
-
return join(home, ".tot",
|
|
84
|
+
const slug = profileSlug(env);
|
|
85
|
+
return join(home, ".tot", slug ? `credentials.${slug}.json` : "credentials.json");
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
/**
|