@north-light/crouter 0.3.164 → 0.3.166
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-root.d.ts +6 -6
- package/dist/build-root.js +12 -13
- package/dist/builtin-memory/00-runtime-base.md +3 -4
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +2 -0
- package/dist/builtin-memory/04-orchestration-kernel.md +16 -18
- package/dist/builtin-memory/05-kinds/advisor/00-base.md +0 -2
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +3 -9
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -3
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +2 -10
- package/dist/builtin-memory/05-kinds/explore/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +0 -2
- package/dist/builtin-memory/05-kinds/general/00-base.md +3 -5
- package/dist/builtin-memory/05-kinds/general/01-orchestrator.md +0 -4
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +12 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +0 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +0 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +0 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +0 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +0 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -3
- package/dist/builtin-memory/05-kinds/spec/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +2 -4
- package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -1
- package/dist/builtin-memory/advisor/council.md +40 -0
- package/dist/builtin-memory/design.md +8 -11
- package/dist/builtin-memory/development.md +6 -9
- package/dist/builtin-memory/internal/INDEX.md +4 -5
- package/dist/builtin-memory/internal/agent-shaping.md +5 -7
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +3 -3
- package/dist/builtin-memory/internal/marketplaces.md +13 -14
- package/dist/builtin-memory/internal/nodes-and-canvas.md +4 -4
- package/dist/builtin-memory/internal/plugins.md +61 -45
- package/dist/builtin-memory/planning.md +4 -7
- package/dist/builtin-memory/spec.md +9 -12
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +8 -22
- package/dist/cli.js +1 -1
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +17 -4
- package/dist/clients/attach/__tests__/context-message.test.js +62 -22
- package/dist/clients/attach/__tests__/crtr-output.test.js +39 -30
- package/dist/clients/attach/render/chat-view.d.ts +20 -20
- package/dist/clients/attach/render/chat-view.js +40 -54
- package/dist/clients/attach/render/{frozen-history.d.ts → condensed-history.d.ts} +1 -18
- package/dist/clients/attach/render/condensed-history.js +60 -0
- package/dist/clients/attach/render/context-message.d.ts +1 -1
- package/dist/clients/attach/render/context-message.js +18 -41
- package/dist/clients/attach/session/bindings.d.ts +5 -3
- package/dist/clients/attach/session/bindings.js +7 -13
- package/dist/clients/attach/session/keys.d.ts +2 -0
- package/dist/clients/attach/session/keys.js +36 -37
- package/dist/clients/attach/session/profile-files.d.ts +8 -0
- package/dist/clients/attach/session/profile-files.js +157 -0
- package/dist/clients/attach/viewer.js +528 -529
- package/dist/commands/node-context.js +3 -2
- package/dist/commands/node.js +2 -2
- package/dist/commands/pkg/plugin-inspect.js +6 -7
- package/dist/commands/pkg/plugin-manage.d.ts +1 -1
- package/dist/commands/pkg/plugin-manage.js +131 -19
- package/dist/commands/pkg/plugin.js +2 -2
- package/dist/commands/pkg.js +6 -11
- package/dist/commands/profile/env.js +3 -3
- package/dist/commands/sys/config.js +17 -76
- package/dist/commands/sys/doctor.js +5 -91
- package/dist/commands/sys/setup-wizard.d.ts +9 -11
- package/dist/commands/sys/setup-wizard.js +47 -81
- package/dist/core/__tests__/base-worker-prompt.test.js +18 -21
- package/dist/core/__tests__/command-plugins-surfaces.test.js +39 -5
- package/dist/core/__tests__/command-plugins.test.js +75 -16
- package/dist/core/__tests__/review-model-floor.test.js +2 -2
- package/dist/core/__tests__/tmux-surface.test.js +10 -1
- package/dist/core/command-manifests/manifest.d.ts +24 -0
- package/dist/core/{configured-clis → command-manifests}/manifest.js +28 -25
- package/dist/core/command-manifests/registry.d.ts +1 -2
- package/dist/core/command-manifests/registry.js +2 -2
- package/dist/core/command-manifests/schema.d.ts +11 -13
- package/dist/core/command-manifests/schema.js +53 -192
- package/dist/core/command-plugins/compose.d.ts +0 -6
- package/dist/core/command-plugins/compose.js +32 -75
- package/dist/core/command-plugins/discovery.d.ts +25 -58
- package/dist/core/command-plugins/discovery.js +171 -261
- package/dist/core/command-plugins/endpoint.d.ts +24 -0
- package/dist/core/command-plugins/endpoint.js +48 -0
- package/dist/core/command-plugins/store.d.ts +16 -0
- package/dist/core/command-plugins/store.js +64 -0
- package/dist/core/command-plugins/{adapter.d.ts → transport/exec-invoke.d.ts} +3 -3
- package/dist/core/command-plugins/{adapter.js → transport/exec-invoke.js} +4 -4
- package/dist/core/{configured-clis/fetch.d.ts → command-plugins/transport/http-fetch.d.ts} +9 -18
- package/dist/core/{configured-clis/fetch.js → command-plugins/transport/http-fetch.js} +15 -35
- package/dist/core/{configured-clis/invoker.d.ts → command-plugins/transport/http-invoke.d.ts} +7 -7
- package/dist/core/{configured-clis/invoker.js → command-plugins/transport/http-invoke.js} +21 -23
- package/dist/core/command.d.ts +8 -9
- package/dist/core/command.js +6 -9
- package/dist/core/config.js +6 -10
- package/dist/core/env-name.d.ts +6 -0
- package/dist/core/env-name.js +9 -0
- package/dist/core/io.d.ts +1 -1
- package/dist/core/keybindings/__tests__/resolve.test.js +40 -3
- package/dist/core/keybindings/attach-control.d.ts +37 -0
- package/dist/core/keybindings/attach-control.js +38 -0
- package/dist/core/keybindings/catalog.d.ts +5 -4
- package/dist/core/keybindings/catalog.js +16 -8
- package/dist/core/keybindings/index.d.ts +1 -0
- package/dist/core/keybindings/index.js +1 -0
- package/dist/core/keybindings/types.d.ts +1 -1
- package/dist/core/preview-registry.js +41 -74
- package/dist/core/runtime/bearings.d.ts +2 -5
- package/dist/core/runtime/bearings.js +2 -5
- package/dist/core/runtime/broker.js +3 -2
- package/dist/core/runtime/front-door.d.ts +1 -1
- package/dist/core/runtime/front-door.js +2 -2
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +4 -3
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/situational-context.d.ts +1 -1
- package/dist/core/runtime/situational-context.js +1 -1
- package/dist/core/runtime/spawn.js +9 -9
- package/dist/core/runtime/tmux.js +29 -2
- package/dist/core/scope.d.ts +0 -5
- package/dist/core/scope.js +0 -10
- package/dist/core/user-settings.d.ts +193 -0
- package/dist/core/user-settings.js +252 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/pi-extensions/canvas-stophook.js +3 -2
- package/dist/prompts/review.js +4 -2
- package/dist/shared/generated-context.d.ts +34 -0
- package/dist/shared/generated-context.js +98 -0
- package/dist/types.d.ts +23 -16
- package/dist/types.js +3 -14
- package/dist/web-client/assets/index-BgLGlZ3D.css +2 -0
- package/dist/web-client/assets/{index-NIuSCOHM.js → index-CmoNqcCv.js} +19 -19
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/public-api.md +1 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/05-kinds/product/00-base.md +0 -25
- package/dist/builtin-memory/05-kinds/product/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/product/teardown.md +0 -15
- package/dist/builtin-memory/internal/workflow-codification.md +0 -82
- package/dist/builtin-memory/product.md +0 -80
- package/dist/clients/attach/render/frozen-history.js +0 -100
- package/dist/commands/pkg/cli-inspect.d.ts +0 -17
- package/dist/commands/pkg/cli-inspect.js +0 -190
- package/dist/commands/pkg/cli-manage.d.ts +0 -3
- package/dist/commands/pkg/cli-manage.js +0 -206
- package/dist/commands/pkg/cli.d.ts +0 -1
- package/dist/commands/pkg/cli.js +0 -14
- package/dist/core/configured-clis/cache.d.ts +0 -16
- package/dist/core/configured-clis/cache.js +0 -57
- package/dist/core/configured-clis/compose.d.ts +0 -14
- package/dist/core/configured-clis/compose.js +0 -60
- package/dist/core/configured-clis/discovery.d.ts +0 -47
- package/dist/core/configured-clis/discovery.js +0 -173
- package/dist/core/configured-clis/manifest.d.ts +0 -24
- package/dist/core/configured-clis/registration.d.ts +0 -40
- package/dist/core/configured-clis/registration.js +0 -201
- package/dist/web-client/assets/index-CqLKj8Xu.css +0 -2
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { defineLeaf } from '../../core/command.js';
|
|
2
|
-
import { notFound } from '../../core/errors.js';
|
|
3
|
-
import { paginate } from '../../core/pagination.js';
|
|
4
|
-
import { resolveScopeArg } from '../../core/scope.js';
|
|
5
|
-
import { SUBTREE_NAMES } from '../../build-root.js';
|
|
6
|
-
import { listConfiguredCliRegistrations, resolveConfiguredCli, } from '../../core/configured-clis/registration.js';
|
|
7
|
-
import { isCacheMetadataValid, readConfiguredCliCacheManifest } from '../../core/configured-clis/cache.js';
|
|
8
|
-
import { buildExternalCommandSnapshot } from '../../core/configured-clis/discovery.js';
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Shared inspection helpers (zero network — read stored manifest bytes only)
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
/** Whether a registration's STORED manifest reads as present. Mirrors the
|
|
13
|
-
* discovery classifier exactly (§3.3): a missing/metadata-mismatched/unreadable
|
|
14
|
-
* manifest, or one whose bytes do not parse as JSON, reads as `absent`; a
|
|
15
|
-
* parseable manifest — even one that later fails §4 validation — is `stored`.
|
|
16
|
-
* Never fetches. */
|
|
17
|
-
export function configuredCliManifestStored(registration) {
|
|
18
|
-
if (!isCacheMetadataValid(registration))
|
|
19
|
-
return false;
|
|
20
|
-
const bytes = readConfiguredCliCacheManifest(registration);
|
|
21
|
-
if (bytes === null)
|
|
22
|
-
return false;
|
|
23
|
-
try {
|
|
24
|
-
JSON.parse(Buffer.from(bytes).toString('utf8'));
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return false; // unparseable → reads as absent
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/** The stored-manifest state string for a registration. */
|
|
32
|
-
export function configuredCliManifestState(registration) {
|
|
33
|
-
return configuredCliManifestStored(registration) ? 'stored' : 'absent';
|
|
34
|
-
}
|
|
35
|
-
/** The core top-level subtree names — the closed set a remote may never mount
|
|
36
|
-
* onto or shadow. Passed to the unified snapshot as `reservedCoreNames`. */
|
|
37
|
-
function reservedCoreNames() {
|
|
38
|
-
return new Set(SUBTREE_NAMES);
|
|
39
|
-
}
|
|
40
|
-
/** Every full command path (space-joined tokens) in a contributed node's
|
|
41
|
-
* subtree, starting at the node itself — the top-level mount path plus every
|
|
42
|
-
* descendant. e.g. `["device", "device reboot", "device status"]`. */
|
|
43
|
-
function enumerateCommandPaths(node, prefix) {
|
|
44
|
-
const here = [...prefix, node.name];
|
|
45
|
-
const out = [here.join(' ')];
|
|
46
|
-
if (node.kind === 'branch') {
|
|
47
|
-
for (const child of node.children)
|
|
48
|
-
out.push(...enumerateCommandPaths(child, here));
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
/** The accepted top-level command names a registration currently contributes,
|
|
53
|
-
* read from THE unified collision-resolved snapshot (a collided command is
|
|
54
|
-
* dropped and appears only in issues). */
|
|
55
|
-
function contributedTopLevel(snapshot, name) {
|
|
56
|
-
return snapshot.contributions
|
|
57
|
-
.filter((c) => c.contributor.kind === 'configured-cli' && c.contributor.name === name)
|
|
58
|
-
.map((c) => c.node.name)
|
|
59
|
-
.sort((a, b) => a.localeCompare(b));
|
|
60
|
-
}
|
|
61
|
-
/** Serialize a single configured-CLI issue to the received/expected/next shape. */
|
|
62
|
-
function issueOut(issue) {
|
|
63
|
-
return {
|
|
64
|
-
code: issue.code,
|
|
65
|
-
...(issue.path !== undefined ? { path: issue.path } : {}),
|
|
66
|
-
message: issue.message,
|
|
67
|
-
received: issue.received,
|
|
68
|
-
expected: issue.expected,
|
|
69
|
-
next: issue.next,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
// list
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
export const cliList = defineLeaf({
|
|
76
|
-
name: 'list',
|
|
77
|
-
description: 'list registered configured CLIs',
|
|
78
|
-
whenToUse: 'enumerating configured-CLI registrations across scopes with endpoint, scope, stored-manifest state, and the top-level command(s) each currently contributes',
|
|
79
|
-
help: {
|
|
80
|
-
name: 'pkg cli list',
|
|
81
|
-
summary: 'list registered configured CLIs (zero network)',
|
|
82
|
-
params: [
|
|
83
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project', 'all'], required: false, constraint: 'Default: all.' },
|
|
84
|
-
{ kind: 'flag', name: 'limit', type: 'int', required: false, default: 50, constraint: 'Default 50, max 200.' },
|
|
85
|
-
{ kind: 'flag', name: 'cursor', type: 'string', required: false, constraint: 'Opaque token from next_cursor. Omit on first call.' },
|
|
86
|
-
],
|
|
87
|
-
output: [
|
|
88
|
-
{ name: 'items', type: 'object[]', required: true, constraint: 'Each: {name, endpoint, scope, manifest_state, top_level_commands}. manifest_state is "stored" or "absent". top_level_commands are the accepted top-level command names from the unified snapshot (empty when absent, invalid, or collided). Sorted by scope (project first) then name ascending.' },
|
|
89
|
-
{ name: 'next_cursor', type: 'string | null', required: true, constraint: 'null means no more items.' },
|
|
90
|
-
{ name: 'total', type: 'integer | null', required: true, constraint: 'Exact count.' },
|
|
91
|
-
],
|
|
92
|
-
outputKind: 'object',
|
|
93
|
-
effects: ['None. Read-only. Reads stored manifests only — never fetches, never revalidates.'],
|
|
94
|
-
},
|
|
95
|
-
run: async (input) => {
|
|
96
|
-
const scopeInput = input['scope'];
|
|
97
|
-
const limitRaw = input['limit'];
|
|
98
|
-
const limit = limitRaw !== undefined ? Math.min(Math.max(1, limitRaw), 200) : 50;
|
|
99
|
-
const cursor = input['cursor'];
|
|
100
|
-
let scopeFilter;
|
|
101
|
-
if (scopeInput !== undefined) {
|
|
102
|
-
const resolved = resolveScopeArg(scopeInput);
|
|
103
|
-
if (resolved !== 'all')
|
|
104
|
-
scopeFilter = resolved;
|
|
105
|
-
}
|
|
106
|
-
const snapshot = buildExternalCommandSnapshot(reservedCoreNames());
|
|
107
|
-
const all = listConfiguredCliRegistrations()
|
|
108
|
-
.filter((r) => scopeFilter === undefined || r.scope === scopeFilter)
|
|
109
|
-
.sort((a, b) => {
|
|
110
|
-
if (a.scope === 'project' && b.scope !== 'project')
|
|
111
|
-
return -1;
|
|
112
|
-
if (a.scope !== 'project' && b.scope === 'project')
|
|
113
|
-
return 1;
|
|
114
|
-
return a.name.localeCompare(b.name);
|
|
115
|
-
});
|
|
116
|
-
const result = paginate(all, { limit, cursor: cursor !== undefined ? cursor : undefined }, { defaultLimit: 50, maxLimit: 200, keyOf: (r) => `${r.scope}:${r.name}`, total: 'count' });
|
|
117
|
-
return {
|
|
118
|
-
items: result.items.map((r) => ({
|
|
119
|
-
name: r.name,
|
|
120
|
-
endpoint: r.endpoint,
|
|
121
|
-
scope: r.scope,
|
|
122
|
-
manifest_state: configuredCliManifestState(r),
|
|
123
|
-
top_level_commands: contributedTopLevel(snapshot, r.name),
|
|
124
|
-
})),
|
|
125
|
-
next_cursor: result.next_cursor,
|
|
126
|
-
total: result.total,
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
// show
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
/** Build the show/refresh report body for one registration from the unified
|
|
134
|
-
* snapshot: its stored-manifest state, every command it currently contributes
|
|
135
|
-
* (each with its full mount path and subtree), and every validation/collision
|
|
136
|
-
* issue attributed to it. Zero network. */
|
|
137
|
-
export function configuredCliReport(registration, snapshot) {
|
|
138
|
-
const commands = snapshot.contributions
|
|
139
|
-
.filter((c) => c.contributor.kind === 'configured-cli' && c.contributor.name === registration.name)
|
|
140
|
-
.map((c) => ({
|
|
141
|
-
command: c.node.name,
|
|
142
|
-
paths: enumerateCommandPaths(c.node, []),
|
|
143
|
-
}))
|
|
144
|
-
.sort((a, b) => a.command.localeCompare(b.command));
|
|
145
|
-
const issues = snapshot.issues
|
|
146
|
-
.filter((i) => i.contributor.kind === 'configured-cli' && i.contributor.name === registration.name)
|
|
147
|
-
.map(issueOut);
|
|
148
|
-
return {
|
|
149
|
-
name: registration.name,
|
|
150
|
-
scope: registration.scope,
|
|
151
|
-
endpoint: registration.endpoint,
|
|
152
|
-
manifest_state: configuredCliManifestState(registration),
|
|
153
|
-
commands,
|
|
154
|
-
issues,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
export const cliShow = defineLeaf({
|
|
158
|
-
name: 'show',
|
|
159
|
-
description: 'inventory one configured CLI',
|
|
160
|
-
whenToUse: 'reading one configured-CLI registration in detail: resolved endpoint, whether a manifest is stored, every command it currently contributes with full mount path and subtree, and every current validation/collision issue',
|
|
161
|
-
help: {
|
|
162
|
-
name: 'pkg cli show',
|
|
163
|
-
summary: 'inventory one configured CLI from its stored manifest (zero network)',
|
|
164
|
-
params: [
|
|
165
|
-
{ kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Registration name.' },
|
|
166
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Narrows resolution. Default: the effective registration (project outranks user).' },
|
|
167
|
-
],
|
|
168
|
-
output: [
|
|
169
|
-
{ name: 'name', type: 'string', required: true, constraint: 'Registration name.' },
|
|
170
|
-
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the registration lives in.' },
|
|
171
|
-
{ name: 'endpoint', type: 'string', required: true, constraint: 'Resolved manifest endpoint URL.' },
|
|
172
|
-
{ name: 'manifest_state', type: 'string', required: true, constraint: '"stored" or "absent". Absent is a valid inventory state (register-time fetch failed), not an error.' },
|
|
173
|
-
{ name: 'commands', type: 'object[]', required: true, constraint: 'Every accepted command this CLI contributes. Each: {command (top-level mount name), paths (every full command path in its subtree, space-joined)}. Empty when the manifest is absent, invalid, or collided.' },
|
|
174
|
-
{ name: 'issues', type: 'object[]', required: true, constraint: 'Every current validation/collision issue for this CLI, from the unified snapshot. Each: {code, path?, message, received, expected, next}.' },
|
|
175
|
-
],
|
|
176
|
-
outputKind: 'object',
|
|
177
|
-
effects: ['None. Read-only. Serves the stored manifest — never fetches, never revalidates.'],
|
|
178
|
-
},
|
|
179
|
-
run: async (input) => {
|
|
180
|
-
const name = input['name'];
|
|
181
|
-
const scopeInput = input['scope'];
|
|
182
|
-
const scope = scopeInput !== undefined ? resolveScopeArg(scopeInput) : undefined;
|
|
183
|
-
const registration = resolveConfiguredCli(name, scope);
|
|
184
|
-
if (registration === null) {
|
|
185
|
-
throw notFound(`configured CLI not found: ${name}`);
|
|
186
|
-
}
|
|
187
|
-
const snapshot = buildExternalCommandSnapshot(reservedCoreNames());
|
|
188
|
-
return configuredCliReport(registration, snapshot);
|
|
189
|
-
},
|
|
190
|
-
});
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { defineLeaf } from '../../core/command.js';
|
|
2
|
-
import { notFound, usage } from '../../core/errors.js';
|
|
3
|
-
import { resolveScopeArg, requireScopeRoot, ensureProjectScopeRoot, } from '../../core/scope.js';
|
|
4
|
-
import { updateConfig, ensureScopeInitialized, readConfig } from '../../core/config.js';
|
|
5
|
-
import { SUBTREE_NAMES } from '../../build-root.js';
|
|
6
|
-
import { resolveConfiguredCli, normalizeEndpoint, validateRegistrationName, validateEndpoint, validateEnvVarName, } from '../../core/configured-clis/registration.js';
|
|
7
|
-
import { removeConfiguredCliCache } from '../../core/configured-clis/cache.js';
|
|
8
|
-
import { registerFetchConfiguredCli, refreshConfiguredCli } from '../../core/configured-clis/fetch.js';
|
|
9
|
-
import { buildExternalCommandSnapshot } from '../../core/configured-clis/discovery.js';
|
|
10
|
-
import { configuredCliManifestState, configuredCliReport } from './cli-inspect.js';
|
|
11
|
-
/** Resolve the target scope for `register`. Spec default is **user** (register
|
|
12
|
-
* declares a remote surface; it does not install a local artifact). Only
|
|
13
|
-
* user/project are valid. */
|
|
14
|
-
function resolveRegisterScope(scopeInput) {
|
|
15
|
-
if (scopeInput === undefined)
|
|
16
|
-
return 'user';
|
|
17
|
-
const resolved = resolveScopeArg(scopeInput);
|
|
18
|
-
if (resolved === 'all' || resolved === 'builtin') {
|
|
19
|
-
throw usage('scope must be "user" or "project"');
|
|
20
|
-
}
|
|
21
|
-
return resolved;
|
|
22
|
-
}
|
|
23
|
-
export const cliRegister = defineLeaf({
|
|
24
|
-
name: 'register',
|
|
25
|
-
description: 'register a configured CLI and load its manifest',
|
|
26
|
-
whenToUse: 'declaring a configured CLI — a remote command surface defined by a manifest endpoint and executed as declarative HTTP. Records {name, endpoint, auth-env} to scope config and fetches-and-stores the manifest. Re-run on every guest boot to reload the manifest: a byte-identical re-register is an idempotent record but still re-fetches. To change an existing endpoint/auth-env, `remove` first.',
|
|
27
|
-
help: {
|
|
28
|
-
name: 'pkg cli register',
|
|
29
|
-
summary: 'register a configured CLI and fetch its manifest',
|
|
30
|
-
params: [
|
|
31
|
-
{ kind: 'flag', name: 'name', type: 'string', required: true, constraint: 'Registration name (kebab-case, unique among this scope\'s configured-CLI registrations). A local handle — NOT a command name; the top-level command(s) a CLI contributes are named by its manifest mounts.' },
|
|
32
|
-
{ kind: 'flag', name: 'endpoint', type: 'string', required: true, constraint: 'Absolute https:// URL crtr GETs to fetch the manifest (http:// permitted for loopback hosts only — local dev). Its origin is the default REST base for leaf calls. No credentials or fragment.' },
|
|
33
|
-
{ kind: 'flag', name: 'auth-env', type: 'string', required: false, constraint: 'Name of an env var holding a bearer token (e.g. NL_CORE_TOKEN). crtr reads process.env[NAME] at fetch/invocation time and sends Authorization: Bearer. Omit for an unauthenticated endpoint. The credential is never stored.' },
|
|
34
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Default: user.' },
|
|
35
|
-
],
|
|
36
|
-
output: [
|
|
37
|
-
{ name: 'name', type: 'string', required: true, constraint: 'Registration name.' },
|
|
38
|
-
{ name: 'endpoint', type: 'string', required: true, constraint: 'Normalized manifest endpoint URL as stored.' },
|
|
39
|
-
{ name: 'auth_env', type: 'string', required: false, constraint: 'Configured auth env var name. Absent when unauthenticated.' },
|
|
40
|
-
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the registration was written to.' },
|
|
41
|
-
{ name: 'manifest_state', type: 'string', required: true, constraint: '"stored" (fetch succeeded, or a prior manifest was preserved) or "absent" (fetch failed and nothing was stored).' },
|
|
42
|
-
{ name: 'unchanged', type: 'boolean', required: false, constraint: 'True when the record already existed identically (idempotent re-register). The manifest was still re-fetched.' },
|
|
43
|
-
],
|
|
44
|
-
outputKind: 'object',
|
|
45
|
-
effects: [
|
|
46
|
-
'Writes configuredClis.<name> to the scope config.json (atomic, lock-guarded), then performs exactly one authenticated GET of the endpoint and stores the returned bytes as the manifest (replacing any prior one on success).',
|
|
47
|
-
'A byte-identical re-register (same endpoint + auth-env) skips the config write but still re-fetches and replaces the stored manifest — the guest-boot reload path.',
|
|
48
|
-
'A fetch failure (network / non-2xx) preserves any prior stored manifest and exits success with a stderr diagnostic suggesting `pkg cli refresh <name>`. A named-but-unset auth env, a differing endpoint/auth-env for an existing name, or an invalid name/endpoint fails before writing or fetching.',
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
run: async (input) => {
|
|
52
|
-
const name = input['name']?.trim();
|
|
53
|
-
const endpointRaw = input['endpoint'];
|
|
54
|
-
const authEnvVar = input['authEnv'];
|
|
55
|
-
const scopeInput = input['scope'];
|
|
56
|
-
// ---- Validate inputs (a failure writes nothing and fetches nothing) ----
|
|
57
|
-
const nameCheck = validateRegistrationName(name);
|
|
58
|
-
if (!nameCheck.isValid) {
|
|
59
|
-
throw usage(`invalid --name: ${nameCheck.errors.join('; ')}`);
|
|
60
|
-
}
|
|
61
|
-
const endpointCheck = validateEndpoint(endpointRaw);
|
|
62
|
-
if (!endpointCheck.isValid) {
|
|
63
|
-
throw usage(`invalid --endpoint: ${endpointCheck.errors.join('; ')}`);
|
|
64
|
-
}
|
|
65
|
-
if (authEnvVar !== undefined) {
|
|
66
|
-
const envCheck = validateEnvVarName(authEnvVar);
|
|
67
|
-
if (!envCheck.isValid) {
|
|
68
|
-
throw usage(`invalid --auth-env: ${envCheck.errors.join('; ')}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const endpoint = normalizeEndpoint(endpointRaw);
|
|
72
|
-
const scope = resolveRegisterScope(scopeInput);
|
|
73
|
-
if (scope === 'project')
|
|
74
|
-
ensureProjectScopeRoot();
|
|
75
|
-
ensureScopeInitialized(scope, requireScopeRoot(scope));
|
|
76
|
-
// ---- Resolve any existing same-scope registration (idempotent / conflict) ----
|
|
77
|
-
const existing = readConfig(scope).configuredClis[name];
|
|
78
|
-
let unchanged = false;
|
|
79
|
-
if (existing !== undefined) {
|
|
80
|
-
const sameEndpoint = normalizeEndpoint(existing.endpoint) === endpoint;
|
|
81
|
-
const sameAuth = (existing.authEnvVar ?? undefined) === (authEnvVar ?? undefined);
|
|
82
|
-
if (sameEndpoint && sameAuth) {
|
|
83
|
-
unchanged = true; // idempotent record — still re-fetch below
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
throw usage(`configured CLI "${name}" already registered in ${scope} scope with ` +
|
|
87
|
-
`endpoint=${existing.endpoint}` +
|
|
88
|
-
`${existing.authEnvVar !== undefined ? ` auth-env=${existing.authEnvVar}` : ' (no auth-env)'}. ` +
|
|
89
|
-
`Run \`crtr pkg cli remove ${name} --scope ${scope}\` first to change it.`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
// ---- Auth-env precondition: a named-but-unset var fails usage before any
|
|
93
|
-
// fetch AND before any config write (a usage error mutates nothing). ----
|
|
94
|
-
if (authEnvVar !== undefined && !process.env[authEnvVar]) {
|
|
95
|
-
throw usage(`auth env var "${authEnvVar}" is required but not set or empty — export it before registering. ` +
|
|
96
|
-
`crtr never prompts, mints, or refreshes a token.`);
|
|
97
|
-
}
|
|
98
|
-
// ---- Record the registration (skip the write when byte-identical) ----
|
|
99
|
-
if (!unchanged) {
|
|
100
|
-
updateConfig(scope, (cfg) => {
|
|
101
|
-
cfg.configuredClis[name] = {
|
|
102
|
-
endpoint,
|
|
103
|
-
...(authEnvVar !== undefined ? { authEnvVar } : {}),
|
|
104
|
-
};
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
const registration = resolveConfiguredCli(name, scope);
|
|
108
|
-
// ---- One authenticated fetch-and-store (register always fetches) ----
|
|
109
|
-
const result = await registerFetchConfiguredCli(registration);
|
|
110
|
-
if (result.status !== 200) {
|
|
111
|
-
if (result.status === 'auth_env_missing') {
|
|
112
|
-
// Precondition re-check above should have caught this; fail loud.
|
|
113
|
-
throw usage(result.message);
|
|
114
|
-
}
|
|
115
|
-
// Network / non-2xx: registration stands, prior manifest preserved,
|
|
116
|
-
// exit success with a one-line stderr diagnostic + refresh remedy.
|
|
117
|
-
process.stderr.write(`crtr: configured CLI "${registration.name}": ${result.message}\n`);
|
|
118
|
-
process.stderr.write(`next: run \`crtr pkg cli refresh ${registration.name}\` once the endpoint is reachable\n`);
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
name: registration.name,
|
|
122
|
-
endpoint: registration.endpoint,
|
|
123
|
-
...(registration.authEnvVar !== undefined ? { auth_env: registration.authEnvVar } : {}),
|
|
124
|
-
scope: registration.scope,
|
|
125
|
-
manifest_state: configuredCliManifestState(registration),
|
|
126
|
-
...(unchanged ? { unchanged: true } : {}),
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
export const cliRemove = defineLeaf({
|
|
131
|
-
name: 'remove',
|
|
132
|
-
description: 'remove a configured CLI registration and its stored manifest',
|
|
133
|
-
whenToUse: 'deregistering a configured CLI: deletes its config entry and its stored manifest directory. Takes effect on the next invocation with no restart',
|
|
134
|
-
help: {
|
|
135
|
-
name: 'pkg cli remove',
|
|
136
|
-
summary: 'remove a configured CLI registration and its stored manifest',
|
|
137
|
-
params: [
|
|
138
|
-
{ kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Registration name to remove.' },
|
|
139
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Narrows resolution. Default: the effective registration (project outranks user).' },
|
|
140
|
-
],
|
|
141
|
-
output: [
|
|
142
|
-
{ name: 'removed', type: 'boolean', required: true, constraint: 'Always true (a miss throws not_found).' },
|
|
143
|
-
{ name: 'name', type: 'string', required: true, constraint: 'Registration name removed.' },
|
|
144
|
-
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the registration was removed from.' },
|
|
145
|
-
],
|
|
146
|
-
outputKind: 'object',
|
|
147
|
-
effects: ['Deletes configuredClis.<name> from the scope config.json, then removes <scopeRoot>/configured-clis/<name>/. Config is authoritative — removed first.'],
|
|
148
|
-
},
|
|
149
|
-
run: async (input) => {
|
|
150
|
-
const name = input['name'];
|
|
151
|
-
const scopeInput = input['scope'];
|
|
152
|
-
const scope = scopeInput !== undefined ? resolveScopeArg(scopeInput) : undefined;
|
|
153
|
-
const registration = resolveConfiguredCli(name, scope);
|
|
154
|
-
if (registration === null) {
|
|
155
|
-
throw notFound(`configured CLI not found: ${name}`);
|
|
156
|
-
}
|
|
157
|
-
// Config is authoritative: delete the entry first so a crash between steps
|
|
158
|
-
// can only leave an inert orphan store, never an active registration that
|
|
159
|
-
// reported removed.
|
|
160
|
-
updateConfig(registration.scope, (cfg) => {
|
|
161
|
-
delete cfg.configuredClis[name];
|
|
162
|
-
});
|
|
163
|
-
removeConfiguredCliCache(registration);
|
|
164
|
-
return { removed: true, name: registration.name, scope: registration.scope };
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
export const cliRefresh = defineLeaf({
|
|
168
|
-
name: 'refresh',
|
|
169
|
-
description: 'refetch and replace a configured CLI manifest',
|
|
170
|
-
whenToUse: 'pulling a backend manifest change into an already-stored CLI: unconditionally refetches and replaces the stored manifest, then reports the accepted command surface and issues. The only way to update a stored manifest short of a re-register',
|
|
171
|
-
help: {
|
|
172
|
-
name: 'pkg cli refresh',
|
|
173
|
-
summary: 'refetch and replace a configured CLI manifest, report the surface',
|
|
174
|
-
params: [
|
|
175
|
-
{ kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Registration name to refresh.' },
|
|
176
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Narrows resolution. Default: the effective registration (project outranks user).' },
|
|
177
|
-
],
|
|
178
|
-
output: [
|
|
179
|
-
{ name: 'name', type: 'string', required: true, constraint: 'Registration name.' },
|
|
180
|
-
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the registration lives in.' },
|
|
181
|
-
{ name: 'endpoint', type: 'string', required: true, constraint: 'Resolved manifest endpoint URL.' },
|
|
182
|
-
{ name: 'manifest_state', type: 'string', required: true, constraint: '"stored" after a successful refresh.' },
|
|
183
|
-
{ name: 'commands', type: 'object[]', required: true, constraint: 'Every accepted command after the refresh. Each: {command, paths}. Empty when the new manifest is invalid or collided.' },
|
|
184
|
-
{ name: 'issues', type: 'object[]', required: true, constraint: 'Every current validation/collision issue. Each: {code, path?, message, received, expected, next}.' },
|
|
185
|
-
],
|
|
186
|
-
outputKind: 'object',
|
|
187
|
-
effects: [
|
|
188
|
-
'Performs one unconditional authenticated GET of the endpoint and replaces the stored manifest with the returned bytes on success.',
|
|
189
|
-
'On failure (network / non-2xx / named-but-unset auth env) it fails loud and KEEPS the previously stored manifest — the command still composes from it.',
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
run: async (input) => {
|
|
193
|
-
const name = input['name'];
|
|
194
|
-
const scopeInput = input['scope'];
|
|
195
|
-
const scope = scopeInput !== undefined ? resolveScopeArg(scopeInput) : undefined;
|
|
196
|
-
const registration = resolveConfiguredCli(name, scope);
|
|
197
|
-
if (registration === null) {
|
|
198
|
-
throw notFound(`configured CLI not found: ${name}`);
|
|
199
|
-
}
|
|
200
|
-
// Unconditional fetch-and-replace. Throws on failure (auth→usage,
|
|
201
|
-
// unreachable→network, protocol→general), preserving the old manifest.
|
|
202
|
-
await refreshConfiguredCli(registration);
|
|
203
|
-
const snapshot = buildExternalCommandSnapshot(new Set(SUBTREE_NAMES));
|
|
204
|
-
return configuredCliReport(registration, snapshot);
|
|
205
|
-
},
|
|
206
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const cliBranch: import("../../core/command.js").BranchDef;
|
package/dist/commands/pkg/cli.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineBranch } from '../../core/command.js';
|
|
2
|
-
import { cliRegister, cliRemove, cliRefresh } from './cli-manage.js';
|
|
3
|
-
import { cliList, cliShow } from './cli-inspect.js';
|
|
4
|
-
export const cliBranch = defineBranch({
|
|
5
|
-
name: 'cli',
|
|
6
|
-
description: 'register and manage configured CLIs',
|
|
7
|
-
whenToUse: 'working with configured CLIs: a command surface whose tree is defined by a remote manifest and executed as declarative HTTP (definition + HTTP, never remote code). Register one against a manifest endpoint, remove, list, show, or refresh it. Use `pkg plugin` for local plugins that ship an executable, and `pkg market` for marketplace sources.',
|
|
8
|
-
help: {
|
|
9
|
-
name: 'pkg cli',
|
|
10
|
-
summary: 'configured-CLI lifecycle and inspection',
|
|
11
|
-
model: 'A configured CLI is the CLI analogue of an MCP client: crtr fetches a manifest (an extension of the command-plugin schema) from a remote endpoint, stores it locally, renders native -h help from it, and runs each leaf as a declarative REST call. No remote code — the trust surface is definition + HTTP. Registration records {name, endpoint, auth-env NAME} in scope config and stores the fetched manifest; the credential itself is never stored. The stored manifest is authoritative with zero freshness checks — it reloads only on a re-register or an explicit refresh.',
|
|
12
|
-
},
|
|
13
|
-
children: [cliRegister, cliRemove, cliList, cliShow, cliRefresh],
|
|
14
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ConfiguredCliRegistration } from './registration.js';
|
|
2
|
-
/** Minimal metadata stored with a cached manifest. */
|
|
3
|
-
export interface CacheMetadata {
|
|
4
|
-
/** The endpoint this manifest was fetched from; a mismatch invalidates the cache. */
|
|
5
|
-
endpoint: string;
|
|
6
|
-
}
|
|
7
|
-
/** Read the cached manifest bytes if present. Returns null if absent or unreadable. */
|
|
8
|
-
export declare function readConfiguredCliCacheManifest(registration: ConfiguredCliRegistration): Uint8Array | null;
|
|
9
|
-
/** Write a manifest to cache atomically. Replaces any prior manifest and stores minimal metadata. */
|
|
10
|
-
export declare function writeConfiguredCliCacheManifest(registration: ConfiguredCliRegistration, rawManifest: Uint8Array): void;
|
|
11
|
-
/** Check if cache metadata exists and matches the current endpoint.
|
|
12
|
-
* Returns true if both manifest and metadata exist with matching endpoint;
|
|
13
|
-
* false if absent, unreadable, or endpoint mismatch. */
|
|
14
|
-
export declare function isCacheMetadataValid(registration: ConfiguredCliRegistration): boolean;
|
|
15
|
-
/** Remove the cache directory for a registration. */
|
|
16
|
-
export declare function removeConfiguredCliCache(registration: ConfiguredCliRegistration): void;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { atomicWriteJson, atomicWriteText, ensureDir, pathExists, readJsonIfExists, removePath, } from '../fs-utils.js';
|
|
3
|
-
import { configuredCliCacheDir, configuredCliCacheMetaPath, configuredCliManifestPath } from './registration.js';
|
|
4
|
-
/** Read the cached manifest bytes if present. Returns null if absent or unreadable. */
|
|
5
|
-
export function readConfiguredCliCacheManifest(registration) {
|
|
6
|
-
const manifestPath = configuredCliManifestPath(registration);
|
|
7
|
-
if (!pathExists(manifestPath)) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
return readFileSync(manifestPath);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/** Write a manifest to cache atomically. Replaces any prior manifest and stores minimal metadata. */
|
|
18
|
-
export function writeConfiguredCliCacheManifest(registration, rawManifest) {
|
|
19
|
-
const cacheDir = configuredCliCacheDir(registration);
|
|
20
|
-
const manifestPath = configuredCliManifestPath(registration);
|
|
21
|
-
const metaPath = configuredCliCacheMetaPath(registration);
|
|
22
|
-
ensureDir(cacheDir);
|
|
23
|
-
// Write manifest (exact bytes preserved).
|
|
24
|
-
atomicWriteText(manifestPath, rawManifest);
|
|
25
|
-
// Write minimal metadata (endpoint only).
|
|
26
|
-
const meta = {
|
|
27
|
-
endpoint: registration.endpoint,
|
|
28
|
-
};
|
|
29
|
-
atomicWriteJson(metaPath, meta);
|
|
30
|
-
}
|
|
31
|
-
/** Check if cache metadata exists and matches the current endpoint.
|
|
32
|
-
* Returns true if both manifest and metadata exist with matching endpoint;
|
|
33
|
-
* false if absent, unreadable, or endpoint mismatch. */
|
|
34
|
-
export function isCacheMetadataValid(registration) {
|
|
35
|
-
const manifestPath = configuredCliManifestPath(registration);
|
|
36
|
-
const metaPath = configuredCliCacheMetaPath(registration);
|
|
37
|
-
if (!pathExists(manifestPath) || !pathExists(metaPath)) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const meta = readJsonIfExists(metaPath);
|
|
42
|
-
if (!meta) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
return meta.endpoint === registration.endpoint;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** Remove the cache directory for a registration. */
|
|
52
|
-
export function removeConfiguredCliCache(registration) {
|
|
53
|
-
const cacheDir = configuredCliCacheDir(registration);
|
|
54
|
-
if (pathExists(cacheDir)) {
|
|
55
|
-
removePath(cacheDir);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CommandContribution } from '../command-manifests/registry.js';
|
|
2
|
-
import type { ValidatedConfiguredCliManifest } from './manifest.js';
|
|
3
|
-
import type { ConfiguredCliRegistration } from './registration.js';
|
|
4
|
-
/**
|
|
5
|
-
* Adapt one registration + its validated manifest into transport-neutral
|
|
6
|
-
* contributions — one per top-level root the manifest declares. Each leaf's
|
|
7
|
-
* `run` closure captures the registration (name/endpoint/authEnvVar), the
|
|
8
|
-
* manifest-level base/timeout overrides, and the leaf's own `rest` mapping,
|
|
9
|
-
* then calls `invokeConfiguredLeaf`. The bearer token is never captured here —
|
|
10
|
-
* the invoker reads it from the environment at call time. `outputKind` is
|
|
11
|
-
* derived from `rest.streaming` (the single source of truth per §4.3): a
|
|
12
|
-
* streaming leaf renders as `jsonl`, a unary leaf as `object`.
|
|
13
|
-
*/
|
|
14
|
-
export declare function adaptConfiguredContributions(registration: ConfiguredCliRegistration, manifest: ValidatedConfiguredCliManifest): CommandContribution[];
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// Configured-CLI contribution adapter — the transport binding for a validated,
|
|
2
|
-
// stored configured-CLI manifest. It turns each of a manifest's top-level roots
|
|
3
|
-
// into a transport-neutral `CommandContribution` whose `adaptLeaf` closes over
|
|
4
|
-
// the registration + manifest metadata and dispatches every leaf through the
|
|
5
|
-
// generic HTTP invoker (D1). This is the configured-CLI analogue of
|
|
6
|
-
// `adaptPluginContributions` (command-plugins/compose.ts): both feed the same
|
|
7
|
-
// unified `resolveCommandRegistry` collision snapshot and the same
|
|
8
|
-
// `composeExternalSubtrees` tree composer, so a configured CLI is
|
|
9
|
-
// indistinguishable from a native command at the `-h` and dispatch surface —
|
|
10
|
-
// only the execution seam differs.
|
|
11
|
-
//
|
|
12
|
-
// This module performs ZERO network and ZERO freshness work: it consumes an
|
|
13
|
-
// already-validated manifest (from discovery.ts, read from the stored bytes)
|
|
14
|
-
// and produces contributions. The HTTP request happens only later, on explicit
|
|
15
|
-
// leaf invocation, inside the invoker.
|
|
16
|
-
import { invokeConfiguredLeaf } from './invoker.js';
|
|
17
|
-
/**
|
|
18
|
-
* Adapt one registration + its validated manifest into transport-neutral
|
|
19
|
-
* contributions — one per top-level root the manifest declares. Each leaf's
|
|
20
|
-
* `run` closure captures the registration (name/endpoint/authEnvVar), the
|
|
21
|
-
* manifest-level base/timeout overrides, and the leaf's own `rest` mapping,
|
|
22
|
-
* then calls `invokeConfiguredLeaf`. The bearer token is never captured here —
|
|
23
|
-
* the invoker reads it from the environment at call time. `outputKind` is
|
|
24
|
-
* derived from `rest.streaming` (the single source of truth per §4.3): a
|
|
25
|
-
* streaming leaf renders as `jsonl`, a unary leaf as `object`.
|
|
26
|
-
*/
|
|
27
|
-
export function adaptConfiguredContributions(registration, manifest) {
|
|
28
|
-
return manifest.roots.map((root) => ({
|
|
29
|
-
contributor: {
|
|
30
|
-
kind: 'configured-cli',
|
|
31
|
-
name: registration.name,
|
|
32
|
-
scope: registration.scope,
|
|
33
|
-
},
|
|
34
|
-
// A `DeclBranch<ConfiguredCliDeclLeaf>` widens structurally to the
|
|
35
|
-
// registry's `DeclBranch<DeclLeafBase>` (the leaf dialect only adds `rest`).
|
|
36
|
-
node: root,
|
|
37
|
-
adaptLeaf: (leaf, commandPath) => {
|
|
38
|
-
// Every leaf reaching this adapter came from a validated configured
|
|
39
|
-
// manifest, so it carries the `rest` mapping.
|
|
40
|
-
const configuredLeaf = leaf;
|
|
41
|
-
const streaming = configuredLeaf.rest.streaming === true;
|
|
42
|
-
return {
|
|
43
|
-
outputKind: streaming ? 'jsonl' : 'object',
|
|
44
|
-
run: (input, context) => invokeConfiguredLeaf({
|
|
45
|
-
registration: {
|
|
46
|
-
name: registration.name,
|
|
47
|
-
endpoint: registration.endpoint,
|
|
48
|
-
...(registration.authEnvVar !== undefined ? { authEnvVar: registration.authEnvVar } : {}),
|
|
49
|
-
},
|
|
50
|
-
...(manifest.baseUrl !== undefined ? { baseUrl: manifest.baseUrl } : {}),
|
|
51
|
-
...(manifest.timeouts !== undefined ? { timeouts: manifest.timeouts } : {}),
|
|
52
|
-
params: configuredLeaf.params,
|
|
53
|
-
rest: configuredLeaf.rest,
|
|
54
|
-
output: configuredLeaf.output,
|
|
55
|
-
commandPath: Array.from(commandPath),
|
|
56
|
-
}, input, context),
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
}));
|
|
60
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { CommandContribution, CommandRegistrySnapshot, ContributorIssue } from '../command-manifests/registry.js';
|
|
2
|
-
/** Raw configured candidates before cross-contributor collision resolution:
|
|
3
|
-
* the contributions from every registration with a valid stored manifest, the
|
|
4
|
-
* validation issues from every registration whose stored manifest is present
|
|
5
|
-
* but invalid, and the names of registrations whose store reads as absent (the
|
|
6
|
-
* hydration-eligible set, for a caller deciding whether a miss should fetch). */
|
|
7
|
-
export interface ConfiguredCliDiscovery {
|
|
8
|
-
candidates: CommandContribution[];
|
|
9
|
-
issues: ContributorIssue[];
|
|
10
|
-
/** Effective registrations with no readable stored manifest (§5.1 hydration). */
|
|
11
|
-
absentStoreNames: string[];
|
|
12
|
-
}
|
|
13
|
-
/** Discover configured-CLI candidates from stored manifests only — zero network.
|
|
14
|
-
* Each effective registration (project over user) contributes its valid roots,
|
|
15
|
-
* reports its invalid-manifest issues, or is noted as absent-store. */
|
|
16
|
-
export declare function discoverConfiguredCliCandidates(reservedCoreNames: ReadonlySet<string>): ConfiguredCliDiscovery;
|
|
17
|
-
/** Build THE unified external-command snapshot: every effective command plugin
|
|
18
|
-
* and every effective configured CLI, resolved through ONE collision pass, with
|
|
19
|
-
* every issue (plugin-structural, configured-manifest-invalid, and cross-
|
|
20
|
-
* contributor collision) merged into one list. Performs zero network I/O — all
|
|
21
|
-
* manifests read from disk. This exact result is consumed by dispatch/root
|
|
22
|
-
* composition, `pkg cli show`/`pkg plugin show`, and `sys doctor`, so their
|
|
23
|
-
* accepted-command and issue views can never diverge. `reservedCoreNames` are
|
|
24
|
-
* the core top-level subtree names — core always wins. */
|
|
25
|
-
export declare function buildExternalCommandSnapshot(reservedCoreNames: ReadonlySet<string>): CommandRegistrySnapshot;
|
|
26
|
-
/** Outcome of a miss-triggered absent-store hydration pass. */
|
|
27
|
-
export interface AbsentStoreHydration {
|
|
28
|
-
/** True if at least one absent-store registration now has freshly stored
|
|
29
|
-
* bytes — the signal for the caller to rebuild the tree and re-resolve once. */
|
|
30
|
-
hydratedAny: boolean;
|
|
31
|
-
/** One concise stderr line per failed hydration (naming the registration, its
|
|
32
|
-
* fetch error, and the refresh remedy). No NETWORK precedence: a failure here
|
|
33
|
-
* never changes the caller's eventual `unknown_path`/USAGE outcome. */
|
|
34
|
-
diagnostics: string[];
|
|
35
|
-
}
|
|
36
|
-
/** Hydrate EVERY effective absent-store registration exactly once (§5.1). For
|
|
37
|
-
* each registration with no readable stored manifest, perform one authenticated
|
|
38
|
-
* `GET endpoint`, storing the exact bytes on success. A fetch failure (network,
|
|
39
|
-
* non-2xx, or named-but-empty auth env) leaves the store absent and records a
|
|
40
|
-
* one-line diagnostic — it never throws and never earns NETWORK precedence over
|
|
41
|
-
* the caller's `unknown_path`/USAGE classification. Registrations that already
|
|
42
|
-
* hold a stored manifest (valid OR merely schema-invalid) are skipped, so with
|
|
43
|
-
* every manifest stored this performs zero network I/O.
|
|
44
|
-
*
|
|
45
|
-
* Intended caller: dispatch (E2), on an unknown-first-token miss, followed by a
|
|
46
|
-
* single tree rebuild + re-resolve when `hydratedAny` is true. */
|
|
47
|
-
export declare function hydrateAbsentConfiguredClis(): Promise<AbsentStoreHydration>;
|