ai-project-manage-cli 4.0.23 → 5.0.1
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 +368 -1446
- package/package.json +4 -11
- 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,145 @@ 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
|
-
|
|
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}`);
|
|
862
|
-
}
|
|
863
|
-
return sessionDir;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// src/commands/branch.ts
|
|
867
|
-
import { execFile } from "child_process";
|
|
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`
|
|
257
|
+
console.error(
|
|
258
|
+
"[apm] \u8BF7\u6C42\u5931\u8D25:",
|
|
259
|
+
raw instanceof Error ? raw.message : raw
|
|
883
260
|
);
|
|
261
|
+
process.exit(1);
|
|
884
262
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
263
|
+
if (!data?.id || !data?.token) {
|
|
264
|
+
console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 id / token\uFF08\u8BF7\u786E\u8BA4\u670D\u52A1\u7AEF\u4E3A /api/auth/login\uFF09");
|
|
265
|
+
process.exit(1);
|
|
888
266
|
}
|
|
889
|
-
|
|
267
|
+
const cfg = {
|
|
268
|
+
baseUrl,
|
|
269
|
+
userId: data.id,
|
|
270
|
+
token: data.token,
|
|
271
|
+
email: opts.email
|
|
272
|
+
};
|
|
273
|
+
await writeApmConfig(cfg);
|
|
274
|
+
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
275
|
+
console.log(JSON.stringify({ userId: cfg.userId, baseUrl: cfg.baseUrl }, null, 2));
|
|
890
276
|
}
|
|
891
|
-
|
|
277
|
+
|
|
278
|
+
// src/commands/branch.ts
|
|
279
|
+
import { execFile } from "child_process";
|
|
280
|
+
import { resolve as resolve2 } from "path";
|
|
281
|
+
import { promisify } from "util";
|
|
282
|
+
var execFileAsync = promisify(execFile);
|
|
283
|
+
async function fetchBaselineBranchFromApi(sessionId, cwd) {
|
|
892
284
|
const cfg = await ensureLoggedConfig();
|
|
893
285
|
const api = createApmApiClient(cfg);
|
|
894
|
-
const workdirPath =
|
|
895
|
-
const res = await api.
|
|
286
|
+
const workdirPath = resolve2(cwd);
|
|
287
|
+
const res = await api.cli.branchBaseline({
|
|
896
288
|
sessionId,
|
|
897
289
|
workdirPath
|
|
898
290
|
});
|
|
@@ -920,7 +312,6 @@ function branchNameForRequirement(requirementId) {
|
|
|
920
312
|
}
|
|
921
313
|
return `feat/req-${id}`;
|
|
922
314
|
}
|
|
923
|
-
var branchNameForTheaterSession = branchNameForRequirement;
|
|
924
315
|
async function execGit(cwd, args, quiet) {
|
|
925
316
|
try {
|
|
926
317
|
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
@@ -971,11 +362,6 @@ async function localBranchExists(cwd, branch) {
|
|
|
971
362
|
return false;
|
|
972
363
|
}
|
|
973
364
|
}
|
|
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
365
|
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
980
366
|
await ensureGitRepo(cwd);
|
|
981
367
|
if (!await isWorkingTreeDirty(cwd)) {
|
|
@@ -1038,538 +424,129 @@ async function ensureFeatureBranch(branch, options, fetchBaseline) {
|
|
|
1038
424
|
console.log(`[apm] \u5DF2\u5C31\u7EEA\u5206\u652F ${branch}`);
|
|
1039
425
|
return branch;
|
|
1040
426
|
}
|
|
1041
|
-
async function runBranch(
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
async function runTheaterSessionBranch(sessionId, options = {}) {
|
|
427
|
+
async function runBranch(sessionId, options = {}) {
|
|
428
|
+
const trimmedSessionId = sessionId.trim();
|
|
429
|
+
if (!trimmedSessionId) {
|
|
430
|
+
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
const cfg = await ensureLoggedConfig();
|
|
434
|
+
const api = createApmApiClient(cfg);
|
|
1051
435
|
const cwd = options.cwd ?? process.cwd();
|
|
1052
|
-
const
|
|
436
|
+
const baseline = await api.cli.branchBaseline({
|
|
437
|
+
sessionId: trimmedSessionId,
|
|
438
|
+
workdirPath: resolve2(cwd)
|
|
439
|
+
});
|
|
440
|
+
const branch = branchNameForRequirement(baseline.requirementId);
|
|
1053
441
|
return ensureFeatureBranch(
|
|
1054
442
|
branch,
|
|
1055
443
|
options,
|
|
1056
|
-
() =>
|
|
444
|
+
() => fetchBaselineBranchFromApi(trimmedSessionId, cwd)
|
|
1057
445
|
);
|
|
1058
446
|
}
|
|
1059
447
|
|
|
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
|
-
);
|
|
448
|
+
// src/commands/pull.ts
|
|
449
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
450
|
+
import { join as join5 } from "path";
|
|
451
|
+
import { stringify as yamlStringify } from "yaml";
|
|
452
|
+
|
|
453
|
+
// src/commands/sync-session-attachments.ts
|
|
454
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
455
|
+
import { join as join4 } from "path";
|
|
456
|
+
async function downloadAttachment(cfg, sessionId, attachmentId) {
|
|
457
|
+
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
458
|
+
const q = new URLSearchParams({ sessionId, attachmentId });
|
|
459
|
+
const url = `${base}/api/cli/attachments/file?${q.toString()}`;
|
|
460
|
+
const res = await fetch(url, {
|
|
461
|
+
headers: { Authorization: `Bearer ${cfg.token}` }
|
|
1117
462
|
});
|
|
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)
|
|
463
|
+
if (!res.ok) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
1175
466
|
);
|
|
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
467
|
}
|
|
1254
|
-
return
|
|
468
|
+
return Buffer.from(await res.arrayBuffer());
|
|
1255
469
|
}
|
|
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
|
-
});
|
|
470
|
+
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
471
|
+
if (attachments.length === 0) return;
|
|
472
|
+
const dir = join4(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
473
|
+
await ensureDirExists(dir);
|
|
474
|
+
for (const item of attachments) {
|
|
475
|
+
const dest = join4(dir, item.name);
|
|
476
|
+
const buffer = await downloadAttachment(cfg, sessionId, item.id);
|
|
477
|
+
writeFileSync3(dest, buffer);
|
|
1343
478
|
console.log(
|
|
1344
|
-
|
|
1345
|
-
runResult.failed ? "(\u6267\u884C\u5931\u8D25)" : ""
|
|
479
|
+
`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${SESSION_ATTACHMENTS_SUBDIR}/${item.name}`
|
|
1346
480
|
);
|
|
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
481
|
}
|
|
1501
482
|
}
|
|
1502
483
|
|
|
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");
|
|
484
|
+
// src/commands/pull.ts
|
|
485
|
+
async function runPull(sessionId, apmRoot) {
|
|
486
|
+
const trimmedId = sessionId.trim();
|
|
487
|
+
if (!trimmedId) {
|
|
488
|
+
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1535
489
|
process.exit(1);
|
|
1536
490
|
}
|
|
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
491
|
const cfg = await ensureLoggedConfig();
|
|
1553
|
-
const filePath = join9(WORKSPACE_APM_DIR, "workitems", requirementId, "prd.md");
|
|
1554
|
-
const content = readFileSync5(filePath, "utf8");
|
|
1555
492
|
const api = createApmApiClient(cfg);
|
|
1556
|
-
const
|
|
1557
|
-
|
|
493
|
+
const [detail, members, documents, attachments] = await Promise.all([
|
|
494
|
+
api.cli.sessionDetail({ sessionId: trimmedId }),
|
|
495
|
+
api.cli.sessionMembers({ sessionId: trimmedId }),
|
|
496
|
+
api.cli.listDocuments({ sessionId: trimmedId }),
|
|
497
|
+
api.cli.listAttachments({ sessionId: trimmedId })
|
|
498
|
+
]);
|
|
499
|
+
const dir = sessionDir(trimmedId, apmRoot);
|
|
500
|
+
const docsDir = sessionDocsDir(trimmedId, apmRoot);
|
|
501
|
+
await ensureDirExists(docsDir);
|
|
502
|
+
writeFileSync4(
|
|
503
|
+
sessionRulePath(trimmedId, apmRoot),
|
|
504
|
+
detail.description ?? "",
|
|
505
|
+
"utf8"
|
|
506
|
+
);
|
|
507
|
+
for (const doc of documents) {
|
|
508
|
+
const fileName = documentLocalFileName(doc.name);
|
|
509
|
+
writeFileSync4(join5(docsDir, fileName), doc.content ?? "", "utf8");
|
|
510
|
+
}
|
|
511
|
+
const sessionYaml = yamlStringify(
|
|
512
|
+
{
|
|
513
|
+
name: detail.title,
|
|
514
|
+
description: "./RULE.md",
|
|
515
|
+
members: members.map((m) => ({
|
|
516
|
+
name: m.displayName,
|
|
517
|
+
position: m.position,
|
|
518
|
+
system_persona: m.systemPersona,
|
|
519
|
+
skills: []
|
|
520
|
+
})),
|
|
521
|
+
skills: [],
|
|
522
|
+
attachments: attachments.map((a) => ({ name: a.name }))
|
|
523
|
+
},
|
|
524
|
+
{ lineWidth: 0 }
|
|
525
|
+
);
|
|
526
|
+
writeFileSync4(
|
|
527
|
+
sessionYamlPath(trimmedId, apmRoot),
|
|
528
|
+
sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
|
|
529
|
+
`,
|
|
530
|
+
"utf8"
|
|
531
|
+
);
|
|
532
|
+
await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
|
|
533
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
|
|
534
|
+
return dir;
|
|
1558
535
|
}
|
|
1559
536
|
|
|
1560
537
|
// src/commands/update.ts
|
|
1561
538
|
import { spawnSync } from "child_process";
|
|
1562
539
|
|
|
1563
540
|
// src/version.ts
|
|
1564
|
-
import { readFileSync as
|
|
1565
|
-
import { dirname as
|
|
541
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
542
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1566
543
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1567
544
|
var CLI_PACKAGE_NAME = "ai-project-manage-cli";
|
|
1568
545
|
function readCliVersion() {
|
|
1569
546
|
try {
|
|
1570
|
-
const dir =
|
|
1571
|
-
const pkgPath =
|
|
1572
|
-
const pkg = JSON.parse(
|
|
547
|
+
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
548
|
+
const pkgPath = join6(dir, "..", "package.json");
|
|
549
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
1573
550
|
return pkg.version ?? "0.0.0";
|
|
1574
551
|
} catch {
|
|
1575
552
|
return "0.0.0";
|
|
@@ -1636,147 +613,139 @@ async function runUpdate() {
|
|
|
1636
613
|
}
|
|
1637
614
|
|
|
1638
615
|
// src/commands/update-skills.ts
|
|
1639
|
-
import {
|
|
1640
|
-
import { join as
|
|
1641
|
-
|
|
616
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
617
|
+
import { join as join7 } from "path";
|
|
618
|
+
function sanitizeDirName(name) {
|
|
619
|
+
const trimmed = name.trim();
|
|
620
|
+
if (!trimmed) return "skill";
|
|
621
|
+
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
622
|
+
}
|
|
1642
623
|
async function runUpdateSkills() {
|
|
1643
|
-
const apmDir =
|
|
1644
|
-
if (!
|
|
624
|
+
const apmDir = workspaceApmDir();
|
|
625
|
+
if (!existsSync2(apmDir)) {
|
|
1645
626
|
console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
1646
627
|
process.exit(1);
|
|
1647
628
|
}
|
|
1648
|
-
const apmStat =
|
|
629
|
+
const apmStat = statSync2(apmDir);
|
|
1649
630
|
if (!apmStat.isDirectory()) {
|
|
1650
631
|
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
|
|
1651
632
|
}
|
|
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)) {
|
|
633
|
+
const cfg = await ensureLoggedConfig();
|
|
634
|
+
const api = createApmApiClient(cfg);
|
|
635
|
+
const { list } = await api.cli.listSkills({});
|
|
636
|
+
const skillsDir = join7(apmDir, "skills");
|
|
637
|
+
if (existsSync2(skillsDir)) {
|
|
1663
638
|
rmSync(skillsDir, { recursive: true, force: true });
|
|
1664
639
|
}
|
|
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 });
|
|
640
|
+
mkdirSync3(skillsDir, { recursive: true });
|
|
641
|
+
for (const skill of list) {
|
|
642
|
+
const dirName = sanitizeDirName(skill.name);
|
|
643
|
+
const skillDir = join7(skillsDir, dirName);
|
|
644
|
+
mkdirSync3(skillDir, { recursive: true });
|
|
645
|
+
writeFileSync5(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
646
|
+
console.log(`[apm] \u5DF2\u5199\u5165\u6280\u80FD: skills/${dirName}/SKILL.md`);
|
|
1700
647
|
}
|
|
1701
|
-
|
|
1702
|
-
cpSync3(TEMPLATE_THEATER_SKILLS_DIR, theaterSkillsDir, { recursive: true });
|
|
1703
|
-
console.log("[apm] \u5DF2\u66F4\u65B0 .apm/theater-skills");
|
|
648
|
+
console.log(`[apm] \u5DF2\u66F4\u65B0 ${list.length} \u4E2A\u6280\u80FD\u5230 .apm/skills`);
|
|
1704
649
|
}
|
|
1705
650
|
|
|
1706
|
-
// src/commands/sync-
|
|
1707
|
-
import { existsSync as
|
|
1708
|
-
|
|
1709
|
-
async function runSyncSessionDocument(sessionId, options) {
|
|
651
|
+
// src/commands/sync-document.ts
|
|
652
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
653
|
+
async function runSyncDocument(sessionId, options) {
|
|
1710
654
|
const trimmedSessionId = sessionId.trim();
|
|
1711
655
|
if (!trimmedSessionId) {
|
|
1712
656
|
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1713
657
|
process.exit(1);
|
|
1714
658
|
}
|
|
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`);
|
|
659
|
+
const fileArg = options.file?.trim();
|
|
660
|
+
if (!fileArg) {
|
|
661
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --file <\u672C\u5730\u8DEF\u5F84>");
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
const absPath = resolveCwdPath(fileArg);
|
|
665
|
+
if (!existsSync3(absPath)) {
|
|
666
|
+
console.error(`[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}`);
|
|
1726
667
|
process.exit(1);
|
|
1727
668
|
}
|
|
1728
|
-
const content =
|
|
669
|
+
const content = readFileSync4(absPath, "utf8");
|
|
670
|
+
const name = documentPlatformName(absPath);
|
|
1729
671
|
const cfg = await ensureLoggedConfig();
|
|
1730
672
|
const api = createApmApiClient(cfg);
|
|
1731
|
-
const
|
|
673
|
+
const doc = await api.cli.upsertDocument({
|
|
1732
674
|
sessionId: trimmedSessionId,
|
|
1733
|
-
|
|
675
|
+
name,
|
|
1734
676
|
content
|
|
1735
677
|
});
|
|
1736
|
-
console.log(
|
|
1737
|
-
`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u6587\u6863: ${localRelativePath} \u2192 ${data.fileName} (artifactId=${data.id})`
|
|
1738
|
-
);
|
|
678
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u6587\u6863: ${name} (id=${doc.id})`);
|
|
1739
679
|
}
|
|
1740
680
|
|
|
1741
|
-
// src/commands/
|
|
1742
|
-
|
|
681
|
+
// src/commands/append-message.ts
|
|
682
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
683
|
+
async function runAppendMessage(options) {
|
|
684
|
+
const messageId = options.id?.trim();
|
|
685
|
+
if (!messageId) {
|
|
686
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --id <messageId>");
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
const fileArg = options.file?.trim();
|
|
690
|
+
if (!fileArg) {
|
|
691
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --file <\u672C\u5730\u8DEF\u5F84>");
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
const absPath = resolveCwdPath(fileArg);
|
|
695
|
+
if (!existsSync4(absPath)) {
|
|
696
|
+
console.error(`[apm] \u6587\u4EF6\u4E0D\u5B58\u5728: ${absPath}`);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
const content = readFileSync5(absPath, "utf8");
|
|
1743
700
|
const cfg = await ensureLoggedConfig();
|
|
1744
701
|
const api = createApmApiClient(cfg);
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
status
|
|
1748
|
-
});
|
|
1749
|
-
console.log(JSON.stringify(data, null, 2));
|
|
702
|
+
await api.cli.appendMessageContent({ id: messageId, content });
|
|
703
|
+
console.log(`[apm] \u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9: ${messageId}`);
|
|
1750
704
|
}
|
|
1751
705
|
|
|
1752
|
-
// src/commands/update-status.ts
|
|
1753
|
-
|
|
706
|
+
// src/commands/update-message-status.ts
|
|
707
|
+
var VALID_STATUSES = [
|
|
708
|
+
"CREATED",
|
|
709
|
+
"TYPING",
|
|
710
|
+
"SUCCESS",
|
|
711
|
+
"FAILED"
|
|
712
|
+
];
|
|
713
|
+
async function runUpdateMessageStatus(options) {
|
|
714
|
+
const messageId = options.id?.trim();
|
|
715
|
+
const status = options.status?.trim().toUpperCase();
|
|
716
|
+
if (!messageId) {
|
|
717
|
+
console.error("[apm] \u8BF7\u6307\u5B9A --id <messageId>");
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
if (!VALID_STATUSES.includes(status)) {
|
|
721
|
+
console.error(
|
|
722
|
+
`[apm] \u65E0\u6548\u72B6\u6001: ${options.status}\uFF0C\u53EF\u9009: ${VALID_STATUSES.join(", ")}`
|
|
723
|
+
);
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
1754
726
|
const cfg = await ensureLoggedConfig();
|
|
1755
727
|
const api = createApmApiClient(cfg);
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
status
|
|
1759
|
-
});
|
|
1760
|
-
console.log(JSON.stringify(data, null, 2));
|
|
728
|
+
await api.cli.updateMessageStatus({ id: messageId, status });
|
|
729
|
+
console.log(`[apm] \u5DF2\u66F4\u65B0\u6D88\u606F\u72B6\u6001: ${messageId} \u2192 ${status}`);
|
|
1761
730
|
}
|
|
1762
731
|
|
|
1763
732
|
// src/commands/deploy/backend.ts
|
|
1764
733
|
import path5 from "node:path";
|
|
1765
734
|
|
|
1766
735
|
// src/commands/deploy/internal/apm-config.ts
|
|
1767
|
-
import { existsSync as
|
|
1768
|
-
import { resolve as
|
|
736
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
|
|
737
|
+
import { resolve as resolve3 } from "node:path";
|
|
1769
738
|
function loadApmConfig(options) {
|
|
1770
|
-
const p =
|
|
739
|
+
const p = resolve3(
|
|
1771
740
|
process.cwd(),
|
|
1772
|
-
options?.configPath ??
|
|
741
|
+
options?.configPath ?? resolve3(workspaceApmDir(), "apm.config.json")
|
|
1773
742
|
);
|
|
1774
|
-
if (!
|
|
743
|
+
if (!existsSync5(p)) {
|
|
1775
744
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
1776
745
|
process.exit(1);
|
|
1777
746
|
}
|
|
1778
747
|
try {
|
|
1779
|
-
const raw =
|
|
748
|
+
const raw = readFileSync6(p, "utf8");
|
|
1780
749
|
return JSON.parse(raw);
|
|
1781
750
|
} catch (e) {
|
|
1782
751
|
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
@@ -1803,9 +772,7 @@ function reqTopLevelName(cfg) {
|
|
|
1803
772
|
function reqBackendPositiveInt(v, field) {
|
|
1804
773
|
const n = Number(v);
|
|
1805
774
|
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
|
-
);
|
|
775
|
+
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
1809
776
|
process.exit(1);
|
|
1810
777
|
}
|
|
1811
778
|
return n;
|
|
@@ -1875,7 +842,7 @@ import path4 from "node:path";
|
|
|
1875
842
|
import Docker from "dockerode";
|
|
1876
843
|
|
|
1877
844
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
1878
|
-
import { existsSync as
|
|
845
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
|
|
1879
846
|
import path from "node:path";
|
|
1880
847
|
function asOptionalTlsBuffer(value) {
|
|
1881
848
|
if (typeof value !== "string") {
|
|
@@ -1887,8 +854,8 @@ function asOptionalTlsBuffer(value) {
|
|
|
1887
854
|
if (normalized === "") {
|
|
1888
855
|
return void 0;
|
|
1889
856
|
}
|
|
1890
|
-
if (
|
|
1891
|
-
return
|
|
857
|
+
if (existsSync6(normalized)) {
|
|
858
|
+
return readFileSync7(normalized);
|
|
1892
859
|
}
|
|
1893
860
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
1894
861
|
if (looksLikePath) {
|
|
@@ -2013,17 +980,17 @@ var DockerodeClient = class {
|
|
|
2013
980
|
await this.client.getImage(image).remove({ force: true });
|
|
2014
981
|
}
|
|
2015
982
|
async pullImage(image, auth) {
|
|
2016
|
-
const stream = await new Promise((
|
|
983
|
+
const stream = await new Promise((resolve4, reject) => {
|
|
2017
984
|
const pullOptions = auth ? { authconfig: auth } : void 0;
|
|
2018
985
|
this.client.pull(image, pullOptions, (err, output) => {
|
|
2019
986
|
if (err || !output) {
|
|
2020
987
|
reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
|
|
2021
988
|
return;
|
|
2022
989
|
}
|
|
2023
|
-
|
|
990
|
+
resolve4(output);
|
|
2024
991
|
});
|
|
2025
992
|
});
|
|
2026
|
-
await new Promise((
|
|
993
|
+
await new Promise((resolve4, reject) => {
|
|
2027
994
|
this.client.modem.followProgress(
|
|
2028
995
|
stream,
|
|
2029
996
|
(err) => {
|
|
@@ -2031,7 +998,7 @@ var DockerodeClient = class {
|
|
|
2031
998
|
reject(err);
|
|
2032
999
|
return;
|
|
2033
1000
|
}
|
|
2034
|
-
|
|
1001
|
+
resolve4();
|
|
2035
1002
|
},
|
|
2036
1003
|
() => void 0
|
|
2037
1004
|
);
|
|
@@ -2098,7 +1065,7 @@ var DockerodeClient = class {
|
|
|
2098
1065
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
2099
1066
|
|
|
2100
1067
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
2101
|
-
import { existsSync as
|
|
1068
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync3 } from "node:fs";
|
|
2102
1069
|
import path2 from "node:path";
|
|
2103
1070
|
function stripSurroundingQuotes(value) {
|
|
2104
1071
|
const t = value.trim();
|
|
@@ -2115,10 +1082,10 @@ function loadEnvFromFile(envFilePath) {
|
|
|
2115
1082
|
return {};
|
|
2116
1083
|
}
|
|
2117
1084
|
const targetPath = path2.resolve(envFilePath);
|
|
2118
|
-
if (!
|
|
1085
|
+
if (!existsSync7(targetPath) || !statSync3(targetPath).isFile()) {
|
|
2119
1086
|
return {};
|
|
2120
1087
|
}
|
|
2121
|
-
const raw =
|
|
1088
|
+
const raw = readFileSync8(targetPath, "utf-8");
|
|
2122
1089
|
const result = {};
|
|
2123
1090
|
for (const line of raw.split(/\r?\n/)) {
|
|
2124
1091
|
const normalized = line.trim();
|
|
@@ -2289,12 +1256,12 @@ function dockerPushImage(params, cwd) {
|
|
|
2289
1256
|
}
|
|
2290
1257
|
|
|
2291
1258
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
2292
|
-
import { existsSync as
|
|
1259
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
2293
1260
|
import path3 from "node:path";
|
|
2294
1261
|
function resolveDockerBuildPaths(cwd) {
|
|
2295
1262
|
const dockerfilePath = path3.join(cwd, "Dockerfile");
|
|
2296
1263
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
2297
|
-
if (!
|
|
1264
|
+
if (!existsSync8(dockerfilePath)) {
|
|
2298
1265
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
2299
1266
|
}
|
|
2300
1267
|
Logger.info("\u2713 Dockerfile \u5B58\u5728");
|
|
@@ -2423,16 +1390,16 @@ import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
|
|
|
2423
1390
|
import path7 from "node:path";
|
|
2424
1391
|
|
|
2425
1392
|
// src/commands/deploy/internal/load-apm-dotenv.ts
|
|
2426
|
-
import { existsSync as
|
|
2427
|
-
import { join as
|
|
1393
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
1394
|
+
import { join as join8 } from "node:path";
|
|
2428
1395
|
function loadApmDotEnvIfPresent() {
|
|
2429
|
-
const p =
|
|
2430
|
-
if (!
|
|
1396
|
+
const p = join8(workspaceApmDir(), ".env");
|
|
1397
|
+
if (!existsSync9(p)) {
|
|
2431
1398
|
return;
|
|
2432
1399
|
}
|
|
2433
1400
|
let text;
|
|
2434
1401
|
try {
|
|
2435
|
-
text =
|
|
1402
|
+
text = readFileSync9(p, "utf8");
|
|
2436
1403
|
} catch {
|
|
2437
1404
|
return;
|
|
2438
1405
|
}
|
|
@@ -2457,14 +1424,14 @@ function loadApmDotEnvIfPresent() {
|
|
|
2457
1424
|
}
|
|
2458
1425
|
|
|
2459
1426
|
// src/commands/deploy/internal/minio.ts
|
|
2460
|
-
import { statSync as
|
|
1427
|
+
import { statSync as statSync4 } from "node:fs";
|
|
2461
1428
|
import { readdir, readFile } from "node:fs/promises";
|
|
2462
1429
|
import path6 from "node:path";
|
|
2463
1430
|
import * as Minio from "minio";
|
|
2464
1431
|
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
2465
1432
|
async function isDirectoryPath(dir) {
|
|
2466
1433
|
try {
|
|
2467
|
-
const st =
|
|
1434
|
+
const st = statSync4(dir);
|
|
2468
1435
|
return st.isDirectory();
|
|
2469
1436
|
} catch {
|
|
2470
1437
|
return false;
|
|
@@ -2494,7 +1461,7 @@ async function collectFiles(root) {
|
|
|
2494
1461
|
if (e.isDirectory()) {
|
|
2495
1462
|
await walk(abs, rel);
|
|
2496
1463
|
} else if (e.isFile()) {
|
|
2497
|
-
const st =
|
|
1464
|
+
const st = statSync4(abs);
|
|
2498
1465
|
out.push({
|
|
2499
1466
|
absPath: abs,
|
|
2500
1467
|
relativePath: rel.replace(/\\/g, "/"),
|
|
@@ -2556,14 +1523,14 @@ var MinioClient = class {
|
|
|
2556
1523
|
async deleteObjectsByPrefix(bucket, prefix) {
|
|
2557
1524
|
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
2558
1525
|
const keys = [];
|
|
2559
|
-
await new Promise((
|
|
1526
|
+
await new Promise((resolve4, reject) => {
|
|
2560
1527
|
objectsStream.on("data", (obj) => {
|
|
2561
1528
|
if (obj.name) {
|
|
2562
1529
|
keys.push(obj.name);
|
|
2563
1530
|
}
|
|
2564
1531
|
});
|
|
2565
1532
|
objectsStream.on("error", reject);
|
|
2566
|
-
objectsStream.on("end",
|
|
1533
|
+
objectsStream.on("end", resolve4);
|
|
2567
1534
|
});
|
|
2568
1535
|
const chunkSize = 500;
|
|
2569
1536
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
@@ -2790,7 +1757,7 @@ function registerDeployCommands(program) {
|
|
|
2790
1757
|
function buildProgram() {
|
|
2791
1758
|
const program = new Command();
|
|
2792
1759
|
program.name("apm").description(
|
|
2793
|
-
|
|
1760
|
+
`OPC \u5E73\u53F0\u547D\u4EE4\u884C\uFF08\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u4E0E\u7814\u53D1\u81EA\u52A8\u5316\uFF09\u3002
|
|
2794
1761
|
\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
1762
|
).version(readCliVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9").showHelpAfterError(true);
|
|
2796
1763
|
program.command("login").description(
|
|
@@ -2800,9 +1767,6 @@ function buildProgram() {
|
|
|
2800
1767
|
await runLogin(opts);
|
|
2801
1768
|
}
|
|
2802
1769
|
);
|
|
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
1770
|
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
1771
|
await runInit(opts.name);
|
|
2808
1772
|
});
|
|
@@ -2811,72 +1775,30 @@ function buildProgram() {
|
|
|
2811
1775
|
).action(async () => {
|
|
2812
1776
|
await runUpdate();
|
|
2813
1777
|
});
|
|
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 () => {
|
|
1778
|
+
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
1779
|
await runUpdateSkills();
|
|
2818
1780
|
});
|
|
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}`);
|
|
1781
|
+
program.command("pull").description(
|
|
1782
|
+
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001docs\u3001attachments\uFF09"
|
|
1783
|
+
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
1784
|
+
await runPull(sessionId);
|
|
2829
1785
|
});
|
|
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 });
|
|
1786
|
+
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) => {
|
|
1787
|
+
await runSyncDocument(sessionId, { file: opts.file });
|
|
2837
1788
|
});
|
|
2838
|
-
program.command("
|
|
2839
|
-
|
|
2840
|
-
).argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
2841
|
-
await runPull(requirementId);
|
|
1789
|
+
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) => {
|
|
1790
|
+
await runAppendMessage(opts);
|
|
2842
1791
|
});
|
|
2843
|
-
program.command("
|
|
2844
|
-
|
|
2845
|
-
).argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
2846
|
-
await runUploadArtifact(requirementId);
|
|
1792
|
+
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) => {
|
|
1793
|
+
await runUpdateMessageStatus(opts);
|
|
2847
1794
|
});
|
|
2848
1795
|
program.command("branch").description(
|
|
2849
|
-
"\u5207\u6362\u6216\u521B\u5EFA\u9700\u6C42\u5206\u652F feat/req-<
|
|
2850
|
-
).argument("<
|
|
1796
|
+
"\u5207\u6362\u6216\u521B\u5EFA\u9700\u6C42\u5206\u652F feat/req-<requirementId>\uFF08\u7531 sessionId \u89E3\u6790\u9700\u6C42\uFF09"
|
|
1797
|
+
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
|
|
2851
1798
|
"-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);
|
|
1799
|
+
"\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E"
|
|
1800
|
+
).action(async (sessionId, opts) => {
|
|
1801
|
+
await runBranch(sessionId, { message: opts.message });
|
|
2880
1802
|
});
|
|
2881
1803
|
registerDeployCommands(program);
|
|
2882
1804
|
return program;
|