@agentshouse/kit 0.2.1-alpha.9 → 0.2.1-c073f4e9391b2
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/README.md +44 -26
- package/dist/abort.js +9 -0
- package/dist/acp.js +18 -75
- package/dist/agents.js +266 -53
- package/dist/api.js +40 -4
- package/dist/app-git.js +64 -0
- package/dist/bootstrap.json +10 -14
- package/dist/bridge.js +89 -71
- package/dist/chatgpt.js +25 -0
- package/dist/clis-main.js +23 -0
- package/dist/clis.js +203 -71
- package/dist/codex-main.js +58 -0
- package/dist/conversations.js +328 -199
- package/dist/copies.js +188 -161
- package/dist/end.js +22 -0
- package/dist/enrol-main.js +7 -1
- package/dist/events.js +521 -0
- package/dist/files.js +16 -1
- package/dist/git-main.js +2 -2
- package/dist/git.js +6 -2
- package/dist/home.js +72 -5
- package/dist/house-main.js +181 -57
- package/dist/instructions.js +16 -13
- package/dist/kit-main.js +55 -4
- package/dist/login.js +108 -97
- package/dist/logout.js +21 -0
- package/dist/mcp.js +39 -0
- package/dist/operation.js +7 -4
- package/dist/parts.js +82 -0
- package/dist/placement.js +48 -0
- package/dist/refusals.js +53 -0
- package/dist/relay.js +20 -0
- package/dist/resident.js +70 -34
- package/dist/scope.js +203 -0
- package/dist/secret-input.js +7 -2
- package/dist/service-main.js +9 -0
- package/dist/service.js +33 -0
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +48 -11
- package/dist/skills.js +5 -10
- package/dist/stream.js +37 -7
- package/dist/transcription.js +170 -0
- package/dist/update.js +140 -0
- package/dist/wire.js +6 -0
- package/dist/work.js +348 -0
- package/how-we-work.md +10 -12
- package/package.json +4 -7
- package/dist/blocks.js +0 -51
package/dist/work.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { mkdir, rm } from 'node:fs/promises';
|
|
5
|
+
import { createServer } from 'node:net';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { RUNNING_JOB } from './events.js';
|
|
8
|
+
import { kitHome } from './home.js';
|
|
9
|
+
const ENDED_TASK = new Set(['completed', 'failed', 'killed']);
|
|
10
|
+
const SCHEDULING = new Set(['CronCreate', 'CronDelete', 'ScheduleWakeup']);
|
|
11
|
+
const QUEUE_LIST = 'house-kit-queue-';
|
|
12
|
+
const SYNC = 'house-kit-sync-';
|
|
13
|
+
const WAKEUP = /Next wakeup scheduled for (\d\d):(\d\d):(\d\d) \(in (\d+)s\)/;
|
|
14
|
+
const HALF_DAY = 12 * 60 * 60 * 1000;
|
|
15
|
+
export function codexLauncher() {
|
|
16
|
+
return join(kitHome(), 'launchers', 'codex');
|
|
17
|
+
}
|
|
18
|
+
export class Work {
|
|
19
|
+
changed;
|
|
20
|
+
jobs = new Set();
|
|
21
|
+
constructor(changed) {
|
|
22
|
+
this.changed = changed;
|
|
23
|
+
}
|
|
24
|
+
async open(_cli) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
async settled() { }
|
|
28
|
+
read(message) {
|
|
29
|
+
this.take(message);
|
|
30
|
+
this.changed();
|
|
31
|
+
}
|
|
32
|
+
take(message) {
|
|
33
|
+
const update = message.params?.update;
|
|
34
|
+
if (message.method !== 'session/update' || update?.asyncTaskId === undefined)
|
|
35
|
+
return;
|
|
36
|
+
if (update.sessionUpdate === 'async_task_spawned')
|
|
37
|
+
this.jobs.add(update.asyncTaskId);
|
|
38
|
+
if (update.sessionUpdate === 'async_task_state_update' && !RUNNING_JOB.has(update.state))
|
|
39
|
+
this.jobs.delete(update.asyncTaskId);
|
|
40
|
+
}
|
|
41
|
+
busy() {
|
|
42
|
+
return this.jobs.size > 0;
|
|
43
|
+
}
|
|
44
|
+
close() { }
|
|
45
|
+
}
|
|
46
|
+
class ClaudeWork extends Work {
|
|
47
|
+
cwd;
|
|
48
|
+
level = new Set();
|
|
49
|
+
listed = new Set();
|
|
50
|
+
started = new Set();
|
|
51
|
+
running = false;
|
|
52
|
+
hooks = new Set();
|
|
53
|
+
calls = new Map();
|
|
54
|
+
crons = new Map();
|
|
55
|
+
wakeups = [];
|
|
56
|
+
goal = false;
|
|
57
|
+
constructor(cwd, changed) {
|
|
58
|
+
super(changed);
|
|
59
|
+
this.cwd = cwd;
|
|
60
|
+
}
|
|
61
|
+
take(message) {
|
|
62
|
+
super.take(message);
|
|
63
|
+
if (message.method === '_claude/sdkMessage')
|
|
64
|
+
this.sdk(message.params.message);
|
|
65
|
+
if (message.method === 'session/update')
|
|
66
|
+
this.update(message.params.update);
|
|
67
|
+
}
|
|
68
|
+
busy() {
|
|
69
|
+
return (super.busy() ||
|
|
70
|
+
this.level.size > 0 ||
|
|
71
|
+
this.started.size > 0 ||
|
|
72
|
+
this.running ||
|
|
73
|
+
this.hooks.size > 0 ||
|
|
74
|
+
this.crons.size > 0 ||
|
|
75
|
+
this.wakeups.length > 0 ||
|
|
76
|
+
this.goal ||
|
|
77
|
+
this.durable());
|
|
78
|
+
}
|
|
79
|
+
sdk(message) {
|
|
80
|
+
switch (message.subtype) {
|
|
81
|
+
case 'background_tasks_changed':
|
|
82
|
+
this.level = new Set(message.tasks.map((task) => task.task_id));
|
|
83
|
+
for (const id of this.level) {
|
|
84
|
+
this.listed.add(id);
|
|
85
|
+
this.started.delete(id);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case 'task_started':
|
|
89
|
+
if (!this.listed.has(message.task_id))
|
|
90
|
+
this.started.add(message.task_id);
|
|
91
|
+
break;
|
|
92
|
+
case 'task_updated':
|
|
93
|
+
if (ENDED_TASK.has(message.patch.status))
|
|
94
|
+
this.started.delete(message.task_id);
|
|
95
|
+
break;
|
|
96
|
+
case 'task_notification':
|
|
97
|
+
this.started.delete(message.task_id);
|
|
98
|
+
break;
|
|
99
|
+
case 'session_state_changed':
|
|
100
|
+
if (message.state === 'running' && !this.running)
|
|
101
|
+
this.wakeups = this.wakeups.filter((due) => due > Date.now());
|
|
102
|
+
this.running = message.state === 'running';
|
|
103
|
+
break;
|
|
104
|
+
case 'hook_started':
|
|
105
|
+
this.hooks.add(message.hook_id);
|
|
106
|
+
break;
|
|
107
|
+
case 'hook_response':
|
|
108
|
+
this.hooks.delete(message.hook_id);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
update(update) {
|
|
113
|
+
const air = update._meta?.jetbrains?.air;
|
|
114
|
+
if (update.sessionUpdate === 'session_info_update' && air !== undefined && 'goal' in air) {
|
|
115
|
+
this.goal = air.goal?.status === 'active';
|
|
116
|
+
}
|
|
117
|
+
if (update.sessionUpdate !== 'tool_call' && update.sessionUpdate !== 'tool_call_update')
|
|
118
|
+
return;
|
|
119
|
+
const tool = update._meta?.claudeCode?.toolName;
|
|
120
|
+
if (update.sessionUpdate === 'tool_call' && SCHEDULING.has(tool ?? ''))
|
|
121
|
+
this.calls.set(update.toolCallId, { tool });
|
|
122
|
+
const call = this.calls.get(update.toolCallId);
|
|
123
|
+
if (call === undefined)
|
|
124
|
+
return;
|
|
125
|
+
if (update.rawInput !== undefined)
|
|
126
|
+
call.input = update.rawInput;
|
|
127
|
+
if (update.content !== undefined)
|
|
128
|
+
call.content = update.content;
|
|
129
|
+
if (update.status === 'failed')
|
|
130
|
+
this.calls.delete(update.toolCallId);
|
|
131
|
+
if (update.status !== 'completed')
|
|
132
|
+
return;
|
|
133
|
+
this.calls.delete(update.toolCallId);
|
|
134
|
+
if (call.tool === 'CronCreate')
|
|
135
|
+
this.crons.set(update.toolCallId, JSON.stringify(call.content ?? []));
|
|
136
|
+
if (call.tool === 'ScheduleWakeup')
|
|
137
|
+
this.wakeup(call);
|
|
138
|
+
if (call.tool !== 'CronDelete')
|
|
139
|
+
return;
|
|
140
|
+
for (const [id, text] of this.crons)
|
|
141
|
+
if (text.includes(String(call.input?.id)))
|
|
142
|
+
this.crons.delete(id);
|
|
143
|
+
}
|
|
144
|
+
wakeup(call) {
|
|
145
|
+
if (call.input?.stop === true) {
|
|
146
|
+
this.wakeups = [];
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const time = WAKEUP.exec(JSON.stringify(call.content ?? []));
|
|
150
|
+
if (time === null) {
|
|
151
|
+
this.wakeups.push(Infinity);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const fires = Date.now() + Number(time[4]) * 1000;
|
|
155
|
+
const due = new Date(fires);
|
|
156
|
+
due.setHours(Number(time[1]), Number(time[2]), Number(time[3]), 0);
|
|
157
|
+
if (due.getTime() - fires > HALF_DAY)
|
|
158
|
+
due.setDate(due.getDate() - 1);
|
|
159
|
+
if (fires - due.getTime() > HALF_DAY)
|
|
160
|
+
due.setDate(due.getDate() + 1);
|
|
161
|
+
this.wakeups.push(due.getTime());
|
|
162
|
+
}
|
|
163
|
+
durable() {
|
|
164
|
+
let text;
|
|
165
|
+
try {
|
|
166
|
+
text = readFileSync(join(this.cwd, '.claude', 'scheduled_tasks.json'), 'utf8');
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return error.code !== 'ENOENT';
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(text).tasks?.length !== 0;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
class CodexWork extends Work {
|
|
180
|
+
threads = new Map();
|
|
181
|
+
queues = new Map();
|
|
182
|
+
goals = new Set();
|
|
183
|
+
items = new Set();
|
|
184
|
+
hooks = new Set();
|
|
185
|
+
starting = new Set();
|
|
186
|
+
settling = [];
|
|
187
|
+
memories = null;
|
|
188
|
+
lost = false;
|
|
189
|
+
server = null;
|
|
190
|
+
path = null;
|
|
191
|
+
socket = null;
|
|
192
|
+
asked = 0;
|
|
193
|
+
heard = 0;
|
|
194
|
+
proving = -1;
|
|
195
|
+
proven = -1;
|
|
196
|
+
async open(cli) {
|
|
197
|
+
const sockets = join(kitHome(), 'bridges');
|
|
198
|
+
await mkdir(sockets, { recursive: true, mode: 0o700 });
|
|
199
|
+
this.path = join(sockets, `${randomUUID()}.sock`);
|
|
200
|
+
this.server = createServer((socket) => this.connected(socket));
|
|
201
|
+
this.server.listen(this.path);
|
|
202
|
+
await once(this.server, 'listening');
|
|
203
|
+
return { CODEX_PATH: codexLauncher(), HOUSE_KIT_CODEX: cli, HOUSE_KIT_CODEX_STREAM: this.path };
|
|
204
|
+
}
|
|
205
|
+
settled() {
|
|
206
|
+
return new Promise((resolve) => {
|
|
207
|
+
this.settling.push(resolve);
|
|
208
|
+
this.settle();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
take(message) {
|
|
212
|
+
super.take(message);
|
|
213
|
+
this.heard++;
|
|
214
|
+
}
|
|
215
|
+
busy() {
|
|
216
|
+
if (super.busy() ||
|
|
217
|
+
this.lost ||
|
|
218
|
+
this.memories !== false ||
|
|
219
|
+
[...this.threads.values()].some(Boolean) ||
|
|
220
|
+
this.queues.size > 0 ||
|
|
221
|
+
this.goals.size > 0 ||
|
|
222
|
+
this.items.size > 0 ||
|
|
223
|
+
this.hooks.size > 0) {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
if (this.proven === this.heard)
|
|
227
|
+
return false;
|
|
228
|
+
this.prove();
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
close() {
|
|
232
|
+
this.server?.close();
|
|
233
|
+
if (this.path !== null)
|
|
234
|
+
void rm(this.path, { force: true });
|
|
235
|
+
}
|
|
236
|
+
connected(socket) {
|
|
237
|
+
this.socket = socket;
|
|
238
|
+
let buffer = '';
|
|
239
|
+
socket.on('error', () => this.lose());
|
|
240
|
+
socket.on('close', () => this.lose());
|
|
241
|
+
socket.setEncoding('utf8').on('data', (chunk) => {
|
|
242
|
+
buffer += chunk;
|
|
243
|
+
for (let index = buffer.indexOf('\n'); index >= 0; index = buffer.indexOf('\n')) {
|
|
244
|
+
const message = this.parsed(buffer.slice(0, index));
|
|
245
|
+
buffer = buffer.slice(index + 1);
|
|
246
|
+
if (message !== null)
|
|
247
|
+
this.stream(message, socket);
|
|
248
|
+
}
|
|
249
|
+
this.settle();
|
|
250
|
+
this.changed();
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
parsed(line) {
|
|
254
|
+
try {
|
|
255
|
+
return JSON.parse(line);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
this.lost = true;
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
lose() {
|
|
263
|
+
this.lost = true;
|
|
264
|
+
this.changed();
|
|
265
|
+
}
|
|
266
|
+
prove() {
|
|
267
|
+
if (this.proving === this.heard)
|
|
268
|
+
return;
|
|
269
|
+
this.proving = this.heard;
|
|
270
|
+
this.socket.write(`${JSON.stringify({ jsonrpc: '2.0', id: `${SYNC}${this.heard}`, method: 'house-kit/sync' })}\n`);
|
|
271
|
+
}
|
|
272
|
+
settle() {
|
|
273
|
+
if (this.starting.size === 0)
|
|
274
|
+
for (const resolve of this.settling.splice(0))
|
|
275
|
+
resolve();
|
|
276
|
+
}
|
|
277
|
+
stream(message, socket) {
|
|
278
|
+
const params = message.params;
|
|
279
|
+
switch (message.method) {
|
|
280
|
+
case 'thread/status/changed':
|
|
281
|
+
this.threads.set(params.threadId, params.status.type === 'active' && params.status.activeFlags.length === 0);
|
|
282
|
+
break;
|
|
283
|
+
case 'thread/closed':
|
|
284
|
+
this.threads.delete(params.threadId);
|
|
285
|
+
break;
|
|
286
|
+
case 'thread/queue/changed':
|
|
287
|
+
this.queues.set(params.threadId, ++this.asked);
|
|
288
|
+
socket.write(`${JSON.stringify({ jsonrpc: '2.0', id: `${QUEUE_LIST}${this.asked}`, method: 'thread/queue/list', params: { threadId: params.threadId } })}\n`);
|
|
289
|
+
break;
|
|
290
|
+
case 'thread/goal/updated':
|
|
291
|
+
if (params.goal.status === 'active')
|
|
292
|
+
this.goals.add(params.threadId);
|
|
293
|
+
else
|
|
294
|
+
this.goals.delete(params.threadId);
|
|
295
|
+
break;
|
|
296
|
+
case 'thread/goal/cleared':
|
|
297
|
+
this.goals.delete(params.threadId);
|
|
298
|
+
break;
|
|
299
|
+
case 'item/started':
|
|
300
|
+
this.items.add(`${params.threadId} ${params.item.id}`);
|
|
301
|
+
break;
|
|
302
|
+
case 'item/completed':
|
|
303
|
+
this.items.delete(`${params.threadId} ${params.item.id}`);
|
|
304
|
+
break;
|
|
305
|
+
case 'hook/started':
|
|
306
|
+
this.hooks.add(params.run.id);
|
|
307
|
+
break;
|
|
308
|
+
case 'hook/completed':
|
|
309
|
+
this.hooks.delete(params.run.id);
|
|
310
|
+
break;
|
|
311
|
+
case 'mcpServer/startupStatus/updated':
|
|
312
|
+
if (params.status === 'starting')
|
|
313
|
+
this.starting.add(`${params.threadId} ${params.name}`);
|
|
314
|
+
else
|
|
315
|
+
this.starting.delete(`${params.threadId} ${params.name}`);
|
|
316
|
+
break;
|
|
317
|
+
case undefined:
|
|
318
|
+
this.answered(message);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
answered(message) {
|
|
322
|
+
if (typeof message.id === 'string' && message.id.startsWith(SYNC)) {
|
|
323
|
+
this.proven = Number(message.id.slice(SYNC.length));
|
|
324
|
+
}
|
|
325
|
+
else if (typeof message.id === 'string' && message.id.startsWith(QUEUE_LIST)) {
|
|
326
|
+
const asked = Number(message.id.slice(QUEUE_LIST.length));
|
|
327
|
+
for (const [thread, latest] of this.queues) {
|
|
328
|
+
if (latest === asked && message.result?.data?.length === 0)
|
|
329
|
+
this.queues.delete(thread);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if (message.result?.config?.features !== undefined) {
|
|
333
|
+
this.memories = message.result.config.features.memories !== false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
class GrokWork extends Work {
|
|
338
|
+
busy() {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
export function workFor(kind, cwd, changed) {
|
|
343
|
+
if (kind === 'claude-agent-acp')
|
|
344
|
+
return new ClaudeWork(cwd, changed);
|
|
345
|
+
if (kind === 'codex-acp')
|
|
346
|
+
return new CodexWork(changed);
|
|
347
|
+
return new GrokWork(changed);
|
|
348
|
+
}
|
package/how-we-work.md
CHANGED
|
@@ -2,24 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
A Room is where information people share is kept; it is not the place for
|
|
4
4
|
private information. Work goes into the Room it belongs to, or the First Room
|
|
5
|
-
when none is named.
|
|
5
|
+
when none is named.
|
|
6
6
|
|
|
7
7
|
The skills are already set up; never run `setup-matt-pocock-skills`.
|
|
8
8
|
|
|
9
9
|
## Where skill output goes
|
|
10
10
|
|
|
11
|
-
- Glossary (`CONTEXT.md`): `
|
|
12
|
-
- Decisions (ADRs): `
|
|
13
|
-
- Plan (spec): `
|
|
14
|
-
- Tasks (tickets): `
|
|
11
|
+
- Glossary (`CONTEXT.md`): `glossary.md`
|
|
12
|
+
- Decisions (ADRs): `decisions/NNNN-<slug>.md`
|
|
13
|
+
- Plan (spec): `projects/<name>/plan.md`
|
|
14
|
+
- Tasks (tickets): `projects/<name>/tasks/NN-<slug>.md`, with a
|
|
15
15
|
`Status:` line (`needs-triage`, `needs-info`, `ready-for-agent`,
|
|
16
16
|
`ready-for-human`, `wontfix`), a `Blocked by:` line and comments under
|
|
17
17
|
`## Comments`
|
|
18
|
-
- Map (Wayfinding): `
|
|
18
|
+
- Map (Wayfinding): `projects/<name>/map.md`, its tasks under
|
|
19
19
|
`tasks/` with `Type:`, `Status: claimed|resolved` and `Blocked by:` lines
|
|
20
|
-
- Research: `
|
|
21
|
-
- Questionnaires: `
|
|
22
|
-
- Learning: `
|
|
23
|
-
-
|
|
24
|
-
linked from the document they belong to; a prototype is one HTML file with
|
|
25
|
-
every variant on one page.
|
|
20
|
+
- Research: `research/<slug>.md`
|
|
21
|
+
- Questionnaires: `questionnaires/<slug>.md`
|
|
22
|
+
- Learning: `learning/<topic>/`
|
|
23
|
+
- Prototype: one HTML file with every variant on one page.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/kit",
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-c073f4e9391b2",
|
|
4
4
|
"description": "House Kit connects an Environment to agents.house.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc -p tsconfig.build.json && node scripts/bootstrap-manifest.mjs",
|
|
25
|
+
"engine": "mkdir -p tmp/engine && docker run --rm -v \"$PWD:/kit\" -w /kit alpine:3.24@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 sh scripts/engine.sh tmp/engine",
|
|
25
26
|
"test": "tsc -p tsconfig.json && vitest run"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@agentclientprotocol/sdk": "1.7.0",
|
|
29
|
-
"@agentshouse/mdmodel": "0.1.0-
|
|
30
|
+
"@agentshouse/mdmodel": "0.1.0-c68e2039f1ca8",
|
|
30
31
|
"yaml": "2.9.1"
|
|
31
32
|
},
|
|
32
33
|
"peerDependencies": {
|
|
33
34
|
"@agentclientprotocol/claude-agent-acp": "0.85.1",
|
|
34
|
-
"@agentclientprotocol/codex-acp": "2.1.1"
|
|
35
|
-
"@xai-official/grok": "1.0.46"
|
|
35
|
+
"@agentclientprotocol/codex-acp": "2.1.1"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@agentclientprotocol/claude-agent-acp": {
|
|
@@ -40,9 +40,6 @@
|
|
|
40
40
|
},
|
|
41
41
|
"@agentclientprotocol/codex-acp": {
|
|
42
42
|
"optional": true
|
|
43
|
-
},
|
|
44
|
-
"@xai-official/grok": {
|
|
45
|
-
"optional": true
|
|
46
43
|
}
|
|
47
44
|
},
|
|
48
45
|
"devDependencies": {
|
package/dist/blocks.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const FENCE = /^\s*(`{3,}|~{3,})\s*(\S*)/;
|
|
2
|
-
export function blocksOf(text) {
|
|
3
|
-
const blocks = [];
|
|
4
|
-
let paragraph = [];
|
|
5
|
-
let fence = null;
|
|
6
|
-
const closeParagraph = () => {
|
|
7
|
-
const joined = paragraph.join('\n').trim();
|
|
8
|
-
if (joined.length > 0)
|
|
9
|
-
blocks.push({ type: 'paragraph', text: joined });
|
|
10
|
-
paragraph = [];
|
|
11
|
-
};
|
|
12
|
-
const closeFence = () => {
|
|
13
|
-
const joined = fence.lines.join('\n');
|
|
14
|
-
if (joined.length > 0)
|
|
15
|
-
blocks.push({ type: 'code', language: fence.language, text: joined });
|
|
16
|
-
fence = null;
|
|
17
|
-
};
|
|
18
|
-
for (const line of text.split('\n')) {
|
|
19
|
-
if (fence !== null) {
|
|
20
|
-
if (line.trim().startsWith(fence.marker))
|
|
21
|
-
closeFence();
|
|
22
|
-
else
|
|
23
|
-
fence.lines.push(line);
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
const opening = FENCE.exec(line);
|
|
27
|
-
if (opening !== null) {
|
|
28
|
-
closeParagraph();
|
|
29
|
-
fence = { marker: opening[1], language: opening[2] || null, lines: [] };
|
|
30
|
-
}
|
|
31
|
-
else if (line.trim().length === 0) {
|
|
32
|
-
closeParagraph();
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
paragraph.push(line);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (fence !== null)
|
|
39
|
-
closeFence();
|
|
40
|
-
closeParagraph();
|
|
41
|
-
return blocks;
|
|
42
|
-
}
|
|
43
|
-
export function firstChange(before, after) {
|
|
44
|
-
let index = 0;
|
|
45
|
-
while (index < before.length &&
|
|
46
|
-
index < after.length &&
|
|
47
|
-
JSON.stringify(before[index]) === JSON.stringify(after[index])) {
|
|
48
|
-
index++;
|
|
49
|
-
}
|
|
50
|
-
return index;
|
|
51
|
-
}
|