@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.
- package/dist/build-info.json +3 -3
- package/dist/ensure-node-pty-helper.d.ts +0 -1
- package/dist/ensure-node-pty-helper.js +0 -5
- package/dist/git-quick-commit.js +18 -19
- package/dist/github-connector.d.ts +11 -5
- package/dist/github-connector.js +105 -33
- package/dist/missions.d.ts +1 -1
- package/dist/missions.js +17 -4
- package/dist/server-file-routes.js +43 -2
- package/dist/server-github-routes.js +50 -53
- package/dist/server-mission-routes.js +11 -10
- package/dist/server-task-routes.d.ts +14 -2
- package/dist/server-task-routes.js +248 -84
- package/dist/server-workspace-routes.js +90 -16
- package/dist/server.js +2 -20
- package/dist/storage.d.ts +23 -6
- package/dist/storage.js +130 -53
- package/dist/structured-exec-host.d.ts +13 -2
- package/dist/structured-exec-host.js +71 -36
- package/dist/structured-exec-pump.d.ts +1 -1
- package/dist/structured-exec-pump.js +14 -8
- package/dist/task-types.d.ts +15 -0
- package/dist/terminal-daemon-client.d.ts +2 -0
- package/dist/terminal-daemon-client.js +7 -1
- package/dist/terminal-daemon-server.js +17 -18
- package/dist/tui/ipc-server.js +3 -1
- package/dist/types.d.ts +1 -0
- package/dist/wand-task-sync.d.ts +9 -0
- package/dist/wand-task-sync.js +50 -0
- package/dist/web-ui/content/scripts.js +85 -85
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/ws-broadcast.js +5 -1
- package/package.json +2 -3
- package/dist/taskboard-bridge.d.ts +0 -11
- package/dist/taskboard-bridge.js +0 -371
- package/vendor/codex-taskboard/LICENSE +0 -201
- package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
- package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
- package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
- package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
- package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
- package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
- package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
- package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
- package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
- package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
- package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
- package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
- package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
- package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
- package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
- package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
- package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
- package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
- package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
- package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
- package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
- package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
- package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
- package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
- package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
- package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
- package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
- package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
- package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
- package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
- package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
- package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
- package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
- package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
- package/vendor/codex-taskboard/dist/web/index.html +0 -20
- package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
- package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
- package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
- package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
- package/vendor/codex-taskboard/server/app.mjs +0 -3211
- package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
- package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
- package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
- package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
- package/vendor/codex-taskboard/server/database.mjs +0 -2845
- package/vendor/codex-taskboard/server/index.mjs +0 -41
- package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
- package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
- package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
- package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
- package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
- package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
- package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
- package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
- package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
- package/vendor/codex-taskboard/shared/domain.mjs +0 -34
- package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
- package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
- package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
- package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
- package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
- package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
- package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
- package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
- 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
|
-
}
|