@tokenoftrust/cli 2.2.6 → 2.2.8
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/package.json +1 -1
- package/src/commands/git-credential.mjs +58 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.8",
|
|
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",
|
|
@@ -22,14 +22,25 @@
|
|
|
22
22
|
* git config by `tot clone agency-kit`) mints through `repo_read_credential`,
|
|
23
23
|
* cached under a key a store's can never take.
|
|
24
24
|
*
|
|
25
|
-
* FAILS SILENT, NEVER LOUD: `get` prints NOTHING
|
|
26
|
-
* problem (not signed in, MCP unreachable, cwd isn't a
|
|
27
|
-
* checkout) — git then falls through to its next
|
|
28
|
-
* own prompt, exactly as if this helper weren't
|
|
29
|
-
* a malformed credential line
|
|
30
|
-
* operation in the checkout. It also NEVER
|
|
31
|
-
* this runs as a non-interactive subprocess
|
|
32
|
-
* fails through rather than trying to open a
|
|
25
|
+
* FAILS SILENT ON STDOUT, NEVER LOUD THERE: `get` prints NOTHING on stdout and
|
|
26
|
+
* exits non-zero on any problem (not signed in, MCP unreachable, cwd isn't a
|
|
27
|
+
* recognizable tenant checkout) — git then falls through to its next
|
|
28
|
+
* configured helper or its own prompt, exactly as if this helper weren't
|
|
29
|
+
* configured. A stack trace or a malformed credential line on STDOUT here
|
|
30
|
+
* would otherwise corrupt EVERY git operation in the checkout. It also NEVER
|
|
31
|
+
* triggers an interactive sign-in — this runs as a non-interactive subprocess
|
|
32
|
+
* of `git`, so a missing session fails through rather than trying to open a
|
|
33
|
+
* browser mid-`git fetch`.
|
|
34
|
+
*
|
|
35
|
+
* It is NOT silent on stderr for every failure, though: when the MCP itself
|
|
36
|
+
* renders an authoritative decision — a `CliError` from `tenant_checkout` or
|
|
37
|
+
* `repo_read_credential`, e.g. "this checkout names a tenant your session
|
|
38
|
+
* doesn't hold" — that diagnosis is lost forever if swallowed, and git passes
|
|
39
|
+
* a credential helper's stderr straight through to the developer's terminal.
|
|
40
|
+
* So those specific catches print the CliError's house-style message (+ its
|
|
41
|
+
* `next` step) to stderr before returning null. A bare network/auth failure
|
|
42
|
+
* (not signed in, MCP unreachable, wrong dir) has no such decision to report
|
|
43
|
+
* and stays silent exactly as before.
|
|
33
44
|
*
|
|
34
45
|
* Dependency-free (global fetch + `git`, via the same MCP client + auth
|
|
35
46
|
* module every other command uses).
|
|
@@ -41,6 +52,7 @@ import { establishSession } from "../auth.mjs";
|
|
|
41
52
|
import { checkoutTenant, mintReadOnlyAlias, resolveReadOnlyAlias } from "./clone.mjs";
|
|
42
53
|
import { detectContext } from "../context.mjs";
|
|
43
54
|
import { repoNameFromRemote, tagFromRepoName } from "./submit.mjs";
|
|
55
|
+
import { CliError, formatError } from "../errors.mjs";
|
|
44
56
|
import {
|
|
45
57
|
parseCredentialInput, formatCredentialOutput, splitAuthedRemote,
|
|
46
58
|
credentialCachePath, readCachedCredential, writeCachedCredential,
|
|
@@ -129,8 +141,13 @@ export async function mintOrCacheCredential({
|
|
|
129
141
|
const fresh = { username: cred.username, password: cred.token };
|
|
130
142
|
writeCachedCredential(cachePath, fresh);
|
|
131
143
|
return fresh;
|
|
132
|
-
} catch {
|
|
133
|
-
|
|
144
|
+
} catch (e) {
|
|
145
|
+
// A CliError is an authoritative decision (e.g. the kit credential was refused) —
|
|
146
|
+
// report it on stderr so `git fetch` doesn't dead-end the developer in silence. A
|
|
147
|
+
// bare network/auth failure (not signed in, MCP unreachable) has no decision to
|
|
148
|
+
// report and stays silent, same as every other "nothing this helper can do" path.
|
|
149
|
+
if (e instanceof CliError) console.error(formatError(e));
|
|
150
|
+
return null;
|
|
134
151
|
}
|
|
135
152
|
}
|
|
136
153
|
|
|
@@ -148,9 +165,22 @@ export async function mintOrCacheCredential({
|
|
|
148
165
|
/* fall through — tagFromRepoName degrades to "main" on a null repo name */
|
|
149
166
|
}
|
|
150
167
|
if (expectedHost && !hostMatches(originUrl, expectedHost)) return null;
|
|
151
|
-
const tag = tagFromRepoName(repoName, ctx.tenant);
|
|
152
168
|
|
|
153
|
-
|
|
169
|
+
// Prefer the tenant encoded in the git remote itself over `.tot/config.json`'s
|
|
170
|
+
// "tenant" field. The remote URL is the forge's OWN record of this checkout's repo
|
|
171
|
+
// (set once by `tenant_checkout`'s returned cloneUrl at clone time); the config file
|
|
172
|
+
// is tenant-owned, committed content a developer's own commit can set wrong at seed
|
|
173
|
+
// time or drift later — exactly what broke every client_switch/tenant_checkout call
|
|
174
|
+
// in fb-1790958790645-7j8gul / fb-1790969588649-zl3xa7. Every real tenant is
|
|
175
|
+
// provisioned with repo === owner (storefront's provisioning enforces this at every
|
|
176
|
+
// call site — buildStaffStoreIdentity, scripts/tenant/provision-gitea-repo.mjs), so
|
|
177
|
+
// the repo name IS the tenant id. Fall back to the config file only when the remote
|
|
178
|
+
// can't be read or parsed (no origin yet) — this never makes the mint MORE likely to
|
|
179
|
+
// fail than before, only less dependent on content the forge itself didn't set.
|
|
180
|
+
const tenant = repoName || ctx.tenant;
|
|
181
|
+
const tag = tagFromRepoName(repoName, tenant);
|
|
182
|
+
|
|
183
|
+
const cachePath = credentialCachePath(tenant, tag, env);
|
|
154
184
|
const cached = readCachedCredential(cachePath);
|
|
155
185
|
if (cached) return { username: cached.username, password: cached.password };
|
|
156
186
|
|
|
@@ -162,14 +192,21 @@ export async function mintOrCacheCredential({
|
|
|
162
192
|
return null; // not signed in (or session unrefreshable) — nothing this helper can do
|
|
163
193
|
}
|
|
164
194
|
try {
|
|
165
|
-
const res = await checkout(client, { tenant
|
|
195
|
+
const res = await checkout(client, { tenant, tag, cloneDir: null });
|
|
166
196
|
const cred = splitAuthedRemote(res.gitRemote || "");
|
|
167
197
|
if (!cred) return null;
|
|
168
198
|
const fresh = { username: cred.username, password: cred.token };
|
|
169
199
|
writeCachedCredential(cachePath, fresh);
|
|
170
200
|
return fresh;
|
|
171
|
-
} catch {
|
|
172
|
-
|
|
201
|
+
} catch (e) {
|
|
202
|
+
// Same distinction as the kit-checkout catch above: `checkoutTenant` renders a
|
|
203
|
+
// CliError when the MCP (or this checkout's own state) has an authoritative answer
|
|
204
|
+
// — e.g. this checkout's `.tot/config.json` names a tenant the session doesn't hold,
|
|
205
|
+
// or `tenant_checkout` echoed a different tenant than the one asked for — and that
|
|
206
|
+
// diagnosis is lost forever if swallowed silently. A bare network/auth failure has
|
|
207
|
+
// no such decision and stays silent, exit non-zero, exactly as before.
|
|
208
|
+
if (e instanceof CliError) console.error(formatError(e));
|
|
209
|
+
return null;
|
|
173
210
|
}
|
|
174
211
|
}
|
|
175
212
|
|
|
@@ -212,8 +249,12 @@ export function forgetCredential({ env = process.env, cwd = process.cwd(), expec
|
|
|
212
249
|
}).toString().trim();
|
|
213
250
|
} catch { /* no origin */ }
|
|
214
251
|
if (expectedHost && !hostMatches(originUrl, expectedHost)) return;
|
|
215
|
-
|
|
216
|
-
|
|
252
|
+
// Mirror `get`'s tenant resolution (remote-derived, falling back to the config
|
|
253
|
+
// file) so `erase` drops the SAME cache key `get` wrote, not a stale sibling.
|
|
254
|
+
const repoName = originUrl ? repoNameFromRemote(originUrl) : null;
|
|
255
|
+
const tenant = repoName || ctx.tenant;
|
|
256
|
+
const tag = tagFromRepoName(repoName, tenant);
|
|
257
|
+
dropCachedCredential(credentialCachePath(tenant, tag, env));
|
|
217
258
|
} catch {
|
|
218
259
|
/* erase is advisory — never fail git over it */
|
|
219
260
|
}
|