@unbrained/pm-cli 2026.8.28 → 2026.8.30
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/marketplace.json +2 -2
- package/CHANGELOG.md +30 -0
- package/dist/cli-bundle/bundle-manifest.json +125 -125
- package/dist/cli-bundle/chunks/{chunk-E73FDIWT.js → chunk-2MKLQL2E.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NTXZHRKA.js → chunk-2UETTHY6.js} +46 -46
- package/dist/cli-bundle/chunks/{chunk-FEVBFFCQ.js → chunk-3TEF3HM5.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BY2FQ2NI.js → chunk-7SKMLSMX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-7X6OZEIV.js +34 -0
- package/dist/cli-bundle/chunks/{chunk-M7OXRQE3.js → chunk-CMIEDWAY.js} +50 -50
- package/dist/cli-bundle/chunks/{chunk-XWEQGHHG.js → chunk-GNWPTEBW.js} +64 -61
- package/dist/cli-bundle/chunks/{chunk-X2RROGZE.js → chunk-MTTKAUBX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-N6OR6FAR.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-NBCBFVZI.js → chunk-TGNAA66W.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QE6WQXFO.js → chunk-WS2GJQVG.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-J35ZPQQ5.js → register-list-query-OMKJ5ONN.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-J6XJJOGU.js → register-mutation-QBSNCOY3.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-AE3JEMFT.js → register-operations-UC77FQZC.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-OQERLLWE.js → register-setup-GLQ5SYV3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HC7ODMH3.js → chunk-3AVAWVZA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3UMBF26I.js +158 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YVVZ3LQ6.js → chunk-73EDXGYE.js} +16 -15
- package/dist/cli-bundle/focused-chunks/chunk-AXZ5AJAF.js +50 -0
- package/dist/cli-bundle/focused-chunks/{chunk-AHAM2HAU.js → chunk-G6PATS5E.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-RKOKG43G.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-TDK3OCX3.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-FC2AXLB5.js → chunk-XXOWRJZ5.js} +2 -2
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -32
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +34 -32
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +8 -8
- package/dist/core/diagnostics/remediation.js +16 -6
- package/dist/core/history/drift-scan.d.ts +2 -0
- package/dist/core/history/drift-scan.js +72 -36
- package/dist/core/history/history.d.ts +3 -3
- package/dist/core/history/history.js +5 -5
- package/dist/core/history/replay.d.ts +1 -1
- package/dist/core/history/replay.js +50 -19
- package/dist/core/item/actionability.d.ts +7 -1
- package/dist/core/item/actionability.js +44 -11
- package/dist/core/item/dependency-reference.d.ts +10 -0
- package/dist/core/item/dependency-reference.js +21 -0
- package/dist/core/item/parse.d.ts +4 -0
- package/dist/core/item/parse.js +38 -16
- package/dist/core/shared/author.js +69 -17
- package/dist/sdk/dependency-provenance.d.ts +51 -2
- package/dist/sdk/dependency-provenance.js +91 -7
- package/dist/sdk/extension/install-sources.d.ts +1 -7
- package/dist/sdk/extension/install-sources.js +94 -33
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +50 -14
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -26
- package/dist/sdk/governance/health.js +86 -22
- package/dist/sdk/governance/validate-history-drift.js +5 -2
- package/dist/sdk/graph/assembly.js +51 -22
- package/dist/sdk/history-repair.d.ts +1 -1
- package/dist/sdk/history-repair.js +20 -10
- package/dist/sdk/lifecycle/create.js +43 -19
- package/dist/sdk/linked-artifacts.js +21 -31
- package/dist/sdk/merge/reconcile.js +3 -16
- package/dist/sdk/query/next.d.ts +6 -0
- package/dist/sdk/query/next.js +7 -2
- package/dist/sdk/test/entry.d.ts +1 -1
- package/dist/sdk/test/entry.js +5 -9
- package/dist/sdk/test/execution.d.ts +3 -0
- package/dist/sdk/test/execution.js +54 -13
- package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +67 -0
- package/docs/MERGE_SAFETY.md +23 -10
- package/docs/README.md +1 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +10 -4
- package/marketplace.json +2 -2
- package/package.json +8 -8
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/sdk/public-surface.json +100 -11
- package/dist/cli-bundle/chunks/chunk-TIQ6AMH2.js +0 -13
- package/dist/cli-bundle/chunks/chunk-XRVVYRRO.js +0 -35
- package/dist/cli-bundle/focused-chunks/chunk-4ZDRZYYJ.js +0 -50
- package/dist/cli-bundle/focused-chunks/chunk-XDPYBQCF.js +0 -156
- package/dist/cli-bundle/focused-chunks/chunk-Y5A7SJJ7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-YJLDHJOD.js +0 -2
package/dist/core/item/parse.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Defines item parsing, formatting, and lifecycle helpers for Parse.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="80965b57-856c-5fef-a372-0c9f3766ada5")}catch(e){}}();
|
|
8
8
|
import { PmCliError } from "../shared/errors.js";
|
|
9
9
|
import { EXIT_CODE } from "../shared/constants.js";
|
|
10
10
|
const STDIN_TOKEN = "-";
|
|
@@ -30,6 +30,16 @@ const CONTINUABLE_VALUE_KEYS = new Set([
|
|
|
30
30
|
"value",
|
|
31
31
|
"why_now",
|
|
32
32
|
]);
|
|
33
|
+
/** Classify plaintext as structured key/value input using an explicit accepted-key vocabulary. */
|
|
34
|
+
export function looksLikeStructuredKeyValueEntry(raw, keys) {
|
|
35
|
+
if (raw.startsWith("```") || raw.includes("\n")) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const keyPattern = keys
|
|
39
|
+
.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
40
|
+
.join("|");
|
|
41
|
+
return new RegExp(`^(?:[-*+]\\s+)?(?:${keyPattern})\\s*[:=]`, "i").test(raw);
|
|
42
|
+
}
|
|
33
43
|
/** Implements parse tags for the public runtime surface of this module. */
|
|
34
44
|
export function parseTags(raw) {
|
|
35
45
|
const trimmed = raw.trim();
|
|
@@ -188,16 +198,16 @@ function splitCsvSegments(raw) {
|
|
|
188
198
|
const segments = [];
|
|
189
199
|
let current = "";
|
|
190
200
|
let inQuotes = false;
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
current +=
|
|
195
|
-
|
|
201
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
202
|
+
const char = raw[index];
|
|
203
|
+
if (char === "\\" && raw[index + 1] === ",") {
|
|
204
|
+
current += ",";
|
|
205
|
+
index += 1;
|
|
196
206
|
continue;
|
|
197
207
|
}
|
|
198
|
-
if (char === "\\") {
|
|
199
|
-
current += char
|
|
200
|
-
|
|
208
|
+
if (char === "\\" && raw[index + 1] !== undefined) {
|
|
209
|
+
current += `${char}${raw[index + 1]}`;
|
|
210
|
+
index += 1;
|
|
201
211
|
continue;
|
|
202
212
|
}
|
|
203
213
|
if (char === '"') {
|
|
@@ -206,14 +216,14 @@ function splitCsvSegments(raw) {
|
|
|
206
216
|
continue;
|
|
207
217
|
}
|
|
208
218
|
if (char === "," && !inQuotes) {
|
|
209
|
-
segments.push(current
|
|
219
|
+
segments.push(current);
|
|
210
220
|
current = "";
|
|
211
221
|
continue;
|
|
212
222
|
}
|
|
213
223
|
current += char;
|
|
214
224
|
}
|
|
215
225
|
if (current.trim()) {
|
|
216
|
-
segments.push(current
|
|
226
|
+
segments.push(current);
|
|
217
227
|
}
|
|
218
228
|
return segments;
|
|
219
229
|
}
|
|
@@ -235,8 +245,8 @@ function unquoteValue(value) {
|
|
|
235
245
|
function findKeyValueDelimiter(segment) {
|
|
236
246
|
const equalsIndex = segment.indexOf("=");
|
|
237
247
|
const colonIndex = segment.indexOf(":");
|
|
238
|
-
const hasEquals = equalsIndex
|
|
239
|
-
const hasColon = colonIndex
|
|
248
|
+
const hasEquals = equalsIndex >= 0;
|
|
249
|
+
const hasColon = colonIndex >= 0;
|
|
240
250
|
if (hasEquals && hasColon) {
|
|
241
251
|
return Math.min(equalsIndex, colonIndex);
|
|
242
252
|
}
|
|
@@ -324,15 +334,18 @@ export function parseCsvKv(raw, optionName) {
|
|
|
324
334
|
const segments = splitCsvSegments(trimmed);
|
|
325
335
|
for (const segment of segments) {
|
|
326
336
|
const delimiterIndex = findKeyValueDelimiter(segment);
|
|
327
|
-
if (delimiterIndex
|
|
337
|
+
if (delimiterIndex >= 0) {
|
|
328
338
|
const key = segment.slice(0, delimiterIndex).trim();
|
|
339
|
+
if (!key) {
|
|
340
|
+
throw new PmCliError(buildInvalidKvMessage(raw, optionName), EXIT_CODE.USAGE);
|
|
341
|
+
}
|
|
329
342
|
const value = unquoteValue(segment.slice(delimiterIndex + 1).trim());
|
|
330
343
|
result[key] = value;
|
|
331
344
|
activeKey = key;
|
|
332
345
|
continue;
|
|
333
346
|
}
|
|
334
347
|
if (activeKey && CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())) {
|
|
335
|
-
result[activeKey] = `${result[activeKey]},${segment
|
|
348
|
+
result[activeKey] = `${result[activeKey]},${segment}`;
|
|
336
349
|
continue;
|
|
337
350
|
}
|
|
338
351
|
throw new PmCliError(buildInvalidKvMessage(raw, optionName), EXIT_CODE.USAGE);
|
|
@@ -358,6 +371,15 @@ export function looksLikeGenericKeyValueEntry(raw) {
|
|
|
358
371
|
}
|
|
359
372
|
return GENERIC_LEADING_KV_KEY_PATTERN.test(trimmed);
|
|
360
373
|
}
|
|
374
|
+
/** Refuse a comma-bearing bare value whose meaning depends on guessing whether the comma separates values or belongs to one value. */
|
|
375
|
+
export function assertNoAmbiguousBareCommaEntry(raw, optionName, noun, structured) {
|
|
376
|
+
if (structured || !raw.includes(","))
|
|
377
|
+
return;
|
|
378
|
+
throw new PmCliError(`${optionName} received an ambiguous comma-bearing bare ${noun}. Repeat ${optionName} for multiple values, or quote or escape literal commas in a structured key=<value> entry.`, EXIT_CODE.USAGE, {
|
|
379
|
+
code: "bare_comma_entry_ambiguous",
|
|
380
|
+
required: `Use one ${optionName} per bare ${noun}, or quote or escape literal commas in a structured key=<value> entry.`,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
361
383
|
/**
|
|
362
384
|
* Reject any key in a parsed CSV/markdown key/value map that is not part of the
|
|
363
385
|
* caller's allowed-key contract, mirroring the strict `test --add` behavior
|
|
@@ -579,4 +601,4 @@ export const _testOnly = {
|
|
|
579
601
|
parseMarkdownKeyValueLines,
|
|
580
602
|
};
|
|
581
603
|
//# sourceMappingURL=parse.js.map
|
|
582
|
-
//# debugId=
|
|
604
|
+
//# debugId=80965b57-856c-5fef-a372-0c9f3766ada5
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides shared primitives and utilities for Author.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="cf550961-0040-5e04-9d8d-16e316027e43")}catch(e){}}();
|
|
8
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
import fs from "node:fs";
|
|
@@ -312,20 +312,50 @@ const MAX_PROVENANCE_PROBE_BYTES = 1_048_576;
|
|
|
312
312
|
const MAX_CODEX_PROVENANCE_PROBE_BYTES = 4_194_304;
|
|
313
313
|
const codexProvenanceSnapshotCache = new Map();
|
|
314
314
|
const EMPTY_CODEX_PROVENANCE_SNAPSHOT = Object.freeze({});
|
|
315
|
-
|
|
315
|
+
const claudeProvenanceSnapshotCache = new Map();
|
|
316
|
+
const EMPTY_CLAUDE_PROVENANCE_SNAPSHOT = Object.freeze({});
|
|
317
|
+
function asProvenanceRecord(value) {
|
|
318
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
319
|
+
? value
|
|
320
|
+
: undefined;
|
|
321
|
+
}
|
|
322
|
+
function parseClaudeProvenanceLine(line) {
|
|
316
323
|
let parsed;
|
|
317
324
|
try {
|
|
318
325
|
parsed = JSON.parse(line);
|
|
319
326
|
}
|
|
320
327
|
catch {
|
|
321
|
-
return
|
|
328
|
+
return EMPTY_CLAUDE_PROVENANCE_SNAPSHOT;
|
|
322
329
|
}
|
|
323
|
-
const record = parsed;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
330
|
+
const record = asProvenanceRecord(parsed);
|
|
331
|
+
if (!record)
|
|
332
|
+
return EMPTY_CLAUDE_PROVENANCE_SNAPSHOT;
|
|
333
|
+
const queue = [
|
|
334
|
+
{ value: record, depth: 0 },
|
|
335
|
+
];
|
|
336
|
+
let visited = 0;
|
|
337
|
+
let model;
|
|
338
|
+
let version;
|
|
339
|
+
while (queue.length > 0 && visited < 64) {
|
|
340
|
+
const current = queue.shift();
|
|
341
|
+
visited += 1;
|
|
342
|
+
model ??= nonBlank(current.value.model)?.slice(0, 256);
|
|
343
|
+
version ??= nonBlank(current.value.version)?.slice(0, 256);
|
|
344
|
+
if (model && version)
|
|
345
|
+
break;
|
|
346
|
+
if (current.depth >= 4)
|
|
347
|
+
continue;
|
|
348
|
+
for (const value of Object.values(current.value)) {
|
|
349
|
+
const nested = asProvenanceRecord(value);
|
|
350
|
+
if (nested) {
|
|
351
|
+
queue.push({
|
|
352
|
+
value: nested,
|
|
353
|
+
depth: current.depth + 1,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return Object.freeze({ model, version });
|
|
329
359
|
}
|
|
330
360
|
function readClaudeSessionProvenance(dimension, signals, env) {
|
|
331
361
|
const session = nonBlank(env.CLAUDE_CODE_SESSION_ID);
|
|
@@ -337,18 +367,40 @@ function readClaudeSessionProvenance(dimension, signals, env) {
|
|
|
337
367
|
try {
|
|
338
368
|
const file = fs.openSync(sessionPath, "r");
|
|
339
369
|
try {
|
|
340
|
-
const
|
|
341
|
-
const
|
|
370
|
+
const stats = fs.fstatSync(file);
|
|
371
|
+
const cacheKey = `${sessionPath}\u0000${stats.size}\u0000${stats.mtimeMs}`;
|
|
372
|
+
const cached = claudeProvenanceSnapshotCache.get(cacheKey);
|
|
373
|
+
if (cached)
|
|
374
|
+
return cached[dimension];
|
|
375
|
+
const probeLength = Math.min(stats.size, MAX_PROVENANCE_PROBE_BYTES);
|
|
376
|
+
const headLength = Math.min(probeLength, Math.ceil(probeLength / 2));
|
|
377
|
+
const tailLength = Math.min(probeLength - headLength, stats.size - headLength);
|
|
378
|
+
const head = Buffer.alloc(headLength);
|
|
379
|
+
fs.readSync(file, head, 0, headLength, 0);
|
|
342
380
|
const tail = Buffer.alloc(tailLength);
|
|
343
|
-
|
|
344
|
-
|
|
381
|
+
if (tailLength > 0) {
|
|
382
|
+
fs.readSync(file, tail, 0, tailLength, stats.size - tailLength);
|
|
383
|
+
}
|
|
384
|
+
const snapshot = {};
|
|
385
|
+
const lines = stats.size <= probeLength
|
|
386
|
+
? Buffer.concat([head, tail]).toString("utf8").split("\n")
|
|
387
|
+
: [
|
|
388
|
+
...head.toString("utf8").split("\n").slice(0, 4_096),
|
|
389
|
+
...tail.toString("utf8").split("\n").slice(-4_096),
|
|
390
|
+
];
|
|
345
391
|
for (const line of lines) {
|
|
346
392
|
if (line.length === 0 || line.length > 262_144)
|
|
347
393
|
continue;
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
394
|
+
const parsed = parseClaudeProvenanceLine(line);
|
|
395
|
+
snapshot.model ??= parsed.model;
|
|
396
|
+
snapshot.version ??= parsed.version;
|
|
397
|
+
if (snapshot.model && snapshot.version)
|
|
398
|
+
break;
|
|
351
399
|
}
|
|
400
|
+
const frozen = Object.freeze(snapshot);
|
|
401
|
+
claudeProvenanceSnapshotCache.clear();
|
|
402
|
+
claudeProvenanceSnapshotCache.set(cacheKey, frozen);
|
|
403
|
+
return frozen[dimension];
|
|
352
404
|
}
|
|
353
405
|
finally {
|
|
354
406
|
fs.closeSync(file);
|
|
@@ -1030,4 +1082,4 @@ export function resolveAuthor(candidate, fallback) {
|
|
|
1030
1082
|
return identity.author;
|
|
1031
1083
|
}
|
|
1032
1084
|
//# sourceMappingURL=author.js.map
|
|
1033
|
-
//# debugId=
|
|
1085
|
+
//# debugId=cf550961-0040-5e04-9d8d-16e316027e43
|
|
@@ -1,9 +1,58 @@
|
|
|
1
|
+
export { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
|
|
1
2
|
/** Provenance value declaring that a dependency id belongs to another workspace. */
|
|
2
3
|
export declare const EXTERNAL_DEPENDENCY_SOURCE_KIND = "global";
|
|
3
4
|
/** Human-facing alias accepted wherever external dependency provenance is parsed. */
|
|
4
5
|
export declare const EXTERNAL_DEPENDENCY_SOURCE_KIND_ALIAS = "external";
|
|
5
|
-
/**
|
|
6
|
-
export declare
|
|
6
|
+
/** Stable external lifecycle values understood without coupling core to a provider. */
|
|
7
|
+
export declare const EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES: readonly ["open", "closed", "unknown"];
|
|
8
|
+
/** External lifecycle value returned by a registered dependency resolver. */
|
|
9
|
+
export type ExternalDependencyResolutionStatus = (typeof EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES)[number];
|
|
10
|
+
/** Provider-owned external dependency data before the SDK applies output bounds. */
|
|
11
|
+
export interface ExternalDependencyResolverResult {
|
|
12
|
+
/** Provider lifecycle state normalized to the universal external status vocabulary. */
|
|
13
|
+
status: ExternalDependencyResolutionStatus;
|
|
14
|
+
/** Optional bounded human context for the remote dependency. */
|
|
15
|
+
title?: string;
|
|
16
|
+
/** Optional canonical provider URL or locator used as evidence. */
|
|
17
|
+
source?: string;
|
|
18
|
+
/** Optional provider observation timestamp; invalid timestamps are replaced by SDK time. */
|
|
19
|
+
checkedAt?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Package-provided resolver contract for one or more external dependency locator families. */
|
|
22
|
+
export interface ExternalDependencyResolver {
|
|
23
|
+
/** Stable resolver identity surfaced in agent-facing evidence. */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Return whether this resolver owns the supplied external locator. */
|
|
26
|
+
supports(reference: string): boolean;
|
|
27
|
+
/** Resolve the locator without exposing provider credentials or unbounded response data. */
|
|
28
|
+
resolve(reference: string): Promise<ExternalDependencyResolverResult | null>;
|
|
29
|
+
}
|
|
30
|
+
/** Bounded provider-neutral evidence returned by the public resolver registry. */
|
|
31
|
+
export interface ExternalDependencyResolution {
|
|
32
|
+
/** Original normalized dependency locator. */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Provider lifecycle state. */
|
|
35
|
+
status: ExternalDependencyResolutionStatus;
|
|
36
|
+
/** Whether trusted provider evidence proves the blocker terminal. */
|
|
37
|
+
resolved: boolean;
|
|
38
|
+
/** Bounded remote title, or null when the provider did not supply one. */
|
|
39
|
+
title: string | null;
|
|
40
|
+
/** Bounded evidence locator. */
|
|
41
|
+
source: string;
|
|
42
|
+
/** Observation timestamp supplied by the provider or SDK clock. */
|
|
43
|
+
checked_at: string;
|
|
44
|
+
/** Registered resolver identity that produced this evidence. */
|
|
45
|
+
resolver: string;
|
|
46
|
+
}
|
|
47
|
+
/** Deterministic clock override for external resolver acceptance and tests. */
|
|
48
|
+
export interface ExternalDependencyResolutionOptions {
|
|
49
|
+
/** Return the timestamp used when provider evidence omits or corrupts checkedAt. */
|
|
50
|
+
now?: () => string;
|
|
51
|
+
}
|
|
52
|
+
/** Register a package-owned external dependency resolver and return its idempotent disposer. */
|
|
53
|
+
export declare function registerExternalDependencyResolver(resolver: ExternalDependencyResolver): () => void;
|
|
54
|
+
/** Resolve one external locator through registered package providers with bounded provider-neutral evidence. */
|
|
55
|
+
export declare function resolveExternalDependencyReference(reference: string, options?: ExternalDependencyResolutionOptions): Promise<ExternalDependencyResolution | null>;
|
|
7
56
|
/** Canonicalize external provenance while preserving other named source kinds. */
|
|
8
57
|
export declare function normalizeDependencySourceKind(sourceKind: string | undefined): string | undefined;
|
|
9
58
|
/**
|
|
@@ -5,17 +5,101 @@
|
|
|
5
5
|
* items outside the current workspace.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f3f3beb2-1d60-52fe-88ce-1ab3cb9dba0d")}catch(e){}}();
|
|
9
9
|
import { normalizeItemId } from "../core/item/id.js";
|
|
10
|
+
import { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
|
|
11
|
+
export { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
|
|
10
12
|
/** Provenance value declaring that a dependency id belongs to another workspace. */
|
|
11
13
|
export const EXTERNAL_DEPENDENCY_SOURCE_KIND = "global";
|
|
12
14
|
/** Human-facing alias accepted wherever external dependency provenance is parsed. */
|
|
13
15
|
export const EXTERNAL_DEPENDENCY_SOURCE_KIND_ALIAS = "external";
|
|
14
|
-
/**
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
/** Stable external lifecycle values understood without coupling core to a provider. */
|
|
17
|
+
export const EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES = [
|
|
18
|
+
"open",
|
|
19
|
+
"closed",
|
|
20
|
+
"unknown",
|
|
21
|
+
];
|
|
22
|
+
const externalDependencyResolvers = new Map();
|
|
23
|
+
const MAX_EXTERNAL_TITLE_LENGTH = 240;
|
|
24
|
+
const MAX_EXTERNAL_SOURCE_LENGTH = 2_048;
|
|
25
|
+
/** Normalize and cap provider strings before they reach agent-facing output. */
|
|
26
|
+
function boundedExternalText(value, fallback, maximumLength) {
|
|
27
|
+
return (value?.trim() || fallback).slice(0, maximumLength);
|
|
28
|
+
}
|
|
29
|
+
/** Reject provider payloads that violate the runtime resolver-result contract. */
|
|
30
|
+
function isExternalDependencyResolverResult(value) {
|
|
31
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const candidate = value;
|
|
35
|
+
return (typeof candidate.status === "string" &&
|
|
36
|
+
(candidate.title == null || typeof candidate.title === "string") &&
|
|
37
|
+
(candidate.source == null || typeof candidate.source === "string") &&
|
|
38
|
+
(candidate.checkedAt == null || typeof candidate.checkedAt === "string"));
|
|
39
|
+
}
|
|
40
|
+
/** Register a package-owned external dependency resolver and return its idempotent disposer. */
|
|
41
|
+
export function registerExternalDependencyResolver(resolver) {
|
|
42
|
+
const name = resolver.name.trim();
|
|
43
|
+
if (name.length === 0) {
|
|
44
|
+
throw new Error("External dependency resolver name must not be empty");
|
|
45
|
+
}
|
|
46
|
+
if (externalDependencyResolvers.has(name)) {
|
|
47
|
+
throw new Error(`External dependency resolver already registered: ${name}`);
|
|
48
|
+
}
|
|
49
|
+
const registeredResolver = {
|
|
50
|
+
name,
|
|
51
|
+
supports: (reference) => resolver.supports(reference),
|
|
52
|
+
resolve: (reference) => resolver.resolve(reference),
|
|
53
|
+
};
|
|
54
|
+
externalDependencyResolvers.set(name, registeredResolver);
|
|
55
|
+
return () => {
|
|
56
|
+
if (externalDependencyResolvers.get(name) === registeredResolver) {
|
|
57
|
+
externalDependencyResolvers.delete(name);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Resolve one external locator through registered package providers with bounded provider-neutral evidence. */
|
|
62
|
+
export async function resolveExternalDependencyReference(reference, options = {}) {
|
|
63
|
+
const id = reference.trim();
|
|
64
|
+
if (!isExternalDependencyReference(id)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const resolvers = [...externalDependencyResolvers.values()].filter((resolver) => {
|
|
68
|
+
try {
|
|
69
|
+
return resolver.supports(id);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const outcomes = await Promise.allSettled(resolvers.map((resolver) => Promise.resolve().then(() => resolver.resolve(id))));
|
|
76
|
+
for (const [index, outcome] of outcomes.entries()) {
|
|
77
|
+
if (outcome.status !== "fulfilled" ||
|
|
78
|
+
!isExternalDependencyResolverResult(outcome.value)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const resolver = resolvers[index];
|
|
82
|
+
const candidate = outcome.value;
|
|
83
|
+
const status = EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES.includes(candidate.status)
|
|
84
|
+
? candidate.status
|
|
85
|
+
: "unknown";
|
|
86
|
+
const title = boundedExternalText(candidate.title, "", MAX_EXTERNAL_TITLE_LENGTH);
|
|
87
|
+
const source = boundedExternalText(candidate.source, id, MAX_EXTERNAL_SOURCE_LENGTH);
|
|
88
|
+
const observedAtMs = Date.parse(candidate.checkedAt?.trim() ?? "");
|
|
89
|
+
const checkedAt = Number.isFinite(observedAtMs)
|
|
90
|
+
? new Date(observedAtMs).toISOString()
|
|
91
|
+
: (options.now?.() ?? new Date().toISOString());
|
|
92
|
+
return {
|
|
93
|
+
id,
|
|
94
|
+
status,
|
|
95
|
+
resolved: status === "closed",
|
|
96
|
+
title: title.length > 0 ? title : null,
|
|
97
|
+
source,
|
|
98
|
+
checked_at: checkedAt,
|
|
99
|
+
resolver: resolver.name,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
19
103
|
}
|
|
20
104
|
/** Canonicalize external provenance while preserving other named source kinds. */
|
|
21
105
|
export function normalizeDependencySourceKind(sourceKind) {
|
|
@@ -39,4 +123,4 @@ export function normalizeDependencySeedId(id, prefix, sourceKind) {
|
|
|
39
123
|
: normalizeItemId(trimmed, prefix);
|
|
40
124
|
}
|
|
41
125
|
//# sourceMappingURL=dependency-provenance.js.map
|
|
42
|
-
//# debugId=
|
|
126
|
+
//# debugId=f3f3beb2-1d60-52fe-88ce-1ab3cb9dba0d
|
|
@@ -107,13 +107,7 @@ export declare function wrapNpmPackResolutionError(spec: string, error: unknown)
|
|
|
107
107
|
declare function resolveNpmPackSpec(spec: string): Promise<string>;
|
|
108
108
|
/** Implements normalize npm local file alias spec for the public runtime surface of this module. */
|
|
109
109
|
export declare function normalizeNpmLocalFileAliasSpec(spec: string, cwd?: string): string;
|
|
110
|
-
|
|
111
|
-
* Parse npm pack output and bind the reported artifact to its isolated target.
|
|
112
|
-
*
|
|
113
|
-
* Supports current JSON metadata and the legacy filename-only form while
|
|
114
|
-
* refusing absolute or traversing results before the filesystem is consulted.
|
|
115
|
-
*/
|
|
116
|
-
declare function parsePackedNpmPackage(stdout: string, packDirectory: string): {
|
|
110
|
+
declare function parsePackedNpmPackage(stdout: string, packDirectory: string, expectedPackage?: string): {
|
|
117
111
|
tarball: string;
|
|
118
112
|
package?: string;
|
|
119
113
|
version?: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements extension package-management support for Install Sources.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="05f56edb-c08d-570c-b8b5-641fc694331e")}catch(e){}}();
|
|
8
8
|
import { execFile } from "node:child_process";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import os from "node:os";
|
|
@@ -237,6 +237,14 @@ function npmPackageNameFromSpec(spec) {
|
|
|
237
237
|
const unscoped = withoutAlias.match(/^([^/@\s]+)/);
|
|
238
238
|
return unscoped?.[1] ?? withoutAlias;
|
|
239
239
|
}
|
|
240
|
+
/** Return whether an npm spec is a complete local-file alias, including unchanged UNC targets. */
|
|
241
|
+
function isNpmLocalFileAliasSpec(spec) {
|
|
242
|
+
const marker = "@file:";
|
|
243
|
+
const markerIndex = spec.lastIndexOf(marker);
|
|
244
|
+
return (markerIndex > 0 &&
|
|
245
|
+
spec.slice(0, markerIndex).trim().length > 0 &&
|
|
246
|
+
spec.slice(markerIndex + marker.length).trim().length > 0);
|
|
247
|
+
}
|
|
240
248
|
/** Implements check whether npm not found error for the public runtime surface of this module. */
|
|
241
249
|
export function isNpmNotFoundError(error) {
|
|
242
250
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -358,14 +366,11 @@ async function resolveNpmPackSpec(spec) {
|
|
|
358
366
|
export function normalizeNpmLocalFileAliasSpec(spec, cwd = process.cwd()) {
|
|
359
367
|
const marker = "@file:";
|
|
360
368
|
const markerIndex = spec.lastIndexOf(marker);
|
|
361
|
-
if (
|
|
369
|
+
if (!isNpmLocalFileAliasSpec(spec)) {
|
|
362
370
|
return spec;
|
|
363
371
|
}
|
|
364
372
|
const packageName = spec.slice(0, markerIndex);
|
|
365
373
|
const target = spec.slice(markerIndex + marker.length);
|
|
366
|
-
if (packageName.trim().length === 0 || target.trim().length === 0) {
|
|
367
|
-
return spec;
|
|
368
|
-
}
|
|
369
374
|
// `file://host/share` (exactly two leading slashes) is a UNC / network spec,
|
|
370
375
|
// not a local path — leave it for npm to resolve.
|
|
371
376
|
if (target.startsWith("//") && !target.startsWith("///")) {
|
|
@@ -403,29 +408,7 @@ export function normalizeNpmLocalFileAliasSpec(spec, cwd = process.cwd()) {
|
|
|
403
408
|
* Supports current JSON metadata and the legacy filename-only form while
|
|
404
409
|
* refusing absolute or traversing results before the filesystem is consulted.
|
|
405
410
|
*/
|
|
406
|
-
function
|
|
407
|
-
let parsed;
|
|
408
|
-
try {
|
|
409
|
-
parsed = JSON.parse(stdout);
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
// Fall back to the last stdout line for older npm output.
|
|
413
|
-
}
|
|
414
|
-
const first = Array.isArray(parsed)
|
|
415
|
-
? parsed[0]
|
|
416
|
-
: undefined;
|
|
417
|
-
const filename = first &&
|
|
418
|
-
typeof first.filename === "string" &&
|
|
419
|
-
first.filename.trim().length > 0
|
|
420
|
-
? first.filename
|
|
421
|
-
: stdout
|
|
422
|
-
.split(/\r?\n/)
|
|
423
|
-
.map((line) => line.trim())
|
|
424
|
-
.filter((line) => line.length > 0)
|
|
425
|
-
.at(-1);
|
|
426
|
-
if (!filename) {
|
|
427
|
-
throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
|
|
428
|
-
}
|
|
411
|
+
function resolvePackedTarball(packDirectory, filename) {
|
|
429
412
|
const tarball = path.resolve(packDirectory, filename);
|
|
430
413
|
const relativeTarball = path.relative(packDirectory, tarball);
|
|
431
414
|
if (relativeTarball.length === 0 ||
|
|
@@ -438,10 +421,86 @@ function parsePackedNpmPackage(stdout, packDirectory) {
|
|
|
438
421
|
why: "pm will not read a package-manager artifact outside the isolated directory created for this install.",
|
|
439
422
|
});
|
|
440
423
|
}
|
|
424
|
+
return tarball;
|
|
425
|
+
}
|
|
426
|
+
function decodePackedNpmCandidate(value, key) {
|
|
427
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
430
|
+
const record = value;
|
|
431
|
+
if (typeof record.filename !== "string" ||
|
|
432
|
+
record.filename.trim().length === 0) {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
...(key === undefined ? {} : { key }),
|
|
437
|
+
filename: record.filename,
|
|
438
|
+
...(typeof record.name === "string" ? { name: record.name } : {}),
|
|
439
|
+
...(typeof record.version === "string" ? { version: record.version } : {}),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function collectPackedNpmCandidates(parsed) {
|
|
443
|
+
const entries = Array.isArray(parsed)
|
|
444
|
+
? parsed.map((value) => ({ value }))
|
|
445
|
+
: typeof parsed === "object" && parsed !== null
|
|
446
|
+
? Object.entries(parsed).map(([key, value]) => ({ key, value }))
|
|
447
|
+
: [];
|
|
448
|
+
return entries.flatMap(({ key, value }) => {
|
|
449
|
+
const candidate = decodePackedNpmCandidate(value, key);
|
|
450
|
+
return candidate ? [candidate] : [];
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
function selectPackedNpmCandidate(candidates, expectedPackage) {
|
|
454
|
+
const normalizedExpected = expectedPackage?.trim().toLowerCase();
|
|
455
|
+
const matchingCandidates = normalizedExpected
|
|
456
|
+
? candidates.filter((candidate) => {
|
|
457
|
+
const normalizedName = candidate.name?.trim().toLowerCase();
|
|
458
|
+
return normalizedName !== undefined
|
|
459
|
+
? normalizedName === normalizedExpected
|
|
460
|
+
: candidate.key?.trim().toLowerCase() === normalizedExpected;
|
|
461
|
+
})
|
|
462
|
+
: [];
|
|
463
|
+
if (matchingCandidates.length === 1)
|
|
464
|
+
return matchingCandidates[0];
|
|
465
|
+
if (candidates.length !== 1)
|
|
466
|
+
return undefined;
|
|
467
|
+
const soleCandidate = candidates[0];
|
|
468
|
+
const soleIdentity = soleCandidate.name ?? soleCandidate.key;
|
|
469
|
+
return normalizedExpected &&
|
|
470
|
+
soleIdentity !== undefined &&
|
|
471
|
+
soleIdentity.trim().toLowerCase() !== normalizedExpected
|
|
472
|
+
? undefined
|
|
473
|
+
: soleCandidate;
|
|
474
|
+
}
|
|
475
|
+
function parsePackedNpmPackage(stdout, packDirectory, expectedPackage) {
|
|
476
|
+
let parsed;
|
|
477
|
+
try {
|
|
478
|
+
parsed = JSON.parse(stdout);
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
const filename = stdout
|
|
482
|
+
.split(/\r?\n/)
|
|
483
|
+
.map((line) => line.trim())
|
|
484
|
+
.filter((line) => line.length > 0)
|
|
485
|
+
.at(-1);
|
|
486
|
+
if (!filename) {
|
|
487
|
+
throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
|
|
488
|
+
}
|
|
489
|
+
return { tarball: resolvePackedTarball(packDirectory, filename) };
|
|
490
|
+
}
|
|
491
|
+
const candidates = collectPackedNpmCandidates(parsed);
|
|
492
|
+
const selected = selectPackedNpmCandidate(candidates, expectedPackage);
|
|
493
|
+
if (!selected) {
|
|
494
|
+
throw new PmCliError("npm pack --json returned an unsupported or ambiguous package receipt.", EXIT_CODE.GENERIC_FAILURE, {
|
|
495
|
+
code: "npm_pack_json_shape_unsupported",
|
|
496
|
+
required: "Use an npm client that returns an array of package receipts or an object keyed by package name, with exactly one requested package entry containing filename.",
|
|
497
|
+
why: `Well-formed JSON contained ${candidates.length} usable package receipt(s), but pm could not select ${expectedPackage ?? "one package"} deterministically.`,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
441
500
|
return {
|
|
442
|
-
tarball,
|
|
443
|
-
package:
|
|
444
|
-
version:
|
|
501
|
+
tarball: resolvePackedTarball(packDirectory, selected.filename),
|
|
502
|
+
package: selected.name ?? selected.key,
|
|
503
|
+
version: selected.version,
|
|
445
504
|
};
|
|
446
505
|
}
|
|
447
506
|
async function resolveNpmSourceDirectory(source) {
|
|
@@ -486,7 +545,9 @@ async function resolveNpmSourceDirectoryWithRunner(source, npmRunner) {
|
|
|
486
545
|
"--pack-destination",
|
|
487
546
|
packDirectory,
|
|
488
547
|
]);
|
|
489
|
-
const packed = parsePackedNpmPackage(packStdout, packDirectory)
|
|
548
|
+
const packed = parsePackedNpmPackage(packStdout, packDirectory, isNpmLocalFileAliasSpec(source.spec)
|
|
549
|
+
? undefined
|
|
550
|
+
: npmPackageNameFromSpec(source.spec));
|
|
490
551
|
if (!(await pathExists(packed.tarball))) {
|
|
491
552
|
throw new PmCliError("npm pack did not create its reported package archive.", EXIT_CODE.GENERIC_FAILURE, {
|
|
492
553
|
code: "npm_package_archive_missing",
|
|
@@ -994,4 +1055,4 @@ export const _testOnlyInstallSources = {
|
|
|
994
1055
|
runNpmCommand,
|
|
995
1056
|
};
|
|
996
1057
|
//# sourceMappingURL=install-sources.js.map
|
|
997
|
-
//# debugId=
|
|
1058
|
+
//# debugId=05f56edb-c08d-570c-b8b5-641fc694331e
|