hypercore-cli 1.1.2 → 1.4.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 (162) hide show
  1. package/LICENSE +92 -21
  2. package/README.md +8 -1
  3. package/dist/App-YMX7FSXR.js +1 -0
  4. package/dist/api-ELP6F4NC.js +1 -0
  5. package/dist/auth-SICBMA3P.js +1 -0
  6. package/dist/auth-X6CUT3DW.js +1 -0
  7. package/dist/background-ACODXSUG.js +1 -0
  8. package/dist/backlog-JD2IM336.js +1 -0
  9. package/dist/chunk-2QI2IU2V.js +1 -0
  10. package/dist/chunk-3KFRDIPQ.js +1 -0
  11. package/dist/chunk-42C5J7PN.js +1 -0
  12. package/dist/chunk-4D7XVJ7Q.js +1 -0
  13. package/dist/chunk-5KUSGQP2.js +1 -0
  14. package/dist/chunk-5OEFAGD5.js +1 -0
  15. package/dist/chunk-AUQ64BK2.js +1 -0
  16. package/dist/chunk-AV244H5C.js +1 -0
  17. package/dist/chunk-BQVBEFS4.js +1 -0
  18. package/dist/chunk-BYWQLFP2.js +1 -0
  19. package/dist/chunk-C6YL7UHE.js +1 -0
  20. package/dist/chunk-COITWWZJ.js +1 -0
  21. package/dist/chunk-CR7UUJVX.js +1 -0
  22. package/dist/chunk-E3MULLBX.js +1 -0
  23. package/dist/chunk-EZHYVJGQ.js +1 -0
  24. package/dist/chunk-FAMURNNH.js +1 -0
  25. package/dist/chunk-FGP56E4W.js +1 -0
  26. package/dist/chunk-FHGATV5B.js +1 -0
  27. package/dist/chunk-I2G27Y5P.js +1 -0
  28. package/dist/chunk-IKF43TX2.js +1 -0
  29. package/dist/chunk-INSPHCBN.js +1 -0
  30. package/dist/chunk-JHMV366T.js +1 -0
  31. package/dist/chunk-L52HX5SX.js +1 -0
  32. package/dist/chunk-LQMDUKIE.js +1 -0
  33. package/dist/chunk-M3MTKGA5.js +1 -0
  34. package/dist/chunk-MPO54FU3.js +1 -0
  35. package/dist/chunk-PVKCZI6A.js +1 -0
  36. package/dist/chunk-Q7KEPCYL.js +1 -0
  37. package/dist/chunk-ROBZ6PAL.js +1 -0
  38. package/dist/chunk-RXB5BS2N.js +1 -0
  39. package/dist/chunk-RZ3HNYMT.js +1 -0
  40. package/dist/chunk-UCGLRMTG.js +1 -0
  41. package/dist/chunk-UEHJVRKB.js +1 -0
  42. package/dist/chunk-UZYX5GGF.js +1 -0
  43. package/dist/chunk-XQJBB725.js +1 -0
  44. package/dist/chunk-YXCRL6K3.js +1 -0
  45. package/dist/claude-US2QPRBA.js +1 -0
  46. package/dist/commands-EKPWCB3T.js +1 -0
  47. package/dist/commands-GYNKP5WV.js +1 -0
  48. package/dist/commands-QHJLREPM.js +1 -0
  49. package/dist/config-2OUL5FLS.js +1 -0
  50. package/dist/config-loader-N7IBWN2P.js +1 -0
  51. package/dist/diagnose-NLHN4SAJ.js +1 -0
  52. package/dist/display-RYAW2GFB.js +1 -0
  53. package/dist/extractor-3KTM2IUL.js +1 -0
  54. package/dist/feature-flag-VVIF5FJG.js +1 -0
  55. package/dist/history-3R2UHRDQ.js +1 -0
  56. package/dist/index.js +1 -402
  57. package/dist/instance-registry-I5AIVJE2.js +1 -0
  58. package/dist/keybindings-RN3A7CRW.js +1 -0
  59. package/dist/loader-3IKPXP4R.js +1 -0
  60. package/dist/network-K5HRJE44.js +1 -0
  61. package/dist/notify-O6FNVHC4.js +1 -0
  62. package/dist/openai-compat-IPCMINVF.js +1 -0
  63. package/dist/panels-2R5YEFXH.js +1 -0
  64. package/dist/permissions-5O7KVAXU.js +1 -0
  65. package/dist/prompt-VWFPFM4N.js +1 -0
  66. package/dist/quality-GPQD25UL.js +1 -0
  67. package/dist/repl-CL4SYHU4.js +1 -0
  68. package/dist/roadmap-QRZODSNJ.js +1 -0
  69. package/dist/server-PKOHK5M2.js +1 -0
  70. package/dist/session-5HDDQQP6.js +1 -0
  71. package/dist/skills-DXWSVJSU.js +1 -0
  72. package/dist/store-WXXTKTTL.js +1 -0
  73. package/dist/team-N2GF4YYS.js +1 -0
  74. package/dist/telemetry-NT4UZLBS.js +1 -0
  75. package/dist/test-runner-F6B6RH3S.js +1 -0
  76. package/dist/theme-JJJ6ABR2.js +1 -0
  77. package/dist/upgrade-RUG3R7R5.js +1 -0
  78. package/dist/verify-6OGRY2PR.js +1 -0
  79. package/dist/version-4RHTDUNQ.js +1 -0
  80. package/dist/web/static/app.js +1 -562
  81. package/dist/web/static/index.html +114 -126
  82. package/dist/web/static/mirror.css +1 -1001
  83. package/dist/web/static/mirror.html +155 -178
  84. package/dist/web/static/mirror.js +1 -1125
  85. package/dist/web/static/onboard.css +1 -302
  86. package/dist/web/static/onboard.html +121 -145
  87. package/dist/web/static/onboard.js +1 -300
  88. package/dist/web/static/style.css +1 -602
  89. package/dist/web/static/utils.js +1 -0
  90. package/dist/web/static/workspace.css +1 -1568
  91. package/dist/web/static/workspace.html +369 -402
  92. package/dist/web/static/workspace.js +1 -1683
  93. package/dist/web-CIC7ZKBM.js +1 -0
  94. package/package.json +26 -4
  95. package/dist/api-JHHOZTL6.js +0 -162
  96. package/dist/auth-5QFJLW7J.js +0 -21
  97. package/dist/background-2EGCAAQH.js +0 -14
  98. package/dist/backlog-Q2NZCLNY.js +0 -24
  99. package/dist/chunk-2CMSCWQW.js +0 -162
  100. package/dist/chunk-4DVYJAJL.js +0 -57
  101. package/dist/chunk-5GDYH676.js +0 -271
  102. package/dist/chunk-5NLVGLD7.js +0 -66
  103. package/dist/chunk-6XTEAFZQ.js +0 -575
  104. package/dist/chunk-AQBSMYLT.js +0 -2025
  105. package/dist/chunk-BE46C7JW.js +0 -46
  106. package/dist/chunk-CLKIMCXZ.js +0 -139
  107. package/dist/chunk-DN4ASQ26.js +0 -167
  108. package/dist/chunk-DUWREZXK.js +0 -173
  109. package/dist/chunk-FCW3K6F2.js +0 -263
  110. package/dist/chunk-GFORWAMW.js +0 -251
  111. package/dist/chunk-GH7E2OJE.js +0 -223
  112. package/dist/chunk-GU2FZQ6A.js +0 -69
  113. package/dist/chunk-I7WI3BMB.js +0 -161
  114. package/dist/chunk-IOPKN5GD.js +0 -190
  115. package/dist/chunk-LBVHDGZE.js +0 -133
  116. package/dist/chunk-MGLJ53QN.js +0 -219
  117. package/dist/chunk-NETIY5UB.js +0 -134
  118. package/dist/chunk-NP47L7LG.js +0 -288
  119. package/dist/chunk-O6MG7TOH.js +0 -58
  120. package/dist/chunk-OPZYEVYR.js +0 -150
  121. package/dist/chunk-R3GPQC7I.js +0 -393
  122. package/dist/chunk-R5T3A2NQ.js +0 -166
  123. package/dist/chunk-RKB2JOV2.js +0 -43
  124. package/dist/chunk-RNG3K465.js +0 -80
  125. package/dist/chunk-TGTYKBGC.js +0 -86
  126. package/dist/chunk-UCX4VZCT.js +0 -681
  127. package/dist/chunk-WHLVZCQY.js +0 -245
  128. package/dist/chunk-Y6HMJZDJ.js +0 -1505
  129. package/dist/chunk-ZSBHUGWR.js +0 -262
  130. package/dist/claude-4BX3MJSK.js +0 -12
  131. package/dist/commands-2X4OB5RF.js +0 -128
  132. package/dist/commands-GLBCEVQK.js +0 -1044
  133. package/dist/commands-IINRNBYX.js +0 -232
  134. package/dist/config-RSNQJQPS.js +0 -8
  135. package/dist/config-loader-SXO674TF.js +0 -24
  136. package/dist/diagnose-7UPLS7I4.js +0 -12
  137. package/dist/display-IIUBEYWN.js +0 -58
  138. package/dist/extractor-D3XWOAXI.js +0 -129
  139. package/dist/history-6I6FADAU.js +0 -180
  140. package/dist/index.d.ts +0 -1
  141. package/dist/instance-registry-J7UJ7U4Z.js +0 -15
  142. package/dist/keybindings-PDXIOV3O.js +0 -15
  143. package/dist/loader-GKEYT6Y7.js +0 -58
  144. package/dist/network-JYGHQXAR.js +0 -279
  145. package/dist/notify-HPTALZDC.js +0 -14
  146. package/dist/openai-compat-R7EKWG6Z.js +0 -12
  147. package/dist/permissions-JUKXMNDH.js +0 -10
  148. package/dist/prompt-UWHSZU4P.js +0 -166
  149. package/dist/quality-ST7PPNFR.js +0 -16
  150. package/dist/repl-QHIZ2JGF.js +0 -3374
  151. package/dist/roadmap-5OBEKROY.js +0 -17
  152. package/dist/server-HCNIP7ZQ.js +0 -57
  153. package/dist/session-5EBECDUP.js +0 -21
  154. package/dist/skills-HBQQTYO4.js +0 -175
  155. package/dist/store-FKUTR7GW.js +0 -25
  156. package/dist/team-7BBBP5YQ.js +0 -385
  157. package/dist/telemetry-6R4EIE6O.js +0 -30
  158. package/dist/test-runner-AUAGIBNM.js +0 -619
  159. package/dist/theme-3SYJ3UQA.js +0 -14
  160. package/dist/upgrade-MZFH7OCN.js +0 -83
  161. package/dist/verify-JUDKTPKZ.js +0 -14
  162. package/dist/web-KS3FUGJA.js +0 -39
@@ -1,46 +0,0 @@
1
- // src/ui/text-utils.ts
2
- function isCJK(char) {
3
- const code = char.codePointAt(0);
4
- if (!code) return false;
5
- return code >= 11904 && code <= 40959 || // CJK 部首/汉字
6
- code >= 44032 && code <= 55215 || // 韩文音节
7
- code >= 63744 && code <= 64255 || // CJK 兼容汉字
8
- code >= 65072 && code <= 65103 || // CJK 兼容形式
9
- code >= 65280 && code <= 65376 || // 全角 ASCII
10
- code >= 65504 && code <= 65510 || // 全角符号
11
- code >= 126976 && code <= 130047 || // 表情/符号
12
- code >= 131072 && code <= 195103;
13
- }
14
- function displayWidth(str) {
15
- const clean = str.replace(/\x1b\[[0-9;]*m/g, "");
16
- let width = 0;
17
- for (const char of clean) {
18
- width += isCJK(char) ? 2 : 1;
19
- }
20
- return width;
21
- }
22
- function padToWidth(str, targetWidth) {
23
- const currentWidth = displayWidth(str);
24
- const padding = targetWidth - currentWidth;
25
- return padding > 0 ? str + " ".repeat(padding) : str;
26
- }
27
- function truncateToWidth(str, maxWidth) {
28
- let width = 0;
29
- let result = "";
30
- for (const char of str) {
31
- const charWidth = isCJK(char) ? 2 : 1;
32
- if (width + charWidth > maxWidth - 1) {
33
- result += "\u2026";
34
- break;
35
- }
36
- result += char;
37
- width += charWidth;
38
- }
39
- return result;
40
- }
41
-
42
- export {
43
- displayWidth,
44
- padToWidth,
45
- truncateToWidth
46
- };
@@ -1,139 +0,0 @@
1
- import {
2
- HYPERCORE_DIR
3
- } from "./chunk-I7WI3BMB.js";
4
-
5
- // src/core/hooks.ts
6
- import { existsSync } from "fs";
7
- import { readFile } from "fs/promises";
8
- import { join } from "path";
9
- import { spawn } from "child_process";
10
- var HookManager = class {
11
- hooks = [];
12
- loaded = false;
13
- /** 从配置文件加载钩子 */
14
- async load() {
15
- this.hooks = [];
16
- const systemPath = join(HYPERCORE_DIR, "hooks.json");
17
- await this.loadFromFile(systemPath);
18
- const projectPath = join(process.cwd(), ".hypercore", "hooks.json");
19
- if (projectPath !== systemPath) {
20
- await this.loadFromFile(projectPath);
21
- }
22
- this.loaded = true;
23
- }
24
- async loadFromFile(filePath) {
25
- if (!existsSync(filePath)) return;
26
- try {
27
- const content = await readFile(filePath, "utf-8");
28
- const config = JSON.parse(content);
29
- if (Array.isArray(config.hooks)) {
30
- for (const hook of config.hooks) {
31
- if (hook.event && hook.command) {
32
- this.hooks.push(hook);
33
- }
34
- }
35
- }
36
- } catch {
37
- }
38
- }
39
- /** 触发钩子,返回拦截结果 */
40
- async trigger(event, context = {}) {
41
- if (!this.loaded) await this.load();
42
- const matching = this.hooks.filter((h) => {
43
- if (h.event !== event) return false;
44
- if (h.match?.toolName && context.toolName !== h.match.toolName) return false;
45
- if (h.match?.model && context.model !== h.match.model) return false;
46
- return true;
47
- });
48
- if (matching.length === 0) return { intercepted: false };
49
- const fullContext = {
50
- event,
51
- ...context
52
- };
53
- const blocking = matching.filter((h) => h.blocking);
54
- const nonBlocking = matching.filter((h) => !h.blocking);
55
- for (const hook of nonBlocking) {
56
- this.executeHook(hook, fullContext).catch(() => {
57
- });
58
- }
59
- for (const hook of blocking) {
60
- const result = await this.executeHook(hook, fullContext);
61
- if (hook.intercept && event === "onToolCall" && result.exitCode !== 0) {
62
- return {
63
- intercepted: true,
64
- reason: result.stdout || `Hook "${hook.name || hook.command}" \u62E6\u622A\u4E86\u64CD\u4F5C (exit code: ${result.exitCode})`
65
- };
66
- }
67
- }
68
- return { intercepted: false };
69
- }
70
- async executeHook(hook, context) {
71
- const timeout = hook.timeout || 1e4;
72
- const env = {
73
- ...process.env,
74
- HYPERCORE_EVENT: context.event,
75
- HYPERCORE_SESSION_ID: context.sessionId || "",
76
- HYPERCORE_MODEL: context.model || "",
77
- HYPERCORE_CWD: process.cwd()
78
- };
79
- if (context.toolName) env.HYPERCORE_TOOL_NAME = context.toolName;
80
- if (context.toolInput) env.HYPERCORE_TOOL_INPUT = context.toolInput.slice(0, 1e3);
81
- if (context.toolResult) env.HYPERCORE_TOOL_RESULT = context.toolResult.slice(0, 1e3);
82
- if (context.userPrompt) env.HYPERCORE_USER_PROMPT = context.userPrompt.slice(0, 500);
83
- if (context.error) env.HYPERCORE_ERROR = context.error.slice(0, 500);
84
- return await new Promise((resolve) => {
85
- const child = spawn("sh", ["-c", hook.command], {
86
- cwd: process.cwd(),
87
- env,
88
- stdio: ["ignore", "pipe", "pipe"]
89
- });
90
- let stdout = "";
91
- let stderr = "";
92
- let settled = false;
93
- const finish = (exitCode, output) => {
94
- if (settled) return;
95
- settled = true;
96
- resolve({ exitCode, stdout: output.trim() });
97
- };
98
- const timer = setTimeout(() => {
99
- child.kill("SIGTERM");
100
- setTimeout(() => child.kill("SIGKILL"), 1e3);
101
- const output = stdout || stderr || `Hook timeout after ${timeout}ms`;
102
- finish(124, output);
103
- }, timeout);
104
- child.stdout?.on("data", (chunk) => {
105
- stdout += chunk.toString("utf-8");
106
- });
107
- child.stderr?.on("data", (chunk) => {
108
- stderr += chunk.toString("utf-8");
109
- });
110
- child.on("error", (err) => {
111
- clearTimeout(timer);
112
- finish(1, stderr || stdout || err.message);
113
- });
114
- child.on("close", (code) => {
115
- clearTimeout(timer);
116
- const output = stdout || stderr;
117
- finish(code ?? 1, output);
118
- });
119
- });
120
- }
121
- /** 获取已加载的钩子数量 */
122
- get count() {
123
- return this.hooks.length;
124
- }
125
- /** 获取按事件分组的钩子列表 */
126
- listByEvent() {
127
- const grouped = {};
128
- for (const hook of this.hooks) {
129
- if (!grouped[hook.event]) grouped[hook.event] = [];
130
- grouped[hook.event].push(hook);
131
- }
132
- return grouped;
133
- }
134
- };
135
- var hookManager = new HookManager();
136
-
137
- export {
138
- hookManager
139
- };
@@ -1,167 +0,0 @@
1
- import {
2
- loadTeam,
3
- updateMemberStatus
4
- } from "./chunk-DUWREZXK.js";
5
- import {
6
- validateToken
7
- } from "./chunk-5NLVGLD7.js";
8
-
9
- // src/web/ws.ts
10
- import { WebSocketServer, WebSocket } from "ws";
11
- var clients = /* @__PURE__ */ new Map();
12
- function getTeamClients(teamId) {
13
- return Array.from(clients.values()).filter((c) => c.teamId === teamId);
14
- }
15
- function sendToMember(memberId, msg) {
16
- const client = clients.get(memberId);
17
- if (client && client.ws.readyState === WebSocket.OPEN) {
18
- client.ws.send(JSON.stringify(msg));
19
- return true;
20
- }
21
- return false;
22
- }
23
- function broadcastToTeam(teamId, msg, excludeId) {
24
- for (const client of getTeamClients(teamId)) {
25
- if (client.member.id !== excludeId && client.ws.readyState === WebSocket.OPEN) {
26
- client.ws.send(JSON.stringify(msg));
27
- }
28
- }
29
- }
30
- function broadcastToAll(teamId, msg) {
31
- for (const client of getTeamClients(teamId)) {
32
- if (client.ws.readyState === WebSocket.OPEN) {
33
- client.ws.send(JSON.stringify(msg));
34
- }
35
- }
36
- }
37
- function makeMsg(type, payload, senderId) {
38
- return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString(), senderId };
39
- }
40
- var pendingCheckpoints = /* @__PURE__ */ new Map();
41
- function resolveCheckpoint(runId, stationIndex, response) {
42
- const key = `${runId}:${stationIndex}`;
43
- const resolver = pendingCheckpoints.get(key);
44
- if (resolver) {
45
- resolver(response);
46
- pendingCheckpoints.delete(key);
47
- return true;
48
- }
49
- return false;
50
- }
51
- async function handleMessage(ws, raw) {
52
- let msg;
53
- try {
54
- msg = JSON.parse(raw);
55
- } catch {
56
- ws.send(JSON.stringify(makeMsg("auth_fail", { reason: "\u65E0\u6548 JSON" })));
57
- return;
58
- }
59
- const existingClient = Array.from(clients.values()).find((c) => c.ws === ws);
60
- if (!existingClient) {
61
- if (msg.type !== "auth") {
62
- ws.send(JSON.stringify(makeMsg("auth_fail", { reason: "\u8BF7\u5148\u8BA4\u8BC1" })));
63
- ws.close();
64
- return;
65
- }
66
- const { teamId: teamId2, token } = msg.payload;
67
- const team = await loadTeam(teamId2);
68
- if (!team) {
69
- ws.send(JSON.stringify(makeMsg("auth_fail", { reason: "\u56E2\u961F\u4E0D\u5B58\u5728" })));
70
- ws.close();
71
- return;
72
- }
73
- const member2 = validateToken(team, token);
74
- if (!member2) {
75
- ws.send(JSON.stringify(makeMsg("auth_fail", { reason: "\u4EE4\u724C\u65E0\u6548" })));
76
- ws.close();
77
- return;
78
- }
79
- clients.set(member2.id, { ws, teamId: teamId2, member: member2 });
80
- await updateMemberStatus(teamId2, member2.id, "online");
81
- ws.send(JSON.stringify(makeMsg("auth_ok", {
82
- memberId: member2.id,
83
- memberName: member2.name,
84
- role: member2.role,
85
- teamName: team.name
86
- })));
87
- broadcastToTeam(teamId2, makeMsg("member_joined", {
88
- memberId: member2.id,
89
- memberName: member2.name
90
- }), member2.id);
91
- return;
92
- }
93
- const { teamId, member } = existingClient;
94
- switch (msg.type) {
95
- // ----- 任务事件 -----
96
- case "task_created":
97
- case "task_updated":
98
- case "task_deleted":
99
- broadcastToTeam(teamId, makeMsg(msg.type, msg.payload, member.id), member.id);
100
- break;
101
- // ----- 检查点响应 -----
102
- case "run_checkpoint_response": {
103
- const { runId, stationIndex, action, feedback } = msg.payload;
104
- resolveCheckpoint(runId, stationIndex, { action, feedback });
105
- break;
106
- }
107
- // ----- 聊天消息 -----
108
- case "chat_message": {
109
- const { content } = msg.payload;
110
- broadcastToAll(teamId, makeMsg("chat_message", {
111
- senderId: member.id,
112
- senderName: member.name,
113
- content
114
- }));
115
- break;
116
- }
117
- case "chat_typing": {
118
- broadcastToTeam(teamId, makeMsg("chat_typing", {
119
- memberId: member.id,
120
- memberName: member.name
121
- }), member.id);
122
- break;
123
- }
124
- default:
125
- break;
126
- }
127
- }
128
- function attachWebSocket(_server, _config) {
129
- const wss = new WebSocketServer({ noServer: true });
130
- wss.on("connection", (ws) => {
131
- ws.on("message", (data) => {
132
- handleMessage(ws, data.toString()).catch((err) => {
133
- console.error("[WS] \u5904\u7406\u6D88\u606F\u9519\u8BEF:", err);
134
- });
135
- });
136
- ws.on("close", async () => {
137
- for (const [memberId, client] of clients.entries()) {
138
- if (client.ws === ws) {
139
- clients.delete(memberId);
140
- await updateMemberStatus(client.teamId, memberId, "offline");
141
- broadcastToTeam(client.teamId, makeMsg("member_left", {
142
- memberId,
143
- memberName: client.member.name
144
- }));
145
- break;
146
- }
147
- }
148
- });
149
- ws.on("error", (err) => {
150
- console.error("[WS] \u8FDE\u63A5\u9519\u8BEF:", err.message);
151
- });
152
- setTimeout(() => {
153
- const isAuthed = Array.from(clients.values()).some((c) => c.ws === ws);
154
- if (!isAuthed && ws.readyState === WebSocket.OPEN) {
155
- ws.send(JSON.stringify(makeMsg("auth_fail", { reason: "\u8BA4\u8BC1\u8D85\u65F6" })));
156
- ws.close();
157
- }
158
- }, 1e4);
159
- });
160
- return wss;
161
- }
162
-
163
- export {
164
- sendToMember,
165
- broadcastToTeam,
166
- attachWebSocket
167
- };
@@ -1,173 +0,0 @@
1
- import {
2
- generateId,
3
- generateJoinCode,
4
- generateToken
5
- } from "./chunk-5NLVGLD7.js";
6
- import {
7
- HYPERCORE_DIR
8
- } from "./chunk-I7WI3BMB.js";
9
-
10
- // src/team/store.ts
11
- import { readFile, writeFile, mkdir, readdir } from "fs/promises";
12
- import { existsSync } from "fs";
13
- import { join } from "path";
14
- var TEAMS_DIR = join(HYPERCORE_DIR, "teams");
15
- function teamDir(teamId) {
16
- return join(TEAMS_DIR, teamId);
17
- }
18
- function configPath(teamId) {
19
- return join(teamDir(teamId), "config.json");
20
- }
21
- function tasksPath(teamId) {
22
- return join(teamDir(teamId), "tasks.json");
23
- }
24
- function runsDir(teamId) {
25
- return join(teamDir(teamId), "runs");
26
- }
27
- function runPath(teamId, runId) {
28
- return join(runsDir(teamId), `${runId}.json`);
29
- }
30
- async function readJSON(path) {
31
- if (!existsSync(path)) return null;
32
- try {
33
- const data = await readFile(path, "utf-8");
34
- return JSON.parse(data);
35
- } catch {
36
- return null;
37
- }
38
- }
39
- async function writeJSON(path, data) {
40
- const dir = join(path, "..");
41
- await mkdir(dir, { recursive: true });
42
- await writeFile(path, JSON.stringify(data, null, 2), "utf-8");
43
- }
44
- async function createTeam(teamName, ownerName) {
45
- const teamId = generateId();
46
- const memberId = generateId();
47
- const token = generateToken();
48
- const owner = {
49
- id: memberId,
50
- name: ownerName,
51
- role: "owner",
52
- token,
53
- joinedAt: (/* @__PURE__ */ new Date()).toISOString(),
54
- status: "online",
55
- lastSeen: (/* @__PURE__ */ new Date()).toISOString()
56
- };
57
- const team = {
58
- id: teamId,
59
- name: teamName,
60
- joinCode: generateJoinCode(),
61
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
62
- owner: memberId,
63
- members: [owner]
64
- };
65
- await writeJSON(configPath(teamId), team);
66
- await writeJSON(tasksPath(teamId), []);
67
- return { team, ownerToken: token };
68
- }
69
- async function loadTeam(teamId) {
70
- return readJSON(configPath(teamId));
71
- }
72
- async function findTeamByJoinCode(code) {
73
- if (!existsSync(TEAMS_DIR)) return null;
74
- const dirs = await readdir(TEAMS_DIR);
75
- for (const dir of dirs) {
76
- const team = await readJSON(configPath(dir));
77
- if (team && team.joinCode === code.toUpperCase()) return team;
78
- }
79
- return null;
80
- }
81
- async function saveTeam(team) {
82
- await writeJSON(configPath(team.id), team);
83
- }
84
- async function addMember(teamId, memberName) {
85
- const team = await loadTeam(teamId);
86
- if (!team) return null;
87
- if (team.members.some((m) => m.name === memberName)) {
88
- memberName = `${memberName}_${team.members.length}`;
89
- }
90
- const member = {
91
- id: generateId(),
92
- name: memberName,
93
- role: "member",
94
- token: generateToken(),
95
- joinedAt: (/* @__PURE__ */ new Date()).toISOString(),
96
- status: "online",
97
- lastSeen: (/* @__PURE__ */ new Date()).toISOString()
98
- };
99
- team.members.push(member);
100
- await saveTeam(team);
101
- return { member, team };
102
- }
103
- async function updateMemberStatus(teamId, memberId, status) {
104
- const team = await loadTeam(teamId);
105
- if (!team) return;
106
- const member = team.members.find((m) => m.id === memberId);
107
- if (member) {
108
- member.status = status;
109
- member.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
110
- await saveTeam(team);
111
- }
112
- }
113
- async function listTasks(teamId) {
114
- return await readJSON(tasksPath(teamId)) || [];
115
- }
116
- async function addTask(teamId, task) {
117
- const tasks = await listTasks(teamId);
118
- const maxId = tasks.reduce((max, t) => Math.max(max, parseInt(t.id) || 0), 0);
119
- const newTask = {
120
- ...task,
121
- id: String(maxId + 1),
122
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
123
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
124
- };
125
- tasks.push(newTask);
126
- await writeJSON(tasksPath(teamId), tasks);
127
- return newTask;
128
- }
129
- async function updateTask(teamId, taskId, updates) {
130
- const tasks = await listTasks(teamId);
131
- const idx = tasks.findIndex((t) => t.id === taskId);
132
- if (idx === -1) return null;
133
- tasks[idx] = { ...tasks[idx], ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
134
- await writeJSON(tasksPath(teamId), tasks);
135
- return tasks[idx];
136
- }
137
- async function deleteTask(teamId, taskId) {
138
- const tasks = await listTasks(teamId);
139
- const filtered = tasks.filter((t) => t.id !== taskId);
140
- if (filtered.length === tasks.length) return false;
141
- await writeJSON(tasksPath(teamId), filtered);
142
- return true;
143
- }
144
- async function loadRun(teamId, runId) {
145
- return readJSON(runPath(teamId, runId));
146
- }
147
- async function listRuns(teamId) {
148
- const dir = runsDir(teamId);
149
- if (!existsSync(dir)) return [];
150
- const files = await readdir(dir);
151
- const runs = [];
152
- for (const file of files) {
153
- if (file.endsWith(".json")) {
154
- const run = await readJSON(join(dir, file));
155
- if (run) runs.push(run);
156
- }
157
- }
158
- return runs.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
159
- }
160
-
161
- export {
162
- createTeam,
163
- loadTeam,
164
- findTeamByJoinCode,
165
- addMember,
166
- updateMemberStatus,
167
- listTasks,
168
- addTask,
169
- updateTask,
170
- deleteTask,
171
- loadRun,
172
- listRuns
173
- };