plugnz 0.0.11 → 0.0.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/dist/plugnz.mjs +1165 -69
- package/package.json +1 -1
package/dist/plugnz.mjs
CHANGED
|
@@ -9795,8 +9795,6 @@ import { existsSync as existsSync12, lstatSync as lstatSync5, readFileSync as re
|
|
|
9795
9795
|
import { isAbsolute as isAbsolute3, join as join15, resolve as resolve7 } from "node:path";
|
|
9796
9796
|
|
|
9797
9797
|
// src/hermes-identity.ts
|
|
9798
|
-
var HERMES_PORTABLE_SURFACE = "portable-surface";
|
|
9799
|
-
var HERMES_PORTABLE_SURFACE_PROBE = "hermes-portable-surface";
|
|
9800
9798
|
function hermesCommandCompanionId(pluginId) {
|
|
9801
9799
|
return `${pluginId}.plgnz-commands`;
|
|
9802
9800
|
}
|
|
@@ -10038,6 +10036,10 @@ function yamlStringify(value) {
|
|
|
10038
10036
|
}
|
|
10039
10037
|
|
|
10040
10038
|
// src/hosts/hermes.ts
|
|
10039
|
+
var hermesProvenManagedRevisions = [
|
|
10040
|
+
"c0d7294769a38c17ceae51d8f7995e66e1dcae27",
|
|
10041
|
+
"dce1e9b37581dd62e480a9064dc04a709c2940d3"
|
|
10042
|
+
];
|
|
10041
10043
|
var hermesTargetProfile = {
|
|
10042
10044
|
kind: "hermes",
|
|
10043
10045
|
parseSyncTarget(value, label) {
|
|
@@ -12393,6 +12395,203 @@ function isRecord3(value) {
|
|
|
12393
12395
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12394
12396
|
}
|
|
12395
12397
|
|
|
12398
|
+
// src/hosts/claude-native-schema.ts
|
|
12399
|
+
var control = /[\p{Cc}\u200E\u200F\u202A-\u202E\u2066-\u2069]/u;
|
|
12400
|
+
var reserved = new Set(["inline", "builtin", "skills-dir", "synced", "claude-plugin-test"]);
|
|
12401
|
+
var official275 = new Set(["claude-community", "claude-plugins-community", "healthcare", "claude-code-marketplace", "claude-code-plugins", "claude-plugins-official", "anthropic-marketplace", "anthropic-plugins", "agent-skills", "anthropic-agent-skills", "life-sciences", "knowledge-work-plugins", "claude-for-legal", "claude-for-financial-services", "financial-services-plugins", "first-party-plugins", "claude-tag-plugins"]);
|
|
12402
|
+
var metadataHosts = new Set(["metadata.google.internal", "metadata.goog", "metadata", "instance-data", "instance-data.ec2.internal", "ip6-localhost", "ip6-loopback", "localhost.localdomain", "localhost4", "localhost4.localdomain4", "localhost6", "localhost6.localdomain6"]);
|
|
12403
|
+
var metadataIps = new Set(["100.100.100.200", "168.63.129.16", "192.0.0.192"]);
|
|
12404
|
+
function object3(value, path, invalid) {
|
|
12405
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
12406
|
+
return invalid(path, "object");
|
|
12407
|
+
return value;
|
|
12408
|
+
}
|
|
12409
|
+
function string2(value, path, invalid) {
|
|
12410
|
+
if (typeof value !== "string")
|
|
12411
|
+
return invalid(path, "string");
|
|
12412
|
+
return value;
|
|
12413
|
+
}
|
|
12414
|
+
function optionalStrings(value, keys, path, invalid) {
|
|
12415
|
+
for (const key of keys)
|
|
12416
|
+
if (value[key] !== undefined)
|
|
12417
|
+
string2(value[key], `${path}.${key}`, invalid);
|
|
12418
|
+
}
|
|
12419
|
+
function optionalBoolean(value, key, path, invalid) {
|
|
12420
|
+
if (value[key] !== undefined && typeof value[key] !== "boolean")
|
|
12421
|
+
invalid(`${path}.${key}`, "boolean");
|
|
12422
|
+
}
|
|
12423
|
+
function headers(value, path, invalid) {
|
|
12424
|
+
if (value.headers !== undefined)
|
|
12425
|
+
for (const [key, entry] of Object.entries(object3(value.headers, `${path}.headers`, invalid)))
|
|
12426
|
+
string2(entry, `${path}.headers.${key}`, invalid);
|
|
12427
|
+
if (value.headersHelper !== undefined)
|
|
12428
|
+
printable(value.headersHelper, `${path}.headersHelper`, false, invalid);
|
|
12429
|
+
}
|
|
12430
|
+
function printable(value, path, nonempty, invalid) {
|
|
12431
|
+
const text = string2(value, path, invalid);
|
|
12432
|
+
if (nonempty && text.length === 0 || text.length > 500 || /[^\x20-\x7E]| {4,}/u.test(text))
|
|
12433
|
+
invalid(path, "printable ASCII, at most 500 characters, no four-space runs");
|
|
12434
|
+
}
|
|
12435
|
+
function url(value, path, invalid) {
|
|
12436
|
+
const text = string2(value, path, invalid);
|
|
12437
|
+
try {
|
|
12438
|
+
return new URL(text);
|
|
12439
|
+
} catch {
|
|
12440
|
+
return invalid(path, "URL");
|
|
12441
|
+
}
|
|
12442
|
+
}
|
|
12443
|
+
function registry2(value, path, invalid) {
|
|
12444
|
+
if (value.registry === undefined)
|
|
12445
|
+
return;
|
|
12446
|
+
const text = string2(value.registry, `${path}.registry`, invalid), parsed = url(text, `${path}.registry`, invalid);
|
|
12447
|
+
if (!/^https?:\/\/[^/\\]/iu.test(text) || !["http:", "https:"].includes(parsed.protocol))
|
|
12448
|
+
invalid(`${path}.registry`, "http(s) registry URL");
|
|
12449
|
+
}
|
|
12450
|
+
function name(value, path, marketplace, version, invalid) {
|
|
12451
|
+
const text = string2(value, path, invalid), lower = text.toLowerCase();
|
|
12452
|
+
if (text.length === 0 || text.includes(" ") || control.test(text))
|
|
12453
|
+
invalid(path, "nonempty name without spaces, control or bidirectional-formatting characters");
|
|
12454
|
+
if (!marketplace)
|
|
12455
|
+
return;
|
|
12456
|
+
if (/[\\/]/u.test(text) || text.includes("..") || text === "." || reserved.has(lower))
|
|
12457
|
+
invalid(path, "native marketplace name");
|
|
12458
|
+
if (version === "2.1.275" && (official275.has(lower) || /[^\u0020-\u007E]/u.test(text) || /(?:official[^a-z0-9]*(anthropic|claude)|(?:anthropic|claude)[^a-z0-9]*official|^(?:anthropic|claude)[^a-z0-9]*(marketplace|plugins|official))/iu.test(text)))
|
|
12459
|
+
invalid(path, "2.1.275 settings marketplace name without reserved/official impersonation");
|
|
12460
|
+
}
|
|
12461
|
+
function blockedIpv4(bytes) {
|
|
12462
|
+
return bytes[0] === 127 || bytes[0] === 0 || bytes[0] === 169 && bytes[1] === 254 || metadataIps.has(bytes.join("."));
|
|
12463
|
+
}
|
|
12464
|
+
function ipv6Bytes(host) {
|
|
12465
|
+
const parts = host.split("::"), left = parts[0] ? parts[0].split(":") : [], right = parts.length === 2 && parts[1] ? parts[1].split(":") : [];
|
|
12466
|
+
const numbers = (values) => values.flatMap((value) => {
|
|
12467
|
+
const n = Number.parseInt(value, 16);
|
|
12468
|
+
return [n >> 8, n & 255];
|
|
12469
|
+
});
|
|
12470
|
+
const start = numbers(left), end = numbers(right);
|
|
12471
|
+
return [...start, ...Array(16 - start.length - end.length).fill(0), ...end];
|
|
12472
|
+
}
|
|
12473
|
+
function archiveHostBlocked(hostname) {
|
|
12474
|
+
let host = hostname.toLowerCase().replace(/^\[|\]$/gu, "");
|
|
12475
|
+
if (host.endsWith("."))
|
|
12476
|
+
host = host.slice(0, -1);
|
|
12477
|
+
if (host === "" || host === "localhost" || host.endsWith(".localhost") || metadataHosts.has(host) || host.startsWith("instance-data.") && host.endsWith(".compute.internal"))
|
|
12478
|
+
return true;
|
|
12479
|
+
if (/^\d+\.\d+\.\d+\.\d+$/u.test(host))
|
|
12480
|
+
return blockedIpv4(host.split(".").map(Number));
|
|
12481
|
+
if (!host.includes(":"))
|
|
12482
|
+
return false;
|
|
12483
|
+
const b = ipv6Bytes(host);
|
|
12484
|
+
const nat64 = b[0] === 0 && b[1] === 100 && b[2] === 255 && b[3] === 155;
|
|
12485
|
+
if (nat64 && b[4] === 0 && b[5] === 1 || b.every((n) => n === 0) || b.slice(0, 15).every((n) => n === 0) && b[15] === 1 || host === "fd00:ec2::254" || b[0] === 254 && b[1] >= 128 && b[1] <= 191)
|
|
12486
|
+
return true;
|
|
12487
|
+
const embedded = [];
|
|
12488
|
+
if (b[0] === 32 && b[1] === 2)
|
|
12489
|
+
embedded.push(b.slice(2, 6));
|
|
12490
|
+
if (b.slice(0, 10).every((n) => n === 0) && (b[10] === 255 && b[11] === 255 || b[10] === 0 && b[11] === 0) || nat64 && b.slice(4, 12).every((n) => n === 0) || (b[8] === 0 || b[8] === 2) && b[9] === 0 && b[10] === 94 && b[11] === 254)
|
|
12491
|
+
embedded.push(b.slice(12, 16));
|
|
12492
|
+
return embedded.some(blockedIpv4);
|
|
12493
|
+
}
|
|
12494
|
+
function pluginSource(value, path, invalid) {
|
|
12495
|
+
const source = object3(value, path, invalid), kind = string2(source.source, `${path}.source`, invalid);
|
|
12496
|
+
switch (kind) {
|
|
12497
|
+
case "npm": {
|
|
12498
|
+
const pkg = string2(source.package, `${path}.package`, invalid);
|
|
12499
|
+
if (!/^(?:file|https?|git(?:\+https?|\+ssh)?|ssh|github|gitlab|bitbucket):/iu.test(pkg) && pkg.includes(".."))
|
|
12500
|
+
invalid(`${path}.package`, "npm reference without path traversal");
|
|
12501
|
+
optionalStrings(source, ["version"], path, invalid);
|
|
12502
|
+
registry2(source, path, invalid);
|
|
12503
|
+
break;
|
|
12504
|
+
}
|
|
12505
|
+
case "url":
|
|
12506
|
+
case "github":
|
|
12507
|
+
case "git-subdir":
|
|
12508
|
+
string2(source[kind === "github" ? "repo" : "url"], `${path}.${kind === "github" ? "repo" : "url"}`, invalid);
|
|
12509
|
+
optionalStrings(source, ["ref", "sha"], path, invalid);
|
|
12510
|
+
if (source.sha !== undefined && !/^[a-f0-9]{40}$/u.test(source.sha))
|
|
12511
|
+
invalid(`${path}.sha`, "40-character lowercase commit SHA");
|
|
12512
|
+
if (kind === "git-subdir" && string2(source.path, `${path}.path`, invalid).length === 0)
|
|
12513
|
+
invalid(`${path}.path`, "nonempty string");
|
|
12514
|
+
break;
|
|
12515
|
+
case "archive": {
|
|
12516
|
+
const parsed = url(source.url, `${path}.url`, invalid);
|
|
12517
|
+
if (parsed.protocol !== "https:" || archiveHostBlocked(parsed.hostname))
|
|
12518
|
+
invalid(`${path}.url`, "HTTPS archive URL without loopback/link-local/metadata host");
|
|
12519
|
+
optionalStrings(source, ["sha256"], path, invalid);
|
|
12520
|
+
if (source.sha256 !== undefined && !/^[a-f0-9]{64}$/iu.test(source.sha256))
|
|
12521
|
+
invalid(`${path}.sha256`, "64-character hex SHA-256");
|
|
12522
|
+
break;
|
|
12523
|
+
}
|
|
12524
|
+
case "command":
|
|
12525
|
+
printable(source.command, `${path}.command`, true, invalid);
|
|
12526
|
+
if (source.timeout !== undefined && (typeof source.timeout !== "number" || !Number.isInteger(source.timeout) || source.timeout <= 0 || source.timeout > 600))
|
|
12527
|
+
invalid(`${path}.timeout`, "positive integer at most 600");
|
|
12528
|
+
if (source.mode !== undefined && source.mode !== "copy" && source.mode !== "link")
|
|
12529
|
+
invalid(`${path}.mode`, "copy or link");
|
|
12530
|
+
break;
|
|
12531
|
+
default:
|
|
12532
|
+
invalid(`${path}.source`, "native remote inline plugin source");
|
|
12533
|
+
}
|
|
12534
|
+
}
|
|
12535
|
+
function assertClaudeMarketplaceSource(value, path, version, invalid) {
|
|
12536
|
+
const source = object3(value, path, invalid), kind = string2(source.source, `${path}.source`, invalid);
|
|
12537
|
+
switch (kind) {
|
|
12538
|
+
case "url":
|
|
12539
|
+
url(source.url, `${path}.url`, invalid);
|
|
12540
|
+
headers(source, path, invalid);
|
|
12541
|
+
break;
|
|
12542
|
+
case "github":
|
|
12543
|
+
case "git":
|
|
12544
|
+
string2(source[kind === "github" ? "repo" : "url"], `${path}.${kind === "github" ? "repo" : "url"}`, invalid);
|
|
12545
|
+
optionalStrings(source, ["ref", "path"], path, invalid);
|
|
12546
|
+
optionalBoolean(source, "skipLfs", path, invalid);
|
|
12547
|
+
if (source.sparsePaths !== undefined && (!Array.isArray(source.sparsePaths) || source.sparsePaths.some((v) => typeof v !== "string")))
|
|
12548
|
+
invalid(`${path}.sparsePaths`, "array of strings");
|
|
12549
|
+
break;
|
|
12550
|
+
case "npm": {
|
|
12551
|
+
const pkg = string2(source.package, `${path}.package`, invalid);
|
|
12552
|
+
if (!(/^@[a-z0-9][a-z0-9._-]*\/\*$/u.test(pkg) || !pkg.includes("..") && !pkg.includes("//") && /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u.test(pkg)))
|
|
12553
|
+
invalid(`${path}.package`, "native npm package name or scope wildcard");
|
|
12554
|
+
optionalStrings(source, ["version"], path, invalid);
|
|
12555
|
+
registry2(source, path, invalid);
|
|
12556
|
+
break;
|
|
12557
|
+
}
|
|
12558
|
+
case "file":
|
|
12559
|
+
case "directory":
|
|
12560
|
+
string2(source.path, `${path}.path`, invalid);
|
|
12561
|
+
break;
|
|
12562
|
+
case "skills-dir":
|
|
12563
|
+
break;
|
|
12564
|
+
case "hostPattern":
|
|
12565
|
+
string2(source.hostPattern, `${path}.hostPattern`, invalid);
|
|
12566
|
+
break;
|
|
12567
|
+
case "pathPattern":
|
|
12568
|
+
string2(source.pathPattern, `${path}.pathPattern`, invalid);
|
|
12569
|
+
break;
|
|
12570
|
+
case "settings": {
|
|
12571
|
+
name(source.name, `${path}.name`, true, version, invalid);
|
|
12572
|
+
if (!Array.isArray(source.plugins))
|
|
12573
|
+
return invalid(`${path}.plugins`, "array of inline plugins");
|
|
12574
|
+
for (const [index, value] of source.plugins.entries()) {
|
|
12575
|
+
const pluginPath = `${path}.plugins[${index}]`, plugin = object3(value, pluginPath, invalid);
|
|
12576
|
+
name(plugin.name, `${pluginPath}.name`, false, version, invalid);
|
|
12577
|
+
pluginSource(plugin.source, `${pluginPath}.source`, invalid);
|
|
12578
|
+
optionalStrings(plugin, ["description", "version"], pluginPath, invalid);
|
|
12579
|
+
optionalBoolean(plugin, "strict", pluginPath, invalid);
|
|
12580
|
+
headers(plugin, pluginPath, invalid);
|
|
12581
|
+
}
|
|
12582
|
+
if (source.owner !== undefined) {
|
|
12583
|
+
const owner = object3(source.owner, `${path}.owner`, invalid);
|
|
12584
|
+
if (string2(owner.name, `${path}.owner.name`, invalid).length === 0)
|
|
12585
|
+
invalid(`${path}.owner.name`, "nonempty string");
|
|
12586
|
+
optionalStrings(owner, ["email", "url"], `${path}.owner`, invalid);
|
|
12587
|
+
}
|
|
12588
|
+
break;
|
|
12589
|
+
}
|
|
12590
|
+
default:
|
|
12591
|
+
invalid(`${path}.source`, "native marketplace source discriminator");
|
|
12592
|
+
}
|
|
12593
|
+
}
|
|
12594
|
+
|
|
12396
12595
|
// src/mcp-write.ts
|
|
12397
12596
|
import { writeFileSync as writeFileSync2 } from "node:fs";
|
|
12398
12597
|
import { join as join20 } from "node:path";
|
|
@@ -14010,7 +14209,8 @@ function selectLifecycleRoute(adapterId, profiles, request) {
|
|
|
14010
14209
|
if (detectedVersion !== undefined && managedAdmission.status === "admitted") {
|
|
14011
14210
|
return selectedDecision(adapterId, target, request, detectedVersion, "managed", managedAdmission);
|
|
14012
14211
|
}
|
|
14013
|
-
const
|
|
14212
|
+
const versionDiagnostic = request.version.kind === "detected" ? undefined : request.version.diagnostic;
|
|
14213
|
+
const gaps = dedupeGaps([...nativeGaps, ...managedAdmission.gaps].map((gap) => versionDiagnostic === undefined ? gap : { ...gap, diagnostic: `${gap.diagnostic}. ${versionDiagnostic}` }));
|
|
14014
14214
|
if (gaps.length === 0)
|
|
14015
14215
|
invariant("a refused route decision must retain at least one capability gap");
|
|
14016
14216
|
return deepFreeze({
|
|
@@ -15127,7 +15327,9 @@ function normalizeVersionObservation(input) {
|
|
|
15127
15327
|
});
|
|
15128
15328
|
if (input.kind !== "unknown" && input.kind !== "unparseable")
|
|
15129
15329
|
invariant("target version observation is invalid");
|
|
15130
|
-
return Object.freeze({ kind: input.kind }
|
|
15330
|
+
return Object.freeze({ kind: input.kind, ...input.diagnostic === undefined ? {} : {
|
|
15331
|
+
diagnostic: stableCopy(input.diagnostic, "version probe diagnostic")
|
|
15332
|
+
} });
|
|
15131
15333
|
}
|
|
15132
15334
|
function frozenTarget(input) {
|
|
15133
15335
|
const parsed = parsePersistedTargetIdentity(input, "lifecycle target");
|
|
@@ -15624,6 +15826,22 @@ function readMarketplaces(file) {
|
|
|
15624
15826
|
throw new Error(`Invalid Claude Code marketplace registry: ${file}`);
|
|
15625
15827
|
return value;
|
|
15626
15828
|
}
|
|
15829
|
+
function assertLifecycleMarketplaceEnvelope(version) {
|
|
15830
|
+
const file = join22(pluginsDir(), "known_marketplaces.json");
|
|
15831
|
+
for (const [name, entry] of Object.entries(readMarketplaces(file))) {
|
|
15832
|
+
const invalid = (field, expectation) => {
|
|
15833
|
+
throw new Error(`Invalid Claude Code marketplace registry: ${file}: ${name}.${field}: expected ${expectation}; repair the native configuration before retrying (no automatic edits)`);
|
|
15834
|
+
};
|
|
15835
|
+
if (!isRecord6(entry))
|
|
15836
|
+
return invalid("entry", "object");
|
|
15837
|
+
assertClaudeMarketplaceSource(entry["source"], "source", version, invalid);
|
|
15838
|
+
for (const field of ["installLocation", "lastUpdated"])
|
|
15839
|
+
if (typeof entry[field] !== "string")
|
|
15840
|
+
invalid(field, "string");
|
|
15841
|
+
if (entry["autoUpdate"] !== undefined && typeof entry["autoUpdate"] !== "boolean")
|
|
15842
|
+
invalid("autoUpdate", "boolean");
|
|
15843
|
+
}
|
|
15844
|
+
}
|
|
15627
15845
|
function marketplacesWithLocalSource(marketplaces, name, sourcePath, sourceIdentity, plugin, forceWrapper) {
|
|
15628
15846
|
const manifest = join22(sourcePath, ".claude-plugin", "marketplace.json");
|
|
15629
15847
|
const catalog = validateMarketplaceRegistration(marketplaces, name, sourcePath, sourceIdentity, plugin, forceWrapper);
|
|
@@ -16088,18 +16306,30 @@ function claudeCodeLifecycleDefinition(contracts) {
|
|
|
16088
16306
|
id: "claude-code",
|
|
16089
16307
|
evidenceProfiles: claudeEvidenceProfiles(detectedVersion, contracts),
|
|
16090
16308
|
probeVersion: async () => version,
|
|
16091
|
-
observeTarget: async (target) => ({ target, installations: observeClaudeInstallations() }),
|
|
16309
|
+
observeTarget: async (target) => ({ target, installations: observeClaudeInstallations(target, detectedVersion) }),
|
|
16092
16310
|
observeNativeMutationScope: async (request) => ({
|
|
16093
16311
|
kind: "bounded",
|
|
16094
16312
|
mode: "exact-package",
|
|
16095
16313
|
affectedNativeIds: [request.nativeId]
|
|
16096
16314
|
}),
|
|
16097
|
-
observeNativeProjection: async (request) =>
|
|
16315
|
+
observeNativeProjection: async (request) => {
|
|
16316
|
+
const result = nativeProjection(contracts, request, detectedVersion);
|
|
16317
|
+
if ("snapshot" in request && result.kind === "requires-managed") {
|
|
16318
|
+
for (const path of ["known_marketplaces.json", "installed_plugins.json", "../settings.json"])
|
|
16319
|
+
assertManagedPath(resolve11(join22(pluginsDir(), path)));
|
|
16320
|
+
assertManagedPath(cacheSlot(request.snapshot.nativeId, slotVersion(request.snapshot.inventory.package.version, request.snapshot.immutableRevision)));
|
|
16321
|
+
if (!matchingForeignLifecycleCatalog(request.snapshot.nativeId, request.snapshot.scopeId, request.snapshot.target, request.snapshot.packageRoot, request.snapshot.inventory.package.version ?? request.snapshot.immutableRevision))
|
|
16322
|
+
validateLifecycleCatalog(request.snapshot.nativeId, request.snapshot.scopeId);
|
|
16323
|
+
}
|
|
16324
|
+
if (!("snapshot" in request) && request.operation === "retire")
|
|
16325
|
+
validateLifecycleCatalogRetirement(request.activation.nativeId, request.activation.scopeId);
|
|
16326
|
+
return result;
|
|
16327
|
+
},
|
|
16098
16328
|
revalidateTargetPrecondition: async (handle) => ({
|
|
16099
16329
|
version: claudeVersionObservation(observeClaudeCodeVersion()),
|
|
16100
16330
|
targetObservationId: createTargetInventoryObservation("claude-code", {
|
|
16101
16331
|
target: handle.target,
|
|
16102
|
-
installations: observeClaudeInstallations()
|
|
16332
|
+
installations: observeClaudeInstallations(handle.target, detectedVersion)
|
|
16103
16333
|
}).observationId
|
|
16104
16334
|
}),
|
|
16105
16335
|
stageActivation: async (request) => {
|
|
@@ -16125,7 +16355,8 @@ function claudeCodeLifecycleDefinition(contracts) {
|
|
|
16125
16355
|
},
|
|
16126
16356
|
captureActivationPreparation: async (projection, projectedFingerprint) => {
|
|
16127
16357
|
const installPath = cacheSlot(projection.nativeId, slotVersion(projection.packageVersion, projection.sourceRevision));
|
|
16128
|
-
const prior = claudeReadback(projection, "none");
|
|
16358
|
+
const prior = claudeReadback(projection, "none", detectedVersion);
|
|
16359
|
+
const nativeCatalog = matchingForeignLifecycleCatalog(projection.nativeId, projection.scopeId, projection.target, projection.stagingRoot, slotVersion(projection.packageVersion, projection.sourceRevision));
|
|
16129
16360
|
return {
|
|
16130
16361
|
prior,
|
|
16131
16362
|
expected: {
|
|
@@ -16136,20 +16367,24 @@ function claudeCodeLifecycleDefinition(contracts) {
|
|
|
16136
16367
|
activation: "active",
|
|
16137
16368
|
transition: sessionRestart(),
|
|
16138
16369
|
installedFingerprint: projectedFingerprint,
|
|
16139
|
-
contentRoots: [
|
|
16370
|
+
contentRoots: [
|
|
16371
|
+
{ label: "plugin", path: installPath, fingerprint: projectedFingerprint },
|
|
16372
|
+
{ label: "catalog-plugin", path: nativeCatalog ?? lifecycleCatalogCopy(projection.nativeId), fingerprint: nativeCatalog === null ? projectedFingerprint : fingerprintTree(nativeCatalog) },
|
|
16373
|
+
...nativeCatalog === null ? [{ label: "selected-catalog-entry", path: join22(lifecycleCatalogRoot(projection.nativeId), ".claude-plugin", "marketplace.json"), fingerprint: catalogEntryFingerprint(ownedLifecycleCatalogEntry(projection.nativeId)) }] : lifecycleCatalogEntryObservation(projection.nativeId)
|
|
16374
|
+
]
|
|
16140
16375
|
},
|
|
16141
16376
|
rollbackReference: captureRollback(projection.attemptId, projection.operationId, projection.nativeId),
|
|
16142
16377
|
rollbackCoverageOperationIds: [...projection.affectedOperationIds]
|
|
16143
16378
|
};
|
|
16144
16379
|
},
|
|
16145
16380
|
captureDisablePreparation: async (request) => ({
|
|
16146
|
-
prior: claudeReadback(activationIdentity(request.activation), "none"),
|
|
16381
|
+
prior: claudeReadback(activationIdentity(request.activation), "none", detectedVersion),
|
|
16147
16382
|
rollbackReference: captureRollback(request.attemptId, request.operationId, request.activation.nativeId),
|
|
16148
16383
|
rollbackCoverageOperationIds: [...request.selection.affectedOperationIds],
|
|
16149
16384
|
transition: sessionRestart()
|
|
16150
16385
|
}),
|
|
16151
16386
|
captureRetirementPreparation: async (request) => ({
|
|
16152
|
-
prior: claudeReadback(activationIdentity(request.activation), "none"),
|
|
16387
|
+
prior: claudeReadback(activationIdentity(request.activation), "none", detectedVersion),
|
|
16153
16388
|
rollbackReference: captureRollback(request.attemptId, request.operationId, request.activation.nativeId),
|
|
16154
16389
|
rollbackCoverageOperationIds: [...request.selection.affectedOperationIds],
|
|
16155
16390
|
transition: settledTransition()
|
|
@@ -16161,6 +16396,7 @@ function claudeCodeLifecycleDefinition(contracts) {
|
|
|
16161
16396
|
},
|
|
16162
16397
|
disable: async (prepared) => setEnabled(prepared.handle, false, `claude-disable-${prepared.handle.operationId}`),
|
|
16163
16398
|
retire: async (prepared) => {
|
|
16399
|
+
retireLifecycleCatalog(prepared.handle.nativeId, prepared.handle.scopeId);
|
|
16164
16400
|
const installPath = recordedInstallPath(prepared.handle.nativeId);
|
|
16165
16401
|
const existed = installPath !== undefined && existsSync17(installPath);
|
|
16166
16402
|
if (installPath !== undefined)
|
|
@@ -16170,7 +16406,7 @@ function claudeCodeLifecycleDefinition(contracts) {
|
|
|
16170
16406
|
return { receiptId: `claude-retire-${prepared.handle.operationId}`, changed: existed };
|
|
16171
16407
|
},
|
|
16172
16408
|
readback: async (handle) => {
|
|
16173
|
-
const observed = claudeReadback(handle, handle.action === "remove" || handle.action === "retire-orphan" ? handle.route : "none");
|
|
16409
|
+
const observed = claudeReadback(handle, handle.action === "remove" || handle.action === "retire-orphan" ? handle.route : "none", detectedVersion);
|
|
16174
16410
|
return { ...observed, transition: readbackTransition(handle, observed) };
|
|
16175
16411
|
},
|
|
16176
16412
|
rollback: async (handle) => restoreRollback(handle),
|
|
@@ -16227,14 +16463,15 @@ function claudeVersionObservation(observed) {
|
|
|
16227
16463
|
return { kind: "detected", version: observed.version, probeId: observed.probeId };
|
|
16228
16464
|
return { kind: observed.kind };
|
|
16229
16465
|
}
|
|
16466
|
+
var CLAUDE_MANAGED_RELEASES = new Set(["2.1.275", "2.1.295"]);
|
|
16230
16467
|
function claudeEvidenceProfiles(version, contracts) {
|
|
16231
16468
|
if (version === undefined)
|
|
16232
16469
|
return [];
|
|
16233
|
-
const managed = claudeProfile(version, "managed", ["local", "git"], ["install", "update", "disable", "retire"]);
|
|
16470
|
+
const managed = CLAUDE_MANAGED_RELEASES.has(version) ? [claudeProfile(version, "managed", ["local", "git"], ["install", "update", "disable", "retire"])] : [];
|
|
16234
16471
|
const native = contracts.filter((contract) => contract.version === version && sandboxPasses(contract));
|
|
16235
16472
|
if (native.length > 1)
|
|
16236
16473
|
throw new Error(`ambiguous Claude Code native update contract for ${version}`);
|
|
16237
|
-
return native.length === 0 ?
|
|
16474
|
+
return native.length === 0 ? managed : [...managed, claudeProfile(version, "native", ["git"], ["update"])];
|
|
16238
16475
|
}
|
|
16239
16476
|
function sandboxPasses(contract) {
|
|
16240
16477
|
return contract.source === "git" && contract.action === "update" && contract.consumesExactSnapshot && contract.forcesSameVersionBytes && contract.rollbackProven && contract.readbackProven;
|
|
@@ -16273,7 +16510,8 @@ function claudeSemantics() {
|
|
|
16273
16510
|
CLAUDE_PROJECTED_SEMANTICS.has(semantic) ? "supported" : "unsupported"
|
|
16274
16511
|
]));
|
|
16275
16512
|
}
|
|
16276
|
-
function observeClaudeInstallations() {
|
|
16513
|
+
function observeClaudeInstallations(target, version) {
|
|
16514
|
+
assertLifecycleMarketplaceEnvelope(version);
|
|
16277
16515
|
const registry = readRegistry(join22(pluginsDir(), "installed_plugins.json"));
|
|
16278
16516
|
const settings = readSettings(join22(pluginsDir(), "..", "settings.json"));
|
|
16279
16517
|
const enabled = settings["enabledPlugins"];
|
|
@@ -16315,11 +16553,11 @@ function observeClaudeInstallations() {
|
|
|
16315
16553
|
} : { kind: "unmanaged" },
|
|
16316
16554
|
presence: "present",
|
|
16317
16555
|
enablement: on ? "enabled" : "disabled",
|
|
16318
|
-
activation: on ? "active" : "inactive",
|
|
16556
|
+
activation: on ? lifecycleCatalogConforms(id, installPath, target) ? "active" : "nonconforming" : "inactive",
|
|
16319
16557
|
installedFingerprint: digest,
|
|
16320
16558
|
installedVersion: typeof row["version"] === "string" ? row["version"] : null,
|
|
16321
16559
|
source: revision !== undefined && locator !== undefined ? { type: "git", immutableRevision: revision, locator } : null,
|
|
16322
|
-
contentRoots:
|
|
16560
|
+
contentRoots: lifecycleContentRoots(id, installPath, digest)
|
|
16323
16561
|
});
|
|
16324
16562
|
}
|
|
16325
16563
|
return installations;
|
|
@@ -16367,7 +16605,8 @@ function sameReadbackState(observed, target) {
|
|
|
16367
16605
|
function activationIdentity(activation) {
|
|
16368
16606
|
return { adapterId: "claude-code", ...activation };
|
|
16369
16607
|
}
|
|
16370
|
-
function claudeReadback(identity, absentRoute) {
|
|
16608
|
+
function claudeReadback(identity, absentRoute, version) {
|
|
16609
|
+
assertLifecycleMarketplaceEnvelope(version);
|
|
16371
16610
|
const installPath = recordedInstallPath(identity.nativeId);
|
|
16372
16611
|
const present = installPath !== undefined && existsSync17(installPath);
|
|
16373
16612
|
const retention = {
|
|
@@ -16394,10 +16633,10 @@ function claudeReadback(identity, absentRoute) {
|
|
|
16394
16633
|
route: installedRoute(installPath),
|
|
16395
16634
|
presence: "present",
|
|
16396
16635
|
enablement: enabled ? "enabled" : "disabled",
|
|
16397
|
-
activation: enabled ? "active" : "inactive",
|
|
16636
|
+
activation: enabled ? lifecycleCatalogConforms(identity.nativeId, installPath, identity.target) ? "active" : "unknown" : "inactive",
|
|
16398
16637
|
transition: settledTransition(),
|
|
16399
16638
|
installedFingerprint: digest,
|
|
16400
|
-
contentRoots:
|
|
16639
|
+
contentRoots: lifecycleContentRoots(identity.nativeId, installPath, digest),
|
|
16401
16640
|
retention
|
|
16402
16641
|
};
|
|
16403
16642
|
}
|
|
@@ -16442,6 +16681,306 @@ function cacheSlot(nativeId, version) {
|
|
|
16442
16681
|
const market = at === -1 ? "local" : nativeId.slice(at + 1);
|
|
16443
16682
|
return resolve11(join22(pluginsDir(), "cache", market, name, version));
|
|
16444
16683
|
}
|
|
16684
|
+
function lifecycleCatalogRoot(nativeId) {
|
|
16685
|
+
const market = marketplaceName(nativeId);
|
|
16686
|
+
if (market === undefined || market.includes("/") || market.includes("\\"))
|
|
16687
|
+
throw new Error("Claude Code lifecycle native identity has no safe marketplace");
|
|
16688
|
+
return resolve11(join22(pluginsDir(), "marketplaces", `.plgnz-${market}`));
|
|
16689
|
+
}
|
|
16690
|
+
function lifecycleCatalogCopy(nativeId) {
|
|
16691
|
+
const name = nativeId.slice(0, nativeId.indexOf("@"));
|
|
16692
|
+
if (!name || name.includes("/") || name.includes("\\"))
|
|
16693
|
+
throw new Error("Claude Code lifecycle native identity has no safe package name");
|
|
16694
|
+
return join22(lifecycleCatalogRoot(nativeId), "plugins", name);
|
|
16695
|
+
}
|
|
16696
|
+
function registeredLifecycleCatalogMember(nativeId) {
|
|
16697
|
+
const market = marketplaceName(nativeId);
|
|
16698
|
+
if (market === undefined)
|
|
16699
|
+
return null;
|
|
16700
|
+
const entry = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"))[market];
|
|
16701
|
+
if (!isRecord6(entry) || !isRecord6(entry["source"]) || entry["source"]["source"] !== "directory" || typeof entry["source"]["path"] !== "string" || entry["installLocation"] !== entry["source"]["path"])
|
|
16702
|
+
return null;
|
|
16703
|
+
const root = entry["source"]["path"];
|
|
16704
|
+
if (!root.startsWith("/") || resolve11(root) !== root || !existsSync17(root))
|
|
16705
|
+
return null;
|
|
16706
|
+
assertCatalogPath(root, join22(root, ".claude-plugin", "marketplace.json"));
|
|
16707
|
+
const selected = catalogEntries(lifecycleCatalogDocument(root, market)).filter((item) => isCatalogMember(item, nativeId));
|
|
16708
|
+
if (selected.length !== 1 || !isRecord6(selected[0]) || typeof selected[0]["source"] !== "string" || !selected[0]["source"].startsWith("./"))
|
|
16709
|
+
return null;
|
|
16710
|
+
const member = resolve11(root, selected[0]["source"]);
|
|
16711
|
+
if (!containedPath(root, member) || root === member || !existsSync17(member))
|
|
16712
|
+
return null;
|
|
16713
|
+
assertCatalogPath(root, member);
|
|
16714
|
+
assertNoSymlinks2(member);
|
|
16715
|
+
return { root, member, entry: selected[0] };
|
|
16716
|
+
}
|
|
16717
|
+
function matchingForeignLifecycleCatalog(nativeId, scopeId, target, packageRoot, version) {
|
|
16718
|
+
const catalog = registeredLifecycleCatalogMember(nativeId);
|
|
16719
|
+
if (catalog === null || catalog.root === lifecycleCatalogRoot(nativeId))
|
|
16720
|
+
return null;
|
|
16721
|
+
if (createDeploymentScopeIdentity({ kind: "local", locator: catalog.root }, target).id !== scopeId)
|
|
16722
|
+
return null;
|
|
16723
|
+
if (!foreignLifecycleEntryEquivalent(catalog.entry))
|
|
16724
|
+
return null;
|
|
16725
|
+
if (!existsSync17(join22(catalog.member, ".claude-plugin", "plugin.json")))
|
|
16726
|
+
return null;
|
|
16727
|
+
const proof = mkdtempSync3(join22(tmpdir(), "plgnz-claude-native-member-"));
|
|
16728
|
+
try {
|
|
16729
|
+
stagePlugin(packageRoot, join22(proof, "incoming"), nativeId.slice(0, nativeId.indexOf("@")), version);
|
|
16730
|
+
cpSync3(catalog.member, join22(proof, "native"), { recursive: true });
|
|
16731
|
+
for (const path of [join22(proof, "incoming"), join22(proof, "native")]) {
|
|
16732
|
+
rmSync3(join22(path, ".plgnz-lifecycle.json"), { force: true });
|
|
16733
|
+
rmSync3(join22(path, OWNERSHIP), { force: true });
|
|
16734
|
+
}
|
|
16735
|
+
return fingerprintTree(join22(proof, "incoming")) === fingerprintTree(join22(proof, "native")) ? catalog.member : null;
|
|
16736
|
+
} finally {
|
|
16737
|
+
rmSync3(proof, { recursive: true, force: true });
|
|
16738
|
+
}
|
|
16739
|
+
}
|
|
16740
|
+
function lifecycleCatalogDocument(root, market) {
|
|
16741
|
+
const file = join22(root, ".claude-plugin", "marketplace.json");
|
|
16742
|
+
if (!existsSync17(file))
|
|
16743
|
+
return null;
|
|
16744
|
+
const parsed = JSON.parse(readFileSync16(file, "utf8"));
|
|
16745
|
+
if (!isRecord6(parsed) || parsed["name"] !== market || !Array.isArray(parsed["plugins"]))
|
|
16746
|
+
throw new Error(`Invalid Claude Code lifecycle catalog identity: ${file}`);
|
|
16747
|
+
return parsed;
|
|
16748
|
+
}
|
|
16749
|
+
function catalogEntries(document) {
|
|
16750
|
+
return document === null ? [] : document["plugins"];
|
|
16751
|
+
}
|
|
16752
|
+
function isCatalogMember(entry, nativeId) {
|
|
16753
|
+
return isRecord6(entry) && entry["name"] === nativeId.slice(0, nativeId.indexOf("@"));
|
|
16754
|
+
}
|
|
16755
|
+
function ownedLifecycleCatalogEntry(nativeId) {
|
|
16756
|
+
const name = nativeId.slice(0, nativeId.indexOf("@"));
|
|
16757
|
+
return { name, source: `./plugins/${name}` };
|
|
16758
|
+
}
|
|
16759
|
+
function catalogEntryFingerprint(entry) {
|
|
16760
|
+
const canonical = (value) => Array.isArray(value) ? `[${value.map(canonical).join(",")}]` : isRecord6(value) ? `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}` : JSON.stringify(value);
|
|
16761
|
+
const hash = new CryptoHasher("sha256");
|
|
16762
|
+
hash.update(canonical(entry));
|
|
16763
|
+
return hash.digest("hex");
|
|
16764
|
+
}
|
|
16765
|
+
function foreignLifecycleEntryEquivalent(entry) {
|
|
16766
|
+
if (["commands", "agents", "skills", "hooks", "outputStyles", "themes"].some((key) => Object.hasOwn(entry, key)))
|
|
16767
|
+
return false;
|
|
16768
|
+
if (entry["strict"] !== undefined && typeof entry["strict"] !== "boolean")
|
|
16769
|
+
return false;
|
|
16770
|
+
if (entry["dependencies"] !== undefined && (!Array.isArray(entry["dependencies"]) || entry["dependencies"].length !== 0))
|
|
16771
|
+
return false;
|
|
16772
|
+
if (entry["defaultEnabled"] !== undefined && typeof entry["defaultEnabled"] !== "boolean")
|
|
16773
|
+
return false;
|
|
16774
|
+
return true;
|
|
16775
|
+
}
|
|
16776
|
+
function lifecycleCatalogEntryObservation(nativeId) {
|
|
16777
|
+
const root = registeredLifecycleCatalogMember(nativeId)?.root ?? lifecycleCatalogRoot(nativeId);
|
|
16778
|
+
const document = lifecycleCatalogDocument(root, marketplaceName(nativeId));
|
|
16779
|
+
const entries = catalogEntries(document).filter((entry) => isCatalogMember(entry, nativeId));
|
|
16780
|
+
return entries.length === 1 ? [{ label: "selected-catalog-entry", path: join22(root, ".claude-plugin", "marketplace.json"), fingerprint: catalogEntryFingerprint(entries[0]) }] : [];
|
|
16781
|
+
}
|
|
16782
|
+
function validateLifecycleCatalog(nativeId, scopeId) {
|
|
16783
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16784
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16785
|
+
const market = marketplaceName(nativeId);
|
|
16786
|
+
assertManagedPath(root);
|
|
16787
|
+
assertManagedPath(join22(root, ".claude-plugin", "marketplace.json"));
|
|
16788
|
+
assertManagedPath(join22(root, OWNERSHIP));
|
|
16789
|
+
assertManagedPath(copy);
|
|
16790
|
+
if (existsSync17(root)) {
|
|
16791
|
+
const owner = readOwnership(root);
|
|
16792
|
+
if (owner?.source !== "plgnz-wrapper" || owner.pluginId !== `marketplace@${market}`)
|
|
16793
|
+
throw new Error(`Claude Code marketplace wrapper ${root} is foreign; refusing to replace it`);
|
|
16794
|
+
}
|
|
16795
|
+
const document = lifecycleCatalogDocument(root, market);
|
|
16796
|
+
const selected = catalogEntries(document).filter((entry) => isCatalogMember(entry, nativeId));
|
|
16797
|
+
if (selected.length > 1 || selected.some((entry) => !isRecord6(entry) || entry["source"] !== `./plugins/${nativeId.slice(0, nativeId.indexOf("@"))}`))
|
|
16798
|
+
throw new Error(`Claude Code marketplace member ${nativeId} is foreign or ambiguous; refusing to replace it`);
|
|
16799
|
+
if (existsSync17(copy)) {
|
|
16800
|
+
assertNoSymlinks2(copy);
|
|
16801
|
+
const owner = readOwnership(copy);
|
|
16802
|
+
if (owner?.pluginId !== nativeId || owner.scopeId !== scopeId)
|
|
16803
|
+
throw new Error(`Claude Code marketplace member ${copy} has a different owned scope; refusing to replace it`);
|
|
16804
|
+
} else if (selected.length > 0) {
|
|
16805
|
+
const installed = recordedInstallPath(nativeId);
|
|
16806
|
+
const owner = installed === undefined ? null : readOwnership(installed);
|
|
16807
|
+
if (owner?.pluginId !== nativeId || owner.scopeId !== scopeId)
|
|
16808
|
+
throw new Error(`Claude Code marketplace member ${nativeId} has no ownership proof; refusing to replace it`);
|
|
16809
|
+
}
|
|
16810
|
+
const known = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"));
|
|
16811
|
+
const extra = readSettings(join22(pluginsDir(), "..", "settings.json"))["extraKnownMarketplaces"];
|
|
16812
|
+
for (const entry of [known[market], isRecord6(extra) ? extra[market] : undefined]) {
|
|
16813
|
+
if (entry === undefined)
|
|
16814
|
+
continue;
|
|
16815
|
+
if (!isRecord6(entry) || !isRecord6(entry["source"]) || entry["source"]["source"] !== "directory" || entry["source"]["path"] !== root || entry["installLocation"] !== undefined && entry["installLocation"] !== root)
|
|
16816
|
+
throw new Error(`Claude Code marketplace ${market} is registered from a different source; refusing to replace it`);
|
|
16817
|
+
}
|
|
16818
|
+
}
|
|
16819
|
+
function installLifecycleCatalog(nativeId, scopeId, stagingRoot) {
|
|
16820
|
+
validateLifecycleCatalog(nativeId, scopeId);
|
|
16821
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16822
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16823
|
+
const market = marketplaceName(nativeId);
|
|
16824
|
+
const document = lifecycleCatalogDocument(root, market) ?? { name: market, owner: { name: "plgnz" }, plugins: [] };
|
|
16825
|
+
rmSync3(copy, { recursive: true, force: true });
|
|
16826
|
+
mkdirSync3(dirname6(copy), { recursive: true });
|
|
16827
|
+
cpSync3(stagingRoot, copy, { recursive: true });
|
|
16828
|
+
mkdirSync3(join22(root, ".claude-plugin"), { recursive: true });
|
|
16829
|
+
writeFileSync3(join22(root, ".claude-plugin", "marketplace.json"), JSON.stringify({ ...document, plugins: [...catalogEntries(document).filter((entry) => !isCatalogMember(entry, nativeId)), ownedLifecycleCatalogEntry(nativeId)] }));
|
|
16830
|
+
writeFileSync3(join22(root, OWNERSHIP), JSON.stringify({ source: "plgnz-wrapper", pluginId: `marketplace@${market}`, fingerprint: "" }));
|
|
16831
|
+
const file = join22(pluginsDir(), "known_marketplaces.json");
|
|
16832
|
+
const known = readMarketplaces(file);
|
|
16833
|
+
const previous = isRecord6(known[market]) ? known[market] : {};
|
|
16834
|
+
writeMarketplaces(file, { ...known, [market]: { ...previous, source: { source: "directory", path: root }, installLocation: root, lastUpdated: new Date().toISOString() } });
|
|
16835
|
+
}
|
|
16836
|
+
function lifecycleCatalogConforms(nativeId, installPath, target) {
|
|
16837
|
+
const market = marketplaceName(nativeId);
|
|
16838
|
+
if (market === undefined)
|
|
16839
|
+
return false;
|
|
16840
|
+
const registered = registeredLifecycleCatalogMember(nativeId);
|
|
16841
|
+
const owner = readOwnership(installPath);
|
|
16842
|
+
if (registered !== null && registered.root !== lifecycleCatalogRoot(nativeId)) {
|
|
16843
|
+
if (owner?.scopeId === undefined)
|
|
16844
|
+
return false;
|
|
16845
|
+
const manifest = existsSync17(join22(installPath, ".plugin", "plugin.json")) ? join22(installPath, ".plugin", "plugin.json") : join22(installPath, "plugin.json");
|
|
16846
|
+
return matchingForeignLifecycleCatalog(nativeId, owner.scopeId, target, installPath, parseManifest(manifest, "canonical").version ?? "") !== null;
|
|
16847
|
+
}
|
|
16848
|
+
const known = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"))[market];
|
|
16849
|
+
if (!isRecord6(known) || !isRecord6(known["source"]) || known["source"]["source"] !== "directory" || known["source"]["path"] !== lifecycleCatalogRoot(nativeId) || known["installLocation"] !== lifecycleCatalogRoot(nativeId))
|
|
16850
|
+
return false;
|
|
16851
|
+
const entries = catalogEntries(lifecycleCatalogDocument(lifecycleCatalogRoot(nativeId), market)).filter((entry) => isCatalogMember(entry, nativeId));
|
|
16852
|
+
if (entries.length !== 1 || catalogEntryFingerprint(entries[0]) !== catalogEntryFingerprint(ownedLifecycleCatalogEntry(nativeId)))
|
|
16853
|
+
return false;
|
|
16854
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16855
|
+
if (!existsSync17(copy))
|
|
16856
|
+
return false;
|
|
16857
|
+
assertManagedPath(copy);
|
|
16858
|
+
assertNoSymlinks2(copy);
|
|
16859
|
+
const catalogOwner = readOwnership(copy);
|
|
16860
|
+
return owner?.pluginId === nativeId && catalogOwner?.pluginId === nativeId && owner.scopeId === catalogOwner.scopeId && fingerprintTree(copy) === fingerprintTree(installPath);
|
|
16861
|
+
}
|
|
16862
|
+
function lifecycleContentRoots(nativeId, installPath, digest) {
|
|
16863
|
+
const copy = registeredLifecycleCatalogMember(nativeId)?.member ?? lifecycleCatalogCopy(nativeId);
|
|
16864
|
+
return [{ label: "plugin", path: installPath, fingerprint: digest }, ...existsSync17(copy) ? [{ label: "catalog-plugin", path: copy, fingerprint: fingerprintTree(copy) }] : [], ...lifecycleCatalogEntryObservation(nativeId)];
|
|
16865
|
+
}
|
|
16866
|
+
function retireLifecycleCatalog(nativeId, scopeId) {
|
|
16867
|
+
validateLifecycleCatalogRetirement(nativeId, scopeId);
|
|
16868
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16869
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16870
|
+
if (!existsSync17(root))
|
|
16871
|
+
return;
|
|
16872
|
+
assertManagedPath(root);
|
|
16873
|
+
const market = marketplaceName(nativeId);
|
|
16874
|
+
const owner = readOwnership(root);
|
|
16875
|
+
if (owner?.source !== "plgnz-wrapper" || owner.pluginId !== `marketplace@${market}`)
|
|
16876
|
+
return;
|
|
16877
|
+
const document = lifecycleCatalogDocument(root, market);
|
|
16878
|
+
if (existsSync17(copy)) {
|
|
16879
|
+
const member = readOwnership(copy);
|
|
16880
|
+
if (member?.pluginId !== nativeId || member.scopeId !== scopeId)
|
|
16881
|
+
throw new Error(`Claude Code catalog retirement cannot prove ownership: ${copy}`);
|
|
16882
|
+
rmSync3(copy, { recursive: true, force: true });
|
|
16883
|
+
}
|
|
16884
|
+
const entries = catalogEntries(document).filter((entry) => !isCatalogMember(entry, nativeId));
|
|
16885
|
+
if (document !== null)
|
|
16886
|
+
writeFileSync3(join22(root, ".claude-plugin", "marketplace.json"), JSON.stringify({ ...document, plugins: entries }));
|
|
16887
|
+
if (entries.length === 0 && emptyOwnedCatalogContainer(root)) {
|
|
16888
|
+
rmSync3(root, { recursive: true, force: true });
|
|
16889
|
+
const file = join22(pluginsDir(), "known_marketplaces.json");
|
|
16890
|
+
const known = readMarketplaces(file);
|
|
16891
|
+
const entry = known[market];
|
|
16892
|
+
if (isRecord6(entry) && isRecord6(entry["source"]) && entry["source"]["path"] === root) {
|
|
16893
|
+
const next = { ...known };
|
|
16894
|
+
delete next[market];
|
|
16895
|
+
writeMarketplaces(file, next);
|
|
16896
|
+
}
|
|
16897
|
+
}
|
|
16898
|
+
}
|
|
16899
|
+
function emptyOwnedCatalogContainer(root) {
|
|
16900
|
+
if (!existsSync17(root))
|
|
16901
|
+
return true;
|
|
16902
|
+
if (readdirSync11(root).some((name) => ![OWNERSHIP, ".claude-plugin", "plugins"].includes(name)))
|
|
16903
|
+
return false;
|
|
16904
|
+
if (existsSync17(join22(root, ".claude-plugin")) && readdirSync11(join22(root, ".claude-plugin")).some((name) => name !== "marketplace.json"))
|
|
16905
|
+
return false;
|
|
16906
|
+
return !existsSync17(join22(root, "plugins")) || readdirSync11(join22(root, "plugins")).length === 0;
|
|
16907
|
+
}
|
|
16908
|
+
function validateLifecycleCatalogRetirement(nativeId, scopeId) {
|
|
16909
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16910
|
+
if (!existsSync17(root))
|
|
16911
|
+
return;
|
|
16912
|
+
assertManagedPath(root);
|
|
16913
|
+
assertManagedPath(join22(root, OWNERSHIP));
|
|
16914
|
+
const owner = readOwnership(root);
|
|
16915
|
+
if (owner?.source !== "plgnz-wrapper" || owner.pluginId !== `marketplace@${marketplaceName(nativeId)}`) {
|
|
16916
|
+
const member = readOwnership(lifecycleCatalogCopy(nativeId));
|
|
16917
|
+
if (member?.pluginId === nativeId && member.scopeId === scopeId)
|
|
16918
|
+
throw new Error(`Claude Code catalog retirement lost its container ownership proof: ${root}`);
|
|
16919
|
+
return;
|
|
16920
|
+
}
|
|
16921
|
+
assertManagedPath(join22(root, ".claude-plugin", "marketplace.json"));
|
|
16922
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16923
|
+
assertManagedPath(copy);
|
|
16924
|
+
const selected = catalogEntries(lifecycleCatalogDocument(root, marketplaceName(nativeId))).filter((entry) => isCatalogMember(entry, nativeId));
|
|
16925
|
+
if (selected.length > 1 || selected.some((entry) => !isRecord6(entry) || entry["source"] !== `./plugins/${nativeId.slice(0, nativeId.indexOf("@"))}`))
|
|
16926
|
+
throw new Error(`Claude Code catalog retirement found a foreign or ambiguous member: ${nativeId}`);
|
|
16927
|
+
if (existsSync17(copy)) {
|
|
16928
|
+
assertNoSymlinks2(copy);
|
|
16929
|
+
const member = readOwnership(copy);
|
|
16930
|
+
if (member?.pluginId !== nativeId || member.scopeId !== scopeId)
|
|
16931
|
+
throw new Error(`Claude Code catalog retirement cannot prove ownership: ${copy}`);
|
|
16932
|
+
}
|
|
16933
|
+
}
|
|
16934
|
+
function captureLifecycleCatalog(reference, nativeId) {
|
|
16935
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16936
|
+
const market = marketplaceName(nativeId);
|
|
16937
|
+
const document = lifecycleCatalogDocument(root, market);
|
|
16938
|
+
writeFileSync3(join22(reference, "catalog.json"), JSON.stringify({ document, selected: catalogEntries(document).filter((entry) => isCatalogMember(entry, nativeId)), marker: existsSync17(join22(root, OWNERSHIP)) ? readFileSync16(join22(root, OWNERSHIP), "utf8") : null }));
|
|
16939
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16940
|
+
if (existsSync17(copy)) {
|
|
16941
|
+
assertNoSymlinks2(copy);
|
|
16942
|
+
cpSync3(copy, join22(reference, "catalog-plugin"), { recursive: true });
|
|
16943
|
+
}
|
|
16944
|
+
}
|
|
16945
|
+
function restoreLifecycleCatalog(reference, nativeId, scopeId) {
|
|
16946
|
+
const saved = JSON.parse(readFileSync16(join22(reference, "catalog.json"), "utf8"));
|
|
16947
|
+
const root = lifecycleCatalogRoot(nativeId);
|
|
16948
|
+
assertManagedPath(root);
|
|
16949
|
+
if (existsSync17(root)) {
|
|
16950
|
+
assertManagedPath(join22(root, OWNERSHIP));
|
|
16951
|
+
const owner = readOwnership(root);
|
|
16952
|
+
if (owner?.source !== "plgnz-wrapper" || owner.pluginId !== `marketplace@${marketplaceName(nativeId)}`)
|
|
16953
|
+
throw new Error(`Claude Code rollback refuses a foreign catalog: ${root}`);
|
|
16954
|
+
}
|
|
16955
|
+
assertManagedPath(join22(root, ".claude-plugin", "marketplace.json"));
|
|
16956
|
+
const market = marketplaceName(nativeId);
|
|
16957
|
+
const current = lifecycleCatalogDocument(root, market);
|
|
16958
|
+
const siblings = catalogEntries(current).filter((entry) => !isCatalogMember(entry, nativeId));
|
|
16959
|
+
const copy = lifecycleCatalogCopy(nativeId);
|
|
16960
|
+
assertManagedPath(copy);
|
|
16961
|
+
if (existsSync17(copy)) {
|
|
16962
|
+
assertNoSymlinks2(copy);
|
|
16963
|
+
const owner = readOwnership(copy);
|
|
16964
|
+
if (owner?.pluginId !== nativeId || owner.scopeId !== scopeId)
|
|
16965
|
+
throw new Error(`Claude Code rollback refuses a foreign catalog member: ${copy}`);
|
|
16966
|
+
}
|
|
16967
|
+
rmSync3(copy, { recursive: true, force: true });
|
|
16968
|
+
if (existsSync17(join22(reference, "catalog-plugin"))) {
|
|
16969
|
+
mkdirSync3(dirname6(copy), { recursive: true });
|
|
16970
|
+
cpSync3(join22(reference, "catalog-plugin"), copy, { recursive: true });
|
|
16971
|
+
}
|
|
16972
|
+
if (saved.document === null && siblings.length === 0 && emptyOwnedCatalogContainer(root)) {
|
|
16973
|
+
rmSync3(root, { recursive: true, force: true });
|
|
16974
|
+
return;
|
|
16975
|
+
}
|
|
16976
|
+
const document = current ?? saved.document;
|
|
16977
|
+
if (document === null)
|
|
16978
|
+
throw new Error("Claude Code catalog rollback has no manifest");
|
|
16979
|
+
mkdirSync3(join22(root, ".claude-plugin"), { recursive: true });
|
|
16980
|
+
writeFileSync3(join22(root, ".claude-plugin", "marketplace.json"), JSON.stringify({ ...document, plugins: [...siblings, ...saved.selected] }));
|
|
16981
|
+
if (saved.marker !== null)
|
|
16982
|
+
writeFileSync3(join22(root, OWNERSHIP), saved.marker);
|
|
16983
|
+
}
|
|
16445
16984
|
function slotVersion(version, revision) {
|
|
16446
16985
|
const selected = version ?? revision;
|
|
16447
16986
|
if (selected.length === 0 || selected.includes("/") || selected.includes("\\")) {
|
|
@@ -16467,6 +17006,7 @@ function captureRollback(attemptId, operationId, nativeId) {
|
|
|
16467
17006
|
const known = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"));
|
|
16468
17007
|
const knownEntry = marketplace !== undefined && Object.hasOwn(known, marketplace) ? known[marketplace] ?? null : null;
|
|
16469
17008
|
writeFileSync3(join22(reference, "known-marketplace.json"), JSON.stringify({ marketplace: marketplace ?? null, entry: knownEntry }));
|
|
17009
|
+
captureLifecycleCatalog(reference, nativeId);
|
|
16470
17010
|
const installPath = recordedInstallPath(nativeId);
|
|
16471
17011
|
if (installPath !== undefined && existsSync17(installPath)) {
|
|
16472
17012
|
cpSync3(installPath, join22(reference, "install"), { recursive: true });
|
|
@@ -16477,6 +17017,18 @@ function captureRollback(attemptId, operationId, nativeId) {
|
|
|
16477
17017
|
function mutateInstall(handle, stagingRoot) {
|
|
16478
17018
|
if (handle.projectedFingerprint === null)
|
|
16479
17019
|
throw new Error("claude-code activation is missing its sealed fingerprint");
|
|
17020
|
+
const nativeCatalog = matchingForeignLifecycleCatalog(handle.nativeId, handle.scopeId, handle.target, stagingRoot, slotVersion(handle.packageVersion, handle.sourceRevision));
|
|
17021
|
+
if (nativeCatalog === null)
|
|
17022
|
+
validateLifecycleCatalog(handle.nativeId, handle.scopeId);
|
|
17023
|
+
const capturedEntry = handle.expected.contentRoots.find((root) => root.label === "selected-catalog-entry");
|
|
17024
|
+
const ownedEntryPath = join22(lifecycleCatalogRoot(handle.nativeId), ".claude-plugin", "marketplace.json");
|
|
17025
|
+
if (nativeCatalog !== null || capturedEntry !== undefined && capturedEntry.path !== ownedEntryPath) {
|
|
17026
|
+
const liveEntry = lifecycleCatalogEntryObservation(handle.nativeId)[0];
|
|
17027
|
+
if (nativeCatalog === null || capturedEntry === undefined || capturedEntry.path === ownedEntryPath || liveEntry?.path !== capturedEntry.path || liveEntry.fingerprint !== capturedEntry.fingerprint) {
|
|
17028
|
+
throw new Error("claude-code selected foreign catalog entry changed after preparation; re-plan before applying");
|
|
17029
|
+
}
|
|
17030
|
+
}
|
|
17031
|
+
const beforeCatalog = lifecycleSelectedMutationState(handle.nativeId);
|
|
16480
17032
|
const installPath = cacheSlot(handle.nativeId, slotVersion(handle.packageVersion, handle.sourceRevision));
|
|
16481
17033
|
const before = existsSync17(installPath) ? fingerprintTree(installPath) : null;
|
|
16482
17034
|
rmSync3(installPath, { recursive: true, force: true });
|
|
@@ -16491,6 +17043,8 @@ function mutateInstall(handle, stagingRoot) {
|
|
|
16491
17043
|
plugins: []
|
|
16492
17044
|
};
|
|
16493
17045
|
try {
|
|
17046
|
+
if (nativeCatalog === null)
|
|
17047
|
+
installLifecycleCatalog(handle.nativeId, handle.scopeId, stagingRoot);
|
|
16494
17048
|
writeRegistry(registryFile, registryWithEntry(readRegistry(registryFile), handle.nativeId, installPath, slotVersion(handle.packageVersion, handle.sourceRevision), source));
|
|
16495
17049
|
writeSettings(settingsFile, settingsWithEnabled(readSettings(settingsFile), handle.nativeId, true));
|
|
16496
17050
|
disableMarketplaceAutoUpdate(handle.nativeId);
|
|
@@ -16500,9 +17054,24 @@ function mutateInstall(handle, stagingRoot) {
|
|
|
16500
17054
|
}
|
|
16501
17055
|
return {
|
|
16502
17056
|
receiptId: `claude-apply-${handle.operationId}`,
|
|
16503
|
-
changed: before !== handle.projectedFingerprint
|
|
17057
|
+
changed: before !== handle.projectedFingerprint || beforeCatalog !== lifecycleSelectedMutationState(handle.nativeId)
|
|
16504
17058
|
};
|
|
16505
17059
|
}
|
|
17060
|
+
function lifecycleSelectedMutationState(nativeId) {
|
|
17061
|
+
const market = marketplaceName(nativeId);
|
|
17062
|
+
const known = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"));
|
|
17063
|
+
const settings = readSettings(join22(pluginsDir(), "..", "settings.json"));
|
|
17064
|
+
const extra = settings["extraKnownMarketplaces"];
|
|
17065
|
+
const member = registeredLifecycleCatalogMember(nativeId)?.member ?? lifecycleCatalogCopy(nativeId);
|
|
17066
|
+
return catalogEntryFingerprint({
|
|
17067
|
+
catalogEntry: lifecycleCatalogEntryObservation(nativeId),
|
|
17068
|
+
member: existsSync17(member) ? fingerprintTree(member) : null,
|
|
17069
|
+
registration: known[market] ?? null,
|
|
17070
|
+
extraRegistration: isRecord6(extra) ? extra[market] ?? null : null,
|
|
17071
|
+
installedRows: readRegistry(join22(pluginsDir(), "installed_plugins.json")).plugins[nativeId] ?? null,
|
|
17072
|
+
enabled: enabledPlugins()[nativeId] ?? null
|
|
17073
|
+
});
|
|
17074
|
+
}
|
|
16506
17075
|
function setEnabled(handle, enabled, receiptId) {
|
|
16507
17076
|
const settingsFile = join22(pluginsDir(), "..", "settings.json");
|
|
16508
17077
|
const was = enabledPlugins()[handle.nativeId] === true;
|
|
@@ -16537,6 +17106,7 @@ function restoreRollback(handle) {
|
|
|
16537
17106
|
const beforeMarkets = existsSync17(join22(pluginsDir(), "known_marketplaces.json")) ? readFileSync16(join22(pluginsDir(), "known_marketplaces.json"), "utf8") : "";
|
|
16538
17107
|
const beforeApplied = existsSync17(appliedSlot) ? fingerprintTree(appliedSlot) : null;
|
|
16539
17108
|
const beforePrevious = previousSlot !== undefined && existsSync17(previousSlot) ? fingerprintTree(previousSlot) : null;
|
|
17109
|
+
restoreLifecycleCatalog(reference, handle.nativeId, handle.scopeId);
|
|
16540
17110
|
restorePluginSlice(handle.nativeId, reference);
|
|
16541
17111
|
if (previousSlot !== undefined && resolve11(previousSlot) !== appliedSlot)
|
|
16542
17112
|
rmSync3(appliedSlot, { recursive: true, force: true });
|
|
@@ -16604,6 +17174,9 @@ function restoreKnownMarketplace(reference) {
|
|
|
16604
17174
|
if (saved.entry === null) {
|
|
16605
17175
|
if (!hadFile || !Object.hasOwn(current, saved.marketplace))
|
|
16606
17176
|
return;
|
|
17177
|
+
const root = lifecycleCatalogRoot(`rollback@${saved.marketplace}`);
|
|
17178
|
+
if (existsSync17(root) && (catalogEntries(lifecycleCatalogDocument(root, saved.marketplace)).length > 0 || !emptyOwnedCatalogContainer(root)))
|
|
17179
|
+
return;
|
|
16607
17180
|
const next = { ...current };
|
|
16608
17181
|
delete next[saved.marketplace];
|
|
16609
17182
|
writeMarketplaces(file, next);
|
|
@@ -16648,10 +17221,29 @@ function marketplaceAutoUpdateRecords(marketplace) {
|
|
|
16648
17221
|
}
|
|
16649
17222
|
function hasForeignMarketplaceSibling(nativeId, marketplace) {
|
|
16650
17223
|
const registry = readRegistry(join22(pluginsDir(), "installed_plugins.json"));
|
|
16651
|
-
|
|
17224
|
+
if (Object.entries(registry.plugins).some(([id, rows]) => {
|
|
16652
17225
|
if (id === nativeId || marketplaceName(id) !== marketplace || !Array.isArray(rows))
|
|
16653
17226
|
return false;
|
|
16654
17227
|
return rows.some((row) => isRecord6(row) && row["scope"] === "user" && !plgnzOwnedInstall(id, row["installPath"]));
|
|
17228
|
+
}))
|
|
17229
|
+
return true;
|
|
17230
|
+
const registration = readMarketplaces(join22(pluginsDir(), "known_marketplaces.json"))[marketplace];
|
|
17231
|
+
if (!isRecord6(registration) || !isRecord6(registration["source"]) || registration["source"]["source"] !== "directory" || typeof registration["source"]["path"] !== "string")
|
|
17232
|
+
return false;
|
|
17233
|
+
const root = registration["source"]["path"];
|
|
17234
|
+
if (!root.startsWith("/") || !existsSync17(root))
|
|
17235
|
+
return false;
|
|
17236
|
+
const document = lifecycleCatalogDocument(root, marketplace);
|
|
17237
|
+
return catalogEntries(document).some((entry) => {
|
|
17238
|
+
if (isCatalogMember(entry, nativeId))
|
|
17239
|
+
return false;
|
|
17240
|
+
if (!isRecord6(entry) || typeof entry["name"] !== "string" || typeof entry["source"] !== "string" || !entry["source"].startsWith("./"))
|
|
17241
|
+
return true;
|
|
17242
|
+
const member = resolve11(root, entry["source"]);
|
|
17243
|
+
if (!containedPath(root, member) || member === root || !existsSync17(member))
|
|
17244
|
+
return true;
|
|
17245
|
+
assertCatalogPath(root, member);
|
|
17246
|
+
return readOwnership(member)?.pluginId !== `${entry["name"]}@${marketplace}`;
|
|
16655
17247
|
});
|
|
16656
17248
|
}
|
|
16657
17249
|
function plgnzOwnedInstall(id, installPath) {
|
|
@@ -19771,9 +20363,9 @@ var ompWriter = {
|
|
|
19771
20363
|
const lockFile = join26(pluginsDir3(), "omp-plugins.lock.json");
|
|
19772
20364
|
const registryFile = join26(pluginsDir3(), "installed_plugins.json");
|
|
19773
20365
|
const lock = readDoc(lockFile, { plugins: {}, settings: {} }, "lockfile");
|
|
19774
|
-
const lockPlugins =
|
|
20366
|
+
const lockPlugins = object4(lock.plugins, "lockfile plugins");
|
|
19775
20367
|
const registry = readDoc(registryFile, { version: 2, plugins: {} }, "registry");
|
|
19776
|
-
const registryPlugins =
|
|
20368
|
+
const registryPlugins = object4(registry.plugins, "registry plugins");
|
|
19777
20369
|
const rows = userRows(registryPlugins[id]);
|
|
19778
20370
|
if (rows.length > 0)
|
|
19779
20371
|
validateLegacy(rows, id, plugin.name, version, opts?.adoptExisting === true);
|
|
@@ -20096,7 +20688,7 @@ function readDoc(path, fallback, label) {
|
|
|
20096
20688
|
throw new Error(`invalid OMP ${label}: ${path} (${error.message})`);
|
|
20097
20689
|
}
|
|
20098
20690
|
}
|
|
20099
|
-
function
|
|
20691
|
+
function object4(value, label) {
|
|
20100
20692
|
if (!isDoc(value))
|
|
20101
20693
|
throw new Error(`invalid OMP ${label}`);
|
|
20102
20694
|
return value;
|
|
@@ -20517,7 +21109,7 @@ async function applyManaged(handle, stagingRoot) {
|
|
|
20517
21109
|
const link = managedLink(handle.nativeId);
|
|
20518
21110
|
const file = lockPath();
|
|
20519
21111
|
const lock = readDoc(file, { plugins: {}, settings: {} }, "lockfile");
|
|
20520
|
-
const plugins =
|
|
21112
|
+
const plugins = object4(lock.plugins, "lockfile plugins");
|
|
20521
21113
|
const before = snapshot(file);
|
|
20522
21114
|
mkdirSafe(dirname9(target));
|
|
20523
21115
|
mkdirSafe(dirname9(link));
|
|
@@ -20547,7 +21139,7 @@ function activationAlreadyCurrent(nativeId, stage, version) {
|
|
|
20547
21139
|
if (!existsSync21(target) || !existsSync21(stage) || fingerprintTree(stage) !== fingerprintTree(target) || !linkPointsTo(managedLink(nativeId), target))
|
|
20548
21140
|
return false;
|
|
20549
21141
|
const lock = readDoc(lockPath(), { plugins: {}, settings: {} }, "lockfile");
|
|
20550
|
-
const plugins =
|
|
21142
|
+
const plugins = object4(lock.plugins, "lockfile plugins");
|
|
20551
21143
|
const row = entry(plugins[npmName(nativeId)]);
|
|
20552
21144
|
return row.enabled === true && row.version === version;
|
|
20553
21145
|
}
|
|
@@ -20555,7 +21147,7 @@ async function disableManaged(handle) {
|
|
|
20555
21147
|
const receiptId = `disable:${handle.attemptId}:${handle.operationId}`;
|
|
20556
21148
|
const file = lockPath();
|
|
20557
21149
|
const lock = readDoc(file, { plugins: {}, settings: {} }, "lockfile");
|
|
20558
|
-
const plugins =
|
|
21150
|
+
const plugins = object4(lock.plugins, "lockfile plugins");
|
|
20559
21151
|
const npm = npmName(handle.nativeId);
|
|
20560
21152
|
const previous = entry(plugins[npm]);
|
|
20561
21153
|
if (previous.enabled === false)
|
|
@@ -20998,7 +21590,7 @@ function observeDcodeTarget(target) {
|
|
|
20998
21590
|
throw new Error("unsupported dcode target identity");
|
|
20999
21591
|
const registry = readRegistry3(dcodeRegistryFile());
|
|
21000
21592
|
const enabled = boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins");
|
|
21001
|
-
return Object.entries(
|
|
21593
|
+
return Object.entries(object5(registry.plugins, "registry plugins")).map(([nativeId, raw]) => {
|
|
21002
21594
|
const rows = rowsFor(raw);
|
|
21003
21595
|
if (rows.length !== 1)
|
|
21004
21596
|
throw new Error(`dcode install record ${nativeId} has duplicate rows`);
|
|
@@ -21053,7 +21645,7 @@ function captureDcodeRollback(binding, attemptId, operationId) {
|
|
|
21053
21645
|
mkdirSync9(dir, { recursive: true });
|
|
21054
21646
|
const registry = readRegistry3(dcodeRegistryFile());
|
|
21055
21647
|
const enabled = boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins");
|
|
21056
|
-
const row =
|
|
21648
|
+
const row = object5(registry.plugins, "registry plugins")[binding.nativeId];
|
|
21057
21649
|
const state = { row: row ?? null, enabled: enabled[binding.nativeId] ?? null, registryExisted: existsSync23(dcodeRegistryFile()), enablementExisted: existsSync23(dcodeEnablementFile()) };
|
|
21058
21650
|
writeFileSync9(join28(dir, "state.json"), JSON.stringify(state));
|
|
21059
21651
|
for (const [index, item] of rowsFor(row).entries()) {
|
|
@@ -21100,7 +21692,7 @@ function preflightAdoption(request) {
|
|
|
21100
21692
|
if (path === undefined)
|
|
21101
21693
|
throw new Error("dcode unmanaged adoption has no native root");
|
|
21102
21694
|
const manifest = declaredManifest(snapshot.packageRoot, snapshot.packageName);
|
|
21103
|
-
assessPrior(
|
|
21695
|
+
assessPrior(object5(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[snapshot.nativeId], snapshot.nativeId, snapshot.nativeGit?.locator ?? `local:${snapshot.scopeId}`, true, snapshot.packageName, manifest.version, snapshot.packageRoot);
|
|
21104
21696
|
if (fingerprintTree(path) !== installed.installedFingerprint)
|
|
21105
21697
|
throw new Error("dcode unmanaged adoption changed during preflight");
|
|
21106
21698
|
}
|
|
@@ -21108,7 +21700,7 @@ function setDcodeRow(nativeId, row, enabled) {
|
|
|
21108
21700
|
assertManagedPath4(dcodeRegistryFile());
|
|
21109
21701
|
assertManagedPath4(dcodeEnablementFile());
|
|
21110
21702
|
const registry = readRegistry3(dcodeRegistryFile()), enablement = readEnablement(dcodeEnablementFile());
|
|
21111
|
-
const plugins = { ...
|
|
21703
|
+
const plugins = { ...object5(registry.plugins, "registry plugins") }, flags = { ...boolObject(enablement.enabledPlugins, "enabledPlugins") };
|
|
21112
21704
|
if (row === null)
|
|
21113
21705
|
delete plugins[nativeId];
|
|
21114
21706
|
else
|
|
@@ -21232,13 +21824,13 @@ var dcodeDefinition = {
|
|
|
21232
21824
|
},
|
|
21233
21825
|
disable: async (prepared) => {
|
|
21234
21826
|
await acquireNativeLock(prepared.handle.attemptId, prepared.handle.operationId);
|
|
21235
|
-
const row =
|
|
21827
|
+
const row = object5(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[prepared.handle.nativeId];
|
|
21236
21828
|
setDcodeRow(prepared.handle.nativeId, row ?? null, false);
|
|
21237
21829
|
return { receiptId: `dcode-disable:${prepared.handle.operationId}`, changed: true };
|
|
21238
21830
|
},
|
|
21239
21831
|
retire: async (prepared) => {
|
|
21240
21832
|
await acquireNativeLock(prepared.handle.attemptId, prepared.handle.operationId);
|
|
21241
|
-
const rows = rowsFor(
|
|
21833
|
+
const rows = rowsFor(object5(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[prepared.handle.nativeId]);
|
|
21242
21834
|
for (const row of rows) {
|
|
21243
21835
|
const path = rowInstall(row, prepared.handle.nativeId);
|
|
21244
21836
|
assertCachePath(path);
|
|
@@ -21288,7 +21880,7 @@ var dcodeDefinition = {
|
|
|
21288
21880
|
}
|
|
21289
21881
|
}
|
|
21290
21882
|
setDcodeRow(handle.nativeId, state.row ?? null, typeof state.enabled === "boolean" ? state.enabled : null);
|
|
21291
|
-
if (state.registryExisted === false && Object.keys(
|
|
21883
|
+
if (state.registryExisted === false && Object.keys(object5(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")).length === 0)
|
|
21292
21884
|
rmSync9(dcodeRegistryFile());
|
|
21293
21885
|
if (state.enablementExisted === false && Object.keys(boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins")).length === 0)
|
|
21294
21886
|
rmSync9(dcodeEnablementFile());
|
|
@@ -21365,7 +21957,7 @@ var dcodeWriter = {
|
|
|
21365
21957
|
assertManagedPath4(enablementFile);
|
|
21366
21958
|
const registry = readRegistry3(registryFile);
|
|
21367
21959
|
const enablement = readEnablement(enablementFile);
|
|
21368
|
-
const plugins =
|
|
21960
|
+
const plugins = object5(registry.plugins, "registry plugins");
|
|
21369
21961
|
const enabled = boolObject(enablement.enabledPlugins, "enabledPlugins");
|
|
21370
21962
|
const manifest = declaredManifest(plugin.dir, plugin.name);
|
|
21371
21963
|
const observation = probeDcodeVersion();
|
|
@@ -21473,7 +22065,7 @@ var dcodeWriter = {
|
|
|
21473
22065
|
assertManagedPath4(enablementFile);
|
|
21474
22066
|
const registry = readRegistry3(registryFile);
|
|
21475
22067
|
const enablement = readEnablement(enablementFile);
|
|
21476
|
-
const plugins =
|
|
22068
|
+
const plugins = object5(registry.plugins, "registry plugins");
|
|
21477
22069
|
const rows = rowsFor(plugins[id]);
|
|
21478
22070
|
if (rows.length === 0)
|
|
21479
22071
|
return;
|
|
@@ -21567,7 +22159,7 @@ function declaredManifest(dir, expectedName) {
|
|
|
21567
22159
|
throw new Error("dcode stage has no supported plugin manifest");
|
|
21568
22160
|
let parsed;
|
|
21569
22161
|
try {
|
|
21570
|
-
parsed =
|
|
22162
|
+
parsed = object5(JSON.parse(readFileSync22(manifest, "utf8")), "manifest");
|
|
21571
22163
|
} catch (error) {
|
|
21572
22164
|
throw new Error(`invalid dcode manifest: ${manifest} (${error.message})`);
|
|
21573
22165
|
}
|
|
@@ -21590,10 +22182,10 @@ function forceAutoUpdateOff(dir) {
|
|
|
21590
22182
|
const manifest = MANIFESTS.map((relative) => join28(dir, relative)).find((file) => existsSync23(file));
|
|
21591
22183
|
if (manifest === undefined)
|
|
21592
22184
|
throw new Error("dcode stage has no supported plugin manifest");
|
|
21593
|
-
const parsed =
|
|
21594
|
-
const extensions = parsed.extensions === undefined ? {} :
|
|
22185
|
+
const parsed = object5(JSON.parse(readFileSync22(manifest, "utf8")), "manifest");
|
|
22186
|
+
const extensions = parsed.extensions === undefined ? {} : object5(parsed.extensions, "manifest extensions");
|
|
21595
22187
|
const current = extensions["com.langchain.deepagents.code"];
|
|
21596
|
-
const dcodeExtension = current === undefined ? {} :
|
|
22188
|
+
const dcodeExtension = current === undefined ? {} : object5(current, "dcode manifest extension");
|
|
21597
22189
|
dcodeExtension.autoUpdate = false;
|
|
21598
22190
|
extensions["com.langchain.deepagents.code"] = dcodeExtension;
|
|
21599
22191
|
parsed.extensions = extensions;
|
|
@@ -21605,7 +22197,7 @@ function assertReadback(id, registryFile, enablementFile, target, version, proje
|
|
|
21605
22197
|
throw new Error(`dcode readback failed for ${id}`);
|
|
21606
22198
|
const registry = readRegistry3(registryFile);
|
|
21607
22199
|
const enablement = readEnablement(enablementFile);
|
|
21608
|
-
const rows = rowsFor(
|
|
22200
|
+
const rows = rowsFor(object5(registry.plugins, "registry plugins")[id]);
|
|
21609
22201
|
if (rows.length !== 1)
|
|
21610
22202
|
throw new Error(`dcode readback failed for ${id}`);
|
|
21611
22203
|
const install = rowInstall(rows[0], id);
|
|
@@ -21615,7 +22207,7 @@ function assertReadback(id, registryFile, enablementFile, target, version, proje
|
|
|
21615
22207
|
throw new Error(`dcode managed install is eligible for native auto-update: ${id}`);
|
|
21616
22208
|
if (declaredManifest(target, id.split("@")[0]).version !== version)
|
|
21617
22209
|
throw new Error(`dcode readback version mismatch for ${id}`);
|
|
21618
|
-
if (boolObject(
|
|
22210
|
+
if (boolObject(object5(enablement.enabledPlugins, "enabledPlugins"), "enabledPlugins")[id] !== true)
|
|
21619
22211
|
throw new Error(`dcode readback enablement mismatch for ${id}`);
|
|
21620
22212
|
const marker = ownership3(target);
|
|
21621
22213
|
if (marker?.kind !== "current" || marker.projectedFingerprint !== projected || marker.sourceRevision !== sourceRevision) {
|
|
@@ -21695,7 +22287,7 @@ function readRegistry3(file) {
|
|
|
21695
22287
|
const registry = readDoc2(file, { version: 2, plugins: {} }, "registry");
|
|
21696
22288
|
if (registry.version !== 1 && registry.version !== 2)
|
|
21697
22289
|
throw new Error(`invalid dcode registry version: ${file}`);
|
|
21698
|
-
const plugins =
|
|
22290
|
+
const plugins = object5(registry.plugins, "registry plugins");
|
|
21699
22291
|
for (const [id, value] of Object.entries(plugins)) {
|
|
21700
22292
|
if (!id || !Array.isArray(value) || value.length === 0)
|
|
21701
22293
|
throw new Error(`invalid dcode registry record: ${id}`);
|
|
@@ -21714,13 +22306,13 @@ function readEnablement(file) {
|
|
|
21714
22306
|
boolObject(enablement.enabledPlugins, "enabledPlugins");
|
|
21715
22307
|
return enablement;
|
|
21716
22308
|
}
|
|
21717
|
-
function
|
|
22309
|
+
function object5(value, label) {
|
|
21718
22310
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
21719
22311
|
throw new Error(`invalid dcode ${label}`);
|
|
21720
22312
|
return value;
|
|
21721
22313
|
}
|
|
21722
22314
|
function boolObject(value, label) {
|
|
21723
|
-
const result =
|
|
22315
|
+
const result = object5(value, label);
|
|
21724
22316
|
if (Object.values(result).some((entry) => typeof entry !== "boolean"))
|
|
21725
22317
|
throw new Error(`invalid dcode ${label}`);
|
|
21726
22318
|
return result;
|
|
@@ -21743,7 +22335,7 @@ function ownership3(dir) {
|
|
|
21743
22335
|
if (!existsSync23(file))
|
|
21744
22336
|
return null;
|
|
21745
22337
|
try {
|
|
21746
|
-
const value =
|
|
22338
|
+
const value = object5(JSON.parse(readFileSync22(file, "utf8")), "ownership marker");
|
|
21747
22339
|
if (typeof value.source !== "string" || typeof value.pluginId !== "string" || typeof value.fingerprint !== "string")
|
|
21748
22340
|
throw new Error("marker fields are invalid");
|
|
21749
22341
|
const projected = value.projectedFingerprint;
|
|
@@ -24456,8 +25048,240 @@ var zcodeCliLifecycle = createLifecycleHostAdapter(zcodeCliLifecycleDefinition);
|
|
|
24456
25048
|
|
|
24457
25049
|
// src/hosts/hermes-writer.ts
|
|
24458
25050
|
import { chmodSync, cpSync as cpSync12, existsSync as existsSync27, lstatSync as lstatSync20, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync13, readFileSync as readFileSync26, readdirSync as readdirSync21, realpathSync as realpathSync5, renameSync as renameSync12, rmSync as rmSync13, statSync as statSync14, writeFileSync as writeFileSync13 } from "node:fs";
|
|
24459
|
-
import { dirname as dirname15, join as join32, resolve as resolve21 } from "node:path";
|
|
25051
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute9, join as join32, resolve as resolve21 } from "node:path";
|
|
24460
25052
|
import { tmpdir as tmpdir11 } from "node:os";
|
|
25053
|
+
|
|
25054
|
+
// src/hosts/hermes-launcher-templates.ts
|
|
25055
|
+
var hermesNativeBootstrap = `import os, re, sys
|
|
25056
|
+
import contextlib, hashlib, io, pathlib, shutil, subprocess
|
|
25057
|
+
os.environ.pop('PYTHONHOME', None)
|
|
25058
|
+
os.environ.pop('PYTHONPATH', None)
|
|
25059
|
+
sys.path.insert(0, '/__PLUGNZ_HERMES_ROOT__')
|
|
25060
|
+
def _hermes_closure_repair():
|
|
25061
|
+
import contextlib, hashlib, io, shutil, subprocess
|
|
25062
|
+
from pathlib import Path
|
|
25063
|
+
root = Path('/__PLUGNZ_HERMES_ROOT__')
|
|
25064
|
+
git = root / '.git'
|
|
25065
|
+
try:
|
|
25066
|
+
if git.is_file():
|
|
25067
|
+
text = git.read_text(encoding='utf-8-sig').strip()
|
|
25068
|
+
git = root / text[7:].strip() if text.startswith('gitdir:') else git
|
|
25069
|
+
fields = dict(line.partition('=')[::2] for line in (git / 'hermes-update-pull').read_text(encoding='utf-8-sig').splitlines())
|
|
25070
|
+
except OSError:
|
|
25071
|
+
return
|
|
25072
|
+
pre = fields.get('pre', '').strip()
|
|
25073
|
+
if not re.fullmatch('[0-9a-f]{40}|[0-9a-f]{64}', pre):
|
|
25074
|
+
return
|
|
25075
|
+
closure = git / 'hermes-update-recovery' / pre
|
|
25076
|
+
files = (*('hermes_cli/_early_recovery.py', 'hermes_cli/update_lock.py', 'hermes_cli/update_custody.py'), 'hermes_cli/__init__.py')
|
|
25077
|
+
|
|
25078
|
+
def blob_id(data):
|
|
25079
|
+
return hashlib.new('sha1' if len(pre) == 40 else 'sha256', b'blob %d\\0' % len(data) + data).hexdigest()
|
|
25080
|
+
|
|
25081
|
+
def verified():
|
|
25082
|
+
try:
|
|
25083
|
+
listed = dict(line.split(' ', 1)[::-1] for line in (closure / 'MANIFEST').read_text(encoding='utf-8-sig').splitlines())
|
|
25084
|
+
return (set(listed) == set(files) and listed['hermes_cli/__init__.py'] == blob_id(b'')
|
|
25085
|
+
and all(blob_id((closure / rel).read_bytes()) == oid for rel, oid in listed.items()))
|
|
25086
|
+
except (OSError, ValueError):
|
|
25087
|
+
return False
|
|
25088
|
+
|
|
25089
|
+
if not verified():
|
|
25090
|
+
exe = fields.get('git', '').strip()
|
|
25091
|
+
if not (os.path.isabs(exe) and os.path.isfile(exe)):
|
|
25092
|
+
names = ('git.exe',) if os.name == 'nt' else ('git',)
|
|
25093
|
+
exe = next((os.path.join(d, n) for d in os.environ.get('PATH', '').split(os.pathsep) if os.path.isabs(d)
|
|
25094
|
+
for n in names if os.path.isfile(os.path.join(d, n))), '')
|
|
25095
|
+
if not exe:
|
|
25096
|
+
return
|
|
25097
|
+
|
|
25098
|
+
def git_out(*args):
|
|
25099
|
+
done = subprocess.run([exe, '--no-replace-objects', '-C', str(root), *args], capture_output=True, timeout=60,
|
|
25100
|
+
stdin=subprocess.DEVNULL, creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0))
|
|
25101
|
+
if done.returncode != 0:
|
|
25102
|
+
raise OSError(done.stderr)
|
|
25103
|
+
return done.stdout
|
|
25104
|
+
|
|
25105
|
+
staging = closure.with_name('.%s.%d.launch' % (pre, os.getpid()))
|
|
25106
|
+
try:
|
|
25107
|
+
ids = {}
|
|
25108
|
+
for entry in git_out('ls-tree', '-z', '--full-tree', pre, '--', *('hermes_cli/_early_recovery.py', 'hermes_cli/update_lock.py', 'hermes_cli/update_custody.py')).split(b'\\0'):
|
|
25109
|
+
meta, _, rel = entry.decode('utf-8', 'replace').partition('\\t')
|
|
25110
|
+
if meta.split()[1:2] == ['blob']:
|
|
25111
|
+
ids[rel] = meta.split()[2]
|
|
25112
|
+
blobs = {'hermes_cli/__init__.py': b''}
|
|
25113
|
+
for rel in ('hermes_cli/_early_recovery.py', 'hermes_cli/update_lock.py', 'hermes_cli/update_custody.py'):
|
|
25114
|
+
blobs[rel] = git_out('cat-file', 'blob', ids[rel])
|
|
25115
|
+
if blob_id(blobs[rel]) != ids[rel]: # an object that inflates to bytes pre never had
|
|
25116
|
+
raise OSError('%s does not hash to %s' % (rel, ids[rel]))
|
|
25117
|
+
blobs['MANIFEST'] = ''.join('%s %s\\n' % (blob_id(data), rel) for rel, data in blobs.items()).encode('utf-8')
|
|
25118
|
+
for rel, data in blobs.items():
|
|
25119
|
+
(staging / rel).parent.mkdir(parents=True, exist_ok=True)
|
|
25120
|
+
(staging / rel).write_bytes(data)
|
|
25121
|
+
shutil.rmtree(closure, ignore_errors=True)
|
|
25122
|
+
os.replace(staging, closure)
|
|
25123
|
+
except (OSError, KeyError, subprocess.SubprocessError):
|
|
25124
|
+
pass
|
|
25125
|
+
finally:
|
|
25126
|
+
shutil.rmtree(staging, ignore_errors=True)
|
|
25127
|
+
if not verified():
|
|
25128
|
+
return
|
|
25129
|
+
for name in [n for n in sys.modules if n.split('.')[0] in ('hermes_cli', 'hermes_bootstrap', 'hermes_constants', 'pm')]:
|
|
25130
|
+
del sys.modules[name]
|
|
25131
|
+
tree = os.path.normcase(os.path.realpath(root))
|
|
25132
|
+
sys.path[:] = [str(closure)] + [p for p in sys.path if p and os.path.normcase(os.path.realpath(p)) != tree
|
|
25133
|
+
and not os.path.normcase(os.path.realpath(p)).startswith(tree + os.sep)]
|
|
25134
|
+
sys.dont_write_bytecode = True
|
|
25135
|
+
said = io.StringIO()
|
|
25136
|
+
restored = False
|
|
25137
|
+
try:
|
|
25138
|
+
with contextlib.redirect_stderr(said):
|
|
25139
|
+
from hermes_cli import _early_recovery
|
|
25140
|
+
restored = _early_recovery.restore_interrupted_pull(root)
|
|
25141
|
+
except Exception as exc:
|
|
25142
|
+
said.write('hermes: the checkout cannot start after an interrupted \`hermes update\`, and it was not '
|
|
25143
|
+
'repaired: %s\\n' % (exc,))
|
|
25144
|
+
if restored:
|
|
25145
|
+
sys.stderr.write('hermes: the checkout could not start after an interrupted \`hermes update\`; '
|
|
25146
|
+
'repaired it with the recovery code saved before the update.\\n' + said.getvalue())
|
|
25147
|
+
_early_recovery.relaunch_after_restore()
|
|
25148
|
+
sys.stderr.write(said.getvalue() or 'hermes: the checkout cannot start after an interrupted \`hermes update\`; '
|
|
25149
|
+
'launch again once the update that owns it finishes.\\n')
|
|
25150
|
+
raise SystemExit(1)
|
|
25151
|
+
if sys.argv[1:2] == ['--print-runtime-command']:
|
|
25152
|
+
sys.dont_write_bytecode = True
|
|
25153
|
+
from pathlib import Path
|
|
25154
|
+
try:
|
|
25155
|
+
from hermes_cli import _early_recovery
|
|
25156
|
+
except Exception as exc:
|
|
25157
|
+
if _hermes_closure_repair() or not isinstance(exc, ImportError):
|
|
25158
|
+
raise
|
|
25159
|
+
_early_recovery = None # a tree without the repair: the imports below report real damage
|
|
25160
|
+
if _early_recovery is not None and _early_recovery.restore_interrupted_pull(Path('/__PLUGNZ_HERMES_ROOT__')):
|
|
25161
|
+
_early_recovery.relaunch_after_restore()
|
|
25162
|
+
from hermes_constants import get_default_hermes_root
|
|
25163
|
+
os.environ['HERMES_HOME'] = os.environ.get('HERMES_HOME') or str(get_default_hermes_root())
|
|
25164
|
+
from hermes_cli._launchers import print_runtime_command
|
|
25165
|
+
print_runtime_command(Path('/__PLUGNZ_HERMES_ROOT__'), sys.argv[2:])
|
|
25166
|
+
sys.exit(0)
|
|
25167
|
+
sys._hermes_pin_default_home = True
|
|
25168
|
+
try:
|
|
25169
|
+
import hermes_bootstrap
|
|
25170
|
+
except Exception:
|
|
25171
|
+
_hermes_closure_repair()
|
|
25172
|
+
raise
|
|
25173
|
+
if not os.environ.get('HERMES_HOME'):
|
|
25174
|
+
from hermes_constants import get_default_hermes_root
|
|
25175
|
+
os.environ['HERMES_HOME'] = str(get_default_hermes_root())
|
|
25176
|
+
if sys.argv[1:2] == ['--run-module']:
|
|
25177
|
+
import runpy
|
|
25178
|
+
if len(sys.argv) < 3: sys.exit('hermes: --run-module needs a module')
|
|
25179
|
+
module = sys.argv.pop(2)
|
|
25180
|
+
del sys.argv[1]
|
|
25181
|
+
runpy.run_module(module, run_name='__main__', alter_sys=True)
|
|
25182
|
+
sys.exit(0)
|
|
25183
|
+
from hermes_cli.main import main
|
|
25184
|
+
sys.argv[0] = re.sub(r'(-script\\.pyw|\\.exe)?$', '', sys.argv[0])
|
|
25185
|
+
sys.exit(main())
|
|
25186
|
+
`;
|
|
25187
|
+
var hermesEditableFinderBody = `from __future__ import annotations
|
|
25188
|
+
import sys
|
|
25189
|
+
from importlib.machinery import ModuleSpec, PathFinder
|
|
25190
|
+
from importlib.machinery import all_suffixes as module_suffixes
|
|
25191
|
+
from importlib.util import spec_from_file_location
|
|
25192
|
+
from itertools import chain
|
|
25193
|
+
from pathlib import Path
|
|
25194
|
+
|
|
25195
|
+
__MAPPING__
|
|
25196
|
+
__NAMESPACES__
|
|
25197
|
+
PATH_PLACEHOLDER = '__EDITABLE_NAME__' + ".__path_hook__"
|
|
25198
|
+
|
|
25199
|
+
|
|
25200
|
+
class _EditableFinder: # MetaPathFinder
|
|
25201
|
+
@classmethod
|
|
25202
|
+
def find_spec(cls, fullname: str, path=None, target=None) -> ModuleSpec | None: # type: ignore
|
|
25203
|
+
# Top-level packages and modules (we know these exist in the FS)
|
|
25204
|
+
if fullname in MAPPING:
|
|
25205
|
+
pkg_path = MAPPING[fullname]
|
|
25206
|
+
return cls._find_spec(fullname, Path(pkg_path))
|
|
25207
|
+
|
|
25208
|
+
# Handle immediate children modules (required for namespaces to work)
|
|
25209
|
+
# To avoid problems with case sensitivity in the file system we delegate
|
|
25210
|
+
# to the importlib.machinery implementation.
|
|
25211
|
+
parent, _, child = fullname.rpartition(".")
|
|
25212
|
+
if parent and parent in MAPPING:
|
|
25213
|
+
return PathFinder.find_spec(fullname, path=[MAPPING[parent]])
|
|
25214
|
+
|
|
25215
|
+
# Other levels of nesting should be handled automatically by importlib
|
|
25216
|
+
# using the parent path.
|
|
25217
|
+
return None
|
|
25218
|
+
|
|
25219
|
+
@classmethod
|
|
25220
|
+
def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None:
|
|
25221
|
+
init = candidate_path / "__init__.py"
|
|
25222
|
+
candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
|
|
25223
|
+
for candidate in chain([init], candidates):
|
|
25224
|
+
if candidate.exists():
|
|
25225
|
+
return spec_from_file_location(fullname, candidate)
|
|
25226
|
+
return None
|
|
25227
|
+
|
|
25228
|
+
|
|
25229
|
+
class _EditableNamespaceFinder: # PathEntryFinder
|
|
25230
|
+
@classmethod
|
|
25231
|
+
def _path_hook(cls, path) -> type[_EditableNamespaceFinder]:
|
|
25232
|
+
if path == PATH_PLACEHOLDER:
|
|
25233
|
+
return cls
|
|
25234
|
+
raise ImportError
|
|
25235
|
+
|
|
25236
|
+
@classmethod
|
|
25237
|
+
def _paths(cls, fullname: str) -> list[str]:
|
|
25238
|
+
paths = NAMESPACES[fullname]
|
|
25239
|
+
if not paths and fullname in MAPPING:
|
|
25240
|
+
paths = [MAPPING[fullname]]
|
|
25241
|
+
# Always add placeholder, for 2 reasons:
|
|
25242
|
+
# 1. __path__ cannot be empty for the spec to be considered namespace.
|
|
25243
|
+
# 2. In the case of nested namespaces, we need to force
|
|
25244
|
+
# import machinery to query _EditableNamespaceFinder again.
|
|
25245
|
+
return [*paths, PATH_PLACEHOLDER]
|
|
25246
|
+
|
|
25247
|
+
@classmethod
|
|
25248
|
+
def find_spec(cls, fullname: str, target=None) -> ModuleSpec | None: # type: ignore
|
|
25249
|
+
if fullname in NAMESPACES:
|
|
25250
|
+
spec = ModuleSpec(fullname, None, is_package=True)
|
|
25251
|
+
spec.submodule_search_locations = cls._paths(fullname)
|
|
25252
|
+
return spec
|
|
25253
|
+
return None
|
|
25254
|
+
|
|
25255
|
+
@classmethod
|
|
25256
|
+
def find_module(cls, _fullname) -> None:
|
|
25257
|
+
return None
|
|
25258
|
+
|
|
25259
|
+
|
|
25260
|
+
def install():
|
|
25261
|
+
if not any(finder == _EditableFinder for finder in sys.meta_path):
|
|
25262
|
+
sys.meta_path.append(_EditableFinder)
|
|
25263
|
+
|
|
25264
|
+
if not NAMESPACES:
|
|
25265
|
+
return
|
|
25266
|
+
|
|
25267
|
+
if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
|
|
25268
|
+
# PathEntryFinder is needed to create NamespaceSpec without private APIS
|
|
25269
|
+
sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
|
|
25270
|
+
if PATH_PLACEHOLDER not in sys.path:
|
|
25271
|
+
sys.path.append(PATH_PLACEHOLDER) # Used just to trigger the path hook
|
|
25272
|
+
`;
|
|
25273
|
+
var hermesUvConsoleBody = `# -*- coding: utf-8 -*-
|
|
25274
|
+
import sys
|
|
25275
|
+
from hermes_cli.main import main
|
|
25276
|
+
if __name__ == "__main__":
|
|
25277
|
+
if sys.argv[0].endswith("-script.pyw"):
|
|
25278
|
+
sys.argv[0] = sys.argv[0][:-11]
|
|
25279
|
+
elif sys.argv[0].endswith(".exe"):
|
|
25280
|
+
sys.argv[0] = sys.argv[0][:-4]
|
|
25281
|
+
sys.exit(main())
|
|
25282
|
+
`;
|
|
25283
|
+
|
|
25284
|
+
// src/hosts/hermes-writer.ts
|
|
24461
25285
|
var MARKER10 = ".plgnz-install.json";
|
|
24462
25286
|
var hermesWriter = {
|
|
24463
25287
|
...hermes,
|
|
@@ -24799,6 +25623,278 @@ function matches(marker, id, ownership) {
|
|
|
24799
25623
|
return marker === id || marker === ownership;
|
|
24800
25624
|
}
|
|
24801
25625
|
var EFFECTIVE = { requirement: "none", status: "effective" };
|
|
25626
|
+
function launcherWords(body) {
|
|
25627
|
+
const words = [];
|
|
25628
|
+
let word = "", active = false, quote = "";
|
|
25629
|
+
for (let at = 0;at < body.length; at++) {
|
|
25630
|
+
const ch = body[at];
|
|
25631
|
+
if (quote === "'") {
|
|
25632
|
+
if (ch === "'")
|
|
25633
|
+
quote = "";
|
|
25634
|
+
else
|
|
25635
|
+
word += ch;
|
|
25636
|
+
} else if (quote === '"') {
|
|
25637
|
+
if (ch === '"')
|
|
25638
|
+
quote = "";
|
|
25639
|
+
else if (ch === "\\") {
|
|
25640
|
+
if (++at >= body.length)
|
|
25641
|
+
return;
|
|
25642
|
+
word += body[at];
|
|
25643
|
+
} else if (ch === "$" && !(word === "" && body.slice(at, at + 3) === '$@"'))
|
|
25644
|
+
return;
|
|
25645
|
+
else if (ch === "`")
|
|
25646
|
+
return;
|
|
25647
|
+
else
|
|
25648
|
+
word += ch;
|
|
25649
|
+
} else if (ch === "'" || ch === '"') {
|
|
25650
|
+
quote = ch;
|
|
25651
|
+
active = true;
|
|
25652
|
+
} else if (/\s/u.test(ch)) {
|
|
25653
|
+
if (active) {
|
|
25654
|
+
words.push(word);
|
|
25655
|
+
word = "";
|
|
25656
|
+
active = false;
|
|
25657
|
+
}
|
|
25658
|
+
} else if (ch === "\\") {
|
|
25659
|
+
if (++at >= body.length)
|
|
25660
|
+
return;
|
|
25661
|
+
word += body[at];
|
|
25662
|
+
active = true;
|
|
25663
|
+
} else if (/[;$`|&<>]/u.test(ch))
|
|
25664
|
+
return;
|
|
25665
|
+
else {
|
|
25666
|
+
word += ch;
|
|
25667
|
+
active = true;
|
|
25668
|
+
}
|
|
25669
|
+
}
|
|
25670
|
+
if (quote !== "")
|
|
25671
|
+
return;
|
|
25672
|
+
if (active)
|
|
25673
|
+
words.push(word);
|
|
25674
|
+
return words;
|
|
25675
|
+
}
|
|
25676
|
+
function literalPythonPath(literal) {
|
|
25677
|
+
if (literal.startsWith('"')) {
|
|
25678
|
+
try {
|
|
25679
|
+
const value = JSON.parse(literal);
|
|
25680
|
+
return typeof value === "string" ? value : undefined;
|
|
25681
|
+
} catch {
|
|
25682
|
+
return;
|
|
25683
|
+
}
|
|
25684
|
+
}
|
|
25685
|
+
if (!/^'(?:[^'\\]|\\['\\])*'$/u.test(literal))
|
|
25686
|
+
return;
|
|
25687
|
+
return literal.slice(1, -1).replace(/\\(['\\])/gu, "$1");
|
|
25688
|
+
}
|
|
25689
|
+
function verifiedEditableFinder(text, root, version) {
|
|
25690
|
+
const literal = String.raw`(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")`;
|
|
25691
|
+
const mapping = /^MAPPING: dict\[str, str\] = (.*)$/mu.exec(text);
|
|
25692
|
+
const namespaces = /^NAMESPACES: dict\[str, list\[str\]\] = (.*)$/mu.exec(text);
|
|
25693
|
+
if (mapping?.[1] === undefined || namespaces?.[1] === undefined)
|
|
25694
|
+
return false;
|
|
25695
|
+
const entry = `${literal}:\\s*${literal}`;
|
|
25696
|
+
const list = `\\[(?:${literal}(?:,\\s*${literal})*)?\\]`;
|
|
25697
|
+
const namespace = `${literal}:\\s*${list}`;
|
|
25698
|
+
if (!new RegExp(`^\\{(?:${entry}(?:,\\s*${entry})*)?\\}$`, "u").test(mapping[1]) || !new RegExp(`^\\{(?:${namespace}(?:,\\s*${namespace})*)?\\}$`, "u").test(namespaces[1]))
|
|
25699
|
+
return false;
|
|
25700
|
+
const data = [...mapping[1].matchAll(new RegExp(`(${literal}):\\s*(${literal})`, "gu"))];
|
|
25701
|
+
if (!data.some((row) => literalPythonPath(row[1]) === "hermes_cli" && literalPythonPath(row[2]) === join32(root, "hermes_cli")))
|
|
25702
|
+
return false;
|
|
25703
|
+
const canonical = (keyLiteral, pathLiteral) => {
|
|
25704
|
+
const key = literalPythonPath(keyLiteral), path = literalPythonPath(pathLiteral);
|
|
25705
|
+
return key !== undefined && /^[a-zA-Z0-9_.-]+$/u.test(key) && path === join32(root, ...key.split("."));
|
|
25706
|
+
};
|
|
25707
|
+
if (data.some((row) => !canonical(row[1], row[2])))
|
|
25708
|
+
return false;
|
|
25709
|
+
for (const row of namespaces[1].matchAll(new RegExp(`(${literal}):\\s*\\[([^\\]]*)\\]`, "gu"))) {
|
|
25710
|
+
if ([...row[2].matchAll(new RegExp(literal, "gu"))].some((path) => !canonical(row[1], path[0])))
|
|
25711
|
+
return false;
|
|
25712
|
+
}
|
|
25713
|
+
return text.replace(mapping[0], "__MAPPING__").replace(namespaces[0], "__NAMESPACES__").replaceAll(`__editable__.hermes_agent-${version}.finder`, "__EDITABLE_NAME__") === hermesEditableFinderBody;
|
|
25714
|
+
}
|
|
25715
|
+
function isolatedVenvConfig(text) {
|
|
25716
|
+
const values = new Map;
|
|
25717
|
+
for (const line of text.split(/\r?\n/u)) {
|
|
25718
|
+
if (line.trim() === "" || line.trimStart().startsWith("#"))
|
|
25719
|
+
continue;
|
|
25720
|
+
const row = /^\s*([^=]+?)\s*=\s*(.*?)\s*$/u.exec(line);
|
|
25721
|
+
if (row === null)
|
|
25722
|
+
return false;
|
|
25723
|
+
const key = row[1].trim().toLowerCase();
|
|
25724
|
+
if (key === "" || values.has(key))
|
|
25725
|
+
return false;
|
|
25726
|
+
values.set(key, row[2].toLowerCase());
|
|
25727
|
+
}
|
|
25728
|
+
return values.get("include-system-site-packages") === "false";
|
|
25729
|
+
}
|
|
25730
|
+
function hasImportShadow(directory, modules, allowed = []) {
|
|
25731
|
+
return readdirSync21(directory).some((entry) => !allowed.includes(entry) && modules.some((module) => entry === module || entry === `${module}_d.pyd` || entry.startsWith(`${module}.`) && /\.(?:py|pyc|so|pyd)$/u.test(entry)));
|
|
25732
|
+
}
|
|
25733
|
+
function verifiedSourceImportClosure(root) {
|
|
25734
|
+
let remaining = 20000;
|
|
25735
|
+
const visit = (directory, top) => {
|
|
25736
|
+
const entries = readdirSync21(directory);
|
|
25737
|
+
remaining -= entries.length;
|
|
25738
|
+
if (remaining < 0)
|
|
25739
|
+
return false;
|
|
25740
|
+
const packages = entries.filter((entry) => /^[a-zA-Z_]\w*$/u.test(entry) && entry !== "__pycache__" && statSync14(join32(directory, entry)).isDirectory());
|
|
25741
|
+
const modules = ["__init__", ...packages, ...entries.filter((entry) => entry.endsWith(".py")).map((entry) => entry.slice(0, -3))];
|
|
25742
|
+
if (top)
|
|
25743
|
+
modules.push("hermes_cli", "hermes_bootstrap", "hermes_constants", "pm");
|
|
25744
|
+
const allowed = entries.filter((entry) => entry.endsWith(".py"));
|
|
25745
|
+
allowed.push(...packages.filter((module) => !entries.includes(`${module}.py`)));
|
|
25746
|
+
if (hasImportShadow(directory, modules, allowed))
|
|
25747
|
+
return false;
|
|
25748
|
+
for (const entry of entries) {
|
|
25749
|
+
if (entry.startsWith(".") || entry === "__pycache__")
|
|
25750
|
+
continue;
|
|
25751
|
+
const path = join32(directory, entry);
|
|
25752
|
+
const stat = lstatSync20(path);
|
|
25753
|
+
if (stat.isSymbolicLink()) {
|
|
25754
|
+
if (entry.endsWith(".py") || statSync14(path).isDirectory())
|
|
25755
|
+
return false;
|
|
25756
|
+
} else if (stat.isDirectory() && !visit(path, false))
|
|
25757
|
+
return false;
|
|
25758
|
+
}
|
|
25759
|
+
return true;
|
|
25760
|
+
};
|
|
25761
|
+
return visit(root, true);
|
|
25762
|
+
}
|
|
25763
|
+
function hermesExecutableSource(binary, depth = 0) {
|
|
25764
|
+
if (depth > 4 || !isAbsolute9(binary) || !existsSync27(binary))
|
|
25765
|
+
return;
|
|
25766
|
+
const executable = realpathSync5(binary);
|
|
25767
|
+
if (!statSync14(executable).isFile())
|
|
25768
|
+
return;
|
|
25769
|
+
const text = readFileSync26(executable, "utf8");
|
|
25770
|
+
if (text.startsWith(`#!/bin/sh
|
|
25771
|
+
exec `)) {
|
|
25772
|
+
const args = launcherWords(text.slice(`#!/bin/sh
|
|
25773
|
+
exec `.length));
|
|
25774
|
+
if (args?.length === 2 && args[1] === "$@")
|
|
25775
|
+
return hermesExecutableSource(args[0], depth + 1);
|
|
25776
|
+
if (args?.length !== 5 || args[1] !== "-I" || args[2] !== "-c" || args[4] !== "$@" || !isAbsolute9(args[0]) || !/^python(?:\d+(?:\.\d+)*)?$/u.test(basename8(args[0])) || !existsSync27(args[0]))
|
|
25777
|
+
return;
|
|
25778
|
+
const code = args[3];
|
|
25779
|
+
if (!code.startsWith(`import os, re, sys
|
|
25780
|
+
`) || !/\nfrom hermes_cli\.main import main\n/u.test(code) || !/\nsys\.exit\(main\(\)\)\s*$/u.test(code))
|
|
25781
|
+
return;
|
|
25782
|
+
const bindings = [...code.matchAll(/^sys\.path\.insert\(0, ('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\)$/gmu)];
|
|
25783
|
+
if (bindings.length !== 1)
|
|
25784
|
+
return;
|
|
25785
|
+
const root = literalPythonPath(bindings[0][1]);
|
|
25786
|
+
if (root === undefined || code.replaceAll(bindings[0][1], "'/__PLUGNZ_HERMES_ROOT__'") !== hermesNativeBootstrap)
|
|
25787
|
+
return;
|
|
25788
|
+
return root !== undefined && isAbsolute9(root) && existsSync27(join32(root, "hermes_cli", "main.py")) && realpathSync5(join32(root, "hermes_cli", "main.py")).startsWith(`${realpathSync5(root)}/`) && verifiedSourceImportClosure(realpathSync5(root)) ? realpathSync5(root) : undefined;
|
|
25789
|
+
}
|
|
25790
|
+
const interpreter = /^#!([^\n]+)\n/u.exec(text)?.[1];
|
|
25791
|
+
if (interpreter === undefined || !isAbsolute9(interpreter) || !existsSync27(interpreter) || !/^python(?:\d+(?:\.\d+)*)?$/u.test(basename8(interpreter)) || text.slice(text.indexOf(`
|
|
25792
|
+
`) + 1) !== hermesUvConsoleBody)
|
|
25793
|
+
return;
|
|
25794
|
+
const venv = dirname15(dirname15(executable));
|
|
25795
|
+
if (dirname15(interpreter) !== join32(venv, "bin") || !existsSync27(join32(venv, "pyvenv.cfg")))
|
|
25796
|
+
return;
|
|
25797
|
+
if (existsSync27(join32(venv, "bin", "pyvenv.cfg")) || !isolatedVenvConfig(readFileSync26(join32(venv, "pyvenv.cfg"), "utf8")) || process.env["PYTHONHOME"] || process.env["PYTHONPATH"])
|
|
25798
|
+
return;
|
|
25799
|
+
if (hasImportShadow(join32(venv, "bin"), ["hermes_cli", "sitecustomize", "usercustomize", "_virtualenv"]))
|
|
25800
|
+
return;
|
|
25801
|
+
const lib = join32(venv, "lib");
|
|
25802
|
+
if (!existsSync27(lib))
|
|
25803
|
+
return;
|
|
25804
|
+
const roots = [];
|
|
25805
|
+
for (const python of readdirSync21(lib).filter((name) => /^python\d+\.\d+$/u.test(name))) {
|
|
25806
|
+
const packages = join32(lib, python, "site-packages");
|
|
25807
|
+
if (!existsSync27(packages))
|
|
25808
|
+
continue;
|
|
25809
|
+
if (hasImportShadow(packages, ["hermes_cli", "sitecustomize", "usercustomize", "_virtualenv"], ["_virtualenv.py"]))
|
|
25810
|
+
return;
|
|
25811
|
+
for (const name of readdirSync21(packages).filter((name) => /^hermes_agent-[^/]+\.dist-info$/u.test(name))) {
|
|
25812
|
+
const distribution = join32(packages, name);
|
|
25813
|
+
const metadata = join32(distribution, "direct_url.json");
|
|
25814
|
+
const entry = join32(distribution, "entry_points.txt");
|
|
25815
|
+
if (!existsSync27(metadata) || !existsSync27(entry) || !/^hermes\s*=\s*hermes_cli\.main:main$/mu.test(readFileSync26(entry, "utf8")))
|
|
25816
|
+
continue;
|
|
25817
|
+
const direct = JSON.parse(readFileSync26(metadata, "utf8"));
|
|
25818
|
+
if (typeof direct !== "object" || direct === null || !("url" in direct) || typeof direct.url !== "string" || !("dir_info" in direct) || typeof direct.dir_info !== "object" || direct.dir_info === null || !("editable" in direct.dir_info) || direct.dir_info.editable !== true || !direct.url.startsWith("file:///"))
|
|
25819
|
+
continue;
|
|
25820
|
+
const root = decodeURIComponent(direct.url.slice("file://".length));
|
|
25821
|
+
if (!isAbsolute9(root) || !existsSync27(join32(root, "hermes_cli", "main.py")))
|
|
25822
|
+
continue;
|
|
25823
|
+
const sourceModules = [
|
|
25824
|
+
"hermes_cli",
|
|
25825
|
+
"hermes_bootstrap",
|
|
25826
|
+
"hermes_constants",
|
|
25827
|
+
"pm",
|
|
25828
|
+
...readdirSync21(root).filter((name) => name.endsWith(".py")).map((name) => name.slice(0, -3)),
|
|
25829
|
+
...readdirSync21(root).filter((name) => existsSync27(join32(root, name, "__init__.py")))
|
|
25830
|
+
];
|
|
25831
|
+
if (hasImportShadow(packages, sourceModules) || hasImportShadow(join32(venv, "bin"), sourceModules))
|
|
25832
|
+
return;
|
|
25833
|
+
if (hasImportShadow(root, ["hermes_cli", "sitecustomize", "usercustomize", "_virtualenv"], ["hermes_cli"]))
|
|
25834
|
+
return;
|
|
25835
|
+
const version = name.slice("hermes_agent-".length, -".dist-info".length);
|
|
25836
|
+
const finder = `__editable___hermes_agent_${version.replace(/[^a-zA-Z0-9]/gu, "_")}_finder`;
|
|
25837
|
+
if (hasImportShadow(packages, [finder], [`${finder}.py`]) || hasImportShadow(join32(venv, "bin"), [finder]) || hasImportShadow(root, [finder]))
|
|
25838
|
+
return;
|
|
25839
|
+
let binding = false;
|
|
25840
|
+
for (const pthName of readdirSync21(packages).filter((name) => name.endsWith(".pth"))) {
|
|
25841
|
+
const pth = readFileSync26(join32(packages, pthName), "utf8").trim();
|
|
25842
|
+
if (pthName === `_virtualenv.pth` && pth === "import _virtualenv") {
|
|
25843
|
+
const helper = join32(packages, "_virtualenv.py");
|
|
25844
|
+
if (!existsSync27(helper) || new CryptoHasher("sha256").update(readFileSync26(helper, "utf8")).digest("hex") !== "cfb3db86aaa53bb62b5ff764970bec2d71c9228590a0ebec57f6ec926cc0bf1a")
|
|
25845
|
+
return;
|
|
25846
|
+
} else if (pthName === `__editable__.hermes_agent-${version}.pth` && pth === root)
|
|
25847
|
+
binding = true;
|
|
25848
|
+
else if (pthName === `__editable__.hermes_agent-${version}.pth` && pth === `import ${finder}; ${finder}.install()` && existsSync27(join32(packages, `${finder}.py`)) && verifiedEditableFinder(readFileSync26(join32(packages, `${finder}.py`), "utf8"), root, version))
|
|
25849
|
+
binding = true;
|
|
25850
|
+
else
|
|
25851
|
+
return;
|
|
25852
|
+
}
|
|
25853
|
+
if (binding && realpathSync5(join32(root, "hermes_cli", "main.py")).startsWith(`${realpathSync5(root)}/`) && verifiedSourceImportClosure(realpathSync5(root)))
|
|
25854
|
+
roots.push(realpathSync5(root));
|
|
25855
|
+
}
|
|
25856
|
+
}
|
|
25857
|
+
return roots.length === 1 ? roots[0] : undefined;
|
|
25858
|
+
}
|
|
25859
|
+
function observeHermesRevision() {
|
|
25860
|
+
const binary = process.env["OPEN_PLUGIN_HERMES_BIN"] ?? which("hermes");
|
|
25861
|
+
if (!binary || !existsSync27(binary))
|
|
25862
|
+
return { kind: "unknown", diagnostic: "Select an existing Hermes launcher with OPEN_PLUGIN_HERMES_BIN; a profile config does not identify its runtime" };
|
|
25863
|
+
try {
|
|
25864
|
+
const root = hermesExecutableSource(binary);
|
|
25865
|
+
if (root === undefined)
|
|
25866
|
+
return { kind: "unparseable", diagnostic: "Hermes launcher source binding is unverified; select its actual executable with OPEN_PLUGIN_HERMES_BIN and prove its whole native/uv body and editable import closure against docs/hosts/hermes.md before admission. Hermes was not executed" };
|
|
25867
|
+
const git = which("git");
|
|
25868
|
+
if (git === null)
|
|
25869
|
+
return { kind: "unknown" };
|
|
25870
|
+
const env = {
|
|
25871
|
+
...process.env,
|
|
25872
|
+
GIT_NO_LAZY_FETCH: "1",
|
|
25873
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
25874
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
25875
|
+
GIT_CONFIG_GLOBAL: "/dev/null"
|
|
25876
|
+
};
|
|
25877
|
+
for (const key of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"])
|
|
25878
|
+
delete env[key];
|
|
25879
|
+
const output = (args) => {
|
|
25880
|
+
const result = spawnSync([git, "-C", root, ...args], { env, stdout: "pipe", stderr: "pipe", timeout: 1e4 });
|
|
25881
|
+
return result.exitCode === 0 ? new TextDecoder().decode(result.stdout) : undefined;
|
|
25882
|
+
};
|
|
25883
|
+
const lines = output(["rev-parse", "--show-toplevel", "HEAD"])?.trim().split(`
|
|
25884
|
+
`);
|
|
25885
|
+
if (lines?.length !== 2 || lines[0] === undefined || lines[1] === undefined || !/^[a-f0-9]{40}$/u.test(lines[1]))
|
|
25886
|
+
return { kind: "unknown" };
|
|
25887
|
+
if (realpathSync5(lines[0]) !== root)
|
|
25888
|
+
return { kind: "unparseable" };
|
|
25889
|
+
const dirty = output(["-c", "core.fsmonitor=false", "status", "--porcelain=v1", "--untracked-files=no"]);
|
|
25890
|
+
if (dirty === undefined)
|
|
25891
|
+
return { kind: "unknown" };
|
|
25892
|
+
const revision = dirty.trim() === "" ? lines[1] : `${lines[1]}+dirty`;
|
|
25893
|
+
return { kind: "detected", version: revision, probeId: `hermes-launcher-git-${revision}` };
|
|
25894
|
+
} catch {
|
|
25895
|
+
return { kind: "unknown" };
|
|
25896
|
+
}
|
|
25897
|
+
}
|
|
24802
25898
|
var HERMES_SUPPORTED = new Set([
|
|
24803
25899
|
"ordinary-skills",
|
|
24804
25900
|
"mcp",
|
|
@@ -24813,20 +25909,20 @@ var HERMES_SUPPORTED = new Set([
|
|
|
24813
25909
|
"activation-reload",
|
|
24814
25910
|
"reversible-disable"
|
|
24815
25911
|
]);
|
|
24816
|
-
var
|
|
25912
|
+
var hermesManagedProfiles = hermesProvenManagedRevisions.map((detectedVersion) => createCapabilityEvidenceProfile({
|
|
24817
25913
|
host: "hermes",
|
|
24818
|
-
detectedVersion
|
|
25914
|
+
detectedVersion,
|
|
24819
25915
|
sourceTypes: ["local", "git"],
|
|
24820
25916
|
operations: ["install", "update", "disable", "retire"],
|
|
24821
25917
|
route: "managed",
|
|
24822
25918
|
operationStatus: "supported",
|
|
24823
25919
|
semantics: Object.fromEntries(PACKAGE_SEMANTICS.map((semantic) => [semantic, HERMES_SUPPORTED.has(semantic) ? "supported" : "unsupported"])),
|
|
24824
25920
|
evidence: [
|
|
24825
|
-
"docs/evidence/hermes-native-loader-c0d7294-20260923.json",
|
|
25921
|
+
detectedVersion === "c0d7294769a38c17ceae51d8f7995e66e1dcae27" ? "docs/evidence/hermes-native-loader-c0d7294-20260923.json" : "docs/evidence/hermes-native-loader-dce1e9b-20261011.json",
|
|
24826
25922
|
"docs/hosts/hermes.md",
|
|
24827
25923
|
"docs/research/native-plugin-update-capabilities-2026-10-09.md"
|
|
24828
25924
|
]
|
|
24829
|
-
});
|
|
25925
|
+
}));
|
|
24830
25926
|
function storeFor(target) {
|
|
24831
25927
|
const identity = hermesInstanceIdentity({ kind: target.kind, instance: target.instance, ...target.context === undefined ? {} : { context: { ...target.context } } });
|
|
24832
25928
|
return { root: identity.root, configPath: resolveInstanceConfig(identity.root, identity.configPath), pluginsDir: join32(identity.root, "plugins") };
|
|
@@ -25176,7 +26272,7 @@ function markerBody2(input) {
|
|
|
25176
26272
|
}
|
|
25177
26273
|
var hermesLifecycleDefinition = {
|
|
25178
26274
|
id: "hermes",
|
|
25179
|
-
evidenceProfiles:
|
|
26275
|
+
evidenceProfiles: hermesManagedProfiles,
|
|
25180
26276
|
async probeVersion(target) {
|
|
25181
26277
|
try {
|
|
25182
26278
|
const store = storeFor(target);
|
|
@@ -25194,7 +26290,7 @@ var hermesLifecycleDefinition = {
|
|
|
25194
26290
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
25195
26291
|
return { kind: "unparseable" };
|
|
25196
26292
|
}
|
|
25197
|
-
return
|
|
26293
|
+
return observeHermesRevision();
|
|
25198
26294
|
} catch {
|
|
25199
26295
|
return { kind: "unknown" };
|
|
25200
26296
|
}
|
|
@@ -25419,7 +26515,7 @@ var cleanupWriters = [...writers, piWriter, opencodeWriter];
|
|
|
25419
26515
|
|
|
25420
26516
|
// src/executor.ts
|
|
25421
26517
|
import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
|
|
25422
|
-
import { isAbsolute as
|
|
26518
|
+
import { isAbsolute as isAbsolute11, join as join34, resolve as resolve22 } from "node:path";
|
|
25423
26519
|
|
|
25424
26520
|
// src/owned-activation.ts
|
|
25425
26521
|
function ownedActivation(scope, source, activation, installation, target) {
|
|
@@ -25566,7 +26662,7 @@ class SyncManifestValidationError extends Error {
|
|
|
25566
26662
|
}
|
|
25567
26663
|
}
|
|
25568
26664
|
function parseSyncManifest(value) {
|
|
25569
|
-
const manifest =
|
|
26665
|
+
const manifest = object6(value, "sync manifest");
|
|
25570
26666
|
exactFields5(manifest, ["schemaVersion", "entries"], "sync manifest");
|
|
25571
26667
|
if (manifest["schemaVersion"] !== SYNC_MANIFEST_SCHEMA_VERSION)
|
|
25572
26668
|
invalidArgument2("sync manifest schemaVersion must be 1");
|
|
@@ -25600,7 +26696,7 @@ function selectManifestPackages(entry, discovered) {
|
|
|
25600
26696
|
});
|
|
25601
26697
|
}
|
|
25602
26698
|
function parseEntry(value, index) {
|
|
25603
|
-
const entry =
|
|
26699
|
+
const entry = object6(value, `sync manifest entry ${index}`);
|
|
25604
26700
|
if (entry["operation"] === "sync") {
|
|
25605
26701
|
exactFields5(entry, ["operation", "source", "target", "selectors"], `sync manifest entry ${index}`);
|
|
25606
26702
|
const source = parseSourceBinding(entry["source"], `sync manifest entry ${index} source`);
|
|
@@ -25678,7 +26774,7 @@ function assertUniqueScopes(entries) {
|
|
|
25678
26774
|
}
|
|
25679
26775
|
}
|
|
25680
26776
|
function parseSourceBinding(value, label) {
|
|
25681
|
-
const binding =
|
|
26777
|
+
const binding = object6(value, label);
|
|
25682
26778
|
if (binding["kind"] === "local") {
|
|
25683
26779
|
exactFields5(binding, ["kind", "locator"], label);
|
|
25684
26780
|
if (typeof binding["locator"] !== "string")
|
|
@@ -25704,7 +26800,7 @@ function parseTarget(value, label) {
|
|
|
25704
26800
|
}
|
|
25705
26801
|
function parseSelector(value, entryIndex, selectorIndex) {
|
|
25706
26802
|
const label = `sync manifest entry ${entryIndex} selector ${selectorIndex}`;
|
|
25707
|
-
const selector =
|
|
26803
|
+
const selector = object6(value, label);
|
|
25708
26804
|
exactFields5(selector, ["package", "adoptExisting"], label);
|
|
25709
26805
|
if (typeof selector["package"] !== "string" || !/^[a-z0-9][a-z0-9._-]*$/iu.test(selector["package"])) {
|
|
25710
26806
|
invalidArgument2(`${label} package must be a valid package name`);
|
|
@@ -25722,7 +26818,7 @@ function validateBinding(source, label) {
|
|
|
25722
26818
|
}
|
|
25723
26819
|
}
|
|
25724
26820
|
function targetProfile(value, label) {
|
|
25725
|
-
const target =
|
|
26821
|
+
const target = object6(value, label);
|
|
25726
26822
|
if (typeof target["kind"] !== "string")
|
|
25727
26823
|
invalidArgument2(`${label} kind must be a string`);
|
|
25728
26824
|
return targetProfileForKind(target["kind"], label);
|
|
@@ -25747,7 +26843,7 @@ function invokeTargetProfile(operation) {
|
|
|
25747
26843
|
throw error;
|
|
25748
26844
|
}
|
|
25749
26845
|
}
|
|
25750
|
-
function
|
|
26846
|
+
function object6(value, label) {
|
|
25751
26847
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
25752
26848
|
invalidArgument2(`${label} must be an object`);
|
|
25753
26849
|
return value;
|
|
@@ -26675,7 +27771,7 @@ function defect(reason, terminalPhase) {
|
|
|
26675
27771
|
// src/state-write.ts
|
|
26676
27772
|
import { mkdirSync as mkdirSync14, rmSync as rmSync14, writeFileSync as writeFileSync14 } from "node:fs";
|
|
26677
27773
|
import * as fs from "node:fs";
|
|
26678
|
-
import { basename as
|
|
27774
|
+
import { basename as basename9, dirname as dirname16, isAbsolute as isAbsolute10, join as join33, relative as relative7 } from "node:path";
|
|
26679
27775
|
function writeState(records, file = stateFile()) {
|
|
26680
27776
|
const existing = readLifecycleState(file);
|
|
26681
27777
|
if (existing.sourceVersion === 2)
|
|
@@ -26765,12 +27861,12 @@ function persistedSource(binding) {
|
|
|
26765
27861
|
}
|
|
26766
27862
|
}
|
|
26767
27863
|
function legacyRelativeDir2(record) {
|
|
26768
|
-
if (record.sourceDir === undefined || !
|
|
27864
|
+
if (record.sourceDir === undefined || !isAbsolute10(record.source) || !isAbsolute10(record.sourceDir))
|
|
26769
27865
|
return;
|
|
26770
27866
|
const candidate = relative7(record.source, record.sourceDir).replaceAll("\\", "/");
|
|
26771
27867
|
if (candidate === "")
|
|
26772
27868
|
return ".";
|
|
26773
|
-
if (candidate === ".." || candidate.startsWith("../") ||
|
|
27869
|
+
if (candidate === ".." || candidate.startsWith("../") || isAbsolute10(candidate))
|
|
26774
27870
|
return;
|
|
26775
27871
|
return candidate;
|
|
26776
27872
|
}
|
|
@@ -26830,7 +27926,7 @@ function legacyScopes(state, scopeId, source, host, attemptId, retiredAttemptId)
|
|
|
26830
27926
|
function atomicWrite(file, contents) {
|
|
26831
27927
|
const parent = dirname16(file);
|
|
26832
27928
|
mkdirSync14(parent, { recursive: true });
|
|
26833
|
-
const temporary = join33(parent, `.${
|
|
27929
|
+
const temporary = join33(parent, `.${basename9(file)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
26834
27930
|
try {
|
|
26835
27931
|
writeFileSync14(temporary, contents);
|
|
26836
27932
|
fs.renameSync(temporary, file);
|
|
@@ -28026,7 +29122,7 @@ function recordedPinCommand(packageRoot, server) {
|
|
|
28026
29122
|
}
|
|
28027
29123
|
function absolutePinExecutable(command) {
|
|
28028
29124
|
if (command.includes("/")) {
|
|
28029
|
-
if (!
|
|
29125
|
+
if (!isAbsolute11(command))
|
|
28030
29126
|
return null;
|
|
28031
29127
|
const canonical = resolve22(command);
|
|
28032
29128
|
return canonical === command ? command : null;
|
|
@@ -28263,7 +29359,7 @@ function createLifecyclePlannerHosts() {
|
|
|
28263
29359
|
// src/pin-lifecycle.ts
|
|
28264
29360
|
import { cpSync as cpSync13, mkdtempSync as mkdtempSync14, rmSync as rmSync15 } from "node:fs";
|
|
28265
29361
|
import { tmpdir as tmpdir12 } from "node:os";
|
|
28266
|
-
import { basename as
|
|
29362
|
+
import { basename as basename10, join as join35, relative as relative8 } from "node:path";
|
|
28267
29363
|
async function runOfflineLifecyclePin(host, plugin, dryRun) {
|
|
28268
29364
|
const adapter = host.lifecyclePinAdapter;
|
|
28269
29365
|
if (adapter === undefined || plugin.path === undefined)
|
|
@@ -28285,7 +29381,7 @@ async function runOfflineLifecyclePin(host, plugin, dryRun) {
|
|
|
28285
29381
|
return preview;
|
|
28286
29382
|
const now = new Date().toISOString();
|
|
28287
29383
|
const frozenRoot = mkdtempSync14(join35(tmpdir12(), "plgnz-offline-pin-"));
|
|
28288
|
-
const attemptId = `pin-${
|
|
29384
|
+
const attemptId = `pin-${basename10(frozenRoot)}`;
|
|
28289
29385
|
const operationId = `${attemptId}:${plugin.id}`;
|
|
28290
29386
|
let handle;
|
|
28291
29387
|
let preparation;
|
|
@@ -28977,7 +30073,7 @@ function legacyCommandAction(command) {
|
|
|
28977
30073
|
// package.json
|
|
28978
30074
|
var package_default = {
|
|
28979
30075
|
name: "plugnz",
|
|
28980
|
-
version: "0.0.
|
|
30076
|
+
version: "0.0.12",
|
|
28981
30077
|
description: "Install, diagnose and update agent plugins (Agent Plugins spec) and MCP configs across Claude Code, Codex, Cursor, Kimi and more",
|
|
28982
30078
|
license: "MIT",
|
|
28983
30079
|
type: "module",
|