@co0ontty/wand 4.57.0 → 4.58.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 (177) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/ensure-node-pty-helper.d.ts +0 -1
  3. package/dist/ensure-node-pty-helper.js +0 -5
  4. package/dist/git-quick-commit.js +18 -19
  5. package/dist/github-connector.d.ts +11 -5
  6. package/dist/github-connector.js +105 -33
  7. package/dist/missions.d.ts +1 -1
  8. package/dist/missions.js +17 -4
  9. package/dist/server-file-routes.js +43 -2
  10. package/dist/server-github-routes.js +50 -53
  11. package/dist/server-mission-routes.js +11 -10
  12. package/dist/server-task-routes.d.ts +14 -2
  13. package/dist/server-task-routes.js +248 -84
  14. package/dist/server-workspace-routes.js +90 -16
  15. package/dist/server.js +2 -20
  16. package/dist/storage.d.ts +23 -6
  17. package/dist/storage.js +130 -53
  18. package/dist/structured-exec-host.d.ts +13 -2
  19. package/dist/structured-exec-host.js +71 -36
  20. package/dist/structured-exec-pump.d.ts +1 -1
  21. package/dist/structured-exec-pump.js +14 -8
  22. package/dist/task-types.d.ts +15 -0
  23. package/dist/terminal-daemon-client.d.ts +2 -0
  24. package/dist/terminal-daemon-client.js +7 -1
  25. package/dist/terminal-daemon-server.js +17 -18
  26. package/dist/tui/ipc-server.js +3 -1
  27. package/dist/types.d.ts +1 -0
  28. package/dist/wand-task-sync.d.ts +9 -0
  29. package/dist/wand-task-sync.js +50 -0
  30. package/dist/web-ui/content/scripts.js +85 -85
  31. package/dist/web-ui/content/styles.css +1 -1
  32. package/dist/web-ui/embedded-assets.js +2 -2
  33. package/dist/ws-broadcast.js +5 -1
  34. package/package.json +2 -3
  35. package/dist/taskboard-bridge.d.ts +0 -11
  36. package/dist/taskboard-bridge.js +0 -371
  37. package/vendor/codex-taskboard/LICENSE +0 -201
  38. package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
  39. package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
  40. package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
  41. package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
  42. package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
  43. package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
  44. package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
  45. package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
  46. package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
  47. package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
  48. package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
  49. package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
  50. package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
  51. package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
  52. package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
  53. package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
  54. package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
  55. package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
  56. package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
  57. package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
  58. package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
  59. package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
  60. package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
  61. package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
  62. package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
  63. package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
  64. package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
  65. package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
  66. package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
  67. package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
  68. package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
  69. package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
  70. package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
  71. package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
  72. package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
  73. package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
  74. package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
  75. package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
  76. package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
  77. package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
  78. package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
  79. package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
  80. package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
  81. package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
  82. package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
  83. package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
  84. package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
  85. package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
  86. package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
  87. package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
  88. package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
  89. package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
  90. package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
  91. package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
  92. package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
  93. package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
  94. package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
  95. package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
  96. package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
  97. package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
  98. package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
  99. package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
  100. package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
  101. package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
  102. package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
  103. package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
  104. package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
  105. package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
  106. package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
  107. package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
  108. package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
  109. package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
  110. package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
  111. package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
  112. package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
  113. package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
  114. package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
  115. package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
  116. package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
  117. package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
  118. package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
  119. package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
  120. package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
  121. package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
  122. package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
  123. package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
  124. package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
  125. package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
  126. package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
  127. package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
  128. package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
  129. package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
  130. package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
  131. package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
  132. package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
  133. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
  134. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
  135. package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
  136. package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
  137. package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
  138. package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
  139. package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
  140. package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
  141. package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
  142. package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
  143. package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
  144. package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
  145. package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
  146. package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
  147. package/vendor/codex-taskboard/dist/web/index.html +0 -20
  148. package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
  149. package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
  150. package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
  151. package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
  152. package/vendor/codex-taskboard/server/app.mjs +0 -3211
  153. package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
  154. package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
  155. package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
  156. package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
  157. package/vendor/codex-taskboard/server/database.mjs +0 -2845
  158. package/vendor/codex-taskboard/server/index.mjs +0 -41
  159. package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
  160. package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
  161. package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
  162. package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
  163. package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
  164. package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
  165. package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
  166. package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
  167. package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
  168. package/vendor/codex-taskboard/shared/domain.mjs +0 -34
  169. package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
  170. package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
  171. package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
  172. package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
  173. package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
  174. package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
  175. package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
  176. package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
  177. package/vendor/codex-taskboard/skills/manage-taskboard/references/cli.md +0 -211
@@ -1,188 +0,0 @@
1
- import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- const CONFIG_VERSION = 1;
5
-
6
- class CloudConfigError extends Error {
7
- constructor(code, message) {
8
- super(message);
9
- this.name = "CloudConfigError";
10
- this.code = code;
11
- }
12
- }
13
-
14
- function emptyConfig() {
15
- return {
16
- version: CONFIG_VERSION,
17
- remoteUrl: null,
18
- actorName: null,
19
- sharedKey: null,
20
- projectMappings: {},
21
- };
22
- }
23
-
24
- export function normalizeCloudUrl(value) {
25
- let url;
26
- try {
27
- url = new URL(value);
28
- } catch {
29
- throw new CloudConfigError("INVALID_CLOUD_URL", "Cloud taskboard URL must be a valid URL");
30
- }
31
- const isLoopback = url.hostname === "localhost"
32
- || url.hostname === "127.0.0.1"
33
- || url.hostname === "[::1]";
34
- if (
35
- (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback))
36
- || url.username
37
- || url.password
38
- || url.pathname !== "/"
39
- || url.search
40
- || url.hash
41
- ) {
42
- throw new CloudConfigError(
43
- "INVALID_CLOUD_URL",
44
- "Cloud taskboard URL must be an HTTPS origin (loopback HTTP is allowed for development)",
45
- );
46
- }
47
- return url.origin;
48
- }
49
-
50
- function validateCredentials(actorName, sharedKey) {
51
- if (
52
- typeof actorName !== "string"
53
- || !actorName.trim()
54
- || actorName.length > 120
55
- || actorName.includes(":")
56
- ) {
57
- throw new CloudConfigError(
58
- "INVALID_CLOUD_ACTOR",
59
- "Cloud actor name must be 1 to 120 characters and cannot contain ':'",
60
- );
61
- }
62
- if (typeof sharedKey !== "string" || !sharedKey || sharedKey.length > 4096) {
63
- throw new CloudConfigError(
64
- "INVALID_CLOUD_KEY",
65
- "Cloud shared key must be 1 to 4096 characters",
66
- );
67
- }
68
- return { actorName: actorName.trim(), sharedKey };
69
- }
70
-
71
- function validateProjectMappings(value) {
72
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
73
- throw new CloudConfigError("INVALID_CLOUD_CONFIG", "Cloud project mappings are invalid");
74
- }
75
- const projectMappings = {};
76
- for (const [projectId, workspacePath] of Object.entries(value)) {
77
- if (!projectId || typeof workspacePath !== "string" || !path.isAbsolute(workspacePath)) {
78
- throw new CloudConfigError("INVALID_CLOUD_CONFIG", "Cloud project mappings are invalid");
79
- }
80
- projectMappings[projectId] = workspacePath;
81
- }
82
- return projectMappings;
83
- }
84
-
85
- function parseConfig(value) {
86
- if (
87
- value === null
88
- || typeof value !== "object"
89
- || Array.isArray(value)
90
- || value.version !== CONFIG_VERSION
91
- ) {
92
- throw new CloudConfigError("INVALID_CLOUD_CONFIG", "Cloud companion configuration is invalid");
93
- }
94
- const allowedKeys = new Set([
95
- "version",
96
- "remoteUrl",
97
- "actorName",
98
- "sharedKey",
99
- "projectMappings",
100
- ]);
101
- if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
102
- throw new CloudConfigError("INVALID_CLOUD_CONFIG", "Cloud companion configuration is invalid");
103
- }
104
- const projectMappings = validateProjectMappings(value.projectMappings);
105
- if (value.remoteUrl === null && value.actorName === null && value.sharedKey === null) {
106
- return { ...emptyConfig(), projectMappings };
107
- }
108
- const credentials = validateCredentials(value.actorName, value.sharedKey);
109
- return {
110
- version: CONFIG_VERSION,
111
- remoteUrl: normalizeCloudUrl(value.remoteUrl),
112
- ...credentials,
113
- projectMappings,
114
- };
115
- }
116
-
117
- export function createCloudConfigStore({ configPath }) {
118
- if (!configPath) throw new Error("configPath is required");
119
- let pendingWrite = Promise.resolve();
120
-
121
- async function readFromDisk() {
122
- try {
123
- return parseConfig(JSON.parse(await readFile(configPath, "utf8")));
124
- } catch (error) {
125
- if (error?.code === "ENOENT") return emptyConfig();
126
- throw error;
127
- }
128
- }
129
-
130
- async function writeAtomically(config) {
131
- await mkdir(path.dirname(configPath), { recursive: true });
132
- const temporaryPath = `${configPath}.${process.pid}.${Date.now()}.tmp`;
133
- await writeFile(temporaryPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
134
- await rename(temporaryPath, configPath);
135
- }
136
-
137
- function update(mutator) {
138
- const operation = pendingWrite.then(async () => {
139
- const next = mutator(await readFromDisk());
140
- await writeAtomically(next);
141
- return next;
142
- });
143
- pendingWrite = operation.catch(() => {});
144
- return operation;
145
- }
146
-
147
- return {
148
- async read() {
149
- await pendingWrite;
150
- return readFromDisk();
151
- },
152
- async configure({ remoteUrl, actorName, sharedKey }) {
153
- const normalizedUrl = normalizeCloudUrl(remoteUrl);
154
- const credentials = validateCredentials(actorName, sharedKey);
155
- return update((config) => ({
156
- ...config,
157
- remoteUrl: normalizedUrl,
158
- ...credentials,
159
- }));
160
- },
161
- clearCloud() {
162
- return update((config) => ({
163
- ...config,
164
- remoteUrl: null,
165
- actorName: null,
166
- sharedKey: null,
167
- }));
168
- },
169
- setProjectWorkspace(projectId, workspacePath) {
170
- if (typeof projectId !== "string" || !projectId.trim()) {
171
- throw new CloudConfigError("INVALID_PROJECT_MAPPING", "projectId is required");
172
- }
173
- if (typeof workspacePath !== "string" || !path.isAbsolute(workspacePath)) {
174
- throw new CloudConfigError(
175
- "INVALID_PROJECT_MAPPING",
176
- "workspacePath must be absolute",
177
- );
178
- }
179
- return update((config) => ({
180
- ...config,
181
- projectMappings: {
182
- ...config.projectMappings,
183
- [projectId]: workspacePath,
184
- },
185
- }));
186
- },
187
- };
188
- }
@@ -1,310 +0,0 @@
1
- import path from "node:path";
2
-
3
- import { DEFAULT_PROJECT_ID } from "../shared/domain.mjs";
4
- import { normalizeCloudUrl } from "./cloud-config.mjs";
5
-
6
- const LOCAL_COMPANION_ROUTES = new Set([
7
- "/health",
8
- "/api/meta",
9
- "/api/device-workspaces",
10
- "/api/local/cloud-session",
11
- ]);
12
-
13
- export class CloudProxyError extends Error {
14
- constructor(status, code, message, details) {
15
- super(message);
16
- this.name = "CloudProxyError";
17
- this.status = status;
18
- this.code = code;
19
- this.details = details;
20
- }
21
- }
22
-
23
- export function isLocalCompanionRoute(pathname) {
24
- return LOCAL_COMPANION_ROUTES.has(pathname)
25
- || pathname.startsWith("/api/local/")
26
- || /^\/api\/projects\/[^/]+\/development-contexts$/.test(pathname);
27
- }
28
-
29
- function basicAuthorization(actorName, sharedKey) {
30
- return `Basic ${Buffer.from(`${actorName}:${sharedKey}`, "utf8").toString("base64")}`;
31
- }
32
-
33
- async function prepareRequest(request, {
34
- assertTaskProjectMoveAllowed,
35
- resolveThreadBinding,
36
- } = {}) {
37
- const url = new URL(request.url);
38
- let projectWorkspace = null;
39
- let body = request.body;
40
- const isJson = request.headers.get("content-type")?.includes("application/json");
41
- const isProjectCreate = request.method === "POST" && url.pathname === "/api/projects";
42
- const taskPatchMatch = request.method === "PATCH"
43
- ? url.pathname.match(/^\/api\/tasks\/([^/]+)$/)
44
- : null;
45
- const isTaskMutation = (
46
- (request.method === "POST" && url.pathname === "/api/tasks")
47
- || Boolean(taskPatchMatch)
48
- );
49
- const isConversationMutation = request.method !== "GET"
50
- && (/^\/api\/tasks(?:\/|$)/.test(url.pathname) || /^\/api\/comments\//.test(url.pathname));
51
-
52
- if (isJson && (isProjectCreate || isTaskMutation || isConversationMutation)) {
53
- let payload;
54
- try {
55
- payload = await request.clone().json();
56
- } catch {
57
- throw new CloudProxyError(400, "INVALID_JSON", "Request body must contain valid JSON");
58
- }
59
- if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
60
- throw new CloudProxyError(400, "INVALID_BODY", "Request body must be a JSON object");
61
- }
62
- if (
63
- taskPatchMatch
64
- && typeof payload.projectId === "string"
65
- && typeof assertTaskProjectMoveAllowed === "function"
66
- ) {
67
- let taskId;
68
- try {
69
- taskId = decodeURIComponent(taskPatchMatch[1]);
70
- } catch {
71
- throw new CloudProxyError(400, "INVALID_PATH", "Task id contains invalid encoding");
72
- }
73
- await assertTaskProjectMoveAllowed(taskId, payload.projectId);
74
- }
75
- if (isProjectCreate && Object.hasOwn(payload, "workspacePath")) {
76
- if (typeof payload.workspacePath === "string") {
77
- if (!path.isAbsolute(payload.workspacePath)) {
78
- throw new CloudProxyError(
79
- 400,
80
- "INVALID_PROJECT_MAPPING",
81
- "Project workspacePath must be absolute",
82
- );
83
- }
84
- projectWorkspace = {
85
- projectId: typeof payload.id === "string" ? payload.id : null,
86
- workspacePath: payload.workspacePath,
87
- };
88
- }
89
- delete payload.workspacePath;
90
- }
91
- if (isTaskMutation && payload.developmentContext?.type === "worktree") {
92
- payload.developmentContext = {
93
- type: "worktree",
94
- ...(payload.developmentContext.branch === undefined
95
- ? {}
96
- : { branch: payload.developmentContext.branch }),
97
- };
98
- }
99
- if (
100
- isConversationMutation
101
- && typeof payload.threadId === "string"
102
- && !Object.hasOwn(payload, "threadBinding")
103
- && typeof resolveThreadBinding === "function"
104
- ) {
105
- const threadBinding = resolveThreadBinding(payload.threadId);
106
- if (threadBinding) payload.threadBinding = threadBinding;
107
- }
108
- body = JSON.stringify(payload);
109
- }
110
-
111
- return { body, projectWorkspace };
112
- }
113
-
114
- async function localizeTask(task, resolveDevelopmentContext) {
115
- if (!task || typeof task !== "object" || task.developmentContext?.type !== "worktree") {
116
- return task;
117
- }
118
- const cloudContext = {
119
- type: "worktree",
120
- ...(task.developmentContext.branch === undefined
121
- ? {}
122
- : { branch: task.developmentContext.branch }),
123
- };
124
- const localContext = resolveDevelopmentContext
125
- ? await resolveDevelopmentContext(task.projectId, cloudContext)
126
- : null;
127
- return {
128
- ...task,
129
- developmentContext: localContext ?? { ...cloudContext, path: null },
130
- };
131
- }
132
-
133
- async function localizeResponse(
134
- response,
135
- { readConfig, setProjectWorkspace, projectWorkspace, resolveDevelopmentContext },
136
- ) {
137
- if (response.status === 401) return response;
138
- if (!response.headers.get("content-type")?.includes("application/json")) return response;
139
- const payload = await response.json();
140
-
141
- if (response.ok && projectWorkspace) {
142
- const projectId = projectWorkspace.projectId ?? payload.project?.id;
143
- if (projectId) {
144
- await setProjectWorkspace(projectId, projectWorkspace.workspacePath);
145
- }
146
- }
147
-
148
- const config = await readConfig();
149
- if (Array.isArray(payload.projects)) {
150
- payload.projects = payload.projects.map((project) => ({
151
- ...project,
152
- workspacePath: project.id === DEFAULT_PROJECT_ID
153
- ? null
154
- : config.projectMappings[project.id] ?? null,
155
- }));
156
- }
157
- if (payload.project && typeof payload.project === "object") {
158
- payload.project = {
159
- ...payload.project,
160
- workspacePath: payload.project.id === DEFAULT_PROJECT_ID
161
- ? null
162
- : config.projectMappings[payload.project.id] ?? null,
163
- };
164
- }
165
- const contexts = new Map();
166
- const scans = new Map();
167
- const resolveOnce = resolveDevelopmentContext
168
- ? (projectId, context) => {
169
- const key = `${projectId ?? ""}\0${context.branch ?? ""}`;
170
- if (!contexts.has(key)) {
171
- contexts.set(key, resolveDevelopmentContext(projectId, context, scans));
172
- }
173
- return contexts.get(key);
174
- }
175
- : null;
176
- if (payload.task) {
177
- payload.task = await localizeTask(payload.task, resolveOnce);
178
- }
179
- if (Array.isArray(payload.tasks)) {
180
- payload.tasks = await Promise.all(
181
- payload.tasks.map((task) => localizeTask(task, resolveOnce)),
182
- );
183
- }
184
-
185
- const headers = new Headers(response.headers);
186
- headers.delete("content-length");
187
- headers.delete("content-encoding");
188
- return new Response(JSON.stringify(payload), {
189
- status: response.status,
190
- statusText: response.statusText,
191
- headers,
192
- });
193
- }
194
-
195
- export function createCloudProxy({
196
- configStore,
197
- getConfig,
198
- fetch: fetchImplementation = globalThis.fetch,
199
- resolveDevelopmentContext,
200
- assertTaskProjectMoveAllowed,
201
- resolveThreadBinding,
202
- }) {
203
- const readConfig = getConfig ?? (() => configStore.read());
204
- const setProjectWorkspace = configStore?.setProjectWorkspace?.bind(configStore);
205
-
206
- return {
207
- async webSocketTarget(pathname = "/api/events") {
208
- const config = await readConfig();
209
- if (!config?.remoteUrl || !config.actorName || !config.sharedKey) {
210
- throw new CloudProxyError(
211
- 409,
212
- "CLOUD_NOT_CONFIGURED",
213
- "Cloud collaboration is not configured",
214
- );
215
- }
216
- let remoteUrl;
217
- try {
218
- remoteUrl = normalizeCloudUrl(config.remoteUrl);
219
- } catch (error) {
220
- throw new CloudProxyError(
221
- 500,
222
- "INVALID_CLOUD_CONFIG",
223
- error instanceof Error ? error.message : String(error),
224
- );
225
- }
226
- const url = new URL(pathname, `${remoteUrl}/`);
227
- url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
228
- return {
229
- url: url.href,
230
- headers: { authorization: basicAuthorization(config.actorName, config.sharedKey) },
231
- };
232
- },
233
- async forward(request) {
234
- const config = await readConfig();
235
- if (!config?.remoteUrl || !config.actorName || !config.sharedKey) {
236
- throw new CloudProxyError(
237
- 409,
238
- "CLOUD_NOT_CONFIGURED",
239
- "Cloud collaboration is not configured",
240
- );
241
- }
242
- let remoteUrl;
243
- try {
244
- remoteUrl = normalizeCloudUrl(config.remoteUrl);
245
- } catch (error) {
246
- throw new CloudProxyError(
247
- 500,
248
- "INVALID_CLOUD_CONFIG",
249
- error instanceof Error ? error.message : String(error),
250
- );
251
- }
252
-
253
- const sourceUrl = new URL(request.url);
254
- const upstreamUrl = new URL(
255
- `${sourceUrl.pathname}${sourceUrl.search}`,
256
- `${remoteUrl}/`,
257
- );
258
- const headers = new Headers(request.headers);
259
- headers.delete("authorization");
260
- headers.delete("host");
261
- headers.delete("connection");
262
- headers.delete("transfer-encoding");
263
- headers.delete("accept-encoding");
264
- for (const name of [...headers.keys()]) {
265
- if (name.toLowerCase().startsWith("x-taskboard-user-")) headers.delete(name);
266
- }
267
- headers.set("authorization", basicAuthorization(config.actorName, config.sharedKey));
268
-
269
- const prepared = await prepareRequest(request, {
270
- assertTaskProjectMoveAllowed,
271
- resolveThreadBinding,
272
- });
273
- if (prepared.projectWorkspace && !setProjectWorkspace) {
274
- throw new CloudProxyError(
275
- 500,
276
- "PROJECT_MAPPING_UNAVAILABLE",
277
- "Local project mapping storage is unavailable",
278
- );
279
- }
280
- if (typeof prepared.body === "string") headers.delete("content-length");
281
- const init = {
282
- method: request.method,
283
- headers,
284
- redirect: "manual",
285
- };
286
- if (request.method !== "GET" && request.method !== "HEAD" && prepared.body !== null) {
287
- init.body = prepared.body;
288
- if (typeof prepared.body !== "string") init.duplex = "half";
289
- }
290
-
291
- let response;
292
- try {
293
- response = await fetchImplementation(upstreamUrl, init);
294
- } catch (error) {
295
- throw new CloudProxyError(
296
- 502,
297
- "REMOTE_UNAVAILABLE",
298
- `Cannot reach cloud taskboard at ${remoteUrl}`,
299
- error instanceof Error ? error.message : String(error),
300
- );
301
- }
302
- return localizeResponse(response, {
303
- readConfig,
304
- setProjectWorkspace,
305
- projectWorkspace: prepared.projectWorkspace,
306
- resolveDevelopmentContext,
307
- });
308
- },
309
- };
310
- }