ai-project-manage-cli 7.1.26 → 7.1.28

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 CHANGED
@@ -314,6 +314,10 @@ var init_request_config = __esm({
314
314
  });
315
315
 
316
316
  // src/api/client.ts
317
+ var client_exports = {};
318
+ __export(client_exports, {
319
+ createApmApiClient: () => createApmApiClient
320
+ });
317
321
  import { createApiClient } from "listpage-http";
318
322
  function createApmApiClient(cfg) {
319
323
  const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
@@ -7424,6 +7428,17 @@ var EventSession = class {
7424
7428
  });
7425
7429
  this.markDirty(this.events.length - 1);
7426
7430
  }
7431
+ /** CLI 本地准备步骤(工作区/分支/文档等),写入日志供 WebIDE 查看 */
7432
+ addCliStep(step, text, status = "ok") {
7433
+ this.events.push({
7434
+ type: "cli_step",
7435
+ step,
7436
+ status,
7437
+ text,
7438
+ at: (/* @__PURE__ */ new Date()).toISOString()
7439
+ });
7440
+ this.markDirty(this.events.length - 1);
7441
+ }
7427
7442
  formatEvent(event) {
7428
7443
  switch (event.type) {
7429
7444
  case "assistant": {
@@ -8351,7 +8366,7 @@ async function runCursorAgent(cfg, ctx, options) {
8351
8366
  customTools,
8352
8367
  enableSandbox
8353
8368
  });
8354
- const eventSession = new EventSession(prompt);
8369
+ const eventSession = options?.eventSession ?? new EventSession(prompt);
8355
8370
  const syncRemoteLog = options?.createRemoteLogSync ? options.createRemoteLogSync(agent.agentId) : options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorMessageLogSync(
8356
8371
  cfg,
8357
8372
  logCtx(ctx, agent.agentId),
@@ -8630,10 +8645,27 @@ function spawnWebIdeMessageWorker(cfg, msg) {
8630
8645
  workerData: { cfg, msg }
8631
8646
  });
8632
8647
  const registry = getWebIdeTerminalRegistry(cfg);
8648
+ let settled = false;
8633
8649
  let resolveDone;
8634
8650
  const done = new Promise((resolve8) => {
8635
8651
  resolveDone = resolve8;
8636
8652
  });
8653
+ const failIfStillQueued = async (reason) => {
8654
+ if (settled) return;
8655
+ try {
8656
+ const { createApmApiClient: createApmApiClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
8657
+ const api = createApmApiClient2(cfg);
8658
+ await api.cli.webideSetMessageError({
8659
+ id: msg.messageId,
8660
+ error: reason
8661
+ });
8662
+ } catch (err) {
8663
+ console.error(
8664
+ `[apm] webide worker \u5931\u8D25\u5199\u7EC8\u6001\u5931\u8D25 messageId=${msg.messageId}:`,
8665
+ err instanceof Error ? err.message : err
8666
+ );
8667
+ }
8668
+ };
8637
8669
  worker.on("message", (m) => {
8638
8670
  if (m?.type === "terminal-rpc") {
8639
8671
  const rpc = m;
@@ -8693,6 +8725,7 @@ function spawnWebIdeMessageWorker(cfg, msg) {
8693
8725
  return;
8694
8726
  }
8695
8727
  if (m?.type === "done" || m?.type === "error") {
8728
+ settled = true;
8696
8729
  if (m.type === "error") {
8697
8730
  console.error(
8698
8731
  `[apm] webide worker error messageId=${m.messageId}: ${m.error}`
@@ -8703,9 +8736,18 @@ function spawnWebIdeMessageWorker(cfg, msg) {
8703
8736
  });
8704
8737
  worker.on("error", (err) => {
8705
8738
  console.error("[apm] webide worker crashed:", err);
8706
- resolveDone();
8739
+ void failIfStillQueued(
8740
+ `WebIDE worker \u5D29\u6E83: ${err instanceof Error ? err.message : String(err)}`
8741
+ ).finally(() => resolveDone());
8707
8742
  });
8708
8743
  worker.on("exit", (code) => {
8744
+ if (!settled && code !== 0) {
8745
+ console.error(`[apm] webide worker exit code=${code}`);
8746
+ void failIfStillQueued(`WebIDE worker \u5F02\u5E38\u9000\u51FA code=${code}`).finally(
8747
+ () => resolveDone()
8748
+ );
8749
+ return;
8750
+ }
8709
8751
  if (code !== 0) {
8710
8752
  console.error(`[apm] webide worker exit code=${code}`);
8711
8753
  }
@@ -9012,6 +9054,24 @@ function attachWsHandlers(ws, ctx, onOpen) {
9012
9054
  if (pendingCancels.has(msg2.messageId)) {
9013
9055
  activeRuns.delete(msg2.messageId);
9014
9056
  pendingCancels.delete(msg2.messageId);
9057
+ const cancelTask = (async () => {
9058
+ try {
9059
+ const api = createApmApiClient(cfg);
9060
+ await api.cli.webideUpdateMessageStatus({
9061
+ id: msg2.messageId,
9062
+ status: "CANCELLED"
9063
+ });
9064
+ } catch (err) {
9065
+ console.error(
9066
+ `[apm] webide-message \u9884\u53D6\u6D88\u5199\u7EC8\u6001\u5931\u8D25 messageId=${msg2.messageId}:`,
9067
+ err instanceof Error ? err.message : err
9068
+ );
9069
+ }
9070
+ })();
9071
+ activeTasks.add(cancelTask);
9072
+ void cancelTask.finally(() => {
9073
+ activeTasks.delete(cancelTask);
9074
+ });
9015
9075
  return;
9016
9076
  }
9017
9077
  const task2 = (async () => {
@@ -1362,6 +1362,17 @@ var EventSession = class {
1362
1362
  });
1363
1363
  this.markDirty(this.events.length - 1);
1364
1364
  }
1365
+ /** CLI 本地准备步骤(工作区/分支/文档等),写入日志供 WebIDE 查看 */
1366
+ addCliStep(step, text, status = "ok") {
1367
+ this.events.push({
1368
+ type: "cli_step",
1369
+ step,
1370
+ status,
1371
+ text,
1372
+ at: (/* @__PURE__ */ new Date()).toISOString()
1373
+ });
1374
+ this.markDirty(this.events.length - 1);
1375
+ }
1365
1376
  formatEvent(event) {
1366
1377
  switch (event.type) {
1367
1378
  case "assistant": {
@@ -2360,7 +2371,7 @@ async function runCursorAgent(cfg, ctx, options) {
2360
2371
  customTools,
2361
2372
  enableSandbox
2362
2373
  });
2363
- const eventSession = new EventSession(prompt);
2374
+ const eventSession = options?.eventSession ?? new EventSession(prompt);
2364
2375
  const syncRemoteLog = options?.createRemoteLogSync ? options.createRemoteLogSync(agent.agentId) : options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorMessageLogSync(
2365
2376
  cfg,
2366
2377
  logCtx(ctx, agent.agentId),
@@ -2707,13 +2718,14 @@ async function upsertLogHeader(cfg, ctx, patch) {
2707
2718
  ...patch
2708
2719
  });
2709
2720
  }
2710
- function createThrottledWebIdeMessageLogSync(cfg, ctx, onError) {
2721
+ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2711
2722
  let lastRunAt = 0;
2712
2723
  let timer;
2713
2724
  let latestSession;
2714
2725
  let syncChain = Promise.resolve();
2715
2726
  let minio;
2716
2727
  let bucket = "";
2728
+ let firstLogNotified = false;
2717
2729
  const ensureMinio = async () => {
2718
2730
  if (minio) return;
2719
2731
  const storage = await fetchDeployArtifactStorage(createApmApiClient(cfg));
@@ -2727,6 +2739,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError) {
2727
2739
  bucket = storage.bucket;
2728
2740
  await minio.ensureBucket(bucket);
2729
2741
  };
2742
+ const notifyFirstLog = async () => {
2743
+ if (firstLogNotified) return;
2744
+ firstLogNotified = true;
2745
+ try {
2746
+ await options?.onFirstLogSynced?.();
2747
+ } catch (err) {
2748
+ firstLogNotified = false;
2749
+ onError(err);
2750
+ }
2751
+ };
2730
2752
  const syncDirtyEventsOnce = async (session) => {
2731
2753
  const events = session.getDirtyEvents();
2732
2754
  if (events.length === 0) return;
@@ -2739,6 +2761,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError) {
2739
2761
  eventCount: session.getEventCount()
2740
2762
  });
2741
2763
  session.clearDirtyIfUnchanged(events);
2764
+ await notifyFirstLog();
2742
2765
  } catch (err) {
2743
2766
  onError(err);
2744
2767
  }
@@ -3768,98 +3791,189 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
3768
3791
  `[apm] webide-message action=${msg.action} taskId=${taskId} messageId=${messageId}`
3769
3792
  );
3770
3793
  await updateStatus(cfg, messageId, "TYPING");
3794
+ const eventSession = new EventSession(msg.content);
3795
+ const prepAgentId = loadWebIdeAgentId(workdir, taskId) ?? "webide-cli-prep";
3796
+ const logSyncRef = {
3797
+ current: createThrottledWebIdeMessageLogSync(
3798
+ cfg,
3799
+ { taskId, messageId, agentId: prepAgentId },
3800
+ (err) => {
3801
+ console.warn(
3802
+ "[apm] WebIDE \u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
3803
+ err instanceof Error ? err.message : err
3804
+ );
3805
+ }
3806
+ )
3807
+ };
3808
+ const syncPrepLog = async () => {
3809
+ const sync = logSyncRef.current;
3810
+ if (!sync) return;
3811
+ sync.schedule(eventSession);
3812
+ await sync.flush(eventSession);
3813
+ };
3814
+ const runPrepStep = async (step, work, formatOk) => {
3815
+ eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
3816
+ await syncPrepLog();
3817
+ try {
3818
+ const result = await work();
3819
+ eventSession.addCliStep(step, formatOk(result), "ok");
3820
+ await syncPrepLog();
3821
+ return result;
3822
+ } catch (err) {
3823
+ const detail = err instanceof Error ? err.message : String(err);
3824
+ eventSession.addCliStep(step, detail, "error");
3825
+ await syncPrepLog();
3826
+ throw err;
3827
+ }
3828
+ };
3771
3829
  try {
3772
3830
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3773
- const { didInit } = await ensureWorkspaceInitialized(workdir);
3774
- if (!didInit) {
3775
- assertApmGitignoredInRepo(workdir);
3776
- }
3831
+ await runPrepStep(
3832
+ "\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
3833
+ async () => {
3834
+ const { didInit } = await ensureWorkspaceInitialized(workdir);
3835
+ if (!didInit) {
3836
+ assertApmGitignoredInRepo(workdir);
3837
+ }
3838
+ return didInit;
3839
+ },
3840
+ (didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
3841
+ );
3777
3842
  const cliVersion = readCliVersion();
3778
3843
  if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
3779
3844
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3780
- console.log(
3781
- `[apm] CLI \u7248\u672C ${cliVersion} \u4E0E\u5DE5\u4F5C\u533A\u8BB0\u5F55\u4E0D\u4E00\u81F4\uFF0C\u6267\u884C update-skills`
3845
+ await runPrepStep(
3846
+ "\u540C\u6B65 Skills",
3847
+ async () => {
3848
+ await syncWorkspaceSkills(cfg, workdir);
3849
+ markSkillsSyncedForCliVersion(workdir, cliVersion);
3850
+ return cliVersion;
3851
+ },
3852
+ (version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
3782
3853
  );
3783
- await syncWorkspaceSkills(cfg, workdir);
3784
- markSkillsSyncedForCliVersion(workdir, cliVersion);
3785
- }
3786
- const workspaceRepos = resolveWorkspaceRepos(workdir);
3787
- try {
3788
- await enrichWorkspaceReposRemoteUrls(workspaceRepos);
3789
- } catch (err) {
3790
- console.warn(
3791
- "[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
3792
- err instanceof Error ? err.message : err
3854
+ } else {
3855
+ eventSession.addCliStep(
3856
+ "\u540C\u6B65 Skills",
3857
+ `\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
3858
+ "skip"
3793
3859
  );
3860
+ await syncPrepLog();
3794
3861
  }
3862
+ await runPrepStep(
3863
+ "\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
3864
+ async () => {
3865
+ const workspaceRepos = resolveWorkspaceRepos(workdir);
3866
+ try {
3867
+ await enrichWorkspaceReposRemoteUrls(workspaceRepos);
3868
+ } catch (err) {
3869
+ console.warn(
3870
+ "[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
3871
+ err instanceof Error ? err.message : err
3872
+ );
3873
+ }
3874
+ return workspaceRepos;
3875
+ },
3876
+ (repos) => `kind=${repos.kind} repos=${repos.repos.length}`
3877
+ );
3795
3878
  if (shouldRunBranch(branchCacheKey, workdir)) {
3796
3879
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3797
- const branchResult = await runTaskBranch(taskId, { cwd: workdir });
3798
- markBranchDone(branchCacheKey, workdir);
3799
- console.log(
3800
- `[apm] webide branch ready kind=${branchResult.kind} branch=${branchResult.branch} repos=${branchResult.repos.length}`
3880
+ await runPrepStep(
3881
+ "\u51C6\u5907\u7279\u6027\u5206\u652F",
3882
+ async () => runTaskBranch(taskId, { cwd: workdir }),
3883
+ (branchResult) => {
3884
+ markBranchDone(branchCacheKey, workdir);
3885
+ return `kind=${branchResult.kind} branch=${branchResult.branch} repos=${branchResult.repos.length}`;
3886
+ }
3801
3887
  );
3802
3888
  } else {
3803
- console.log(
3804
- `[apm] step=branch skipped taskId=${taskId} workdir=${workdir}`
3805
- );
3889
+ eventSession.addCliStep("\u51C6\u5907\u7279\u6027\u5206\u652F", "\u5DF2\u7F13\u5B58\uFF0C\u8DF3\u8FC7", "skip");
3890
+ await syncPrepLog();
3806
3891
  }
3807
3892
  if (shouldEnsureWebIdePullRequests(msg.action)) {
3808
3893
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3809
- const pushed = await pushWorkspaceRepos(workdir);
3810
- console.log(
3811
- `[apm] webide \u6D4B\u8BD5\u524D push action=${msg.action} repos=${pushed}`
3894
+ await runPrepStep(
3895
+ "\u6D4B\u8BD5\u524D\u63A8\u9001",
3896
+ async () => pushWorkspaceRepos(workdir),
3897
+ (pushed) => `\u5DF2 push ${pushed} \u4E2A\u4ED3\u5E93`
3812
3898
  );
3813
3899
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3814
- await ensureWebIdePullRequests(cfg, taskId, workdir);
3900
+ await runPrepStep(
3901
+ "\u786E\u4FDD PR",
3902
+ async () => {
3903
+ await ensureWebIdePullRequests(cfg, taskId, workdir);
3904
+ return true;
3905
+ },
3906
+ () => "PR \u68C0\u67E5\u5B8C\u6210"
3907
+ );
3815
3908
  }
3816
3909
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
3817
3910
  const api = createApmApiClient(cfg);
3818
3911
  let projectId = null;
3819
- try {
3820
- const baseline = await api.cli.projectBaseBranch({ taskId });
3821
- projectId = baseline.projectId?.trim() || null;
3822
- if (projectId) {
3823
- const docs = await syncProjectDocumentsPull(workdir, void 0, {
3824
- projectId
3825
- });
3826
- if (docs.synced) {
3827
- console.log(
3828
- `[apm] webide \u9879\u76EE\u6587\u6863\u5DF2\u5C31\u7EEA projectId=${projectId} downloaded=${docs.downloaded} deleted=${docs.deleted}`
3912
+ await runPrepStep(
3913
+ "\u540C\u6B65\u9879\u76EE\u6587\u6863",
3914
+ async () => {
3915
+ try {
3916
+ const baseline = await api.cli.projectBaseBranch({ taskId });
3917
+ projectId = baseline.projectId?.trim() || null;
3918
+ if (!projectId) {
3919
+ return { synced: false, reason: "\u65E0 projectId" };
3920
+ }
3921
+ const docs = await syncProjectDocumentsPull(workdir, void 0, {
3922
+ projectId
3923
+ });
3924
+ return {
3925
+ synced: docs.synced,
3926
+ downloaded: docs.downloaded,
3927
+ deleted: docs.deleted
3928
+ };
3929
+ } catch (err) {
3930
+ console.warn(
3931
+ "[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
3932
+ err instanceof Error ? err.message : err
3829
3933
  );
3934
+ return {
3935
+ synced: false,
3936
+ reason: err instanceof Error ? err.message : String(err)
3937
+ };
3830
3938
  }
3831
- } else {
3832
- console.warn(
3833
- `[apm] WebIDE \u65E0\u6CD5\u89E3\u6790\u4EFB\u52A1\u6240\u5C5E\u9879\u76EE\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65 taskId=${taskId}`
3834
- );
3835
- }
3836
- } catch (err) {
3837
- console.warn(
3838
- "[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
3839
- err instanceof Error ? err.message : err
3840
- );
3841
- }
3842
- try {
3843
- const attachments = await api.cli.webideListAttachments({ taskId });
3844
- const names = await syncWebIdeAttachments(
3845
- cfg,
3846
- taskId,
3847
- workdir,
3848
- attachments ?? []
3849
- );
3850
- if (names.length > 0) {
3851
- console.log(
3852
- `[apm] webide \u9644\u4EF6\u5DF2\u5C31\u7EEA count=${names.length} dir=.apm/webide/${taskId}/attachments`
3853
- );
3939
+ },
3940
+ (result) => {
3941
+ if ("reason" in result && result.reason) {
3942
+ return `\u8DF3\u8FC7\uFF1A${result.reason}`;
3943
+ }
3944
+ if (!result.synced) return "\u65E0\u9700\u540C\u6B65";
3945
+ return `downloaded=${result.downloaded ?? 0} deleted=${result.deleted ?? 0}`;
3854
3946
  }
3855
- } catch (err) {
3856
- console.warn(
3857
- "[apm] WebIDE \u9644\u4EF6\u540C\u6B65\u5931\u8D25:",
3858
- err instanceof Error ? err.message : err
3859
- );
3860
- }
3947
+ );
3948
+ await runPrepStep(
3949
+ "\u540C\u6B65\u4EFB\u52A1\u9644\u4EF6",
3950
+ async () => {
3951
+ try {
3952
+ const attachments = await api.cli.webideListAttachments({ taskId });
3953
+ const names = await syncWebIdeAttachments(
3954
+ cfg,
3955
+ taskId,
3956
+ workdir,
3957
+ attachments ?? []
3958
+ );
3959
+ return names;
3960
+ } catch (err) {
3961
+ console.warn(
3962
+ "[apm] WebIDE \u9644\u4EF6\u540C\u6B65\u5931\u8D25:",
3963
+ err instanceof Error ? err.message : err
3964
+ );
3965
+ return [];
3966
+ }
3967
+ },
3968
+ (names) => names.length > 0 ? `\u5DF2\u540C\u6B65 ${names.length} \u4E2A\u9644\u4EF6` : "\u65E0\u9644\u4EF6\u6216\u540C\u6B65\u8DF3\u8FC7"
3969
+ );
3970
+ eventSession.addCliStep(
3971
+ "\u542F\u52A8 Cursor Agent",
3972
+ "\u51C6\u5907 resume/create\u2026",
3973
+ "running"
3974
+ );
3975
+ await syncPrepLog();
3861
3976
  const savedAgentId = loadWebIdeAgentId(workdir, taskId);
3862
- const logSyncRef = { current: null };
3863
3977
  const outcome = await runCursorAgent(
3864
3978
  cfg,
3865
3979
  {
@@ -3874,6 +3988,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
3874
3988
  },
3875
3989
  {
3876
3990
  signal,
3991
+ forceSend: true,
3992
+ eventSession,
3877
3993
  appendMessageContent: (content) => appendContent(cfg, messageId, content),
3878
3994
  askQuestionExecute: createWebIdeAskQuestionExecute({
3879
3995
  cfg,
@@ -3881,7 +3997,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
3881
3997
  signal
3882
3998
  }),
3883
3999
  enableWebIdePlanTools: true,
3884
- // 本地 SDK sandbox 会拦截 custom-user-tools MCP,WebIDE 先关闭
3885
4000
  enableSandbox: false,
3886
4001
  taskId,
3887
4002
  terminalRpc: options?.terminalRpc,
@@ -3901,7 +4016,14 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
3901
4016
  },
3902
4017
  onRunStarted: async ({ agentId, runId }) => {
3903
4018
  saveWebIdeAgentId(workdir, taskId, agentId);
4019
+ eventSession.addCliStep(
4020
+ "\u542F\u52A8 Cursor Agent",
4021
+ `agentId=${agentId} runId=${runId}`,
4022
+ "ok"
4023
+ );
4024
+ logSyncRef.current?.schedule(eventSession);
3904
4025
  await logSyncRef.current?.markRun(runId, "running");
4026
+ await logSyncRef.current?.flush(eventSession);
3905
4027
  }
3906
4028
  }
3907
4029
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "7.1.26",
3
+ "version": "7.1.28",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,