xling 0.0.2 → 0.3.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 (122) hide show
  1. package/README.md +276 -1
  2. package/dist/base-BWvHbkly.js +69 -0
  3. package/dist/base-BWvHbkly.js.map +1 -0
  4. package/dist/base-BXJVsMwL.d.ts +45 -0
  5. package/dist/base-BXJVsMwL.d.ts.map +1 -0
  6. package/dist/base-DdIJJBHV.js +34 -0
  7. package/dist/base-DdIJJBHV.js.map +1 -0
  8. package/dist/base-EQ6uvBQs.d.ts +43 -0
  9. package/dist/base-EQ6uvBQs.d.ts.map +1 -0
  10. package/dist/claude-BsdlWM7z.js +31 -0
  11. package/dist/claude-BsdlWM7z.js.map +1 -0
  12. package/dist/claude-D7KdpYHQ.js +209 -0
  13. package/dist/claude-D7KdpYHQ.js.map +1 -0
  14. package/dist/claudeDefault-pd-Kyu6o.js +19 -0
  15. package/dist/claudeDefault-pd-Kyu6o.js.map +1 -0
  16. package/dist/codex-Crifr9cw.js +32 -0
  17. package/dist/codex-Crifr9cw.js.map +1 -0
  18. package/dist/codex-UJ2PYHA6.js +85 -0
  19. package/dist/codex-UJ2PYHA6.js.map +1 -0
  20. package/dist/commands/settings/get.d.ts +24 -0
  21. package/dist/commands/settings/get.d.ts.map +1 -0
  22. package/dist/commands/settings/get.js +118 -0
  23. package/dist/commands/settings/get.js.map +1 -0
  24. package/dist/commands/settings/inspect.d.ts +19 -0
  25. package/dist/commands/settings/inspect.d.ts.map +1 -0
  26. package/dist/commands/settings/inspect.js +88 -0
  27. package/dist/commands/settings/inspect.js.map +1 -0
  28. package/dist/commands/settings/list.d.ts +25 -0
  29. package/dist/commands/settings/list.d.ts.map +1 -0
  30. package/dist/commands/settings/list.js +147 -0
  31. package/dist/commands/settings/list.js.map +1 -0
  32. package/dist/commands/settings/set.d.ts +22 -0
  33. package/dist/commands/settings/set.d.ts.map +1 -0
  34. package/dist/commands/settings/set.js +83 -0
  35. package/dist/commands/settings/set.js.map +1 -0
  36. package/dist/commands/settings/switch.d.ts +27 -0
  37. package/dist/commands/settings/switch.d.ts.map +1 -0
  38. package/dist/commands/settings/switch.js +164 -0
  39. package/dist/commands/settings/switch.js.map +1 -0
  40. package/dist/commands/x/index.d.ts +25 -0
  41. package/dist/commands/x/index.d.ts.map +1 -0
  42. package/dist/commands/x/index.js +130 -0
  43. package/dist/commands/x/index.js.map +1 -0
  44. package/dist/dispatcher-BUU7wUgm.js +94 -0
  45. package/dist/dispatcher-BUU7wUgm.js.map +1 -0
  46. package/dist/dispatcher-Co94YvDc.js +76 -0
  47. package/dist/dispatcher-Co94YvDc.js.map +1 -0
  48. package/dist/domain/interfaces.d.ts +3 -0
  49. package/dist/domain/interfaces.js +1 -0
  50. package/dist/domain/types.d.ts +2 -0
  51. package/dist/domain/types.js +1 -0
  52. package/dist/domain/validators.d.ts +82 -0
  53. package/dist/domain/validators.d.ts.map +1 -0
  54. package/dist/domain/validators.js +58 -0
  55. package/dist/domain/validators.js.map +1 -0
  56. package/dist/editor-D4qoje1V.js +32 -0
  57. package/dist/editor-D4qoje1V.js.map +1 -0
  58. package/dist/errors-CAZ5k5YT.js +89 -0
  59. package/dist/errors-CAZ5k5YT.js.map +1 -0
  60. package/dist/format-Cqecj3RS.js +229 -0
  61. package/dist/format-Cqecj3RS.js.map +1 -0
  62. package/dist/fsStore-BPnFUGta.js +122 -0
  63. package/dist/fsStore-BPnFUGta.js.map +1 -0
  64. package/dist/gemini-Qo5146d_.js +52 -0
  65. package/dist/gemini-Qo5146d_.js.map +1 -0
  66. package/dist/interfaces-DRNAGN0l.d.ts +75 -0
  67. package/dist/interfaces-DRNAGN0l.d.ts.map +1 -0
  68. package/dist/run.d.ts +1 -0
  69. package/dist/run.js +15 -0
  70. package/dist/run.js.map +1 -0
  71. package/dist/runner-BE7zZq1g.js +89 -0
  72. package/dist/runner-BE7zZq1g.js.map +1 -0
  73. package/dist/services/launch/adapters/base.d.ts +4 -0
  74. package/dist/services/launch/adapters/base.js +4 -0
  75. package/dist/services/launch/adapters/claude.d.ts +25 -0
  76. package/dist/services/launch/adapters/claude.d.ts.map +1 -0
  77. package/dist/services/launch/adapters/claude.js +5 -0
  78. package/dist/services/launch/adapters/codex.d.ts +26 -0
  79. package/dist/services/launch/adapters/codex.d.ts.map +1 -0
  80. package/dist/services/launch/adapters/codex.js +5 -0
  81. package/dist/services/launch/dispatcher.d.ts +44 -0
  82. package/dist/services/launch/dispatcher.d.ts.map +1 -0
  83. package/dist/services/launch/dispatcher.js +8 -0
  84. package/dist/services/settings/adapters/base.d.ts +4 -0
  85. package/dist/services/settings/adapters/base.js +5 -0
  86. package/dist/services/settings/adapters/claude.d.ts +43 -0
  87. package/dist/services/settings/adapters/claude.d.ts.map +1 -0
  88. package/dist/services/settings/adapters/claude.js +9 -0
  89. package/dist/services/settings/adapters/codex.d.ts +45 -0
  90. package/dist/services/settings/adapters/codex.d.ts.map +1 -0
  91. package/dist/services/settings/adapters/codex.js +6 -0
  92. package/dist/services/settings/adapters/gemini.d.ts +32 -0
  93. package/dist/services/settings/adapters/gemini.d.ts.map +1 -0
  94. package/dist/services/settings/adapters/gemini.js +6 -0
  95. package/dist/services/settings/dispatcher.d.ts +36 -0
  96. package/dist/services/settings/dispatcher.d.ts.map +1 -0
  97. package/dist/services/settings/dispatcher.js +12 -0
  98. package/dist/services/settings/fsStore.d.ts +46 -0
  99. package/dist/services/settings/fsStore.d.ts.map +1 -0
  100. package/dist/services/settings/fsStore.js +4 -0
  101. package/dist/services/settings/templates/claudeDefault.d.ts +20 -0
  102. package/dist/services/settings/templates/claudeDefault.d.ts.map +1 -0
  103. package/dist/services/settings/templates/claudeDefault.js +3 -0
  104. package/dist/types--0tjriPy.d.ts +104 -0
  105. package/dist/types--0tjriPy.d.ts.map +1 -0
  106. package/dist/utils/editor.d.ts +6 -0
  107. package/dist/utils/editor.d.ts.map +1 -0
  108. package/dist/utils/editor.js +4 -0
  109. package/dist/utils/errors.d.ts +61 -0
  110. package/dist/utils/errors.d.ts.map +1 -0
  111. package/dist/utils/errors.js +3 -0
  112. package/dist/utils/format.d.ts +20 -0
  113. package/dist/utils/format.d.ts.map +1 -0
  114. package/dist/utils/format.js +3 -0
  115. package/dist/utils/logger.d.ts +32 -0
  116. package/dist/utils/logger.d.ts.map +1 -0
  117. package/dist/utils/logger.js +46 -0
  118. package/dist/utils/logger.js.map +1 -0
  119. package/dist/utils/runner.d.ts +36 -0
  120. package/dist/utils/runner.d.ts.map +1 -0
  121. package/dist/utils/runner.js +3 -0
  122. package/package.json +66 -6
@@ -0,0 +1,229 @@
1
+ import Table from "cli-table3";
2
+
3
+ //#region src/utils/format.ts
4
+ /**
5
+ * 输出格式化工具
6
+ */
7
+ /**
8
+ * 格式化为 JSON 字符串
9
+ */
10
+ function formatJson(data, pretty = true) {
11
+ return JSON.stringify(data, null, pretty ? 2 : 0);
12
+ }
13
+ /**
14
+ * 格式化为表格
15
+ */
16
+ function formatTable(data) {
17
+ const table = new Table({
18
+ head: ["Key", "Value"],
19
+ colWidths: [30, 50],
20
+ wordWrap: true
21
+ });
22
+ for (const [key, value] of Object.entries(data)) table.push([key, formatValue(value)]);
23
+ return table.toString();
24
+ }
25
+ /**
26
+ * 格式化 settings 文件清单
27
+ */
28
+ function formatFilesTable(files) {
29
+ const table = new Table({
30
+ head: [
31
+ "Variant",
32
+ "File",
33
+ "Status",
34
+ "Size",
35
+ "Updated"
36
+ ],
37
+ colWidths: [
38
+ 15,
39
+ 40,
40
+ 12,
41
+ 12,
42
+ 26
43
+ ],
44
+ wordWrap: true
45
+ });
46
+ for (const file of files) table.push([
47
+ file.variant,
48
+ file.path,
49
+ formatStatus(file),
50
+ formatBytes(file.size),
51
+ formatTimestamp(file.lastModified)
52
+ ]);
53
+ return table.toString();
54
+ }
55
+ function formatDiff(currentValue, nextValue) {
56
+ const current = JSON.stringify(currentValue ?? {}, null, 2);
57
+ const next = JSON.stringify(nextValue ?? {}, null, 2);
58
+ if (current === next) return null;
59
+ const hunks = buildUnifiedHunks(buildDiffParts(current.split("\n"), next.split("\n")));
60
+ const lines = ["--- current", "+++ variant"];
61
+ for (const hunk of hunks) {
62
+ lines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
63
+ lines.push(...hunk.lines);
64
+ }
65
+ return lines.map(colorizeDiffLine).join("\n");
66
+ }
67
+ /**
68
+ * 格式化单个值
69
+ */
70
+ function formatValue(value) {
71
+ if (value === null) return "null";
72
+ if (value === void 0) return "undefined";
73
+ if (typeof value === "string") return value;
74
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
75
+ if (Array.isArray(value)) return JSON.stringify(value);
76
+ if (typeof value === "object") return JSON.stringify(value, null, 2);
77
+ return String(value);
78
+ }
79
+ function formatStatus(file) {
80
+ if (file.active) return file.exists ? "active" : "missing";
81
+ return file.exists ? "available" : "missing";
82
+ }
83
+ function formatBytes(size) {
84
+ if (typeof size !== "number") return "-";
85
+ const units = [
86
+ "B",
87
+ "KB",
88
+ "MB",
89
+ "GB"
90
+ ];
91
+ let value = size;
92
+ let unitIndex = 0;
93
+ while (value >= 1024 && unitIndex < units.length - 1) {
94
+ value /= 1024;
95
+ unitIndex++;
96
+ }
97
+ return `${value >= 10 || value % 1 === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`;
98
+ }
99
+ function formatTimestamp(date) {
100
+ if (!date) return "-";
101
+ return new Intl.DateTimeFormat(void 0, {
102
+ dateStyle: "medium",
103
+ timeStyle: "short"
104
+ }).format(date);
105
+ }
106
+ const ANSI = {
107
+ reset: "\x1B[0m",
108
+ red: "\x1B[31m",
109
+ green: "\x1B[32m",
110
+ yellow: "\x1B[33m",
111
+ cyan: "\x1B[36m",
112
+ dim: "\x1B[90m",
113
+ bold: "\x1B[1m"
114
+ };
115
+ function supportsColor() {
116
+ if (process.env.NO_COLOR) return false;
117
+ if (process.env.FORCE_COLOR === "0") return false;
118
+ return Boolean(process.stdout?.isTTY);
119
+ }
120
+ function colorizeDiffLine(line) {
121
+ if (!supportsColor()) return line;
122
+ if (line.startsWith("@@")) return `${ANSI.cyan}${ANSI.bold}${line}${ANSI.reset}`;
123
+ if (line.startsWith("---") || line.startsWith("+++")) return `${ANSI.yellow}${line}${ANSI.reset}`;
124
+ if (line.startsWith("+")) return `${ANSI.green}${line}${ANSI.reset}`;
125
+ if (line.startsWith("-")) return `${ANSI.red}${line}${ANSI.reset}`;
126
+ if (line.startsWith(" ")) return `${ANSI.dim}${line}${ANSI.reset}`;
127
+ return line;
128
+ }
129
+ function buildDiffParts(oldLines, newLines) {
130
+ const m = oldLines.length;
131
+ const n = newLines.length;
132
+ const dp = Array.from({ length: m + 1 }, () => Array.from({ length: n + 1 }, () => 0));
133
+ for (let i$1 = m - 1; i$1 >= 0; i$1--) for (let j$1 = n - 1; j$1 >= 0; j$1--) if (oldLines[i$1] === newLines[j$1]) dp[i$1][j$1] = dp[i$1 + 1][j$1 + 1] + 1;
134
+ else dp[i$1][j$1] = Math.max(dp[i$1 + 1][j$1], dp[i$1][j$1 + 1]);
135
+ const parts = [];
136
+ const push = (type, value) => {
137
+ const last = parts[parts.length - 1];
138
+ if (last && last.type === type) last.lines.push(value);
139
+ else parts.push({
140
+ type,
141
+ lines: [value]
142
+ });
143
+ };
144
+ let i = 0;
145
+ let j = 0;
146
+ while (i < m && j < n) if (oldLines[i] === newLines[j]) {
147
+ push("equal", oldLines[i]);
148
+ i++;
149
+ j++;
150
+ } else if (dp[i + 1][j] >= dp[i][j + 1]) {
151
+ push("delete", oldLines[i]);
152
+ i++;
153
+ } else {
154
+ push("insert", newLines[j]);
155
+ j++;
156
+ }
157
+ while (i < m) push("delete", oldLines[i++]);
158
+ while (j < n) push("insert", newLines[j++]);
159
+ return parts;
160
+ }
161
+ function buildUnifiedHunks(parts) {
162
+ const context = 3;
163
+ const hunks = [];
164
+ let oldLine = 1;
165
+ let newLine = 1;
166
+ let currentHunk = null;
167
+ let contextBuffer = [];
168
+ const startHunk = () => {
169
+ currentHunk = {
170
+ oldStart: Math.max(oldLine - contextBuffer.length, 1),
171
+ newStart: Math.max(newLine - contextBuffer.length, 1),
172
+ oldLines: contextBuffer.length,
173
+ newLines: contextBuffer.length,
174
+ lines: contextBuffer.map((line) => ` ${line}`)
175
+ };
176
+ contextBuffer = [];
177
+ };
178
+ const closeHunk = () => {
179
+ if (currentHunk) {
180
+ hunks.push(currentHunk);
181
+ currentHunk = null;
182
+ }
183
+ };
184
+ for (const part of parts) {
185
+ if (part.type === "equal") {
186
+ if (currentHunk !== null) {
187
+ const active$1 = currentHunk;
188
+ const leading = part.lines.slice(0, context);
189
+ for (const line of leading) {
190
+ active$1.lines.push(` ${line}`);
191
+ active$1.oldLines++;
192
+ active$1.newLines++;
193
+ }
194
+ oldLine += leading.length;
195
+ newLine += leading.length;
196
+ if (part.lines.length > context) {
197
+ closeHunk();
198
+ contextBuffer = part.lines.slice(part.lines.length - context);
199
+ oldLine += part.lines.length - leading.length;
200
+ newLine += part.lines.length - leading.length;
201
+ } else contextBuffer = [...leading].slice(-context);
202
+ } else {
203
+ contextBuffer.push(...part.lines);
204
+ if (contextBuffer.length > context) contextBuffer.splice(0, contextBuffer.length - context);
205
+ oldLine += part.lines.length;
206
+ newLine += part.lines.length;
207
+ }
208
+ continue;
209
+ }
210
+ if (!currentHunk) startHunk();
211
+ if (!currentHunk) throw new Error("Failed to initialize diff hunk");
212
+ const active = currentHunk;
213
+ for (const line of part.lines) if (part.type === "delete") {
214
+ active.lines.push(`-${line}`);
215
+ active.oldLines++;
216
+ oldLine++;
217
+ } else {
218
+ active.lines.push(`+${line}`);
219
+ active.newLines++;
220
+ newLine++;
221
+ }
222
+ }
223
+ closeHunk();
224
+ return hunks;
225
+ }
226
+
227
+ //#endregion
228
+ export { formatTable as i, formatFilesTable as n, formatJson as r, formatDiff as t };
229
+ //# sourceMappingURL=format-Cqecj3RS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-Cqecj3RS.js","names":["lines: string[]","dp: number[][]","i","j","parts: DiffPart[]","hunks: Hunk[]","currentHunk: Hunk | null","contextBuffer: string[]","active: Hunk"],"sources":["../src/utils/format.ts"],"sourcesContent":["/**\n * 输出格式化工具\n */\n\nimport Table from \"cli-table3\";\nimport type { SettingsFileEntry } from \"@/domain/types.ts\";\n\n/**\n * 格式化为 JSON 字符串\n */\nexport function formatJson(data: unknown, pretty = true): string {\n return JSON.stringify(data, null, pretty ? 2 : 0);\n}\n\n/**\n * 格式化为表格\n */\nexport function formatTable(data: Record<string, unknown>): string {\n const table = new Table({\n head: [\"Key\", \"Value\"],\n colWidths: [30, 50],\n wordWrap: true,\n });\n\n for (const [key, value] of Object.entries(data)) {\n table.push([key, formatValue(value)]);\n }\n\n return table.toString();\n}\n\n/**\n * 格式化 settings 文件清单\n */\nexport function formatFilesTable(files: SettingsFileEntry[]): string {\n const table = new Table({\n head: [\"Variant\", \"File\", \"Status\", \"Size\", \"Updated\"],\n colWidths: [15, 40, 12, 12, 26],\n wordWrap: true,\n });\n\n for (const file of files) {\n table.push([\n file.variant,\n file.path,\n formatStatus(file),\n formatBytes(file.size),\n formatTimestamp(file.lastModified),\n ]);\n }\n\n return table.toString();\n}\n\nexport function formatDiff(\n currentValue: unknown,\n nextValue: unknown,\n): string | null {\n const current = JSON.stringify(currentValue ?? {}, null, 2);\n const next = JSON.stringify(nextValue ?? {}, null, 2);\n\n if (current === next) {\n return null;\n }\n\n const parts = buildDiffParts(current.split(\"\\n\"), next.split(\"\\n\"));\n const hunks = buildUnifiedHunks(parts);\n\n const lines: string[] = [\"--- current\", \"+++ variant\"];\n for (const hunk of hunks) {\n lines.push(\n `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`,\n );\n lines.push(...hunk.lines);\n }\n\n return lines.map(colorizeDiffLine).join(\"\\n\");\n}\n\n/**\n * 格式化单个值\n */\nfunction formatValue(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\")\n return String(value);\n if (Array.isArray(value)) return JSON.stringify(value);\n if (typeof value === \"object\") return JSON.stringify(value, null, 2);\n return String(value);\n}\n\nfunction formatStatus(file: SettingsFileEntry): string {\n if (file.active) {\n return file.exists ? \"active\" : \"missing\";\n }\n return file.exists ? \"available\" : \"missing\";\n}\n\nfunction formatBytes(size?: number): string {\n if (typeof size !== \"number\") {\n return \"-\";\n }\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let value = size;\n let unitIndex = 0;\n\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n\n const formatted =\n value >= 10 || value % 1 === 0 ? value.toFixed(0) : value.toFixed(1);\n return `${formatted} ${units[unitIndex]}`;\n}\n\nfunction formatTimestamp(date?: Date): string {\n if (!date) {\n return \"-\";\n }\n\n return new Intl.DateTimeFormat(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n }).format(date);\n}\n\nconst ANSI = {\n reset: \"\\x1b[0m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n cyan: \"\\x1b[36m\",\n dim: \"\\x1b[90m\",\n bold: \"\\x1b[1m\",\n};\n\nfunction supportsColor(): boolean {\n if (process.env.NO_COLOR) return false;\n if (process.env.FORCE_COLOR === \"0\") return false;\n return Boolean(process.stdout?.isTTY);\n}\n\nfunction colorizeDiffLine(line: string): string {\n if (!supportsColor()) {\n return line;\n }\n\n if (line.startsWith(\"@@\")) {\n return `${ANSI.cyan}${ANSI.bold}${line}${ANSI.reset}`;\n }\n if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n return `${ANSI.yellow}${line}${ANSI.reset}`;\n }\n if (line.startsWith(\"+\")) {\n return `${ANSI.green}${line}${ANSI.reset}`;\n }\n if (line.startsWith(\"-\")) {\n return `${ANSI.red}${line}${ANSI.reset}`;\n }\n if (line.startsWith(\" \")) {\n return `${ANSI.dim}${line}${ANSI.reset}`;\n }\n return line;\n}\n\ntype DiffPart = {\n type: \"equal\" | \"insert\" | \"delete\";\n lines: string[];\n};\n\ntype Hunk = {\n oldStart: number;\n newStart: number;\n oldLines: number;\n newLines: number;\n lines: string[];\n};\n\nfunction buildDiffParts(oldLines: string[], newLines: string[]): DiffPart[] {\n const m = oldLines.length;\n const n = newLines.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array.from({ length: n + 1 }, () => 0),\n );\n\n for (let i = m - 1; i >= 0; i--) {\n for (let j = n - 1; j >= 0; j--) {\n if (oldLines[i] === newLines[j]) {\n dp[i][j] = dp[i + 1][j + 1] + 1;\n } else {\n dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);\n }\n }\n }\n\n const parts: DiffPart[] = [];\n const push = (type: DiffPart[\"type\"], value: string) => {\n const last = parts[parts.length - 1];\n if (last && last.type === type) {\n last.lines.push(value);\n } else {\n parts.push({ type, lines: [value] });\n }\n };\n\n let i = 0;\n let j = 0;\n\n while (i < m && j < n) {\n if (oldLines[i] === newLines[j]) {\n push(\"equal\", oldLines[i]);\n i++;\n j++;\n } else if (dp[i + 1][j] >= dp[i][j + 1]) {\n push(\"delete\", oldLines[i]);\n i++;\n } else {\n push(\"insert\", newLines[j]);\n j++;\n }\n }\n\n while (i < m) {\n push(\"delete\", oldLines[i++]);\n }\n while (j < n) {\n push(\"insert\", newLines[j++]);\n }\n\n return parts;\n}\n\nfunction buildUnifiedHunks(parts: DiffPart[]): Hunk[] {\n const context = 3;\n const hunks: Hunk[] = [];\n let oldLine = 1;\n let newLine = 1;\n let currentHunk: Hunk | null = null;\n let contextBuffer: string[] = [];\n\n const startHunk = () => {\n const oldStart = Math.max(oldLine - contextBuffer.length, 1);\n const newStart = Math.max(newLine - contextBuffer.length, 1);\n currentHunk = {\n oldStart,\n newStart,\n oldLines: contextBuffer.length,\n newLines: contextBuffer.length,\n lines: contextBuffer.map((line) => ` ${line}`),\n };\n contextBuffer = [];\n };\n\n const closeHunk = () => {\n if (currentHunk) {\n hunks.push(currentHunk);\n currentHunk = null;\n }\n };\n\n for (const part of parts) {\n if (part.type === \"equal\") {\n if (currentHunk !== null) {\n const active: Hunk = currentHunk;\n const leading = part.lines.slice(0, context);\n for (const line of leading) {\n active.lines.push(` ${line}`);\n active.oldLines++;\n active.newLines++;\n }\n oldLine += leading.length;\n newLine += leading.length;\n\n if (part.lines.length > context) {\n closeHunk();\n contextBuffer = part.lines.slice(part.lines.length - context);\n oldLine += part.lines.length - leading.length;\n newLine += part.lines.length - leading.length;\n } else {\n contextBuffer = [...leading].slice(-context);\n }\n } else {\n contextBuffer.push(...part.lines);\n if (contextBuffer.length > context) {\n contextBuffer.splice(0, contextBuffer.length - context);\n }\n oldLine += part.lines.length;\n newLine += part.lines.length;\n }\n continue;\n }\n\n if (!currentHunk) {\n startHunk();\n }\n\n if (!currentHunk) {\n throw new Error(\"Failed to initialize diff hunk\");\n }\n\n const active: Hunk = currentHunk;\n for (const line of part.lines) {\n if (part.type === \"delete\") {\n active.lines.push(`-${line}`);\n active.oldLines++;\n oldLine++;\n } else {\n active.lines.push(`+${line}`);\n active.newLines++;\n newLine++;\n }\n }\n }\n\n closeHunk();\n return hunks;\n}\n"],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { i as FileWriteError, n as ConfigParseError, t as ConfigFileNotFoundError } from "./errors-CAZ5k5YT.js";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import * as toml from "@iarna/toml";
6
+
7
+ //#region src/services/settings/fsStore.ts
8
+ /**
9
+ * 文件系统存储工具
10
+ */
11
+ /**
12
+ * 解析 ~ 为用户主目录
13
+ */
14
+ function resolveHome(filepath) {
15
+ if (filepath.startsWith("~/") || filepath === "~") return path.join(os.homedir(), filepath.slice(1));
16
+ return filepath;
17
+ }
18
+ /**
19
+ * 确保目录存在
20
+ */
21
+ function ensureDir(dirPath) {
22
+ const resolvedPath = resolveHome(dirPath);
23
+ if (!fs.existsSync(resolvedPath)) fs.mkdirSync(resolvedPath, { recursive: true });
24
+ }
25
+ /**
26
+ * 读取 JSON 文件
27
+ */
28
+ function readJSON(filepath) {
29
+ const resolvedPath = resolveHome(filepath);
30
+ if (!fs.existsSync(resolvedPath)) throw new ConfigFileNotFoundError(resolvedPath);
31
+ try {
32
+ const content = fs.readFileSync(resolvedPath, "utf-8");
33
+ return JSON.parse(content);
34
+ } catch (error) {
35
+ throw new ConfigParseError(resolvedPath, error.message);
36
+ }
37
+ }
38
+ /**
39
+ * 写入 JSON 文件
40
+ */
41
+ function writeJSON(filepath, data, backup = true) {
42
+ const resolvedPath = resolveHome(filepath);
43
+ ensureDir(path.dirname(resolvedPath));
44
+ if (backup && fs.existsSync(resolvedPath)) {
45
+ const backupPath = `${resolvedPath}.bak`;
46
+ fs.copyFileSync(resolvedPath, backupPath);
47
+ }
48
+ try {
49
+ const tempPath = `${resolvedPath}.tmp`;
50
+ fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), "utf-8");
51
+ fs.renameSync(tempPath, resolvedPath);
52
+ } catch (error) {
53
+ throw new FileWriteError(resolvedPath, error.message);
54
+ }
55
+ }
56
+ /**
57
+ * 读取 TOML 文件
58
+ */
59
+ function readTOML(filepath) {
60
+ const resolvedPath = resolveHome(filepath);
61
+ if (!fs.existsSync(resolvedPath)) throw new ConfigFileNotFoundError(resolvedPath);
62
+ try {
63
+ const content = fs.readFileSync(resolvedPath, "utf-8");
64
+ return toml.parse(content);
65
+ } catch (error) {
66
+ throw new ConfigParseError(resolvedPath, error.message);
67
+ }
68
+ }
69
+ /**
70
+ * 写入 TOML 文件
71
+ */
72
+ function writeTOML(filepath, data, backup = true) {
73
+ const resolvedPath = resolveHome(filepath);
74
+ ensureDir(path.dirname(resolvedPath));
75
+ if (backup && fs.existsSync(resolvedPath)) {
76
+ const backupPath = `${resolvedPath}.bak`;
77
+ fs.copyFileSync(resolvedPath, backupPath);
78
+ }
79
+ try {
80
+ const tempPath = `${resolvedPath}.tmp`;
81
+ fs.writeFileSync(tempPath, toml.stringify(data), "utf-8");
82
+ fs.renameSync(tempPath, resolvedPath);
83
+ } catch (error) {
84
+ throw new FileWriteError(resolvedPath, error.message);
85
+ }
86
+ }
87
+ /**
88
+ * 深度合并对象
89
+ */
90
+ function deepMerge(target, source) {
91
+ const result = { ...target };
92
+ for (const key in source) if (Object.prototype.hasOwnProperty.call(source, key)) {
93
+ const sourceValue = source[key];
94
+ const targetValue = result[key];
95
+ if (typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
96
+ else result[key] = sourceValue;
97
+ }
98
+ return result;
99
+ }
100
+ /**
101
+ * 检查文件是否存在
102
+ */
103
+ function fileExists(filepath) {
104
+ const resolvedPath = resolveHome(filepath);
105
+ return fs.existsSync(resolvedPath);
106
+ }
107
+ /**
108
+ * 获取文件信息
109
+ */
110
+ function getFileInfo(filepath) {
111
+ const resolvedPath = resolveHome(filepath);
112
+ if (!fs.existsSync(resolvedPath)) return null;
113
+ const stats = fs.statSync(resolvedPath);
114
+ return {
115
+ size: stats.size,
116
+ lastModified: stats.mtime
117
+ };
118
+ }
119
+
120
+ //#endregion
121
+ export { readJSON as a, writeJSON as c, getFileInfo as i, writeTOML as l, ensureDir as n, readTOML as o, fileExists as r, resolveHome as s, deepMerge as t };
122
+ //# sourceMappingURL=fsStore-BPnFUGta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fsStore-BPnFUGta.js","names":[],"sources":["../src/services/settings/fsStore.ts"],"sourcesContent":["/**\n * 文件系统存储工具\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as os from \"os\";\nimport * as toml from \"@iarna/toml\";\nimport {\n ConfigFileNotFoundError,\n ConfigParseError,\n FileWriteError,\n} from \"@/utils/errors.ts\";\n\n/**\n * 解析 ~ 为用户主目录\n */\nexport function resolveHome(filepath: string): string {\n if (filepath.startsWith(\"~/\") || filepath === \"~\") {\n return path.join(os.homedir(), filepath.slice(1));\n }\n return filepath;\n}\n\n/**\n * 确保目录存在\n */\nexport function ensureDir(dirPath: string): void {\n const resolvedPath = resolveHome(dirPath);\n if (!fs.existsSync(resolvedPath)) {\n fs.mkdirSync(resolvedPath, { recursive: true });\n }\n}\n\n/**\n * 读取 JSON 文件\n */\nexport function readJSON(filepath: string): Record<string, unknown> {\n const resolvedPath = resolveHome(filepath);\n\n if (!fs.existsSync(resolvedPath)) {\n throw new ConfigFileNotFoundError(resolvedPath);\n }\n\n try {\n const content = fs.readFileSync(resolvedPath, \"utf-8\");\n return JSON.parse(content);\n } catch (error) {\n throw new ConfigParseError(resolvedPath, (error as Error).message);\n }\n}\n\n/**\n * 写入 JSON 文件\n */\nexport function writeJSON(\n filepath: string,\n data: Record<string, unknown>,\n backup = true,\n): void {\n const resolvedPath = resolveHome(filepath);\n\n // 确保目录存在\n ensureDir(path.dirname(resolvedPath));\n\n // 备份现有文件\n if (backup && fs.existsSync(resolvedPath)) {\n const backupPath = `${resolvedPath}.bak`;\n fs.copyFileSync(resolvedPath, backupPath);\n }\n\n try {\n // 原子写入:先写临时文件,再重命名\n const tempPath = `${resolvedPath}.tmp`;\n fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), \"utf-8\");\n fs.renameSync(tempPath, resolvedPath);\n } catch (error) {\n throw new FileWriteError(resolvedPath, (error as Error).message);\n }\n}\n\n/**\n * 读取 TOML 文件\n */\nexport function readTOML(filepath: string): Record<string, unknown> {\n const resolvedPath = resolveHome(filepath);\n\n if (!fs.existsSync(resolvedPath)) {\n throw new ConfigFileNotFoundError(resolvedPath);\n }\n\n try {\n const content = fs.readFileSync(resolvedPath, \"utf-8\");\n return toml.parse(content) as Record<string, unknown>;\n } catch (error) {\n throw new ConfigParseError(resolvedPath, (error as Error).message);\n }\n}\n\n/**\n * 写入 TOML 文件\n */\nexport function writeTOML(\n filepath: string,\n data: Record<string, unknown>,\n backup = true,\n): void {\n const resolvedPath = resolveHome(filepath);\n\n // 确保目录存在\n ensureDir(path.dirname(resolvedPath));\n\n // 备份现有文件\n if (backup && fs.existsSync(resolvedPath)) {\n const backupPath = `${resolvedPath}.bak`;\n fs.copyFileSync(resolvedPath, backupPath);\n }\n\n try {\n // 原子写入\n const tempPath = `${resolvedPath}.tmp`;\n fs.writeFileSync(tempPath, toml.stringify(data as any), \"utf-8\");\n fs.renameSync(tempPath, resolvedPath);\n } catch (error) {\n throw new FileWriteError(resolvedPath, (error as Error).message);\n }\n}\n\n/**\n * 深度合并对象\n */\nexport function deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...target };\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n const sourceValue = source[key];\n const targetValue = result[key];\n\n if (\n typeof sourceValue === \"object\" &&\n sourceValue !== null &&\n !Array.isArray(sourceValue) &&\n typeof targetValue === \"object\" &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n result[key] = deepMerge(\n targetValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>,\n );\n } else {\n result[key] = sourceValue;\n }\n }\n }\n\n return result;\n}\n\n/**\n * 检查文件是否存在\n */\nexport function fileExists(filepath: string): boolean {\n const resolvedPath = resolveHome(filepath);\n return fs.existsSync(resolvedPath);\n}\n\n/**\n * 获取文件信息\n */\nexport function getFileInfo(filepath: string) {\n const resolvedPath = resolveHome(filepath);\n if (!fs.existsSync(resolvedPath)) {\n return null;\n }\n\n const stats = fs.statSync(resolvedPath);\n return {\n size: stats.size,\n lastModified: stats.mtime,\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,SAAgB,YAAY,UAA0B;AACpD,KAAI,SAAS,WAAW,KAAK,IAAI,aAAa,IAC5C,QAAO,KAAK,KAAK,GAAG,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;AAEnD,QAAO;;;;;AAMT,SAAgB,UAAU,SAAuB;CAC/C,MAAM,eAAe,YAAY,QAAQ;AACzC,KAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,IAAG,UAAU,cAAc,EAAE,WAAW,MAAM,CAAC;;;;;AAOnD,SAAgB,SAAS,UAA2C;CAClE,MAAM,eAAe,YAAY,SAAS;AAE1C,KAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,OAAM,IAAI,wBAAwB,aAAa;AAGjD,KAAI;EACF,MAAM,UAAU,GAAG,aAAa,cAAc,QAAQ;AACtD,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,QAAM,IAAI,iBAAiB,cAAe,MAAgB,QAAQ;;;;;;AAOtE,SAAgB,UACd,UACA,MACA,SAAS,MACH;CACN,MAAM,eAAe,YAAY,SAAS;AAG1C,WAAU,KAAK,QAAQ,aAAa,CAAC;AAGrC,KAAI,UAAU,GAAG,WAAW,aAAa,EAAE;EACzC,MAAM,aAAa,GAAG,aAAa;AACnC,KAAG,aAAa,cAAc,WAAW;;AAG3C,KAAI;EAEF,MAAM,WAAW,GAAG,aAAa;AACjC,KAAG,cAAc,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,QAAQ;AAClE,KAAG,WAAW,UAAU,aAAa;UAC9B,OAAO;AACd,QAAM,IAAI,eAAe,cAAe,MAAgB,QAAQ;;;;;;AAOpE,SAAgB,SAAS,UAA2C;CAClE,MAAM,eAAe,YAAY,SAAS;AAE1C,KAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,OAAM,IAAI,wBAAwB,aAAa;AAGjD,KAAI;EACF,MAAM,UAAU,GAAG,aAAa,cAAc,QAAQ;AACtD,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,QAAM,IAAI,iBAAiB,cAAe,MAAgB,QAAQ;;;;;;AAOtE,SAAgB,UACd,UACA,MACA,SAAS,MACH;CACN,MAAM,eAAe,YAAY,SAAS;AAG1C,WAAU,KAAK,QAAQ,aAAa,CAAC;AAGrC,KAAI,UAAU,GAAG,WAAW,aAAa,EAAE;EACzC,MAAM,aAAa,GAAG,aAAa;AACnC,KAAG,aAAa,cAAc,WAAW;;AAG3C,KAAI;EAEF,MAAM,WAAW,GAAG,aAAa;AACjC,KAAG,cAAc,UAAU,KAAK,UAAU,KAAY,EAAE,QAAQ;AAChE,KAAG,WAAW,UAAU,aAAa;UAC9B,OAAO;AACd,QAAM,IAAI,eAAe,cAAe,MAAgB,QAAQ;;;;;;AAOpE,SAAgB,UACd,QACA,QACyB;CACzB,MAAM,SAAS,EAAE,GAAG,QAAQ;AAE5B,MAAK,MAAM,OAAO,OAChB,KAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,EAAE;EACrD,MAAM,cAAc,OAAO;EAC3B,MAAM,cAAc,OAAO;AAE3B,MACE,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,CAAC,MAAM,QAAQ,YAAY,IAC3B,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,CAAC,MAAM,QAAQ,YAAY,CAE3B,QAAO,OAAO,UACZ,aACA,YACD;MAED,QAAO,OAAO;;AAKpB,QAAO;;;;;AAMT,SAAgB,WAAW,UAA2B;CACpD,MAAM,eAAe,YAAY,SAAS;AAC1C,QAAO,GAAG,WAAW,aAAa;;;;;AAMpC,SAAgB,YAAY,UAAkB;CAC5C,MAAM,eAAe,YAAY,SAAS;AAC1C,KAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO;CAGT,MAAM,QAAQ,GAAG,SAAS,aAAa;AACvC,QAAO;EACL,MAAM,MAAM;EACZ,cAAc,MAAM;EACrB"}
@@ -0,0 +1,52 @@
1
+ import { t as BaseAdapter } from "./base-BWvHbkly.js";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+
5
+ //#region src/services/settings/adapters/gemini.ts
6
+ /**
7
+ * Gemini CLI 配置适配器
8
+ *
9
+ * 配置文件路径:
10
+ * - user: ~/.gemini/settings.json
11
+ * - project: <cwd>/.gemini/settings.json
12
+ * - system: 平台相关路径
13
+ */
14
+ var GeminiAdapter = class extends BaseAdapter {
15
+ toolId = "gemini";
16
+ /**
17
+ * 解析配置文件路径
18
+ */
19
+ resolvePath(scope) {
20
+ switch (scope) {
21
+ case "user": return "~/.gemini/settings.json";
22
+ case "project": return ".gemini/settings.json";
23
+ case "system": return this.getSystemConfigPath();
24
+ default: throw new Error(`Unsupported scope for Gemini: ${scope}`);
25
+ }
26
+ }
27
+ /**
28
+ * 验证 scope 是否有效
29
+ */
30
+ validateScope(scope) {
31
+ return [
32
+ "user",
33
+ "project",
34
+ "system"
35
+ ].includes(scope);
36
+ }
37
+ /**
38
+ * 获取系统级配置路径(跨平台)
39
+ */
40
+ getSystemConfigPath() {
41
+ switch (os.platform()) {
42
+ case "darwin": return "/Library/Application Support/Gemini/settings.json";
43
+ case "win32": return path.join(process.env.PROGRAMDATA || "C:\\ProgramData", "Gemini", "settings.json");
44
+ case "linux": return "/etc/gemini/settings.json";
45
+ default: return "/etc/gemini/settings.json";
46
+ }
47
+ }
48
+ };
49
+
50
+ //#endregion
51
+ export { GeminiAdapter as t };
52
+ //# sourceMappingURL=gemini-Qo5146d_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-Qo5146d_.js","names":[],"sources":["../src/services/settings/adapters/gemini.ts"],"sourcesContent":["/**\n * Gemini CLI 适配器\n */\n\nimport type { Scope } from \"@/domain/types.ts\";\nimport { BaseAdapter } from \"./base.ts\";\nimport * as os from \"os\";\nimport * as path from \"path\";\n\n/**\n * Gemini CLI 配置适配器\n *\n * 配置文件路径:\n * - user: ~/.gemini/settings.json\n * - project: <cwd>/.gemini/settings.json\n * - system: 平台相关路径\n */\nexport class GeminiAdapter extends BaseAdapter {\n readonly toolId = \"gemini\" as const;\n\n /**\n * 解析配置文件路径\n */\n resolvePath(scope: Scope): string {\n switch (scope) {\n case \"user\":\n return \"~/.gemini/settings.json\";\n case \"project\":\n return \".gemini/settings.json\";\n case \"system\":\n return this.getSystemConfigPath();\n default:\n throw new Error(`Unsupported scope for Gemini: ${scope}`);\n }\n }\n\n /**\n * 验证 scope 是否有效\n */\n validateScope(scope: Scope): boolean {\n return [\"user\", \"project\", \"system\"].includes(scope);\n }\n\n /**\n * 获取系统级配置路径(跨平台)\n */\n private getSystemConfigPath(): string {\n const platform = os.platform();\n\n switch (platform) {\n case \"darwin\": // macOS\n return \"/Library/Application Support/Gemini/settings.json\";\n case \"win32\": // Windows\n return path.join(\n process.env.PROGRAMDATA || \"C:\\\\ProgramData\",\n \"Gemini\",\n \"settings.json\",\n );\n case \"linux\":\n return \"/etc/gemini/settings.json\";\n default:\n return \"/etc/gemini/settings.json\";\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,IAAa,gBAAb,cAAmC,YAAY;CAC7C,AAAS,SAAS;;;;CAKlB,YAAY,OAAsB;AAChC,UAAQ,OAAR;GACE,KAAK,OACH,QAAO;GACT,KAAK,UACH,QAAO;GACT,KAAK,SACH,QAAO,KAAK,qBAAqB;GACnC,QACE,OAAM,IAAI,MAAM,iCAAiC,QAAQ;;;;;;CAO/D,cAAc,OAAuB;AACnC,SAAO;GAAC;GAAQ;GAAW;GAAS,CAAC,SAAS,MAAM;;;;;CAMtD,AAAQ,sBAA8B;AAGpC,UAFiB,GAAG,UAAU,EAE9B;GACE,KAAK,SACH,QAAO;GACT,KAAK,QACH,QAAO,KAAK,KACV,QAAQ,IAAI,eAAe,mBAC3B,UACA,gBACD;GACH,KAAK,QACH,QAAO;GACT,QACE,QAAO"}
@@ -0,0 +1,75 @@
1
+ import { f as SettingsResult, m as ToolId, n as InspectResult, p as SwitchOptions, r as LaunchCommandSpec, s as Scope, t as EditOptions, u as SettingsListData } from "./types--0tjriPy.js";
2
+
3
+ //#region src/domain/interfaces.d.ts
4
+
5
+ /**
6
+ * Settings 适配器接口
7
+ * 所有工具适配器必须实现此接口(LSP 原则)
8
+ */
9
+ interface SettingsAdapter {
10
+ /**
11
+ * 工具标识符
12
+ */
13
+ readonly toolId: ToolId;
14
+ /**
15
+ * 列出指定 scope 的所有配置
16
+ */
17
+ list(scope: Scope): Promise<SettingsListData>;
18
+ /**
19
+ * 切换 profile(可选,仅 Codex 支持)
20
+ */
21
+ switchProfile?(scope: Scope, profile: string, options?: SwitchOptions): Promise<SettingsResult>;
22
+ /**
23
+ * 打开配置文件供编辑(可选)
24
+ */
25
+ edit?(scope: Scope, options: EditOptions): Promise<SettingsResult>;
26
+ /**
27
+ * 检查配置文件状态
28
+ */
29
+ inspect?(scope: Scope): Promise<InspectResult>;
30
+ /**
31
+ * 解析配置文件路径
32
+ */
33
+ resolvePath(scope: Scope): string;
34
+ /**
35
+ * 验证 scope 是否有效
36
+ */
37
+ validateScope(scope: Scope): boolean;
38
+ }
39
+ /**
40
+ * Launch 适配器接口
41
+ * 负责构建工具启动命令(ISP 原则:与 SettingsAdapter 分离)
42
+ */
43
+ interface LaunchAdapter {
44
+ /**
45
+ * 工具标识符
46
+ */
47
+ readonly toolId: ToolId;
48
+ /**
49
+ * 可执行文件名称
50
+ */
51
+ readonly executable: string;
52
+ /**
53
+ * 构建启动命令配置
54
+ * @param payload Launch 请求参数
55
+ * @returns 命令规范
56
+ */
57
+ buildCommandSpec(payload: {
58
+ yolo?: boolean;
59
+ resume?: boolean;
60
+ continue?: boolean;
61
+ }): LaunchCommandSpec;
62
+ /**
63
+ * 验证工具是否可用(在 PATH 中)
64
+ * @returns 工具是否可用
65
+ */
66
+ validateAvailability(): Promise<boolean>;
67
+ /**
68
+ * 获取工具版本信息(可选)
69
+ * @returns 版本字符串
70
+ */
71
+ getVersion?(): Promise<string>;
72
+ }
73
+ //#endregion
74
+ export { SettingsAdapter as n, LaunchAdapter as t };
75
+ //# sourceMappingURL=interfaces-DRNAGN0l.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces-DRNAGN0l.d.ts","names":[],"sources":["../src/domain/interfaces.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAkCW,UAfM,eAAA,CAeN;EAEG;;;EAMC,SAAA,MAAA,EAnBI,MAmBJ;EAAgB;;;EAKb,IAAA,CAAA,KAAA,EAnBJ,KAmBI,CAAA,EAnBI,OAmBJ,CAnBY,gBAmBZ,CAAA;EAAgB;;;EAUX,aAAA,EAAA,KAAA,EAvBZ,KAuBY,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EArBT,aAqBS,CAAA,EApBlB,OAoBkB,CApBV,cAoBU,CAAA;EAAK;AAO5B;;EAoBM,IAAA,EAAA,KAAA,EA1CS,KA0CT,EAAA,OAAA,EA1CyB,WA0CzB,CAAA,EA1CuC,OA0CvC,CA1C+C,cA0C/C,CAAA;EAMoB;;;kBA3CR,QAAQ,QAAQ;;;;qBAKb;;;;uBAKE;;;;;;UAON,aAAA;;;;mBAIE;;;;;;;;;;;;;;MAgBb;;;;;0BAMoB;;;;;iBAMT"}
package/dist/run.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/run.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { execute } from "@oclif/core";
3
+
4
+ //#region src/run.ts
5
+ /**
6
+ * xling CLI 入口点
7
+ */
8
+ await execute({
9
+ development: false,
10
+ dir: import.meta.url
11
+ });
12
+
13
+ //#endregion
14
+ export { };
15
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","names":[],"sources":["../src/run.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * xling CLI 入口点\n */\n\nimport { execute } from \"@oclif/core\";\n\nawait execute({ development: false, dir: import.meta.url });\n"],"mappings":";;;;;;;AAQA,MAAM,QAAQ;CAAE,aAAa;CAAO,KAAK,OAAO,KAAK;CAAK,CAAC"}
@@ -0,0 +1,89 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ //#region src/utils/runner.ts
4
+ /**
5
+ * 进程启动和管理工具
6
+ * 遵循 SRP 原则:只负责进程启动,不涉及业务逻辑
7
+ */
8
+ /**
9
+ * 启动子进程
10
+ * @param spec 命令规范
11
+ * @param options 启动选项
12
+ * @returns 进程 ID 和完整命令
13
+ */
14
+ async function spawnProcess(spec, options) {
15
+ const args = [...spec.baseArgs];
16
+ if (spec.yoloArgs) args.push(...spec.yoloArgs);
17
+ if (options?.args) args.push(...options.args);
18
+ const env = {
19
+ ...process.env,
20
+ ...spec.envVars,
21
+ ...options?.env
22
+ };
23
+ const child = spawn(spec.executable, args, {
24
+ cwd: options?.cwd ?? process.cwd(),
25
+ env,
26
+ stdio: "inherit",
27
+ detached: false
28
+ });
29
+ const command = `${spec.executable} ${args.join(" ")}`;
30
+ return new Promise((resolve, reject) => {
31
+ child.on("error", (error) => {
32
+ reject(/* @__PURE__ */ new Error(`Failed to spawn process: ${error.message}`));
33
+ });
34
+ child.on("spawn", () => {
35
+ resolve({
36
+ pid: child.pid,
37
+ command
38
+ });
39
+ });
40
+ child.on("exit", (code) => {
41
+ if (code !== 0 && code !== null) reject(/* @__PURE__ */ new Error(`Process exited with code ${code}`));
42
+ });
43
+ });
44
+ }
45
+ /**
46
+ * 检查可执行文件是否存在于 PATH 中
47
+ * @param name 可执行文件名
48
+ * @returns 是否存在
49
+ */
50
+ async function checkExecutable(name) {
51
+ return new Promise((resolve) => {
52
+ const child = spawn(process.platform === "win32" ? "where" : "which", [name], { stdio: "ignore" });
53
+ child.on("error", () => resolve(false));
54
+ child.on("exit", (code) => resolve(code === 0));
55
+ });
56
+ }
57
+ /**
58
+ * 获取可执行文件的版本信息
59
+ * @param executable 可执行文件名
60
+ * @param versionArgs 版本参数(默认 --version)
61
+ * @returns 版本字符串
62
+ */
63
+ async function getExecutableVersion(executable, versionArgs = ["--version"]) {
64
+ return new Promise((resolve, reject) => {
65
+ let output = "";
66
+ const child = spawn(executable, versionArgs, { stdio: [
67
+ "ignore",
68
+ "pipe",
69
+ "pipe"
70
+ ] });
71
+ child.stdout?.on("data", (data) => {
72
+ output += data.toString();
73
+ });
74
+ child.stderr?.on("data", (data) => {
75
+ output += data.toString();
76
+ });
77
+ child.on("error", (error) => {
78
+ reject(/* @__PURE__ */ new Error(`Failed to get version: ${error.message}`));
79
+ });
80
+ child.on("exit", (code) => {
81
+ if (code === 0) resolve(output.trim());
82
+ else reject(/* @__PURE__ */ new Error("Failed to get version"));
83
+ });
84
+ });
85
+ }
86
+
87
+ //#endregion
88
+ export { getExecutableVersion as n, spawnProcess as r, checkExecutable as t };
89
+ //# sourceMappingURL=runner-BE7zZq1g.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-BE7zZq1g.js","names":[],"sources":["../src/utils/runner.ts"],"sourcesContent":["/**\n * 进程启动和管理工具\n * 遵循 SRP 原则:只负责进程启动,不涉及业务逻辑\n */\n\nimport { spawn } from \"node:child_process\";\nimport type { LaunchCommandSpec } from \"@/domain/types.ts\";\n\nexport interface SpawnOptions {\n cwd?: string;\n env?: Record<string, string>;\n args?: string[];\n}\n\nexport interface SpawnResult {\n pid: number;\n command: string;\n}\n\n/**\n * 启动子进程\n * @param spec 命令规范\n * @param options 启动选项\n * @returns 进程 ID 和完整命令\n */\nexport async function spawnProcess(\n spec: LaunchCommandSpec,\n options?: SpawnOptions,\n): Promise<SpawnResult> {\n // 1. 合并参数\n const args = [...spec.baseArgs];\n\n // 添加 yolo 参数\n if (spec.yoloArgs) {\n args.push(...spec.yoloArgs);\n }\n\n // 添加用户透传的额外参数\n if (options?.args) {\n args.push(...options.args);\n }\n\n // 2. 合并环境变量\n const env = {\n ...process.env,\n ...spec.envVars,\n ...options?.env,\n };\n\n // 3. 启动进程\n const child = spawn(spec.executable, args, {\n cwd: options?.cwd ?? process.cwd(),\n env,\n stdio: \"inherit\", // 继承父进程的标准输入输出\n detached: false,\n });\n\n // 构建完整命令字符串(用于日志)\n const command = `${spec.executable} ${args.join(\" \")}`;\n\n // 等待进程启动\n return new Promise((resolve, reject) => {\n child.on(\"error\", (error) => {\n reject(new Error(`Failed to spawn process: ${error.message}`));\n });\n\n // 进程成功启动后立即返回\n child.on(\"spawn\", () => {\n resolve({\n pid: child.pid!,\n command,\n });\n });\n\n // 如果进程立即退出(如命令不存在),视为错误\n child.on(\"exit\", (code) => {\n if (code !== 0 && code !== null) {\n reject(new Error(`Process exited with code ${code}`));\n }\n });\n });\n}\n\n/**\n * 检查可执行文件是否存在于 PATH 中\n * @param name 可执行文件名\n * @returns 是否存在\n */\nexport async function checkExecutable(name: string): Promise<boolean> {\n return new Promise((resolve) => {\n const command = process.platform === \"win32\" ? \"where\" : \"which\";\n const child = spawn(command, [name], {\n stdio: \"ignore\",\n });\n\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\n/**\n * 获取可执行文件的版本信息\n * @param executable 可执行文件名\n * @param versionArgs 版本参数(默认 --version)\n * @returns 版本字符串\n */\nexport async function getExecutableVersion(\n executable: string,\n versionArgs: string[] = [\"--version\"],\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let output = \"\";\n\n const child = spawn(executable, versionArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n child.stdout?.on(\"data\", (data) => {\n output += data.toString();\n });\n\n child.stderr?.on(\"data\", (data) => {\n output += data.toString();\n });\n\n child.on(\"error\", (error) => {\n reject(new Error(`Failed to get version: ${error.message}`));\n });\n\n child.on(\"exit\", (code) => {\n if (code === 0) {\n resolve(output.trim());\n } else {\n reject(new Error(\"Failed to get version\"));\n }\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAyBA,eAAsB,aACpB,MACA,SACsB;CAEtB,MAAM,OAAO,CAAC,GAAG,KAAK,SAAS;AAG/B,KAAI,KAAK,SACP,MAAK,KAAK,GAAG,KAAK,SAAS;AAI7B,KAAI,SAAS,KACX,MAAK,KAAK,GAAG,QAAQ,KAAK;CAI5B,MAAM,MAAM;EACV,GAAG,QAAQ;EACX,GAAG,KAAK;EACR,GAAG,SAAS;EACb;CAGD,MAAM,QAAQ,MAAM,KAAK,YAAY,MAAM;EACzC,KAAK,SAAS,OAAO,QAAQ,KAAK;EAClC;EACA,OAAO;EACP,UAAU;EACX,CAAC;CAGF,MAAM,UAAU,GAAG,KAAK,WAAW,GAAG,KAAK,KAAK,IAAI;AAGpD,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,QAAM,GAAG,UAAU,UAAU;AAC3B,0BAAO,IAAI,MAAM,4BAA4B,MAAM,UAAU,CAAC;IAC9D;AAGF,QAAM,GAAG,eAAe;AACtB,WAAQ;IACN,KAAK,MAAM;IACX;IACD,CAAC;IACF;AAGF,QAAM,GAAG,SAAS,SAAS;AACzB,OAAI,SAAS,KAAK,SAAS,KACzB,wBAAO,IAAI,MAAM,4BAA4B,OAAO,CAAC;IAEvD;GACF;;;;;;;AAQJ,eAAsB,gBAAgB,MAAgC;AACpE,QAAO,IAAI,SAAS,YAAY;EAE9B,MAAM,QAAQ,MADE,QAAQ,aAAa,UAAU,UAAU,SAC5B,CAAC,KAAK,EAAE,EACnC,OAAO,UACR,CAAC;AAEF,QAAM,GAAG,eAAe,QAAQ,MAAM,CAAC;AACvC,QAAM,GAAG,SAAS,SAAS,QAAQ,SAAS,EAAE,CAAC;GAC/C;;;;;;;;AASJ,eAAsB,qBACpB,YACA,cAAwB,CAAC,YAAY,EACpB;AACjB,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,SAAS;EAEb,MAAM,QAAQ,MAAM,YAAY,aAAa,EAC3C,OAAO;GAAC;GAAU;GAAQ;GAAO,EAClC,CAAC;AAEF,QAAM,QAAQ,GAAG,SAAS,SAAS;AACjC,aAAU,KAAK,UAAU;IACzB;AAEF,QAAM,QAAQ,GAAG,SAAS,SAAS;AACjC,aAAU,KAAK,UAAU;IACzB;AAEF,QAAM,GAAG,UAAU,UAAU;AAC3B,0BAAO,IAAI,MAAM,0BAA0B,MAAM,UAAU,CAAC;IAC5D;AAEF,QAAM,GAAG,SAAS,SAAS;AACzB,OAAI,SAAS,EACX,SAAQ,OAAO,MAAM,CAAC;OAEtB,wBAAO,IAAI,MAAM,wBAAwB,CAAC;IAE5C;GACF"}
@@ -0,0 +1,4 @@
1
+ import "../../../types--0tjriPy.js";
2
+ import "../../../interfaces-DRNAGN0l.js";
3
+ import { t as BaseLaunchAdapter } from "../../../base-BXJVsMwL.js";
4
+ export { BaseLaunchAdapter };
@@ -0,0 +1,4 @@
1
+ import "../../../runner-BE7zZq1g.js";
2
+ import { t as BaseLaunchAdapter } from "../../../base-DdIJJBHV.js";
3
+
4
+ export { BaseLaunchAdapter };