ai-project-manage-cli 4.0.23 → 5.0.2
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/README.md +23 -12
- package/dist/index.js +371 -1446
- package/package.json +4 -13
- package/template/apm.config.json +1 -24
- package/template/deploy/README.md +0 -3
- package/template/skills/apm-apply-change/SKILL.md +0 -191
- package/template/skills/apm-deploy/SKILL.md +0 -135
- package/template/skills/apm-dev/SKILL.md +0 -161
- package/template/skills/apm-propose/SKILL.md +0 -123
- package/template/skills/apm-propose/design-instruction.md +0 -94
- package/template/skills/apm-propose/propose-instruction.md +0 -81
- package/template/skills/apm-propose/specs-instruction.md +0 -114
- package/template/skills/apm-propose/tasks-instruction.md +0 -90
- package/template/skills/apm-refine/SKILL.md +0 -99
- package/template/skills/apm-refine/apm-refine-template.md +0 -73
- package/template/skills/apm-review/SKILL.md +0 -111
- package/template/skills/apm-review/output-template.md +0 -60
- package/template/theater-skills/README.md +0 -13
- package/template/theater-skills/apm-theater-backend/SKILL.md +0 -115
- package/template/theater-skills/apm-theater-backend/apm-theater-api-template.md +0 -146
- package/template/theater-skills/apm-theater-backend/apm-theater-backend-template.md +0 -132
- package/template/theater-skills/apm-theater-dev/SKILL.md +0 -149
- package/template/theater-skills/apm-theater-prd/SKILL.md +0 -98
- package/template/workitems/README.md +0 -20
package/dist/index.js
CHANGED
|
@@ -40,12 +40,6 @@ function defaultApmConfig() {
|
|
|
40
40
|
email: ""
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
function httpBaseToWsOrigin(httpBase) {
|
|
44
|
-
const u = httpBase.trim().replace(/\/+$/, "");
|
|
45
|
-
if (u.startsWith("https://")) return "wss://" + u.slice("https://".length);
|
|
46
|
-
if (u.startsWith("http://")) return "ws://" + u.slice("http://".length);
|
|
47
|
-
throw new Error("baseUrl \u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934");
|
|
48
|
-
}
|
|
49
43
|
async function ensureApmConfig() {
|
|
50
44
|
const existing = await readApmConfig();
|
|
51
45
|
if (existing) return existing;
|
|
@@ -65,183 +59,47 @@ async function writeApmConfig(cfg) {
|
|
|
65
59
|
mode: 384
|
|
66
60
|
});
|
|
67
61
|
}
|
|
68
|
-
function buildAgentWsUrl(httpBase, token) {
|
|
69
|
-
const origin = httpBaseToWsOrigin(httpBase);
|
|
70
|
-
const q = new URLSearchParams({ token });
|
|
71
|
-
return `${origin}/ws/agent?${q.toString()}`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// src/commands/comment.ts
|
|
75
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
76
|
-
|
|
77
|
-
// src/api/client.ts
|
|
78
|
-
import { createApiClient } from "listpage-http";
|
|
79
|
-
|
|
80
|
-
// src/api/request-config.ts
|
|
81
|
-
import { defineEndpoint } from "listpage-http";
|
|
82
|
-
var requestConfig = {
|
|
83
|
-
auth: {
|
|
84
|
-
login: defineEndpoint({
|
|
85
|
-
method: "POST",
|
|
86
|
-
path: "/auth/login",
|
|
87
|
-
authRequired: false
|
|
88
|
-
})
|
|
89
|
-
},
|
|
90
|
-
cliRequirements: {
|
|
91
|
-
pull: defineEndpoint({
|
|
92
|
-
method: "GET",
|
|
93
|
-
path: "/cli/requirements/pull"
|
|
94
|
-
}),
|
|
95
|
-
branchBaseline: defineEndpoint({
|
|
96
|
-
method: "GET",
|
|
97
|
-
path: "/cli/requirements/branch-baseline"
|
|
98
|
-
}),
|
|
99
|
-
comment: defineEndpoint({
|
|
100
|
-
method: "POST",
|
|
101
|
-
path: "/cli/requirements/comment"
|
|
102
|
-
}),
|
|
103
|
-
commentStructured: defineEndpoint({
|
|
104
|
-
method: "POST",
|
|
105
|
-
path: "/cli/requirements/comment-structured"
|
|
106
|
-
}),
|
|
107
|
-
refine: defineEndpoint({
|
|
108
|
-
method: "POST",
|
|
109
|
-
path: "/cli/requirements/refine"
|
|
110
|
-
}),
|
|
111
|
-
updateStatus: defineEndpoint({
|
|
112
|
-
method: "POST",
|
|
113
|
-
path: "/cli/requirements/update-status"
|
|
114
|
-
}),
|
|
115
|
-
updateDevStatus: defineEndpoint({
|
|
116
|
-
method: "POST",
|
|
117
|
-
path: "/cli/requirements/update-dev-status"
|
|
118
|
-
})
|
|
119
|
-
},
|
|
120
|
-
requirementAttachment: {
|
|
121
|
-
list: defineEndpoint({
|
|
122
|
-
method: "GET",
|
|
123
|
-
path: "/requirement-attachments/list"
|
|
124
|
-
}),
|
|
125
|
-
download: defineEndpoint({
|
|
126
|
-
method: "GET",
|
|
127
|
-
path: "/requirement-attachments/download",
|
|
128
|
-
mode: "download"
|
|
129
|
-
})
|
|
130
|
-
},
|
|
131
|
-
requirementArtifact: {
|
|
132
|
-
list: defineEndpoint({
|
|
133
|
-
method: "GET",
|
|
134
|
-
path: "/requirement-artifacts/list"
|
|
135
|
-
}),
|
|
136
|
-
get: defineEndpoint({
|
|
137
|
-
method: "GET",
|
|
138
|
-
path: "/requirement-artifacts/get"
|
|
139
|
-
}),
|
|
140
|
-
create: defineEndpoint({
|
|
141
|
-
method: "POST",
|
|
142
|
-
path: "/requirement-artifacts/create"
|
|
143
|
-
}),
|
|
144
|
-
upsert: defineEndpoint({
|
|
145
|
-
method: "POST",
|
|
146
|
-
path: "/requirement-artifacts/upsert"
|
|
147
|
-
}),
|
|
148
|
-
update: defineEndpoint({
|
|
149
|
-
method: "POST",
|
|
150
|
-
path: "/requirement-artifacts/update"
|
|
151
|
-
}),
|
|
152
|
-
delete: defineEndpoint({
|
|
153
|
-
method: "POST",
|
|
154
|
-
path: "/requirement-artifacts/delete"
|
|
155
|
-
})
|
|
156
|
-
},
|
|
157
|
-
theaterSessionArtifact: {
|
|
158
|
-
upsert: defineEndpoint({
|
|
159
|
-
method: "POST",
|
|
160
|
-
path: "/theater-session-artifacts/upsert"
|
|
161
|
-
}),
|
|
162
|
-
create: defineEndpoint({
|
|
163
|
-
method: "POST",
|
|
164
|
-
path: "/theater-session-artifacts/create"
|
|
165
|
-
}),
|
|
166
|
-
update: defineEndpoint({
|
|
167
|
-
method: "POST",
|
|
168
|
-
path: "/theater-session-artifacts/update"
|
|
169
|
-
}),
|
|
170
|
-
list: defineEndpoint({
|
|
171
|
-
method: "GET",
|
|
172
|
-
path: "/theater-session-artifacts/list"
|
|
173
|
-
}),
|
|
174
|
-
get: defineEndpoint({
|
|
175
|
-
method: "GET",
|
|
176
|
-
path: "/theater-session-artifacts/get"
|
|
177
|
-
})
|
|
178
|
-
},
|
|
179
|
-
theater: {
|
|
180
|
-
sessionBranchBaseline: defineEndpoint({
|
|
181
|
-
method: "GET",
|
|
182
|
-
path: "/theater/sessions/branch-baseline"
|
|
183
|
-
}),
|
|
184
|
-
reportMemberAgentProgress: defineEndpoint({
|
|
185
|
-
method: "POST",
|
|
186
|
-
path: "/theater/sessions/report-member-agent-progress"
|
|
187
|
-
}),
|
|
188
|
-
submitMemberResponse: defineEndpoint({
|
|
189
|
-
method: "POST",
|
|
190
|
-
path: "/theater/sessions/submit-member-response"
|
|
191
|
-
})
|
|
192
|
-
},
|
|
193
|
-
spaceWorkflow: {
|
|
194
|
-
aiStartNode: defineEndpoint({
|
|
195
|
-
method: "POST",
|
|
196
|
-
path: "/space-workflows/ai-start-node"
|
|
197
|
-
}),
|
|
198
|
-
aiConfirmNode: defineEndpoint(
|
|
199
|
-
{
|
|
200
|
-
method: "POST",
|
|
201
|
-
path: "/space-workflows/ai-confirm-node"
|
|
202
|
-
}
|
|
203
|
-
),
|
|
204
|
-
aiFailNode: defineEndpoint({
|
|
205
|
-
method: "POST",
|
|
206
|
-
path: "/space-workflows/ai-fail-node"
|
|
207
|
-
})
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
62
|
|
|
211
|
-
// src/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return createApiClient(requestConfig, {
|
|
215
|
-
baseURL,
|
|
216
|
-
getToken: () => cfg.token || void 0,
|
|
217
|
-
successCodes: [200, 201],
|
|
218
|
-
unauthorizedCodes: [401]
|
|
219
|
-
});
|
|
220
|
-
}
|
|
63
|
+
// src/commands/init.ts
|
|
64
|
+
import { join as join3 } from "path";
|
|
65
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
221
66
|
|
|
222
67
|
// src/command-utils.ts
|
|
223
68
|
import { cpSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
224
|
-
import { dirname, join as join2, resolve } from "path";
|
|
69
|
+
import { basename, dirname, extname, join as join2, resolve } from "path";
|
|
225
70
|
import { fileURLToPath } from "url";
|
|
226
71
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
227
72
|
var CLI_TEMPLATE_DIR = resolve(__dirname, "../template");
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
73
|
+
function workspaceApmDir(cwd = process.cwd()) {
|
|
74
|
+
return resolve(cwd, ".apm");
|
|
75
|
+
}
|
|
76
|
+
var SESSIONS_SUBDIR = "sessions";
|
|
77
|
+
var SESSION_DOCS_SUBDIR = "docs";
|
|
78
|
+
var SESSION_ATTACHMENTS_SUBDIR = "attachments";
|
|
79
|
+
function sessionDir(sessionId, apmRoot) {
|
|
80
|
+
return join2(apmRoot ?? workspaceApmDir(), SESSIONS_SUBDIR, sessionId);
|
|
231
81
|
}
|
|
232
|
-
function
|
|
233
|
-
return join2(
|
|
82
|
+
function sessionDocsDir(sessionId, apmRoot) {
|
|
83
|
+
return join2(sessionDir(sessionId, apmRoot), SESSION_DOCS_SUBDIR);
|
|
234
84
|
}
|
|
235
|
-
function
|
|
236
|
-
return
|
|
85
|
+
function sessionRulePath(sessionId, apmRoot) {
|
|
86
|
+
return join2(sessionDir(sessionId, apmRoot), "RULE.md");
|
|
237
87
|
}
|
|
238
|
-
function
|
|
239
|
-
|
|
240
|
-
return `docs/${base}`;
|
|
88
|
+
function sessionYamlPath(sessionId, apmRoot) {
|
|
89
|
+
return join2(sessionDir(sessionId, apmRoot), "session.yaml");
|
|
241
90
|
}
|
|
242
|
-
function
|
|
243
|
-
const
|
|
244
|
-
|
|
91
|
+
function documentLocalFileName(platformName) {
|
|
92
|
+
const trimmed = platformName.trim();
|
|
93
|
+
if (!trimmed) return "document.md";
|
|
94
|
+
if (extname(trimmed).toLowerCase() === ".md") return trimmed;
|
|
95
|
+
return `${trimmed}.md`;
|
|
96
|
+
}
|
|
97
|
+
function documentPlatformName(filePath) {
|
|
98
|
+
const base = basename(filePath.trim().replace(/\\/g, "/"));
|
|
99
|
+
if (base.toLowerCase().endsWith(".md")) {
|
|
100
|
+
return base.slice(0, -3);
|
|
101
|
+
}
|
|
102
|
+
return base;
|
|
245
103
|
}
|
|
246
104
|
async function ensureLoggedConfig() {
|
|
247
105
|
const cfg = await ensureApmConfig();
|
|
@@ -257,7 +115,7 @@ async function ensureDirExists(dir) {
|
|
|
257
115
|
mkdirSync2(dir, { recursive: true });
|
|
258
116
|
}
|
|
259
117
|
async function ensureWorkspaceApmDirForInit() {
|
|
260
|
-
const dir =
|
|
118
|
+
const dir = workspaceApmDir();
|
|
261
119
|
if (!existsSync(dir)) {
|
|
262
120
|
mkdirSync2(dir, { recursive: true });
|
|
263
121
|
return;
|
|
@@ -288,611 +146,148 @@ async function copyTemplateFiles(targetDir) {
|
|
|
288
146
|
})
|
|
289
147
|
);
|
|
290
148
|
}
|
|
291
|
-
function xmlEscape(v) {
|
|
292
|
-
return v.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
293
|
-
}
|
|
294
149
|
function resolveCwdPath(file) {
|
|
295
150
|
return resolve(process.cwd(), file);
|
|
296
151
|
}
|
|
297
152
|
|
|
298
|
-
// src/
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
function asRecord(value) {
|
|
310
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
311
|
-
return value;
|
|
312
|
-
}
|
|
313
|
-
throw new Error("YAML \u7ED3\u6784\u65E0\u6548\uFF1A\u9700\u8981\u5BF9\u8C61\u6839\u8282\u70B9");
|
|
314
|
-
}
|
|
315
|
-
function parseAnchor(anchor, index) {
|
|
316
|
-
const rec = asRecord(anchor);
|
|
317
|
-
const start = Number(rec.start ?? rec.startLine);
|
|
318
|
-
const end = Number(rec.end ?? rec.endLine);
|
|
319
|
-
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 1 || end < start) {
|
|
320
|
-
throw new Error(`items[${index}].anchor \u884C\u53F7\u65E0\u6548\uFF08\u9700 1 \u2264 start \u2264 end\uFF09`);
|
|
321
|
-
}
|
|
322
|
-
return { start, end };
|
|
323
|
-
}
|
|
324
|
-
function parseKind(raw, index) {
|
|
325
|
-
const key = String(raw ?? "").trim();
|
|
326
|
-
const kind = KIND_MAP[key] ?? KIND_MAP[key.toLowerCase()];
|
|
327
|
-
if (!kind) {
|
|
328
|
-
if (key.toLowerCase() === "coordination" || key === "COORDINATION") {
|
|
329
|
-
throw new Error(
|
|
330
|
-
`items[${index}].kind \u4E0D\u518D\u652F\u6301 coordination\uFF08\u8054\u8C03\u4F9D\u8D56\uFF09\uFF1B\u8BF7\u52FF\u8F93\u51FA\u63A5\u53E3/\u8054\u8C03\u7C7B\u8BC4\u5BA1\u6761\u76EE`
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
throw new Error(
|
|
334
|
-
`items[${index}].kind \u65E0\u6548\uFF0C\u5E94\u4E3A clarify | difficulty | business`
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
return kind;
|
|
338
|
-
}
|
|
339
|
-
function parseStructuredReviewYaml(raw, cliModel) {
|
|
340
|
-
let doc;
|
|
341
|
-
try {
|
|
342
|
-
doc = parse(raw);
|
|
343
|
-
} catch (e) {
|
|
344
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
345
|
-
throw new Error(`YAML \u89E3\u6790\u5931\u8D25\uFF1A${msg}`);
|
|
346
|
-
}
|
|
347
|
-
const root = asRecord(doc);
|
|
348
|
-
const reviewer = asRecord(root.reviewer ?? {});
|
|
349
|
-
const stance = String(reviewer.stance ?? root.stance ?? "").trim();
|
|
350
|
-
if (!STANCE_VALUES.has(stance)) {
|
|
351
|
-
throw new Error("reviewer.stance \u5FC5\u987B\u4E3A frontend\u3001backend \u6216 fullstack");
|
|
352
|
-
}
|
|
353
|
-
const modelFromYaml = reviewer.model != null ? String(reviewer.model).trim() : "";
|
|
354
|
-
const model = cliModel?.trim() || modelFromYaml || null;
|
|
355
|
-
const itemsRaw = root.items;
|
|
356
|
-
if (!Array.isArray(itemsRaw) || itemsRaw.length === 0) {
|
|
357
|
-
throw new Error("items \u4E0D\u80FD\u4E3A\u7A7A");
|
|
358
|
-
}
|
|
359
|
-
const items = itemsRaw.map((entry, index) => {
|
|
360
|
-
const item = asRecord(entry);
|
|
361
|
-
const anchor = parseAnchor(item.anchor ?? item, index);
|
|
362
|
-
const kind = parseKind(item.kind, index);
|
|
363
|
-
const body = typeof item.body === "string" ? item.body.trim() : String(item.body ?? "").trim();
|
|
364
|
-
if (!body) {
|
|
365
|
-
throw new Error(`items[${index}].body \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
366
|
-
}
|
|
367
|
-
return {
|
|
368
|
-
startLine: anchor.start,
|
|
369
|
-
endLine: anchor.end,
|
|
370
|
-
kind,
|
|
371
|
-
body
|
|
372
|
-
};
|
|
373
|
-
});
|
|
374
|
-
return { stance, model, items };
|
|
375
|
-
}
|
|
376
|
-
function inferCommentFormat(filePath, explicit) {
|
|
377
|
-
if (explicit === "structured" || explicit === "legacy") {
|
|
378
|
-
return explicit;
|
|
379
|
-
}
|
|
380
|
-
const lower = filePath.toLowerCase();
|
|
381
|
-
if (lower.endsWith(".yaml") || lower.endsWith(".yml")) {
|
|
382
|
-
return "structured";
|
|
383
|
-
}
|
|
384
|
-
return "legacy";
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// src/commands/comment.ts
|
|
388
|
-
async function runComment(requirementId, file, options) {
|
|
389
|
-
const cfg = await ensureLoggedConfig();
|
|
390
|
-
const resolvedPath = resolveCwdPath(file);
|
|
391
|
-
const raw = readFileSync2(resolvedPath, "utf8");
|
|
392
|
-
const format = inferCommentFormat(resolvedPath, options?.format);
|
|
393
|
-
const api = createApmApiClient(cfg);
|
|
394
|
-
if (format === "structured") {
|
|
395
|
-
const parsed = parseStructuredReviewYaml(raw, options?.model);
|
|
396
|
-
const data2 = await api.cliRequirements.commentStructured({
|
|
397
|
-
requirementId,
|
|
398
|
-
stance: parsed.stance,
|
|
399
|
-
model: parsed.model,
|
|
400
|
-
items: parsed.items
|
|
401
|
-
});
|
|
402
|
-
console.log(JSON.stringify(data2, null, 2));
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (!raw.trim()) {
|
|
406
|
-
throw new Error("\u8BC4\u8BBA\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A");
|
|
153
|
+
// src/commands/init.ts
|
|
154
|
+
async function runInit(name) {
|
|
155
|
+
await ensureWorkspaceApmDirForInit();
|
|
156
|
+
const apmDir = workspaceApmDir();
|
|
157
|
+
await copyTemplateFiles(apmDir);
|
|
158
|
+
if (name) {
|
|
159
|
+
const apmConfigPath = join3(apmDir, "apm.config.json");
|
|
160
|
+
const config = readFileSync2(apmConfigPath, "utf8");
|
|
161
|
+
const configJson = JSON.parse(config);
|
|
162
|
+
configJson.name = name;
|
|
163
|
+
writeFileSync2(apmConfigPath, JSON.stringify(configJson, null, 2), "utf8");
|
|
407
164
|
}
|
|
408
|
-
const data = await api.cliRequirements.comment({
|
|
409
|
-
requirementId,
|
|
410
|
-
content: raw,
|
|
411
|
-
model: options?.model
|
|
412
|
-
});
|
|
413
|
-
console.log(JSON.stringify(data, null, 2));
|
|
414
165
|
}
|
|
415
166
|
|
|
416
|
-
// src/commands/
|
|
417
|
-
import {
|
|
418
|
-
import WebSocket from "ws";
|
|
419
|
-
import { Agent } from "@cursor/sdk";
|
|
420
|
-
import { join as join7 } from "path";
|
|
421
|
-
|
|
422
|
-
// src/session-utils.ts
|
|
423
|
-
import { appendFileSync } from "fs";
|
|
424
|
-
import { resolve as resolve2 } from "path";
|
|
425
|
-
var EventSession = class {
|
|
426
|
-
events = [];
|
|
427
|
-
constructor(prompt) {
|
|
428
|
-
this.events.push({
|
|
429
|
-
type: "input",
|
|
430
|
-
content: prompt
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
addEvent(event) {
|
|
434
|
-
const latestEvent = this.events[this.events.length - 1];
|
|
435
|
-
const formatedEvent = this.formatEvent(event);
|
|
436
|
-
if (!formatedEvent) {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
if (formatedEvent.type === "tool_call") {
|
|
440
|
-
const existingToolCall = this.events.find(
|
|
441
|
-
(e) => e.type === "tool_call" && e.call_id === formatedEvent.call_id
|
|
442
|
-
);
|
|
443
|
-
if (existingToolCall) {
|
|
444
|
-
existingToolCall.args = formatedEvent.args;
|
|
445
|
-
existingToolCall.result = formatedEvent.result;
|
|
446
|
-
existingToolCall.status = formatedEvent.status;
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
this.events.push(formatedEvent);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
if (formatedEvent.type === "status") {
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (formatedEvent.type === "request") {
|
|
456
|
-
this.events.push(formatedEvent);
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
if (latestEvent?.type === formatedEvent.type) {
|
|
460
|
-
switch (formatedEvent.type) {
|
|
461
|
-
case "assistant":
|
|
462
|
-
latestEvent.content += formatedEvent.content;
|
|
463
|
-
break;
|
|
464
|
-
case "thinking":
|
|
465
|
-
latestEvent.content += formatedEvent.content;
|
|
466
|
-
break;
|
|
467
|
-
case "task":
|
|
468
|
-
latestEvent.status = formatedEvent.status;
|
|
469
|
-
latestEvent.text = formatedEvent.text;
|
|
470
|
-
break;
|
|
471
|
-
}
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
this.events.push(formatedEvent);
|
|
475
|
-
}
|
|
476
|
-
formatEvent(event) {
|
|
477
|
-
switch (event.type) {
|
|
478
|
-
case "assistant":
|
|
479
|
-
return {
|
|
480
|
-
type: "assistant",
|
|
481
|
-
content: event.message.content[0].text || event.content || ""
|
|
482
|
-
};
|
|
483
|
-
case "thinking":
|
|
484
|
-
return {
|
|
485
|
-
type: "thinking",
|
|
486
|
-
content: event.text || event.content || ""
|
|
487
|
-
};
|
|
488
|
-
case "tool_call":
|
|
489
|
-
return {
|
|
490
|
-
type: "tool_call",
|
|
491
|
-
args: event.args,
|
|
492
|
-
result: event.result,
|
|
493
|
-
status: event.status,
|
|
494
|
-
call_id: event.call_id,
|
|
495
|
-
name: event.name
|
|
496
|
-
};
|
|
497
|
-
case "task":
|
|
498
|
-
return {
|
|
499
|
-
type: "task",
|
|
500
|
-
status: event.status,
|
|
501
|
-
text: event.text
|
|
502
|
-
};
|
|
503
|
-
case "request":
|
|
504
|
-
return {
|
|
505
|
-
...event,
|
|
506
|
-
type: "request"
|
|
507
|
-
};
|
|
508
|
-
case "status":
|
|
509
|
-
return { type: "status", status: event.status, message: event.message };
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
/** 合并所有 assistant 片段,供剧场成员回传等场景使用 */
|
|
513
|
-
getAssistantText() {
|
|
514
|
-
return this.events.filter((e) => e.type === "assistant").map((e) => String(e.content ?? "")).join("\n").trim();
|
|
515
|
-
}
|
|
516
|
-
writeToFile(cwd, requirementId, agentId) {
|
|
517
|
-
const sessionsDir = resolve2(
|
|
518
|
-
cwd,
|
|
519
|
-
`.apm/workitems/${requirementId}/sessions`
|
|
520
|
-
);
|
|
521
|
-
ensureDirExists(sessionsDir);
|
|
522
|
-
const sessionFile = resolve2(sessionsDir, `${agentId}.md`);
|
|
523
|
-
this.events.forEach((event) => {
|
|
524
|
-
const type = event.type;
|
|
525
|
-
const content = (function(type2) {
|
|
526
|
-
if (type2 === "input") {
|
|
527
|
-
return `## \u7528\u6237\u8F93\u5165
|
|
528
|
-
|
|
529
|
-
${event.content}
|
|
530
|
-
`;
|
|
531
|
-
}
|
|
532
|
-
if (type2 === "assistant") {
|
|
533
|
-
return `## \u6A21\u578B\u8F93\u51FA
|
|
534
|
-
|
|
535
|
-
${event.content}
|
|
536
|
-
`;
|
|
537
|
-
}
|
|
538
|
-
if (type2 === "thinking") {
|
|
539
|
-
return `## \u6A21\u578B\u601D\u8003
|
|
167
|
+
// src/commands/login.ts
|
|
168
|
+
import { ApiError } from "listpage-http";
|
|
540
169
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
if (type2 === "tool_call") {
|
|
545
|
-
return "````toolcall\n" + JSON.stringify(event, null, 2) + "\n````\n";
|
|
546
|
-
}
|
|
547
|
-
return `## \u672A\u77E5\u4E8B\u4EF6\uFF1A${type2}
|
|
170
|
+
// src/api/client.ts
|
|
171
|
+
import { createApiClient } from "listpage-http";
|
|
548
172
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
173
|
+
// src/api/request-config.ts
|
|
174
|
+
import { defineEndpoint } from "listpage-http";
|
|
175
|
+
var requestConfig = {
|
|
176
|
+
auth: {
|
|
177
|
+
login: defineEndpoint({
|
|
178
|
+
method: "POST",
|
|
179
|
+
path: "/auth/login",
|
|
180
|
+
authRequired: false
|
|
181
|
+
})
|
|
182
|
+
},
|
|
183
|
+
cli: {
|
|
184
|
+
sessionDetail: defineEndpoint({
|
|
185
|
+
method: "GET",
|
|
186
|
+
path: "/cli/sessions/detail"
|
|
187
|
+
}),
|
|
188
|
+
sessionMembers: defineEndpoint({
|
|
189
|
+
method: "GET",
|
|
190
|
+
path: "/cli/sessions/members"
|
|
191
|
+
}),
|
|
192
|
+
listDocuments: defineEndpoint({
|
|
193
|
+
method: "GET",
|
|
194
|
+
path: "/cli/documents"
|
|
195
|
+
}),
|
|
196
|
+
listAttachments: defineEndpoint({
|
|
197
|
+
method: "GET",
|
|
198
|
+
path: "/cli/attachments"
|
|
199
|
+
}),
|
|
200
|
+
upsertDocument: defineEndpoint({
|
|
201
|
+
method: "PUT",
|
|
202
|
+
path: "/cli/documents/upsert"
|
|
203
|
+
}),
|
|
204
|
+
appendMessageContent: defineEndpoint({
|
|
205
|
+
method: "PUT",
|
|
206
|
+
path: "/cli/messages/content"
|
|
207
|
+
}),
|
|
208
|
+
updateMessageStatus: defineEndpoint({
|
|
209
|
+
method: "PUT",
|
|
210
|
+
path: "/cli/messages/status"
|
|
211
|
+
}),
|
|
212
|
+
branchBaseline: defineEndpoint({
|
|
213
|
+
method: "GET",
|
|
214
|
+
path: "/cli/requirements/branch-baseline"
|
|
215
|
+
}),
|
|
216
|
+
listSkills: defineEndpoint({
|
|
217
|
+
method: "GET",
|
|
218
|
+
path: "/cli/skills"
|
|
219
|
+
})
|
|
555
220
|
}
|
|
556
221
|
};
|
|
557
222
|
|
|
558
|
-
// src/
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const dir = join3(workitemsDir, REQUIREMENT_ATTACHMENTS_SUBDIR);
|
|
568
|
-
await ensureDirExists(dir);
|
|
569
|
-
const { items } = await api.requirementAttachment.list({ requirementId });
|
|
570
|
-
if (items.length === 0) return;
|
|
571
|
-
for (const item of items) {
|
|
572
|
-
const dest = join3(dir, item.fileName);
|
|
573
|
-
if (existsSync2(dest)) continue;
|
|
574
|
-
const { blob } = await api.requirementAttachment.download({
|
|
575
|
-
attachmentId: item.id
|
|
576
|
-
});
|
|
577
|
-
const buffer = Buffer.from(await blob.arrayBuffer());
|
|
578
|
-
writeFileSync2(dest, buffer);
|
|
579
|
-
console.log(
|
|
580
|
-
`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${REQUIREMENT_ATTACHMENTS_SUBDIR}/${item.fileName}`
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// src/commands/upload-artifact.ts
|
|
586
|
-
var EXCLUDED_RELATIVE_PATHS = /* @__PURE__ */ new Set([
|
|
587
|
-
"defect.xml",
|
|
588
|
-
"prd.md",
|
|
589
|
-
"requirement-status.yaml",
|
|
590
|
-
"reviews.xml",
|
|
591
|
-
"testcase.xml"
|
|
592
|
-
]);
|
|
593
|
-
function toPosixRelative(root, absoluteFile) {
|
|
594
|
-
return relative(root, absoluteFile).split(sep).join("/");
|
|
595
|
-
}
|
|
596
|
-
function artifactTagFromRelPath(relPosix) {
|
|
597
|
-
const parts = relPosix.split("/");
|
|
598
|
-
const fileName = parts[parts.length - 1] ?? relPosix;
|
|
599
|
-
if (parts.length > 1) {
|
|
600
|
-
return parts[0] ?? fileName;
|
|
601
|
-
}
|
|
602
|
-
const dot = fileName.lastIndexOf(".");
|
|
603
|
-
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
604
|
-
}
|
|
605
|
-
function* walkMarkdownFiles(dir) {
|
|
606
|
-
const names = readdirSync2(dir);
|
|
607
|
-
for (const name of names) {
|
|
608
|
-
if (name.startsWith(".")) continue;
|
|
609
|
-
if (name === REQUIREMENT_ATTACHMENTS_SUBDIR) continue;
|
|
610
|
-
const full = join4(dir, name);
|
|
611
|
-
const st = statSync2(full);
|
|
612
|
-
if (st.isDirectory()) {
|
|
613
|
-
yield* walkMarkdownFiles(full);
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
if (!st.isFile()) continue;
|
|
617
|
-
if (!name.toLowerCase().endsWith(".md")) continue;
|
|
618
|
-
yield full;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
async function runUploadArtifact(requirementId, workspaceDir) {
|
|
622
|
-
const cfg = await ensureLoggedConfig();
|
|
623
|
-
const api = createApmApiClient(cfg);
|
|
624
|
-
const root = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
625
|
-
if (!existsSync3(root)) {
|
|
626
|
-
console.error(
|
|
627
|
-
`[apm] \u76EE\u5F55\u4E0D\u5B58\u5728: ${root}
|
|
628
|
-
\u8BF7\u5148\u6267\u884C: apm pull ${requirementId}`
|
|
629
|
-
);
|
|
630
|
-
process.exit(1);
|
|
631
|
-
}
|
|
632
|
-
for (const abs of walkMarkdownFiles(root)) {
|
|
633
|
-
const relPosix = toPosixRelative(root, abs);
|
|
634
|
-
if (EXCLUDED_RELATIVE_PATHS.has(relPosix)) continue;
|
|
635
|
-
const content = readFileSync3(abs, "utf8");
|
|
636
|
-
const tag = artifactTagFromRelPath(relPosix);
|
|
637
|
-
await api.requirementArtifact.upsert({
|
|
638
|
-
requirementId,
|
|
639
|
-
tag,
|
|
640
|
-
fileName: relPosix,
|
|
641
|
-
content
|
|
642
|
-
});
|
|
643
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u4EA7\u7269: ${relPosix}`);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// src/commands/pull.ts
|
|
648
|
-
import { writeFileSync as writeFileSync3 } from "fs";
|
|
649
|
-
import { join as join5 } from "path";
|
|
650
|
-
import { stringify as yamlStringify } from "yaml";
|
|
651
|
-
|
|
652
|
-
// src/pull-reviews-xml.ts
|
|
653
|
-
function escapeForCdata(text) {
|
|
654
|
-
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
655
|
-
}
|
|
656
|
-
function buildReviewsXml(reviews) {
|
|
657
|
-
const lines = ["<reviews>"];
|
|
658
|
-
for (const review of reviews) {
|
|
659
|
-
lines.push(
|
|
660
|
-
` <review id="${xmlEscape(review.id)}" model="${xmlEscape(
|
|
661
|
-
review.model ?? ""
|
|
662
|
-
)}" stance="${xmlEscape(review.stance ?? "")}" memberRole="${xmlEscape(
|
|
663
|
-
review.memberRole
|
|
664
|
-
)}">`
|
|
665
|
-
);
|
|
666
|
-
for (const item of review.items) {
|
|
667
|
-
const kind = item.kind.toLowerCase();
|
|
668
|
-
lines.push(
|
|
669
|
-
` <item id="${xmlEscape(item.id)}" start="${item.startLine}" end="${item.endLine}" kind="${xmlEscape(kind)}" status="open">`
|
|
670
|
-
);
|
|
671
|
-
lines.push(
|
|
672
|
-
` <body><![CDATA[${escapeForCdata(item.body ?? "")}]]></body>`
|
|
673
|
-
);
|
|
674
|
-
const reply = item.reply?.trim() ?? "";
|
|
675
|
-
lines.push(` <reply><![CDATA[${escapeForCdata(reply)}]]></reply>`);
|
|
676
|
-
lines.push(" </item>");
|
|
677
|
-
}
|
|
678
|
-
lines.push(" </review>");
|
|
679
|
-
}
|
|
680
|
-
lines.push("</reviews>", "");
|
|
681
|
-
return lines.join("\n");
|
|
223
|
+
// src/api/client.ts
|
|
224
|
+
function createApmApiClient(cfg) {
|
|
225
|
+
const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api`;
|
|
226
|
+
return createApiClient(requestConfig, {
|
|
227
|
+
baseURL,
|
|
228
|
+
getToken: () => cfg.token || void 0,
|
|
229
|
+
successCodes: [0],
|
|
230
|
+
unauthorizedCodes: [401]
|
|
231
|
+
});
|
|
682
232
|
}
|
|
683
233
|
|
|
684
|
-
// src/commands/
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const pageSize = 500;
|
|
698
|
-
let page = 1;
|
|
699
|
-
const items = [];
|
|
700
|
-
while (true) {
|
|
701
|
-
const batch = await api.requirementArtifact.list({
|
|
702
|
-
requirementId,
|
|
703
|
-
page,
|
|
704
|
-
pageSize
|
|
234
|
+
// src/commands/login.ts
|
|
235
|
+
async function runLogin(opts) {
|
|
236
|
+
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
237
|
+
const api = createApmApiClient({
|
|
238
|
+
baseUrl,
|
|
239
|
+
userId: "",
|
|
240
|
+
token: ""
|
|
241
|
+
});
|
|
242
|
+
let data;
|
|
243
|
+
try {
|
|
244
|
+
data = await api.auth.login({
|
|
245
|
+
email: opts.email,
|
|
246
|
+
password: opts.password
|
|
705
247
|
});
|
|
706
|
-
|
|
707
|
-
if (
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
);
|
|
713
|
-
if (!hit) return null;
|
|
714
|
-
const art = await api.requirementArtifact.get({ artifactId: hit.id });
|
|
715
|
-
return art.content;
|
|
716
|
-
}
|
|
717
|
-
function valueToXmlContent(value) {
|
|
718
|
-
if (value === null || value === void 0) return "";
|
|
719
|
-
if (typeof value === "string") return xmlEscape(value);
|
|
720
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
721
|
-
return xmlEscape(String(value));
|
|
722
|
-
}
|
|
723
|
-
return `<![CDATA[${JSON.stringify(value)}]]>`;
|
|
724
|
-
}
|
|
725
|
-
function recordToXmlLines(record, indent) {
|
|
726
|
-
const lines = [];
|
|
727
|
-
for (const [rawKey, val] of Object.entries(record)) {
|
|
728
|
-
const key = /^[a-zA-Z_][\w.-]*$/.test(rawKey) ? rawKey : "field";
|
|
729
|
-
lines.push(`${indent}<${key}>${valueToXmlContent(val)}</${key}>`);
|
|
730
|
-
}
|
|
731
|
-
return lines;
|
|
732
|
-
}
|
|
733
|
-
function unknownArrayToXml(rootName, itemName, items) {
|
|
734
|
-
const lines = [`<${rootName}>`];
|
|
735
|
-
items.forEach((item, index) => {
|
|
736
|
-
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
737
|
-
lines.push(` <${itemName} index="${index}">`);
|
|
738
|
-
lines.push(...recordToXmlLines(item, " "));
|
|
739
|
-
lines.push(` </${itemName}>`);
|
|
740
|
-
} else {
|
|
741
|
-
lines.push(
|
|
742
|
-
` <${itemName} index="${index}">${valueToXmlContent(
|
|
743
|
-
item
|
|
744
|
-
)}</${itemName}>`
|
|
248
|
+
} catch (raw) {
|
|
249
|
+
if (raw instanceof ApiError) {
|
|
250
|
+
const err = raw;
|
|
251
|
+
console.error(
|
|
252
|
+
`[apm] \u767B\u5F55\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
|
|
253
|
+
err.message
|
|
745
254
|
);
|
|
255
|
+
process.exit(1);
|
|
746
256
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
return lines.join("\n");
|
|
750
|
-
}
|
|
751
|
-
function escapeForCdata2(text) {
|
|
752
|
-
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
753
|
-
}
|
|
754
|
-
function defectsToXml(defects) {
|
|
755
|
-
const sorted = [...defects].sort(
|
|
756
|
-
(a, b) => new Date(a.createdAt ?? 0).getTime() - new Date(b.createdAt ?? 0).getTime()
|
|
757
|
-
);
|
|
758
|
-
const lines = ["<defects>"];
|
|
759
|
-
for (const d of sorted) {
|
|
760
|
-
lines.push(` <defect id="${xmlEscape(d.id)}">`);
|
|
761
|
-
lines.push(` <status>${xmlEscape(d.status)}</status>`);
|
|
762
|
-
lines.push(
|
|
763
|
-
` <current><![CDATA[${escapeForCdata2(
|
|
764
|
-
d.currentState ?? ""
|
|
765
|
-
)}]]></current>`
|
|
766
|
-
);
|
|
767
|
-
lines.push(
|
|
768
|
-
` <expected><![CDATA[${escapeForCdata2(
|
|
769
|
-
d.expectedEffect ?? ""
|
|
770
|
-
)}]]></expected>`
|
|
771
|
-
);
|
|
772
|
-
lines.push(` </defect>`);
|
|
773
|
-
}
|
|
774
|
-
lines.push("</defects>", "");
|
|
775
|
-
return lines.join("\n");
|
|
776
|
-
}
|
|
777
|
-
async function runPull(requirementId, workspaceDir) {
|
|
778
|
-
const cfg = await ensureLoggedConfig();
|
|
779
|
-
const api = createApmApiClient(cfg);
|
|
780
|
-
const data = await api.cliRequirements.pull({ requirementId });
|
|
781
|
-
const WORKITEMS_DIR = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
782
|
-
await ensureDirExists(WORKITEMS_DIR);
|
|
783
|
-
const req2 = data.requirement;
|
|
784
|
-
const statusYaml = yamlStringify(
|
|
785
|
-
{
|
|
786
|
-
id: req2.id,
|
|
787
|
-
status: req2.status,
|
|
788
|
-
title: req2.title
|
|
789
|
-
},
|
|
790
|
-
{ lineWidth: 0 }
|
|
791
|
-
);
|
|
792
|
-
writeFileSync3(
|
|
793
|
-
join5(WORKITEMS_DIR, "requirement-status.yaml"),
|
|
794
|
-
statusYaml.endsWith("\n") ? statusYaml : `${statusYaml}
|
|
795
|
-
`,
|
|
796
|
-
"utf8"
|
|
797
|
-
);
|
|
798
|
-
writeFileSync3(join5(WORKITEMS_DIR, "prd.md"), req2.content || "", "utf8");
|
|
799
|
-
const reviewsXml = buildReviewsXml(data.reviews ?? []);
|
|
800
|
-
writeFileSync3(join5(WORKITEMS_DIR, "reviews.xml"), reviewsXml, "utf8");
|
|
801
|
-
const defectsXml = defectsToXml(data.defects ?? []);
|
|
802
|
-
writeFileSync3(join5(WORKITEMS_DIR, "defect.xml"), defectsXml, "utf8");
|
|
803
|
-
const testCasesXml = unknownArrayToXml(
|
|
804
|
-
"testcases",
|
|
805
|
-
"case",
|
|
806
|
-
data.testCases ?? []
|
|
807
|
-
);
|
|
808
|
-
writeFileSync3(join5(WORKITEMS_DIR, "testcase.xml"), testCasesXml, "utf8");
|
|
809
|
-
for (const fileName of PULL_ARTIFACT_FILE_NAMES) {
|
|
810
|
-
const content = await fetchArtifactContentByFileName(
|
|
811
|
-
api,
|
|
812
|
-
requirementId,
|
|
813
|
-
fileName
|
|
814
|
-
);
|
|
815
|
-
if (content !== null) {
|
|
816
|
-
writeFileSync3(join5(WORKITEMS_DIR, fileName), content, "utf8");
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
await syncRequirementAttachments(api, requirementId, WORKITEMS_DIR);
|
|
820
|
-
return WORKITEMS_DIR;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
// src/commands/pull-theater-session.ts
|
|
824
|
-
import { writeFileSync as writeFileSync4 } from "fs";
|
|
825
|
-
import { dirname as dirname2, join as join6 } from "path";
|
|
826
|
-
function normalizeRelativePath(fileName) {
|
|
827
|
-
return theaterArtifactLocalRelativePath(fileName);
|
|
828
|
-
}
|
|
829
|
-
async function runPullTheaterSession(sessionId, apmRoot = WORKSPACE_APM_DIR) {
|
|
830
|
-
const trimmedSessionId = sessionId.trim();
|
|
831
|
-
if (!trimmedSessionId) {
|
|
832
|
-
throw new Error("sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
833
|
-
}
|
|
834
|
-
const cfg = await ensureLoggedConfig();
|
|
835
|
-
const api = createApmApiClient(cfg);
|
|
836
|
-
const sessionDir = theaterSessionDir(trimmedSessionId, apmRoot);
|
|
837
|
-
await ensureDirExists(sessionDir);
|
|
838
|
-
await ensureDirExists(join6(sessionDir, "docs"));
|
|
839
|
-
const pageSize = 500;
|
|
840
|
-
let page = 1;
|
|
841
|
-
const items = [];
|
|
842
|
-
while (true) {
|
|
843
|
-
const batch = await api.theaterSessionArtifact.list({
|
|
844
|
-
sessionId: trimmedSessionId,
|
|
845
|
-
page,
|
|
846
|
-
pageSize
|
|
847
|
-
});
|
|
848
|
-
items.push(...batch.items);
|
|
849
|
-
if (batch.total === 0 || items.length >= batch.total) break;
|
|
850
|
-
page += 1;
|
|
851
|
-
}
|
|
852
|
-
for (const item of items) {
|
|
853
|
-
const art = await api.theaterSessionArtifact.get({
|
|
854
|
-
sessionId: trimmedSessionId,
|
|
855
|
-
artifactId: item.id
|
|
856
|
-
});
|
|
857
|
-
const relativePath = normalizeRelativePath(art.fileName);
|
|
858
|
-
const destPath = join6(sessionDir, relativePath);
|
|
859
|
-
await ensureDirExists(dirname2(destPath));
|
|
860
|
-
writeFileSync4(destPath, art.content, "utf8");
|
|
861
|
-
console.log(`[apm] \u5DF2\u8986\u76D6\u4F1A\u8BDD\u6587\u6863: ${relativePath}`);
|
|
257
|
+
console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
|
|
258
|
+
process.exit(1);
|
|
862
259
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
import { resolve as resolve3 } from "path";
|
|
869
|
-
import { promisify } from "util";
|
|
870
|
-
var execFileAsync = promisify(execFile);
|
|
871
|
-
async function fetchBaselineBranchFromApi(requirementId, cwd) {
|
|
872
|
-
const cfg = await ensureLoggedConfig();
|
|
873
|
-
const api = createApmApiClient(cfg);
|
|
874
|
-
const workdirPath = resolve3(cwd);
|
|
875
|
-
const res = await api.cliRequirements.branchBaseline({
|
|
876
|
-
requirementId,
|
|
877
|
-
workdirPath
|
|
878
|
-
});
|
|
879
|
-
if (!res.repositoryId) {
|
|
880
|
-
throw new Error(
|
|
881
|
-
`[apm] \u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}
|
|
882
|
-
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`
|
|
260
|
+
const userId = data?.user?.id;
|
|
261
|
+
const token = data?.token;
|
|
262
|
+
if (!userId || !token) {
|
|
263
|
+
console.error(
|
|
264
|
+
"[apm] \u54CD\u5E94\u7F3A\u5C11 user.id / token\uFF08\u8BF7\u786E\u8BA4\u670D\u52A1\u7AEF\u4E3A /api/auth/login\uFF09"
|
|
883
265
|
);
|
|
266
|
+
process.exit(1);
|
|
884
267
|
}
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
268
|
+
const cfg = {
|
|
269
|
+
baseUrl,
|
|
270
|
+
userId,
|
|
271
|
+
token,
|
|
272
|
+
email: opts.email
|
|
273
|
+
};
|
|
274
|
+
await writeApmConfig(cfg);
|
|
275
|
+
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
276
|
+
console.log(
|
|
277
|
+
JSON.stringify({ userId: cfg.userId, baseUrl: cfg.baseUrl }, null, 2)
|
|
278
|
+
);
|
|
890
279
|
}
|
|
891
|
-
|
|
280
|
+
|
|
281
|
+
// src/commands/branch.ts
|
|
282
|
+
import { execFile } from "child_process";
|
|
283
|
+
import { resolve as resolve2 } from "path";
|
|
284
|
+
import { promisify } from "util";
|
|
285
|
+
var execFileAsync = promisify(execFile);
|
|
286
|
+
async function fetchBaselineBranchFromApi(sessionId, cwd) {
|
|
892
287
|
const cfg = await ensureLoggedConfig();
|
|
893
288
|
const api = createApmApiClient(cfg);
|
|
894
|
-
const workdirPath =
|
|
895
|
-
const res = await api.
|
|
289
|
+
const workdirPath = resolve2(cwd);
|
|
290
|
+
const res = await api.cli.branchBaseline({
|
|
896
291
|
sessionId,
|
|
897
292
|
workdirPath
|
|
898
293
|
});
|
|
@@ -920,7 +315,6 @@ function branchNameForRequirement(requirementId) {
|
|
|
920
315
|
}
|
|
921
316
|
return `feat/req-${id}`;
|
|
922
317
|
}
|
|
923
|
-
var branchNameForTheaterSession = branchNameForRequirement;
|
|
924
318
|
async function execGit(cwd, args, quiet) {
|
|
925
319
|
try {
|
|
926
320
|
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
@@ -971,11 +365,6 @@ async function localBranchExists(cwd, branch) {
|
|
|
971
365
|
return false;
|
|
972
366
|
}
|
|
973
367
|
}
|
|
974
|
-
function theaterSessionCommitMessage(sessionId, memberKey) {
|
|
975
|
-
const id = sessionId.trim();
|
|
976
|
-
const key = memberKey?.trim();
|
|
977
|
-
return key ? `feat(req-${id}): ${key} theater agent` : `feat(req-${id}): theater agent`;
|
|
978
|
-
}
|
|
979
368
|
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
980
369
|
await ensureGitRepo(cwd);
|
|
981
370
|
if (!await isWorkingTreeDirty(cwd)) {
|
|
@@ -1038,538 +427,129 @@ async function ensureFeatureBranch(branch, options, fetchBaseline) {
|
|
|
1038
427
|
console.log(`[apm] \u5DF2\u5C31\u7EEA\u5206\u652F ${branch}`);
|
|
1039
428
|
return branch;
|
|
1040
429
|
}
|
|
1041
|
-
async function runBranch(
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
async function runTheaterSessionBranch(sessionId, options = {}) {
|
|
430
|
+
async function runBranch(sessionId, options = {}) {
|
|
431
|
+
const trimmedSessionId = sessionId.trim();
|
|
432
|
+
if (!trimmedSessionId) {
|
|
433
|
+
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
const cfg = await ensureLoggedConfig();
|
|
437
|
+
const api = createApmApiClient(cfg);
|
|
1051
438
|
const cwd = options.cwd ?? process.cwd();
|
|
1052
|
-
const
|
|
439
|
+
const baseline = await api.cli.branchBaseline({
|
|
440
|
+
sessionId: trimmedSessionId,
|
|
441
|
+
workdirPath: resolve2(cwd)
|
|
442
|
+
});
|
|
443
|
+
const branch = branchNameForRequirement(baseline.requirementId);
|
|
1053
444
|
return ensureFeatureBranch(
|
|
1054
445
|
branch,
|
|
1055
446
|
options,
|
|
1056
|
-
() =>
|
|
447
|
+
() => fetchBaselineBranchFromApi(trimmedSessionId, cwd)
|
|
1057
448
|
);
|
|
1058
449
|
}
|
|
1059
450
|
|
|
1060
|
-
// src/
|
|
1061
|
-
import {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
return "\u672A\u77E5\u9519\u8BEF";
|
|
1075
|
-
}
|
|
1076
|
-
function isUnauthorizedMessage(msg) {
|
|
1077
|
-
return msg.includes("\u672A\u767B\u5F55") || /401/.test(msg);
|
|
1078
|
-
}
|
|
1079
|
-
function sendTheaterReportWs(ws, payload) {
|
|
1080
|
-
return new Promise((resolve5, reject) => {
|
|
1081
|
-
const msg_id = randomUUID();
|
|
1082
|
-
const timer = setTimeout(() => {
|
|
1083
|
-
ws.off("message", onMessage);
|
|
1084
|
-
reject(new Error("WebSocket \u4E0A\u62A5\u8D85\u65F6"));
|
|
1085
|
-
}, 3e4);
|
|
1086
|
-
const onMessage = (data) => {
|
|
1087
|
-
const text = typeof data === "string" ? data : data.toString();
|
|
1088
|
-
let frame;
|
|
1089
|
-
try {
|
|
1090
|
-
frame = JSON.parse(text);
|
|
1091
|
-
} catch {
|
|
1092
|
-
return;
|
|
1093
|
-
}
|
|
1094
|
-
if (frame.msg_id !== msg_id) return;
|
|
1095
|
-
clearTimeout(timer);
|
|
1096
|
-
ws.off("message", onMessage);
|
|
1097
|
-
if (frame.type === "ERROR") {
|
|
1098
|
-
reject(
|
|
1099
|
-
new Error(frame.payload?.message?.trim() || "WebSocket \u4E0A\u62A5\u5931\u8D25")
|
|
1100
|
-
);
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
if (frame.type === "ACK") {
|
|
1104
|
-
resolve5();
|
|
1105
|
-
return;
|
|
1106
|
-
}
|
|
1107
|
-
};
|
|
1108
|
-
ws.on("message", onMessage);
|
|
1109
|
-
ws.send(
|
|
1110
|
-
JSON.stringify({
|
|
1111
|
-
type: "THEATER_REPORT",
|
|
1112
|
-
msg_id,
|
|
1113
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1114
|
-
payload
|
|
1115
|
-
})
|
|
1116
|
-
);
|
|
451
|
+
// src/commands/pull.ts
|
|
452
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
453
|
+
import { join as join5 } from "path";
|
|
454
|
+
import { stringify as yamlStringify } from "yaml";
|
|
455
|
+
|
|
456
|
+
// src/commands/sync-session-attachments.ts
|
|
457
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
458
|
+
import { join as join4 } from "path";
|
|
459
|
+
async function downloadAttachment(cfg, sessionId, attachmentId) {
|
|
460
|
+
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
461
|
+
const q = new URLSearchParams({ sessionId, attachmentId });
|
|
462
|
+
const url = `${base}/api/cli/attachments/file?${q.toString()}`;
|
|
463
|
+
const res = await fetch(url, {
|
|
464
|
+
headers: { Authorization: `Bearer ${cfg.token}` }
|
|
1117
465
|
});
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
sessionId: params.sessionId,
|
|
1122
|
-
memberKey: params.memberKey,
|
|
1123
|
-
status: "IN_PROGRESS",
|
|
1124
|
-
logId: params.logId
|
|
1125
|
-
};
|
|
1126
|
-
try {
|
|
1127
|
-
await api.theater.reportMemberAgentProgress(body);
|
|
1128
|
-
} catch (err) {
|
|
1129
|
-
const msg = extractErrorMessage(err);
|
|
1130
|
-
if (!isUnauthorizedMessage(msg)) throw err;
|
|
1131
|
-
await sendTheaterReportWs(ws, { action: "progress", ...body });
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
async function submitTheaterResult(api, ws, params) {
|
|
1135
|
-
const body = {
|
|
1136
|
-
sessionId: params.sessionId,
|
|
1137
|
-
memberKey: params.memberKey,
|
|
1138
|
-
content: params.content,
|
|
1139
|
-
agentId: params.agentId ?? "",
|
|
1140
|
-
agentStatus: params.agentStatus,
|
|
1141
|
-
logId: params.logId
|
|
1142
|
-
};
|
|
1143
|
-
try {
|
|
1144
|
-
await api.theater.submitMemberResponse(body);
|
|
1145
|
-
} catch (err) {
|
|
1146
|
-
const msg = extractErrorMessage(err);
|
|
1147
|
-
if (!isUnauthorizedMessage(msg)) throw err;
|
|
1148
|
-
await sendTheaterReportWs(ws, {
|
|
1149
|
-
action: "submit",
|
|
1150
|
-
sessionId: params.sessionId,
|
|
1151
|
-
memberKey: params.memberKey,
|
|
1152
|
-
content: params.content,
|
|
1153
|
-
agentId: params.agentId,
|
|
1154
|
-
agentStatus: params.agentStatus,
|
|
1155
|
-
logId: params.logId
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
async function submitTheaterFailure(api, ws, params) {
|
|
1160
|
-
const content = `[${params.stage}] ${params.error}`;
|
|
1161
|
-
try {
|
|
1162
|
-
await submitTheaterResult(api, ws, {
|
|
1163
|
-
sessionId: params.sessionId,
|
|
1164
|
-
memberKey: params.memberKey,
|
|
1165
|
-
content,
|
|
1166
|
-
agentId: params.agentId,
|
|
1167
|
-
agentStatus: "FAILED",
|
|
1168
|
-
logId: params.logId
|
|
1169
|
-
});
|
|
1170
|
-
return true;
|
|
1171
|
-
} catch (submitErr) {
|
|
1172
|
-
console.error(
|
|
1173
|
-
"[apm] \u65E0\u6CD5\u5C06\u5931\u8D25\u72B6\u6001\u5199\u5165\u5267\u573A\u4F1A\u8BDD:",
|
|
1174
|
-
extractErrorMessage(submitErr)
|
|
466
|
+
if (!res.ok) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
1175
469
|
);
|
|
1176
|
-
return false;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
function logTheaterJobError(stage, err, meta) {
|
|
1180
|
-
const msg = extractErrorMessage(err);
|
|
1181
|
-
const ctx = [
|
|
1182
|
-
meta?.sessionId ? `session=${meta.sessionId}` : "",
|
|
1183
|
-
meta?.memberKey ? `member=${meta.memberKey}` : ""
|
|
1184
|
-
].filter(Boolean).join(" ");
|
|
1185
|
-
console.error(
|
|
1186
|
-
`[apm] \u5267\u573A THEATER_JOB \u5931\u8D25 \xB7 \u9636\u6BB5=${stage}${ctx ? ` \xB7 ${ctx}` : ""} \xB7 ${msg}`
|
|
1187
|
-
);
|
|
1188
|
-
if (isUnauthorizedMessage(msg)) {
|
|
1189
|
-
console.error("[apm] \u63D0\u793A: \u8BF7\u6267\u884C apm login \u91CD\u65B0\u767B\u5F55\u540E\u518D\u8BD5");
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
// src/commands/connect.ts
|
|
1194
|
-
var DEFAULT_AGENT_MODEL_ID = "default";
|
|
1195
|
-
function resolveAgentModelId(model) {
|
|
1196
|
-
const id = model?.trim();
|
|
1197
|
-
return id || DEFAULT_AGENT_MODEL_ID;
|
|
1198
|
-
}
|
|
1199
|
-
async function createAgentForJob(payload) {
|
|
1200
|
-
const options = {
|
|
1201
|
-
apiKey: payload.apiKey,
|
|
1202
|
-
model: { id: resolveAgentModelId(payload.model) },
|
|
1203
|
-
local: { cwd: payload.cwd }
|
|
1204
|
-
};
|
|
1205
|
-
if (payload.agentId) {
|
|
1206
|
-
return Agent.resume(payload.agentId, options);
|
|
1207
|
-
}
|
|
1208
|
-
return Agent.create(options);
|
|
1209
|
-
}
|
|
1210
|
-
async function runAgentStream(payload, session, agent) {
|
|
1211
|
-
const run = await agent.send(payload.prompt);
|
|
1212
|
-
let failed = false;
|
|
1213
|
-
let failReason = "";
|
|
1214
|
-
for await (const event of run.stream()) {
|
|
1215
|
-
if (event.type === "status") {
|
|
1216
|
-
console.log(`[Status]`, event.message || event.status);
|
|
1217
|
-
if (event.status === "ERROR") {
|
|
1218
|
-
failed = true;
|
|
1219
|
-
failReason = event.message?.trim() ?? "";
|
|
1220
|
-
console.error("[apm] Agent \u6267\u884C\u5931\u8D25:", failReason || "(\u65E0\u5931\u8D25\u539F\u56E0)");
|
|
1221
|
-
break;
|
|
1222
|
-
}
|
|
1223
|
-
continue;
|
|
1224
|
-
}
|
|
1225
|
-
if (event.type === "assistant") {
|
|
1226
|
-
const output = event.message.content[0].text;
|
|
1227
|
-
console.log(`[Output]`, output);
|
|
1228
|
-
session.addEvent(event);
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
if (event.type === "thinking") {
|
|
1232
|
-
console.log(`[Thinking]`, event.text);
|
|
1233
|
-
continue;
|
|
1234
|
-
}
|
|
1235
|
-
if (event.type === "tool_call") {
|
|
1236
|
-
if (event.status === "completed" || event.status === "error") {
|
|
1237
|
-
console.log(`[ToolCall(${event.call_id}) Result]`);
|
|
1238
|
-
}
|
|
1239
|
-
session.addEvent(event);
|
|
1240
|
-
continue;
|
|
1241
|
-
}
|
|
1242
|
-
if (event.type === "task") {
|
|
1243
|
-
console.log(`[Task:${event.status}]`);
|
|
1244
|
-
session.addEvent(event);
|
|
1245
|
-
continue;
|
|
1246
|
-
}
|
|
1247
|
-
if (event.type === "request") {
|
|
1248
|
-
console.log(JSON.stringify(event, null, 2));
|
|
1249
|
-
session.addEvent(event);
|
|
1250
|
-
continue;
|
|
1251
|
-
}
|
|
1252
|
-
console.log("\u672A\u77E5\u4E8B\u4EF6:", JSON.stringify(event, null, 2));
|
|
1253
470
|
}
|
|
1254
|
-
return
|
|
471
|
+
return Buffer.from(await res.arrayBuffer());
|
|
1255
472
|
}
|
|
1256
|
-
async function
|
|
1257
|
-
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
throw new Error("THEATER_JOB \u7F3A\u5C11 memberKey");
|
|
1265
|
-
}
|
|
1266
|
-
console.log("[apm] \u5267\u573A\u4F1A\u8BDD:", sessionId, "\u6210\u5458:", memberKey);
|
|
1267
|
-
const apmRoot = join7(payload.cwd, ".apm");
|
|
1268
|
-
let agentId = payload.agentId?.trim() || void 0;
|
|
1269
|
-
try {
|
|
1270
|
-
await reportTheaterProgress(api, ws, { sessionId, memberKey, logId });
|
|
1271
|
-
} catch (err) {
|
|
1272
|
-
logTheaterJobError("\u4E0A\u62A5\u6267\u884C\u8FDB\u5EA6", err, { sessionId, memberKey });
|
|
1273
|
-
await submitTheaterFailure(api, ws, {
|
|
1274
|
-
sessionId,
|
|
1275
|
-
memberKey,
|
|
1276
|
-
logId,
|
|
1277
|
-
agentId,
|
|
1278
|
-
stage: "\u4E0A\u62A5\u8FDB\u5EA6",
|
|
1279
|
-
error: extractErrorMessage(err)
|
|
1280
|
-
});
|
|
1281
|
-
return;
|
|
1282
|
-
}
|
|
1283
|
-
try {
|
|
1284
|
-
await runTheaterSessionBranch(sessionId, { cwd: payload.cwd });
|
|
1285
|
-
} catch (branchErr) {
|
|
1286
|
-
logTheaterJobError("\u5207\u6362\u5206\u652F", branchErr, { sessionId, memberKey });
|
|
1287
|
-
await submitTheaterFailure(api, ws, {
|
|
1288
|
-
sessionId,
|
|
1289
|
-
memberKey,
|
|
1290
|
-
logId,
|
|
1291
|
-
agentId,
|
|
1292
|
-
stage: "\u5207\u6362\u5206\u652F",
|
|
1293
|
-
error: extractErrorMessage(branchErr)
|
|
1294
|
-
});
|
|
1295
|
-
return;
|
|
1296
|
-
}
|
|
1297
|
-
try {
|
|
1298
|
-
await runPullTheaterSession(sessionId, apmRoot);
|
|
1299
|
-
} catch (pullErr) {
|
|
1300
|
-
logTheaterJobError("\u62C9\u53D6\u4F1A\u8BDD\u6587\u6863", pullErr, { sessionId, memberKey });
|
|
1301
|
-
await submitTheaterFailure(api, ws, {
|
|
1302
|
-
sessionId,
|
|
1303
|
-
memberKey,
|
|
1304
|
-
logId,
|
|
1305
|
-
agentId,
|
|
1306
|
-
stage: "\u62C9\u53D6\u4F1A\u8BDD\u6587\u6863",
|
|
1307
|
-
error: extractErrorMessage(pullErr)
|
|
1308
|
-
});
|
|
1309
|
-
return;
|
|
1310
|
-
}
|
|
1311
|
-
try {
|
|
1312
|
-
const eventSession = new EventSession(payload.prompt);
|
|
1313
|
-
const agent = await createAgentForJob(payload);
|
|
1314
|
-
const runResult = await runAgentStream(payload, eventSession, agent);
|
|
1315
|
-
agentId = runResult.agentId;
|
|
1316
|
-
try {
|
|
1317
|
-
await commitWorkingTreeIfDirty(
|
|
1318
|
-
payload.cwd,
|
|
1319
|
-
theaterSessionCommitMessage(sessionId, memberKey)
|
|
1320
|
-
);
|
|
1321
|
-
} catch (commitErr) {
|
|
1322
|
-
logTheaterJobError("\u63D0\u4EA4\u5DE5\u4F5C\u533A", commitErr, { sessionId, memberKey });
|
|
1323
|
-
await submitTheaterFailure(api, ws, {
|
|
1324
|
-
sessionId,
|
|
1325
|
-
memberKey,
|
|
1326
|
-
logId,
|
|
1327
|
-
agentId,
|
|
1328
|
-
stage: "\u63D0\u4EA4\u5DE5\u4F5C\u533A",
|
|
1329
|
-
error: extractErrorMessage(commitErr)
|
|
1330
|
-
});
|
|
1331
|
-
return;
|
|
1332
|
-
}
|
|
1333
|
-
const assistantText = eventSession.getAssistantText();
|
|
1334
|
-
const content = runResult.failed ? runResult.failReason || assistantText || "\u672A\u77E5\u9519\u8BEF" : assistantText || "[Agent \u672A\u4EA7\u751F\u6587\u672C\u8F93\u51FA]";
|
|
1335
|
-
await submitTheaterResult(api, ws, {
|
|
1336
|
-
sessionId,
|
|
1337
|
-
memberKey,
|
|
1338
|
-
content,
|
|
1339
|
-
agentId,
|
|
1340
|
-
agentStatus: runResult.failed ? "FAILED" : "SUCCESS",
|
|
1341
|
-
logId
|
|
1342
|
-
});
|
|
473
|
+
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
474
|
+
if (attachments.length === 0) return;
|
|
475
|
+
const dir = join4(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
476
|
+
await ensureDirExists(dir);
|
|
477
|
+
for (const item of attachments) {
|
|
478
|
+
const dest = join4(dir, item.name);
|
|
479
|
+
const buffer = await downloadAttachment(cfg, sessionId, item.id);
|
|
480
|
+
writeFileSync3(dest, buffer);
|
|
1343
481
|
console.log(
|
|
1344
|
-
|
|
1345
|
-
runResult.failed ? "(\u6267\u884C\u5931\u8D25)" : ""
|
|
482
|
+
`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${SESSION_ATTACHMENTS_SUBDIR}/${item.name}`
|
|
1346
483
|
);
|
|
1347
|
-
} catch (err) {
|
|
1348
|
-
logTheaterJobError("\u6267\u884C\u6216\u63D0\u4EA4\u56DE\u590D", err, { sessionId, memberKey });
|
|
1349
|
-
const reported = await submitTheaterFailure(api, ws, {
|
|
1350
|
-
sessionId,
|
|
1351
|
-
memberKey,
|
|
1352
|
-
logId,
|
|
1353
|
-
agentId,
|
|
1354
|
-
stage: "\u6267\u884C\u4EFB\u52A1",
|
|
1355
|
-
error: extractErrorMessage(err)
|
|
1356
|
-
});
|
|
1357
|
-
if (reported) {
|
|
1358
|
-
console.error("[apm] \u5DF2\u5C06\u5931\u8D25\u539F\u56E0\u5199\u5165\u5267\u573A\u4F1A\u8BDD\uFF0C\u53EF\u5728\u7F51\u9875\u67E5\u770B");
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
async function handleWorkflowJob(api, payload) {
|
|
1363
|
-
const apmRoot = join7(payload.cwd, ".apm");
|
|
1364
|
-
console.log("[apm] ROOT:", apmRoot);
|
|
1365
|
-
console.log("[apm] workflow node:", payload.nodeId);
|
|
1366
|
-
try {
|
|
1367
|
-
await runPull(payload.requirementId, apmRoot);
|
|
1368
|
-
} catch (pullErr) {
|
|
1369
|
-
console.error("[apm] apm pull \u5931\u8D25:", pullErr);
|
|
1370
|
-
throw pullErr;
|
|
1371
|
-
}
|
|
1372
|
-
const agent = await createAgentForJob(payload);
|
|
1373
|
-
await api.cliRequirements.updateDevStatus({
|
|
1374
|
-
requirementId: payload.requirementId,
|
|
1375
|
-
status: "WORKING"
|
|
1376
|
-
});
|
|
1377
|
-
await api.spaceWorkflow.aiStartNode({
|
|
1378
|
-
requirementId: payload.requirementId,
|
|
1379
|
-
nodeId: payload.nodeId,
|
|
1380
|
-
agentId: agent.agentId
|
|
1381
|
-
});
|
|
1382
|
-
const session = new EventSession(payload.prompt);
|
|
1383
|
-
const { agentId, failed, failReason } = await runAgentStream(
|
|
1384
|
-
payload,
|
|
1385
|
-
session,
|
|
1386
|
-
agent
|
|
1387
|
-
);
|
|
1388
|
-
await api.cliRequirements.updateDevStatus({
|
|
1389
|
-
requirementId: payload.requirementId,
|
|
1390
|
-
status: "IDLE"
|
|
1391
|
-
});
|
|
1392
|
-
session.writeToFile(payload.cwd, payload.requirementId, agentId);
|
|
1393
|
-
if (failed) {
|
|
1394
|
-
const failResult = await api.spaceWorkflow.aiFailNode({
|
|
1395
|
-
requirementId: payload.requirementId,
|
|
1396
|
-
nodeId: payload.nodeId,
|
|
1397
|
-
error: failReason
|
|
1398
|
-
});
|
|
1399
|
-
console.log("[apm] \u5DE5\u4F5C\u6D41\u8282\u70B9\u72B6\u6001:", failResult.nodeStatus);
|
|
1400
|
-
return;
|
|
1401
|
-
}
|
|
1402
|
-
console.log("[Done]");
|
|
1403
|
-
try {
|
|
1404
|
-
await runUploadArtifact(payload.requirementId, apmRoot);
|
|
1405
|
-
} catch (pullErr) {
|
|
1406
|
-
console.error("[apm] apm upload-artifact \u5931\u8D25:", pullErr);
|
|
1407
|
-
throw pullErr;
|
|
1408
|
-
}
|
|
1409
|
-
const confirmResult = await api.spaceWorkflow.aiConfirmNode({
|
|
1410
|
-
requirementId: payload.requirementId,
|
|
1411
|
-
nodeId: payload.nodeId
|
|
1412
|
-
});
|
|
1413
|
-
console.log("[apm] \u5DE5\u4F5C\u6D41\u8282\u70B9\u72B6\u6001:", confirmResult.nodeStatus);
|
|
1414
|
-
}
|
|
1415
|
-
function runConnect(opts) {
|
|
1416
|
-
void (async () => {
|
|
1417
|
-
const cfg = await ensureApmConfig();
|
|
1418
|
-
const server = opts.server?.trim();
|
|
1419
|
-
if (server) {
|
|
1420
|
-
cfg.baseUrl = server.replace(/\/+$/, "");
|
|
1421
|
-
await writeApmConfig(cfg);
|
|
1422
|
-
}
|
|
1423
|
-
if (!cfg.token) {
|
|
1424
|
-
console.error(
|
|
1425
|
-
"[apm] \u672A\u68C0\u6D4B\u5230 token\uFF0C\u8BF7\u5148\u6267\u884C: apm login --email ... --password ..."
|
|
1426
|
-
);
|
|
1427
|
-
console.error(`[apm] \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${APM_CONFIG_PATH}`);
|
|
1428
|
-
process.exit(1);
|
|
1429
|
-
}
|
|
1430
|
-
const api = createApmApiClient(cfg);
|
|
1431
|
-
const url = buildAgentWsUrl(cfg.baseUrl, cfg.token);
|
|
1432
|
-
console.error(`[apm] \u8FDE\u63A5 ${url.replace(cfg.token, "<token>")} \u2026`);
|
|
1433
|
-
const ws = new WebSocket(url);
|
|
1434
|
-
ws.on("open", () => {
|
|
1435
|
-
console.error("[apm] \u5DF2\u8FDE\u63A5");
|
|
1436
|
-
const sendHeartbeat = () => {
|
|
1437
|
-
const frame = {
|
|
1438
|
-
type: "HEARTBEAT",
|
|
1439
|
-
msg_id: randomUUID2(),
|
|
1440
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1441
|
-
payload: {}
|
|
1442
|
-
};
|
|
1443
|
-
ws.send(JSON.stringify(frame));
|
|
1444
|
-
};
|
|
1445
|
-
sendHeartbeat();
|
|
1446
|
-
const interval = setInterval(sendHeartbeat, 15e3);
|
|
1447
|
-
ws.on("close", () => clearInterval(interval));
|
|
1448
|
-
});
|
|
1449
|
-
ws.on("message", async (data) => {
|
|
1450
|
-
const text = typeof data === "string" ? data : data.toString();
|
|
1451
|
-
let msg;
|
|
1452
|
-
try {
|
|
1453
|
-
msg = JSON.parse(text);
|
|
1454
|
-
} catch (err) {
|
|
1455
|
-
console.error(
|
|
1456
|
-
"[apm] \u65E0\u6CD5\u89E3\u6790 WebSocket \u6D88\u606F:",
|
|
1457
|
-
text,
|
|
1458
|
-
err.message
|
|
1459
|
-
);
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1462
|
-
try {
|
|
1463
|
-
const jobType = msg.type;
|
|
1464
|
-
if (jobType === "THEATER_JOB") {
|
|
1465
|
-
await handleTheaterJob(api, ws, msg.payload);
|
|
1466
|
-
return;
|
|
1467
|
-
}
|
|
1468
|
-
if (jobType === "JOB") {
|
|
1469
|
-
await handleWorkflowJob(api, msg.payload);
|
|
1470
|
-
return;
|
|
1471
|
-
}
|
|
1472
|
-
} catch (err) {
|
|
1473
|
-
const jobType = msg.type;
|
|
1474
|
-
const label = jobType === "THEATER_JOB" ? "\u5267\u573A THEATER_JOB" : jobType === "JOB" ? "\u5DE5\u4F5C\u6D41 JOB" : String(jobType);
|
|
1475
|
-
console.error(`[apm] ${label} \u5904\u7406\u5931\u8D25:`, extractErrorMessage(err));
|
|
1476
|
-
}
|
|
1477
|
-
});
|
|
1478
|
-
ws.on("error", (err) => {
|
|
1479
|
-
console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
|
|
1480
|
-
});
|
|
1481
|
-
ws.on("close", (code, reason) => {
|
|
1482
|
-
console.error(`[apm] \u8FDE\u63A5\u5173\u95ED code=${code} reason=${reason.toString()}`);
|
|
1483
|
-
process.exit(code === 1e3 ? 0 : 1);
|
|
1484
|
-
});
|
|
1485
|
-
})();
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
// src/commands/init.ts
|
|
1489
|
-
import { join as join8 } from "path";
|
|
1490
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
1491
|
-
async function runInit(name) {
|
|
1492
|
-
await ensureWorkspaceApmDirForInit();
|
|
1493
|
-
await copyTemplateFiles(WORKSPACE_APM_DIR);
|
|
1494
|
-
if (name) {
|
|
1495
|
-
const apmConfigPath = join8(WORKSPACE_APM_DIR, "apm.config.json");
|
|
1496
|
-
const config = readFileSync4(apmConfigPath, "utf8");
|
|
1497
|
-
const configJson = JSON.parse(config);
|
|
1498
|
-
configJson.name = name;
|
|
1499
|
-
writeFileSync5(apmConfigPath, JSON.stringify(configJson, null, 2), "utf8");
|
|
1500
484
|
}
|
|
1501
485
|
}
|
|
1502
486
|
|
|
1503
|
-
// src/commands/
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
baseUrl,
|
|
1509
|
-
userId: "",
|
|
1510
|
-
token: ""
|
|
1511
|
-
});
|
|
1512
|
-
let data;
|
|
1513
|
-
try {
|
|
1514
|
-
data = await api.auth.login({
|
|
1515
|
-
email: opts.email,
|
|
1516
|
-
password: opts.password
|
|
1517
|
-
});
|
|
1518
|
-
} catch (raw) {
|
|
1519
|
-
if (raw instanceof ApiError) {
|
|
1520
|
-
const err = raw;
|
|
1521
|
-
console.error(
|
|
1522
|
-
`[apm] \u767B\u5F55\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
|
|
1523
|
-
err.message
|
|
1524
|
-
);
|
|
1525
|
-
process.exit(1);
|
|
1526
|
-
}
|
|
1527
|
-
console.error(
|
|
1528
|
-
"[apm] \u8BF7\u6C42\u5931\u8D25:",
|
|
1529
|
-
raw instanceof Error ? raw.message : raw
|
|
1530
|
-
);
|
|
1531
|
-
process.exit(1);
|
|
1532
|
-
}
|
|
1533
|
-
if (!data?.id || !data?.token) {
|
|
1534
|
-
console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 id / token\uFF08\u8BF7\u786E\u8BA4\u670D\u52A1\u7AEF\u4E3A /api/auth/login\uFF09");
|
|
487
|
+
// src/commands/pull.ts
|
|
488
|
+
async function runPull(sessionId, apmRoot) {
|
|
489
|
+
const trimmedId = sessionId.trim();
|
|
490
|
+
if (!trimmedId) {
|
|
491
|
+
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1535
492
|
process.exit(1);
|
|
1536
493
|
}
|
|
1537
|
-
const cfg = {
|
|
1538
|
-
baseUrl,
|
|
1539
|
-
userId: data.id,
|
|
1540
|
-
token: data.token,
|
|
1541
|
-
email: opts.email
|
|
1542
|
-
};
|
|
1543
|
-
await writeApmConfig(cfg);
|
|
1544
|
-
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
1545
|
-
console.log(JSON.stringify({ userId: cfg.userId, baseUrl: cfg.baseUrl }, null, 2));
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
// src/commands/refine.ts
|
|
1549
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
1550
|
-
import { join as join9 } from "path";
|
|
1551
|
-
async function runRefine(requirementId) {
|
|
1552
494
|
const cfg = await ensureLoggedConfig();
|
|
1553
|
-
const filePath = join9(WORKSPACE_APM_DIR, "workitems", requirementId, "prd.md");
|
|
1554
|
-
const content = readFileSync5(filePath, "utf8");
|
|
1555
495
|
const api = createApmApiClient(cfg);
|
|
1556
|
-
const
|
|
1557
|
-
|
|
496
|
+
const [detail, members, documents, attachments] = await Promise.all([
|
|
497
|
+
api.cli.sessionDetail({ sessionId: trimmedId }),
|
|
498
|
+
api.cli.sessionMembers({ sessionId: trimmedId }),
|
|
499
|
+
api.cli.listDocuments({ sessionId: trimmedId }),
|
|
500
|
+
api.cli.listAttachments({ sessionId: trimmedId })
|
|
501
|
+
]);
|
|
502
|
+
const dir = sessionDir(trimmedId, apmRoot);
|
|
503
|
+
const docsDir = sessionDocsDir(trimmedId, apmRoot);
|
|
504
|
+
await ensureDirExists(docsDir);
|
|
505
|
+
writeFileSync4(
|
|
506
|
+
sessionRulePath(trimmedId, apmRoot),
|
|
507
|
+
detail.description ?? "",
|
|
508
|
+
"utf8"
|
|
509
|
+
);
|
|
510
|
+
for (const doc of documents) {
|
|
511
|
+
const fileName = documentLocalFileName(doc.name);
|
|
512
|
+
writeFileSync4(join5(docsDir, fileName), doc.content ?? "", "utf8");
|
|
513
|
+
}
|
|
514
|
+
const sessionYaml = yamlStringify(
|
|
515
|
+
{
|
|
516
|
+
name: detail.title,
|
|
517
|
+
description: "./RULE.md",
|
|
518
|
+
members: members.map((m) => ({
|
|
519
|
+
name: m.displayName,
|
|
520
|
+
position: m.position,
|
|
521
|
+
system_persona: m.systemPersona,
|
|
522
|
+
skills: []
|
|
523
|
+
})),
|
|
524
|
+
skills: [],
|
|
525
|
+
attachments: attachments.map((a) => ({ name: a.name }))
|
|
526
|
+
},
|
|
527
|
+
{ lineWidth: 0 }
|
|
528
|
+
);
|
|
529
|
+
writeFileSync4(
|
|
530
|
+
sessionYamlPath(trimmedId, apmRoot),
|
|
531
|
+
sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
|
|
532
|
+
`,
|
|
533
|
+
"utf8"
|
|
534
|
+
);
|
|
535
|
+
await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
|
|
536
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
|
|
537
|
+
return dir;
|
|
1558
538
|
}
|
|
1559
539
|
|
|
1560
540
|
// src/commands/update.ts
|
|
1561
541
|
import { spawnSync } from "child_process";
|
|
1562
542
|
|
|
1563
543
|
// src/version.ts
|
|
1564
|
-
import { readFileSync as
|
|
1565
|
-
import { dirname as
|
|
544
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
545
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1566
546
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1567
547
|
var CLI_PACKAGE_NAME = "ai-project-manage-cli";
|
|
1568
548
|
function readCliVersion() {
|
|
1569
549
|
try {
|
|
1570
|
-
const dir =
|
|
1571
|
-
const pkgPath =
|
|
1572
|
-
const pkg = JSON.parse(
|
|
550
|
+
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
551
|
+
const pkgPath = join6(dir, "..", "package.json");
|
|
552
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
1573
553
|
return pkg.version ?? "0.0.0";
|
|
1574
554
|
} catch {
|
|
1575
555
|
return "0.0.0";
|
|
@@ -1636,147 +616,139 @@ async function runUpdate() {
|
|
|
1636
616
|
}
|
|
1637
617
|
|
|
1638
618
|
// src/commands/update-skills.ts
|
|
1639
|
-
import {
|
|
1640
|
-
import { join as
|
|
1641
|
-
|
|
619
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
620
|
+
import { join as join7 } from "path";
|
|
621
|
+
function sanitizeDirName(name) {
|
|
622
|
+
const trimmed = name.trim();
|
|
623
|
+
if (!trimmed) return "skill";
|
|
624
|
+
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
625
|
+
}
|
|
1642
626
|
async function runUpdateSkills() {
|
|
1643
|
-
const apmDir =
|
|
1644
|
-
if (!
|
|
627
|
+
const apmDir = workspaceApmDir();
|
|
628
|
+
if (!existsSync2(apmDir)) {
|
|
1645
629
|
console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
1646
630
|
process.exit(1);
|
|
1647
631
|
}
|
|
1648
|
-
const apmStat =
|
|
632
|
+
const apmStat = statSync2(apmDir);
|
|
1649
633
|
if (!apmStat.isDirectory()) {
|
|
1650
634
|
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
|
|
1651
635
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
}
|
|
1658
|
-
if (!templateStat.isDirectory()) {
|
|
1659
|
-
throw new Error(`[apm] \u5185\u7F6E\u6280\u80FD\u6A21\u677F\u4E0D\u662F\u76EE\u5F55: ${TEMPLATE_SKILLS_DIR}`);
|
|
1660
|
-
}
|
|
1661
|
-
const skillsDir = join11(apmDir, "skills");
|
|
1662
|
-
if (existsSync4(skillsDir)) {
|
|
636
|
+
const cfg = await ensureLoggedConfig();
|
|
637
|
+
const api = createApmApiClient(cfg);
|
|
638
|
+
const { list } = await api.cli.listSkills({});
|
|
639
|
+
const skillsDir = join7(apmDir, "skills");
|
|
640
|
+
if (existsSync2(skillsDir)) {
|
|
1663
641
|
rmSync(skillsDir, { recursive: true, force: true });
|
|
1664
642
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
import { join as join12 } from "path";
|
|
1673
|
-
var TEMPLATE_THEATER_SKILLS_DIR = join12(CLI_TEMPLATE_DIR, "theater-skills");
|
|
1674
|
-
async function runUpdateTheaterSkills() {
|
|
1675
|
-
const apmDir = WORKSPACE_APM_DIR;
|
|
1676
|
-
if (!existsSync5(apmDir)) {
|
|
1677
|
-
console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
1678
|
-
process.exit(1);
|
|
1679
|
-
}
|
|
1680
|
-
const apmStat = statSync4(apmDir);
|
|
1681
|
-
if (!apmStat.isDirectory()) {
|
|
1682
|
-
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
|
|
1683
|
-
}
|
|
1684
|
-
let templateStat;
|
|
1685
|
-
try {
|
|
1686
|
-
templateStat = statSync4(TEMPLATE_THEATER_SKILLS_DIR);
|
|
1687
|
-
} catch {
|
|
1688
|
-
throw new Error(
|
|
1689
|
-
`[apm] \u5185\u7F6E\u5267\u573A\u6280\u80FD\u6A21\u677F\u4E0D\u5B58\u5728: ${TEMPLATE_THEATER_SKILLS_DIR}`
|
|
1690
|
-
);
|
|
1691
|
-
}
|
|
1692
|
-
if (!templateStat.isDirectory()) {
|
|
1693
|
-
throw new Error(
|
|
1694
|
-
`[apm] \u5185\u7F6E\u5267\u573A\u6280\u80FD\u6A21\u677F\u4E0D\u662F\u76EE\u5F55: ${TEMPLATE_THEATER_SKILLS_DIR}`
|
|
1695
|
-
);
|
|
1696
|
-
}
|
|
1697
|
-
const theaterSkillsDir = join12(apmDir, "theater-skills");
|
|
1698
|
-
if (existsSync5(theaterSkillsDir)) {
|
|
1699
|
-
rmSync2(theaterSkillsDir, { recursive: true, force: true });
|
|
643
|
+
mkdirSync3(skillsDir, { recursive: true });
|
|
644
|
+
for (const skill of list) {
|
|
645
|
+
const dirName = sanitizeDirName(skill.name);
|
|
646
|
+
const skillDir = join7(skillsDir, dirName);
|
|
647
|
+
mkdirSync3(skillDir, { recursive: true });
|
|
648
|
+
writeFileSync5(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
649
|
+
console.log(`[apm] \u5DF2\u5199\u5165\u6280\u80FD: skills/${dirName}/SKILL.md`);
|
|
1700
650
|
}
|
|
1701
|
-
|
|
1702
|
-
cpSync3(TEMPLATE_THEATER_SKILLS_DIR, theaterSkillsDir, { recursive: true });
|
|
1703
|
-
console.log("[apm] \u5DF2\u66F4\u65B0 .apm/theater-skills");
|
|
651
|
+
console.log(`[apm] \u5DF2\u66F4\u65B0 ${list.length} \u4E2A\u6280\u80FD\u5230 .apm/skills`);
|
|
1704
652
|
}
|
|
1705
653
|
|
|
1706
|
-
// src/commands/sync-
|
|
1707
|
-
import { existsSync as
|
|
1708
|
-
|
|
1709
|
-
async function runSyncSessionDocument(sessionId, options) {
|
|
654
|
+
// src/commands/sync-document.ts
|
|
655
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
656
|
+
async function runSyncDocument(sessionId, options) {
|
|
1710
657
|
const trimmedSessionId = sessionId.trim();
|
|
1711
658
|
if (!trimmedSessionId) {
|
|
1712
659
|
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1713
660
|
process.exit(1);
|
|
1714
661
|
}
|
|
1715
|
-
const fileArg =
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
if (!existsSync6(absPath)) {
|
|
1724
|
-
console.error(`[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}
|
|
1725
|
-
\u8BF7\u5148\u521B\u5EFA\u8BE5\u6587\u4EF6\u540E\u518D\u540C\u6B65`);
|
|
662
|
+
const fileArg = options.file?.trim();
|
|
663
|
+
if (!fileArg) {
|
|
664
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --file <\u672C\u5730\u8DEF\u5F84>");
|
|
665
|
+
process.exit(1);
|
|
666
|
+
}
|
|
667
|
+
const absPath = resolveCwdPath(fileArg);
|
|
668
|
+
if (!existsSync3(absPath)) {
|
|
669
|
+
console.error(`[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}`);
|
|
1726
670
|
process.exit(1);
|
|
1727
671
|
}
|
|
1728
|
-
const content =
|
|
672
|
+
const content = readFileSync4(absPath, "utf8");
|
|
673
|
+
const name = documentPlatformName(absPath);
|
|
1729
674
|
const cfg = await ensureLoggedConfig();
|
|
1730
675
|
const api = createApmApiClient(cfg);
|
|
1731
|
-
const
|
|
676
|
+
const doc = await api.cli.upsertDocument({
|
|
1732
677
|
sessionId: trimmedSessionId,
|
|
1733
|
-
|
|
678
|
+
name,
|
|
1734
679
|
content
|
|
1735
680
|
});
|
|
1736
|
-
console.log(
|
|
1737
|
-
`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u6587\u6863: ${localRelativePath} \u2192 ${data.fileName} (artifactId=${data.id})`
|
|
1738
|
-
);
|
|
681
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u6587\u6863: ${name} (id=${doc.id})`);
|
|
1739
682
|
}
|
|
1740
683
|
|
|
1741
|
-
// src/commands/
|
|
1742
|
-
|
|
684
|
+
// src/commands/append-message.ts
|
|
685
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
686
|
+
async function runAppendMessage(options) {
|
|
687
|
+
const messageId = options.id?.trim();
|
|
688
|
+
if (!messageId) {
|
|
689
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --id <messageId>");
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
const fileArg = options.file?.trim();
|
|
693
|
+
if (!fileArg) {
|
|
694
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --file <\u672C\u5730\u8DEF\u5F84>");
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
const absPath = resolveCwdPath(fileArg);
|
|
698
|
+
if (!existsSync4(absPath)) {
|
|
699
|
+
console.error(`[apm] \u6587\u4EF6\u4E0D\u5B58\u5728: ${absPath}`);
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
const content = readFileSync5(absPath, "utf8");
|
|
1743
703
|
const cfg = await ensureLoggedConfig();
|
|
1744
704
|
const api = createApmApiClient(cfg);
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
status
|
|
1748
|
-
});
|
|
1749
|
-
console.log(JSON.stringify(data, null, 2));
|
|
705
|
+
await api.cli.appendMessageContent({ id: messageId, content });
|
|
706
|
+
console.log(`[apm] \u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9: ${messageId}`);
|
|
1750
707
|
}
|
|
1751
708
|
|
|
1752
|
-
// src/commands/update-status.ts
|
|
1753
|
-
|
|
709
|
+
// src/commands/update-message-status.ts
|
|
710
|
+
var VALID_STATUSES = [
|
|
711
|
+
"CREATED",
|
|
712
|
+
"TYPING",
|
|
713
|
+
"SUCCESS",
|
|
714
|
+
"FAILED"
|
|
715
|
+
];
|
|
716
|
+
async function runUpdateMessageStatus(options) {
|
|
717
|
+
const messageId = options.id?.trim();
|
|
718
|
+
const status = options.status?.trim().toUpperCase();
|
|
719
|
+
if (!messageId) {
|
|
720
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --id <messageId>");
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
723
|
+
if (!VALID_STATUSES.includes(status)) {
|
|
724
|
+
console.error(
|
|
725
|
+
`[apm] \u65E0\u6548\u72B6\u6001: ${options.status}\uFF0C\u53EF\u9009: ${VALID_STATUSES.join(", ")}`
|
|
726
|
+
);
|
|
727
|
+
process.exit(1);
|
|
728
|
+
}
|
|
1754
729
|
const cfg = await ensureLoggedConfig();
|
|
1755
730
|
const api = createApmApiClient(cfg);
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
status
|
|
1759
|
-
});
|
|
1760
|
-
console.log(JSON.stringify(data, null, 2));
|
|
731
|
+
await api.cli.updateMessageStatus({ id: messageId, status });
|
|
732
|
+
console.log(`[apm] \u5DF2\u66F4\u65B0\u6D88\u606F\u72B6\u6001: ${messageId} \u2192 ${status}`);
|
|
1761
733
|
}
|
|
1762
734
|
|
|
1763
735
|
// src/commands/deploy/backend.ts
|
|
1764
736
|
import path5 from "node:path";
|
|
1765
737
|
|
|
1766
738
|
// src/commands/deploy/internal/apm-config.ts
|
|
1767
|
-
import { existsSync as
|
|
1768
|
-
import { resolve as
|
|
739
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
|
|
740
|
+
import { resolve as resolve3 } from "node:path";
|
|
1769
741
|
function loadApmConfig(options) {
|
|
1770
|
-
const p =
|
|
742
|
+
const p = resolve3(
|
|
1771
743
|
process.cwd(),
|
|
1772
|
-
options?.configPath ??
|
|
744
|
+
options?.configPath ?? resolve3(workspaceApmDir(), "apm.config.json")
|
|
1773
745
|
);
|
|
1774
|
-
if (!
|
|
746
|
+
if (!existsSync5(p)) {
|
|
1775
747
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
1776
748
|
process.exit(1);
|
|
1777
749
|
}
|
|
1778
750
|
try {
|
|
1779
|
-
const raw =
|
|
751
|
+
const raw = readFileSync6(p, "utf8");
|
|
1780
752
|
return JSON.parse(raw);
|
|
1781
753
|
} catch (e) {
|
|
1782
754
|
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
@@ -1803,9 +775,7 @@ function reqTopLevelName(cfg) {
|
|
|
1803
775
|
function reqBackendPositiveInt(v, field) {
|
|
1804
776
|
const n = Number(v);
|
|
1805
777
|
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
1806
|
-
console.error(
|
|
1807
|
-
`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`
|
|
1808
|
-
);
|
|
778
|
+
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
1809
779
|
process.exit(1);
|
|
1810
780
|
}
|
|
1811
781
|
return n;
|
|
@@ -1875,7 +845,7 @@ import path4 from "node:path";
|
|
|
1875
845
|
import Docker from "dockerode";
|
|
1876
846
|
|
|
1877
847
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
1878
|
-
import { existsSync as
|
|
848
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
|
|
1879
849
|
import path from "node:path";
|
|
1880
850
|
function asOptionalTlsBuffer(value) {
|
|
1881
851
|
if (typeof value !== "string") {
|
|
@@ -1887,8 +857,8 @@ function asOptionalTlsBuffer(value) {
|
|
|
1887
857
|
if (normalized === "") {
|
|
1888
858
|
return void 0;
|
|
1889
859
|
}
|
|
1890
|
-
if (
|
|
1891
|
-
return
|
|
860
|
+
if (existsSync6(normalized)) {
|
|
861
|
+
return readFileSync7(normalized);
|
|
1892
862
|
}
|
|
1893
863
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
1894
864
|
if (looksLikePath) {
|
|
@@ -2013,17 +983,17 @@ var DockerodeClient = class {
|
|
|
2013
983
|
await this.client.getImage(image).remove({ force: true });
|
|
2014
984
|
}
|
|
2015
985
|
async pullImage(image, auth) {
|
|
2016
|
-
const stream = await new Promise((
|
|
986
|
+
const stream = await new Promise((resolve4, reject) => {
|
|
2017
987
|
const pullOptions = auth ? { authconfig: auth } : void 0;
|
|
2018
988
|
this.client.pull(image, pullOptions, (err, output) => {
|
|
2019
989
|
if (err || !output) {
|
|
2020
990
|
reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
|
|
2021
991
|
return;
|
|
2022
992
|
}
|
|
2023
|
-
|
|
993
|
+
resolve4(output);
|
|
2024
994
|
});
|
|
2025
995
|
});
|
|
2026
|
-
await new Promise((
|
|
996
|
+
await new Promise((resolve4, reject) => {
|
|
2027
997
|
this.client.modem.followProgress(
|
|
2028
998
|
stream,
|
|
2029
999
|
(err) => {
|
|
@@ -2031,7 +1001,7 @@ var DockerodeClient = class {
|
|
|
2031
1001
|
reject(err);
|
|
2032
1002
|
return;
|
|
2033
1003
|
}
|
|
2034
|
-
|
|
1004
|
+
resolve4();
|
|
2035
1005
|
},
|
|
2036
1006
|
() => void 0
|
|
2037
1007
|
);
|
|
@@ -2098,7 +1068,7 @@ var DockerodeClient = class {
|
|
|
2098
1068
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
2099
1069
|
|
|
2100
1070
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
2101
|
-
import { existsSync as
|
|
1071
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync3 } from "node:fs";
|
|
2102
1072
|
import path2 from "node:path";
|
|
2103
1073
|
function stripSurroundingQuotes(value) {
|
|
2104
1074
|
const t = value.trim();
|
|
@@ -2115,10 +1085,10 @@ function loadEnvFromFile(envFilePath) {
|
|
|
2115
1085
|
return {};
|
|
2116
1086
|
}
|
|
2117
1087
|
const targetPath = path2.resolve(envFilePath);
|
|
2118
|
-
if (!
|
|
1088
|
+
if (!existsSync7(targetPath) || !statSync3(targetPath).isFile()) {
|
|
2119
1089
|
return {};
|
|
2120
1090
|
}
|
|
2121
|
-
const raw =
|
|
1091
|
+
const raw = readFileSync8(targetPath, "utf-8");
|
|
2122
1092
|
const result = {};
|
|
2123
1093
|
for (const line of raw.split(/\r?\n/)) {
|
|
2124
1094
|
const normalized = line.trim();
|
|
@@ -2289,12 +1259,12 @@ function dockerPushImage(params, cwd) {
|
|
|
2289
1259
|
}
|
|
2290
1260
|
|
|
2291
1261
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
2292
|
-
import { existsSync as
|
|
1262
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
2293
1263
|
import path3 from "node:path";
|
|
2294
1264
|
function resolveDockerBuildPaths(cwd) {
|
|
2295
1265
|
const dockerfilePath = path3.join(cwd, "Dockerfile");
|
|
2296
1266
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
2297
|
-
if (!
|
|
1267
|
+
if (!existsSync8(dockerfilePath)) {
|
|
2298
1268
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
2299
1269
|
}
|
|
2300
1270
|
Logger.info("\u2713 Dockerfile \u5B58\u5728");
|
|
@@ -2423,16 +1393,16 @@ import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
|
|
|
2423
1393
|
import path7 from "node:path";
|
|
2424
1394
|
|
|
2425
1395
|
// src/commands/deploy/internal/load-apm-dotenv.ts
|
|
2426
|
-
import { existsSync as
|
|
2427
|
-
import { join as
|
|
1396
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
1397
|
+
import { join as join8 } from "node:path";
|
|
2428
1398
|
function loadApmDotEnvIfPresent() {
|
|
2429
|
-
const p =
|
|
2430
|
-
if (!
|
|
1399
|
+
const p = join8(workspaceApmDir(), ".env");
|
|
1400
|
+
if (!existsSync9(p)) {
|
|
2431
1401
|
return;
|
|
2432
1402
|
}
|
|
2433
1403
|
let text;
|
|
2434
1404
|
try {
|
|
2435
|
-
text =
|
|
1405
|
+
text = readFileSync9(p, "utf8");
|
|
2436
1406
|
} catch {
|
|
2437
1407
|
return;
|
|
2438
1408
|
}
|
|
@@ -2457,14 +1427,14 @@ function loadApmDotEnvIfPresent() {
|
|
|
2457
1427
|
}
|
|
2458
1428
|
|
|
2459
1429
|
// src/commands/deploy/internal/minio.ts
|
|
2460
|
-
import { statSync as
|
|
1430
|
+
import { statSync as statSync4 } from "node:fs";
|
|
2461
1431
|
import { readdir, readFile } from "node:fs/promises";
|
|
2462
1432
|
import path6 from "node:path";
|
|
2463
1433
|
import * as Minio from "minio";
|
|
2464
1434
|
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
2465
1435
|
async function isDirectoryPath(dir) {
|
|
2466
1436
|
try {
|
|
2467
|
-
const st =
|
|
1437
|
+
const st = statSync4(dir);
|
|
2468
1438
|
return st.isDirectory();
|
|
2469
1439
|
} catch {
|
|
2470
1440
|
return false;
|
|
@@ -2494,7 +1464,7 @@ async function collectFiles(root) {
|
|
|
2494
1464
|
if (e.isDirectory()) {
|
|
2495
1465
|
await walk(abs, rel);
|
|
2496
1466
|
} else if (e.isFile()) {
|
|
2497
|
-
const st =
|
|
1467
|
+
const st = statSync4(abs);
|
|
2498
1468
|
out.push({
|
|
2499
1469
|
absPath: abs,
|
|
2500
1470
|
relativePath: rel.replace(/\\/g, "/"),
|
|
@@ -2556,14 +1526,14 @@ var MinioClient = class {
|
|
|
2556
1526
|
async deleteObjectsByPrefix(bucket, prefix) {
|
|
2557
1527
|
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
2558
1528
|
const keys = [];
|
|
2559
|
-
await new Promise((
|
|
1529
|
+
await new Promise((resolve4, reject) => {
|
|
2560
1530
|
objectsStream.on("data", (obj) => {
|
|
2561
1531
|
if (obj.name) {
|
|
2562
1532
|
keys.push(obj.name);
|
|
2563
1533
|
}
|
|
2564
1534
|
});
|
|
2565
1535
|
objectsStream.on("error", reject);
|
|
2566
|
-
objectsStream.on("end",
|
|
1536
|
+
objectsStream.on("end", resolve4);
|
|
2567
1537
|
});
|
|
2568
1538
|
const chunkSize = 500;
|
|
2569
1539
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
@@ -2790,7 +1760,7 @@ function registerDeployCommands(program) {
|
|
|
2790
1760
|
function buildProgram() {
|
|
2791
1761
|
const program = new Command();
|
|
2792
1762
|
program.name("apm").description(
|
|
2793
|
-
|
|
1763
|
+
`OPC \u5E73\u53F0\u547D\u4EE4\u884C\uFF08\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u4E0E\u7814\u53D1\u81EA\u52A8\u5316\uFF09\u3002
|
|
2794
1764
|
\u672A\u4F20 --server \u65F6\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u53D8\u91CF AI_PM_SERVER\uFF0C\u5426\u5219\u9ED8\u8BA4 ${DEFAULT_BASE_URL}\u3002`
|
|
2795
1765
|
).version(readCliVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9").showHelpAfterError(true);
|
|
2796
1766
|
program.command("login").description(
|
|
@@ -2800,9 +1770,6 @@ function buildProgram() {
|
|
|
2800
1770
|
await runLogin(opts);
|
|
2801
1771
|
}
|
|
2802
1772
|
);
|
|
2803
|
-
program.command("connect").description("\u8FDE\u63A5 WebSocket /ws/agent\uFF0C\u5E76\u6309\u56FA\u5B9A\u95F4\u9694\u53D1\u9001 HEARTBEAT").option("--server <url>", "API \u6839\u5730\u5740\uFF08\u5199\u5165\u672C\u5730\u914D\u7F6E\uFF09").action((opts) => {
|
|
2804
|
-
runConnect(opts);
|
|
2805
|
-
});
|
|
2806
1773
|
program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521D\u59CB\u5316 .apm \u6A21\u677F\uFF08\u82E5 .apm \u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\u5219\u62A5\u9519\uFF09").option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
|
|
2807
1774
|
await runInit(opts.name);
|
|
2808
1775
|
});
|
|
@@ -2811,72 +1778,30 @@ function buildProgram() {
|
|
|
2811
1778
|
).action(async () => {
|
|
2812
1779
|
await runUpdate();
|
|
2813
1780
|
});
|
|
2814
|
-
program.command("update-skills").description(
|
|
2815
|
-
"\u5220\u9664\u5DE5\u4F5C\u533A .apm/skills \u540E\uFF0C\u4ECE\u5F53\u524D CLI \u5185\u7F6E\u6A21\u677F\u91CD\u65B0\u590D\u5236\u6280\u80FD\u76EE\u5F55"
|
|
2816
|
-
).action(async () => {
|
|
1781
|
+
program.command("update-skills").description("\u4ECE\u5E73\u53F0\u62C9\u53D6\u5F53\u524D\u7528\u6237\u53EF\u8BBF\u95EE\u6280\u80FD\u5230 .apm/skills/").action(async () => {
|
|
2817
1782
|
await runUpdateSkills();
|
|
2818
1783
|
});
|
|
2819
|
-
program.command("
|
|
2820
|
-
"\
|
|
2821
|
-
).action(async () => {
|
|
2822
|
-
await
|
|
2823
|
-
});
|
|
2824
|
-
program.command("pull-session").description(
|
|
2825
|
-
"\u5C06\u5E73\u53F0\u5267\u573A\u4F1A\u8BDD\u4EA7\u7269\u6587\u6863\u4E0B\u8F7D\u5230 .apm/theater-sessions/<sessionId>/\uFF08\u5982 docs/prd.md\uFF09\uFF1B\u672C\u5730\u5DF2\u6709\u540C\u540D\u6587\u4EF6\u65F6\u8986\u76D6"
|
|
2826
|
-
).argument("<sessionId>", "\u5267\u573A\u4F1A\u8BDD ID").action(async (sessionId) => {
|
|
2827
|
-
const dir = await runPullTheaterSession(sessionId);
|
|
2828
|
-
console.log(`[apm] \u4F1A\u8BDD\u6587\u6863\u76EE\u5F55: ${dir}`);
|
|
1784
|
+
program.command("pull").description(
|
|
1785
|
+
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001docs\u3001attachments\uFF09"
|
|
1786
|
+
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
1787
|
+
await runPull(sessionId);
|
|
2829
1788
|
});
|
|
2830
|
-
program.command("sync-
|
|
2831
|
-
|
|
2832
|
-
).argument("<sessionId>", "\u5267\u573A\u4F1A\u8BDD ID").option(
|
|
2833
|
-
"--file <path>",
|
|
2834
|
-
"\u6587\u6863\u540D\u6216\u76F8\u5BF9\u8DEF\u5F84\uFF08\u5982 prd.md\u3001docs/backend.md\uFF1B\u672C\u5730\u56FA\u5B9A\u843D\u5728 docs/ \u4E0B\uFF09"
|
|
2835
|
-
).action(async (sessionId, opts) => {
|
|
2836
|
-
await runSyncSessionDocument(sessionId, { file: opts.file });
|
|
1789
|
+
program.command("sync-document").description("\u5C06\u672C\u5730 Markdown \u8986\u76D6\u5F0F upsert \u5230\u5E73\u53F0\u9700\u6C42\u6587\u6863").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").requiredOption("--file <path>", "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84").action(async (sessionId, opts) => {
|
|
1790
|
+
await runSyncDocument(sessionId, { file: opts.file });
|
|
2837
1791
|
});
|
|
2838
|
-
program.command("
|
|
2839
|
-
|
|
2840
|
-
).argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
2841
|
-
await runPull(requirementId);
|
|
1792
|
+
program.command("append-message").description("\u5411\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u8FFD\u52A0\u5185\u5BB9\uFF08PUT /api/cli/messages/content\uFF09").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption("--file <path>", "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84").action(async (opts) => {
|
|
1793
|
+
await runAppendMessage(opts);
|
|
2842
1794
|
});
|
|
2843
|
-
program.command("
|
|
2844
|
-
|
|
2845
|
-
).argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
2846
|
-
await runUploadArtifact(requirementId);
|
|
1795
|
+
program.command("update-message-status").description("\u66F4\u65B0\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u72B6\u6001").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption("--status <status>", "CREATED | TYPING | SUCCESS | FAILED").action(async (opts) => {
|
|
1796
|
+
await runUpdateMessageStatus(opts);
|
|
2847
1797
|
});
|
|
2848
1798
|
program.command("branch").description(
|
|
2849
|
-
"\u5207\u6362\u6216\u521B\u5EFA\u9700\u6C42\u5206\u652F feat/req-<
|
|
2850
|
-
).argument("<
|
|
1799
|
+
"\u5207\u6362\u6216\u521B\u5EFA\u9700\u6C42\u5206\u652F feat/req-<requirementId>\uFF08\u7531 sessionId \u89E3\u6790\u9700\u6C42\uFF09"
|
|
1800
|
+
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
|
|
2851
1801
|
"-m, --message <text>",
|
|
2852
|
-
"\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E
|
|
2853
|
-
).action(async (
|
|
2854
|
-
await runBranch(
|
|
2855
|
-
});
|
|
2856
|
-
program.command("comment").description(
|
|
2857
|
-
"\u63D0\u4EA4\u9700\u6C42\u8BC4\u5BA1\uFF1Alegacy \u4E3A\u6574\u7BC7 Markdown\uFF08POST \u2026/comment\uFF09\uFF1Bstructured \u4E3A\u884C\u7EA7 YAML\uFF08POST \u2026/comment-structured\uFF09"
|
|
2858
|
-
).argument("<requirementId>", "\u9700\u6C42 ID").requiredOption(
|
|
2859
|
-
"--file <path>",
|
|
2860
|
-
"\u8BC4\u8BBA\u6587\u4EF6\u8DEF\u5F84\uFF08.yaml/.yml \u9ED8\u8BA4 structured\uFF09"
|
|
2861
|
-
).option(
|
|
2862
|
-
"--format <format>",
|
|
2863
|
-
"structured | legacy\uFF08\u9ED8\u8BA4 legacy\uFF1B.yaml/.yml \u672A\u6307\u5B9A\u65F6\u89C6\u4E3A structured\uFF09"
|
|
2864
|
-
).option("--model <model>", "\u8BC4\u8BBA\u6A21\u578B\uFF08\u8986\u76D6 YAML reviewer.model\uFF09").action(
|
|
2865
|
-
async (requirementId, options) => {
|
|
2866
|
-
await runComment(requirementId, options.file, {
|
|
2867
|
-
model: options.model,
|
|
2868
|
-
format: options.format
|
|
2869
|
-
});
|
|
2870
|
-
}
|
|
2871
|
-
);
|
|
2872
|
-
program.command("refine").description("POST /api/cli/requirements/refine\uFF08\u6B63\u6587\u6765\u81EA\u6587\u4EF6\uFF09").argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
2873
|
-
await runRefine(requirementId);
|
|
2874
|
-
});
|
|
2875
|
-
program.command("update-status").description("POST /api/cli/requirements/update-status").argument("<requirementId>", "\u9700\u6C42 ID").requiredOption("--status <RequirementStatus>", "\u9700\u6C42\u72B6\u6001\u679A\u4E3E\u503C").action(async (requirementId, options) => {
|
|
2876
|
-
await runUpdateStatus(requirementId, options.status);
|
|
2877
|
-
});
|
|
2878
|
-
program.command("update-dev-status").description("POST /api/cli/requirements/update-dev-status").argument("<requirementId>", "\u9700\u6C42 ID").requiredOption("--status <status>", "\u6210\u5458\u5F00\u53D1\u72B6\u6001\uFF08\u81EA\u7531\u6587\u672C\uFF09").action(async (requirementId, options) => {
|
|
2879
|
-
await runUpdateDevStatus(requirementId, options.status);
|
|
1802
|
+
"\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E"
|
|
1803
|
+
).action(async (sessionId, opts) => {
|
|
1804
|
+
await runBranch(sessionId, { message: opts.message });
|
|
2880
1805
|
});
|
|
2881
1806
|
registerDeployCommands(program);
|
|
2882
1807
|
return program;
|