@acidicsoil/portable-capabilities 0.1.9 → 0.1.10
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/CHANGELOG.md +6 -0
- package/README.md +1 -0
- package/dist-release/cli.js +199 -114
- package/dist-release/cli.js.map +4 -4
- package/dist-release/index.js +197 -52
- package/dist-release/index.js.map +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#75](https://github.com/AcidicSoil/portable-capabilities/pull/75) [`169d407`](https://github.com/AcidicSoil/portable-capabilities/commit/169d407d8b7e544db6f9b71f1521bac87df0d459) Thanks [@AcidicSoil](https://github.com/AcidicSoil)! - ● @acidicsoil/portable-capabilities@0.1.9
|
|
8
|
+
|
|
3
9
|
## 0.1.9
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
package/dist-release/cli.js
CHANGED
|
@@ -11733,7 +11733,7 @@ function writeEnvelope(envelope, format2) {
|
|
|
11733
11733
|
}
|
|
11734
11734
|
|
|
11735
11735
|
// packages/cli/src/release.ts
|
|
11736
|
-
var cliVersion = true ? "0.1.
|
|
11736
|
+
var cliVersion = true ? "0.1.10" : createRequire(import.meta.url)("../../../package.json").version;
|
|
11737
11737
|
|
|
11738
11738
|
// packages/adapters/src/antigravity/materialize.ts
|
|
11739
11739
|
import { resolve as resolve2 } from "node:path";
|
|
@@ -13073,6 +13073,7 @@ var codexRuntimeEvidence = Object.freeze([
|
|
|
13073
13073
|
]);
|
|
13074
13074
|
|
|
13075
13075
|
// packages/adapters/src/dcode/materialize.ts
|
|
13076
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
13076
13077
|
import { resolve as resolve5 } from "node:path";
|
|
13077
13078
|
var marker4 = "<!-- portable-capabilities:owned dcode -->";
|
|
13078
13079
|
function destination4(value) {
|
|
@@ -13118,7 +13119,32 @@ function registration3(operation) {
|
|
|
13118
13119
|
return `${JSON.stringify({ adapterId: operation.adapterId, kind: operation.kind, target: operation.target }, null, 2)}
|
|
13119
13120
|
`;
|
|
13120
13121
|
}
|
|
13121
|
-
function
|
|
13122
|
+
async function mergedInstructions(root, path9, operations, removedIds) {
|
|
13123
|
+
let existing;
|
|
13124
|
+
try {
|
|
13125
|
+
existing = await readFile3(resolve5(root, path9), "utf8");
|
|
13126
|
+
} catch (error) {
|
|
13127
|
+
if (error.code !== "ENOENT") throw error;
|
|
13128
|
+
}
|
|
13129
|
+
if (!existing && operations.length === 0) return void 0;
|
|
13130
|
+
const selectedIds = new Set(operations.map((operation) => operation.id));
|
|
13131
|
+
const existingText = existing ?? "";
|
|
13132
|
+
const firstHeading = existingText.search(/^## .+$/mu);
|
|
13133
|
+
const preamble = firstHeading >= 0 ? existingText.slice(0, firstHeading).trim() : existingText.trim();
|
|
13134
|
+
const sections = firstHeading >= 0 ? existingText.slice(firstHeading).split(/(?=^## .+$)/mu) : [];
|
|
13135
|
+
const retained = sections.filter((section) => {
|
|
13136
|
+
const id = section.match(/^## ([^\n]+)/u)?.[1] ?? "";
|
|
13137
|
+
return !selectedIds.has(id) && !removedIds.some((removedId) => id.startsWith(`${removedId}:`));
|
|
13138
|
+
}).map((section) => section.trim());
|
|
13139
|
+
const generated = operations.slice().sort((left, right) => left.id.localeCompare(right.id)).map((operation) => `## ${operation.id}
|
|
13140
|
+
|
|
13141
|
+
${operation.content.trim()}`);
|
|
13142
|
+
const sectionsToWrite = [...retained, ...generated];
|
|
13143
|
+
return sectionsToWrite.length > 0 ? `${preamble || marker4}
|
|
13144
|
+
${sectionsToWrite.join("\n\n")}
|
|
13145
|
+
` : void 0;
|
|
13146
|
+
}
|
|
13147
|
+
async function prepareDCodeMaterialization(plan, root) {
|
|
13122
13148
|
if (plan.runtimeId !== "dcode") throw new Error(`DCode adapter cannot prepare ${plan.runtimeId}`);
|
|
13123
13149
|
const mutations = [];
|
|
13124
13150
|
const diagnostics = [];
|
|
@@ -13220,28 +13246,39 @@ function prepareDCodeMaterialization(plan, root) {
|
|
|
13220
13246
|
);
|
|
13221
13247
|
}
|
|
13222
13248
|
}
|
|
13223
|
-
|
|
13249
|
+
const instructionRemovals = plan.operations.filter(
|
|
13250
|
+
(operation) => operation.kind === "RemoveOwnedArtifact" && /^\.deepagents\/skills\/[^/]+\/SKILL\.md$/u.test(destination4(operation.detail))
|
|
13251
|
+
);
|
|
13252
|
+
if (instructionOperations.length > 0 || instructionRemovals.length > 0) {
|
|
13224
13253
|
const instructionPath = ".deepagents/AGENTS.md";
|
|
13225
13254
|
confined4(root, instructionPath);
|
|
13226
|
-
const
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
path: instructionPath,
|
|
13232
|
-
kind: "write",
|
|
13233
|
-
content: `<!-- portable-capabilities:owned dcode -->
|
|
13234
|
-
${content}
|
|
13235
|
-
`,
|
|
13236
|
-
mode: 420,
|
|
13237
|
-
operationId: "dcode:shared-instructions",
|
|
13238
|
-
sourceProjectionOperationId: first.sourceProjectionOperationId,
|
|
13239
|
-
ownershipIntent: first.ownershipIntent,
|
|
13240
|
-
approval: first.approval,
|
|
13241
|
-
rollback: first.rollback,
|
|
13242
|
-
upgrade: first.upgrade,
|
|
13243
|
-
uninstall: first.uninstall
|
|
13255
|
+
const first = instructionOperations[0] ?? instructionRemovals[0];
|
|
13256
|
+
if (!first) throw new Error("DCode shared instruction operation is missing");
|
|
13257
|
+
const removedIds = instructionRemovals.flatMap((operation) => {
|
|
13258
|
+
const match = operation.detail.match(/^\.deepagents\/skills\/([^/]+)\/SKILL\.md$/u);
|
|
13259
|
+
return match?.[1] ? [match[1]] : [];
|
|
13244
13260
|
});
|
|
13261
|
+
const content = await mergedInstructions(
|
|
13262
|
+
root,
|
|
13263
|
+
instructionPath,
|
|
13264
|
+
instructionOperations,
|
|
13265
|
+
removedIds
|
|
13266
|
+
);
|
|
13267
|
+
mutations.push(
|
|
13268
|
+
content ? {
|
|
13269
|
+
path: instructionPath,
|
|
13270
|
+
kind: "write",
|
|
13271
|
+
content,
|
|
13272
|
+
mode: 420,
|
|
13273
|
+
operationId: "dcode:shared-instructions",
|
|
13274
|
+
sourceProjectionOperationId: first.sourceProjectionOperationId,
|
|
13275
|
+
ownershipIntent: first.ownershipIntent,
|
|
13276
|
+
approval: first.approval,
|
|
13277
|
+
rollback: first.rollback,
|
|
13278
|
+
upgrade: first.upgrade,
|
|
13279
|
+
uninstall: first.uninstall
|
|
13280
|
+
} : { path: instructionPath, kind: "remove", ...metadata3(first) }
|
|
13281
|
+
);
|
|
13245
13282
|
}
|
|
13246
13283
|
return Object.freeze({
|
|
13247
13284
|
runtimeId: plan.runtimeId,
|
|
@@ -15253,19 +15290,19 @@ function normalizeSetupRequest(input) {
|
|
|
15253
15290
|
// packages/cli/src/setup/service.ts
|
|
15254
15291
|
import { createHash as createHash16 } from "node:crypto";
|
|
15255
15292
|
import { existsSync as existsSync2 } from "node:fs";
|
|
15256
|
-
import { readdir as readdir3, readFile as
|
|
15293
|
+
import { readdir as readdir3, readFile as readFile6 } from "node:fs/promises";
|
|
15257
15294
|
import { dirname as dirname4, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve11 } from "node:path";
|
|
15258
15295
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
15259
15296
|
import { parse as parse3 } from "yaml";
|
|
15260
15297
|
|
|
15261
15298
|
// packages/cli/src/installation/lifecycle.ts
|
|
15262
15299
|
import { createHash as createHash15 } from "node:crypto";
|
|
15263
|
-
import { chmod, copyFile, lstat as lstat2, mkdir as mkdir3, readFile as
|
|
15300
|
+
import { chmod, copyFile, lstat as lstat2, mkdir as mkdir3, readFile as readFile5, rename as rename2, rm, writeFile as writeFile3 } from "node:fs/promises";
|
|
15264
15301
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve10, sep } from "node:path";
|
|
15265
15302
|
|
|
15266
15303
|
// packages/cli/src/installation/manifest.ts
|
|
15267
15304
|
import { createHash as createHash14 } from "node:crypto";
|
|
15268
|
-
import { mkdir as mkdir2, readFile as
|
|
15305
|
+
import { mkdir as mkdir2, readFile as readFile4, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
15269
15306
|
import { dirname as dirname2, join as join2 } from "node:path";
|
|
15270
15307
|
var installationManifestName = ".portable-capabilities-manifest.json";
|
|
15271
15308
|
var installationJournalName = ".portable-capabilities-journal.json";
|
|
@@ -15319,7 +15356,7 @@ function verifyInstallationManifestDigest(manifest) {
|
|
|
15319
15356
|
}
|
|
15320
15357
|
}
|
|
15321
15358
|
async function digestFile(path9) {
|
|
15322
|
-
return digestBytes(await
|
|
15359
|
+
return digestBytes(await readFile4(path9));
|
|
15323
15360
|
}
|
|
15324
15361
|
function manifestPath(targetRoot) {
|
|
15325
15362
|
return join2(targetRoot, installationManifestName);
|
|
@@ -15330,7 +15367,7 @@ function journalPath(targetRoot) {
|
|
|
15330
15367
|
async function readInstallationManifest(targetRoot) {
|
|
15331
15368
|
try {
|
|
15332
15369
|
const parsed = JSON.parse(
|
|
15333
|
-
await
|
|
15370
|
+
await readFile4(manifestPath(targetRoot), "utf8")
|
|
15334
15371
|
);
|
|
15335
15372
|
if (parsed.schemaVersion !== "1.1.0" || !Array.isArray(parsed.entries)) {
|
|
15336
15373
|
throw new Error("Invalid installation manifest shape");
|
|
@@ -15607,6 +15644,19 @@ async function assertPlanPathsSafe(targetRoot, plan) {
|
|
|
15607
15644
|
}
|
|
15608
15645
|
async function executePreparedInstallation(options) {
|
|
15609
15646
|
const manifest = await readInstallationManifest(options.targetRoot);
|
|
15647
|
+
const operation = options.preparedOperation ?? "install";
|
|
15648
|
+
if (options.preparedDiagnostics?.length) {
|
|
15649
|
+
return result(operation, "degraded", {
|
|
15650
|
+
ok: false,
|
|
15651
|
+
diagnostics: options.preparedDiagnostics
|
|
15652
|
+
});
|
|
15653
|
+
}
|
|
15654
|
+
if (options.preparedMutations.length === 0) {
|
|
15655
|
+
return result(operation, "degraded", {
|
|
15656
|
+
ok: false,
|
|
15657
|
+
diagnostics: ["No prepared mutations can be installed"]
|
|
15658
|
+
});
|
|
15659
|
+
}
|
|
15610
15660
|
const duplicatePaths = /* @__PURE__ */ new Set();
|
|
15611
15661
|
for (const mutation of options.preparedMutations) {
|
|
15612
15662
|
if (duplicatePaths.has(mutation.path)) {
|
|
@@ -15635,7 +15685,7 @@ async function executePreparedInstallation(options) {
|
|
|
15635
15685
|
const exists = await pathExists(target);
|
|
15636
15686
|
if (mutation.ownershipIntent === "preserve-user" && exists) continue;
|
|
15637
15687
|
if (exists && mutation.kind === "write" && mutation.ownershipIntent === "own") {
|
|
15638
|
-
const current = await
|
|
15688
|
+
const current = await readFile5(target, "utf8");
|
|
15639
15689
|
const ownedEntry = manifest?.entries.find((entry) => entry.path === mutation.path);
|
|
15640
15690
|
const isManifestOwned = ownedEntry !== void 0 && await digestFile(target) === ownedEntry.digest;
|
|
15641
15691
|
if (!current.includes("portable-capabilities:owned") && !isManifestOwned)
|
|
@@ -15681,7 +15731,6 @@ async function executePreparedInstallation(options) {
|
|
|
15681
15731
|
packageIdentities,
|
|
15682
15732
|
entries: nextEntries
|
|
15683
15733
|
};
|
|
15684
|
-
const operation = options.preparedOperation ?? "install";
|
|
15685
15734
|
if (options.dryRun)
|
|
15686
15735
|
return result(operation, manifest ? "healthy" : "missing", {
|
|
15687
15736
|
dryRun: true,
|
|
@@ -15716,63 +15765,6 @@ async function statusInstallation(targetRoot) {
|
|
|
15716
15765
|
manifest
|
|
15717
15766
|
});
|
|
15718
15767
|
}
|
|
15719
|
-
async function removeInstallation(targetRoot, faultInjector, capabilityIds) {
|
|
15720
|
-
const manifest = await readInstallationManifest(targetRoot);
|
|
15721
|
-
if (!manifest) return result("remove", "missing", { ok: false });
|
|
15722
|
-
const selected = new Set(capabilityIds ?? []);
|
|
15723
|
-
const scoped = (path9) => {
|
|
15724
|
-
if (selected.size === 0) return true;
|
|
15725
|
-
const normalized = path9.replaceAll("\\", "/");
|
|
15726
|
-
return [...selected].some(
|
|
15727
|
-
(id) => normalized.includes(`/skills/${id}/`) || normalized.endsWith(`/skills/${id}`)
|
|
15728
|
-
);
|
|
15729
|
-
};
|
|
15730
|
-
const classifications = await classifyManifestEntries(manifest);
|
|
15731
|
-
const unchanged = new Set(
|
|
15732
|
-
classifications.unchanged.filter((entry) => scoped(entry.path)).map((entry) => entry.path)
|
|
15733
|
-
);
|
|
15734
|
-
const removes = manifest.entries.filter((entry) => unchanged.has(entry.path)).map((entry) => entry.path);
|
|
15735
|
-
const preserved = manifest.entries.filter((entry) => !unchanged.has(entry.path));
|
|
15736
|
-
const nextManifest = preserved.length ? (() => {
|
|
15737
|
-
const entries = Object.freeze(preserved);
|
|
15738
|
-
const preservedPaths = new Set(entries.map((entry) => entry.path));
|
|
15739
|
-
const packageIdentities = Object.freeze(
|
|
15740
|
-
manifest.packageIdentities.filter((identity3) => preservedPaths.has(identity3.lockPath))
|
|
15741
|
-
);
|
|
15742
|
-
const packageDigest = packageEntriesDigest(entries, packageIdentities);
|
|
15743
|
-
const base = {
|
|
15744
|
-
runtimeId: manifest.runtimeId,
|
|
15745
|
-
packageVersion: manifest.packageVersion,
|
|
15746
|
-
targetRoot: manifest.targetRoot,
|
|
15747
|
-
journalVersion: manifest.journalVersion,
|
|
15748
|
-
lifecycleState: "modified",
|
|
15749
|
-
packageIdentities,
|
|
15750
|
-
entries
|
|
15751
|
-
};
|
|
15752
|
-
return {
|
|
15753
|
-
...manifest,
|
|
15754
|
-
...base,
|
|
15755
|
-
packageDigest,
|
|
15756
|
-
installationDigest: installationOwnershipDigest(packageDigest, base)
|
|
15757
|
-
};
|
|
15758
|
-
})() : void 0;
|
|
15759
|
-
return commitPlan(
|
|
15760
|
-
"remove",
|
|
15761
|
-
{
|
|
15762
|
-
sourceRoot: targetRoot,
|
|
15763
|
-
targetRoot,
|
|
15764
|
-
runtimeId: manifest.runtimeId,
|
|
15765
|
-
packageVersion: manifest.packageVersion,
|
|
15766
|
-
...faultInjector ? { faultInjector } : {}
|
|
15767
|
-
},
|
|
15768
|
-
{
|
|
15769
|
-
writes: [],
|
|
15770
|
-
removes,
|
|
15771
|
-
preserved: preserved.map((entry) => entry.path),
|
|
15772
|
-
...nextManifest ? { nextManifest } : {}
|
|
15773
|
-
}
|
|
15774
|
-
);
|
|
15775
|
-
}
|
|
15776
15768
|
|
|
15777
15769
|
// packages/cli/src/setup/service.ts
|
|
15778
15770
|
function roleFacet(role, name) {
|
|
@@ -15833,7 +15825,7 @@ async function loadCanonicalArtifact(catalogRoot, capabilityId) {
|
|
|
15833
15825
|
if (!capabilityId || /[\\/]/u.test(capabilityId))
|
|
15834
15826
|
throw new Error("capability IDs must be single path segments");
|
|
15835
15827
|
const file = resolve11(catalogRoot, "roles", `${capabilityId}.yaml`);
|
|
15836
|
-
const role = parse3(await
|
|
15828
|
+
const role = parse3(await readFile6(file, "utf8"));
|
|
15837
15829
|
if (role.schemaVersion !== "1.0" || role.id !== capabilityId || !role.purpose || !Array.isArray(role.inputs) || !role.output || !role.family || !role.taxonomy || !Array.isArray(role.precedence) || !Array.isArray(role.requiredBehavior) || !Array.isArray(role.forbiddenBehavior) || !Array.isArray(role.evaluation?.criteria)) {
|
|
15838
15830
|
throw new Error(`Invalid canonical capability: ${file}`);
|
|
15839
15831
|
}
|
|
@@ -15842,7 +15834,7 @@ async function loadCanonicalArtifact(catalogRoot, capabilityId) {
|
|
|
15842
15834
|
const schemaRelativePath = relative3(resolve11(catalogRoot), schemaFile);
|
|
15843
15835
|
if (isAbsolute3(schemaRelativePath) || schemaRelativePath.startsWith(".."))
|
|
15844
15836
|
throw new Error(`Canonical output schema escapes catalog: ${role.output.schema}`);
|
|
15845
|
-
const schemaContent = await
|
|
15837
|
+
const schemaContent = await readFile6(schemaFile, "utf8");
|
|
15846
15838
|
const contract = operationalContract(role);
|
|
15847
15839
|
const contractContent = `${JSON.stringify(
|
|
15848
15840
|
{ ...role, output: { ...role.output, schema: "resources/output.schema.json" } },
|
|
@@ -15915,6 +15907,27 @@ function previewSetup(request, artifact, descriptor, claims, ownershipManifest)
|
|
|
15915
15907
|
const body = { ...plan, selectedSurfaces, conversions, conflicts };
|
|
15916
15908
|
return { ...body, digest: stableDigest(body) };
|
|
15917
15909
|
}
|
|
15910
|
+
function composeInstallationPlan(previews, artifacts, runtimeId) {
|
|
15911
|
+
const first = previews[0]?.installationPlan;
|
|
15912
|
+
if (!first) throw new Error("at least one installation plan is required");
|
|
15913
|
+
const operations = previews.flatMap((preview, index) => {
|
|
15914
|
+
const capabilityId = artifacts[index]?.id;
|
|
15915
|
+
if (!capabilityId) throw new Error("installation plan is missing its capability");
|
|
15916
|
+
return preview.installationPlan.operations.map((operation) => ({
|
|
15917
|
+
...operation,
|
|
15918
|
+
id: `${capabilityId}:${operation.id}`,
|
|
15919
|
+
sourceProjectionOperationId: `${capabilityId}:${operation.sourceProjectionOperationId}`
|
|
15920
|
+
}));
|
|
15921
|
+
});
|
|
15922
|
+
const base = {
|
|
15923
|
+
schemaVersion: first.schemaVersion,
|
|
15924
|
+
id: `installation:${runtimeId}:${artifacts.map((artifact) => artifact.id).join(",")}`,
|
|
15925
|
+
runtimeId,
|
|
15926
|
+
scope: first.scope,
|
|
15927
|
+
operations
|
|
15928
|
+
};
|
|
15929
|
+
return { ...base, digest: stableDigest(base) };
|
|
15930
|
+
}
|
|
15918
15931
|
function defaultCatalogRoot() {
|
|
15919
15932
|
const moduleDir = dirname4(fileURLToPath4(import.meta.url));
|
|
15920
15933
|
const packagedCatalog = [resolve11(moduleDir, "catalog"), resolve11(moduleDir, "../catalog")].find(
|
|
@@ -15973,23 +15986,33 @@ async function executeSetupSelection(request, artifacts, descriptor, claims, con
|
|
|
15973
15986
|
...context.faultInjector ? { faultInjector: context.faultInjector } : {}
|
|
15974
15987
|
};
|
|
15975
15988
|
const runtime = resolveProductionRuntime(executionRequest.runtimeId);
|
|
15976
|
-
const lifecycle = executionRequest.intent === "setup" || executionRequest.intent === "update" ? await
|
|
15977
|
-
|
|
15978
|
-
|
|
15979
|
-
|
|
15980
|
-
|
|
15981
|
-
)
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15989
|
+
const lifecycle = executionRequest.intent === "setup" || executionRequest.intent === "update" || executionRequest.intent === "remove" ? await (async () => {
|
|
15990
|
+
const installationPlan = composeInstallationPlan(
|
|
15991
|
+
previews,
|
|
15992
|
+
planningArtifacts,
|
|
15993
|
+
executionRequest.runtimeId
|
|
15994
|
+
);
|
|
15995
|
+
const prepared = await runtime.prepare(installationPlan, targetRoot);
|
|
15996
|
+
if (prepared.runtimeId !== installationPlan.runtimeId || prepared.planId !== installationPlan.id) {
|
|
15997
|
+
throw new Error(`Materializer returned an invalid ${executionRequest.runtimeId} plan`);
|
|
15998
|
+
}
|
|
15999
|
+
const duplicatePath = prepared.mutations.map((mutation) => mutation.path).find((path9, index, paths) => paths.indexOf(path9) !== index);
|
|
16000
|
+
if (duplicatePath)
|
|
16001
|
+
throw new Error(`Selection produced duplicate prepared path: ${duplicatePath}`);
|
|
16002
|
+
return executePreparedInstallation({
|
|
16003
|
+
...lifecycleOptions,
|
|
16004
|
+
preparedOperation: executionRequest.intent === "update" ? "upgrade" : executionRequest.intent === "remove" ? "remove" : "install",
|
|
16005
|
+
preparedMutations: prepared.mutations,
|
|
16006
|
+
...prepared.diagnostics ? {
|
|
16007
|
+
preparedDiagnostics: prepared.diagnostics.map(
|
|
16008
|
+
(diagnostic6) => `${diagnostic6.kind}: ${diagnostic6.message}`
|
|
16009
|
+
)
|
|
16010
|
+
} : {}
|
|
16011
|
+
});
|
|
16012
|
+
})() : await statusInstallation(targetRoot);
|
|
15987
16013
|
return { request, previews, lifecycle };
|
|
15988
16014
|
}
|
|
15989
16015
|
|
|
15990
|
-
// packages/cli/src/wizard.ts
|
|
15991
|
-
import { execFileSync } from "node:child_process";
|
|
15992
|
-
|
|
15993
16016
|
// node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
15994
16017
|
import { styleText } from "node:util";
|
|
15995
16018
|
import { stdout, stdin } from "node:process";
|
|
@@ -17197,6 +17220,73 @@ ${r2}
|
|
|
17197
17220
|
}
|
|
17198
17221
|
}).prompt();
|
|
17199
17222
|
|
|
17223
|
+
// packages/cli/src/setup/probe.ts
|
|
17224
|
+
import { execFileSync } from "node:child_process";
|
|
17225
|
+
var executables = Object.freeze({
|
|
17226
|
+
antigravity: "agy",
|
|
17227
|
+
"claude-code": "claude",
|
|
17228
|
+
codex: "codex",
|
|
17229
|
+
dcode: "dcode",
|
|
17230
|
+
"oh-my-pi": "omp",
|
|
17231
|
+
opencode: "opencode",
|
|
17232
|
+
pi: "pi"
|
|
17233
|
+
});
|
|
17234
|
+
function resolveRuntimeProbe(runtimeId) {
|
|
17235
|
+
const runtime = resolveProductionRuntime(runtimeId);
|
|
17236
|
+
const canonicalId = runtime.descriptor.id;
|
|
17237
|
+
const executable = executables[canonicalId];
|
|
17238
|
+
if (!executable) throw new Error(`No runtime probe configured for ${canonicalId}`);
|
|
17239
|
+
return {
|
|
17240
|
+
runtimeId: canonicalId,
|
|
17241
|
+
executable,
|
|
17242
|
+
args: ["--version"],
|
|
17243
|
+
supportedVersions: runtime.descriptor.supportedVersions
|
|
17244
|
+
};
|
|
17245
|
+
}
|
|
17246
|
+
function parseVersion2(output) {
|
|
17247
|
+
return output.match(/\bv?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/u)?.[1];
|
|
17248
|
+
}
|
|
17249
|
+
function defaultRun(probe) {
|
|
17250
|
+
return execFileSync(probe.executable, probe.args, {
|
|
17251
|
+
encoding: "utf8",
|
|
17252
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
17253
|
+
timeout: 5e3
|
|
17254
|
+
});
|
|
17255
|
+
}
|
|
17256
|
+
function probeRuntimeVersion(runtimeId, options = {}) {
|
|
17257
|
+
const probe = resolveRuntimeProbe(runtimeId);
|
|
17258
|
+
let output;
|
|
17259
|
+
try {
|
|
17260
|
+
output = (options.run ?? defaultRun)(probe);
|
|
17261
|
+
} catch (error) {
|
|
17262
|
+
const code = error.code;
|
|
17263
|
+
const unavailable = code === "ENOENT" || code === "EACCES";
|
|
17264
|
+
return {
|
|
17265
|
+
status: unavailable ? "unavailable" : "failed",
|
|
17266
|
+
runtimeId: probe.runtimeId,
|
|
17267
|
+
executable: probe.executable,
|
|
17268
|
+
reason: unavailable ? `${probe.executable} is not installed or is not executable` : error instanceof Error ? error.message : String(error)
|
|
17269
|
+
};
|
|
17270
|
+
}
|
|
17271
|
+
const version = parseVersion2(output);
|
|
17272
|
+
if (!version)
|
|
17273
|
+
return {
|
|
17274
|
+
status: "unknown-version",
|
|
17275
|
+
runtimeId: probe.runtimeId,
|
|
17276
|
+
executable: probe.executable,
|
|
17277
|
+
reason: "version probe returned no parseable semantic version"
|
|
17278
|
+
};
|
|
17279
|
+
if (!probe.supportedVersions.some((range) => isRuntimeVersionInRange(range, version)))
|
|
17280
|
+
return {
|
|
17281
|
+
status: "unsupported-version",
|
|
17282
|
+
runtimeId: probe.runtimeId,
|
|
17283
|
+
executable: probe.executable,
|
|
17284
|
+
version,
|
|
17285
|
+
reason: `version ${version} is outside the supported runtime ranges`
|
|
17286
|
+
};
|
|
17287
|
+
return { status: "detected", runtimeId: probe.runtimeId, executable: probe.executable, version };
|
|
17288
|
+
}
|
|
17289
|
+
|
|
17200
17290
|
// packages/cli/src/wizard.ts
|
|
17201
17291
|
var supportedRuntimes = [...productionRuntimeRegistry.keys()];
|
|
17202
17292
|
function unwrap(value) {
|
|
@@ -17207,17 +17297,6 @@ function unwrap(value) {
|
|
|
17207
17297
|
}
|
|
17208
17298
|
return value;
|
|
17209
17299
|
}
|
|
17210
|
-
function detectRuntimeVersion(runtime) {
|
|
17211
|
-
try {
|
|
17212
|
-
const output = execFileSync(runtime, ["--version"], {
|
|
17213
|
-
encoding: "utf8",
|
|
17214
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
17215
|
-
});
|
|
17216
|
-
return output.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0] ?? "unknown";
|
|
17217
|
-
} catch {
|
|
17218
|
-
return "unknown";
|
|
17219
|
-
}
|
|
17220
|
-
}
|
|
17221
17300
|
async function runInstallWizard() {
|
|
17222
17301
|
intro("Portable Capabilities setup");
|
|
17223
17302
|
try {
|
|
@@ -17237,6 +17316,12 @@ async function runInstallWizard() {
|
|
|
17237
17316
|
options: supportedRuntimes.map((value) => ({ value, label: value }))
|
|
17238
17317
|
})
|
|
17239
17318
|
);
|
|
17319
|
+
const probe = probeRuntimeVersion(runtime);
|
|
17320
|
+
if (probe.status !== "detected" || !probe.version) {
|
|
17321
|
+
log.error(`${runtime}: ${probe.reason ?? probe.status}`);
|
|
17322
|
+
process.exitCode = 4;
|
|
17323
|
+
return;
|
|
17324
|
+
}
|
|
17240
17325
|
const target = String(
|
|
17241
17326
|
unwrap(
|
|
17242
17327
|
await text({
|
|
@@ -17254,7 +17339,7 @@ async function runInstallWizard() {
|
|
|
17254
17339
|
);
|
|
17255
17340
|
const request = normalizeSetupRequest({
|
|
17256
17341
|
runtimeId: runtime,
|
|
17257
|
-
runtimeVersion:
|
|
17342
|
+
runtimeVersion: probe.version,
|
|
17258
17343
|
scope: "project",
|
|
17259
17344
|
projectDirectory: target,
|
|
17260
17345
|
capabilityIds,
|
|
@@ -17276,7 +17361,7 @@ async function runInstallWizard() {
|
|
|
17276
17361
|
runtimeEntry.claims,
|
|
17277
17362
|
{ sourceRoot: process.cwd(), packageVersion: cliVersion }
|
|
17278
17363
|
);
|
|
17279
|
-
if (!result2.lifecycle
|
|
17364
|
+
if (!result2.lifecycle?.ok) {
|
|
17280
17365
|
for (const diagnostic6 of result2.lifecycle?.diagnostics ?? ["Approval required."])
|
|
17281
17366
|
log.error(diagnostic6);
|
|
17282
17367
|
process.exitCode = 4;
|