@tokenoftrust/cli 2.2.8 → 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 +24 -30
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
|
|
|
@@ -180,10 +180,21 @@ export async function mintOrCacheCredential({
|
|
|
180
180
|
const tenant = repoName || ctx.tenant;
|
|
181
181
|
const tag = tagFromRepoName(repoName, tenant);
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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.
|
|
187
198
|
const baseUrl = env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
|
|
188
199
|
const client = createClient(baseUrl);
|
|
189
200
|
try {
|
|
@@ -195,9 +206,7 @@ export async function mintOrCacheCredential({
|
|
|
195
206
|
const res = await checkout(client, { tenant, tag, cloneDir: null });
|
|
196
207
|
const cred = splitAuthedRemote(res.gitRemote || "");
|
|
197
208
|
if (!cred) return null;
|
|
198
|
-
|
|
199
|
-
writeCachedCredential(cachePath, fresh);
|
|
200
|
-
return fresh;
|
|
209
|
+
return { username: cred.username, password: cred.token };
|
|
201
210
|
} catch (e) {
|
|
202
211
|
// Same distinction as the kit-checkout catch above: `checkoutTenant` renders a
|
|
203
212
|
// CliError when the MCP (or this checkout's own state) has an authoritative answer
|
|
@@ -227,34 +236,19 @@ export function kitCheckout(cwd) {
|
|
|
227
236
|
}
|
|
228
237
|
|
|
229
238
|
/**
|
|
230
|
-
* git reported
|
|
231
|
-
*
|
|
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
|
|
232
243
|
* checkout's own origin host, like `get`. Never throws.
|
|
233
244
|
* @param {{ env?: NodeJS.ProcessEnv, cwd?: string, expectedHost?: string|null }} [opts]
|
|
234
245
|
*/
|
|
235
246
|
export function forgetCredential({ env = process.env, cwd = process.cwd(), expectedHost = null } = {}) {
|
|
236
247
|
try {
|
|
237
248
|
const kit = kitCheckout(cwd);
|
|
238
|
-
if (kit)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
const ctx = detectContext(cwd);
|
|
244
|
-
if (ctx.mode !== "checkout" || !ctx.tenant || !ctx.workspacePath) return;
|
|
245
|
-
let originUrl = null;
|
|
246
|
-
try {
|
|
247
|
-
originUrl = execFileSync("git", ["-C", ctx.workspacePath, "remote", "get-url", "origin"], {
|
|
248
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
249
|
-
}).toString().trim();
|
|
250
|
-
} catch { /* no origin */ }
|
|
251
|
-
if (expectedHost && !hostMatches(originUrl, expectedHost)) return;
|
|
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));
|
|
249
|
+
if (!kit) return;
|
|
250
|
+
if (expectedHost && !hostMatches(kit.originUrl, expectedHost)) return;
|
|
251
|
+
dropCachedCredential(kitCredentialCachePath(kit.target, env));
|
|
258
252
|
} catch {
|
|
259
253
|
/* erase is advisory — never fail git over it */
|
|
260
254
|
}
|