@adhdev/daemon-core 0.9.82-rc.16 → 0.9.82-rc.161
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/boot/process-hardening.d.ts +50 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +19 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +104 -63
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +27 -0
- package/dist/commands/handler.d.ts +66 -0
- package/dist/commands/mesh-coordinator.d.ts +72 -1
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +7 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +71 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +31 -6
- package/dist/index.js +17210 -3575
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17127 -3551
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +72 -0
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +30 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +90 -0
- package/dist/mesh/mesh-events.d.ts +77 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +58 -1
- package/dist/mesh/mesh-refine-status.d.ts +26 -0
- package/dist/mesh/mesh-work-queue.d.ts +44 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
- package/dist/providers/approval-utils.d.ts +9 -0
- package/dist/providers/cli-provider-instance.d.ts +7 -2
- package/dist/providers/contracts.d.ts +98 -5
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/dispatcher.d.ts +24 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-loader.d.ts +19 -1
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +64 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +169 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +56 -0
- package/dist/providers/spec/cli-adapter.d.ts +76 -0
- package/dist/providers/spec/driver.d.ts +148 -0
- package/dist/providers/spec/evaluator.d.ts +47 -0
- package/dist/providers/spec/loader.d.ts +14 -0
- package/dist/providers/spec/native-history-executor.d.ts +39 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +507 -0
- package/dist/providers/spec/types.d.ts +211 -0
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/repo-mesh-types.d.ts +100 -1
- package/dist/sessions/registry.d.ts +3 -0
- package/dist/shared-types.d.ts +12 -0
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -2
- package/src/boot/daemon-lifecycle.ts +17 -10
- package/src/boot/process-hardening.ts +89 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +14 -1
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +18 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1086 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +664 -1137
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +13 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +57 -11
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
- package/src/commands/chat-commands.ts +1597 -60
- package/src/commands/cli-manager.ts +195 -3
- package/src/commands/handler.ts +547 -2
- package/src/commands/mesh-coordinator.ts +231 -127
- package/src/commands/router.ts +3547 -502
- package/src/config/chat-history.ts +87 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +264 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +91 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +479 -0
- package/src/mesh/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +204 -30
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +437 -0
- package/src/mesh/mesh-events.ts +820 -61
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +457 -104
- package/src/mesh/mesh-refine-status.ts +144 -0
- package/src/mesh/mesh-work-queue.ts +216 -158
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +247 -0
- package/src/providers/approval-utils.ts +39 -5
- package/src/providers/chat-message-normalization.ts +4 -11
- package/src/providers/cli-provider-instance.ts +388 -47
- package/src/providers/contracts.ts +105 -5
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +419 -0
- package/src/providers/native-history/dispatcher.ts +227 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-loader.ts +420 -31
- package/src/providers/provider-schema.ts +31 -13
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +245 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +457 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +119 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +210 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +153 -0
- package/src/providers/spec/cli-adapter.ts +318 -0
- package/src/providers/spec/driver.ts +498 -0
- package/src/providers/spec/evaluator.ts +268 -0
- package/src/providers/spec/loader.ts +130 -0
- package/src/providers/spec/native-history-executor.ts +612 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/schema.gen.ts +507 -0
- package/src/providers/spec/schema.json +210 -0
- package/src/providers/spec/types.ts +230 -0
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +110 -1
- package/src/sessions/registry.ts +3 -0
- package/src/shared-types.ts +9 -0
- package/src/status/builders.ts +23 -6
- package/src/status/reporter.ts +15 -0
- package/src/status/snapshot.ts +35 -11
- package/src/system/host-memory.ts +29 -12
- package/src/types.ts +5 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types across provider categories.
|
|
3
|
+
*
|
|
4
|
+
* Extracted at build time into the Apache 2.0 npm package
|
|
5
|
+
* `@adhdev/provider-types`. External provider authors import from there;
|
|
6
|
+
* daemon-core imports from this file directly.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─── Settings ───────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface BooleanSettingDef {
|
|
12
|
+
type: 'boolean';
|
|
13
|
+
default: boolean;
|
|
14
|
+
public?: boolean;
|
|
15
|
+
label?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface NumberSettingDef {
|
|
20
|
+
type: 'number';
|
|
21
|
+
default: number;
|
|
22
|
+
public?: boolean;
|
|
23
|
+
label?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
min?: number;
|
|
26
|
+
max?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StringSettingDef {
|
|
30
|
+
type: 'string';
|
|
31
|
+
default: string;
|
|
32
|
+
public?: boolean;
|
|
33
|
+
label?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SelectSettingDef {
|
|
38
|
+
type: 'select';
|
|
39
|
+
default: string;
|
|
40
|
+
options: ReadonlyArray<string | { value: string; label?: string }>;
|
|
41
|
+
public?: boolean;
|
|
42
|
+
label?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type SettingDef =
|
|
47
|
+
| BooleanSettingDef
|
|
48
|
+
| NumberSettingDef
|
|
49
|
+
| StringSettingDef
|
|
50
|
+
| SelectSettingDef;
|
|
51
|
+
|
|
52
|
+
export type SettingsDef = Readonly<Record<string, SettingDef>>;
|
|
53
|
+
|
|
54
|
+
// ─── Capabilities ───────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
export interface CapabilityInputDef {
|
|
57
|
+
multipart?: boolean;
|
|
58
|
+
mediaTypes?: ReadonlyArray<'text' | 'image' | 'audio' | string>;
|
|
59
|
+
strategies?: ReadonlyArray<{
|
|
60
|
+
mediaType: string;
|
|
61
|
+
strategies: ReadonlyArray<string>;
|
|
62
|
+
native?: boolean;
|
|
63
|
+
degradation?: ReadonlyArray<string>;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CapabilityOutputDef {
|
|
68
|
+
richContent?: boolean;
|
|
69
|
+
mediaTypes?: ReadonlyArray<string>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CapabilityControlsDef {
|
|
73
|
+
typedResults?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CapabilitiesDef {
|
|
77
|
+
input?: CapabilityInputDef;
|
|
78
|
+
output?: CapabilityOutputDef;
|
|
79
|
+
controls?: CapabilityControlsDef;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Auth ───────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
export interface EnvVarAuthDef {
|
|
85
|
+
type: 'env_var';
|
|
86
|
+
id: string;
|
|
87
|
+
name: string;
|
|
88
|
+
vars: ReadonlyArray<{ name: string; description?: string }>;
|
|
89
|
+
link?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CliCommandAuthDef {
|
|
93
|
+
type: 'cli_command';
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
command: string;
|
|
97
|
+
link?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type AuthDef = EnvVarAuthDef | CliCommandAuthDef;
|
|
101
|
+
|
|
102
|
+
// ─── Spawn ──────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
export interface SpawnDef {
|
|
105
|
+
command: string;
|
|
106
|
+
args?: ReadonlyArray<string>;
|
|
107
|
+
shell?: boolean;
|
|
108
|
+
env?: Readonly<Record<string, string>>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── Timeouts ───────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export interface TimeoutsDef {
|
|
114
|
+
/** Gap between writes before considering a chunk settled (ms). */
|
|
115
|
+
ptyFlush?: number;
|
|
116
|
+
/** Delay before sending an approval key (ms). */
|
|
117
|
+
dialogAccept?: number;
|
|
118
|
+
/** Window during which a freshly resolved modal is suppressed (ms). */
|
|
119
|
+
approvalCooldown?: number;
|
|
120
|
+
/** Fallback "still generating" assumption window (ms). */
|
|
121
|
+
generatingIdle?: number;
|
|
122
|
+
/** Grace before declaring generating→idle final (ms). */
|
|
123
|
+
idleFinish?: number;
|
|
124
|
+
/** Second-pass confirmation window (ms). */
|
|
125
|
+
idleFinishConfirm?: number;
|
|
126
|
+
/** PTY-quiet threshold before marking activity ended (ms). */
|
|
127
|
+
statusActivityHold?: number;
|
|
128
|
+
/** Hard upper bound on a single generation (ms). */
|
|
129
|
+
maxResponse?: number;
|
|
130
|
+
/** SIGTERM→SIGKILL window (ms). */
|
|
131
|
+
shutdownGrace?: number;
|
|
132
|
+
/** Post-write debounce (ms). */
|
|
133
|
+
outputSettle?: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Resume ─────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
export type StopStrategy = 'command' | 'ctrl_c' | 'signal';
|
|
139
|
+
export type SessionIdFormat = 'uuid' | 'free' | 'string';
|
|
140
|
+
|
|
141
|
+
export interface ResumeDef {
|
|
142
|
+
supported: boolean;
|
|
143
|
+
stopStrategy?: StopStrategy;
|
|
144
|
+
stopCommand?: string;
|
|
145
|
+
shutdownGraceMs?: number;
|
|
146
|
+
sessionIdFormat?: SessionIdFormat;
|
|
147
|
+
/** Args appended to spawn.args when starting a session under a daemon-issued id. `{{id}}` is replaced at launch. */
|
|
148
|
+
newSessionArgs?: ReadonlyArray<string>;
|
|
149
|
+
/** Args appended when resuming a specific session id. */
|
|
150
|
+
resumeSessionArgs?: ReadonlyArray<string>;
|
|
151
|
+
/** Args appended when "continue most recent" semantics are wanted. */
|
|
152
|
+
resumeArgs?: ReadonlyArray<string>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── Mesh coordinator ───────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
export interface McpConfigDef {
|
|
158
|
+
mode: 'manual' | 'auto_import';
|
|
159
|
+
format?: string;
|
|
160
|
+
path?: string;
|
|
161
|
+
configPathCommand?: string;
|
|
162
|
+
serverName?: string;
|
|
163
|
+
requiresRestart?: boolean;
|
|
164
|
+
instructions?: string;
|
|
165
|
+
/** Template string used by daemon to render the actual config snippet at install time. */
|
|
166
|
+
template?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface MeshCoordinatorDef {
|
|
170
|
+
supported: boolean;
|
|
171
|
+
mcpConfig?: McpConfigDef;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ─── Compatibility ──────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
export interface CompatibilityEntryDef {
|
|
177
|
+
/** SemVer range against the agent's own version. Optional. */
|
|
178
|
+
ideVersion?: string;
|
|
179
|
+
/** Script subdirectory to load when this entry matches. e.g. "scripts/1.0". */
|
|
180
|
+
scriptDir: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── Controls (dashboard chips/buttons) ─────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export type ControlType = 'select' | 'action' | 'toggle';
|
|
186
|
+
|
|
187
|
+
export interface ControlDef {
|
|
188
|
+
id: string;
|
|
189
|
+
type: ControlType;
|
|
190
|
+
label: string;
|
|
191
|
+
icon?: string;
|
|
192
|
+
placement?: string;
|
|
193
|
+
order?: number;
|
|
194
|
+
dynamic?: boolean;
|
|
195
|
+
hidden?: boolean;
|
|
196
|
+
options?: ReadonlyArray<unknown>;
|
|
197
|
+
listScript?: string;
|
|
198
|
+
setScript?: string;
|
|
199
|
+
invokeScript?: string;
|
|
200
|
+
readFrom?: string;
|
|
201
|
+
resultDisplay?: string;
|
|
202
|
+
defaultValue?: unknown;
|
|
203
|
+
confirmTitle?: string;
|
|
204
|
+
confirmMessage?: string;
|
|
205
|
+
confirmLabel?: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ─── Provider status flag ───────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
export type ProviderLifecycleStatus = 'Stable' | 'Beta' | 'Experimental' | 'Deprecated';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validators — public surface.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
analyzeOverrideTaint,
|
|
7
|
+
formatTaintResult,
|
|
8
|
+
type TaintLevel,
|
|
9
|
+
type TaintCategory,
|
|
10
|
+
type TaintFinding,
|
|
11
|
+
type TaintResult,
|
|
12
|
+
} from './taint.js';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
validateCliProviderManifest,
|
|
16
|
+
formatManifestValidationIssues,
|
|
17
|
+
type ManifestValidationIssue,
|
|
18
|
+
type ManifestValidationResult,
|
|
19
|
+
} from './manifest.js';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest validators — runtime JSON Schema check for v1 provider manifests.
|
|
3
|
+
*
|
|
4
|
+
* The schemas live under `schemas/{category}/provider.schema.json` and are
|
|
5
|
+
* the authoritative contract for what a v1 manifest may contain. Daemon
|
|
6
|
+
* code that loads a manifest at install or boot time runs it through
|
|
7
|
+
* `validateCliProviderManifest()` first; failures are reported back to
|
|
8
|
+
* the caller with structured paths so manifest authors can locate the
|
|
9
|
+
* offending field without guessing.
|
|
10
|
+
*
|
|
11
|
+
* Validation lives in the SDK layer (not in provider-loader) so dashboards,
|
|
12
|
+
* registry workers, and the marketplace publish flow can all reuse the
|
|
13
|
+
* same code path and produce identical error messages.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import Ajv2020, { type ErrorObject, type ValidateFunction } from 'ajv/dist/2020.js';
|
|
17
|
+
import addFormats from 'ajv-formats';
|
|
18
|
+
import cliProviderSchema from '../schemas/cli/provider.schema.json';
|
|
19
|
+
|
|
20
|
+
export interface ManifestValidationIssue {
|
|
21
|
+
/** JSON Pointer-style path into the manifest. Empty string for the root. */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Schema-keyword that failed. */
|
|
24
|
+
keyword: string;
|
|
25
|
+
/** Human-readable explanation. */
|
|
26
|
+
message: string;
|
|
27
|
+
/** Allowed values when the failure is an `enum` / `const` / `additionalProperties`. */
|
|
28
|
+
allowed?: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ManifestValidationResult {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
issues: ManifestValidationIssue[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let _ajv: Ajv2020 | null = null;
|
|
37
|
+
let _cliValidator: ValidateFunction | null = null;
|
|
38
|
+
|
|
39
|
+
function getAjv(): Ajv2020 {
|
|
40
|
+
if (_ajv) return _ajv;
|
|
41
|
+
// strict=false because the schema authors freely use schema-level
|
|
42
|
+
// `description`/`examples` fields that ajv would otherwise warn about.
|
|
43
|
+
// allErrors=true so a single validation run reports every problem at
|
|
44
|
+
// once instead of bailing on the first — that's what makes the
|
|
45
|
+
// structured-error output usable for manifest authors.
|
|
46
|
+
_ajv = new Ajv2020({
|
|
47
|
+
strict: false,
|
|
48
|
+
allErrors: true,
|
|
49
|
+
allowUnionTypes: true,
|
|
50
|
+
});
|
|
51
|
+
addFormats(_ajv);
|
|
52
|
+
return _ajv;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getCliValidator(): ValidateFunction {
|
|
56
|
+
if (_cliValidator) return _cliValidator;
|
|
57
|
+
_cliValidator = getAjv().compile(cliProviderSchema as unknown as object);
|
|
58
|
+
return _cliValidator;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function formatIssue(err: ErrorObject): ManifestValidationIssue {
|
|
62
|
+
const path = err.instancePath || '';
|
|
63
|
+
const params = err.params as Record<string, unknown>;
|
|
64
|
+
let message = err.message || 'validation failed';
|
|
65
|
+
let allowed: unknown;
|
|
66
|
+
|
|
67
|
+
if (err.keyword === 'additionalProperties') {
|
|
68
|
+
const extra = params.additionalProperty;
|
|
69
|
+
message = `unexpected property "${extra}"`;
|
|
70
|
+
} else if (err.keyword === 'required') {
|
|
71
|
+
message = `missing required property "${params.missingProperty}"`;
|
|
72
|
+
} else if (err.keyword === 'enum') {
|
|
73
|
+
allowed = params.allowedValues;
|
|
74
|
+
message = `must be one of ${JSON.stringify(allowed)}`;
|
|
75
|
+
} else if (err.keyword === 'const') {
|
|
76
|
+
allowed = params.allowedValue;
|
|
77
|
+
message = `must equal ${JSON.stringify(allowed)}`;
|
|
78
|
+
} else if (err.keyword === 'type') {
|
|
79
|
+
message = `must be ${params.type}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { path, keyword: err.keyword, message, ...(allowed !== undefined ? { allowed } : {}) };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate a CLI provider.v1.json manifest. Returns `{ ok: true, issues: [] }`
|
|
87
|
+
* on success and `{ ok: false, issues: [...] }` on failure with each issue
|
|
88
|
+
* pointing at the offending field. Never throws — manifest parse errors
|
|
89
|
+
* should be caught by the caller before this is called.
|
|
90
|
+
*/
|
|
91
|
+
export function validateCliProviderManifest(manifest: unknown): ManifestValidationResult {
|
|
92
|
+
const validator = getCliValidator();
|
|
93
|
+
const ok = validator(manifest) as boolean;
|
|
94
|
+
if (ok) return { ok: true, issues: [] };
|
|
95
|
+
const issues = (validator.errors || []).map(formatIssue);
|
|
96
|
+
return { ok: false, issues };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Render a list of validation issues as a single multi-line string,
|
|
101
|
+
* suitable for log lines or daemon command error fields.
|
|
102
|
+
*/
|
|
103
|
+
export function formatManifestValidationIssues(issues: ManifestValidationIssue[]): string {
|
|
104
|
+
return issues
|
|
105
|
+
.map((issue) => {
|
|
106
|
+
const where = issue.path || '<root>';
|
|
107
|
+
return ` - ${where}: ${issue.message} (${issue.keyword})`;
|
|
108
|
+
})
|
|
109
|
+
.join('\n');
|
|
110
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static taint analyzer for extended-tier override JS.
|
|
3
|
+
*
|
|
4
|
+
* Goal: classify the override JS shipped with an extended-tier provider into
|
|
5
|
+
* one of three risk tiers, so the marketplace + daemon trust prompt can
|
|
6
|
+
* surface accurate language to the operator instead of a single generic
|
|
7
|
+
* "this provider contains JS" warning.
|
|
8
|
+
*
|
|
9
|
+
* - clean : no flagged APIs at all. The override looks like pure string
|
|
10
|
+
* manipulation. Still extended-tier, but the warning copy is
|
|
11
|
+
* "this provider contains JS; nothing dangerous was detected".
|
|
12
|
+
* - elevated : touches process / dynamic codegen / dynamic require / eval
|
|
13
|
+
* / Function constructor / child_process. The warning copy is
|
|
14
|
+
* explicit ("this provider can run shell commands / dynamically
|
|
15
|
+
* load code").
|
|
16
|
+
* - hostile : combines elevated APIs with network egress, AND uses obvious
|
|
17
|
+
* obfuscation (base64-decoded strings fed into Function / eval).
|
|
18
|
+
* The publish endpoint should reject these.
|
|
19
|
+
*
|
|
20
|
+
* Implementation note: this is a *static* analyzer — string + light AST scan
|
|
21
|
+
* via Node's built-in parser. It is intentionally fail-loud: anything that
|
|
22
|
+
* looks suspicious is flagged. False positives are fine; this exists to gate
|
|
23
|
+
* the trust prompt, not to be a real sandbox. The real sandbox lives in
|
|
24
|
+
* Phase 5 (isolated-vm).
|
|
25
|
+
*
|
|
26
|
+
* Used by:
|
|
27
|
+
* - `adhdev provider validate` (CLI tier classification)
|
|
28
|
+
* - registry publish endpoint static-check
|
|
29
|
+
* - daemon at provider-load time, to size the warning
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
33
|
+
import { resolve, dirname, join } from 'node:path';
|
|
34
|
+
|
|
35
|
+
// ─── Findings ──────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export type TaintLevel = 'clean' | 'elevated' | 'hostile';
|
|
38
|
+
|
|
39
|
+
export interface TaintFinding {
|
|
40
|
+
/** What was found, e.g. "child_process.exec". */
|
|
41
|
+
api: string;
|
|
42
|
+
/** Coarse category. */
|
|
43
|
+
category: TaintCategory;
|
|
44
|
+
/** File the finding came from. */
|
|
45
|
+
file: string;
|
|
46
|
+
/** Line number (1-indexed) of the first character of the matched token. */
|
|
47
|
+
line: number;
|
|
48
|
+
/** The surrounding source excerpt, trimmed. */
|
|
49
|
+
excerpt: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type TaintCategory =
|
|
53
|
+
| 'process-control' // process.exit, process.kill
|
|
54
|
+
| 'shell-exec' // child_process.{exec,execSync,spawn,spawnSync,fork}
|
|
55
|
+
| 'fs-write' // fs.writeFile / writeFileSync outside override dir
|
|
56
|
+
| 'fs-read' // fs.readFile / readFileSync
|
|
57
|
+
| 'network' // http(s).request, fetch, net.connect, dgram
|
|
58
|
+
| 'dynamic-codegen' // eval, new Function(), Function('...')
|
|
59
|
+
| 'dynamic-require' // require(variable), import(variable)
|
|
60
|
+
| 'obfuscation' // Buffer.from(...,'base64').toString + (eval|Function)
|
|
61
|
+
| 'global-mutation'; // mutating Object.prototype, process.env
|
|
62
|
+
|
|
63
|
+
export interface TaintResult {
|
|
64
|
+
level: TaintLevel;
|
|
65
|
+
findings: TaintFinding[];
|
|
66
|
+
files: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Patterns ──────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
interface PatternRule {
|
|
72
|
+
category: TaintCategory;
|
|
73
|
+
/** Display label. */
|
|
74
|
+
label: string;
|
|
75
|
+
/** Regex applied per-line. */
|
|
76
|
+
pattern: RegExp;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const PATTERNS: PatternRule[] = [
|
|
80
|
+
{
|
|
81
|
+
category: 'shell-exec',
|
|
82
|
+
label: 'child_process.exec',
|
|
83
|
+
pattern: /\bchild_process\b[\s\S]{0,40}?\b(exec|execSync|spawn|spawnSync|fork|execFile)\b/,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
category: 'shell-exec',
|
|
87
|
+
label: 'require("child_process")',
|
|
88
|
+
pattern: /\brequire\s*\(\s*['"]child_process['"]\s*\)/,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
category: 'shell-exec',
|
|
92
|
+
label: 'import child_process',
|
|
93
|
+
pattern: /\bfrom\s+['"](?:node:)?child_process['"]/,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
category: 'process-control',
|
|
97
|
+
label: 'process.exit / kill',
|
|
98
|
+
pattern: /\bprocess\.(exit|kill|abort)\s*\(/,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
category: 'global-mutation',
|
|
102
|
+
label: 'process.env mutation',
|
|
103
|
+
pattern: /\bprocess\.env\.[A-Z_][A-Z0-9_]*\s*=/,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
category: 'global-mutation',
|
|
107
|
+
label: 'prototype pollution',
|
|
108
|
+
pattern: /\bObject\.prototype\b.*=/,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
category: 'dynamic-codegen',
|
|
112
|
+
label: 'eval()',
|
|
113
|
+
pattern: /(?<![A-Za-z0-9_$])eval\s*\(/,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
category: 'dynamic-codegen',
|
|
117
|
+
label: 'new Function()',
|
|
118
|
+
pattern: /\bnew\s+Function\s*\(/,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
category: 'dynamic-require',
|
|
122
|
+
label: 'require(variable)',
|
|
123
|
+
pattern: /\brequire\s*\(\s*(?!['"`])/,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
category: 'dynamic-require',
|
|
127
|
+
label: 'import(variable)',
|
|
128
|
+
pattern: /(?<![.\w])import\s*\(\s*(?!['"`])/,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
category: 'network',
|
|
132
|
+
label: 'fetch()',
|
|
133
|
+
pattern: /(?<![A-Za-z0-9_$])fetch\s*\(/,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
category: 'network',
|
|
137
|
+
label: 'http(s).request',
|
|
138
|
+
pattern: /\b(https?)\.(request|get)\s*\(/,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
category: 'network',
|
|
142
|
+
label: 'net.connect / Socket',
|
|
143
|
+
pattern: /\b(net\.connect|tls\.connect|new\s+net\.Socket)\b/,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
category: 'network',
|
|
147
|
+
label: 'dgram',
|
|
148
|
+
pattern: /\brequire\s*\(\s*['"](?:node:)?dgram['"]/,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
category: 'fs-write',
|
|
152
|
+
label: 'fs.writeFile / writeFileSync',
|
|
153
|
+
pattern: /\bfs\.(writeFile|writeFileSync|appendFile|appendFileSync|unlink|unlinkSync|rm|rmSync)\b/,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
category: 'fs-read',
|
|
157
|
+
label: 'fs.readFile / readFileSync',
|
|
158
|
+
pattern: /\bfs\.(readFile|readFileSync|readdir|readdirSync|stat|statSync)\b/,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
// Obfuscation heuristic: base64 / hex decode feeding into eval or Function.
|
|
163
|
+
const OBFUSCATION_PATTERN =
|
|
164
|
+
/Buffer\.from\s*\([^)]*['"](?:base64|hex)['"]\s*\)\s*\.toString\s*\([^)]*\)[\s\S]{0,80}?(eval|Function)\s*\(/;
|
|
165
|
+
|
|
166
|
+
// ─── File walker — picks up everything reachable from overrides ────────
|
|
167
|
+
|
|
168
|
+
function listOverrideJsFiles(manifestPath: string): string[] {
|
|
169
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as { overrides?: Record<string, unknown> };
|
|
170
|
+
if (!manifest.overrides) return [];
|
|
171
|
+
const base = dirname(manifestPath);
|
|
172
|
+
const out = new Set<string>();
|
|
173
|
+
for (const value of Object.values(manifest.overrides)) {
|
|
174
|
+
let rel: string | null = null;
|
|
175
|
+
if (typeof value === 'string') rel = value;
|
|
176
|
+
else if (value && typeof value === 'object' && 'path' in value && typeof (value as { path: unknown }).path === 'string') {
|
|
177
|
+
rel = (value as { path: string }).path;
|
|
178
|
+
}
|
|
179
|
+
if (!rel) continue;
|
|
180
|
+
const abs = resolve(base, rel);
|
|
181
|
+
if (existsSync(abs)) out.add(abs);
|
|
182
|
+
}
|
|
183
|
+
// Best-effort: also scan sibling scripts/ tree, since overrides commonly
|
|
184
|
+
// require() helpers from there.
|
|
185
|
+
for (const candidate of ['scripts']) {
|
|
186
|
+
const dir = join(base, candidate);
|
|
187
|
+
if (existsSync(dir)) {
|
|
188
|
+
try {
|
|
189
|
+
walk(dir, out);
|
|
190
|
+
} catch {
|
|
191
|
+
// Ignore individual unreadable files.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return [...out];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function walk(dir: string, into: Set<string>): void {
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
200
|
+
const { readdirSync, statSync } = require('node:fs') as typeof import('node:fs');
|
|
201
|
+
for (const entry of readdirSync(dir)) {
|
|
202
|
+
const abs = join(dir, entry);
|
|
203
|
+
let st;
|
|
204
|
+
try { st = statSync(abs); } catch { continue; }
|
|
205
|
+
if (st.isDirectory()) walk(abs, into);
|
|
206
|
+
else if (st.isFile() && /\.(?:m|c)?js$/.test(entry)) into.add(abs);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── Scanner ───────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
function scanFile(file: string): TaintFinding[] {
|
|
213
|
+
const source = readFileSync(file, 'utf-8');
|
|
214
|
+
const findings: TaintFinding[] = [];
|
|
215
|
+
// Per-line scan keeps line numbers honest.
|
|
216
|
+
const lines = source.split('\n');
|
|
217
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
218
|
+
const line = lines[i];
|
|
219
|
+
for (const rule of PATTERNS) {
|
|
220
|
+
if (rule.pattern.test(line)) {
|
|
221
|
+
findings.push({
|
|
222
|
+
api: rule.label,
|
|
223
|
+
category: rule.category,
|
|
224
|
+
file,
|
|
225
|
+
line: i + 1,
|
|
226
|
+
excerpt: line.trim().slice(0, 200),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Whole-file scan for obfuscation pattern (may span lines).
|
|
232
|
+
if (OBFUSCATION_PATTERN.test(source)) {
|
|
233
|
+
findings.push({
|
|
234
|
+
api: 'base64/hex → eval|Function',
|
|
235
|
+
category: 'obfuscation',
|
|
236
|
+
file,
|
|
237
|
+
line: 1,
|
|
238
|
+
excerpt: '<obfuscation pattern across multiple lines>',
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return findings;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ─── Classification ────────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
const ELEVATED_CATEGORIES = new Set<TaintCategory>([
|
|
247
|
+
'shell-exec',
|
|
248
|
+
'process-control',
|
|
249
|
+
'dynamic-codegen',
|
|
250
|
+
'dynamic-require',
|
|
251
|
+
'fs-write',
|
|
252
|
+
'global-mutation',
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
const HOSTILE_REQUIRES = new Set<TaintCategory>([
|
|
256
|
+
'obfuscation',
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
function classify(findings: TaintFinding[]): TaintLevel {
|
|
260
|
+
if (findings.length === 0) return 'clean';
|
|
261
|
+
const cats = new Set(findings.map((f) => f.category));
|
|
262
|
+
if ([...cats].some((c) => HOSTILE_REQUIRES.has(c))) return 'hostile';
|
|
263
|
+
// Hostile also: elevated + network combined.
|
|
264
|
+
const hasElevated = [...cats].some((c) => ELEVATED_CATEGORIES.has(c));
|
|
265
|
+
const hasNetwork = cats.has('network');
|
|
266
|
+
if (hasElevated && hasNetwork) return 'hostile';
|
|
267
|
+
if (hasElevated || hasNetwork) return 'elevated';
|
|
268
|
+
return 'clean'; // only fs-read on its own is fine.
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ─── Public API ────────────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
export function analyzeOverrideTaint(manifestPath: string): TaintResult {
|
|
274
|
+
const files = listOverrideJsFiles(manifestPath);
|
|
275
|
+
const findings: TaintFinding[] = [];
|
|
276
|
+
for (const f of files) findings.push(...scanFile(f));
|
|
277
|
+
return {
|
|
278
|
+
level: classify(findings),
|
|
279
|
+
findings,
|
|
280
|
+
files,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Format a result as a single CLI-friendly block. */
|
|
285
|
+
export function formatTaintResult(result: TaintResult): string {
|
|
286
|
+
const lines: string[] = [];
|
|
287
|
+
lines.push(`Tier: extended (override JS present)`);
|
|
288
|
+
lines.push(`Risk level: ${result.level}`);
|
|
289
|
+
lines.push(`Files scanned: ${result.files.length}`);
|
|
290
|
+
if (result.findings.length === 0) {
|
|
291
|
+
lines.push('No flagged APIs detected.');
|
|
292
|
+
return lines.join('\n');
|
|
293
|
+
}
|
|
294
|
+
// Group by category for a tighter report.
|
|
295
|
+
const byCat = new Map<TaintCategory, TaintFinding[]>();
|
|
296
|
+
for (const f of result.findings) {
|
|
297
|
+
const arr = byCat.get(f.category) ?? [];
|
|
298
|
+
arr.push(f);
|
|
299
|
+
byCat.set(f.category, arr);
|
|
300
|
+
}
|
|
301
|
+
for (const [cat, items] of byCat) {
|
|
302
|
+
lines.push(` ${cat}: ${items.length} occurrence(s)`);
|
|
303
|
+
for (const f of items.slice(0, 5)) {
|
|
304
|
+
lines.push(` ${f.file}:${f.line} ${f.api}`);
|
|
305
|
+
}
|
|
306
|
+
if (items.length > 5) lines.push(` …and ${items.length - 5} more`);
|
|
307
|
+
}
|
|
308
|
+
return lines.join('\n');
|
|
309
|
+
}
|