kennel 0.2.2 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -13
- package/dist/cli/dispatch.js +16 -3
- package/dist/cli/src/cli/dispatch.js +16 -3
- package/dist/cli/src/cli/usage.js +1 -1
- package/dist/cli/src/core/command.js +1 -0
- package/dist/cli/src/core/init.js +5 -5
- package/dist/cli/src/plugins/external.d.ts +29 -3
- package/dist/cli/src/plugins/external.js +78 -24
- package/dist/cli/src/site/server-plugins.d.ts +2 -7
- package/dist/cli/src/site/viewer.js +1 -0
- package/dist/cli/usage.js +1 -1
- package/dist/core/command.js +1 -0
- package/dist/core/init.js +5 -5
- package/dist/core/runtime.js +357 -3207
- package/dist/package/preview.js +1232 -5113
- package/dist/plugins/external.d.ts +29 -3
- package/dist/plugins/external.js +78 -24
- package/dist/site/server-plugins.d.ts +2 -7
- package/dist/site/server-plugins.js +552 -10447
- package/dist/site/viewer.js +1 -0
- package/kernel/apps/web/package.json +1 -5
- package/kernel/apps/web/scripts/dev.mjs +53 -0
- package/kernel/apps/web/src/app.css +0 -3
- package/kernel/apps/web/src/main.tsx +0 -1
- package/kernel/apps/web/src/plugins.ts +0 -30
- package/kernel/apps/web/tsconfig.json +1 -2
- package/kernel/apps/web/vite.config.ts +31 -13
- package/kernel/packages/api/package.json +0 -4
- package/kernel/packages/api/src/server/folder-metadata-plugin.ts +13 -38
- package/kernel/packages/api/src/server/index.ts +0 -1
- package/kernel/packages/api/src/server/plugins.ts +49 -16
- package/kernel/packages/api/test/core.test.ts +13 -25
- package/kernel/packages/markdown/README.md +1 -1
- package/package.json +1 -1
- package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
- package/web/assets/app-CX20nNIa.js +2 -0
- package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
- package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
- package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
- package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
- package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
- package/web/assets/channel-X7WHyxxT.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
- package/web/assets/client-7OWw5kW2.js +6 -0
- package/web/assets/client-DYj_td4Q.js +17 -0
- package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
- package/web/assets/command-zkzH4hVz.js +38 -0
- package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
- package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
- package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
- package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
- package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
- package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
- package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
- package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
- package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
- package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
- package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
- package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
- package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
- package/web/assets/index-CegvVQ1C.js +1 -0
- package/web/assets/index-Dwhf8F_U.css +1 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
- package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
- package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
- package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
- package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
- package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
- package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
- package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
- package/web/assets/select-CtXC_PLC.js +30 -0
- package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
- package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
- package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
- package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
- package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
- package/web/index.html +2 -2
- package/kernel/packages/api/test/partial-loading.test.ts +0 -43
- package/kernel/plugins/calendar/SKILL.md +0 -173
- package/kernel/plugins/calendar/eslint.config.js +0 -10
- package/kernel/plugins/calendar/package.json +0 -73
- package/kernel/plugins/calendar/schema.json +0 -137
- package/kernel/plugins/calendar/src/calendar.tsx +0 -328
- package/kernel/plugins/calendar/src/client.tsx +0 -139
- package/kernel/plugins/calendar/src/context.ts +0 -48
- package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
- package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
- package/kernel/plugins/calendar/src/index.ts +0 -4
- package/kernel/plugins/calendar/src/manifest.ts +0 -7
- package/kernel/plugins/calendar/src/parse.ts +0 -610
- package/kernel/plugins/calendar/src/server.ts +0 -160
- package/kernel/plugins/calendar/src/utils.ts +0 -80
- package/kernel/plugins/calendar/src/views.tsx +0 -418
- package/kernel/plugins/calendar/test/context.test.mjs +0 -60
- package/kernel/plugins/calendar/test/package.test.mjs +0 -43
- package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
- package/kernel/plugins/calendar/test/server.test.mjs +0 -126
- package/kernel/plugins/calendar/tsconfig.json +0 -17
- package/kernel/plugins/mail/SKILL.md +0 -97
- package/kernel/plugins/mail/eslint.config.js +0 -10
- package/kernel/plugins/mail/package.json +0 -79
- package/kernel/plugins/mail/src/api.ts +0 -359
- package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
- package/kernel/plugins/mail/src/client.tsx +0 -14
- package/kernel/plugins/mail/src/flock.ts +0 -137
- package/kernel/plugins/mail/src/folders.ts +0 -37
- package/kernel/plugins/mail/src/history.ts +0 -485
- package/kernel/plugins/mail/src/index.ts +0 -3
- package/kernel/plugins/mail/src/mail-card.tsx +0 -123
- package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
- package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
- package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
- package/kernel/plugins/mail/src/mail-page.tsx +0 -109
- package/kernel/plugins/mail/src/mail-request.ts +0 -78
- package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
- package/kernel/plugins/mail/src/mailos.ts +0 -723
- package/kernel/plugins/mail/src/manifest.ts +0 -7
- package/kernel/plugins/mail/src/node.ts +0 -171
- package/kernel/plugins/mail/src/parse.ts +0 -139
- package/kernel/plugins/mail/src/server.ts +0 -125
- package/kernel/plugins/mail/src/types.ts +0 -10
- package/kernel/plugins/mail/src/viewer.tsx +0 -147
- package/kernel/plugins/mail/test/flock.test.mjs +0 -156
- package/kernel/plugins/mail/test/folders.test.mjs +0 -138
- package/kernel/plugins/mail/test/history.test.mjs +0 -460
- package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
- package/kernel/plugins/mail/test/node.test.mjs +0 -207
- package/kernel/plugins/mail/test/package.test.mjs +0 -54
- package/kernel/plugins/mail/test/parse.test.mjs +0 -32
- package/kernel/plugins/mail/test/server.test.mjs +0 -116
- package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
- package/kernel/plugins/mail/tsconfig.json +0 -17
- package/kernel/plugins/projects/SKILL.md +0 -73
- package/kernel/plugins/projects/eslint.config.js +0 -10
- package/kernel/plugins/projects/package.json +0 -70
- package/kernel/plugins/projects/src/client.tsx +0 -17
- package/kernel/plugins/projects/src/data/model.ts +0 -129
- package/kernel/plugins/projects/src/index.ts +0 -17
- package/kernel/plugins/projects/src/manifest.ts +0 -7
- package/kernel/plugins/projects/src/project-context.ts +0 -41
- package/kernel/plugins/projects/src/server.ts +0 -276
- package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
- package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
- package/kernel/plugins/projects/test/package.test.mjs +0 -51
- package/kernel/plugins/projects/tsconfig.json +0 -17
- package/kernel/plugins/tasks/SKILL.md +0 -341
- package/kernel/plugins/tasks/eslint.config.js +0 -10
- package/kernel/plugins/tasks/package.json +0 -74
- package/kernel/plugins/tasks/schema.json +0 -939
- package/kernel/plugins/tasks/src/api.ts +0 -212
- package/kernel/plugins/tasks/src/client.tsx +0 -23
- package/kernel/plugins/tasks/src/data/parse.ts +0 -544
- package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
- package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
- package/kernel/plugins/tasks/src/index.ts +0 -34
- package/kernel/plugins/tasks/src/manifest.ts +0 -7
- package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
- package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
- package/kernel/plugins/tasks/src/server.ts +0 -447
- package/kernel/plugins/tasks/src/styles.css +0 -9
- package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
- package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
- package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
- package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
- package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
- package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
- package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
- package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
- package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
- package/kernel/plugins/tasks/test/api.test.mjs +0 -99
- package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
- package/kernel/plugins/tasks/test/package.test.mjs +0 -54
- package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
- package/kernel/plugins/tasks/test/server.test.mjs +0 -386
- package/kernel/plugins/tasks/tsconfig.json +0 -17
- package/web/assets/app-DoGr2Zi7.js +0 -2
- package/web/assets/channel-uMEjV8qc.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
- package/web/assets/client-C6Nxdfif.js +0 -2
- package/web/assets/client-CXYfBko5.js +0 -17
- package/web/assets/client-CoKtOq-X.js +0 -7
- package/web/assets/client-DyRQz5RU.js +0 -53
- package/web/assets/client-OY6HUzuD.js +0 -98
- package/web/assets/client-YOYWZIFi.js +0 -34
- package/web/assets/command-BugZBGXE.js +0 -38
- package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
- package/web/assets/index-BaRnbYRw.js +0 -1
- package/web/assets/index-BexmLxIP.css +0 -1
- package/web/assets/index-DDirZMgx.js +0 -1
- package/web/assets/note-card-DBfX6gW1.js +0 -6
- package/web/assets/select-D8cqNgcp.js +0 -30
- package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
- package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { readdir, readFile, writeFile } from "node:fs/promises"
|
|
2
|
-
import { dirname, resolve } from "node:path"
|
|
3
|
-
import type { IncomingMessage, ServerResponse } from "node:http"
|
|
4
|
-
|
|
5
|
-
import type { ApiRouteHandle } from "@raggle-ai/folder-plugin/endpoint"
|
|
6
|
-
import {
|
|
7
|
-
apiError,
|
|
8
|
-
jsonResponse,
|
|
9
|
-
parseJsonBody,
|
|
10
|
-
runApiRoute,
|
|
11
|
-
} from "@raggle-ai/folder-plugin/endpoint"
|
|
12
|
-
|
|
13
|
-
import { parseTodoSource, todoIssueSummary } from "./data/parse.ts"
|
|
14
|
-
import { parseQualifiedTaskId, refMatchesFolder } from "./data/task-id.ts"
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Context provided by the host. `authorizeFolder` enforces the real viewer
|
|
18
|
-
* root (realpath + locked root) and `watch` wires folder live refresh.
|
|
19
|
-
* `readOnly` disables mutations on remote/restricted hosts.
|
|
20
|
-
*/
|
|
21
|
-
export type TasksApiContext = {
|
|
22
|
-
authorizeFolder: (value: string | null) => string
|
|
23
|
-
/** Mutations are disabled for remote/restricted hosts. */
|
|
24
|
-
readOnly?: boolean
|
|
25
|
-
watch: (folder: string) => void
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
type Handler = ApiRouteHandle<TasksApiContext>
|
|
29
|
-
|
|
30
|
-
const TASKS_FILE = "todo.json"
|
|
31
|
-
|
|
32
|
-
function requireString(value: unknown, name: string) {
|
|
33
|
-
if (typeof value !== "string" || !value.trim()) {
|
|
34
|
-
throw apiError("bad-request", `Missing ${name} parameter`)
|
|
35
|
-
}
|
|
36
|
-
return value.trim()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Re-serialize a parsed task document back into source text. Formatting
|
|
41
|
-
* mirrors the original file: the detected item indent (default two spaces)
|
|
42
|
-
* and the trailing newline are preserved so hand-maintained files do not
|
|
43
|
-
* churn.
|
|
44
|
-
*/
|
|
45
|
-
export function serializeTodoSource(
|
|
46
|
-
document: unknown,
|
|
47
|
-
original: string,
|
|
48
|
-
): string {
|
|
49
|
-
const indent = /^\{[\s\S]*?\n([ \t]+)"/m.exec(original)?.[1] ?? " "
|
|
50
|
-
const trailing = original.endsWith("\n") ? "\n" : ""
|
|
51
|
-
return `${JSON.stringify(document, null, indent)}${trailing}`
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type TaskToggleResult = {
|
|
55
|
-
done: boolean
|
|
56
|
-
next: string
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Pure toggle transform on the raw source text: a complete (or canceled)
|
|
61
|
-
* task reopens as `todo`; any other task becomes `done` with
|
|
62
|
-
* `resolution: "completed"` and a `completedAt` timestamp. `updatedAt` is
|
|
63
|
-
* always refreshed. Never touches the file system, so tests can exercise it
|
|
64
|
-
* on strings.
|
|
65
|
-
*/
|
|
66
|
-
export function toggleTaskSource(source: string, id: string): TaskToggleResult {
|
|
67
|
-
const parsed = parseTodoSource(source)
|
|
68
|
-
if (!parsed.ok) {
|
|
69
|
-
throw apiError("bad-request", todoIssueSummary(parsed.issues))
|
|
70
|
-
}
|
|
71
|
-
const tasks = Array.isArray(parsed.document.tasks)
|
|
72
|
-
? parsed.document.tasks
|
|
73
|
-
: []
|
|
74
|
-
const task = tasks.find((item) => item.id === id || item.key === id)
|
|
75
|
-
if (!task) throw apiError("not-found", `Task not found: ${id}`)
|
|
76
|
-
|
|
77
|
-
const wasDone = task.done === true || task.status === "done"
|
|
78
|
-
const done = !wasDone
|
|
79
|
-
const now = new Date().toISOString()
|
|
80
|
-
if (done) {
|
|
81
|
-
task.done = true
|
|
82
|
-
task.status = "done"
|
|
83
|
-
task.resolution = "completed"
|
|
84
|
-
task.completedAt = now
|
|
85
|
-
} else {
|
|
86
|
-
task.done = false
|
|
87
|
-
task.status = "todo"
|
|
88
|
-
task.resolution = null
|
|
89
|
-
task.completedAt = null
|
|
90
|
-
}
|
|
91
|
-
task.updatedAt = now
|
|
92
|
-
|
|
93
|
-
return { done, next: serializeTodoSource(parsed.document, source) }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Recursively discover the `todo.json` file that owns a task id/key under
|
|
98
|
-
* the authorized folder. Hidden directories and `node_modules` are skipped
|
|
99
|
-
* and directory symlinks are not followed, so the search stays contained
|
|
100
|
-
* (mirrors the plugin's source discovery). Linked folders are never touched:
|
|
101
|
-
* linked content is read-only by contract.
|
|
102
|
-
*
|
|
103
|
-
* `id` may be a qualified address (`<folderPathId(folder)>@<id|key>`), which
|
|
104
|
-
* restricts the search to that exact source file so identical file-local ids
|
|
105
|
-
* `todo:1` edit the right file. A ref matching no file falls back to the
|
|
106
|
-
* bare-id search; a ref matching a real file without the task stays
|
|
107
|
-
* not-found instead of writing a different file.
|
|
108
|
-
*/
|
|
109
|
-
async function findTaskSource(folder: string, id: string) {
|
|
110
|
-
const search = async (
|
|
111
|
-
paths: string[],
|
|
112
|
-
target: string,
|
|
113
|
-
): Promise<{ path: string; source: string } | undefined> => {
|
|
114
|
-
for (const path of paths) {
|
|
115
|
-
const source = await readFile(path, "utf8")
|
|
116
|
-
const parsed = parseTodoSource(source)
|
|
117
|
-
if (!parsed.ok) continue
|
|
118
|
-
const tasks = Array.isArray(parsed.document.tasks)
|
|
119
|
-
? parsed.document.tasks
|
|
120
|
-
: []
|
|
121
|
-
if (tasks.some((task) => task.id === target || task.key === target)) {
|
|
122
|
-
return { path, source }
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return undefined
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const visit = async (current: string): Promise<string[]> => {
|
|
129
|
-
const entries = await readdir(current, { withFileTypes: true }).catch(
|
|
130
|
-
() => [],
|
|
131
|
-
)
|
|
132
|
-
const paths: string[] = []
|
|
133
|
-
for (const entry of entries) {
|
|
134
|
-
if (entry.isFile() && entry.name === TASKS_FILE) {
|
|
135
|
-
paths.push(resolve(current, entry.name))
|
|
136
|
-
continue
|
|
137
|
-
}
|
|
138
|
-
if (
|
|
139
|
-
!entry.isDirectory() ||
|
|
140
|
-
entry.name.startsWith(".") ||
|
|
141
|
-
entry.name === "node_modules"
|
|
142
|
-
)
|
|
143
|
-
continue
|
|
144
|
-
paths.push(...(await visit(resolve(current, entry.name))))
|
|
145
|
-
}
|
|
146
|
-
return paths
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const paths = (await visit(folder)).sort((left, right) =>
|
|
150
|
-
left.localeCompare(right),
|
|
151
|
-
)
|
|
152
|
-
const qualified = parseQualifiedTaskId(id)
|
|
153
|
-
if (qualified) {
|
|
154
|
-
const owned = paths.filter((path) =>
|
|
155
|
-
refMatchesFolder(qualified.ref, dirname(path)),
|
|
156
|
-
)
|
|
157
|
-
if (owned.length > 0) {
|
|
158
|
-
return await search(owned, qualified.id)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return await search(paths, qualified?.id ?? id)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const toggleTask: Handler = async ({ body, context, response }) => {
|
|
165
|
-
if (context.readOnly) {
|
|
166
|
-
throw apiError("forbidden", "Task updates are disabled for remote access")
|
|
167
|
-
}
|
|
168
|
-
const parsed = parseJsonBody(body)
|
|
169
|
-
const id = requireString(parsed.id, "id")
|
|
170
|
-
const folder = context.authorizeFolder(
|
|
171
|
-
typeof parsed.path === "string" && parsed.path ? parsed.path : null,
|
|
172
|
-
)
|
|
173
|
-
const found = await findTaskSource(folder, id)
|
|
174
|
-
if (!found) throw apiError("not-found", `Task not found: ${id}`)
|
|
175
|
-
|
|
176
|
-
// The toggle transform matches file-local ids: strip the source qualifier.
|
|
177
|
-
const target = parseQualifiedTaskId(id)?.id ?? id
|
|
178
|
-
const { done, next } = toggleTaskSource(found.source, target)
|
|
179
|
-
await writeFile(found.path, next, "utf8")
|
|
180
|
-
context.watch(folder)
|
|
181
|
-
|
|
182
|
-
jsonResponse(response, 200, { done, folder, id, ok: true })
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const specByPath: Record<
|
|
186
|
-
string,
|
|
187
|
-
{ handle: Handler; maxBodyBytes?: number; method: "POST"; timeoutMs?: number }
|
|
188
|
-
> = {
|
|
189
|
-
"/api/tasks/toggle": {
|
|
190
|
-
method: "POST",
|
|
191
|
-
maxBodyBytes: 16 * 1024,
|
|
192
|
-
timeoutMs: 30_000,
|
|
193
|
-
handle: toggleTask,
|
|
194
|
-
},
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* All tasks HTTP routes. `/api/tasks/toggle` is a POST mutation guarded by
|
|
199
|
-
* the endpoint wrapper's same-origin check, bounded body, and timeout.
|
|
200
|
-
* Returns false for non-tasks paths so the host can continue its routing.
|
|
201
|
-
*/
|
|
202
|
-
export async function handleTasksApiRoutes(
|
|
203
|
-
request: IncomingMessage,
|
|
204
|
-
response: ServerResponse,
|
|
205
|
-
context: TasksApiContext,
|
|
206
|
-
): Promise<boolean> {
|
|
207
|
-
const url = new URL(request.url ?? "/", "http://127.0.0.1")
|
|
208
|
-
const spec = specByPath[url.pathname]
|
|
209
|
-
if (!spec) return false
|
|
210
|
-
await runApiRoute(request, response, url, spec, context)
|
|
211
|
-
return true
|
|
212
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
|
|
2
|
-
|
|
3
|
-
import { tasksManifest } from "./manifest"
|
|
4
|
-
import { TaskDetailPage } from "./pages/task-detail"
|
|
5
|
-
import { TaskListPage } from "./pages/task-list"
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Client entry: route between the two task pages by subpath. The list page
|
|
9
|
-
* renders the workspace task list; every subpath renders the single task
|
|
10
|
-
* page for that address.
|
|
11
|
-
*/
|
|
12
|
-
export function TasksPlugin(props: FolderPluginViewProps) {
|
|
13
|
-
return props.subpath ? (
|
|
14
|
-
<TaskDetailPage {...props} />
|
|
15
|
-
) : (
|
|
16
|
-
<TaskListPage {...props} />
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const tasksClient = {
|
|
21
|
-
...tasksManifest,
|
|
22
|
-
component: TasksPlugin,
|
|
23
|
-
}
|