@tea-agent/loop-agent 0.38.2 → 0.38.4
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/AGENTS.md +4 -1
- package/CHANGELOG.md +171 -96
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +76 -10
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +265 -15
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/operator-actions.js +116 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +36 -5
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4bmo3iY.js → abnfDiagram-N423BO3Z-gauhD9kD.js} +1 -1
- package/dist/worker/console/static/assets/{arc-SAD7McOS.js → arc-pm3c2Rsl.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DAVLUdK1.js → architectureDiagram-T3A2C74G-D_JAg3wH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CPPdv4vj.js → blockDiagram-VBNYF7ZC-Dk7-ex52.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-D1GRGZax.js → c4Diagram-5PPSVZJV-Uval9oRI.js} +1 -1
- package/dist/worker/console/static/assets/channel-CVrnUdZl.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D_Bq2ZqK.js → chunk-2GRJ4B5K-BSYjHs-v.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CPLVtamK.js → chunk-2Q5K7J3B-KGUuBXI8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DnaZWcL3.js → chunk-5RXB4S5H-JqAtxD9N.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BRTmFgnq.js → chunk-5VM5RSS4-D6EJOVQK.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BRlfYZoH.js → chunk-6Q2QTUOP-B1JriSjm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjNybD4u.js → chunk-GF5L2VYU-D62b96Jd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-D3e-74fG.js → chunk-JWPE2WC7-BsNKEC_r.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Br3ir6f2.js → chunk-KBJHAD2P-B56v6-ZP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-1NgVuRbh.js → chunk-RYQCIY6F-D2KPnd8K.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BTYuvuWN.js → chunk-XXDRQBXY-kzKSCaaL.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BC6Bgosf.js → cose-bilkent-JH36ORCC-C6N6KNJd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DV-HypfL.js → cynefin-VYW2F7L2-DiyymT2D.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DhTaddO-.js → cynefinDiagram-MW4NZA55-BeKAXvf3.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BnEcOmiI.js → dagre-VZM6K2ZE-TkIdXfPF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-xphGRVKp.js → diagram-7IWD3JNH-BFmSG3ks.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CrdD1mRG.js → diagram-B4RE2ZJO-DqpJT2Fs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-D4YdLsaF.js → diagram-LBJQPF4R-r5ATN43t.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-LCKyGZ5U.js → diagram-Q27KOJAE-DEcSCMuR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BeiTe7t0.js → diagram-UB23O5K3-VOvYRLVc.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BO2cq8GS.js → ebnfDiagram-BXEA7PRR-D7f4Cb1u.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CmWww3Nt.js → erDiagram-JOGREHBK-ziUf3U0G.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-C2kkUYg5.js → flowDiagram-UKHOOZJN-BoRdQ-1y.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DY_JZ2iQ.js → ganttDiagram-PKOTCBZU-B1-GncNm.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BizhCSw9.js → gitGraphDiagram-DS77QQ5N-CTW-ph8W.js} +1 -1
- package/dist/worker/console/static/assets/index-C8rOIAJr.css +1 -0
- package/dist/worker/console/static/assets/{index-BXN89VPX.js → index-DEbyrEfW.js} +147 -68
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Dlo2eK_d.js → infoDiagram-6WML65LV-BFA337tZ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-3EIlIIuZ.js → ishikawaDiagram-WSZJBQD7-BtEujgRR.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DiMDe5Yh.js → journeyDiagram-NVQOT4AX-C8ZwD2Jp.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-sN553oIN.js → kanban-definition-27J2QSJJ-Ck-z8B11.js} +1 -1
- package/dist/worker/console/static/assets/{linear-D9Py31Ld.js → linear-CAyXjQjN.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-D7Occzk_.js → mermaid.core-CcWKn6tS.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DjDRboiR.js → mindmap-definition-FAOFIHXS-C2NuDO7E.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Cgc4qlzp.js → pegDiagram-VL7TDLO6-BFub3PDz.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DxZj8pKd.js → pieDiagram-7S7Q4E2Y-BJx5THZ5.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CJ50MngR.js → quadrantDiagram-CIZ2JOQS-DhalkDWV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BY-OD8Tr.js → railroadDiagram-AXF67PYL-CVcBM36o.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CQFButfK.js → requirementDiagram-LRYGKXZP-D_vy4n0w.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BB_QUK_b.js → sankeyDiagram-W5VNT64P-DGYdJL1D.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CqhXZjyx.js → sequenceDiagram-SI44F4Z6-C1xJFg3k.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-2l1U4rgJ.js → sizeCapture-X5ZJPWSS-D_Fc43df.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BX1FbrhJ.js → stateDiagram-OKZ733FA-BhGbIjbu.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DUt1lyFL.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DOSnT7hb.js → swimlanes-SLNWSIFB-DVkNcO-V.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BM6SGr7j.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DVJQA7AN.js → timeline-definition-Z64GVDOM-BQU9ufV0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-74uxl9gx.js → vennDiagram-T6HMQDX7-BCQhRmYB.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DsBxlcg_.js → wardleyDiagram-T6FBY63Y-DWerOyHO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-snuW9Mjw.js → xychartDiagram-ELKLHX3M-DxLeebQU.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +76 -6
- package/dist/workflows/dag/backend-test-scenario-partitions.js +73 -1
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/failure-category.js +12 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +145 -40
- package/dist/workflows/dag/frontend-prewrite-gate.js +87 -135
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +160 -28
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +196 -117
- package/dist/workflows/dag/node-execution.js +139 -10
- package/dist/workflows/dag/prompt.js +15 -1
- package/dist/workflows/dag/rerun-plan.js +134 -3
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +91 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +32 -1
- package/dist/workflows/dag/validate.js +41 -1
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +2 -2
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-R6oQIDkw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BPXlmPWI.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BPXlmPWI.js +0 -1
- package/dist/worker/console/static/assets/index-CuH5CNFu.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DJKi0Qel.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-YYe7EEm5.js +0 -8
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* next-info: read-only report of the current npm `next` binding.
|
|
4
|
+
*
|
|
5
|
+
* Resolves the moving git ref `npm-next` (local, then origin) and the
|
|
6
|
+
* published tarball build stamp. Does not write the repo, move dist-tags,
|
|
7
|
+
* or call GitHub APIs.
|
|
8
|
+
*/
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import process from "node:process";
|
|
14
|
+
import {
|
|
15
|
+
commandLaunch,
|
|
16
|
+
defaultRun,
|
|
17
|
+
isAncestor,
|
|
18
|
+
normalizeCommandArgs,
|
|
19
|
+
npmViewDistTags,
|
|
20
|
+
} from "./next-publish-gate.mjs";
|
|
21
|
+
import {
|
|
22
|
+
downloadNpmTarball,
|
|
23
|
+
readTarballBuildStamp,
|
|
24
|
+
} from "./release-source-binding.mjs";
|
|
25
|
+
|
|
26
|
+
export const UNPUBLISHED_COMMIT_LIMIT_DEFAULT = 50;
|
|
27
|
+
const GIT_REF_CANDIDATES = ["refs/heads/npm-next", "origin/npm-next"];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} cmd
|
|
31
|
+
* @param {string[]} args
|
|
32
|
+
* @param {string} [cwd]
|
|
33
|
+
*/
|
|
34
|
+
function runWithCwd(cmd, args, cwd) {
|
|
35
|
+
const launch = commandLaunch(cmd, normalizeCommandArgs(cmd, args));
|
|
36
|
+
return execFileSync(launch.cmd, launch.args, {
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
39
|
+
cwd,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function scriptEntryHref(argv1) {
|
|
44
|
+
try {
|
|
45
|
+
return argv1 ? new URL(`file://${argv1}`).href : "";
|
|
46
|
+
} catch {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function trimSha(value) {
|
|
52
|
+
if (typeof value !== "string") return null;
|
|
53
|
+
const sha = value.trim();
|
|
54
|
+
return sha ? sha : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function equalSha(left, right) {
|
|
58
|
+
if (!left || !right) return false;
|
|
59
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function tryRevParse(run, ref) {
|
|
63
|
+
try {
|
|
64
|
+
return trimSha(run("git", ["rev-parse", ref]));
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveGitRefSha(run) {
|
|
71
|
+
for (const ref of GIT_REF_CANDIDATES) {
|
|
72
|
+
const sha = tryRevParse(run, ref);
|
|
73
|
+
if (sha) return { ref, sha };
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readStampBuiltAt(tarballPath, run) {
|
|
79
|
+
try {
|
|
80
|
+
const listing = run("tar", ["-tzf", tarballPath]);
|
|
81
|
+
const stampEntry = String(listing)
|
|
82
|
+
.split("\n")
|
|
83
|
+
.map((line) => line.trim())
|
|
84
|
+
.find((line) => /\/dist\/build-stamp\.json$/.test(line));
|
|
85
|
+
if (!stampEntry) return null;
|
|
86
|
+
const stampJson = run("tar", ["-xzf", tarballPath, "-O", stampEntry]);
|
|
87
|
+
const parsed = JSON.parse(String(stampJson));
|
|
88
|
+
return typeof parsed.builtAt === "string" ? parsed.builtAt : null;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Download the published next tarball and read gitSha + builtAt. */
|
|
95
|
+
export function defaultNextStampProbe(version, run = defaultRun) {
|
|
96
|
+
const dir = mkdtempSync(join(tmpdir(), "loop-agent-next-info-"));
|
|
97
|
+
try {
|
|
98
|
+
const tarball = downloadNpmTarball(version, dir, run);
|
|
99
|
+
const stamp = readTarballBuildStamp(tarball, run);
|
|
100
|
+
return {
|
|
101
|
+
gitSha: stamp.gitSha,
|
|
102
|
+
version: stamp.version ?? null,
|
|
103
|
+
builtAt: readStampBuiltAt(tarball, run),
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
} finally {
|
|
108
|
+
rmSync(dir, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function emptyResult(overrides) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
exitCode: 1,
|
|
116
|
+
npmNextVersion: null,
|
|
117
|
+
gitSha: null,
|
|
118
|
+
gitRef: null,
|
|
119
|
+
gitRefSha: null,
|
|
120
|
+
stampSha: null,
|
|
121
|
+
builtAt: null,
|
|
122
|
+
source: null,
|
|
123
|
+
aheadBy: null,
|
|
124
|
+
aheadByReason: null,
|
|
125
|
+
unpublishedCommits: [],
|
|
126
|
+
unpublishedTruncated: false,
|
|
127
|
+
unpublishedLimit: UNPUBLISHED_COMMIT_LIMIT_DEFAULT,
|
|
128
|
+
drift: false,
|
|
129
|
+
error: null,
|
|
130
|
+
...overrides,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function collectAheadState(gitSha, run) {
|
|
135
|
+
if (!gitSha) {
|
|
136
|
+
return {
|
|
137
|
+
aheadBy: null,
|
|
138
|
+
aheadByReason: "no gitSha to compare against HEAD",
|
|
139
|
+
unpublishedCommits: [],
|
|
140
|
+
unpublishedTruncated: false,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const headSha = tryRevParse(run, "HEAD");
|
|
144
|
+
if (!headSha) {
|
|
145
|
+
return {
|
|
146
|
+
aheadBy: null,
|
|
147
|
+
aheadByReason: "HEAD is unavailable",
|
|
148
|
+
unpublishedCommits: [],
|
|
149
|
+
unpublishedTruncated: false,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let unpublishedCommits = [];
|
|
153
|
+
let unpublishedTruncated = false;
|
|
154
|
+
try {
|
|
155
|
+
const log = run("git", ["log", "--oneline", `${gitSha}..HEAD`]);
|
|
156
|
+
const lines = String(log ?? "")
|
|
157
|
+
.split("\n")
|
|
158
|
+
.map((line) => line.trim())
|
|
159
|
+
.filter(Boolean);
|
|
160
|
+
unpublishedTruncated = lines.length > UNPUBLISHED_COMMIT_LIMIT_DEFAULT;
|
|
161
|
+
unpublishedCommits = lines.slice(0, UNPUBLISHED_COMMIT_LIMIT_DEFAULT);
|
|
162
|
+
} catch {
|
|
163
|
+
unpublishedCommits = [];
|
|
164
|
+
}
|
|
165
|
+
if (!isAncestor(gitSha, headSha, run)) {
|
|
166
|
+
return {
|
|
167
|
+
aheadBy: null,
|
|
168
|
+
aheadByReason: "gitSha is not an ancestor of HEAD",
|
|
169
|
+
unpublishedCommits,
|
|
170
|
+
unpublishedTruncated,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const count = Number(
|
|
175
|
+
String(run("git", ["rev-list", "--count", `${gitSha}..HEAD`])).trim(),
|
|
176
|
+
);
|
|
177
|
+
if (!Number.isFinite(count)) {
|
|
178
|
+
return {
|
|
179
|
+
aheadBy: null,
|
|
180
|
+
aheadByReason: "rev-list count is not a number",
|
|
181
|
+
unpublishedCommits,
|
|
182
|
+
unpublishedTruncated,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
aheadBy: count,
|
|
187
|
+
aheadByReason: null,
|
|
188
|
+
unpublishedCommits,
|
|
189
|
+
unpublishedTruncated,
|
|
190
|
+
};
|
|
191
|
+
} catch {
|
|
192
|
+
return {
|
|
193
|
+
aheadBy: null,
|
|
194
|
+
aheadByReason: "rev-list failed",
|
|
195
|
+
unpublishedCommits,
|
|
196
|
+
unpublishedTruncated,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Pure-ish collector. Tests inject `run` / `viewDistTags` / `stampProbe`
|
|
203
|
+
* so this never hits the real npm registry.
|
|
204
|
+
*
|
|
205
|
+
* @param {{
|
|
206
|
+
* cwd?: string,
|
|
207
|
+
* run?: (cmd: string, args: string[]) => string,
|
|
208
|
+
* viewDistTags?: () => Record<string, string>,
|
|
209
|
+
* stampProbe?: (version: string) =>
|
|
210
|
+
* | { gitSha?: string, builtAt?: string | null }
|
|
211
|
+
* | null,
|
|
212
|
+
* }} [deps]
|
|
213
|
+
*/
|
|
214
|
+
export function collectNextInfo(deps = {}) {
|
|
215
|
+
const run =
|
|
216
|
+
deps.run ??
|
|
217
|
+
((cmd, args) =>
|
|
218
|
+
deps.cwd ? runWithCwd(cmd, args, deps.cwd) : defaultRun(cmd, args));
|
|
219
|
+
const viewDistTags = deps.viewDistTags ?? (() => npmViewDistTags(run));
|
|
220
|
+
const stampProbe =
|
|
221
|
+
deps.stampProbe ?? ((version) => defaultNextStampProbe(version, run));
|
|
222
|
+
|
|
223
|
+
let distTags = {};
|
|
224
|
+
try {
|
|
225
|
+
distTags = viewDistTags() ?? {};
|
|
226
|
+
} catch {
|
|
227
|
+
distTags = {};
|
|
228
|
+
}
|
|
229
|
+
const npmNextVersion = distTags.next ? String(distTags.next) : null;
|
|
230
|
+
const gitRef = resolveGitRefSha(run);
|
|
231
|
+
const gitRefSha = gitRef?.sha ?? null;
|
|
232
|
+
let stamp = null;
|
|
233
|
+
if (npmNextVersion) {
|
|
234
|
+
try {
|
|
235
|
+
stamp = stampProbe(npmNextVersion);
|
|
236
|
+
} catch {
|
|
237
|
+
stamp = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const stampSha = trimSha(stamp?.gitSha);
|
|
241
|
+
const builtAt = typeof stamp?.builtAt === "string" ? stamp.builtAt : null;
|
|
242
|
+
|
|
243
|
+
if (!gitRefSha && !stampSha) {
|
|
244
|
+
const error = npmNextVersion
|
|
245
|
+
? `no npm-next git ref and tarball stamp unreadable for next=${npmNextVersion}`
|
|
246
|
+
: "no npm-next git ref and no dist-tags.next";
|
|
247
|
+
return emptyResult({ error, npmNextVersion });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let source;
|
|
251
|
+
let gitSha;
|
|
252
|
+
let drift = false;
|
|
253
|
+
let error = null;
|
|
254
|
+
let ok = true;
|
|
255
|
+
let exitCode = 0;
|
|
256
|
+
|
|
257
|
+
if (gitRefSha && stampSha) {
|
|
258
|
+
source = "both";
|
|
259
|
+
if (!equalSha(gitRefSha, stampSha)) {
|
|
260
|
+
drift = true;
|
|
261
|
+
ok = false;
|
|
262
|
+
exitCode = 1;
|
|
263
|
+
error =
|
|
264
|
+
`npm-next git ref (${gitRefSha}) does not match tarball stamp gitSha (${stampSha})`;
|
|
265
|
+
}
|
|
266
|
+
gitSha = gitRefSha;
|
|
267
|
+
} else if (gitRefSha) {
|
|
268
|
+
source = "git-ref";
|
|
269
|
+
gitSha = gitRefSha;
|
|
270
|
+
} else {
|
|
271
|
+
source = "tarball-stamp";
|
|
272
|
+
gitSha = stampSha;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const ahead = collectAheadState(gitSha, run);
|
|
276
|
+
return {
|
|
277
|
+
ok,
|
|
278
|
+
exitCode,
|
|
279
|
+
npmNextVersion,
|
|
280
|
+
gitSha,
|
|
281
|
+
gitRef: gitRef?.ref ?? null,
|
|
282
|
+
gitRefSha,
|
|
283
|
+
stampSha,
|
|
284
|
+
builtAt,
|
|
285
|
+
source,
|
|
286
|
+
aheadBy: ahead.aheadBy,
|
|
287
|
+
aheadByReason: ahead.aheadByReason,
|
|
288
|
+
unpublishedCommits: ahead.unpublishedCommits,
|
|
289
|
+
unpublishedTruncated: ahead.unpublishedTruncated,
|
|
290
|
+
unpublishedLimit: UNPUBLISHED_COMMIT_LIMIT_DEFAULT,
|
|
291
|
+
drift,
|
|
292
|
+
error,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function formatNextInfo(result, { json = false } = {}) {
|
|
297
|
+
if (json) {
|
|
298
|
+
return `${JSON.stringify(result, null, 2)}\n`;
|
|
299
|
+
}
|
|
300
|
+
const lines = [];
|
|
301
|
+
if (result.npmNextVersion) {
|
|
302
|
+
lines.push(`npm next: ${result.npmNextVersion}`);
|
|
303
|
+
} else {
|
|
304
|
+
lines.push("npm next: (no dist-tags.next)");
|
|
305
|
+
}
|
|
306
|
+
if (result.gitSha) {
|
|
307
|
+
lines.push(`gitSha: ${result.gitSha} (source: ${result.source})`);
|
|
308
|
+
} else {
|
|
309
|
+
lines.push("gitSha: (unavailable)");
|
|
310
|
+
}
|
|
311
|
+
if (result.gitRefSha && result.stampSha && result.drift) {
|
|
312
|
+
lines.push(`git-ref SHA: ${result.gitRefSha}`);
|
|
313
|
+
lines.push(`tarball stamp SHA: ${result.stampSha}`);
|
|
314
|
+
}
|
|
315
|
+
if (result.builtAt) {
|
|
316
|
+
lines.push(`builtAt: ${result.builtAt}`);
|
|
317
|
+
}
|
|
318
|
+
if (result.aheadBy === null) {
|
|
319
|
+
lines.push(
|
|
320
|
+
`aheadBy: null${result.aheadByReason ? ` (${result.aheadByReason})` : ""}`,
|
|
321
|
+
);
|
|
322
|
+
} else {
|
|
323
|
+
lines.push(`aheadBy: ${result.aheadBy}`);
|
|
324
|
+
}
|
|
325
|
+
const commits = result.unpublishedCommits ?? [];
|
|
326
|
+
if (commits.length === 0) {
|
|
327
|
+
lines.push("unpublishedCommits: (none)");
|
|
328
|
+
} else {
|
|
329
|
+
lines.push("unpublishedCommits:");
|
|
330
|
+
for (const commit of commits) lines.push(` ${commit}`);
|
|
331
|
+
if (result.unpublishedTruncated) {
|
|
332
|
+
lines.push(
|
|
333
|
+
` … truncated to ${result.unpublishedLimit} commits`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (result.error) {
|
|
338
|
+
lines.push(`error: ${result.error}`);
|
|
339
|
+
}
|
|
340
|
+
return `${lines.join("\n")}\n`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function main(argv = process.argv.slice(2)) {
|
|
344
|
+
const json = argv.includes("--json");
|
|
345
|
+
const result = collectNextInfo();
|
|
346
|
+
process.stdout.write(formatNextInfo(result, { json }));
|
|
347
|
+
process.exit(result.ok ? 0 : result.exitCode || 1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const invokedAsScript =
|
|
351
|
+
Boolean(process.argv[1]) &&
|
|
352
|
+
import.meta.url === scriptEntryHref(process.argv[1]);
|
|
353
|
+
|
|
354
|
+
if (invokedAsScript) {
|
|
355
|
+
main();
|
|
356
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* next-publish-gate: throttle main -> npm `next` publishing.
|
|
4
|
+
*
|
|
5
|
+
* Every main push used to publish a new next build. With this gate a
|
|
6
|
+
* push-triggered run only publishes when HEAD is at least COMMIT_THRESHOLD
|
|
7
|
+
* commits ahead of the commit that produced the current dist-tags.next
|
|
8
|
+
* build (read from the published tarball's dist/build-stamp.json).
|
|
9
|
+
*
|
|
10
|
+
* Bypasses (publish regardless of the count):
|
|
11
|
+
* - workflow_dispatch runs (manual trigger, e.g. right before cutting a
|
|
12
|
+
* release line); the workflow passes --force for those.
|
|
13
|
+
* - recovery: HEAD already IS the source of the current next build, so
|
|
14
|
+
* re-publishing would be the same version anyway.
|
|
15
|
+
* - unknown last-published SHA (no npm history / unreadable stamp /
|
|
16
|
+
* SHA not an ancestor of HEAD): fail open — a missed next build is
|
|
17
|
+
* cheap, permanently stuck publishing is not.
|
|
18
|
+
*
|
|
19
|
+
* Exit codes: 0 = publish, 2 = skip (gate), 1 = hard error. The workflow
|
|
20
|
+
* maps skip to a successful early exit so non-publishing runs are green.
|
|
21
|
+
*/
|
|
22
|
+
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
24
|
+
import { tmpdir } from "node:os";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import process from "node:process";
|
|
27
|
+
import { readTarballBuildStamp } from "./release-source-binding.mjs";
|
|
28
|
+
|
|
29
|
+
export const COMMIT_THRESHOLD_DEFAULT = 5;
|
|
30
|
+
const SKIP_EXIT_CODE = 2;
|
|
31
|
+
|
|
32
|
+
/** Run a command and return trimmed stdout; injected for tests. */
|
|
33
|
+
function normalizeWindowsTarArg(value) {
|
|
34
|
+
const match = /^([A-Za-z]):[\\/](.*)$/.exec(String(value));
|
|
35
|
+
if (!match) return value;
|
|
36
|
+
return `/${match[1].toLowerCase()}/${match[2].replaceAll("\\", "/")}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizeCommandArgs(cmd, args, platform = process.platform) {
|
|
40
|
+
if (platform !== "win32" || cmd !== "tar") return args;
|
|
41
|
+
return args.map(normalizeWindowsTarArg);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function commandLaunch(cmd, args, platform = process.platform) {
|
|
45
|
+
if (platform === "win32" && cmd === "npm") {
|
|
46
|
+
return {
|
|
47
|
+
cmd: process.env.ComSpec || "cmd.exe",
|
|
48
|
+
args: ["/d", "/s", "/c", "npm", ...args],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return { cmd, args };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function defaultRun(cmd, args) {
|
|
55
|
+
const launch = commandLaunch(cmd, normalizeCommandArgs(cmd, args));
|
|
56
|
+
return execFileSync(launch.cmd, launch.args, {
|
|
57
|
+
encoding: "utf8",
|
|
58
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function npmViewDistTags(run = defaultRun) {
|
|
63
|
+
const out = run("npm", [
|
|
64
|
+
"view",
|
|
65
|
+
"@tea-agent/loop-agent",
|
|
66
|
+
"dist-tags",
|
|
67
|
+
"--json",
|
|
68
|
+
]);
|
|
69
|
+
const trimmed = String(out ?? "").trim();
|
|
70
|
+
if (!trimmed) return {};
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(trimmed);
|
|
74
|
+
} catch {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Download the exact published next version and read its build stamp. */
|
|
81
|
+
export function defaultStampProbe(version) {
|
|
82
|
+
const dir = mkdtempSync(join(tmpdir(), "loop-agent-next-gate-"));
|
|
83
|
+
try {
|
|
84
|
+
defaultRun("npm", [
|
|
85
|
+
"pack",
|
|
86
|
+
`@tea-agent/loop-agent@${version}`,
|
|
87
|
+
"--pack-destination",
|
|
88
|
+
dir,
|
|
89
|
+
]);
|
|
90
|
+
const file = readdirSync(dir).find((entry) => entry.endsWith(".tgz"));
|
|
91
|
+
if (!file) return null;
|
|
92
|
+
try {
|
|
93
|
+
return readTarballBuildStamp(join(dir, file), defaultRun);
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
} finally {
|
|
100
|
+
rmSync(dir, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function isAncestor(sha, head, run = defaultRun) {
|
|
105
|
+
try {
|
|
106
|
+
run("git", ["merge-base", "--is-ancestor", sha, head]);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pure decision. Inputs:
|
|
115
|
+
* - threshold: minimum commits ahead of lastPublishedSha to publish
|
|
116
|
+
* - aheadBy: commits between lastPublishedSha and headSha (null = unknown)
|
|
117
|
+
* - lastPublishedSha / headSha: for the recovery equality check
|
|
118
|
+
* Returns { publish, reason, aheadBy }.
|
|
119
|
+
*/
|
|
120
|
+
export function shouldPublishNext({
|
|
121
|
+
threshold = COMMIT_THRESHOLD_DEFAULT,
|
|
122
|
+
lastPublishedSha,
|
|
123
|
+
headSha,
|
|
124
|
+
aheadBy,
|
|
125
|
+
force = false,
|
|
126
|
+
}) {
|
|
127
|
+
if (force) {
|
|
128
|
+
return { publish: true, reason: "force-bypass", aheadBy };
|
|
129
|
+
}
|
|
130
|
+
if (!headSha) {
|
|
131
|
+
return { publish: true, reason: "head-unknown-fail-open", aheadBy };
|
|
132
|
+
}
|
|
133
|
+
if (!lastPublishedSha) {
|
|
134
|
+
return {
|
|
135
|
+
publish: true,
|
|
136
|
+
reason: "last-published-sha-unknown-fail-open",
|
|
137
|
+
aheadBy,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const last = String(lastPublishedSha).toLowerCase();
|
|
141
|
+
const head = String(headSha).toLowerCase();
|
|
142
|
+
if (last === head) {
|
|
143
|
+
return { publish: true, reason: "same-commit-recovery", aheadBy: 0 };
|
|
144
|
+
}
|
|
145
|
+
if (aheadBy === null || aheadBy === undefined || !Number.isFinite(aheadBy)) {
|
|
146
|
+
return { publish: true, reason: "ahead-unknown-fail-open", aheadBy: null };
|
|
147
|
+
}
|
|
148
|
+
const normalizedThreshold = Number(threshold);
|
|
149
|
+
if (!Number.isFinite(normalizedThreshold) || normalizedThreshold <= 0) {
|
|
150
|
+
return { publish: true, reason: "threshold-invalid-fail-open", aheadBy };
|
|
151
|
+
}
|
|
152
|
+
if (aheadBy >= normalizedThreshold) {
|
|
153
|
+
return { publish: true, reason: "ahead-threshold-met", aheadBy };
|
|
154
|
+
}
|
|
155
|
+
return { publish: false, reason: "below-threshold-skip", aheadBy };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseArgs(argv) {
|
|
159
|
+
return Object.fromEntries(
|
|
160
|
+
argv
|
|
161
|
+
.filter((arg) => arg.startsWith("--"))
|
|
162
|
+
.map((arg) => {
|
|
163
|
+
const [key, ...value] = arg.replace(/^--/, "").split("=");
|
|
164
|
+
return [key, value.join("=") || "true"];
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function main() {
|
|
170
|
+
const args = parseArgs(process.argv.slice(2));
|
|
171
|
+
const threshold = Number(args.threshold || COMMIT_THRESHOLD_DEFAULT);
|
|
172
|
+
const distTags = npmViewDistTags();
|
|
173
|
+
const currentNext = distTags.next ? String(distTags.next) : null;
|
|
174
|
+
let lastPublishedSha = null;
|
|
175
|
+
let currentNextNote = "no dist-tags.next";
|
|
176
|
+
if (currentNext) {
|
|
177
|
+
const stamp = defaultStampProbe(currentNext);
|
|
178
|
+
if (stamp && stamp.gitSha) {
|
|
179
|
+
lastPublishedSha = stamp.gitSha;
|
|
180
|
+
currentNextNote = `next=${currentNext} from ${stamp.gitSha.slice(0, 10)}`;
|
|
181
|
+
} else {
|
|
182
|
+
currentNextNote = `next=${currentNext} (stamp unreadable)`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
let headSha = null;
|
|
186
|
+
let aheadBy = null;
|
|
187
|
+
try {
|
|
188
|
+
headSha = defaultRun("git", ["rev-parse", "HEAD"]).trim();
|
|
189
|
+
} catch {
|
|
190
|
+
headSha = null;
|
|
191
|
+
}
|
|
192
|
+
if (headSha && lastPublishedSha) {
|
|
193
|
+
if (!isAncestor(lastPublishedSha, headSha)) {
|
|
194
|
+
aheadBy = null;
|
|
195
|
+
} else {
|
|
196
|
+
aheadBy = Number(
|
|
197
|
+
defaultRun("git", [
|
|
198
|
+
"rev-list",
|
|
199
|
+
"--count",
|
|
200
|
+
`${lastPublishedSha}..${headSha}`,
|
|
201
|
+
]).trim(),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const decision = shouldPublishNext({
|
|
206
|
+
threshold,
|
|
207
|
+
lastPublishedSha,
|
|
208
|
+
headSha,
|
|
209
|
+
aheadBy,
|
|
210
|
+
force: args.force === "true",
|
|
211
|
+
});
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
`next-publish-gate: ${currentNextNote}\n` +
|
|
214
|
+
`next-publish-gate: head=${headSha ? headSha.slice(0, 10) : "unknown"} ` +
|
|
215
|
+
`aheadBy=${aheadBy === null ? "unknown" : aheadBy} ` +
|
|
216
|
+
`threshold=${threshold}\n` +
|
|
217
|
+
`next-publish-gate: ${decision.publish ? "PUBLISH" : "SKIP"} ` +
|
|
218
|
+
`(${decision.reason})\n`,
|
|
219
|
+
);
|
|
220
|
+
process.exit(decision.publish ? 0 : SKIP_EXIT_CODE);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// CLI 入口安全判断(argv[1] 非合法 file: URL 时 new URL 会抛异常)
|
|
224
|
+
function scriptEntryHref(argv1) {
|
|
225
|
+
try {
|
|
226
|
+
return argv1 ? new URL(`file://${argv1}`).href : "";
|
|
227
|
+
} catch {
|
|
228
|
+
return "";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const invokedAsScript =
|
|
233
|
+
Boolean(process.argv[1]) &&
|
|
234
|
+
import.meta.url === scriptEntryHref(process.argv[1]);
|
|
235
|
+
|
|
236
|
+
if (invokedAsScript) {
|
|
237
|
+
main();
|
|
238
|
+
}
|