@webpresso/agent-kit 3.3.3 → 3.3.5
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.
Potentially problematic release.
This version of @webpresso/agent-kit might be problematic. Click here for more details.
- package/THIRD-PARTY-NOTICES.md +12 -10
- package/catalog/agent/rules/changeset-release.md +15 -4
- package/catalog/agent/rules/managed-tool-binaries.md +67 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
- package/catalog/agent/rules/pre-implementation.md +36 -20
- package/catalog/agent/rules/rtk-routing.md +22 -16
- package/catalog/agent/skills/autopilot/SKILL.md +4 -3
- package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
- package/dist/esm/audit/agents.js +126 -26
- package/dist/esm/audit/hook-surface.js +5 -1
- package/dist/esm/audit/repo-guardrails.d.ts +8 -0
- package/dist/esm/audit/repo-guardrails.js +71 -1
- package/dist/esm/blueprint/core/parser.js +38 -12
- package/dist/esm/blueprint/execution/artifacts.js +8 -4
- package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
- package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
- package/dist/esm/blueprint/execution/metadata.js +8 -4
- package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
- package/dist/esm/blueprint/lifecycle/audit.js +89 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.js +63 -10
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
- package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
- package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
- package/dist/esm/blueprint/markdown/helpers.js +10 -4
- package/dist/esm/blueprint/tracked-document/parser.js +3 -3
- package/dist/esm/blueprint/trust/dossier.js +41 -1
- package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
- package/dist/esm/blueprint/trust/gate-row.js +113 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
- package/dist/esm/blueprint/trust/promotion.js +19 -19
- package/dist/esm/build/atomic-file-copy.d.ts +32 -0
- package/dist/esm/build/atomic-file-copy.js +44 -0
- package/dist/esm/build/vendor-oxlint-plugins.d.ts +11 -0
- package/dist/esm/build/vendor-oxlint-plugins.js +48 -0
- package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
- package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
- package/dist/esm/cli/commands/compile.d.ts +13 -0
- package/dist/esm/cli/commands/compile.js +182 -62
- package/dist/esm/cli/commands/format.js +17 -1
- package/dist/esm/cli/commands/gain/index.js +10 -3
- package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
- package/dist/esm/cli/commands/init/index.js +32 -52
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +48 -13
- package/dist/esm/cli/commands/init/mcp-spec.js +86 -30
- package/dist/esm/cli/commands/init/merge.js +30 -10
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
- package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
- package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
- package/dist/esm/cli/commands/pr-help.js +1 -1
- package/dist/esm/cli/commands/pr.js +18 -4
- package/dist/esm/cli/commands/review.d.ts +1 -0
- package/dist/esm/cli/commands/review.js +137 -7
- package/dist/esm/cli/commands/sync.d.ts +8 -6
- package/dist/esm/cli/commands/sync.js +11 -9
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
- package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
- package/dist/esm/cli/commands/worktree/router.js +5 -2
- package/dist/esm/cli/direct-provider-launch.js +12 -4
- package/dist/esm/compiler/flatten.d.ts +10 -1
- package/dist/esm/compiler/flatten.js +14 -4
- package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
- package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
- package/dist/esm/compose/compose.d.ts +8 -0
- package/dist/esm/compose/compose.js +115 -8
- package/dist/esm/compose/ops-report-schema.js +17 -3
- package/dist/esm/compose/registry-resolve.d.ts +17 -0
- package/dist/esm/compose/registry-resolve.js +78 -11
- package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
- package/dist/esm/config/oxlint/import-hygiene.js +21 -8
- package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
- package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
- package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
- package/dist/esm/config/oxlint/oxlintrc.js +19 -31
- package/dist/esm/config/oxlint/path-roles.js +53 -0
- package/dist/esm/config/oxlint/query-patterns.js +2 -5
- package/dist/esm/content/dispatch.js +4 -0
- package/dist/esm/content/loader.d.ts +1 -1
- package/dist/esm/content/loader.js +10 -6
- package/dist/esm/content/skill-policy.d.ts +11 -0
- package/dist/esm/content/skill-policy.js +33 -0
- package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
- package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
- package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
- package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
- package/dist/esm/daemon/domains/launch.d.ts +25 -0
- package/dist/esm/daemon/domains/launch.js +30 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
- package/dist/esm/daemon/handlers/product.d.ts +10 -0
- package/dist/esm/daemon/handlers/product.js +10 -1
- package/dist/esm/daemon/methods.d.ts +21 -1
- package/dist/esm/daemon/methods.js +20 -0
- package/dist/esm/daemon/protocol/contract.d.ts +58 -0
- package/dist/esm/daemon/protocol/contract.js +38 -0
- package/dist/esm/daemon/server.js +7 -0
- package/dist/esm/docs-linter/blueprint-plan.js +2 -2
- package/dist/esm/errors/wp-error.js +21 -2
- package/dist/esm/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +114 -45
- package/dist/esm/hooks/pretool-guard/dev-routing.js +68 -5
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
- package/dist/esm/hooks/pretool-guard/runner.js +6 -0
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +62 -3
- package/dist/esm/hooks/status/index.js +1 -1
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
- package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
- package/dist/esm/mcp/blueprint/registration.js +1 -1
- package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
- package/dist/esm/mcp/tools/_shared/redact.js +114 -8
- package/dist/esm/mcp/tools/gain.js +28 -2
- package/dist/esm/mcp/tools/worktree.js +65 -3
- package/dist/esm/mcp/tools/wp-ui-compose.js +38 -4
- package/dist/esm/mcp/tools/wp-ui-preview.js +14 -7
- package/dist/esm/review/authority.js +56 -1
- package/dist/esm/review/delivery-verifier.d.ts +4 -0
- package/dist/esm/review/delivery-verifier.js +9 -1
- package/dist/esm/review/lifecycle-writers.d.ts +76 -0
- package/dist/esm/review/lifecycle-writers.js +330 -0
- package/dist/esm/review/subject.js +66 -32
- package/dist/esm/status/snapshot.d.ts +5 -0
- package/dist/esm/status/snapshot.js +27 -2
- package/dist/esm/symlinker/consumers.d.ts +3 -1
- package/dist/esm/symlinker/consumers.js +7 -1
- package/dist/esm/symlinker/unified-sync.d.ts +3 -3
- package/dist/esm/symlinker/unified-sync.js +88 -11
- package/dist/esm/test/shard-durations.json +0 -2
- package/dist/esm/test-helpers/global-setup.js +38 -3
- package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
- package/dist/esm/tool-runtime/managed-rtk.js +275 -0
- package/dist/esm/tool-runtime/resolve-runner.js +18 -194
- package/dist/esm/utils/package-root.d.ts +32 -0
- package/dist/esm/utils/package-root.js +82 -0
- package/dist/esm/worktrees/git-metadata.d.ts +41 -0
- package/dist/esm/worktrees/git-metadata.js +48 -0
- package/package.json +16 -21
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.d.ts +0 -6
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +0 -77
- package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
- package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
- package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
- package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
- package/dist/esm/config/oxlint/index.d.ts +0 -22
- package/dist/esm/config/oxlint/index.js +0 -28
- package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
- package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
- package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
- package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
- package/dist/esm/output-transforms/rulesync.d.ts +0 -2
- package/dist/esm/output-transforms/rulesync.js +0 -79
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* the canonical `.agent/{rules,skills}` SSOT always project.
|
|
18
18
|
*
|
|
19
19
|
* The `UNIFIED_CONSUMERS` registry below describes per-IDE projection of the
|
|
20
|
-
* unified rule/skill content kinds (catalog ∪ consumer). Strategies:
|
|
20
|
+
* unified rule/skill/agent content kinds (catalog ∪ consumer). Strategies:
|
|
21
21
|
* - 'symlink': create a relative symlink to the source (file or dir)
|
|
22
22
|
* - 'copy': copy file or recursively copy dir tree
|
|
23
23
|
* - 'transform': run a transform function over the body and write the
|
|
@@ -59,6 +59,8 @@ export function unifiedRuleFilename(consumer, slug) {
|
|
|
59
59
|
* `selectUnifiedConsumers(hosts)`:
|
|
60
60
|
* - `.agent/{rules,skills}/` (working dir, SSOT): symlink, always
|
|
61
61
|
* - `.claude/rules/`: symlink, always (rules are not plugin-delivered)
|
|
62
|
+
* - `.claude/agents/`: symlink, always (canonical subagents; Claude has no
|
|
63
|
+
* native subagent delivery channel, so this projection is not plugin-owned)
|
|
62
64
|
*
|
|
63
65
|
* Project-local legacy skill roots are intentionally omitted. Codex has no
|
|
64
66
|
* `.codex/agents/` consumer.
|
|
@@ -69,6 +71,10 @@ export const DEFAULT_UNIFIED_CONSUMERS = [
|
|
|
69
71
|
{ id: "agent-skills", dir: ".agent/skills", acceptsKind: "skill", strategy: "symlink" },
|
|
70
72
|
// Claude: rules are scaffolded to .claude/rules; skills come from native channels.
|
|
71
73
|
{ id: "claude-rules", dir: ".claude/rules", acceptsKind: "rule", strategy: "symlink" },
|
|
74
|
+
// Claude: canonical subagents are scaffolded to .claude/agents. Host-agnostic
|
|
75
|
+
// (unconditional) because subagents were previously scaffolded unconditionally
|
|
76
|
+
// by the now-retired standalone scaffoldSubagents scaffolder.
|
|
77
|
+
{ id: "claude-agents", dir: ".claude/agents", acceptsKind: "agent", strategy: "symlink" },
|
|
72
78
|
];
|
|
73
79
|
/**
|
|
74
80
|
* Projection surfaces retired by a newer agent-kit release.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unified rule + skill sync.
|
|
2
|
+
* Unified rule + skill + agent sync.
|
|
3
3
|
*
|
|
4
|
-
* Source of truth: catalog (`<pkg>/dist/catalog/agent/{rules,skills}/`)
|
|
5
|
-
* consumer (`<repo>/agent-{rules,skills}/`). The loader returns a
|
|
4
|
+
* Source of truth: catalog (`<pkg>/dist/catalog/agent/{rules,skills,agents}/`)
|
|
5
|
+
* UNION consumer (`<repo>/agent-{rules,skills,agents}/`). The loader returns a
|
|
6
6
|
* source-tagged record list; this module projects that list into per-IDE
|
|
7
7
|
* surfaces according to `DEFAULT_UNIFIED_CONSUMERS`.
|
|
8
8
|
*
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unified rule + skill sync.
|
|
2
|
+
* Unified rule + skill + agent sync.
|
|
3
3
|
*
|
|
4
|
-
* Source of truth: catalog (`<pkg>/dist/catalog/agent/{rules,skills}/`)
|
|
5
|
-
* consumer (`<repo>/agent-{rules,skills}/`). The loader returns a
|
|
4
|
+
* Source of truth: catalog (`<pkg>/dist/catalog/agent/{rules,skills,agents}/`)
|
|
5
|
+
* UNION consumer (`<repo>/agent-{rules,skills,agents}/`). The loader returns a
|
|
6
6
|
* source-tagged record list; this module projects that list into per-IDE
|
|
7
7
|
* surfaces according to `DEFAULT_UNIFIED_CONSUMERS`.
|
|
8
8
|
*
|
|
@@ -86,7 +86,9 @@ function planTargets(records, consumers, consumerRoot) {
|
|
|
86
86
|
const consumerDirAbs = join(consumerRoot, consumer.dir);
|
|
87
87
|
let entryName;
|
|
88
88
|
let targetPath;
|
|
89
|
-
if (record.kind === "rule") {
|
|
89
|
+
if (record.kind === "rule" || record.kind === "agent") {
|
|
90
|
+
// agent records are file-shaped like rules (a single <slug>.md),
|
|
91
|
+
// not dir-shaped like skills.
|
|
90
92
|
entryName = unifiedRuleFilename(consumer, record.slug);
|
|
91
93
|
targetPath = join(consumerDirAbs, entryName);
|
|
92
94
|
}
|
|
@@ -248,13 +250,40 @@ function applyTarget(plan, check) {
|
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* True when `fullPath` is a symlink whose resolved target's directory is one
|
|
255
|
+
* of `managedSourceDirs`. Used to distinguish an agent-kit-managed symlink
|
|
256
|
+
* (safe to prune when stale/renamed) from a real file or a foreign symlink
|
|
257
|
+
* (never agent-kit-managed, must survive `wp sync`). The resolved target
|
|
258
|
+
* itself need not currently exist — a dangling symlink to a since-removed
|
|
259
|
+
* catalog agent still resolves into the managed source dir and is prunable.
|
|
260
|
+
*/
|
|
261
|
+
function isManagedAgentSymlink(fullPath, managedSourceDirs) {
|
|
262
|
+
let stat;
|
|
263
|
+
try {
|
|
264
|
+
stat = lstatSync(fullPath);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
if (!stat.isSymbolicLink())
|
|
270
|
+
return false;
|
|
271
|
+
try {
|
|
272
|
+
const linkTarget = readlinkSync(fullPath);
|
|
273
|
+
const resolved = resolve(dirname(fullPath), linkTarget);
|
|
274
|
+
return managedSourceDirs.has(dirname(resolved));
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
251
280
|
/**
|
|
252
281
|
* Remove entries under each consumer dir that no longer correspond to any
|
|
253
282
|
* planned record. Only removes entries whose shape matches the consumer's
|
|
254
283
|
* managed pattern (rule files: `<slug><ext>`; skill dirs: subdirectories).
|
|
255
284
|
* Real files outside the managed pattern (README.md, .gitkeep) are left alone.
|
|
256
285
|
*/
|
|
257
|
-
function pruneStale(plans, consumers, consumerRoot, check, preserveSkillSlugs) {
|
|
286
|
+
function pruneStale(plans, consumers, consumerRoot, check, preserveSkillSlugs, configuredAgentSourceDirs) {
|
|
258
287
|
const expectedByConsumerDir = new Map();
|
|
259
288
|
for (const plan of plans) {
|
|
260
289
|
const key = plan.consumer.dir;
|
|
@@ -265,6 +294,25 @@ function pruneStale(plans, consumers, consumerRoot, check, preserveSkillSlugs) {
|
|
|
265
294
|
}
|
|
266
295
|
set.add(plan.entryName);
|
|
267
296
|
}
|
|
297
|
+
// Directories that agent-kind sources resolve from (the catalog `agents/`
|
|
298
|
+
// dir, and a consumer `agent-agents/` override dir if present). Unlike
|
|
299
|
+
// `.claude/rules` and `.claude/skills` — surfaces with no legitimate
|
|
300
|
+
// hand-authoring workflow — `.claude/agents` is Claude Code's own native
|
|
301
|
+
// subagent authoring location. A consumer-authored `.claude/agents/foo.md`
|
|
302
|
+
// that was never agent-kit-managed must survive `wp sync`; only symlinks
|
|
303
|
+
// that resolve into a known managed source dir are eligible for pruning.
|
|
304
|
+
//
|
|
305
|
+
// The set unions plan-derived dirs with the CONFIGURED source dirs so it is
|
|
306
|
+
// never empty: with an empty or missing catalog agents dir there are no
|
|
307
|
+
// plans, and a plans-only set would leave nothing provably managed — the
|
|
308
|
+
// sweep must then delete nothing, not everything. Dangling managed symlinks
|
|
309
|
+
// (pointing into a configured dir whose file is gone) stay prunable.
|
|
310
|
+
const agentSourceDirs = new Set([
|
|
311
|
+
...configuredAgentSourceDirs,
|
|
312
|
+
...plans
|
|
313
|
+
.filter((plan) => plan.record.kind === "agent")
|
|
314
|
+
.map((plan) => dirname(plan.record.filePath)),
|
|
315
|
+
]);
|
|
268
316
|
let removed = 0;
|
|
269
317
|
const mismatches = [];
|
|
270
318
|
// Each unique dir gets a single sweep — multiple consumers can share a dir
|
|
@@ -292,15 +340,28 @@ function pruneStale(plans, consumers, consumerRoot, check, preserveSkillSlugs) {
|
|
|
292
340
|
preserveSkillSlugs.has(name) &&
|
|
293
341
|
entry.isDirectory())
|
|
294
342
|
continue;
|
|
295
|
-
// Only prune entries that match a managed shape: rule extension
|
|
296
|
-
// directory (skill).
|
|
297
|
-
const matchesRulePattern = consumersForDir.some((c) => c.acceptsKind === "rule"
|
|
343
|
+
// Only prune entries that match a managed shape: rule/agent extension
|
|
344
|
+
// OR a directory (skill). Agent records are file-shaped like rules.
|
|
345
|
+
const matchesRulePattern = consumersForDir.some((c) => (c.acceptsKind === "rule" || c.acceptsKind === "agent") &&
|
|
346
|
+
name.endsWith(c.ruleExtension ?? ".md"));
|
|
298
347
|
const matchesSkillPattern = consumersForDir.some((c) => c.acceptsKind === "skill") && entry.isDirectory();
|
|
299
348
|
// Also handle stale symlinks of either shape.
|
|
300
349
|
const isLink = entry.isSymbolicLink();
|
|
301
350
|
if (!matchesRulePattern && !matchesSkillPattern && !isLink)
|
|
302
351
|
continue;
|
|
303
352
|
const fullPath = join(dirAbs, name);
|
|
353
|
+
// Conservative prune for agent-only dirs: skip real files and foreign
|
|
354
|
+
// symlinks (anything not resolving into a known agent-kit managed
|
|
355
|
+
// source dir). This guard is unconditional — the source-dir set is
|
|
356
|
+
// configuration-derived and never empty, so a broken install (empty or
|
|
357
|
+
// missing catalog agents dir) cannot degrade the sweep into deleting
|
|
358
|
+
// hand-authored files. Rules/skills dirs have no legitimate
|
|
359
|
+
// hand-authoring workflow, so their existing
|
|
360
|
+
// sweep-everything-unexpected behavior is unchanged.
|
|
361
|
+
const isAgentOnlyDir = consumersForDir.every((c) => c.acceptsKind === "agent");
|
|
362
|
+
if (isAgentOnlyDir && !isManagedAgentSymlink(fullPath, agentSourceDirs)) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
304
365
|
if (check) {
|
|
305
366
|
mismatches.push({
|
|
306
367
|
consumerId: consumersForDir[0]?.id ?? dir,
|
|
@@ -348,7 +409,7 @@ function pruneEmptyRetiredConsumerDirs(consumers, consumerRoot, check) {
|
|
|
348
409
|
* Main entrypoint. See module docstring.
|
|
349
410
|
*/
|
|
350
411
|
export function runUnifiedSync(options) {
|
|
351
|
-
const kinds = options.kinds ?? ["rule", "skill"];
|
|
412
|
+
const kinds = options.kinds ?? ["rule", "skill", "agent"];
|
|
352
413
|
const kindSet = new Set(kinds);
|
|
353
414
|
const consumers = (options.consumers ?? selectUnifiedConsumers(options.hosts)).filter((consumer) => kindSet.has(consumer.acceptsKind));
|
|
354
415
|
const activeDirs = new Set(consumers.map((consumer) => consumer.dir));
|
|
@@ -394,10 +455,26 @@ export function runUnifiedSync(options) {
|
|
|
394
455
|
}
|
|
395
456
|
}
|
|
396
457
|
}
|
|
397
|
-
|
|
458
|
+
// Configuration-derived agent source dirs (catalog `agents/` plus the
|
|
459
|
+
// consumer `agent-agents/` override). Added in both raw-resolved and
|
|
460
|
+
// realpath forms so set membership survives the /var → /private/var realm
|
|
461
|
+
// split on macOS. Present even when the dirs are empty or absent — that is
|
|
462
|
+
// the point: prune eligibility must not collapse to "everything" when a
|
|
463
|
+
// broken install yields zero planned agent records.
|
|
464
|
+
const configuredAgentSourceDirs = new Set();
|
|
465
|
+
for (const candidate of [
|
|
466
|
+
join(options.catalogDir, "agents"),
|
|
467
|
+
join(consumerRoot, "agent-agents"),
|
|
468
|
+
]) {
|
|
469
|
+
configuredAgentSourceDirs.add(resolve(candidate));
|
|
470
|
+
if (existsSync(candidate)) {
|
|
471
|
+
configuredAgentSourceDirs.add(realpathSync(candidate));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const prune = pruneStale(plans, consumers, consumerRoot, options.check === true, options.preserveSkillSlugs, configuredAgentSourceDirs);
|
|
398
475
|
fixCount += prune.removed;
|
|
399
476
|
mismatches.push(...prune.mismatches);
|
|
400
|
-
const retiredPrune = pruneStale([], retiredConsumers, consumerRoot, options.check === true, undefined);
|
|
477
|
+
const retiredPrune = pruneStale([], retiredConsumers, consumerRoot, options.check === true, undefined, configuredAgentSourceDirs);
|
|
401
478
|
fixCount += retiredPrune.removed;
|
|
402
479
|
mismatches.push(...retiredPrune.mismatches);
|
|
403
480
|
const retiredDirectories = pruneEmptyRetiredConsumerDirs(retiredConsumers, consumerRoot, options.check === true);
|
|
@@ -296,7 +296,6 @@
|
|
|
296
296
|
"src/hooks/dispatch/index.test.ts": 11.771874999999994,
|
|
297
297
|
"src/hooks/shared/hook-bootstrap.test.ts": 58.217375000000004,
|
|
298
298
|
"src/cli/commands/init/scaffolders/agent-hooks/emitters/grok.test.ts": 67.57654199999999,
|
|
299
|
-
"src/cli/commands/init/scaffolders/subagents/index.test.ts": 49.99270800000005,
|
|
300
299
|
"src/blueprint/core/validation/criteria.test.ts": 9.635125000000016,
|
|
301
300
|
"src/cli/commands/session.test.ts": 662.8378339999999,
|
|
302
301
|
"src/cli/commands/dash/run-registry.test.ts": 793.002083,
|
|
@@ -560,7 +559,6 @@
|
|
|
560
559
|
"src/audit/no-legacy-cli-bin.test.ts": 56.865957999999864,
|
|
561
560
|
"src/mcp/tools/session-elision.test.ts": 4564.6535,
|
|
562
561
|
"src/hooks/pretool-guard/validators/ux-quality.test.ts": 105.19341700000041,
|
|
563
|
-
"src/config/oxlint/monorepo-paths.test.ts": 11.33191700000043,
|
|
564
562
|
"src/cli/commands/init/source-repo-hook-policy.test.ts": 65.89804200000003,
|
|
565
563
|
"src/secrets/config/schema.test.ts": 190.3588749999999,
|
|
566
564
|
"scripts/bench/scenarios/_schema.test.ts": 53.78470799999968,
|
|
@@ -40,13 +40,38 @@ async function withDistBuildLock(fn) {
|
|
|
40
40
|
// idempotent, re-checked-under-lock build; every other process blocks until that release, then finds
|
|
41
41
|
// the sentinels already satisfied and returns immediately. In the steady state (dist/ already built)
|
|
42
42
|
// this is a single existsSync check with no lock contention at all.
|
|
43
|
+
const AGENT_CORE_SENTINEL = join(REPO_ROOT, "packages/agent-core/dist/esm/repo-root/index.js");
|
|
44
|
+
const AGENT_CONFIG_OXLINT_SENTINEL = join(REPO_ROOT, "packages/agent-config/dist/esm/oxlint/import-hygiene.js");
|
|
45
|
+
function buildWorkspacePackage(filter) {
|
|
46
|
+
execFileSync("pnpm", ["--filter", filter, "run", "build"], {
|
|
47
|
+
cwd: REPO_ROOT,
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
env: { ...process.env, HUSKY: "0" },
|
|
50
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const VENDORED_OXLINT_PLUGIN = join(REPO_ROOT, "dist/esm/config/oxlint/import-hygiene.js");
|
|
54
|
+
/** agent-config oxlint SSOT is imported at build/test time (rules map + vendor source). */
|
|
55
|
+
function ensureAgentConfigBuiltSync() {
|
|
56
|
+
if (!existsSync(AGENT_CORE_SENTINEL)) {
|
|
57
|
+
buildWorkspacePackage("@webpresso/agent-core");
|
|
58
|
+
}
|
|
59
|
+
if (!existsSync(AGENT_CONFIG_OXLINT_SENTINEL)) {
|
|
60
|
+
buildWorkspacePackage("@webpresso/agent-config");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
43
63
|
async function ensureDistPrepared() {
|
|
44
|
-
// Fast path:
|
|
45
|
-
if (existsSync(DIST_SENTINEL) &&
|
|
64
|
+
// Fast path: agent-kit dist + migration assets + vendored inject plugins.
|
|
65
|
+
if (existsSync(DIST_SENTINEL) &&
|
|
66
|
+
existsSync(MIGRATION_SENTINEL) &&
|
|
67
|
+
existsSync(VENDORED_OXLINT_PLUGIN) &&
|
|
68
|
+
existsSync(AGENT_CONFIG_OXLINT_SENTINEL)) {
|
|
46
69
|
return;
|
|
70
|
+
}
|
|
47
71
|
await withDistBuildLock(async () => {
|
|
48
72
|
let builtDist = false;
|
|
49
73
|
// Re-check under the lock: another run may have completed the build while we waited.
|
|
74
|
+
ensureAgentConfigBuiltSync();
|
|
50
75
|
if (!existsSync(DIST_SENTINEL)) {
|
|
51
76
|
execFileSync("./node_modules/.bin/tshy", [], {
|
|
52
77
|
cwd: REPO_ROOT,
|
|
@@ -73,7 +98,17 @@ async function ensureDistPrepared() {
|
|
|
73
98
|
env: { ...process.env, HUSKY: "0" },
|
|
74
99
|
});
|
|
75
100
|
}
|
|
76
|
-
if (builtDist) {
|
|
101
|
+
if (builtDist || !existsSync(VENDORED_OXLINT_PLUGIN)) {
|
|
102
|
+
execFileSync("bun", ["src/build/generate-oxlintrc.ts"], {
|
|
103
|
+
cwd: REPO_ROOT,
|
|
104
|
+
encoding: "utf8",
|
|
105
|
+
env: { ...process.env, HUSKY: "0" },
|
|
106
|
+
});
|
|
107
|
+
execFileSync("bun", ["src/build/vendor-oxlint-plugins.ts"], {
|
|
108
|
+
cwd: REPO_ROOT,
|
|
109
|
+
encoding: "utf8",
|
|
110
|
+
env: { ...process.env, HUSKY: "0" },
|
|
111
|
+
});
|
|
77
112
|
execFileSync("bun", ["src/build/normalize-tsconfig-json-exports.ts"], {
|
|
78
113
|
cwd: REPO_ROOT,
|
|
79
114
|
encoding: "utf8",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed RTK binary — single ownership for install + resolve + capability probe.
|
|
3
|
+
*
|
|
4
|
+
* Consumers never brew/cargo install rtk. Setup downloads a pinned upstream
|
|
5
|
+
* release into the Webpresso cache and every call site uses that absolute path.
|
|
6
|
+
* Filters stay upstream (Apache-2.0); we only own packaging + path resolution.
|
|
7
|
+
*/
|
|
8
|
+
/** Pinned upstream token-killer release (https://github.com/rtk-ai/rtk). */
|
|
9
|
+
export declare const RTK_MANAGED_VERSION = "0.43.0";
|
|
10
|
+
export type ManagedRtkEnsureResult = {
|
|
11
|
+
readonly ok: true;
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly installed: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly error: string;
|
|
17
|
+
};
|
|
18
|
+
export type ManagedRtkDownloader = (url: string, destFile: string) => void;
|
|
19
|
+
/** Test seam. */
|
|
20
|
+
export declare function resetManagedRtkCacheForTests(): void;
|
|
21
|
+
export declare function managedRtkAssetName(platform?: NodeJS.Platform, arch?: string): string | null;
|
|
22
|
+
export declare function managedRtkDownloadUrl(version?: string): string | null;
|
|
23
|
+
/** Cache dir: `envPaths("webpresso").cache/tools/rtk/<version>/`. */
|
|
24
|
+
export declare function managedRtkInstallDir(version?: string): string;
|
|
25
|
+
export declare function managedRtkExecutablePath(version?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the managed executable if present on disk (no network, no probe).
|
|
28
|
+
* Prefers explicit WP_RTK_BIN, then managed cache install.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveManagedRtkExecutable(): string | null;
|
|
31
|
+
/** Capability probe against a concrete binary (or managed path). Fail-open. */
|
|
32
|
+
export declare function probeRtkCapabilityAt(executable: string): boolean;
|
|
33
|
+
/** Process-cached: managed binary exists and passes capability probe. */
|
|
34
|
+
export declare function isManagedRtkAvailable(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Ensure the pinned managed rtk binary is on disk. Downloads once into the
|
|
37
|
+
* Webpresso cache. No brew/cargo. Idempotent.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ensureManagedRtk(options?: {
|
|
40
|
+
readonly downloader?: ManagedRtkDownloader;
|
|
41
|
+
readonly version?: string;
|
|
42
|
+
}): ManagedRtkEnsureResult;
|
|
43
|
+
/** Write a tiny marker file for diagnostics (optional). */
|
|
44
|
+
export declare function writeManagedRtkVersionMarker(path: string, version: string): void;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed RTK binary — single ownership for install + resolve + capability probe.
|
|
3
|
+
*
|
|
4
|
+
* Consumers never brew/cargo install rtk. Setup downloads a pinned upstream
|
|
5
|
+
* release into the Webpresso cache and every call site uses that absolute path.
|
|
6
|
+
* Filters stay upstream (Apache-2.0); we only own packaging + path resolution.
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { randomBytes } from "node:crypto";
|
|
10
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { basename, dirname, join } from "node:path";
|
|
13
|
+
import envPaths from "env-paths";
|
|
14
|
+
/** Pinned upstream token-killer release (https://github.com/rtk-ai/rtk). */
|
|
15
|
+
export const RTK_MANAGED_VERSION = "0.43.0";
|
|
16
|
+
const RTK_PROBE_TIMEOUT_MS = 3000;
|
|
17
|
+
const RTK_CAPABILITY_ARGS = ["gain", "--help"];
|
|
18
|
+
const BINARY_NAME = process.platform === "win32" ? "rtk.exe" : "rtk";
|
|
19
|
+
let cachedExecutable;
|
|
20
|
+
let cachedCapability;
|
|
21
|
+
/** Test seam. */
|
|
22
|
+
export function resetManagedRtkCacheForTests() {
|
|
23
|
+
cachedExecutable = undefined;
|
|
24
|
+
cachedCapability = undefined;
|
|
25
|
+
}
|
|
26
|
+
export function managedRtkAssetName(platform = process.platform, arch = process.arch) {
|
|
27
|
+
if (platform === "darwin" && arch === "arm64")
|
|
28
|
+
return "rtk-aarch64-apple-darwin.tar.gz";
|
|
29
|
+
if (platform === "darwin" && arch === "x64")
|
|
30
|
+
return "rtk-x86_64-apple-darwin.tar.gz";
|
|
31
|
+
if (platform === "linux" && arch === "arm64")
|
|
32
|
+
return "rtk-aarch64-unknown-linux-gnu.tar.gz";
|
|
33
|
+
if (platform === "linux" && arch === "x64")
|
|
34
|
+
return "rtk-x86_64-unknown-linux-musl.tar.gz";
|
|
35
|
+
if (platform === "win32" && arch === "x64")
|
|
36
|
+
return "rtk-x86_64-pc-windows-msvc.zip";
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
export function managedRtkDownloadUrl(version = RTK_MANAGED_VERSION) {
|
|
40
|
+
const asset = managedRtkAssetName();
|
|
41
|
+
if (!asset)
|
|
42
|
+
return null;
|
|
43
|
+
return `https://github.com/rtk-ai/rtk/releases/download/v${version}/${asset}`;
|
|
44
|
+
}
|
|
45
|
+
/** Cache dir: `envPaths("webpresso").cache/tools/rtk/<version>/`. */
|
|
46
|
+
export function managedRtkInstallDir(version = RTK_MANAGED_VERSION) {
|
|
47
|
+
const cacheRoot = process.env.WP_RTK_CACHE_ROOT ?? envPaths("webpresso", { suffix: "" }).cache;
|
|
48
|
+
return join(cacheRoot, "tools", "rtk", version);
|
|
49
|
+
}
|
|
50
|
+
export function managedRtkExecutablePath(version = RTK_MANAGED_VERSION) {
|
|
51
|
+
return join(managedRtkInstallDir(version), BINARY_NAME);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the managed executable if present on disk (no network, no probe).
|
|
55
|
+
* Prefers explicit WP_RTK_BIN, then managed cache install.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveManagedRtkExecutable() {
|
|
58
|
+
// Skip is process-env policy for this call — do not poison the path cache.
|
|
59
|
+
if (process.env.WP_SKIP_RTK === "1")
|
|
60
|
+
return null;
|
|
61
|
+
if (cachedExecutable !== undefined)
|
|
62
|
+
return cachedExecutable;
|
|
63
|
+
const override = process.env.WP_RTK_BIN?.trim();
|
|
64
|
+
if (override && existsSync(override)) {
|
|
65
|
+
cachedExecutable = override;
|
|
66
|
+
return cachedExecutable;
|
|
67
|
+
}
|
|
68
|
+
const managed = managedRtkExecutablePath();
|
|
69
|
+
if (existsSync(managed)) {
|
|
70
|
+
cachedExecutable = managed;
|
|
71
|
+
return cachedExecutable;
|
|
72
|
+
}
|
|
73
|
+
cachedExecutable = null;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/** Capability probe against a concrete binary (or managed path). Fail-open. */
|
|
77
|
+
export function probeRtkCapabilityAt(executable) {
|
|
78
|
+
try {
|
|
79
|
+
const result = spawnSync(executable, [...RTK_CAPABILITY_ARGS], {
|
|
80
|
+
encoding: "utf8",
|
|
81
|
+
timeout: RTK_PROBE_TIMEOUT_MS,
|
|
82
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
83
|
+
});
|
|
84
|
+
return result.error == null && result.status === 0;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Process-cached: managed binary exists and passes capability probe. */
|
|
91
|
+
export function isManagedRtkAvailable() {
|
|
92
|
+
if (cachedCapability !== undefined)
|
|
93
|
+
return cachedCapability;
|
|
94
|
+
if (process.env.WP_SKIP_RTK === "1") {
|
|
95
|
+
cachedCapability = false;
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const path = resolveManagedRtkExecutable();
|
|
99
|
+
if (!path) {
|
|
100
|
+
cachedCapability = false;
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
cachedCapability = probeRtkCapabilityAt(path);
|
|
104
|
+
return cachedCapability;
|
|
105
|
+
}
|
|
106
|
+
function defaultDownloader(url, destFile) {
|
|
107
|
+
// Prefer curl when present (HTTP/2, redirects); fall back to node fetch via spawn node -e is avoided.
|
|
108
|
+
const curl = spawnSync("curl", ["-fsSL", "--retry", "2", "--retry-delay", "1", "-o", destFile, url], { encoding: "utf8", timeout: 120_000 });
|
|
109
|
+
if (curl.error || curl.status !== 0) {
|
|
110
|
+
throw new Error(`download failed (${curl.status ?? "error"}): ${String(curl.stderr ?? curl.error ?? "").trim() || url}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isRegularFile(path) {
|
|
114
|
+
try {
|
|
115
|
+
return lstatSync(path).isFile();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Prefer a real file named rtk / rtk.exe — ignore directories of the same name. */
|
|
122
|
+
function findExtractedBinary(root) {
|
|
123
|
+
const stack = [root];
|
|
124
|
+
while (stack.length > 0) {
|
|
125
|
+
const dir = stack.pop();
|
|
126
|
+
let entries;
|
|
127
|
+
try {
|
|
128
|
+
entries = readdirSync(dir);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
for (const name of entries) {
|
|
134
|
+
const full = join(dir, name);
|
|
135
|
+
if ((name === BINARY_NAME || name === "rtk") && isRegularFile(full)) {
|
|
136
|
+
return full;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
// shallow walk only one extra level for nested release layouts
|
|
140
|
+
const nested = readdirSync(full);
|
|
141
|
+
for (const child of nested) {
|
|
142
|
+
const nestedFull = join(full, child);
|
|
143
|
+
if ((child === BINARY_NAME || child === "rtk") && isRegularFile(nestedFull)) {
|
|
144
|
+
return nestedFull;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// not a dir
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function extractArchive(archivePath, extractDir) {
|
|
156
|
+
mkdirSync(extractDir, { recursive: true });
|
|
157
|
+
if (archivePath.endsWith(".zip")) {
|
|
158
|
+
const unzip = spawnSync("unzip", ["-qo", archivePath, "-d", extractDir], {
|
|
159
|
+
encoding: "utf8",
|
|
160
|
+
timeout: 60_000,
|
|
161
|
+
});
|
|
162
|
+
if (unzip.status !== 0) {
|
|
163
|
+
throw new Error(`unzip failed: ${String(unzip.stderr ?? "").trim()}`);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const tar = spawnSync("tar", ["-xzf", archivePath, "-C", extractDir], {
|
|
168
|
+
encoding: "utf8",
|
|
169
|
+
timeout: 60_000,
|
|
170
|
+
});
|
|
171
|
+
if (tar.status !== 0) {
|
|
172
|
+
throw new Error(`tar extract failed: ${String(tar.stderr ?? "").trim()}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Ensure the pinned managed rtk binary is on disk. Downloads once into the
|
|
177
|
+
* Webpresso cache. No brew/cargo. Idempotent.
|
|
178
|
+
*/
|
|
179
|
+
export function ensureManagedRtk(options = {}) {
|
|
180
|
+
if (process.env.WP_SKIP_RTK === "1") {
|
|
181
|
+
return { ok: false, error: "WP_SKIP_RTK=1" };
|
|
182
|
+
}
|
|
183
|
+
const version = options.version ?? RTK_MANAGED_VERSION;
|
|
184
|
+
const dest = managedRtkExecutablePath(version);
|
|
185
|
+
if (existsSync(dest) && probeRtkCapabilityAt(dest)) {
|
|
186
|
+
resetManagedRtkCacheForTests();
|
|
187
|
+
cachedExecutable = dest;
|
|
188
|
+
cachedCapability = true;
|
|
189
|
+
return { ok: true, path: dest, installed: false };
|
|
190
|
+
}
|
|
191
|
+
const url = managedRtkDownloadUrl(version);
|
|
192
|
+
if (!url) {
|
|
193
|
+
return {
|
|
194
|
+
ok: false,
|
|
195
|
+
error: `no managed rtk asset for ${process.platform}/${process.arch}`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const installDir = managedRtkInstallDir(version);
|
|
199
|
+
mkdirSync(installDir, { recursive: true });
|
|
200
|
+
const work = mkdtempSync(join(tmpdir(), "wp-rtk-"));
|
|
201
|
+
try {
|
|
202
|
+
const archiveName = basename(url);
|
|
203
|
+
const archivePath = join(work, archiveName);
|
|
204
|
+
const download = options.downloader ?? defaultDownloader;
|
|
205
|
+
download(url, archivePath);
|
|
206
|
+
const extractDir = join(work, "extract");
|
|
207
|
+
extractArchive(archivePath, extractDir);
|
|
208
|
+
const found = findExtractedBinary(extractDir);
|
|
209
|
+
if (!found) {
|
|
210
|
+
return { ok: false, error: `archive did not contain ${BINARY_NAME}` };
|
|
211
|
+
}
|
|
212
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
213
|
+
// Stage under a unique name so concurrent setup never clobbers a live dest
|
|
214
|
+
// mid-probe, and we only delete our own staged file on failure.
|
|
215
|
+
const staged = join(installDir, `.rtk-staging-${process.pid}-${Date.now()}-${randomBytes(6).toString("hex")}`);
|
|
216
|
+
try {
|
|
217
|
+
try {
|
|
218
|
+
renameSync(found, staged);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
const cp = spawnSync("cp", [found, staged], { encoding: "utf8" });
|
|
222
|
+
if (cp.status !== 0) {
|
|
223
|
+
return { ok: false, error: `failed to stage managed rtk at ${staged}` };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (process.platform !== "win32") {
|
|
227
|
+
chmodSync(staged, 0o755);
|
|
228
|
+
}
|
|
229
|
+
if (!probeRtkCapabilityAt(staged)) {
|
|
230
|
+
return { ok: false, error: "downloaded rtk failed capability probe (rtk gain --help)" };
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
renameSync(staged, dest);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Cross-device rename: replace dest via copy then drop stage.
|
|
237
|
+
const cp = spawnSync("cp", [staged, dest], { encoding: "utf8" });
|
|
238
|
+
if (cp.status !== 0) {
|
|
239
|
+
return { ok: false, error: `failed to place managed rtk at ${dest}` };
|
|
240
|
+
}
|
|
241
|
+
if (process.platform !== "win32") {
|
|
242
|
+
chmodSync(dest, 0o755);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
try {
|
|
248
|
+
rmSync(staged, { force: true });
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// ignore
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
resetManagedRtkCacheForTests();
|
|
255
|
+
cachedExecutable = dest;
|
|
256
|
+
cachedCapability = true;
|
|
257
|
+
return { ok: true, path: dest, installed: true };
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
261
|
+
return { ok: false, error: message };
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
try {
|
|
265
|
+
rmSync(work, { recursive: true, force: true });
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// ignore
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/** Write a tiny marker file for diagnostics (optional). */
|
|
273
|
+
export function writeManagedRtkVersionMarker(path, version) {
|
|
274
|
+
writeFileSync(`${path}.version`, `${version}\n`, "utf8");
|
|
275
|
+
}
|