mainflow 0.44.0-alpha.3 → 0.44.0-alpha.4
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.js +64 -5
- package/dist/facts.d.ts +1 -0
- package/dist/facts.js +2 -1
- package/dist/grant-check.d.ts +2 -2
- package/dist/grant-check.js +2 -2
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +3 -3
- package/dist/{host-CsZY5trM.js → host-CfBcUcg2.js} +270 -46
- package/dist/host.d.ts +1 -1
- package/dist/host.js +1 -1
- package/dist/{index-jq7lN8V8.d.ts → index-lsW80LDU.d.ts} +28 -5
- package/dist/launch.d.ts +1 -1
- package/dist/launch.js +2 -2
- package/dist/native/mainflow-swap-darwin-arm64 +0 -0
- package/dist/native/mainflow-swap-darwin-x64 +0 -0
- package/dist/{operation-BroV98VB.d.ts → operation-BTQm13OQ.d.ts} +1 -1
- package/dist/{operation-MKNwQkAY.js → operation-BmNzQC-L.js} +1 -1
- package/dist/{ui-Bv29R0WG.js → ui-CnNAOM_Y.js} +2 -2
- package/dist/web/assets/{index-B4w2iRCQ.js → index-BX_V5mK5.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
|
@@ -1033,7 +1033,13 @@ const CONFIG_FILE = "config.yaml";
|
|
|
1033
1033
|
const ISSUES_DIR = "issues";
|
|
1034
1034
|
const FINDINGS_DIR = "findings";
|
|
1035
1035
|
const GROUPS_DIR = "groups";
|
|
1036
|
+
/** The sessions settling a turn reads: those still running, paused, or ended while kept for their family (ENDED_SESSIONS_DIR). */
|
|
1036
1037
|
const SESSIONS_DIR = "local/sessions";
|
|
1038
|
+
/**
|
|
1039
|
+
* The sessions that ended or were handed off, moved out of SESSIONS_DIR so the work a turn's end does follows the
|
|
1040
|
+
* sessions still under way, not every session the hub ever had. A session comes back to SESSIONS_DIR when it is resumed.
|
|
1041
|
+
*/
|
|
1042
|
+
const ENDED_SESSIONS_DIR = `${SESSIONS_DIR}/ended`;
|
|
1037
1043
|
/** The document inside each mainline and task directory: front matter for attributes, body for prose. */
|
|
1038
1044
|
const MAINLINE_FILE = "mainline.md";
|
|
1039
1045
|
const TASK_FILE = "task.md";
|
|
@@ -1079,7 +1085,16 @@ function decisionFileIn(hub, mainline, decision) {
|
|
|
1079
1085
|
if (!file) throw new WriteError(`decision ${mainline}/${decision} not found`);
|
|
1080
1086
|
return file;
|
|
1081
1087
|
}
|
|
1088
|
+
/** Where the session `id` is kept while it is under way; one that ended may be in endedSessionFileIn instead. */
|
|
1082
1089
|
const sessionFileIn = (id) => `${SESSIONS_DIR}/${id}.yaml`;
|
|
1090
|
+
/** Where the session `id` is kept once it ended or was handed off. */
|
|
1091
|
+
const endedSessionFileIn = (id) => `${ENDED_SESSIONS_DIR}/${id}.yaml`;
|
|
1092
|
+
/** The session a file in either session directory holds, by its path relative to the data directory; null for any other path. */
|
|
1093
|
+
function sessionIdOfPath(path) {
|
|
1094
|
+
const parts = path.split("/");
|
|
1095
|
+
const name = parts[0] === "local" && parts[1] === "sessions" ? parts.length === 3 ? parts[2] : parts.length === 4 && parts[2] === "ended" ? parts[3] : void 0 : void 0;
|
|
1096
|
+
return name?.endsWith(".yaml") && name.length > 5 ? name.slice(0, -5) : null;
|
|
1097
|
+
}
|
|
1083
1098
|
function groupFileIn(hub, id) {
|
|
1084
1099
|
const file = entryFileIn(hub, GROUPS_DIR, id);
|
|
1085
1100
|
if (!file) throw new WriteError(`group ${id} not found`);
|
|
@@ -3258,18 +3273,27 @@ function removeFile(hub, input) {
|
|
|
3258
3273
|
}
|
|
3259
3274
|
//#endregion
|
|
3260
3275
|
//#region ../core/dist/ops/sessions.js
|
|
3261
|
-
/**
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3276
|
+
/** The statuses of a session that is done with; such a session is kept in ENDED_SESSIONS_DIR (belongsInEnded). */
|
|
3277
|
+
const FINISHED = ["ended", "handed-off"];
|
|
3278
|
+
/** Read the sessions in `dir`, those named in `skip` aside; one that does not parse or validate is reported as a problem and skipped. */
|
|
3279
|
+
function readSessionDirIn(hub, dir, out, skip = () => false) {
|
|
3280
|
+
const names = hub.list(dir).filter((e) => !e.dir && e.name.endsWith(".yaml")).map((e) => e.name);
|
|
3281
|
+
for (const name of names) {
|
|
3282
|
+
const path = `${dir}/${name}`;
|
|
3267
3283
|
const file = `${DATA_DIR}/${path}`;
|
|
3284
|
+
if (skip(name)) {
|
|
3285
|
+
out.problems.push({
|
|
3286
|
+
file,
|
|
3287
|
+
field: "",
|
|
3288
|
+
message: `the session is in ${DATA_DIR}/${SESSIONS_DIR}/ as well, which is the copy read; remove the copy that is out of date`
|
|
3289
|
+
});
|
|
3290
|
+
continue;
|
|
3291
|
+
}
|
|
3268
3292
|
let data;
|
|
3269
3293
|
try {
|
|
3270
3294
|
data = parse$1(hub.read(path) ?? "");
|
|
3271
3295
|
} catch (error) {
|
|
3272
|
-
problems.push({
|
|
3296
|
+
out.problems.push({
|
|
3273
3297
|
file,
|
|
3274
3298
|
field: "",
|
|
3275
3299
|
message: `cannot parse YAML: ${error.message}`
|
|
@@ -3277,27 +3301,124 @@ function readSessionsIn(hub) {
|
|
|
3277
3301
|
continue;
|
|
3278
3302
|
}
|
|
3279
3303
|
const result = sessionSchema.safeParse(data);
|
|
3280
|
-
if (result.success)
|
|
3281
|
-
|
|
3304
|
+
if (!result.success) {
|
|
3305
|
+
for (const i of result.error.issues) out.problems.push({
|
|
3306
|
+
file,
|
|
3307
|
+
field: i.path.join("."),
|
|
3308
|
+
message: i.message
|
|
3309
|
+
});
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
3312
|
+
if (dir === ENDED_SESSIONS_DIR && !FINISHED.includes(result.data.status)) out.problems.push({
|
|
3282
3313
|
file,
|
|
3283
|
-
field:
|
|
3284
|
-
message:
|
|
3314
|
+
field: "status",
|
|
3315
|
+
message: `${result.data.status}, but ${DATA_DIR}/${ENDED_SESSIONS_DIR}/ keeps only sessions that ended or were handed off; run "mainflow migrate" to move it back to ${DATA_DIR}/${SESSIONS_DIR}/`
|
|
3285
3316
|
});
|
|
3317
|
+
out.sessions.push(result.data);
|
|
3286
3318
|
}
|
|
3287
|
-
return
|
|
3288
|
-
|
|
3289
|
-
|
|
3319
|
+
return names;
|
|
3320
|
+
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Read every session: those in local/sessions/ and, unless `ended` is false, those in local/sessions/ended/ too. One
|
|
3323
|
+
* that does not parse or validate is reported as a problem and skipped; so is a session in both directories, whose copy
|
|
3324
|
+
* in local/sessions/ is read. Settling a turn needs only the first directory; listing, checking and looking back over
|
|
3325
|
+
* past work need both.
|
|
3326
|
+
*/
|
|
3327
|
+
function readSessionsIn(hub, options = {}) {
|
|
3328
|
+
const out = {
|
|
3329
|
+
sessions: [],
|
|
3330
|
+
problems: []
|
|
3290
3331
|
};
|
|
3332
|
+
const names = new Set(readSessionDirIn(hub, SESSIONS_DIR, out));
|
|
3333
|
+
if (options.ended !== false) readSessionDirIn(hub, ENDED_SESSIONS_DIR, out, (name) => names.has(name));
|
|
3334
|
+
return out;
|
|
3335
|
+
}
|
|
3336
|
+
/** Where the session `id` is kept: local/sessions/ while under way, local/sessions/ended/ once ended; null for a session neither holds. */
|
|
3337
|
+
function sessionPathIn(hub, id) {
|
|
3338
|
+
for (const path of [sessionFileIn(id), endedSessionFileIn(id)]) if (hub.read(path) !== null) return path;
|
|
3339
|
+
return null;
|
|
3291
3340
|
}
|
|
3292
3341
|
function readSessionIn(hub, id) {
|
|
3293
|
-
const path =
|
|
3294
|
-
return
|
|
3342
|
+
const path = sessionPathIn(hub, id);
|
|
3343
|
+
return path === null ? null : readYamlIn(hub, path, sessionSchema);
|
|
3295
3344
|
}
|
|
3296
3345
|
function requireSession(hub, id) {
|
|
3297
|
-
const path =
|
|
3298
|
-
if (
|
|
3346
|
+
const path = sessionPathIn(hub, id);
|
|
3347
|
+
if (path === null) throw new WriteError(`session ${id} not found; start one with "mainflow session start"`);
|
|
3299
3348
|
return path;
|
|
3300
3349
|
}
|
|
3350
|
+
function sessionLookup(hub, known = []) {
|
|
3351
|
+
const cache = new Map(known.map((s) => [s.id, s]));
|
|
3352
|
+
const lookup = (id) => {
|
|
3353
|
+
if (!cache.has(id)) {
|
|
3354
|
+
let found = null;
|
|
3355
|
+
try {
|
|
3356
|
+
found = readSessionIn(hub, id);
|
|
3357
|
+
} catch {}
|
|
3358
|
+
cache.set(id, found);
|
|
3359
|
+
}
|
|
3360
|
+
return cache.get(id);
|
|
3361
|
+
};
|
|
3362
|
+
return Object.assign(lookup, { set: (session) => void cache.set(session.id, session) });
|
|
3363
|
+
}
|
|
3364
|
+
/**
|
|
3365
|
+
* Whether a session belongs in local/sessions/ended/: it ended or was handed off, and each session it was delegated
|
|
3366
|
+
* from, up to the main session, tracked an action. Settling a session reads the subagents under it in local/sessions/
|
|
3367
|
+
* only (settleSession), and a subagent left there is one that may still decide it: a session whose family tracked no
|
|
3368
|
+
* action is removed with the family. A subagent moves out only once every session above it tracked an action, and no
|
|
3369
|
+
* write takes an action away, so none of them can be removed any more and the subagent, which no longer runs, changes
|
|
3370
|
+
* nothing settling decides. A subagent whose parent is missing or does not read stays, since nothing tells.
|
|
3371
|
+
*/
|
|
3372
|
+
function belongsInEnded(session, lookup) {
|
|
3373
|
+
if (!FINISHED.includes(session.status)) return false;
|
|
3374
|
+
const seen = /* @__PURE__ */ new Set([session.id]);
|
|
3375
|
+
for (let parent = session.delegatedFrom; parent && !seen.has(parent);) {
|
|
3376
|
+
seen.add(parent);
|
|
3377
|
+
const above = lookup(parent);
|
|
3378
|
+
if (!above || above.actions.length === 0) return false;
|
|
3379
|
+
parent = above.delegatedFrom;
|
|
3380
|
+
}
|
|
3381
|
+
return true;
|
|
3382
|
+
}
|
|
3383
|
+
/** The path the session belongs at (belongsInEnded). */
|
|
3384
|
+
const placeOf = (session, lookup) => belongsInEnded(session, lookup) ? endedSessionFileIn(session.id) : sessionFileIn(session.id);
|
|
3385
|
+
/**
|
|
3386
|
+
* Where the session at `from` goes when it belongs at `to`: `from` itself when another file is at `to` already, as when
|
|
3387
|
+
* an older mainflow started the session again in local/sessions/ while it was in local/sessions/ended/. Writing there
|
|
3388
|
+
* would replace that copy, and the record it holds, so the session stays and the write says so; check lists both copies.
|
|
3389
|
+
*/
|
|
3390
|
+
function destination(draft, id, from, to) {
|
|
3391
|
+
if (to === from || draft.read(to) === null) return to;
|
|
3392
|
+
const message = `${DATA_DIR}/${to} holds session ${id} too, so ${DATA_DIR}/${from} stays where it is rather than replace it; keep the copy that is up to date and remove the other`;
|
|
3393
|
+
if (!draft.notices.some((n) => n.path === from && n.message === message)) draft.notice(from, message);
|
|
3394
|
+
return from;
|
|
3395
|
+
}
|
|
3396
|
+
/** Move the file at `from` to `to`, unchanged; nothing when they are the same or another file is at `to` (destination). */
|
|
3397
|
+
function moveFile(draft, id, from, to) {
|
|
3398
|
+
if (destination(draft, id, from, to) === from) return;
|
|
3399
|
+
draft.write(to, draft.read(from));
|
|
3400
|
+
draft.remove(from);
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Write `next`, the session read as `current` from `from`, to the directory it belongs in now (belongsInEnded), keeping
|
|
3404
|
+
* the fields its file has beyond the schema; a session that moves is removed from `from`. One whose place another file
|
|
3405
|
+
* holds is written at `from` (destination).
|
|
3406
|
+
*/
|
|
3407
|
+
function writeSession(draft, from, current, next, lookup = sessionLookup(draft)) {
|
|
3408
|
+
const to = destination(draft, next.id, from, placeOf(next, lookup));
|
|
3409
|
+
draft.write(to, toYaml(yamlKeeping(draft, from, current, next)));
|
|
3410
|
+
if (to !== from) draft.remove(from);
|
|
3411
|
+
lookup.set(next);
|
|
3412
|
+
return next;
|
|
3413
|
+
}
|
|
3414
|
+
/** Mutate, validate and write a session, which moves to the directory its new status puts it in (writeSession). */
|
|
3415
|
+
function editSession(draft, id, mutate, lookup) {
|
|
3416
|
+
const from = requireSession(draft, id);
|
|
3417
|
+
const current = readYamlIn(draft, from, sessionSchema);
|
|
3418
|
+
const next = structuredClone(current);
|
|
3419
|
+
mutate(next);
|
|
3420
|
+
return writeSession(draft, from, current, validate(sessionSchema, next, draft.where(from)), lookup);
|
|
3421
|
+
}
|
|
3301
3422
|
/** Split `claude-code-1a2b3c4d#a003` into its session and action ids. */
|
|
3302
3423
|
function splitActionRef(ref) {
|
|
3303
3424
|
const hash = ref.lastIndexOf("#");
|
|
@@ -3412,11 +3533,11 @@ function takeUnfinished(draft, ctx, from, to, existing, at) {
|
|
|
3412
3533
|
doneAt: at
|
|
3413
3534
|
};
|
|
3414
3535
|
});
|
|
3415
|
-
|
|
3536
|
+
writeSession(draft, previousFile, previous, validate(sessionSchema, {
|
|
3416
3537
|
...previous,
|
|
3417
3538
|
status: "handed-off",
|
|
3418
3539
|
actions: left
|
|
3419
|
-
}, draft.where(previousFile)))
|
|
3540
|
+
}, draft.where(previousFile)));
|
|
3420
3541
|
return carried;
|
|
3421
3542
|
}
|
|
3422
3543
|
/**
|
|
@@ -3429,7 +3550,7 @@ function startSession(hub, input, ctx) {
|
|
|
3429
3550
|
const { minute } = stamps(ctx);
|
|
3430
3551
|
const draft = new Draft(hub);
|
|
3431
3552
|
const file = sessionFileIn(id);
|
|
3432
|
-
if (draft
|
|
3553
|
+
if (sessionPathIn(draft, id) !== null) throw new WriteError(`session ${id} already exists`);
|
|
3433
3554
|
if (input.delegatedFrom) requireSession(draft, input.delegatedFrom);
|
|
3434
3555
|
const carried = input.handoffFrom ? takeUnfinished(draft, ctx, input.handoffFrom, id, [], minute) : [];
|
|
3435
3556
|
const session = validate(sessionSchema, {
|
|
@@ -3446,8 +3567,8 @@ function startSession(hub, input, ctx) {
|
|
|
3446
3567
|
return draft.result(session);
|
|
3447
3568
|
}
|
|
3448
3569
|
/**
|
|
3449
|
-
* Start the session unless it already exists. An existing paused or ended session becomes active again,
|
|
3450
|
-
* an archived one comes back into view, since resumed work should not stay hidden.
|
|
3570
|
+
* Start the session unless it already exists. An existing paused or ended session becomes active again, back in
|
|
3571
|
+
* local/sessions/, and an archived one comes back into view, since resumed work should not stay hidden.
|
|
3451
3572
|
*/
|
|
3452
3573
|
function ensureSession(hub, input, ctx) {
|
|
3453
3574
|
const existing = readSessionIn(hub, input.id);
|
|
@@ -3466,7 +3587,7 @@ function ensureSession(hub, input, ctx) {
|
|
|
3466
3587
|
session: existing,
|
|
3467
3588
|
created: false
|
|
3468
3589
|
});
|
|
3469
|
-
const session =
|
|
3590
|
+
const session = editSession(draft, input.id, (s) => {
|
|
3470
3591
|
s.status = "active";
|
|
3471
3592
|
delete s.archivedAt;
|
|
3472
3593
|
});
|
|
@@ -3499,8 +3620,7 @@ function handoffSession(hub, input, ctx) {
|
|
|
3499
3620
|
handoffFrom: from,
|
|
3500
3621
|
actions: [...session.actions, ...carried]
|
|
3501
3622
|
}, draft.where(file));
|
|
3502
|
-
draft.
|
|
3503
|
-
return draft.result(next);
|
|
3623
|
+
return draft.result(writeSession(draft, file, session, next));
|
|
3504
3624
|
}
|
|
3505
3625
|
function setSessionInfo(hub, input) {
|
|
3506
3626
|
const draft = new Draft(hub);
|
|
@@ -3527,7 +3647,7 @@ function setCheckReported(hub, input) {
|
|
|
3527
3647
|
function setSessionStatus(hub, input) {
|
|
3528
3648
|
const value = requireOneOf(input.status, SESSION_STATUS, "session status");
|
|
3529
3649
|
const draft = new Draft(hub);
|
|
3530
|
-
return draft.result(
|
|
3650
|
+
return draft.result(editSession(draft, input.id, (s) => {
|
|
3531
3651
|
s.status = value;
|
|
3532
3652
|
}));
|
|
3533
3653
|
}
|
|
@@ -3536,28 +3656,99 @@ function setSessionStatus(hub, input) {
|
|
|
3536
3656
|
* no record, since nothing in it points at the hub's work. Then every active main session with no action left to
|
|
3537
3657
|
* do and no subagent still running ends, the others as well as this one: another session may have finished its
|
|
3538
3658
|
* last action, or it was closed before its own turn could settle it. The next prompt or action brings it back.
|
|
3659
|
+
*
|
|
3660
|
+
* Only the sessions in local/sessions/ are read, and the session itself wherever it is. An ended session moves to
|
|
3661
|
+
* local/sessions/ended/, with the finished subagents under it and under this session that belong there now
|
|
3662
|
+
* (belongsInEnded); a subagent already there cannot change what settling decides, so it is not read.
|
|
3539
3663
|
*/
|
|
3540
3664
|
function settleSession(hub, input) {
|
|
3541
3665
|
const { id } = input;
|
|
3542
3666
|
const draft = new Draft(hub);
|
|
3543
|
-
const { sessions } = readSessionsIn(draft);
|
|
3544
|
-
const
|
|
3545
|
-
|
|
3667
|
+
const { sessions } = readSessionsIn(draft, { ended: false });
|
|
3668
|
+
const lookup = sessionLookup(draft, sessions);
|
|
3669
|
+
/** Whether `session` was delegated from `parent`, directly or through other subagents. */
|
|
3670
|
+
const isUnder = (session, parent) => {
|
|
3671
|
+
const seen = /* @__PURE__ */ new Set([session.id]);
|
|
3672
|
+
for (let above = session.delegatedFrom; above && !seen.has(above); above = lookup(above)?.delegatedFrom ?? null) {
|
|
3673
|
+
if (above === parent) return true;
|
|
3674
|
+
seen.add(above);
|
|
3675
|
+
}
|
|
3676
|
+
return false;
|
|
3677
|
+
};
|
|
3678
|
+
const subagents = (parent) => sessions.filter((s) => isUnder(s, parent));
|
|
3679
|
+
const self = lookup(id);
|
|
3680
|
+
const family = self ? [self, ...subagents(id)] : [];
|
|
3546
3681
|
const removed = family.length > 0 && family.every((s) => s.actions.length === 0);
|
|
3547
|
-
if (removed) for (const s of family) draft.remove(
|
|
3682
|
+
if (removed) for (const s of family) draft.remove(sessionPathIn(draft, s.id));
|
|
3548
3683
|
let result = removed ? "removed" : "kept";
|
|
3684
|
+
const ended = [];
|
|
3549
3685
|
for (const s of sessions) {
|
|
3550
3686
|
if (s.status !== "active" || s.delegatedFrom || removed && s.id === id) continue;
|
|
3551
3687
|
if (!s.actions.every((a) => a.status === "done" || a.status === "dropped") || subagents(s.id).some((x) => x.status === "active")) continue;
|
|
3552
3688
|
if (s.id === id && s.actions.length === 0) continue;
|
|
3553
|
-
|
|
3689
|
+
writeSession(draft, sessionFileIn(s.id), s, validate(sessionSchema, {
|
|
3554
3690
|
...s,
|
|
3555
3691
|
status: "ended"
|
|
3556
|
-
}, draft.where(sessionFileIn(s.id))))
|
|
3692
|
+
}, draft.where(sessionFileIn(s.id))), lookup);
|
|
3693
|
+
ended.push(s.id);
|
|
3557
3694
|
if (s.id === id) result = "ended";
|
|
3558
3695
|
}
|
|
3696
|
+
const roots = [...self && !removed ? [id] : [], ...ended];
|
|
3697
|
+
for (const s of sessions) {
|
|
3698
|
+
if (roots.includes(s.id) || !roots.some((root) => isUnder(s, root)) || draft.read(sessionFileIn(s.id)) === null) continue;
|
|
3699
|
+
moveFile(draft, s.id, sessionFileIn(s.id), placeOf(lookup(s.id), lookup));
|
|
3700
|
+
}
|
|
3701
|
+
if (self && !removed && sessionPathIn(draft, id) === sessionFileIn(id)) moveFile(draft, id, sessionFileIn(id), placeOf(lookup(id), lookup));
|
|
3559
3702
|
return draft.result(result);
|
|
3560
3703
|
}
|
|
3704
|
+
/**
|
|
3705
|
+
* Move every session file to the directory it belongs in (belongsInEnded), as `mainflow migrate` does for a hub that
|
|
3706
|
+
* kept every session in local/sessions/: at most `limit` files, and never more than RELOCATE_BATCH, so a large hub moves
|
|
3707
|
+
* in several writes; `more` says another file is to move after them. `dryRun` moves none and lists them all. A file that
|
|
3708
|
+
* does not read, or that both directories hold, stays where it is; check reports it. Running it again once nothing is
|
|
3709
|
+
* left to move changes nothing.
|
|
3710
|
+
*/
|
|
3711
|
+
function relocateSessions$1(hub, input = {}) {
|
|
3712
|
+
const draft = new Draft(hub);
|
|
3713
|
+
const asked = typeof input.limit === "number" && Number.isFinite(input.limit) ? Math.floor(input.limit) : 500;
|
|
3714
|
+
const limit = input.dryRun ? Infinity : Math.min(Math.max(1, asked), 500);
|
|
3715
|
+
const lookup = sessionLookup(draft);
|
|
3716
|
+
const value = {
|
|
3717
|
+
ended: [],
|
|
3718
|
+
resumed: [],
|
|
3719
|
+
more: false
|
|
3720
|
+
};
|
|
3721
|
+
const names = (dir) => draft.list(dir).filter((e) => !e.dir && e.name.endsWith(".yaml")).map((e) => e.name.slice(0, -5));
|
|
3722
|
+
const active = names(SESSIONS_DIR);
|
|
3723
|
+
const ended = new Set(names(ENDED_SESSIONS_DIR));
|
|
3724
|
+
const moves = [];
|
|
3725
|
+
/** Plan the move of the file at `from`, if it is elsewhere than it belongs; false, with `more`, at a move past the limit. */
|
|
3726
|
+
const consider = (id, from, list) => {
|
|
3727
|
+
let session;
|
|
3728
|
+
try {
|
|
3729
|
+
session = readYamlIn(draft, from, sessionSchema);
|
|
3730
|
+
} catch {
|
|
3731
|
+
return true;
|
|
3732
|
+
}
|
|
3733
|
+
if (session.id !== id) return true;
|
|
3734
|
+
lookup.set(session);
|
|
3735
|
+
const to = placeOf(session, lookup);
|
|
3736
|
+
if (to === from) return true;
|
|
3737
|
+
if (moves.length >= limit) {
|
|
3738
|
+
value.more = true;
|
|
3739
|
+
return false;
|
|
3740
|
+
}
|
|
3741
|
+
moves.push([from, to]);
|
|
3742
|
+
list.push(id);
|
|
3743
|
+
return true;
|
|
3744
|
+
};
|
|
3745
|
+
for (const id of active) if (!ended.has(id) && !consider(id, sessionFileIn(id), value.ended)) break;
|
|
3746
|
+
if (!value.more) {
|
|
3747
|
+
for (const id of ended) if (!active.includes(id) && !consider(id, endedSessionFileIn(id), value.resumed)) break;
|
|
3748
|
+
}
|
|
3749
|
+
if (!input.dryRun) for (const [from, to] of moves) moveFile(draft, sessionIdOfPath(from), from, to);
|
|
3750
|
+
return draft.result(value);
|
|
3751
|
+
}
|
|
3561
3752
|
/** Archive or unarchive a session; the status stays as it is, so an ended session is visible until archived. */
|
|
3562
3753
|
function setSessionArchived(hub, input, ctx) {
|
|
3563
3754
|
const { day } = stamps(ctx);
|
|
@@ -3581,7 +3772,7 @@ function addAction(hub, input, ctx) {
|
|
|
3581
3772
|
requireServes(hub, input.serves);
|
|
3582
3773
|
const draft = new Draft(hub);
|
|
3583
3774
|
let created = "";
|
|
3584
|
-
|
|
3775
|
+
editSession(draft, input.session, (s) => {
|
|
3585
3776
|
created = draft.number(ctx, {
|
|
3586
3777
|
prefix: "a",
|
|
3587
3778
|
taken: s.actions.map((a) => a.id),
|
|
@@ -3933,8 +4124,9 @@ function migrateHub$1(hub) {
|
|
|
3933
4124
|
/**
|
|
3934
4125
|
* The operations by name, for a server that receives them from elsewhere. The names are the protocol's
|
|
3935
4126
|
* (OPERATION_NAMES in protocol.ts): the table must hold every one of them and nothing else. `migrate` and `init`
|
|
3936
|
-
* are absent:
|
|
3937
|
-
* is sent as an operation.
|
|
4127
|
+
* are absent: migrating the data format rewrites the whole hub in one go and a new hub starts on the caller's machine,
|
|
4128
|
+
* so neither is sent as an operation. Moving the session files to where they belong, which `migrate` also does, is
|
|
4129
|
+
* one (`session.relocate`), sent again until nothing is left to move.
|
|
3938
4130
|
*/
|
|
3939
4131
|
const OPERATIONS = {
|
|
3940
4132
|
"repo.add": addRepo,
|
|
@@ -3976,6 +4168,7 @@ const OPERATIONS = {
|
|
|
3976
4168
|
"session.checked": setCheckReported,
|
|
3977
4169
|
"session.set": setSessionStatus,
|
|
3978
4170
|
"session.settle": settleSession,
|
|
4171
|
+
"session.relocate": relocateSessions$1,
|
|
3979
4172
|
"session.archive": setSessionArchived,
|
|
3980
4173
|
"action.add": addAction,
|
|
3981
4174
|
"action.set": updateAction,
|
|
@@ -4015,18 +4208,20 @@ function partsOf(path, dir) {
|
|
|
4015
4208
|
if (parts.some((part) => part === "" || part === "." || part === ".." || part === ".git")) return null;
|
|
4016
4209
|
return (dir ? parts : parts.slice(0, -1)).some((part) => OWN_DIRS.has(part)) ? null : parts;
|
|
4017
4210
|
}
|
|
4018
|
-
/**
|
|
4211
|
+
/** The directories of local/ whose files are synced: the sessions under way, and those that ended. */
|
|
4212
|
+
const SESSION_DIRS = [SESSIONS_DIR, ENDED_SESSIONS_DIR];
|
|
4213
|
+
/** Whether the file at `path` is synced: not in a directory never synced, not in local/ but the session directories, and not a transient name. */
|
|
4019
4214
|
function syncedPath(path) {
|
|
4020
4215
|
const parts = partsOf(path, false);
|
|
4021
4216
|
if (parts === null) return false;
|
|
4022
|
-
if (parts[0] === "local" && !(parts
|
|
4217
|
+
if (parts[0] === "local" && !SESSION_DIRS.includes(parts.slice(0, -1).join("/"))) return false;
|
|
4023
4218
|
return !TRANSIENT.some((pattern) => pattern.test(parts.at(-1)));
|
|
4024
4219
|
}
|
|
4025
|
-
/** Whether the directory at `path` may hold synced files, so a scan walks into it: local/ itself and
|
|
4220
|
+
/** Whether the directory at `path` may hold synced files, so a scan walks into it: local/ itself and the session directories but nothing else in local/. */
|
|
4026
4221
|
function mayHoldSynced(path) {
|
|
4027
4222
|
if (path === "") return true;
|
|
4028
4223
|
if (partsOf(path, true) === null) return false;
|
|
4029
|
-
return path === "local" || !path.startsWith("local/") || path
|
|
4224
|
+
return path === "local" || !path.startsWith("local/") || SESSION_DIRS.includes(path);
|
|
4030
4225
|
}
|
|
4031
4226
|
/** Whether the directory at `path` is synced: one that may hold synced files, apart from .mainflow/ and local/, which every machine has of its own. */
|
|
4032
4227
|
const syncedDir = (path) => path !== "" && path !== "local" && mayHoldSynced(path);
|
|
@@ -4103,7 +4298,8 @@ const ENTRY = /^([mtdifw]\d{3})(-.+)$/;
|
|
|
4103
4298
|
/** The scope of a file in the data directory, from its path: the mainline and task directories above it, or its session. */
|
|
4104
4299
|
function scopeOfPath(path) {
|
|
4105
4300
|
const [top, second, third, fourth] = path.split("/");
|
|
4106
|
-
|
|
4301
|
+
const session = sessionIdOfPath(path);
|
|
4302
|
+
if (session !== null) return { session };
|
|
4107
4303
|
if (top === "groups" && second && third === void 0) {
|
|
4108
4304
|
const group = ENTRY.exec(second)?.[1];
|
|
4109
4305
|
return group ? { group } : {};
|
|
@@ -4726,9 +4922,12 @@ function grantLines(dirs, session) {
|
|
|
4726
4922
|
}
|
|
4727
4923
|
//#endregion
|
|
4728
4924
|
//#region ../core/dist/sessions.js
|
|
4729
|
-
/**
|
|
4730
|
-
|
|
4731
|
-
|
|
4925
|
+
/**
|
|
4926
|
+
* Read every session under `.mainflow/local/sessions/`, and under `.mainflow/local/sessions/ended/` unless `ended` is
|
|
4927
|
+
* false, as for what only sessions under way matter to; sessions are local and never version-controlled.
|
|
4928
|
+
*/
|
|
4929
|
+
function loadSessions(dirs, options = {}) {
|
|
4930
|
+
return readSessionsIn(nodeStorage(dirs), options);
|
|
4732
4931
|
}
|
|
4733
4932
|
function readSession(dirs, id) {
|
|
4734
4933
|
return readSessionIn(nodeStorage(dirs), id);
|
|
@@ -5078,7 +5277,7 @@ function duplicateClaimLines(dirs, sessionId, actionId) {
|
|
|
5078
5277
|
* over into this one. Subagent sessions are left out: their work returns to the session that delegated it.
|
|
5079
5278
|
*/
|
|
5080
5279
|
function handoffLines(dirs, session, cli) {
|
|
5081
|
-
const others = loadSessions(dirs).sessions.filter((s) => s.id !== session.id && !s.delegatedFrom && !s.archivedAt && (s.status === "active" || s.status === "paused") && s.actions.some(isCarried));
|
|
5280
|
+
const others = loadSessions(dirs, { ended: false }).sessions.filter((s) => s.id !== session.id && !s.delegatedFrom && !s.archivedAt && (s.status === "active" || s.status === "paused") && s.actions.some(isCarried));
|
|
5082
5281
|
if (others.length === 0) return [];
|
|
5083
5282
|
return [`其他會話還有沒做完的行動。要接續的是其中一場已經停下的工作時,先用 ${cli} session handoff <會話> 把它的行動接進這場會話,再開始做:`, ...others.map((s) => `- ${s.id}${mayBeRunning(dirs, s) ? "(15 分鐘內還有寫入對話紀錄,可能還在執行,不要接手)" : ""}:${s.actions.filter(isCarried).map((a) => `${a.id} ${a.do}(${a.status},服務 ${a.serves.join("、") || "未指定"})`).join(";")}`)];
|
|
5084
5283
|
}
|
|
@@ -9170,6 +9369,31 @@ function migrateHub(dirs, options = {}) {
|
|
|
9170
9369
|
} : result;
|
|
9171
9370
|
}).value;
|
|
9172
9371
|
}
|
|
9372
|
+
/**
|
|
9373
|
+
* Move the session files of a hub on disk to the directory each belongs in: those that ended to local/sessions/ended/,
|
|
9374
|
+
* any resumed one back. The files do not depend on the data format, so this runs on a hub of any format; with `dryRun`
|
|
9375
|
+
* it only lists the moves.
|
|
9376
|
+
*/
|
|
9377
|
+
function relocateSessions(dirs, options = {}) {
|
|
9378
|
+
const storage = nodeStorage(dirs);
|
|
9379
|
+
if (storage.read("config.yaml") === null) throw new WriteError(`no hub at ${dirs.root}; run mainflow init first`);
|
|
9380
|
+
if (options.dryRun) return applyOp(storage, (hub) => ({
|
|
9381
|
+
...relocateSessions$1(hub, { dryRun: true }),
|
|
9382
|
+
changes: []
|
|
9383
|
+
})).value;
|
|
9384
|
+
const moved = {
|
|
9385
|
+
ended: [],
|
|
9386
|
+
resumed: [],
|
|
9387
|
+
more: false
|
|
9388
|
+
};
|
|
9389
|
+
for (let more = true; more;) {
|
|
9390
|
+
const batch = applyOp(storage, (hub) => relocateSessions$1(hub, { limit: 500 })).value;
|
|
9391
|
+
moved.ended.push(...batch.ended);
|
|
9392
|
+
moved.resumed.push(...batch.resumed);
|
|
9393
|
+
more = batch.more;
|
|
9394
|
+
}
|
|
9395
|
+
return moved;
|
|
9396
|
+
}
|
|
9173
9397
|
//#endregion
|
|
9174
9398
|
//#region ../core/dist/migrate-data.js
|
|
9175
9399
|
/** What the original data directory is renamed to, inside the hub repository, once the hub reads from its new place. */
|
|
@@ -9701,4 +9925,4 @@ function openInApp(hub, target, closeAfterMs = 150) {
|
|
|
9701
9925
|
return true;
|
|
9702
9926
|
}
|
|
9703
9927
|
//#endregion
|
|
9704
|
-
export {
|
|
9928
|
+
export { syncedPath as $, unregisterHub as $t, readNotices as A, requestTime as At, mayDecorate as B, endedSessionFileIn as Bt, untilState as C, MAX_FILE_BYTES as Ct, HANDLED_FILE as D, comparePaths as Dt, turnReminder as E, VERSION_HEADER as Et, grantScope as F, versionMismatch as Ft, renumberRequest as G, inRepositoryHubAt as Gt, traceTranscript as H, sessionFileIn as Ht, matchGrants as I, ENDED_SESSIONS_DIR as It, renumberText as J, readRegistry as Jt, renumberFiles as K, listHubs as Kt, decorateReply as L, MAINLINE_FILE as Lt, loadSessions as M, formatMismatch as Mt, readSession as N, hubFormatVersion as Nt, markNoticesHandled as O, exceedsFileLimit as Ot, transcriptPath as P, hubRequirements as Pt, syncedDir as Q, resolveHub as Qt, endsWithMarker as R, SESSIONS_DIR as Rt, initHub as S, MAX_CHANGE_PAGE as St, sessionBriefing as T, SOCKET_MAX_AGE_CLOSE as Tt, transcriptTitle as U, hubAtRoot as Ut, attributeTrace as V, requireSlug as Vt, applyOffline as W, hubHoldingFile as Wt, hubDigest as X, registeredName as Xt, scopeOfPath as Y, registerHub as Yt, mayHoldSynced as Z, registryFile as Zt, retroReportFromReceiver as _, readFrontMatter as _t, openInApp as a, hubPath as an, loadHub as at, ARCHIVE_KINDS as b, BLOB_HASH as bt, siteBlock as c, ConflictError as cn, dependencyGraph as ct, groupPlan as d, contentHash as dn, memberOfGroup as dt, userDataDir as en, applyOperation as et, listProgress as f, splitFrontMatter as fn, refIndex as ft, retroReport as g, nowMinute as gt, relocateSessions as h, taskFile as ht, hostName as i, gitCheckout as in, loadConfig as it, unhandledNotices as j, textBytes as jt, parseWarn as k, readsAsText as kt, syncHost as l, DATA_DIR as ln, mainlineEdges as lt, migrateHub as m, unavailableHubs as mt, DEFAULT_UI_PORT as n, currentBranch as nn, HubError as nt, removeDanglingLinks as o, realPath as on, loadMainlines as ot, migrateHubData as p, remoteMembers as pt, renumberPath as q, notRegisteredMessage as qt, caddySites as r, dataFile as rn, isOpenMainline as rt, removeHostLink as s, worktreesOf as sn, sectionText as st, APP_BUNDLE_ID as t, nodeStorage as tn, checkHubName as tt, sessionView as u, WriteError as un, linksInto as ut, receiverFromEnv as v, readYaml as vt, duplicateClaimLines as w, ROUTES as wt, setArchivedMany as x, MAX_BLOB_BYTES as xt, RETRY_RULES as y, ACCESS_HEADERS as yt, headerLine as z, TASK_FILE as zt };
|
package/dist/host.d.ts
CHANGED
package/dist/host.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as openInApp, c as siteBlock, i as hostName, l as syncHost, n as DEFAULT_UI_PORT, o as removeDanglingLinks, r as caddySites, s as removeHostLink, t as APP_BUNDLE_ID } from "./host-
|
|
1
|
+
import { a as openInApp, c as siteBlock, i as hostName, l as syncHost, n as DEFAULT_UI_PORT, o as removeDanglingLinks, r as caddySites, s as removeHostLink, t as APP_BUNDLE_ID } from "./host-CfBcUcg2.js";
|
|
2
2
|
export { APP_BUNDLE_ID, DEFAULT_UI_PORT, caddySites, hostName, openInApp, removeDanglingLinks, removeHostLink, siteBlock, syncHost };
|
|
@@ -414,8 +414,8 @@ interface SessionInput {
|
|
|
414
414
|
*/
|
|
415
415
|
declare function startSession(hub: HubReader, input: SessionInput, ctx: OpContext): OpResult<Session>;
|
|
416
416
|
/**
|
|
417
|
-
* Start the session unless it already exists. An existing paused or ended session becomes active again,
|
|
418
|
-
* an archived one comes back into view, since resumed work should not stay hidden.
|
|
417
|
+
* Start the session unless it already exists. An existing paused or ended session becomes active again, back in
|
|
418
|
+
* local/sessions/, and an archived one comes back into view, since resumed work should not stay hidden.
|
|
419
419
|
*/
|
|
420
420
|
declare function ensureSession(hub: HubReader, input: SessionInput, ctx: OpContext): OpResult<{
|
|
421
421
|
session: Session;
|
|
@@ -457,10 +457,31 @@ type Settled = 'removed' | 'ended' | 'kept';
|
|
|
457
457
|
* no record, since nothing in it points at the hub's work. Then every active main session with no action left to
|
|
458
458
|
* do and no subagent still running ends, the others as well as this one: another session may have finished its
|
|
459
459
|
* last action, or it was closed before its own turn could settle it. The next prompt or action brings it back.
|
|
460
|
+
*
|
|
461
|
+
* Only the sessions in local/sessions/ are read, and the session itself wherever it is. An ended session moves to
|
|
462
|
+
* local/sessions/ended/, with the finished subagents under it and under this session that belong there now
|
|
463
|
+
* (belongsInEnded); a subagent already there cannot change what settling decides, so it is not read.
|
|
460
464
|
*/
|
|
461
465
|
declare function settleSession(hub: HubReader, input: {
|
|
462
466
|
id: string;
|
|
463
467
|
}): OpResult<Settled>;
|
|
468
|
+
/** What relocating the session files moved: the sessions moved to local/sessions/ended/, those moved back, and whether more are left. */
|
|
469
|
+
interface Relocated {
|
|
470
|
+
ended: string[];
|
|
471
|
+
resumed: string[];
|
|
472
|
+
more: boolean;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Move every session file to the directory it belongs in (belongsInEnded), as `mainflow migrate` does for a hub that
|
|
476
|
+
* kept every session in local/sessions/: at most `limit` files, and never more than RELOCATE_BATCH, so a large hub moves
|
|
477
|
+
* in several writes; `more` says another file is to move after them. `dryRun` moves none and lists them all. A file that
|
|
478
|
+
* does not read, or that both directories hold, stays where it is; check reports it. Running it again once nothing is
|
|
479
|
+
* left to move changes nothing.
|
|
480
|
+
*/
|
|
481
|
+
declare function relocateSessions(hub: HubReader, input?: {
|
|
482
|
+
dryRun?: boolean;
|
|
483
|
+
limit?: number;
|
|
484
|
+
}): OpResult<Relocated>;
|
|
464
485
|
/** Archive or unarchive a session; the status stays as it is, so an ended session is visible until archived. */
|
|
465
486
|
declare function setSessionArchived(hub: HubReader, input: {
|
|
466
487
|
id: string;
|
|
@@ -495,15 +516,16 @@ declare function updateAction(hub: HubReader, input: {
|
|
|
495
516
|
* (`item check` and `item uncheck` send `item.check`, with done true or false). The operations no command sends
|
|
496
517
|
* are listed in COMMANDLESS_OPERATIONS with who sends them.
|
|
497
518
|
*/
|
|
498
|
-
declare const OPERATION_NAMES: readonly ['repo.add', 'hub.add', 'mainline.new', 'mainline.set', 'mainline.title', 'mainline.archive', 'criterion.add', 'criterion.check', 'criterion.set', 'task.new', 'task.set', 'item.add', 'item.check', 'item.drop', 'item.undrop', 'decision.new', 'decision.accept', 'decision.supersede', 'issue.new', 'issue.set', 'issue.plan', 'issue.close', 'issue.reopen', 'issue.archive', 'finding.new', 'finding.set', 'finding.archive', 'group.new', 'group.set', 'ops.add', 'section.append', 'session.start', 'session.ensure', 'session.handoff', 'session.info', 'session.reminder', 'session.checked', 'session.set', 'session.settle', 'session.archive', 'action.add', 'action.set', 'file.remove'];
|
|
519
|
+
declare const OPERATION_NAMES: readonly ['repo.add', 'hub.add', 'mainline.new', 'mainline.set', 'mainline.title', 'mainline.archive', 'criterion.add', 'criterion.check', 'criterion.set', 'task.new', 'task.set', 'item.add', 'item.check', 'item.drop', 'item.undrop', 'decision.new', 'decision.accept', 'decision.supersede', 'issue.new', 'issue.set', 'issue.plan', 'issue.close', 'issue.reopen', 'issue.archive', 'finding.new', 'finding.set', 'finding.archive', 'group.new', 'group.set', 'ops.add', 'section.append', 'session.start', 'session.ensure', 'session.handoff', 'session.info', 'session.reminder', 'session.checked', 'session.set', 'session.settle', 'session.relocate', 'session.archive', 'action.add', 'action.set', 'file.remove'];
|
|
499
520
|
type OperationName = (typeof OPERATION_NAMES)[number];
|
|
500
521
|
//#endregion
|
|
501
522
|
//#region ../core/dist/ops/operations.d.ts
|
|
502
523
|
/**
|
|
503
524
|
* The operations by name, for a server that receives them from elsewhere. The names are the protocol's
|
|
504
525
|
* (OPERATION_NAMES in protocol.ts): the table must hold every one of them and nothing else. `migrate` and `init`
|
|
505
|
-
* are absent:
|
|
506
|
-
* is sent as an operation.
|
|
526
|
+
* are absent: migrating the data format rewrites the whole hub in one go and a new hub starts on the caller's machine,
|
|
527
|
+
* so neither is sent as an operation. Moving the session files to where they belong, which `migrate` also does, is
|
|
528
|
+
* one (`session.relocate`), sent again until nothing is left to move.
|
|
507
529
|
*/
|
|
508
530
|
declare const OPERATIONS: {
|
|
509
531
|
readonly 'repo.add': typeof addRepo;
|
|
@@ -545,6 +567,7 @@ declare const OPERATIONS: {
|
|
|
545
567
|
readonly 'session.checked': typeof setCheckReported;
|
|
546
568
|
readonly 'session.set': typeof setSessionStatus;
|
|
547
569
|
readonly 'session.settle': typeof settleSession;
|
|
570
|
+
readonly 'session.relocate': typeof relocateSessions;
|
|
548
571
|
readonly 'session.archive': typeof setSessionArchived;
|
|
549
572
|
readonly 'action.add': typeof addAction;
|
|
550
573
|
readonly 'action.set': typeof updateAction;
|
package/dist/launch.d.ts
CHANGED
package/dist/launch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as hostName, l as syncHost, n as DEFAULT_UI_PORT,
|
|
1
|
+
import { i as hostName, l as syncHost, n as DEFAULT_UI_PORT, s as removeHostLink, un as WriteError } from "./host-CfBcUcg2.js";
|
|
2
2
|
import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
@@ -273,7 +273,7 @@ async function launchUi(dirs, hub, configured, options) {
|
|
|
273
273
|
}
|
|
274
274
|
let server;
|
|
275
275
|
if (!reuse) {
|
|
276
|
-
const { serveUi } = await import("./ui-
|
|
276
|
+
const { serveUi } = await import("./ui-CnNAOM_Y.js");
|
|
277
277
|
server = await serveUi(dirs, choice.port);
|
|
278
278
|
writeUiRecord(dirs, {
|
|
279
279
|
port: choice.port,
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Renumbering, i as Notice, n as OperationValue, r as OperationName, t as OperationInput } from "./index-
|
|
1
|
+
import { a as Renumbering, i as Notice, n as OperationValue, r as OperationName, t as OperationInput } from "./index-lsW80LDU.js";
|
|
2
2
|
//#region src/remote/operation.d.ts
|
|
3
3
|
/** What a write reports: the operation's value, and what its caller tells the user about it. */
|
|
4
4
|
interface Written<N extends OperationName = OperationName> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as syncedPath, At as requestTime, Ct as MAX_FILE_BYTES, Dt as comparePaths, Et as VERSION_HEADER, Ft as versionMismatch, G as renumberRequest, J as renumberText, K as renumberFiles, Ot as exceedsFileLimit, Pt as hubRequirements, Q as syncedDir, Tt as SOCKET_MAX_AGE_CLOSE, W as applyOffline, X as hubDigest, Xt as registeredName, Y as scopeOfPath, Z as mayHoldSynced, bt as BLOB_HASH, cn as ConflictError, dn as contentHash, et as applyOperation, fn as splitFrontMatter, gt as nowMinute, jt as textBytes, kt as readsAsText, q as renumberPath, tn as nodeStorage, un as WriteError, wt as ROUTES, xt as MAX_BLOB_BYTES, yt as ACCESS_HEADERS } from "./host-CfBcUcg2.js";
|
|
2
2
|
import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, watch, writeFileSync } from "node:fs";
|