@cmodjs/core 0.3.0 → 1.5.0
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/docs/commands.md +61 -10
- package/docs/dependencies.md +16 -14
- package/docs/hooks.md +64 -29
- package/docs/index.md +29 -25
- package/docs/install-steps.md +13 -9
- package/docs/jobs.md +59 -111
- package/docs/mod.md +227 -175
- package/docs/options.md +2 -2
- package/docs/permissions.md +27 -16
- package/docs/session.md +311 -0
- package/docs/state.md +11 -7
- package/docs/testing.md +68 -23
- package/docs/ui.md +116 -76
- package/jobs/check.d.ts.map +1 -1
- package/jobs/check.js +5 -2
- package/jobs/context.d.ts +7 -8
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +23 -73
- package/jobs/permissions/decide-permission.d.ts +5 -3
- package/jobs/permissions/decide-permission.d.ts.map +1 -1
- package/jobs/permissions/decide-permission.js +6 -4
- package/jobs/permissions/find-project-scope.d.ts +1 -1
- package/jobs/permissions/find-project-scope.d.ts.map +1 -1
- package/jobs/permissions/find-project-scope.js +8 -11
- package/jobs/permissions/match-target.d.ts +2 -2
- package/jobs/permissions/match-target.d.ts.map +1 -1
- package/jobs/permissions.d.ts +1 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +6 -4
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +14 -4
- package/jobs/prompt.d.ts +4 -2
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +16 -15
- package/jobs/schedule.d.ts +2 -2
- package/jobs/schedule.d.ts.map +1 -1
- package/jobs/schedule.js +9 -9
- package/jobs/slash-command.d.ts +4 -4
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +14 -9
- package/jobs/status-line.d.ts +1 -11
- package/jobs/status-line.d.ts.map +1 -1
- package/jobs/status-line.js +8 -12
- package/jobs/tool.d.ts +2 -2
- package/jobs/tool.d.ts.map +1 -1
- package/jobs/tool.js +15 -9
- package/mod.d.ts +178 -75
- package/mod.d.ts.map +1 -1
- package/mod.js +127 -3
- package/options.d.ts +2 -2
- package/options.js +19 -19
- package/package.json +3 -3
- package/records.d.ts +13 -8
- package/records.d.ts.map +1 -1
- package/records.js +33 -17
- package/register.d.ts.map +1 -1
- package/register.js +34 -5
- package/runtime/announce.d.ts +18 -0
- package/runtime/announce.d.ts.map +1 -0
- package/runtime/announce.js +68 -0
- package/runtime/before.d.ts +32 -0
- package/runtime/before.d.ts.map +1 -0
- package/runtime/before.js +214 -0
- package/runtime/bound.d.ts +4 -0
- package/runtime/bound.d.ts.map +1 -0
- package/runtime/bound.js +9 -0
- package/runtime/claude.d.ts +4 -2
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +17 -0
- package/runtime/conversations.d.ts.map +1 -0
- package/runtime/conversations.js +563 -0
- package/runtime/deadline.d.ts +0 -1
- package/runtime/deadline.d.ts.map +1 -1
- package/runtime/deadline.js +1 -1
- package/runtime/dependencies.d.ts +2 -0
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +10 -1
- package/runtime/engine.d.ts +13 -0
- package/runtime/engine.d.ts.map +1 -0
- package/runtime/engine.js +10 -0
- package/runtime/grants.d.ts +4 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +68 -31
- package/runtime/hooks.d.ts +5 -4
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +29 -9
- package/runtime/installer.d.ts +1 -1
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +17 -14
- package/runtime/lifecycle.d.ts +3 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +414 -212
- package/runtime/metadata.d.ts +3 -1
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +25 -14
- package/runtime/options.d.ts +3 -1
- package/runtime/options.d.ts.map +1 -1
- package/runtime/options.js +4 -4
- package/runtime/programs.d.ts +8 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +39 -1
- package/runtime/router.d.ts +2 -1
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +11 -3
- package/runtime/sessions.d.ts +58 -0
- package/runtime/sessions.d.ts.map +1 -0
- package/runtime/sessions.js +433 -0
- package/runtime/state.d.ts +6 -2
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +46 -13
- package/runtime/toaster.d.ts +24 -0
- package/runtime/toaster.d.ts.map +1 -0
- package/runtime/toaster.js +286 -0
- package/runtime/ui.d.ts +6 -17
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +62 -118
- package/session/context/context.d.ts +12 -0
- package/session/context/context.d.ts.map +1 -0
- package/session/context/context.js +2 -0
- package/session/context/turn/message/document.d.ts +7 -0
- package/session/context/turn/message/document.d.ts.map +1 -0
- package/session/context/turn/message/document.js +1 -0
- package/session/context/turn/message/message.d.ts +55 -0
- package/session/context/turn/message/message.d.ts.map +1 -0
- package/session/context/turn/message/message.js +178 -0
- package/session/context/turn/message/note.d.ts +8 -0
- package/session/context/turn/message/note.d.ts.map +1 -0
- package/session/context/turn/message/note.js +1 -0
- package/session/context/turn/message/notice.d.ts +6 -0
- package/session/context/turn/message/notice.d.ts.map +1 -0
- package/session/context/turn/message/notice.js +1 -0
- package/session/context/turn/message/prompt.d.ts +8 -0
- package/session/context/turn/message/prompt.d.ts.map +1 -0
- package/session/context/turn/message/prompt.js +1 -0
- package/session/context/turn/message/reply.d.ts +6 -0
- package/session/context/turn/message/reply.d.ts.map +1 -0
- package/session/context/turn/message/reply.js +1 -0
- package/session/context/turn/message/sender.d.ts +14 -0
- package/session/context/turn/message/sender.d.ts.map +1 -0
- package/session/context/turn/message/sender.js +30 -0
- package/session/context/turn/message/skill.d.ts +7 -0
- package/session/context/turn/message/skill.d.ts.map +1 -0
- package/session/context/turn/message/skill.js +1 -0
- package/session/context/turn/message/tool-call.d.ts +9 -0
- package/session/context/turn/message/tool-call.d.ts.map +1 -0
- package/session/context/turn/message/tool-call.js +1 -0
- package/session/context/turn/message/tool-result.d.ts +8 -0
- package/session/context/turn/message/tool-result.d.ts.map +1 -0
- package/session/context/turn/message/tool-result.js +1 -0
- package/session/context/turn/turn.d.ts +13 -0
- package/session/context/turn/turn.d.ts.map +1 -0
- package/session/context/turn/turn.js +2 -0
- package/session/conversation.d.ts +73 -0
- package/session/conversation.d.ts.map +1 -0
- package/session/conversation.js +146 -0
- package/session/goal/goal.d.ts +11 -0
- package/session/goal/goal.d.ts.map +1 -0
- package/session/goal/goal.js +1 -0
- package/session/goal/task.d.ts +16 -0
- package/session/goal/task.d.ts.map +1 -0
- package/session/goal/task.js +3 -0
- package/session/session.d.ts +158 -0
- package/session/session.d.ts.map +1 -0
- package/session/session.js +2 -0
- package/src/jobs/check.ts +7 -4
- package/src/jobs/context.ts +24 -82
- package/src/jobs/permissions/decide-permission.ts +10 -8
- package/src/jobs/permissions/find-project-scope.ts +8 -14
- package/src/jobs/permissions/match-target.ts +3 -3
- package/src/jobs/permissions.ts +8 -6
- package/src/jobs/program.ts +13 -4
- package/src/jobs/prompt.ts +21 -24
- package/src/jobs/schedule.ts +14 -14
- package/src/jobs/slash-command.ts +20 -14
- package/src/jobs/status-line.ts +9 -21
- package/src/jobs/tool.ts +17 -11
- package/src/mod.ts +252 -97
- package/src/options.ts +22 -22
- package/src/records.ts +40 -23
- package/src/register.ts +34 -5
- package/src/runtime/announce.ts +76 -0
- package/src/runtime/before.ts +200 -0
- package/src/runtime/bound.ts +12 -0
- package/src/runtime/claude.ts +7 -2
- package/src/runtime/conversations.ts +579 -0
- package/src/runtime/deadline.ts +2 -2
- package/src/runtime/dependencies.ts +13 -1
- package/src/runtime/engine.ts +23 -0
- package/src/runtime/grants.ts +69 -29
- package/src/runtime/hooks.ts +39 -14
- package/src/runtime/installer.ts +16 -14
- package/src/runtime/lifecycle.ts +413 -201
- package/src/runtime/metadata.ts +37 -14
- package/src/runtime/options.ts +19 -4
- package/src/runtime/programs.ts +40 -1
- package/src/runtime/router.ts +11 -4
- package/src/runtime/sessions.ts +447 -0
- package/src/runtime/state.ts +50 -14
- package/src/runtime/toaster.ts +334 -0
- package/src/runtime/ui.ts +65 -143
- package/src/session/context/context.ts +14 -0
- package/src/session/context/turn/message/document.ts +8 -0
- package/src/session/context/turn/message/message.ts +203 -0
- package/src/session/context/turn/message/note.ts +9 -0
- package/src/session/context/turn/message/notice.ts +7 -0
- package/src/session/context/turn/message/prompt.ts +9 -0
- package/src/session/context/turn/message/reply.ts +7 -0
- package/src/session/context/turn/message/sender.ts +38 -0
- package/src/session/context/turn/message/skill.ts +8 -0
- package/src/session/context/turn/message/tool-call.ts +10 -0
- package/src/session/context/turn/message/tool-result.ts +9 -0
- package/src/session/context/turn/turn.ts +15 -0
- package/src/session/conversation.ts +194 -0
- package/src/session/goal/goal.ts +10 -0
- package/src/session/goal/task.ts +19 -0
- package/src/session/session.ts +141 -0
- package/src/testing/fake-claude.ts +25 -17
- package/src/testing/fake-elements.ts +3 -2
- package/src/testing/fake-files.ts +1 -1
- package/src/testing/fake-sessions.ts +277 -0
- package/src/testing.ts +138 -35
- package/src/ui/components.ts +3 -3
- package/src/ui/elements.ts +22 -8
- package/src/ui/markdown.ts +22 -26
- package/src/ui/places.ts +49 -0
- package/src/utils/call-effects.ts +5 -5
- package/src/utils/changelog.ts +32 -0
- package/src/utils/disposable.ts +3 -0
- package/src/utils/matcher.ts +37 -0
- package/src/utils/metadata.ts +17 -15
- package/src/utils/parse-shell.ts +9 -8
- package/src/utils/paths.ts +21 -4
- package/src/utils/text.ts +4 -0
- package/testing/fake-claude.d.ts +5 -1
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +21 -12
- package/testing/fake-elements.js +4 -2
- package/testing/fake-files.js +1 -1
- package/testing/fake-sessions.d.ts +81 -0
- package/testing/fake-sessions.d.ts.map +1 -0
- package/testing/fake-sessions.js +249 -0
- package/testing.d.ts +34 -13
- package/testing.d.ts.map +1 -1
- package/testing.js +115 -23
- package/ui/components.d.ts +2 -3
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +3 -3
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +19 -9
- package/ui/markdown.d.ts +35 -33
- package/ui/markdown.d.ts.map +1 -1
- package/ui/markdown.js +14 -16
- package/ui/places.d.ts +43 -0
- package/ui/places.d.ts.map +1 -0
- package/ui/places.js +31 -0
- package/utils/call-effects.d.ts +2 -1
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +3 -3
- package/utils/changelog.d.ts +9 -0
- package/utils/changelog.d.ts.map +1 -0
- package/utils/changelog.js +29 -0
- package/utils/disposable.d.ts +4 -0
- package/utils/disposable.d.ts.map +1 -0
- package/utils/disposable.js +1 -0
- package/utils/matcher.d.ts +13 -0
- package/utils/matcher.d.ts.map +1 -0
- package/utils/matcher.js +32 -0
- package/utils/metadata.d.ts +3 -3
- package/utils/metadata.js +18 -14
- package/utils/parse-shell.js +9 -7
- package/utils/paths.d.ts +4 -1
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +19 -4
- package/utils/text.d.ts +1 -0
- package/utils/text.d.ts.map +1 -1
- package/utils/text.js +3 -0
- package/src/ui/slots.ts +0 -36
- package/ui/slots.d.ts +0 -208
- package/ui/slots.d.ts.map +0 -1
- package/ui/slots.js +0 -19
package/jobs/context.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { JobContext
|
|
1
|
+
import type { JobContext } from '../mod.js';
|
|
2
2
|
import { type Deadline } from '../runtime/deadline.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Call } from '../utils/call-effects.js';
|
|
4
4
|
import { type Workspace } from './permissions/find-project-scope.js';
|
|
5
5
|
import { type Target } from './permissions/match-target.js';
|
|
6
|
-
export declare function workspaceReader({ mod
|
|
7
|
-
export declare function afterCall<State extends object>(job: JobContext<State>, contextAfter: (call:
|
|
8
|
-
export declare function
|
|
9
|
-
readonly call?: ToolCall;
|
|
6
|
+
export declare function workspaceReader({ mod }: JobContext): () => Promise<Workspace>;
|
|
7
|
+
export declare function afterCall<State extends object>(job: JobContext<State>, contextAfter: (call: Call, workspace: Workspace) => Promise<readonly string[]>, failed: (error: unknown) => readonly string[]): void;
|
|
8
|
+
export declare function inFolder(call: Call, folder: string, workspace: Workspace): Promise<Call & {
|
|
10
9
|
readonly folder: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function
|
|
10
|
+
}>;
|
|
11
|
+
export declare function withinDeadline<Value>(mod: object, deadline: Deadline, subject: string, run: () => Value | Promise<Value>): Promise<Value>;
|
|
13
12
|
export declare function targetWords(target: Target): string;
|
|
14
13
|
//# sourceMappingURL=context.d.ts.map
|
package/jobs/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/jobs/context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/jobs/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAItE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAY,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAKrE,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,EAAE,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAmB7E;AAED,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,MAAM,EAAE,GAAG,IAAI,CAW3M;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAE5H;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAEzI;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASlD"}
|
package/jobs/context.js
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import { beforeDeadline } from '../runtime/deadline.js';
|
|
2
|
-
import {
|
|
2
|
+
import { engineOf } from '../runtime/engine.js';
|
|
3
3
|
import { toolInputOf } from '../runtime/tool-calls.js';
|
|
4
4
|
import { listed } from '../utils/text.js';
|
|
5
5
|
import { findProjectScope } from './permissions/find-project-scope.js';
|
|
6
6
|
import { targetOf } from './permissions/match-target.js';
|
|
7
|
-
export function workspaceReader({ mod
|
|
7
|
+
export function workspaceReader({ mod }) {
|
|
8
|
+
const { claude } = engineOf(mod);
|
|
8
9
|
let session;
|
|
9
10
|
return async () => {
|
|
10
|
-
session ??= readSession(
|
|
11
|
+
session ??= readSession();
|
|
11
12
|
const [{ home, scope, fs }, projectRoot, cwd] = await Promise.all([session, claude.session.root(), claude.session.cwd()]);
|
|
12
13
|
return { projectRoot, cwd, home, fs, scope };
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
async function readSession() {
|
|
16
|
+
const fs = {
|
|
17
|
+
read: (path) => claude.fs.read(path),
|
|
18
|
+
stat: (path) => claude.fs.stat(path, { resolve: true }),
|
|
19
|
+
exists: (path) => claude.fs.exists(path),
|
|
20
|
+
};
|
|
21
|
+
const home = await claude.env.home();
|
|
22
|
+
if (home === undefined)
|
|
23
|
+
throw new Error('HOME is not set, so ~ in a path has no meaning. Start Claude Code with HOME set.');
|
|
24
|
+
return { home, fs, scope: await findProjectScope(claude.plugin.root, home, fs) };
|
|
25
|
+
}
|
|
25
26
|
}
|
|
26
27
|
export function afterCall(job, contextAfter, failed) {
|
|
28
|
+
const { router, toolCalls } = engineOf(job.mod);
|
|
27
29
|
const readWorkspace = workspaceReader(job);
|
|
28
|
-
|
|
29
|
-
const before = Promise.all([
|
|
30
|
-
await
|
|
30
|
+
router.add('tool.call', async (e, next) => {
|
|
31
|
+
const before = Promise.all([toolCalls.agentOf(e.tool_use_id).then((agent) => ({ tool: e.tool, input: toolInputOf(e), ...agent })), readWorkspace()]);
|
|
32
|
+
await Promise.allSettled([before]);
|
|
31
33
|
const result = await next(e);
|
|
32
34
|
if (result.deny !== undefined || result.isError === true)
|
|
33
35
|
return result;
|
|
@@ -35,63 +37,11 @@ export function afterCall(job, contextAfter, failed) {
|
|
|
35
37
|
return added.length === 0 ? result : { ...result, context: [...(result.context ?? []), ...added] };
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
|
-
async function
|
|
39
|
-
return {
|
|
40
|
-
}
|
|
41
|
-
export function modOf(job, { call, folder }, workspace, deadline) {
|
|
42
|
-
return modWithin(job, deadline, async () => workspace.scope?.workTreeOf(call !== undefined && 'path' in call ? call.path : folder));
|
|
40
|
+
export async function inFolder(call, folder, workspace) {
|
|
41
|
+
return { ...call, folder: (await workspace.scope?.workTreeOf('path' in call ? call.path : folder)) ?? folder };
|
|
43
42
|
}
|
|
44
|
-
export function
|
|
45
|
-
|
|
46
|
-
const within = (call, task) => beforeDeadline(claude, deadline, call, task);
|
|
47
|
-
const folderOf = async (cwd) => {
|
|
48
|
-
const folder = cwd ?? (await workTree());
|
|
49
|
-
return folder === undefined ? {} : { cwd: folder };
|
|
50
|
-
};
|
|
51
|
-
const overrides = {
|
|
52
|
-
process: {
|
|
53
|
-
async run(argv, init) {
|
|
54
|
-
const folder = await folderOf(init?.cwd);
|
|
55
|
-
const timeoutMs = Math.min(init?.timeoutMs ?? deadline.ms, deadline.longestMs ?? deadline.ms);
|
|
56
|
-
return beforeDeadline(claude, { ...deadline, ms: timeoutMs }, 'mod.process.run', mod.process.run(argv, { ...init, ...folder, timeoutMs }));
|
|
57
|
-
},
|
|
58
|
-
spawn: (argv, init) => spawnWithin(claude, deadline, folderOf(init?.cwd).then((folder) => mod.process.spawn(argv, { ...init, ...folder }))),
|
|
59
|
-
},
|
|
60
|
-
fs: {
|
|
61
|
-
read: (path) => within('mod.fs.read', mod.fs.read(path)),
|
|
62
|
-
write: (path, text) => within('mod.fs.write', mod.fs.write(path, text)),
|
|
63
|
-
list: (path) => within('mod.fs.list', mod.fs.list(path)),
|
|
64
|
-
exists: (path) => within('mod.fs.exists', mod.fs.exists(path)),
|
|
65
|
-
stat: (path, options) => within('mod.fs.stat', mod.fs.stat(path, options)),
|
|
66
|
-
find: (glob) => within('mod.fs.find', mod.fs.find(glob)),
|
|
67
|
-
},
|
|
68
|
-
http: { fetch: (url, init) => within('mod.http.fetch', mod.http.fetch(url, init)) },
|
|
69
|
-
dependencies: dependencyCalls(claude, within),
|
|
70
|
-
};
|
|
71
|
-
return Object.assign(Object.create(mod), overrides);
|
|
72
|
-
}
|
|
73
|
-
function spawnWithin(claude, deadline, started) {
|
|
74
|
-
async function* pieces() {
|
|
75
|
-
const stream = await started;
|
|
76
|
-
let finish = () => undefined;
|
|
77
|
-
const running = beforeDeadline(claude, deadline, 'mod.process.spawn', new Promise((resolve) => (finish = resolve)));
|
|
78
|
-
const passed = running.then(() => new Promise(() => undefined));
|
|
79
|
-
try {
|
|
80
|
-
for (;;) {
|
|
81
|
-
const piece = await Promise.race([stream.next(), passed]);
|
|
82
|
-
if (piece.done === true)
|
|
83
|
-
return piece.value;
|
|
84
|
-
yield piece.value;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
finish();
|
|
89
|
-
void stream.return?.();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
const result = started.then((stream) => stream.result);
|
|
93
|
-
result.catch(() => undefined);
|
|
94
|
-
return Object.assign(pieces(), { result });
|
|
43
|
+
export function withinDeadline(mod, deadline, subject, run) {
|
|
44
|
+
return beforeDeadline(engineOf(mod).claude, deadline, subject, Promise.resolve().then(run));
|
|
95
45
|
}
|
|
96
46
|
export function targetWords(target) {
|
|
97
47
|
const { key, patterns, exclusions } = targetOf(target);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Call } from '../../utils/call-effects.js';
|
|
2
2
|
import type { Workspace } from './find-project-scope.js';
|
|
3
3
|
import { type TargetCalls, type TargetKey, type TargetOf } from './match-target.js';
|
|
4
4
|
export type Rule<Mod> = {
|
|
5
5
|
[Key in TargetKey]: TargetOf<Key> & {
|
|
6
|
-
when?: (call: TargetCalls[Key]
|
|
6
|
+
when?: (call: TargetCalls[Key] & {
|
|
7
|
+
readonly folder: string;
|
|
8
|
+
}, mod: Mod) => boolean | Promise<boolean>;
|
|
7
9
|
reason?: string;
|
|
8
10
|
};
|
|
9
11
|
}[TargetKey];
|
|
@@ -17,5 +19,5 @@ export type Verdict = {
|
|
|
17
19
|
reason?: string;
|
|
18
20
|
};
|
|
19
21
|
export declare function stricterVerdict<Result extends Verdict>(first: Result, second: Result): Result;
|
|
20
|
-
export declare function decidePermission<Mod>(rules: PermissionRules<Mod>, use:
|
|
22
|
+
export declare function decidePermission<Mod>(rules: PermissionRules<Mod>, use: Call, workspace: Workspace, mod: Mod, within: (run: () => boolean | Promise<boolean>) => Promise<boolean>): Promise<Verdict | undefined>;
|
|
21
23
|
//# sourceMappingURL=decide-permission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide-permission.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/decide-permission.ts"],"names":[],"mappings":"AAEA,OAAO,EAA6C,KAAK,
|
|
1
|
+
{"version":3,"file":"decide-permission.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/decide-permission.ts"],"names":[],"mappings":"AAEA,OAAO,EAA6C,KAAK,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAClG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAuE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAIxJ,MAAM,MAAM,IAAI,CAAC,GAAG,IAAI;KACrB,GAAG,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG;QAClC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG;YAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;QACrG,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB;CACF,CAAC,SAAS,CAAC,CAAA;AAEZ,MAAM,MAAM,eAAe,CAAC,GAAG,IAAI;IAAE,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;CAAE,CAAA;AAC5E,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAA;AAC/C,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAI7D,wBAAgB,eAAe,CAAC,MAAM,SAAS,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7F;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAerN"}
|
|
@@ -3,16 +3,17 @@ import { messageOf } from '../../utils/text.js';
|
|
|
3
3
|
import { callBaseOf, callEffects } from '../../utils/call-effects.js';
|
|
4
4
|
import { folderInScope, matchTarget, targetOf } from './match-target.js';
|
|
5
5
|
import { resolve } from '../../vendor.js';
|
|
6
|
+
import { inFolder } from '../context.js';
|
|
6
7
|
const wordBoundary = '\\s;&|()<>\'"`=';
|
|
7
8
|
export function stricterVerdict(first, second) {
|
|
8
9
|
return strictness.indexOf(second.decision) > strictness.indexOf(first.decision) ? second : first;
|
|
9
10
|
}
|
|
10
|
-
export async function decidePermission(rules, use, workspace,
|
|
11
|
+
export async function decidePermission(rules, use, workspace, mod, within) {
|
|
11
12
|
try {
|
|
12
13
|
const effects = callEffects(use, workspace);
|
|
13
14
|
for (const decision of ['deny', 'ask']) {
|
|
14
15
|
for (const rule of rules[decision] ?? []) {
|
|
15
|
-
const outcome = await ruleOutcome(rule, use, effects, workspace,
|
|
16
|
+
const outcome = await ruleOutcome(rule, use, effects, workspace, mod, within);
|
|
16
17
|
if (outcome === undefined)
|
|
17
18
|
continue;
|
|
18
19
|
const reason = [rule.reason, outcome.failure === undefined ? undefined : `Its when check failed: ${outcome.failure}`].filter((part) => part !== undefined).join(' ');
|
|
@@ -25,7 +26,7 @@ export async function decidePermission(rules, use, workspace, modOf) {
|
|
|
25
26
|
return { decision: 'deny', reason: `The permissions job failed, so it denies the call: ${messageOf(error)}` };
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
async function ruleOutcome(rule, use, effects, workspace,
|
|
29
|
+
async function ruleOutcome(rule, use, effects, workspace, mod, within) {
|
|
29
30
|
const matched = await matchTarget(rule, use, effects, workspace);
|
|
30
31
|
const calls = matched.length > 0 ? matched : await unparsedMatches(rule, use, effects, workspace);
|
|
31
32
|
if (calls.length === 0)
|
|
@@ -36,7 +37,8 @@ async function ruleOutcome(rule, use, effects, workspace, modOf) {
|
|
|
36
37
|
for (const { call, access, folder } of calls) {
|
|
37
38
|
const fullCall = access?.contents === undefined ? call : { ...call, ...(await access.contents()) };
|
|
38
39
|
try {
|
|
39
|
-
|
|
40
|
+
const placed = await inFolder(fullCall, folder, workspace);
|
|
41
|
+
if (await within(() => when(placed, mod)))
|
|
40
42
|
return { failure: undefined };
|
|
41
43
|
}
|
|
42
44
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-project-scope.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/find-project-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"find-project-scope.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/find-project-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAGjE,MAAM,MAAM,YAAY,GAAG;IAAE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CAAE,CAAA;AAExF,MAAM,MAAM,SAAS,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,CAAA;AAE3H,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAwB1H"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ifMissing } from '../../utils/paths.js';
|
|
1
2
|
import { basename, dirname, resolve } from '../../vendor.js';
|
|
2
3
|
export async function findProjectScope(pluginRoot, home, fs) {
|
|
3
4
|
const skills = dirname(pluginRoot);
|
|
@@ -32,20 +33,16 @@ export async function findProjectScope(pluginRoot, home, fs) {
|
|
|
32
33
|
}
|
|
33
34
|
async function readRepository(workTree, fs) {
|
|
34
35
|
const gitPath = resolve(workTree, '.git');
|
|
35
|
-
const git = await fs.stat(gitPath).catch((
|
|
36
|
+
const git = await fs.stat(gitPath).catch(ifMissing(undefined));
|
|
36
37
|
if (git === undefined)
|
|
37
38
|
return undefined;
|
|
38
39
|
if (git.kind === 'dir')
|
|
39
40
|
return git.realPath ?? gitPath;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (gitDirectory === undefined)
|
|
43
|
-
throw new Error(`${gitPath} has no gitdir line.`);
|
|
44
|
-
const linkedDirectory = resolve(workTree, gitDirectory);
|
|
45
|
-
const commonDirectory = await fs.read(resolve(linkedDirectory, 'commondir')).then((text) => resolve(linkedDirectory, text.trim()), () => linkedDirectory);
|
|
46
|
-
return (await fs.stat(commonDirectory)).realPath ?? commonDirectory;
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
41
|
+
const gitDirectory = /^gitdir:\s*(.+)$/m.exec(await fs.read(gitPath))?.[1]?.trim();
|
|
42
|
+
if (gitDirectory === undefined)
|
|
49
43
|
return workTree;
|
|
50
|
-
|
|
44
|
+
const linkedDirectory = resolve(workTree, gitDirectory);
|
|
45
|
+
const commonDirectory = await fs.read(resolve(linkedDirectory, 'commondir')).then((text) => resolve(linkedDirectory, text.trim()), ifMissing(linkedDirectory));
|
|
46
|
+
const common = await fs.stat(commonDirectory).catch(ifMissing(undefined));
|
|
47
|
+
return common === undefined ? workTree : (common.realPath ?? commonDirectory);
|
|
51
48
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type
|
|
1
|
+
import { type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type Call } from '../../utils/call-effects.js';
|
|
2
2
|
import type { Workspace } from './find-project-scope.js';
|
|
3
3
|
export type TargetCalls = {
|
|
4
4
|
command: CommandCall;
|
|
@@ -14,7 +14,7 @@ export type Target = {
|
|
|
14
14
|
[Key in TargetKey]: TargetOf<Key>;
|
|
15
15
|
}[TargetKey];
|
|
16
16
|
export type MatchedCall = {
|
|
17
|
-
call:
|
|
17
|
+
call: Call;
|
|
18
18
|
access: FileAccess | undefined;
|
|
19
19
|
folder: string;
|
|
20
20
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-target.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/match-target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"match-target.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/match-target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,6BAA6B,CAAA;AACtK,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAIxD,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AACzI,MAAM,MAAM,SAAS,GAAG,MAAM,WAAW,CAAA;AACzC,MAAM,MAAM,QAAQ,CAAC,GAAG,SAAS,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAA;AAC9H,MAAM,MAAM,MAAM,GAAG;KAAG,GAAG,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC;CAAE,CAAC,SAAS,CAAC,CAAA;AACrE,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAIxF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAWrG;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAkCpI;AA4BD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI/H"}
|
package/jobs/permissions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Job, Mod } from '../mod.js';
|
|
2
2
|
import { type PermissionRules as RulesFor, type Rule as RuleFor } from './permissions/decide-permission.js';
|
|
3
|
-
export type { CommandCall, FetchCall, FileCall,
|
|
3
|
+
export type { CommandCall, FetchCall, FileCall, Call } from '../utils/call-effects.js';
|
|
4
4
|
export type { Target } from './permissions/match-target.js';
|
|
5
5
|
export type Rule<State extends object = Record<never, never>> = RuleFor<Mod<State>>;
|
|
6
6
|
export type PermissionRules<State extends object = Record<never, never>> = RulesFor<Mod<State>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/jobs/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/jobs/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIzC,OAAO,EAAqC,KAAK,eAAe,IAAI,QAAQ,EAAE,KAAK,IAAI,IAAI,OAAO,EAAgB,MAAM,oCAAoC,CAAA;AAK5J,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACtF,YAAY,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAC3D,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AACnF,MAAM,MAAM,eAAe,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAI/F,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA8BrK"}
|
package/jobs/permissions.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { engineOf } from '../runtime/engine.js';
|
|
1
2
|
import { inputOf } from '../utils/call-effects.js';
|
|
2
3
|
import { decidePermission, stricterVerdict } from './permissions/decide-permission.js';
|
|
3
4
|
import { targetOf } from './permissions/match-target.js';
|
|
4
5
|
import { messageOf } from '../utils/text.js';
|
|
5
|
-
import {
|
|
6
|
+
import { withinDeadline, workspaceReader } from './context.js';
|
|
6
7
|
const deadline = { ms: 2000, job: 'permissions' };
|
|
7
8
|
export function permissions(rules) {
|
|
8
9
|
const count = typeof rules === 'function' ? undefined : checkedCount(rules);
|
|
9
10
|
if (count === 0)
|
|
10
11
|
throw new Error('permissions: give it a deny or an ask rule, or remove it from setup.');
|
|
11
12
|
return (job) => {
|
|
13
|
+
const { router, toolCalls } = engineOf(job.mod);
|
|
12
14
|
const readWorkspace = workspaceReader(job);
|
|
13
15
|
const verdictOf = async (e) => {
|
|
14
16
|
let current;
|
|
@@ -20,14 +22,14 @@ export function permissions(rules) {
|
|
|
20
22
|
return { decision: 'deny', reason: `The permission rules of ${job.mod.name} failed, so it denies the call: ${messageOf(error)}` };
|
|
21
23
|
}
|
|
22
24
|
try {
|
|
23
|
-
const [agent, workspace] = await Promise.all([
|
|
24
|
-
return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace,
|
|
25
|
+
const [agent, workspace] = await Promise.all([toolCalls.agentOf(e.tool_use_id), readWorkspace()]);
|
|
26
|
+
return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace, job.mod, (run) => withinDeadline(job.mod, deadline, 'it', run));
|
|
25
27
|
}
|
|
26
28
|
catch (error) {
|
|
27
29
|
return { decision: 'deny', reason: `The permissions job could not read the session, so it denies the call: ${messageOf(error)}` };
|
|
28
30
|
}
|
|
29
31
|
};
|
|
30
|
-
|
|
32
|
+
router.add('tool.check', async (e, next) => {
|
|
31
33
|
const [below, ours] = await Promise.all([next(e), verdictOf(e)]);
|
|
32
34
|
return ours === undefined ? below : stricterVerdict(below, ours);
|
|
33
35
|
});
|
package/jobs/program.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAKpC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;IACxE,KAAK,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACvD,CAAA;AAMD,wBAAgB,OAAO,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAmFrI"}
|
package/jobs/program.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { PermissionRefused } from '../runtime/grants.js';
|
|
1
|
+
import { PermissionRefused, programAddresses } from '../runtime/grants.js';
|
|
2
|
+
import { engineOf } from '../runtime/engine.js';
|
|
2
3
|
import { messageOf } from '../utils/text.js';
|
|
3
4
|
const backoffSeconds = [1, 2, 4, 8, 16];
|
|
4
5
|
export function program(options) {
|
|
5
6
|
const { command, environment } = options;
|
|
6
7
|
return ({ mod, announce }) => {
|
|
7
|
-
const { claude } = mod;
|
|
8
|
+
const { claude, report } = engineOf(mod);
|
|
8
9
|
const [executable] = command;
|
|
9
10
|
if (executable === undefined)
|
|
10
11
|
throw new Error(`${mod.name}: the program has no command. Name the program on PATH and its arguments, such as ['preview-server', '--port', '0'].`);
|
|
@@ -13,10 +14,16 @@ export function program(options) {
|
|
|
13
14
|
let failure;
|
|
14
15
|
let exits = 0;
|
|
15
16
|
const waiting = [];
|
|
17
|
+
const ownAddresses = programAddresses(mod);
|
|
16
18
|
const stop = (reason) => {
|
|
17
19
|
state = 'fatal';
|
|
18
20
|
failure = new Error(`${mod.name}: the ${executable} program stopped: ${reason}`);
|
|
19
|
-
|
|
21
|
+
const retry = () => {
|
|
22
|
+
exits = 0;
|
|
23
|
+
failure = undefined;
|
|
24
|
+
void start();
|
|
25
|
+
};
|
|
26
|
+
report({ id: `program:${executable}`, message: `The ${executable} program stopped`, description: reason, retry });
|
|
20
27
|
for (const waiter of waiting.splice(0))
|
|
21
28
|
waiter.reject(failure);
|
|
22
29
|
};
|
|
@@ -44,6 +51,7 @@ export function program(options) {
|
|
|
44
51
|
}
|
|
45
52
|
state = 'running';
|
|
46
53
|
exits = 0;
|
|
54
|
+
ownAddresses.add(address.socketPath ?? new URL(address.url).host);
|
|
47
55
|
for (const waiter of waiting.splice(0))
|
|
48
56
|
waiter.resolve(address);
|
|
49
57
|
}
|
|
@@ -53,11 +61,13 @@ export function program(options) {
|
|
|
53
61
|
return stop(`${messageOf(error)} Add it, then run /reload-plugins.`);
|
|
54
62
|
lastError = messageOf(error);
|
|
55
63
|
}
|
|
64
|
+
if (address !== undefined)
|
|
65
|
+
ownAddresses.delete(address.socketPath ?? new URL(address.url).host);
|
|
56
66
|
address = undefined;
|
|
57
67
|
exits += 1;
|
|
58
68
|
const wait = backoffSeconds[exits - 1];
|
|
59
69
|
if (wait === undefined) {
|
|
60
|
-
stop(`it did not start again after ${backoffSeconds.length} tries: ${lastError === '' ? 'no standard error' : lastError}
|
|
70
|
+
stop(`it did not start again after ${backoffSeconds.length} tries: ${lastError === '' ? 'no standard error' : lastError}.`);
|
|
61
71
|
return;
|
|
62
72
|
}
|
|
63
73
|
state = 'backoff';
|
package/jobs/prompt.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Job, Mod } from '../mod.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Call } from '../utils/call-effects.js';
|
|
3
3
|
import { type Target } from './permissions/match-target.js';
|
|
4
4
|
type PromptInput = {
|
|
5
5
|
userPrompt?: string;
|
|
6
|
-
call?:
|
|
6
|
+
call?: Call & {
|
|
7
|
+
readonly folder: string;
|
|
8
|
+
};
|
|
7
9
|
};
|
|
8
10
|
type PromptOptions<State extends object> = {
|
|
9
11
|
name: string;
|
package/jobs/prompt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/jobs/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAc,GAAG,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/jobs/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAc,GAAG,EAAE,MAAM,WAAW,CAAA;AAKrD,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAGlF,KAAK,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAErF,KAAK,aAAa,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IAC5G,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC1B,CAAA;AAID,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoC/I"}
|
package/jobs/prompt.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { engineOf } from '../runtime/engine.js';
|
|
2
2
|
import { onStateChange } from '../runtime/state.js';
|
|
3
3
|
import { listed, messageOf } from '../utils/text.js';
|
|
4
4
|
import { callEffects } from '../utils/call-effects.js';
|
|
5
5
|
import { matchTarget, targetOf } from './permissions/match-target.js';
|
|
6
|
-
import { afterCall,
|
|
7
|
-
const deadline = { ms: 5000,
|
|
6
|
+
import { afterCall, inFolder, targetWords, withinDeadline } from './context.js';
|
|
7
|
+
const deadline = { ms: 5000, job: 'prompt' };
|
|
8
8
|
export function prompt({ name, prompt: text, when, after }) {
|
|
9
9
|
if (name.trim() === '')
|
|
10
10
|
throw new Error('prompt: give the block a name. Claude reads the text under it.');
|
|
@@ -15,10 +15,10 @@ export function prompt({ name, prompt: text, when, after }) {
|
|
|
15
15
|
targetOf(target);
|
|
16
16
|
return (job) => {
|
|
17
17
|
job.reserveName('prompt', name, `prompt: ${job.mod.name} adds the name "${name}" two times. Give each prompt its own name.`);
|
|
18
|
-
const log = (reason) => job.mod.
|
|
19
|
-
const textFor = async (input
|
|
18
|
+
const log = (reason) => job.mod.log(`prompt "${name}" added nothing: ${reason}`);
|
|
19
|
+
const textFor = async (input) => {
|
|
20
20
|
try {
|
|
21
|
-
const value = typeof text === 'string' ? text : await text(input, mod);
|
|
21
|
+
const value = typeof text === 'string' ? text : await withinDeadline(job.mod, deadline, `the ${name} prompt`, () => text(input, job.mod));
|
|
22
22
|
return value === '' ? undefined : value;
|
|
23
23
|
}
|
|
24
24
|
catch (error) {
|
|
@@ -26,12 +26,12 @@ export function prompt({ name, prompt: text, when, after }) {
|
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
-
const headedText = async (input
|
|
30
|
-
const value = await textFor(input
|
|
29
|
+
const headedText = async (input) => {
|
|
30
|
+
const value = await textFor(input);
|
|
31
31
|
return value === undefined ? undefined : `# ${name}\n${value}`;
|
|
32
32
|
};
|
|
33
33
|
if (when !== undefined) {
|
|
34
|
-
addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }
|
|
34
|
+
addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }), log);
|
|
35
35
|
job.announce(`the ${name} prompt after matching user prompts`);
|
|
36
36
|
}
|
|
37
37
|
else if (targets.length > 0) {
|
|
@@ -39,23 +39,24 @@ export function prompt({ name, prompt: text, when, after }) {
|
|
|
39
39
|
job.announce(`the ${name} prompt after ${listed(targets.map(targetWords))}`);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
addKeptCurrent(job, name, () => textFor({}
|
|
42
|
+
addKeptCurrent(job, name, () => textFor({}), log);
|
|
43
43
|
job.announce(`the ${name} prompt`);
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
function addKeptCurrent(job, name, textFor, log) {
|
|
48
|
+
const { claude, router } = engineOf(job.mod);
|
|
48
49
|
let given;
|
|
49
50
|
let hasStarted = false;
|
|
50
51
|
let isQueued = false;
|
|
51
52
|
let updates = Promise.resolve();
|
|
52
|
-
|
|
53
|
+
router.add('classic.SessionStart', (e, next) => {
|
|
53
54
|
hasStarted = e.source === 'resume' || e.source === 'compact';
|
|
54
55
|
if (!hasStarted)
|
|
55
56
|
given = undefined;
|
|
56
57
|
return next(e);
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
+
router.add('prompt.context', async (e, next) => {
|
|
59
60
|
const below = await next(e);
|
|
60
61
|
hasStarted = true;
|
|
61
62
|
if (below.blocks.some((block) => block.name === name)) {
|
|
@@ -73,7 +74,7 @@ function addKeptCurrent(job, name, textFor, log) {
|
|
|
73
74
|
if (value === undefined || value === given)
|
|
74
75
|
return;
|
|
75
76
|
given = value;
|
|
76
|
-
await
|
|
77
|
+
await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: `# ${name}\n${value}` }] } }).then((added) => {
|
|
77
78
|
if (added.deny !== undefined)
|
|
78
79
|
log(`Claude Code refused the new text: ${added.deny}`);
|
|
79
80
|
});
|
|
@@ -86,7 +87,7 @@ function addKeptCurrent(job, name, textFor, log) {
|
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
function addAfterUserPrompts(job, when, textFor, log) {
|
|
89
|
-
job.mod.
|
|
90
|
+
engineOf(job.mod).router.add('prompt.submit', async (e, next) => {
|
|
90
91
|
let value;
|
|
91
92
|
try {
|
|
92
93
|
if (typeof when === 'function' ? when(e.text) : e.text.search(when) >= 0)
|
|
@@ -105,7 +106,7 @@ function addAfterCalls(job, targets, textFor, log) {
|
|
|
105
106
|
const [matched] = await matchTarget(target, use, effects, workspace);
|
|
106
107
|
if (matched === undefined)
|
|
107
108
|
continue;
|
|
108
|
-
const value = await textFor({ call: matched.call
|
|
109
|
+
const value = await textFor({ call: await inFolder(matched.call, matched.folder, workspace) });
|
|
109
110
|
return value === undefined ? [] : [value];
|
|
110
111
|
}
|
|
111
112
|
return [];
|
package/jobs/schedule.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Job } from '../mod.js';
|
|
2
|
-
export type
|
|
2
|
+
export type ScheduledPrompt = {
|
|
3
3
|
readonly cron: string;
|
|
4
4
|
readonly prompt: string;
|
|
5
5
|
};
|
|
6
|
-
export declare function schedule<State extends object = Record<never, never>>(entry:
|
|
6
|
+
export declare function schedule<State extends object = Record<never, never>>(entry: ScheduledPrompt | ((state: State) => ScheduledPrompt | undefined)): Job<void, State>;
|
|
7
7
|
//# sourceMappingURL=schedule.d.ts.map
|
package/jobs/schedule.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/jobs/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIpC,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/jobs/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIpC,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAIhF,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,eAAe,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,eAAe,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoDhK"}
|
package/jobs/schedule.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { onStateChange } from '../runtime/state.js';
|
|
2
|
-
import {
|
|
2
|
+
import { engineOf } from '../runtime/engine.js';
|
|
3
3
|
const scheduleCounts = new WeakMap();
|
|
4
4
|
export function schedule(entry) {
|
|
5
5
|
if (typeof entry !== 'function')
|
|
6
6
|
checked(entry);
|
|
7
7
|
return (job) => {
|
|
8
8
|
const { mod } = job;
|
|
9
|
-
const { claude } = mod;
|
|
9
|
+
const { claude, report: reportFailure } = engineOf(mod);
|
|
10
10
|
const order = scheduleCounts.get(mod) ?? 0;
|
|
11
11
|
scheduleCounts.set(mod, order + 1);
|
|
12
12
|
const storeKey = `cmod:schedule:${order}`;
|
|
13
|
-
const
|
|
13
|
+
const report = (message, error) => reportFailure({ id: storeKey, message: `The schedule ${message}`, error });
|
|
14
14
|
let current;
|
|
15
15
|
let isQueued = false;
|
|
16
16
|
const remove = async (id) => {
|
|
17
|
-
await claude.tool.call({ tool: 'CronDelete', id }).catch(() =>
|
|
17
|
+
await claude.tool.call({ tool: 'CronDelete', id }).catch((error) => report(`could not delete its last cron ${id}`, error));
|
|
18
18
|
await claude.store.delete(storeKey);
|
|
19
19
|
};
|
|
20
20
|
const update = async () => {
|
|
@@ -26,7 +26,7 @@ export function schedule(entry) {
|
|
|
26
26
|
checked(next);
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
|
-
|
|
29
|
+
report('kept its last cron', error);
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
const wanted = next === undefined ? undefined : JSON.stringify([next.cron, next.prompt]);
|
|
@@ -39,9 +39,9 @@ export function schedule(entry) {
|
|
|
39
39
|
return;
|
|
40
40
|
const answer = await claude.tool.call({ tool: 'CronCreate', cron: next.cron, prompt: next.prompt, recurring: true });
|
|
41
41
|
if (answer.deny !== undefined)
|
|
42
|
-
return
|
|
42
|
+
return report('was refused', answer.deny);
|
|
43
43
|
if (answer.isError === true)
|
|
44
|
-
return
|
|
44
|
+
return report('failed', answer.text ?? 'CronCreate answered an error');
|
|
45
45
|
current = { id: answer.result.id, wanted: wanted };
|
|
46
46
|
await claude.store.set(storeKey, current.id);
|
|
47
47
|
};
|
|
@@ -49,12 +49,12 @@ export function schedule(entry) {
|
|
|
49
49
|
.get(storeKey)
|
|
50
50
|
.then((left) => (typeof left === 'string' ? remove(left) : undefined))
|
|
51
51
|
.then(update)
|
|
52
|
-
.catch((error) =>
|
|
52
|
+
.catch((error) => report('failed', error));
|
|
53
53
|
onStateChange(mod.state, () => {
|
|
54
54
|
if (isQueued)
|
|
55
55
|
return;
|
|
56
56
|
isQueued = true;
|
|
57
|
-
updates = updates.then(update).catch((error) =>
|
|
57
|
+
updates = updates.then(update).catch((error) => report('failed', error));
|
|
58
58
|
});
|
|
59
59
|
job.announce(typeof entry === 'function' ? 'a schedule from its state' : `a schedule at ${entry.cron}`);
|
|
60
60
|
};
|