hypercore-cli 1.1.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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/api-XGC7D5AW.js +162 -0
  4. package/dist/auth-DNQWYQKT.js +21 -0
  5. package/dist/background-2EGCAAQH.js +14 -0
  6. package/dist/backlog-Q2NZCLNY.js +24 -0
  7. package/dist/chunk-2CMSCWQW.js +162 -0
  8. package/dist/chunk-2LJ2DVEB.js +167 -0
  9. package/dist/chunk-3RPFCQKJ.js +288 -0
  10. package/dist/chunk-43OLRXM5.js +263 -0
  11. package/dist/chunk-4DVYJAJL.js +57 -0
  12. package/dist/chunk-6OL3GA3P.js +173 -0
  13. package/dist/chunk-AUHU7ALH.js +2023 -0
  14. package/dist/chunk-B6A2AKLN.js +139 -0
  15. package/dist/chunk-BE46C7JW.js +46 -0
  16. package/dist/chunk-CUVAUOXL.js +58 -0
  17. package/dist/chunk-GH7E2OJE.js +223 -0
  18. package/dist/chunk-GOOTEPBK.js +271 -0
  19. package/dist/chunk-GPPMJYSM.js +133 -0
  20. package/dist/chunk-GU2FZQ6A.js +69 -0
  21. package/dist/chunk-IOPKN5GD.js +190 -0
  22. package/dist/chunk-IXOIOGR5.js +1505 -0
  23. package/dist/chunk-KRPOPWGA.js +251 -0
  24. package/dist/chunk-MGLJ53QN.js +219 -0
  25. package/dist/chunk-MV4TTRYX.js +533 -0
  26. package/dist/chunk-OPZYEVYR.js +150 -0
  27. package/dist/chunk-QTSLP47C.js +166 -0
  28. package/dist/chunk-R3GPQC7I.js +393 -0
  29. package/dist/chunk-RKB2JOV2.js +43 -0
  30. package/dist/chunk-RNG3K465.js +80 -0
  31. package/dist/chunk-TGTYKBGC.js +86 -0
  32. package/dist/chunk-U5SGAIMM.js +681 -0
  33. package/dist/chunk-V5UHPPSY.js +140 -0
  34. package/dist/chunk-WHLVZCQY.js +245 -0
  35. package/dist/chunk-XDRCBMZZ.js +66 -0
  36. package/dist/chunk-XOS6HPEF.js +134 -0
  37. package/dist/chunk-ZSBHUGWR.js +262 -0
  38. package/dist/claude-NSQ442XD.js +12 -0
  39. package/dist/commands-CK3WFAGI.js +128 -0
  40. package/dist/commands-U63OEO5J.js +1044 -0
  41. package/dist/commands-ZE6GD3WC.js +232 -0
  42. package/dist/config-4EW42BSF.js +8 -0
  43. package/dist/config-loader-SXO674TF.js +24 -0
  44. package/dist/diagnose-AFW3ZTZ4.js +12 -0
  45. package/dist/display-IIUBEYWN.js +58 -0
  46. package/dist/extractor-QV53W2YJ.js +129 -0
  47. package/dist/history-WMSCHERZ.js +180 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.js +406 -0
  50. package/dist/instance-registry-YSIJXSO7.js +15 -0
  51. package/dist/keybindings-JAAMLH3G.js +15 -0
  52. package/dist/loader-WHNTZTLP.js +58 -0
  53. package/dist/network-MM6YWPGO.js +279 -0
  54. package/dist/notify-HPTALZDC.js +14 -0
  55. package/dist/openai-compat-UQWJXBEK.js +12 -0
  56. package/dist/permissions-JUKXMNDH.js +10 -0
  57. package/dist/prompt-QV45TXRL.js +166 -0
  58. package/dist/quality-ST7PPNFR.js +16 -0
  59. package/dist/repl-RT3AHL7M.js +3375 -0
  60. package/dist/roadmap-5OBEKROY.js +17 -0
  61. package/dist/server-PORT7OEG.js +57 -0
  62. package/dist/session-4VUNDWLH.js +21 -0
  63. package/dist/skills-V4A35XKG.js +175 -0
  64. package/dist/store-Y4LU5QTO.js +25 -0
  65. package/dist/team-HO7Z4SIM.js +385 -0
  66. package/dist/telemetry-6R4EIE6O.js +30 -0
  67. package/dist/test-runner-ZQH5Y6OJ.js +619 -0
  68. package/dist/theme-3SYJ3UQA.js +14 -0
  69. package/dist/upgrade-7TGI3SXO.js +83 -0
  70. package/dist/verify-JUDKTPKZ.js +14 -0
  71. package/dist/web/static/app.js +562 -0
  72. package/dist/web/static/index.html +132 -0
  73. package/dist/web/static/mirror.css +1001 -0
  74. package/dist/web/static/mirror.html +184 -0
  75. package/dist/web/static/mirror.js +1125 -0
  76. package/dist/web/static/onboard.css +302 -0
  77. package/dist/web/static/onboard.html +140 -0
  78. package/dist/web/static/onboard.js +260 -0
  79. package/dist/web/static/style.css +602 -0
  80. package/dist/web/static/workspace.css +1568 -0
  81. package/dist/web/static/workspace.html +408 -0
  82. package/dist/web/static/workspace.js +1683 -0
  83. package/dist/web-Z5HSCQHW.js +39 -0
  84. package/package.json +67 -0
@@ -0,0 +1,140 @@
1
+ // src/core/config.ts
2
+ import { join } from "path";
3
+ import { readFile } from "fs/promises";
4
+ import { existsSync } from "fs";
5
+
6
+ // src/core/types.ts
7
+ var MODEL_PROVIDERS = {
8
+ anthropic: {
9
+ defaultModel: "claude-sonnet-4-20250514",
10
+ name: "Anthropic (Claude)",
11
+ sdkType: "anthropic"
12
+ },
13
+ minimax: {
14
+ baseURL: "https://api.minimax.io/anthropic",
15
+ defaultModel: "MiniMax-M2.5",
16
+ name: "MiniMax (M2.5)",
17
+ sdkType: "anthropic"
18
+ },
19
+ gemini: {
20
+ baseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
21
+ defaultModel: "gemini-2.5-flash",
22
+ name: "Google (Gemini)",
23
+ sdkType: "openai"
24
+ },
25
+ deepseek: {
26
+ baseURL: "https://api.deepseek.com/v1",
27
+ defaultModel: "deepseek-chat",
28
+ name: "DeepSeek",
29
+ sdkType: "openai"
30
+ },
31
+ "openai-compatible": {
32
+ defaultModel: "gpt-4o",
33
+ name: "OpenAI Compatible",
34
+ sdkType: "openai"
35
+ }
36
+ };
37
+ var MODEL_ALIASES = {
38
+ // Claude 系列
39
+ "sonnet": { provider: "anthropic", model: "claude-sonnet-4-20250514" },
40
+ "haiku": { provider: "anthropic", model: "claude-3-5-haiku-20241022" },
41
+ "opus": { provider: "anthropic", model: "claude-opus-4-20250514" },
42
+ // Gemini 系列
43
+ "flash": { provider: "gemini", model: "gemini-2.5-flash" },
44
+ "gemini": { provider: "gemini", model: "gemini-2.5-flash" },
45
+ "gemini-pro": { provider: "gemini", model: "gemini-2.5-pro-preview-05-06" },
46
+ // DeepSeek
47
+ "deepseek": { provider: "deepseek", model: "deepseek-chat" },
48
+ "ds": { provider: "deepseek", model: "deepseek-chat" },
49
+ "deepseek-coder": { provider: "deepseek", model: "deepseek-coder" },
50
+ "deepseek-r1": { provider: "deepseek", model: "deepseek-reasoner" },
51
+ // MiniMax
52
+ "minimax": { provider: "minimax", model: "MiniMax-M2.5" },
53
+ "mm": { provider: "minimax", model: "MiniMax-M2.5" }
54
+ };
55
+
56
+ // src/core/config.ts
57
+ var HYPERCORE_DIR = join(process.env.HOME || "~", ".hypercore");
58
+ function parseSectionValues(content, sectionName) {
59
+ const sectionRegex = new RegExp(`\\[${sectionName}\\]([\\s\\S]*?)(?=\\[|$)`);
60
+ const match = content.match(sectionRegex);
61
+ if (!match) return {};
62
+ const result = {};
63
+ const lines = match[1].split("\n");
64
+ for (const line of lines) {
65
+ const kvMatch = line.match(/^\s*(\w+)\s*=\s*"([^"]*)"/);
66
+ if (kvMatch) {
67
+ result[kvMatch[1]] = kvMatch[2];
68
+ }
69
+ }
70
+ return result;
71
+ }
72
+ async function loadConfig() {
73
+ const configPath = join(HYPERCORE_DIR, "config.toml");
74
+ if (!existsSync(configPath)) {
75
+ throw new Error("\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\u3002\u8BF7\u5148\u8FD0\u884C hypercore init");
76
+ }
77
+ const content = await readFile(configPath, "utf-8");
78
+ const modelSection = parseSectionValues(content, "model");
79
+ const provider = modelSection["provider"] || "anthropic";
80
+ const apiKey = modelSection["api_key"];
81
+ if (!apiKey) {
82
+ throw new Error("\u914D\u7F6E\u6587\u4EF6 [model] \u6BB5\u672A\u627E\u5230 api_key");
83
+ }
84
+ const providerConfig = MODEL_PROVIDERS[provider];
85
+ if (!providerConfig) {
86
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u6A21\u578B\u63D0\u4F9B\u5546: ${provider}\u3002\u652F\u6301: ${Object.keys(MODEL_PROVIDERS).join(", ")}`);
87
+ }
88
+ const searchSection = parseSectionValues(content, "search");
89
+ const outputSection = parseSectionValues(content, "output");
90
+ const keysSection = parseSectionValues(content, "keys");
91
+ const providerKeys = {};
92
+ providerKeys[provider] = apiKey;
93
+ const keyMapping = {
94
+ anthropic: "anthropic",
95
+ gemini: "gemini",
96
+ deepseek: "deepseek",
97
+ minimax: "minimax",
98
+ openai: "openai-compatible"
99
+ };
100
+ for (const [name, value] of Object.entries(keysSection)) {
101
+ const mappedProvider = keyMapping[name];
102
+ if (mappedProvider && value) {
103
+ providerKeys[mappedProvider] = value;
104
+ }
105
+ }
106
+ const uiSection = parseSectionValues(content, "ui");
107
+ const uiConfig = {};
108
+ if (uiSection["theme"]) {
109
+ const t = uiSection["theme"];
110
+ if (t === "dark" || t === "light" || t === "high-contrast") {
111
+ uiConfig.theme = t;
112
+ }
113
+ }
114
+ if (uiSection["vim_mode"] === "true") uiConfig.vimMode = true;
115
+ if (uiSection["notifications"] === "false") {
116
+ uiConfig.notifications = false;
117
+ } else {
118
+ uiConfig.notifications = true;
119
+ }
120
+ return {
121
+ modelConfig: {
122
+ provider,
123
+ model: modelSection["name"] || providerConfig.defaultModel,
124
+ apiKey,
125
+ baseURL: modelSection["base_url"] || providerConfig.baseURL,
126
+ sdkType: providerConfig.sdkType
127
+ },
128
+ tavilyApiKey: searchSection["api_key"] || void 0,
129
+ outputDir: outputSection["dir"] || join(HYPERCORE_DIR, "outputs"),
130
+ providerKeys,
131
+ uiConfig
132
+ };
133
+ }
134
+
135
+ export {
136
+ MODEL_PROVIDERS,
137
+ MODEL_ALIASES,
138
+ HYPERCORE_DIR,
139
+ loadConfig
140
+ };
@@ -0,0 +1,245 @@
1
+ // src/core/config-loader.ts
2
+ import { readFile, readdir } from "fs/promises";
3
+ import { join } from "path";
4
+ function splitByHeadings(markdown, level) {
5
+ const regex = new RegExp(`^${"#".repeat(level)}\\s+(.+)$`, "gm");
6
+ const sections = [];
7
+ let lastIndex = 0;
8
+ let lastTitle = "";
9
+ let match;
10
+ while ((match = regex.exec(markdown)) !== null) {
11
+ if (lastTitle) {
12
+ sections.push({
13
+ title: lastTitle,
14
+ content: markdown.slice(lastIndex, match.index).trim(),
15
+ level
16
+ });
17
+ }
18
+ lastTitle = match[1].trim();
19
+ lastIndex = match.index + match[0].length;
20
+ }
21
+ if (lastTitle) {
22
+ sections.push({
23
+ title: lastTitle,
24
+ content: markdown.slice(lastIndex).trim(),
25
+ level
26
+ });
27
+ }
28
+ return sections;
29
+ }
30
+ function parseKeyValue(text) {
31
+ const result = {};
32
+ const lines = text.split("\n");
33
+ let currentKey = "";
34
+ let currentValue = "";
35
+ let inMultiline = false;
36
+ for (const line of lines) {
37
+ if (inMultiline) {
38
+ if (/^\w+:/.test(line) && !line.startsWith(" ")) {
39
+ result[currentKey] = currentValue.trim();
40
+ inMultiline = false;
41
+ } else {
42
+ currentValue += "\n" + line;
43
+ continue;
44
+ }
45
+ }
46
+ const kvMatch = line.match(/^(\w[\w_]*)\s*:\s*(.*)$/);
47
+ if (kvMatch) {
48
+ const [, key, value] = kvMatch;
49
+ if (value === "|" || value === "") {
50
+ currentKey = key;
51
+ currentValue = "";
52
+ inMultiline = true;
53
+ } else {
54
+ result[key] = value.trim();
55
+ }
56
+ }
57
+ }
58
+ if (inMultiline && currentKey) {
59
+ result[currentKey] = currentValue.trim();
60
+ }
61
+ return result;
62
+ }
63
+ function parseList(text) {
64
+ return text.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^[\s]*-\s*/, "").trim());
65
+ }
66
+ function parsePWP(content) {
67
+ const sections = splitByHeadings(content, 2);
68
+ let identity = "";
69
+ let qualityStandards = [];
70
+ for (const section of sections) {
71
+ const titleLower = section.title.toLowerCase();
72
+ if (titleLower.includes("\u8EAB\u4EFD") || titleLower.includes("identity")) {
73
+ identity = section.content;
74
+ } else if (titleLower.includes("\u54C1\u8D28") || titleLower.includes("quality") || titleLower.includes("\u6807\u51C6")) {
75
+ qualityStandards = parseList(section.content);
76
+ }
77
+ }
78
+ return { identity, qualityStandards, rawContent: content };
79
+ }
80
+ function parseAgent(content) {
81
+ const sections = splitByHeadings(content, 2);
82
+ const firstLine = content.split("\n").find((l) => l.startsWith("# "));
83
+ const name = firstLine ? firstLine.replace(/^#\s+/, "").trim() : "unknown";
84
+ let role = "";
85
+ let skillIds = [];
86
+ let style = "";
87
+ for (const section of sections) {
88
+ const titleLower = section.title.toLowerCase();
89
+ if (titleLower.includes("\u89D2\u8272") || titleLower.includes("role")) {
90
+ role = section.content;
91
+ } else if (titleLower.includes("\u6280\u80FD") || titleLower.includes("skill")) {
92
+ skillIds = parseList(section.content);
93
+ } else if (titleLower.includes("\u98CE\u683C") || titleLower.includes("style") || titleLower.includes("\u6027\u683C")) {
94
+ style = section.content;
95
+ }
96
+ }
97
+ return { name, role, skillIds, style, rawContent: content };
98
+ }
99
+ function parseSkill(content) {
100
+ const sections = splitByHeadings(content, 2);
101
+ const firstLine = content.split("\n").find((l) => l.startsWith("# "));
102
+ const name = firstLine ? firstLine.replace(/^#\s+/, "").trim() : "unknown";
103
+ let domain = "";
104
+ let knowledge = "";
105
+ let toolStrategy = "";
106
+ let qualityStandards = [];
107
+ for (const section of sections) {
108
+ const titleLower = section.title.toLowerCase();
109
+ if (titleLower.includes("\u9886\u57DF") || titleLower.includes("domain")) {
110
+ domain = section.content;
111
+ } else if (titleLower.includes("\u77E5\u8BC6") || titleLower.includes("knowledge") || titleLower.includes("\u65B9\u6CD5")) {
112
+ knowledge = section.content;
113
+ } else if (titleLower.includes("\u5DE5\u5177") || titleLower.includes("tool")) {
114
+ toolStrategy = section.content;
115
+ } else if (titleLower.includes("\u8D28\u91CF") || titleLower.includes("quality") || titleLower.includes("\u6807\u51C6")) {
116
+ qualityStandards = parseList(section.content);
117
+ }
118
+ }
119
+ return { name, domain, knowledge, toolStrategy, qualityStandards, rawContent: content };
120
+ }
121
+ function parseProductionLine(content) {
122
+ const sections = splitByHeadings(content, 2);
123
+ const firstLine = content.split("\n").find((l) => l.startsWith("# "));
124
+ const name = firstLine ? firstLine.replace(/^#\s+/, "").trim() : "unknown";
125
+ let description = "";
126
+ let icon;
127
+ let inputs = [];
128
+ let stations = [];
129
+ let output = { format: "markdown", description: "" };
130
+ for (const section of sections) {
131
+ const titleLower = section.title.toLowerCase();
132
+ if (titleLower.includes("\u63CF\u8FF0") || titleLower.includes("description")) {
133
+ description = section.content;
134
+ } else if (titleLower.includes("\u56FE\u6807") || titleLower.includes("icon")) {
135
+ icon = section.content.trim();
136
+ } else if (titleLower.includes("\u8F93\u5165") || titleLower.includes("input")) {
137
+ inputs = parseInputs(section.content);
138
+ } else if (titleLower.includes("\u5DE5\u4F4D") || titleLower.includes("station")) {
139
+ stations = parseStations(section.content);
140
+ } else if (titleLower.includes("\u8F93\u51FA") || titleLower.includes("output")) {
141
+ const kv = parseKeyValue(section.content);
142
+ output = {
143
+ format: kv["format"] || "markdown",
144
+ description: kv["description"] || ""
145
+ };
146
+ }
147
+ }
148
+ return { name, description, icon, inputs, stations, output };
149
+ }
150
+ function parseInputs(content) {
151
+ return parseList(content).map((line) => {
152
+ const nameMatch = line.match(/^(\w+)\s*[::]\s*(.+)/);
153
+ if (!nameMatch) return { name: line, type: "string", required: false, description: line };
154
+ const [, paramName, rest] = nameMatch;
155
+ const required = rest.includes("\u5FC5\u586B") || rest.includes("required");
156
+ const defaultMatch = rest.match(/默认值?\s*[::]\s*(.+?)(?:[,,))]|$)/);
157
+ return {
158
+ name: paramName,
159
+ type: "string",
160
+ required,
161
+ description: rest,
162
+ defaultValue: defaultMatch?.[1]?.trim()
163
+ };
164
+ });
165
+ }
166
+ function parseStations(content) {
167
+ const stationSections = splitByHeadings(content, 3);
168
+ return stationSections.map((section, i) => {
169
+ const nameMatch = section.title.match(/^\d+\.\s*(.+)/);
170
+ const stationName = nameMatch ? nameMatch[1].trim() : section.title;
171
+ const kv = parseKeyValue(section.content);
172
+ let checkpoint;
173
+ if (kv["checkpoint"]) {
174
+ const cp = kv["checkpoint"].toLowerCase();
175
+ if (cp === "decision" || cp === "approval") {
176
+ checkpoint = cp;
177
+ }
178
+ }
179
+ return {
180
+ index: i + 1,
181
+ name: stationName,
182
+ agentName: kv["agent"] || "default",
183
+ task: kv["task"] || section.content,
184
+ skillName: kv["skill"],
185
+ model: kv["model"],
186
+ retry: kv["retry"] ? parseInt(kv["retry"], 10) : void 0,
187
+ condition: kv["condition"],
188
+ checkpoint,
189
+ checkpointDescription: kv["checkpoint_description"]
190
+ };
191
+ });
192
+ }
193
+ async function loadFile(path) {
194
+ return readFile(path, "utf-8");
195
+ }
196
+ async function loadPWP(dir) {
197
+ const content = await loadFile(join(dir, "PWP.md"));
198
+ return parsePWP(content);
199
+ }
200
+ async function loadAgent(dir, name) {
201
+ const content = await loadFile(join(dir, "agents", `${name}.agent.md`));
202
+ return parseAgent(content);
203
+ }
204
+ async function loadSkill(dir, name) {
205
+ const content = await loadFile(join(dir, "skills", `${name}.skill.md`));
206
+ return parseSkill(content);
207
+ }
208
+ async function loadLine(dir, name) {
209
+ const content = await loadFile(join(dir, "lines", `${name}.line.md`));
210
+ return parseProductionLine(content);
211
+ }
212
+ async function listLines(dir) {
213
+ const linesDir = join(dir, "lines");
214
+ try {
215
+ const files = await readdir(linesDir);
216
+ const lines = [];
217
+ for (const file of files) {
218
+ if (file.endsWith(".line.md")) {
219
+ const content = await loadFile(join(linesDir, file));
220
+ const line = parseProductionLine(content);
221
+ lines.push({
222
+ name: line.name,
223
+ description: line.description,
224
+ icon: line.icon
225
+ });
226
+ }
227
+ }
228
+ return lines;
229
+ } catch {
230
+ return [];
231
+ }
232
+ }
233
+
234
+ export {
235
+ parsePWP,
236
+ parseAgent,
237
+ parseSkill,
238
+ parseProductionLine,
239
+ loadFile,
240
+ loadPWP,
241
+ loadAgent,
242
+ loadSkill,
243
+ loadLine,
244
+ listLines
245
+ };
@@ -0,0 +1,66 @@
1
+ import {
2
+ HYPERCORE_DIR
3
+ } from "./chunk-V5UHPPSY.js";
4
+
5
+ // src/team/auth.ts
6
+ import { randomBytes } from "crypto";
7
+ import { readFile, writeFile, mkdir } from "fs/promises";
8
+ import { existsSync } from "fs";
9
+ import { join } from "path";
10
+ function generateId() {
11
+ return randomBytes(4).toString("hex");
12
+ }
13
+ function generateToken() {
14
+ return randomBytes(8).toString("hex");
15
+ }
16
+ function generateJoinCode() {
17
+ const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
18
+ let code = "";
19
+ const bytes = randomBytes(6);
20
+ for (let i = 0; i < 6; i++) {
21
+ code += chars[bytes[i] % chars.length];
22
+ }
23
+ return code;
24
+ }
25
+ function validateToken(team, token) {
26
+ return team.members.find((m) => m.token === token) || null;
27
+ }
28
+ function isOwner(member) {
29
+ return member.role === "owner";
30
+ }
31
+ var TOKEN_FILE = "team-token.json";
32
+ function getTokenPath() {
33
+ return join(HYPERCORE_DIR, TOKEN_FILE);
34
+ }
35
+ async function saveLocalToken(token) {
36
+ await mkdir(HYPERCORE_DIR, { recursive: true });
37
+ await writeFile(getTokenPath(), JSON.stringify(token, null, 2), "utf-8");
38
+ }
39
+ async function loadLocalToken() {
40
+ const p = getTokenPath();
41
+ if (!existsSync(p)) return null;
42
+ try {
43
+ const data = await readFile(p, "utf-8");
44
+ return JSON.parse(data);
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ async function clearLocalToken() {
50
+ const p = getTokenPath();
51
+ if (existsSync(p)) {
52
+ const { unlink } = await import("fs/promises");
53
+ await unlink(p);
54
+ }
55
+ }
56
+
57
+ export {
58
+ generateId,
59
+ generateToken,
60
+ generateJoinCode,
61
+ validateToken,
62
+ isOwner,
63
+ saveLocalToken,
64
+ loadLocalToken,
65
+ clearLocalToken
66
+ };
@@ -0,0 +1,134 @@
1
+ import {
2
+ HYPERCORE_DIR
3
+ } from "./chunk-V5UHPPSY.js";
4
+
5
+ // src/repl/session.ts
6
+ import { mkdir, writeFile, readFile, readdir, rename } from "fs/promises";
7
+ import { join } from "path";
8
+ import { existsSync } from "fs";
9
+ var SESSIONS_DIR = join(HYPERCORE_DIR, "sessions");
10
+ function generateSessionId() {
11
+ const now = /* @__PURE__ */ new Date();
12
+ const date = now.toISOString().split("T")[0];
13
+ const time = now.toTimeString().split(" ")[0].replace(/:/g, "");
14
+ return `${date}-${time}`;
15
+ }
16
+ async function saveSession(sessionId, messages, name) {
17
+ await mkdir(SESSIONS_DIR, { recursive: true });
18
+ const data = {
19
+ id: sessionId,
20
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
21
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22
+ messages
23
+ };
24
+ const filePath = join(SESSIONS_DIR, `${sessionId}.json`);
25
+ if (existsSync(filePath)) {
26
+ try {
27
+ const existing = JSON.parse(await readFile(filePath, "utf-8"));
28
+ data.createdAt = existing.createdAt;
29
+ if (!name && existing.name) data.name = existing.name;
30
+ } catch {
31
+ }
32
+ }
33
+ if (name) data.name = name;
34
+ await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
35
+ }
36
+ async function loadSession(sessionId) {
37
+ const filePath = join(SESSIONS_DIR, `${sessionId}.json`);
38
+ if (!existsSync(filePath)) {
39
+ throw new Error(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728`);
40
+ }
41
+ const data = JSON.parse(await readFile(filePath, "utf-8"));
42
+ return data.messages;
43
+ }
44
+ async function listSessions(limit = 10) {
45
+ if (!existsSync(SESSIONS_DIR)) return [];
46
+ const files = await readdir(SESSIONS_DIR);
47
+ const sessions = [];
48
+ for (const file of files) {
49
+ if (!file.endsWith(".json")) continue;
50
+ try {
51
+ const data = JSON.parse(await readFile(join(SESSIONS_DIR, file), "utf-8"));
52
+ sessions.push({
53
+ id: data.id,
54
+ name: data.name,
55
+ updatedAt: data.updatedAt,
56
+ messageCount: data.messages.length
57
+ });
58
+ } catch {
59
+ }
60
+ }
61
+ sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
62
+ return sessions.slice(0, limit);
63
+ }
64
+ async function renameSession(oldId, newId) {
65
+ const oldPath = join(SESSIONS_DIR, `${oldId}.json`);
66
+ const newPath = join(SESSIONS_DIR, `${newId}.json`);
67
+ if (!existsSync(oldPath)) throw new Error(`\u4F1A\u8BDD ${oldId} \u4E0D\u5B58\u5728`);
68
+ if (existsSync(newPath)) throw new Error(`\u4F1A\u8BDD ${newId} \u5DF2\u5B58\u5728`);
69
+ const data = JSON.parse(await readFile(oldPath, "utf-8"));
70
+ data.id = newId;
71
+ data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
72
+ await writeFile(newPath, JSON.stringify(data, null, 2), "utf-8");
73
+ await rename(oldPath, join(SESSIONS_DIR, `${oldId}.json.bak`)).catch(() => {
74
+ });
75
+ const { unlink } = await import("fs/promises");
76
+ await unlink(join(SESSIONS_DIR, `${oldId}.json.bak`)).catch(() => {
77
+ });
78
+ }
79
+ async function forkSession(sourceId) {
80
+ const sourcePath = join(SESSIONS_DIR, `${sourceId}.json`);
81
+ if (!existsSync(sourcePath)) throw new Error(`\u4F1A\u8BDD ${sourceId} \u4E0D\u5B58\u5728`);
82
+ const newId = generateSessionId() + "-fork";
83
+ const newPath = join(SESSIONS_DIR, `${newId}.json`);
84
+ const data = JSON.parse(await readFile(sourcePath, "utf-8"));
85
+ data.id = newId;
86
+ data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
87
+ await writeFile(newPath, JSON.stringify(data, null, 2), "utf-8");
88
+ return newId;
89
+ }
90
+ async function exportSession(sessionId, outputPath) {
91
+ const filePath = join(SESSIONS_DIR, `${sessionId}.json`);
92
+ if (!existsSync(filePath)) throw new Error(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728`);
93
+ const data = JSON.parse(await readFile(filePath, "utf-8"));
94
+ const lines = [
95
+ `# \u4F1A\u8BDD: ${data.id}`,
96
+ ``,
97
+ `- \u521B\u5EFA: ${data.createdAt}`,
98
+ `- \u66F4\u65B0: ${data.updatedAt}`,
99
+ `- \u6D88\u606F\u6570: ${data.messages.length}`,
100
+ ``,
101
+ `---`,
102
+ ``
103
+ ];
104
+ for (const msg of data.messages) {
105
+ const role = msg.role === "user" ? "\u{1F464} \u7528\u6237" : "\u{1F916} AI";
106
+ lines.push(`## ${role}
107
+ `);
108
+ lines.push(msg.content);
109
+ lines.push("");
110
+ lines.push("---");
111
+ lines.push("");
112
+ }
113
+ const mdContent = lines.join("\n");
114
+ const dest = outputPath || join(process.cwd(), `session-${sessionId}.md`);
115
+ await writeFile(dest, mdContent, "utf-8");
116
+ return dest;
117
+ }
118
+ async function deleteSession(sessionId) {
119
+ const filePath = join(SESSIONS_DIR, `${sessionId}.json`);
120
+ if (!existsSync(filePath)) throw new Error(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728`);
121
+ const { unlink } = await import("fs/promises");
122
+ await unlink(filePath);
123
+ }
124
+
125
+ export {
126
+ generateSessionId,
127
+ saveSession,
128
+ loadSession,
129
+ listSessions,
130
+ renameSession,
131
+ forkSession,
132
+ exportSession,
133
+ deleteSession
134
+ };