@starlove/orbit 1.4.0 → 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.62905fcb.js +0 -952
- package/dist/full/js/app.62905fcb.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 -229
- 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/boards.js
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { getRegistry, tx } from "./db.js";
|
|
2
|
-
import { boardById, compactStatePositions } from "./queries.js";
|
|
3
|
-
import {
|
|
4
|
-
computeNewBoardDbPath,
|
|
5
|
-
ensureBoardDbFileAndSchema,
|
|
6
|
-
insertBoard,
|
|
7
|
-
updateBoardMeta
|
|
8
|
-
} from "./registry.js";
|
|
9
|
-
import { requireBoardAccess, requirePermission } from "./auth.js";
|
|
10
|
-
import { recordEvent } from "./events.js";
|
|
11
|
-
import { buildBoardFromRepo } from "./seed.js";
|
|
12
|
-
import { generateHelperFiles } from "./agents-md.js";
|
|
13
|
-
import { httpError, id, now, normalizePath, normalizeProjectEntryType, requiredString, slugify } from "./util.js";
|
|
14
|
-
import { DATA_DIR, MCP_SERVER_PATH, ROOT_DIR } from "./paths.js";
|
|
15
|
-
import { resolve } from "node:path";
|
|
16
|
-
|
|
17
|
-
/** Create a brand-new board on disk + register it. Does not require an
|
|
18
|
-
* existing ctx.board / ctx.db: this is registry-only. */
|
|
19
|
-
export function createBoard(body, ctx) {
|
|
20
|
-
const { actor } = ctx;
|
|
21
|
-
requirePermission(actor, "owner");
|
|
22
|
-
|
|
23
|
-
const name = requiredString(body.name, "name");
|
|
24
|
-
const slug = slugify(body.slug || name);
|
|
25
|
-
// Folder is optional: a board without a repo_path is a pure kanban board (no
|
|
26
|
-
// agent dispatch, no managed helper files). When present it is the coding
|
|
27
|
-
// project where SKILL-ORBIT.md / AGENTS.md live for the AI agent's reference.
|
|
28
|
-
const repoPath = normalizePath(body.repo_path || body.system_path || "");
|
|
29
|
-
// Only meaningful with a folder; default on so the common "link my repo" path
|
|
30
|
-
// keeps writing the docs. Stored so the serve-time refresh respects opt-out.
|
|
31
|
-
const manageHelperFiles = repoPath ? body.manage_helper_files !== false : false;
|
|
32
|
-
const reg = getRegistry();
|
|
33
|
-
if (reg.prepare("SELECT id FROM boards WHERE slug = ?").get(slug)) {
|
|
34
|
-
throw httpError(409, "board_slug_taken");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const dbPath = computeNewBoardDbPath(repoPath, slug);
|
|
38
|
-
const db = ensureBoardDbFileAndSchema(dbPath);
|
|
39
|
-
|
|
40
|
-
const boardId = id();
|
|
41
|
-
const time = now();
|
|
42
|
-
const sniff = repoPath ? buildBoardFromRepo(repoPath) : { repoUrl: "", defaultBranch: "main" };
|
|
43
|
-
|
|
44
|
-
const aiEnabled = body.ai_enabled === undefined ? true : Boolean(body.ai_enabled);
|
|
45
|
-
const defaultStates = body.states || (aiEnabled ? [
|
|
46
|
-
["Backlog", null],
|
|
47
|
-
["Todo", null],
|
|
48
|
-
["AI Ready", "ai_ready"],
|
|
49
|
-
["In Progress", "in_progress"],
|
|
50
|
-
["Review", "review"],
|
|
51
|
-
["Done", "done"],
|
|
52
|
-
["Cancelled", null]
|
|
53
|
-
] : [
|
|
54
|
-
["Backlog", null],
|
|
55
|
-
["Todo", null],
|
|
56
|
-
["In Progress", "in_progress"],
|
|
57
|
-
["Review", "review"],
|
|
58
|
-
["Done", "done"],
|
|
59
|
-
["Cancelled", null]
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
// Board row + lanes + creation event land atomically in the board DB. The
|
|
63
|
-
// registry insert below is a different database (can't share this
|
|
64
|
-
// transaction); syncRegistryFromBoardDb repairs it idempotently if that
|
|
65
|
-
// second write is ever lost.
|
|
66
|
-
tx(db, () => {
|
|
67
|
-
db.prepare(
|
|
68
|
-
`INSERT INTO boards
|
|
69
|
-
(id, slug, name, repo_url, system_path, default_branch, ai_enabled, created_at, updated_at)
|
|
70
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
71
|
-
).run(
|
|
72
|
-
boardId,
|
|
73
|
-
slug,
|
|
74
|
-
name,
|
|
75
|
-
body.repo_url || sniff.repoUrl || "",
|
|
76
|
-
repoPath,
|
|
77
|
-
body.default_branch || sniff.defaultBranch || "main",
|
|
78
|
-
aiEnabled ? 1 : 0,
|
|
79
|
-
time,
|
|
80
|
-
time
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
defaultStates.forEach(([stateName, role], position) => {
|
|
84
|
-
db.prepare(
|
|
85
|
-
`INSERT INTO states
|
|
86
|
-
(id, board_id, name, position, is_default, role, created_at)
|
|
87
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
88
|
-
).run(id(), boardId, stateName, position, stateName === "Todo" ? 1 : 0, role || null, time);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (aiEnabled) ensureAgentLanes(db, boardId);
|
|
92
|
-
recordEvent(db, boardId, "board_created", null, actor.name, { board_id: boardId, slug });
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
insertBoard({
|
|
96
|
-
id: boardId,
|
|
97
|
-
slug,
|
|
98
|
-
name,
|
|
99
|
-
repo_path: repoPath,
|
|
100
|
-
db_path: dbPath,
|
|
101
|
-
repo_url: body.repo_url || sniff.repoUrl || "",
|
|
102
|
-
default_branch: body.default_branch || sniff.defaultBranch || "main",
|
|
103
|
-
manage_helper_files: manageHelperFiles ? 1 : 0,
|
|
104
|
-
last_active_at: time,
|
|
105
|
-
created_at: time,
|
|
106
|
-
updated_at: time
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Mirror `orbit init`: write SKILL-ORBIT.md + the AGENTS.md Orbit section into
|
|
110
|
-
// the project so the browser flow leaves the same files on disk. Best-effort —
|
|
111
|
-
// a board is still created if the files can't be written.
|
|
112
|
-
if (manageHelperFiles) {
|
|
113
|
-
try {
|
|
114
|
-
generateHelperFiles(repoPath, resolve(ROOT_DIR, "SKILL-ORBIT.md"));
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.warn(`[orbit] Could not write helper files in ${repoPath}: ${error.message}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return boardById(db, boardId);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const AGENT_LANES = [
|
|
124
|
-
{ name: "AI Ready", role: "ai_ready" },
|
|
125
|
-
{ name: "In Progress", role: "in_progress" },
|
|
126
|
-
{ name: "Review", role: "review" }
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
function desiredAgentLanePosition(db, boardId, role) {
|
|
130
|
-
if (role === "ai_ready") {
|
|
131
|
-
const todo = db.prepare("SELECT position FROM states WHERE board_id = ? AND name = 'Todo'").get(boardId);
|
|
132
|
-
if (todo) return Number(todo.position) + 1;
|
|
133
|
-
const inProgress = db.prepare("SELECT position FROM states WHERE board_id = ? AND role = 'in_progress'").get(boardId);
|
|
134
|
-
if (inProgress) return Number(inProgress.position);
|
|
135
|
-
}
|
|
136
|
-
const maxPosition = db
|
|
137
|
-
.prepare("SELECT MAX(position) AS value FROM states WHERE board_id = ?")
|
|
138
|
-
.get(boardId).value;
|
|
139
|
-
return Number(maxPosition ?? -1) + 1;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function openStateSlot(db, boardId, position, exceptStateId = null) {
|
|
143
|
-
db.prepare(
|
|
144
|
-
`UPDATE states
|
|
145
|
-
SET position = position + 1
|
|
146
|
-
WHERE board_id = ?
|
|
147
|
-
AND position >= ?
|
|
148
|
-
AND (? IS NULL OR id != ?)`
|
|
149
|
-
).run(boardId, position, exceptStateId, exceptStateId);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Ensure the role-bearing lanes that agent flow depends on exist on the
|
|
153
|
-
* board. AI Ready is kept directly after Todo so claim-next has a predictable
|
|
154
|
-
* handoff column. Idempotent. */
|
|
155
|
-
export function ensureAgentLanes(db, boardId) {
|
|
156
|
-
const time = now();
|
|
157
|
-
for (const lane of AGENT_LANES) {
|
|
158
|
-
const existing = db
|
|
159
|
-
.prepare("SELECT id, position FROM states WHERE board_id = ? AND role = ?")
|
|
160
|
-
.get(boardId, lane.role);
|
|
161
|
-
if (existing) {
|
|
162
|
-
if (lane.role === "ai_ready") {
|
|
163
|
-
let desiredPosition = desiredAgentLanePosition(db, boardId, lane.role);
|
|
164
|
-
if (Number(existing.position) !== desiredPosition) {
|
|
165
|
-
db.prepare("UPDATE states SET position = -1 WHERE id = ?").run(existing.id);
|
|
166
|
-
compactStatePositions(db, boardId);
|
|
167
|
-
desiredPosition = desiredAgentLanePosition(db, boardId, lane.role);
|
|
168
|
-
openStateSlot(db, boardId, desiredPosition, existing.id);
|
|
169
|
-
db.prepare("UPDATE states SET position = ? WHERE id = ?").run(desiredPosition, existing.id);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
const position = desiredAgentLanePosition(db, boardId, lane.role);
|
|
175
|
-
openStateSlot(db, boardId, position);
|
|
176
|
-
db.prepare(
|
|
177
|
-
`INSERT INTO states
|
|
178
|
-
(id, board_id, name, position, is_default, role, created_at)
|
|
179
|
-
VALUES (?, ?, ?, ?, 0, ?, ?)`
|
|
180
|
-
).run(id(), boardId, lane.name, position, lane.role, time);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function enableAiCollaboration(db, boardId, options = {}) {
|
|
185
|
-
const innerBoard = boardById(db, boardId);
|
|
186
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
187
|
-
|
|
188
|
-
const time = now();
|
|
189
|
-
tx(db, () => {
|
|
190
|
-
db.prepare("UPDATE boards SET ai_enabled = 1, updated_at = ? WHERE id = ?").run(time, boardId);
|
|
191
|
-
ensureAgentLanes(db, boardId);
|
|
192
|
-
|
|
193
|
-
if (options.stageOnboardingTicket) {
|
|
194
|
-
const aiReadyLane = db.prepare("SELECT id FROM states WHERE board_id = ? AND role = 'ai_ready'").get(boardId);
|
|
195
|
-
if (aiReadyLane) {
|
|
196
|
-
db.prepare(
|
|
197
|
-
`UPDATE tickets
|
|
198
|
-
SET state_id = ?, updated_at = ?
|
|
199
|
-
WHERE board_id = ?
|
|
200
|
-
AND number = 12
|
|
201
|
-
AND title = 'Try Orbit MCP on this ticket'`
|
|
202
|
-
).run(aiReadyLane.id, time, boardId);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
recordEvent(db, boardId, "ai_enabled", null, options.actor || "system", { board_id: boardId });
|
|
207
|
-
});
|
|
208
|
-
return boardById(db, boardId);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function disableAiCollaboration(db, boardId, options = {}) {
|
|
212
|
-
const innerBoard = boardById(db, boardId);
|
|
213
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
214
|
-
|
|
215
|
-
const time = now();
|
|
216
|
-
return tx(db, () => {
|
|
217
|
-
db.prepare("UPDATE boards SET ai_enabled = 0, updated_at = ? WHERE id = ?").run(time, boardId);
|
|
218
|
-
recordEvent(db, boardId, "ai_disabled", null, options.actor || "system", { board_id: boardId });
|
|
219
|
-
return boardById(db, boardId);
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function updateBoard(boardId, body, ctx) {
|
|
224
|
-
const { db, board, actor } = ctx;
|
|
225
|
-
if (boardId !== board.id) throw httpError(404, "board_not_found");
|
|
226
|
-
const innerBoard = boardById(db, boardId);
|
|
227
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
228
|
-
requireBoardAccess(actor, innerBoard);
|
|
229
|
-
requirePermission(actor, "write");
|
|
230
|
-
|
|
231
|
-
const allowed = [
|
|
232
|
-
"name",
|
|
233
|
-
"repo_url",
|
|
234
|
-
"system_path",
|
|
235
|
-
"default_branch",
|
|
236
|
-
"project_notes",
|
|
237
|
-
"agent_instructions"
|
|
238
|
-
];
|
|
239
|
-
const sets = [];
|
|
240
|
-
const values = [];
|
|
241
|
-
|
|
242
|
-
for (const field of allowed) {
|
|
243
|
-
if (body[field] !== undefined) {
|
|
244
|
-
sets.push(`${field} = ?`);
|
|
245
|
-
values.push(field === "name" ? requiredString(body[field], "name") : String(body[field] ?? ""));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const aiTurningOn = body.ai_enabled !== undefined && Boolean(body.ai_enabled);
|
|
250
|
-
if (body.ai_enabled !== undefined) {
|
|
251
|
-
sets.push("ai_enabled = ?");
|
|
252
|
-
values.push(body.ai_enabled ? 1 : 0);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// manage_helper_files lives on the registry row (not the per-board DB), so it
|
|
256
|
-
// is handled separately from `sets`. A request that only toggles it must
|
|
257
|
-
// still be processed even when no per-board columns change.
|
|
258
|
-
const manageHelperFiles =
|
|
259
|
-
body.manage_helper_files === undefined ? undefined : Boolean(body.manage_helper_files);
|
|
260
|
-
|
|
261
|
-
if (sets.length === 0 && manageHelperFiles === undefined) return innerBoard;
|
|
262
|
-
|
|
263
|
-
if (sets.length > 0) {
|
|
264
|
-
sets.push("updated_at = ?");
|
|
265
|
-
values.push(now(), boardId);
|
|
266
|
-
tx(db, () => {
|
|
267
|
-
db.prepare(`UPDATE boards SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
268
|
-
if (aiTurningOn) ensureAgentLanes(db, boardId);
|
|
269
|
-
recordEvent(db, boardId, "board_updated", null, actor.name, { board_id: boardId, fields: Object.keys(body) });
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Mirror the changed fields into the registry row so cross-board listings
|
|
274
|
-
// stay coherent without needing a second source of truth.
|
|
275
|
-
const after = boardById(db, boardId);
|
|
276
|
-
const repoPath = normalizePath(after.system_path || "");
|
|
277
|
-
updateBoardMeta(boardId, {
|
|
278
|
-
name: after.name,
|
|
279
|
-
repo_url: after.repo_url,
|
|
280
|
-
default_branch: after.default_branch,
|
|
281
|
-
repo_path: repoPath,
|
|
282
|
-
...(manageHelperFiles === undefined ? {} : { manage_helper_files: manageHelperFiles ? 1 : 0 })
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// Generating the helper files is the active half of turning the toggle on:
|
|
286
|
-
// write them now rather than waiting for the next `orbit run` refresh.
|
|
287
|
-
// Turning it off just stops the managed refresh; existing files are left.
|
|
288
|
-
if (manageHelperFiles && repoPath) {
|
|
289
|
-
try {
|
|
290
|
-
generateHelperFiles(repoPath, resolve(ROOT_DIR, "SKILL-ORBIT.md"));
|
|
291
|
-
} catch (error) {
|
|
292
|
-
console.warn(`[orbit] Could not write helper files in ${repoPath}: ${error.message}`);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return after;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export function getBoardContext(boardId, ctx, options = {}) {
|
|
300
|
-
const { db, board, actor } = ctx;
|
|
301
|
-
if (boardId !== board.id) throw httpError(404, "board_not_found");
|
|
302
|
-
const innerBoard = boardById(db, boardId);
|
|
303
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
304
|
-
requireBoardAccess(actor, innerBoard);
|
|
305
|
-
return boardManual(boardId, ctx, options);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export function boardManual(boardId, ctx, options = {}) {
|
|
309
|
-
const { db } = ctx;
|
|
310
|
-
const innerBoard = boardById(db, boardId);
|
|
311
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
312
|
-
const includeStruck = Boolean(options.includeStruck);
|
|
313
|
-
const allEntries = db
|
|
314
|
-
.prepare("SELECT * FROM board_entries WHERE board_id = ? ORDER BY created_at DESC")
|
|
315
|
-
.all(boardId);
|
|
316
|
-
const activeEntries = allEntries.filter((entry) => !entry.struck_at);
|
|
317
|
-
const entries = includeStruck ? allEntries : activeEntries;
|
|
318
|
-
return {
|
|
319
|
-
board: {
|
|
320
|
-
id: innerBoard.id,
|
|
321
|
-
slug: innerBoard.slug,
|
|
322
|
-
name: innerBoard.name,
|
|
323
|
-
repo_url: innerBoard.repo_url,
|
|
324
|
-
system_path: innerBoard.system_path || ctx.board.repo_path || "",
|
|
325
|
-
default_branch: innerBoard.default_branch,
|
|
326
|
-
agent_instructions: innerBoard.agent_instructions,
|
|
327
|
-
ai_enabled: innerBoard.ai_enabled,
|
|
328
|
-
manage_helper_files: ctx.board.manage_helper_files,
|
|
329
|
-
created_at: innerBoard.created_at,
|
|
330
|
-
updated_at: innerBoard.updated_at
|
|
331
|
-
},
|
|
332
|
-
entries,
|
|
333
|
-
deployment: {
|
|
334
|
-
app_root: ROOT_DIR,
|
|
335
|
-
system_path: innerBoard.system_path || ctx.board.repo_path || "",
|
|
336
|
-
data_dir: DATA_DIR,
|
|
337
|
-
mcp_server_path: MCP_SERVER_PATH,
|
|
338
|
-
db_path: ctx.board.db_path
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
export function createBoardEntry(boardId, body, ctx) {
|
|
344
|
-
const { db, board, actor } = ctx;
|
|
345
|
-
if (boardId !== board.id) throw httpError(404, "board_not_found");
|
|
346
|
-
const innerBoard = boardById(db, boardId);
|
|
347
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
348
|
-
requireBoardAccess(actor, innerBoard);
|
|
349
|
-
requirePermission(actor, "write");
|
|
350
|
-
|
|
351
|
-
const entryType = normalizeProjectEntryType(body.type);
|
|
352
|
-
const entryTitle = requiredString(body.title, "title");
|
|
353
|
-
return tx(db, () => {
|
|
354
|
-
const entry = insertBoardEntry(
|
|
355
|
-
db,
|
|
356
|
-
boardId,
|
|
357
|
-
entryType,
|
|
358
|
-
entryTitle,
|
|
359
|
-
body.body || "",
|
|
360
|
-
actor.name,
|
|
361
|
-
body.ticket_id || null
|
|
362
|
-
);
|
|
363
|
-
recordEvent(db, boardId, "board_entry_created", body.ticket_id || null, actor.name, {
|
|
364
|
-
board_id: boardId,
|
|
365
|
-
entry_id: entry.id,
|
|
366
|
-
type: entry.type
|
|
367
|
-
});
|
|
368
|
-
return entry;
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
export function updateBoardEntry(entryId, body, ctx) {
|
|
373
|
-
const { db, board, actor } = ctx;
|
|
374
|
-
const entry = db.prepare("SELECT * FROM board_entries WHERE id = ?").get(entryId);
|
|
375
|
-
if (!entry || entry.board_id !== board.id) throw httpError(404, "board_entry_not_found");
|
|
376
|
-
|
|
377
|
-
const innerBoard = boardById(db, entry.board_id);
|
|
378
|
-
if (!innerBoard) throw httpError(404, "board_not_found");
|
|
379
|
-
requireBoardAccess(actor, innerBoard);
|
|
380
|
-
requirePermission(actor, "write");
|
|
381
|
-
|
|
382
|
-
const shouldStrike = Boolean(body.struck);
|
|
383
|
-
const struckAt = shouldStrike ? entry.struck_at || now() : null;
|
|
384
|
-
const updatedAt = now();
|
|
385
|
-
return tx(db, () => {
|
|
386
|
-
db.prepare("UPDATE board_entries SET struck_at = ?, updated_at = ? WHERE id = ?").run(struckAt, updatedAt, entryId);
|
|
387
|
-
const updated = db.prepare("SELECT * FROM board_entries WHERE id = ?").get(entryId);
|
|
388
|
-
recordEvent(
|
|
389
|
-
db,
|
|
390
|
-
entry.board_id,
|
|
391
|
-
shouldStrike ? "board_entry_struck" : "board_entry_restored",
|
|
392
|
-
entry.ticket_id || null,
|
|
393
|
-
actor.name,
|
|
394
|
-
{ board_id: entry.board_id, entry_id: entry.id, type: entry.type }
|
|
395
|
-
);
|
|
396
|
-
return updated;
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
export function insertBoardEntry(db, boardId, type, title, body, createdBy, ticketId = null) {
|
|
401
|
-
const entryId = id();
|
|
402
|
-
const time = now();
|
|
403
|
-
db.prepare(
|
|
404
|
-
`INSERT INTO board_entries
|
|
405
|
-
(id, board_id, type, title, body, ticket_id, created_by, created_at, updated_at)
|
|
406
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
407
|
-
).run(entryId, boardId, normalizeProjectEntryType(type), title, body || "", ticketId, createdBy, time, time);
|
|
408
|
-
return db.prepare("SELECT * FROM board_entries WHERE id = ?").get(entryId);
|
|
409
|
-
}
|
package/src/core/bootstrap.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { compactTicket, ticketById } from "./queries.js";
|
|
2
|
-
import { canAccessBoard, publicActor } from "./auth.js";
|
|
3
|
-
import { EXPORT_DIR } from "./paths.js";
|
|
4
|
-
import {
|
|
5
|
-
getBoardByRegistryId,
|
|
6
|
-
listBoards,
|
|
7
|
-
openBoardDb,
|
|
8
|
-
pickDefaultBoard,
|
|
9
|
-
touchBoardActive
|
|
10
|
-
} from "./registry.js";
|
|
11
|
-
|
|
12
|
-
function boardSummaryFromRegistry(r) {
|
|
13
|
-
return {
|
|
14
|
-
id: r.id,
|
|
15
|
-
slug: r.slug,
|
|
16
|
-
name: r.name,
|
|
17
|
-
repo_url: r.repo_url,
|
|
18
|
-
default_branch: r.default_branch,
|
|
19
|
-
repo_path: r.repo_path,
|
|
20
|
-
system_path: r.repo_path,
|
|
21
|
-
project_notes: "",
|
|
22
|
-
agent_instructions: "",
|
|
23
|
-
ai_enabled: 0,
|
|
24
|
-
created_at: r.created_at,
|
|
25
|
-
updated_at: r.updated_at
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Build the bootstrap payload for one board (or just registry metadata if the
|
|
31
|
-
* actor has no accessible boards). No global state is mutated; the chosen
|
|
32
|
-
* board's last_active_at is bumped so subsequent default-pick prefers it.
|
|
33
|
-
*
|
|
34
|
-
* @param actor authenticated actor
|
|
35
|
-
* @param requestedBoardId optional board id from `?board_id=` (or null)
|
|
36
|
-
* @param requestedBoardSlug optional board slug from `?board=` (or null)
|
|
37
|
-
*/
|
|
38
|
-
export function getBootstrap(actor, requestedBoardId = null, requestedBoardSlug = null) {
|
|
39
|
-
const registryRows = listBoards();
|
|
40
|
-
const accessible = registryRows.filter((row) => canAccessBoard(actor, { slug: row.slug }));
|
|
41
|
-
|
|
42
|
-
if (accessible.length === 0) {
|
|
43
|
-
return {
|
|
44
|
-
actor: publicActor(actor),
|
|
45
|
-
meta: {
|
|
46
|
-
db_path: null,
|
|
47
|
-
export_dir: EXPORT_DIR
|
|
48
|
-
},
|
|
49
|
-
boards: [],
|
|
50
|
-
board_entries: [],
|
|
51
|
-
states: [],
|
|
52
|
-
labels: [],
|
|
53
|
-
tickets: []
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let chosen = null;
|
|
58
|
-
if (requestedBoardId) {
|
|
59
|
-
chosen = accessible.find((row) => row.id === requestedBoardId) || null;
|
|
60
|
-
}
|
|
61
|
-
if (!chosen && requestedBoardSlug) {
|
|
62
|
-
chosen = accessible.find((row) => row.slug === requestedBoardSlug) || null;
|
|
63
|
-
}
|
|
64
|
-
if (!chosen) {
|
|
65
|
-
const defaultRow = pickDefaultBoard();
|
|
66
|
-
if (defaultRow && accessible.some((row) => row.id === defaultRow.id)) {
|
|
67
|
-
chosen = defaultRow;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (!chosen) {
|
|
71
|
-
const sorted = [...accessible].sort(
|
|
72
|
-
(a, b) =>
|
|
73
|
-
String(b.last_active_at || "").localeCompare(String(a.last_active_at || "")) ||
|
|
74
|
-
String(a.name).localeCompare(String(b.name))
|
|
75
|
-
);
|
|
76
|
-
chosen = sorted[0];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Re-fetch in case the chosen row came from pickDefaultBoard before the
|
|
80
|
-
// accessible filter. Either way, this is the row we open.
|
|
81
|
-
const chosenRow = getBoardByRegistryId(chosen.id);
|
|
82
|
-
touchBoardActive(chosenRow.id);
|
|
83
|
-
const db = openBoardDb(chosenRow);
|
|
84
|
-
const innerBoard = db.prepare("SELECT * FROM boards LIMIT 1").get();
|
|
85
|
-
|
|
86
|
-
const boards = accessible.map((r) => {
|
|
87
|
-
if (innerBoard && r.id === innerBoard.id) {
|
|
88
|
-
return {
|
|
89
|
-
...innerBoard,
|
|
90
|
-
repo_path: r.repo_path,
|
|
91
|
-
system_path: innerBoard.system_path || r.repo_path
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
return boardSummaryFromRegistry(r);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const boardIds = new Set(innerBoard ? [innerBoard.id] : []);
|
|
98
|
-
|
|
99
|
-
const states = db
|
|
100
|
-
.prepare("SELECT * FROM states ORDER BY position")
|
|
101
|
-
.all()
|
|
102
|
-
.filter((state) => boardIds.has(state.board_id));
|
|
103
|
-
|
|
104
|
-
const tickets = db
|
|
105
|
-
.prepare(
|
|
106
|
-
`SELECT t.*, s.name AS state_name, s.role AS state_role,
|
|
107
|
-
b.slug AS board_slug, b.name AS board_name
|
|
108
|
-
FROM tickets t
|
|
109
|
-
JOIN states s ON s.id = t.state_id
|
|
110
|
-
JOIN boards b ON b.id = t.board_id
|
|
111
|
-
WHERE t.archived_at IS NULL
|
|
112
|
-
ORDER BY t.updated_at DESC, t.id ASC`
|
|
113
|
-
)
|
|
114
|
-
.all()
|
|
115
|
-
.filter((ticket) => boardIds.has(ticket.board_id));
|
|
116
|
-
|
|
117
|
-
const labels = db
|
|
118
|
-
.prepare("SELECT * FROM labels ORDER BY name")
|
|
119
|
-
.all()
|
|
120
|
-
.filter((label) => boardIds.has(label.board_id));
|
|
121
|
-
|
|
122
|
-
const ticketLabels = db
|
|
123
|
-
.prepare(
|
|
124
|
-
`SELECT tl.ticket_id, l.id, l.name, l.color
|
|
125
|
-
FROM ticket_labels tl
|
|
126
|
-
JOIN labels l ON l.id = tl.label_id`
|
|
127
|
-
)
|
|
128
|
-
.all()
|
|
129
|
-
.filter((label) => tickets.some((ticket) => ticket.id === label.ticket_id));
|
|
130
|
-
|
|
131
|
-
const commentCounts = db
|
|
132
|
-
.prepare("SELECT ticket_id, COUNT(*) AS count FROM comments GROUP BY ticket_id")
|
|
133
|
-
.all()
|
|
134
|
-
.filter((row) => tickets.some((ticket) => ticket.id === row.ticket_id));
|
|
135
|
-
|
|
136
|
-
const childCounts = db
|
|
137
|
-
.prepare(
|
|
138
|
-
"SELECT parent_ticket_id AS ticket_id, COUNT(*) AS count FROM tickets WHERE parent_ticket_id IS NOT NULL AND archived_at IS NULL GROUP BY parent_ticket_id"
|
|
139
|
-
)
|
|
140
|
-
.all()
|
|
141
|
-
.filter((row) => tickets.some((ticket) => ticket.id === row.ticket_id));
|
|
142
|
-
|
|
143
|
-
const boardEntries = db
|
|
144
|
-
.prepare("SELECT * FROM board_entries ORDER BY created_at DESC")
|
|
145
|
-
.all()
|
|
146
|
-
.filter((entry) => boardIds.has(entry.board_id));
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
actor: publicActor(actor),
|
|
150
|
-
// The server may choose a default board that is not first in the registry's
|
|
151
|
-
// alphabetic board list. Expose that choice explicitly so first-page load
|
|
152
|
-
// selects the same board whose lanes/tickets are included below.
|
|
153
|
-
active_board_id: chosenRow.id,
|
|
154
|
-
meta: {
|
|
155
|
-
db_path: chosenRow.db_path,
|
|
156
|
-
export_dir: EXPORT_DIR
|
|
157
|
-
},
|
|
158
|
-
boards,
|
|
159
|
-
board_entries: boardEntries,
|
|
160
|
-
states,
|
|
161
|
-
labels,
|
|
162
|
-
tickets: tickets.map((ticket) => ({
|
|
163
|
-
...ticket,
|
|
164
|
-
labels: ticketLabels.filter((label) => label.ticket_id === ticket.id),
|
|
165
|
-
comment_count: commentCounts.find((row) => row.ticket_id === ticket.id)?.count || 0,
|
|
166
|
-
child_count: childCounts.find((row) => row.ticket_id === ticket.id)?.count || 0,
|
|
167
|
-
parent_ticket: ticket.parent_ticket_id ? compactTicket(ticketById(db, ticket.parent_ticket_id)) : null
|
|
168
|
-
}))
|
|
169
|
-
};
|
|
170
|
-
}
|
package/src/core/css-bundle.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// CSS bundling seam for Starscape UI package imports.
|
|
2
|
-
//
|
|
3
|
-
// public/styles.css remains Orbit's editable source of truth, but it can now
|
|
4
|
-
// import @starlove/ui component CSS. Static builds and source dev serving both
|
|
5
|
-
// pass that source through esbuild so browsers never have to resolve package
|
|
6
|
-
// specifiers from raw CSS.
|
|
7
|
-
|
|
8
|
-
const CURSOR_ASSET_LOADERS = {
|
|
9
|
-
".cur": "file"
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export async function bundleCss({ entryPoint, outfile, minify = false }) {
|
|
13
|
-
const { build } = await import("esbuild");
|
|
14
|
-
const result = await build({
|
|
15
|
-
entryPoints: [entryPoint],
|
|
16
|
-
bundle: true,
|
|
17
|
-
target: "es2022",
|
|
18
|
-
minify,
|
|
19
|
-
sourcemap: false,
|
|
20
|
-
write: false,
|
|
21
|
-
outfile,
|
|
22
|
-
assetNames: "cursors/[name]",
|
|
23
|
-
loader: CURSOR_ASSET_LOADERS,
|
|
24
|
-
external: ["./fonts/*", "fonts/*"],
|
|
25
|
-
legalComments: "none"
|
|
26
|
-
});
|
|
27
|
-
const cssFile = result.outputFiles.find((file) => file.path.endsWith(".css"));
|
|
28
|
-
if (!cssFile) throw new Error("CSS bundle did not produce a stylesheet");
|
|
29
|
-
return { cssFile, outputFiles: result.outputFiles };
|
|
30
|
-
}
|