@aiwg/cli 2026.10.5 → 2026.10.7
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/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +7 -3
- package/dist/src/a2a/agent-card.js +11 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +57 -6
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
- package/dist/src/artifacts/fortemi-shard-export.js +284 -0
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +143 -14
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +2 -0
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +114 -55
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/auth/client.js +5 -1
- package/dist/src/auth/credential-store.js +5 -1
- package/dist/src/auth/sensitive-input.js +151 -0
- package/dist/src/channel/manager.mjs +3 -0
- package/dist/src/cli/handlers/agentcard.js +47 -25
- package/dist/src/cli/handlers/auth.js +22 -2
- package/dist/src/cli/handlers/decision.js +22 -3
- package/dist/src/cli/handlers/effect.js +4 -84
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/install.js +6 -0
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/marketplace.js +15 -4
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +1 -0
- package/dist/src/cli/handlers/setup.js +432 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +335 -107
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +81 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/gitignore.js +4 -0
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/config/workspace.js +2 -1
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/adapters/jev.js +20 -31
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/ask.js +6 -1
- package/dist/src/decision/benchmark.js +482 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +23 -18
- package/dist/src/decision/qualification/load-staged.js +576 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +12 -5
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +238 -0
- package/dist/src/discovery/plan-apply.js +567 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/effects/host.js +91 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-local-gitignore.js +2 -0
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/issues/cli.js +16 -5
- package/dist/src/issues/live.js +44 -12
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/marketplace/exchange.js +18 -6
- package/dist/src/marketplace/provenance.js +3 -2
- package/dist/src/mcp/adapters/codex-runtime.js +46 -76
- package/dist/src/mcp/cli.mjs +22 -96
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/grok-build-config.mjs +2 -1
- package/dist/src/mcp/muse-native-config.mjs +2 -1
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/project-registry.js +2 -1
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/mission-protocol/worker-status.js +91 -1
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +8 -1
- package/dist/src/models/worker-installation.mjs +231 -38
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/packages/adapters/git.js +99 -23
- package/dist/src/packages/adapters/gitea.js +8 -27
- package/dist/src/packages/adapters/github.js +7 -25
- package/dist/src/packages/registry.js +4 -4
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/process/cli-command.d.mts +5 -0
- package/dist/src/process/cli-command.mjs +42 -0
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +124 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +10 -9
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +63 -53
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/workspace-discovery.js +3 -4
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
- package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
- package/dist/src/smiths/context-pipeline/finalization.js +3 -2
- package/dist/src/smiths/context-pipeline/generator.js +7 -19
- package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
- package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
- package/dist/src/tracker/project-inputs.js +2 -0
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/update/notifier.mjs +2 -4
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +24 -14
- package/tools/agents/providers/claude.mjs +6 -3
- package/tools/agents/providers/codex.mjs +54 -57
- package/tools/agents/providers/context-write.mjs +28 -0
- package/tools/agents/providers/copilot.mjs +13 -4
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +12 -15
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +31 -5
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +289 -80
- package/tools/agents/providers/warp.mjs +4 -1
- package/tools/agents/providers/windsurf.mjs +13 -3
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +49 -8
- package/tools/ralph-external/session-launcher.mjs +405 -35
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -11,11 +11,40 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { execFile } from 'child_process';
|
|
13
13
|
import { promisify } from 'util';
|
|
14
|
-
import { mkdir, mkdtemp, readFile, rename, rm } from 'fs/promises';
|
|
14
|
+
import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'fs/promises';
|
|
15
15
|
import { join } from 'path';
|
|
16
|
-
import { homedir } from 'os';
|
|
16
|
+
import { homedir, tmpdir } from 'os';
|
|
17
17
|
import { existsSync } from 'fs';
|
|
18
|
+
import { resolveSensitiveInput } from '../../auth/sensitive-input.js';
|
|
19
|
+
import { helperEnvironment, HELPER_ENV_ALLOWLIST } from '../../process/cli-command.mjs';
|
|
18
20
|
const execFileAsync = promisify(execFile);
|
|
21
|
+
// Deliberately separate from serializable sources, locks and provenance.
|
|
22
|
+
const credentials = new WeakMap();
|
|
23
|
+
function hasTokenSource(input) {
|
|
24
|
+
return Boolean(input.interactive || input.stdin || input.file !== undefined || input.store !== undefined || input.env !== undefined);
|
|
25
|
+
}
|
|
26
|
+
/** Explicit sources precede the established env/file lookup; no token means SSH. */
|
|
27
|
+
export async function resolvePackageToken(input = {}, envName, paths = []) {
|
|
28
|
+
if (hasTokenSource(input))
|
|
29
|
+
return resolveSensitiveInput({ ...input, prefix: 'token' });
|
|
30
|
+
if (envName && process.env[envName])
|
|
31
|
+
return resolveSensitiveInput({ prefix: 'token', env: envName });
|
|
32
|
+
for (const file of paths) {
|
|
33
|
+
if (existsSync(file))
|
|
34
|
+
return resolveSensitiveInput({ prefix: 'token', file });
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
// GitHub rejects Bearer on git smart HTTP; Basic with x-access-token works on GitHub and Gitea.
|
|
39
|
+
function basicCredential(token) {
|
|
40
|
+
return Buffer.from(`x-access-token:${token}`).toString('base64');
|
|
41
|
+
}
|
|
42
|
+
function redact(value, token) {
|
|
43
|
+
if (!token)
|
|
44
|
+
return value;
|
|
45
|
+
return [token, encodeURIComponent(token), basicCredential(token)]
|
|
46
|
+
.reduce((result, secret) => result.split(secret).join('[REDACTED]'), value);
|
|
47
|
+
}
|
|
19
48
|
/**
|
|
20
49
|
* Default cache root
|
|
21
50
|
*/
|
|
@@ -34,16 +63,24 @@ export function buildCachePath(owner, name, version) {
|
|
|
34
63
|
/**
|
|
35
64
|
* Run a git command, returning stdout
|
|
36
65
|
*/
|
|
37
|
-
async function git(args, cwd) {
|
|
38
|
-
const env =
|
|
66
|
+
async function git(args, cwd, authentication) {
|
|
67
|
+
const env = helperEnvironment(process.env, [...HELPER_ENV_ALLOWLIST, 'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
|
|
68
|
+
'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSH_VARIANT', 'XDG_CONFIG_HOME']);
|
|
39
69
|
// Suppress interactive prompts
|
|
40
70
|
env.GIT_TERMINAL_PROMPT = '0';
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
env
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
71
|
+
if (authentication) {
|
|
72
|
+
env.GIT_CONFIG_COUNT = '1';
|
|
73
|
+
env.GIT_CONFIG_KEY_0 = 'include.path';
|
|
74
|
+
env.GIT_CONFIG_VALUE_0 = authentication.config;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const { stdout } = await execFileAsync('git', args, { cwd, env, timeout: 120_000 });
|
|
78
|
+
return redact(stdout.trim(), authentication?.token);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
// Never rethrow execFile's object: it carries raw stdout/stderr properties.
|
|
82
|
+
throw new Error(redact(error instanceof Error ? error.message : 'Git command failed.', authentication?.token));
|
|
83
|
+
}
|
|
47
84
|
}
|
|
48
85
|
/**
|
|
49
86
|
* Detect the manifest type from a cloned package directory
|
|
@@ -69,9 +106,9 @@ async function detectManifestType(cachePath) {
|
|
|
69
106
|
/**
|
|
70
107
|
* Resolve the latest tag from a remote git repo
|
|
71
108
|
*/
|
|
72
|
-
async function resolveLatestTag(gitUrl) {
|
|
109
|
+
async function resolveLatestTag(gitUrl, authentication) {
|
|
73
110
|
try {
|
|
74
|
-
const output = await git(['ls-remote', '--tags', '--sort=-v:refname', gitUrl]);
|
|
111
|
+
const output = await git(['ls-remote', '--tags', '--sort=-v:refname', gitUrl], undefined, authentication);
|
|
75
112
|
const firstLine = output.split('\n')[0] ?? '';
|
|
76
113
|
const match = firstLine.match(/refs\/tags\/(.+)/);
|
|
77
114
|
if (match && match[1])
|
|
@@ -82,14 +119,14 @@ async function resolveLatestTag(gitUrl) {
|
|
|
82
119
|
}
|
|
83
120
|
return 'latest';
|
|
84
121
|
}
|
|
85
|
-
async function resolveRemoteCommit(gitUrl, requestedRef) {
|
|
122
|
+
async function resolveRemoteCommit(gitUrl, requestedRef, authentication) {
|
|
86
123
|
if (/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(requestedRef))
|
|
87
124
|
return requestedRef;
|
|
88
125
|
const patterns = requestedRef === 'HEAD'
|
|
89
126
|
? ['HEAD']
|
|
90
127
|
: [`refs/tags/${requestedRef}^{}`, `refs/tags/${requestedRef}`, `refs/heads/${requestedRef}`, requestedRef];
|
|
91
128
|
try {
|
|
92
|
-
const output = await git(['ls-remote', gitUrl, ...patterns]);
|
|
129
|
+
const output = await git(['ls-remote', gitUrl, ...patterns], undefined, authentication);
|
|
93
130
|
const rows = output.split('\n').filter(Boolean).map((line) => {
|
|
94
131
|
const [oid = '', ref = ''] = line.split(/\s+/, 2);
|
|
95
132
|
return { oid, ref };
|
|
@@ -136,30 +173,69 @@ export class GitAdapter {
|
|
|
136
173
|
ref.startsWith('ssh://') ||
|
|
137
174
|
ref.startsWith('git+https://'));
|
|
138
175
|
}
|
|
139
|
-
async resolve(ref) {
|
|
176
|
+
async resolve(ref, options = {}) {
|
|
140
177
|
if (!ref.rawUrl)
|
|
141
178
|
return null;
|
|
142
|
-
|
|
179
|
+
const source = {
|
|
143
180
|
gitUrl: ref.rawUrl,
|
|
144
181
|
ref: ref.version,
|
|
145
182
|
label: ref.rawUrl,
|
|
146
183
|
};
|
|
184
|
+
const token = await resolvePackageToken(options.tokenInput);
|
|
185
|
+
return this.withToken(source, token);
|
|
186
|
+
}
|
|
187
|
+
withToken(source, token) {
|
|
188
|
+
if (/^https?:\/\//.test(source.gitUrl)) {
|
|
189
|
+
const url = new URL(source.gitUrl);
|
|
190
|
+
if (url.username || url.password)
|
|
191
|
+
throw new Error('Credentials in Git URLs are refused; use --token-stdin, --token-file, --token-store or --token-env.');
|
|
192
|
+
}
|
|
193
|
+
if (token)
|
|
194
|
+
credentials.set(source, token);
|
|
195
|
+
return source;
|
|
147
196
|
}
|
|
148
197
|
async fetch(source, options = {}) {
|
|
198
|
+
this.withToken(source);
|
|
199
|
+
const token = credentials.get(source) ?? (hasTokenSource(options.tokenInput ?? {})
|
|
200
|
+
? await resolvePackageToken(options.tokenInput) : undefined);
|
|
201
|
+
let privateDir;
|
|
202
|
+
try {
|
|
203
|
+
let authentication;
|
|
204
|
+
if (token) {
|
|
205
|
+
if (process.platform === 'win32')
|
|
206
|
+
throw new Error('Git token authentication requires private temporary-file ACL validation on Windows; use SSH-agent authentication.');
|
|
207
|
+
const url = new URL(source.gitUrl);
|
|
208
|
+
if (url.protocol !== 'https:')
|
|
209
|
+
throw new Error('Git token authentication requires HTTPS.');
|
|
210
|
+
privateDir = await mkdtemp(join(tmpdir(), 'aiwg-git-credentials-'));
|
|
211
|
+
const config = join(privateDir, 'config');
|
|
212
|
+
// A path-only include survives NODE_DEBUG=child_process safely. The
|
|
213
|
+
// origin-scoped header is transient and never saved in .git/config.
|
|
214
|
+
await writeFile(config, `[http ${JSON.stringify(`${url.origin}/`)}]\n\textraHeader = ${JSON.stringify(`Authorization: Basic ${basicCredential(token)}`)}\n`, { mode: 0o600 });
|
|
215
|
+
authentication = { config, token };
|
|
216
|
+
}
|
|
217
|
+
return await this.fetchAuthenticated(source, options, authentication);
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
if (privateDir)
|
|
221
|
+
await rm(privateDir, { recursive: true, force: true });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async fetchAuthenticated(source, options, authentication) {
|
|
149
225
|
// Resolve discovery inputs before any deployment and cache by immutable
|
|
150
226
|
// commit rather than by a movable tag/branch name (#2009).
|
|
151
227
|
let requestedRef = source.ref;
|
|
152
228
|
if (!requestedRef) {
|
|
153
|
-
const latest = await resolveLatestTag(source.gitUrl);
|
|
229
|
+
const latest = await resolveLatestTag(source.gitUrl, authentication);
|
|
154
230
|
requestedRef = latest === 'latest' ? 'HEAD' : latest;
|
|
155
231
|
source.ref = requestedRef;
|
|
156
232
|
}
|
|
157
|
-
const advertisedCommit = await resolveRemoteCommit(source.gitUrl, requestedRef);
|
|
233
|
+
const advertisedCommit = await resolveRemoteCommit(source.gitUrl, requestedRef, authentication);
|
|
158
234
|
const { owner, name } = urlCacheIdentity(source.gitUrl);
|
|
159
235
|
if (advertisedCommit) {
|
|
160
236
|
const existing = buildCachePath(owner, name, advertisedCommit);
|
|
161
237
|
if (!options.refresh && existsSync(join(existing, '.git'))) {
|
|
162
|
-
const existingCommit = await git(['rev-parse', 'HEAD^{commit}'], existing);
|
|
238
|
+
const existingCommit = await git(['rev-parse', 'HEAD^{commit}'], existing, authentication);
|
|
163
239
|
if (existingCommit !== advertisedCommit)
|
|
164
240
|
throw new Error(`Immutable package cache mismatch at ${existing}`);
|
|
165
241
|
return existing;
|
|
@@ -169,9 +245,9 @@ export class GitAdapter {
|
|
|
169
245
|
await mkdir(ownerDir, { recursive: true });
|
|
170
246
|
const stage = await mkdtemp(join(ownerDir, `.${name}.resolve-`));
|
|
171
247
|
try {
|
|
172
|
-
await git(['clone', '--no-checkout', source.gitUrl, stage]);
|
|
173
|
-
await git(['checkout', '--detach', requestedRef], stage);
|
|
174
|
-
const resolvedCommit = await git(['rev-parse', 'HEAD^{commit}'], stage);
|
|
248
|
+
await git(['clone', '--no-checkout', source.gitUrl, stage], undefined, authentication);
|
|
249
|
+
await git(['checkout', '--detach', requestedRef], stage, authentication);
|
|
250
|
+
const resolvedCommit = await git(['rev-parse', 'HEAD^{commit}'], stage, authentication);
|
|
175
251
|
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(resolvedCommit)) {
|
|
176
252
|
throw new Error(`Git resolved '${requestedRef}' to invalid commit '${resolvedCommit}'`);
|
|
177
253
|
}
|
|
@@ -180,7 +256,7 @@ export class GitAdapter {
|
|
|
180
256
|
}
|
|
181
257
|
const cachePath = buildCachePath(owner, name, resolvedCommit);
|
|
182
258
|
if (existsSync(join(cachePath, '.git'))) {
|
|
183
|
-
const cachedCommit = await git(['rev-parse', 'HEAD^{commit}'], cachePath);
|
|
259
|
+
const cachedCommit = await git(['rev-parse', 'HEAD^{commit}'], cachePath, authentication);
|
|
184
260
|
if (cachedCommit !== resolvedCommit)
|
|
185
261
|
throw new Error(`Immutable package cache mismatch at ${cachePath}`);
|
|
186
262
|
await rm(stage, { recursive: true, force: true });
|
|
@@ -11,36 +11,17 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Authentication:
|
|
13
13
|
* - SSH clone: uses default SSH agent (recommended for teams)
|
|
14
|
-
* - HTTPS clone: reads token from GITEA_TOKEN env or ~/.config/gitea/token
|
|
14
|
+
* - HTTPS clone: resolves explicit sources, otherwise reads token from GITEA_TOKEN env or ~/.config/gitea/token
|
|
15
15
|
*
|
|
16
16
|
* @implements #557
|
|
17
17
|
*/
|
|
18
|
-
import { existsSync } from 'fs';
|
|
19
|
-
import { readFile } from 'fs/promises';
|
|
20
18
|
import { join } from 'path';
|
|
21
19
|
import { homedir } from 'os';
|
|
22
|
-
import { GitAdapter } from './git.js';
|
|
20
|
+
import { GitAdapter, resolvePackageToken } from './git.js';
|
|
23
21
|
/**
|
|
24
22
|
* Regex for "owner/name" or "owner/name@version" (no scheme prefix)
|
|
25
23
|
*/
|
|
26
24
|
const GITEA_SHORTHAND = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+(@[a-zA-Z0-9._/-]+)?$/;
|
|
27
|
-
/**
|
|
28
|
-
* Read Gitea token from standard file path (same as global CLAUDE.md convention)
|
|
29
|
-
*/
|
|
30
|
-
async function readGiteaToken() {
|
|
31
|
-
if (process.env.GITEA_TOKEN)
|
|
32
|
-
return process.env.GITEA_TOKEN;
|
|
33
|
-
const tokenPaths = [
|
|
34
|
-
join(homedir(), '.config', 'gitea', 'token'),
|
|
35
|
-
join(homedir(), '.aiwg', 'credentials', 'gitea'),
|
|
36
|
-
];
|
|
37
|
-
for (const p of tokenPaths) {
|
|
38
|
-
if (existsSync(p)) {
|
|
39
|
-
return (await readFile(p, 'utf-8')).trim();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
25
|
/**
|
|
45
26
|
* Resolve the Gitea host
|
|
46
27
|
*/
|
|
@@ -66,25 +47,25 @@ export class GiteaAdapter extends GitAdapter {
|
|
|
66
47
|
return false; // has scheme prefix → not gitea shorthand
|
|
67
48
|
return GITEA_SHORTHAND.test(ref);
|
|
68
49
|
}
|
|
69
|
-
async resolve(ref) {
|
|
50
|
+
async resolve(ref, options = {}) {
|
|
70
51
|
if (!ref.owner || !ref.name)
|
|
71
52
|
return null;
|
|
72
53
|
const host = resolveGiteaHost();
|
|
73
|
-
const token = await
|
|
54
|
+
const token = await resolvePackageToken(options.tokenInput, 'GITEA_TOKEN', [join(homedir(), '.config', 'gitea', 'token'), join(homedir(), '.aiwg', 'credentials', 'gitea')]);
|
|
74
55
|
let gitUrl;
|
|
75
56
|
if (token) {
|
|
76
|
-
// HTTPS
|
|
77
|
-
gitUrl = `https://${
|
|
57
|
+
// HTTPS; credential is held outside the serializable source
|
|
58
|
+
gitUrl = `https://${host}/${ref.owner}/${ref.name}.git`;
|
|
78
59
|
}
|
|
79
60
|
else {
|
|
80
61
|
// SSH (relies on SSH agent)
|
|
81
62
|
gitUrl = `git@${host}:${ref.owner}/${ref.name}.git`;
|
|
82
63
|
}
|
|
83
|
-
return {
|
|
64
|
+
return this.withToken({
|
|
84
65
|
gitUrl,
|
|
85
66
|
ref: ref.version,
|
|
86
67
|
label: `${host}/${ref.owner}/${ref.name}${ref.version ? `@${ref.version}` : ''}`,
|
|
87
|
-
};
|
|
68
|
+
}, token);
|
|
88
69
|
}
|
|
89
70
|
async fetch(source, options = {}) {
|
|
90
71
|
return super.fetch(source, options);
|
|
@@ -5,32 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Authentication:
|
|
7
7
|
* - SSH clone: uses default SSH agent
|
|
8
|
-
* - HTTPS clone: reads token from GITHUB_TOKEN env or ~/.aiwg/credentials/github
|
|
8
|
+
* - HTTPS clone: resolves explicit sources, otherwise reads token from GITHUB_TOKEN env or ~/.aiwg/credentials/github
|
|
9
9
|
*
|
|
10
10
|
* @implements #557
|
|
11
11
|
*/
|
|
12
|
-
import { existsSync } from 'fs';
|
|
13
|
-
import { readFile } from 'fs/promises';
|
|
14
12
|
import { join } from 'path';
|
|
15
13
|
import { homedir } from 'os';
|
|
16
|
-
import { GitAdapter } from './git.js';
|
|
14
|
+
import { GitAdapter, resolvePackageToken } from './git.js';
|
|
17
15
|
const GITHUB_HOST = 'github.com';
|
|
18
|
-
/**
|
|
19
|
-
* Read GitHub token from standard locations
|
|
20
|
-
*/
|
|
21
|
-
async function readGitHubToken() {
|
|
22
|
-
if (process.env.GITHUB_TOKEN)
|
|
23
|
-
return process.env.GITHUB_TOKEN;
|
|
24
|
-
const tokenPaths = [
|
|
25
|
-
join(homedir(), '.aiwg', 'credentials', 'github'),
|
|
26
|
-
];
|
|
27
|
-
for (const p of tokenPaths) {
|
|
28
|
-
if (existsSync(p)) {
|
|
29
|
-
return (await readFile(p, 'utf-8')).trim();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
16
|
/**
|
|
35
17
|
* GitHubAdapter
|
|
36
18
|
*
|
|
@@ -45,22 +27,22 @@ export class GitHubAdapter extends GitAdapter {
|
|
|
45
27
|
canResolve(ref) {
|
|
46
28
|
return ref.startsWith('github:');
|
|
47
29
|
}
|
|
48
|
-
async resolve(ref) {
|
|
30
|
+
async resolve(ref, options = {}) {
|
|
49
31
|
if (!ref.owner || !ref.name)
|
|
50
32
|
return null;
|
|
51
|
-
const token = await
|
|
33
|
+
const token = await resolvePackageToken(options.tokenInput, 'GITHUB_TOKEN', [join(homedir(), '.aiwg', 'credentials', 'github')]);
|
|
52
34
|
let gitUrl;
|
|
53
35
|
if (token) {
|
|
54
|
-
gitUrl = `https://${
|
|
36
|
+
gitUrl = `https://${GITHUB_HOST}/${ref.owner}/${ref.name}.git`;
|
|
55
37
|
}
|
|
56
38
|
else {
|
|
57
39
|
gitUrl = `git@${GITHUB_HOST}:${ref.owner}/${ref.name}.git`;
|
|
58
40
|
}
|
|
59
|
-
return {
|
|
41
|
+
return this.withToken({
|
|
60
42
|
gitUrl,
|
|
61
43
|
ref: ref.version,
|
|
62
44
|
label: `github.com/${ref.owner}/${ref.name}${ref.version ? `@${ref.version}` : ''}`,
|
|
63
|
-
};
|
|
45
|
+
}, token);
|
|
64
46
|
}
|
|
65
47
|
async fetch(source, options = {}) {
|
|
66
48
|
return super.fetch(source, options);
|
|
@@ -99,11 +99,11 @@ export function parseRef(raw) {
|
|
|
99
99
|
/**
|
|
100
100
|
* Resolve a ref to a PackageSource using the appropriate adapter
|
|
101
101
|
*/
|
|
102
|
-
export async function resolveRef(ref) {
|
|
102
|
+
export async function resolveRef(ref, options = {}) {
|
|
103
103
|
for (const adapter of ALL_ADAPTERS) {
|
|
104
104
|
if (!adapter.canResolve(ref.raw))
|
|
105
105
|
continue;
|
|
106
|
-
const source = await adapter.resolve(ref);
|
|
106
|
+
const source = await adapter.resolve(ref, options);
|
|
107
107
|
if (source)
|
|
108
108
|
return { source, adapter };
|
|
109
109
|
}
|
|
@@ -171,7 +171,7 @@ export async function readPackageNamespace(cachePath, registryKey) {
|
|
|
171
171
|
*/
|
|
172
172
|
export async function installPackage(rawRef, options = {}) {
|
|
173
173
|
const ref = parseRef(rawRef);
|
|
174
|
-
const resolved = await resolveRef(ref);
|
|
174
|
+
const resolved = await resolveRef(ref, options);
|
|
175
175
|
if (!resolved) {
|
|
176
176
|
throw new Error(`Cannot resolve package reference: '${rawRef}'\n` +
|
|
177
177
|
`Supported formats:\n` +
|
|
@@ -185,7 +185,7 @@ export async function installPackage(rawRef, options = {}) {
|
|
|
185
185
|
const { source, adapter } = resolved;
|
|
186
186
|
if (options.ref)
|
|
187
187
|
source.ref = options.ref;
|
|
188
|
-
const cachePath = await adapter.fetch(source, { refresh: options.refresh });
|
|
188
|
+
const cachePath = await adapter.fetch(source, { refresh: options.refresh, tokenInput: options.tokenInput });
|
|
189
189
|
const discovered = await discoverInstallablePackage(cachePath, options.packageSelector);
|
|
190
190
|
const standardEnvelopePaths = [
|
|
191
191
|
join(cachePath, '.aiwg', 'marketplace', 'envelope.json'),
|
|
@@ -177,7 +177,7 @@ export class MetadataValidator {
|
|
|
177
177
|
result.manifest = manifest;
|
|
178
178
|
}
|
|
179
179
|
else {
|
|
180
|
-
for (const issue of skillResult.errors.
|
|
180
|
+
for (const issue of skillResult.errors.issues) {
|
|
181
181
|
result.errors.push({
|
|
182
182
|
path: filename,
|
|
183
183
|
field: issue.path.join('.'),
|
|
@@ -14,3 +14,8 @@ export function cliCommand(
|
|
|
14
14
|
pathExt?: string,
|
|
15
15
|
cwd?: string,
|
|
16
16
|
): [string, string[]];
|
|
17
|
+
|
|
18
|
+
export const HELPER_ENV_ALLOWLIST: readonly string[];
|
|
19
|
+
export const DESKTOP_HELPER_ENV_ALLOWLIST: readonly string[];
|
|
20
|
+
export const DEPLOYMENT_HELPER_ENV_ALLOWLIST: readonly string[];
|
|
21
|
+
export function helperEnvironment(source?: NodeJS.ProcessEnv, names?: readonly string[]): Record<string, string>;
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
// Like worker dispatch, helper processes receive only explicitly named state.
|
|
5
|
+
// Keep provider credentials, NODE_OPTIONS and unrelated shell variables out of
|
|
6
|
+
// process launch diagnostics as well as the child's own environment.
|
|
7
|
+
export const HELPER_ENV_ALLOWLIST = Object.freeze([
|
|
8
|
+
'PATH', 'PATHEXT', 'SYSTEMROOT', 'SYSTEMDRIVE', 'WINDIR', 'COMSPEC',
|
|
9
|
+
'HOME', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH',
|
|
10
|
+
'TMPDIR', 'TMP', 'TEMP', 'LANG', 'LC_ALL', 'LC_CTYPE', 'TERM',
|
|
11
|
+
// Platform and network plumbing a child may need to reach anything at all
|
|
12
|
+
// (corporate proxies and CA bundles, Windows per-user data, git global config).
|
|
13
|
+
'APPDATA', 'LOCALAPPDATA', 'PROGRAMDATA',
|
|
14
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY', 'http_proxy', 'https_proxy', 'no_proxy',
|
|
15
|
+
'NODE_EXTRA_CA_CERTS', 'SSL_CERT_FILE', 'SSL_CERT_DIR',
|
|
16
|
+
'GIT_CONFIG_GLOBAL', 'GIT_CONFIG_SYSTEM', 'GIT_CONFIG_NOSYSTEM',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
export const DESKTOP_HELPER_ENV_ALLOWLIST = Object.freeze([
|
|
20
|
+
'DISPLAY', 'WAYLAND_DISPLAY', 'XAUTHORITY', 'XDG_RUNTIME_DIR',
|
|
21
|
+
'DBUS_SESSION_BUS_ADDRESS', 'XDG_CURRENT_DESKTOP',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
// Deployment helpers read project/configuration locations and policy switches;
|
|
25
|
+
// none consumes provider authentication or the caller's complete shell state.
|
|
26
|
+
export const DEPLOYMENT_HELPER_ENV_ALLOWLIST = Object.freeze([
|
|
27
|
+
...HELPER_ENV_ALLOWLIST, 'AIWG_ROOT', 'AIWG_HOME', 'AIWG_CONFIG', 'AIWG_PROJECT_DIR',
|
|
28
|
+
'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'LOCALAPPDATA',
|
|
29
|
+
'AIWG_ARTIFACTS_PATH', 'AIWG_PROJECT_ARTIFACTS_PATH', 'AIWG_PROJECT_AIWG_DIR',
|
|
30
|
+
'AIWG_PROJECT_MEMORY_HOME', 'AIWG_USER_REGISTRY_PATH', 'AIWG_RULE_BINDING_INDEX_CACHE',
|
|
31
|
+
'AIWG_DEPLOY_SUBPROCESS', 'AIWG_REPORT_DEPLOY_COLLISIONS', 'CODEX_HOME', 'HERMES_HOME',
|
|
32
|
+
'OPENCLAW_PROFILE', 'OMP_PROFILE', 'PI_PROFILE', 'PI_CONFIG_DIR', 'PI_CODING_AGENT_DIR',
|
|
33
|
+
'AIWG_RESOLVE_IMPL_FROM', 'NODE_ENV', 'VITEST', 'NO_UPDATE_NOTIFIER', 'NO_COLOR', 'CI',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** Copy only the helper's declared variable names; never merge the parent. */
|
|
37
|
+
export function helperEnvironment(source = process.env, names = HELPER_ENV_ALLOWLIST) {
|
|
38
|
+
const environment = {};
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
const value = source?.[name];
|
|
41
|
+
if (typeof value === 'string' && !value.includes('\0')) environment[name] = value;
|
|
42
|
+
}
|
|
43
|
+
return environment;
|
|
44
|
+
}
|
|
45
|
+
|
|
4
46
|
// Share PATH/PATHEXT lookup with callers that can launch arbitrary batch files.
|
|
5
47
|
function* executableCandidates(command, searchPath, pathExt, preferNative = false, cwd = process.cwd()) {
|
|
6
48
|
const candidates = path.isAbsolute(command)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolveDeployVersion(root: string, explicitVersion?: string | null, warn?: (message: string) => void): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/** Resolve the selected corpus version before provider inputs are staged. */
|
|
5
|
+
export function resolveDeployVersion(root, explicitVersion, warn = console.warn) {
|
|
6
|
+
const valid = value => typeof value === 'string' && value.trim() &&
|
|
7
|
+
!/unknown|\s/i.test(value);
|
|
8
|
+
if (valid(explicitVersion)) return explicitVersion;
|
|
9
|
+
if (!explicitVersion) {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
12
|
+
if (valid(pkg.version)) return pkg.version;
|
|
13
|
+
} catch { /* Report missing, unreadable and invalid versions consistently. */ }
|
|
14
|
+
}
|
|
15
|
+
warn(`Unable to resolve AIWG deploy version from ${path.join(root, 'package.json')}; managed markers will use vunknown. Restore the package version or pass --deploy-version to the deployer.`);
|
|
16
|
+
return 'unknown';
|
|
17
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { integer, url } from '../schema/zod-validation.mjs';
|
|
2
3
|
import { homedir } from 'os';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
+
import { isAbsolute, join } from 'path';
|
|
4
5
|
import { resolveHermesHomePath } from './hermes-home.js';
|
|
5
6
|
import { resolveGrokbotSkillsDir } from './grokbot-paths.js';
|
|
6
7
|
import { getProviderCapabilities, } from './capability-matrix.js';
|
|
@@ -38,9 +39,9 @@ const ProviderDefinitionSchema = z.object({
|
|
|
38
39
|
status: z.enum(['stable', 'experimental', 'deprecated']),
|
|
39
40
|
builtIn: z.boolean(),
|
|
40
41
|
upstream: z.object({
|
|
41
|
-
source:
|
|
42
|
+
source: url(),
|
|
42
43
|
version: z.string().min(1),
|
|
43
|
-
revision: z.string().regex(/^[0-9a-f]{40}$/).refine((value) => value !== '0'.repeat(40), {
|
|
44
|
+
revision: z.string().regex(/^[0-9a-f]{40}$/).refine((value) => value !== '0'.repeat(40), { error: 'upstream.revision must be a real reviewed SHA, not all-zero' }),
|
|
44
45
|
runtime: z.string().min(1),
|
|
45
46
|
lastVerified: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
46
47
|
}).optional(),
|
|
@@ -67,7 +68,7 @@ const ProviderDefinitionSchema = z.object({
|
|
|
67
68
|
env: z.array(z.string().min(1)),
|
|
68
69
|
process: z.array(z.string().min(1)),
|
|
69
70
|
capabilityId: z.string().min(1),
|
|
70
|
-
runtimeEnvPriority:
|
|
71
|
+
runtimeEnvPriority: integer().positive().optional(),
|
|
71
72
|
}),
|
|
72
73
|
paths: z.object({
|
|
73
74
|
deployTarget: z.enum(['project', 'home', 'mixed']),
|
|
@@ -95,8 +96,8 @@ const ProviderDefinitionSchema = z.object({
|
|
|
95
96
|
includeSyntax: z.string().nullable(),
|
|
96
97
|
configRegistration: z.object({ file: z.string().min(1), key: z.string().min(1) }).nullable(),
|
|
97
98
|
bootstrapTargets: z.array(z.string().min(1)),
|
|
98
|
-
maxContextBytes:
|
|
99
|
-
recommendedMaxLines:
|
|
99
|
+
maxContextBytes: integer().positive().nullable(),
|
|
100
|
+
recommendedMaxLines: integer().positive().nullable(),
|
|
100
101
|
nestedContext: z.boolean(),
|
|
101
102
|
support: z.enum(['supported', 'degraded', 'unsupported']),
|
|
102
103
|
verification: z.object({
|
|
@@ -119,8 +120,8 @@ const ProviderDefinitionSchema = z.object({
|
|
|
119
120
|
pathType: z.enum(['project', 'home-dir']),
|
|
120
121
|
skillsBaseDir: z.string().min(1),
|
|
121
122
|
subdirLayout: z.boolean(),
|
|
122
|
-
maxNameLength:
|
|
123
|
-
maxDescriptionLength:
|
|
123
|
+
maxNameLength: integer().positive().optional(),
|
|
124
|
+
maxDescriptionLength: integer().positive().optional(),
|
|
124
125
|
appendToDescription: z.string().min(1).optional(),
|
|
125
126
|
}),
|
|
126
127
|
adapters: z.object({
|
|
@@ -132,8 +133,8 @@ const ProviderDefinitionSchema = z.object({
|
|
|
132
133
|
}),
|
|
133
134
|
capabilities: z.object({
|
|
134
135
|
matrixRef: z.string().nullable(),
|
|
135
|
-
nativeFeatures: z.record(z.boolean()),
|
|
136
|
-
emulation: z.record(z.string().nullable()),
|
|
136
|
+
nativeFeatures: z.record(z.string(), z.boolean()),
|
|
137
|
+
emulation: z.record(z.string(), z.string().nullable()),
|
|
137
138
|
}),
|
|
138
139
|
});
|
|
139
140
|
export const PROVIDER_IDS = [
|
|
@@ -755,10 +756,11 @@ const BUILT_IN_SEEDS = [
|
|
|
755
756
|
agents: null,
|
|
756
757
|
commands: null,
|
|
757
758
|
skills: resolveHermesHomePath('skills', '.aiwg'),
|
|
758
|
-
rules:
|
|
759
|
+
rules: '.hermes/rules',
|
|
759
760
|
behaviors: null,
|
|
760
761
|
},
|
|
761
762
|
kernelSkills: resolveHermesHomePath('skills'),
|
|
763
|
+
contextDiscovery: { agents: null, skills: resolveHermesHomePath('skills', '.aiwg'), rules: null, behaviors: null },
|
|
762
764
|
configFile: 'AGENTS.md',
|
|
763
765
|
contextFiles: { aiwgMd: true, agentsMd: true, claudeMdHook: false, hookFile: '.hermes.md', contextFile: 'AGENTS.md' },
|
|
764
766
|
},
|
|
@@ -1080,7 +1082,7 @@ const BUILT_IN_SEEDS = [
|
|
|
1080
1082
|
agents: '.agents/skills',
|
|
1081
1083
|
commands: '.pi/prompts',
|
|
1082
1084
|
skills: '.pi/.aiwg/skills',
|
|
1083
|
-
rules:
|
|
1085
|
+
rules: '.pi/rules',
|
|
1084
1086
|
behaviors: '.pi/extensions',
|
|
1085
1087
|
},
|
|
1086
1088
|
kernelSkills: '.agents/skills',
|
|
@@ -1425,13 +1427,14 @@ export function getProviderKernelSkillPath(provider) {
|
|
|
1425
1427
|
const definition = getProviderDefinition(provider);
|
|
1426
1428
|
return expandProviderHomePath(definition?.paths.kernelSkills ?? null);
|
|
1427
1429
|
}
|
|
1428
|
-
export function resolveProviderPathValue(providerPath, projectPath) {
|
|
1429
|
-
const expandedPath =
|
|
1430
|
+
export function resolveProviderPathValue(providerPath, projectPath, paths = { join, isAbsolute }, homeDirectory = homedir()) {
|
|
1431
|
+
const expandedPath = !providerPath ? '' : providerPath === '~' ? homeDirectory
|
|
1432
|
+
: providerPath.startsWith('~/') ? paths.join(homeDirectory, providerPath.slice(2)) : providerPath;
|
|
1430
1433
|
if (!expandedPath)
|
|
1431
1434
|
return '';
|
|
1432
|
-
if (
|
|
1435
|
+
if (paths.isAbsolute(expandedPath))
|
|
1433
1436
|
return expandedPath;
|
|
1434
|
-
return join(projectPath, expandedPath);
|
|
1437
|
+
return paths.join(projectPath, expandedPath);
|
|
1435
1438
|
}
|
|
1436
1439
|
export function normalizeProviderDefinitionId(provider) {
|
|
1437
1440
|
const normalized = provider?.trim().toLowerCase();
|
|
@@ -2,8 +2,17 @@ export interface SkillReceipt {
|
|
|
2
2
|
skillSource?: string;
|
|
3
3
|
managed?: Record<string, { hash: string; frameworkSlug?: string; source?: string; version?: string }>;
|
|
4
4
|
}
|
|
5
|
+
export interface SkillRemovalPlanEntry {
|
|
6
|
+
path: string;
|
|
7
|
+
action: 'remove' | 'preserve';
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SkillRemovalOptions {
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
plan?: SkillRemovalPlanEntry[];
|
|
13
|
+
}
|
|
5
14
|
export function extractFrameworkSlug(source: string): string | null;
|
|
6
15
|
export function sameSkillOwner(receipt: SkillReceipt | null, skillSource: string, frameworkSlug?: string | null): boolean;
|
|
7
16
|
export function canDeploySkill(receipt: SkillReceipt | null, skillSource: string, opts?: { force?: boolean }): boolean;
|
|
8
|
-
export function pruneRecordedSkillFiles(skillDir: string, managed: SkillReceipt['managed'], desired: Set<string>, opts?:
|
|
9
|
-
export function releaseSkillOwnership(skillDir: string, owner: { frameworkSlug?: string; skillSource?: string; sourceRoot?: string }, opts?:
|
|
17
|
+
export function pruneRecordedSkillFiles(skillDir: string, managed: SkillReceipt['managed'], desired: Set<string>, opts?: SkillRemovalOptions): void;
|
|
18
|
+
export function releaseSkillOwnership(skillDir: string, owner: { frameworkSlug?: string; skillSource?: string; sourceRoot?: string }, opts?: SkillRemovalOptions): boolean | null;
|
|
@@ -40,28 +40,40 @@ export function canDeploySkill(receipt, skillSource, { force = false } = {}) {
|
|
|
40
40
|
|
|
41
41
|
/** Remove unchanged recorded files, allowing symlinks above (but never inside) the skill root. */
|
|
42
42
|
export function pruneRecordedSkillFiles(skillDir, managed, desired, opts = {}) {
|
|
43
|
-
|
|
43
|
+
const root = path.resolve(skillDir);
|
|
44
44
|
for (const [relative, record] of Object.entries(managed || {})) {
|
|
45
|
-
if (desired.has(relative)) continue;
|
|
46
45
|
const target = path.resolve(skillDir, relative);
|
|
47
|
-
|
|
46
|
+
let action = 'preserve';
|
|
47
|
+
let reason = 'modified or unverifiable managed file';
|
|
48
|
+
if (desired.has(relative)) reason = 'retained managed file';
|
|
49
|
+
else if (!target.startsWith(`${root}${path.sep}`)) reason = 'path outside skill directory';
|
|
50
|
+
else {
|
|
51
|
+
try {
|
|
52
|
+
const expected = path.resolve(fs.realpathSync(skillDir), relative);
|
|
53
|
+
let current = target;
|
|
54
|
+
let symlink = false;
|
|
55
|
+
while (current !== root) {
|
|
56
|
+
if (fs.lstatSync(current).isSymbolicLink()) { symlink = true; break; }
|
|
57
|
+
current = path.dirname(current);
|
|
58
|
+
}
|
|
59
|
+
if (symlink) reason = 'symlink preserved';
|
|
60
|
+
else if (fs.realpathSync(target) === expected && fs.lstatSync(target).isFile()
|
|
61
|
+
&& `sha256:${createHash('sha256').update(fs.readFileSync(target)).digest('hex')}` === record?.hash) {
|
|
62
|
+
action = 'remove';
|
|
63
|
+
reason = 'unchanged managed file';
|
|
64
|
+
}
|
|
65
|
+
} catch { reason = 'missing or unreadable path; cannot verify'; }
|
|
66
|
+
}
|
|
67
|
+
opts.plan?.push({ path: target, action, reason });
|
|
68
|
+
if (opts.dryRun || action !== 'remove') continue;
|
|
48
69
|
try {
|
|
49
|
-
const expected = path.resolve(fs.realpathSync(skillDir), relative);
|
|
50
|
-
let current = target;
|
|
51
|
-
let symlink = false;
|
|
52
|
-
while (current !== path.resolve(skillDir)) {
|
|
53
|
-
if (fs.lstatSync(current).isSymbolicLink()) { symlink = true; break; }
|
|
54
|
-
current = path.dirname(current);
|
|
55
|
-
}
|
|
56
|
-
if (symlink || fs.realpathSync(target) !== expected || !fs.lstatSync(target).isFile()) continue;
|
|
57
|
-
if (`sha256:${createHash('sha256').update(fs.readFileSync(target)).digest('hex')}` !== record.hash) continue;
|
|
58
70
|
fs.unlinkSync(target);
|
|
59
71
|
let parent = path.dirname(target);
|
|
60
|
-
while (parent !==
|
|
72
|
+
while (parent !== root) {
|
|
61
73
|
try { fs.rmdirSync(parent); } catch { break; }
|
|
62
74
|
parent = path.dirname(parent);
|
|
63
75
|
}
|
|
64
|
-
} catch { /*
|
|
76
|
+
} catch { /* Files that cannot be removed are left alone. */ }
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
79
|
|
|
@@ -80,9 +92,15 @@ export function releaseSkillOwnership(skillDir, owner, opts = {}) {
|
|
|
80
92
|
&& !path.isAbsolute(path.relative(path.resolve(owner.sourceRoot), path.resolve(receipt.skillSource)));
|
|
81
93
|
if (!insideRoot && (slug ? slug !== owner.frameworkSlug
|
|
82
94
|
: !owner.skillSource || !sameSkillOwner(receipt, owner.skillSource, null))) return null;
|
|
83
|
-
if (opts.dryRun) return false;
|
|
84
95
|
pruneRecordedSkillFiles(skillDir, receipt.managed, new Set(), opts);
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
for (const target of [manifestPath, path.join(skillDir, '.aiwg-managed')]) {
|
|
97
|
+
// Metadata is released even when operator files keep the directory alive.
|
|
98
|
+
try {
|
|
99
|
+
fs.lstatSync(target);
|
|
100
|
+
opts.plan?.push({ path: path.resolve(target), action: 'remove', reason: 'ownership metadata released' });
|
|
101
|
+
} catch { /* Missing metadata has nothing to remove. */ }
|
|
102
|
+
if (!opts.dryRun) fs.rmSync(target, { force: true });
|
|
103
|
+
}
|
|
104
|
+
if (opts.dryRun) return false;
|
|
87
105
|
try { fs.rmdirSync(skillDir); return true; } catch { return false; }
|
|
88
106
|
}
|