ai-project-manage-cli 8.0.8 → 8.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +123 -1
- package/dist/webide-message-worker.js +411 -13
- package/package.json +1 -1
- package/template/rules/webide_package.md +75 -0
package/dist/index.js
CHANGED
|
@@ -195,6 +195,18 @@ var init_request_config = __esm({
|
|
|
195
195
|
getApmLogStorage: defineEndpoint({
|
|
196
196
|
method: "GET",
|
|
197
197
|
path: "/cli/apm-log-storage"
|
|
198
|
+
}),
|
|
199
|
+
projectPackageUpdateStatus: defineEndpoint({
|
|
200
|
+
method: "PUT",
|
|
201
|
+
path: "/cli/project-packages/status"
|
|
202
|
+
}),
|
|
203
|
+
projectPackageAppendContent: defineEndpoint({
|
|
204
|
+
method: "PUT",
|
|
205
|
+
path: "/cli/project-packages/content"
|
|
206
|
+
}),
|
|
207
|
+
projectPackageEnsureContent: defineEndpoint({
|
|
208
|
+
method: "PUT",
|
|
209
|
+
path: "/cli/project-packages/ensure-content"
|
|
198
210
|
})
|
|
199
211
|
}
|
|
200
212
|
};
|
|
@@ -1050,6 +1062,9 @@ init_config();
|
|
|
1050
1062
|
function isWebIdeStartProjectTarget(value) {
|
|
1051
1063
|
return value === "frontend" || value === "backend" || value === "mobile";
|
|
1052
1064
|
}
|
|
1065
|
+
function isWebIdePackageTarget(value) {
|
|
1066
|
+
return value === "frontend" || value === "backend";
|
|
1067
|
+
}
|
|
1053
1068
|
function nonEmptyString(v) {
|
|
1054
1069
|
return typeof v === "string" && v.trim().length > 0;
|
|
1055
1070
|
}
|
|
@@ -1186,13 +1201,64 @@ function validateWebIdeStartProjectPush(o) {
|
|
|
1186
1201
|
}
|
|
1187
1202
|
};
|
|
1188
1203
|
}
|
|
1204
|
+
function validateWebIdePackagePush(o) {
|
|
1205
|
+
if (o.type !== "webide-package") {
|
|
1206
|
+
return { ok: false, reason: "\u671F\u671B webide-package" };
|
|
1207
|
+
}
|
|
1208
|
+
if (!nonEmptyString(o.packageId)) {
|
|
1209
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 packageId" };
|
|
1210
|
+
}
|
|
1211
|
+
if (!nonEmptyString(o.messageId)) {
|
|
1212
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 messageId" };
|
|
1213
|
+
}
|
|
1214
|
+
if (!nonEmptyString(o.projectId)) {
|
|
1215
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 projectId" };
|
|
1216
|
+
}
|
|
1217
|
+
if (!isWebIdePackageTarget(o.target)) {
|
|
1218
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11\u6709\u6548 target" };
|
|
1219
|
+
}
|
|
1220
|
+
if (!nonEmptyString(o.baseBranch)) {
|
|
1221
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 baseBranch" };
|
|
1222
|
+
}
|
|
1223
|
+
if (!nonEmptyString(o.content)) {
|
|
1224
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 content" };
|
|
1225
|
+
}
|
|
1226
|
+
if (!nonEmptyString(o.model)) {
|
|
1227
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 model" };
|
|
1228
|
+
}
|
|
1229
|
+
if (!nonEmptyString(o.apiKey)) {
|
|
1230
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 apiKey" };
|
|
1231
|
+
}
|
|
1232
|
+
if (!nonEmptyString(o.workdir)) {
|
|
1233
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 workdir" };
|
|
1234
|
+
}
|
|
1235
|
+
if (!nonEmptyString(o.user)) {
|
|
1236
|
+
return { ok: false, reason: "webide-package \u7F3A\u5C11 user" };
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
ok: true,
|
|
1240
|
+
data: {
|
|
1241
|
+
type: "webide-package",
|
|
1242
|
+
packageId: o.packageId.trim(),
|
|
1243
|
+
messageId: o.messageId.trim(),
|
|
1244
|
+
projectId: o.projectId.trim(),
|
|
1245
|
+
target: o.target,
|
|
1246
|
+
baseBranch: o.baseBranch.trim().replace(/^origin\//, ""),
|
|
1247
|
+
content: o.content,
|
|
1248
|
+
model: o.model.trim(),
|
|
1249
|
+
apiKey: o.apiKey.trim(),
|
|
1250
|
+
workdir: o.workdir.trim(),
|
|
1251
|
+
user: o.user.trim()
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1189
1255
|
function validateAgentWsMessage(value, kind) {
|
|
1190
1256
|
if (typeof value !== "object" || value === null) {
|
|
1191
1257
|
return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
1192
1258
|
}
|
|
1193
1259
|
const o = value;
|
|
1194
1260
|
const type = o.type;
|
|
1195
|
-
if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project" && type !== "webide-stop-terminals") {
|
|
1261
|
+
if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project" && type !== "webide-stop-terminals" && type !== "webide-package") {
|
|
1196
1262
|
return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
|
|
1197
1263
|
}
|
|
1198
1264
|
if (kind === "heartbeat" || type === "heartbeat") {
|
|
@@ -1207,6 +1273,9 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1207
1273
|
if (type === "webide-stop-terminals") {
|
|
1208
1274
|
return validateWebIdeStopTerminals(o);
|
|
1209
1275
|
}
|
|
1276
|
+
if (type === "webide-package") {
|
|
1277
|
+
return validateWebIdePackagePush(o);
|
|
1278
|
+
}
|
|
1210
1279
|
return validateWebIdeMessagePush(o);
|
|
1211
1280
|
}
|
|
1212
1281
|
|
|
@@ -1267,6 +1336,14 @@ function spawnWebIdeMessageWorker(cfg, msg) {
|
|
|
1267
1336
|
try {
|
|
1268
1337
|
const { createApmApiClient: createApmApiClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
1269
1338
|
const api = createApmApiClient2(cfg);
|
|
1339
|
+
if (msg.type === "webide-package") {
|
|
1340
|
+
await api.cli.projectPackageUpdateStatus({
|
|
1341
|
+
id: msg.packageId,
|
|
1342
|
+
status: "CANCELLED",
|
|
1343
|
+
error: reason
|
|
1344
|
+
});
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1270
1347
|
await api.cli.webideSetMessageError({
|
|
1271
1348
|
id: msg.messageId,
|
|
1272
1349
|
error: reason,
|
|
@@ -1504,6 +1581,48 @@ function handleStopTerminalsAction(payload, ctx) {
|
|
|
1504
1581
|
trackActionTask(ctx, task);
|
|
1505
1582
|
}
|
|
1506
1583
|
|
|
1584
|
+
// src/commands/connect/actions/package.ts
|
|
1585
|
+
init_client();
|
|
1586
|
+
function handleWebIdePackageAction(payload, ctx) {
|
|
1587
|
+
const perMessageController = new AbortController();
|
|
1588
|
+
ctx.activeRuns.set(payload.messageId, perMessageController);
|
|
1589
|
+
if (ctx.pendingCancels.has(payload.messageId)) {
|
|
1590
|
+
ctx.activeRuns.delete(payload.messageId);
|
|
1591
|
+
ctx.pendingCancels.delete(payload.messageId);
|
|
1592
|
+
const cancelTask = (async () => {
|
|
1593
|
+
try {
|
|
1594
|
+
const api = createApmApiClient(ctx.cfg);
|
|
1595
|
+
await api.cli.projectPackageUpdateStatus({
|
|
1596
|
+
id: payload.packageId,
|
|
1597
|
+
status: "CANCELLED",
|
|
1598
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
1599
|
+
});
|
|
1600
|
+
} catch (err) {
|
|
1601
|
+
console.error(
|
|
1602
|
+
`[apm] webide-package \u9884\u53D6\u6D88\u5199\u7EC8\u6001\u5931\u8D25 packageId=${payload.packageId}:`,
|
|
1603
|
+
err instanceof Error ? err.message : err
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
})();
|
|
1607
|
+
trackActionTask(ctx, cancelTask);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
const task = (async () => {
|
|
1611
|
+
const handle = spawnWebIdeMessageWorker(ctx.cfg, payload);
|
|
1612
|
+
const onAbort = () => handle.cancel(payload.messageId);
|
|
1613
|
+
perMessageController.signal.addEventListener("abort", onAbort, {
|
|
1614
|
+
once: true
|
|
1615
|
+
});
|
|
1616
|
+
try {
|
|
1617
|
+
await handle.done;
|
|
1618
|
+
} finally {
|
|
1619
|
+
perMessageController.signal.removeEventListener("abort", onAbort);
|
|
1620
|
+
ctx.activeRuns.delete(payload.messageId);
|
|
1621
|
+
}
|
|
1622
|
+
})();
|
|
1623
|
+
trackActionTask(ctx, task);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1507
1626
|
// src/commands/connect/actions/index.ts
|
|
1508
1627
|
function dispatchConnectAction(payload, ctx) {
|
|
1509
1628
|
switch (payload.type) {
|
|
@@ -1521,6 +1640,9 @@ function dispatchConnectAction(payload, ctx) {
|
|
|
1521
1640
|
case "webide-stop-terminals":
|
|
1522
1641
|
handleStopTerminalsAction(payload, ctx);
|
|
1523
1642
|
return;
|
|
1643
|
+
case "webide-package":
|
|
1644
|
+
handleWebIdePackageAction(payload, ctx);
|
|
1645
|
+
return;
|
|
1524
1646
|
}
|
|
1525
1647
|
}
|
|
1526
1648
|
|
|
@@ -99,6 +99,18 @@ var requestConfig = {
|
|
|
99
99
|
getApmLogStorage: defineEndpoint({
|
|
100
100
|
method: "GET",
|
|
101
101
|
path: "/cli/apm-log-storage"
|
|
102
|
+
}),
|
|
103
|
+
projectPackageUpdateStatus: defineEndpoint({
|
|
104
|
+
method: "PUT",
|
|
105
|
+
path: "/cli/project-packages/status"
|
|
106
|
+
}),
|
|
107
|
+
projectPackageAppendContent: defineEndpoint({
|
|
108
|
+
method: "PUT",
|
|
109
|
+
path: "/cli/project-packages/content"
|
|
110
|
+
}),
|
|
111
|
+
projectPackageEnsureContent: defineEndpoint({
|
|
112
|
+
method: "PUT",
|
|
113
|
+
path: "/cli/project-packages/ensure-content"
|
|
102
114
|
})
|
|
103
115
|
}
|
|
104
116
|
};
|
|
@@ -942,6 +954,49 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
942
954
|
repos: repoRoots
|
|
943
955
|
};
|
|
944
956
|
}
|
|
957
|
+
async function checkoutBaselineBranches(baselineBranch, options = {}) {
|
|
958
|
+
const baseline = baselineBranch.trim().replace(/^origin\//, "");
|
|
959
|
+
if (!baseline) {
|
|
960
|
+
throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
|
|
961
|
+
}
|
|
962
|
+
const cwd = options.cwd ?? process.cwd();
|
|
963
|
+
const workdirPath = resolveWorkdirPath(cwd);
|
|
964
|
+
const manifest = loadWorkspaceReposCache(workdirPath);
|
|
965
|
+
const repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
966
|
+
for (const gitRoot of repoRoots) {
|
|
967
|
+
const label = formatRepoLabel(workdirPath, gitRoot);
|
|
968
|
+
console.log(`[apm] \u5207\u6362\u57FA\u7EBF\u5206\u652F ${baseline} @ ${label}`);
|
|
969
|
+
await checkoutBaselineBranch(baseline, { ...options, cwd: gitRoot });
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
baseline,
|
|
973
|
+
kind: manifest.kind,
|
|
974
|
+
repos: repoRoots
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async function checkoutBaselineBranch(baselineBranch, options = {}) {
|
|
978
|
+
const baseline = baselineBranch.trim().replace(/^origin\//, "");
|
|
979
|
+
if (!baseline) {
|
|
980
|
+
throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
|
|
981
|
+
}
|
|
982
|
+
const cwd = options.cwd ?? process.cwd();
|
|
983
|
+
const gitRoot = await resolveGitRepoRoot(cwd);
|
|
984
|
+
await ensureGitRepo(gitRoot);
|
|
985
|
+
if (await isWorkingTreeDirty(gitRoot)) {
|
|
986
|
+
throw new Error(`[apm] \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u65E0\u6CD5\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`);
|
|
987
|
+
}
|
|
988
|
+
await ensureRemoteBaselineBranch(gitRoot, baseline);
|
|
989
|
+
const current = await getCurrentBranch(gitRoot);
|
|
990
|
+
if (current !== baseline) {
|
|
991
|
+
await execGit(gitRoot, ["checkout", "-B", baseline, `origin/${baseline}`]);
|
|
992
|
+
}
|
|
993
|
+
await execGit(gitRoot, ["reset", "--hard", `origin/${baseline}`]);
|
|
994
|
+
const head = (await execGit(gitRoot, ["rev-parse", "--short", "HEAD"], true)).trim();
|
|
995
|
+
console.log(
|
|
996
|
+
`[apm] \u5DF2\u5207\u6362\u5230\u57FA\u7EBF ${baseline}\uFF0C\u5E76\u5BF9\u9F50 origin/${baseline}\uFF08${head}\uFF09`
|
|
997
|
+
);
|
|
998
|
+
return baseline;
|
|
999
|
+
}
|
|
945
1000
|
|
|
946
1001
|
// src/commands/connect/cursor-agent.ts
|
|
947
1002
|
import {
|
|
@@ -1803,6 +1858,45 @@ function createMysqlExecuteTool(options) {
|
|
|
1803
1858
|
};
|
|
1804
1859
|
}
|
|
1805
1860
|
|
|
1861
|
+
// src/commands/connect/tools/package-status-tool.ts
|
|
1862
|
+
function createSetPackageFailedTool(options) {
|
|
1863
|
+
const { packageId, setFailed } = options;
|
|
1864
|
+
return {
|
|
1865
|
+
SetPackageFailed: {
|
|
1866
|
+
description: "\u6253\u5305\u5931\u8D25\u65F6\u8C03\u7528\uFF1A\u5C06\u5F53\u524D\u6253\u5305\u8BB0\u5F55\u6807\u4E3A FAILED\uFF0C\u5E76\u5199\u5165\u5931\u8D25\u539F\u56E0\u3002\u6784\u5EFA\u5931\u8D25\u3001\u7F3A\u4EA7\u7269\u3001\u6587\u6863\u7F3A\u5931\u7B49\u5747\u5E94\u8C03\u7528\uFF1B\u6210\u529F\u65F6\u4E0D\u8981\u8C03\u7528\u3002",
|
|
1867
|
+
inputSchema: {
|
|
1868
|
+
type: "object",
|
|
1869
|
+
properties: {
|
|
1870
|
+
error: {
|
|
1871
|
+
type: "string",
|
|
1872
|
+
description: "\u5931\u8D25\u539F\u56E0\u6458\u8981\uFF08\u4F1A\u5199\u5165\u6253\u5305\u8BB0\u5F55\uFF0C\u4F9B\u5217\u8868/\u65E5\u5FD7\u5C55\u793A\uFF09"
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
required: ["error"]
|
|
1876
|
+
},
|
|
1877
|
+
execute: async (args) => {
|
|
1878
|
+
const error = typeof args.error === "string" ? args.error.trim() : "";
|
|
1879
|
+
if (!error) {
|
|
1880
|
+
return {
|
|
1881
|
+
content: [{ type: "text", text: "error \u4E0D\u80FD\u4E3A\u7A7A" }],
|
|
1882
|
+
isError: true
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
try {
|
|
1886
|
+
await setFailed(error);
|
|
1887
|
+
return `\u5DF2\u5C06\u6253\u5305\u8BB0\u5F55 ${packageId} \u6807\u4E3A FAILED`;
|
|
1888
|
+
} catch (err) {
|
|
1889
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1890
|
+
return {
|
|
1891
|
+
content: [{ type: "text", text: `\u5199\u5165\u5931\u8D25\u72B6\u6001\u5931\u8D25: ${detail}` }],
|
|
1892
|
+
isError: true
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1806
1900
|
// src/commands/connect/tools/index.ts
|
|
1807
1901
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
1808
1902
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
@@ -1853,6 +1947,24 @@ function createCursorCustomTools(cfg, options) {
|
|
|
1853
1947
|
);
|
|
1854
1948
|
}
|
|
1855
1949
|
}
|
|
1950
|
+
if (options.enablePackageStatusTools && options.packageId) {
|
|
1951
|
+
const packageId = options.packageId;
|
|
1952
|
+
const { cli } = createApmApiClient(cfg);
|
|
1953
|
+
Object.assign(
|
|
1954
|
+
tools,
|
|
1955
|
+
createSetPackageFailedTool({
|
|
1956
|
+
packageId,
|
|
1957
|
+
setFailed: async (error) => {
|
|
1958
|
+
await cli.projectPackageUpdateStatus({
|
|
1959
|
+
id: packageId,
|
|
1960
|
+
status: "FAILED",
|
|
1961
|
+
error
|
|
1962
|
+
});
|
|
1963
|
+
await options.onPackageFailed?.(error);
|
|
1964
|
+
}
|
|
1965
|
+
})
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1856
1968
|
return tools;
|
|
1857
1969
|
}
|
|
1858
1970
|
function withPlanModeToolHint(prompt, mode) {
|
|
@@ -1973,7 +2085,10 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1973
2085
|
enableMysqlTools: options.enableMysqlTools,
|
|
1974
2086
|
sqlExecutionId: options.sqlExecutionId,
|
|
1975
2087
|
taskId: options.taskId,
|
|
1976
|
-
workdir
|
|
2088
|
+
workdir,
|
|
2089
|
+
enablePackageStatusTools: options.enablePackageStatusTools,
|
|
2090
|
+
packageId: options.packageId,
|
|
2091
|
+
onPackageFailed: options.onPackageFailed
|
|
1977
2092
|
});
|
|
1978
2093
|
const enableSandbox = Boolean(options.enableSandbox);
|
|
1979
2094
|
const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
|
|
@@ -2429,12 +2544,12 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
|
|
|
2429
2544
|
});
|
|
2430
2545
|
}
|
|
2431
2546
|
}
|
|
2432
|
-
async function upsertLogHeader(cfg, ctx, patch) {
|
|
2547
|
+
async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
|
|
2433
2548
|
const api = createApmApiClient(cfg);
|
|
2434
2549
|
await api.cli.webideUpsertMessageLog({
|
|
2435
2550
|
messageId: ctx.messageId,
|
|
2436
2551
|
agentId: ctx.agentId,
|
|
2437
|
-
objectPrefix
|
|
2552
|
+
objectPrefix,
|
|
2438
2553
|
...patch
|
|
2439
2554
|
});
|
|
2440
2555
|
}
|
|
@@ -2446,6 +2561,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2446
2561
|
let minio;
|
|
2447
2562
|
let bucket = "";
|
|
2448
2563
|
let firstLogNotified = false;
|
|
2564
|
+
const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
|
|
2449
2565
|
const ensureMinio = async () => {
|
|
2450
2566
|
if (minio) return;
|
|
2451
2567
|
const created = await createApmLogMinioClient(cfg);
|
|
@@ -2469,11 +2585,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2469
2585
|
lastRunAt = Date.now();
|
|
2470
2586
|
try {
|
|
2471
2587
|
await ensureMinio();
|
|
2472
|
-
const prefix =
|
|
2588
|
+
const prefix = resolvePrefix();
|
|
2473
2589
|
await putDirtyEvents(minio, bucket, prefix, events);
|
|
2474
|
-
await upsertLogHeader(
|
|
2475
|
-
|
|
2476
|
-
|
|
2590
|
+
await upsertLogHeader(
|
|
2591
|
+
cfg,
|
|
2592
|
+
ctx,
|
|
2593
|
+
{
|
|
2594
|
+
eventCount: session.getEventCount()
|
|
2595
|
+
},
|
|
2596
|
+
prefix
|
|
2597
|
+
);
|
|
2477
2598
|
session.clearDirtyIfUnchanged(events);
|
|
2478
2599
|
await notifyFirstLog();
|
|
2479
2600
|
} catch (err) {
|
|
@@ -2491,12 +2612,17 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2491
2612
|
return {
|
|
2492
2613
|
async markRun(runId, runStatus, lastError, tokenUsage) {
|
|
2493
2614
|
try {
|
|
2494
|
-
await upsertLogHeader(
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2615
|
+
await upsertLogHeader(
|
|
2616
|
+
cfg,
|
|
2617
|
+
ctx,
|
|
2618
|
+
{
|
|
2619
|
+
runId,
|
|
2620
|
+
runStatus,
|
|
2621
|
+
lastError: lastError ?? null,
|
|
2622
|
+
...tokenUsage ? { tokenUsage } : {}
|
|
2623
|
+
},
|
|
2624
|
+
resolvePrefix()
|
|
2625
|
+
);
|
|
2500
2626
|
} catch (err) {
|
|
2501
2627
|
onError(err);
|
|
2502
2628
|
}
|
|
@@ -3856,6 +3982,276 @@ async function handleStartProject(cfg, msg, signal) {
|
|
|
3856
3982
|
}
|
|
3857
3983
|
}
|
|
3858
3984
|
|
|
3985
|
+
// src/commands/connect/handle-package.ts
|
|
3986
|
+
async function updatePackageStatus(cfg, packageId, status, extra) {
|
|
3987
|
+
const api = createApmApiClient(cfg);
|
|
3988
|
+
await api.cli.projectPackageUpdateStatus({
|
|
3989
|
+
id: packageId,
|
|
3990
|
+
status,
|
|
3991
|
+
...extra?.error != null ? { error: extra.error } : {},
|
|
3992
|
+
...extra?.content != null ? { content: extra.content } : {},
|
|
3993
|
+
...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
|
|
3994
|
+
});
|
|
3995
|
+
}
|
|
3996
|
+
function expectedArtifactPath(target, packageId) {
|
|
3997
|
+
if (target === "frontend") {
|
|
3998
|
+
return `/data/artifacts/vue/${packageId}/dist.zip`;
|
|
3999
|
+
}
|
|
4000
|
+
return `/data/artifacts/springboot/${packageId}/manifest.json`;
|
|
4001
|
+
}
|
|
4002
|
+
async function handleWebIdePackage(cfg, msg, signal) {
|
|
4003
|
+
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
4004
|
+
const messageId = msg.messageId;
|
|
4005
|
+
const packageId = msg.packageId;
|
|
4006
|
+
const projectId = msg.projectId;
|
|
4007
|
+
console.log(
|
|
4008
|
+
`[apm] webide-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
|
|
4009
|
+
);
|
|
4010
|
+
await updatePackageStatus(cfg, packageId, "RUNNING");
|
|
4011
|
+
const eventSession = new EventSession(msg.content);
|
|
4012
|
+
const logSyncRef = {
|
|
4013
|
+
current: createThrottledWebIdeMessageLogSync(
|
|
4014
|
+
cfg,
|
|
4015
|
+
{
|
|
4016
|
+
taskId: `package:${packageId}`,
|
|
4017
|
+
messageId,
|
|
4018
|
+
agentId: "webide-package"
|
|
4019
|
+
},
|
|
4020
|
+
(err) => {
|
|
4021
|
+
console.warn(
|
|
4022
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4023
|
+
err instanceof Error ? err.message : err
|
|
4024
|
+
);
|
|
4025
|
+
},
|
|
4026
|
+
{
|
|
4027
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4028
|
+
}
|
|
4029
|
+
)
|
|
4030
|
+
};
|
|
4031
|
+
const syncPrepLog = async () => {
|
|
4032
|
+
const sync = logSyncRef.current;
|
|
4033
|
+
if (!sync) return;
|
|
4034
|
+
sync.schedule(eventSession);
|
|
4035
|
+
await sync.flush(eventSession);
|
|
4036
|
+
};
|
|
4037
|
+
const runPrepStep = async (step, work, formatOk) => {
|
|
4038
|
+
eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
|
|
4039
|
+
await syncPrepLog();
|
|
4040
|
+
try {
|
|
4041
|
+
const result = await work();
|
|
4042
|
+
eventSession.addCliStep(step, formatOk(result), "ok");
|
|
4043
|
+
await syncPrepLog();
|
|
4044
|
+
return result;
|
|
4045
|
+
} catch (err) {
|
|
4046
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4047
|
+
eventSession.addCliStep(step, detail, "error");
|
|
4048
|
+
await syncPrepLog();
|
|
4049
|
+
throw err;
|
|
4050
|
+
}
|
|
4051
|
+
};
|
|
4052
|
+
try {
|
|
4053
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4054
|
+
await runPrepStep(
|
|
4055
|
+
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
4056
|
+
async () => {
|
|
4057
|
+
const { didInit } = await ensureWorkspaceInitialized(workdir);
|
|
4058
|
+
return didInit;
|
|
4059
|
+
},
|
|
4060
|
+
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|
|
4061
|
+
);
|
|
4062
|
+
const cliVersion = readCliVersion();
|
|
4063
|
+
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
4064
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4065
|
+
await runPrepStep(
|
|
4066
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
4067
|
+
async () => {
|
|
4068
|
+
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
4069
|
+
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
4070
|
+
return cliVersion;
|
|
4071
|
+
},
|
|
4072
|
+
(version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
|
|
4073
|
+
);
|
|
4074
|
+
} else {
|
|
4075
|
+
eventSession.addCliStep(
|
|
4076
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
4077
|
+
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
4078
|
+
"skip"
|
|
4079
|
+
);
|
|
4080
|
+
await syncPrepLog();
|
|
4081
|
+
}
|
|
4082
|
+
await runPrepStep(
|
|
4083
|
+
"\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
|
|
4084
|
+
async () => {
|
|
4085
|
+
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
4086
|
+
try {
|
|
4087
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
4088
|
+
} catch (err) {
|
|
4089
|
+
console.warn(
|
|
4090
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
4091
|
+
err instanceof Error ? err.message : err
|
|
4092
|
+
);
|
|
4093
|
+
}
|
|
4094
|
+
return workspaceRepos;
|
|
4095
|
+
},
|
|
4096
|
+
(repos) => `kind=${repos.kind} repos=${repos.repos.length}`
|
|
4097
|
+
);
|
|
4098
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4099
|
+
await runPrepStep(
|
|
4100
|
+
"\u5207\u6362\u57FA\u7EBF\u5E76\u62C9\u53D6\u6700\u65B0",
|
|
4101
|
+
async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
|
|
4102
|
+
(result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
|
|
4103
|
+
);
|
|
4104
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4105
|
+
await runPrepStep(
|
|
4106
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863",
|
|
4107
|
+
async () => {
|
|
4108
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
4109
|
+
projectId
|
|
4110
|
+
});
|
|
4111
|
+
return docs;
|
|
4112
|
+
},
|
|
4113
|
+
(result) => {
|
|
4114
|
+
if (!result.synced) {
|
|
4115
|
+
return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
|
|
4116
|
+
}
|
|
4117
|
+
return `downloaded=${result.downloaded} deleted=${result.deleted}`;
|
|
4118
|
+
}
|
|
4119
|
+
);
|
|
4120
|
+
eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
|
|
4121
|
+
await syncPrepLog();
|
|
4122
|
+
const artifactHint = expectedArtifactPath(msg.target, packageId);
|
|
4123
|
+
let failedByTool = false;
|
|
4124
|
+
const outcome = await runCursorAgent(
|
|
4125
|
+
cfg,
|
|
4126
|
+
{
|
|
4127
|
+
messageId,
|
|
4128
|
+
prompt: msg.content,
|
|
4129
|
+
model: msg.model,
|
|
4130
|
+
apiKey: msg.apiKey,
|
|
4131
|
+
workdir,
|
|
4132
|
+
user: msg.user || "webide"
|
|
4133
|
+
},
|
|
4134
|
+
{
|
|
4135
|
+
signal,
|
|
4136
|
+
forceSend: true,
|
|
4137
|
+
eventSession,
|
|
4138
|
+
enableAppendMessage: false,
|
|
4139
|
+
enableAskQuestion: false,
|
|
4140
|
+
enableWebIdePlanTools: false,
|
|
4141
|
+
enablePtySessionMcp: false,
|
|
4142
|
+
enableSandbox: false,
|
|
4143
|
+
enablePackageStatusTools: true,
|
|
4144
|
+
packageId,
|
|
4145
|
+
onPackageFailed: async () => {
|
|
4146
|
+
failedByTool = true;
|
|
4147
|
+
},
|
|
4148
|
+
taskId: `package:${packageId}`,
|
|
4149
|
+
createRemoteLogSync: (agentId) => {
|
|
4150
|
+
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
4151
|
+
cfg,
|
|
4152
|
+
{
|
|
4153
|
+
taskId: `package:${packageId}`,
|
|
4154
|
+
messageId,
|
|
4155
|
+
agentId
|
|
4156
|
+
},
|
|
4157
|
+
(err) => {
|
|
4158
|
+
console.warn(
|
|
4159
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4160
|
+
err instanceof Error ? err.message : err
|
|
4161
|
+
);
|
|
4162
|
+
},
|
|
4163
|
+
{
|
|
4164
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4165
|
+
}
|
|
4166
|
+
);
|
|
4167
|
+
return logSyncRef.current;
|
|
4168
|
+
},
|
|
4169
|
+
onRunStarted: async ({ agentId, runId }) => {
|
|
4170
|
+
eventSession.addCliStep(
|
|
4171
|
+
"\u542F\u52A8\u6253\u5305 Agent",
|
|
4172
|
+
`agentId=${agentId} runId=${runId}`,
|
|
4173
|
+
"ok"
|
|
4174
|
+
);
|
|
4175
|
+
logSyncRef.current?.schedule(eventSession);
|
|
4176
|
+
await logSyncRef.current?.markRun(runId, "running");
|
|
4177
|
+
await logSyncRef.current?.flush(eventSession);
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
);
|
|
4181
|
+
const tokenUsage = outcome.usage != null ? {
|
|
4182
|
+
modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
|
|
4183
|
+
inputTokens: outcome.usage.inputTokens,
|
|
4184
|
+
outputTokens: outcome.usage.outputTokens,
|
|
4185
|
+
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
4186
|
+
cacheWriteTokens: outcome.usage.cacheWriteTokens,
|
|
4187
|
+
totalTokens: outcome.usage.totalTokens,
|
|
4188
|
+
...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
|
|
4189
|
+
} : void 0;
|
|
4190
|
+
if (outcome.status === "error") {
|
|
4191
|
+
const detail = formatCursorRunFailure(outcome.runId, {
|
|
4192
|
+
resultText: outcome.result
|
|
4193
|
+
});
|
|
4194
|
+
await logSyncRef.current?.markRun(
|
|
4195
|
+
outcome.runId,
|
|
4196
|
+
"error",
|
|
4197
|
+
detail,
|
|
4198
|
+
tokenUsage
|
|
4199
|
+
);
|
|
4200
|
+
if (!failedByTool) {
|
|
4201
|
+
await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
|
|
4202
|
+
}
|
|
4203
|
+
return;
|
|
4204
|
+
}
|
|
4205
|
+
if (outcome.status === "cancelled" || signal.aborted) {
|
|
4206
|
+
await logSyncRef.current?.markRun(
|
|
4207
|
+
outcome.runId,
|
|
4208
|
+
"cancelled",
|
|
4209
|
+
"\u5DF2\u53D6\u6D88",
|
|
4210
|
+
tokenUsage
|
|
4211
|
+
);
|
|
4212
|
+
await updatePackageStatus(cfg, packageId, "CANCELLED", {
|
|
4213
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
4214
|
+
});
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
await logSyncRef.current?.markRun(
|
|
4218
|
+
outcome.runId,
|
|
4219
|
+
"finished",
|
|
4220
|
+
null,
|
|
4221
|
+
tokenUsage
|
|
4222
|
+
);
|
|
4223
|
+
if (failedByTool) {
|
|
4224
|
+
console.log(
|
|
4225
|
+
`[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
|
|
4226
|
+
);
|
|
4227
|
+
return;
|
|
4228
|
+
}
|
|
4229
|
+
await updatePackageStatus(cfg, packageId, "SUCCESS", {
|
|
4230
|
+
artifactPath: artifactHint
|
|
4231
|
+
});
|
|
4232
|
+
console.log(
|
|
4233
|
+
`[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
|
|
4234
|
+
);
|
|
4235
|
+
} catch (err) {
|
|
4236
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4237
|
+
console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
|
|
4238
|
+
try {
|
|
4239
|
+
if (signal.aborted) {
|
|
4240
|
+
await updatePackageStatus(cfg, packageId, "CANCELLED", {
|
|
4241
|
+
error: detail
|
|
4242
|
+
});
|
|
4243
|
+
} else {
|
|
4244
|
+
await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
|
|
4245
|
+
}
|
|
4246
|
+
} catch (statusErr) {
|
|
4247
|
+
console.error(
|
|
4248
|
+
"[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
|
|
4249
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4250
|
+
);
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
|
|
3859
4255
|
// src/commands/connect/webide-message-worker.ts
|
|
3860
4256
|
var controllers = /* @__PURE__ */ new Map();
|
|
3861
4257
|
async function runJob(cfg, msg) {
|
|
@@ -3864,6 +4260,8 @@ async function runJob(cfg, msg) {
|
|
|
3864
4260
|
try {
|
|
3865
4261
|
if (msg.type === "webide-start-project") {
|
|
3866
4262
|
await handleStartProject(cfg, msg, controller.signal);
|
|
4263
|
+
} else if (msg.type === "webide-package") {
|
|
4264
|
+
await handleWebIdePackage(cfg, msg, controller.signal);
|
|
3867
4265
|
} else {
|
|
3868
4266
|
await handleWebIdeInboundMessage(cfg, msg, controller.signal);
|
|
3869
4267
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## WebIDE 项目打包(webide-package)
|
|
2
|
+
|
|
3
|
+
适用:独立打包 Agent(不 resume 任务对话)。只打 payload 指定的 **一个** target(`frontend` / `backend`)。
|
|
4
|
+
|
|
5
|
+
### 开始前
|
|
6
|
+
|
|
7
|
+
1. Read 本规则全文。
|
|
8
|
+
2. Read `.apm/project/<项目ID>/` 下 path/文件名含 `deploy` 的文档(先 manifest,再全文)。命令与仓路径**只**来自文档,禁止猜测。
|
|
9
|
+
3. Read `.apm/workspace-repos.json`,按多仓清单定位本轮要打的前端/后端仓库路径。
|
|
10
|
+
|
|
11
|
+
### 通用硬性约束
|
|
12
|
+
|
|
13
|
+
- **禁止**修改业务代码、提交 Git、创建 PR。
|
|
14
|
+
- **禁止**为修依赖或修编译错误而改代码 / 装依赖 / 改 lockfile;构建失败 → 以错误结束。
|
|
15
|
+
- **禁止**在仓库工作区内留下交付用 zip / 拷贝后的 jar;临时文件只用 `/data/` 下目录(如 `/data/artifacts/.tmp/<packageId>/`),成功后可删临时目录。
|
|
16
|
+
- 临时目录与最终产物均在 `/data` 下,不要用 `/tmp`。
|
|
17
|
+
|
|
18
|
+
### target=frontend(Vue)
|
|
19
|
+
|
|
20
|
+
1. 按 deploy 文档进入前端目录。
|
|
21
|
+
2. **不要**执行 `npm i` / `pnpm i` 等;分支切换后依赖已就绪。
|
|
22
|
+
3. 执行文档中的打包命令(通常 `npm run build`)。
|
|
23
|
+
4. 确认产出 `dist/`(或文档写明的目录)。
|
|
24
|
+
5. 在 `/data` 下压缩(勿在工作区生成 `dist.zip`),落到:
|
|
25
|
+
|
|
26
|
+
`/data/artifacts/vue/<packageId>/dist.zip`
|
|
27
|
+
|
|
28
|
+
6. 缺产物或构建失败 → 调用 **SetPackageFailed**(传入失败原因)后结束。
|
|
29
|
+
|
|
30
|
+
### target=backend(SpringBoot)
|
|
31
|
+
|
|
32
|
+
1. 按 deploy 文档进入后端目录,执行 `mvn clean package`(或以文档为准)。
|
|
33
|
+
2. 按文档收集**全量**需交付的 jar 与 sql(增量规则若文档另有说明则从其规定;无说明则收文档列出的全部)。
|
|
34
|
+
3. 对每个文件算内容 hash(sha256),写入全局池(已存在同 hash 则跳过复制):
|
|
35
|
+
|
|
36
|
+
- jar → `/data/artifacts/springboot/jars/<hash>.jar`
|
|
37
|
+
- sql → `/data/artifacts/springboot/sqls/<hash>.sql`(或保留扩展名,文件名主体为 hash)
|
|
38
|
+
|
|
39
|
+
4. 写入清单(**仅**此目录放 manifest,不要堆 jar/sql 副本):
|
|
40
|
+
|
|
41
|
+
`/data/artifacts/springboot/<packageId>/manifest.json`
|
|
42
|
+
|
|
43
|
+
5. manifest 示例(字段名与结构须一致;值按实际填写):
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"packageId": "pkg_…",
|
|
48
|
+
"projectId": "clx…",
|
|
49
|
+
"target": "backend",
|
|
50
|
+
"baseBranch": "release/1.0",
|
|
51
|
+
"createdAt": "2026-09-02T07:00:00.000Z",
|
|
52
|
+
"commitId": "a1b2c3d4…",
|
|
53
|
+
"jars": [
|
|
54
|
+
{
|
|
55
|
+
"name": "app-1.2.3.jar",
|
|
56
|
+
"hash": "e3b0…b855",
|
|
57
|
+
"path": "/data/artifacts/springboot/jars/e3b0…b855.jar"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"sqls": [
|
|
61
|
+
{
|
|
62
|
+
"name": "20260301_add_col.sql",
|
|
63
|
+
"hash": "2c26…e7ae",
|
|
64
|
+
"path": "/data/artifacts/springboot/sqls/2c26…e7ae.sql"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
6. 构建或落盘失败 → 调用 **SetPackageFailed**(传入失败原因)后结束。
|
|
71
|
+
|
|
72
|
+
### 结束
|
|
73
|
+
|
|
74
|
+
- 成功:确认产物已落到约定路径后正常结束(不要调用 SetPackageFailed)。
|
|
75
|
+
- 失败:先调用 SetPackageFailed 写入原因,再结束。
|