ai-project-manage-cli 8.0.8 → 8.0.9
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 +417 -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,55 @@ 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(
|
|
987
|
+
`[apm] \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u65E0\u6CD5\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
await ensureRemoteBaselineBranch(gitRoot, baseline);
|
|
991
|
+
const current = await getCurrentBranch(gitRoot);
|
|
992
|
+
if (current === baseline) {
|
|
993
|
+
await execGit(gitRoot, ["reset", "--hard", `origin/${baseline}`]);
|
|
994
|
+
console.log(`[apm] \u5DF2\u5728\u57FA\u7EBF ${baseline}\uFF0C\u5DF2\u5BF9\u9F50 origin/${baseline}`);
|
|
995
|
+
return baseline;
|
|
996
|
+
}
|
|
997
|
+
await execGit(gitRoot, [
|
|
998
|
+
"checkout",
|
|
999
|
+
"-B",
|
|
1000
|
+
baseline,
|
|
1001
|
+
`origin/${baseline}`
|
|
1002
|
+
]);
|
|
1003
|
+
console.log(`[apm] \u5DF2\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`);
|
|
1004
|
+
return baseline;
|
|
1005
|
+
}
|
|
945
1006
|
|
|
946
1007
|
// src/commands/connect/cursor-agent.ts
|
|
947
1008
|
import {
|
|
@@ -1803,6 +1864,45 @@ function createMysqlExecuteTool(options) {
|
|
|
1803
1864
|
};
|
|
1804
1865
|
}
|
|
1805
1866
|
|
|
1867
|
+
// src/commands/connect/tools/package-status-tool.ts
|
|
1868
|
+
function createSetPackageFailedTool(options) {
|
|
1869
|
+
const { packageId, setFailed } = options;
|
|
1870
|
+
return {
|
|
1871
|
+
SetPackageFailed: {
|
|
1872
|
+
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",
|
|
1873
|
+
inputSchema: {
|
|
1874
|
+
type: "object",
|
|
1875
|
+
properties: {
|
|
1876
|
+
error: {
|
|
1877
|
+
type: "string",
|
|
1878
|
+
description: "\u5931\u8D25\u539F\u56E0\u6458\u8981\uFF08\u4F1A\u5199\u5165\u6253\u5305\u8BB0\u5F55\uFF0C\u4F9B\u5217\u8868/\u65E5\u5FD7\u5C55\u793A\uFF09"
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
required: ["error"]
|
|
1882
|
+
},
|
|
1883
|
+
execute: async (args) => {
|
|
1884
|
+
const error = typeof args.error === "string" ? args.error.trim() : "";
|
|
1885
|
+
if (!error) {
|
|
1886
|
+
return {
|
|
1887
|
+
content: [{ type: "text", text: "error \u4E0D\u80FD\u4E3A\u7A7A" }],
|
|
1888
|
+
isError: true
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
try {
|
|
1892
|
+
await setFailed(error);
|
|
1893
|
+
return `\u5DF2\u5C06\u6253\u5305\u8BB0\u5F55 ${packageId} \u6807\u4E3A FAILED`;
|
|
1894
|
+
} catch (err) {
|
|
1895
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1896
|
+
return {
|
|
1897
|
+
content: [{ type: "text", text: `\u5199\u5165\u5931\u8D25\u72B6\u6001\u5931\u8D25: ${detail}` }],
|
|
1898
|
+
isError: true
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1806
1906
|
// src/commands/connect/tools/index.ts
|
|
1807
1907
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
1808
1908
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
@@ -1853,6 +1953,24 @@ function createCursorCustomTools(cfg, options) {
|
|
|
1853
1953
|
);
|
|
1854
1954
|
}
|
|
1855
1955
|
}
|
|
1956
|
+
if (options.enablePackageStatusTools && options.packageId) {
|
|
1957
|
+
const packageId = options.packageId;
|
|
1958
|
+
const { cli } = createApmApiClient(cfg);
|
|
1959
|
+
Object.assign(
|
|
1960
|
+
tools,
|
|
1961
|
+
createSetPackageFailedTool({
|
|
1962
|
+
packageId,
|
|
1963
|
+
setFailed: async (error) => {
|
|
1964
|
+
await cli.projectPackageUpdateStatus({
|
|
1965
|
+
id: packageId,
|
|
1966
|
+
status: "FAILED",
|
|
1967
|
+
error
|
|
1968
|
+
});
|
|
1969
|
+
await options.onPackageFailed?.(error);
|
|
1970
|
+
}
|
|
1971
|
+
})
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1856
1974
|
return tools;
|
|
1857
1975
|
}
|
|
1858
1976
|
function withPlanModeToolHint(prompt, mode) {
|
|
@@ -1973,7 +2091,10 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1973
2091
|
enableMysqlTools: options.enableMysqlTools,
|
|
1974
2092
|
sqlExecutionId: options.sqlExecutionId,
|
|
1975
2093
|
taskId: options.taskId,
|
|
1976
|
-
workdir
|
|
2094
|
+
workdir,
|
|
2095
|
+
enablePackageStatusTools: options.enablePackageStatusTools,
|
|
2096
|
+
packageId: options.packageId,
|
|
2097
|
+
onPackageFailed: options.onPackageFailed
|
|
1977
2098
|
});
|
|
1978
2099
|
const enableSandbox = Boolean(options.enableSandbox);
|
|
1979
2100
|
const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
|
|
@@ -2429,12 +2550,12 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
|
|
|
2429
2550
|
});
|
|
2430
2551
|
}
|
|
2431
2552
|
}
|
|
2432
|
-
async function upsertLogHeader(cfg, ctx, patch) {
|
|
2553
|
+
async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
|
|
2433
2554
|
const api = createApmApiClient(cfg);
|
|
2434
2555
|
await api.cli.webideUpsertMessageLog({
|
|
2435
2556
|
messageId: ctx.messageId,
|
|
2436
2557
|
agentId: ctx.agentId,
|
|
2437
|
-
objectPrefix
|
|
2558
|
+
objectPrefix,
|
|
2438
2559
|
...patch
|
|
2439
2560
|
});
|
|
2440
2561
|
}
|
|
@@ -2446,6 +2567,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2446
2567
|
let minio;
|
|
2447
2568
|
let bucket = "";
|
|
2448
2569
|
let firstLogNotified = false;
|
|
2570
|
+
const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
|
|
2449
2571
|
const ensureMinio = async () => {
|
|
2450
2572
|
if (minio) return;
|
|
2451
2573
|
const created = await createApmLogMinioClient(cfg);
|
|
@@ -2469,11 +2591,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2469
2591
|
lastRunAt = Date.now();
|
|
2470
2592
|
try {
|
|
2471
2593
|
await ensureMinio();
|
|
2472
|
-
const prefix =
|
|
2594
|
+
const prefix = resolvePrefix();
|
|
2473
2595
|
await putDirtyEvents(minio, bucket, prefix, events);
|
|
2474
|
-
await upsertLogHeader(
|
|
2475
|
-
|
|
2476
|
-
|
|
2596
|
+
await upsertLogHeader(
|
|
2597
|
+
cfg,
|
|
2598
|
+
ctx,
|
|
2599
|
+
{
|
|
2600
|
+
eventCount: session.getEventCount()
|
|
2601
|
+
},
|
|
2602
|
+
prefix
|
|
2603
|
+
);
|
|
2477
2604
|
session.clearDirtyIfUnchanged(events);
|
|
2478
2605
|
await notifyFirstLog();
|
|
2479
2606
|
} catch (err) {
|
|
@@ -2491,12 +2618,17 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2491
2618
|
return {
|
|
2492
2619
|
async markRun(runId, runStatus, lastError, tokenUsage) {
|
|
2493
2620
|
try {
|
|
2494
|
-
await upsertLogHeader(
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2621
|
+
await upsertLogHeader(
|
|
2622
|
+
cfg,
|
|
2623
|
+
ctx,
|
|
2624
|
+
{
|
|
2625
|
+
runId,
|
|
2626
|
+
runStatus,
|
|
2627
|
+
lastError: lastError ?? null,
|
|
2628
|
+
...tokenUsage ? { tokenUsage } : {}
|
|
2629
|
+
},
|
|
2630
|
+
resolvePrefix()
|
|
2631
|
+
);
|
|
2500
2632
|
} catch (err) {
|
|
2501
2633
|
onError(err);
|
|
2502
2634
|
}
|
|
@@ -3856,6 +3988,276 @@ async function handleStartProject(cfg, msg, signal) {
|
|
|
3856
3988
|
}
|
|
3857
3989
|
}
|
|
3858
3990
|
|
|
3991
|
+
// src/commands/connect/handle-package.ts
|
|
3992
|
+
async function updatePackageStatus(cfg, packageId, status, extra) {
|
|
3993
|
+
const api = createApmApiClient(cfg);
|
|
3994
|
+
await api.cli.projectPackageUpdateStatus({
|
|
3995
|
+
id: packageId,
|
|
3996
|
+
status,
|
|
3997
|
+
...extra?.error != null ? { error: extra.error } : {},
|
|
3998
|
+
...extra?.content != null ? { content: extra.content } : {},
|
|
3999
|
+
...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
|
|
4000
|
+
});
|
|
4001
|
+
}
|
|
4002
|
+
function expectedArtifactPath(target, packageId) {
|
|
4003
|
+
if (target === "frontend") {
|
|
4004
|
+
return `/data/artifacts/vue/${packageId}/dist.zip`;
|
|
4005
|
+
}
|
|
4006
|
+
return `/data/artifacts/springboot/${packageId}/manifest.json`;
|
|
4007
|
+
}
|
|
4008
|
+
async function handleWebIdePackage(cfg, msg, signal) {
|
|
4009
|
+
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
4010
|
+
const messageId = msg.messageId;
|
|
4011
|
+
const packageId = msg.packageId;
|
|
4012
|
+
const projectId = msg.projectId;
|
|
4013
|
+
console.log(
|
|
4014
|
+
`[apm] webide-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
|
|
4015
|
+
);
|
|
4016
|
+
await updatePackageStatus(cfg, packageId, "RUNNING");
|
|
4017
|
+
const eventSession = new EventSession(msg.content);
|
|
4018
|
+
const logSyncRef = {
|
|
4019
|
+
current: createThrottledWebIdeMessageLogSync(
|
|
4020
|
+
cfg,
|
|
4021
|
+
{
|
|
4022
|
+
taskId: `package:${packageId}`,
|
|
4023
|
+
messageId,
|
|
4024
|
+
agentId: "webide-package"
|
|
4025
|
+
},
|
|
4026
|
+
(err) => {
|
|
4027
|
+
console.warn(
|
|
4028
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4029
|
+
err instanceof Error ? err.message : err
|
|
4030
|
+
);
|
|
4031
|
+
},
|
|
4032
|
+
{
|
|
4033
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4034
|
+
}
|
|
4035
|
+
)
|
|
4036
|
+
};
|
|
4037
|
+
const syncPrepLog = async () => {
|
|
4038
|
+
const sync = logSyncRef.current;
|
|
4039
|
+
if (!sync) return;
|
|
4040
|
+
sync.schedule(eventSession);
|
|
4041
|
+
await sync.flush(eventSession);
|
|
4042
|
+
};
|
|
4043
|
+
const runPrepStep = async (step, work, formatOk) => {
|
|
4044
|
+
eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
|
|
4045
|
+
await syncPrepLog();
|
|
4046
|
+
try {
|
|
4047
|
+
const result = await work();
|
|
4048
|
+
eventSession.addCliStep(step, formatOk(result), "ok");
|
|
4049
|
+
await syncPrepLog();
|
|
4050
|
+
return result;
|
|
4051
|
+
} catch (err) {
|
|
4052
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4053
|
+
eventSession.addCliStep(step, detail, "error");
|
|
4054
|
+
await syncPrepLog();
|
|
4055
|
+
throw err;
|
|
4056
|
+
}
|
|
4057
|
+
};
|
|
4058
|
+
try {
|
|
4059
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4060
|
+
await runPrepStep(
|
|
4061
|
+
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
4062
|
+
async () => {
|
|
4063
|
+
const { didInit } = await ensureWorkspaceInitialized(workdir);
|
|
4064
|
+
return didInit;
|
|
4065
|
+
},
|
|
4066
|
+
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|
|
4067
|
+
);
|
|
4068
|
+
const cliVersion = readCliVersion();
|
|
4069
|
+
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
4070
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4071
|
+
await runPrepStep(
|
|
4072
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
4073
|
+
async () => {
|
|
4074
|
+
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
4075
|
+
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
4076
|
+
return cliVersion;
|
|
4077
|
+
},
|
|
4078
|
+
(version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
|
|
4079
|
+
);
|
|
4080
|
+
} else {
|
|
4081
|
+
eventSession.addCliStep(
|
|
4082
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
4083
|
+
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
4084
|
+
"skip"
|
|
4085
|
+
);
|
|
4086
|
+
await syncPrepLog();
|
|
4087
|
+
}
|
|
4088
|
+
await runPrepStep(
|
|
4089
|
+
"\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
|
|
4090
|
+
async () => {
|
|
4091
|
+
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
4092
|
+
try {
|
|
4093
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
4094
|
+
} catch (err) {
|
|
4095
|
+
console.warn(
|
|
4096
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
4097
|
+
err instanceof Error ? err.message : err
|
|
4098
|
+
);
|
|
4099
|
+
}
|
|
4100
|
+
return workspaceRepos;
|
|
4101
|
+
},
|
|
4102
|
+
(repos) => `kind=${repos.kind} repos=${repos.repos.length}`
|
|
4103
|
+
);
|
|
4104
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4105
|
+
await runPrepStep(
|
|
4106
|
+
"\u5207\u6362\u57FA\u7EBF\u5206\u652F",
|
|
4107
|
+
async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
|
|
4108
|
+
(result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
|
|
4109
|
+
);
|
|
4110
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4111
|
+
await runPrepStep(
|
|
4112
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863",
|
|
4113
|
+
async () => {
|
|
4114
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
4115
|
+
projectId
|
|
4116
|
+
});
|
|
4117
|
+
return docs;
|
|
4118
|
+
},
|
|
4119
|
+
(result) => {
|
|
4120
|
+
if (!result.synced) {
|
|
4121
|
+
return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
|
|
4122
|
+
}
|
|
4123
|
+
return `downloaded=${result.downloaded} deleted=${result.deleted}`;
|
|
4124
|
+
}
|
|
4125
|
+
);
|
|
4126
|
+
eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
|
|
4127
|
+
await syncPrepLog();
|
|
4128
|
+
const artifactHint = expectedArtifactPath(msg.target, packageId);
|
|
4129
|
+
let failedByTool = false;
|
|
4130
|
+
const outcome = await runCursorAgent(
|
|
4131
|
+
cfg,
|
|
4132
|
+
{
|
|
4133
|
+
messageId,
|
|
4134
|
+
prompt: msg.content,
|
|
4135
|
+
model: msg.model,
|
|
4136
|
+
apiKey: msg.apiKey,
|
|
4137
|
+
workdir,
|
|
4138
|
+
user: msg.user || "webide"
|
|
4139
|
+
},
|
|
4140
|
+
{
|
|
4141
|
+
signal,
|
|
4142
|
+
forceSend: true,
|
|
4143
|
+
eventSession,
|
|
4144
|
+
enableAppendMessage: false,
|
|
4145
|
+
enableAskQuestion: false,
|
|
4146
|
+
enableWebIdePlanTools: false,
|
|
4147
|
+
enablePtySessionMcp: false,
|
|
4148
|
+
enableSandbox: false,
|
|
4149
|
+
enablePackageStatusTools: true,
|
|
4150
|
+
packageId,
|
|
4151
|
+
onPackageFailed: async () => {
|
|
4152
|
+
failedByTool = true;
|
|
4153
|
+
},
|
|
4154
|
+
taskId: `package:${packageId}`,
|
|
4155
|
+
createRemoteLogSync: (agentId) => {
|
|
4156
|
+
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
4157
|
+
cfg,
|
|
4158
|
+
{
|
|
4159
|
+
taskId: `package:${packageId}`,
|
|
4160
|
+
messageId,
|
|
4161
|
+
agentId
|
|
4162
|
+
},
|
|
4163
|
+
(err) => {
|
|
4164
|
+
console.warn(
|
|
4165
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4166
|
+
err instanceof Error ? err.message : err
|
|
4167
|
+
);
|
|
4168
|
+
},
|
|
4169
|
+
{
|
|
4170
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4171
|
+
}
|
|
4172
|
+
);
|
|
4173
|
+
return logSyncRef.current;
|
|
4174
|
+
},
|
|
4175
|
+
onRunStarted: async ({ agentId, runId }) => {
|
|
4176
|
+
eventSession.addCliStep(
|
|
4177
|
+
"\u542F\u52A8\u6253\u5305 Agent",
|
|
4178
|
+
`agentId=${agentId} runId=${runId}`,
|
|
4179
|
+
"ok"
|
|
4180
|
+
);
|
|
4181
|
+
logSyncRef.current?.schedule(eventSession);
|
|
4182
|
+
await logSyncRef.current?.markRun(runId, "running");
|
|
4183
|
+
await logSyncRef.current?.flush(eventSession);
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
);
|
|
4187
|
+
const tokenUsage = outcome.usage != null ? {
|
|
4188
|
+
modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
|
|
4189
|
+
inputTokens: outcome.usage.inputTokens,
|
|
4190
|
+
outputTokens: outcome.usage.outputTokens,
|
|
4191
|
+
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
4192
|
+
cacheWriteTokens: outcome.usage.cacheWriteTokens,
|
|
4193
|
+
totalTokens: outcome.usage.totalTokens,
|
|
4194
|
+
...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
|
|
4195
|
+
} : void 0;
|
|
4196
|
+
if (outcome.status === "error") {
|
|
4197
|
+
const detail = formatCursorRunFailure(outcome.runId, {
|
|
4198
|
+
resultText: outcome.result
|
|
4199
|
+
});
|
|
4200
|
+
await logSyncRef.current?.markRun(
|
|
4201
|
+
outcome.runId,
|
|
4202
|
+
"error",
|
|
4203
|
+
detail,
|
|
4204
|
+
tokenUsage
|
|
4205
|
+
);
|
|
4206
|
+
if (!failedByTool) {
|
|
4207
|
+
await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
|
|
4208
|
+
}
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
if (outcome.status === "cancelled" || signal.aborted) {
|
|
4212
|
+
await logSyncRef.current?.markRun(
|
|
4213
|
+
outcome.runId,
|
|
4214
|
+
"cancelled",
|
|
4215
|
+
"\u5DF2\u53D6\u6D88",
|
|
4216
|
+
tokenUsage
|
|
4217
|
+
);
|
|
4218
|
+
await updatePackageStatus(cfg, packageId, "CANCELLED", {
|
|
4219
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
4220
|
+
});
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
await logSyncRef.current?.markRun(
|
|
4224
|
+
outcome.runId,
|
|
4225
|
+
"finished",
|
|
4226
|
+
null,
|
|
4227
|
+
tokenUsage
|
|
4228
|
+
);
|
|
4229
|
+
if (failedByTool) {
|
|
4230
|
+
console.log(
|
|
4231
|
+
`[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
|
|
4232
|
+
);
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
await updatePackageStatus(cfg, packageId, "SUCCESS", {
|
|
4236
|
+
artifactPath: artifactHint
|
|
4237
|
+
});
|
|
4238
|
+
console.log(
|
|
4239
|
+
`[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
|
|
4240
|
+
);
|
|
4241
|
+
} catch (err) {
|
|
4242
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4243
|
+
console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
|
|
4244
|
+
try {
|
|
4245
|
+
if (signal.aborted) {
|
|
4246
|
+
await updatePackageStatus(cfg, packageId, "CANCELLED", {
|
|
4247
|
+
error: detail
|
|
4248
|
+
});
|
|
4249
|
+
} else {
|
|
4250
|
+
await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
|
|
4251
|
+
}
|
|
4252
|
+
} catch (statusErr) {
|
|
4253
|
+
console.error(
|
|
4254
|
+
"[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
|
|
4255
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
|
|
3859
4261
|
// src/commands/connect/webide-message-worker.ts
|
|
3860
4262
|
var controllers = /* @__PURE__ */ new Map();
|
|
3861
4263
|
async function runJob(cfg, msg) {
|
|
@@ -3864,6 +4266,8 @@ async function runJob(cfg, msg) {
|
|
|
3864
4266
|
try {
|
|
3865
4267
|
if (msg.type === "webide-start-project") {
|
|
3866
4268
|
await handleStartProject(cfg, msg, controller.signal);
|
|
4269
|
+
} else if (msg.type === "webide-package") {
|
|
4270
|
+
await handleWebIdePackage(cfg, msg, controller.signal);
|
|
3867
4271
|
} else {
|
|
3868
4272
|
await handleWebIdeInboundMessage(cfg, msg, controller.signal);
|
|
3869
4273
|
}
|
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 写入原因,再结束。
|