@tokenoftrust/cli 2.0.6 → 2.0.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/clone.mjs +13 -5
- package/src/commands/sync.mjs +7 -2
- package/src/git-credential.mjs +21 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.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",
|
package/src/commands/clone.mjs
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
*
|
|
47
47
|
* Dependency-free (global fetch + `git` via child_process).
|
|
48
48
|
*/
|
|
49
|
-
import { execFile } from "node:child_process";
|
|
49
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
50
50
|
import { promisify } from "node:util";
|
|
51
51
|
import { createMcpClient } from "../mcp.mjs";
|
|
52
52
|
import { establishSession, AuthUnavailableError } from "../auth.mjs";
|
|
@@ -54,7 +54,7 @@ import { offerSignIn } from "./login.mjs";
|
|
|
54
54
|
import { CliError, fail, formatError } from "../errors.mjs";
|
|
55
55
|
import { writeNvmrc } from "../sample.mjs";
|
|
56
56
|
import { emitObstacle } from "../obstacle.mjs";
|
|
57
|
-
import {
|
|
57
|
+
import { splitAuthedRemote, basicAuthExtraHeader, installForgeCredentialHelper } from "../git-credential.mjs";
|
|
58
58
|
|
|
59
59
|
const execFileP = promisify(execFile);
|
|
60
60
|
|
|
@@ -366,11 +366,19 @@ async function cloneRepo(gitRemote, dir, redact) {
|
|
|
366
366
|
next: `check the target dir is empty and you can reach the remote, then re-run`,
|
|
367
367
|
});
|
|
368
368
|
}
|
|
369
|
+
// Install the HOST-SCOPED helper, not a bare `credential.helper`. A bare one is appended to
|
|
370
|
+
// whatever is inherited, and on a Mac that inherits `credential.helper = osxkeychain` — which
|
|
371
|
+
// runs FIRST, answers with a stale forge credential, and shadows ours. Gitea reports the
|
|
372
|
+
// resulting unauthorized fetch as "Repository not found", so a freshly cloned checkout looks
|
|
373
|
+
// like a missing repo rather than a credential problem. The host-scoped form writes
|
|
374
|
+
// ["", <helper>]: the empty reset clears the inherited helper for this host only.
|
|
369
375
|
try {
|
|
370
|
-
|
|
376
|
+
const gitSync = (/** @type {string[]} */ cargs) =>
|
|
377
|
+
execFileSync("git", ["-C", dir, ...cargs], { encoding: "utf8" });
|
|
378
|
+
installForgeCredentialHelper(gitSync, { host: new URL(gitRemote).host });
|
|
371
379
|
} catch {
|
|
372
|
-
// Best-effort —
|
|
373
|
-
//
|
|
380
|
+
// Best-effort — never fail a good clone over credential plumbing. The next
|
|
381
|
+
// `tot preview`/`tot sync` installs it, and the fetch that needs it says why.
|
|
374
382
|
}
|
|
375
383
|
const head = (await git(["-C", dir, "log", "-1", "--oneline"])).trim();
|
|
376
384
|
writeNvmrc(dir); // version-manager hooks land on a supported Node on cd
|
package/src/commands/sync.mjs
CHANGED
|
@@ -166,8 +166,13 @@ export async function run(argv, ctx) {
|
|
|
166
166
|
// used to 401 even right after signing back in. Best-effort, never blocks sync.
|
|
167
167
|
try {
|
|
168
168
|
ensureTokenlessRemote(git);
|
|
169
|
-
} catch {
|
|
170
|
-
|
|
169
|
+
} catch (e) {
|
|
170
|
+
// Best-effort, but never SILENT: this call installs the host-scoped credential helper, and
|
|
171
|
+
// without it the fetch below fails as "Repository not found" — Gitea's wording for an
|
|
172
|
+
// unauthorized read. Swallowing the reason turns a credential problem into a phantom
|
|
173
|
+
// missing repo, with nothing pointing back here.
|
|
174
|
+
console.error(`~ could not repair this checkout's forge credentials: ${e?.message || e}`);
|
|
175
|
+
console.error(" a fetch failure below is likely this, not a missing repo.");
|
|
171
176
|
}
|
|
172
177
|
|
|
173
178
|
// Refuse a dirty tree up front — a merge on top of uncommitted edits is how
|
package/src/git-credential.mjs
CHANGED
|
@@ -159,7 +159,15 @@ export function forgeShimPath(env = process.env) {
|
|
|
159
159
|
/** The shim's contents: pin the ABSOLUTE node + CLI entry active at write time, with a
|
|
160
160
|
* PATH-search fallback for node, and FAIL LOUD to stderr (never silently) when no node
|
|
161
161
|
* is found — so a broken helper says how to fix itself instead of yielding an opaque
|
|
162
|
-
* "Repository not found". Pure — unit-tested.
|
|
162
|
+
* "Repository not found". Pure — unit-tested.
|
|
163
|
+
*
|
|
164
|
+
* The pinned ENTRY gets the same fail-loud treatment as NODE, because the path pinned at
|
|
165
|
+
* write time can stop existing: running the CLI from a dev checkout or a git worktree pins
|
|
166
|
+
* the shim to THAT tree, and reaping it breaks forge git auth for every checkout on the
|
|
167
|
+
* machine — including ones created by the globally-installed `tot`. Unguarded, the failure
|
|
168
|
+
* surfaces three layers away as a raw node MODULE_NOT_FOUND followed by git's
|
|
169
|
+
* "could not read Username" / "Repository not found", which names neither the stale path
|
|
170
|
+
* nor the fix. */
|
|
163
171
|
export function renderForgeShim(nodePath, entryPath) {
|
|
164
172
|
return [
|
|
165
173
|
"#!/bin/sh",
|
|
@@ -171,7 +179,18 @@ export function renderForgeShim(nodePath, entryPath) {
|
|
|
171
179
|
' echo "tot: no node runtime for the git credential helper — reinstall: npm i -g @tokenoftrust/cli@latest" >&2',
|
|
172
180
|
" exit 1",
|
|
173
181
|
"fi",
|
|
174
|
-
`
|
|
182
|
+
`ENTRY=${shq(entryPath)}`,
|
|
183
|
+
'if [ ! -f "$ENTRY" ]; then',
|
|
184
|
+
' echo "tot: the git credential helper points at a CLI that no longer exists:" >&2',
|
|
185
|
+
' echo " $ENTRY" >&2',
|
|
186
|
+
' echo " This shim was pinned by whichever tot ran last — if that was a dev" >&2',
|
|
187
|
+
' echo " checkout or a git worktree that has since been removed, forge git auth" >&2',
|
|
188
|
+
' echo " is broken for EVERY checkout on this machine until it is re-pinned." >&2',
|
|
189
|
+
' echo " Fix: run any tot command that touches git (e.g. \\`tot clone <tenant>\\`)" >&2',
|
|
190
|
+
' echo " from an installed tot, or reinstall: npm i -g @tokenoftrust/cli@latest" >&2',
|
|
191
|
+
" exit 1",
|
|
192
|
+
"fi",
|
|
193
|
+
'exec "$NODE" "$ENTRY" git-credential "$@"',
|
|
175
194
|
"",
|
|
176
195
|
].join("\n");
|
|
177
196
|
}
|