@cmodjs/core 4.0.3 → 4.1.1
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 +44 -9
- package/docs/index.md +3 -3
- package/docs/install-steps.md +8 -8
- package/docs/jobs.md +2 -1
- package/docs/mod.md +1 -1
- package/docs/options.md +1 -1
- package/docs/permissions.md +1 -1
- package/docs/session.md +46 -13
- package/docs/testing.md +15 -11
- package/docs/ui.md +43 -11
- package/installer.d.ts +3 -2
- package/installer.d.ts.map +1 -1
- package/jobs/program.d.ts +1 -0
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +39 -23
- package/mod.d.ts +3 -2
- package/mod.d.ts.map +1 -1
- package/mod.js +1 -0
- package/package.json +1 -1
- package/records.d.ts +15 -1
- package/records.d.ts.map +1 -1
- package/records.js +39 -1
- package/register.d.ts.map +1 -1
- package/register.js +8 -5
- package/runtime/before.d.ts +6 -2
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +56 -18
- package/runtime/buttons.d.ts +10 -0
- package/runtime/buttons.d.ts.map +1 -0
- package/runtime/buttons.js +12 -0
- package/runtime/conversations.d.ts +3 -0
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +164 -27
- package/runtime/engine.d.ts +2 -0
- package/runtime/engine.d.ts.map +1 -1
- package/runtime/grants.d.ts +1 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +5 -2
- package/runtime/hotkeys.d.ts.map +1 -1
- package/runtime/hotkeys.js +7 -4
- package/runtime/installer.d.ts +10 -6
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +106 -48
- package/runtime/lifecycle.d.ts +2 -2
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +144 -42
- package/runtime/pages.d.ts +3 -1
- package/runtime/pages.d.ts.map +1 -1
- package/runtime/pages.js +48 -14
- package/runtime/parts.d.ts +1 -0
- package/runtime/parts.d.ts.map +1 -1
- package/runtime/parts.js +6 -3
- package/runtime/programs.d.ts +1 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +4 -1
- package/runtime/sessions.d.ts +10 -3
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +104 -59
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +23 -9
- package/runtime/ui.d.ts +22 -4
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +161 -59
- package/runtime/watches.d.ts.map +1 -1
- package/runtime/watches.js +18 -4
- package/runtime/widgets.d.ts +7 -5
- package/runtime/widgets.d.ts.map +1 -1
- package/runtime/widgets.js +31 -21
- package/session/context/turn/message/attached-file.d.ts +13 -0
- package/session/context/turn/message/attached-file.d.ts.map +1 -0
- package/session/context/turn/message/attached-file.js +21 -0
- package/session/context/turn/message/message.d.ts +3 -2
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +20 -6
- package/session/conversation.d.ts +15 -3
- package/session/conversation.d.ts.map +1 -1
- package/session/conversation.js +23 -4
- package/session/goal/task.d.ts +5 -0
- package/session/goal/task.d.ts.map +1 -1
- package/session/session.d.ts +36 -7
- package/session/session.d.ts.map +1 -1
- package/session/session.js +2 -1
- package/src/installer.ts +3 -2
- package/src/jobs/program.ts +41 -21
- package/src/mod.ts +3 -2
- package/src/records.ts +49 -1
- package/src/register.ts +11 -8
- package/src/runtime/before.ts +61 -22
- package/src/runtime/buttons.ts +22 -0
- package/src/runtime/conversations.ts +170 -34
- package/src/runtime/engine.ts +2 -0
- package/src/runtime/grants.ts +6 -2
- package/src/runtime/hotkeys.ts +7 -4
- package/src/runtime/installer.ts +147 -83
- package/src/runtime/lifecycle.ts +149 -48
- package/src/runtime/pages.ts +56 -12
- package/src/runtime/parts.ts +8 -4
- package/src/runtime/programs.ts +5 -1
- package/src/runtime/sessions.ts +106 -59
- package/src/runtime/toaster.ts +27 -9
- package/src/runtime/ui.ts +166 -62
- package/src/runtime/watches.ts +20 -5
- package/src/runtime/widgets.ts +35 -23
- package/src/session/context/turn/message/attached-file.ts +36 -0
- package/src/session/context/turn/message/message.ts +20 -7
- package/src/session/conversation.ts +31 -6
- package/src/session/goal/task.ts +6 -0
- package/src/session/session.ts +18 -7
- package/src/testing/fake-claude.ts +21 -6
- package/src/testing/fake-elements.ts +57 -8
- package/src/testing/fake-sessions.ts +72 -24
- package/src/testing.ts +114 -33
- package/src/ui/areas.ts +1 -0
- package/src/ui/components.ts +14 -8
- package/src/ui/elements.ts +6 -2
- package/src/ui/markdown.ts +3 -3
- package/src/ui/panes.ts +2 -2
- package/src/ui/styles.ts +6 -2
- package/testing/fake-claude.d.ts +3 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +18 -5
- package/testing/fake-elements.d.ts.map +1 -1
- package/testing/fake-elements.js +52 -7
- package/testing/fake-sessions.d.ts +24 -10
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +66 -20
- package/testing.d.ts +8 -12
- package/testing.d.ts.map +1 -1
- package/testing.js +113 -27
- package/ui/areas.d.ts +4 -0
- package/ui/areas.d.ts.map +1 -1
- package/ui/components.d.ts +1 -0
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +11 -7
- package/ui/elements.d.ts +3 -0
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +6 -1
- package/ui/markdown.d.ts +2 -2
- package/ui/markdown.d.ts.map +1 -1
- package/ui/panes.d.ts +2 -1
- package/ui/panes.d.ts.map +1 -1
- package/ui/styles.d.ts +11 -2
- package/ui/styles.d.ts.map +1 -1
package/runtime/lifecycle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { consentPath, dataFolder, finishStepsMethod, grantFix, coreVersion, isAtLeast, marketplaceOf, parseConsent, pendingStepsMethod, parseEvent, permissionWords, readRecord, readSteps, recordPath, scriptsSha256, storeFolder, updatesToTurnOn } from '../records.js';
|
|
1
|
+
import { consentPath, dataFolder, finishStepsMethod, grantFix, coreVersion, isAtLeast, isProgramNewer, marketplaceOf, parseConsent, pendingStepsMethod, parseEvent, permissionWords, programPath, readRecord, readSteps, recordPath, scriptsSha256, storeFolder, updatesToTurnOn } from '../records.js';
|
|
2
2
|
import { homePrefix, ifMissing, relativePath } from '../utils/paths.js';
|
|
3
3
|
import { firstLine, formatExit, messageOf } from '../utils/text.js';
|
|
4
4
|
import { beforeDeadline } from './deadline.js';
|
|
@@ -8,8 +8,8 @@ import { serveConversations } from './conversations.js';
|
|
|
8
8
|
import { answerCall, answeringOwnCalls, cmodPluginName, dependencyCalls, withBareRefusals } from './dependencies.js';
|
|
9
9
|
import { keepEngine } from './engine.js';
|
|
10
10
|
import { classicFailure, classicHook, userSkillHook } from './hooks.js';
|
|
11
|
-
import { checkedAnswer, checkingGrants, checkSettings, checksAnswers, checkWrite, isCovered, itemOf, passedDown, programAddresses } from './grants.js';
|
|
12
|
-
import { cmodError, locatingPrograms, readLines } from './programs.js';
|
|
11
|
+
import { checkedAnswer, checkingGrants, checkRun, checkSettings, checksAnswers, checkWrite, isCovered, itemOf, passedDown, programAddresses } from './grants.js';
|
|
12
|
+
import { cmodError, locatingPrograms, readLines, spawnCmod } from './programs.js';
|
|
13
13
|
import { createModFiles, resolvedPath } from './metadata.js';
|
|
14
14
|
import { fitsOption } from '../options.js';
|
|
15
15
|
import { createInstaller } from './installer.js';
|
|
@@ -35,6 +35,8 @@ const askingId = 'cmod:asking';
|
|
|
35
35
|
const recordId = 'cmod:record';
|
|
36
36
|
const installRetrySeconds = [2, 4, 8, 16, 32, 64, 128];
|
|
37
37
|
const cmodCheckMs = 1000;
|
|
38
|
+
const recordCheckMs = 1000;
|
|
39
|
+
const drawsPerMs = 1000;
|
|
38
40
|
const cmodWaitMs = 60_000;
|
|
39
41
|
const cmodSlowCheckMs = 10_000;
|
|
40
42
|
const hookMs = 10_000;
|
|
@@ -68,7 +70,8 @@ export async function readPlugin(claude) {
|
|
|
68
70
|
if (version === undefined || record?.scriptsSha256 !== scripts)
|
|
69
71
|
return { ...plugin, isInstalled: false, shouldRecord: false };
|
|
70
72
|
const isInstalled = record.version === version || record.root === root;
|
|
71
|
-
|
|
73
|
+
const programModifiedMs = record.program === null ? undefined : (await claude.fs.stat(programPath(store, record.program, record.version)).catch(ifMissing(undefined)))?.mtimeMs;
|
|
74
|
+
return { ...plugin, isInstalled, shouldRecord: isInstalled && (record.version !== version || isProgramNewer(record, programModifiedMs)) };
|
|
72
75
|
}
|
|
73
76
|
async function filesBelow(claude, folder) {
|
|
74
77
|
const entries = await claude.fs.list(folder);
|
|
@@ -121,6 +124,8 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
121
124
|
let raise;
|
|
122
125
|
let failure;
|
|
123
126
|
let pluginOptions = options;
|
|
127
|
+
let drawCount = 0;
|
|
128
|
+
const nextDraw = () => (drawCount += 1);
|
|
124
129
|
const installer = createInstaller(definition.name, () => claude(), {
|
|
125
130
|
show: (open) => {
|
|
126
131
|
if (phase !== 'active')
|
|
@@ -128,7 +133,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
128
133
|
runtime?.toaster.toast.info('Installing', { id: askingId, description: 'Waiting for your answer', duration: Infinity, action: { label: 'Answer', onClick: open } });
|
|
129
134
|
},
|
|
130
135
|
hide: () => runtime?.toaster.toast.dismiss(askingId),
|
|
131
|
-
});
|
|
136
|
+
}, nextDraw);
|
|
132
137
|
let shouldRecord = false;
|
|
133
138
|
let recording;
|
|
134
139
|
let missedSessionStart;
|
|
@@ -147,6 +152,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
147
152
|
failure ??= new Error(`${definition.name}: ${reason}${/[.!?]$/.test(reason) ? '' : '.'}${fix === undefined ? '' : ` ${fix}`}`, { cause: error });
|
|
148
153
|
const again = retry === undefined ? {} : { retry: () => ((failure = undefined), stopRetrying(), retry().catch(report)) };
|
|
149
154
|
toastFailure({ id: installId, message: reason, ...(fix === undefined ? {} : { description: fix }), ...(error === undefined ? {} : { error }), ...again });
|
|
155
|
+
watchRecord();
|
|
150
156
|
};
|
|
151
157
|
let consented;
|
|
152
158
|
const installAgain = () => install(consented);
|
|
@@ -255,29 +261,34 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
255
261
|
};
|
|
256
262
|
const runSteps = async (active) => {
|
|
257
263
|
const steps = asksPerson ? (definition.installer ?? []) : [];
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
264
|
+
try {
|
|
265
|
+
for (const [index, step] of steps.entries()) {
|
|
266
|
+
if (await isStepDone(active, step))
|
|
267
|
+
continue;
|
|
268
|
+
const isAnswered = (await claude().session.surfaces()).length > 0 && (await installer.step(active, step, index + 1, steps.length));
|
|
269
|
+
if (!isAnswered) {
|
|
270
|
+
runtime?.toaster.toast.warning(steps.length - index === 1 ? 'Needs one more step' : `Needs ${steps.length - index} more steps`, {
|
|
271
|
+
id: 'cmod:steps',
|
|
272
|
+
description: `Starting with ${step.title}.`,
|
|
273
|
+
duration: Infinity,
|
|
274
|
+
action: { label: 'Finish', onClick: () => runSteps(active) },
|
|
275
|
+
});
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
270
278
|
}
|
|
271
279
|
}
|
|
272
|
-
|
|
280
|
+
finally {
|
|
281
|
+
await installer.close();
|
|
282
|
+
}
|
|
273
283
|
};
|
|
274
284
|
const activate = async () => {
|
|
275
285
|
if (runtime === undefined || plugin === undefined)
|
|
276
286
|
return;
|
|
277
287
|
const activeRouter = createRouter();
|
|
278
|
-
const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants, permissionRoutes, options: pluginOptions }).catch(async (error) => {
|
|
288
|
+
const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants, permissionRoutes, options: pluginOptions, nextDraw }).catch(async (error) => {
|
|
279
289
|
if (error instanceof MissingOptions && (await askOptions(error)))
|
|
280
290
|
return undefined;
|
|
291
|
+
await installer.close();
|
|
281
292
|
fail(messageOf(error), error instanceof MissingOptions ? `Retry asks for ${error.keys.length === 1 ? 'it' : 'them'}.` : undefined, begin, error);
|
|
282
293
|
throw error;
|
|
283
294
|
});
|
|
@@ -289,7 +300,10 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
289
300
|
raise = active.raise;
|
|
290
301
|
router = activeRouter;
|
|
291
302
|
phase = 'active';
|
|
303
|
+
stopWatchingRecord();
|
|
292
304
|
runtime.claude.ui.invalidate('ui.render');
|
|
305
|
+
if (definition.name === cmodPluginName)
|
|
306
|
+
runtime.claude.ui.invalidate('command.describe');
|
|
293
307
|
void runSteps(active.mod).catch((error) => toastFailure({ id: 'cmod:steps', message: 'Its installer stopped', error }));
|
|
294
308
|
const missed = missedSessionStart;
|
|
295
309
|
missedSessionStart = undefined;
|
|
@@ -343,9 +357,15 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
343
357
|
toastFailure({ id: 'cmod:updates', message: 'Could not read whether its marketplace updates automatically', error });
|
|
344
358
|
return undefined;
|
|
345
359
|
});
|
|
346
|
-
const
|
|
360
|
+
const installedElsewhere = new Promise((resolve) => (settleAsked = () => resolve('installed')));
|
|
361
|
+
watchRecord();
|
|
362
|
+
const answer = await Promise.race([installer.consent({ ...event, program: plugin?.steps.program ?? '', updates }), installedElsewhere]).finally(() => {
|
|
363
|
+
settleAsked = undefined;
|
|
364
|
+
});
|
|
347
365
|
await installer.close();
|
|
348
|
-
if (
|
|
366
|
+
if (answer === 'installed')
|
|
367
|
+
return startInstalled();
|
|
368
|
+
if (answer) {
|
|
349
369
|
consented = event.sha256;
|
|
350
370
|
return install(consented);
|
|
351
371
|
}
|
|
@@ -480,8 +500,8 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
480
500
|
};
|
|
481
501
|
let recordMs = 0;
|
|
482
502
|
let readStarted;
|
|
483
|
-
const
|
|
484
|
-
if (plugin === undefined || readStarted === undefined
|
|
503
|
+
const isNewlyInstalled = async () => {
|
|
504
|
+
if (plugin === undefined || readStarted === undefined)
|
|
485
505
|
return false;
|
|
486
506
|
const modifiedMs = await stampOf(recordPath(plugin.store, plugin.name));
|
|
487
507
|
if (modifiedMs === recordMs)
|
|
@@ -491,12 +511,40 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
491
511
|
if (!current.isInstalled)
|
|
492
512
|
return false;
|
|
493
513
|
plugin = current;
|
|
514
|
+
return true;
|
|
515
|
+
};
|
|
516
|
+
const startInstalled = async () => {
|
|
494
517
|
failure = undefined;
|
|
495
518
|
activation = undefined;
|
|
496
519
|
await refreshGrants();
|
|
497
520
|
await finish();
|
|
521
|
+
};
|
|
522
|
+
let settleAsked;
|
|
523
|
+
const recover = async () => {
|
|
524
|
+
if ((settleAsked === undefined && phase !== 'failed' && phase !== 'declined') || !(await isNewlyInstalled()))
|
|
525
|
+
return false;
|
|
526
|
+
if (settleAsked === undefined)
|
|
527
|
+
await startInstalled();
|
|
528
|
+
else
|
|
529
|
+
settleAsked();
|
|
498
530
|
return true;
|
|
499
531
|
};
|
|
532
|
+
let recordCheck;
|
|
533
|
+
let checking;
|
|
534
|
+
const watchRecord = () => {
|
|
535
|
+
recordCheck ??= claude().clock.every(recordCheckMs, () => {
|
|
536
|
+
checking ??= recover()
|
|
537
|
+
.then(() => undefined)
|
|
538
|
+
.catch((error) => claude().ui.log(`${definition.name} could not read whether another cmod installed it: ${messageOf(error)}`, { to: 'debug' }))
|
|
539
|
+
.finally(() => {
|
|
540
|
+
checking = undefined;
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
const stopWatchingRecord = () => {
|
|
545
|
+
recordCheck?.cancel();
|
|
546
|
+
recordCheck = undefined;
|
|
547
|
+
};
|
|
500
548
|
const refreshGrants = async () => {
|
|
501
549
|
if (plugin === undefined || plugin.name === cmodPluginName)
|
|
502
550
|
return;
|
|
@@ -579,6 +627,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
579
627
|
runtime = { claude: programs.claude, toaster: createToaster(claudeCalls, definition.name, () => plugin?.version, () => openSettings('permissions')) };
|
|
580
628
|
readStarted = read;
|
|
581
629
|
try {
|
|
630
|
+
drawCount = (await claudeCalls.clock.now()) * drawsPerMs;
|
|
582
631
|
await programs.refresh();
|
|
583
632
|
await begin().catch(report);
|
|
584
633
|
}
|
|
@@ -589,10 +638,16 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
589
638
|
async route(event, e, next) {
|
|
590
639
|
if (imports !== undefined)
|
|
591
640
|
bind(imports);
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
641
|
+
const asked = e;
|
|
642
|
+
if (event === 'cmod.call' && asked.to === definition.name && asked.caller === cmodPluginName) {
|
|
643
|
+
if (asked.method === partMethods.shown)
|
|
644
|
+
installer.hear(asked.input);
|
|
645
|
+
const answered = installer.answer(asked.method, asked.input);
|
|
646
|
+
if (answered !== undefined)
|
|
647
|
+
return (await answered.catch((error) => ({ deny: `${definition.name}: ${messageOf(error)}` })));
|
|
648
|
+
if (asked.method === partMethods.shown && phase !== 'active')
|
|
649
|
+
return { value: null };
|
|
650
|
+
}
|
|
596
651
|
if (event === 'classic.SessionStart') {
|
|
597
652
|
missedSessionStart = undefined;
|
|
598
653
|
hasHeardSessionStart = true;
|
|
@@ -605,10 +660,11 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
605
660
|
record();
|
|
606
661
|
if (event === 'classic.UserPromptSubmit' || event === 'classic.SessionStart')
|
|
607
662
|
await recover().catch((error) => claude().ui.log(`${definition.name} stays stopped: ${messageOf(error)}`, { to: 'debug' }));
|
|
608
|
-
const
|
|
663
|
+
const tool = e.tool;
|
|
664
|
+
const isCallToThisMod = (event === 'cmod.call' && e.to === definition.name) || (event === 'tool.call' && typeof tool === 'string' && tool.startsWith(`mcp__${definition.name}__`));
|
|
609
665
|
if (event === 'cmod.call' && !isCallToThisMod)
|
|
610
666
|
return next(e);
|
|
611
|
-
await (event === 'classic.SessionStart' || isCallToThisMod ? holdUntilStarted() : whenActive());
|
|
667
|
+
await (event === 'classic.SessionStart' || event === 'session.append' || isCallToThisMod ? holdUntilStarted() : whenActive());
|
|
612
668
|
if (event === 'classic.SessionStart' && phase !== 'active') {
|
|
613
669
|
missedSessionStart = e;
|
|
614
670
|
return next(e);
|
|
@@ -621,8 +677,11 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
621
677
|
if (event === 'cmod.call' && call.to === definition.name && mod !== undefined) {
|
|
622
678
|
if (call.method === pendingStepsMethod)
|
|
623
679
|
return { value: await pendingSteps(mod) };
|
|
624
|
-
if (call.method === finishStepsMethod)
|
|
625
|
-
|
|
680
|
+
if (call.method === finishStepsMethod) {
|
|
681
|
+
const active = mod;
|
|
682
|
+
const opened = await installer.collect(() => Promise.race([runSteps(active).catch((error) => void toastFailure({ id: 'cmod:steps', message: 'Could not finish the setup', error })), installer.shown()]));
|
|
683
|
+
return { value: opened };
|
|
684
|
+
}
|
|
626
685
|
}
|
|
627
686
|
if (event === 'classic.UserPromptSubmit' || event === 'classic.SessionStart')
|
|
628
687
|
await refreshGrants().catch((error) => claude().ui.log(`${definition.name} keeps its grants from before: ${messageOf(error)}`, { to: 'debug' }));
|
|
@@ -638,7 +697,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
638
697
|
return answer;
|
|
639
698
|
return runtime.toaster.draw((await answer), runtime.claude.ui.resolve(render), render.props, render.requestId);
|
|
640
699
|
},
|
|
641
|
-
async caught(event, e, next, { kind, message }) {
|
|
700
|
+
async caught(event, e, next, { error: { kind, message }, called }) {
|
|
642
701
|
const call = e;
|
|
643
702
|
const isAsk = event === 'cmod.call' && call.to === definition.name && beforeAskOf(call) !== undefined;
|
|
644
703
|
const reasons = { timeout: `it ran past the ${hookMs / 1000} s Claude Code gives a hook`, throw: message ?? 'it threw' };
|
|
@@ -646,7 +705,9 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
646
705
|
if (reason === undefined)
|
|
647
706
|
return isAsk ? { value: null } : next(e);
|
|
648
707
|
const error = new Error(reason);
|
|
649
|
-
|
|
708
|
+
const failedBeneath = called && kind === 'throw' ? await next(e).then(() => undefined, messageOf) : undefined;
|
|
709
|
+
if (failedBeneath === undefined || failedBeneath !== message)
|
|
710
|
+
toastFailure({ id: `stopped:${event}`, message: `Claude Code stopped its ${event.replace(/^classic\./, '')} hook`, error });
|
|
650
711
|
return router.dispatchFailure(event, e, error, next);
|
|
651
712
|
},
|
|
652
713
|
};
|
|
@@ -689,7 +750,13 @@ async function createMod(definition, runtime) {
|
|
|
689
750
|
let parentAsked;
|
|
690
751
|
let hasEarlierPrompts = true;
|
|
691
752
|
let agentType;
|
|
692
|
-
|
|
753
|
+
let parts;
|
|
754
|
+
const borrowed = (styler, cached, input) => {
|
|
755
|
+
const name = `The markdown style of ${styler}`;
|
|
756
|
+
void parts?.fill(styler, cached, input, name)?.then(() => claude.ui.invalidate('ui.render'));
|
|
757
|
+
return parts?.element(styler, cached, `${styler}/markdown:`, name, `markdown:${styler}`);
|
|
758
|
+
};
|
|
759
|
+
const area = createUi({ name: definition.name, claude, router, report, announce, mod: () => mod, borrowed, nextDraw: runtime.nextDraw });
|
|
693
760
|
const askParent = async () => {
|
|
694
761
|
if (definition.name === cmodPluginName)
|
|
695
762
|
return undefined;
|
|
@@ -968,9 +1035,24 @@ async function createMod(definition, runtime) {
|
|
|
968
1035
|
const conversations = definition.name === cmodPluginName ? serveConversations({ claude, router, store: runtime.store, project: () => modState.root, report: (id, message, error) => report({ id, message, error }), clear: (id) => toaster.toast.dismiss(id) }) : undefined;
|
|
969
1036
|
let pages;
|
|
970
1037
|
const reportPart = (id, message, error) => report({ id, message, error });
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
1038
|
+
const pressElsewhere = (to, input) => void claude.cmod.call({ to, method: partMethods.press, input }).catch((error) => {
|
|
1039
|
+
parts?.forget(to);
|
|
1040
|
+
claude.ui.invalidate('ui.render');
|
|
1041
|
+
reportPart(`press:${to}`, `${to} did not take the press`, error);
|
|
1042
|
+
});
|
|
1043
|
+
parts = drawnParts({ claude, report: reportPart, press: (to, input) => (pages === undefined ? pressElsewhere(to, input) : pages.press(to, input)) });
|
|
1044
|
+
const widgets = conversations === undefined ? undefined : serveWidgets({ claude, router, store: runtime.store, report: reportPart, turnClosedBy: (rowId) => conversations.turnClosedBy(rowId), parts, markdownIn: area.markdownIn });
|
|
1045
|
+
pages = widgets === undefined ? undefined : servePages({ claude, router, report: reportPart, widgets, parts, tellStylers: area.restyled });
|
|
1046
|
+
const tellColor = (to, told) => {
|
|
1047
|
+
if (told.session === '' || to === cmodPluginName)
|
|
1048
|
+
return;
|
|
1049
|
+
claude.clock.after(0, () => void claude.cmod.call({ to, method: conversationMethods.sessionColor, input: told }).catch((error) => claude.ui.log(`cmod: ${to} did not hear the session's color: ${messageOf(error)}`, { to: 'debug' })));
|
|
1050
|
+
};
|
|
1051
|
+
conversations?.onColor((told) => {
|
|
1052
|
+
client.recolor(told);
|
|
1053
|
+
for (const { mod: to } of pages?.declared() ?? [])
|
|
1054
|
+
tellColor(to, told);
|
|
1055
|
+
});
|
|
974
1056
|
router.add('cmod.call', async (e, next) => {
|
|
975
1057
|
if (e.to !== definition.name)
|
|
976
1058
|
return next(e);
|
|
@@ -982,17 +1064,24 @@ async function createMod(definition, runtime) {
|
|
|
982
1064
|
client.changed(changedSession, change, isInProject !== false);
|
|
983
1065
|
return { value: null };
|
|
984
1066
|
}
|
|
1067
|
+
if (e.method === conversationMethods.sessionColor && call.caller === cmodPluginName) {
|
|
1068
|
+
client.recolor(e.input);
|
|
1069
|
+
return { value: null };
|
|
1070
|
+
}
|
|
985
1071
|
const ask = beforeAskOf(call);
|
|
986
1072
|
if (ask !== undefined)
|
|
987
1073
|
return { value: (await client.answerBefore(ask.session, ask.event, ask.change, ask.isInProject)) ?? null };
|
|
988
1074
|
if (e.method === partMethods.changed && call.caller !== undefined && pages !== undefined) {
|
|
989
|
-
|
|
1075
|
+
const update = e.input;
|
|
1076
|
+
if (update.parts !== undefined && conversations !== undefined)
|
|
1077
|
+
tellColor(call.caller, conversations.color());
|
|
1078
|
+
return { value: { isHeld: pages.update(call.caller, update), stylers: pages.stylers() } };
|
|
990
1079
|
}
|
|
991
|
-
if (call.caller
|
|
1080
|
+
if (call.caller !== undefined) {
|
|
992
1081
|
const turn = e.method === partMethods.draw ? e.input.turn : undefined;
|
|
993
|
-
const answered = area.answer(e.method, turn === undefined ? e.input : { ...e.input, turn: client.turn(turn) });
|
|
1082
|
+
const answered = area.answer(e.method, turn === undefined ? e.input : { ...e.input, turn: client.turn(turn) }, call.caller);
|
|
994
1083
|
if (answered !== undefined)
|
|
995
|
-
return answered;
|
|
1084
|
+
return answered.catch((error) => ({ deny: `${definition.name}: ${messageOf(error)}` }));
|
|
996
1085
|
}
|
|
997
1086
|
const served = await conversations?.answer(call);
|
|
998
1087
|
return served ?? answerCall(definition.name, api, e);
|
|
@@ -1003,7 +1092,20 @@ async function createMod(definition, runtime) {
|
|
|
1003
1092
|
return ask === undefined ? { deny: `${definition.name}: ${error.message}` } : { value: client.failedBefore(ask.session, ask.event, ask.change, ask.isInProject, error) ?? null };
|
|
1004
1093
|
});
|
|
1005
1094
|
const agents = toolCalls(claude, router);
|
|
1006
|
-
keepEngine(mod, {
|
|
1095
|
+
keepEngine(mod, {
|
|
1096
|
+
claude: checked,
|
|
1097
|
+
router,
|
|
1098
|
+
toolCalls: agents,
|
|
1099
|
+
report,
|
|
1100
|
+
hasPermissionCheck: () => runtime.permissionRoutes() !== undefined,
|
|
1101
|
+
answerInput: area.answerInput,
|
|
1102
|
+
spawnCmod: async (args, program) => {
|
|
1103
|
+
await checkRun(grants, 'process.spawn', program.argv);
|
|
1104
|
+
return spawnCmod(claude, args, session, program.env);
|
|
1105
|
+
},
|
|
1106
|
+
...(widgets === undefined ? {} : { widgets }),
|
|
1107
|
+
...(pages === undefined ? {} : { pages }),
|
|
1108
|
+
});
|
|
1007
1109
|
const names = new Set();
|
|
1008
1110
|
const reserveName = (type, name, taken) => {
|
|
1009
1111
|
const key = `${type}:${name}`;
|
package/runtime/pages.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type ModPages = {
|
|
|
11
11
|
export type PageApply = (mod: string, requests: readonly PageRequest[]) => Promise<void>;
|
|
12
12
|
export type Pages = {
|
|
13
13
|
declared(): readonly ModPages[];
|
|
14
|
+
stylers(): readonly string[];
|
|
14
15
|
element(mod: string, page: string, params: unknown, columns: number, isFocused: boolean): RenderElement | undefined;
|
|
15
16
|
press(mod: string, press: PartPress): void;
|
|
16
17
|
update(mod: string, update: PartsUpdate): boolean;
|
|
@@ -18,11 +19,12 @@ export type Pages = {
|
|
|
18
19
|
apply(listener: PageApply): void;
|
|
19
20
|
};
|
|
20
21
|
export declare function pagesOf(input: unknown): PageDeclaration[];
|
|
21
|
-
export declare function servePages({ claude, router, report, widgets, parts }: {
|
|
22
|
+
export declare function servePages({ claude, router, report, widgets, parts, tellStylers, }: {
|
|
22
23
|
readonly claude: Claude;
|
|
23
24
|
readonly router: Router;
|
|
24
25
|
readonly report: (id: string, message: string, error: unknown) => void;
|
|
25
26
|
readonly widgets: Pick<Widgets, 'update'>;
|
|
26
27
|
readonly parts: DrawnParts;
|
|
28
|
+
readonly tellStylers: (stylers: readonly string[]) => void;
|
|
27
29
|
}): Pages;
|
|
28
30
|
//# sourceMappingURL=pages.d.ts.map
|
package/runtime/pages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../src/runtime/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../src/runtime/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA2B,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAC5I,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAI3C,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAA;AAE3F,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAExF,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,IAAI,SAAS,QAAQ,EAAE,CAAA;IAC/B,OAAO,IAAI,SAAS,MAAM,EAAE,CAAA;IAC5B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAA;IACnH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAA;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC1C,KAAK,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,EAAE,CAGzD;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,MAAM,EACN,OAAO,EACP,KAAK,EACL,WAAW,GACZ,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAA;CAC3D,GAAG,KAAK,CA4HR"}
|
package/runtime/pages.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { isObject } from '../records.js';
|
|
2
|
+
import { Text } from '../ui/elements.js';
|
|
2
3
|
import { messageOf } from '../utils/text.js';
|
|
3
|
-
import {
|
|
4
|
+
import { cmodPluginName } from './dependencies.js';
|
|
5
|
+
import { partMethods, stalePress } from './ui.js';
|
|
4
6
|
const askedWaitMs = 1000;
|
|
5
7
|
export function pagesOf(input) {
|
|
6
8
|
const listed = isObject(input) && Array.isArray(input['pages']) ? input['pages'] : [];
|
|
7
9
|
return listed.filter(isObject).flatMap((each) => (typeof each['id'] === 'string' && typeof each['title'] === 'string' && (each['place'] === 'sidebar' || each['place'] === 'settings') ? [{ id: each['id'], title: each['title'], place: each['place'] }] : []));
|
|
8
10
|
}
|
|
9
|
-
export function servePages({ claude, router, report, widgets, parts }) {
|
|
11
|
+
export function servePages({ claude, router, report, widgets, parts, tellStylers, }) {
|
|
10
12
|
const declared = new Map();
|
|
13
|
+
const stylers = new Set();
|
|
11
14
|
const asking = new Map();
|
|
12
15
|
const pressing = new Set();
|
|
13
16
|
let waiting = [];
|
|
@@ -20,16 +23,18 @@ export function servePages({ claude, router, report, widgets, parts }) {
|
|
|
20
23
|
for (const { mod, requests } of taken)
|
|
21
24
|
await run(mod, requests);
|
|
22
25
|
};
|
|
23
|
-
const settle = async () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const settle = async (earlier) => {
|
|
27
|
+
const own = () => [...pressing].filter((pending) => !earlier.has(pending));
|
|
28
|
+
for (let pending = own(); pending.length > 0; pending = own())
|
|
29
|
+
await Promise.allSettled(pending);
|
|
26
30
|
await takeWaiting();
|
|
27
31
|
};
|
|
28
32
|
const wrapped = async (next) => {
|
|
29
33
|
inputDepth += 1;
|
|
34
|
+
const earlier = new Set(pressing);
|
|
30
35
|
try {
|
|
31
36
|
const answer = await next();
|
|
32
|
-
await settle();
|
|
37
|
+
await settle(earlier);
|
|
33
38
|
return answer;
|
|
34
39
|
}
|
|
35
40
|
finally {
|
|
@@ -47,11 +52,27 @@ export function servePages({ claude, router, report, widgets, parts }) {
|
|
|
47
52
|
asking.set(mod, pending);
|
|
48
53
|
return pending;
|
|
49
54
|
};
|
|
55
|
+
const restyle = (mod, isStyler) => {
|
|
56
|
+
if (!isStyler && !stylers.delete(mod))
|
|
57
|
+
return;
|
|
58
|
+
if (isStyler)
|
|
59
|
+
stylers.add(mod);
|
|
60
|
+
parts.forget(mod);
|
|
61
|
+
const listed = [...stylers];
|
|
62
|
+
tellStylers(listed);
|
|
63
|
+
for (const told of declared.keys()) {
|
|
64
|
+
if (told !== cmodPluginName)
|
|
65
|
+
void claude.cmod.call({ to: told, method: partMethods.stylers, input: listed }).catch((error) => claude.ui.log(`cmod: ${told} did not hear which mods style markdown: ${messageOf(error)}`, { to: 'debug' }));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
50
68
|
const update = (mod, { parts: listing, redraw, requests, isAsked }) => {
|
|
51
69
|
if (listing !== undefined)
|
|
52
70
|
declared.set(mod, pagesOf(listing));
|
|
53
|
-
if (listing !== undefined
|
|
54
|
-
|
|
71
|
+
if (listing !== undefined)
|
|
72
|
+
restyle(mod, isObject(listing) && listing['stylesMarkdown'] === true);
|
|
73
|
+
const isRedrawn = redraw === true || requests?.some((request) => 'open' in request) === true;
|
|
74
|
+
if (listing !== undefined || isRedrawn)
|
|
75
|
+
widgets.update(mod, { ...(listing === undefined ? {} : { parts: listing }), ...(isRedrawn ? { redraw: true } : {}) });
|
|
55
76
|
if (requests === undefined || requests.length === 0)
|
|
56
77
|
return false;
|
|
57
78
|
if (isAsked !== true)
|
|
@@ -68,28 +89,41 @@ export function servePages({ claude, router, report, widgets, parts }) {
|
|
|
68
89
|
.then((answer) => void update(mod, (isObject(answer) ? answer : {})), (error) => {
|
|
69
90
|
parts.forget(mod);
|
|
70
91
|
claude.ui.invalidate('ui.render');
|
|
71
|
-
|
|
92
|
+
if (messageOf(error).includes(stalePress))
|
|
93
|
+
claude.ui.log(`cmod: ${mod} draws its page again, because the button pressed was from an older drawing:${messageOf(error)}`, { to: 'debug' });
|
|
94
|
+
else
|
|
95
|
+
report(`press:${mod}`, `${mod} did not take the press`, error);
|
|
72
96
|
})
|
|
73
97
|
.finally(() => pressing.delete(pending));
|
|
74
98
|
pressing.add(pending);
|
|
75
99
|
if (inputDepth === 0)
|
|
76
100
|
void pending.then(takeWaiting);
|
|
77
101
|
};
|
|
78
|
-
const
|
|
102
|
+
const lastDrawn = new Map();
|
|
103
|
+
const plain = (data) => JSON.parse(JSON.stringify(data));
|
|
79
104
|
return {
|
|
80
105
|
declared: () => [...declared].map(([mod, pages]) => ({ mod, pages })),
|
|
106
|
+
stylers: () => [...stylers],
|
|
81
107
|
element(mod, page, params, columns, isFocused) {
|
|
82
108
|
if (!declared.has(mod))
|
|
83
109
|
void ask(mod);
|
|
84
|
-
const
|
|
110
|
+
const given = JSON.stringify(params ?? null);
|
|
111
|
+
const cached = `${page}\u0000${given}\u0000${columns}\u0000${isFocused}`;
|
|
85
112
|
const name = `The ${page} page of ${mod}`;
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
const keyPrefix = `${mod}/${page}:`;
|
|
114
|
+
void parts.fill(mod, cached, { page, params: plain(params ?? null), columns, isFocused }, name)?.then(() => claude.ui.invalidate('ui.render'));
|
|
115
|
+
const failure = parts.failure(mod, cached);
|
|
116
|
+
const drawnAt = (key) => parts.element(mod, key, keyPrefix, name, `page:${mod}/${page}`);
|
|
117
|
+
const last = lastDrawn.get(keyPrefix);
|
|
118
|
+
const drawn = drawnAt(cached);
|
|
119
|
+
if (drawn !== undefined)
|
|
120
|
+
lastDrawn.set(keyPrefix, { params: given, cached });
|
|
121
|
+
return drawn ?? (last?.params === given ? drawnAt(last.cached) : undefined) ?? (failure === undefined ? undefined : Text({ color: 'error', children: `${name} could not draw: ${failure}` }));
|
|
88
122
|
},
|
|
89
123
|
press,
|
|
90
124
|
update,
|
|
91
125
|
tell(mod, shown) {
|
|
92
|
-
void claude.cmod.call({ to: mod, method: partMethods.shown, input: shown }).catch((error) => claude.ui.log(`cmod: ${mod} did not hear which of its pages show: ${messageOf(error)}`, { to: 'debug' }));
|
|
126
|
+
void claude.cmod.call({ to: mod, method: partMethods.shown, input: plain(shown) }).catch((error) => claude.ui.log(`cmod: ${mod} did not hear which of its pages show: ${messageOf(error)}`, { to: 'debug' }));
|
|
93
127
|
},
|
|
94
128
|
apply(listener) {
|
|
95
129
|
applyTo = listener;
|
package/runtime/parts.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type PartPress } from './ui.js';
|
|
|
4
4
|
export type DrawnParts = {
|
|
5
5
|
fill(mod: string, cached: string, input: Readonly<Record<string, unknown>>, name: string): Promise<void> | undefined;
|
|
6
6
|
element(mod: string, cached: string, keyPrefix: string, name: string, reportId: string): RenderElement | undefined;
|
|
7
|
+
failure(mod: string, cached: string): string | undefined;
|
|
7
8
|
forget(mod: string): void;
|
|
8
9
|
};
|
|
9
10
|
export declare function drawnParts({ claude, report, press }: {
|
package/runtime/parts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../src/runtime/parts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAA;IAClH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAMD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../src/runtime/parts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAA;IAClH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACxD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAMD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,GAAG,UAAU,CAwD9N"}
|
package/runtime/parts.js
CHANGED
|
@@ -14,9 +14,9 @@ export function drawnParts({ claude, report, press }) {
|
|
|
14
14
|
const generation = generationOf(mod);
|
|
15
15
|
if (drawn.get(key)?.generation === generation)
|
|
16
16
|
return undefined;
|
|
17
|
-
const keep = (data) => {
|
|
17
|
+
const keep = (data, failure) => {
|
|
18
18
|
drawn.delete(key);
|
|
19
|
-
drawn.set(key, { data, generation });
|
|
19
|
+
drawn.set(key, { data, ...(failure === undefined ? {} : { failure }), generation });
|
|
20
20
|
const [oldest] = drawn.keys();
|
|
21
21
|
if (drawn.size > keptDrawings && oldest !== undefined)
|
|
22
22
|
drawn.delete(oldest);
|
|
@@ -28,7 +28,7 @@ export function drawnParts({ claude, report, press }) {
|
|
|
28
28
|
.call({ to: mod, method: partMethods.draw, input })
|
|
29
29
|
.then((data) => keep((data ?? null)), (error) => {
|
|
30
30
|
claude.ui.log(`cmod: ${name} drew nothing: ${messageOf(error)}`, { to: 'debug' });
|
|
31
|
-
keep(null);
|
|
31
|
+
keep(drawn.get(key)?.data ?? null, messageOf(error));
|
|
32
32
|
})
|
|
33
33
|
.finally(() => drawing.delete(key));
|
|
34
34
|
drawing.set(key, pending);
|
|
@@ -52,6 +52,9 @@ export function drawnParts({ claude, report, press }) {
|
|
|
52
52
|
return undefined;
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
+
failure(mod, cached) {
|
|
56
|
+
return drawn.get(keyOf(mod, cached))?.failure;
|
|
57
|
+
},
|
|
55
58
|
forget(mod) {
|
|
56
59
|
generations.set(mod, generationOf(mod) + 1);
|
|
57
60
|
},
|
package/runtime/programs.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare function readLines(stream: HookStream<ProcessSpawnChunk, ProcessS
|
|
|
10
10
|
code: number | null;
|
|
11
11
|
lastError: string;
|
|
12
12
|
}>;
|
|
13
|
+
export declare function spawnCmod(claude: Claude, args: readonly string[], session: string, env?: Readonly<Record<string, string>>): HookStream<ProcessSpawnChunk, ProcessSpawnResult>;
|
|
13
14
|
export declare function runCmod(claude: Claude, args: readonly string[], session: string): Promise<string>;
|
|
14
15
|
export declare function locatingPrograms(claude: Claude): Programs;
|
|
15
16
|
//# sourceMappingURL=programs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../src/runtime/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAIpF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAE5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI7E;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9K;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAsBzD"}
|
|
1
|
+
{"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../src/runtime/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAIpF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAE5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI7E;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9K;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAAG,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAEjL;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAsBzD"}
|
package/runtime/programs.js
CHANGED
|
@@ -30,9 +30,12 @@ export async function readLines(stream, onLine) {
|
|
|
30
30
|
onLine(pending);
|
|
31
31
|
return { code: piece.value.code, lastError };
|
|
32
32
|
}
|
|
33
|
+
export function spawnCmod(claude, args, session, env = {}) {
|
|
34
|
+
return claude.process.spawn({ argv: ['cmod', ...args], env: { ...env, CLAUDE_CODE_SESSION_ID: session } });
|
|
35
|
+
}
|
|
33
36
|
export async function runCmod(claude, args, session) {
|
|
34
37
|
const lines = [];
|
|
35
|
-
const { code, lastError } = await readLines(claude
|
|
38
|
+
const { code, lastError } = await readLines(spawnCmod(claude, args, session), (line) => void lines.push(line));
|
|
36
39
|
if (code !== 0)
|
|
37
40
|
throw new Error(cmodError(lastError, args[0]) || `cmod ${args.slice(0, 2).join(' ')} ${formatExit(code, '')}.`);
|
|
38
41
|
return lines.join('\n');
|
package/runtime/sessions.d.ts
CHANGED
|
@@ -30,8 +30,14 @@ export declare const conversationMethods: {
|
|
|
30
30
|
readonly act: 'cmod:act';
|
|
31
31
|
readonly changed: 'cmod:changed';
|
|
32
32
|
readonly before: 'cmod:before';
|
|
33
|
+
readonly sessionColor: 'cmod:sessionColor';
|
|
33
34
|
};
|
|
34
|
-
export type
|
|
35
|
+
export type SessionColor = {
|
|
36
|
+
readonly session: string;
|
|
37
|
+
readonly color?: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function pickedColor(args: string): string | undefined;
|
|
40
|
+
export type Act = 'send' | 'startTask' | 'fork' | 'clone' | 'startBackgroundTask' | 'callTool' | 'usage' | 'surfaces' | 'abort';
|
|
35
41
|
export declare const beforePermission: (event: BeforeEvent) => string;
|
|
36
42
|
export declare const isAct: (action: string) => action is Act;
|
|
37
43
|
export declare function toolPermission(tool: string): string | undefined;
|
|
@@ -55,7 +61,8 @@ export type SessionsClient = {
|
|
|
55
61
|
listen(method: string, args: readonly unknown[]): Subscription;
|
|
56
62
|
turn(data: TurnData): Turn;
|
|
57
63
|
/** An empty title keeps the title the session already has. */
|
|
58
|
-
follow(current: Omit<SessionData, 'isRunning' | 'lastAt'>): void;
|
|
64
|
+
follow(current: Omit<SessionData, 'isRunning' | 'lastAt' | 'color'>): void;
|
|
65
|
+
recolor(told: SessionColor): void;
|
|
59
66
|
};
|
|
60
67
|
export declare function usageOf(claude: Pick<Claude, 'session'>): Promise<Usage>;
|
|
61
68
|
export declare function payloadOf(change: SessionChange): unknown;
|
|
@@ -90,7 +97,7 @@ export type RouteOn = {
|
|
|
90
97
|
export declare const recordOf: (value: unknown) => Readonly<Record<string, unknown>>;
|
|
91
98
|
export declare const defined: (fields: HookFields) => HookFields;
|
|
92
99
|
type ProcessFieldsName = Exclude<ProcessName, 'PermissionCheck'>;
|
|
93
|
-
export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs): Promise<TaskData>;
|
|
100
|
+
export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs, forkedFrom?: string): Promise<TaskData>;
|
|
94
101
|
export declare function processPayloadOf(named: ProcessFieldsName, input: unknown): HookFields;
|
|
95
102
|
export declare function resultOf(where: Pick<Place, 'session' | 'context' | 'turn'>, call: ToolCallArgs & {
|
|
96
103
|
readonly cwd: string;
|