@tokenoftrust/cli 2.2.7 → 2.2.9
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 +38 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.9",
|
|
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",
|
|
@@ -55,7 +55,7 @@ import { repoNameFromRemote, tagFromRepoName } from "./submit.mjs";
|
|
|
55
55
|
import { CliError, formatError } from "../errors.mjs";
|
|
56
56
|
import {
|
|
57
57
|
parseCredentialInput, formatCredentialOutput, splitAuthedRemote,
|
|
58
|
-
|
|
58
|
+
readCachedCredential, writeCachedCredential,
|
|
59
59
|
kitCredentialCachePath, dropCachedCredential, readCheckoutKind, READ_ONLY_KIT_KIND,
|
|
60
60
|
} from "../git-credential.mjs";
|
|
61
61
|
|
|
@@ -165,12 +165,36 @@ export async function mintOrCacheCredential({
|
|
|
165
165
|
/* fall through — tagFromRepoName degrades to "main" on a null repo name */
|
|
166
166
|
}
|
|
167
167
|
if (expectedHost && !hostMatches(originUrl, expectedHost)) return null;
|
|
168
|
-
const tag = tagFromRepoName(repoName, ctx.tenant);
|
|
169
168
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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);
|
|
173
182
|
|
|
183
|
+
// NEVER cache this credential (fb-1790975400361-eg8wxt). The push credential's mint
|
|
184
|
+
// (tot-mcp's checkoutRepo -> mintUserToken) ROTATES: it revokes EVERY prior token for
|
|
185
|
+
// this (developer, tenant) Gitea user on each call -- by design, so a leaked push
|
|
186
|
+
// credential stops working the moment a fresh one is issued (gitea-rest.ts:1559-1569).
|
|
187
|
+
// A local cache keyed only by (tenant, tag) -- not by checkout directory -- has no way
|
|
188
|
+
// to learn that ANOTHER checkout, another machine, or even a retry just rotated the
|
|
189
|
+
// token it is holding: it only knows its own TTL, so it keeps serving a credential
|
|
190
|
+
// Gitea already revoked until that TTL expires. That produced exactly the reported
|
|
191
|
+
// symptom -- a brand-new clone's first fetch fails "Failed to authenticate user" until
|
|
192
|
+
// the cache is cleared, because an earlier clone's mint had already rotated this one
|
|
193
|
+
// out from under it. The read-only kit-credential path avoids this by minting with
|
|
194
|
+
// `rotate: false` (gitea-rest.ts:571, "would yank a concurrent fetch's token") -- that
|
|
195
|
+
// option isn't available here without weakening the push credential's own leak-safety
|
|
196
|
+
// property, so the fix is on this side: mint fresh every time instead of trusting a
|
|
197
|
+
// local TTL the server can invalidate out of band.
|
|
174
198
|
const baseUrl = env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
|
|
175
199
|
const client = createClient(baseUrl);
|
|
176
200
|
try {
|
|
@@ -179,12 +203,10 @@ export async function mintOrCacheCredential({
|
|
|
179
203
|
return null; // not signed in (or session unrefreshable) — nothing this helper can do
|
|
180
204
|
}
|
|
181
205
|
try {
|
|
182
|
-
const res = await checkout(client, { tenant
|
|
206
|
+
const res = await checkout(client, { tenant, tag, cloneDir: null });
|
|
183
207
|
const cred = splitAuthedRemote(res.gitRemote || "");
|
|
184
208
|
if (!cred) return null;
|
|
185
|
-
|
|
186
|
-
writeCachedCredential(cachePath, fresh);
|
|
187
|
-
return fresh;
|
|
209
|
+
return { username: cred.username, password: cred.token };
|
|
188
210
|
} catch (e) {
|
|
189
211
|
// Same distinction as the kit-checkout catch above: `checkoutTenant` renders a
|
|
190
212
|
// CliError when the MCP (or this checkout's own state) has an authoritative answer
|
|
@@ -214,30 +236,19 @@ export function kitCheckout(cwd) {
|
|
|
214
236
|
}
|
|
215
237
|
|
|
216
238
|
/**
|
|
217
|
-
* git reported
|
|
218
|
-
*
|
|
239
|
+
* git reported a credential rejected (`erase`). Only the read-only kit credential is ever
|
|
240
|
+
* cached (see `mintOrCacheCredential` — the tenant push credential is never cached, because
|
|
241
|
+
* its mint rotates server-side out from under any local TTL), so this only has work to do
|
|
242
|
+
* for a kit checkout; a tenant checkout's `erase` is a correct no-op. Scoped to the
|
|
219
243
|
* checkout's own origin host, like `get`. Never throws.
|
|
220
244
|
* @param {{ env?: NodeJS.ProcessEnv, cwd?: string, expectedHost?: string|null }} [opts]
|
|
221
245
|
*/
|
|
222
246
|
export function forgetCredential({ env = process.env, cwd = process.cwd(), expectedHost = null } = {}) {
|
|
223
247
|
try {
|
|
224
248
|
const kit = kitCheckout(cwd);
|
|
225
|
-
if (kit)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const ctx = detectContext(cwd);
|
|
231
|
-
if (ctx.mode !== "checkout" || !ctx.tenant || !ctx.workspacePath) return;
|
|
232
|
-
let originUrl = null;
|
|
233
|
-
try {
|
|
234
|
-
originUrl = execFileSync("git", ["-C", ctx.workspacePath, "remote", "get-url", "origin"], {
|
|
235
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
236
|
-
}).toString().trim();
|
|
237
|
-
} catch { /* no origin */ }
|
|
238
|
-
if (expectedHost && !hostMatches(originUrl, expectedHost)) return;
|
|
239
|
-
const tag = tagFromRepoName(originUrl ? repoNameFromRemote(originUrl) : null, ctx.tenant);
|
|
240
|
-
dropCachedCredential(credentialCachePath(ctx.tenant, tag, env));
|
|
249
|
+
if (!kit) return;
|
|
250
|
+
if (expectedHost && !hostMatches(kit.originUrl, expectedHost)) return;
|
|
251
|
+
dropCachedCredential(kitCredentialCachePath(kit.target, env));
|
|
241
252
|
} catch {
|
|
242
253
|
/* erase is advisory — never fail git over it */
|
|
243
254
|
}
|