slidemind 0.3.19 → 0.4.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.
Files changed (186) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/AGENTS.md +591 -15
  4. package/README.md +202 -7
  5. package/bin/slidemind.mjs +50 -2
  6. package/package.json +2 -2
  7. package/skills/create-slides/SKILL.md +484 -886
  8. package/skills/search/SKILL.md +46 -23
  9. package/src/bridge/build-and-push.mjs +548 -73
  10. package/src/bridge/bundle.mjs +100 -31
  11. package/src/bridge/capture.mjs +14 -17
  12. package/src/bridge/client/brands.mjs +157 -18
  13. package/src/bridge/client/engine-routing.mjs +48 -13
  14. package/src/bridge/client/engine-search.mjs +24 -1
  15. package/src/bridge/client/engine-slides.mjs +1 -1
  16. package/src/bridge/client/render.mjs +27 -1
  17. package/src/bridge/client/transport.mjs +499 -23
  18. package/src/bridge/client.mjs +10 -0
  19. package/src/bridge/config.mjs +67 -6
  20. package/src/bridge/engine-download.mjs +0 -10
  21. package/src/bridge/graph-auth.mjs +55 -3
  22. package/src/bridge/image-intent-envelope.mjs +239 -0
  23. package/src/bridge/package-preview.mjs +67 -0
  24. package/src/bridge/sharepoint.mjs +914 -0
  25. package/src/builders/asset-fetch.mjs +203 -0
  26. package/src/builders/block-dividers.mjs +83 -0
  27. package/src/builders/build.mjs +171 -87
  28. package/src/builders/card-height.mjs +294 -0
  29. package/src/builders/chart-hole.mjs +183 -0
  30. package/src/builders/chart-jsx.mjs +410 -0
  31. package/src/builders/chart-labels.mjs +326 -0
  32. package/src/builders/concept-geometry.mjs +370 -0
  33. package/src/builders/concept-intent.mjs +200 -0
  34. package/src/builders/concept-layout.mjs +79 -0
  35. package/src/builders/concept-lines.mjs +509 -0
  36. package/src/builders/concept-lists.mjs +270 -0
  37. package/src/builders/concept-marks.mjs +84 -0
  38. package/src/builders/concept-relationships.mjs +12 -0
  39. package/src/builders/concept-spec.mjs +645 -0
  40. package/src/builders/concept-text.mjs +140 -0
  41. package/src/builders/concept-waves.mjs +57 -0
  42. package/src/builders/copy-fit.mjs +139 -0
  43. package/src/builders/design-tokens.mjs +347 -0
  44. package/src/builders/draft-check.mjs +552 -340
  45. package/src/builders/draft-lines.mjs +369 -0
  46. package/src/builders/edit-route.mjs +29 -0
  47. package/src/builders/figure-sketch.mjs +375 -0
  48. package/src/builders/fold-sets.mjs +639 -0
  49. package/src/builders/gate-server.mjs +9 -4
  50. package/src/builders/gate-session.js +273 -12
  51. package/src/builders/gate-shell.js +1494 -0
  52. package/src/builders/image-intent-check.mjs +308 -0
  53. package/src/builders/image-use-reconcile.mjs +107 -0
  54. package/src/builders/kit-helpers.mjs +121 -0
  55. package/src/builders/layout-names.mjs +32 -0
  56. package/src/builders/legacy-reference-uses.mjs +179 -0
  57. package/src/builders/line-fit.mjs +224 -0
  58. package/src/builders/line-native.mjs +895 -0
  59. package/src/builders/line-relations.mjs +230 -0
  60. package/src/builders/lint-fix.mjs +117 -9
  61. package/src/builders/lint.mjs +25 -20
  62. package/src/builders/list-jsx.mjs +88 -0
  63. package/src/builders/placement.mjs +133 -0
  64. package/src/builders/plan-checks.mjs +135 -0
  65. package/src/builders/plan-comments.mjs +19 -0
  66. package/src/builders/plan-handlers.mjs +95 -0
  67. package/src/builders/plan-markdown-edit.mjs +3 -3
  68. package/src/builders/plan-overlay.js +1530 -1608
  69. package/src/builders/plan.mjs +689 -186
  70. package/src/builders/preset-definitions.json +1 -0
  71. package/src/builders/preset-eval.mjs +244 -0
  72. package/src/builders/preview.html +51 -120
  73. package/src/builders/red-thread-backbone.mjs +135 -36
  74. package/src/builders/repair.mjs +205 -0
  75. package/src/builders/repeat-groups.mjs +540 -0
  76. package/src/builders/resolve-skill.mjs +5 -4
  77. package/src/builders/review-overlay.js +296 -293
  78. package/src/builders/review.mjs +305 -84
  79. package/src/builders/rule-shift.mjs +96 -0
  80. package/src/builders/seat.mjs +354 -0
  81. package/src/builders/settle-driver.mjs +236 -0
  82. package/src/builders/settle.mjs +1448 -0
  83. package/src/builders/slide-prep.mjs +67 -0
  84. package/src/builders/spec-assets.mjs +236 -0
  85. package/src/builders/spec-match.mjs +56 -0
  86. package/src/builders/stack.mjs +413 -0
  87. package/src/builders/storyboard-markdown.mjs +70 -27
  88. package/src/builders/storyboard-schema.mjs +54 -114
  89. package/src/builders/text-blocks.mjs +212 -0
  90. package/src/builders/text-card-fit.mjs +552 -0
  91. package/src/builders/text-stack.mjs +276 -0
  92. package/src/builders/transcribe.mjs +1069 -0
  93. package/src/cli/assets.mjs +164 -0
  94. package/src/cli/common.mjs +173 -12
  95. package/src/cli/concept-spec-support.mjs +76 -0
  96. package/src/cli/concepts.mjs +1002 -103
  97. package/src/cli/deck.mjs +401 -65
  98. package/src/cli/diagnose.mjs +4 -2
  99. package/src/cli/engine-helpers.mjs +17 -10
  100. package/src/cli/engine-results.mjs +76 -3
  101. package/src/cli/help.mjs +232 -28
  102. package/src/cli/install.mjs +9 -0
  103. package/src/cli/maintenance.mjs +104 -0
  104. package/src/cli/read.mjs +2 -1
  105. package/src/cli/ready.mjs +6 -3
  106. package/src/cli/record.mjs +522 -0
  107. package/src/cli/remote.mjs +818 -0
  108. package/src/cli/revision.mjs +686 -0
  109. package/src/cli/search.mjs +30 -11
  110. package/src/cli/session.mjs +56 -13
  111. package/src/cli/setup.mjs +46 -2
  112. package/src/cli/status.mjs +142 -33
  113. package/src/cli/tasks.mjs +246 -0
  114. package/src/cli/transcribe.mjs +854 -0
  115. package/src/cli/workspace-ui.mjs +260 -0
  116. package/src/cli/workspace.mjs +212 -34
  117. package/src/install-channel.mjs +64 -7
  118. package/src/lib/adopt.mjs +346 -25
  119. package/src/lib/concept-spec-binding.mjs +96 -0
  120. package/src/lib/conversions.mjs +70 -6
  121. package/src/lib/convert-folded.mjs +107 -0
  122. package/src/lib/deck-paths.mjs +418 -0
  123. package/src/lib/durable-ids.mjs +47 -0
  124. package/src/lib/edit-operations.mjs +316 -0
  125. package/src/lib/edit-state.mjs +1244 -0
  126. package/src/lib/edit-transitions.mjs +120 -0
  127. package/src/lib/extensions.mjs +501 -0
  128. package/src/lib/image-assets.mjs +465 -0
  129. package/src/lib/image-derivatives.mjs +234 -0
  130. package/src/lib/image-intent-fingerprint.mjs +70 -0
  131. package/src/lib/image-intent-tags.mjs +176 -0
  132. package/src/lib/image-intent.mjs +802 -0
  133. package/src/lib/image-use-overrides.mjs +279 -0
  134. package/src/lib/inline-components.mjs +613 -0
  135. package/src/lib/lint-converted.mjs +68 -4
  136. package/src/lib/merge-json.mjs +163 -0
  137. package/src/lib/overwrite-guard.mjs +3 -3
  138. package/src/lib/presentation-context.mjs +531 -0
  139. package/src/lib/preservation-tags.mjs +157 -0
  140. package/src/lib/preservation-verdict.mjs +1342 -0
  141. package/src/lib/publish.mjs +1557 -0
  142. package/src/lib/rebase.mjs +945 -0
  143. package/src/lib/reconcile-ack.mjs +4 -6
  144. package/src/lib/reconcile.mjs +62 -24
  145. package/src/lib/record-client.mjs +175 -0
  146. package/src/lib/record-state.mjs +151 -0
  147. package/src/lib/remote.mjs +661 -0
  148. package/src/lib/revision.mjs +1210 -0
  149. package/src/lib/slide-guids.mjs +4 -6
  150. package/src/lib/slide-preservation.mjs +423 -0
  151. package/src/lib/slide-selection.mjs +9 -0
  152. package/src/lib/workspace-transaction.mjs +117 -0
  153. package/src/pptx/content-digest.mjs +334 -0
  154. package/src/pptx/deck-assemble.mjs +1278 -0
  155. package/src/pptx/fill/engine.mjs +2 -3
  156. package/src/pptx/fill/freeform.mjs +6 -3
  157. package/src/pptx/ooxml.mjs +20 -0
  158. package/src/pptx/package-evidence.mjs +383 -0
  159. package/src/pptx/tags.mjs +163 -11
  160. package/src/update-check.mjs +22 -4
  161. package/src/workspace/adapters.mjs +222 -0
  162. package/src/workspace/auth.mjs +256 -0
  163. package/src/workspace/autostart.mjs +204 -0
  164. package/src/workspace/daemon.mjs +360 -0
  165. package/src/workspace/discovery.mjs +180 -0
  166. package/src/workspace/edit.mjs +167 -0
  167. package/src/workspace/events.mjs +52 -0
  168. package/src/workspace/gate-client.mjs +290 -0
  169. package/src/workspace/handback.mjs +73 -0
  170. package/src/workspace/lifecycle.mjs +635 -0
  171. package/src/workspace/operations.mjs +200 -0
  172. package/src/workspace/pick-folder.mjs +152 -0
  173. package/src/workspace/registry.mjs +359 -0
  174. package/src/workspace/remote-links.mjs +397 -0
  175. package/src/workspace/remote-status.mjs +359 -0
  176. package/src/workspace/routes.mjs +1331 -0
  177. package/src/workspace/server.mjs +159 -0
  178. package/src/workspace/sync.mjs +1119 -0
  179. package/src/workspace/task-actions.mjs +215 -0
  180. package/src/workspace/tasks.mjs +507 -0
  181. package/src/workspace/ui/app.js +1886 -0
  182. package/src/workspace/ui/index.html +141 -0
  183. package/src/workspace/ui/plan-bridge.js +120 -0
  184. package/src/workspace/ui/sharepoint.svg +59 -0
  185. package/src/workspace/ui/styles.css +423 -0
  186. package/src/builders/visual-families.mjs +0 -46
@@ -0,0 +1,159 @@
1
+ // The workspace's loopback HTTP server — skeleton. Binds 127.0.0.1 only, serves
2
+ // GET /health (protocol version + draining, nothing else) and two bearer-protected
3
+ // process routes the lifecycle needs (`/api/v1/status`, `/api/v1/stop`).
4
+ // Everything else is the API lane's: pass `handle(req, res, ctx)` and it is
5
+ // asked first for any other path; return false to fall through to 404.
6
+ //
7
+ // No process.exit anywhere: `close()` resolves once the listener and every
8
+ // open socket are gone, and the process ends when nothing else holds it.
9
+
10
+ import { timingSafeEqual } from "node:crypto";
11
+ import http from "node:http";
12
+
13
+ export const PROTOCOL_VERSION = 1;
14
+ export const PREFERRED_PORT = 57103;
15
+ export const LOOPBACK = "127.0.0.1";
16
+
17
+ /**
18
+ * The port a workspace calls home when it has none remembered: 57103, or
19
+ * `SLIDEMIND_WORKSPACE_PORT`. The test preload gives every test file its own,
20
+ * so no test server ever takes the developer's real workspace port — one that
21
+ * did, while the real workspace restarted into new code, pushed it off its
22
+ * port and every link an agent had handed out with it (2026-10-11).
23
+ */
24
+ export function homePort() {
25
+ const n = Number(process.env.SLIDEMIND_WORKSPACE_PORT);
26
+ return Number.isInteger(n) && n > 0 && n < 65536 ? n : PREFERRED_PORT;
27
+ }
28
+
29
+ export function sendJson(res, status, body) {
30
+ const data = JSON.stringify(body);
31
+ res.writeHead(status, {
32
+ "Content-Type": "application/json; charset=utf-8",
33
+ "Cache-Control": "no-store",
34
+ "Content-Length": Buffer.byteLength(data),
35
+ });
36
+ res.end(data);
37
+ }
38
+
39
+ export function sendError(res, status, code, message, { retryable = false } = {}) {
40
+ sendJson(res, status, { ok: false, error: { code, message, retryable } });
41
+ }
42
+
43
+ /** Constant-time bearer check against the descriptor's `cliBearer`. */
44
+ export function authorizeBearer(req, bearer) {
45
+ const header = String(req.headers.authorization ?? "");
46
+ if (!bearer || !header.startsWith("Bearer ")) return false;
47
+ const got = Buffer.from(header.slice(7));
48
+ const want = Buffer.from(bearer);
49
+ return got.length === want.length && timingSafeEqual(got, want);
50
+ }
51
+
52
+ /**
53
+ * `createServer({ bearer, nonce, pendingJobs, pages, onStop, handle, ports })`
54
+ * bearer/nonce from the descriptor the daemon writes
55
+ * pendingJobs() → number, reported by status/stop
56
+ * pages() → number of connected browser pages, reported by status
57
+ * onStop() → Promise, the daemon's drain; the response is sent after it
58
+ * handle optional `(req, res, ctx) => boolean|Promise<boolean>` for the API lane
59
+ * ports the ports to try in order (default homePort())
60
+ * Returns `{ server, listen(), close(), url }`; `listen()` tries each of `ports`
61
+ * and falls back to an ephemeral port when they are all taken (never touching
62
+ * whoever holds them).
63
+ */
64
+ export function createServer({ bearer, nonce, pid = process.pid, pendingJobs = () => 0, pages = () => 0, onStop = async () => {}, handle = null, ports = [homePort()] } = {}) {
65
+ const state = { draining: false, url: null };
66
+ const sockets = new Set();
67
+
68
+ const server = http.createServer(async (req, res) => {
69
+ const url = new URL(req.url, `http://${LOOPBACK}`);
70
+ // Only the workspace's own pages may frame it (the plan and preview iframes):
71
+ // another site cannot lay it under its own page and steer a click onto Approve.
72
+ res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");
73
+ res.setHeader("X-Frame-Options", "SAMEORIGIN");
74
+ // The Host must be exactly the bound loopback authority: a page on another
75
+ // origin rebinding a DNS name to 127.0.0.1 gets nothing.
76
+ if (state.url && req.headers.host !== new URL(state.url).host) {
77
+ return sendError(res, 421, "bad_host", "unexpected Host header");
78
+ }
79
+ if (req.method === "GET" && url.pathname === "/health") {
80
+ // a draining workspace is on its way out: nobody may reuse it
81
+ return sendJson(res, 200, { protocolVersion: PROTOCOL_VERSION, draining: state.draining });
82
+ }
83
+ if (state.draining && url.pathname !== "/api/v1/status") {
84
+ return sendError(res, 503, "workspace_stopping", "workspace is shutting down", { retryable: true });
85
+ }
86
+ if (url.pathname === "/api/v1/status" || url.pathname === "/api/v1/stop") {
87
+ if (!authorizeBearer(req, bearer)) return sendError(res, 401, "unauthorized", "missing or invalid bearer");
88
+ if (url.pathname === "/api/v1/status" && req.method === "GET") {
89
+ return sendJson(res, 200, {
90
+ ok: true,
91
+ protocolVersion: PROTOCOL_VERSION,
92
+ nonce,
93
+ pid,
94
+ draining: state.draining,
95
+ pendingJobs: pendingJobs(),
96
+ pages: pages(),
97
+ });
98
+ }
99
+ if (url.pathname === "/api/v1/stop" && req.method === "POST") {
100
+ if (!state.draining) {
101
+ state.draining = true;
102
+ state.stopping = Promise.resolve().then(onStop);
103
+ }
104
+ await state.stopping;
105
+ sendJson(res, 200, { ok: true, stopped: true, pendingJobs: pendingJobs() });
106
+ // Let the response reach the wire, then drop every socket so the listener closes.
107
+ res.on("finish", () => setTimeout(() => api.close(), 50));
108
+ return;
109
+ }
110
+ return sendError(res, 405, "method_not_allowed", "method not allowed");
111
+ }
112
+ try {
113
+ if (handle && (await handle(req, res, { url, state, bearer }))) return;
114
+ } catch (err) {
115
+ if (!res.headersSent) return sendError(res, 500, "internal", err?.message ?? "internal error");
116
+ return;
117
+ }
118
+ sendError(res, 404, "not_found", "no such route");
119
+ });
120
+ server.on("connection", (s) => {
121
+ sockets.add(s);
122
+ s.on("close", () => sockets.delete(s));
123
+ });
124
+
125
+ const api = {
126
+ server,
127
+ state,
128
+ get url() {
129
+ return state.url;
130
+ },
131
+ listen() {
132
+ // the same port across restarts keeps the page's origin, and with it its cookie
133
+ const order = [...new Set(ports.filter((p) => Number.isInteger(p) && p > 0 && p < 65536)), 0];
134
+ return new Promise((resolve, reject) => {
135
+ const tryPort = (i) => {
136
+ const port = order[i];
137
+ server.once("error", (err) => {
138
+ if ((err.code === "EADDRINUSE" || err.code === "EACCES") && port !== 0) return tryPort(i + 1);
139
+ reject(err);
140
+ });
141
+ server.listen(port, LOOPBACK, () => {
142
+ server.removeAllListeners("error");
143
+ state.url = `http://${LOOPBACK}:${server.address().port}`;
144
+ resolve(state.url);
145
+ });
146
+ };
147
+ tryPort(0);
148
+ });
149
+ },
150
+ close() {
151
+ return new Promise((resolve) => {
152
+ if (!server.listening) return resolve();
153
+ server.close(() => resolve());
154
+ for (const s of sockets) s.destroy();
155
+ });
156
+ },
157
+ };
158
+ return api;
159
+ }