@tokenoftrust/cli 2.0.8 → 2.0.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/submit.mjs +59 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.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",
|
package/src/commands/submit.mjs
CHANGED
|
@@ -1154,7 +1154,7 @@ function reportCandidate(result, changeId, { quiet = false } = {}) {
|
|
|
1154
1154
|
* Defaults to null when no numeric PR number was known at result-build time.
|
|
1155
1155
|
* Pure — unit-tested.
|
|
1156
1156
|
* @param {{ ok: boolean, ref?: string|null, commit?: string|null, changeId?: string|null,
|
|
1157
|
-
* candidate?: {prNumber?: number, number?: number, state?: string, url?: string}|null,
|
|
1157
|
+
* candidate?: {prNumber?: number, number?: number, state?: string, url?: string, headSha?: string|null}|null,
|
|
1158
1158
|
* status?: {status?: string, reconcile?: object|null, compliance?: object|null,
|
|
1159
1159
|
* previewUrl?: string|null, shipped?: object|null, dispatched?: boolean|null,
|
|
1160
1160
|
* notDispatched?: boolean, delivery?: object|null}|null,
|
|
@@ -1165,6 +1165,10 @@ export function buildJsonResult({ ok, ref = null, commit = null, changeId = null
|
|
|
1165
1165
|
ok,
|
|
1166
1166
|
ref,
|
|
1167
1167
|
commit,
|
|
1168
|
+
// The sha the `status`/`reconcile`/`compliance` fields above are reported against —
|
|
1169
|
+
// the candidate head when the candidate names one, else the local `commit`. Kept
|
|
1170
|
+
// beside `commit` rather than replacing it so automation can tell the two apart.
|
|
1171
|
+
candidateHeadSha: candidate ? resolvePreviewStatusSha(commit, candidate) : null,
|
|
1168
1172
|
changeId,
|
|
1169
1173
|
candidate: candidate
|
|
1170
1174
|
? { number: candidate.prNumber ?? candidate.number ?? null, state: candidate.state ?? null, url: candidate.url ?? null }
|
|
@@ -1184,6 +1188,43 @@ export function buildJsonResult({ ok, ref = null, commit = null, changeId = null
|
|
|
1184
1188
|
};
|
|
1185
1189
|
}
|
|
1186
1190
|
|
|
1191
|
+
/**
|
|
1192
|
+
* The sha `preview_status` actually resolves against. The candidate the forge opened
|
|
1193
|
+
* may carry a DIFFERENT head than the commit you pushed (it is rebuilt onto the
|
|
1194
|
+
* current store), and `preview_status` only knows the candidate head — so polling the
|
|
1195
|
+
* local commit returns a misleading `pending` / "no reconcile delivery observed"
|
|
1196
|
+
* instead of the real result. Prefer the candidate head whenever the candidate names
|
|
1197
|
+
* one; fall back to the local commit (no candidate, or an older MCP that omits it).
|
|
1198
|
+
* Pure — unit-tested.
|
|
1199
|
+
* @param {string|null} commit local HEAD sha
|
|
1200
|
+
* @param {{headSha?: string|null}|null} candidate the candidate_open projection
|
|
1201
|
+
*/
|
|
1202
|
+
export function resolvePreviewStatusSha(commit, candidate) {
|
|
1203
|
+
const head = typeof candidate?.headSha === "string" ? candidate.headSha.trim() : "";
|
|
1204
|
+
return head || commit;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* The two-sha notice, printed ONLY when they actually differ (when they match there is
|
|
1209
|
+
* nothing to disambiguate and a second line would be noise). Names which sha the
|
|
1210
|
+
* reconcile result is reported against, so a developer polling by hand — or reading a
|
|
1211
|
+
* failure — knows which one to use. Pure — unit-tested.
|
|
1212
|
+
* @param {string|null} commit local HEAD sha
|
|
1213
|
+
* @param {{headSha?: string|null}|null} candidate the candidate_open projection
|
|
1214
|
+
* @returns {string[]} lines to print (empty when there is no distinction to draw)
|
|
1215
|
+
*/
|
|
1216
|
+
export function formatCandidateHeadNotice(commit, candidate) {
|
|
1217
|
+
const head = resolvePreviewStatusSha(commit, candidate);
|
|
1218
|
+
if (!commit || !head || head === commit) return [];
|
|
1219
|
+
return [
|
|
1220
|
+
"",
|
|
1221
|
+
` ▸ local commit: ${commit.slice(0, 9)} (what you committed here)`,
|
|
1222
|
+
` candidate head: ${head.slice(0, 9)} (what preview_status reports on)`,
|
|
1223
|
+
" They differ because your candidate is rebuilt on the current store. Follow the",
|
|
1224
|
+
" candidate head — the local commit reads as \"never dispatched\", not as the result.",
|
|
1225
|
+
];
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1187
1228
|
/** Print the `--json` result as one pretty-printed object on stdout — a no-op
|
|
1188
1229
|
* unless `args.json` was passed, so call sites can invoke it unconditionally. */
|
|
1189
1230
|
function emitJson(args, payload) {
|
|
@@ -1663,9 +1704,18 @@ export async function run(argv, ctx, { verb = "preview" } = {}) {
|
|
|
1663
1704
|
} catch { /* best-effort local hint — a miss just re-derives the stable id */ }
|
|
1664
1705
|
}
|
|
1665
1706
|
|
|
1707
|
+
// Poll the sha `preview_status` actually resolves against — the candidate head, not
|
|
1708
|
+
// the local commit, whenever the candidate names its own. Printed first (when they
|
|
1709
|
+
// differ) so the developer reads the result against the right sha.
|
|
1710
|
+
const statusSha = resolvePreviewStatusSha(commit, candidate) ?? commit;
|
|
1711
|
+
const statusShort = statusSha.slice(0, 9);
|
|
1712
|
+
if (!args.json) {
|
|
1713
|
+
for (const line of formatCandidateHeadNotice(commit, candidate)) console.log(line);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1666
1716
|
let status;
|
|
1667
1717
|
if (args.noWait) {
|
|
1668
|
-
status = normalizePreviewStatus(await client.callTool("preview_status", { commit }));
|
|
1718
|
+
status = normalizePreviewStatus(await client.callTool("preview_status", { commit: statusSha }));
|
|
1669
1719
|
} else {
|
|
1670
1720
|
// One in-place status line (TTY: a spinner with an elapsed-seconds counter;
|
|
1671
1721
|
// non-TTY: a ~10s heartbeat) instead of a newline per poll — the wait reads
|
|
@@ -1681,11 +1731,11 @@ export async function run(argv, ctx, { verb = "preview" } = {}) {
|
|
|
1681
1731
|
// stage text ("still reconciling…") IS truthful and is left as-is below.
|
|
1682
1732
|
let phase = "reconcile";
|
|
1683
1733
|
if (!args.json) {
|
|
1684
|
-
progress = startProgress(`waiting for reconcile of ${
|
|
1685
|
-
stages: [{ afterMs: 45_000, text: `still reconciling ${
|
|
1734
|
+
progress = startProgress(`waiting for reconcile of ${statusShort}…`, {
|
|
1735
|
+
stages: [{ afterMs: 45_000, text: `still reconciling ${statusShort}… (larger changes take longer)` }],
|
|
1686
1736
|
});
|
|
1687
1737
|
}
|
|
1688
|
-
status = await pollPreviewStatus(client,
|
|
1738
|
+
status = await pollPreviewStatus(client, statusSha, {
|
|
1689
1739
|
...(args.watch ? WATCH_POLL : DEFAULT_POLL),
|
|
1690
1740
|
onTick: (s) => {
|
|
1691
1741
|
// Reconcile is done but we're still waiting on a ship decision (--watch):
|
|
@@ -1693,7 +1743,7 @@ export async function run(argv, ctx, { verb = "preview" } = {}) {
|
|
|
1693
1743
|
if (!args.json && s.status === "reconciled" && !s.shipped && phase !== "ship") {
|
|
1694
1744
|
phase = "ship";
|
|
1695
1745
|
progress.stop();
|
|
1696
|
-
progress = startProgress(`reconciled ${
|
|
1746
|
+
progress = startProgress(`reconciled ${statusShort} — waiting for a ship decision…`);
|
|
1697
1747
|
}
|
|
1698
1748
|
},
|
|
1699
1749
|
});
|
|
@@ -1704,7 +1754,9 @@ export async function run(argv, ctx, { verb = "preview" } = {}) {
|
|
|
1704
1754
|
}
|
|
1705
1755
|
// --json also skips the browser auto-open (open: !args.noOpen && !args.json)
|
|
1706
1756
|
// — automation doesn't want a browser popping up.
|
|
1707
|
-
|
|
1757
|
+
// `commit: statusSha` — the diagnostic blocks name the sha that was actually polled,
|
|
1758
|
+
// so a "never dispatched" hint points at a sha `preview_status` knows about.
|
|
1759
|
+
reportStatus(status, tenant, { open: !args.noOpen && !args.json, quiet: args.json, commit: statusSha, ref, verb, noChanges: patchEntries.length === 0 });
|
|
1708
1760
|
emitJson(args, buildJsonResult({ ok: status?.status !== "failed", ref, commit, changeId, candidate, status, previewPrUrl }));
|
|
1709
1761
|
return status?.status === "failed" ? 1 : 0;
|
|
1710
1762
|
} catch (e) {
|