@starlove/orbit 1.4.1 → 2.0.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/Dockerfile +10 -8
- package/README.md +146 -164
- package/dist/full/csp.txt +1 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
- package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
- package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
- package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
- package/dist/full/fonts/Mona_Sans/README.txt +10 -0
- package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
- package/dist/full/fonts/Newsreader/README.txt +8 -0
- package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
- package/dist/full/index.html +36 -653
- package/dist/full/js/app.BmuponPJ.js +32 -0
- package/dist/full/js/app.BmuponPJ.js.map +1 -0
- package/dist/full/js/chunk.1fJGQGbW.js +2 -0
- package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
- package/dist/full/js/chunk.5D7JO2zp.js +2 -0
- package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
- package/dist/full/js/chunk.BrNn_D79.js +2 -0
- package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
- package/dist/full/js/chunk.CokJXIqM.js +2 -0
- package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
- package/dist/full/js/chunk.D_Haqy6R.js +2 -0
- package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
- package/dist/full/js/chunk.RO0jsH6I.js +15 -0
- package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
- package/dist/full/js/chunk.dqWOEtBM.js +2 -0
- package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
- package/dist/full/js/warm.f260bbbc.js +15 -0
- package/dist/full/orbits-favicon.png +0 -0
- package/dist/full/orbits-logo-bg-wide.png +0 -0
- package/dist/full/orbits-logo-bg.png +0 -0
- package/dist/full/orbits-logo.png +0 -0
- package/dist/full/styles.2b154ffe.css +1 -0
- package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
- package/dist/full/version.json +2 -2
- package/docs/AGENT_GUIDE.md +114 -0
- package/docs/UPDATING.md +249 -64
- package/lib/adapters/indexeddb/blobs.d.ts +3 -0
- package/lib/adapters/indexeddb/blobs.js +54 -0
- package/lib/adapters/indexeddb/database.d.ts +60 -0
- package/lib/adapters/indexeddb/database.js +126 -0
- package/lib/adapters/indexeddb/index.d.ts +5 -0
- package/lib/adapters/indexeddb/index.js +3 -0
- package/lib/adapters/indexeddb/locks.d.ts +14 -0
- package/lib/adapters/indexeddb/locks.js +40 -0
- package/lib/adapters/indexeddb/store.d.ts +40 -0
- package/lib/adapters/indexeddb/store.js +424 -0
- package/lib/adapters/indexeddb/tables.d.ts +43 -0
- package/lib/adapters/indexeddb/tables.js +127 -0
- package/lib/adapters/memory/blobs.d.ts +3 -0
- package/lib/adapters/memory/blobs.js +44 -0
- package/lib/adapters/memory/board.d.ts +41 -0
- package/lib/adapters/memory/board.js +521 -0
- package/lib/adapters/memory/index.d.ts +2 -0
- package/lib/adapters/memory/index.js +2 -0
- package/lib/adapters/memory/search.d.ts +6 -0
- package/lib/adapters/memory/search.js +30 -0
- package/lib/adapters/memory/store.d.ts +34 -0
- package/lib/adapters/memory/store.js +266 -0
- package/lib/adapters/sqlite/backups.d.ts +53 -0
- package/lib/adapters/sqlite/backups.js +232 -0
- package/lib/adapters/sqlite/blobs.d.ts +14 -0
- package/lib/adapters/sqlite/blobs.js +109 -0
- package/lib/adapters/sqlite/board.d.ts +77 -0
- package/lib/adapters/sqlite/board.js +328 -0
- package/lib/adapters/sqlite/database.d.ts +67 -0
- package/lib/adapters/sqlite/database.js +285 -0
- package/lib/adapters/sqlite/index.d.ts +3 -0
- package/lib/adapters/sqlite/index.js +2 -0
- package/lib/adapters/sqlite/lock.d.ts +11 -0
- package/lib/adapters/sqlite/lock.js +34 -0
- package/lib/adapters/sqlite/records.d.ts +54 -0
- package/lib/adapters/sqlite/records.js +546 -0
- package/lib/adapters/sqlite/schema.d.ts +54 -0
- package/lib/adapters/sqlite/schema.js +276 -0
- package/lib/adapters/sqlite/store.d.ts +39 -0
- package/lib/adapters/sqlite/store.js +385 -0
- package/lib/browser/in-page.d.ts +31 -0
- package/lib/browser/in-page.js +46 -0
- package/lib/browser/old-preview.d.ts +13 -0
- package/lib/browser/old-preview.js +80 -0
- package/lib/browser/preview-board.d.ts +8 -0
- package/lib/browser/preview-board.js +69 -0
- package/lib/cli/main.d.ts +7 -0
- package/lib/cli/main.js +343 -0
- package/lib/cli/orbit.d.ts +8 -0
- package/lib/cli/orbit.js +25 -0
- package/lib/client/http.d.ts +38 -0
- package/lib/client/http.js +334 -0
- package/lib/client/local.d.ts +26 -0
- package/lib/client/local.js +190 -0
- package/lib/client/object-url.d.ts +8 -0
- package/lib/client/object-url.js +21 -0
- package/lib/client/types.d.ts +268 -0
- package/lib/client/types.js +8 -0
- package/lib/commands/agent-tools.d.ts +31 -0
- package/lib/commands/agent-tools.js +100 -0
- package/lib/commands/index.d.ts +56 -0
- package/lib/commands/index.js +298 -0
- package/lib/commands/parse.d.ts +73 -0
- package/lib/commands/parse.js +231 -0
- package/lib/commands/present.d.ts +134 -0
- package/lib/commands/present.js +198 -0
- package/lib/commands/session.d.ts +85 -0
- package/lib/commands/session.js +138 -0
- package/lib/commands/verbs.d.ts +35 -0
- package/lib/commands/verbs.js +842 -0
- package/lib/core/actors.d.ts +18 -0
- package/lib/core/actors.js +45 -0
- package/lib/core/agent.d.ts +128 -0
- package/lib/core/agent.js +323 -0
- package/lib/core/boards.d.ts +113 -0
- package/lib/core/boards.js +288 -0
- package/lib/core/bootstrap.d.ts +47 -0
- package/lib/core/bootstrap.js +100 -0
- package/lib/core/bytes.d.ts +10 -0
- package/lib/core/bytes.js +52 -0
- package/lib/core/card-paths.d.ts +25 -0
- package/lib/core/card-paths.js +44 -0
- package/lib/core/context.d.ts +60 -0
- package/lib/core/context.js +42 -0
- package/lib/core/cross-board.d.ts +81 -0
- package/lib/core/cross-board.js +228 -0
- package/lib/core/describe.d.ts +21 -0
- package/lib/core/describe.js +81 -0
- package/lib/core/errors.d.ts +159 -0
- package/lib/core/errors.js +160 -0
- package/lib/core/files.d.ts +79 -0
- package/lib/core/files.js +262 -0
- package/lib/core/history.d.ts +20 -0
- package/lib/core/history.js +53 -0
- package/lib/core/image-types.d.ts +16 -0
- package/lib/core/image-types.js +36 -0
- package/lib/core/model.d.ts +263 -0
- package/lib/core/model.js +34 -0
- package/lib/core/nebula-link.d.ts +14 -0
- package/lib/core/nebula-link.js +39 -0
- package/lib/core/relations.d.ts +58 -0
- package/lib/core/relations.js +112 -0
- package/lib/core/search.d.ts +38 -0
- package/lib/core/search.js +92 -0
- package/lib/core/sessions.d.ts +44 -0
- package/lib/core/sessions.js +158 -0
- package/lib/core/snapshot-schema.d.ts +295 -0
- package/lib/core/snapshot-schema.js +82 -0
- package/lib/core/snapshots.d.ts +75 -0
- package/lib/core/snapshots.js +409 -0
- package/lib/core/states.d.ts +25 -0
- package/lib/core/states.js +126 -0
- package/lib/core/text.d.ts +40 -0
- package/lib/core/text.js +113 -0
- package/lib/core/tickets.d.ts +69 -0
- package/lib/core/tickets.js +311 -0
- package/lib/core/trello.d.ts +17 -0
- package/lib/core/trello.js +269 -0
- package/lib/core/verdicts.d.ts +28 -0
- package/lib/core/verdicts.js +92 -0
- package/lib/core/views.d.ts +95 -0
- package/lib/core/views.js +186 -0
- package/lib/http/app.d.ts +46 -0
- package/lib/http/app.js +491 -0
- package/lib/mcp-server.d.ts +10 -0
- package/lib/mcp-server.js +120 -0
- package/lib/node/assets.d.ts +7 -0
- package/lib/node/assets.js +68 -0
- package/lib/node/data-folder.d.ts +21 -0
- package/lib/node/data-folder.js +34 -0
- package/lib/node/env.d.ts +38 -0
- package/lib/node/env.js +49 -0
- package/lib/node/host-check.d.ts +24 -0
- package/lib/node/host-check.js +53 -0
- package/lib/node/host-services.d.ts +9 -0
- package/lib/node/host-services.js +150 -0
- package/lib/node/local.d.ts +22 -0
- package/lib/node/local.js +61 -0
- package/lib/node/migrate.d.ts +53 -0
- package/lib/node/migrate.js +635 -0
- package/lib/node/package.d.ts +7 -0
- package/lib/node/package.js +14 -0
- package/lib/node/repo-board.d.ts +49 -0
- package/lib/node/repo-board.js +245 -0
- package/lib/node/serve.d.ts +8 -0
- package/lib/node/serve.js +106 -0
- package/lib/orbit.d.ts +47 -0
- package/lib/orbit.js +44 -0
- package/lib/quiet-sqlite-warning.d.ts +1 -0
- package/lib/quiet-sqlite-warning.js +18 -0
- package/lib/store/blob-path.d.ts +6 -0
- package/lib/store/blob-path.js +12 -0
- package/lib/store/conformance.d.ts +52 -0
- package/lib/store/conformance.js +1449 -0
- package/lib/store/search-words.d.ts +10 -0
- package/lib/store/search-words.js +19 -0
- package/lib/store/types.d.ts +452 -0
- package/lib/store/types.js +34 -0
- package/llms.txt +63 -0
- package/package.json +109 -25
- package/src/cli/orbit.js +11 -658
- package/src/mcp-server.js +16 -876
- package/SKILL-ORBIT.md +0 -151
- package/build.mjs +0 -210
- package/dist/full/js/app.4e6fbe20.js +0 -952
- package/dist/full/js/app.4e6fbe20.js.map +0 -7
- package/dist/full/styles.4e60d824.css +0 -1
- package/docs/AGENT_PROTOCOL.md +0 -424
- package/docs/MCP_SETUP.md +0 -54
- package/docs/ORBIT_DISPATCH.md +0 -280
- package/docs/SS-Orbit-Dark.png +0 -0
- package/docs/SS-Orbit-Light.png +0 -0
- package/docs/mcp-client.example.json +0 -13
- package/docs/setup/orbit-init-wizard.md +0 -174
- package/public/apple-touch-icon.png +0 -0
- package/public/cursors/live_arrow.cur +0 -0
- package/public/cursors/live_link.cur +0 -0
- package/public/cursors/live_move.cur +0 -0
- package/public/cursors/live_select.cur +0 -0
- package/public/cursors/live_unavail.cur +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/OFL.txt +0 -93
- package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/OFL.txt +0 -93
- package/public/fonts/Inter/README.txt +0 -118
- package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/README.txt +0 -79
- package/public/fonts/OpenRing-Regular.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/README.txt +0 -100
- package/public/fonts/Orbitron/OFL.txt +0 -93
- package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/public/fonts/Orbitron/README.txt +0 -68
- package/public/fonts/Rajdhani/OFL.txt +0 -93
- package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
- package/public/fonts/runescape-uf.ttf +0 -0
- package/public/index.html +0 -769
- package/public/js/actor-labels.js +0 -65
- package/public/js/api.js +0 -39
- package/public/js/app.js +0 -45
- package/public/js/artifact-drawer.js +0 -79
- package/public/js/board-export.js +0 -161
- package/public/js/board-menu.js +0 -374
- package/public/js/card-actions.js +0 -545
- package/public/js/config.js +0 -20
- package/public/js/create-card.js +0 -190
- package/public/js/dom.js +0 -19
- package/public/js/drawer.js +0 -273
- package/public/js/font-preference.js +0 -230
- package/public/js/format.js +0 -433
- package/public/js/help-menu.js +0 -31
- package/public/js/icons.js +0 -16
- package/public/js/kanban.js +0 -694
- package/public/js/local-backend.js +0 -1015
- package/public/js/main.js +0 -161
- package/public/js/modal.js +0 -33
- package/public/js/motion-preference.js +0 -64
- package/public/js/panel.js +0 -58
- package/public/js/router.js +0 -134
- package/public/js/search.js +0 -56
- package/public/js/settings-tabs.js +0 -7
- package/public/js/settings.js +0 -1634
- package/public/js/sse.js +0 -159
- package/public/js/starscape.js +0 -332
- package/public/js/state.js +0 -129
- package/public/js/text-editing.js +0 -128
- package/public/js/theme-preference.js +0 -95
- package/public/js/ticket-detail.js +0 -1516
- package/public/js/ticket-typeahead.js +0 -49
- package/public/js/toast.js +0 -89
- package/public/js/topbar-notes.js +0 -155
- package/public/js/transport.js +0 -22
- package/public/js/typeahead.js +0 -206
- package/public/js/unread.js +0 -121
- package/public/js/update-check.js +0 -58
- package/public/js/url-routes.js +0 -94
- package/public/orbits-favicon.png +0 -0
- package/public/orbits-logo-bg-wide.png +0 -0
- package/public/orbits-logo-bg.png +0 -0
- package/public/orbits-logo.png +0 -0
- package/public/styles.css +0 -7709
- package/src/core/agent.js +0 -387
- package/src/core/agents-md.js +0 -109
- package/src/core/artifacts.js +0 -149
- package/src/core/attachments.js +0 -238
- package/src/core/auth.js +0 -85
- package/src/core/backups.js +0 -142
- package/src/core/board.js +0 -148
- package/src/core/boards.js +0 -409
- package/src/core/bootstrap.js +0 -170
- package/src/core/css-bundle.js +0 -30
- package/src/core/db.js +0 -315
- package/src/core/delete-board.js +0 -163
- package/src/core/dispatch.js +0 -1007
- package/src/core/events.js +0 -314
- package/src/core/http.js +0 -115
- package/src/core/migrate.js +0 -198
- package/src/core/paths.js +0 -55
- package/src/core/provision-repo-board.js +0 -119
- package/src/core/queries.js +0 -246
- package/src/core/registry.js +0 -211
- package/src/core/relations.js +0 -163
- package/src/core/review-verdicts.js +0 -138
- package/src/core/router.js +0 -556
- package/src/core/search.js +0 -183
- package/src/core/seed.js +0 -234
- package/src/core/snapshots.js +0 -692
- package/src/core/states.js +0 -167
- package/src/core/system-picker.js +0 -109
- package/src/core/tickets.js +0 -400
- package/src/core/util.js +0 -106
- package/src/core/version-check.js +0 -154
- package/src/mcp/orbit-client.js +0 -287
- package/src/server.js +0 -115
package/src/core/attachments.js
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, extname, join, normalize, sep } from "node:path";
|
|
4
|
-
import { boardById, ticketById } from "./queries.js";
|
|
5
|
-
import { requireBoardAccess, requirePermission } from "./auth.js";
|
|
6
|
-
import { tx } from "./db.js";
|
|
7
|
-
import { recordEvent } from "./events.js";
|
|
8
|
-
import { bumpTicketUpdatedAt } from "./tickets.js";
|
|
9
|
-
import { httpError, id, now } from "./util.js";
|
|
10
|
-
|
|
11
|
-
export const MAX_IMAGE_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
12
|
-
|
|
13
|
-
const IMAGE_EXTENSIONS = new Map([
|
|
14
|
-
["image/png", ".png"],
|
|
15
|
-
["image/jpeg", ".jpg"],
|
|
16
|
-
["image/gif", ".gif"],
|
|
17
|
-
["image/webp", ".webp"],
|
|
18
|
-
["image/svg+xml", ".svg"],
|
|
19
|
-
["image/bmp", ".bmp"],
|
|
20
|
-
["image/avif", ".avif"]
|
|
21
|
-
]);
|
|
22
|
-
|
|
23
|
-
export function attachmentStorageRoot(boardRow) {
|
|
24
|
-
return join(dirname(boardRow.db_path), "attachments");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function ticketAttachmentDir(boardRow, ticketId) {
|
|
28
|
-
return join(attachmentStorageRoot(boardRow), "tickets", ticketId);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function safeOriginalName(value) {
|
|
32
|
-
const name = String(value || "image").replace(/[\0\r\n]/g, "").trim();
|
|
33
|
-
return name.slice(0, 240) || "image";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function normalizeImageMime(value) {
|
|
37
|
-
const mime = String(value || "").split(";")[0].trim().toLowerCase();
|
|
38
|
-
if (!IMAGE_EXTENSIONS.has(mime)) throw httpError(415, "unsupported_attachment_type");
|
|
39
|
-
return mime;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function extensionFor(mime, originalName = "") {
|
|
43
|
-
const originalExt = extname(originalName).toLowerCase();
|
|
44
|
-
if ([...IMAGE_EXTENSIONS.values()].includes(originalExt)) return originalExt;
|
|
45
|
-
return IMAGE_EXTENSIONS.get(mime) || ".img";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function relativeStoredPath(ticketId, attachmentId, mime, originalName) {
|
|
49
|
-
return normalize(join("tickets", ticketId, `${attachmentId}${extensionFor(mime, originalName)}`)).replace(/\\/g, "/");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function resolveStoredPath(boardRow, storedPath) {
|
|
53
|
-
const root = attachmentStorageRoot(boardRow);
|
|
54
|
-
const abs = join(root, storedPath);
|
|
55
|
-
const normalizedRoot = normalize(root + sep);
|
|
56
|
-
const normalizedAbs = normalize(abs);
|
|
57
|
-
if (!normalizedAbs.startsWith(normalizedRoot)) throw httpError(400, "invalid_attachment_path");
|
|
58
|
-
return normalizedAbs;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function assertTicket(ctx, ticketId, write = false) {
|
|
62
|
-
const { db, board, actor } = ctx;
|
|
63
|
-
const ticket = ticketById(db, ticketId);
|
|
64
|
-
if (!ticket || ticket.board_id !== board.id) throw httpError(404, "ticket_not_found");
|
|
65
|
-
requireBoardAccess(actor, boardById(db, ticket.board_id));
|
|
66
|
-
if (write) requirePermission(actor, "write");
|
|
67
|
-
return ticket;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function rowToAttachment(boardRow, row) {
|
|
71
|
-
const path = resolveStoredPath(boardRow, row.stored_path);
|
|
72
|
-
let missing = true;
|
|
73
|
-
let size = row.size_bytes;
|
|
74
|
-
try {
|
|
75
|
-
const stat = statSync(path);
|
|
76
|
-
missing = !stat.isFile();
|
|
77
|
-
if (!missing) size = stat.size;
|
|
78
|
-
} catch {
|
|
79
|
-
missing = true;
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
id: row.id,
|
|
83
|
-
ticket_id: row.ticket_id,
|
|
84
|
-
board_id: row.board_id,
|
|
85
|
-
original_name: row.original_name,
|
|
86
|
-
mime_type: row.mime_type,
|
|
87
|
-
size_bytes: row.size_bytes,
|
|
88
|
-
sha256: row.sha256,
|
|
89
|
-
stored_path: row.stored_path,
|
|
90
|
-
created_by: row.created_by,
|
|
91
|
-
created_at: row.created_at,
|
|
92
|
-
missing,
|
|
93
|
-
content_url: missing ? null : `/api/tickets/${encodeURIComponent(row.ticket_id)}/attachments/${encodeURIComponent(row.id)}/content`,
|
|
94
|
-
filesystem_size_bytes: missing ? null : size
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function listTicketAttachments(ticketId, ctx) {
|
|
99
|
-
assertTicket(ctx, ticketId);
|
|
100
|
-
const rows = ctx.db
|
|
101
|
-
.prepare("SELECT * FROM ticket_attachments WHERE ticket_id = ? ORDER BY created_at ASC")
|
|
102
|
-
.all(ticketId);
|
|
103
|
-
return { attachments: rows.map((row) => rowToAttachment(ctx.board, row)) };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function getTicketAttachmentContent(ticketId, attachmentId, ctx) {
|
|
107
|
-
assertTicket(ctx, ticketId);
|
|
108
|
-
const row = ctx.db
|
|
109
|
-
.prepare("SELECT * FROM ticket_attachments WHERE id = ? AND ticket_id = ?")
|
|
110
|
-
.get(attachmentId, ticketId);
|
|
111
|
-
if (!row) throw httpError(404, "attachment_not_found");
|
|
112
|
-
const filePath = resolveStoredPath(ctx.board, row.stored_path);
|
|
113
|
-
if (!existsSync(filePath)) throw httpError(404, "attachment_file_missing");
|
|
114
|
-
return { row, filePath };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function createTicketAttachment(ticketId, input, ctx) {
|
|
118
|
-
const ticket = assertTicket(ctx, ticketId, true);
|
|
119
|
-
const mime = normalizeImageMime(input.mime_type);
|
|
120
|
-
const bytes = Buffer.isBuffer(input.bytes) ? input.bytes : Buffer.from(input.bytes || []);
|
|
121
|
-
if (bytes.length === 0) throw httpError(400, "empty_attachment");
|
|
122
|
-
if (bytes.length > MAX_IMAGE_ATTACHMENT_BYTES) throw httpError(413, "attachment_too_large");
|
|
123
|
-
const originalName = safeOriginalName(input.original_name);
|
|
124
|
-
const attachmentId = id();
|
|
125
|
-
const storedPath = relativeStoredPath(ticketId, attachmentId, mime, originalName);
|
|
126
|
-
const filePath = resolveStoredPath(ctx.board, storedPath);
|
|
127
|
-
const hash = createHash("sha256").update(bytes).digest("hex");
|
|
128
|
-
const time = now();
|
|
129
|
-
|
|
130
|
-
mkdirSync(ticketAttachmentDir(ctx.board, ticketId), { recursive: true });
|
|
131
|
-
writeFileSync(filePath, bytes, { flag: "wx" });
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
return tx(ctx.db, () => {
|
|
135
|
-
ctx.db.prepare(
|
|
136
|
-
`INSERT INTO ticket_attachments
|
|
137
|
-
(id, ticket_id, board_id, original_name, stored_path, mime_type, size_bytes, sha256, created_by, created_at)
|
|
138
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
139
|
-
).run(attachmentId, ticketId, ticket.board_id, originalName, storedPath, mime, bytes.length, hash, ctx.actor.name, time);
|
|
140
|
-
ctx.db.prepare("UPDATE tickets SET updated_at = ? WHERE id = ?").run(time, ticketId);
|
|
141
|
-
recordEvent(ctx.db, ticket.board_id, "attachment_created", ticketId, ctx.actor.name, {
|
|
142
|
-
attachment_id: attachmentId,
|
|
143
|
-
original_name: originalName,
|
|
144
|
-
mime_type: mime,
|
|
145
|
-
size_bytes: bytes.length,
|
|
146
|
-
sha256: hash
|
|
147
|
-
});
|
|
148
|
-
bumpTicketUpdatedAt(ctx.db, ticket.parent_ticket_id, time);
|
|
149
|
-
return rowToAttachment(ctx.board, ctx.db.prepare("SELECT * FROM ticket_attachments WHERE id = ?").get(attachmentId));
|
|
150
|
-
});
|
|
151
|
-
} catch (error) {
|
|
152
|
-
try { unlinkSync(filePath); } catch {}
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function deleteTicketAttachment(ticketId, attachmentId, ctx) {
|
|
158
|
-
const ticket = assertTicket(ctx, ticketId, true);
|
|
159
|
-
const row = ctx.db
|
|
160
|
-
.prepare("SELECT * FROM ticket_attachments WHERE id = ? AND ticket_id = ?")
|
|
161
|
-
.get(attachmentId, ticketId);
|
|
162
|
-
if (!row) throw httpError(404, "attachment_not_found");
|
|
163
|
-
const filePath = resolveStoredPath(ctx.board, row.stored_path);
|
|
164
|
-
return tx(ctx.db, () => {
|
|
165
|
-
ctx.db.prepare("DELETE FROM ticket_attachments WHERE id = ?").run(attachmentId);
|
|
166
|
-
const time = now();
|
|
167
|
-
ctx.db.prepare("UPDATE tickets SET updated_at = ? WHERE id = ?").run(time, ticketId);
|
|
168
|
-
recordEvent(ctx.db, ticket.board_id, "attachment_deleted", ticketId, ctx.actor.name, {
|
|
169
|
-
attachment_id: attachmentId,
|
|
170
|
-
original_name: row.original_name
|
|
171
|
-
});
|
|
172
|
-
bumpTicketUpdatedAt(ctx.db, ticket.parent_ticket_id, time);
|
|
173
|
-
try { unlinkSync(filePath); } catch {}
|
|
174
|
-
return { ok: true, deleted_id: attachmentId };
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function attachmentRowsForBoard(db, boardId) {
|
|
179
|
-
return db
|
|
180
|
-
.prepare("SELECT * FROM ticket_attachments WHERE board_id = ? ORDER BY created_at ASC")
|
|
181
|
-
.all(boardId);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function serializeAttachmentForExport(boardRow, row, includeBytes = false) {
|
|
185
|
-
const attachment = rowToAttachment(boardRow, row);
|
|
186
|
-
if (!includeBytes || attachment.missing) {
|
|
187
|
-
return { ...attachment, data_base64: null, included: false };
|
|
188
|
-
}
|
|
189
|
-
const filePath = resolveStoredPath(boardRow, row.stored_path);
|
|
190
|
-
const data = readFileSync(filePath);
|
|
191
|
-
return {
|
|
192
|
-
...attachment,
|
|
193
|
-
data_base64: data.toString("base64"),
|
|
194
|
-
included: true,
|
|
195
|
-
missing: false
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export function importAttachmentRows(snapshot, ctx, targetBoardId) {
|
|
200
|
-
const rows = Array.isArray(snapshot.attachments) ? snapshot.attachments : [];
|
|
201
|
-
for (const attachment of rows) {
|
|
202
|
-
const attachmentId = attachment.id || id();
|
|
203
|
-
const ticketId = attachment.ticket_id;
|
|
204
|
-
if (!ticketId || !ctx.db.prepare("SELECT 1 FROM tickets WHERE id = ?").get(ticketId)) continue;
|
|
205
|
-
const mime = normalizeImageMime(attachment.mime_type || "image/png");
|
|
206
|
-
const originalName = safeOriginalName(attachment.original_name);
|
|
207
|
-
const storedPath = relativeStoredPath(ticketId, attachmentId, mime, originalName);
|
|
208
|
-
let size = Number(attachment.size_bytes || 0);
|
|
209
|
-
let sha = String(attachment.sha256 || "");
|
|
210
|
-
|
|
211
|
-
if (attachment.data_base64) {
|
|
212
|
-
const bytes = Buffer.from(String(attachment.data_base64), "base64");
|
|
213
|
-
if (bytes.length > MAX_IMAGE_ATTACHMENT_BYTES) throw httpError(413, "attachment_too_large");
|
|
214
|
-
size = bytes.length;
|
|
215
|
-
sha = createHash("sha256").update(bytes).digest("hex");
|
|
216
|
-
const filePath = resolveStoredPath(ctx.board, storedPath);
|
|
217
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
218
|
-
writeFileSync(filePath, bytes);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
ctx.db.prepare(
|
|
222
|
-
`INSERT OR IGNORE INTO ticket_attachments
|
|
223
|
-
(id, ticket_id, board_id, original_name, stored_path, mime_type, size_bytes, sha256, created_by, created_at)
|
|
224
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
225
|
-
).run(
|
|
226
|
-
attachmentId,
|
|
227
|
-
ticketId,
|
|
228
|
-
targetBoardId,
|
|
229
|
-
originalName,
|
|
230
|
-
storedPath,
|
|
231
|
-
mime,
|
|
232
|
-
size,
|
|
233
|
-
sha,
|
|
234
|
-
attachment.created_by || "import",
|
|
235
|
-
attachment.created_at || now()
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
}
|
package/src/core/auth.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
export function localOwnerActor() {
|
|
2
|
-
return {
|
|
3
|
-
id: "local",
|
|
4
|
-
name: "local",
|
|
5
|
-
type: "human",
|
|
6
|
-
role: "owner",
|
|
7
|
-
permissions: ["owner", "read", "write", "claim"],
|
|
8
|
-
boards: ["*"]
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function localAgentActor() {
|
|
13
|
-
return {
|
|
14
|
-
id: "local-agent",
|
|
15
|
-
name: "agent",
|
|
16
|
-
type: "agent",
|
|
17
|
-
role: "owner",
|
|
18
|
-
permissions: ["owner", "read", "write", "claim"],
|
|
19
|
-
boards: ["*"]
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function headerValue(req, name) {
|
|
24
|
-
const value = req?.headers?.[name.toLowerCase()];
|
|
25
|
-
if (Array.isArray(value)) return value[0] || "";
|
|
26
|
-
return String(value || "");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function cleanActorField(value, fallback) {
|
|
30
|
-
const clean = String(value || "")
|
|
31
|
-
.trim()
|
|
32
|
-
.replace(/\s+/g, " ")
|
|
33
|
-
.slice(0, 80);
|
|
34
|
-
return clean || fallback;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function actorIdFromName(name) {
|
|
38
|
-
return String(name || "agent")
|
|
39
|
-
.toLowerCase()
|
|
40
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
41
|
-
.replace(/^-+|-+$/g, "")
|
|
42
|
-
.slice(0, 80) || "agent";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function actorFromHttpRequest(req) {
|
|
46
|
-
const actorType = headerValue(req, "x-orbit-actor-type").trim().toLowerCase();
|
|
47
|
-
const agentName = headerValue(req, "x-orbit-agent-name");
|
|
48
|
-
if (actorType !== "agent" && !agentName.trim()) return localOwnerActor();
|
|
49
|
-
|
|
50
|
-
const name = cleanActorField(headerValue(req, "x-orbit-actor-name") || agentName, "agent");
|
|
51
|
-
const id = cleanActorField(headerValue(req, "x-orbit-actor-id"), actorIdFromName(name));
|
|
52
|
-
return {
|
|
53
|
-
...localAgentActor(),
|
|
54
|
-
id,
|
|
55
|
-
name
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function authenticate() {
|
|
60
|
-
return localOwnerActor();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function actorFromToken() {
|
|
64
|
-
return localOwnerActor();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function canAccessBoard() {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function requireBoardAccess() {}
|
|
72
|
-
|
|
73
|
-
export function requireOwner() {}
|
|
74
|
-
|
|
75
|
-
export function requirePermission() {}
|
|
76
|
-
|
|
77
|
-
export function publicActor(actor) {
|
|
78
|
-
return {
|
|
79
|
-
id: actor.id,
|
|
80
|
-
name: actor.name,
|
|
81
|
-
type: actor.type,
|
|
82
|
-
role: actor.role,
|
|
83
|
-
permissions: actor.permissions
|
|
84
|
-
};
|
|
85
|
-
}
|
package/src/core/backups.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { closeConnection, getRegistry, hasConnection, openConnection } from "./db.js";
|
|
4
|
-
import { BACKUP_DIR, REGISTRY_DB_PATH } from "./paths.js";
|
|
5
|
-
import { now } from "./util.js";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_RETENTION = 30;
|
|
8
|
-
const DEFAULT_AUTO_DELAY_MS = 120_000;
|
|
9
|
-
const scheduledBoardBackups = new Map();
|
|
10
|
-
|
|
11
|
-
function backupsEnabled() {
|
|
12
|
-
return !["0", "false", "off"].includes(String(process.env.ORBIT_AUTO_BACKUPS || "1").toLowerCase());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function retentionLimit() {
|
|
16
|
-
const parsed = Number(process.env.ORBIT_BACKUP_RETENTION || DEFAULT_RETENTION);
|
|
17
|
-
if (!Number.isFinite(parsed) || parsed < 1) return DEFAULT_RETENTION;
|
|
18
|
-
return Math.floor(parsed);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function cleanSegment(value) {
|
|
22
|
-
return String(value || "unknown")
|
|
23
|
-
.trim()
|
|
24
|
-
.replace(/[^a-zA-Z0-9._-]+/g, "-")
|
|
25
|
-
.replace(/^-+|-+$/g, "")
|
|
26
|
-
.slice(0, 80) || "unknown";
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function timestampForPath() {
|
|
30
|
-
return now().replace(/[:.]/g, "-");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function sqlLiteral(value) {
|
|
34
|
-
return `'${String(value).replace(/'/g, "''")}'`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function pruneBackups(dir, suffix) {
|
|
38
|
-
if (!existsSync(dir)) return;
|
|
39
|
-
const limit = retentionLimit();
|
|
40
|
-
const files = readdirSync(dir)
|
|
41
|
-
.filter((name) => name.endsWith(suffix))
|
|
42
|
-
.sort()
|
|
43
|
-
.reverse();
|
|
44
|
-
for (const stale of files.slice(limit)) {
|
|
45
|
-
unlinkSync(join(dir, stale));
|
|
46
|
-
const manifest = join(dir, `${stale}.json`);
|
|
47
|
-
if (existsSync(manifest)) unlinkSync(manifest);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function latestBackupStartsWith(dir, prefix, suffix) {
|
|
52
|
-
if (!existsSync(dir)) return false;
|
|
53
|
-
return readdirSync(dir).some((name) => name.startsWith(prefix) && name.endsWith(suffix));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function writeBackup(db, targetPath) {
|
|
57
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
58
|
-
db.exec(`VACUUM INTO ${sqlLiteral(targetPath)};`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function writeManifest(targetPath, payload) {
|
|
62
|
-
writeFileSync(`${targetPath}.json`, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function automaticDelayMs() {
|
|
66
|
-
const parsed = Number(process.env.ORBIT_AUTO_BACKUP_DELAY_MS || DEFAULT_AUTO_DELAY_MS);
|
|
67
|
-
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_AUTO_DELAY_MS;
|
|
68
|
-
return Math.floor(parsed);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function backupBoardDatabase(boardRow, db, reason = "manual", options = {}) {
|
|
72
|
-
if (!backupsEnabled()) return null;
|
|
73
|
-
const boardId = cleanSegment(boardRow.id);
|
|
74
|
-
const slug = cleanSegment(boardRow.slug);
|
|
75
|
-
const dir = join(BACKUP_DIR, "boards", boardId);
|
|
76
|
-
const dayPrefix = `${now().slice(0, 10)}T`;
|
|
77
|
-
if (options.oncePerDay && latestBackupStartsWith(dir, dayPrefix, ".board.db")) return null;
|
|
78
|
-
|
|
79
|
-
const filename = `${timestampForPath()}-${cleanSegment(reason)}-${slug}.board.db`;
|
|
80
|
-
const targetPath = join(dir, filename);
|
|
81
|
-
writeBackup(db, targetPath);
|
|
82
|
-
writeManifest(targetPath, {
|
|
83
|
-
kind: "board",
|
|
84
|
-
reason,
|
|
85
|
-
created_at: now(),
|
|
86
|
-
board_id: boardRow.id,
|
|
87
|
-
slug: boardRow.slug,
|
|
88
|
-
name: boardRow.name,
|
|
89
|
-
repo_path: boardRow.repo_path,
|
|
90
|
-
db_path: boardRow.db_path
|
|
91
|
-
});
|
|
92
|
-
pruneBackups(dir, ".board.db");
|
|
93
|
-
return { path: targetPath };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function scheduleAutomaticBoardBackup(boardRow, db) {
|
|
97
|
-
if (!backupsEnabled() || !boardRow?.id || !db) return;
|
|
98
|
-
const existing = scheduledBoardBackups.get(boardRow.id);
|
|
99
|
-
if (existing) clearTimeout(existing);
|
|
100
|
-
|
|
101
|
-
const timer = setTimeout(() => {
|
|
102
|
-
scheduledBoardBackups.delete(boardRow.id);
|
|
103
|
-
const hadBoardConnection = hasConnection(boardRow.db_path);
|
|
104
|
-
const hadRegistryConnection = hasConnection(REGISTRY_DB_PATH);
|
|
105
|
-
const backupDb = openConnection(boardRow.db_path);
|
|
106
|
-
try {
|
|
107
|
-
backupBoardDatabase(boardRow, backupDb, "auto-write");
|
|
108
|
-
backupRegistry("auto-write");
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.warn(`[orbit] Automatic write backup failed for "${boardRow.slug}": ${error.message}`);
|
|
111
|
-
} finally {
|
|
112
|
-
if (!hadBoardConnection) closeConnection(boardRow.db_path);
|
|
113
|
-
if (!hadRegistryConnection) closeConnection(REGISTRY_DB_PATH);
|
|
114
|
-
}
|
|
115
|
-
}, automaticDelayMs());
|
|
116
|
-
timer.unref?.();
|
|
117
|
-
scheduledBoardBackups.set(boardRow.id, timer);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function cancelAutomaticBoardBackup(boardId) {
|
|
121
|
-
const existing = scheduledBoardBackups.get(boardId);
|
|
122
|
-
if (!existing) return;
|
|
123
|
-
clearTimeout(existing);
|
|
124
|
-
scheduledBoardBackups.delete(boardId);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function backupRegistry(reason = "manual", options = {}) {
|
|
128
|
-
if (!backupsEnabled()) return null;
|
|
129
|
-
const dir = join(BACKUP_DIR, "registry");
|
|
130
|
-
const dayPrefix = `${now().slice(0, 10)}T`;
|
|
131
|
-
if (options.oncePerDay && latestBackupStartsWith(dir, dayPrefix, ".registry.db")) return null;
|
|
132
|
-
|
|
133
|
-
const targetPath = join(dir, `${timestampForPath()}-${cleanSegment(reason)}.registry.db`);
|
|
134
|
-
writeBackup(getRegistry(), targetPath);
|
|
135
|
-
writeManifest(targetPath, {
|
|
136
|
-
kind: "registry",
|
|
137
|
-
reason,
|
|
138
|
-
created_at: now()
|
|
139
|
-
});
|
|
140
|
-
pruneBackups(dir, ".registry.db");
|
|
141
|
-
return { path: targetPath };
|
|
142
|
-
}
|
package/src/core/board.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// Barrel module + startup orchestration.
|
|
2
|
-
//
|
|
3
|
-
// Startup contract:
|
|
4
|
-
// 1. Ensure the registry DB and its schema exist.
|
|
5
|
-
// 2. If the registry has zero boards, create a first board under
|
|
6
|
-
// PROJECT_ROOT and seed it. This is the open-source one-command-startup
|
|
7
|
-
// ergonomic — drop into a repo, run the server, get a board.
|
|
8
|
-
// 3. Iterate every registry board, ensuring the per-board schema exists
|
|
9
|
-
// and reindexing FTS for that board's DB. No "active board" singleton:
|
|
10
|
-
// each iteration opens its own connection.
|
|
11
|
-
// 4. When launched through `orbit run`, refresh Orbit-managed
|
|
12
|
-
// SKILL-ORBIT.md copies for registered repo paths.
|
|
13
|
-
//
|
|
14
|
-
// Beyond startup this module just re-exports per-domain functions for
|
|
15
|
-
// `src/server.js` and `src/mcp-server.js` to consume.
|
|
16
|
-
|
|
17
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
18
|
-
import { resolve } from "node:path";
|
|
19
|
-
import "./paths.js";
|
|
20
|
-
import { syncSkillOrbitMd } from "./agents-md.js";
|
|
21
|
-
import { createBoardSchema, createRegistrySchema } from "./db.js";
|
|
22
|
-
import { reindexAllTickets } from "./tickets.js";
|
|
23
|
-
import { deleteBoard, listBoards, openBoardDb, syncRegistryFromBoardDb } from "./registry.js";
|
|
24
|
-
import { provisionRepoBoard } from "./provision-repo-board.js";
|
|
25
|
-
import { migrateInRepoBoards } from "./migrate.js";
|
|
26
|
-
import { id, slugify } from "./util.js";
|
|
27
|
-
import {
|
|
28
|
-
DATA_DIR,
|
|
29
|
-
EXPORT_DIR,
|
|
30
|
-
BACKUP_DIR,
|
|
31
|
-
PORT,
|
|
32
|
-
PROJECT_ROOT,
|
|
33
|
-
PUBLIC_DIR,
|
|
34
|
-
REGISTRY_DB_PATH,
|
|
35
|
-
ROOT_DIR
|
|
36
|
-
} from "./paths.js";
|
|
37
|
-
|
|
38
|
-
function ensureWorkspace() {
|
|
39
|
-
mkdirSync(DATA_DIR, { recursive: true });
|
|
40
|
-
createRegistrySchema();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function ensureFirstBoard() {
|
|
44
|
-
if (listBoards().length > 0) return;
|
|
45
|
-
// Bootstrap a first board under PROJECT_ROOT so the server is useful out of
|
|
46
|
-
// the box. Shared logic with `orbit init` (provision-repo-board.js).
|
|
47
|
-
provisionRepoBoard(PROJECT_ROOT);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function ensureSchemaForEveryBoard() {
|
|
51
|
-
for (const row of listBoards()) {
|
|
52
|
-
if (!existsSync(row.db_path)) {
|
|
53
|
-
console.warn(
|
|
54
|
-
`[orbit] Removing stale registry row for "${row.slug}": database file missing (${row.db_path}).`
|
|
55
|
-
);
|
|
56
|
-
deleteBoard(row.id);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const db = openBoardDb(row);
|
|
60
|
-
createBoardSchema(db);
|
|
61
|
-
reindexAllTickets(db);
|
|
62
|
-
syncRegistryFromBoardDb(row, db);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function syncManagedSkillOrbitFiles() {
|
|
67
|
-
const skillSrc = resolve(ROOT_DIR, "SKILL-ORBIT.md");
|
|
68
|
-
for (const row of listBoards()) {
|
|
69
|
-
if (!row.repo_path) continue;
|
|
70
|
-
// Respect boards created without "Generate AI helper files" — don't
|
|
71
|
-
// re-push SKILL-ORBIT.md into a repo the user opted out of.
|
|
72
|
-
if (row.manage_helper_files === 0) continue;
|
|
73
|
-
try {
|
|
74
|
-
const result = syncSkillOrbitMd(row.repo_path, skillSrc);
|
|
75
|
-
if (result.status === "missing_source") {
|
|
76
|
-
console.warn(`[orbit] Missing managed SKILL-ORBIT.md source at ${skillSrc}; skipped repo guidance refresh.`);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (result.status === "written") {
|
|
80
|
-
console.log(`[orbit] Refreshed managed SKILL-ORBIT.md in ${row.repo_path}`);
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.warn(`[orbit] Could not refresh SKILL-ORBIT.md in ${row.repo_path}: ${error.message}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
ensureWorkspace();
|
|
89
|
-
// Move any per-board SQLite files that still live in in-repo .orbit/ directories
|
|
90
|
-
// into the central DATA_DIR/boards/<slug>/ store. Must run before
|
|
91
|
-
// ensureSchemaForEveryBoard so all connections open against the canonical paths.
|
|
92
|
-
migrateInRepoBoards();
|
|
93
|
-
// Reconcile the registry against disk *before* deciding whether to bootstrap a
|
|
94
|
-
// first board — otherwise a stale row from a deleted .orbit/ folder makes
|
|
95
|
-
// listBoards() non-empty and we never re-seed.
|
|
96
|
-
ensureSchemaForEveryBoard();
|
|
97
|
-
ensureFirstBoard();
|
|
98
|
-
if (process.env.ORBIT_SYNC_MANAGED_SKILL_ORBIT === "1") {
|
|
99
|
-
syncManagedSkillOrbitFiles();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export const boardRuntime = {
|
|
103
|
-
rootDir: ROOT_DIR,
|
|
104
|
-
publicDir: PUBLIC_DIR,
|
|
105
|
-
projectRoot: PROJECT_ROOT,
|
|
106
|
-
dataDir: DATA_DIR,
|
|
107
|
-
registryPath: REGISTRY_DB_PATH,
|
|
108
|
-
exportDir: EXPORT_DIR,
|
|
109
|
-
backupDir: BACKUP_DIR,
|
|
110
|
-
port: PORT
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
export function startupSummary(port = PORT, host = "localhost") {
|
|
114
|
-
const rows = listBoards();
|
|
115
|
-
return {
|
|
116
|
-
url: `http://${host}:${port}`,
|
|
117
|
-
registryPath: REGISTRY_DB_PATH,
|
|
118
|
-
boardCount: rows.length,
|
|
119
|
-
boardPaths: rows.map((row) => row.db_path)
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export { localOwnerActor, localAgentActor } from "./auth.js";
|
|
124
|
-
export { startSSEStream } from "./events.js";
|
|
125
|
-
export { sendEmpty, sendJson, serveStatic } from "./http.js";
|
|
126
|
-
export { handleApi } from "./router.js";
|
|
127
|
-
export { getBootstrap } from "./bootstrap.js";
|
|
128
|
-
export {
|
|
129
|
-
createBoard,
|
|
130
|
-
createBoardEntry,
|
|
131
|
-
getBoardContext,
|
|
132
|
-
insertBoardEntry,
|
|
133
|
-
updateBoard,
|
|
134
|
-
updateBoardEntry,
|
|
135
|
-
boardManual
|
|
136
|
-
} from "./boards.js";
|
|
137
|
-
export { archiveTicket, createComment, createTicket, deleteTicket, restoreTicket, updateTicket } from "./tickets.js";
|
|
138
|
-
export { createReviewVerdict, getReviewVerdict, listReviewVerdicts } from "./review-verdicts.js";
|
|
139
|
-
export { archivedTicketsForBoard } from "./queries.js";
|
|
140
|
-
export { getTicketBlockers, getTicketRelations } from "./relations.js";
|
|
141
|
-
export { checkpointTicket, claimNext, completeTicket, getAgentDispatchPacket, getContextPack, getContextPackFull, readComments, readTicket } from "./agent.js";
|
|
142
|
-
export { searchTickets } from "./search.js";
|
|
143
|
-
export { getTicketMarkdownArtifact, storeTicketMarkdownArtifact } from "./artifacts.js";
|
|
144
|
-
export { exportBoard } from "./snapshots.js";
|
|
145
|
-
|
|
146
|
-
// Helpers for ad-hoc id/slug minting in callers (e.g. MCP tools that mint
|
|
147
|
-
// per-process tokens). Intentionally kept tiny and side-effect-free.
|
|
148
|
-
export const mint = { id, slugify };
|