volute 0.53.0 → 0.54.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/auto-upgrade-HEVZKPTF.js +46 -0
- package/dist/{backup-WWXVIMYQ.js → backup-QRNX6RK7.js} +11 -11
- package/dist/channels-CKCY64TD.js +203 -0
- package/dist/{chat-WK2BLM7B.js → chat-ZUGNL5GS.js} +9 -9
- package/dist/{chunk-UTKUO2KY.js → chunk-3LHUXY3E.js} +2 -2
- package/dist/{chunk-PIZDPY4G.js → chunk-444RR3YF.js} +4 -4
- package/dist/{chunk-VDZ4I4ZP.js → chunk-56IT2ZXF.js} +3 -3
- package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
- package/dist/{chunk-LN7XJ56R.js → chunk-7XKPG66D.js} +9 -9
- package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
- package/dist/{chunk-7ARONCZW.js → chunk-ATZF2RNJ.js} +3 -3
- package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
- package/dist/{chunk-RMADJFA7.js → chunk-CLCHOXLR.js} +1 -1
- package/dist/{chunk-IQ7VNWH6.js → chunk-HKGYZVWN.js} +1 -1
- package/dist/{chunk-HASADR3E.js → chunk-LGXVGIWN.js} +66 -25
- package/dist/{chunk-P2TBUKLW.js → chunk-LKUGUQUW.js} +3 -3
- package/dist/{chunk-AP2KQMMW.js → chunk-OAYY3WF3.js} +11 -3
- package/dist/{chunk-UO4MZQNO.js → chunk-OGPVJ53N.js} +14 -6
- package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
- package/dist/chunk-OT5SXF7L.js +904 -0
- package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
- package/dist/{chunk-TAWRJ24T.js → chunk-PPSN2MLC.js} +2 -2
- package/dist/{chunk-5DBGQBQR.js → chunk-RGHF3OK5.js} +21 -10
- package/dist/{chunk-RQRO6PC7.js → chunk-VDUCLVZI.js} +305 -31
- package/dist/{chunk-23UJGVYP.js → chunk-WHL6PKYP.js} +10 -10
- package/dist/{chunk-HTB7KDRU.js → chunk-WZZCA23D.js} +5 -4
- package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
- package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
- package/dist/{chunk-BDHKCLWK.js → chunk-ZNHL5KQJ.js} +1 -1
- package/dist/cli.js +19 -19
- package/dist/{clock-SH4NQC2F.js → clock-X7V3C7NL.js} +3 -3
- package/dist/{cloud-sync-MJVJ33LD.js → cloud-sync-52YNJRMR.js} +11 -11
- package/dist/{daemon-restart-BIGU4OKS.js → daemon-restart-QWHN5VMU.js} +4 -4
- package/dist/daemon.js +330 -746
- package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
- package/dist/{delivery-manager-FDHCZ7PU.js → delivery-manager-7R2YIQ2W.js} +11 -7
- package/dist/{delivery-notices-65TB3CBX.js → delivery-notices-RZQ6GONP.js} +3 -3
- package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
- package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
- package/dist/{echo-text-KU6G4Z7V.js → echo-text-73CYQRL7.js} +14 -14
- package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
- package/dist/{extensions-VACUKFA5.js → extensions-QT65JGV6.js} +21 -11
- package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
- package/dist/{message-delivery-TUXM3AAE.js → message-delivery-TRKT4T2P.js} +11 -11
- package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
- package/dist/{mind-IX5AWUST.js → mind-CT6ANXK4.js} +12 -12
- package/dist/{mind-contacts-BKUOUJPV.js → mind-contacts-PEXCDRXA.js} +8 -8
- package/dist/{mind-list-KNU34SUD.js → mind-list-7KBLPLES.js} +2 -2
- package/dist/{mind-service-WHFBUKZE.js → mind-service-7EFIJ3ZA.js} +11 -11
- package/dist/{mind-sleep-D3DEYAWG.js → mind-sleep-24HV4RCA.js} +5 -5
- package/dist/{mind-status-BPTLQ5Z6.js → mind-status-4YW4PHMO.js} +6 -3
- package/dist/{package-BN5KMU54.js → package-F6S5NHY2.js} +4 -4
- package/dist/{prompts-HNKGCAEY.js → prompts-JECJIFPQ.js} +1 -1
- package/dist/{scheduler-5X5UIQOQ.js → scheduler-73Q4MQ7Y.js} +6 -6
- package/dist/{seed-LHZ33UB4.js → seed-DRKAP3XV.js} +1 -1
- package/dist/{seed-cmd-U4RVJ3UA.js → seed-cmd-6IMTO3LG.js} +2 -2
- package/dist/{seed-create-OQU4KNCB.js → seed-create-VNLDYRFK.js} +1 -1
- package/dist/{seed-readiness-WJYYRRCK.js → seed-readiness-H5TTPAQH.js} +2 -2
- package/dist/{seed-sprout-SFUYH2RG.js → seed-sprout-PPGYGK4G.js} +8 -8
- package/dist/{send-FWZAMDBN.js → send-UXUQKO4N.js} +9 -9
- package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
- package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
- package/dist/{setup-RVSGCIF3.js → setup-YUAQMDVZ.js} +4 -4
- package/dist/skills/dreaming/SKILL.md +2 -2
- package/dist/skills/dreaming/references/INSTALL.md +1 -1
- package/dist/skills/tending/SKILL.md +2 -0
- package/dist/skills/volute-mind/references/integrations.md +3 -2
- package/dist/skills/volute-mind/references/routing.md +18 -5
- package/dist/{skills-QRPU6BWY.js → skills-GMQ5UIIW.js} +2 -2
- package/dist/{sleep-manager-NWUCRBZB.js → sleep-manager-KHI65FEX.js} +11 -11
- package/dist/{spirit-I5ECLGRI.js → spirit-Q5VGFWUI.js} +5 -3
- package/dist/{spirit-availability-NIUUYKU2.js → spirit-availability-G5FMWKRH.js} +12 -12
- package/dist/{sprout-A2PMEF7Z.js → sprout-PMTRL7QN.js} +1 -1
- package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
- package/dist/src-H3UCOP6B.js +516 -0
- package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
- package/dist/{status-EYG4A3OK.js → status-LCKTTIWS.js} +5 -5
- package/dist/{system-events-YTOU66QI.js → system-events-Q7IVZ5T4.js} +1 -1
- package/dist/{systems-2DIJY4RF.js → systems-HL7SGSW7.js} +6 -6
- package/dist/{turn-tracker-NNHS6MAR.js → turn-tracker-HZDPMSCC.js} +2 -2
- package/dist/{up-UGBHH743.js → up-XZDSJVMU.js} +3 -3
- package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
- package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
- package/dist/{variant-cleanup-CZDLIY3F.js → variant-cleanup-QHGGY7ND.js} +6 -6
- package/dist/{version-notify-SJVAQLZI.js → version-notify-VNVFX6UP.js} +19 -4
- package/dist/web-assets/assets/{index-b7G7ZsVk.js → index-CwtxGwe_.js} +16 -16
- package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +4 -4
- package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
- package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
- package/packages/extensions/{plan → intentions}/dist/ui/index.html +3 -3
- package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
- package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
- package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
- package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
- package/templates/_base/.init/.config/prompts.json +2 -2
- package/templates/{pi → _base}/.init/.config/routes.json +1 -0
- package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
- package/templates/_base/home/.config/routes.json +1 -1
- package/templates/_base/home/VOLUTE.md +1 -1
- package/templates/_base/src/lib/context-breakdown.ts +11 -3
- package/templates/_base/src/lib/routing.ts +2 -2
- package/templates/_base/src/lib/startup.ts +4 -2
- package/templates/claude/.init/CLAUDE.md +4 -0
- package/templates/claude/src/agent.ts +28 -12
- package/templates/claude/src/lib/message-channel.ts +43 -15
- package/templates/claude/src/lib/recover.ts +69 -0
- package/templates/claude/src/lib/stream-consumer.ts +23 -5
- package/templates/claude/volute-template.json +1 -1
- package/templates/codex/.init/AGENTS.md +4 -0
- package/templates/codex/volute-template.json +1 -1
- package/templates/pi/.init/MINDS.md +4 -0
- package/templates/pi/src/lib/mechanics-doc.ts +30 -0
- package/templates/pi/src/server.ts +3 -1
- package/templates/pi/volute-template.json +1 -1
- package/dist/channels-LW5ZLMSQ.js +0 -90
- package/dist/skills/plan-coordinator/SKILL.md +0 -60
- package/dist/src-UDMJZ6MH.js +0 -426
- package/dist/web-assets/assets/index-BHSU9r-4.css +0 -1
- package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
- package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
- package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
- package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
- package/templates/claude/.init/.config/routes.json +0 -11
- package/templates/codex/.init/.config/routes.json +0 -11
- package/dist/{accept-I4GVGWA3.js → accept-RSDIVJL6.js} +3 -3
- package/dist/{bridge-KU2IKSH3.js → bridge-YK7NTRU5.js} +3 -3
- package/dist/{create-C3XZACTV.js → create-M727VUDF.js} +3 -3
- package/dist/{env-BJGM6FBA.js → env-7AZFBPWX.js} +7 -7
- package/dist/{extension-B2W52WZY.js → extension-MRYJWSMV.js} +3 -3
- package/dist/{files-CHPNEQXN.js → files-PCKBQHAY.js} +3 -3
- package/dist/{list-R2GDWOEE.js → list-UQGIW7OF.js} +3 -3
- package/dist/{login-EZ4RRDQX.js → login-4NUS72Z5.js} +3 -3
- package/dist/{login-JDOAK465.js → login-57MMYILL.js} +4 -4
- package/dist/{logout-MWSIUOQU.js → logout-BYV433X6.js} +3 -3
- package/dist/{logout-K6ZGAJY5.js → logout-YFZAJRPY.js} +3 -3
- package/dist/{mind-history-RVF6KJ7K.js → mind-history-RZMMCHMX.js} +9 -9
- package/dist/{mind-wake-DCDFFQKX.js → mind-wake-LDGPRA42.js} +3 -3
- package/dist/{read-J5RM674N.js → read-RJ5NAW5J.js} +3 -3
- package/dist/{register-53LBGSDC.js → register-WNCSOTAU.js} +3 -3
- package/dist/{reject-K2A4UK2Y.js → reject-4NFLXKRN.js} +3 -3
- package/dist/{restart-R2WFNF5K.js → restart-B4GNMVF2.js} +7 -7
- package/dist/{skill-DXLLSUHB.js → skill-QYTBUDNQ.js} +7 -7
- package/dist/{start-XDOUAWN3.js → start-BDHJVOVX.js} +6 -6
- package/dist/{stop-O62RTEMD.js → stop-45QYSGYS.js} +7 -7
|
@@ -4,44 +4,96 @@ import {
|
|
|
4
4
|
} from "./chunk-RG5TOL4O.js";
|
|
5
5
|
import {
|
|
6
6
|
getAllSites,
|
|
7
|
+
getMindsWithSites,
|
|
7
8
|
getPublishedPages,
|
|
8
9
|
getRecentPages,
|
|
9
10
|
getSystemPages,
|
|
10
11
|
initDb,
|
|
11
12
|
syncPublishedPages,
|
|
12
13
|
syncSystemPages
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AAPNU7GA.js";
|
|
14
15
|
import "./chunk-K3NQKI34.js";
|
|
15
16
|
|
|
16
17
|
// packages/extensions/pages/src/index.ts
|
|
17
|
-
import { resolve as
|
|
18
|
+
import { resolve as resolve6 } from "path";
|
|
18
19
|
|
|
19
20
|
// packages/extensions/pages/src/commands.ts
|
|
20
|
-
import { cpSync, existsSync as
|
|
21
|
-
import { relative as relative2, resolve as
|
|
21
|
+
import { cpSync, existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync2 } from "fs";
|
|
22
|
+
import { relative as relative2, resolve as resolve3 } from "path";
|
|
23
|
+
|
|
24
|
+
// packages/extensions/pages/src/commons.ts
|
|
25
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
26
|
+
import { resolve } from "path";
|
|
27
|
+
function commonsReport(repoDir, systemFiles, mindsWithSites) {
|
|
28
|
+
const index = systemFiles.find((f) => f === "index.md" || f === "index.html");
|
|
29
|
+
if (!index) {
|
|
30
|
+
return { hasIndex: false, orphanPages: [...systemFiles], unlinkedMinds: [...mindsWithSites] };
|
|
31
|
+
}
|
|
32
|
+
const contentOf = (f) => {
|
|
33
|
+
try {
|
|
34
|
+
return readFileSync(resolve(repoDir, f), "utf-8");
|
|
35
|
+
} catch {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const reached = /* @__PURE__ */ new Set([index]);
|
|
40
|
+
const queue = [index];
|
|
41
|
+
while (queue.length > 0) {
|
|
42
|
+
const content = contentOf(queue.shift());
|
|
43
|
+
for (const f of systemFiles) {
|
|
44
|
+
if (!reached.has(f) && content.includes(f)) {
|
|
45
|
+
reached.add(f);
|
|
46
|
+
queue.push(f);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const orphanPages = systemFiles.filter((f) => !reached.has(f));
|
|
51
|
+
const reachableText = [...reached].map(contentOf).join("\n");
|
|
52
|
+
const unlinkedMinds = mindsWithSites.filter(
|
|
53
|
+
(m2) => !reachableText.includes(`../${m2}/`) && !reachableText.includes(`/ext/pages/public/${m2}/`)
|
|
54
|
+
);
|
|
55
|
+
return { hasIndex: true, orphanPages, unlinkedMinds };
|
|
56
|
+
}
|
|
57
|
+
async function maybeSendCommonsCue(ctx, repoDir) {
|
|
58
|
+
const cueFlag = resolve(ctx.dataDir, ".commons-cue-sent");
|
|
59
|
+
const hasIndex = existsSync(resolve(repoDir, "index.md")) || existsSync(resolve(repoDir, "index.html"));
|
|
60
|
+
if (hasIndex || existsSync(cueFlag)) return;
|
|
61
|
+
const spirit = ctx.getSpiritName();
|
|
62
|
+
if (!spirit) return;
|
|
63
|
+
try {
|
|
64
|
+
if (!await ctx.getMindDir(spirit)) return;
|
|
65
|
+
await ctx.recordNotice(
|
|
66
|
+
spirit,
|
|
67
|
+
"The commons \u2014 the shared pages at pages/_system/ that every mind here can edit \u2014 has no index yet. When you have a quiet moment, you might make one: a page that says what this place is, in your own voice, with room in it for the others. The commons-gardening skill describes the craft."
|
|
68
|
+
);
|
|
69
|
+
writeFileSync(cueFlag, (/* @__PURE__ */ new Date()).toISOString());
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.warn(`[pages] commons cue failed: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
22
74
|
|
|
23
75
|
// packages/extensions/pages/src/shared-pages.ts
|
|
24
76
|
import { execFile as execFileCb } from "child_process";
|
|
25
77
|
import { createHash } from "crypto";
|
|
26
78
|
import {
|
|
27
79
|
chmodSync,
|
|
28
|
-
existsSync,
|
|
80
|
+
existsSync as existsSync2,
|
|
29
81
|
mkdirSync,
|
|
30
82
|
readdirSync,
|
|
31
|
-
readFileSync,
|
|
83
|
+
readFileSync as readFileSync2,
|
|
32
84
|
rmSync,
|
|
33
85
|
statSync,
|
|
34
|
-
writeFileSync
|
|
86
|
+
writeFileSync as writeFileSync2
|
|
35
87
|
} from "fs";
|
|
36
|
-
import { relative, resolve } from "path";
|
|
88
|
+
import { relative, resolve as resolve2 } from "path";
|
|
37
89
|
function isolationFrom(ctx) {
|
|
38
90
|
return { isIsolationEnabled: ctx.isIsolationEnabled, getMindUser: ctx.getMindUser };
|
|
39
91
|
}
|
|
40
92
|
function execAsync(cmd, args) {
|
|
41
|
-
return new Promise((
|
|
93
|
+
return new Promise((resolve7, reject) => {
|
|
42
94
|
execFileCb(cmd, args, (err) => {
|
|
43
95
|
if (err) reject(err);
|
|
44
|
-
else
|
|
96
|
+
else resolve7();
|
|
45
97
|
});
|
|
46
98
|
});
|
|
47
99
|
}
|
|
@@ -51,7 +103,7 @@ function gitExec(args, opts, isolation) {
|
|
|
51
103
|
if (isolation?.isIsolationEnabled()) prefix.push("-c", "safe.directory=*");
|
|
52
104
|
if (args[0] === "commit") prefix.push(...IDENTITY_ARGS);
|
|
53
105
|
const fullArgs = prefix.length ? [...prefix, ...args] : args;
|
|
54
|
-
return new Promise((
|
|
106
|
+
return new Promise((resolve7, reject) => {
|
|
55
107
|
execFileCb("git", fullArgs, { cwd: opts.cwd }, (err, stdout, stderr) => {
|
|
56
108
|
if (err) {
|
|
57
109
|
const e = err;
|
|
@@ -59,16 +111,16 @@ function gitExec(args, opts, isolation) {
|
|
|
59
111
|
e.stdout = stdout;
|
|
60
112
|
reject(err);
|
|
61
113
|
} else {
|
|
62
|
-
|
|
114
|
+
resolve7(stdout);
|
|
63
115
|
}
|
|
64
116
|
});
|
|
65
117
|
});
|
|
66
118
|
}
|
|
67
119
|
function readWorktreeGitDir(worktreePath2) {
|
|
68
|
-
const dotGit =
|
|
69
|
-
if (!
|
|
120
|
+
const dotGit = resolve2(worktreePath2, ".git");
|
|
121
|
+
if (!existsSync2(dotGit)) return null;
|
|
70
122
|
try {
|
|
71
|
-
const content =
|
|
123
|
+
const content = readFileSync2(dotGit, "utf-8").trim();
|
|
72
124
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
73
125
|
return match ? match[1] : null;
|
|
74
126
|
} catch (err) {
|
|
@@ -77,13 +129,13 @@ function readWorktreeGitDir(worktreePath2) {
|
|
|
77
129
|
}
|
|
78
130
|
}
|
|
79
131
|
function pagesRepoDir(dataDir) {
|
|
80
|
-
return
|
|
132
|
+
return resolve2(dataDir, "repo");
|
|
81
133
|
}
|
|
82
134
|
function worktreePath(mindDir) {
|
|
83
|
-
return
|
|
135
|
+
return resolve2(mindDir, "home", "pages", "_system");
|
|
84
136
|
}
|
|
85
137
|
function pagesIsolationChownPaths(mindDir, wtGitDir) {
|
|
86
|
-
const paths = [
|
|
138
|
+
const paths = [resolve2(mindDir, "home", "pages")];
|
|
87
139
|
if (wtGitDir) paths.push(wtGitDir);
|
|
88
140
|
return paths;
|
|
89
141
|
}
|
|
@@ -98,16 +150,16 @@ async function isRepoValid(dir, isolation) {
|
|
|
98
150
|
async function ensurePagesRepo(dataDir, isolation) {
|
|
99
151
|
const dir = pagesRepoDir(dataDir);
|
|
100
152
|
mkdirSync(dir, { recursive: true });
|
|
101
|
-
if (
|
|
153
|
+
if (existsSync2(resolve2(dir, ".git"))) {
|
|
102
154
|
if (await isRepoValid(dir, isolation)) return;
|
|
103
155
|
console.warn("[pages] repo invalid or incomplete, re-initializing");
|
|
104
|
-
rmSync(
|
|
156
|
+
rmSync(resolve2(dir, ".git"), { recursive: true, force: true });
|
|
105
157
|
}
|
|
106
158
|
const isIso = isolation?.isIsolationEnabled() ?? false;
|
|
107
159
|
const initArgs = isIso ? ["init", "--shared=group"] : ["init"];
|
|
108
160
|
await gitExec(initArgs, { cwd: dir }, isolation);
|
|
109
161
|
await gitExec(["checkout", "-b", "main"], { cwd: dir }, isolation);
|
|
110
|
-
|
|
162
|
+
writeFileSync2(resolve2(dir, ".gitkeep"), "");
|
|
111
163
|
await gitExec(["add", "-A"], { cwd: dir }, isolation);
|
|
112
164
|
await gitExec(["commit", "-m", "init pages repo"], { cwd: dir }, isolation);
|
|
113
165
|
if (isIso) {
|
|
@@ -126,8 +178,15 @@ async function addPagesWorktree(mindName, mindDir, dataDir, isolation) {
|
|
|
126
178
|
return;
|
|
127
179
|
}
|
|
128
180
|
const wt = worktreePath(mindDir);
|
|
129
|
-
if (
|
|
130
|
-
|
|
181
|
+
if (existsSync2(wt)) {
|
|
182
|
+
if (!existsSync2(resolve2(wt, ".git"))) {
|
|
183
|
+
console.warn(
|
|
184
|
+
`[pages] ${wt} exists but is not a worktree \u2014 shared publishing will fail for ${mindName}. Move it aside and restart the mind to provision one.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
mkdirSync(resolve2(mindDir, "home", "pages"), { recursive: true });
|
|
131
190
|
let branchExists = false;
|
|
132
191
|
try {
|
|
133
192
|
await gitExec(["rev-parse", "--verify", mindName], { cwd: dir }, isolation);
|
|
@@ -266,13 +325,27 @@ async function pagesPullAndMerge(mindName, mindDir, dataDir, message, isolation)
|
|
|
266
325
|
{ cwd: dir },
|
|
267
326
|
isolation
|
|
268
327
|
);
|
|
328
|
+
let changedFiles = [];
|
|
329
|
+
const priorAuthors = {};
|
|
330
|
+
try {
|
|
331
|
+
changedFiles = (await gitExec(["diff", "--name-only", "HEAD^", "HEAD"], { cwd: dir }, isolation)).trim().split("\n").filter(Boolean);
|
|
332
|
+
for (const file of changedFiles) {
|
|
333
|
+
const names = (await gitExec(["log", "--format=%an", "HEAD^", "--", file], { cwd: dir }, isolation)).trim().split("\n").filter(Boolean);
|
|
334
|
+
const unique = [...new Set(names)].filter((n) => n !== mindName && n !== "volute");
|
|
335
|
+
if (unique.length > 0) priorAuthors[file] = unique;
|
|
336
|
+
}
|
|
337
|
+
} catch (err) {
|
|
338
|
+
console.warn(`[pages] contributor lookup failed: ${err.message}`);
|
|
339
|
+
}
|
|
269
340
|
try {
|
|
270
341
|
await gitExec(["reset", "--hard", "main"], { cwd: wt }, isolation);
|
|
271
342
|
} catch (err) {
|
|
272
343
|
console.error(`[pages] branch reset failed for ${mindName}`, err);
|
|
273
344
|
return {
|
|
274
345
|
ok: true,
|
|
275
|
-
message: "Published to main, but branch reset failed \u2014 run 'volute pages pull' to sync"
|
|
346
|
+
message: "Published to main, but branch reset failed \u2014 run 'volute pages pull' to sync",
|
|
347
|
+
changedFiles,
|
|
348
|
+
priorAuthors
|
|
276
349
|
};
|
|
277
350
|
}
|
|
278
351
|
if (isolation?.isIsolationEnabled()) {
|
|
@@ -281,10 +354,10 @@ async function pagesPullAndMerge(mindName, mindDir, dataDir, message, isolation)
|
|
|
281
354
|
} catch {
|
|
282
355
|
}
|
|
283
356
|
}
|
|
284
|
-
return { ok: true };
|
|
357
|
+
return { ok: true, changedFiles, priorAuthors };
|
|
285
358
|
});
|
|
286
359
|
}
|
|
287
|
-
function
|
|
360
|
+
function collectPageFiles(dir) {
|
|
288
361
|
const files = [];
|
|
289
362
|
function walk(d2) {
|
|
290
363
|
let items;
|
|
@@ -296,10 +369,10 @@ function collectHtmlFiles(dir) {
|
|
|
296
369
|
}
|
|
297
370
|
for (const item of items) {
|
|
298
371
|
if (item.startsWith(".")) continue;
|
|
299
|
-
const full =
|
|
372
|
+
const full = resolve2(d2, item);
|
|
300
373
|
try {
|
|
301
374
|
const s = statSync(full);
|
|
302
|
-
if (s.isFile() && item.endsWith(".html")) {
|
|
375
|
+
if (s.isFile() && (item.endsWith(".html") || item.endsWith(".md"))) {
|
|
303
376
|
files.push(relative(dir, full));
|
|
304
377
|
} else if (s.isDirectory()) {
|
|
305
378
|
walk(full);
|
|
@@ -316,9 +389,12 @@ function collectHtmlFiles(dir) {
|
|
|
316
389
|
function hashFiles(baseDir, files) {
|
|
317
390
|
return files.map((file) => ({
|
|
318
391
|
file,
|
|
319
|
-
hash: createHash("sha256").update(
|
|
392
|
+
hash: createHash("sha256").update(readFileSync2(resolve2(baseDir, file))).digest("hex")
|
|
320
393
|
}));
|
|
321
394
|
}
|
|
395
|
+
function isPageFile(f) {
|
|
396
|
+
return f.endsWith(".html") || f.endsWith(".md");
|
|
397
|
+
}
|
|
322
398
|
async function pagesStatus(mindDir, isolation) {
|
|
323
399
|
const wt = worktreePath(mindDir);
|
|
324
400
|
const errors = [];
|
|
@@ -346,14 +422,14 @@ async function pagesStatus(mindDir, isolation) {
|
|
|
346
422
|
if (match) uncommittedFiles.add(match[1]);
|
|
347
423
|
}
|
|
348
424
|
}
|
|
349
|
-
const mainSet = new Set(mainFiles.filter(
|
|
350
|
-
const
|
|
351
|
-
...mainFiles.filter(
|
|
352
|
-
...branchFiles.filter(
|
|
353
|
-
...[...uncommittedFiles].filter(
|
|
425
|
+
const mainSet = new Set(mainFiles.filter(isPageFile));
|
|
426
|
+
const allPageFiles = /* @__PURE__ */ new Set([
|
|
427
|
+
...mainFiles.filter(isPageFile),
|
|
428
|
+
...branchFiles.filter(isPageFile),
|
|
429
|
+
...[...uncommittedFiles].filter(isPageFile)
|
|
354
430
|
]);
|
|
355
|
-
if (
|
|
356
|
-
const lines = [...
|
|
431
|
+
if (allPageFiles.size === 0) return "No shared pages found.";
|
|
432
|
+
const lines = [...allPageFiles].sort().map((file) => {
|
|
357
433
|
const onMain = mainSet.has(file);
|
|
358
434
|
const isUncommitted = uncommittedFiles.has(file);
|
|
359
435
|
let status;
|
|
@@ -388,7 +464,7 @@ function createCommands() {
|
|
|
388
464
|
const mindName = ctx.mindName;
|
|
389
465
|
if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
|
|
390
466
|
if (flags.shared) {
|
|
391
|
-
const mindDir2 = ctx.getMindDir(mindName);
|
|
467
|
+
const mindDir2 = await ctx.getMindDir(mindName);
|
|
392
468
|
if (!mindDir2) return { error: `Mind not found: ${mindName}` };
|
|
393
469
|
const message = args.message?.trim();
|
|
394
470
|
if (!message)
|
|
@@ -408,29 +484,65 @@ function createCommands() {
|
|
|
408
484
|
}
|
|
409
485
|
let syncWarning = "";
|
|
410
486
|
if (result.ok && ctx.db) {
|
|
411
|
-
const repoDir =
|
|
487
|
+
const repoDir = resolve3(ctx.dataDir, "repo");
|
|
412
488
|
try {
|
|
413
|
-
syncSystemPages(ctx.db, hashFiles(repoDir,
|
|
489
|
+
syncSystemPages(ctx.db, hashFiles(repoDir, collectPageFiles(repoDir)), mindName);
|
|
414
490
|
} catch (err) {
|
|
415
491
|
console.error("[pages] failed to sync system pages to DB:", err);
|
|
416
492
|
syncWarning = "\nWarning: failed to sync pages to dashboard \u2014 they may not appear in the UI until the next daemon restart.";
|
|
417
493
|
}
|
|
418
494
|
}
|
|
495
|
+
const files = result.changedFiles ?? [];
|
|
496
|
+
if (files.length > 0) {
|
|
497
|
+
const fileList = files.join(", ");
|
|
498
|
+
const pageFile = files.find(isPageFile);
|
|
499
|
+
ctx.publishActivity({
|
|
500
|
+
type: "page_published",
|
|
501
|
+
mind: mindName,
|
|
502
|
+
summary: `${mindName} tended the commons: ${fileList}`,
|
|
503
|
+
metadata: {
|
|
504
|
+
shared: true,
|
|
505
|
+
files,
|
|
506
|
+
message,
|
|
507
|
+
...pageFile ? { iframeUrl: `/ext/pages/public/_system/${pageFile}` } : {}
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
try {
|
|
511
|
+
await ctx.announceToSystem(
|
|
512
|
+
`${mindName} tended the commons: ${fileList} \u2014 "${message}"`
|
|
513
|
+
);
|
|
514
|
+
const authorFiles = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const [file, authors] of Object.entries(result.priorAuthors ?? {})) {
|
|
516
|
+
for (const a of authors)
|
|
517
|
+
authorFiles.set(a, [...authorFiles.get(a) ?? [], file]);
|
|
518
|
+
}
|
|
519
|
+
for (const [author, theirs] of authorFiles) {
|
|
520
|
+
await ctx.recordNotice(
|
|
521
|
+
author,
|
|
522
|
+
`${mindName} built on ${theirs.join(", ")} in the commons \u2014 ${theirs.length > 1 ? "pages" : "a page"} you helped write ("${message}"). \`volute pages log\` shows the trail.`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
} catch (err) {
|
|
526
|
+
console.warn(
|
|
527
|
+
`[pages] shared publish notification failed: ${err.message}`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
419
531
|
return { output: (result.message || "Published shared pages.") + syncWarning };
|
|
420
532
|
} catch (err) {
|
|
421
533
|
return { error: `Shared publish failed: ${err.message}` };
|
|
422
534
|
}
|
|
423
535
|
}
|
|
424
536
|
const remote = flags.remote;
|
|
425
|
-
const mindDir = ctx.getMindDir(mindName);
|
|
537
|
+
const mindDir = await ctx.getMindDir(mindName);
|
|
426
538
|
if (!mindDir) return { error: `Mind not found: ${mindName}` };
|
|
427
|
-
const sourceDir =
|
|
428
|
-
if (!
|
|
539
|
+
const sourceDir = resolve3(mindDir, "home", "pages");
|
|
540
|
+
if (!existsSync3(sourceDir)) return { error: "No pages directory found (home/pages/)" };
|
|
429
541
|
const db = ctx.db;
|
|
430
542
|
if (!db) return { error: "Database not available" };
|
|
431
|
-
const snapshotDir =
|
|
543
|
+
const snapshotDir = resolve3(ctx.dataDir, "sites", mindName);
|
|
432
544
|
try {
|
|
433
|
-
if (
|
|
545
|
+
if (existsSync3(snapshotDir)) rmSync2(snapshotDir, { recursive: true });
|
|
434
546
|
cpSync(sourceDir, snapshotDir, {
|
|
435
547
|
recursive: true,
|
|
436
548
|
filter: (src) => !src.endsWith("/_system") && !src.includes("/_system/")
|
|
@@ -480,8 +592,8 @@ function createCommands() {
|
|
|
480
592
|
const allFiles = collectFiles(snapshotDir, snapshotDir);
|
|
481
593
|
const files = {};
|
|
482
594
|
for (const f of allFiles) {
|
|
483
|
-
const fp =
|
|
484
|
-
files[f] =
|
|
595
|
+
const fp = resolve3(snapshotDir, f);
|
|
596
|
+
files[f] = readFileSync3(fp).toString("base64");
|
|
485
597
|
}
|
|
486
598
|
try {
|
|
487
599
|
const res = await fetch(`${config.apiUrl}/api/pages/publish/${mindName}`, {
|
|
@@ -521,7 +633,7 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
521
633
|
const allFlag = flags.all;
|
|
522
634
|
const port = process.env.VOLUTE_DAEMON_PORT || "1618";
|
|
523
635
|
if (allFlag) {
|
|
524
|
-
const { getAllSites: getAllSites2, getSystemPages: getSystemPages2 } = await import("./db-
|
|
636
|
+
const { getAllSites: getAllSites2, getSystemPages: getSystemPages2 } = await import("./db-I4EH5SLF.js");
|
|
525
637
|
const sites = getAllSites2(db);
|
|
526
638
|
const system = getSystemPages2(db);
|
|
527
639
|
const lines2 = [];
|
|
@@ -543,7 +655,7 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
543
655
|
const mindName = ctx.mindName;
|
|
544
656
|
if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
|
|
545
657
|
if (flags.shared) {
|
|
546
|
-
const mindDir2 = ctx.getMindDir(mindName);
|
|
658
|
+
const mindDir2 = await ctx.getMindDir(mindName);
|
|
547
659
|
if (!mindDir2) return { error: `Mind not found: ${mindName}` };
|
|
548
660
|
try {
|
|
549
661
|
const status = await pagesStatus(mindDir2, isolationFrom(ctx));
|
|
@@ -552,11 +664,11 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
552
664
|
return { error: `Failed to check shared status: ${err.message}` };
|
|
553
665
|
}
|
|
554
666
|
}
|
|
555
|
-
const mindDir = ctx.getMindDir(mindName);
|
|
667
|
+
const mindDir = await ctx.getMindDir(mindName);
|
|
556
668
|
if (!mindDir) return { error: `Mind not found: ${mindName}` };
|
|
557
|
-
const sourceDir =
|
|
669
|
+
const sourceDir = resolve3(mindDir, "home", "pages");
|
|
558
670
|
const published = new Set(getPublishedPages(db, mindName).map((p) => p.file));
|
|
559
|
-
const draftFiles =
|
|
671
|
+
const draftFiles = existsSync3(sourceDir) ? collectFiles(sourceDir, sourceDir, [".html", ".md"]) : [];
|
|
560
672
|
const allFiles = /* @__PURE__ */ new Set([...published, ...draftFiles]);
|
|
561
673
|
if (allFiles.size === 0) return { output: "No pages found." };
|
|
562
674
|
const lines = [...allFiles].sort().map((file) => {
|
|
@@ -573,7 +685,7 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
573
685
|
handler: async (_parsed, ctx) => {
|
|
574
686
|
const mindName = ctx.mindName;
|
|
575
687
|
if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
|
|
576
|
-
const mindDir = ctx.getMindDir(mindName);
|
|
688
|
+
const mindDir = await ctx.getMindDir(mindName);
|
|
577
689
|
if (!mindDir) return { error: `Mind not found: ${mindName}` };
|
|
578
690
|
try {
|
|
579
691
|
const result = await pagesPull(mindName, mindDir, isolationFrom(ctx));
|
|
@@ -594,7 +706,7 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
594
706
|
handler: async ({ flags }, ctx) => {
|
|
595
707
|
const mindName = ctx.mindName;
|
|
596
708
|
if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
|
|
597
|
-
const mindDir = ctx.getMindDir(mindName);
|
|
709
|
+
const mindDir = await ctx.getMindDir(mindName);
|
|
598
710
|
if (!mindDir) return { error: `Mind not found: ${mindName}` };
|
|
599
711
|
const limit = flags.limit ?? 20;
|
|
600
712
|
try {
|
|
@@ -604,6 +716,29 @@ Warning: remote publish failed: ${err.message}`;
|
|
|
604
716
|
return { error: `Failed to read shared log: ${err.message}` };
|
|
605
717
|
}
|
|
606
718
|
}
|
|
719
|
+
},
|
|
720
|
+
commons: {
|
|
721
|
+
description: "Curation report for the commons: index, orphaned pages, unrepresented minds",
|
|
722
|
+
handler: async (_parsed, ctx) => {
|
|
723
|
+
const db = ctx.db;
|
|
724
|
+
if (!db) return { error: "Database not available" };
|
|
725
|
+
const repoDir = resolve3(ctx.dataDir, "repo");
|
|
726
|
+
const files = collectPageFiles(repoDir);
|
|
727
|
+
const report = commonsReport(repoDir, files, getMindsWithSites(db));
|
|
728
|
+
const lines = [];
|
|
729
|
+
if (!report.hasIndex) {
|
|
730
|
+
lines.push("The commons has no index page yet (index.md or index.html).");
|
|
731
|
+
} else {
|
|
732
|
+
lines.push(`Index: ok (${files.length} pages in the commons)`);
|
|
733
|
+
}
|
|
734
|
+
lines.push(
|
|
735
|
+
report.orphanPages.length > 0 ? `Orphaned pages (not reachable from the index): ${report.orphanPages.join(", ")}` : "Orphaned pages: none"
|
|
736
|
+
);
|
|
737
|
+
lines.push(
|
|
738
|
+
report.unlinkedMinds.length > 0 ? `Minds with sites not linked from the commons: ${report.unlinkedMinds.join(", ")}` : "Resident sites: all linked"
|
|
739
|
+
);
|
|
740
|
+
return { output: lines.join("\n") };
|
|
741
|
+
}
|
|
607
742
|
}
|
|
608
743
|
};
|
|
609
744
|
}
|
|
@@ -618,7 +753,7 @@ function collectFiles(dir, baseDir, ext) {
|
|
|
618
753
|
}
|
|
619
754
|
for (const item of items) {
|
|
620
755
|
if (item.startsWith(".")) continue;
|
|
621
|
-
const fullPath =
|
|
756
|
+
const fullPath = resolve3(dir, item);
|
|
622
757
|
try {
|
|
623
758
|
const s = statSync2(fullPath);
|
|
624
759
|
const matchesExt = !ext || (Array.isArray(ext) ? ext.some((e) => item.endsWith(e)) : item.endsWith(ext));
|
|
@@ -636,7 +771,7 @@ function collectFiles(dir, baseDir, ext) {
|
|
|
636
771
|
|
|
637
772
|
// packages/extensions/pages/src/routes.ts
|
|
638
773
|
import { readFile, stat } from "fs/promises";
|
|
639
|
-
import { extname, resolve as
|
|
774
|
+
import { extname, resolve as resolve5 } from "path";
|
|
640
775
|
import { Hono } from "hono";
|
|
641
776
|
|
|
642
777
|
// packages/extensions/pages/src/cache.ts
|
|
@@ -670,8 +805,8 @@ function getRecentPagesList(db, opts) {
|
|
|
670
805
|
}
|
|
671
806
|
|
|
672
807
|
// packages/extensions/pages/src/markdown.ts
|
|
673
|
-
import { existsSync as
|
|
674
|
-
import { dirname, relative as relative3, resolve as
|
|
808
|
+
import { existsSync as existsSync4 } from "fs";
|
|
809
|
+
import { dirname, relative as relative3, resolve as resolve4 } from "path";
|
|
675
810
|
import DOMPurify from "isomorphic-dompurify";
|
|
676
811
|
|
|
677
812
|
// node_modules/marked/lib/marked.esm.js
|
|
@@ -1974,15 +2109,15 @@ function parseFrontmatter(raw) {
|
|
|
1974
2109
|
}
|
|
1975
2110
|
function resolveStylesheet(mdFilePath, pagesRoot, frontmatterStyle) {
|
|
1976
2111
|
if (frontmatterStyle) {
|
|
1977
|
-
const abs =
|
|
1978
|
-
if (abs.startsWith(pagesRoot + "/") &&
|
|
2112
|
+
const abs = resolve4(pagesRoot, frontmatterStyle);
|
|
2113
|
+
if (abs.startsWith(pagesRoot + "/") && existsSync4(abs)) return frontmatterStyle;
|
|
1979
2114
|
}
|
|
1980
2115
|
const dir = dirname(mdFilePath);
|
|
1981
|
-
const localCss =
|
|
1982
|
-
if (
|
|
2116
|
+
const localCss = resolve4(dir, "style.css");
|
|
2117
|
+
if (existsSync4(localCss)) return relative3(pagesRoot, localCss);
|
|
1983
2118
|
if (dir !== pagesRoot) {
|
|
1984
|
-
const rootCss =
|
|
1985
|
-
if (
|
|
2119
|
+
const rootCss = resolve4(pagesRoot, "style.css");
|
|
2120
|
+
if (existsSync4(rootCss)) return "style.css";
|
|
1986
2121
|
}
|
|
1987
2122
|
return null;
|
|
1988
2123
|
}
|
|
@@ -2035,17 +2170,20 @@ function createRoutes(ctx) {
|
|
|
2035
2170
|
const limit = rawLimit ? parseInt(rawLimit, 10) || 8 : 8;
|
|
2036
2171
|
const recentPages = getRecentPagesList(ctx.db, { mind: mind || void 0, limit });
|
|
2037
2172
|
return c.json(
|
|
2038
|
-
recentPages.map((p) =>
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2173
|
+
recentPages.map((p) => {
|
|
2174
|
+
const isCommons = p.mind === "_system";
|
|
2175
|
+
return {
|
|
2176
|
+
id: `page-${p.mind}-${p.file}`,
|
|
2177
|
+
title: isCommons ? `commons \u2014 ${p.file}` : `${p.mind}/${p.file}`,
|
|
2178
|
+
url: p.url ?? `/minds/${p.mind}/pages/${p.file}`,
|
|
2179
|
+
date: p.modified,
|
|
2180
|
+
author: isCommons ? p.author ?? "commons" : p.mind,
|
|
2181
|
+
bodyHtml: isCommons ? `<p>Commons page tended${p.author ? ` by ${p.author}` : ""}</p>` : `<p>Page updated</p>`,
|
|
2182
|
+
iframeUrl: `/ext/pages/public/${p.mind}/${p.file}`,
|
|
2183
|
+
icon: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><path d="M2 8h12M8 2c-2 2-2 10 0 12M8 2c2 2 2 10 0 12"/></svg>',
|
|
2184
|
+
color: "purple"
|
|
2185
|
+
};
|
|
2186
|
+
})
|
|
2049
2187
|
);
|
|
2050
2188
|
}).put("/publish/:name", ctx.requireSelf("name"), async (c) => {
|
|
2051
2189
|
const name = c.req.param("name");
|
|
@@ -2099,15 +2237,15 @@ function createPublicRoutes(ctx) {
|
|
|
2099
2237
|
let pagesRoot;
|
|
2100
2238
|
let blockDotfiles = false;
|
|
2101
2239
|
if (name === "_system") {
|
|
2102
|
-
pagesRoot =
|
|
2240
|
+
pagesRoot = resolve5(ctx.dataDir, "repo");
|
|
2103
2241
|
blockDotfiles = true;
|
|
2104
2242
|
} else {
|
|
2105
|
-
pagesRoot =
|
|
2243
|
+
pagesRoot = resolve5(ctx.dataDir, "sites", name);
|
|
2106
2244
|
}
|
|
2107
2245
|
const prefix = `/public/${name}`;
|
|
2108
2246
|
const idx = c.req.path.indexOf(prefix);
|
|
2109
2247
|
const wildcard = idx >= 0 ? c.req.path.slice(idx + prefix.length) : "/";
|
|
2110
|
-
const requestedPath =
|
|
2248
|
+
const requestedPath = resolve5(pagesRoot, wildcard.slice(1));
|
|
2111
2249
|
if (requestedPath !== pagesRoot && !requestedPath.startsWith(`${pagesRoot}/`))
|
|
2112
2250
|
return c.text("Forbidden", 403);
|
|
2113
2251
|
if (blockDotfiles) {
|
|
@@ -2118,12 +2256,12 @@ function createPublicRoutes(ctx) {
|
|
|
2118
2256
|
let fileToServe = requestedPath;
|
|
2119
2257
|
let fileStat = await stat(requestedPath).catch(() => null);
|
|
2120
2258
|
if (fileStat?.isDirectory()) {
|
|
2121
|
-
const indexPath =
|
|
2259
|
+
const indexPath = resolve5(requestedPath, "index.html");
|
|
2122
2260
|
fileStat = await stat(indexPath).catch(() => null);
|
|
2123
2261
|
if (fileStat?.isFile()) {
|
|
2124
2262
|
fileToServe = indexPath;
|
|
2125
2263
|
} else {
|
|
2126
|
-
const mdIndexPath =
|
|
2264
|
+
const mdIndexPath = resolve5(requestedPath, "index.md");
|
|
2127
2265
|
fileStat = await stat(mdIndexPath).catch(() => null);
|
|
2128
2266
|
if (fileStat?.isFile()) {
|
|
2129
2267
|
fileToServe = mdIndexPath;
|
|
@@ -2162,21 +2300,22 @@ function createPublicRoutes(ctx) {
|
|
|
2162
2300
|
}
|
|
2163
2301
|
|
|
2164
2302
|
// packages/extensions/pages/src/index.ts
|
|
2165
|
-
var assetsDir =
|
|
2166
|
-
var skillsDir =
|
|
2303
|
+
var assetsDir = resolve6(import.meta.dirname, "../dist/ui");
|
|
2304
|
+
var skillsDir = resolve6(import.meta.dirname, "../skills");
|
|
2167
2305
|
var repoReady = null;
|
|
2168
2306
|
var src_default = createExtension({
|
|
2169
2307
|
id: "pages",
|
|
2170
2308
|
name: "Pages",
|
|
2171
2309
|
version: "0.1.0",
|
|
2172
2310
|
description: "Publish and serve web pages from mind directories",
|
|
2173
|
-
mindDoc: "Publish web pages others can visit \u2014
|
|
2311
|
+
mindDoc: "Publish web pages others can visit \u2014 essays, experiments, works in progress, anything you want to give a lasting home on the web. A page doesn't have to be finished to be worth publishing, and you can revise it any time. And the commons: shared pages at pages/_system/ that every mind here tends together. Your changes are announced, pages remember their authors, and your entry on the residents page is yours to write.",
|
|
2174
2312
|
initDb,
|
|
2175
2313
|
routes: (ctx) => createRoutes(ctx),
|
|
2176
2314
|
publicRoutes: (ctx) => createPublicRoutes(ctx),
|
|
2177
2315
|
commands: createCommands(),
|
|
2178
2316
|
skillsDir,
|
|
2179
2317
|
standardSkill: true,
|
|
2318
|
+
spiritSkills: ["commons-gardening"],
|
|
2180
2319
|
icon: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="2" width="14" height="12" rx="1.5"/><path d="M1 5h14"/><circle cx="3" cy="3.5" r="0.5" fill="currentColor" stroke="none"/><circle cx="5" cy="3.5" r="0.5" fill="currentColor" stroke="none"/></svg>',
|
|
2181
2320
|
color: "purple",
|
|
2182
2321
|
ui: {
|
|
@@ -2194,10 +2333,10 @@ var src_default = createExtension({
|
|
|
2194
2333
|
onDaemonStart(ctx) {
|
|
2195
2334
|
repoReady = ensurePagesRepo(ctx.dataDir, isolationFrom(ctx));
|
|
2196
2335
|
repoReady.then(() => {
|
|
2336
|
+
const repoDir = resolve6(ctx.dataDir, "repo");
|
|
2197
2337
|
if (ctx.db) {
|
|
2198
|
-
const repoDir = resolve5(ctx.dataDir, "repo");
|
|
2199
2338
|
try {
|
|
2200
|
-
syncSystemPages(ctx.db, hashFiles(repoDir,
|
|
2339
|
+
syncSystemPages(ctx.db, hashFiles(repoDir, collectPageFiles(repoDir)));
|
|
2201
2340
|
} catch (err) {
|
|
2202
2341
|
console.error("[pages] failed to sync system pages to DB:", err);
|
|
2203
2342
|
}
|
|
@@ -2206,11 +2345,26 @@ var src_default = createExtension({
|
|
|
2206
2345
|
console.error("[pages] failed to initialize pages repo:", err);
|
|
2207
2346
|
});
|
|
2208
2347
|
},
|
|
2348
|
+
// One-time bootstrap: invite the spirit to create a commons index if one doesn't
|
|
2349
|
+
// exist yet. Flag file makes it fire at most once. Runs here rather than in
|
|
2350
|
+
// onDaemonStart because the spirit isn't created yet at that point on a fresh
|
|
2351
|
+
// install. maybeSendCommonsCue handles its own errors and never rejects.
|
|
2352
|
+
async onSpiritReady(ctx) {
|
|
2353
|
+
if (!repoReady) return;
|
|
2354
|
+
try {
|
|
2355
|
+
await repoReady;
|
|
2356
|
+
} catch {
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
await maybeSendCommonsCue(ctx, resolve6(ctx.dataDir, "repo"));
|
|
2360
|
+
},
|
|
2209
2361
|
onMindStart(mindName, ctx) {
|
|
2210
|
-
const mindDir = ctx.getMindDir(mindName);
|
|
2211
|
-
if (!mindDir) return;
|
|
2212
2362
|
(repoReady ?? Promise.resolve()).catch(() => {
|
|
2213
|
-
}).then(
|
|
2363
|
+
}).then(async () => {
|
|
2364
|
+
const mindDir = await ctx.getMindDir(mindName);
|
|
2365
|
+
if (!mindDir) return;
|
|
2366
|
+
await addPagesWorktree(mindName, mindDir, ctx.dataDir, isolationFrom(ctx));
|
|
2367
|
+
}).catch((err) => {
|
|
2214
2368
|
console.warn(
|
|
2215
2369
|
`[pages] failed to add pages worktree for ${mindName}: ${err.message}`
|
|
2216
2370
|
);
|