ai-project-manage-cli 4.0.18 → 4.0.19

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.
Files changed (2) hide show
  1. package/dist/index.js +209 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -374,7 +374,7 @@ async function runComment(requirementId, file, options) {
374
374
  }
375
375
 
376
376
  // src/commands/connect.ts
377
- import { randomUUID } from "crypto";
377
+ import { randomUUID as randomUUID2 } from "crypto";
378
378
  import WebSocket from "ws";
379
379
  import { Agent } from "@cursor/sdk";
380
380
  import { join as join6 } from "path";
@@ -781,11 +781,149 @@ async function runPull(requirementId, workspaceDir) {
781
781
  return WORKITEMS_DIR;
782
782
  }
783
783
 
784
+ // src/theater-job-report.ts
785
+ import { randomUUID } from "crypto";
786
+ function extractErrorMessage(err) {
787
+ if (err instanceof Error && err.message.trim()) {
788
+ return err.message.trim();
789
+ }
790
+ if (typeof err === "string" && err.trim()) {
791
+ return err.trim();
792
+ }
793
+ if (typeof err === "object" && err && "message" in err) {
794
+ const msg = err.message;
795
+ if (typeof msg === "string" && msg.trim()) return msg.trim();
796
+ if (Array.isArray(msg)) return msg.map(String).join("\uFF1B");
797
+ }
798
+ return "\u672A\u77E5\u9519\u8BEF";
799
+ }
800
+ function isUnauthorizedMessage(msg) {
801
+ return msg.includes("\u672A\u767B\u5F55") || /401/.test(msg);
802
+ }
803
+ function sendTheaterReportWs(ws, payload) {
804
+ return new Promise((resolve5, reject) => {
805
+ const msg_id = randomUUID();
806
+ const timer = setTimeout(() => {
807
+ ws.off("message", onMessage);
808
+ reject(new Error("WebSocket \u4E0A\u62A5\u8D85\u65F6"));
809
+ }, 3e4);
810
+ const onMessage = (data) => {
811
+ const text = typeof data === "string" ? data : data.toString();
812
+ let frame;
813
+ try {
814
+ frame = JSON.parse(text);
815
+ } catch {
816
+ return;
817
+ }
818
+ if (frame.msg_id !== msg_id) return;
819
+ clearTimeout(timer);
820
+ ws.off("message", onMessage);
821
+ if (frame.type === "ERROR") {
822
+ reject(
823
+ new Error(frame.payload?.message?.trim() || "WebSocket \u4E0A\u62A5\u5931\u8D25")
824
+ );
825
+ return;
826
+ }
827
+ if (frame.type === "ACK") {
828
+ resolve5();
829
+ return;
830
+ }
831
+ };
832
+ ws.on("message", onMessage);
833
+ ws.send(
834
+ JSON.stringify({
835
+ type: "THEATER_REPORT",
836
+ msg_id,
837
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
838
+ payload
839
+ })
840
+ );
841
+ });
842
+ }
843
+ async function reportTheaterProgress(api, ws, params) {
844
+ const body = {
845
+ sessionId: params.sessionId,
846
+ memberKey: params.memberKey,
847
+ status: "IN_PROGRESS",
848
+ logId: params.logId
849
+ };
850
+ try {
851
+ await api.theater.reportMemberAgentProgress(body);
852
+ } catch (err) {
853
+ const msg = extractErrorMessage(err);
854
+ if (!isUnauthorizedMessage(msg)) throw err;
855
+ await sendTheaterReportWs(ws, { action: "progress", ...body });
856
+ }
857
+ }
858
+ async function submitTheaterResult(api, ws, params) {
859
+ const body = {
860
+ sessionId: params.sessionId,
861
+ memberKey: params.memberKey,
862
+ content: params.content,
863
+ agentId: params.agentId ?? "",
864
+ agentStatus: params.agentStatus,
865
+ logId: params.logId
866
+ };
867
+ try {
868
+ await api.theater.submitMemberResponse(body);
869
+ } catch (err) {
870
+ const msg = extractErrorMessage(err);
871
+ if (!isUnauthorizedMessage(msg)) throw err;
872
+ await sendTheaterReportWs(ws, {
873
+ action: "submit",
874
+ sessionId: params.sessionId,
875
+ memberKey: params.memberKey,
876
+ content: params.content,
877
+ agentId: params.agentId,
878
+ agentStatus: params.agentStatus,
879
+ logId: params.logId
880
+ });
881
+ }
882
+ }
883
+ async function submitTheaterFailure(api, ws, params) {
884
+ const content = `[${params.stage}] ${params.error}`;
885
+ try {
886
+ await submitTheaterResult(api, ws, {
887
+ sessionId: params.sessionId,
888
+ memberKey: params.memberKey,
889
+ content,
890
+ agentId: params.agentId,
891
+ agentStatus: "FAILED",
892
+ logId: params.logId
893
+ });
894
+ return true;
895
+ } catch (submitErr) {
896
+ console.error(
897
+ "[apm] \u65E0\u6CD5\u5C06\u5931\u8D25\u72B6\u6001\u5199\u5165\u5267\u573A\u4F1A\u8BDD:",
898
+ extractErrorMessage(submitErr)
899
+ );
900
+ return false;
901
+ }
902
+ }
903
+ function logTheaterJobError(stage, err, meta) {
904
+ const msg = extractErrorMessage(err);
905
+ const ctx = [
906
+ meta?.sessionId ? `session=${meta.sessionId}` : "",
907
+ meta?.memberKey ? `member=${meta.memberKey}` : ""
908
+ ].filter(Boolean).join(" ");
909
+ console.error(
910
+ `[apm] \u5267\u573A THEATER_JOB \u5931\u8D25 \xB7 \u9636\u6BB5=${stage}${ctx ? ` \xB7 ${ctx}` : ""} \xB7 ${msg}`
911
+ );
912
+ if (isUnauthorizedMessage(msg)) {
913
+ console.error("[apm] \u63D0\u793A: \u8BF7\u6267\u884C apm login \u91CD\u65B0\u767B\u5F55\u540E\u518D\u8BD5");
914
+ }
915
+ }
916
+
784
917
  // src/commands/connect.ts
918
+ var DEFAULT_AGENT_MODEL_ID = "default";
919
+ function resolveAgentModelId(model) {
920
+ const id = model?.trim();
921
+ return id || DEFAULT_AGENT_MODEL_ID;
922
+ }
785
923
  async function createAgentForJob(payload) {
786
924
  const options = {
787
925
  apiKey: payload.apiKey,
788
- model: { id: payload.model ?? "default" },
926
+ model: { id: resolveAgentModelId(payload.model) },
789
927
  local: { cwd: payload.cwd }
790
928
  };
791
929
  if (payload.agentId) {
@@ -839,9 +977,10 @@ async function runAgentStream(payload, session, agent) {
839
977
  }
840
978
  return { agentId: run.agentId, failed, failReason };
841
979
  }
842
- async function handleTheaterJob(api, payload) {
980
+ async function handleTheaterJob(api, ws, payload) {
843
981
  const sessionId = payload.theaterSessionId.trim();
844
982
  const memberKey = payload.memberKey.trim();
983
+ const logId = payload.logId?.trim() || void 0;
845
984
  if (!sessionId) {
846
985
  throw new Error("THEATER_JOB \u7F3A\u5C11 theaterSessionId");
847
986
  }
@@ -849,36 +988,53 @@ async function handleTheaterJob(api, payload) {
849
988
  throw new Error("THEATER_JOB \u7F3A\u5C11 memberKey");
850
989
  }
851
990
  console.log("[apm] \u5267\u573A\u4F1A\u8BDD:", sessionId, "\u6210\u5458:", memberKey);
852
- await api.theater.reportMemberAgentProgress({
853
- sessionId,
854
- memberKey,
855
- status: "IN_PROGRESS",
856
- logId: payload.logId?.trim() || void 0
857
- });
858
- const eventSession = new EventSession(payload.prompt);
859
- const agent = await createAgentForJob(payload);
860
- const { agentId, failed, failReason } = await runAgentStream(
861
- payload,
862
- eventSession,
863
- agent
864
- );
865
- const assistantText = eventSession.getAssistantText();
866
- const content = failed ? failReason || assistantText || "\u672A\u77E5\u9519\u8BEF" : assistantText || "[Agent \u672A\u4EA7\u751F\u6587\u672C\u8F93\u51FA]";
867
- const result = await api.theater.submitMemberResponse({
868
- sessionId,
869
- memberKey,
870
- content,
871
- agentId,
872
- agentStatus: failed ? "FAILED" : "SUCCESS",
873
- logId: payload.logId?.trim() || void 0
874
- });
875
- console.log(
876
- "[apm] \u5267\u573A\u6210\u5458\u56DE\u590D\u5DF2\u63D0\u4EA4",
877
- failed ? "(\u542B\u5931\u8D25\u8BF4\u660E)" : "",
878
- result.clearedPending ? "\xB7 \u5DF2\u6E05\u9664\u5F85\u56DE\u590D" : ""
879
- );
880
- if (result.canAdvance) {
881
- console.log("[apm] \u53EF\u624B\u52A8\u63A8\u8FDB\u4E3B\u6301\u8F6E\u6B21\uFF08autoPlay \u5173\u95ED\uFF09");
991
+ let agentId = payload.agentId?.trim() || void 0;
992
+ try {
993
+ await reportTheaterProgress(api, ws, { sessionId, memberKey, logId });
994
+ } catch (err) {
995
+ logTheaterJobError("\u4E0A\u62A5\u6267\u884C\u8FDB\u5EA6", err, { sessionId, memberKey });
996
+ await submitTheaterFailure(api, ws, {
997
+ sessionId,
998
+ memberKey,
999
+ logId,
1000
+ agentId,
1001
+ stage: "\u4E0A\u62A5\u8FDB\u5EA6",
1002
+ error: extractErrorMessage(err)
1003
+ });
1004
+ return;
1005
+ }
1006
+ try {
1007
+ const eventSession = new EventSession(payload.prompt);
1008
+ const agent = await createAgentForJob(payload);
1009
+ const runResult = await runAgentStream(payload, eventSession, agent);
1010
+ agentId = runResult.agentId;
1011
+ const assistantText = eventSession.getAssistantText();
1012
+ const content = runResult.failed ? runResult.failReason || assistantText || "\u672A\u77E5\u9519\u8BEF" : assistantText || "[Agent \u672A\u4EA7\u751F\u6587\u672C\u8F93\u51FA]";
1013
+ await submitTheaterResult(api, ws, {
1014
+ sessionId,
1015
+ memberKey,
1016
+ content,
1017
+ agentId,
1018
+ agentStatus: runResult.failed ? "FAILED" : "SUCCESS",
1019
+ logId
1020
+ });
1021
+ console.log(
1022
+ "[apm] \u5267\u573A\u6210\u5458\u56DE\u590D\u5DF2\u63D0\u4EA4",
1023
+ runResult.failed ? "(\u6267\u884C\u5931\u8D25)" : ""
1024
+ );
1025
+ } catch (err) {
1026
+ logTheaterJobError("\u6267\u884C\u6216\u63D0\u4EA4\u56DE\u590D", err, { sessionId, memberKey });
1027
+ const reported = await submitTheaterFailure(api, ws, {
1028
+ sessionId,
1029
+ memberKey,
1030
+ logId,
1031
+ agentId,
1032
+ stage: "\u6267\u884C\u4EFB\u52A1",
1033
+ error: extractErrorMessage(err)
1034
+ });
1035
+ if (reported) {
1036
+ console.error("[apm] \u5DF2\u5C06\u5931\u8D25\u539F\u56E0\u5199\u5165\u5267\u573A\u4F1A\u8BDD\uFF0C\u53EF\u5728\u7F51\u9875\u67E5\u770B");
1037
+ }
882
1038
  }
883
1039
  }
884
1040
  async function handleWorkflowJob(api, payload) {
@@ -958,7 +1114,7 @@ function runConnect(opts) {
958
1114
  const sendHeartbeat = () => {
959
1115
  const frame = {
960
1116
  type: "HEARTBEAT",
961
- msg_id: randomUUID(),
1117
+ msg_id: randomUUID2(),
962
1118
  ts: (/* @__PURE__ */ new Date()).toISOString(),
963
1119
  payload: {}
964
1120
  };
@@ -970,22 +1126,31 @@ function runConnect(opts) {
970
1126
  });
971
1127
  ws.on("message", async (data) => {
972
1128
  const text = typeof data === "string" ? data : data.toString();
1129
+ let msg;
973
1130
  try {
974
- const msg = JSON.parse(text);
975
- if (msg.type === "THEATER_JOB") {
976
- await handleTheaterJob(api, msg.payload);
977
- return;
978
- }
979
- if (msg.type === "JOB") {
980
- await handleWorkflowJob(api, msg.payload);
981
- return;
982
- }
1131
+ msg = JSON.parse(text);
983
1132
  } catch (err) {
984
1133
  console.error(
985
1134
  "[apm] \u65E0\u6CD5\u89E3\u6790 WebSocket \u6D88\u606F:",
986
1135
  text,
987
1136
  err.message
988
1137
  );
1138
+ return;
1139
+ }
1140
+ try {
1141
+ const jobType = msg.type;
1142
+ if (jobType === "THEATER_JOB") {
1143
+ await handleTheaterJob(api, ws, msg.payload);
1144
+ return;
1145
+ }
1146
+ if (jobType === "JOB") {
1147
+ await handleWorkflowJob(api, msg.payload);
1148
+ return;
1149
+ }
1150
+ } catch (err) {
1151
+ const jobType = msg.type;
1152
+ const label = jobType === "THEATER_JOB" ? "\u5267\u573A THEATER_JOB" : jobType === "JOB" ? "\u5DE5\u4F5C\u6D41 JOB" : String(jobType);
1153
+ console.error(`[apm] ${label} \u5904\u7406\u5931\u8D25:`, extractErrorMessage(err));
989
1154
  }
990
1155
  });
991
1156
  ws.on("error", (err) => {
@@ -2044,7 +2209,7 @@ function loadApmDotEnvIfPresent() {
2044
2209
 
2045
2210
  // src/commands/deploy/internal/minio.ts
2046
2211
  import { statSync as statSync5 } from "node:fs";
2047
- import { readdir } from "node:fs/promises";
2212
+ import { readdir, readFile } from "node:fs/promises";
2048
2213
  import path6 from "node:path";
2049
2214
  import * as Minio from "minio";
2050
2215
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
@@ -2094,7 +2259,6 @@ async function collectFiles(root) {
2094
2259
  return out;
2095
2260
  }
2096
2261
  async function readArtifactFile(absPath) {
2097
- const { readFile } = await import("node:fs/promises");
2098
2262
  return readFile(absPath);
2099
2263
  }
2100
2264
  function toMB(bytes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "4.0.18",
3
+ "version": "4.0.19",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,