@pareto4/pp-core 1.13.0 → 1.18.0
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/.claude-plugin/plugin.json +3 -3
- package/README.md +31 -12
- package/agents/pp-code-fixer.md +0 -11
- package/agents/pp-code-reviewer.md +1 -77
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +12 -29
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +34 -20
- package/agents/pp-vault-reviewer.md +100 -23
- package/agents/pp-verifier.md +1 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-manage.md +1 -1
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-promote.md +7 -8
- package/commands/pp-vault-review.md +6 -5
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +10 -18
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +201 -229
- package/pp-core/bin/lib/capability-state.cjs +93 -1
- package/pp-core/bin/lib/capability-writer.cjs +3 -3
- package/pp-core/bin/lib/check-command-router.cjs +1906 -1514
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +151 -62
- package/pp-core/bin/lib/decisions.cjs +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +11 -12
- package/pp-core/bin/lib/git-provider.cjs +9 -10
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +620 -738
- package/pp-core/bin/lib/init-interactive.cjs +62 -4
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +4 -2
- package/pp-core/bin/lib/loop-resolver.cjs +165 -129
- package/pp-core/bin/lib/org-profile.cjs +402 -161
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-command-router.cjs +1682 -477
- package/pp-core/bin/lib/vault-coordinates.cjs +16 -7
- package/pp-core/bin/lib/vault-index.cjs +656 -0
- package/pp-core/bin/lib/vault-seal.cjs +1851 -0
- package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
- package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
- package/pp-core/bin/lib/vault-standards.cjs +1504 -0
- package/pp-core/bin/lib/vault-trace.cjs +155 -2778
- package/pp-core/bin/lib/vault-update.cjs +10 -59
- package/pp-core/bin/lib/vault.cjs +2570 -950
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +157 -54
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +2 -2
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -25
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-standards.md +311 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +56 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -3
- package/pp-core/workflows/code-review.md +10 -57
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +207 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +10 -2
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +327 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +78 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +219 -20
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +147 -0
- package/pp-core/workflows/execute-phase.md +152 -128
- package/pp-core/workflows/execute-plan.md +54 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +161 -23
- package/pp-core/workflows/vault-write-back.md +73 -110
- package/pp-core/workflows/verify-work.md +12 -14
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +120 -13
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +3 -19
- package/scripts/lint-test-file-count.cjs +5 -6
- package/scripts/prompt-injection-scan.sh +19 -17
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/bin/lib/practices-review-check.cjs +0 -938
- package/pp-core/bin/lib/vault-audit.cjs +0 -2070
- package/pp-core/bin/lib/vault-practices.cjs +0 -1990
- package/pp-core/bin/lib/vault-review.cjs +0 -2359
- package/pp-core/bin/lib/vault-write-back.cjs +0 -877
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/references/planner-vault-practices.md +0 -112
- package/pp-core/templates/practices/README.md +0 -251
- package/pp-core/templates/practices/error-handling.md +0 -18
- package/pp-core/templates/practices/minimal-comments.md +0 -14
- package/pp-core/templates/practices/naming-convention.md +0 -18
- package/pp-core/templates/practices/practice-template.md +0 -23
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- package/scripts/lint-org-coordinates.cjs +0 -426
|
@@ -44,6 +44,9 @@ exports.CLUSTERS = Object.freeze({
|
|
|
44
44
|
'eval-review',
|
|
45
45
|
'add-tests',
|
|
46
46
|
'secure-phase',
|
|
47
|
+
// Retroactive, non-gating review of a completed phase against the client
|
|
48
|
+
// vault's standards documents (`check vault-review`, a report) — the same
|
|
49
|
+
// family as secure-phase / ui-review / validate-phase.
|
|
47
50
|
'vault-review',
|
|
48
51
|
]),
|
|
49
52
|
milestone: Object.freeze([
|
|
@@ -698,27 +698,6 @@ exports.NON_FAMILY_COMMAND_ALIASES = [
|
|
|
698
698
|
],
|
|
699
699
|
"mutation": true
|
|
700
700
|
},
|
|
701
|
-
{
|
|
702
|
-
"canonical": "phase.dispatch-plan",
|
|
703
|
-
"aliases": [
|
|
704
|
-
"phase dispatch-plan"
|
|
705
|
-
],
|
|
706
|
-
"mutation": false
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
"canonical": "phase.install-run",
|
|
710
|
-
"aliases": [
|
|
711
|
-
"phase install-run"
|
|
712
|
-
],
|
|
713
|
-
"mutation": true
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
"canonical": "phase.manifest",
|
|
717
|
-
"aliases": [
|
|
718
|
-
"phase manifest"
|
|
719
|
-
],
|
|
720
|
-
"mutation": true
|
|
721
|
-
},
|
|
722
701
|
{
|
|
723
702
|
"canonical": "phase.mvp-mode",
|
|
724
703
|
"aliases": [
|
|
@@ -726,20 +705,6 @@ exports.NON_FAMILY_COMMAND_ALIASES = [
|
|
|
726
705
|
],
|
|
727
706
|
"mutation": false
|
|
728
707
|
},
|
|
729
|
-
{
|
|
730
|
-
"canonical": "phase.tier",
|
|
731
|
-
"aliases": [
|
|
732
|
-
"phase tier"
|
|
733
|
-
],
|
|
734
|
-
"mutation": false
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
"canonical": "phase.tier-propose",
|
|
738
|
-
"aliases": [
|
|
739
|
-
"phase tier-propose"
|
|
740
|
-
],
|
|
741
|
-
"mutation": true
|
|
742
|
-
},
|
|
743
708
|
{
|
|
744
709
|
"canonical": "progress.bar",
|
|
745
710
|
"aliases": [
|
|
@@ -47,6 +47,10 @@ const { extractFrontmatter } = frontmatter;
|
|
|
47
47
|
const modelProfiles = require("./model-profiles.cjs");
|
|
48
48
|
const { MODEL_PROFILES, VALID_PHASE_TYPES } = modelProfiles;
|
|
49
49
|
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
50
|
+
// 16-03: the one `.planning/` path test. The seal leaf's top level imports only
|
|
51
|
+
// Node built-ins and it never requires this module, so the edge adds no cycle.
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
53
|
+
const vaultSeal = require("./vault-seal.cjs");
|
|
50
54
|
// ─── Phase Status ─────────────────────────────────────────────────────────────
|
|
51
55
|
/**
|
|
52
56
|
* Determine phase status by checking plan/summary counts AND verification state.
|
|
@@ -458,7 +462,7 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
|
|
|
458
462
|
const phaseMatch = (files || []).join(' ').match(/(\d+(?:\.\d+)*)-/);
|
|
459
463
|
if (phaseMatch) {
|
|
460
464
|
const phaseNum = phaseMatch[1];
|
|
461
|
-
// D-03: a prior milestone's archived phase whose number the current
|
|
465
|
+
// PBI 75323 D-03: a prior milestone's archived phase whose number the current
|
|
462
466
|
// ROADMAP reuses names no branch (the phase-scope owner drops it).
|
|
463
467
|
const phaseInfo = resolvePhaseForRead(cwd, phaseNum);
|
|
464
468
|
if (phaseInfo) {
|
|
@@ -907,7 +911,7 @@ function cmdTodoMatchPhase(cwd, phase, raw) {
|
|
|
907
911
|
const phaseKeywords = new Set(phaseText.split(/[\s\-_/.,;:()\[\]{}|]+/)
|
|
908
912
|
.map(w => w.replace(/[^a-z0-9]/g, ''))
|
|
909
913
|
.filter(w => w.length > 2 && !stopWords.has(w)));
|
|
910
|
-
// Find phase directory to get expected file paths (D-03: through the
|
|
914
|
+
// Find phase directory to get expected file paths (PBI 75323 D-03: through the
|
|
911
915
|
// phase-scope owner, so a prior milestone's archive never lends this phase its plans)
|
|
912
916
|
const phaseInfoDisk = resolvePhaseForRead(cwd, phase);
|
|
913
917
|
const phasePlans = [];
|
|
@@ -993,7 +997,7 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
993
997
|
const { phase, name } = options;
|
|
994
998
|
const padded = phase ? normalizePhaseName(phase) : '00';
|
|
995
999
|
const today = new Date().toISOString().split('T')[0];
|
|
996
|
-
// Find phase directory. D-02 (todo "template-fill-writes-
|
|
1000
|
+
// Find phase directory. PBI 75323 / quick 261009-gvf D-02 (todo "template-fill-writes-
|
|
997
1001
|
// into-an-archived-phase-directory"): context / uat / verification never write into a
|
|
998
1002
|
// closed milestone's archive. The phase-scope owner hands back a live match only; an
|
|
999
1003
|
// archive this phase still names is refused naming it, and a prior milestone's archive
|
|
@@ -1223,7 +1227,8 @@ function cmdCheckCommit(cwd, raw) {
|
|
|
1223
1227
|
// commit_docs is false — check if any .planning/ files are staged
|
|
1224
1228
|
const stagedResult = (0, shell_command_projection_cjs_1.execGit)(['diff', '--cached', '--name-only'], { cwd });
|
|
1225
1229
|
if (stagedResult.exitCode === 0) {
|
|
1226
|
-
|
|
1230
|
+
// 16-03: the seal leaf owns the `.planning/` path test (both separators).
|
|
1231
|
+
const planningFiles = stagedResult.stdout.split('\n').filter((f) => vaultSeal.isPlanningPath(f));
|
|
1227
1232
|
if (planningFiles.length > 0) {
|
|
1228
1233
|
error(`commit_docs is false but ${planningFiles.length} .planning/ file(s) are staged:\n` +
|
|
1229
1234
|
planningFiles.map(f => ` ${f}`).join('\n') +
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
* `JSON.stringify`/object literals — never by `.md` shell string
|
|
32
32
|
* concatenation.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* Its importers today are `src/vault-update.cts`, `src/init-contract.cts`
|
|
35
|
+
* and (quick 261003-lqi) `src/check-command-router.cts`
|
|
36
|
+
* — and this module imports none of them, so its top-level imports of `./org-profile.cjs`, `./platform-bindings.cjs`,
|
|
36
37
|
* `./git-provider.cjs`, `./vault-coordinates.cjs`, `./io.cjs`,
|
|
37
38
|
* `./project-root.cjs`, `./package-identity.cjs` and
|
|
38
39
|
* `./shell-command-projection.cjs` create no require cycle: none of those
|
|
@@ -49,13 +50,16 @@ exports.COMPOSE_BINDINGS_FLAGS = exports.SEED_VALUE_MAX_LENGTH = void 0;
|
|
|
49
50
|
exports._resetPlatformTagMemoForTests = _resetPlatformTagMemoForTests;
|
|
50
51
|
exports.listStableTagsAt = listStableTagsAt;
|
|
51
52
|
exports.pickLatestStableTag = pickLatestStableTag;
|
|
53
|
+
exports.resolveLatestVaultTag = resolveLatestVaultTag;
|
|
54
|
+
exports.resolvePlatformRepoUrl = resolvePlatformRepoUrl;
|
|
55
|
+
exports.resolveLatestPlatformTag = resolveLatestPlatformTag;
|
|
52
56
|
exports.resolveCreatedWithFrom = resolveCreatedWithFrom;
|
|
53
57
|
exports.composeBindings = composeBindings;
|
|
54
58
|
exports.redactComposeResultForEmission = redactComposeResultForEmission;
|
|
55
59
|
exports.cmdComposeBindings = cmdComposeBindings;
|
|
60
|
+
exports.cmdPlatformLatestTag = cmdPlatformLatestTag;
|
|
56
61
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
57
62
|
const node_path_1 = __importDefault(require("node:path"));
|
|
58
|
-
const node_child_process_1 = __importDefault(require("node:child_process"));
|
|
59
63
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
60
64
|
const io = require("./io.cjs");
|
|
61
65
|
const { output } = io;
|
|
@@ -356,17 +360,112 @@ const platformTagMemo = new Map();
|
|
|
356
360
|
* `src/active-workstream-store.cts`'s controlling-TTY probe cache. */
|
|
357
361
|
function _resetPlatformTagMemoForTests() {
|
|
358
362
|
platformTagMemo.clear();
|
|
363
|
+
vaultTagMemo.clear();
|
|
364
|
+
}
|
|
365
|
+
/** Per-process memo for `resolveLatestVaultTag`, keyed by clone target. */
|
|
366
|
+
const vaultTagMemo = new Map();
|
|
367
|
+
/**
|
|
368
|
+
* List the stable `vX.Y.Z` tags at `url`, never shell-eval'd (T-34-25):
|
|
369
|
+
* `git ls-remote` is spawned with an argv array through `execGitLocal`, the
|
|
370
|
+
* owner of a vault git invocation's environment (D-116, D-117): the URL names
|
|
371
|
+
* the remote, but git still reads the config of the repository an inherited
|
|
372
|
+
* location variable selects, so the location is unset and transport is kept.
|
|
373
|
+
* Bounded by a 10 s `timeout`, `windowsHide` and spawnSync's default buffer
|
|
374
|
+
* (T-34-26), with the child's stderr piped and discarded — the owner prints
|
|
375
|
+
* nothing (`init-tag-lookup-raw-git-error`). `--` separates the target from
|
|
376
|
+
* the options so a flag-shaped value can never be read as one (T-29-26).
|
|
377
|
+
* Filtered with `isStableTripletSemver`, the single blessed predicate. THROWS
|
|
378
|
+
* on any git fault (a non-zero exit, which the owner also reports for a
|
|
379
|
+
* timeout or a missing git) — callers own the degrade-to-warning policy.
|
|
380
|
+
*/
|
|
381
|
+
function listStableTagsAt(url, opts = {}) {
|
|
382
|
+
const lsOutput = (() => {
|
|
383
|
+
if (typeof opts._lsRemote === 'function')
|
|
384
|
+
return opts._lsRemote(url);
|
|
385
|
+
const result = (0, shell_command_projection_cjs_1.execGitLocal)(['ls-remote', '--tags', '--refs', '--sort=-v:refname', '--', url, 'v*'], { timeout: 10_000 });
|
|
386
|
+
if (result.exitCode !== 0 || result.error)
|
|
387
|
+
throw new Error(`git ls-remote exited ${result.exitCode}`);
|
|
388
|
+
return result.stdout;
|
|
389
|
+
})();
|
|
390
|
+
return lsOutput
|
|
391
|
+
.split('\n')
|
|
392
|
+
.map((line) => {
|
|
393
|
+
const m = line.match(/\brefs\/tags\/([^\s^]+)$/);
|
|
394
|
+
return m ? m[1].trim() : '';
|
|
395
|
+
})
|
|
396
|
+
.filter((t) => t && (0, semver_compare_cjs_1.isStableTripletSemver)(t));
|
|
397
|
+
}
|
|
398
|
+
/** The highest tag by `compareSemverCore`, or `null` for an empty list. */
|
|
399
|
+
function pickLatestStableTag(tags) {
|
|
400
|
+
if (tags.length === 0)
|
|
401
|
+
return null;
|
|
402
|
+
let latest = tags[0];
|
|
403
|
+
for (const t of tags) {
|
|
404
|
+
if ((0, semver_compare_cjs_1.compareSemverCore)(t, latest) > 0)
|
|
405
|
+
latest = t;
|
|
406
|
+
}
|
|
407
|
+
return latest;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* FORK-ONLY (PBI 74316). Propose the vault version a NEW project builds on:
|
|
411
|
+
* the latest stable tag of the client vault repo. The embedded org profile
|
|
412
|
+
* seeds no vault ref on purpose — the version is a per-project choice,
|
|
413
|
+
* committed in `.platform-bindings.json`, so no pp-core release can move it.
|
|
414
|
+
* The proposal reaches the operator through the provenance table
|
|
415
|
+
* (`resolved-tag`) they confirm before anything is written.
|
|
416
|
+
*
|
|
417
|
+
* The clone target is composed by `composeCloneTarget`, so the scp-style SSH
|
|
418
|
+
* form passes through verbatim. Tagless, offline and unreachable all degrade
|
|
419
|
+
* to `{ tag: null }` plus one bounded warning naming the consequence: with no
|
|
420
|
+
* `client.vaultRef` the vault resolves `main`, i.e. the project is unpinned.
|
|
421
|
+
*/
|
|
422
|
+
function resolveLatestVaultTag(repo, host, provider, opts = {}) {
|
|
423
|
+
const unpinned = (why) => ({
|
|
424
|
+
tag: null,
|
|
425
|
+
warnings: [
|
|
426
|
+
bound(`client.vaultRef could not be proposed — ${why}; the vault resolves "main" until the project is pinned with "pp-tools vault update <tag>".`),
|
|
427
|
+
],
|
|
428
|
+
});
|
|
429
|
+
const target = (0, git_provider_cjs_1.composeCloneTarget)(provider, repo, host);
|
|
430
|
+
const cached = vaultTagMemo.get(target.url);
|
|
431
|
+
if (cached)
|
|
432
|
+
return cached;
|
|
433
|
+
let result;
|
|
434
|
+
try {
|
|
435
|
+
const tag = pickLatestStableTag(listStableTagsAt(target.url, opts));
|
|
436
|
+
result = tag ? { tag, warnings: [] } : unpinned('the vault repo has no stable v* tag');
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
result = unpinned('the tag lookup failed or was unreachable');
|
|
440
|
+
}
|
|
441
|
+
vaultTagMemo.set(target.url, result);
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* The platform repo URL rule — the ONE place the platform URL string is
|
|
446
|
+
* composed (quick 261003-my0). The host is `resolvePlatformHost(cwd).host`
|
|
447
|
+
* when it resolves, else `defaultHostForProvider(provider)`; it is never
|
|
448
|
+
* derived from the repo string (Phase 31 D-09). `resolveLatestPlatformTag`
|
|
449
|
+
* and, through it, `pp-tools init`, `check ship.platform-drift` and
|
|
450
|
+
* `query platform-latest-tag` all read this rule, so a shell copy cannot
|
|
451
|
+
* drift from it. May throw when `resolvePlatformHost` throws; the owner's
|
|
452
|
+
* try/catch is the degrade policy.
|
|
453
|
+
*/
|
|
454
|
+
function resolvePlatformRepoUrl(cwd, repo, provider) {
|
|
455
|
+
const host = (0, vault_coordinates_cjs_1.resolvePlatformHost)(cwd).host ?? (0, vault_coordinates_cjs_1.defaultHostForProvider)(provider);
|
|
456
|
+
return `https://${host}/${repo}`;
|
|
359
457
|
}
|
|
360
458
|
/**
|
|
361
459
|
* Resolve the latest STABLE tag for `repo` over the resolved host, never
|
|
362
|
-
* shell-eval'd (T-34-25): `git ls-remote` is spawned with an argv array
|
|
363
|
-
* `
|
|
364
|
-
* (T-34-26). The
|
|
365
|
-
*
|
|
366
|
-
*
|
|
460
|
+
* shell-eval'd (T-34-25): `git ls-remote` is spawned with an argv array
|
|
461
|
+
* through `execGitLocal` (D-116, D-117), bounded by `timeout`/`windowsHide`
|
|
462
|
+
* (T-34-26). The URL comes from `resolvePlatformRepoUrl`: the host is
|
|
463
|
+
* `resolvePlatformHost(cwd).host` when it resolves, else
|
|
464
|
+
* `defaultHostForProvider(provider)` — the host is authoritative and never
|
|
465
|
+
* derived from the repo string (Phase 31 D-09).
|
|
367
466
|
*
|
|
368
467
|
* The child's stderr is piped into the parent's buffer rather than inherited
|
|
369
|
-
* —
|
|
468
|
+
* — the owner spawns with `stdio: 'pipe'` — and deliberately
|
|
370
469
|
* discarded rather than logged: the bounded warning this function returns on
|
|
371
470
|
* failure is the single user-visible surface for a lookup failure. Inheriting
|
|
372
471
|
* the child's stream let a raw git `fatal:` line reach the terminal during an
|
|
@@ -374,22 +473,29 @@ function _resetPlatformTagMemoForTests() {
|
|
|
374
473
|
*
|
|
375
474
|
* Every candidate ref is filtered with `isStableTripletSemver` and ordered
|
|
376
475
|
* with `compareSemverCore` — the single blessed comparison source, never
|
|
377
|
-
* reimplemented. An empty result degrades to `{ tag: null,
|
|
378
|
-
* (tagless is valid, D-01/D-02); any thrown fault
|
|
379
|
-
* DNS failure, non-zero git exit
|
|
476
|
+
* reimplemented. An empty result degrades to `{ tag: null, outcome:
|
|
477
|
+
* 'tagless', warnings: [] }` (tagless is valid, D-01/D-02); any thrown fault
|
|
478
|
+
* (offline, unreachable, DNS failure, non-zero git exit, an unresolvable
|
|
479
|
+
* host) degrades to `{ tag: null, outcome: 'unreachable', warnings: [one
|
|
380
480
|
* bounded warning] }` — offline, unreachable and tagless all resolve to the
|
|
381
|
-
* platform being unpinned, never a compose failure
|
|
481
|
+
* platform being unpinned, never a compose failure, and `outcome` says which
|
|
482
|
+
* one was measured (quick 261003-my0).
|
|
483
|
+
*
|
|
484
|
+
* Quick 261003-lqi: the ship-time drift check (`cmdShipPlatformDrift`) calls
|
|
485
|
+
* this same owner, so init and ship resolve one URL from one host rule.
|
|
486
|
+
* Quick 261003-my0: the init.md / new-project.md preflights reach it through
|
|
487
|
+
* `query platform-latest-tag` (`cmdPlatformLatestTag`).
|
|
382
488
|
*/
|
|
383
489
|
function resolveLatestPlatformTag(cwd, repo, provider, opts) {
|
|
384
490
|
let url;
|
|
385
491
|
try {
|
|
386
|
-
|
|
387
|
-
const host = hostRes.host ?? (0, vault_coordinates_cjs_1.defaultHostForProvider)(provider);
|
|
388
|
-
url = `https://${host}/${repo}`;
|
|
492
|
+
url = resolvePlatformRepoUrl(cwd, repo, provider);
|
|
389
493
|
}
|
|
390
494
|
catch {
|
|
391
495
|
return {
|
|
392
496
|
tag: null,
|
|
497
|
+
url: null,
|
|
498
|
+
outcome: 'unreachable',
|
|
393
499
|
warnings: [
|
|
394
500
|
bound('platform.tag could not be resolved — the tag lookup failed or was unreachable; the platform is unpinned.'),
|
|
395
501
|
],
|
|
@@ -400,11 +506,14 @@ function resolveLatestPlatformTag(cwd, repo, provider, opts) {
|
|
|
400
506
|
return cached;
|
|
401
507
|
let result;
|
|
402
508
|
try {
|
|
403
|
-
|
|
509
|
+
const tag = pickLatestStableTag(listStableTagsAt(url, opts));
|
|
510
|
+
result = tag ? { tag, url, outcome: 'resolved', warnings: [] } : { tag: null, url, outcome: 'tagless', warnings: [] };
|
|
404
511
|
}
|
|
405
512
|
catch {
|
|
406
513
|
result = {
|
|
407
514
|
tag: null,
|
|
515
|
+
url,
|
|
516
|
+
outcome: 'unreachable',
|
|
408
517
|
warnings: [
|
|
409
518
|
bound('platform.tag could not be resolved — the tag lookup failed or was unreachable; the platform is unpinned.'),
|
|
410
519
|
],
|
|
@@ -413,44 +522,6 @@ function resolveLatestPlatformTag(cwd, repo, provider, opts) {
|
|
|
413
522
|
platformTagMemo.set(url, result);
|
|
414
523
|
return result;
|
|
415
524
|
}
|
|
416
|
-
/**
|
|
417
|
-
* List the stable `vX.Y.Z` tags at `url`, never shell-eval'd (T-34-25):
|
|
418
|
-
* `git ls-remote` is spawned with an argv array, bounded by
|
|
419
|
-
* `timeout`/`maxBuffer`/`windowsHide` (T-34-26), with the child's stderr piped
|
|
420
|
-
* and discarded. `--` ends the options, so a flag-shaped target can never be
|
|
421
|
-
* read as one. Filtered with `isStableTripletSemver`, the single blessed
|
|
422
|
-
* predicate. THROWS on any git fault — callers own the degrade policy. Shared
|
|
423
|
-
* by the platform-tag lookup and `vault update`.
|
|
424
|
-
*/
|
|
425
|
-
function listStableTagsAt(url, opts = {}) {
|
|
426
|
-
const lsOutput = typeof opts._lsRemote === 'function'
|
|
427
|
-
? opts._lsRemote(url)
|
|
428
|
-
: node_child_process_1.default.execFileSync('git', ['ls-remote', '--tags', '--refs', '--sort=-v:refname', '--', url, 'v*'], {
|
|
429
|
-
encoding: 'utf-8',
|
|
430
|
-
timeout: 10_000,
|
|
431
|
-
maxBuffer: 512 * 1024,
|
|
432
|
-
windowsHide: true,
|
|
433
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
434
|
-
});
|
|
435
|
-
return lsOutput
|
|
436
|
-
.split('\n')
|
|
437
|
-
.map((line) => {
|
|
438
|
-
const m = line.match(/\brefs\/tags\/([^\s^]+)$/);
|
|
439
|
-
return m ? m[1].trim() : '';
|
|
440
|
-
})
|
|
441
|
-
.filter((t) => t && (0, semver_compare_cjs_1.isStableTripletSemver)(t));
|
|
442
|
-
}
|
|
443
|
-
/** The highest tag by `compareSemverCore`, or `null` for an empty list. */
|
|
444
|
-
function pickLatestStableTag(tags) {
|
|
445
|
-
if (tags.length === 0)
|
|
446
|
-
return null;
|
|
447
|
-
let latest = tags[0];
|
|
448
|
-
for (const t of tags) {
|
|
449
|
-
if ((0, semver_compare_cjs_1.compareSemverCore)(t, latest) > 0)
|
|
450
|
-
latest = t;
|
|
451
|
-
}
|
|
452
|
-
return latest;
|
|
453
|
-
}
|
|
454
525
|
/**
|
|
455
526
|
* Read the first readable, semver-shaped `VERSION` file among the candidate
|
|
456
527
|
* locations resolved against `baseDir`, returning `<binName>@<version>`.
|
|
@@ -547,6 +618,25 @@ function composeCandidate(cwd, seed, provider, opts) {
|
|
|
547
618
|
setField('client.vaultRepo', seed.clientVaultRepo, 'profile-seed');
|
|
548
619
|
setField('client.vaultRef', seed.clientVaultRef, 'profile-seed');
|
|
549
620
|
setField('client.vaultHost', seed.clientVaultHost, 'profile-seed');
|
|
621
|
+
// client.vaultRef (resolved-tag) — FORK-ONLY (PBI 74316). When the seed
|
|
622
|
+
// names a vault repo but no ref, propose the repo's latest stable tag.
|
|
623
|
+
if (isUsableString(seed.clientVaultRepo) && !isUsableString(seed.clientVaultRef)) {
|
|
624
|
+
if (opts.offline === true) {
|
|
625
|
+
pushDeduped(warnings, [
|
|
626
|
+
bound('client.vaultRef was not proposed (offline) — the vault resolves "main" until the project is pinned with "pp-tools vault update <tag>".'),
|
|
627
|
+
]);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const vaultHost = isUsableString(seed.clientVaultHost)
|
|
631
|
+
? seed.clientVaultHost
|
|
632
|
+
: (hostRes.host ?? (0, vault_coordinates_cjs_1.defaultHostForProvider)(provider));
|
|
633
|
+
const vaultTag = resolveLatestVaultTag(seed.clientVaultRepo, vaultHost, provider, opts);
|
|
634
|
+
pushDeduped(warnings, vaultTag.warnings);
|
|
635
|
+
if (vaultTag.tag) {
|
|
636
|
+
setField('client.vaultRef', vaultTag.tag, 'resolved-tag');
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
550
640
|
// project.name / project.slug (project-answer)
|
|
551
641
|
setField('project.name', cfgGet(cwd, 'project.name', undefined), 'project-answer');
|
|
552
642
|
setField('project.slug', cfgGet(cwd, 'project.slug', undefined), 'project-answer');
|
|
@@ -810,6 +900,41 @@ function composeBindings(cwd, opts = {}) {
|
|
|
810
900
|
};
|
|
811
901
|
}
|
|
812
902
|
}
|
|
903
|
+
// ─── Emission projection (VAULT-EGRESS-1) ─────────────────────────────────────
|
|
904
|
+
/** The seed short names whose bindings field is a coordinate — DERIVED from
|
|
905
|
+
* `SEED_KEY_SHORT_NAMES` and the coordinate field list, never retyped. */
|
|
906
|
+
const SEED_COORDINATE_SHORT_NAMES = Object.freeze(Object.entries(SEED_KEY_SHORT_NAMES)
|
|
907
|
+
.filter(([seedKey]) => platform_bindings_cjs_1.BINDINGS_COORDINATE_FIELDS.includes(seedKey.slice(org_profile_cjs_1.ORG_PROFILE_SEED_SECTION.length + 1)))
|
|
908
|
+
.map(([, short]) => short));
|
|
909
|
+
/**
|
|
910
|
+
* VAULT-EGRESS-1 (quick 261005-8py gap closure): the display-safe copy of a
|
|
911
|
+
* compose result, for EMISSION only. Every place a coordinate field can carry
|
|
912
|
+
* a value — `composed`, `seed`, each `provenance[].value` and each
|
|
913
|
+
* `drift[].profileValue/effectiveValue` — goes through the one owner. The
|
|
914
|
+
* comparisons that decided `drift` and `action` already ran on the RAW values
|
|
915
|
+
* inside `composeBindings`, and `composeBindings(..., { write: true })` writes
|
|
916
|
+
* the RAW composed object; neither path ever sees this projection.
|
|
917
|
+
*/
|
|
918
|
+
function redactComposeResultForEmission(result) {
|
|
919
|
+
const seed = result.seed === undefined ? undefined : { ...result.seed };
|
|
920
|
+
if (seed !== undefined) {
|
|
921
|
+
for (const short of SEED_COORDINATE_SHORT_NAMES) {
|
|
922
|
+
if (typeof seed[short] === 'string')
|
|
923
|
+
seed[short] = (0, platform_bindings_cjs_1.redactCoordinateForEmission)(seed[short]);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
...result,
|
|
928
|
+
...(seed === undefined ? {} : { seed }),
|
|
929
|
+
composed: result.composed === null ? null : (0, platform_bindings_cjs_1.redactBindingsForEmission)(result.composed),
|
|
930
|
+
provenance: result.provenance.map((e) => ({ ...e, value: (0, platform_bindings_cjs_1.redactBindingsField)(e.field, e.value) })),
|
|
931
|
+
drift: result.drift.map((d) => ({
|
|
932
|
+
...d,
|
|
933
|
+
profileValue: (0, platform_bindings_cjs_1.redactBindingsField)(d.key, d.profileValue),
|
|
934
|
+
effectiveValue: (0, platform_bindings_cjs_1.redactBindingsField)(d.key, d.effectiveValue),
|
|
935
|
+
})),
|
|
936
|
+
};
|
|
937
|
+
}
|
|
813
938
|
// ─── cmdComposeBindings ───────────────────────────────────────────────────────
|
|
814
939
|
/**
|
|
815
940
|
* CLI adapter for `pp-tools query compose-bindings`. Resolves the project
|
|
@@ -817,39 +942,6 @@ function composeBindings(cwd, opts = {}) {
|
|
|
817
942
|
* does, then calls `composeBindings` and emits the result as data — a
|
|
818
943
|
* verdict is never a process error.
|
|
819
944
|
*/
|
|
820
|
-
/**
|
|
821
|
-
* Pack 6 D-07: the display-safe copy of a compose result, for EMISSION only.
|
|
822
|
-
* Every remote coordinate field (`platform.repo`, `client.vaultRepo`) in
|
|
823
|
-
* `composed`, `seed`, `provenance` and `drift` goes through the one owner
|
|
824
|
-
* (`redactCloneTarget`, src/vault.cts); warnings go through the prose
|
|
825
|
-
* redactor. `composeBindings` itself, its drift/action comparison and every
|
|
826
|
-
* write keep the RAW values — never write or compare the result of this.
|
|
827
|
-
*/
|
|
828
|
-
function redactComposeResultForEmission(result) {
|
|
829
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
830
|
-
const v = require('./vault.cjs');
|
|
831
|
-
const COORD = new Set(['platform.repo', 'client.vaultRepo']);
|
|
832
|
-
const safe = (x) => (typeof x === 'string' ? v.redactCloneTarget(x) : x);
|
|
833
|
-
const out = JSON.parse(JSON.stringify(result));
|
|
834
|
-
if (out.composed && typeof out.composed === 'object') {
|
|
835
|
-
for (const field of COORD) {
|
|
836
|
-
const [ns, key] = field.split('.');
|
|
837
|
-
const parent = out.composed[ns];
|
|
838
|
-
if (parent && typeof parent === 'object' && typeof parent[key] === 'string')
|
|
839
|
-
parent[key] = safe(parent[key]);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
if (out.seed) {
|
|
843
|
-
if (typeof out.seed.platformRepo === 'string')
|
|
844
|
-
out.seed.platformRepo = safe(out.seed.platformRepo);
|
|
845
|
-
if (typeof out.seed.clientVaultRepo === 'string')
|
|
846
|
-
out.seed.clientVaultRepo = safe(out.seed.clientVaultRepo);
|
|
847
|
-
}
|
|
848
|
-
out.provenance = (out.provenance ?? []).map((p) => (COORD.has(p.field) ? { ...p, value: safe(p.value) } : p));
|
|
849
|
-
out.drift = (out.drift ?? []).map((d) => COORD.has(d.key) ? { ...d, profileValue: safe(d.profileValue), effectiveValue: safe(d.effectiveValue) } : d);
|
|
850
|
-
out.warnings = (out.warnings ?? []).map((w) => (typeof w === 'string' ? v.redactRemoteText(w) : w));
|
|
851
|
-
return out;
|
|
852
|
-
}
|
|
853
945
|
function cmdComposeBindings(cwd, options, raw) {
|
|
854
946
|
let root;
|
|
855
947
|
try {
|
|
@@ -861,3 +953,74 @@ function cmdComposeBindings(cwd, options, raw) {
|
|
|
861
953
|
const result = composeBindings(root, { write: options.write === true, offline: options.offline === true });
|
|
862
954
|
output(redactComposeResultForEmission(result), raw);
|
|
863
955
|
}
|
|
956
|
+
// ─── cmdPlatformLatestTag ─────────────────────────────────────────────────────
|
|
957
|
+
/**
|
|
958
|
+
* CLI adapter for `pp-tools query platform-latest-tag` (quick 261003-my0).
|
|
959
|
+
* Its whole job is to run the owner — `resolveLatestPlatformTag`, the same
|
|
960
|
+
* lookup `pp-tools init` and `check ship.platform-drift` use — and print
|
|
961
|
+
* `{ present, pinned, repo, provider, url, latest, outcome }`, so a shell
|
|
962
|
+
* preflight (init.md / new-project.md Step 0) reads `--pick latest` instead
|
|
963
|
+
* of composing a URL of its own.
|
|
964
|
+
*
|
|
965
|
+
* Absent bindings → `{ present: false }` with NO network call (the parity
|
|
966
|
+
* `--help` smoke runs from a repo root that has no bindings file). Malformed
|
|
967
|
+
* bindings → `outcome: 'malformed'` and no lookup — inert data, never
|
|
968
|
+
* `error()`, the same posture as the drift check's malformed arm. No
|
|
969
|
+
* `platform.repo` → `outcome: 'no-repo'`. `args` is accepted for the
|
|
970
|
+
* dispatcher's uniform shape; no flags are read (`--pick`/`--raw`/`--cwd`
|
|
971
|
+
* are stripped before dispatch, and `--pick` prints `''` for a null field —
|
|
972
|
+
* the shell contract). The owner's warnings are init-time prose and are not
|
|
973
|
+
* forwarded.
|
|
974
|
+
*/
|
|
975
|
+
function cmdPlatformLatestTag(cwd, args, raw) {
|
|
976
|
+
void args;
|
|
977
|
+
let root;
|
|
978
|
+
try {
|
|
979
|
+
root = (0, project_root_cjs_1.findProjectRoot)(cwd) || cwd;
|
|
980
|
+
}
|
|
981
|
+
catch {
|
|
982
|
+
root = cwd;
|
|
983
|
+
}
|
|
984
|
+
const bindingsPath = node_path_1.default.join(root, '.platform-bindings.json');
|
|
985
|
+
if (!node_fs_1.default.existsSync(bindingsPath)) {
|
|
986
|
+
output({ present: false }, raw);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
let provider;
|
|
990
|
+
try {
|
|
991
|
+
provider = (0, git_provider_cjs_1.resolveProvider)(root).provider;
|
|
992
|
+
}
|
|
993
|
+
catch {
|
|
994
|
+
provider = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
995
|
+
}
|
|
996
|
+
let parsed;
|
|
997
|
+
try {
|
|
998
|
+
parsed = JSON.parse(node_fs_1.default.readFileSync(bindingsPath, 'utf-8'));
|
|
999
|
+
}
|
|
1000
|
+
catch {
|
|
1001
|
+
parsed = null;
|
|
1002
|
+
}
|
|
1003
|
+
if (!isObject(parsed)) {
|
|
1004
|
+
output({ present: true, pinned: null, repo: null, provider, url: null, latest: null, outcome: 'malformed' }, raw);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const platform = isObject(parsed.platform) ? parsed.platform : null;
|
|
1008
|
+
const trimmedOrNull = (v) => typeof v === 'string' && v.trim() !== '' ? v.trim() : null;
|
|
1009
|
+
const pinned = trimmedOrNull(platform?.tag);
|
|
1010
|
+
const repo = trimmedOrNull(platform?.repo);
|
|
1011
|
+
if (repo === null) {
|
|
1012
|
+
output({ present: true, pinned, repo: null, provider, url: null, latest: null, outcome: 'no-repo' }, raw);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const result = resolveLatestPlatformTag(root, repo, provider, {});
|
|
1016
|
+
// VAULT-EGRESS-1: `repo` and `url` are display-safe; the lookup above used the raw repo.
|
|
1017
|
+
output({
|
|
1018
|
+
present: true,
|
|
1019
|
+
pinned,
|
|
1020
|
+
repo: (0, platform_bindings_cjs_1.redactCoordinateForEmission)(repo),
|
|
1021
|
+
provider,
|
|
1022
|
+
url: (0, platform_bindings_cjs_1.redactCoordinateForEmission)(result.url),
|
|
1023
|
+
latest: result.tag,
|
|
1024
|
+
outcome: result.outcome,
|
|
1025
|
+
}, raw);
|
|
1026
|
+
}
|