mainflow 0.28.0 → 0.29.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/dist/cli.d.ts +3 -1
- package/dist/cli.js +817 -213
- package/dist/facts.d.ts +64 -2
- package/dist/facts.js +64 -3
- package/dist/grant-commands.d.ts +1 -1
- package/dist/hooks.d.ts +514 -3
- package/dist/hooks.js +66 -35
- package/dist/host-BJnrv8xF.js +7300 -0
- package/dist/host.js +1 -1
- package/dist/index-DV6Gqu8K.d.ts +423 -0
- package/dist/{launch-BKKoCyTj.js → launch-DefacJEL.js} +2 -2
- package/dist/launch.js +1 -1
- package/dist/native/mainflow-swap-linux-x64 +0 -0
- package/dist/native/mainflow-swap.c +67 -0
- package/dist/operation-Bp4U2bx3.js +2862 -0
- package/dist/retro-formats-ECy0gGOL.js +30 -0
- package/dist/ui-CiapFoik.js +658 -0
- package/dist/web/assets/index-CyQML0b0.css +1 -0
- package/dist/web/assets/index-tAb99n75.js +82 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -1
- package/dist/host-CeN3dvXg.js +0 -3268
- package/dist/index-bIFT1ORw.d.ts +0 -46
- package/dist/ui-D_9x6XNs.js +0 -501
- package/dist/web/assets/index-BUxmYhLT.css +0 -1
- package/dist/web/assets/index-ypKt0hMg.js +0 -80
package/dist/hooks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as endsWithMarker, E as decorateReply, G as loadHub, O as headerLine, S as readSession, T as matchGrants, W as HubError, b as turnReminder, et as readYaml, i as hostName, it as worktreesOf, k as mayDecorate, nt as currentBranch, ot as DATA_DIR, rt as findHubRoot, w as grantScope, x as loadSessions, y as sessionBriefing } from "./host-BJnrv8xF.js";
|
|
2
2
|
import { Z as describeRequest, j as configSchema } from "./dist-BbamrIPW.js";
|
|
3
|
+
import { n as hubWriter, r as writtenLines } from "./operation-Bp4U2bx3.js";
|
|
3
4
|
import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
|
|
4
5
|
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
5
6
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -74,24 +75,27 @@ const claudeCodeSessionId = (raw) => `claude-code-${shortId(raw)}`;
|
|
|
74
75
|
* bring an ended or archived one back, since the viewer is working in it again. Its environment facts are
|
|
75
76
|
* written when it is created and whenever `refresh` asks, such as on SessionStart after a resume.
|
|
76
77
|
*/
|
|
77
|
-
function recordSession(root, payload, id, refresh) {
|
|
78
|
+
async function recordSession(write, root, payload, id, refresh) {
|
|
78
79
|
const cwd = payload.cwd ?? root;
|
|
79
|
-
const { session, created } =
|
|
80
|
+
const { session, created } = (await write("session.ensure", {
|
|
80
81
|
id,
|
|
81
82
|
agent: "claude-code",
|
|
82
83
|
cwd
|
|
83
|
-
});
|
|
84
|
+
})).value;
|
|
84
85
|
if (!created && !refresh) return session;
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
return (await write("session.info", {
|
|
87
|
+
id,
|
|
88
|
+
info: {
|
|
89
|
+
transcript: payload.transcript_path,
|
|
90
|
+
worktree: worktreesOf(cwd)?.current,
|
|
91
|
+
branch: currentBranch(cwd) ?? void 0,
|
|
92
|
+
effort: payload.effort?.level
|
|
93
|
+
}
|
|
94
|
+
})).value;
|
|
91
95
|
}
|
|
92
96
|
/** Settle the main session by the rules settleSession states; prints nothing. */
|
|
93
|
-
const settleOnEnd = (
|
|
94
|
-
if (payload.session_id)
|
|
97
|
+
const settleOnEnd = async (_root, { payload, write }) => {
|
|
98
|
+
if (payload.session_id) await write("session.settle", { id: claudeCodeSessionId(payload.session_id) });
|
|
95
99
|
return null;
|
|
96
100
|
};
|
|
97
101
|
/** How a hub's interface is linked from a reply: through its /open route, which shows the address in the app. */
|
|
@@ -193,21 +197,24 @@ function missingHeaderRequest(root, { payload, cli }) {
|
|
|
193
197
|
* stops it from asking twice in a row. A session asked to list an action is not settled yet: settling removes a
|
|
194
198
|
* session with no actions, and the action it is asked to add needs the session to exist.
|
|
195
199
|
*/
|
|
196
|
-
const settleAndCheckMarker = (root, context) => {
|
|
200
|
+
const settleAndCheckMarker = async (root, context) => {
|
|
197
201
|
const request = missingHeaderRequest(root, context);
|
|
198
202
|
const session = context.payload.session_id ? readSession(root, claudeCodeSessionId(context.payload.session_id)) : null;
|
|
199
|
-
if (!request || session && session.actions.length > 0) settleOnEnd(root, context);
|
|
203
|
+
if (!request || session && session.actions.length > 0) await settleOnEnd(root, context);
|
|
200
204
|
return request ? JSON.stringify({ hookSpecificOutput: {
|
|
201
205
|
hookEventName: "Stop",
|
|
202
206
|
additionalContext: request
|
|
203
207
|
} }) : null;
|
|
204
208
|
};
|
|
205
209
|
const CLAUDE_CODE_EVENTS = {
|
|
206
|
-
"session-start": (root, { payload, envFile, cli, startUi }) => {
|
|
210
|
+
"session-start": async (root, { payload, envFile, cli, startUi, write }) => {
|
|
207
211
|
if (!payload.session_id) return null;
|
|
208
212
|
const id = claudeCodeSessionId(payload.session_id);
|
|
209
|
-
const session = recordSession(root, payload, id, true);
|
|
210
|
-
|
|
213
|
+
const session = await recordSession(write, root, payload, id, true);
|
|
214
|
+
await write("session.reminder", {
|
|
215
|
+
id,
|
|
216
|
+
digest: null
|
|
217
|
+
});
|
|
211
218
|
if (envFile) appendFileSync(envFile, `export MAINFLOW_SESSION=${id}\n`);
|
|
212
219
|
startUi?.(root);
|
|
213
220
|
return sessionBriefing(root, session, {
|
|
@@ -215,16 +222,26 @@ const CLAUDE_CODE_EVENTS = {
|
|
|
215
222
|
envNote: envFile ? "已設定環境變數 MAINFLOW_SESSION,指令不必再帶 --session" : `執行指令時帶 --session ${id}`
|
|
216
223
|
});
|
|
217
224
|
},
|
|
218
|
-
"user-prompt-submit": (root, { payload, cli }) => {
|
|
225
|
+
"user-prompt-submit": async (root, { payload, cli, write }) => {
|
|
219
226
|
if (!payload.session_id) return null;
|
|
220
|
-
|
|
227
|
+
const session = await recordSession(write, root, payload, claudeCodeSessionId(payload.session_id), false);
|
|
228
|
+
const saved = [];
|
|
229
|
+
const text = turnReminder(root, session, {
|
|
230
|
+
cli,
|
|
231
|
+
saveDigest: (digest) => saved.push(write("session.reminder", {
|
|
232
|
+
id: session.id,
|
|
233
|
+
digest
|
|
234
|
+
}).catch(() => void 0))
|
|
235
|
+
});
|
|
236
|
+
await Promise.all(saved);
|
|
237
|
+
return text;
|
|
221
238
|
},
|
|
222
239
|
stop: settleAndCheckMarker,
|
|
223
240
|
"session-end": settleOnEnd,
|
|
224
|
-
"subagent-start": (root, { payload }) => {
|
|
241
|
+
"subagent-start": async (root, { payload, write }) => {
|
|
225
242
|
if (!payload.agent_id) return null;
|
|
226
243
|
const parent = payload.session_id ? claudeCodeSessionId(payload.session_id) : null;
|
|
227
|
-
|
|
244
|
+
await write("session.ensure", {
|
|
228
245
|
id: claudeCodeSessionId(payload.agent_id),
|
|
229
246
|
agent: payload.agent_type ? `claude-code/${payload.agent_type}` : "claude-code",
|
|
230
247
|
cwd: payload.cwd ?? root,
|
|
@@ -232,18 +249,24 @@ const CLAUDE_CODE_EVENTS = {
|
|
|
232
249
|
});
|
|
233
250
|
return null;
|
|
234
251
|
},
|
|
235
|
-
"subagent-stop": (root, { payload }) => {
|
|
252
|
+
"subagent-stop": async (root, { payload, write }) => {
|
|
236
253
|
const id = payload.agent_id ? claudeCodeSessionId(payload.agent_id) : null;
|
|
237
254
|
if (!id || !readSession(root, id)) return null;
|
|
238
255
|
const transcript = payload.transcript_path ? subagentTranscript(payload.transcript_path, payload.agent_id) : void 0;
|
|
239
256
|
const meta = transcript ? readMeta(transcript) : {};
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
257
|
+
await write("session.info", {
|
|
258
|
+
id,
|
|
259
|
+
info: {
|
|
260
|
+
transcript: transcript && existsSync(transcript) ? transcript : void 0,
|
|
261
|
+
description: meta.description,
|
|
262
|
+
depth: meta.spawnDepth,
|
|
263
|
+
summary: brief(payload.last_assistant_message) || void 0
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
await write("session.set", {
|
|
267
|
+
id,
|
|
268
|
+
status: "ended"
|
|
245
269
|
});
|
|
246
|
-
setSessionStatus(root, id, "ended");
|
|
247
270
|
return null;
|
|
248
271
|
},
|
|
249
272
|
"pre-tool-use": decideByGrants,
|
|
@@ -289,9 +312,9 @@ function askWhenGrantable({ payload }, failure) {
|
|
|
289
312
|
/** The ops log's fields cannot hold its separator, so notes that contain one are written with a broken bar. */
|
|
290
313
|
const opsText = (text) => text.replace(/\s\|\s/g, " ¦ ").replace(/\s+/g, " ").slice(0, 200);
|
|
291
314
|
/** Log the allowed operation with the grant that covered it; a failure only warns, the call stays allowed. */
|
|
292
|
-
function logGranted(
|
|
315
|
+
async function logGranted(write, scope, call, request, grant, warn) {
|
|
293
316
|
try {
|
|
294
|
-
|
|
317
|
+
await write("ops.add", {
|
|
295
318
|
target: request.action === "publish" ? `npm ${request.name}` : `github release ${request.repos.join("/")}`,
|
|
296
319
|
operation: opsText(call.text),
|
|
297
320
|
result: `授權放行 ${grant.ref}(${describeRequest(request)})`,
|
|
@@ -309,7 +332,7 @@ function logGranted(root, scope, call, request, grant, warn) {
|
|
|
309
332
|
* print nothing, so other hooks and the usual permission rules decide them. Any failure asks: the caller turns a
|
|
310
333
|
* throw into the fallback above.
|
|
311
334
|
*/
|
|
312
|
-
function decideByGrants(root, { payload, warn }) {
|
|
335
|
+
async function decideByGrants(root, { payload, warn, write }) {
|
|
313
336
|
const scan = grantableCall(payload);
|
|
314
337
|
if (!scan) return null;
|
|
315
338
|
if (scan.kind === "refused") return askBecause(scan.reason);
|
|
@@ -329,7 +352,7 @@ function decideByGrants(root, { payload, warn }) {
|
|
|
329
352
|
const match = matchGrants(scoped.scope, [resolved.request]);
|
|
330
353
|
if (!match.ok) return askBecause(match.reason);
|
|
331
354
|
const { grant } = match.matches[0];
|
|
332
|
-
logGranted(
|
|
355
|
+
await logGranted(write, scoped.scope, scan.call, resolved.request, grant, warn);
|
|
333
356
|
return permission("allow", `主脈絡授權 ${grant.ref} 涵蓋 ${describeRequest(resolved.request)}`);
|
|
334
357
|
}
|
|
335
358
|
/** How many problems outside the written file the reminder lists before it only gives the count. */
|
|
@@ -417,7 +440,7 @@ function hookFallback(platform, event, ctx, failure) {
|
|
|
417
440
|
* failure is reported on stderr while the caller still exits 0. Events listed in the fallbacks print their
|
|
418
441
|
* fallback instead of staying silent.
|
|
419
442
|
*/
|
|
420
|
-
function runHook(platform, event, ctx) {
|
|
443
|
+
async function runHook(platform, event, ctx) {
|
|
421
444
|
const handler = HOOK_PLATFORMS[platform]?.[event];
|
|
422
445
|
if (!handler) return {
|
|
423
446
|
stdout: null,
|
|
@@ -431,10 +454,18 @@ function runHook(platform, event, ctx) {
|
|
|
431
454
|
stdout: hookFallback(platform, event, ctx, { kind: "outside-hub" }),
|
|
432
455
|
error: null
|
|
433
456
|
};
|
|
457
|
+
const warn = (message) => warnings.push(message);
|
|
458
|
+
const writer = ctx.write ?? hubWriter(root);
|
|
459
|
+
const write = async (op, input) => {
|
|
460
|
+
const written = await writer(op, input);
|
|
461
|
+
for (const line of writtenLines(written)) if (line.startsWith("!")) warn(`mainflow hook ${platform} ${event}: ${line.slice(2)}`);
|
|
462
|
+
return written;
|
|
463
|
+
};
|
|
434
464
|
return {
|
|
435
|
-
stdout: handler(root, {
|
|
465
|
+
stdout: await handler(root, {
|
|
436
466
|
...ctx,
|
|
437
|
-
warn
|
|
467
|
+
warn,
|
|
468
|
+
write
|
|
438
469
|
}),
|
|
439
470
|
error: errorText()
|
|
440
471
|
};
|