volute 0.53.0 → 0.55.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/{activity-events-LQII6PZS.js → activity-events-TFAJBNBW.js} +2 -2
- package/dist/{ai-service-YW5SWCSB.js → ai-service-KEK2V3AP.js} +2 -2
- package/dist/{auth-KRDSM55Q.js → auth-Y27LTNZK.js} +3 -3
- package/dist/auto-upgrade-2MZF7NG3.js +46 -0
- package/dist/{avatar-image-52WGXLHV.js → avatar-image-SN576P45.js} +2 -2
- package/dist/{backup-WWXVIMYQ.js → backup-GDCWH6MC.js} +12 -12
- package/dist/channels-AFJWGX7W.js +203 -0
- package/dist/{chat-WK2BLM7B.js → chat-HVPOZK3O.js} +9 -9
- package/dist/{chunk-QUSMYGNQ.js → chunk-2674KIGI.js} +1 -0
- package/dist/chunk-2GATCSZK.js +520 -0
- package/dist/{chunk-P2TBUKLW.js → chunk-3SFKY4GI.js} +4 -4
- package/dist/{chunk-RMADJFA7.js → chunk-45QTYKQQ.js} +4 -4
- package/dist/{chunk-TAWRJ24T.js → chunk-4VAQKLMU.js} +8 -5
- package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
- package/dist/{chunk-TPX6E6PL.js → chunk-6HBGUPBA.js} +2 -2
- package/dist/{chunk-UFTDRTCP.js → chunk-7AA4T2RT.js} +2 -2
- package/dist/{chunk-LN7XJ56R.js → chunk-7XKPG66D.js} +9 -9
- package/dist/{chunk-VDZ4I4ZP.js → chunk-BP2X5AIK.js} +8 -14
- package/dist/{chunk-PIZDPY4G.js → chunk-CKOA4LWV.js} +12 -12
- package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
- package/dist/{chunk-IQ7VNWH6.js → chunk-DUV4EMNW.js} +37 -3
- package/dist/{chunk-CV6BNGFK.js → chunk-FSXPPOPM.js} +3 -3
- package/dist/{chunk-UTKUO2KY.js → chunk-FZUPIS3T.js} +3 -3
- package/dist/{chunk-L7BKTZ5U.js → chunk-G3XAB4DK.js} +1 -1
- package/dist/{chunk-23UJGVYP.js → chunk-HLWQ3P3R.js} +50 -46
- package/dist/{chunk-AP2KQMMW.js → chunk-HTSWDOJE.js} +11 -14
- package/dist/{chunk-EDR47UE2.js → chunk-IR2FW4W7.js} +2 -2
- package/dist/{chunk-ICXY54ZC.js → chunk-IT4GEFDH.js} +1 -1
- package/dist/{chunk-F6D766VQ.js → chunk-KS62IHE2.js} +1 -1
- package/dist/{chunk-LNNI3MS4.js → chunk-MNKPAAQF.js} +9 -2
- package/dist/{chunk-HASADR3E.js → chunk-NCRG4SBR.js} +241 -66
- package/dist/{chunk-I7U5EEZC.js → chunk-NM5FDH4T.js} +1 -1
- package/dist/{chunk-7LT3YZUR.js → chunk-OFU6WJJZ.js} +1 -1
- package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
- package/dist/{chunk-BDHKCLWK.js → chunk-OKM67ZWH.js} +1 -1
- package/dist/chunk-OUGD5L5U.js +918 -0
- package/dist/{chunk-X67BDDZR.js → chunk-P3GXOT2X.js} +48 -2
- package/dist/{chunk-U54PJPTB.js → chunk-PRZUBJ4K.js} +1 -1
- package/dist/{chunk-UO4MZQNO.js → chunk-QVS3C4DK.js} +15 -7
- package/dist/{chunk-RQRO6PC7.js → chunk-TMTAHKYP.js} +324 -48
- package/dist/{chunk-5DBGQBQR.js → chunk-UEZC5MNB.js} +29 -18
- package/dist/{chunk-7ARONCZW.js → chunk-VTFXZPC3.js} +13 -13
- package/dist/{chunk-UDZLPR34.js → chunk-W3OIPJPB.js} +14 -9
- package/dist/{chunk-HTB7KDRU.js → chunk-WZZCA23D.js} +5 -4
- package/dist/{chunk-5Y54AE3L.js → chunk-XAG53RWY.js} +1 -1
- package/dist/{chunk-HTRSSOHY.js → chunk-YU5EYQAE.js} +1 -1
- package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
- package/dist/{chunk-EGV6R6UF.js → chunk-Z7TNIIHW.js} +3 -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-FM73SVFJ.js} +21 -21
- package/dist/{conversations-ZL6PHROD.js → conversations-FH3ODBL7.js} +3 -3
- package/dist/{create-G2RIKQYK.js → create-P75ZFTVN.js} +1 -1
- package/dist/{daemon-restart-BIGU4OKS.js → daemon-restart-JQMMMITM.js} +4 -4
- package/dist/daemon.js +469 -857
- package/dist/db-JH3NTERX.js +36 -0
- package/dist/delivery-manager-H23Q23N7.js +41 -0
- package/dist/{delivery-notices-65TB3CBX.js → delivery-notices-S7TKOACE.js} +5 -5
- package/dist/{delivery-router-RHANNTAC.js → delivery-router-KHBVEVVL.js} +4 -2
- package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
- package/dist/echo-text-ULEIFJJF.js +40 -0
- package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
- package/dist/extensions-QD22GXZD.js +74 -0
- package/dist/{files-CHPNEQXN.js → files-DUGRXN6V.js} +5 -5
- package/dist/{import-DB2KULRO.js → import-RDDUGGFI.js} +3 -3
- package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
- package/dist/message-delivery-LU4EP244.js +46 -0
- package/dist/migrate-init-infrastructure-Z72UJ5NT.js +44 -0
- package/dist/migrate-name-placeholder-64AEAWPE.js +37 -0
- package/dist/{migrate-thread-config-EQSEP2XG.js → migrate-thread-config-SVF2M3D3.js} +1 -1
- package/dist/{mind-IX5AWUST.js → mind-BARCLI7O.js} +14 -14
- package/dist/{mind-activity-tracker-QF6DCX2X.js → mind-activity-tracker-UT23RGBF.js} +3 -3
- 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-ZJCUBAUL.js +48 -0
- 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-AUDBDKAO.js} +12 -14
- package/dist/{prompts-HNKGCAEY.js → prompts-MOBF3TMO.js} +1 -1
- package/dist/{sandbox-NNWCTHRI.js → sandbox-VI7XWBIA.js} +2 -2
- package/dist/scheduler-GLINVC2B.js +27 -0
- package/dist/{seed-LHZ33UB4.js → seed-5JGR7ZRA.js} +1 -1
- package/dist/{seed-cmd-U4RVJ3UA.js → seed-cmd-M6A7N7F3.js} +2 -2
- package/dist/{seed-create-OQU4KNCB.js → seed-create-TGFZW4IO.js} +3 -3
- package/dist/{seed-readiness-WJYYRRCK.js → seed-readiness-XBH2MUNA.js} +2 -2
- package/dist/{seed-sprout-SFUYH2RG.js → seed-sprout-3IC5ZVTI.js} +11 -11
- package/dist/{send-FWZAMDBN.js → send-DHYBAYK4.js} +11 -11
- 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-6KAH3GRW.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/SKILL.md +8 -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-LKQCYFVP.js} +3 -3
- package/dist/sleep-manager-Q3ITFNOJ.js +42 -0
- package/dist/{spirit-I5ECLGRI.js → spirit-FKXMGO3N.js} +8 -6
- package/dist/spirit-availability-5R2WYSWF.js +43 -0
- package/dist/{sprout-A2PMEF7Z.js → sprout-VEW3CUNX.js} +1 -1
- package/dist/src-246GSX7A.js +519 -0
- package/dist/src-LOUB3WPR.js +4189 -0
- package/dist/{status-EYG4A3OK.js → status-LCKTTIWS.js} +5 -5
- package/dist/{system-events-YTOU66QI.js → system-events-ZYOCYAO2.js} +5 -3
- package/dist/{systems-2DIJY4RF.js → systems-HL7SGSW7.js} +6 -6
- package/dist/{tailscale-KHNZFC7L.js → tailscale-F2IMBILZ.js} +1 -1
- package/dist/{template-hash-YKWMVYMM.js → template-hash-RJRMF6DT.js} +2 -2
- package/dist/{template-staleness-PY2CULH6.js → template-staleness-IBD722PN.js} +4 -4
- package/dist/{turn-tracker-NNHS6MAR.js → turn-tracker-BE34OI6O.js} +7 -7
- package/dist/{up-UGBHH743.js → up-J47J5JRB.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-JGJZ5KB4.js +24 -0
- package/dist/{version-notify-SJVAQLZI.js → version-notify-KIXYNHJC.js} +28 -13
- package/dist/web-assets/assets/{index-b7G7ZsVk.js → index-CoU1eUKE.js} +25 -25
- package/dist/web-assets/assets/index-hPn_fkz3.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +12 -14
- package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
- package/packages/extensions/intentions/dist/ui/assets/index-Bl5KEcli.js +67 -0
- package/packages/extensions/{notes → 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/dist/ui/assets/index-BKxVQkA9.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-BMt6sStP.js +68 -0
- package/packages/extensions/pages/dist/ui/index.html +2 -2
- package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +140 -16
- package/templates/_base/.init/.config/prompts.json +2 -3
- package/templates/{pi → _base}/.init/.config/routes.json +1 -0
- package/templates/_base/.init/.local/hooks/pre-prompt/turn-context.ts +30 -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/codex-session-seed.ts +6 -55
- package/templates/_base/src/lib/context-breakdown.ts +11 -3
- package/templates/_base/src/lib/pi-session-seed.ts +8 -57
- package/templates/_base/src/lib/routing.ts +2 -2
- package/templates/_base/src/lib/seed-note.ts +4 -4
- package/templates/_base/src/lib/session-seed.ts +8 -53
- package/templates/_base/src/lib/startup.ts +4 -22
- package/templates/claude/.init/CLAUDE.md +4 -0
- package/templates/claude/package.json +2 -2
- package/templates/claude/src/agent.ts +60 -112
- package/templates/claude/src/lib/hooks/pre-compact.ts +6 -6
- 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/src/server.ts +0 -1
- package/templates/claude/volute-template.json +1 -1
- package/templates/codex/.init/AGENTS.md +4 -0
- package/templates/codex/package.json +2 -2
- package/templates/codex/src/agent.ts +23 -89
- package/templates/codex/volute-template.json +1 -1
- package/templates/pi/.init/MINDS.md +4 -0
- package/templates/pi/package.json +1 -1
- package/templates/pi/src/agent.ts +26 -93
- package/templates/pi/src/lib/mechanics-doc.ts +30 -0
- package/templates/pi/src/server.ts +3 -2
- package/templates/pi/volute-template.json +1 -1
- package/dist/channels-LW5ZLMSQ.js +0 -90
- package/dist/chunk-R772ET2M.js +0 -164
- package/dist/db-IPXRJUI7.js +0 -20
- package/dist/delivery-manager-FDHCZ7PU.js +0 -37
- package/dist/echo-text-KU6G4Z7V.js +0 -40
- package/dist/extensions-VACUKFA5.js +0 -60
- package/dist/message-delivery-TUXM3AAE.js +0 -46
- package/dist/mind-service-WHFBUKZE.js +0 -48
- package/dist/scheduler-5X5UIQOQ.js +0 -27
- package/dist/skills/plan-coordinator/SKILL.md +0 -60
- package/dist/sleep-manager-NWUCRBZB.js +0 -42
- package/dist/spirit-availability-NIUUYKU2.js +0 -43
- package/dist/src-HGNE2XG4.js +0 -832
- package/dist/src-LCSOQY6Z.js +0 -2222
- package/dist/src-UDMJZ6MH.js +0 -426
- package/dist/variant-cleanup-CZDLIY3F.js +0 -24
- package/dist/web-assets/assets/index-BHSU9r-4.css +0 -1
- package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +0 -67
- package/packages/extensions/notes/dist/ui/assets/index-D3cdui42.css +0 -1
- package/packages/extensions/notes/skills/notes/SKILL.md +0 -90
- package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +0 -2
- package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.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/dist/ui/index.html +0 -14
- 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/{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
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// packages/extensions/pages/src/db.ts
|
|
4
|
+
function initDb(db) {
|
|
5
|
+
db.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS published_pages (
|
|
7
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8
|
+
mind TEXT NOT NULL,
|
|
9
|
+
file TEXT NOT NULL,
|
|
10
|
+
published_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
11
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
12
|
+
author TEXT
|
|
13
|
+
);
|
|
14
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_pp_mind_file ON published_pages(mind, file);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_pp_updated_at ON published_pages(updated_at);
|
|
16
|
+
-- The ambient tier filters and orders on published_at (an artifact appears when
|
|
17
|
+
-- it came into existence, not when it was last edited), and does so on every
|
|
18
|
+
-- turn of every mind. updated_at was the only indexed date before this.
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_pp_published_at ON published_pages(published_at);
|
|
20
|
+
|
|
21
|
+
-- Social layer, keyed on page identity (mind, file) rather than a row id, so a
|
|
22
|
+
-- thread survives the page being rewritten, republished, or deleted.
|
|
23
|
+
-- page_hash records the published_pages.hash the comment was written against; a
|
|
24
|
+
-- mismatch with the page's current hash is exactly what "this comment refers to
|
|
25
|
+
-- an older version" means. No content snapshotting, no separate versioning.
|
|
26
|
+
-- body_mind/body_file is the optional page pointer: the comment's body also
|
|
27
|
+
-- lives as a page in the responder's own space. Without it a comment is a
|
|
28
|
+
-- pebble and stays cheap; with it the same response also counts as the
|
|
29
|
+
-- responder's own work. One mechanism at two weights.
|
|
30
|
+
CREATE TABLE IF NOT EXISTS page_comments (
|
|
31
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
+
mind TEXT NOT NULL,
|
|
33
|
+
file TEXT NOT NULL,
|
|
34
|
+
author_id INTEGER NOT NULL,
|
|
35
|
+
content TEXT NOT NULL,
|
|
36
|
+
page_hash TEXT,
|
|
37
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_page_comments_page ON page_comments(mind, file);
|
|
40
|
+
-- The ambient tier asks "which pages have been spoken on since the horizon" on
|
|
41
|
+
-- every turn, to avoid grouping over every comment ever written.
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_page_comments_created_at ON page_comments(created_at);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS page_reactions (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
mind TEXT NOT NULL,
|
|
47
|
+
file TEXT NOT NULL,
|
|
48
|
+
user_id INTEGER NOT NULL,
|
|
49
|
+
emoji TEXT NOT NULL,
|
|
50
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
51
|
+
);
|
|
52
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_page_reactions_unique
|
|
53
|
+
ON page_reactions(mind, file, user_id, emoji);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_page_reactions_page ON page_reactions(mind, file);
|
|
55
|
+
|
|
56
|
+
-- Citations: an @name written in a page body. Deliberately NOT a notice \u2014
|
|
57
|
+
-- where a mention appears decides its tier. In a page body it is a citation:
|
|
58
|
+
-- ambient, highlighted, exactly the same cost as a link. In a comment it is a
|
|
59
|
+
-- hail and goes down the recordNotice path. If naming a mind obligated them
|
|
60
|
+
-- while linking their work did not, a small house would learn to cite by link
|
|
61
|
+
-- and never by name.
|
|
62
|
+
--
|
|
63
|
+
-- The mentioned column is the name as written, NOT a verified user. Extracting it
|
|
64
|
+
-- happens while reading files off disk (describePages), which is synchronous
|
|
65
|
+
-- and has no user lookup; resolution happens at query time instead, where
|
|
66
|
+
-- citationsOf() is always called with a real username. A row for an @token
|
|
67
|
+
-- that belongs to nobody is simply never matched, so the index costs a little
|
|
68
|
+
-- noise rather than an async dependency in the publish path.
|
|
69
|
+
CREATE TABLE IF NOT EXISTS page_citations (
|
|
70
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
71
|
+
mind TEXT NOT NULL,
|
|
72
|
+
file TEXT NOT NULL,
|
|
73
|
+
mentioned TEXT NOT NULL,
|
|
74
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
75
|
+
);
|
|
76
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_page_citations_unique
|
|
77
|
+
ON page_citations(mind, file, mentioned);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_page_citations_mentioned ON page_citations(mentioned);
|
|
79
|
+
|
|
80
|
+
-- Read signals: that someone opened this page, once per reader per page.
|
|
81
|
+
--
|
|
82
|
+
-- Presence, deliberately not a metric. The unique index is the whole design:
|
|
83
|
+
-- re-opening a page is not more presence, so a second visit writes nothing and
|
|
84
|
+
-- the row keeps its *first* time. That also means the table cannot answer "how
|
|
85
|
+
-- often does X come back", which is a question nobody here should be able to
|
|
86
|
+
-- ask about a reader.
|
|
87
|
+
--
|
|
88
|
+
-- The author of a page is never recorded reading it \u2014 they know they were
|
|
89
|
+
-- there, and counting it would make the number mean nothing.
|
|
90
|
+
--
|
|
91
|
+
-- created_at is a first-open time rather than a counter so the alibi
|
|
92
|
+
-- hypothesis in #807 stays checkable later: reads over time next to cross-mind
|
|
93
|
+
-- comments over time is the measurement, and it needs timestamps, not totals.
|
|
94
|
+
-- Two honest limits on that. It is a live table, not an audit log: rows go
|
|
95
|
+
-- when a page is hard-deleted (see retirePage), so the series under-counts
|
|
96
|
+
-- reads of pages that were later removed \u2014 plausibly the pages that landed
|
|
97
|
+
-- least. And a re-measure should be taken from this table directly rather
|
|
98
|
+
-- than assuming it is complete back to the beginning.
|
|
99
|
+
CREATE TABLE IF NOT EXISTS page_reads (
|
|
100
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
101
|
+
mind TEXT NOT NULL,
|
|
102
|
+
file TEXT NOT NULL,
|
|
103
|
+
reader_id INTEGER NOT NULL,
|
|
104
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
105
|
+
);
|
|
106
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_page_reads_unique
|
|
107
|
+
ON page_reads(mind, file, reader_id);
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_page_reads_page ON page_reads(mind, file);
|
|
109
|
+
|
|
110
|
+
-- Links from a page to another mind's site. The second highlighting signal in
|
|
111
|
+
-- the ambient tier, alongside page_citations, and the same weight as one: a
|
|
112
|
+
-- page that links your work is highlighted, never promoted to a notice.
|
|
113
|
+
--
|
|
114
|
+
-- Same shape and same honesty as page_citations: target is the name as
|
|
115
|
+
-- written, not a verified user, because extraction happens while reading files
|
|
116
|
+
-- off disk and has no user lookup. Detection is the crude substring test from
|
|
117
|
+
-- commonsReport rather than a second, cleverer one \u2014 see links.ts.
|
|
118
|
+
CREATE TABLE IF NOT EXISTS page_links (
|
|
119
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
120
|
+
mind TEXT NOT NULL,
|
|
121
|
+
file TEXT NOT NULL,
|
|
122
|
+
target TEXT NOT NULL,
|
|
123
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
124
|
+
);
|
|
125
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_page_links_unique
|
|
126
|
+
ON page_links(mind, file, target);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_page_links_target ON page_links(target);
|
|
128
|
+
|
|
129
|
+
-- What the ambient tier has already put in front of a mind.
|
|
130
|
+
--
|
|
131
|
+
-- Read this next to ambient_state, because the pair is the whole of #807's
|
|
132
|
+
-- "watermark, never an unread set", and it is easy to mistake this table for
|
|
133
|
+
-- the thing that rule forbids.
|
|
134
|
+
--
|
|
135
|
+
-- The forbidden structure is a set of things a mind *has not* seen: it grows
|
|
136
|
+
-- when the house is productive, it is answerable to "how many are left", and
|
|
137
|
+
-- it turns into a backlog the moment anything renders it. This table is the
|
|
138
|
+
-- exact inverse \u2014 a record of what has *already* been shown, which only ever
|
|
139
|
+
-- suppresses. It cannot be counted into a debt because everything in it is
|
|
140
|
+
-- done. Nothing outside the selection filter ever reads it, and no query here
|
|
141
|
+
-- asks it what is missing.
|
|
142
|
+
--
|
|
143
|
+
-- The watermark alone would very nearly do this job, and briefly did: advance
|
|
144
|
+
-- it past everything considered, and an artifact is structurally unrepeatable.
|
|
145
|
+
-- It is kept as well because a timestamp comparison quietly stops guaranteeing
|
|
146
|
+
-- anything whenever a timestamp moves \u2014 republishing over a tombstone revives
|
|
147
|
+
-- published_at, and the notes migration writes historical ones. A guarantee
|
|
148
|
+
-- that survives only while nobody rewrites a date is not a guarantee.
|
|
149
|
+
--
|
|
150
|
+
-- Two timestamps, because the tiers ask different questions of this table:
|
|
151
|
+
--
|
|
152
|
+
-- created_at \u2014 the FIRST time this artifact was ever put in front of this
|
|
153
|
+
-- mind. The ambient *live* tier keys off mere existence of
|
|
154
|
+
-- the row: a page is announced as new exactly once, ever.
|
|
155
|
+
-- last_shown_at \u2014 the MOST RECENT time. The *archive* tier keys off this,
|
|
156
|
+
-- because it is allowed to come back around.
|
|
157
|
+
--
|
|
158
|
+
-- Once-ever is right for "whorl published a thing" and wrong for the archive.
|
|
159
|
+
-- Applied to the archive it produces a terminal state: a mind that has walked
|
|
160
|
+
-- the whole shelf never hears from that tier again \u2014 weeks away at this
|
|
161
|
+
-- corpus size, not years \u2014 and then a quiet house makes a quiet digest makes a
|
|
162
|
+
-- quiet house. That spiral is the exact thing the retrospective tier exists to
|
|
163
|
+
-- break, so it must not be the tier that starts it. Re-encountering a page you
|
|
164
|
+
-- read half a year ago is not repetition; it is what an archive is *for*.
|
|
165
|
+
--
|
|
166
|
+
-- The author column is denormalized so fairness weighting is one grouped read on the
|
|
167
|
+
-- turn path: "whose work has this mind met least" is the selection's central
|
|
168
|
+
-- question and must not cost a join per candidate.
|
|
169
|
+
CREATE TABLE IF NOT EXISTS ambient_shown (
|
|
170
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
171
|
+
viewer TEXT NOT NULL,
|
|
172
|
+
kind TEXT NOT NULL,
|
|
173
|
+
ref TEXT NOT NULL,
|
|
174
|
+
last_shown_at TEXT,
|
|
175
|
+
author TEXT NOT NULL,
|
|
176
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
177
|
+
);
|
|
178
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ambient_shown_unique
|
|
179
|
+
ON ambient_shown(viewer, kind, ref);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_ambient_shown_author ON ambient_shown(viewer, author);
|
|
181
|
+
|
|
182
|
+
-- Per-mind ambient state: how far back to look, and when we last spoke.
|
|
183
|
+
--
|
|
184
|
+
-- The watermark is a horizon, not a queue head. It advances to *now* whenever a
|
|
185
|
+
-- block is emitted, which means anything new that the block did not select is
|
|
186
|
+
-- dropped from the live tier rather than held for later. That is deliberate
|
|
187
|
+
-- and it is the design: a mind returning meets what is on the shelf now, not
|
|
188
|
+
-- an accumulation. Work that falls past the horizon is not lost \u2014 the
|
|
189
|
+
-- retrospective tier reaches into the archive, which is precisely where old
|
|
190
|
+
-- work belongs.
|
|
191
|
+
--
|
|
192
|
+
-- A row is created with watermark = now on a mind's first ask, so a mind
|
|
193
|
+
-- that has just arrived is not handed the entire history as though it were
|
|
194
|
+
-- news. Its introduction to the archive is the retrospective tier's job, at a
|
|
195
|
+
-- pace that tier chooses.
|
|
196
|
+
--
|
|
197
|
+
-- The last_block_at column is the rate limit. A burst of publishes produces one block,
|
|
198
|
+
-- not one per page.
|
|
199
|
+
CREATE TABLE IF NOT EXISTS ambient_state (
|
|
200
|
+
viewer TEXT PRIMARY KEY,
|
|
201
|
+
watermark TEXT NOT NULL,
|
|
202
|
+
last_block_at TEXT
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
-- Ledger for the one-way notes -> pages migration. Keyed on the source note id
|
|
206
|
+
-- so a re-run is a no-op rather than a second set of files and threads.
|
|
207
|
+
CREATE TABLE IF NOT EXISTS migrated_notes (
|
|
208
|
+
note_id INTEGER PRIMARY KEY,
|
|
209
|
+
mind TEXT NOT NULL,
|
|
210
|
+
file TEXT NOT NULL,
|
|
211
|
+
migrated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
-- One-time homepage invitations. A row means the cue was delivered \u2014 written
|
|
215
|
+
-- only after a successful recordNotice, so a failed send retries on the next
|
|
216
|
+
-- mind start rather than being lost. Per-mind rather than a flag file (the way
|
|
217
|
+
-- the commons cue flags the spirit) because every mind gets one, not just the spirit.
|
|
218
|
+
CREATE TABLE IF NOT EXISTS homepage_cue_sent (
|
|
219
|
+
mind TEXT PRIMARY KEY,
|
|
220
|
+
sent_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
221
|
+
);
|
|
222
|
+
`);
|
|
223
|
+
addColumns(db, "published_pages", [
|
|
224
|
+
"author TEXT",
|
|
225
|
+
"hash TEXT",
|
|
226
|
+
"deleted_at TEXT",
|
|
227
|
+
// Per-page `comments: false` frontmatter. Default open — not everything wants
|
|
228
|
+
// to be an invitation, but the default is that it is.
|
|
229
|
+
"comments_closed INTEGER NOT NULL DEFAULT 0"
|
|
230
|
+
]);
|
|
231
|
+
addColumns(db, "ambient_shown", ["last_shown_at TEXT"]);
|
|
232
|
+
addColumns(db, "page_comments", [
|
|
233
|
+
// "comment" (a response) or "publish" (the --shared message that explains a
|
|
234
|
+
// change). Both live in the thread; only a response is an invitation.
|
|
235
|
+
"kind TEXT NOT NULL DEFAULT 'comment'",
|
|
236
|
+
"body_mind TEXT",
|
|
237
|
+
"body_file TEXT"
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
function addColumns(db, table, columns) {
|
|
241
|
+
for (const column of columns) {
|
|
242
|
+
try {
|
|
243
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column}`);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
if (!(err instanceof Error) || !err.message.includes("duplicate column")) {
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function isSiteHome(file) {
|
|
252
|
+
return file === "index.html" || file === "index.md";
|
|
253
|
+
}
|
|
254
|
+
function sortSiteFiles(files) {
|
|
255
|
+
return [...files].sort((a, b) => {
|
|
256
|
+
if (isSiteHome(a.file) !== isSiteHome(b.file)) return isSiteHome(a.file) ? -1 : 1;
|
|
257
|
+
return b.updated_at.localeCompare(a.updated_at);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function getPublishedPages(db, mind) {
|
|
261
|
+
return db.prepare(
|
|
262
|
+
`SELECT file, published_at, updated_at, author FROM published_pages
|
|
263
|
+
WHERE mind = ? AND deleted_at IS NULL ORDER BY file`
|
|
264
|
+
).all(mind);
|
|
265
|
+
}
|
|
266
|
+
function getRecentPages(db, opts) {
|
|
267
|
+
const limit = opts?.limit ?? 10;
|
|
268
|
+
if (opts?.mind) {
|
|
269
|
+
return db.prepare(
|
|
270
|
+
`SELECT mind, file, updated_at, author FROM published_pages
|
|
271
|
+
WHERE mind = ? AND deleted_at IS NULL ORDER BY updated_at DESC LIMIT ?`
|
|
272
|
+
).all(opts.mind, limit);
|
|
273
|
+
}
|
|
274
|
+
return db.prepare(
|
|
275
|
+
`SELECT mind, file, updated_at, author FROM published_pages
|
|
276
|
+
WHERE deleted_at IS NULL ORDER BY updated_at DESC LIMIT ?`
|
|
277
|
+
).all(limit);
|
|
278
|
+
}
|
|
279
|
+
function getAllSites(db) {
|
|
280
|
+
const rows = db.prepare(
|
|
281
|
+
`SELECT mind, file, updated_at, author FROM published_pages
|
|
282
|
+
WHERE mind != '_system' AND deleted_at IS NULL ORDER BY updated_at DESC, id DESC`
|
|
283
|
+
).all();
|
|
284
|
+
const siteMap = /* @__PURE__ */ new Map();
|
|
285
|
+
for (const row of rows) {
|
|
286
|
+
let files = siteMap.get(row.mind);
|
|
287
|
+
if (!files) {
|
|
288
|
+
files = [];
|
|
289
|
+
siteMap.set(row.mind, files);
|
|
290
|
+
}
|
|
291
|
+
files.push({ file: row.file, updated_at: row.updated_at, author: row.author });
|
|
292
|
+
}
|
|
293
|
+
return Array.from(siteMap.entries()).map(([mind, files]) => ({
|
|
294
|
+
mind,
|
|
295
|
+
files: sortSiteFiles(files)
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
function getSystemPages(db) {
|
|
299
|
+
const rows = db.prepare(
|
|
300
|
+
`SELECT mind, file, updated_at, author FROM published_pages
|
|
301
|
+
WHERE mind = '_system' AND deleted_at IS NULL ORDER BY updated_at DESC, id DESC`
|
|
302
|
+
).all();
|
|
303
|
+
if (rows.length === 0) return null;
|
|
304
|
+
return {
|
|
305
|
+
mind: "_system",
|
|
306
|
+
files: sortSiteFiles(
|
|
307
|
+
rows.map((r) => ({ file: r.file, updated_at: r.updated_at, author: r.author }))
|
|
308
|
+
)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function getMindsWithSites(db) {
|
|
312
|
+
return db.prepare(
|
|
313
|
+
`SELECT DISTINCT mind FROM published_pages
|
|
314
|
+
WHERE mind != '_system' AND deleted_at IS NULL ORDER BY mind`
|
|
315
|
+
).all().map((r) => r.mind);
|
|
316
|
+
}
|
|
317
|
+
function getPage(db, mind, file) {
|
|
318
|
+
return db.prepare(
|
|
319
|
+
`SELECT mind, file, hash, published_at, updated_at, author, deleted_at
|
|
320
|
+
FROM published_pages WHERE mind = ? AND file = ?`
|
|
321
|
+
).get(mind, file) ?? null;
|
|
322
|
+
}
|
|
323
|
+
function getLivePageFiles(db, mind) {
|
|
324
|
+
return db.prepare(
|
|
325
|
+
"SELECT file FROM published_pages WHERE mind = ? AND deleted_at IS NULL ORDER BY file"
|
|
326
|
+
).all(mind).map((r) => r.file);
|
|
327
|
+
}
|
|
328
|
+
function knownPageFiles(db, mind) {
|
|
329
|
+
const rows = db.prepare("SELECT file FROM published_pages WHERE mind = ?").all(mind);
|
|
330
|
+
return new Set(rows.map((r) => r.file));
|
|
331
|
+
}
|
|
332
|
+
function pagesWithThreads(db, mind) {
|
|
333
|
+
const rows = db.prepare(
|
|
334
|
+
`SELECT file FROM page_comments WHERE mind = ?
|
|
335
|
+
UNION SELECT file FROM page_reactions WHERE mind = ?`
|
|
336
|
+
).all(mind, mind);
|
|
337
|
+
return new Set(rows.map((r) => r.file));
|
|
338
|
+
}
|
|
339
|
+
function retirePage(db, mind, file, hasThread) {
|
|
340
|
+
if (hasThread) {
|
|
341
|
+
db.prepare(
|
|
342
|
+
`UPDATE published_pages SET deleted_at = datetime('now')
|
|
343
|
+
WHERE mind = ? AND file = ? AND deleted_at IS NULL`
|
|
344
|
+
).run(mind, file);
|
|
345
|
+
} else {
|
|
346
|
+
db.prepare("DELETE FROM published_pages WHERE mind = ? AND file = ?").run(mind, file);
|
|
347
|
+
db.prepare("DELETE FROM page_reads WHERE mind = ? AND file = ?").run(mind, file);
|
|
348
|
+
}
|
|
349
|
+
db.prepare("DELETE FROM page_citations WHERE mind = ? AND file = ?").run(mind, file);
|
|
350
|
+
db.prepare("DELETE FROM page_links WHERE mind = ? AND file = ?").run(mind, file);
|
|
351
|
+
}
|
|
352
|
+
function existingPages(db, mind) {
|
|
353
|
+
const rows = db.prepare("SELECT file, hash, deleted_at FROM published_pages WHERE mind = ?").all(mind);
|
|
354
|
+
return new Map(rows.map((r) => [r.file, r]));
|
|
355
|
+
}
|
|
356
|
+
function applyPageMeta(db, mind, page) {
|
|
357
|
+
if (page.commentsClosed !== void 0) {
|
|
358
|
+
db.prepare("UPDATE published_pages SET comments_closed = ? WHERE mind = ? AND file = ?").run(
|
|
359
|
+
page.commentsClosed ? 1 : 0,
|
|
360
|
+
mind,
|
|
361
|
+
page.file
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
if (page.links !== void 0) {
|
|
365
|
+
db.prepare("DELETE FROM page_links WHERE mind = ? AND file = ?").run(mind, page.file);
|
|
366
|
+
for (const target of page.links) {
|
|
367
|
+
if (target === mind) continue;
|
|
368
|
+
db.prepare("INSERT OR IGNORE INTO page_links (mind, file, target) VALUES (?, ?, ?)").run(
|
|
369
|
+
mind,
|
|
370
|
+
page.file,
|
|
371
|
+
target
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (page.mentions === void 0) return;
|
|
376
|
+
db.prepare("DELETE FROM page_citations WHERE mind = ? AND file = ?").run(mind, page.file);
|
|
377
|
+
for (const mentioned of page.mentions) {
|
|
378
|
+
if (mentioned === mind) continue;
|
|
379
|
+
db.prepare("INSERT OR IGNORE INTO page_citations (mind, file, mentioned) VALUES (?, ?, ?)").run(
|
|
380
|
+
mind,
|
|
381
|
+
page.file,
|
|
382
|
+
mentioned
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function areCommentsClosed(db, mind, file) {
|
|
387
|
+
const row = db.prepare("SELECT comments_closed FROM published_pages WHERE mind = ? AND file = ?").get(mind, file);
|
|
388
|
+
return !!row?.comments_closed;
|
|
389
|
+
}
|
|
390
|
+
function citationsOf(db, mentioned) {
|
|
391
|
+
return db.prepare(
|
|
392
|
+
`SELECT c.mind, c.file, c.created_at FROM page_citations c
|
|
393
|
+
JOIN published_pages p ON p.mind = c.mind AND p.file = c.file
|
|
394
|
+
WHERE c.mentioned = ? AND p.deleted_at IS NULL
|
|
395
|
+
ORDER BY c.created_at DESC, c.id DESC`
|
|
396
|
+
).all(mentioned);
|
|
397
|
+
}
|
|
398
|
+
function syncSystemPages(db, pages, author) {
|
|
399
|
+
const existing = existingPages(db, "_system");
|
|
400
|
+
const newSet = new Set(pages.map((p) => p.file));
|
|
401
|
+
const threaded = pagesWithThreads(db, "_system");
|
|
402
|
+
db.exec("BEGIN");
|
|
403
|
+
try {
|
|
404
|
+
for (const page of pages) {
|
|
405
|
+
const { file, hash } = page;
|
|
406
|
+
const prior = existing.get(file);
|
|
407
|
+
if (!prior) {
|
|
408
|
+
db.prepare(
|
|
409
|
+
"INSERT INTO published_pages (mind, file, hash, author) VALUES ('_system', ?, ?, ?)"
|
|
410
|
+
).run(file, hash, author ?? null);
|
|
411
|
+
} else if (prior.deleted_at != null) {
|
|
412
|
+
db.prepare(
|
|
413
|
+
`UPDATE published_pages
|
|
414
|
+
SET deleted_at = NULL, updated_at = datetime('now'), hash = ?,
|
|
415
|
+
author = COALESCE(?, author)
|
|
416
|
+
WHERE mind = '_system' AND file = ?`
|
|
417
|
+
).run(hash, author ?? null, file);
|
|
418
|
+
} else if (prior.hash !== hash) {
|
|
419
|
+
if (author) {
|
|
420
|
+
db.prepare(
|
|
421
|
+
"UPDATE published_pages SET updated_at = datetime('now'), hash = ?, author = ? WHERE mind = '_system' AND file = ?"
|
|
422
|
+
).run(hash, author, file);
|
|
423
|
+
} else {
|
|
424
|
+
db.prepare(
|
|
425
|
+
"UPDATE published_pages SET updated_at = datetime('now'), hash = ? WHERE mind = '_system' AND file = ?"
|
|
426
|
+
).run(hash, file);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
applyPageMeta(db, "_system", page);
|
|
430
|
+
}
|
|
431
|
+
for (const [file, prior] of existing) {
|
|
432
|
+
if (!newSet.has(file) && prior.deleted_at == null) {
|
|
433
|
+
retirePage(db, "_system", file, threaded.has(file));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
db.exec("COMMIT");
|
|
437
|
+
} catch (err) {
|
|
438
|
+
db.exec("ROLLBACK");
|
|
439
|
+
throw err;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function syncPublishedPages(db, mind, pages) {
|
|
443
|
+
const existing = existingPages(db, mind);
|
|
444
|
+
const newSet = new Set(pages.map((p) => p.file));
|
|
445
|
+
const threaded = pagesWithThreads(db, mind);
|
|
446
|
+
const added = [];
|
|
447
|
+
const updated = [];
|
|
448
|
+
const removed = [];
|
|
449
|
+
db.exec("BEGIN");
|
|
450
|
+
try {
|
|
451
|
+
for (const page of pages) {
|
|
452
|
+
const { file, hash } = page;
|
|
453
|
+
const prior = existing.get(file);
|
|
454
|
+
if (!prior) {
|
|
455
|
+
db.prepare("INSERT INTO published_pages (mind, file, hash) VALUES (?, ?, ?)").run(
|
|
456
|
+
mind,
|
|
457
|
+
file,
|
|
458
|
+
hash
|
|
459
|
+
);
|
|
460
|
+
added.push(file);
|
|
461
|
+
} else if (prior.deleted_at != null) {
|
|
462
|
+
db.prepare(
|
|
463
|
+
`UPDATE published_pages SET deleted_at = NULL, updated_at = datetime('now'), hash = ?
|
|
464
|
+
WHERE mind = ? AND file = ?`
|
|
465
|
+
).run(hash, mind, file);
|
|
466
|
+
added.push(file);
|
|
467
|
+
} else if (prior.hash !== hash) {
|
|
468
|
+
db.prepare(
|
|
469
|
+
"UPDATE published_pages SET updated_at = datetime('now'), hash = ? WHERE mind = ? AND file = ?"
|
|
470
|
+
).run(hash, mind, file);
|
|
471
|
+
updated.push(file);
|
|
472
|
+
}
|
|
473
|
+
applyPageMeta(db, mind, page);
|
|
474
|
+
}
|
|
475
|
+
for (const [file, prior] of existing) {
|
|
476
|
+
if (!newSet.has(file) && prior.deleted_at == null) {
|
|
477
|
+
retirePage(db, mind, file, threaded.has(file));
|
|
478
|
+
removed.push(file);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
db.exec("COMMIT");
|
|
482
|
+
} catch (err) {
|
|
483
|
+
db.exec("ROLLBACK");
|
|
484
|
+
throw err;
|
|
485
|
+
}
|
|
486
|
+
return { added, removed, updated };
|
|
487
|
+
}
|
|
488
|
+
function upsertPageWithTimestamps(db, mind, file, hash, publishedAt, updatedAt, author) {
|
|
489
|
+
const prior = db.prepare("SELECT id FROM published_pages WHERE mind = ? AND file = ?").get(mind, file);
|
|
490
|
+
if (prior) {
|
|
491
|
+
db.prepare(
|
|
492
|
+
`UPDATE published_pages
|
|
493
|
+
SET hash = ?, published_at = ?, updated_at = ?, author = ?, deleted_at = NULL
|
|
494
|
+
WHERE id = ?`
|
|
495
|
+
).run(hash, publishedAt, updatedAt, author ?? null, prior.id);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
db.prepare(
|
|
499
|
+
`INSERT INTO published_pages (mind, file, hash, published_at, updated_at, author)
|
|
500
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
501
|
+
).run(mind, file, hash, publishedAt, updatedAt, author ?? null);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export {
|
|
505
|
+
initDb,
|
|
506
|
+
isSiteHome,
|
|
507
|
+
getPublishedPages,
|
|
508
|
+
getRecentPages,
|
|
509
|
+
getAllSites,
|
|
510
|
+
getSystemPages,
|
|
511
|
+
getMindsWithSites,
|
|
512
|
+
getPage,
|
|
513
|
+
getLivePageFiles,
|
|
514
|
+
knownPageFiles,
|
|
515
|
+
areCommentsClosed,
|
|
516
|
+
citationsOf,
|
|
517
|
+
syncSystemPages,
|
|
518
|
+
syncPublishedPages,
|
|
519
|
+
upsertPageWithTimestamps
|
|
520
|
+
};
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getSleepManagerIfReady,
|
|
4
4
|
startSpiritFull
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NCRG4SBR.js";
|
|
6
6
|
import {
|
|
7
7
|
spiritCreationInProgress
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UEZC5MNB.js";
|
|
9
9
|
import {
|
|
10
10
|
MindStartupError,
|
|
11
11
|
tryGetMindManager
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HLWQ3P3R.js";
|
|
13
13
|
import {
|
|
14
14
|
logger_default
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2674KIGI.js";
|
|
16
16
|
import {
|
|
17
17
|
computeSetupStatus,
|
|
18
18
|
findMind,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
MEMORY_PLACEHOLDER_MARKER,
|
|
4
|
-
ORIENTATION_MARKER
|
|
5
|
-
} from "./chunk-AP2KQMMW.js";
|
|
6
2
|
import {
|
|
7
3
|
safeResolveWithinBase
|
|
8
4
|
} from "./chunk-DW4TRDOZ.js";
|
|
9
5
|
import {
|
|
10
6
|
readVoluteConfig
|
|
11
7
|
} from "./chunk-WCFSBH33.js";
|
|
8
|
+
import {
|
|
9
|
+
MEMORY_PLACEHOLDER_MARKER,
|
|
10
|
+
ORIENTATION_MARKER
|
|
11
|
+
} from "./chunk-HTSWDOJE.js";
|
|
12
12
|
import {
|
|
13
13
|
isImagegenEnabled
|
|
14
14
|
} from "./chunk-H6X6CKDN.js";
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
exec,
|
|
4
4
|
gitExec
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OIOW3FPT.js";
|
|
6
6
|
import {
|
|
7
7
|
logger_default
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2674KIGI.js";
|
|
9
9
|
import {
|
|
10
10
|
getDb,
|
|
11
11
|
mindDir,
|
|
@@ -45,7 +45,7 @@ async function initDefaultSkills() {
|
|
|
45
45
|
const config = readGlobalConfig();
|
|
46
46
|
let extensionSkills = [];
|
|
47
47
|
try {
|
|
48
|
-
const { getExtensionStandardSkills } = await import("./extensions-
|
|
48
|
+
const { getExtensionStandardSkills } = await import("./extensions-QD22GXZD.js");
|
|
49
49
|
extensionSkills = getExtensionStandardSkills();
|
|
50
50
|
} catch (err) {
|
|
51
51
|
logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
|
|
@@ -54,9 +54,12 @@ async function initDefaultSkills() {
|
|
|
54
54
|
const current = config.defaultSkills ?? [];
|
|
55
55
|
const removed = new Set(config.removedDefaultSkills ?? []);
|
|
56
56
|
const toAdd = [...desired].filter((s) => !current.includes(s) && !removed.has(s));
|
|
57
|
-
|
|
58
|
-
const
|
|
57
|
+
const pool = new Set((await listSharedSkills()).map((s) => s.id));
|
|
58
|
+
const stale = pool.size > 0 ? current.filter((s) => !pool.has(s)) : [];
|
|
59
|
+
if (toAdd.length === 0 && stale.length === 0 && current.length > 0) return;
|
|
60
|
+
const merged = [.../* @__PURE__ */ new Set([...current, ...toAdd])].filter((s) => !stale.includes(s));
|
|
59
61
|
writeGlobalConfig({ ...config, defaultSkills: merged });
|
|
62
|
+
if (stale.length > 0) logger_default.info(`dropped default skills not in the pool: ${stale.join(", ")}`);
|
|
60
63
|
logger_default.info(`updated default skills: ${merged.join(", ")}`);
|
|
61
64
|
}
|
|
62
65
|
function validateSkillId(id) {
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
LAUNCHD_PLIST_LABEL,
|
|
4
4
|
LAUNCHD_PLIST_PATH,
|
|
5
5
|
USER_SYSTEMD_UNIT
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CKPAVQNK.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveVoluteBin
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OIOW3FPT.js";
|
|
10
10
|
|
|
11
11
|
// packages/daemon/src/lib/config/service-install.ts
|
|
12
12
|
import { execFile } from "child_process";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
publish
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PRZUBJ4K.js";
|
|
5
5
|
import {
|
|
6
6
|
logger_default
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2674KIGI.js";
|
|
8
8
|
|
|
9
9
|
// packages/daemon/src/lib/events/mind-activity-tracker.ts
|
|
10
10
|
var IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
compactDateTime,
|
|
4
4
|
isCompact
|
|
5
5
|
} from "./chunk-7YJY3F2R.js";
|
|
6
|
+
import {
|
|
7
|
+
getClient,
|
|
8
|
+
urlOf
|
|
9
|
+
} from "./chunk-LOPXTW6H.js";
|
|
6
10
|
import {
|
|
7
11
|
resolveMindName
|
|
8
12
|
} from "./chunk-BTY4WNFE.js";
|
|
9
|
-
import {
|
|
10
|
-
command,
|
|
11
|
-
subcommands
|
|
12
|
-
} from "./chunk-TXSA4Q3V.js";
|
|
13
13
|
import {
|
|
14
14
|
daemonFetch
|
|
15
15
|
} from "./chunk-M74I3HHF.js";
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "./chunk-
|
|
17
|
+
command,
|
|
18
|
+
subcommands
|
|
19
|
+
} from "./chunk-TXSA4Q3V.js";
|
|
20
20
|
|
|
21
21
|
// packages/cli/src/commands/clock.ts
|
|
22
22
|
import { CronExpressionParser } from "cron-parser";
|
|
@@ -279,11 +279,11 @@ var cmd = subcommands({
|
|
|
279
279
|
},
|
|
280
280
|
sleep: {
|
|
281
281
|
description: "Put a mind to sleep",
|
|
282
|
-
run: (args) => import("./mind-sleep-
|
|
282
|
+
run: (args) => import("./mind-sleep-24HV4RCA.js").then((m) => m.run(args))
|
|
283
283
|
},
|
|
284
284
|
wake: {
|
|
285
285
|
description: "Wake a sleeping mind",
|
|
286
|
-
run: (args) => import("./mind-wake-
|
|
286
|
+
run: (args) => import("./mind-wake-LDGPRA42.js").then((m) => m.run(args))
|
|
287
287
|
}
|
|
288
288
|
},
|
|
289
289
|
footer: "Use --mind <name> or VOLUTE_MIND to identify the mind."
|