@nick848/sf-cli 1.0.15 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +613 -643
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +22 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var fs7 = require('fs/promises');
|
|
5
|
-
var fs9 = require('fs');
|
|
6
|
-
var path7 = require('path');
|
|
7
|
-
var crypto = require('crypto');
|
|
8
|
-
var os = require('os');
|
|
9
4
|
var tiktoken = require('tiktoken');
|
|
10
|
-
require('@modelcontextprotocol/sdk/client/index.js');
|
|
11
|
-
require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
12
5
|
var chalk9 = require('chalk');
|
|
13
6
|
var enquirer = require('enquirer');
|
|
14
7
|
var child_process = require('child_process');
|
|
8
|
+
var fs7 = require('fs');
|
|
9
|
+
var path5 = require('path');
|
|
10
|
+
var fs5 = require('fs/promises');
|
|
15
11
|
var commander = require('commander');
|
|
16
12
|
var readline = require('readline');
|
|
17
13
|
require('uuid');
|
|
14
|
+
var crypto = require('crypto');
|
|
15
|
+
var os = require('os');
|
|
16
|
+
require('@modelcontextprotocol/sdk/client/index.js');
|
|
17
|
+
require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
18
18
|
|
|
19
19
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
20
|
|
|
@@ -36,13 +36,13 @@ function _interopNamespace(e) {
|
|
|
36
36
|
return Object.freeze(n);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
39
40
|
var fs7__namespace = /*#__PURE__*/_interopNamespace(fs7);
|
|
40
|
-
var
|
|
41
|
-
var
|
|
41
|
+
var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
|
|
42
|
+
var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
|
|
43
|
+
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
42
44
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
43
45
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
44
|
-
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
45
|
-
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
46
46
|
|
|
47
47
|
var __defProp = Object.defineProperty;
|
|
48
48
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -59,138 +59,6 @@ var init_cjs_shims = __esm({
|
|
|
59
59
|
"node_modules/tsup/assets/cjs_shims.js"() {
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
|
-
function getOrCreateEncryptionKey() {
|
|
63
|
-
const keyPath = path7__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
|
|
64
|
-
try {
|
|
65
|
-
if (fs9__namespace.existsSync(keyPath)) {
|
|
66
|
-
const keyBase64 = fs9__namespace.readFileSync(keyPath, "utf-8").trim();
|
|
67
|
-
return Buffer.from(keyBase64, "base64");
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
const key = crypto__namespace.randomBytes(32);
|
|
72
|
-
try {
|
|
73
|
-
const keyDir = path7__namespace.dirname(keyPath);
|
|
74
|
-
if (!fs9__namespace.existsSync(keyDir)) {
|
|
75
|
-
fs9__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
76
|
-
}
|
|
77
|
-
fs9__namespace.writeFileSync(keyPath, key.toString("base64"), {
|
|
78
|
-
mode: 384,
|
|
79
|
-
// 仅所有者可读写
|
|
80
|
-
encoding: "utf-8"
|
|
81
|
-
});
|
|
82
|
-
return key;
|
|
83
|
-
} catch {
|
|
84
|
-
const machineId = [
|
|
85
|
-
os__namespace.hostname(),
|
|
86
|
-
os__namespace.platform(),
|
|
87
|
-
os__namespace.arch(),
|
|
88
|
-
process.env.USERNAME || process.env.USER || "default"
|
|
89
|
-
].join("-");
|
|
90
|
-
return crypto__namespace.createHash("sha256").update(machineId).digest();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
var DEFAULT_CONFIG, ALGORITHM, IV_LENGTH, KEY_DIR, KEY_FILE, ConfigManager;
|
|
94
|
-
var init_config = __esm({
|
|
95
|
-
"src/services/config.ts"() {
|
|
96
|
-
init_cjs_shims();
|
|
97
|
-
DEFAULT_CONFIG = {
|
|
98
|
-
model: "GLM-5",
|
|
99
|
-
apiKey: "",
|
|
100
|
-
yolo: false,
|
|
101
|
-
locale: "zh-CN",
|
|
102
|
-
theme: "auto"
|
|
103
|
-
};
|
|
104
|
-
ALGORITHM = "aes-256-gcm";
|
|
105
|
-
IV_LENGTH = 16;
|
|
106
|
-
KEY_DIR = ".sf-cli";
|
|
107
|
-
KEY_FILE = ".key";
|
|
108
|
-
ConfigManager = class {
|
|
109
|
-
config;
|
|
110
|
-
configPath = "";
|
|
111
|
-
projectPath = "";
|
|
112
|
-
encryptionKey;
|
|
113
|
-
constructor() {
|
|
114
|
-
this.config = { ...DEFAULT_CONFIG };
|
|
115
|
-
this.encryptionKey = getOrCreateEncryptionKey();
|
|
116
|
-
}
|
|
117
|
-
async load(projectPath) {
|
|
118
|
-
this.projectPath = projectPath;
|
|
119
|
-
this.configPath = path7__namespace.join(projectPath, ".sf-cli", "config.json");
|
|
120
|
-
try {
|
|
121
|
-
const content = await fs7__namespace.readFile(this.configPath, "utf-8");
|
|
122
|
-
const loaded = JSON.parse(content);
|
|
123
|
-
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
124
|
-
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
125
|
-
delete loaded.apiKeyEncrypted;
|
|
126
|
-
}
|
|
127
|
-
this.config = { ...DEFAULT_CONFIG, ...loaded };
|
|
128
|
-
} catch {
|
|
129
|
-
this.config = { ...DEFAULT_CONFIG };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
async save() {
|
|
133
|
-
if (!this.configPath) return;
|
|
134
|
-
const configToSave = { ...this.config };
|
|
135
|
-
if (configToSave.apiKey) {
|
|
136
|
-
const encrypted = this.encrypt(configToSave.apiKey);
|
|
137
|
-
configToSave.apiKey = encrypted;
|
|
138
|
-
configToSave.apiKeyEncrypted = true;
|
|
139
|
-
}
|
|
140
|
-
await fs7__namespace.mkdir(path7__namespace.dirname(this.configPath), { recursive: true });
|
|
141
|
-
await fs7__namespace.writeFile(
|
|
142
|
-
this.configPath,
|
|
143
|
-
JSON.stringify(configToSave, null, 2),
|
|
144
|
-
"utf-8"
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
get(key) {
|
|
148
|
-
return this.config[key];
|
|
149
|
-
}
|
|
150
|
-
set(key, value) {
|
|
151
|
-
this.config[key] = value;
|
|
152
|
-
}
|
|
153
|
-
getAll() {
|
|
154
|
-
return { ...this.config };
|
|
155
|
-
}
|
|
156
|
-
update(updates) {
|
|
157
|
-
this.config = { ...this.config, ...updates };
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* 加密敏感数据
|
|
161
|
-
*/
|
|
162
|
-
encrypt(text) {
|
|
163
|
-
const iv = crypto__namespace.randomBytes(IV_LENGTH);
|
|
164
|
-
const cipher = crypto__namespace.createCipheriv(ALGORITHM, this.encryptionKey, iv);
|
|
165
|
-
let encrypted = cipher.update(text, "utf8", "base64");
|
|
166
|
-
encrypted += cipher.final("base64");
|
|
167
|
-
const authTag = cipher.getAuthTag();
|
|
168
|
-
return `${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* 解密敏感数据
|
|
172
|
-
*/
|
|
173
|
-
decrypt(encryptedData) {
|
|
174
|
-
try {
|
|
175
|
-
const parts = encryptedData.split(":");
|
|
176
|
-
if (parts.length !== 3) {
|
|
177
|
-
return encryptedData;
|
|
178
|
-
}
|
|
179
|
-
const [ivBase64, authTagBase64, encrypted] = parts;
|
|
180
|
-
const iv = Buffer.from(ivBase64, "base64");
|
|
181
|
-
const authTag = Buffer.from(authTagBase64, "base64");
|
|
182
|
-
const decipher = crypto__namespace.createDecipheriv(ALGORITHM, this.encryptionKey, iv);
|
|
183
|
-
decipher.setAuthTag(authTag);
|
|
184
|
-
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
185
|
-
decrypted += decipher.final("utf8");
|
|
186
|
-
return decrypted;
|
|
187
|
-
} catch {
|
|
188
|
-
return "";
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
62
|
|
|
195
63
|
// src/types/model.ts
|
|
196
64
|
function getModelInfo(modelId) {
|
|
@@ -1078,312 +946,6 @@ var init_adapters = __esm({
|
|
|
1078
946
|
DEEPSEEK_API_ENDPOINT = "https://api.deepseek.com/v1";
|
|
1079
947
|
}
|
|
1080
948
|
});
|
|
1081
|
-
var ModelService;
|
|
1082
|
-
var init_model2 = __esm({
|
|
1083
|
-
"src/services/model.ts"() {
|
|
1084
|
-
init_cjs_shims();
|
|
1085
|
-
init_model();
|
|
1086
|
-
init_adapters();
|
|
1087
|
-
ModelService = class {
|
|
1088
|
-
adapters = /* @__PURE__ */ new Map();
|
|
1089
|
-
currentConfig = null;
|
|
1090
|
-
stats;
|
|
1091
|
-
configManager;
|
|
1092
|
-
statsPath = "";
|
|
1093
|
-
constructor(configManager) {
|
|
1094
|
-
this.configManager = configManager;
|
|
1095
|
-
this.stats = {
|
|
1096
|
-
totalInput: 0,
|
|
1097
|
-
totalOutput: 0,
|
|
1098
|
-
byModel: {},
|
|
1099
|
-
byAgent: {},
|
|
1100
|
-
byDate: {}
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1103
|
-
/**
|
|
1104
|
-
* 初始化服务
|
|
1105
|
-
*/
|
|
1106
|
-
async initialize(statsDir) {
|
|
1107
|
-
this.statsPath = path7__namespace.join(statsDir, "tokens");
|
|
1108
|
-
await this.loadStats();
|
|
1109
|
-
const model = this.configManager.get("model");
|
|
1110
|
-
const apiKey = this.configManager.get("apiKey");
|
|
1111
|
-
if (model && apiKey) {
|
|
1112
|
-
await this.configureModel({
|
|
1113
|
-
provider: this.getProviderFromModel(model),
|
|
1114
|
-
model,
|
|
1115
|
-
apiKey
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* 配置模型
|
|
1121
|
-
*/
|
|
1122
|
-
async configureModel(config) {
|
|
1123
|
-
let adapter = this.adapters.get(config.provider);
|
|
1124
|
-
if (!adapter) {
|
|
1125
|
-
adapter = createAdapter(config.provider);
|
|
1126
|
-
this.adapters.set(config.provider, adapter);
|
|
1127
|
-
}
|
|
1128
|
-
await adapter.initialize(config);
|
|
1129
|
-
this.currentConfig = config;
|
|
1130
|
-
this.configManager.set("model", config.model);
|
|
1131
|
-
this.configManager.set("apiKey", config.apiKey);
|
|
1132
|
-
await this.configManager.save();
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* 发送消息
|
|
1136
|
-
*/
|
|
1137
|
-
async sendMessage(messages, options) {
|
|
1138
|
-
if (!this.currentConfig) {
|
|
1139
|
-
throw new Error("\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C /model \u547D\u4EE4");
|
|
1140
|
-
}
|
|
1141
|
-
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
1142
|
-
if (!adapter || !adapter.isInitialized()) {
|
|
1143
|
-
throw new Error("\u6A21\u578B\u9002\u914D\u5668\u672A\u521D\u59CB\u5316");
|
|
1144
|
-
}
|
|
1145
|
-
try {
|
|
1146
|
-
const response = await adapter.sendMessage(messages, options);
|
|
1147
|
-
this.updateStats(response.usage, {
|
|
1148
|
-
model: this.currentConfig.model,
|
|
1149
|
-
agent: options?.agent
|
|
1150
|
-
});
|
|
1151
|
-
this.saveStats().catch(() => {
|
|
1152
|
-
});
|
|
1153
|
-
return response;
|
|
1154
|
-
} catch (error) {
|
|
1155
|
-
throw this.wrapError(error);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
/**
|
|
1159
|
-
* 流式发送消息
|
|
1160
|
-
*/
|
|
1161
|
-
async *streamMessage(messages, options) {
|
|
1162
|
-
if (!this.currentConfig) {
|
|
1163
|
-
throw new Error("\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C /model \u547D\u4EE4");
|
|
1164
|
-
}
|
|
1165
|
-
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
1166
|
-
if (!adapter || !adapter.isInitialized()) {
|
|
1167
|
-
throw new Error("\u6A21\u578B\u9002\u914D\u5668\u672A\u521D\u59CB\u5316");
|
|
1168
|
-
}
|
|
1169
|
-
let totalTokens = 0;
|
|
1170
|
-
try {
|
|
1171
|
-
for await (const chunk of adapter.streamMessage(messages, options)) {
|
|
1172
|
-
totalTokens += chunk.delta.length;
|
|
1173
|
-
yield chunk;
|
|
1174
|
-
}
|
|
1175
|
-
const inputTokens = adapter.countTokens(
|
|
1176
|
-
messages.map((m) => m.content).join("\n")
|
|
1177
|
-
);
|
|
1178
|
-
this.updateStats(
|
|
1179
|
-
{ inputTokens, outputTokens: Math.ceil(totalTokens / 4), totalTokens: 0 },
|
|
1180
|
-
{ model: this.currentConfig.model, agent: options?.agent }
|
|
1181
|
-
);
|
|
1182
|
-
this.saveStats().catch(() => {
|
|
1183
|
-
});
|
|
1184
|
-
} catch (error) {
|
|
1185
|
-
throw this.wrapError(error);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
/**
|
|
1189
|
-
* 计算Token数量
|
|
1190
|
-
*/
|
|
1191
|
-
countTokens(text) {
|
|
1192
|
-
if (!this.currentConfig) {
|
|
1193
|
-
return Math.ceil(text.length / 4);
|
|
1194
|
-
}
|
|
1195
|
-
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
1196
|
-
return adapter?.countTokens(text) || Math.ceil(text.length / 4);
|
|
1197
|
-
}
|
|
1198
|
-
/**
|
|
1199
|
-
* 获取当前模型
|
|
1200
|
-
*/
|
|
1201
|
-
getCurrentModel() {
|
|
1202
|
-
return this.currentConfig?.model || null;
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* 获取当前提供商
|
|
1206
|
-
*/
|
|
1207
|
-
getCurrentProvider() {
|
|
1208
|
-
return this.currentConfig?.provider || null;
|
|
1209
|
-
}
|
|
1210
|
-
/**
|
|
1211
|
-
* 获取Token统计
|
|
1212
|
-
*/
|
|
1213
|
-
getStats() {
|
|
1214
|
-
return { ...this.stats };
|
|
1215
|
-
}
|
|
1216
|
-
/**
|
|
1217
|
-
* 获取今日统计
|
|
1218
|
-
*/
|
|
1219
|
-
getTodayStats() {
|
|
1220
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1221
|
-
return this.stats.byDate[today] || { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1222
|
-
}
|
|
1223
|
-
/**
|
|
1224
|
-
* 重置统计
|
|
1225
|
-
*/
|
|
1226
|
-
resetStats() {
|
|
1227
|
-
this.stats = {
|
|
1228
|
-
totalInput: 0,
|
|
1229
|
-
totalOutput: 0,
|
|
1230
|
-
byModel: {},
|
|
1231
|
-
byAgent: {},
|
|
1232
|
-
byDate: {}
|
|
1233
|
-
};
|
|
1234
|
-
this.saveStats().catch(() => {
|
|
1235
|
-
});
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* 验证API Key
|
|
1239
|
-
*/
|
|
1240
|
-
async validateApiKey(provider, apiKey) {
|
|
1241
|
-
let adapter = this.adapters.get(provider);
|
|
1242
|
-
if (!adapter) {
|
|
1243
|
-
adapter = createAdapter(provider);
|
|
1244
|
-
}
|
|
1245
|
-
return adapter.validateApiKey(apiKey);
|
|
1246
|
-
}
|
|
1247
|
-
// ==================== 私有方法 ====================
|
|
1248
|
-
getProviderFromModel(modelId) {
|
|
1249
|
-
const info = getModelInfo(modelId);
|
|
1250
|
-
return info?.provider || "openai";
|
|
1251
|
-
}
|
|
1252
|
-
updateStats(usage, context) {
|
|
1253
|
-
this.stats.totalInput += usage.inputTokens;
|
|
1254
|
-
this.stats.totalOutput += usage.outputTokens;
|
|
1255
|
-
if (!this.stats.byModel[context.model]) {
|
|
1256
|
-
this.stats.byModel[context.model] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1257
|
-
}
|
|
1258
|
-
this.stats.byModel[context.model].inputTokens += usage.inputTokens;
|
|
1259
|
-
this.stats.byModel[context.model].outputTokens += usage.outputTokens;
|
|
1260
|
-
this.stats.byModel[context.model].totalTokens += usage.totalTokens;
|
|
1261
|
-
if (context.agent) {
|
|
1262
|
-
if (!this.stats.byAgent[context.agent]) {
|
|
1263
|
-
this.stats.byAgent[context.agent] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1264
|
-
}
|
|
1265
|
-
this.stats.byAgent[context.agent].inputTokens += usage.inputTokens;
|
|
1266
|
-
this.stats.byAgent[context.agent].outputTokens += usage.outputTokens;
|
|
1267
|
-
this.stats.byAgent[context.agent].totalTokens += usage.totalTokens;
|
|
1268
|
-
}
|
|
1269
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1270
|
-
if (!this.stats.byDate[today]) {
|
|
1271
|
-
this.stats.byDate[today] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1272
|
-
}
|
|
1273
|
-
this.stats.byDate[today].inputTokens += usage.inputTokens;
|
|
1274
|
-
this.stats.byDate[today].outputTokens += usage.outputTokens;
|
|
1275
|
-
this.stats.byDate[today].totalTokens += usage.totalTokens;
|
|
1276
|
-
}
|
|
1277
|
-
async loadStats() {
|
|
1278
|
-
if (!this.statsPath) return;
|
|
1279
|
-
try {
|
|
1280
|
-
const dailyPath = path7__namespace.join(this.statsPath, "daily.json");
|
|
1281
|
-
const totalPath = path7__namespace.join(this.statsPath, "total.json");
|
|
1282
|
-
const [daily, total] = await Promise.all([
|
|
1283
|
-
fs7__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
1284
|
-
fs7__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
1285
|
-
]);
|
|
1286
|
-
const dailyData = JSON.parse(daily);
|
|
1287
|
-
const totalData = JSON.parse(total);
|
|
1288
|
-
this.stats.byDate = dailyData;
|
|
1289
|
-
this.stats.totalInput = totalData.totalInput || 0;
|
|
1290
|
-
this.stats.totalOutput = totalData.totalOutput || 0;
|
|
1291
|
-
} catch {
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
async saveStats() {
|
|
1295
|
-
if (!this.statsPath) return;
|
|
1296
|
-
try {
|
|
1297
|
-
await fs7__namespace.mkdir(this.statsPath, { recursive: true });
|
|
1298
|
-
const dailyPath = path7__namespace.join(this.statsPath, "daily.json");
|
|
1299
|
-
const totalPath = path7__namespace.join(this.statsPath, "total.json");
|
|
1300
|
-
await Promise.all([
|
|
1301
|
-
fs7__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
1302
|
-
fs7__namespace.writeFile(totalPath, JSON.stringify({
|
|
1303
|
-
totalInput: this.stats.totalInput,
|
|
1304
|
-
totalOutput: this.stats.totalOutput
|
|
1305
|
-
}))
|
|
1306
|
-
]);
|
|
1307
|
-
} catch {
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
wrapError(error) {
|
|
1311
|
-
if (error instanceof ModelError) {
|
|
1312
|
-
return error;
|
|
1313
|
-
}
|
|
1314
|
-
const err = error;
|
|
1315
|
-
if (err.message.includes("timeout")) {
|
|
1316
|
-
return new ModelError("TIMEOUT", err.message, { retryable: true });
|
|
1317
|
-
}
|
|
1318
|
-
if (err.message.includes("network") || err.message.includes("ECONNREFUSED")) {
|
|
1319
|
-
return new ModelError("NETWORK_ERROR", err.message, { retryable: true });
|
|
1320
|
-
}
|
|
1321
|
-
return new ModelError("UNKNOWN_ERROR", err.message, { retryable: false });
|
|
1322
|
-
}
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
// src/types/mcp.ts
|
|
1328
|
-
var init_mcp = __esm({
|
|
1329
|
-
"src/types/mcp.ts"() {
|
|
1330
|
-
init_cjs_shims();
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
var init_base2 = __esm({
|
|
1334
|
-
"src/services/mcp/base.ts"() {
|
|
1335
|
-
init_cjs_shims();
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
|
|
1339
|
-
// src/services/mcp/lanhu.ts
|
|
1340
|
-
var init_lanhu = __esm({
|
|
1341
|
-
"src/services/mcp/lanhu.ts"() {
|
|
1342
|
-
init_cjs_shims();
|
|
1343
|
-
init_base2();
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
|
|
1347
|
-
// src/services/mcp/figma.ts
|
|
1348
|
-
var init_figma = __esm({
|
|
1349
|
-
"src/services/mcp/figma.ts"() {
|
|
1350
|
-
init_cjs_shims();
|
|
1351
|
-
init_base2();
|
|
1352
|
-
}
|
|
1353
|
-
});
|
|
1354
|
-
|
|
1355
|
-
// src/services/mcp/manager.ts
|
|
1356
|
-
var init_manager = __esm({
|
|
1357
|
-
"src/services/mcp/manager.ts"() {
|
|
1358
|
-
init_cjs_shims();
|
|
1359
|
-
init_base2();
|
|
1360
|
-
init_lanhu();
|
|
1361
|
-
init_figma();
|
|
1362
|
-
}
|
|
1363
|
-
});
|
|
1364
|
-
|
|
1365
|
-
// src/services/mcp/index.ts
|
|
1366
|
-
var init_mcp2 = __esm({
|
|
1367
|
-
"src/services/mcp/index.ts"() {
|
|
1368
|
-
init_cjs_shims();
|
|
1369
|
-
init_mcp();
|
|
1370
|
-
init_base2();
|
|
1371
|
-
init_lanhu();
|
|
1372
|
-
init_figma();
|
|
1373
|
-
init_manager();
|
|
1374
|
-
}
|
|
1375
|
-
});
|
|
1376
|
-
|
|
1377
|
-
// src/services/index.ts
|
|
1378
|
-
var init_services = __esm({
|
|
1379
|
-
"src/services/index.ts"() {
|
|
1380
|
-
init_cjs_shims();
|
|
1381
|
-
init_config();
|
|
1382
|
-
init_model2();
|
|
1383
|
-
init_adapters();
|
|
1384
|
-
init_mcp2();
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
949
|
|
|
1388
950
|
// src/commands/model.ts
|
|
1389
951
|
var model_exports = {};
|
|
@@ -1399,17 +961,18 @@ __export(model_exports, {
|
|
|
1399
961
|
async function handleModel(args, ctx) {
|
|
1400
962
|
const subCommand = args[0];
|
|
1401
963
|
const configManager = ctx.configManager;
|
|
964
|
+
const modelService = ctx.modelService;
|
|
1402
965
|
switch (subCommand) {
|
|
1403
966
|
case "list":
|
|
1404
967
|
return listModels();
|
|
1405
968
|
case "current":
|
|
1406
969
|
return showCurrentModel(configManager);
|
|
1407
970
|
case "set":
|
|
1408
|
-
return setModelDirectly(args[1], configManager);
|
|
971
|
+
return setModelDirectly(args[1], configManager, modelService);
|
|
1409
972
|
case "verify":
|
|
1410
973
|
return verifyCurrentModel(configManager);
|
|
1411
974
|
default:
|
|
1412
|
-
return selectModel(configManager);
|
|
975
|
+
return selectModel(configManager, modelService);
|
|
1413
976
|
}
|
|
1414
977
|
}
|
|
1415
978
|
async function listModels() {
|
|
@@ -1450,7 +1013,7 @@ async function showCurrentModel(configManager) {
|
|
|
1450
1013
|
];
|
|
1451
1014
|
return { output: lines.join("\n") };
|
|
1452
1015
|
}
|
|
1453
|
-
async function setModelDirectly(modelId, configManager) {
|
|
1016
|
+
async function setModelDirectly(modelId, configManager, modelService) {
|
|
1454
1017
|
if (!modelId) {
|
|
1455
1018
|
return {
|
|
1456
1019
|
output: chalk9__default.default.red("\u8BF7\u6307\u5B9A\u6A21\u578BID\uFF0C\u4F8B\u5982: /model set gpt-4o")
|
|
@@ -1465,11 +1028,22 @@ async function setModelDirectly(modelId, configManager) {
|
|
|
1465
1028
|
}
|
|
1466
1029
|
configManager.set("model", modelId);
|
|
1467
1030
|
await configManager.save();
|
|
1031
|
+
const apiKey = configManager.get("apiKey");
|
|
1032
|
+
if (apiKey) {
|
|
1033
|
+
try {
|
|
1034
|
+
await modelService.configureModel({
|
|
1035
|
+
provider: modelInfo.provider,
|
|
1036
|
+
model: modelId,
|
|
1037
|
+
apiKey
|
|
1038
|
+
});
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1468
1042
|
return {
|
|
1469
1043
|
output: chalk9__default.default.green(`\u2713 \u5DF2\u5207\u6362\u5230\u6A21\u578B: ${modelInfo.name} (${modelInfo.provider})`)
|
|
1470
1044
|
};
|
|
1471
1045
|
}
|
|
1472
|
-
async function verifyCurrentModel(configManager) {
|
|
1046
|
+
async function verifyCurrentModel(configManager, modelService) {
|
|
1473
1047
|
const currentModel = configManager.get("model");
|
|
1474
1048
|
const apiKey = configManager.get("apiKey");
|
|
1475
1049
|
if (!apiKey) {
|
|
@@ -1502,7 +1076,7 @@ async function verifyCurrentModel(configManager) {
|
|
|
1502
1076
|
};
|
|
1503
1077
|
}
|
|
1504
1078
|
}
|
|
1505
|
-
async function selectModel(configManager) {
|
|
1079
|
+
async function selectModel(configManager, modelService) {
|
|
1506
1080
|
try {
|
|
1507
1081
|
const currentModel = configManager.get("model");
|
|
1508
1082
|
const currentApiKey = configManager.get("apiKey");
|
|
@@ -1566,6 +1140,11 @@ async function selectModel(configManager) {
|
|
|
1566
1140
|
configManager.set("model", modelResponse.model);
|
|
1567
1141
|
configManager.set("apiKey", apiKeyResponse.apiKey);
|
|
1568
1142
|
await configManager.save();
|
|
1143
|
+
await modelService.configureModel({
|
|
1144
|
+
provider: selectedModel.provider,
|
|
1145
|
+
model: modelResponse.model,
|
|
1146
|
+
apiKey: apiKeyResponse.apiKey
|
|
1147
|
+
});
|
|
1569
1148
|
return {
|
|
1570
1149
|
output: chalk9__default.default.green(`
|
|
1571
1150
|
\u2713 \u6A21\u578B\u914D\u7F6E\u5B8C\u6210
|
|
@@ -1614,10 +1193,10 @@ function createSpinner(message) {
|
|
|
1614
1193
|
};
|
|
1615
1194
|
}
|
|
1616
1195
|
var model_default;
|
|
1617
|
-
var
|
|
1196
|
+
var init_model2 = __esm({
|
|
1618
1197
|
"src/commands/model.ts"() {
|
|
1619
1198
|
init_cjs_shims();
|
|
1620
|
-
|
|
1199
|
+
init_adapters();
|
|
1621
1200
|
init_model();
|
|
1622
1201
|
model_default = selectModel;
|
|
1623
1202
|
}
|
|
@@ -1633,13 +1212,13 @@ __export(update_exports, {
|
|
|
1633
1212
|
});
|
|
1634
1213
|
function getPackageInfo() {
|
|
1635
1214
|
const possiblePaths = [
|
|
1636
|
-
|
|
1637
|
-
|
|
1215
|
+
path5__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
1216
|
+
path5__namespace.resolve(__dirname, "..", "..", "..", "package.json")
|
|
1638
1217
|
];
|
|
1639
1218
|
for (const pkgPath of possiblePaths) {
|
|
1640
1219
|
try {
|
|
1641
|
-
if (
|
|
1642
|
-
const content =
|
|
1220
|
+
if (fs7__namespace.existsSync(pkgPath)) {
|
|
1221
|
+
const content = fs7__namespace.readFileSync(pkgPath, "utf-8");
|
|
1643
1222
|
return JSON.parse(content);
|
|
1644
1223
|
}
|
|
1645
1224
|
} catch {
|
|
@@ -2321,18 +1900,18 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
2321
1900
|
});
|
|
2322
1901
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
2323
1902
|
for (const block of codeBlocks) {
|
|
2324
|
-
const filePath =
|
|
2325
|
-
const dir =
|
|
2326
|
-
await
|
|
2327
|
-
await
|
|
1903
|
+
const filePath = path5__namespace.join(workingDir, block.filename);
|
|
1904
|
+
const dir = path5__namespace.dirname(filePath);
|
|
1905
|
+
await fs5__namespace.mkdir(dir, { recursive: true });
|
|
1906
|
+
await fs5__namespace.writeFile(filePath, block.code, "utf-8");
|
|
2328
1907
|
files.push(block.filename);
|
|
2329
1908
|
}
|
|
2330
1909
|
if (files.length === 0) {
|
|
2331
|
-
const implDir =
|
|
2332
|
-
await
|
|
1910
|
+
const implDir = path5__namespace.join(workingDir, "src", "features");
|
|
1911
|
+
await fs5__namespace.mkdir(implDir, { recursive: true });
|
|
2333
1912
|
const featureName = session.specItems[0]?.title || "feature";
|
|
2334
1913
|
const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
|
|
2335
|
-
const filePath =
|
|
1914
|
+
const filePath = path5__namespace.join(implDir, fileName);
|
|
2336
1915
|
const stubCode = `/**
|
|
2337
1916
|
* ${session.requirement}
|
|
2338
1917
|
*
|
|
@@ -2344,7 +1923,7 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
2344
1923
|
console.log('${featureName} - \u5F85\u5B9E\u73B0');
|
|
2345
1924
|
}
|
|
2346
1925
|
`;
|
|
2347
|
-
await
|
|
1926
|
+
await fs5__namespace.writeFile(filePath, stubCode, "utf-8");
|
|
2348
1927
|
files.push(`src/features/${fileName}`);
|
|
2349
1928
|
}
|
|
2350
1929
|
return { success: true, files };
|
|
@@ -2399,7 +1978,7 @@ async function executeReview(ctx, session) {
|
|
|
2399
1978
|
const codeContents = [];
|
|
2400
1979
|
for (const file of session.implFiles) {
|
|
2401
1980
|
try {
|
|
2402
|
-
const content = await
|
|
1981
|
+
const content = await fs5__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2403
1982
|
codeContents.push(`// ${file}
|
|
2404
1983
|
${content}`);
|
|
2405
1984
|
} catch {
|
|
@@ -2408,7 +1987,7 @@ ${content}`);
|
|
|
2408
1987
|
const testContents = [];
|
|
2409
1988
|
for (const file of session.testFiles) {
|
|
2410
1989
|
try {
|
|
2411
|
-
const content = await
|
|
1990
|
+
const content = await fs5__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2412
1991
|
testContents.push(`// ${file}
|
|
2413
1992
|
${content}`);
|
|
2414
1993
|
} catch {
|
|
@@ -2487,7 +2066,7 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
|
2487
2066
|
}
|
|
2488
2067
|
async function readProjectContext(workingDir) {
|
|
2489
2068
|
const context = {
|
|
2490
|
-
name:
|
|
2069
|
+
name: path5__namespace.basename(workingDir),
|
|
2491
2070
|
type: "unknown",
|
|
2492
2071
|
framework: null,
|
|
2493
2072
|
techStack: [],
|
|
@@ -2496,11 +2075,11 @@ async function readProjectContext(workingDir) {
|
|
|
2496
2075
|
agentsMd: "",
|
|
2497
2076
|
configYaml: ""
|
|
2498
2077
|
};
|
|
2499
|
-
const agentsPath =
|
|
2078
|
+
const agentsPath = path5__namespace.join(workingDir, "AGENTS.md");
|
|
2500
2079
|
try {
|
|
2501
|
-
const stats = await
|
|
2080
|
+
const stats = await fs5__namespace.stat(agentsPath);
|
|
2502
2081
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2503
|
-
context.agentsMd = await
|
|
2082
|
+
context.agentsMd = await fs5__namespace.readFile(agentsPath, "utf-8");
|
|
2504
2083
|
const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
2505
2084
|
if (nameMatch) context.name = nameMatch[1];
|
|
2506
2085
|
const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
|
|
@@ -2512,11 +2091,11 @@ async function readProjectContext(workingDir) {
|
|
|
2512
2091
|
}
|
|
2513
2092
|
} catch {
|
|
2514
2093
|
}
|
|
2515
|
-
const configPath =
|
|
2094
|
+
const configPath = path5__namespace.join(workingDir, "openspec", "config.yaml");
|
|
2516
2095
|
try {
|
|
2517
|
-
const stats = await
|
|
2096
|
+
const stats = await fs5__namespace.stat(configPath);
|
|
2518
2097
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2519
|
-
context.configYaml = await
|
|
2098
|
+
context.configYaml = await fs5__namespace.readFile(configPath, "utf-8");
|
|
2520
2099
|
const nameMatch = context.configYaml.match(/name:\s*(.+)/);
|
|
2521
2100
|
if (nameMatch) context.name = nameMatch[1].trim();
|
|
2522
2101
|
const typeMatch = context.configYaml.match(/type:\s*(.+)/);
|
|
@@ -2535,11 +2114,11 @@ async function readProjectContext(workingDir) {
|
|
|
2535
2114
|
}
|
|
2536
2115
|
} catch {
|
|
2537
2116
|
}
|
|
2538
|
-
const devStandardsPath =
|
|
2117
|
+
const devStandardsPath = path5__namespace.join(workingDir, ".sf-cli", "norms", "devstanded.md");
|
|
2539
2118
|
try {
|
|
2540
|
-
const stats = await
|
|
2119
|
+
const stats = await fs5__namespace.stat(devStandardsPath);
|
|
2541
2120
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2542
|
-
context.devStandards = await
|
|
2121
|
+
context.devStandards = await fs5__namespace.readFile(devStandardsPath, "utf-8");
|
|
2543
2122
|
}
|
|
2544
2123
|
} catch {
|
|
2545
2124
|
}
|
|
@@ -2669,11 +2248,11 @@ function generateSpecItems(requirement, context, bddScenarios, questions) {
|
|
|
2669
2248
|
return items;
|
|
2670
2249
|
}
|
|
2671
2250
|
async function saveSpecFile(workingDir, session) {
|
|
2672
|
-
const specDir =
|
|
2673
|
-
await
|
|
2674
|
-
const specPath =
|
|
2251
|
+
const specDir = path5__namespace.join(workingDir, "openspec", "changes");
|
|
2252
|
+
await fs5__namespace.mkdir(specDir, { recursive: true });
|
|
2253
|
+
const specPath = path5__namespace.join(specDir, `${session.id}-spec.md`);
|
|
2675
2254
|
const content = formatSpecFile(session);
|
|
2676
|
-
await
|
|
2255
|
+
await fs5__namespace.writeFile(specPath, content, "utf-8");
|
|
2677
2256
|
return specPath;
|
|
2678
2257
|
}
|
|
2679
2258
|
function formatSpecFile(session) {
|
|
@@ -2734,14 +2313,14 @@ function formatSpecFile(session) {
|
|
|
2734
2313
|
return lines.join("\n");
|
|
2735
2314
|
}
|
|
2736
2315
|
async function generateTests(workingDir, session) {
|
|
2737
|
-
const testDir =
|
|
2738
|
-
await
|
|
2316
|
+
const testDir = path5__namespace.join(workingDir, "tests");
|
|
2317
|
+
await fs5__namespace.mkdir(testDir, { recursive: true });
|
|
2739
2318
|
const testFiles = [];
|
|
2740
2319
|
for (const scenario of session.bddScenarios) {
|
|
2741
2320
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2742
|
-
const testPath =
|
|
2321
|
+
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2743
2322
|
const content = generateTestFile(scenario);
|
|
2744
|
-
await
|
|
2323
|
+
await fs5__namespace.writeFile(testPath, content, "utf-8");
|
|
2745
2324
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2746
2325
|
}
|
|
2747
2326
|
return testFiles;
|
|
@@ -2765,9 +2344,9 @@ function generateTestFile(scenario) {
|
|
|
2765
2344
|
}
|
|
2766
2345
|
async function archiveWorkflow(workingDir) {
|
|
2767
2346
|
if (!activeSession) return;
|
|
2768
|
-
const archiveDir =
|
|
2769
|
-
await
|
|
2770
|
-
const archivePath =
|
|
2347
|
+
const archiveDir = path5__namespace.join(workingDir, "openspec", "spec");
|
|
2348
|
+
await fs5__namespace.mkdir(archiveDir, { recursive: true });
|
|
2349
|
+
const archivePath = path5__namespace.join(archiveDir, `${activeSession.id}.md`);
|
|
2771
2350
|
const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
|
|
2772
2351
|
|
|
2773
2352
|
> \u5F52\u6863\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
@@ -2793,7 +2372,7 @@ ${activeSession.refinedRequirement}
|
|
|
2793
2372
|
|
|
2794
2373
|
${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
|
|
2795
2374
|
`;
|
|
2796
|
-
await
|
|
2375
|
+
await fs5__namespace.writeFile(archivePath, content, "utf-8");
|
|
2797
2376
|
}
|
|
2798
2377
|
function generateSessionId() {
|
|
2799
2378
|
const timestamp = Date.now().toString(36);
|
|
@@ -3051,11 +2630,11 @@ var NormsManager = class {
|
|
|
3051
2630
|
const files = await this.collectProjectFiles(projectPath);
|
|
3052
2631
|
for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
|
|
3053
2632
|
try {
|
|
3054
|
-
const stats = await
|
|
2633
|
+
const stats = await fs5__namespace.stat(filePath);
|
|
3055
2634
|
if (stats.size > MAX_FILE_SIZE) {
|
|
3056
2635
|
continue;
|
|
3057
2636
|
}
|
|
3058
|
-
const content = await
|
|
2637
|
+
const content = await fs5__namespace.readFile(filePath, "utf-8");
|
|
3059
2638
|
const patterns = await this.learnFromFile(filePath, content);
|
|
3060
2639
|
result.patternsFound += patterns.length;
|
|
3061
2640
|
result.filesScanned++;
|
|
@@ -3078,7 +2657,7 @@ var NormsManager = class {
|
|
|
3078
2657
|
* 从单个文件学习规范
|
|
3079
2658
|
*/
|
|
3080
2659
|
async learnFromFile(filePath, content) {
|
|
3081
|
-
const ext =
|
|
2660
|
+
const ext = path5__namespace.extname(filePath);
|
|
3082
2661
|
const patterns = this.extractPatterns(filePath, content, ext);
|
|
3083
2662
|
for (const pattern of patterns) {
|
|
3084
2663
|
await this.recordOccurrence({
|
|
@@ -3681,16 +3260,16 @@ ${response}`;
|
|
|
3681
3260
|
const files = [];
|
|
3682
3261
|
async function scan(dir) {
|
|
3683
3262
|
try {
|
|
3684
|
-
const entries = await
|
|
3263
|
+
const entries = await fs5__namespace.readdir(dir, { withFileTypes: true });
|
|
3685
3264
|
for (const entry of entries) {
|
|
3686
3265
|
if (entry.isDirectory()) {
|
|
3687
3266
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
3688
|
-
await scan(
|
|
3267
|
+
await scan(path5__namespace.join(dir, entry.name));
|
|
3689
3268
|
}
|
|
3690
3269
|
} else if (entry.isFile()) {
|
|
3691
|
-
const ext =
|
|
3270
|
+
const ext = path5__namespace.extname(entry.name);
|
|
3692
3271
|
if (SCAN_EXTENSIONS.includes(ext)) {
|
|
3693
|
-
files.push(
|
|
3272
|
+
files.push(path5__namespace.join(dir, entry.name));
|
|
3694
3273
|
}
|
|
3695
3274
|
}
|
|
3696
3275
|
}
|
|
@@ -3724,16 +3303,16 @@ ${response}`;
|
|
|
3724
3303
|
* 确保规范目录存在
|
|
3725
3304
|
*/
|
|
3726
3305
|
async ensureNormsDir() {
|
|
3727
|
-
await
|
|
3728
|
-
await
|
|
3306
|
+
await fs5__namespace.mkdir(this.config.normsDir, { recursive: true });
|
|
3307
|
+
await fs5__namespace.mkdir(path5__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
3729
3308
|
}
|
|
3730
3309
|
/**
|
|
3731
3310
|
* 加载已有规范
|
|
3732
3311
|
*/
|
|
3733
3312
|
async loadStandards() {
|
|
3734
|
-
const standardsPath =
|
|
3313
|
+
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
3735
3314
|
try {
|
|
3736
|
-
const content = await
|
|
3315
|
+
const content = await fs5__namespace.readFile(standardsPath, "utf-8");
|
|
3737
3316
|
const data = JSON.parse(content);
|
|
3738
3317
|
for (const standard of data) {
|
|
3739
3318
|
this.standards.set(standard.id, {
|
|
@@ -3749,9 +3328,9 @@ ${response}`;
|
|
|
3749
3328
|
* 加载权重数据
|
|
3750
3329
|
*/
|
|
3751
3330
|
async loadWeights() {
|
|
3752
|
-
const weightsPath =
|
|
3331
|
+
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
3753
3332
|
try {
|
|
3754
|
-
const content = await
|
|
3333
|
+
const content = await fs5__namespace.readFile(weightsPath, "utf-8");
|
|
3755
3334
|
const data = JSON.parse(content);
|
|
3756
3335
|
for (const weight of data) {
|
|
3757
3336
|
this.weights.set(weight.standardId, {
|
|
@@ -3766,8 +3345,8 @@ ${response}`;
|
|
|
3766
3345
|
* 保存规范
|
|
3767
3346
|
*/
|
|
3768
3347
|
async saveStandards() {
|
|
3769
|
-
const standardsPath =
|
|
3770
|
-
await
|
|
3348
|
+
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
3349
|
+
await fs5__namespace.writeFile(
|
|
3771
3350
|
standardsPath,
|
|
3772
3351
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
3773
3352
|
"utf-8"
|
|
@@ -3777,8 +3356,8 @@ ${response}`;
|
|
|
3777
3356
|
* 保存权重
|
|
3778
3357
|
*/
|
|
3779
3358
|
async saveWeights() {
|
|
3780
|
-
const weightsPath =
|
|
3781
|
-
await
|
|
3359
|
+
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
3360
|
+
await fs5__namespace.writeFile(
|
|
3782
3361
|
weightsPath,
|
|
3783
3362
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
3784
3363
|
"utf-8"
|
|
@@ -3805,8 +3384,8 @@ ${response}`;
|
|
|
3805
3384
|
* 保存周报
|
|
3806
3385
|
*/
|
|
3807
3386
|
async saveWeeklyReport(weekId, report) {
|
|
3808
|
-
const reportPath =
|
|
3809
|
-
await
|
|
3387
|
+
const reportPath = path5__namespace.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
3388
|
+
await fs5__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
3810
3389
|
}
|
|
3811
3390
|
/**
|
|
3812
3391
|
* 更新 devstanded.md
|
|
@@ -3814,8 +3393,8 @@ ${response}`;
|
|
|
3814
3393
|
async updateDevStandedMd() {
|
|
3815
3394
|
const standards = this.getEffectiveStandards();
|
|
3816
3395
|
const content = this.generateDevStandedMd(standards);
|
|
3817
|
-
const mdPath =
|
|
3818
|
-
await
|
|
3396
|
+
const mdPath = path5__namespace.join(this.config.normsDir, "devstanded.md");
|
|
3397
|
+
await fs5__namespace.writeFile(mdPath, content, "utf-8");
|
|
3819
3398
|
}
|
|
3820
3399
|
/**
|
|
3821
3400
|
* 生成 devstanded.md 内容
|
|
@@ -3954,7 +3533,7 @@ async function handleInit(args, ctx) {
|
|
|
3954
3533
|
async function initProject(options = {}, workingDir) {
|
|
3955
3534
|
const cwd = workingDir || process.cwd();
|
|
3956
3535
|
try {
|
|
3957
|
-
const stats = await
|
|
3536
|
+
const stats = await fs5__namespace.stat(cwd);
|
|
3958
3537
|
if (!stats.isDirectory()) {
|
|
3959
3538
|
return {
|
|
3960
3539
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -3965,9 +3544,9 @@ async function initProject(options = {}, workingDir) {
|
|
|
3965
3544
|
output: chalk9__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
3966
3545
|
};
|
|
3967
3546
|
}
|
|
3968
|
-
const sfCliDir =
|
|
3969
|
-
const openspecDir =
|
|
3970
|
-
const agentsMdPath =
|
|
3547
|
+
const sfCliDir = path5__namespace.join(cwd, ".sf-cli");
|
|
3548
|
+
const openspecDir = path5__namespace.join(cwd, "openspec");
|
|
3549
|
+
const agentsMdPath = path5__namespace.join(cwd, "AGENTS.md");
|
|
3971
3550
|
try {
|
|
3972
3551
|
const agentsExists = await fileExists(agentsMdPath);
|
|
3973
3552
|
if (agentsExists && !options.force) {
|
|
@@ -3986,7 +3565,7 @@ async function initProject(options = {}, workingDir) {
|
|
|
3986
3565
|
await normsManager.initialize();
|
|
3987
3566
|
const scanResult = await normsManager.scanProject(cwd);
|
|
3988
3567
|
const agentsContent = generateAgentsMd(projectInfo, scanResult);
|
|
3989
|
-
await
|
|
3568
|
+
await fs5__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
|
|
3990
3569
|
await generateOpenSpecConfig(openspecDir, projectInfo);
|
|
3991
3570
|
return {
|
|
3992
3571
|
output: chalk9__default.default.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9__default.default.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
|
|
@@ -4027,7 +3606,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
4027
3606
|
"logs"
|
|
4028
3607
|
];
|
|
4029
3608
|
for (const dir of dirs) {
|
|
4030
|
-
await
|
|
3609
|
+
await fs5__namespace.mkdir(path5__namespace.join(basePath, dir), { recursive: true });
|
|
4031
3610
|
}
|
|
4032
3611
|
const config = {
|
|
4033
3612
|
version: "1.0.0",
|
|
@@ -4035,37 +3614,37 @@ async function createSfCliDirectory(basePath) {
|
|
|
4035
3614
|
yolo: false,
|
|
4036
3615
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4037
3616
|
};
|
|
4038
|
-
await
|
|
4039
|
-
|
|
3617
|
+
await fs5__namespace.writeFile(
|
|
3618
|
+
path5__namespace.join(basePath, "config.json"),
|
|
4040
3619
|
JSON.stringify(config, null, 2),
|
|
4041
3620
|
"utf-8"
|
|
4042
3621
|
);
|
|
4043
|
-
await
|
|
4044
|
-
|
|
3622
|
+
await fs5__namespace.writeFile(
|
|
3623
|
+
path5__namespace.join(basePath, "agents", "registry.json"),
|
|
4045
3624
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
4046
3625
|
"utf-8"
|
|
4047
3626
|
);
|
|
4048
|
-
await
|
|
4049
|
-
|
|
3627
|
+
await fs5__namespace.writeFile(
|
|
3628
|
+
path5__namespace.join(basePath, "skills", "registry.json"),
|
|
4050
3629
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
4051
3630
|
"utf-8"
|
|
4052
3631
|
);
|
|
4053
|
-
await
|
|
4054
|
-
|
|
3632
|
+
await fs5__namespace.writeFile(
|
|
3633
|
+
path5__namespace.join(basePath, "health", "health.md"),
|
|
4055
3634
|
generateHealthTemplate(),
|
|
4056
3635
|
"utf-8"
|
|
4057
3636
|
);
|
|
4058
3637
|
}
|
|
4059
3638
|
async function createOpenSpecDirectory(basePath) {
|
|
4060
|
-
const changesDir =
|
|
4061
|
-
const archiveDir =
|
|
4062
|
-
const specDir =
|
|
4063
|
-
await
|
|
4064
|
-
await
|
|
3639
|
+
const changesDir = path5__namespace.join(basePath, "changes");
|
|
3640
|
+
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
3641
|
+
const specDir = path5__namespace.join(basePath, "spec");
|
|
3642
|
+
await fs5__namespace.mkdir(archiveDir, { recursive: true });
|
|
3643
|
+
await fs5__namespace.mkdir(specDir, { recursive: true });
|
|
4065
3644
|
}
|
|
4066
3645
|
async function analyzeProject(cwd) {
|
|
4067
3646
|
const result = {
|
|
4068
|
-
name:
|
|
3647
|
+
name: path5__namespace.basename(cwd),
|
|
4069
3648
|
type: "unknown",
|
|
4070
3649
|
framework: null,
|
|
4071
3650
|
techStack: [],
|
|
@@ -4084,9 +3663,9 @@ async function analyzeProject(cwd) {
|
|
|
4084
3663
|
hasEslint: false,
|
|
4085
3664
|
hasPrettier: false
|
|
4086
3665
|
};
|
|
4087
|
-
const pkgPath =
|
|
3666
|
+
const pkgPath = path5__namespace.join(cwd, "package.json");
|
|
4088
3667
|
try {
|
|
4089
|
-
const pkgContent = await
|
|
3668
|
+
const pkgContent = await fs5__namespace.readFile(pkgPath, "utf-8");
|
|
4090
3669
|
const pkg = JSON.parse(pkgContent);
|
|
4091
3670
|
result.name = pkg.name || result.name;
|
|
4092
3671
|
result.dependencies = pkg.dependencies || {};
|
|
@@ -4169,7 +3748,7 @@ async function analyzeDirectoryStructure(cwd) {
|
|
|
4169
3748
|
others: []
|
|
4170
3749
|
};
|
|
4171
3750
|
try {
|
|
4172
|
-
const entries = await
|
|
3751
|
+
const entries = await fs5__namespace.readdir(cwd, { withFileTypes: true });
|
|
4173
3752
|
for (const entry of entries) {
|
|
4174
3753
|
if (!entry.isDirectory()) continue;
|
|
4175
3754
|
const name = entry.name;
|
|
@@ -4205,7 +3784,7 @@ async function findEntryPoints(cwd) {
|
|
|
4205
3784
|
"index.js"
|
|
4206
3785
|
];
|
|
4207
3786
|
for (const entry of possibleEntries) {
|
|
4208
|
-
const fullPath =
|
|
3787
|
+
const fullPath = path5__namespace.join(cwd, entry);
|
|
4209
3788
|
if (await fileExists(fullPath)) {
|
|
4210
3789
|
entryPoints.push(entry);
|
|
4211
3790
|
}
|
|
@@ -4213,15 +3792,15 @@ async function findEntryPoints(cwd) {
|
|
|
4213
3792
|
return entryPoints;
|
|
4214
3793
|
}
|
|
4215
3794
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
4216
|
-
const analysisPath =
|
|
4217
|
-
await
|
|
3795
|
+
const analysisPath = path5__namespace.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
3796
|
+
await fs5__namespace.writeFile(
|
|
4218
3797
|
analysisPath,
|
|
4219
3798
|
JSON.stringify(analysis, null, 2),
|
|
4220
3799
|
"utf-8"
|
|
4221
3800
|
);
|
|
4222
3801
|
}
|
|
4223
3802
|
async function generateOpenSpecConfig(openspecDir, analysis) {
|
|
4224
|
-
const configPath =
|
|
3803
|
+
const configPath = path5__namespace.join(openspecDir, "config.yaml");
|
|
4225
3804
|
const content = `# OpenSpec \u9879\u76EE\u914D\u7F6E
|
|
4226
3805
|
# \u6B64\u6587\u4EF6\u5B9A\u4E49\u9879\u76EE\u7684\u57FA\u672C\u4FE1\u606F\uFF0C\u7528\u4E8E\u6307\u5BFC AI \u7406\u89E3\u9879\u76EE\u4E0A\u4E0B\u6587
|
|
4227
3806
|
|
|
@@ -4255,7 +3834,7 @@ architecture:
|
|
|
4255
3834
|
# \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
|
|
4256
3835
|
standards: []
|
|
4257
3836
|
`;
|
|
4258
|
-
await
|
|
3837
|
+
await fs5__namespace.writeFile(configPath, content, "utf-8");
|
|
4259
3838
|
}
|
|
4260
3839
|
function generateAgentsMd(info, scanResult) {
|
|
4261
3840
|
const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
|
|
@@ -4377,7 +3956,7 @@ function generateHealthTemplate() {
|
|
|
4377
3956
|
}
|
|
4378
3957
|
async function fileExists(filePath) {
|
|
4379
3958
|
try {
|
|
4380
|
-
await
|
|
3959
|
+
await fs5__namespace.access(filePath);
|
|
4381
3960
|
return true;
|
|
4382
3961
|
} catch {
|
|
4383
3962
|
return false;
|
|
@@ -4541,7 +4120,7 @@ ${chalk9__default.default.yellow("\u793A\u4F8B:")}
|
|
|
4541
4120
|
}
|
|
4542
4121
|
|
|
4543
4122
|
// src/commands/runner.ts
|
|
4544
|
-
|
|
4123
|
+
init_model2();
|
|
4545
4124
|
init_update();
|
|
4546
4125
|
|
|
4547
4126
|
// src/commands/clear.ts
|
|
@@ -4832,7 +4411,7 @@ var WorkflowEngine = class {
|
|
|
4832
4411
|
*/
|
|
4833
4412
|
async initialize(projectPath) {
|
|
4834
4413
|
this.projectPath = projectPath;
|
|
4835
|
-
this.openspecPath =
|
|
4414
|
+
this.openspecPath = path5__namespace.join(projectPath, "openspec");
|
|
4836
4415
|
await this.ensureDirectories();
|
|
4837
4416
|
await this.loadProjectContext();
|
|
4838
4417
|
await this.restoreState();
|
|
@@ -4842,21 +4421,21 @@ var WorkflowEngine = class {
|
|
|
4842
4421
|
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
4843
4422
|
*/
|
|
4844
4423
|
async loadProjectContext() {
|
|
4845
|
-
const agentsMdPath =
|
|
4424
|
+
const agentsMdPath = path5__namespace.join(this.projectPath, "AGENTS.md");
|
|
4846
4425
|
try {
|
|
4847
|
-
this.projectContext = await
|
|
4426
|
+
this.projectContext = await fs5__namespace.readFile(agentsMdPath, "utf-8");
|
|
4848
4427
|
} catch {
|
|
4849
4428
|
this.projectContext = "";
|
|
4850
4429
|
}
|
|
4851
|
-
const configPath =
|
|
4430
|
+
const configPath = path5__namespace.join(this.openspecPath, "config.yaml");
|
|
4852
4431
|
try {
|
|
4853
|
-
this.projectConfig = await
|
|
4432
|
+
this.projectConfig = await fs5__namespace.readFile(configPath, "utf-8");
|
|
4854
4433
|
} catch {
|
|
4855
4434
|
this.projectConfig = "";
|
|
4856
4435
|
}
|
|
4857
|
-
const devstandedPath =
|
|
4436
|
+
const devstandedPath = path5__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
4858
4437
|
try {
|
|
4859
|
-
this.devStandards = await
|
|
4438
|
+
this.devStandards = await fs5__namespace.readFile(devstandedPath, "utf-8");
|
|
4860
4439
|
} catch {
|
|
4861
4440
|
this.devStandards = "";
|
|
4862
4441
|
}
|
|
@@ -4876,7 +4455,7 @@ var WorkflowEngine = class {
|
|
|
4876
4455
|
*/
|
|
4877
4456
|
getSpecFilePath() {
|
|
4878
4457
|
if (!this.state) return null;
|
|
4879
|
-
return
|
|
4458
|
+
return path5__namespace.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
|
|
4880
4459
|
}
|
|
4881
4460
|
/**
|
|
4882
4461
|
* 检查规格文件是否存在
|
|
@@ -4885,7 +4464,7 @@ var WorkflowEngine = class {
|
|
|
4885
4464
|
const specPath = this.getSpecFilePath();
|
|
4886
4465
|
if (!specPath) return false;
|
|
4887
4466
|
try {
|
|
4888
|
-
await
|
|
4467
|
+
await fs5__namespace.access(specPath);
|
|
4889
4468
|
return true;
|
|
4890
4469
|
} catch {
|
|
4891
4470
|
return false;
|
|
@@ -5117,13 +4696,13 @@ var WorkflowEngine = class {
|
|
|
5117
4696
|
*/
|
|
5118
4697
|
async getAllActiveWorkflows() {
|
|
5119
4698
|
const workflows = [];
|
|
5120
|
-
const changesDir =
|
|
4699
|
+
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
5121
4700
|
try {
|
|
5122
|
-
const files = await
|
|
4701
|
+
const files = await fs5__namespace.readdir(changesDir);
|
|
5123
4702
|
for (const file of files) {
|
|
5124
4703
|
if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
|
|
5125
|
-
const filePath =
|
|
5126
|
-
const content = await
|
|
4704
|
+
const filePath = path5__namespace.join(changesDir, file);
|
|
4705
|
+
const content = await fs5__namespace.readFile(filePath, "utf-8");
|
|
5127
4706
|
const state = this.parseChangeRecord(content);
|
|
5128
4707
|
if (state && state.status === "running") {
|
|
5129
4708
|
workflows.push(state);
|
|
@@ -5172,9 +4751,9 @@ var WorkflowEngine = class {
|
|
|
5172
4751
|
if (this.state) {
|
|
5173
4752
|
await this.saveState();
|
|
5174
4753
|
}
|
|
5175
|
-
const statePath =
|
|
4754
|
+
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
|
|
5176
4755
|
try {
|
|
5177
|
-
const content = await
|
|
4756
|
+
const content = await fs5__namespace.readFile(statePath, "utf-8");
|
|
5178
4757
|
this.state = JSON.parse(content, (key, value) => {
|
|
5179
4758
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5180
4759
|
return new Date(value);
|
|
@@ -5184,10 +4763,10 @@ var WorkflowEngine = class {
|
|
|
5184
4763
|
await this.restoreSnapshots();
|
|
5185
4764
|
return true;
|
|
5186
4765
|
} catch {
|
|
5187
|
-
const changesDir =
|
|
5188
|
-
const changeFile =
|
|
4766
|
+
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
4767
|
+
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
5189
4768
|
try {
|
|
5190
|
-
const content = await
|
|
4769
|
+
const content = await fs5__namespace.readFile(changeFile, "utf-8");
|
|
5191
4770
|
const parsed = this.parseChangeRecord(content);
|
|
5192
4771
|
if (parsed && parsed.status === "running") {
|
|
5193
4772
|
this.state = parsed;
|
|
@@ -5248,12 +4827,12 @@ var WorkflowEngine = class {
|
|
|
5248
4827
|
await this.createSpecDocument(summary);
|
|
5249
4828
|
await this.updateChangeRecord("archived");
|
|
5250
4829
|
await this.saveState();
|
|
5251
|
-
const changesDir =
|
|
5252
|
-
const archiveDir =
|
|
5253
|
-
const changeFile =
|
|
5254
|
-
const archiveFile =
|
|
5255
|
-
await
|
|
5256
|
-
await
|
|
4830
|
+
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
4831
|
+
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
4832
|
+
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
4833
|
+
const archiveFile = path5__namespace.join(archiveDir, `${changeId}.md`);
|
|
4834
|
+
await fs5__namespace.mkdir(archiveDir, { recursive: true });
|
|
4835
|
+
await fs5__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
5257
4836
|
});
|
|
5258
4837
|
this.state = null;
|
|
5259
4838
|
this.snapshots.clear();
|
|
@@ -5275,27 +4854,27 @@ var WorkflowEngine = class {
|
|
|
5275
4854
|
}
|
|
5276
4855
|
// ==================== 私有方法 ====================
|
|
5277
4856
|
async ensureDirectories() {
|
|
5278
|
-
const changesDir =
|
|
5279
|
-
const archiveDir =
|
|
5280
|
-
const specDir =
|
|
5281
|
-
const statesDir =
|
|
5282
|
-
await
|
|
5283
|
-
await
|
|
5284
|
-
await
|
|
4857
|
+
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
4858
|
+
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
4859
|
+
const specDir = path5__namespace.join(this.openspecPath, "spec");
|
|
4860
|
+
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
4861
|
+
await fs5__namespace.mkdir(archiveDir, { recursive: true });
|
|
4862
|
+
await fs5__namespace.mkdir(specDir, { recursive: true });
|
|
4863
|
+
await fs5__namespace.mkdir(statesDir, { recursive: true });
|
|
5285
4864
|
}
|
|
5286
4865
|
async restoreState() {
|
|
5287
|
-
const activePath =
|
|
4866
|
+
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5288
4867
|
let activeId = null;
|
|
5289
4868
|
try {
|
|
5290
|
-
const activeContent = await
|
|
4869
|
+
const activeContent = await fs5__namespace.readFile(activePath, "utf-8");
|
|
5291
4870
|
const activeData = JSON.parse(activeContent);
|
|
5292
4871
|
activeId = activeData.activeId;
|
|
5293
4872
|
} catch {
|
|
5294
4873
|
}
|
|
5295
4874
|
if (activeId) {
|
|
5296
|
-
const statePath =
|
|
4875
|
+
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
5297
4876
|
try {
|
|
5298
|
-
const content = await
|
|
4877
|
+
const content = await fs5__namespace.readFile(statePath, "utf-8");
|
|
5299
4878
|
this.state = JSON.parse(content, (key, value) => {
|
|
5300
4879
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5301
4880
|
return new Date(value);
|
|
@@ -5306,9 +4885,9 @@ var WorkflowEngine = class {
|
|
|
5306
4885
|
} catch {
|
|
5307
4886
|
}
|
|
5308
4887
|
}
|
|
5309
|
-
const oldStatePath =
|
|
4888
|
+
const oldStatePath = path5__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
5310
4889
|
try {
|
|
5311
|
-
const content = await
|
|
4890
|
+
const content = await fs5__namespace.readFile(oldStatePath, "utf-8");
|
|
5312
4891
|
this.state = JSON.parse(content, (key, value) => {
|
|
5313
4892
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5314
4893
|
return new Date(value);
|
|
@@ -5317,7 +4896,7 @@ var WorkflowEngine = class {
|
|
|
5317
4896
|
});
|
|
5318
4897
|
if (this.state) {
|
|
5319
4898
|
await this.saveState();
|
|
5320
|
-
await
|
|
4899
|
+
await fs5__namespace.unlink(oldStatePath).catch(() => {
|
|
5321
4900
|
});
|
|
5322
4901
|
}
|
|
5323
4902
|
} catch (e) {
|
|
@@ -5330,17 +4909,17 @@ var WorkflowEngine = class {
|
|
|
5330
4909
|
}
|
|
5331
4910
|
async saveState() {
|
|
5332
4911
|
if (!this.state) return;
|
|
5333
|
-
const statesDir =
|
|
5334
|
-
await
|
|
5335
|
-
const statePath =
|
|
5336
|
-
await
|
|
5337
|
-
const activePath =
|
|
5338
|
-
await
|
|
4912
|
+
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
4913
|
+
await fs5__namespace.mkdir(statesDir, { recursive: true });
|
|
4914
|
+
const statePath = path5__namespace.join(statesDir, `${this.state.id}.json`);
|
|
4915
|
+
await fs5__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
4916
|
+
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
4917
|
+
await fs5__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
5339
4918
|
}
|
|
5340
4919
|
async restoreSnapshots() {
|
|
5341
|
-
const snapshotsPath =
|
|
4920
|
+
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5342
4921
|
try {
|
|
5343
|
-
const content = await
|
|
4922
|
+
const content = await fs5__namespace.readFile(snapshotsPath, "utf-8");
|
|
5344
4923
|
const data = JSON.parse(content, (key, value) => {
|
|
5345
4924
|
if (key === "timestamp" && typeof value === "string") {
|
|
5346
4925
|
return new Date(value);
|
|
@@ -5355,9 +4934,9 @@ var WorkflowEngine = class {
|
|
|
5355
4934
|
}
|
|
5356
4935
|
}
|
|
5357
4936
|
async saveSnapshots() {
|
|
5358
|
-
const snapshotsPath =
|
|
4937
|
+
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5359
4938
|
const data = Array.from(this.snapshots.values());
|
|
5360
|
-
await
|
|
4939
|
+
await fs5__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
5361
4940
|
}
|
|
5362
4941
|
async createSnapshot() {
|
|
5363
4942
|
if (!this.state) return;
|
|
@@ -5377,16 +4956,16 @@ var WorkflowEngine = class {
|
|
|
5377
4956
|
}
|
|
5378
4957
|
async createChangeRecord() {
|
|
5379
4958
|
if (!this.state) return;
|
|
5380
|
-
const changePath =
|
|
5381
|
-
await
|
|
4959
|
+
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4960
|
+
await fs5__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5382
4961
|
}
|
|
5383
4962
|
async updateChangeRecord(status) {
|
|
5384
4963
|
if (!this.state) return;
|
|
5385
4964
|
if (status) {
|
|
5386
4965
|
this.state.status = status;
|
|
5387
4966
|
}
|
|
5388
|
-
const changePath =
|
|
5389
|
-
await
|
|
4967
|
+
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4968
|
+
await fs5__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5390
4969
|
}
|
|
5391
4970
|
formatChangeRecord() {
|
|
5392
4971
|
if (!this.state) return "";
|
|
@@ -5425,7 +5004,7 @@ ${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
|
5425
5004
|
}
|
|
5426
5005
|
async createSpecDocument(summary) {
|
|
5427
5006
|
if (!this.state) return;
|
|
5428
|
-
const specPath =
|
|
5007
|
+
const specPath = path5__namespace.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
5429
5008
|
const content = `# Spec: ${this.state.title}
|
|
5430
5009
|
|
|
5431
5010
|
> \u53D8\u66F4ID: ${this.state.id}
|
|
@@ -5450,7 +5029,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
|
|
|
5450
5029
|
|
|
5451
5030
|
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
5452
5031
|
`;
|
|
5453
|
-
await
|
|
5032
|
+
await fs5__namespace.writeFile(specPath, content);
|
|
5454
5033
|
}
|
|
5455
5034
|
};
|
|
5456
5035
|
var ConfirmationRequiredError = class extends Error {
|
|
@@ -5947,15 +5526,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
5947
5526
|
devStandards: ""
|
|
5948
5527
|
};
|
|
5949
5528
|
try {
|
|
5950
|
-
context.agentsMd = await
|
|
5529
|
+
context.agentsMd = await fs5__namespace.readFile(path5__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
5951
5530
|
} catch {
|
|
5952
5531
|
}
|
|
5953
5532
|
try {
|
|
5954
|
-
context.configYaml = await
|
|
5533
|
+
context.configYaml = await fs5__namespace.readFile(path5__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
5955
5534
|
} catch {
|
|
5956
5535
|
}
|
|
5957
5536
|
try {
|
|
5958
|
-
context.devStandards = await
|
|
5537
|
+
context.devStandards = await fs5__namespace.readFile(path5__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
5959
5538
|
} catch {
|
|
5960
5539
|
}
|
|
5961
5540
|
return context;
|
|
@@ -6422,11 +6001,11 @@ ${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cy
|
|
|
6422
6001
|
}
|
|
6423
6002
|
async function updateChangelog(workingDirectory, summary, changeId) {
|
|
6424
6003
|
try {
|
|
6425
|
-
const changelogPath =
|
|
6426
|
-
const pkgPath =
|
|
6004
|
+
const changelogPath = path5__namespace.join(workingDirectory, "CHANGELOG.md");
|
|
6005
|
+
const pkgPath = path5__namespace.join(workingDirectory, "package.json");
|
|
6427
6006
|
let version = "1.0.0";
|
|
6428
6007
|
try {
|
|
6429
|
-
const pkgContent =
|
|
6008
|
+
const pkgContent = fs7__namespace.readFileSync(pkgPath, "utf-8");
|
|
6430
6009
|
const pkg = JSON.parse(pkgContent);
|
|
6431
6010
|
version = pkg.version || "1.0.0";
|
|
6432
6011
|
} catch {
|
|
@@ -6440,22 +6019,22 @@ async function updateChangelog(workingDirectory, summary, changeId) {
|
|
|
6440
6019
|
|
|
6441
6020
|
- ${summary} (${changeId})
|
|
6442
6021
|
`;
|
|
6443
|
-
if (
|
|
6444
|
-
const content =
|
|
6022
|
+
if (fs7__namespace.existsSync(changelogPath)) {
|
|
6023
|
+
const content = fs7__namespace.readFileSync(changelogPath, "utf-8");
|
|
6445
6024
|
const versionPattern = /^## v\d+\.\d+\.\d+/m;
|
|
6446
6025
|
const match = content.match(versionPattern);
|
|
6447
6026
|
if (match && match.index !== void 0) {
|
|
6448
6027
|
const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
|
|
6449
|
-
|
|
6028
|
+
fs7__namespace.writeFileSync(changelogPath, newContent, "utf-8");
|
|
6450
6029
|
} else {
|
|
6451
|
-
|
|
6030
|
+
fs7__namespace.appendFileSync(changelogPath, entry, "utf-8");
|
|
6452
6031
|
}
|
|
6453
6032
|
} else {
|
|
6454
6033
|
const header = `# Changelog
|
|
6455
6034
|
|
|
6456
6035
|
All notable changes to this project will be documented in this file.
|
|
6457
6036
|
`;
|
|
6458
|
-
|
|
6037
|
+
fs7__namespace.writeFileSync(changelogPath, header + entry, "utf-8");
|
|
6459
6038
|
}
|
|
6460
6039
|
} catch (error) {
|
|
6461
6040
|
}
|
|
@@ -6585,7 +6164,7 @@ async function handleRegenerateSpec(workflow, ctx) {
|
|
|
6585
6164
|
return { output: chalk9__default.default.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
|
|
6586
6165
|
}
|
|
6587
6166
|
const workingDir = ctx.options.workingDirectory;
|
|
6588
|
-
const specPath =
|
|
6167
|
+
const specPath = path5__namespace.join(workingDir, "openspec", "changes", `${state.id}-spec.md`);
|
|
6589
6168
|
workflow.confirm("spec-review", "regenerate");
|
|
6590
6169
|
const lines = [];
|
|
6591
6170
|
lines.push(chalk9__default.default.cyan("\u{1F504} \u91CD\u65B0\u751F\u6210\u89C4\u683C..."));
|
|
@@ -6594,7 +6173,7 @@ async function handleRegenerateSpec(workflow, ctx) {
|
|
|
6594
6173
|
const context = await readProjectContext2(workingDir);
|
|
6595
6174
|
const analysis = analyzeComplexity2(state.requirement, context);
|
|
6596
6175
|
const specContent = await generateSpecContent(state.id, state.requirement, analysis, context);
|
|
6597
|
-
await
|
|
6176
|
+
await fs7__namespace.promises.writeFile(specPath, specContent, "utf-8");
|
|
6598
6177
|
lines.push(chalk9__default.default.green("\u2713 \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210"));
|
|
6599
6178
|
lines.push(chalk9__default.default.gray(`\u8DEF\u5F84: ${specPath}`));
|
|
6600
6179
|
lines.push("");
|
|
@@ -6688,9 +6267,9 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
|
|
|
6688
6267
|
return { output: lines.join("\n") };
|
|
6689
6268
|
}
|
|
6690
6269
|
async function checkPendingSpec(workingDirectory, changeId) {
|
|
6691
|
-
const specPath =
|
|
6270
|
+
const specPath = path5__namespace.join(workingDirectory, "openspec", "changes", `${changeId}-spec.md`);
|
|
6692
6271
|
try {
|
|
6693
|
-
await
|
|
6272
|
+
await fs7__namespace.promises.access(specPath);
|
|
6694
6273
|
return specPath;
|
|
6695
6274
|
} catch {
|
|
6696
6275
|
return null;
|
|
@@ -6804,13 +6383,13 @@ async function handleSwitch(workflow, args, ctx) {
|
|
|
6804
6383
|
// src/commands/runner.ts
|
|
6805
6384
|
function getVersion2() {
|
|
6806
6385
|
const possiblePaths = [
|
|
6807
|
-
|
|
6808
|
-
|
|
6386
|
+
path5__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
6387
|
+
path5__namespace.resolve(__dirname, "..", "..", "..", "package.json")
|
|
6809
6388
|
];
|
|
6810
6389
|
for (const pkgPath of possiblePaths) {
|
|
6811
6390
|
try {
|
|
6812
|
-
if (
|
|
6813
|
-
const content =
|
|
6391
|
+
if (fs7__namespace.existsSync(pkgPath)) {
|
|
6392
|
+
const content = fs7__namespace.readFileSync(pkgPath, "utf-8");
|
|
6814
6393
|
const pkg = JSON.parse(content);
|
|
6815
6394
|
return pkg.version;
|
|
6816
6395
|
}
|
|
@@ -6867,7 +6446,7 @@ function normalizeCommand(command) {
|
|
|
6867
6446
|
}
|
|
6868
6447
|
|
|
6869
6448
|
// src/commands/index.ts
|
|
6870
|
-
|
|
6449
|
+
init_model2();
|
|
6871
6450
|
init_update();
|
|
6872
6451
|
init_new();
|
|
6873
6452
|
|
|
@@ -6875,11 +6454,11 @@ init_new();
|
|
|
6875
6454
|
init_cjs_shims();
|
|
6876
6455
|
async function handleFileReference(filePath, ctx) {
|
|
6877
6456
|
const cwd = ctx.options.workingDirectory;
|
|
6878
|
-
const absolutePath =
|
|
6457
|
+
const absolutePath = path5__namespace.isAbsolute(filePath) ? filePath : path5__namespace.join(cwd, filePath);
|
|
6879
6458
|
try {
|
|
6880
|
-
const stats = await
|
|
6459
|
+
const stats = await fs5__namespace.stat(absolutePath);
|
|
6881
6460
|
if (stats.isDirectory()) {
|
|
6882
|
-
const files = await
|
|
6461
|
+
const files = await fs5__namespace.readdir(absolutePath);
|
|
6883
6462
|
return {
|
|
6884
6463
|
output: chalk9__default.default.cyan(`\u{1F4C1} ${filePath}/`) + chalk9__default.default.gray(`
|
|
6885
6464
|
${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.default.gray(`
|
|
@@ -6887,7 +6466,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.defaul
|
|
|
6887
6466
|
contextUsed: 0
|
|
6888
6467
|
};
|
|
6889
6468
|
}
|
|
6890
|
-
const content = await
|
|
6469
|
+
const content = await fs5__namespace.readFile(absolutePath, "utf-8");
|
|
6891
6470
|
const lines = content.split("\n");
|
|
6892
6471
|
ctx.contextManager.addMessage({
|
|
6893
6472
|
role: "user",
|
|
@@ -7197,7 +6776,7 @@ var ContextManager = class {
|
|
|
7197
6776
|
}
|
|
7198
6777
|
async initialize(projectPath) {
|
|
7199
6778
|
this.projectPath = projectPath;
|
|
7200
|
-
this.persistPath =
|
|
6779
|
+
this.persistPath = path5__namespace.join(projectPath, ".sf-cli", "cache", "context", "context.json");
|
|
7201
6780
|
await this.loadPersistedContext();
|
|
7202
6781
|
}
|
|
7203
6782
|
/**
|
|
@@ -7438,15 +7017,15 @@ ${summary}`,
|
|
|
7438
7017
|
*/
|
|
7439
7018
|
async persist() {
|
|
7440
7019
|
if (!this.persistPath) return;
|
|
7441
|
-
const dir =
|
|
7442
|
-
await
|
|
7020
|
+
const dir = path5__namespace.dirname(this.persistPath);
|
|
7021
|
+
await fs5__namespace.mkdir(dir, { recursive: true });
|
|
7443
7022
|
const data = {
|
|
7444
7023
|
messages: this.state.messages,
|
|
7445
7024
|
totalTokens: this.state.totalTokens,
|
|
7446
7025
|
limit: this.state.limit,
|
|
7447
7026
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7448
7027
|
};
|
|
7449
|
-
await
|
|
7028
|
+
await fs5__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7450
7029
|
}
|
|
7451
7030
|
/**
|
|
7452
7031
|
* 加载持久化的上下文
|
|
@@ -7454,7 +7033,7 @@ ${summary}`,
|
|
|
7454
7033
|
async loadPersistedContext() {
|
|
7455
7034
|
if (!this.persistPath) return;
|
|
7456
7035
|
try {
|
|
7457
|
-
const content = await
|
|
7036
|
+
const content = await fs5__namespace.readFile(this.persistPath, "utf-8");
|
|
7458
7037
|
const data = JSON.parse(content);
|
|
7459
7038
|
this.state.messages = data.messages || [];
|
|
7460
7039
|
this.state.totalTokens = data.totalTokens || 0;
|
|
@@ -7483,8 +7062,400 @@ ${summary}`,
|
|
|
7483
7062
|
}
|
|
7484
7063
|
};
|
|
7485
7064
|
|
|
7486
|
-
// src/
|
|
7487
|
-
|
|
7065
|
+
// src/services/index.ts
|
|
7066
|
+
init_cjs_shims();
|
|
7067
|
+
|
|
7068
|
+
// src/services/config.ts
|
|
7069
|
+
init_cjs_shims();
|
|
7070
|
+
var DEFAULT_CONFIG = {
|
|
7071
|
+
model: "GLM-5",
|
|
7072
|
+
apiKey: "",
|
|
7073
|
+
yolo: false,
|
|
7074
|
+
locale: "zh-CN",
|
|
7075
|
+
theme: "auto"
|
|
7076
|
+
};
|
|
7077
|
+
var ALGORITHM = "aes-256-gcm";
|
|
7078
|
+
var IV_LENGTH = 16;
|
|
7079
|
+
var KEY_DIR = ".sf-cli";
|
|
7080
|
+
var KEY_FILE = ".key";
|
|
7081
|
+
function getOrCreateEncryptionKey() {
|
|
7082
|
+
const keyPath = path5__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
|
|
7083
|
+
try {
|
|
7084
|
+
if (fs7__namespace.existsSync(keyPath)) {
|
|
7085
|
+
const keyBase64 = fs7__namespace.readFileSync(keyPath, "utf-8").trim();
|
|
7086
|
+
return Buffer.from(keyBase64, "base64");
|
|
7087
|
+
}
|
|
7088
|
+
} catch {
|
|
7089
|
+
}
|
|
7090
|
+
const key = crypto__namespace.randomBytes(32);
|
|
7091
|
+
try {
|
|
7092
|
+
const keyDir = path5__namespace.dirname(keyPath);
|
|
7093
|
+
if (!fs7__namespace.existsSync(keyDir)) {
|
|
7094
|
+
fs7__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
7095
|
+
}
|
|
7096
|
+
fs7__namespace.writeFileSync(keyPath, key.toString("base64"), {
|
|
7097
|
+
mode: 384,
|
|
7098
|
+
// 仅所有者可读写
|
|
7099
|
+
encoding: "utf-8"
|
|
7100
|
+
});
|
|
7101
|
+
return key;
|
|
7102
|
+
} catch {
|
|
7103
|
+
const machineId = [
|
|
7104
|
+
os__namespace.hostname(),
|
|
7105
|
+
os__namespace.platform(),
|
|
7106
|
+
os__namespace.arch(),
|
|
7107
|
+
process.env.USERNAME || process.env.USER || "default"
|
|
7108
|
+
].join("-");
|
|
7109
|
+
return crypto__namespace.createHash("sha256").update(machineId).digest();
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
var ConfigManager = class {
|
|
7113
|
+
config;
|
|
7114
|
+
configPath = "";
|
|
7115
|
+
projectPath = "";
|
|
7116
|
+
encryptionKey;
|
|
7117
|
+
constructor() {
|
|
7118
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
7119
|
+
this.encryptionKey = getOrCreateEncryptionKey();
|
|
7120
|
+
}
|
|
7121
|
+
async load(projectPath) {
|
|
7122
|
+
this.projectPath = projectPath;
|
|
7123
|
+
this.configPath = path5__namespace.join(projectPath, ".sf-cli", "config.json");
|
|
7124
|
+
try {
|
|
7125
|
+
const content = await fs5__namespace.readFile(this.configPath, "utf-8");
|
|
7126
|
+
const loaded = JSON.parse(content);
|
|
7127
|
+
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
7128
|
+
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
7129
|
+
delete loaded.apiKeyEncrypted;
|
|
7130
|
+
}
|
|
7131
|
+
this.config = { ...DEFAULT_CONFIG, ...loaded };
|
|
7132
|
+
} catch {
|
|
7133
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
async save() {
|
|
7137
|
+
if (!this.configPath) return;
|
|
7138
|
+
const configToSave = { ...this.config };
|
|
7139
|
+
if (configToSave.apiKey) {
|
|
7140
|
+
const encrypted = this.encrypt(configToSave.apiKey);
|
|
7141
|
+
configToSave.apiKey = encrypted;
|
|
7142
|
+
configToSave.apiKeyEncrypted = true;
|
|
7143
|
+
}
|
|
7144
|
+
await fs5__namespace.mkdir(path5__namespace.dirname(this.configPath), { recursive: true });
|
|
7145
|
+
await fs5__namespace.writeFile(
|
|
7146
|
+
this.configPath,
|
|
7147
|
+
JSON.stringify(configToSave, null, 2),
|
|
7148
|
+
"utf-8"
|
|
7149
|
+
);
|
|
7150
|
+
}
|
|
7151
|
+
get(key) {
|
|
7152
|
+
return this.config[key];
|
|
7153
|
+
}
|
|
7154
|
+
set(key, value) {
|
|
7155
|
+
this.config[key] = value;
|
|
7156
|
+
}
|
|
7157
|
+
getAll() {
|
|
7158
|
+
return { ...this.config };
|
|
7159
|
+
}
|
|
7160
|
+
update(updates) {
|
|
7161
|
+
this.config = { ...this.config, ...updates };
|
|
7162
|
+
}
|
|
7163
|
+
/**
|
|
7164
|
+
* 加密敏感数据
|
|
7165
|
+
*/
|
|
7166
|
+
encrypt(text) {
|
|
7167
|
+
const iv = crypto__namespace.randomBytes(IV_LENGTH);
|
|
7168
|
+
const cipher = crypto__namespace.createCipheriv(ALGORITHM, this.encryptionKey, iv);
|
|
7169
|
+
let encrypted = cipher.update(text, "utf8", "base64");
|
|
7170
|
+
encrypted += cipher.final("base64");
|
|
7171
|
+
const authTag = cipher.getAuthTag();
|
|
7172
|
+
return `${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`;
|
|
7173
|
+
}
|
|
7174
|
+
/**
|
|
7175
|
+
* 解密敏感数据
|
|
7176
|
+
*/
|
|
7177
|
+
decrypt(encryptedData) {
|
|
7178
|
+
try {
|
|
7179
|
+
const parts = encryptedData.split(":");
|
|
7180
|
+
if (parts.length !== 3) {
|
|
7181
|
+
return encryptedData;
|
|
7182
|
+
}
|
|
7183
|
+
const [ivBase64, authTagBase64, encrypted] = parts;
|
|
7184
|
+
const iv = Buffer.from(ivBase64, "base64");
|
|
7185
|
+
const authTag = Buffer.from(authTagBase64, "base64");
|
|
7186
|
+
const decipher = crypto__namespace.createDecipheriv(ALGORITHM, this.encryptionKey, iv);
|
|
7187
|
+
decipher.setAuthTag(authTag);
|
|
7188
|
+
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
7189
|
+
decrypted += decipher.final("utf8");
|
|
7190
|
+
return decrypted;
|
|
7191
|
+
} catch {
|
|
7192
|
+
return "";
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
};
|
|
7196
|
+
|
|
7197
|
+
// src/services/model.ts
|
|
7198
|
+
init_cjs_shims();
|
|
7199
|
+
init_model();
|
|
7200
|
+
init_adapters();
|
|
7201
|
+
var ModelService = class {
|
|
7202
|
+
adapters = /* @__PURE__ */ new Map();
|
|
7203
|
+
currentConfig = null;
|
|
7204
|
+
stats;
|
|
7205
|
+
configManager;
|
|
7206
|
+
statsPath = "";
|
|
7207
|
+
constructor(configManager) {
|
|
7208
|
+
this.configManager = configManager;
|
|
7209
|
+
this.stats = {
|
|
7210
|
+
totalInput: 0,
|
|
7211
|
+
totalOutput: 0,
|
|
7212
|
+
byModel: {},
|
|
7213
|
+
byAgent: {},
|
|
7214
|
+
byDate: {}
|
|
7215
|
+
};
|
|
7216
|
+
}
|
|
7217
|
+
/**
|
|
7218
|
+
* 初始化服务
|
|
7219
|
+
*/
|
|
7220
|
+
async initialize(statsDir) {
|
|
7221
|
+
this.statsPath = path5__namespace.join(statsDir, "tokens");
|
|
7222
|
+
await this.loadStats();
|
|
7223
|
+
const model = this.configManager.get("model");
|
|
7224
|
+
const apiKey = this.configManager.get("apiKey");
|
|
7225
|
+
if (model && apiKey) {
|
|
7226
|
+
await this.configureModel({
|
|
7227
|
+
provider: this.getProviderFromModel(model),
|
|
7228
|
+
model,
|
|
7229
|
+
apiKey
|
|
7230
|
+
});
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
/**
|
|
7234
|
+
* 配置模型
|
|
7235
|
+
*/
|
|
7236
|
+
async configureModel(config) {
|
|
7237
|
+
let adapter = this.adapters.get(config.provider);
|
|
7238
|
+
if (!adapter) {
|
|
7239
|
+
adapter = createAdapter(config.provider);
|
|
7240
|
+
this.adapters.set(config.provider, adapter);
|
|
7241
|
+
}
|
|
7242
|
+
await adapter.initialize(config);
|
|
7243
|
+
this.currentConfig = config;
|
|
7244
|
+
this.configManager.set("model", config.model);
|
|
7245
|
+
this.configManager.set("apiKey", config.apiKey);
|
|
7246
|
+
await this.configManager.save();
|
|
7247
|
+
}
|
|
7248
|
+
/**
|
|
7249
|
+
* 发送消息
|
|
7250
|
+
*/
|
|
7251
|
+
async sendMessage(messages, options) {
|
|
7252
|
+
if (!this.currentConfig) {
|
|
7253
|
+
throw new Error("\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C /model \u547D\u4EE4");
|
|
7254
|
+
}
|
|
7255
|
+
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
7256
|
+
if (!adapter || !adapter.isInitialized()) {
|
|
7257
|
+
throw new Error("\u6A21\u578B\u9002\u914D\u5668\u672A\u521D\u59CB\u5316");
|
|
7258
|
+
}
|
|
7259
|
+
try {
|
|
7260
|
+
const response = await adapter.sendMessage(messages, options);
|
|
7261
|
+
this.updateStats(response.usage, {
|
|
7262
|
+
model: this.currentConfig.model,
|
|
7263
|
+
agent: options?.agent
|
|
7264
|
+
});
|
|
7265
|
+
this.saveStats().catch(() => {
|
|
7266
|
+
});
|
|
7267
|
+
return response;
|
|
7268
|
+
} catch (error) {
|
|
7269
|
+
throw this.wrapError(error);
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
/**
|
|
7273
|
+
* 流式发送消息
|
|
7274
|
+
*/
|
|
7275
|
+
async *streamMessage(messages, options) {
|
|
7276
|
+
if (!this.currentConfig) {
|
|
7277
|
+
throw new Error("\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C /model \u547D\u4EE4");
|
|
7278
|
+
}
|
|
7279
|
+
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
7280
|
+
if (!adapter || !adapter.isInitialized()) {
|
|
7281
|
+
throw new Error("\u6A21\u578B\u9002\u914D\u5668\u672A\u521D\u59CB\u5316");
|
|
7282
|
+
}
|
|
7283
|
+
let totalTokens = 0;
|
|
7284
|
+
try {
|
|
7285
|
+
for await (const chunk of adapter.streamMessage(messages, options)) {
|
|
7286
|
+
totalTokens += chunk.delta.length;
|
|
7287
|
+
yield chunk;
|
|
7288
|
+
}
|
|
7289
|
+
const inputTokens = adapter.countTokens(
|
|
7290
|
+
messages.map((m) => m.content).join("\n")
|
|
7291
|
+
);
|
|
7292
|
+
this.updateStats(
|
|
7293
|
+
{ inputTokens, outputTokens: Math.ceil(totalTokens / 4), totalTokens: 0 },
|
|
7294
|
+
{ model: this.currentConfig.model, agent: options?.agent }
|
|
7295
|
+
);
|
|
7296
|
+
this.saveStats().catch(() => {
|
|
7297
|
+
});
|
|
7298
|
+
} catch (error) {
|
|
7299
|
+
throw this.wrapError(error);
|
|
7300
|
+
}
|
|
7301
|
+
}
|
|
7302
|
+
/**
|
|
7303
|
+
* 计算Token数量
|
|
7304
|
+
*/
|
|
7305
|
+
countTokens(text) {
|
|
7306
|
+
if (!this.currentConfig) {
|
|
7307
|
+
return Math.ceil(text.length / 4);
|
|
7308
|
+
}
|
|
7309
|
+
const adapter = this.adapters.get(this.currentConfig.provider);
|
|
7310
|
+
return adapter?.countTokens(text) || Math.ceil(text.length / 4);
|
|
7311
|
+
}
|
|
7312
|
+
/**
|
|
7313
|
+
* 获取当前模型
|
|
7314
|
+
*/
|
|
7315
|
+
getCurrentModel() {
|
|
7316
|
+
return this.currentConfig?.model || null;
|
|
7317
|
+
}
|
|
7318
|
+
/**
|
|
7319
|
+
* 获取当前提供商
|
|
7320
|
+
*/
|
|
7321
|
+
getCurrentProvider() {
|
|
7322
|
+
return this.currentConfig?.provider || null;
|
|
7323
|
+
}
|
|
7324
|
+
/**
|
|
7325
|
+
* 获取Token统计
|
|
7326
|
+
*/
|
|
7327
|
+
getStats() {
|
|
7328
|
+
return { ...this.stats };
|
|
7329
|
+
}
|
|
7330
|
+
/**
|
|
7331
|
+
* 获取今日统计
|
|
7332
|
+
*/
|
|
7333
|
+
getTodayStats() {
|
|
7334
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7335
|
+
return this.stats.byDate[today] || { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
7336
|
+
}
|
|
7337
|
+
/**
|
|
7338
|
+
* 重置统计
|
|
7339
|
+
*/
|
|
7340
|
+
resetStats() {
|
|
7341
|
+
this.stats = {
|
|
7342
|
+
totalInput: 0,
|
|
7343
|
+
totalOutput: 0,
|
|
7344
|
+
byModel: {},
|
|
7345
|
+
byAgent: {},
|
|
7346
|
+
byDate: {}
|
|
7347
|
+
};
|
|
7348
|
+
this.saveStats().catch(() => {
|
|
7349
|
+
});
|
|
7350
|
+
}
|
|
7351
|
+
/**
|
|
7352
|
+
* 验证API Key
|
|
7353
|
+
*/
|
|
7354
|
+
async validateApiKey(provider, apiKey) {
|
|
7355
|
+
let adapter = this.adapters.get(provider);
|
|
7356
|
+
if (!adapter) {
|
|
7357
|
+
adapter = createAdapter(provider);
|
|
7358
|
+
}
|
|
7359
|
+
return adapter.validateApiKey(apiKey);
|
|
7360
|
+
}
|
|
7361
|
+
// ==================== 私有方法 ====================
|
|
7362
|
+
getProviderFromModel(modelId) {
|
|
7363
|
+
const info = getModelInfo(modelId);
|
|
7364
|
+
return info?.provider || "openai";
|
|
7365
|
+
}
|
|
7366
|
+
updateStats(usage, context) {
|
|
7367
|
+
this.stats.totalInput += usage.inputTokens;
|
|
7368
|
+
this.stats.totalOutput += usage.outputTokens;
|
|
7369
|
+
if (!this.stats.byModel[context.model]) {
|
|
7370
|
+
this.stats.byModel[context.model] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
7371
|
+
}
|
|
7372
|
+
this.stats.byModel[context.model].inputTokens += usage.inputTokens;
|
|
7373
|
+
this.stats.byModel[context.model].outputTokens += usage.outputTokens;
|
|
7374
|
+
this.stats.byModel[context.model].totalTokens += usage.totalTokens;
|
|
7375
|
+
if (context.agent) {
|
|
7376
|
+
if (!this.stats.byAgent[context.agent]) {
|
|
7377
|
+
this.stats.byAgent[context.agent] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
7378
|
+
}
|
|
7379
|
+
this.stats.byAgent[context.agent].inputTokens += usage.inputTokens;
|
|
7380
|
+
this.stats.byAgent[context.agent].outputTokens += usage.outputTokens;
|
|
7381
|
+
this.stats.byAgent[context.agent].totalTokens += usage.totalTokens;
|
|
7382
|
+
}
|
|
7383
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7384
|
+
if (!this.stats.byDate[today]) {
|
|
7385
|
+
this.stats.byDate[today] = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
7386
|
+
}
|
|
7387
|
+
this.stats.byDate[today].inputTokens += usage.inputTokens;
|
|
7388
|
+
this.stats.byDate[today].outputTokens += usage.outputTokens;
|
|
7389
|
+
this.stats.byDate[today].totalTokens += usage.totalTokens;
|
|
7390
|
+
}
|
|
7391
|
+
async loadStats() {
|
|
7392
|
+
if (!this.statsPath) return;
|
|
7393
|
+
try {
|
|
7394
|
+
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
7395
|
+
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
7396
|
+
const [daily, total] = await Promise.all([
|
|
7397
|
+
fs5__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
7398
|
+
fs5__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
7399
|
+
]);
|
|
7400
|
+
const dailyData = JSON.parse(daily);
|
|
7401
|
+
const totalData = JSON.parse(total);
|
|
7402
|
+
this.stats.byDate = dailyData;
|
|
7403
|
+
this.stats.totalInput = totalData.totalInput || 0;
|
|
7404
|
+
this.stats.totalOutput = totalData.totalOutput || 0;
|
|
7405
|
+
} catch {
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7408
|
+
async saveStats() {
|
|
7409
|
+
if (!this.statsPath) return;
|
|
7410
|
+
try {
|
|
7411
|
+
await fs5__namespace.mkdir(this.statsPath, { recursive: true });
|
|
7412
|
+
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
7413
|
+
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
7414
|
+
await Promise.all([
|
|
7415
|
+
fs5__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
7416
|
+
fs5__namespace.writeFile(totalPath, JSON.stringify({
|
|
7417
|
+
totalInput: this.stats.totalInput,
|
|
7418
|
+
totalOutput: this.stats.totalOutput
|
|
7419
|
+
}))
|
|
7420
|
+
]);
|
|
7421
|
+
} catch {
|
|
7422
|
+
}
|
|
7423
|
+
}
|
|
7424
|
+
wrapError(error) {
|
|
7425
|
+
if (error instanceof ModelError) {
|
|
7426
|
+
return error;
|
|
7427
|
+
}
|
|
7428
|
+
const err = error;
|
|
7429
|
+
if (err.message.includes("timeout")) {
|
|
7430
|
+
return new ModelError("TIMEOUT", err.message, { retryable: true });
|
|
7431
|
+
}
|
|
7432
|
+
if (err.message.includes("network") || err.message.includes("ECONNREFUSED")) {
|
|
7433
|
+
return new ModelError("NETWORK_ERROR", err.message, { retryable: true });
|
|
7434
|
+
}
|
|
7435
|
+
return new ModelError("UNKNOWN_ERROR", err.message, { retryable: false });
|
|
7436
|
+
}
|
|
7437
|
+
};
|
|
7438
|
+
|
|
7439
|
+
// src/services/index.ts
|
|
7440
|
+
init_adapters();
|
|
7441
|
+
|
|
7442
|
+
// src/services/mcp/index.ts
|
|
7443
|
+
init_cjs_shims();
|
|
7444
|
+
|
|
7445
|
+
// src/types/mcp.ts
|
|
7446
|
+
init_cjs_shims();
|
|
7447
|
+
|
|
7448
|
+
// src/services/mcp/base.ts
|
|
7449
|
+
init_cjs_shims();
|
|
7450
|
+
|
|
7451
|
+
// src/services/mcp/lanhu.ts
|
|
7452
|
+
init_cjs_shims();
|
|
7453
|
+
|
|
7454
|
+
// src/services/mcp/figma.ts
|
|
7455
|
+
init_cjs_shims();
|
|
7456
|
+
|
|
7457
|
+
// src/services/mcp/manager.ts
|
|
7458
|
+
init_cjs_shims();
|
|
7488
7459
|
|
|
7489
7460
|
// src/cli/status.ts
|
|
7490
7461
|
init_cjs_shims();
|
|
@@ -7649,17 +7620,17 @@ var Completer = class {
|
|
|
7649
7620
|
prefix = filePath;
|
|
7650
7621
|
} else {
|
|
7651
7622
|
const dir = filePath.slice(0, lastSlash);
|
|
7652
|
-
dirPath =
|
|
7623
|
+
dirPath = path5__namespace.resolve(this.workingDirectory, dir);
|
|
7653
7624
|
prefix = filePath.slice(lastSlash + 1);
|
|
7654
7625
|
}
|
|
7655
7626
|
try {
|
|
7656
|
-
const entries = await
|
|
7627
|
+
const entries = await fs5__namespace.readdir(dirPath, { withFileTypes: true });
|
|
7657
7628
|
const matches = [];
|
|
7658
7629
|
for (const entry of entries) {
|
|
7659
7630
|
if (entry.name.startsWith(prefix)) {
|
|
7660
7631
|
const isDir = entry.isDirectory();
|
|
7661
7632
|
matches.push({
|
|
7662
|
-
text: "@" +
|
|
7633
|
+
text: "@" + path5__namespace.relative(this.workingDirectory, path5__namespace.join(dirPath, entry.name)).replace(/\\/g, "/") + (isDir ? "/" : ""),
|
|
7663
7634
|
displayText: entry.name + (isDir ? "/" : ""),
|
|
7664
7635
|
description: isDir ? "\u76EE\u5F55" : "\u6587\u4EF6",
|
|
7665
7636
|
type: isDir ? "directory" : "file"
|
|
@@ -7732,10 +7703,10 @@ var Completer = class {
|
|
|
7732
7703
|
// src/cli/repl.ts
|
|
7733
7704
|
init_new();
|
|
7734
7705
|
async function startInteractiveMode(options) {
|
|
7735
|
-
const historyFile =
|
|
7706
|
+
const historyFile = path5__namespace.join(options.workingDirectory, ".sf-cli", "history.json");
|
|
7736
7707
|
let history = [];
|
|
7737
7708
|
try {
|
|
7738
|
-
const historyData = await
|
|
7709
|
+
const historyData = await fs5__namespace.readFile(historyFile, "utf-8");
|
|
7739
7710
|
history = JSON.parse(historyData);
|
|
7740
7711
|
} catch {
|
|
7741
7712
|
}
|
|
@@ -7768,11 +7739,11 @@ async function startInteractiveMode(options) {
|
|
|
7768
7739
|
const modelService = new ModelService(configManager);
|
|
7769
7740
|
const normsManager = new NormsManager({
|
|
7770
7741
|
projectPath: options.workingDirectory,
|
|
7771
|
-
normsDir:
|
|
7742
|
+
normsDir: path5__namespace.join(options.workingDirectory, ".sf-cli", "norms")
|
|
7772
7743
|
});
|
|
7773
7744
|
await configManager.load(options.workingDirectory);
|
|
7774
7745
|
await contextManager.initialize(options.workingDirectory);
|
|
7775
|
-
const statsDir =
|
|
7746
|
+
const statsDir = path5__namespace.join(options.workingDirectory, ".sf-cli");
|
|
7776
7747
|
await modelService.initialize(statsDir);
|
|
7777
7748
|
await normsManager.initialize();
|
|
7778
7749
|
const state = {
|
|
@@ -7822,8 +7793,8 @@ async function startInteractiveMode(options) {
|
|
|
7822
7793
|
}
|
|
7823
7794
|
const saveHistory = async () => {
|
|
7824
7795
|
try {
|
|
7825
|
-
await
|
|
7826
|
-
await
|
|
7796
|
+
await fs5__namespace.mkdir(path5__namespace.dirname(historyFile), { recursive: true });
|
|
7797
|
+
await fs5__namespace.writeFile(historyFile, JSON.stringify(history.slice(-500)));
|
|
7827
7798
|
} catch {
|
|
7828
7799
|
}
|
|
7829
7800
|
};
|
|
@@ -7894,16 +7865,15 @@ async function startInteractiveMode(options) {
|
|
|
7894
7865
|
}
|
|
7895
7866
|
|
|
7896
7867
|
// src/cli/index.ts
|
|
7897
|
-
init_config();
|
|
7898
7868
|
function getPackageJson() {
|
|
7899
7869
|
const possiblePaths = [
|
|
7900
|
-
|
|
7901
|
-
|
|
7870
|
+
path5__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
7871
|
+
path5__namespace.resolve(__dirname, "..", "..", "..", "package.json")
|
|
7902
7872
|
];
|
|
7903
7873
|
for (const pkgPath of possiblePaths) {
|
|
7904
7874
|
try {
|
|
7905
|
-
if (
|
|
7906
|
-
const content =
|
|
7875
|
+
if (fs7__namespace.existsSync(pkgPath)) {
|
|
7876
|
+
const content = fs7__namespace.readFileSync(pkgPath, "utf-8");
|
|
7907
7877
|
return JSON.parse(content);
|
|
7908
7878
|
}
|
|
7909
7879
|
} catch {
|
|
@@ -7932,7 +7902,7 @@ commander.program.command("init").description("\u521D\u59CB\u5316\u63A5\u7BA1\u9
|
|
|
7932
7902
|
commander.program.command("model").description("\u9009\u62E9AI\u6A21\u578B").action(async () => {
|
|
7933
7903
|
const configManager = new ConfigManager();
|
|
7934
7904
|
await configManager.load(process.cwd());
|
|
7935
|
-
const { selectModel: selectModel2 } = await Promise.resolve().then(() => (
|
|
7905
|
+
const { selectModel: selectModel2 } = await Promise.resolve().then(() => (init_model2(), model_exports));
|
|
7936
7906
|
await selectModel2(configManager);
|
|
7937
7907
|
});
|
|
7938
7908
|
commander.program.command("update").description("\u66F4\u65B0CLI\u5230\u6700\u65B0\u7248\u672C").action(async () => {
|