@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.
Files changed (329) hide show
  1. package/Dockerfile +10 -8
  2. package/README.md +146 -164
  3. package/dist/full/csp.txt +1 -0
  4. package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
  5. package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
  6. package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
  7. package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
  8. package/dist/full/fonts/Mona_Sans/README.txt +10 -0
  9. package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
  10. package/dist/full/fonts/Newsreader/README.txt +8 -0
  11. package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
  12. package/dist/full/index.html +36 -653
  13. package/dist/full/js/app.BmuponPJ.js +32 -0
  14. package/dist/full/js/app.BmuponPJ.js.map +1 -0
  15. package/dist/full/js/chunk.1fJGQGbW.js +2 -0
  16. package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
  17. package/dist/full/js/chunk.5D7JO2zp.js +2 -0
  18. package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
  19. package/dist/full/js/chunk.BrNn_D79.js +2 -0
  20. package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
  21. package/dist/full/js/chunk.CokJXIqM.js +2 -0
  22. package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
  23. package/dist/full/js/chunk.D_Haqy6R.js +2 -0
  24. package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
  25. package/dist/full/js/chunk.RO0jsH6I.js +15 -0
  26. package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
  27. package/dist/full/js/chunk.dqWOEtBM.js +2 -0
  28. package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
  29. package/dist/full/js/warm.f260bbbc.js +15 -0
  30. package/dist/full/orbits-favicon.png +0 -0
  31. package/dist/full/orbits-logo-bg-wide.png +0 -0
  32. package/dist/full/orbits-logo-bg.png +0 -0
  33. package/dist/full/orbits-logo.png +0 -0
  34. package/dist/full/styles.2b154ffe.css +1 -0
  35. package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
  36. package/dist/full/version.json +2 -2
  37. package/docs/AGENT_GUIDE.md +114 -0
  38. package/docs/UPDATING.md +249 -64
  39. package/lib/adapters/indexeddb/blobs.d.ts +3 -0
  40. package/lib/adapters/indexeddb/blobs.js +54 -0
  41. package/lib/adapters/indexeddb/database.d.ts +60 -0
  42. package/lib/adapters/indexeddb/database.js +126 -0
  43. package/lib/adapters/indexeddb/index.d.ts +5 -0
  44. package/lib/adapters/indexeddb/index.js +3 -0
  45. package/lib/adapters/indexeddb/locks.d.ts +14 -0
  46. package/lib/adapters/indexeddb/locks.js +40 -0
  47. package/lib/adapters/indexeddb/store.d.ts +40 -0
  48. package/lib/adapters/indexeddb/store.js +424 -0
  49. package/lib/adapters/indexeddb/tables.d.ts +43 -0
  50. package/lib/adapters/indexeddb/tables.js +127 -0
  51. package/lib/adapters/memory/blobs.d.ts +3 -0
  52. package/lib/adapters/memory/blobs.js +44 -0
  53. package/lib/adapters/memory/board.d.ts +41 -0
  54. package/lib/adapters/memory/board.js +521 -0
  55. package/lib/adapters/memory/index.d.ts +2 -0
  56. package/lib/adapters/memory/index.js +2 -0
  57. package/lib/adapters/memory/search.d.ts +6 -0
  58. package/lib/adapters/memory/search.js +30 -0
  59. package/lib/adapters/memory/store.d.ts +34 -0
  60. package/lib/adapters/memory/store.js +266 -0
  61. package/lib/adapters/sqlite/backups.d.ts +53 -0
  62. package/lib/adapters/sqlite/backups.js +232 -0
  63. package/lib/adapters/sqlite/blobs.d.ts +14 -0
  64. package/lib/adapters/sqlite/blobs.js +109 -0
  65. package/lib/adapters/sqlite/board.d.ts +77 -0
  66. package/lib/adapters/sqlite/board.js +328 -0
  67. package/lib/adapters/sqlite/database.d.ts +67 -0
  68. package/lib/adapters/sqlite/database.js +285 -0
  69. package/lib/adapters/sqlite/index.d.ts +3 -0
  70. package/lib/adapters/sqlite/index.js +2 -0
  71. package/lib/adapters/sqlite/lock.d.ts +11 -0
  72. package/lib/adapters/sqlite/lock.js +34 -0
  73. package/lib/adapters/sqlite/records.d.ts +54 -0
  74. package/lib/adapters/sqlite/records.js +546 -0
  75. package/lib/adapters/sqlite/schema.d.ts +54 -0
  76. package/lib/adapters/sqlite/schema.js +276 -0
  77. package/lib/adapters/sqlite/store.d.ts +39 -0
  78. package/lib/adapters/sqlite/store.js +385 -0
  79. package/lib/browser/in-page.d.ts +31 -0
  80. package/lib/browser/in-page.js +46 -0
  81. package/lib/browser/old-preview.d.ts +13 -0
  82. package/lib/browser/old-preview.js +80 -0
  83. package/lib/browser/preview-board.d.ts +8 -0
  84. package/lib/browser/preview-board.js +69 -0
  85. package/lib/cli/main.d.ts +7 -0
  86. package/lib/cli/main.js +343 -0
  87. package/lib/cli/orbit.d.ts +8 -0
  88. package/lib/cli/orbit.js +25 -0
  89. package/lib/client/http.d.ts +38 -0
  90. package/lib/client/http.js +334 -0
  91. package/lib/client/local.d.ts +26 -0
  92. package/lib/client/local.js +190 -0
  93. package/lib/client/object-url.d.ts +8 -0
  94. package/lib/client/object-url.js +21 -0
  95. package/lib/client/types.d.ts +268 -0
  96. package/lib/client/types.js +8 -0
  97. package/lib/commands/agent-tools.d.ts +31 -0
  98. package/lib/commands/agent-tools.js +100 -0
  99. package/lib/commands/index.d.ts +56 -0
  100. package/lib/commands/index.js +298 -0
  101. package/lib/commands/parse.d.ts +73 -0
  102. package/lib/commands/parse.js +231 -0
  103. package/lib/commands/present.d.ts +134 -0
  104. package/lib/commands/present.js +198 -0
  105. package/lib/commands/session.d.ts +85 -0
  106. package/lib/commands/session.js +138 -0
  107. package/lib/commands/verbs.d.ts +35 -0
  108. package/lib/commands/verbs.js +842 -0
  109. package/lib/core/actors.d.ts +18 -0
  110. package/lib/core/actors.js +45 -0
  111. package/lib/core/agent.d.ts +128 -0
  112. package/lib/core/agent.js +323 -0
  113. package/lib/core/boards.d.ts +113 -0
  114. package/lib/core/boards.js +288 -0
  115. package/lib/core/bootstrap.d.ts +47 -0
  116. package/lib/core/bootstrap.js +100 -0
  117. package/lib/core/bytes.d.ts +10 -0
  118. package/lib/core/bytes.js +52 -0
  119. package/lib/core/card-paths.d.ts +25 -0
  120. package/lib/core/card-paths.js +44 -0
  121. package/lib/core/context.d.ts +60 -0
  122. package/lib/core/context.js +42 -0
  123. package/lib/core/cross-board.d.ts +81 -0
  124. package/lib/core/cross-board.js +228 -0
  125. package/lib/core/describe.d.ts +21 -0
  126. package/lib/core/describe.js +81 -0
  127. package/lib/core/errors.d.ts +159 -0
  128. package/lib/core/errors.js +160 -0
  129. package/lib/core/files.d.ts +79 -0
  130. package/lib/core/files.js +262 -0
  131. package/lib/core/history.d.ts +20 -0
  132. package/lib/core/history.js +53 -0
  133. package/lib/core/image-types.d.ts +16 -0
  134. package/lib/core/image-types.js +36 -0
  135. package/lib/core/model.d.ts +263 -0
  136. package/lib/core/model.js +34 -0
  137. package/lib/core/nebula-link.d.ts +14 -0
  138. package/lib/core/nebula-link.js +39 -0
  139. package/lib/core/relations.d.ts +58 -0
  140. package/lib/core/relations.js +112 -0
  141. package/lib/core/search.d.ts +38 -0
  142. package/lib/core/search.js +92 -0
  143. package/lib/core/sessions.d.ts +44 -0
  144. package/lib/core/sessions.js +158 -0
  145. package/lib/core/snapshot-schema.d.ts +295 -0
  146. package/lib/core/snapshot-schema.js +82 -0
  147. package/lib/core/snapshots.d.ts +75 -0
  148. package/lib/core/snapshots.js +409 -0
  149. package/lib/core/states.d.ts +25 -0
  150. package/lib/core/states.js +126 -0
  151. package/lib/core/text.d.ts +40 -0
  152. package/lib/core/text.js +113 -0
  153. package/lib/core/tickets.d.ts +69 -0
  154. package/lib/core/tickets.js +311 -0
  155. package/lib/core/trello.d.ts +17 -0
  156. package/lib/core/trello.js +269 -0
  157. package/lib/core/verdicts.d.ts +28 -0
  158. package/lib/core/verdicts.js +92 -0
  159. package/lib/core/views.d.ts +95 -0
  160. package/lib/core/views.js +186 -0
  161. package/lib/http/app.d.ts +46 -0
  162. package/lib/http/app.js +491 -0
  163. package/lib/mcp-server.d.ts +10 -0
  164. package/lib/mcp-server.js +120 -0
  165. package/lib/node/assets.d.ts +7 -0
  166. package/lib/node/assets.js +68 -0
  167. package/lib/node/data-folder.d.ts +21 -0
  168. package/lib/node/data-folder.js +34 -0
  169. package/lib/node/env.d.ts +38 -0
  170. package/lib/node/env.js +49 -0
  171. package/lib/node/host-check.d.ts +24 -0
  172. package/lib/node/host-check.js +53 -0
  173. package/lib/node/host-services.d.ts +9 -0
  174. package/lib/node/host-services.js +150 -0
  175. package/lib/node/local.d.ts +22 -0
  176. package/lib/node/local.js +61 -0
  177. package/lib/node/migrate.d.ts +53 -0
  178. package/lib/node/migrate.js +635 -0
  179. package/lib/node/package.d.ts +7 -0
  180. package/lib/node/package.js +14 -0
  181. package/lib/node/repo-board.d.ts +49 -0
  182. package/lib/node/repo-board.js +245 -0
  183. package/lib/node/serve.d.ts +8 -0
  184. package/lib/node/serve.js +106 -0
  185. package/lib/orbit.d.ts +47 -0
  186. package/lib/orbit.js +44 -0
  187. package/lib/quiet-sqlite-warning.d.ts +1 -0
  188. package/lib/quiet-sqlite-warning.js +18 -0
  189. package/lib/store/blob-path.d.ts +6 -0
  190. package/lib/store/blob-path.js +12 -0
  191. package/lib/store/conformance.d.ts +52 -0
  192. package/lib/store/conformance.js +1449 -0
  193. package/lib/store/search-words.d.ts +10 -0
  194. package/lib/store/search-words.js +19 -0
  195. package/lib/store/types.d.ts +452 -0
  196. package/lib/store/types.js +34 -0
  197. package/llms.txt +63 -0
  198. package/package.json +109 -25
  199. package/src/cli/orbit.js +11 -658
  200. package/src/mcp-server.js +16 -876
  201. package/SKILL-ORBIT.md +0 -151
  202. package/build.mjs +0 -210
  203. package/dist/full/js/app.4e6fbe20.js +0 -952
  204. package/dist/full/js/app.4e6fbe20.js.map +0 -7
  205. package/dist/full/styles.4e60d824.css +0 -1
  206. package/docs/AGENT_PROTOCOL.md +0 -424
  207. package/docs/MCP_SETUP.md +0 -54
  208. package/docs/ORBIT_DISPATCH.md +0 -280
  209. package/docs/SS-Orbit-Dark.png +0 -0
  210. package/docs/SS-Orbit-Light.png +0 -0
  211. package/docs/mcp-client.example.json +0 -13
  212. package/docs/setup/orbit-init-wizard.md +0 -174
  213. package/public/apple-touch-icon.png +0 -0
  214. package/public/cursors/live_arrow.cur +0 -0
  215. package/public/cursors/live_link.cur +0 -0
  216. package/public/cursors/live_move.cur +0 -0
  217. package/public/cursors/live_select.cur +0 -0
  218. package/public/cursors/live_unavail.cur +0 -0
  219. package/public/favicon-32x32.png +0 -0
  220. package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
  221. package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
  222. package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
  223. package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
  224. package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
  225. package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
  226. package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
  227. package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
  228. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
  229. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
  230. package/public/fonts/Chakra_Petch/OFL.txt +0 -93
  231. package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
  232. package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
  233. package/public/fonts/Inter/OFL.txt +0 -93
  234. package/public/fonts/Inter/README.txt +0 -118
  235. package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  236. package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
  237. package/public/fonts/JetBrains_Mono/README.txt +0 -79
  238. package/public/fonts/OpenRing-Regular.ttf +0 -0
  239. package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  240. package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  241. package/public/fonts/Open_Sans/README.txt +0 -100
  242. package/public/fonts/Orbitron/OFL.txt +0 -93
  243. package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
  244. package/public/fonts/Orbitron/README.txt +0 -68
  245. package/public/fonts/Rajdhani/OFL.txt +0 -93
  246. package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
  247. package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
  248. package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
  249. package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
  250. package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
  251. package/public/fonts/runescape-uf.ttf +0 -0
  252. package/public/index.html +0 -769
  253. package/public/js/actor-labels.js +0 -65
  254. package/public/js/api.js +0 -39
  255. package/public/js/app.js +0 -45
  256. package/public/js/artifact-drawer.js +0 -79
  257. package/public/js/board-export.js +0 -161
  258. package/public/js/board-menu.js +0 -374
  259. package/public/js/card-actions.js +0 -545
  260. package/public/js/config.js +0 -20
  261. package/public/js/create-card.js +0 -190
  262. package/public/js/dom.js +0 -19
  263. package/public/js/drawer.js +0 -273
  264. package/public/js/font-preference.js +0 -230
  265. package/public/js/format.js +0 -433
  266. package/public/js/help-menu.js +0 -31
  267. package/public/js/icons.js +0 -16
  268. package/public/js/kanban.js +0 -694
  269. package/public/js/local-backend.js +0 -1015
  270. package/public/js/main.js +0 -161
  271. package/public/js/modal.js +0 -33
  272. package/public/js/motion-preference.js +0 -64
  273. package/public/js/panel.js +0 -58
  274. package/public/js/router.js +0 -134
  275. package/public/js/search.js +0 -56
  276. package/public/js/settings-tabs.js +0 -7
  277. package/public/js/settings.js +0 -1634
  278. package/public/js/sse.js +0 -159
  279. package/public/js/starscape.js +0 -332
  280. package/public/js/state.js +0 -129
  281. package/public/js/text-editing.js +0 -128
  282. package/public/js/theme-preference.js +0 -95
  283. package/public/js/ticket-detail.js +0 -1516
  284. package/public/js/ticket-typeahead.js +0 -49
  285. package/public/js/toast.js +0 -89
  286. package/public/js/topbar-notes.js +0 -155
  287. package/public/js/transport.js +0 -22
  288. package/public/js/typeahead.js +0 -206
  289. package/public/js/unread.js +0 -121
  290. package/public/js/update-check.js +0 -58
  291. package/public/js/url-routes.js +0 -94
  292. package/public/orbits-favicon.png +0 -0
  293. package/public/orbits-logo-bg-wide.png +0 -0
  294. package/public/orbits-logo-bg.png +0 -0
  295. package/public/orbits-logo.png +0 -0
  296. package/public/styles.css +0 -7709
  297. package/src/core/agent.js +0 -387
  298. package/src/core/agents-md.js +0 -109
  299. package/src/core/artifacts.js +0 -149
  300. package/src/core/attachments.js +0 -238
  301. package/src/core/auth.js +0 -85
  302. package/src/core/backups.js +0 -142
  303. package/src/core/board.js +0 -148
  304. package/src/core/boards.js +0 -409
  305. package/src/core/bootstrap.js +0 -170
  306. package/src/core/css-bundle.js +0 -30
  307. package/src/core/db.js +0 -315
  308. package/src/core/delete-board.js +0 -163
  309. package/src/core/dispatch.js +0 -1007
  310. package/src/core/events.js +0 -314
  311. package/src/core/http.js +0 -115
  312. package/src/core/migrate.js +0 -198
  313. package/src/core/paths.js +0 -55
  314. package/src/core/provision-repo-board.js +0 -119
  315. package/src/core/queries.js +0 -246
  316. package/src/core/registry.js +0 -211
  317. package/src/core/relations.js +0 -163
  318. package/src/core/review-verdicts.js +0 -138
  319. package/src/core/router.js +0 -556
  320. package/src/core/search.js +0 -183
  321. package/src/core/seed.js +0 -234
  322. package/src/core/snapshots.js +0 -692
  323. package/src/core/states.js +0 -167
  324. package/src/core/system-picker.js +0 -109
  325. package/src/core/tickets.js +0 -400
  326. package/src/core/util.js +0 -106
  327. package/src/core/version-check.js +0 -154
  328. package/src/mcp/orbit-client.js +0 -287
  329. package/src/server.js +0 -115
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Cards linked to cards on other boards of the same Orbit: a card blocked by
3
+ * another board's card (a game waiting on an identity board's payments), or
4
+ * related to one. Each board is its own store, which nothing can change at
5
+ * once with another, so a link lives on its source card's board
6
+ * (`CrossBoardRelation`) and nothing on the other board changes. Whether a
7
+ * link still blocks is read from the other card when asked: it blocks until
8
+ * that card is in a closed lane (done, cancelled), archived or gone, and an
9
+ * epic blocks through its open sub-cards, as on one board. So nothing has to
10
+ * clean a link up when the other card is done.
11
+ */
12
+ import type { BoardCtx, Ctx } from "./context.ts";
13
+ import type { CrossBoardRelation, Id, LaneRole, RegisteredBoard, Ticket } from "./model.ts";
14
+ import type { BoardReader } from "../store/types.ts";
15
+ /** The link types that cross boards: a blocker, or a relation. */
16
+ export declare const CROSS_BOARD_TYPES: readonly ["blocked_by", "relates_to"];
17
+ /** A card on another board, as a link names it. */
18
+ export interface CrossCard {
19
+ board_id: Id;
20
+ board_slug: string;
21
+ id: Id;
22
+ number: number;
23
+ title: string;
24
+ type: Ticket["type"];
25
+ priority: Ticket["priority"];
26
+ state_name: string;
27
+ state_role: LaneRole | null;
28
+ /** Set when it blocks because it is an open sub-card of the epic the link names. */
29
+ via_epic_id: Id | null;
30
+ }
31
+ /** One cross-board link as a card's relations list it. */
32
+ export interface CrossLink {
33
+ id: Id;
34
+ type: CrossBoardRelation["type"];
35
+ /** outgoing: this card's link to the other card; incoming: the other card's link to this one. */
36
+ direction: "outgoing" | "incoming";
37
+ /** Whether the other card still blocks this one (outgoing blocked_by), or this card blocks it (incoming). */
38
+ blocking: boolean;
39
+ /** The other card; null when it is gone, or its board is. */
40
+ other_ticket: Omit<CrossCard, "via_epic_id"> | null;
41
+ other_board_id: Id;
42
+ }
43
+ /**
44
+ * What blocks each card from other boards, from the board's cross-board
45
+ * links already read: one read per other board, never one per card.
46
+ */
47
+ export declare function crossBlockers(c: Ctx, relations: readonly CrossBoardRelation[]): Promise<Map<Id, CrossCard[]>>;
48
+ /** What blocks one card from other boards (`r` is its own board's reader, inside a read or a write). */
49
+ export declare function crossBlockersFor(c: Ctx, r: BoardReader, ticketId: Id): Promise<CrossCard[]>;
50
+ /** A card's links across boards: its own (outgoing), and other boards' links to it (incoming). */
51
+ export declare function crossLinks(c: BoardCtx, ticketId: Id): Promise<CrossLink[]>;
52
+ /**
53
+ * A card's links across boards from what was read on its own board (its
54
+ * links, and whether it is still open), reading only the other boards: safe
55
+ * inside a read of the card's board.
56
+ */
57
+ export declare function crossLinksFrom(c: Ctx, outgoing: readonly CrossBoardRelation[], incoming: readonly {
58
+ board_id: Id;
59
+ relation: CrossBoardRelation;
60
+ }[], open: boolean): Promise<CrossLink[]>;
61
+ /**
62
+ * Links a card to a card on another board. `blocks` is said from the other
63
+ * side: the link is made on the blocked card's board, as `blocked_by`.
64
+ */
65
+ /** A cross-board link as it was made, with the board it lives on. */
66
+ export type CrossBoardRelationAnswer = CrossBoardRelation & {
67
+ board_id: Id;
68
+ };
69
+ export declare function linkAcrossBoards(c: Ctx, from: BoardCtx, fromId: Id, to: BoardCtx, toId: Id, type: unknown): Promise<CrossBoardRelationAnswer>;
70
+ /**
71
+ * The board holding a cross-board link with this id, or null. The `hint`
72
+ * board is asked first: a caller that knows where the link lives (the card
73
+ * drawer names it for a link into its card) removes it there.
74
+ */
75
+ export declare function boardHoldingCrossLink(c: Ctx, id: Id, hint: RegisteredBoard | null): Promise<RegisteredBoard | null>;
76
+ /** Removes a cross-board link from the board it lives on. */
77
+ export declare function unlinkAcrossBoards(c: BoardCtx, relationId: Id): Promise<{
78
+ ok: true;
79
+ }>;
80
+ /** Another board's card as the command language names it: `garden#3`. */
81
+ export declare function crossRef(card: Pick<CrossCard, "board_slug" | "number">): string;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Cards linked to cards on other boards of the same Orbit: a card blocked by
3
+ * another board's card (a game waiting on an identity board's payments), or
4
+ * related to one. Each board is its own store, which nothing can change at
5
+ * once with another, so a link lives on its source card's board
6
+ * (`CrossBoardRelation`) and nothing on the other board changes. Whether a
7
+ * link still blocks is read from the other card when asked: it blocks until
8
+ * that card is in a closed lane (done, cancelled), archived or gone, and an
9
+ * epic blocks through its open sub-cards, as on one board. So nothing has to
10
+ * clean a link up when the other card is done.
11
+ */
12
+ import { OrbitError, isOrbitError } from "./errors.js";
13
+ import { record } from "./history.js";
14
+ import { ticketOn } from "./tickets.js";
15
+ import { Lanes, isClosedLane } from "./views.js";
16
+ /** The link types that cross boards: a blocker, or a relation. */
17
+ export const CROSS_BOARD_TYPES = ["blocked_by", "relates_to"];
18
+ /**
19
+ * Reads the named cards on each board once (an epic's open sub-cards with
20
+ * them). A board that is gone, or can't be read, reads as having none of them.
21
+ */
22
+ async function readBoards(c, wanted) {
23
+ const out = new Map();
24
+ for (const [boardId, ids] of wanted) {
25
+ const board = await c.store.boards.get(boardId);
26
+ if (!board)
27
+ continue;
28
+ try {
29
+ const data = await c.store.board(boardId);
30
+ const read = await data.read(async (r) => {
31
+ const lanes = await Lanes.of(r);
32
+ const tickets = await r.tickets.list({ ids: [...ids] });
33
+ const children = new Map();
34
+ for (const epic of tickets.filter((t) => t.type === "epic" && !t.archived_at))
35
+ children.set(epic.id, await r.tickets.list({ parent: epic.id, archived: false }));
36
+ return { lanes, tickets: new Map(tickets.map((t) => [t.id, t])), children };
37
+ });
38
+ out.set(boardId, { board, ...read });
39
+ }
40
+ catch (error) {
41
+ // A board whose file is gone, or that went meanwhile, blocks nothing.
42
+ if (!isOrbitError(error))
43
+ throw error;
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ const closed = (read, ticket) => {
49
+ const state = read.lanes.get(ticket.state_id);
50
+ return Boolean(ticket.archived_at) || (state ? isClosedLane(state) : false);
51
+ };
52
+ function linkedCard(read, ticket) {
53
+ return {
54
+ board_id: read.board.id,
55
+ board_slug: read.board.slug,
56
+ id: ticket.id,
57
+ number: ticket.number,
58
+ title: ticket.title,
59
+ type: ticket.type,
60
+ priority: ticket.priority,
61
+ state_name: read.lanes.nameOf(ticket),
62
+ state_role: read.lanes.roleOf(ticket)
63
+ };
64
+ }
65
+ function crossCard(read, ticket, viaEpic) {
66
+ return { ...linkedCard(read, ticket), via_epic_id: viaEpic };
67
+ }
68
+ /** What still blocks the card each link starts from: the other card, or an epic's open sub-cards. */
69
+ function blockersOf(read, relation) {
70
+ const target = read?.tickets.get(relation.target_ticket_id);
71
+ if (!read || !target || closed(read, target))
72
+ return [];
73
+ if (target.type !== "epic")
74
+ return [crossCard(read, target, null)];
75
+ return (read.children.get(target.id) ?? []).filter((child) => !closed(read, child)).map((child) => crossCard(read, child, target.id));
76
+ }
77
+ function wantedBy(relations) {
78
+ const wanted = new Map();
79
+ for (const relation of relations) {
80
+ const ids = wanted.get(relation.target_board_id) ?? new Set();
81
+ ids.add(relation.target_ticket_id);
82
+ wanted.set(relation.target_board_id, ids);
83
+ }
84
+ return wanted;
85
+ }
86
+ /**
87
+ * What blocks each card from other boards, from the board's cross-board
88
+ * links already read: one read per other board, never one per card.
89
+ */
90
+ export async function crossBlockers(c, relations) {
91
+ const blocking = relations.filter((relation) => relation.type === "blocked_by");
92
+ const out = new Map();
93
+ if (!blocking.length)
94
+ return out;
95
+ const boards = await readBoards(c, wantedBy(blocking));
96
+ for (const relation of blocking) {
97
+ const found = blockersOf(boards.get(relation.target_board_id), relation);
98
+ if (!found.length)
99
+ continue;
100
+ const list = out.get(relation.source_ticket_id) ?? [];
101
+ for (const card of found)
102
+ if (!list.some((other) => other.id === card.id && other.board_id === card.board_id))
103
+ list.push(card);
104
+ out.set(relation.source_ticket_id, list);
105
+ }
106
+ for (const list of out.values())
107
+ list.sort((a, b) => (a.board_slug < b.board_slug ? -1 : a.board_slug > b.board_slug ? 1 : a.number - b.number));
108
+ return out;
109
+ }
110
+ /** What blocks one card from other boards (`r` is its own board's reader, inside a read or a write). */
111
+ export async function crossBlockersFor(c, r, ticketId) {
112
+ return (await crossBlockers(c, await r.crossBoardRelations.list(ticketId))).get(ticketId) ?? [];
113
+ }
114
+ /** A card's links across boards: its own (outgoing), and other boards' links to it (incoming). */
115
+ export async function crossLinks(c, ticketId) {
116
+ const here = await c.data.read(async (r) => {
117
+ const [outgoing, lanes, ticket] = await Promise.all([r.crossBoardRelations.list(ticketId), Lanes.of(r), r.tickets.get(ticketId)]);
118
+ const state = ticket ? lanes.get(ticket.state_id) : undefined;
119
+ return { outgoing, open: Boolean(ticket && !ticket.archived_at && !(state && isClosedLane(state))) };
120
+ });
121
+ const incoming = (await c.store.relationsInto(c.board.id)).filter((row) => row.relation.target_ticket_id === ticketId);
122
+ return crossLinksFrom(c, here.outgoing, incoming, here.open);
123
+ }
124
+ /**
125
+ * A card's links across boards from what was read on its own board (its
126
+ * links, and whether it is still open), reading only the other boards: safe
127
+ * inside a read of the card's board.
128
+ */
129
+ export async function crossLinksFrom(c, outgoing, incoming, open) {
130
+ const wanted = wantedBy(outgoing);
131
+ for (const row of incoming) {
132
+ const ids = wanted.get(row.board_id) ?? new Set();
133
+ ids.add(row.relation.source_ticket_id);
134
+ wanted.set(row.board_id, ids);
135
+ }
136
+ const boards = await readBoards(c, wanted);
137
+ const other = (boardId, cardId) => {
138
+ const read = boards.get(boardId);
139
+ const ticket = read?.tickets.get(cardId);
140
+ if (!read || !ticket || ticket.archived_at)
141
+ return null;
142
+ return linkedCard(read, ticket);
143
+ };
144
+ const links = outgoing.map((relation) => ({
145
+ id: relation.id,
146
+ type: relation.type,
147
+ direction: "outgoing",
148
+ blocking: relation.type === "blocked_by" && blockersOf(boards.get(relation.target_board_id), relation).length > 0,
149
+ other_ticket: other(relation.target_board_id, relation.target_ticket_id),
150
+ other_board_id: relation.target_board_id
151
+ }));
152
+ for (const { board_id, relation } of incoming) {
153
+ links.push({ id: relation.id, type: relation.type, direction: "incoming", blocking: relation.type === "blocked_by" && open, other_ticket: other(board_id, relation.source_ticket_id), other_board_id: board_id });
154
+ }
155
+ return links;
156
+ }
157
+ export async function linkAcrossBoards(c, from, fromId, to, toId, type) {
158
+ if (from.board.id === to.board.id)
159
+ throw new OrbitError("invalid_relation_type", { message: "Both cards are on one board: link them as one board's cards." });
160
+ const kind = typeof type === "string" && type ? type : "relates_to";
161
+ if (kind === "child_of" || kind === "parent_of")
162
+ throw new OrbitError("hierarchy_uses_parent_ticket_id");
163
+ if (kind !== "blocks" && !CROSS_BOARD_TYPES.includes(kind))
164
+ throw new OrbitError("invalid_relation_type");
165
+ // The link lives on the card that waits (or, for a relation, the card it starts from).
166
+ const [source, sourceId, target, targetId] = kind === "blocks" ? [to, toId, from, fromId] : [from, fromId, to, toId];
167
+ const linkType = kind === "relates_to" ? "relates_to" : "blocked_by";
168
+ const other = await target.data.read(async (r) => {
169
+ const ticket = await ticketOn(r, targetId);
170
+ const lanes = await Lanes.of(r);
171
+ const state = lanes.get(ticket.state_id);
172
+ return { ticket, done: Boolean(ticket.archived_at) || (state ? isClosedLane(state) : false) };
173
+ });
174
+ if (other.ticket.archived_at)
175
+ throw new OrbitError("ticket_not_found");
176
+ // A blocker already done would block nothing.
177
+ if (linkType === "blocked_by" && other.done)
178
+ throw new OrbitError("blocker_target_already_done");
179
+ const relation = await source.data.write(async (w) => {
180
+ const card = await ticketOn(w, sourceId);
181
+ const existing = await w.crossBoardRelations.list(card.id);
182
+ if (existing.some((x) => x.target_board_id === target.board.id && x.target_ticket_id === targetId && x.type === linkType))
183
+ throw new OrbitError("relation_exists");
184
+ const made = { id: c.ids.next(), source_ticket_id: card.id, target_board_id: target.board.id, target_ticket_id: targetId, type: linkType, created_at: c.clock.now() };
185
+ await w.crossBoardRelations.insert(made);
186
+ await record(w, c, "relation_created", card.id, { target_board_id: target.board.id, target_board: target.board.slug, target_ticket_id: targetId, target_number: other.ticket.number, type: linkType });
187
+ return made;
188
+ });
189
+ source.changed(source.board.id);
190
+ return { ...relation, board_id: source.board.id };
191
+ }
192
+ /**
193
+ * The board holding a cross-board link with this id, or null. The `hint`
194
+ * board is asked first: a caller that knows where the link lives (the card
195
+ * drawer names it for a link into its card) removes it there.
196
+ */
197
+ export async function boardHoldingCrossLink(c, id, hint) {
198
+ const rows = await c.store.boards.list();
199
+ const ordered = hint ? [hint, ...rows.filter((row) => row.id !== hint.id)] : rows;
200
+ for (const row of ordered) {
201
+ try {
202
+ const data = await c.store.board(row.id);
203
+ if ((await data.read((r) => r.crossBoardRelations.list())).some((relation) => relation.id === id))
204
+ return row;
205
+ }
206
+ catch (error) {
207
+ if (!isOrbitError(error))
208
+ throw error;
209
+ }
210
+ }
211
+ return null;
212
+ }
213
+ /** Removes a cross-board link from the board it lives on. */
214
+ export async function unlinkAcrossBoards(c, relationId) {
215
+ await c.data.write(async (w) => {
216
+ const relation = (await w.crossBoardRelations.list()).find((x) => x.id === relationId);
217
+ if (!relation)
218
+ throw new OrbitError("relation_not_found");
219
+ await w.crossBoardRelations.remove(relationId);
220
+ await record(w, c, "relation_deleted", relation.source_ticket_id, { target_board_id: relation.target_board_id, target_ticket_id: relation.target_ticket_id, type: relation.type });
221
+ });
222
+ c.changed(c.board.id);
223
+ return { ok: true };
224
+ }
225
+ /** Another board's card as the command language names it: `garden#3`. */
226
+ export function crossRef(card) {
227
+ return `${card.board_slug}#${card.number}`;
228
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Orbit's own words for what people wrote and what happened: text folded to
3
+ * one safe line, and an event said as a short phrase ("took #14"). Shared by
4
+ * the command language's answers and the quick menu's sessions.
5
+ */
6
+ /**
7
+ * Text as one safe line: control characters and line breaks become spaces,
8
+ * so a title can never forge a lane header or another card's line.
9
+ */
10
+ export declare function oneLine(value: unknown, max?: number): string;
11
+ /** A lane's name as shown: one line (a board made before lane names were cleaned may hold anything). */
12
+ export declare function laneText(name: unknown): string;
13
+ /** An event with its card's number and title, as `changes` and the sessions read it. */
14
+ export interface EventLine {
15
+ type: string;
16
+ body_json: string;
17
+ number?: number | null;
18
+ title?: string | null;
19
+ }
20
+ /** One event in words: "took #14", "asked about #8 (In Progress -> Review)". */
21
+ export declare function describeEvent(event: EventLine): string;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Orbit's own words for what people wrote and what happened: text folded to
3
+ * one safe line, and an event said as a short phrase ("took #14"). Shared by
4
+ * the command language's answers and the quick menu's sessions.
5
+ */
6
+ import { asText } from "./text.js";
7
+ /**
8
+ * Text as one safe line: control characters and line breaks become spaces,
9
+ * so a title can never forge a lane header or another card's line.
10
+ */
11
+ export function oneLine(value, max = 160) {
12
+ const clean = asText(value ?? "")
13
+ .replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, " ")
14
+ .replace(/\s+/g, " ")
15
+ .trim();
16
+ return clean.length > max ? `${clean.slice(0, max - 1).trimEnd()}…` : clean;
17
+ }
18
+ /** A lane's name as shown: one line (a board made before lane names were cleaned may hold anything). */
19
+ export function laneText(name) {
20
+ return oneLine(name, 60);
21
+ }
22
+ const FIELD_WORDS = {
23
+ parent_ticket_id: "parent",
24
+ ai_plan: "plan",
25
+ implementation_summary: "summary",
26
+ implementation_updates: "revisions",
27
+ state_id: "lane",
28
+ plan_artifact_path: "plan link",
29
+ implementation_artifact_path: "summary link",
30
+ nebula_url: "Nebula link"
31
+ };
32
+ /** One event in words: "took #14", "asked about #8 (In Progress -> Review)". */
33
+ export function describeEvent(event) {
34
+ const n = event.number ? `#${event.number}` : "a card";
35
+ let body = {};
36
+ try {
37
+ const parsed = JSON.parse(event.body_json || "{}");
38
+ if (parsed && typeof parsed === "object")
39
+ body = parsed;
40
+ }
41
+ catch {
42
+ body = {};
43
+ }
44
+ const from = body.from;
45
+ const to = body.to;
46
+ const path = from && to ? ` (${laneText(from)} -> ${laneText(to)})` : to ? ` (-> ${laneText(to)})` : "";
47
+ switch (event.type) {
48
+ case "ticket_created":
49
+ return `created ${n} ${oneLine(body.title || event.title || "", 80)}`.trimEnd();
50
+ case "agent_claimed":
51
+ if (body.took_over_from)
52
+ return `took over ${n} from @${oneLine(body.took_over_from, 40)}`;
53
+ return from && from === to ? `took ${n}` : `took ${n}${path}`;
54
+ case "state_changed":
55
+ return `moved ${n}${path}`;
56
+ case "agent_completed":
57
+ return `finished ${n}${path}`;
58
+ case "checkpoint_requested":
59
+ return `asked about ${n}${path}`;
60
+ case "comment_created":
61
+ return `noted on ${n}`;
62
+ case "ticket_updated":
63
+ return `edited ${n} (${Object.keys(body).map((key) => FIELD_WORDS[key] ?? key).join(", ") || "fields"})`;
64
+ case "ticket_archived":
65
+ return `archived ${n}`;
66
+ case "ticket_restored":
67
+ return `restored ${n}`;
68
+ case "ticket_deleted":
69
+ return `deleted ${oneLine(body.title || "a card", 80)}`;
70
+ case "relation_created":
71
+ if (typeof body.target_board === "string" && typeof body.target_number === "number")
72
+ return `linked ${n} to ${body.target_board}#${body.target_number} (${asText(body.type ?? "").replace(/_/g, " ")})`;
73
+ return `linked ${n} (${asText(body.type ?? "").replace(/_/g, " ")})`;
74
+ case "relation_deleted":
75
+ return `unlinked ${n}`;
76
+ case "board_entry_created":
77
+ return `wrote a ${typeof body.type === "string" && body.type ? body.type : "journal entry"} in the journal`;
78
+ default:
79
+ return `${event.type.replace(/_/g, " ")}${event.number ? ` ${n}` : ""}`;
80
+ }
81
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Orbit's errors. Each carries a code, the same codes Orbit's answers have
3
+ * always used (`invalid_state`, `board_not_found`...), and a kind that says
4
+ * what went wrong in words any transport understands. The core and the storage
5
+ * adapters throw them; each transport maps the kind to its own form: an HTTP
6
+ * status (`HTTP_STATUS` below), an MCP tool error, a CLI exit code.
7
+ *
8
+ * `message` is what people and agents read. It is the code itself unless a
9
+ * clearer sentence was given, which is what Orbit's answers and the command
10
+ * language ("Orbit refused: invalid state.") already show.
11
+ */
12
+ export type ErrorKind =
13
+ /** The request is malformed or names something that can't be used. */
14
+ "invalid"
15
+ /** No one is signed in where an account is needed (hosts with accounts). */
16
+ | "unauthorized"
17
+ /** The actor may not do this. */
18
+ | "forbidden" | "not_found"
19
+ /** The address exists, but not for this method (HTTP only). */
20
+ | "method_not_allowed"
21
+ /** The request collides with what is there now. */
22
+ | "conflict" | "too_large" | "unsupported_type"
23
+ /** This Orbit can't do it here (a feature of another host). */
24
+ | "not_supported"
25
+ /** Storage is busy or out of room; the same request may work later. */
26
+ | "unavailable"
27
+ /** A fault in Orbit or its storage. The message never carries a database's words. */
28
+ | "internal";
29
+ /** Every code Orbit raises, with its kind. */
30
+ export declare const ERROR_CODES: {
31
+ readonly board_not_found: "not_found";
32
+ readonly board_id_required: "invalid";
33
+ readonly board_id_mismatch: "invalid";
34
+ readonly board_slug_taken: "conflict";
35
+ readonly board_slug_confirmation_required: "invalid";
36
+ readonly board_has_no_states: "invalid";
37
+ readonly board_files_busy: "conflict";
38
+ readonly board_file_missing: "conflict";
39
+ readonly unsafe_board_db_path: "conflict";
40
+ readonly missing_name: "invalid";
41
+ readonly state_not_found: "not_found";
42
+ readonly invalid_state: "invalid";
43
+ readonly invalid_state_order: "invalid";
44
+ readonly invalid_state_role: "invalid";
45
+ readonly state_has_tickets: "conflict";
46
+ readonly cannot_delete_last_state: "conflict";
47
+ readonly cannot_rename_ai_ready_lane: "conflict";
48
+ readonly ai_ready_lane_must_be_named_ai_ready: "conflict";
49
+ readonly missing_in_progress_state: "invalid";
50
+ readonly missing_review_state: "invalid";
51
+ readonly missing_next_state: "invalid";
52
+ readonly ticket_not_found: "not_found";
53
+ readonly missing_title: "invalid";
54
+ readonly invalid_ticket_type: "invalid";
55
+ readonly invalid_number: "invalid";
56
+ readonly invalid_nebula_url: "invalid";
57
+ readonly ticket_id_or_number_or_title_required: "invalid";
58
+ readonly missing_ticket_id: "invalid";
59
+ readonly ticket_not_archived: "conflict";
60
+ readonly invalid_parent_ticket: "invalid";
61
+ readonly epic_cannot_have_parent: "invalid";
62
+ readonly ticket_cannot_parent_itself: "invalid";
63
+ readonly ticket_parent_cycle: "invalid";
64
+ readonly missing_label: "invalid";
65
+ readonly comment_not_found: "not_found";
66
+ readonly missing_body: "invalid";
67
+ readonly omitted_for_ai_required: "invalid";
68
+ readonly relation_not_found: "not_found";
69
+ readonly relation_exists: "conflict";
70
+ readonly relation_self: "invalid";
71
+ readonly relation_redundant_with_parent: "invalid";
72
+ readonly hierarchy_uses_parent_ticket_id: "invalid";
73
+ readonly blocker_target_already_done: "invalid";
74
+ readonly blocker_source_already_done: "invalid";
75
+ readonly invalid_relation_type: "invalid";
76
+ readonly missing_source_ticket_id: "invalid";
77
+ readonly missing_target_ticket_id: "invalid";
78
+ readonly board_entry_not_found: "not_found";
79
+ readonly invalid_project_entry_type: "invalid";
80
+ readonly missing_entry_title: "invalid";
81
+ readonly entry_not_struck: "conflict";
82
+ readonly forbidden: "forbidden";
83
+ readonly missing_message: "invalid";
84
+ readonly missing_summary: "invalid";
85
+ readonly invalid_since: "invalid";
86
+ readonly review_verdict_not_found: "not_found";
87
+ readonly missing_verdict: "invalid";
88
+ readonly invalid_review_verdict: "invalid";
89
+ readonly invalid_blocking_findings: "invalid";
90
+ readonly invalid_optional_findings: "invalid";
91
+ readonly invalid_evidence_commands: "invalid";
92
+ readonly invalid_supersedes_prior_review_id: "invalid";
93
+ readonly superseded_review_ticket_mismatch: "invalid";
94
+ readonly attachment_not_found: "not_found";
95
+ readonly attachment_file_missing: "not_found";
96
+ readonly empty_attachment: "invalid";
97
+ readonly invalid_attachment_path: "invalid";
98
+ readonly attachment_too_large: "too_large";
99
+ readonly unsupported_attachment_type: "unsupported_type";
100
+ readonly attachment_type_mismatch: "unsupported_type";
101
+ readonly artifact_not_found: "not_found";
102
+ readonly artifact_path_required: "invalid";
103
+ readonly artifact_path_outside_allowed_roots: "invalid";
104
+ readonly artifact_root_unavailable: "invalid";
105
+ readonly artifact_not_file: "invalid";
106
+ readonly artifact_too_large: "too_large";
107
+ readonly unsupported_artifact_type: "unsupported_type";
108
+ readonly invalid_import_snapshot: "invalid";
109
+ readonly snapshot_missing_board: "invalid";
110
+ readonly import_requires_empty_board_or_replace_existing: "conflict";
111
+ readonly import_would_delete_hidden_comments: "conflict";
112
+ readonly not_found: "not_found";
113
+ readonly method_not_allowed: "method_not_allowed";
114
+ readonly invalid_json: "invalid";
115
+ readonly invalid_body: "invalid";
116
+ readonly payload_too_large: "too_large";
117
+ readonly cross_site_request: "forbidden";
118
+ readonly folder_picker_unavailable: "not_supported";
119
+ readonly duplicate_id: "conflict";
120
+ readonly ticket_number_taken: "conflict";
121
+ readonly state_name_taken: "conflict";
122
+ readonly label_name_taken: "conflict";
123
+ readonly invalid_reference: "invalid";
124
+ readonly invalid_blob_path: "invalid";
125
+ readonly blob_exists: "conflict";
126
+ readonly write_closed: "internal";
127
+ readonly storage_busy: "unavailable";
128
+ readonly storage_full: "unavailable";
129
+ readonly storage_failed: "internal";
130
+ readonly data_needs_migration: "unavailable";
131
+ readonly data_from_newer_orbit: "unavailable";
132
+ readonly migration_source_unreadable: "invalid";
133
+ readonly migration_target_in_use: "conflict";
134
+ };
135
+ export type ErrorCode = keyof typeof ERROR_CODES;
136
+ /** The HTTP status each kind answers with: the statuses Orbit's API has always used for these codes. */
137
+ export declare const HTTP_STATUS: {
138
+ readonly invalid: 400;
139
+ readonly unauthorized: 401;
140
+ readonly forbidden: 403;
141
+ readonly not_found: 404;
142
+ readonly method_not_allowed: 405;
143
+ readonly conflict: 409;
144
+ readonly too_large: 413;
145
+ readonly unsupported_type: 415;
146
+ readonly not_supported: 501;
147
+ readonly unavailable: 503;
148
+ readonly internal: 500;
149
+ };
150
+ export declare class OrbitError extends Error {
151
+ readonly code: ErrorCode;
152
+ readonly kind: ErrorKind;
153
+ constructor(code: ErrorCode, options?: {
154
+ message?: string;
155
+ cause?: unknown;
156
+ });
157
+ }
158
+ /** True for an OrbitError, from this copy of Orbit or another (a host may bundle its own). */
159
+ export declare function isOrbitError(value: unknown): value is OrbitError;