@code-yeongyu/senpi 2026.6.28-4 → 2026.6.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +2 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +17 -4
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/command-runner.d.ts +25 -0
- package/dist/core/extensions/builtin/hooks/command-runner.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/command-runner.js +152 -0
- package/dist/core/extensions/builtin/hooks/command-runner.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/command.d.ts +11 -0
- package/dist/core/extensions/builtin/hooks/command.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/command.js +180 -0
- package/dist/core/extensions/builtin/hooks/command.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/config-loader.d.ts +19 -0
- package/dist/core/extensions/builtin/hooks/config-loader.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/config-loader.js +125 -0
- package/dist/core/extensions/builtin/hooks/config-loader.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/diagnostics.d.ts +10 -0
- package/dist/core/extensions/builtin/hooks/diagnostics.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/diagnostics.js +14 -0
- package/dist/core/extensions/builtin/hooks/diagnostics.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/dispatcher.d.ts +60 -0
- package/dist/core/extensions/builtin/hooks/dispatcher.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/dispatcher.js +161 -0
- package/dist/core/extensions/builtin/hooks/dispatcher.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/handler.d.ts +12 -0
- package/dist/core/extensions/builtin/hooks/handler.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/handler.js +128 -0
- package/dist/core/extensions/builtin/hooks/handler.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/index.d.ts +6 -0
- package/dist/core/extensions/builtin/hooks/index.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/index.js +216 -0
- package/dist/core/extensions/builtin/hooks/index.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.d.ts +29 -0
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.js +276 -0
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/matcher.d.ts +7 -0
- package/dist/core/extensions/builtin/hooks/matcher.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/matcher.js +109 -0
- package/dist/core/extensions/builtin/hooks/matcher.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/output-bounds.d.ts +29 -0
- package/dist/core/extensions/builtin/hooks/output-bounds.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/output-bounds.js +47 -0
- package/dist/core/extensions/builtin/hooks/output-bounds.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/output-parser.d.ts +27 -0
- package/dist/core/extensions/builtin/hooks/output-parser.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/output-parser.js +168 -0
- package/dist/core/extensions/builtin/hooks/output-parser.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/plugin-loader.d.ts +11 -0
- package/dist/core/extensions/builtin/hooks/plugin-loader.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/plugin-loader.js +138 -0
- package/dist/core/extensions/builtin/hooks/plugin-loader.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/plugin-manifest.d.ts +47 -0
- package/dist/core/extensions/builtin/hooks/plugin-manifest.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js +86 -0
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/prompt-adapter.d.ts +30 -0
- package/dist/core/extensions/builtin/hooks/prompt-adapter.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/prompt-adapter.js +59 -0
- package/dist/core/extensions/builtin/hooks/prompt-adapter.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/safety.d.ts +14 -0
- package/dist/core/extensions/builtin/hooks/safety.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/safety.js +179 -0
- package/dist/core/extensions/builtin/hooks/safety.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/schema.d.ts +3 -0
- package/dist/core/extensions/builtin/hooks/schema.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/schema.js +94 -0
- package/dist/core/extensions/builtin/hooks/schema.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/stop-adapter.d.ts +18 -0
- package/dist/core/extensions/builtin/hooks/stop-adapter.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/stop-adapter.js +282 -0
- package/dist/core/extensions/builtin/hooks/stop-adapter.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/tool-adapter.d.ts +11 -0
- package/dist/core/extensions/builtin/hooks/tool-adapter.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/tool-adapter.js +158 -0
- package/dist/core/extensions/builtin/hooks/tool-adapter.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts +24 -0
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust-storage.js +102 -0
- package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust.d.ts +34 -0
- package/dist/core/extensions/builtin/hooks/trust.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust.js +194 -0
- package/dist/core/extensions/builtin/hooks/trust.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/types.d.ts +134 -0
- package/dist/core/extensions/builtin/hooks/types.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/types.js +36 -0
- package/dist/core/extensions/builtin/hooks/types.js.map +1 -0
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.js +2 -0
- package/dist/core/extensions/builtin/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +20 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +17 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +14 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts +14 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +38 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/version-check.d.ts +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +7 -0
- package/dist/utils/version-check.js.map +1 -1
- package/docs/extensions.md +450 -0
- package/docs/index.md +1 -1
- package/docs/packages.md +9 -6
- package/examples/extensions/README.md +1 -0
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +17 -24
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +0 -24
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +16 -35
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js +12 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js +12 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js +12 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js +15 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +3 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/npm-shrinkwrap.json +9 -9
- package/package.json +4 -4
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function diagnostic(draft, source) {
|
|
2
|
+
const base = {
|
|
3
|
+
code: draft.code,
|
|
4
|
+
message: draft.message,
|
|
5
|
+
path: draft.path,
|
|
6
|
+
severity: draft.severity ?? "error",
|
|
7
|
+
source,
|
|
8
|
+
};
|
|
9
|
+
if (draft.event === undefined) {
|
|
10
|
+
return base;
|
|
11
|
+
}
|
|
12
|
+
return { ...base, event: draft.event };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/diagnostics.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,UAAU,CAAC,KAAsB,EAAE,MAA0B;IAC5E,MAAM,IAAI,GAAG;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAO;QACnC,MAAM;KACN,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import type { HookDiagnostic, HookDiagnosticCode, HookSourceMetadata } from \"./types.ts\";\n\nexport type DiagnosticDraft = {\n\treadonly code: HookDiagnosticCode;\n\treadonly message: string;\n\treadonly path: string;\n\treadonly event?: string;\n\treadonly severity?: \"error\" | \"warning\";\n};\n\nexport function diagnostic(draft: DiagnosticDraft, source: HookSourceMetadata): HookDiagnostic {\n\tconst base = {\n\t\tcode: draft.code,\n\t\tmessage: draft.message,\n\t\tpath: draft.path,\n\t\tseverity: draft.severity ?? \"error\",\n\t\tsource,\n\t};\n\tif (draft.event === undefined) {\n\t\treturn base;\n\t}\n\treturn { ...base, event: draft.event };\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type CommandHookRunOptions, type CommandHookRunResult } from "./command-runner.ts";
|
|
2
|
+
import { type ParsedHookOutput } from "./output-parser.ts";
|
|
3
|
+
import { type HookOutputPolicy } from "./safety.ts";
|
|
4
|
+
import { type HookTrustOptions, type HookTrustRecord } from "./trust.ts";
|
|
5
|
+
import type { ExecutableHookHandler, HookDiagnostic, HookInputWire, HookSourceMetadata, HookTrustState } from "./types.ts";
|
|
6
|
+
export type HookCommandRunner = (handler: ExecutableHookHandler, input: HookInputWire, options: CommandHookRunOptions) => Promise<CommandHookRunResult>;
|
|
7
|
+
export type HookDispatchDecision = {
|
|
8
|
+
readonly kind: "none";
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: "allow" | "block";
|
|
11
|
+
readonly reason?: string;
|
|
12
|
+
readonly source: HookSourceMetadata;
|
|
13
|
+
readonly sourceCommand: string;
|
|
14
|
+
readonly updatedInput?: unknown;
|
|
15
|
+
} | {
|
|
16
|
+
readonly fallback: {
|
|
17
|
+
readonly kind: "block";
|
|
18
|
+
readonly reason: string;
|
|
19
|
+
};
|
|
20
|
+
readonly kind: "ask";
|
|
21
|
+
readonly nativeRepresentable: false;
|
|
22
|
+
readonly reason?: string;
|
|
23
|
+
readonly source: HookSourceMetadata;
|
|
24
|
+
readonly sourceCommand: string;
|
|
25
|
+
};
|
|
26
|
+
export type HookDispatchSkipped = {
|
|
27
|
+
readonly diagnostics: readonly HookDiagnostic[];
|
|
28
|
+
readonly handler: ExecutableHookHandler;
|
|
29
|
+
readonly reason: "disabled" | "untrusted" | "unsafe";
|
|
30
|
+
readonly record: HookTrustRecord;
|
|
31
|
+
};
|
|
32
|
+
export type HookDispatchSummary = {
|
|
33
|
+
readonly completionIndex: number;
|
|
34
|
+
readonly diagnostics: readonly HookDiagnostic[];
|
|
35
|
+
readonly handler: ExecutableHookHandler;
|
|
36
|
+
readonly output: ParsedHookOutput["output"];
|
|
37
|
+
readonly run: CommandHookRunResult;
|
|
38
|
+
};
|
|
39
|
+
export type HookDispatchResult = {
|
|
40
|
+
readonly decision: HookDispatchDecision;
|
|
41
|
+
readonly diagnostics: readonly HookDiagnostic[];
|
|
42
|
+
readonly executableHandlers: readonly ExecutableHookHandler[];
|
|
43
|
+
readonly matchedHandlers: readonly ExecutableHookHandler[];
|
|
44
|
+
readonly skipped: readonly HookDispatchSkipped[];
|
|
45
|
+
readonly summaries: readonly HookDispatchSummary[];
|
|
46
|
+
};
|
|
47
|
+
export type HookDispatchOptions = {
|
|
48
|
+
readonly cwd: string;
|
|
49
|
+
readonly envPassthrough?: readonly string[];
|
|
50
|
+
readonly handlers: readonly ExecutableHookHandler[];
|
|
51
|
+
readonly input: HookInputWire;
|
|
52
|
+
readonly outputPolicy?: HookOutputPolicy;
|
|
53
|
+
readonly runCommand?: HookCommandRunner;
|
|
54
|
+
readonly signal?: AbortSignal;
|
|
55
|
+
readonly sourceEnv?: NodeJS.ProcessEnv;
|
|
56
|
+
readonly trustOptions?: HookTrustOptions;
|
|
57
|
+
readonly trustState: HookTrustState;
|
|
58
|
+
};
|
|
59
|
+
export declare function dispatchHookEvent(options: HookDispatchOptions): Promise<HookDispatchResult>;
|
|
60
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { runCommandHook } from "./command-runner.js";
|
|
2
|
+
import { matchingHookHandlers } from "./matcher.js";
|
|
3
|
+
import { parseHookOutput } from "./output-parser.js";
|
|
4
|
+
import { validateHookHandlerSafety } from "./safety.js";
|
|
5
|
+
import { listHookTrustRecords } from "./trust.js";
|
|
6
|
+
export async function dispatchHookEvent(options) {
|
|
7
|
+
const match = matchingHookHandlers(options.input, options.handlers);
|
|
8
|
+
const ordered = declarationOrdered(match.handlers);
|
|
9
|
+
const selected = selectExecutableHandlers(ordered, options.trustState, options.trustOptions);
|
|
10
|
+
let nextCompletionIndex = 0;
|
|
11
|
+
const runner = options.runCommand ?? runCommandHook;
|
|
12
|
+
const completed = await Promise.all(selected.runnable.map(async ({ handler, declarationIndex }) => {
|
|
13
|
+
const run = await runner(handler, options.input, {
|
|
14
|
+
cwd: options.cwd,
|
|
15
|
+
...(options.envPassthrough === undefined ? {} : { envPassthrough: options.envPassthrough }),
|
|
16
|
+
...(options.outputPolicy === undefined ? {} : { outputPolicy: options.outputPolicy }),
|
|
17
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
18
|
+
...(options.sourceEnv === undefined ? {} : { sourceEnv: options.sourceEnv }),
|
|
19
|
+
});
|
|
20
|
+
const parsed = parseHookOutput({
|
|
21
|
+
event: options.input.event,
|
|
22
|
+
exitCode: run.exitCode ?? 1,
|
|
23
|
+
source: handler.source,
|
|
24
|
+
stderr: run.stderr,
|
|
25
|
+
stdout: run.stdout,
|
|
26
|
+
});
|
|
27
|
+
const completionIndex = nextCompletionIndex;
|
|
28
|
+
nextCompletionIndex += 1;
|
|
29
|
+
return { completionIndex, declarationIndex, handler, parsed, run };
|
|
30
|
+
}));
|
|
31
|
+
const declarationSummaries = completed
|
|
32
|
+
.toSorted((left, right) => left.declarationIndex - right.declarationIndex)
|
|
33
|
+
.map((run) => ({
|
|
34
|
+
completionIndex: run.completionIndex,
|
|
35
|
+
diagnostics: run.parsed.diagnostics,
|
|
36
|
+
handler: run.handler,
|
|
37
|
+
output: run.parsed.output,
|
|
38
|
+
run: run.run,
|
|
39
|
+
}));
|
|
40
|
+
const diagnostics = [
|
|
41
|
+
...match.diagnostics,
|
|
42
|
+
...selected.skipped.flatMap((skip) => skip.diagnostics),
|
|
43
|
+
...declarationSummaries.flatMap((summary) => summary.diagnostics),
|
|
44
|
+
];
|
|
45
|
+
return {
|
|
46
|
+
decision: aggregateDecision(options.input.event, declarationSummaries),
|
|
47
|
+
diagnostics,
|
|
48
|
+
executableHandlers: selected.runnable.map((item) => item.handler),
|
|
49
|
+
matchedHandlers: ordered.map((item) => item.handler),
|
|
50
|
+
skipped: selected.skipped,
|
|
51
|
+
summaries: declarationSummaries,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function declarationOrdered(handlers) {
|
|
55
|
+
return handlers
|
|
56
|
+
.map((handler, index) => ({ declarationIndex: index, handler }))
|
|
57
|
+
.toSorted((left, right) => compareHandlers(left.handler, right.handler) || left.declarationIndex - right.declarationIndex);
|
|
58
|
+
}
|
|
59
|
+
function compareHandlers(left, right) {
|
|
60
|
+
return (left.source.displayOrder - right.source.displayOrder ||
|
|
61
|
+
left.groupIndex - right.groupIndex ||
|
|
62
|
+
left.handlerIndex - right.handlerIndex);
|
|
63
|
+
}
|
|
64
|
+
function selectExecutableHandlers(handlers, trustState, trustOptions) {
|
|
65
|
+
const records = listHookTrustRecords(handlers.map((item) => item.handler), trustState, trustOptions);
|
|
66
|
+
const runnable = [];
|
|
67
|
+
const skipped = [];
|
|
68
|
+
for (const [index, item] of handlers.entries()) {
|
|
69
|
+
const record = records[index];
|
|
70
|
+
if (record === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
const safetyDiagnostics = validateHookHandlerSafety(item.handler);
|
|
73
|
+
const unsafe = safetyDiagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
74
|
+
if (unsafe) {
|
|
75
|
+
skipped.push({ diagnostics: safetyDiagnostics, handler: item.handler, reason: "unsafe", record });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!record.enabled) {
|
|
79
|
+
skipped.push({ diagnostics: [], handler: item.handler, reason: "disabled", record });
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!record.trusted) {
|
|
83
|
+
skipped.push({ diagnostics: [], handler: item.handler, reason: "untrusted", record });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
runnable.push({ ...item, record });
|
|
87
|
+
}
|
|
88
|
+
return { runnable, skipped };
|
|
89
|
+
}
|
|
90
|
+
function aggregateDecision(event, summaries) {
|
|
91
|
+
switch (event) {
|
|
92
|
+
case "PreToolUse":
|
|
93
|
+
return aggregatePreToolUseDecision(summaries);
|
|
94
|
+
case "PostToolUse":
|
|
95
|
+
case "UserPromptSubmit":
|
|
96
|
+
case "SessionStart":
|
|
97
|
+
case "PreCompact":
|
|
98
|
+
case "PostCompact":
|
|
99
|
+
case "Stop":
|
|
100
|
+
return aggregateBlockingDecision(summaries);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function aggregatePreToolUseDecision(summaries) {
|
|
104
|
+
const blocker = summaries.find(hasBlockingDecision);
|
|
105
|
+
if (blocker !== undefined) {
|
|
106
|
+
return decisionWithSource("block", blocker, blocker.output.reason);
|
|
107
|
+
}
|
|
108
|
+
const ask = summaries.find((summary) => summary.output.decision === "ask");
|
|
109
|
+
if (ask !== undefined) {
|
|
110
|
+
const reason = ask.output.reason ?? "Hook requested manual approval.";
|
|
111
|
+
return {
|
|
112
|
+
fallback: { kind: "block", reason },
|
|
113
|
+
kind: "ask",
|
|
114
|
+
nativeRepresentable: false,
|
|
115
|
+
...reasonField(ask.output.reason),
|
|
116
|
+
source: ask.handler.source,
|
|
117
|
+
sourceCommand: ask.handler.config.command,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
let allowSummary;
|
|
121
|
+
for (const summary of summaries) {
|
|
122
|
+
if (summary.output.decision === "allow" || summary.output.decision === "approve")
|
|
123
|
+
allowSummary = summary;
|
|
124
|
+
}
|
|
125
|
+
if (allowSummary !== undefined) {
|
|
126
|
+
return decisionWithSource("allow", allowSummary, undefined, allowSummary.output.updatedInput);
|
|
127
|
+
}
|
|
128
|
+
return { kind: "none" };
|
|
129
|
+
}
|
|
130
|
+
function aggregateBlockingDecision(summaries) {
|
|
131
|
+
const blocker = summaries.find(hasBlockingDecision);
|
|
132
|
+
if (blocker !== undefined) {
|
|
133
|
+
return decisionWithSource("block", blocker, blocker.output.reason ?? blocker.output.stopReason);
|
|
134
|
+
}
|
|
135
|
+
return { kind: "none" };
|
|
136
|
+
}
|
|
137
|
+
function hasBlockingDecision(summary) {
|
|
138
|
+
return summary.output.decision === "block" || summary.output.decision === "deny";
|
|
139
|
+
}
|
|
140
|
+
function decisionWithSource(kind, summary, reason, updatedInput) {
|
|
141
|
+
return {
|
|
142
|
+
kind,
|
|
143
|
+
...reasonField(reason),
|
|
144
|
+
source: summary.handler.source,
|
|
145
|
+
sourceCommand: summary.handler.config.command,
|
|
146
|
+
...updatedInputField(updatedInput),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function reasonField(reason) {
|
|
150
|
+
if (reason === undefined) {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
return { reason };
|
|
154
|
+
}
|
|
155
|
+
function updatedInputField(updatedInput) {
|
|
156
|
+
if (updatedInput === undefined) {
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
return { updatedInput };
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/dispatcher.ts"],"names":[],"mappings":"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","sourcesContent":["import { type CommandHookRunOptions, type CommandHookRunResult, runCommandHook } from \"./command-runner.ts\";\nimport { matchingHookHandlers } from \"./matcher.ts\";\nimport { type ParsedHookOutput, parseHookOutput } from \"./output-parser.ts\";\nimport { type HookOutputPolicy, validateHookHandlerSafety } from \"./safety.ts\";\nimport { type HookTrustOptions, type HookTrustRecord, listHookTrustRecords } from \"./trust.ts\";\nimport type {\n\tExecutableHookHandler,\n\tHookDiagnostic,\n\tHookInputWire,\n\tHookSourceMetadata,\n\tHookTrustState,\n} from \"./types.ts\";\n\nexport type HookCommandRunner = (\n\thandler: ExecutableHookHandler,\n\tinput: HookInputWire,\n\toptions: CommandHookRunOptions,\n) => Promise<CommandHookRunResult>;\n\nexport type HookDispatchDecision =\n\t| { readonly kind: \"none\" }\n\t| {\n\t\t\treadonly kind: \"allow\" | \"block\";\n\t\t\treadonly reason?: string;\n\t\t\treadonly source: HookSourceMetadata;\n\t\t\treadonly sourceCommand: string;\n\t\t\treadonly updatedInput?: unknown;\n\t }\n\t| {\n\t\t\treadonly fallback: { readonly kind: \"block\"; readonly reason: string };\n\t\t\treadonly kind: \"ask\";\n\t\t\treadonly nativeRepresentable: false;\n\t\t\treadonly reason?: string;\n\t\t\treadonly source: HookSourceMetadata;\n\t\t\treadonly sourceCommand: string;\n\t };\n\nexport type HookDispatchSkipped = {\n\treadonly diagnostics: readonly HookDiagnostic[];\n\treadonly handler: ExecutableHookHandler;\n\treadonly reason: \"disabled\" | \"untrusted\" | \"unsafe\";\n\treadonly record: HookTrustRecord;\n};\n\nexport type HookDispatchSummary = {\n\treadonly completionIndex: number;\n\treadonly diagnostics: readonly HookDiagnostic[];\n\treadonly handler: ExecutableHookHandler;\n\treadonly output: ParsedHookOutput[\"output\"];\n\treadonly run: CommandHookRunResult;\n};\n\nexport type HookDispatchResult = {\n\treadonly decision: HookDispatchDecision;\n\treadonly diagnostics: readonly HookDiagnostic[];\n\treadonly executableHandlers: readonly ExecutableHookHandler[];\n\treadonly matchedHandlers: readonly ExecutableHookHandler[];\n\treadonly skipped: readonly HookDispatchSkipped[];\n\treadonly summaries: readonly HookDispatchSummary[];\n};\n\nexport type HookDispatchOptions = {\n\treadonly cwd: string;\n\treadonly envPassthrough?: readonly string[];\n\treadonly handlers: readonly ExecutableHookHandler[];\n\treadonly input: HookInputWire;\n\treadonly outputPolicy?: HookOutputPolicy;\n\treadonly runCommand?: HookCommandRunner;\n\treadonly signal?: AbortSignal;\n\treadonly sourceEnv?: NodeJS.ProcessEnv;\n\treadonly trustOptions?: HookTrustOptions;\n\treadonly trustState: HookTrustState;\n};\n\ntype OrderedHandler = {\n\treadonly declarationIndex: number;\n\treadonly handler: ExecutableHookHandler;\n};\n\ntype RunnableHandler = OrderedHandler & {\n\treadonly record: HookTrustRecord;\n};\n\nexport async function dispatchHookEvent(options: HookDispatchOptions): Promise<HookDispatchResult> {\n\tconst match = matchingHookHandlers(options.input, options.handlers);\n\tconst ordered = declarationOrdered(match.handlers);\n\tconst selected = selectExecutableHandlers(ordered, options.trustState, options.trustOptions);\n\tlet nextCompletionIndex = 0;\n\tconst runner = options.runCommand ?? runCommandHook;\n\tconst completed = await Promise.all(\n\t\tselected.runnable.map(async ({ handler, declarationIndex }) => {\n\t\t\tconst run = await runner(handler, options.input, {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\t...(options.envPassthrough === undefined ? {} : { envPassthrough: options.envPassthrough }),\n\t\t\t\t...(options.outputPolicy === undefined ? {} : { outputPolicy: options.outputPolicy }),\n\t\t\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t\t\t\t...(options.sourceEnv === undefined ? {} : { sourceEnv: options.sourceEnv }),\n\t\t\t});\n\t\t\tconst parsed = parseHookOutput({\n\t\t\t\tevent: options.input.event,\n\t\t\t\texitCode: run.exitCode ?? 1,\n\t\t\t\tsource: handler.source,\n\t\t\t\tstderr: run.stderr,\n\t\t\t\tstdout: run.stdout,\n\t\t\t});\n\t\t\tconst completionIndex = nextCompletionIndex;\n\t\t\tnextCompletionIndex += 1;\n\t\t\treturn { completionIndex, declarationIndex, handler, parsed, run };\n\t\t}),\n\t);\n\tconst declarationSummaries = completed\n\t\t.toSorted((left, right) => left.declarationIndex - right.declarationIndex)\n\t\t.map((run) => ({\n\t\t\tcompletionIndex: run.completionIndex,\n\t\t\tdiagnostics: run.parsed.diagnostics,\n\t\t\thandler: run.handler,\n\t\t\toutput: run.parsed.output,\n\t\t\trun: run.run,\n\t\t}));\n\tconst diagnostics = [\n\t\t...match.diagnostics,\n\t\t...selected.skipped.flatMap((skip) => skip.diagnostics),\n\t\t...declarationSummaries.flatMap((summary) => summary.diagnostics),\n\t];\n\treturn {\n\t\tdecision: aggregateDecision(options.input.event, declarationSummaries),\n\t\tdiagnostics,\n\t\texecutableHandlers: selected.runnable.map((item) => item.handler),\n\t\tmatchedHandlers: ordered.map((item) => item.handler),\n\t\tskipped: selected.skipped,\n\t\tsummaries: declarationSummaries,\n\t};\n}\n\nfunction declarationOrdered(handlers: readonly ExecutableHookHandler[]): readonly OrderedHandler[] {\n\treturn handlers\n\t\t.map((handler, index) => ({ declarationIndex: index, handler }))\n\t\t.toSorted(\n\t\t\t(left, right) =>\n\t\t\t\tcompareHandlers(left.handler, right.handler) || left.declarationIndex - right.declarationIndex,\n\t\t);\n}\n\nfunction compareHandlers(left: ExecutableHookHandler, right: ExecutableHookHandler): number {\n\treturn (\n\t\tleft.source.displayOrder - right.source.displayOrder ||\n\t\tleft.groupIndex - right.groupIndex ||\n\t\tleft.handlerIndex - right.handlerIndex\n\t);\n}\n\nfunction selectExecutableHandlers(\n\thandlers: readonly OrderedHandler[],\n\ttrustState: HookTrustState,\n\ttrustOptions: HookTrustOptions | undefined,\n): { readonly runnable: readonly RunnableHandler[]; readonly skipped: readonly HookDispatchSkipped[] } {\n\tconst records = listHookTrustRecords(\n\t\thandlers.map((item) => item.handler),\n\t\ttrustState,\n\t\ttrustOptions,\n\t);\n\tconst runnable: RunnableHandler[] = [];\n\tconst skipped: HookDispatchSkipped[] = [];\n\tfor (const [index, item] of handlers.entries()) {\n\t\tconst record = records[index];\n\t\tif (record === undefined) continue;\n\t\tconst safetyDiagnostics = validateHookHandlerSafety(item.handler);\n\t\tconst unsafe = safetyDiagnostics.some((diagnostic) => diagnostic.severity === \"error\");\n\t\tif (unsafe) {\n\t\t\tskipped.push({ diagnostics: safetyDiagnostics, handler: item.handler, reason: \"unsafe\", record });\n\t\t\tcontinue;\n\t\t}\n\t\tif (!record.enabled) {\n\t\t\tskipped.push({ diagnostics: [], handler: item.handler, reason: \"disabled\", record });\n\t\t\tcontinue;\n\t\t}\n\t\tif (!record.trusted) {\n\t\t\tskipped.push({ diagnostics: [], handler: item.handler, reason: \"untrusted\", record });\n\t\t\tcontinue;\n\t\t}\n\t\trunnable.push({ ...item, record });\n\t}\n\treturn { runnable, skipped };\n}\n\nfunction aggregateDecision(\n\tevent: HookInputWire[\"event\"],\n\tsummaries: readonly HookDispatchSummary[],\n): HookDispatchDecision {\n\tswitch (event) {\n\t\tcase \"PreToolUse\":\n\t\t\treturn aggregatePreToolUseDecision(summaries);\n\t\tcase \"PostToolUse\":\n\t\tcase \"UserPromptSubmit\":\n\t\tcase \"SessionStart\":\n\t\tcase \"PreCompact\":\n\t\tcase \"PostCompact\":\n\t\tcase \"Stop\":\n\t\t\treturn aggregateBlockingDecision(summaries);\n\t}\n}\n\nfunction aggregatePreToolUseDecision(summaries: readonly HookDispatchSummary[]): HookDispatchDecision {\n\tconst blocker = summaries.find(hasBlockingDecision);\n\tif (blocker !== undefined) {\n\t\treturn decisionWithSource(\"block\", blocker, blocker.output.reason);\n\t}\n\tconst ask = summaries.find((summary) => summary.output.decision === \"ask\");\n\tif (ask !== undefined) {\n\t\tconst reason = ask.output.reason ?? \"Hook requested manual approval.\";\n\t\treturn {\n\t\t\tfallback: { kind: \"block\", reason },\n\t\t\tkind: \"ask\",\n\t\t\tnativeRepresentable: false,\n\t\t\t...reasonField(ask.output.reason),\n\t\t\tsource: ask.handler.source,\n\t\t\tsourceCommand: ask.handler.config.command,\n\t\t};\n\t}\n\tlet allowSummary: HookDispatchSummary | undefined;\n\tfor (const summary of summaries) {\n\t\tif (summary.output.decision === \"allow\" || summary.output.decision === \"approve\") allowSummary = summary;\n\t}\n\tif (allowSummary !== undefined) {\n\t\treturn decisionWithSource(\"allow\", allowSummary, undefined, allowSummary.output.updatedInput);\n\t}\n\treturn { kind: \"none\" };\n}\n\nfunction aggregateBlockingDecision(summaries: readonly HookDispatchSummary[]): HookDispatchDecision {\n\tconst blocker = summaries.find(hasBlockingDecision);\n\tif (blocker !== undefined) {\n\t\treturn decisionWithSource(\"block\", blocker, blocker.output.reason ?? blocker.output.stopReason);\n\t}\n\treturn { kind: \"none\" };\n}\n\nfunction hasBlockingDecision(summary: HookDispatchSummary): boolean {\n\treturn summary.output.decision === \"block\" || summary.output.decision === \"deny\";\n}\n\nfunction decisionWithSource(\n\tkind: \"allow\" | \"block\",\n\tsummary: HookDispatchSummary,\n\treason: string | undefined,\n\tupdatedInput?: unknown,\n): HookDispatchDecision {\n\treturn {\n\t\tkind,\n\t\t...reasonField(reason),\n\t\tsource: summary.handler.source,\n\t\tsourceCommand: summary.handler.config.command,\n\t\t...updatedInputField(updatedInput),\n\t};\n}\n\nfunction reasonField(reason: string | undefined): { readonly reason?: string } {\n\tif (reason === undefined) {\n\t\treturn {};\n\t}\n\treturn { reason };\n}\n\nfunction updatedInputField(updatedInput: unknown): { readonly updatedInput?: unknown } {\n\tif (updatedInput === undefined) {\n\t\treturn {};\n\t}\n\treturn { updatedInput };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExecutableHookHandler, HookDiagnostic, HookSourceMetadata, SupportedHookEvent } from "./types.ts";
|
|
2
|
+
type HandlerParseContext = {
|
|
3
|
+
readonly event: SupportedHookEvent;
|
|
4
|
+
readonly matcher?: string;
|
|
5
|
+
readonly groupIndex: number;
|
|
6
|
+
readonly handlerIndex: number;
|
|
7
|
+
readonly source: HookSourceMetadata;
|
|
8
|
+
readonly diagnostics: HookDiagnostic[];
|
|
9
|
+
};
|
|
10
|
+
export declare function parseHandler(handler: unknown, context: HandlerParseContext): ExecutableHookHandler | undefined;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,MAAM,YAAY,CAAC;AAIpB,KAAK,mBAAmB,GAAG;IAC1B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,qBAAqB,GAAG,SAAS,CAiE9G"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { diagnostic } from "./diagnostics.js";
|
|
2
|
+
import { isValidHookTimeoutSeconds } from "./safety.js";
|
|
3
|
+
const UNSUPPORTED_COMMAND_FIELDS = ["if", "shell", "asyncRewake", "terminalSequence", "continueOnBlock"];
|
|
4
|
+
export function parseHandler(handler, context) {
|
|
5
|
+
const path = `hooks.${context.event}[${context.groupIndex}].hooks[${context.handlerIndex}]`;
|
|
6
|
+
if (!isRecord(handler)) {
|
|
7
|
+
context.diagnostics.push(diagnostic({ code: "invalid_handler", event: context.event, message: "Hook handler must be an object.", path }, context.source));
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const type = handler.type;
|
|
11
|
+
if (type !== "command") {
|
|
12
|
+
context.diagnostics.push(diagnostic({
|
|
13
|
+
code: "unsupported_handler_type",
|
|
14
|
+
event: context.event,
|
|
15
|
+
message: 'Only type: "command" hook handlers are executable in builtin hooks v1.',
|
|
16
|
+
path: `${path}.type`,
|
|
17
|
+
severity: "warning",
|
|
18
|
+
}, context.source));
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (hasUnsupportedCommandShape(handler, path, context)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const command = handler.command;
|
|
25
|
+
if (typeof command !== "string") {
|
|
26
|
+
context.diagnostics.push(diagnostic({
|
|
27
|
+
code: "invalid_command",
|
|
28
|
+
event: context.event,
|
|
29
|
+
message: "Command hook command must be a string.",
|
|
30
|
+
path: `${path}.command`,
|
|
31
|
+
}, context.source));
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const normalized = normalizeCommandConfig(handler, command, path, context);
|
|
35
|
+
if (!normalized) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const base = {
|
|
39
|
+
config: normalized,
|
|
40
|
+
event: context.event,
|
|
41
|
+
groupIndex: context.groupIndex,
|
|
42
|
+
handlerIndex: context.handlerIndex,
|
|
43
|
+
source: context.source,
|
|
44
|
+
};
|
|
45
|
+
if (context.matcher === undefined) {
|
|
46
|
+
return base;
|
|
47
|
+
}
|
|
48
|
+
return { ...base, matcher: context.matcher };
|
|
49
|
+
}
|
|
50
|
+
function hasUnsupportedCommandShape(handler, path, context) {
|
|
51
|
+
let unsupported = false;
|
|
52
|
+
for (const field of UNSUPPORTED_COMMAND_FIELDS) {
|
|
53
|
+
if (field in handler) {
|
|
54
|
+
unsupported = true;
|
|
55
|
+
context.diagnostics.push(diagnostic({
|
|
56
|
+
code: "unsupported_field",
|
|
57
|
+
event: context.event,
|
|
58
|
+
message: `Command hook field ${field} is not executable in builtin hooks v1.`,
|
|
59
|
+
path: `${path}.${field}`,
|
|
60
|
+
severity: "warning",
|
|
61
|
+
}, context.source));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (handler.async === true) {
|
|
65
|
+
unsupported = true;
|
|
66
|
+
context.diagnostics.push(diagnostic({
|
|
67
|
+
code: "unsupported_async_handler",
|
|
68
|
+
event: context.event,
|
|
69
|
+
message: "Async hook handlers are not executable in builtin hooks v1.",
|
|
70
|
+
path: `${path}.async`,
|
|
71
|
+
severity: "warning",
|
|
72
|
+
}, context.source));
|
|
73
|
+
}
|
|
74
|
+
if ("args" in handler || isRecord(handler.command)) {
|
|
75
|
+
unsupported = true;
|
|
76
|
+
context.diagnostics.push(diagnostic({
|
|
77
|
+
code: "unsupported_command_variant",
|
|
78
|
+
event: context.event,
|
|
79
|
+
message: "Exec-form command hooks are not executable in builtin hooks v1.",
|
|
80
|
+
path: `${path}.command`,
|
|
81
|
+
severity: "warning",
|
|
82
|
+
}, context.source));
|
|
83
|
+
}
|
|
84
|
+
return unsupported;
|
|
85
|
+
}
|
|
86
|
+
function normalizeCommandConfig(handler, command, path, context) {
|
|
87
|
+
const commandWindowsValue = handler.commandWindows ?? handler.command_windows;
|
|
88
|
+
if (commandWindowsValue !== undefined && typeof commandWindowsValue !== "string") {
|
|
89
|
+
context.diagnostics.push(diagnostic({
|
|
90
|
+
code: "invalid_command_windows",
|
|
91
|
+
event: context.event,
|
|
92
|
+
message: "commandWindows must be a string.",
|
|
93
|
+
path: `${path}.commandWindows`,
|
|
94
|
+
}, context.source));
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const timeout = handler.timeout;
|
|
98
|
+
if (timeout !== undefined && (typeof timeout !== "number" || !isValidHookTimeoutSeconds(timeout))) {
|
|
99
|
+
context.diagnostics.push(diagnostic({
|
|
100
|
+
code: "invalid_timeout",
|
|
101
|
+
event: context.event,
|
|
102
|
+
message: "Command hook timeout must be a finite number greater than 0.",
|
|
103
|
+
path: `${path}.timeout`,
|
|
104
|
+
}, context.source));
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const statusMessage = handler.statusMessage;
|
|
108
|
+
if (statusMessage !== undefined && typeof statusMessage !== "string") {
|
|
109
|
+
context.diagnostics.push(diagnostic({
|
|
110
|
+
code: "invalid_status_message",
|
|
111
|
+
event: context.event,
|
|
112
|
+
message: "Command hook statusMessage must be a string.",
|
|
113
|
+
path: `${path}.statusMessage`,
|
|
114
|
+
}, context.source));
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
type: "command",
|
|
119
|
+
command,
|
|
120
|
+
...(typeof commandWindowsValue === "string" ? { commandWindows: commandWindowsValue } : {}),
|
|
121
|
+
...(typeof timeout === "number" ? { timeout } : {}),
|
|
122
|
+
...(typeof statusMessage === "string" ? { statusMessage } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function isRecord(value) {
|
|
126
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { diagnostic } from \"./diagnostics.ts\";\nimport { isValidHookTimeoutSeconds } from \"./safety.ts\";\nimport type {\n\tCommandHookConfig,\n\tExecutableHookHandler,\n\tHookDiagnostic,\n\tHookSourceMetadata,\n\tSupportedHookEvent,\n} from \"./types.ts\";\n\nconst UNSUPPORTED_COMMAND_FIELDS = [\"if\", \"shell\", \"asyncRewake\", \"terminalSequence\", \"continueOnBlock\"] as const;\n\ntype HandlerParseContext = {\n\treadonly event: SupportedHookEvent;\n\treadonly matcher?: string;\n\treadonly groupIndex: number;\n\treadonly handlerIndex: number;\n\treadonly source: HookSourceMetadata;\n\treadonly diagnostics: HookDiagnostic[];\n};\n\nexport function parseHandler(handler: unknown, context: HandlerParseContext): ExecutableHookHandler | undefined {\n\tconst path = `hooks.${context.event}[${context.groupIndex}].hooks[${context.handlerIndex}]`;\n\tif (!isRecord(handler)) {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{ code: \"invalid_handler\", event: context.event, message: \"Hook handler must be an object.\", path },\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst type = handler.type;\n\tif (type !== \"command\") {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"unsupported_handler_type\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: 'Only type: \"command\" hook handlers are executable in builtin hooks v1.',\n\t\t\t\t\tpath: `${path}.type`,\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tif (hasUnsupportedCommandShape(handler, path, context)) {\n\t\treturn undefined;\n\t}\n\n\tconst command = handler.command;\n\tif (typeof command !== \"string\") {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"invalid_command\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"Command hook command must be a string.\",\n\t\t\t\t\tpath: `${path}.command`,\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst normalized = normalizeCommandConfig(handler, command, path, context);\n\tif (!normalized) {\n\t\treturn undefined;\n\t}\n\n\tconst base = {\n\t\tconfig: normalized,\n\t\tevent: context.event,\n\t\tgroupIndex: context.groupIndex,\n\t\thandlerIndex: context.handlerIndex,\n\t\tsource: context.source,\n\t};\n\tif (context.matcher === undefined) {\n\t\treturn base;\n\t}\n\treturn { ...base, matcher: context.matcher };\n}\n\nfunction hasUnsupportedCommandShape(\n\thandler: Record<string, unknown>,\n\tpath: string,\n\tcontext: HandlerParseContext,\n): boolean {\n\tlet unsupported = false;\n\tfor (const field of UNSUPPORTED_COMMAND_FIELDS) {\n\t\tif (field in handler) {\n\t\t\tunsupported = true;\n\t\t\tcontext.diagnostics.push(\n\t\t\t\tdiagnostic(\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"unsupported_field\",\n\t\t\t\t\t\tevent: context.event,\n\t\t\t\t\t\tmessage: `Command hook field ${field} is not executable in builtin hooks v1.`,\n\t\t\t\t\t\tpath: `${path}.${field}`,\n\t\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\t},\n\t\t\t\t\tcontext.source,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (handler.async === true) {\n\t\tunsupported = true;\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"unsupported_async_handler\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"Async hook handlers are not executable in builtin hooks v1.\",\n\t\t\t\t\tpath: `${path}.async`,\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\"args\" in handler || isRecord(handler.command)) {\n\t\tunsupported = true;\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"unsupported_command_variant\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"Exec-form command hooks are not executable in builtin hooks v1.\",\n\t\t\t\t\tpath: `${path}.command`,\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t}\n\treturn unsupported;\n}\n\nfunction normalizeCommandConfig(\n\thandler: Record<string, unknown>,\n\tcommand: string,\n\tpath: string,\n\tcontext: HandlerParseContext,\n): CommandHookConfig | undefined {\n\tconst commandWindowsValue = handler.commandWindows ?? handler.command_windows;\n\tif (commandWindowsValue !== undefined && typeof commandWindowsValue !== \"string\") {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"invalid_command_windows\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"commandWindows must be a string.\",\n\t\t\t\t\tpath: `${path}.commandWindows`,\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst timeout = handler.timeout;\n\tif (timeout !== undefined && (typeof timeout !== \"number\" || !isValidHookTimeoutSeconds(timeout))) {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"invalid_timeout\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"Command hook timeout must be a finite number greater than 0.\",\n\t\t\t\t\tpath: `${path}.timeout`,\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst statusMessage = handler.statusMessage;\n\tif (statusMessage !== undefined && typeof statusMessage !== \"string\") {\n\t\tcontext.diagnostics.push(\n\t\t\tdiagnostic(\n\t\t\t\t{\n\t\t\t\t\tcode: \"invalid_status_message\",\n\t\t\t\t\tevent: context.event,\n\t\t\t\t\tmessage: \"Command hook statusMessage must be a string.\",\n\t\t\t\t\tpath: `${path}.statusMessage`,\n\t\t\t\t},\n\t\t\t\tcontext.source,\n\t\t\t),\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\ttype: \"command\",\n\t\tcommand,\n\t\t...(typeof commandWindowsValue === \"string\" ? { commandWindows: commandWindowsValue } : {}),\n\t\t...(typeof timeout === \"number\" ? { timeout } : {}),\n\t\t...(typeof statusMessage === \"string\" ? { statusMessage } : {}),\n\t};\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "../../types.ts";
|
|
2
|
+
export { parseHookConfig } from "./schema.ts";
|
|
3
|
+
export type { CommandHookConfig, ExecutableHookHandler, HookDiagnostic, HookDiagnosticCode, HookInputWire, HookOutputWire, HookRuntimeState, HookSourceMetadata, HookTrustEntry, HookTrustState, ParsedHookConfig, SupportedHookEvent, UnsupportedKnownHookEvent, } from "./types.ts";
|
|
4
|
+
export default function hooksExtension(pi: ExtensionAPI): void;
|
|
5
|
+
export { SUPPORTED_HOOK_EVENTS, UNSUPPORTED_HANDLER_TYPES, UNSUPPORTED_KNOWN_HOOK_EVENTS, } from "./types.ts";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,YAAY,EAAuC,MAAM,gBAAgB,CAAC;AAqCrH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EACX,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GACzB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAyM7D;AAmBD,OAAO,EACN,qBAAqB,EACrB,yBAAyB,EACzB,6BAA6B,GAC7B,MAAM,YAAY,CAAC"}
|