@unbrained/pm-cli 2026.5.10 → 2026.5.12
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 +4 -4
- package/AGENTS.md +3 -116
- package/CHANGELOG.md +14 -0
- package/PRD.md +11 -11
- package/README.md +20 -2
- package/dist/cli/argv-utils.d.ts +5 -0
- package/dist/cli/argv-utils.js +34 -0
- package/dist/cli/argv-utils.js.map +1 -0
- package/dist/cli/bootstrap-args.d.ts +15 -0
- package/dist/cli/bootstrap-args.js +211 -0
- package/dist/cli/bootstrap-args.js.map +1 -1
- package/dist/cli/commander-usage.js +109 -3
- package/dist/cli/commander-usage.js.map +1 -1
- package/dist/cli/commands/completion.js +7 -3
- package/dist/cli/commands/completion.js.map +1 -1
- package/dist/cli/commands/contracts.d.ts +19 -0
- package/dist/cli/commands/contracts.js +40 -2
- package/dist/cli/commands/contracts.js.map +1 -1
- package/dist/cli/commands/create.js +112 -51
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/docs.js +9 -2
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/extension.d.ts +12 -3
- package/dist/cli/commands/extension.js +421 -69
- package/dist/cli/commands/extension.js.map +1 -1
- package/dist/cli/commands/files.js +9 -2
- package/dist/cli/commands/files.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +21 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/metadata-normalizers.d.ts +4 -0
- package/dist/cli/commands/metadata-normalizers.js +37 -0
- package/dist/cli/commands/metadata-normalizers.js.map +1 -0
- package/dist/cli/commands/reindex.js +173 -135
- package/dist/cli/commands/reindex.js.map +1 -1
- package/dist/cli/commands/search.js +16 -6
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/test.js +23 -8
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/commands/update.js +70 -39
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/commands/upgrade.d.ts +63 -0
- package/dist/cli/commands/upgrade.js +260 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/error-guidance.d.ts +9 -1
- package/dist/cli/error-guidance.js +147 -6
- package/dist/cli/error-guidance.js.map +1 -1
- package/dist/cli/guide-topics.js +18 -16
- package/dist/cli/guide-topics.js.map +1 -1
- package/dist/cli/help-content.js +42 -2
- package/dist/cli/help-content.js.map +1 -1
- package/dist/cli/help-json-payload.js +11 -1
- package/dist/cli/help-json-payload.js.map +1 -1
- package/dist/cli/main.js +69 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/register-setup.js +174 -82
- package/dist/cli/register-setup.js.map +1 -1
- package/dist/cli/telemetry-flush.d.ts +2 -0
- package/dist/cli/telemetry-flush.js +4 -0
- package/dist/cli/telemetry-flush.js.map +1 -0
- package/dist/cli.js +1 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/extensions/extension-types.d.ts +72 -0
- package/dist/core/extensions/extension-types.js +24 -0
- package/dist/core/extensions/extension-types.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -0
- package/dist/core/extensions/loader.js +766 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/lock/lock.js +2 -0
- package/dist/core/lock/lock.js.map +1 -1
- package/dist/core/packages/manifest.d.ts +13 -0
- package/dist/core/packages/manifest.js +139 -0
- package/dist/core/packages/manifest.js.map +1 -0
- package/dist/core/sentry/instrument.d.ts +15 -0
- package/dist/core/sentry/instrument.js +35 -3
- package/dist/core/sentry/instrument.js.map +1 -1
- package/dist/core/shared/constants.js +20 -0
- package/dist/core/shared/constants.js.map +1 -1
- package/dist/core/shared/errors.d.ts +8 -0
- package/dist/core/shared/errors.js.map +1 -1
- package/dist/core/shared/levenshtein.d.ts +1 -0
- package/dist/core/shared/levenshtein.js +37 -0
- package/dist/core/shared/levenshtein.js.map +1 -0
- package/dist/core/store/paths.js +34 -1
- package/dist/core/store/paths.js.map +1 -1
- package/dist/core/store/settings.js +210 -1
- package/dist/core/store/settings.js.map +1 -1
- package/dist/core/telemetry/runtime.d.ts +1 -0
- package/dist/core/telemetry/runtime.js +102 -3
- package/dist/core/telemetry/runtime.js.map +1 -1
- package/dist/mcp/server.js +11 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/cli-contracts.d.ts +38 -17
- package/dist/sdk/cli-contracts.js +387 -35
- package/dist/sdk/cli-contracts.js.map +1 -1
- package/dist/sdk/index.d.ts +13 -1
- package/dist/sdk/index.js +9 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/types.d.ts +41 -0
- package/dist/types.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CLAUDE_CODE_PLUGIN.md +39 -0
- package/docs/COMMANDS.md +14 -1
- package/docs/EXTENSIONS.md +782 -12
- package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
- package/docs/QUICKSTART.md +10 -2
- package/docs/README.md +4 -6
- package/docs/SDK.md +445 -0
- package/docs/examples/ci/github-actions-pm-extension-gate.yml +53 -0
- package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +41 -0
- package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +45 -0
- package/docs/examples/policy-restricted-extension/README.md +74 -0
- package/docs/examples/policy-restricted-extension/index.js +21 -0
- package/docs/examples/policy-restricted-extension/manifest.json +21 -0
- package/docs/examples/policy-restricted-extension/package.json +8 -0
- package/docs/examples/sdk-app-embedding/README.md +39 -0
- package/docs/examples/sdk-app-embedding/package.json +9 -0
- package/docs/examples/sdk-app-embedding/run-embedded-pm.mjs +61 -0
- package/docs/examples/sdk-contract-consumer/README.md +57 -0
- package/docs/examples/sdk-contract-consumer/inspect-contracts.mjs +47 -0
- package/docs/examples/sdk-contract-consumer/package.json +10 -0
- package/docs/examples/starter-extension/README.md +57 -42
- package/docs/examples/starter-extension/manifest.json +15 -0
- package/marketplace.json +3 -3
- package/package.json +5 -23
- package/packages/pm-beads/README.md +10 -0
- package/{.agents/pm → packages/pm-beads}/extensions/beads/index.js +24 -9
- package/packages/pm-beads/extensions/beads/index.ts +131 -0
- package/packages/pm-beads/package.json +17 -0
- package/packages/pm-todos/README.md +11 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/index.js +24 -9
- package/packages/pm-todos/extensions/todos/index.ts +149 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.js +1 -1
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.ts +1 -1
- package/packages/pm-todos/package.json +17 -0
- package/plugins/pm-cli-claude/.claude-plugin/plugin.json +2 -2
- package/plugins/pm-cli-claude/README.md +54 -14
- package/plugins/pm-cli-claude/agents/pm-delivery-chain.md +88 -0
- package/plugins/pm-cli-claude/agents/pm-triage-agent.md +83 -0
- package/plugins/pm-cli-claude/agents/pm-verification-agent.md +88 -0
- package/plugins/pm-cli-claude/hooks/session-start.mjs +35 -21
- package/.agents/pm/extensions/.managed-extensions.json +0 -42
- package/.agents/skills/HARNESS_COMPATIBILITY.md +0 -45
- package/.agents/skills/README.md +0 -21
- package/.agents/skills/pm-developer/SKILL.md +0 -73
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +0 -48
- package/.agents/skills/pm-developer/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-extensions/SKILL.md +0 -57
- package/.agents/skills/pm-extensions/references/LIFECYCLE.md +0 -40
- package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +0 -25
- package/.agents/skills/pm-sdk/SKILL.md +0 -50
- package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +0 -31
- package/.agents/skills/pm-sdk/references/PROMPTS.md +0 -13
- package/.agents/skills/pm-user/SKILL.md +0 -59
- package/.agents/skills/pm-user/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-user/references/WORKFLOWS.md +0 -35
- package/.pi/README.md +0 -26
- package/.pi/extensions/pm-cli/index.js +0 -147
- package/.pi/prompts/pm-workflow.md +0 -5
- package/.pi/skills/pm-native/SKILL.md +0 -40
- package/.pi/skills/pm-release/SKILL.md +0 -35
- package/dist/pi/native.d.ts +0 -5
- package/dist/pi/native.js +0 -183
- package/dist/pi/native.js.map +0 -1
- package/docs/PI_PACKAGE.md +0 -56
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/manifest.json +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.js +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.ts +0 -0
- /package/{.agents/pm → packages/pm-todos}/extensions/todos/manifest.json +0 -0
|
@@ -2,11 +2,12 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import { activateExtensions, loadExtensions } from "../../core/extensions/index.js";
|
|
7
|
+
import { activateExtensions, loadExtensions, nextExtensionReloadToken } from "../../core/extensions/index.js";
|
|
8
8
|
import { EXTENSION_CAPABILITY_CONTRACT, KNOWN_EXTENSION_CAPABILITIES, parseLegacyExtensionCapabilityAliasWarning, parseUnknownExtensionCapabilityWarning, resolveExtensionRoots, } from "../../core/extensions/loader.js";
|
|
9
9
|
import { pathExists } from "../../core/fs/fs-utils.js";
|
|
10
|
+
import { collectPackageExtensionDirectories } from "../../core/packages/manifest.js";
|
|
10
11
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
11
12
|
import { PmCliError } from "../../core/shared/errors.js";
|
|
12
13
|
import { nowIso } from "../../core/shared/time.js";
|
|
@@ -17,10 +18,17 @@ const DEFAULT_EXTENSION_PRIORITY = 100;
|
|
|
17
18
|
const MANAGED_EXTENSION_STATE_FILENAME = ".managed-extensions.json";
|
|
18
19
|
const MANAGED_EXTENSION_STATE_VERSION = 1;
|
|
19
20
|
const PM_PACKAGE_ROOT_ENV = "PM_CLI_PACKAGE_ROOT";
|
|
20
|
-
const
|
|
21
|
-
beads:
|
|
22
|
-
|
|
21
|
+
const BUNDLED_PACKAGE_ALIASES = {
|
|
22
|
+
beads: {
|
|
23
|
+
package_directory: "pm-beads",
|
|
24
|
+
legacy_extension_directory: "beads",
|
|
25
|
+
},
|
|
26
|
+
todos: {
|
|
27
|
+
package_directory: "pm-todos",
|
|
28
|
+
legacy_extension_directory: "todos",
|
|
29
|
+
},
|
|
23
30
|
};
|
|
31
|
+
const BUNDLED_PACKAGE_INSTALL_ALL_TARGETS = new Set(["*", "all"]);
|
|
24
32
|
function resolvePackageRootCandidates() {
|
|
25
33
|
const candidates = [];
|
|
26
34
|
const envRoot = process.env[PM_PACKAGE_ROOT_ENV];
|
|
@@ -33,21 +41,112 @@ function resolvePackageRootCandidates() {
|
|
|
33
41
|
}
|
|
34
42
|
async function resolveBundledExtensionAliasSource(input) {
|
|
35
43
|
const normalized = input.trim().toLowerCase();
|
|
36
|
-
const alias =
|
|
44
|
+
const alias = BUNDLED_PACKAGE_ALIASES[normalized];
|
|
37
45
|
if (!alias) {
|
|
38
46
|
return null;
|
|
39
47
|
}
|
|
40
48
|
for (const packageRoot of resolvePackageRootCandidates()) {
|
|
41
|
-
const
|
|
42
|
-
if (await pathExists(path.join(
|
|
43
|
-
return
|
|
49
|
+
const packagePath = path.join(packageRoot, "packages", alias.package_directory);
|
|
50
|
+
if (await pathExists(path.join(packagePath, "package.json"))) {
|
|
51
|
+
return packagePath;
|
|
52
|
+
}
|
|
53
|
+
const legacyExtensionPath = path.join(packageRoot, ".agents", "pm", "extensions", alias.legacy_extension_directory);
|
|
54
|
+
if (await pathExists(path.join(legacyExtensionPath, "manifest.json"))) {
|
|
55
|
+
return legacyExtensionPath;
|
|
44
56
|
}
|
|
45
57
|
}
|
|
46
58
|
return null;
|
|
47
59
|
}
|
|
60
|
+
function isBundledPackageInstallAllTarget(input) {
|
|
61
|
+
return BUNDLED_PACKAGE_INSTALL_ALL_TARGETS.has(input.trim().toLowerCase());
|
|
62
|
+
}
|
|
63
|
+
function listBundledPackageAliases() {
|
|
64
|
+
return Object.keys(BUNDLED_PACKAGE_ALIASES).sort((left, right) => left.localeCompare(right));
|
|
65
|
+
}
|
|
48
66
|
function normalizeStringList(values) {
|
|
49
67
|
return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))].sort((left, right) => left.localeCompare(right));
|
|
50
68
|
}
|
|
69
|
+
function summarizePolicyWarnings(warnings) {
|
|
70
|
+
let warningCount = 0;
|
|
71
|
+
let violationCount = 0;
|
|
72
|
+
let blockedCount = 0;
|
|
73
|
+
for (const warning of warnings) {
|
|
74
|
+
if (!warning.startsWith("extension_policy_")) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
warningCount += 1;
|
|
78
|
+
if (warning.startsWith("extension_policy_violation_")) {
|
|
79
|
+
violationCount += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (warning.startsWith("extension_policy_blocked_")) {
|
|
83
|
+
blockedCount += 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
warning_count: warningCount,
|
|
88
|
+
violation_count: violationCount,
|
|
89
|
+
blocked_count: blockedCount,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function buildExtensionPolicyDetails(policy) {
|
|
93
|
+
const overrides = (policy.extension_overrides ?? [])
|
|
94
|
+
.map((override) => ({
|
|
95
|
+
name: override.name.trim(),
|
|
96
|
+
disabled: override.disabled === true ? true : undefined,
|
|
97
|
+
require_trusted: override.require_trusted === true ? true : undefined,
|
|
98
|
+
require_provenance: override.require_provenance === true ? true : undefined,
|
|
99
|
+
sandbox_profile: override.sandbox_profile,
|
|
100
|
+
allowed_capabilities: normalizeStringList(override.allowed_capabilities ?? []),
|
|
101
|
+
blocked_capabilities: normalizeStringList(override.blocked_capabilities ?? []),
|
|
102
|
+
allowed_surfaces: normalizeStringList(override.allowed_surfaces ?? []),
|
|
103
|
+
blocked_surfaces: normalizeStringList(override.blocked_surfaces ?? []),
|
|
104
|
+
allowed_commands: normalizeStringList(override.allowed_commands ?? []),
|
|
105
|
+
blocked_commands: normalizeStringList(override.blocked_commands ?? []),
|
|
106
|
+
allowed_actions: normalizeStringList(override.allowed_actions ?? []),
|
|
107
|
+
blocked_actions: normalizeStringList(override.blocked_actions ?? []),
|
|
108
|
+
allowed_services: normalizeStringList(override.allowed_services ?? []),
|
|
109
|
+
blocked_services: normalizeStringList(override.blocked_services ?? []),
|
|
110
|
+
}))
|
|
111
|
+
.filter((override) => override.name.length > 0)
|
|
112
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
113
|
+
return {
|
|
114
|
+
mode: policy.mode,
|
|
115
|
+
trust_mode: policy.trust_mode,
|
|
116
|
+
require_provenance: policy.require_provenance === true,
|
|
117
|
+
trusted_extensions: normalizeStringList(policy.trusted_extensions ?? []),
|
|
118
|
+
default_sandbox_profile: policy.default_sandbox_profile ?? "none",
|
|
119
|
+
allowed_extensions: normalizeStringList(policy.allowed_extensions ?? []),
|
|
120
|
+
blocked_extensions: normalizeStringList(policy.blocked_extensions ?? []),
|
|
121
|
+
allowed_capabilities: normalizeStringList(policy.allowed_capabilities ?? []),
|
|
122
|
+
blocked_capabilities: normalizeStringList(policy.blocked_capabilities ?? []),
|
|
123
|
+
allowed_surfaces: normalizeStringList(policy.allowed_surfaces ?? []),
|
|
124
|
+
blocked_surfaces: normalizeStringList(policy.blocked_surfaces ?? []),
|
|
125
|
+
allowed_commands: normalizeStringList(policy.allowed_commands ?? []),
|
|
126
|
+
blocked_commands: normalizeStringList(policy.blocked_commands ?? []),
|
|
127
|
+
allowed_actions: normalizeStringList(policy.allowed_actions ?? []),
|
|
128
|
+
blocked_actions: normalizeStringList(policy.blocked_actions ?? []),
|
|
129
|
+
allowed_services: normalizeStringList(policy.allowed_services ?? []),
|
|
130
|
+
blocked_services: normalizeStringList(policy.blocked_services ?? []),
|
|
131
|
+
extension_overrides: overrides.map((override) => ({
|
|
132
|
+
name: override.name,
|
|
133
|
+
...(override.disabled === true ? { disabled: true } : {}),
|
|
134
|
+
...(override.require_trusted === true ? { require_trusted: true } : {}),
|
|
135
|
+
...(override.require_provenance === true ? { require_provenance: true } : {}),
|
|
136
|
+
...(override.sandbox_profile ? { sandbox_profile: override.sandbox_profile } : {}),
|
|
137
|
+
...(override.allowed_capabilities.length > 0 ? { allowed_capabilities: override.allowed_capabilities } : {}),
|
|
138
|
+
...(override.blocked_capabilities.length > 0 ? { blocked_capabilities: override.blocked_capabilities } : {}),
|
|
139
|
+
...(override.allowed_surfaces.length > 0 ? { allowed_surfaces: override.allowed_surfaces } : {}),
|
|
140
|
+
...(override.blocked_surfaces.length > 0 ? { blocked_surfaces: override.blocked_surfaces } : {}),
|
|
141
|
+
...(override.allowed_commands.length > 0 ? { allowed_commands: override.allowed_commands } : {}),
|
|
142
|
+
...(override.blocked_commands.length > 0 ? { blocked_commands: override.blocked_commands } : {}),
|
|
143
|
+
...(override.allowed_actions.length > 0 ? { allowed_actions: override.allowed_actions } : {}),
|
|
144
|
+
...(override.blocked_actions.length > 0 ? { blocked_actions: override.blocked_actions } : {}),
|
|
145
|
+
...(override.allowed_services.length > 0 ? { allowed_services: override.allowed_services } : {}),
|
|
146
|
+
...(override.blocked_services.length > 0 ? { blocked_services: override.blocked_services } : {}),
|
|
147
|
+
})),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
51
150
|
function normalizeManagedDirectoryName(name) {
|
|
52
151
|
const normalized = name
|
|
53
152
|
.trim()
|
|
@@ -192,7 +291,7 @@ function normalizeManagedState(raw) {
|
|
|
192
291
|
continue;
|
|
193
292
|
}
|
|
194
293
|
const source = entry.source;
|
|
195
|
-
if ((source.kind !== "local" && source.kind !== "github") ||
|
|
294
|
+
if ((source.kind !== "local" && source.kind !== "github" && source.kind !== "npm") ||
|
|
196
295
|
typeof source.input !== "string" ||
|
|
197
296
|
typeof source.location !== "string") {
|
|
198
297
|
continue;
|
|
@@ -210,6 +309,8 @@ function normalizeManagedState(raw) {
|
|
|
210
309
|
kind: source.kind,
|
|
211
310
|
input: source.input,
|
|
212
311
|
location: source.location,
|
|
312
|
+
package: typeof source.package === "string" ? source.package : undefined,
|
|
313
|
+
version: typeof source.version === "string" ? source.version : undefined,
|
|
213
314
|
repository: typeof source.repository === "string" ? source.repository : undefined,
|
|
214
315
|
owner: typeof source.owner === "string" ? source.owner : undefined,
|
|
215
316
|
repo: typeof source.repo === "string" ? source.repo : undefined,
|
|
@@ -285,7 +386,11 @@ async function resolveBundledAliasManifestName(input) {
|
|
|
285
386
|
return null;
|
|
286
387
|
}
|
|
287
388
|
try {
|
|
288
|
-
const
|
|
389
|
+
const extensionDirectories = await collectPackageExtensionDirectories(bundledAliasSource);
|
|
390
|
+
if (extensionDirectories.length !== 1) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const validated = await validateExtensionDirectory(extensionDirectories[0]);
|
|
289
394
|
return validated.manifest.name;
|
|
290
395
|
}
|
|
291
396
|
catch {
|
|
@@ -370,6 +475,7 @@ function resolveAction(target, options) {
|
|
|
370
475
|
options.uninstall ? "uninstall" : null,
|
|
371
476
|
options.explore ? "explore" : null,
|
|
372
477
|
options.manage ? "manage" : null,
|
|
478
|
+
options.reload ? "reload" : null,
|
|
373
479
|
options.doctor ? "doctor" : null,
|
|
374
480
|
options.init ? "init" : null,
|
|
375
481
|
options.scaffold ? "init" : null,
|
|
@@ -382,10 +488,13 @@ function resolveAction(target, options) {
|
|
|
382
488
|
if (typeof target === "string" && target.trim().toLowerCase() === "doctor") {
|
|
383
489
|
return "doctor";
|
|
384
490
|
}
|
|
491
|
+
if (typeof target === "string" && target.trim().toLowerCase() === "reload") {
|
|
492
|
+
return "reload";
|
|
493
|
+
}
|
|
385
494
|
if (typeof target === "string" && (target.trim().toLowerCase() === "init" || target.trim().toLowerCase() === "scaffold")) {
|
|
386
495
|
return "init";
|
|
387
496
|
}
|
|
388
|
-
throw new PmCliError("One action flag is required. Use one of: --install, --uninstall, --explore, --manage, --doctor, --init/--scaffold, --adopt, --adopt-all, --activate, --deactivate.", EXIT_CODE.USAGE);
|
|
497
|
+
throw new PmCliError("One action flag is required. Use one of: --install, --uninstall, --explore, --manage, --reload, --doctor, --init/--scaffold, --adopt, --adopt-all, --activate, --deactivate.", EXIT_CODE.USAGE);
|
|
389
498
|
}
|
|
390
499
|
if (selected.length > 1) {
|
|
391
500
|
throw new PmCliError("Extension action flags are mutually exclusive.", EXIT_CODE.USAGE);
|
|
@@ -442,6 +551,23 @@ export function parseExtensionInstallSource(input, options = {}) {
|
|
|
442
551
|
throw new PmCliError("Extension source is required for --install.", EXIT_CODE.USAGE);
|
|
443
552
|
}
|
|
444
553
|
const refOverride = typeof options.ref === "string" && options.ref.trim().length > 0 ? options.ref.trim() : undefined;
|
|
554
|
+
if (normalizedInput.startsWith("npm:")) {
|
|
555
|
+
const spec = normalizedInput.slice("npm:".length).trim();
|
|
556
|
+
if (spec.length === 0) {
|
|
557
|
+
throw new PmCliError('npm package source must include a package spec after "npm:".', EXIT_CODE.USAGE);
|
|
558
|
+
}
|
|
559
|
+
if (options.forceGithub) {
|
|
560
|
+
throw new PmCliError('Options "--gh/--github" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
|
|
561
|
+
}
|
|
562
|
+
if (refOverride) {
|
|
563
|
+
throw new PmCliError('Option "--ref" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
kind: "npm",
|
|
567
|
+
input: normalizedInput,
|
|
568
|
+
spec,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
445
571
|
const maybeGithubByUrl = (() => {
|
|
446
572
|
try {
|
|
447
573
|
const parsed = new URL(normalizedInput);
|
|
@@ -493,22 +619,123 @@ async function runGitCommand(args) {
|
|
|
493
619
|
throw new PmCliError(`Git command failed: git ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
|
|
494
620
|
}
|
|
495
621
|
}
|
|
496
|
-
async function
|
|
497
|
-
|
|
498
|
-
|
|
622
|
+
async function runNpmCommand(args, cwd) {
|
|
623
|
+
try {
|
|
624
|
+
const result = await execFileAsync("npm", args, { cwd, encoding: "utf8" });
|
|
625
|
+
return (result.stdout ?? "").trim();
|
|
499
626
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
627
|
+
catch (error) {
|
|
628
|
+
const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr) : "";
|
|
629
|
+
const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);
|
|
630
|
+
throw new PmCliError(`npm command failed: npm ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function resolveLocalNpmPackagePath(spec) {
|
|
634
|
+
if (path.isAbsolute(spec) || spec.startsWith(".") || spec.startsWith("..")) {
|
|
635
|
+
const absolutePath = path.resolve(process.cwd(), spec);
|
|
636
|
+
return (await pathExists(absolutePath)) ? absolutePath : null;
|
|
637
|
+
}
|
|
638
|
+
try {
|
|
639
|
+
const parsed = new URL(spec);
|
|
640
|
+
if (parsed.protocol === "file:") {
|
|
641
|
+
const absolutePath = fileURLToPath(parsed);
|
|
642
|
+
return (await pathExists(absolutePath)) ? absolutePath : null;
|
|
505
643
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
// Registry package specs are not URLs.
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
async function resolveNpmPackSpec(spec) {
|
|
651
|
+
const localPath = await resolveLocalNpmPackagePath(spec);
|
|
652
|
+
if (localPath) {
|
|
653
|
+
return pathToFileURL(localPath).href;
|
|
654
|
+
}
|
|
655
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(spec)) {
|
|
656
|
+
return spec;
|
|
657
|
+
}
|
|
658
|
+
return spec;
|
|
659
|
+
}
|
|
660
|
+
function parsePackedNpmPackage(stdout, packDirectory) {
|
|
661
|
+
try {
|
|
662
|
+
const parsed = JSON.parse(stdout);
|
|
663
|
+
const first = Array.isArray(parsed) ? parsed[0] : undefined;
|
|
664
|
+
if (first && typeof first.filename === "string" && first.filename.trim().length > 0) {
|
|
665
|
+
return {
|
|
666
|
+
tarball: path.resolve(packDirectory, first.filename),
|
|
667
|
+
package: typeof first.name === "string" ? first.name : undefined,
|
|
668
|
+
version: typeof first.version === "string" ? first.version : undefined,
|
|
669
|
+
};
|
|
509
670
|
}
|
|
510
671
|
}
|
|
511
|
-
|
|
672
|
+
catch {
|
|
673
|
+
// Fall back to the last stdout line for older npm output.
|
|
674
|
+
}
|
|
675
|
+
const lastLine = stdout
|
|
676
|
+
.split(/\r?\n/)
|
|
677
|
+
.map((line) => line.trim())
|
|
678
|
+
.filter((line) => line.length > 0)
|
|
679
|
+
.at(-1);
|
|
680
|
+
if (!lastLine) {
|
|
681
|
+
throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
tarball: path.resolve(packDirectory, lastLine),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
async function resolveNpmSourceDirectory(source) {
|
|
688
|
+
const localPackageRoot = await resolveLocalNpmPackagePath(source.spec);
|
|
689
|
+
if (localPackageRoot) {
|
|
690
|
+
const packageJsonPath = path.join(localPackageRoot, "package.json");
|
|
691
|
+
const packageJson = (await pathExists(packageJsonPath))
|
|
692
|
+
? JSON.parse(await fs.readFile(packageJsonPath, "utf8"))
|
|
693
|
+
: {};
|
|
694
|
+
return {
|
|
695
|
+
directory: await resolvePackageExtensionDirectory(localPackageRoot, source.input),
|
|
696
|
+
package: typeof packageJson.name === "string" ? packageJson.name : undefined,
|
|
697
|
+
version: typeof packageJson.version === "string" ? packageJson.version : undefined,
|
|
698
|
+
cleanup: async () => { },
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "pm-npm-package-source-"));
|
|
702
|
+
const packDirectory = path.join(tempRoot, "pack");
|
|
703
|
+
const extractDirectory = path.join(tempRoot, "extract");
|
|
704
|
+
await fs.mkdir(packDirectory, { recursive: true });
|
|
705
|
+
await fs.mkdir(extractDirectory, { recursive: true });
|
|
706
|
+
try {
|
|
707
|
+
const packSpec = await resolveNpmPackSpec(source.spec);
|
|
708
|
+
const packStdout = await runNpmCommand(["pack", packSpec, "--json", "--pack-destination", packDirectory]);
|
|
709
|
+
const packed = parsePackedNpmPackage(packStdout, packDirectory);
|
|
710
|
+
await execFileAsync("tar", ["-xzf", packed.tarball, "-C", extractDirectory], { encoding: "utf8" });
|
|
711
|
+
const packageRoot = path.join(extractDirectory, "package");
|
|
712
|
+
const directory = await resolvePackageExtensionDirectory(packageRoot, source.input);
|
|
713
|
+
return {
|
|
714
|
+
directory,
|
|
715
|
+
package: packed.package,
|
|
716
|
+
version: packed.version,
|
|
717
|
+
cleanup: async () => {
|
|
718
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
724
|
+
throw error;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async function resolvePackageExtensionDirectory(packageRoot, sourceLabel) {
|
|
728
|
+
const discovered = await collectPackageExtensionDirectories(packageRoot);
|
|
729
|
+
if (discovered.length === 1) {
|
|
730
|
+
return discovered[0];
|
|
731
|
+
}
|
|
732
|
+
if (discovered.length > 1) {
|
|
733
|
+
const choices = discovered
|
|
734
|
+
.map((entry) => path.relative(packageRoot, entry).replaceAll(path.sep, "/"))
|
|
735
|
+
.sort((left, right) => left.localeCompare(right));
|
|
736
|
+
throw new PmCliError(`Package source "${sourceLabel}" contains multiple extension manifests. Provide an explicit extension path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
|
|
737
|
+
}
|
|
738
|
+
throw new PmCliError(`Unable to locate a pm extension manifest in package source "${sourceLabel}". Add package.json pm.extensions or an extensions/ directory.`, EXIT_CODE.USAGE);
|
|
512
739
|
}
|
|
513
740
|
async function resolveGithubSourceDirectory(cloneDirectory, source) {
|
|
514
741
|
const candidatePaths = [];
|
|
@@ -527,25 +754,11 @@ async function resolveGithubSourceDirectory(cloneDirectory, source) {
|
|
|
527
754
|
if (await pathExists(path.join(cloneDirectory, "manifest.json"))) {
|
|
528
755
|
return { directory: cloneDirectory, resolved_subpath: "." };
|
|
529
756
|
}
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
path.
|
|
534
|
-
|
|
535
|
-
const discovered = (await Promise.all(defaultRoots.map((defaultRoot) => listManifestDirectories(defaultRoot)))).flat();
|
|
536
|
-
if (discovered.length === 1) {
|
|
537
|
-
return {
|
|
538
|
-
directory: discovered[0],
|
|
539
|
-
resolved_subpath: path.relative(cloneDirectory, discovered[0]).replaceAll(path.sep, "/"),
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
if (discovered.length > 1) {
|
|
543
|
-
const choices = discovered
|
|
544
|
-
.map((entry) => path.relative(cloneDirectory, entry).replaceAll(path.sep, "/"))
|
|
545
|
-
.sort((left, right) => left.localeCompare(right));
|
|
546
|
-
throw new PmCliError(`GitHub source "${source.input}" contains multiple extension manifests. Provide an explicit path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
|
|
547
|
-
}
|
|
548
|
-
throw new PmCliError(`Unable to locate extension manifest in GitHub source "${source.input}". Provide an explicit extension path.`, EXIT_CODE.USAGE);
|
|
757
|
+
const discoveredDirectory = await resolvePackageExtensionDirectory(cloneDirectory, source.input);
|
|
758
|
+
return {
|
|
759
|
+
directory: discoveredDirectory,
|
|
760
|
+
resolved_subpath: path.relative(cloneDirectory, discoveredDirectory).replaceAll(path.sep, "/"),
|
|
761
|
+
};
|
|
549
762
|
}
|
|
550
763
|
async function resolveInstallSource(source) {
|
|
551
764
|
if (source.kind === "local") {
|
|
@@ -556,9 +769,21 @@ async function resolveInstallSource(source) {
|
|
|
556
769
|
if (!stats.isDirectory()) {
|
|
557
770
|
throw new PmCliError(`Local extension source must be a directory: "${source.absolute_path}".`, EXIT_CODE.USAGE);
|
|
558
771
|
}
|
|
772
|
+
const directory = await resolvePackageExtensionDirectory(source.absolute_path, source.input);
|
|
559
773
|
return {
|
|
560
774
|
source,
|
|
561
|
-
directory
|
|
775
|
+
directory,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
if (source.kind === "npm") {
|
|
779
|
+
const resolved = await resolveNpmSourceDirectory(source);
|
|
780
|
+
return {
|
|
781
|
+
source,
|
|
782
|
+
directory: resolved.directory,
|
|
783
|
+
cleanup: resolved.cleanup,
|
|
784
|
+
resolved_subpath: path.relative(path.dirname(resolved.directory), resolved.directory).replaceAll(path.sep, "/"),
|
|
785
|
+
npm_package: resolved.package,
|
|
786
|
+
npm_version: resolved.version,
|
|
562
787
|
};
|
|
563
788
|
}
|
|
564
789
|
const cloneDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "pm-extension-source-"));
|
|
@@ -916,19 +1141,21 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
|
|
|
916
1141
|
capabilities: ["commands"],
|
|
917
1142
|
}, null, 2)}\n`;
|
|
918
1143
|
const entrypoint = [
|
|
919
|
-
"
|
|
920
|
-
"
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
"
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
"
|
|
930
|
-
"
|
|
931
|
-
"
|
|
1144
|
+
"export function activate(api) {",
|
|
1145
|
+
" api.registerCommand({",
|
|
1146
|
+
` name: ${JSON.stringify(commandName)},`,
|
|
1147
|
+
' description: "Starter scaffold command. Replace with your own behavior.",',
|
|
1148
|
+
" run: async (context) => ({",
|
|
1149
|
+
" ok: true,",
|
|
1150
|
+
` source: ${JSON.stringify(extensionName)},`,
|
|
1151
|
+
" command: context.command,",
|
|
1152
|
+
' message: "Starter extension scaffold is active.",',
|
|
1153
|
+
" }),",
|
|
1154
|
+
" });",
|
|
1155
|
+
"}",
|
|
1156
|
+
"",
|
|
1157
|
+
"export default {",
|
|
1158
|
+
" activate,",
|
|
932
1159
|
"};",
|
|
933
1160
|
"",
|
|
934
1161
|
].join("\n");
|
|
@@ -949,7 +1176,7 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
|
|
|
949
1176
|
"```",
|
|
950
1177
|
"",
|
|
951
1178
|
"## Notes",
|
|
952
|
-
"- This scaffold uses
|
|
1179
|
+
"- This scaffold uses ESM exports so it works in package scopes with `type: module`.",
|
|
953
1180
|
"- Update `manifest.json` capabilities and `index.js` command behavior as your extension evolves.",
|
|
954
1181
|
"",
|
|
955
1182
|
].join("\n");
|
|
@@ -1056,6 +1283,7 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
|
|
|
1056
1283
|
: [];
|
|
1057
1284
|
const effectiveWarnings = [...new Set([...normalizedWarnings, ...partialCoverageWarnings])].sort((left, right) => left.localeCompare(right));
|
|
1058
1285
|
const warningCodes = [...new Set(effectiveWarnings.map((value) => warningCode(value)))].sort((left, right) => left.localeCompare(right));
|
|
1286
|
+
const policyWarnings = summarizePolicyWarnings(effectiveWarnings);
|
|
1059
1287
|
const scopeFlag = scope === "global" ? "--global" : "--project";
|
|
1060
1288
|
const remediation = [];
|
|
1061
1289
|
if (normalizedWarnings.length > 0) {
|
|
@@ -1088,6 +1316,9 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
|
|
|
1088
1316
|
if (normalizedWarnings.some((warning) => warning.startsWith("extension_manager_state_"))) {
|
|
1089
1317
|
remediation.push(`Review and repair ${scope} managed extension state file if schema/read warnings persist.`);
|
|
1090
1318
|
}
|
|
1319
|
+
if (policyWarnings.warning_count > 0) {
|
|
1320
|
+
remediation.push("Extension governance policy warnings detected. Review settings.extensions.policy mode and allow/block lists to confirm intended capabilities and registration surfaces.");
|
|
1321
|
+
}
|
|
1091
1322
|
}
|
|
1092
1323
|
if (updateHealthPartial) {
|
|
1093
1324
|
remediation.push(`Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via pm extension --manage ${scopeFlag} --fix-managed-state (or pm extension --adopt-all ${scopeFlag}, pm extension --adopt <name> ${scopeFlag}, or reinstall via pm extension --install ${scopeFlag} <source>).`);
|
|
@@ -1109,6 +1340,9 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
|
|
|
1109
1340
|
warning_count: effectiveWarnings.length,
|
|
1110
1341
|
warning_codes: warningCodes,
|
|
1111
1342
|
warnings: effectiveWarnings,
|
|
1343
|
+
policy_warning_count: policyWarnings.warning_count,
|
|
1344
|
+
policy_violation_count: policyWarnings.violation_count,
|
|
1345
|
+
policy_blocked_count: policyWarnings.blocked_count,
|
|
1112
1346
|
total_extensions: extensions.length,
|
|
1113
1347
|
managed_total: managedTotal,
|
|
1114
1348
|
enabled_total: enabledTotal,
|
|
@@ -1234,6 +1468,9 @@ export async function runExtension(target, options, global) {
|
|
|
1234
1468
|
if (options.trace === true && action !== "doctor") {
|
|
1235
1469
|
throw new PmCliError("--trace is only valid with --doctor.", EXIT_CODE.USAGE);
|
|
1236
1470
|
}
|
|
1471
|
+
if (options.watch === true && action !== "reload") {
|
|
1472
|
+
throw new PmCliError("--watch is only valid with --reload.", EXIT_CODE.USAGE);
|
|
1473
|
+
}
|
|
1237
1474
|
if (options.runtimeProbe === true && action !== "manage") {
|
|
1238
1475
|
throw new PmCliError("--runtime-probe is only valid with --manage.", EXIT_CODE.USAGE);
|
|
1239
1476
|
}
|
|
@@ -1245,6 +1482,9 @@ export async function runExtension(target, options, global) {
|
|
|
1245
1482
|
if (action === "doctor" && normalizedInput === "doctor") {
|
|
1246
1483
|
return undefined;
|
|
1247
1484
|
}
|
|
1485
|
+
if (action === "reload" && normalizedInput === "reload") {
|
|
1486
|
+
return undefined;
|
|
1487
|
+
}
|
|
1248
1488
|
const inferredInitAlias = action === "init" &&
|
|
1249
1489
|
options.init !== true &&
|
|
1250
1490
|
options.scaffold !== true &&
|
|
@@ -1294,9 +1534,87 @@ export async function runExtension(target, options, global) {
|
|
|
1294
1534
|
],
|
|
1295
1535
|
});
|
|
1296
1536
|
}
|
|
1537
|
+
if (action === "reload") {
|
|
1538
|
+
if (normalizedTarget !== undefined) {
|
|
1539
|
+
throw new PmCliError('Action "reload" does not accept a target argument.', EXIT_CODE.USAGE);
|
|
1540
|
+
}
|
|
1541
|
+
const settings = await readSettings(resolvedRoots.settings_root);
|
|
1542
|
+
const reloadToken = nextExtensionReloadToken();
|
|
1543
|
+
const reloaded = await loadExtensions({
|
|
1544
|
+
pmRoot: resolvedRoots.settings_root,
|
|
1545
|
+
settings,
|
|
1546
|
+
cwd: process.cwd(),
|
|
1547
|
+
noExtensions: global.noExtensions,
|
|
1548
|
+
reload_token: reloadToken,
|
|
1549
|
+
cache_bust: true,
|
|
1550
|
+
});
|
|
1551
|
+
warnings.push(...reloaded.warnings);
|
|
1552
|
+
const activation = await activateExtensions(reloaded);
|
|
1553
|
+
warnings.push(...activation.warnings);
|
|
1554
|
+
const details = {
|
|
1555
|
+
reload: {
|
|
1556
|
+
token: reloadToken,
|
|
1557
|
+
cache_bust: true,
|
|
1558
|
+
watch: options.watch === true,
|
|
1559
|
+
},
|
|
1560
|
+
loaded_count: reloaded.loaded.length,
|
|
1561
|
+
failed_count: reloaded.failed.length,
|
|
1562
|
+
activated_count: Math.max(0, reloaded.loaded.length - activation.failed.length),
|
|
1563
|
+
activation_failed_count: activation.failed.length,
|
|
1564
|
+
loaded_extensions: reloaded.loaded.map((entry) => ({
|
|
1565
|
+
name: entry.name,
|
|
1566
|
+
layer: entry.layer,
|
|
1567
|
+
version: entry.version,
|
|
1568
|
+
})),
|
|
1569
|
+
failed_extensions: reloaded.failed.map((entry) => ({
|
|
1570
|
+
name: entry.name,
|
|
1571
|
+
layer: entry.layer,
|
|
1572
|
+
error: entry.error,
|
|
1573
|
+
})),
|
|
1574
|
+
activation_failures: activation.failed.map((entry) => ({
|
|
1575
|
+
name: entry.name,
|
|
1576
|
+
layer: entry.layer,
|
|
1577
|
+
error: entry.error,
|
|
1578
|
+
})),
|
|
1579
|
+
};
|
|
1580
|
+
if (options.watch === true) {
|
|
1581
|
+
warnings.push("extension_reload_watch_hint:watch_mode_requested_non_interactive_single_pass_only");
|
|
1582
|
+
}
|
|
1583
|
+
return withResult(details);
|
|
1584
|
+
}
|
|
1297
1585
|
if (action === "install") {
|
|
1298
1586
|
const githubOption = resolveGithubOption(options);
|
|
1299
1587
|
const explicitSourceInput = githubOption ?? requireTarget(normalizedTarget, action);
|
|
1588
|
+
if (typeof githubOption !== "string" && isBundledPackageInstallAllTarget(explicitSourceInput)) {
|
|
1589
|
+
if (typeof options.ref === "string" && options.ref.trim().length > 0) {
|
|
1590
|
+
throw new PmCliError('Action "install all" does not accept --ref.', EXIT_CODE.USAGE);
|
|
1591
|
+
}
|
|
1592
|
+
const aliases = listBundledPackageAliases();
|
|
1593
|
+
const packages = [];
|
|
1594
|
+
for (const alias of aliases) {
|
|
1595
|
+
packages.push({
|
|
1596
|
+
alias,
|
|
1597
|
+
result: await runExtension(alias, { ...options, install: true }, global),
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
for (const entry of packages) {
|
|
1601
|
+
warnings.push(...entry.result.warnings);
|
|
1602
|
+
}
|
|
1603
|
+
return withResult({
|
|
1604
|
+
installed_all: true,
|
|
1605
|
+
installed_count: packages.length,
|
|
1606
|
+
packages: packages.map((entry) => ({
|
|
1607
|
+
alias: entry.alias,
|
|
1608
|
+
ok: entry.result.ok,
|
|
1609
|
+
extension: entry.result.details.extension,
|
|
1610
|
+
source: entry.result.details.source,
|
|
1611
|
+
destination_path: entry.result.details.destination_path,
|
|
1612
|
+
activated: entry.result.details.activated,
|
|
1613
|
+
settings_changed: entry.result.details.settings_changed,
|
|
1614
|
+
warnings: entry.result.warnings,
|
|
1615
|
+
})),
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1300
1618
|
const bundledAliasSource = typeof githubOption === "string" ? null : await resolveBundledExtensionAliasSource(explicitSourceInput);
|
|
1301
1619
|
const sourceInput = bundledAliasSource ?? explicitSourceInput;
|
|
1302
1620
|
const installSource = parseExtensionInstallSource(sourceInput, {
|
|
@@ -1326,17 +1644,25 @@ export async function runExtension(target, options, global) {
|
|
|
1326
1644
|
input: installSource.input,
|
|
1327
1645
|
location: installSource.absolute_path,
|
|
1328
1646
|
}
|
|
1329
|
-
:
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1647
|
+
: installSource.kind === "npm"
|
|
1648
|
+
? {
|
|
1649
|
+
kind: "npm",
|
|
1650
|
+
input: installSource.input,
|
|
1651
|
+
location: resolvedSource.resolved_subpath ?? ".",
|
|
1652
|
+
package: resolvedSource.npm_package,
|
|
1653
|
+
version: resolvedSource.npm_version,
|
|
1654
|
+
}
|
|
1655
|
+
: {
|
|
1656
|
+
kind: "github",
|
|
1657
|
+
input: installSource.input,
|
|
1658
|
+
location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
|
|
1659
|
+
repository: installSource.repository,
|
|
1660
|
+
owner: installSource.owner,
|
|
1661
|
+
repo: installSource.repo,
|
|
1662
|
+
ref: installSource.ref,
|
|
1663
|
+
subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
|
|
1664
|
+
commit: resolvedSource.commit,
|
|
1665
|
+
};
|
|
1340
1666
|
const now = nowIso();
|
|
1341
1667
|
const existingManagedEntry = managedStateRead.state.entries.find((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(validated.manifest.name));
|
|
1342
1668
|
const managedState = upsertManagedEntry(managedStateRead.state, {
|
|
@@ -1603,6 +1929,26 @@ export async function runExtension(target, options, global) {
|
|
|
1603
1929
|
const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions);
|
|
1604
1930
|
warnings.push(...triage.warnings);
|
|
1605
1931
|
const normalizedWarnings = [...triage.warnings];
|
|
1932
|
+
const policySummary = {
|
|
1933
|
+
mode: loadResult.policy.mode,
|
|
1934
|
+
trust_mode: loadResult.policy.trust_mode,
|
|
1935
|
+
require_provenance: loadResult.policy.require_provenance,
|
|
1936
|
+
default_sandbox_profile: loadResult.policy.default_sandbox_profile,
|
|
1937
|
+
trusted_extensions_count: loadResult.policy.trusted_extensions.length,
|
|
1938
|
+
allowed_extensions_count: loadResult.policy.allowed_extensions.length,
|
|
1939
|
+
blocked_extensions_count: loadResult.policy.blocked_extensions.length,
|
|
1940
|
+
allowed_capabilities_count: loadResult.policy.allowed_capabilities.length,
|
|
1941
|
+
blocked_capabilities_count: loadResult.policy.blocked_capabilities.length,
|
|
1942
|
+
allowed_surfaces_count: loadResult.policy.allowed_surfaces.length,
|
|
1943
|
+
blocked_surfaces_count: loadResult.policy.blocked_surfaces.length,
|
|
1944
|
+
allowed_commands_count: loadResult.policy.allowed_commands.length,
|
|
1945
|
+
blocked_commands_count: loadResult.policy.blocked_commands.length,
|
|
1946
|
+
allowed_actions_count: loadResult.policy.allowed_actions.length,
|
|
1947
|
+
blocked_actions_count: loadResult.policy.blocked_actions.length,
|
|
1948
|
+
allowed_services_count: loadResult.policy.allowed_services.length,
|
|
1949
|
+
blocked_services_count: loadResult.policy.blocked_services.length,
|
|
1950
|
+
extension_override_count: loadResult.policy.extension_overrides.length,
|
|
1951
|
+
};
|
|
1606
1952
|
const capabilityGuidance = collectUnknownCapabilityGuidance(normalizedWarnings);
|
|
1607
1953
|
const capabilityContract = buildCapabilityContractMetadata();
|
|
1608
1954
|
const warningCodes = triage.warning_codes;
|
|
@@ -1651,6 +1997,7 @@ export async function runExtension(target, options, global) {
|
|
|
1651
1997
|
has_blocking_failures: loadResult.failed.length + activationResult.failed.length > 0,
|
|
1652
1998
|
consistency_warning_count: doctorConsistency.warnings.length,
|
|
1653
1999
|
trace_enabled: includeTrace,
|
|
2000
|
+
policy: policySummary,
|
|
1654
2001
|
remediation,
|
|
1655
2002
|
};
|
|
1656
2003
|
const managedStateFixSummary = managedStateFix
|
|
@@ -1676,6 +2023,7 @@ export async function runExtension(target, options, global) {
|
|
|
1676
2023
|
capability_contract: capabilityContract,
|
|
1677
2024
|
capability_guidance: capabilityGuidance,
|
|
1678
2025
|
managed_state_fix: managedStateFixSummary,
|
|
2026
|
+
policy: loadResult.policy,
|
|
1679
2027
|
};
|
|
1680
2028
|
if (detailMode === "deep") {
|
|
1681
2029
|
const activationFailedDetails = includeTrace
|
|
@@ -1698,6 +2046,7 @@ export async function runExtension(target, options, global) {
|
|
|
1698
2046
|
installed_extensions: runtimeInstalledExtensions,
|
|
1699
2047
|
load: {
|
|
1700
2048
|
roots: loadResult.roots,
|
|
2049
|
+
policy: loadResult.policy,
|
|
1701
2050
|
warnings: loadResult.warnings,
|
|
1702
2051
|
failed: loadResult.failed,
|
|
1703
2052
|
loaded: loadResult.loaded.map((entry) => ({
|
|
@@ -1744,6 +2093,7 @@ export async function runExtension(target, options, global) {
|
|
|
1744
2093
|
}
|
|
1745
2094
|
if (action === "explore" || action === "manage") {
|
|
1746
2095
|
const settings = await readSettings(resolvedRoots.settings_root);
|
|
2096
|
+
const configuredPolicy = buildExtensionPolicyDetails(settings.extensions.policy);
|
|
1747
2097
|
const managedStateRead = await readManagedExtensionState(resolvedRoots.selected_root);
|
|
1748
2098
|
warnings.push(...managedStateRead.warnings);
|
|
1749
2099
|
const installed = await listInstalledExtensions(resolvedRoots.selected_root, scope, settings, managedStateRead.state);
|
|
@@ -1806,6 +2156,7 @@ export async function runExtension(target, options, global) {
|
|
|
1806
2156
|
load_failure_count: loadResult.failed.length,
|
|
1807
2157
|
activation_failure_count: activationResult.failed.length,
|
|
1808
2158
|
warning_count: [...new Set([...loadResult.warnings, ...activationResult.warnings])].length,
|
|
2159
|
+
policy: loadResult.policy,
|
|
1809
2160
|
};
|
|
1810
2161
|
}
|
|
1811
2162
|
else if (action === "manage") {
|
|
@@ -1823,6 +2174,7 @@ export async function runExtension(target, options, global) {
|
|
|
1823
2174
|
active_total: runtimeInstalledExtensions.filter((entry) => entry.active).length,
|
|
1824
2175
|
extensions: runtimeInstalledExtensions,
|
|
1825
2176
|
triage,
|
|
2177
|
+
policy: configuredPolicy,
|
|
1826
2178
|
};
|
|
1827
2179
|
if (action === "manage") {
|
|
1828
2180
|
details.runtime_probe = runtimeProbeSummary;
|