ai-project-manage-cli 7.1.12 → 7.1.15

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
@@ -1,15 +1,23 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res, err) => function __init() {
5
+ if (err) throw err[0];
6
+ try {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ } catch (e) {
9
+ throw err = [e], e;
10
+ }
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
5
16
 
6
17
  // src/config.ts
7
18
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
8
19
  import { homedir } from "os";
9
20
  import { join } from "path";
10
- var APM_CONFIG_DIR = join(homedir(), ".config", "apm");
11
- var APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
12
- var DEFAULT_BASE_URL = "http://127.0.0.1:3000";
13
21
  function resolveClientMachineId(cfg) {
14
22
  return (cfg.clientMachineId ?? cfg.userId ?? "").trim();
15
23
  }
@@ -78,12 +86,1018 @@ function buildAgentWsUrl(httpBase, apiKey) {
78
86
  const q = new URLSearchParams({ apiKey });
79
87
  return `${origin}/ws/agent?${q.toString()}`;
80
88
  }
89
+ var APM_CONFIG_DIR, APM_CONFIG_PATH, DEFAULT_BASE_URL;
90
+ var init_config = __esm({
91
+ "src/config.ts"() {
92
+ "use strict";
93
+ APM_CONFIG_DIR = join(homedir(), ".config", "apm");
94
+ APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
95
+ DEFAULT_BASE_URL = "http://127.0.0.1:3000";
96
+ }
97
+ });
98
+
99
+ // src/api/request-config.ts
100
+ import { defineEndpoint } from "listpage-http";
101
+ var requestConfig;
102
+ var init_request_config = __esm({
103
+ "src/api/request-config.ts"() {
104
+ "use strict";
105
+ requestConfig = {
106
+ cli: {
107
+ me: defineEndpoint(
108
+ {
109
+ method: "GET",
110
+ path: "/cli/me"
111
+ }
112
+ ),
113
+ sessionDetail: defineEndpoint({
114
+ method: "GET",
115
+ path: "/cli/sessions/detail"
116
+ }),
117
+ sessionMembers: defineEndpoint({
118
+ method: "GET",
119
+ path: "/cli/sessions/members"
120
+ }),
121
+ listSessionMessages: defineEndpoint({
122
+ method: "GET",
123
+ path: "/cli/messages"
124
+ }),
125
+ listDocuments: defineEndpoint({
126
+ method: "GET",
127
+ path: "/cli/documents"
128
+ }),
129
+ getDevGate: defineEndpoint({
130
+ method: "GET",
131
+ path: "/cli/dev-gate"
132
+ }),
133
+ listAttachments: defineEndpoint(
134
+ {
135
+ method: "GET",
136
+ path: "/cli/attachments"
137
+ }
138
+ ),
139
+ upsertDocument: defineEndpoint({
140
+ method: "PUT",
141
+ path: "/cli/documents/upsert"
142
+ }),
143
+ appendMessageContent: defineEndpoint({
144
+ method: "PUT",
145
+ path: "/cli/messages/content"
146
+ }),
147
+ setMessageError: defineEndpoint({
148
+ method: "PUT",
149
+ path: "/cli/messages/error"
150
+ }),
151
+ upsertCursorMessageLog: defineEndpoint({
152
+ method: "PUT",
153
+ path: "/cli/cursor-message-logs"
154
+ }),
155
+ updateMessageStatus: defineEndpoint({
156
+ method: "PUT",
157
+ path: "/cli/messages/status"
158
+ }),
159
+ webideAppendMessageContent: defineEndpoint({
160
+ method: "PUT",
161
+ path: "/cli/webide/messages/content"
162
+ }),
163
+ webideEnsureMessageContent: defineEndpoint({
164
+ method: "PUT",
165
+ path: "/cli/webide/messages/ensure-content"
166
+ }),
167
+ webideSetMessageError: defineEndpoint({
168
+ method: "PUT",
169
+ path: "/cli/webide/messages/error"
170
+ }),
171
+ webideUpdateMessageStatus: defineEndpoint({
172
+ method: "PUT",
173
+ path: "/cli/webide/messages/status"
174
+ }),
175
+ webideUpsertMessageLog: defineEndpoint({
176
+ method: "PUT",
177
+ path: "/cli/webide/message-logs"
178
+ }),
179
+ webideReplaceAssumptions: defineEndpoint({
180
+ method: "PUT",
181
+ path: "/cli/webide/assumptions"
182
+ }),
183
+ webideListAssumptions: defineEndpoint({
184
+ method: "GET",
185
+ path: "/cli/webide/assumptions"
186
+ }),
187
+ webideRecommendPlan: defineEndpoint({
188
+ method: "PUT",
189
+ path: "/cli/webide/plan-recommendation"
190
+ }),
191
+ webideUpsertPlan: defineEndpoint({
192
+ method: "PUT",
193
+ path: "/cli/webide/plan"
194
+ }),
195
+ webideReplaceTestCases: defineEndpoint({
196
+ method: "PUT",
197
+ path: "/cli/webide/test-cases"
198
+ }),
199
+ webideListTerminals: defineEndpoint({
200
+ method: "GET",
201
+ path: "/cli/webide/terminals"
202
+ }),
203
+ webideUpsertTerminal: defineEndpoint({
204
+ method: "POST",
205
+ path: "/cli/webide/terminals"
206
+ }),
207
+ webideUpdateTerminalStatus: defineEndpoint({
208
+ method: "PUT",
209
+ path: "/cli/webide/terminals/status"
210
+ }),
211
+ webideUpdateTerminalMeta: defineEndpoint({
212
+ method: "PUT",
213
+ path: "/cli/webide/terminals/meta"
214
+ }),
215
+ acquireSteelBrowser: defineEndpoint({
216
+ method: "POST",
217
+ path: "/cli/steel-browser/acquire"
218
+ }),
219
+ branchBaseline: defineEndpoint({
220
+ method: "GET",
221
+ path: "/cli/tasks/branch-baseline"
222
+ }),
223
+ projectBaseBranch: defineEndpoint({
224
+ method: "GET",
225
+ path: "/cli/tasks/project-base-branch"
226
+ }),
227
+ listSessionsForBranchCleanup: defineEndpoint({
228
+ method: "GET",
229
+ path: "/cli/sessions/branch-cleanup"
230
+ }),
231
+ workspaceBaseline: defineEndpoint({
232
+ method: "GET",
233
+ path: "/cli/workspaces/baseline"
234
+ }),
235
+ getDeploymentConfiguration: defineEndpoint({
236
+ method: "GET",
237
+ path: "/cli/deployment-configurations"
238
+ }),
239
+ matchRepository: defineEndpoint({
240
+ method: "GET",
241
+ path: "/cli/repositories/match"
242
+ }),
243
+ listSkills: defineEndpoint({
244
+ method: "GET",
245
+ path: "/cli/skills"
246
+ }),
247
+ listRules: defineEndpoint({
248
+ method: "GET",
249
+ path: "/cli/rules"
250
+ }),
251
+ createPullRequest: defineEndpoint({
252
+ method: "POST",
253
+ path: "/cli/pull-requests"
254
+ }),
255
+ createWebIdeDraftPullRequest: defineEndpoint({
256
+ method: "POST",
257
+ path: "/cli/webide/draft-pull-requests"
258
+ }),
259
+ webideMergePullRequests: defineEndpoint({
260
+ method: "POST",
261
+ path: "/cli/webide/pull-requests/merge"
262
+ }),
263
+ getProjectDocumentManifest: defineEndpoint({
264
+ method: "GET",
265
+ path: "/cli/project-documents/manifest"
266
+ }),
267
+ listProjectDocuments: defineEndpoint({
268
+ method: "GET",
269
+ path: "/cli/project-documents"
270
+ }),
271
+ upsertProjectDocument: defineEndpoint({
272
+ method: "PUT",
273
+ path: "/cli/project-documents/upsert"
274
+ }),
275
+ removeProjectDocument: defineEndpoint({
276
+ method: "DELETE",
277
+ path: "/cli/project-documents"
278
+ }),
279
+ createTaskDeployment: defineEndpoint({
280
+ method: "POST",
281
+ path: "/cli/task-deployments"
282
+ }),
283
+ updateTaskDeploymentStatus: defineEndpoint({
284
+ method: "PUT",
285
+ path: "/cli/task-deployments/status"
286
+ }),
287
+ syncTaskDeploymentLog: defineEndpoint({
288
+ method: "PUT",
289
+ path: "/cli/task-deployments/log"
290
+ }),
291
+ completeTaskDeployment: defineEndpoint({
292
+ method: "PUT",
293
+ path: "/cli/task-deployments/complete"
294
+ }),
295
+ getDeployArtifactStorage: defineEndpoint(
296
+ {
297
+ method: "GET",
298
+ path: "/cli/deploy-artifact-storage"
299
+ }
300
+ ),
301
+ attachTaskDeploymentArtifact: defineEndpoint({
302
+ method: "PUT",
303
+ path: "/cli/task-deployments/artifact"
304
+ })
305
+ }
306
+ };
307
+ }
308
+ });
309
+
310
+ // src/api/client.ts
311
+ import { createApiClient } from "listpage-http";
312
+ function createApmApiClient(cfg) {
313
+ const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
314
+ return createApiClient(requestConfig, {
315
+ baseURL,
316
+ getToken: () => resolveApiKey(cfg) || void 0,
317
+ successCodes: [0],
318
+ unauthorizedCodes: [401]
319
+ });
320
+ }
321
+ var init_client = __esm({
322
+ "src/api/client.ts"() {
323
+ "use strict";
324
+ init_request_config();
325
+ init_config();
326
+ }
327
+ });
328
+
329
+ // src/commands/deploy/internal/minio.ts
330
+ import { statSync as statSync5 } from "node:fs";
331
+ import { readdir, readFile } from "node:fs/promises";
332
+ import path from "node:path";
333
+ import * as Minio from "minio";
334
+ async function isDirectoryPath(dir) {
335
+ try {
336
+ const st = statSync5(dir);
337
+ return st.isDirectory();
338
+ } catch {
339
+ return false;
340
+ }
341
+ }
342
+ function sanitizeRelativePath(rel) {
343
+ const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
344
+ const segments = norm.split("/").filter(Boolean);
345
+ for (const s of segments) {
346
+ if (s === "." || s === "..") {
347
+ throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
348
+ }
349
+ }
350
+ return segments.join("/");
351
+ }
352
+ async function collectFiles(root) {
353
+ const out = [];
354
+ async function walk(dir, prefix) {
355
+ const entries = await readdir(dir, { withFileTypes: true });
356
+ for (const e of entries) {
357
+ const name = e.name;
358
+ if (name === "." || name === "..") {
359
+ continue;
360
+ }
361
+ const abs = path.join(dir, name);
362
+ const rel = prefix ? `${prefix}/${name}` : name;
363
+ if (e.isDirectory()) {
364
+ await walk(abs, rel);
365
+ } else if (e.isFile()) {
366
+ const st = statSync5(abs);
367
+ out.push({
368
+ absPath: abs,
369
+ relativePath: rel.replace(/\\/g, "/"),
370
+ size: st.size
371
+ });
372
+ }
373
+ }
374
+ }
375
+ await walk(root, "");
376
+ out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
377
+ return out;
378
+ }
379
+ async function readArtifactFile(absPath) {
380
+ return readFile(absPath);
381
+ }
382
+ function toMB(bytes) {
383
+ return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
384
+ }
385
+ function detectMimeType(filePath) {
386
+ const ext = path.extname(filePath).toLowerCase();
387
+ return MIME[ext] ?? "";
388
+ }
389
+ var DEFAULT_MAX_FILE_SIZE_MB, MIME, MinioClient;
390
+ var init_minio = __esm({
391
+ "src/commands/deploy/internal/minio.ts"() {
392
+ "use strict";
393
+ DEFAULT_MAX_FILE_SIZE_MB = 50;
394
+ MIME = {
395
+ ".html": "text/html; charset=utf-8",
396
+ ".css": "text/css; charset=utf-8",
397
+ ".js": "application/javascript; charset=utf-8",
398
+ ".json": "application/json; charset=utf-8",
399
+ ".svg": "image/svg+xml",
400
+ ".png": "image/png",
401
+ ".jpg": "image/jpeg",
402
+ ".jpeg": "image/jpeg",
403
+ ".gif": "image/gif",
404
+ ".webp": "image/webp",
405
+ ".woff": "font/woff",
406
+ ".woff2": "font/woff2",
407
+ ".ttf": "font/ttf",
408
+ ".ico": "image/x-icon",
409
+ ".txt": "text/plain; charset=utf-8",
410
+ ".map": "application/json"
411
+ };
412
+ MinioClient = class {
413
+ inner;
414
+ constructor(opts) {
415
+ const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
416
+ this.inner = new Minio.Client({
417
+ endPoint,
418
+ port: opts.port,
419
+ useSSL: opts.useSSL,
420
+ accessKey: opts.accessKey,
421
+ secretKey: opts.secretKey
422
+ });
423
+ }
424
+ async ensureBucket(bucket) {
425
+ const exists = await this.inner.bucketExists(bucket);
426
+ if (!exists) {
427
+ await this.inner.makeBucket(bucket);
428
+ }
429
+ }
430
+ async deleteObjectsByPrefix(bucket, prefix) {
431
+ const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
432
+ const keys = [];
433
+ await new Promise((resolve7, reject) => {
434
+ objectsStream.on("data", (obj) => {
435
+ if (obj.name) {
436
+ keys.push(obj.name);
437
+ }
438
+ });
439
+ objectsStream.on("error", reject);
440
+ objectsStream.on("end", resolve7);
441
+ });
442
+ const chunkSize = 500;
443
+ for (let i = 0; i < keys.length; i += chunkSize) {
444
+ const chunk = keys.slice(i, i + chunkSize);
445
+ await this.inner.removeObjects(
446
+ bucket,
447
+ chunk.map((name) => name)
448
+ );
449
+ }
450
+ }
451
+ async putObject(bucket, objectKey, body, meta) {
452
+ await this.inner.putObject(bucket, objectKey, body, body.length, meta);
453
+ }
454
+ /** 匿名可读当前桶全部对象(便于静态站点直链) */
455
+ async setBucketPublicRead(bucket) {
456
+ const policy = {
457
+ Version: "2012-10-17",
458
+ Statement: [
459
+ {
460
+ Effect: "Allow",
461
+ Principal: { AWS: ["*"] },
462
+ Action: ["s3:GetObject"],
463
+ Resource: [`arn:aws:s3:::${bucket}/*`]
464
+ }
465
+ ]
466
+ };
467
+ await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
468
+ }
469
+ };
470
+ }
471
+ });
472
+
473
+ // src/commands/deploy/internal/deploy-artifact-minio.ts
474
+ import { readFile as readFile2 } from "node:fs/promises";
475
+ function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
476
+ const pad2 = (n) => String(n).padStart(2, "0");
477
+ return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}${pad2(date.getHours())}${pad2(date.getMinutes())}${pad2(date.getSeconds())}`;
478
+ }
479
+ function sanitizeDeployProjectName(name) {
480
+ const trimmed = name.trim().replace(/\\/g, "/");
481
+ const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
482
+ try {
483
+ return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
484
+ } catch {
485
+ return "project";
486
+ }
487
+ }
488
+ function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
489
+ const safeName = sanitizeDeployProjectName(projectName);
490
+ const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
491
+ return `${timestamp}-${safeName}.${suffix}`;
492
+ }
493
+ function buildDeployArtifactObjectKey(projectName, fileName) {
494
+ const safeProject = sanitizeDeployProjectName(projectName);
495
+ return `deploy/${safeProject}/${fileName}`;
496
+ }
497
+ function resolveDeploymentRunIdFromEnv() {
498
+ const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
499
+ return raw || null;
500
+ }
501
+ async function fetchDeployArtifactStorage(api) {
502
+ return api.cli.getDeployArtifactStorage(void 0);
503
+ }
504
+ async function uploadDeployArtifactZip(options) {
505
+ const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
506
+ if (!deploymentRunId && !options.uploadWithoutRunId) {
507
+ return null;
508
+ }
509
+ const cfg = await tryReadApmConfig();
510
+ if (!cfg || !resolveApiKey(cfg)) {
511
+ console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
512
+ return null;
513
+ }
514
+ const api = options.api ?? createApmApiClient(cfg);
515
+ let storage;
516
+ try {
517
+ storage = await fetchDeployArtifactStorage(api);
518
+ } catch (error) {
519
+ const detail = error instanceof Error ? error.message : String(error);
520
+ console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
521
+ return null;
522
+ }
523
+ const fileName = buildDeployArtifactFileName(
524
+ options.kind,
525
+ options.projectName,
526
+ options.timestamp
527
+ );
528
+ const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
529
+ let body;
530
+ try {
531
+ body = await readFile2(options.zipPath);
532
+ } catch (error) {
533
+ const detail = error instanceof Error ? error.message : String(error);
534
+ console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
535
+ return null;
536
+ }
537
+ try {
538
+ const client = new MinioClient({
539
+ endPoint: storage.endpoint,
540
+ port: storage.port,
541
+ useSSL: storage.useSsl,
542
+ accessKey: storage.accessKey,
543
+ secretKey: storage.secretKey
544
+ });
545
+ await client.ensureBucket(storage.bucket);
546
+ await client.putObject(storage.bucket, objectKey, body, {
547
+ "Content-Type": "application/zip"
548
+ });
549
+ console.error(
550
+ `[apm] \u672C\u5730\u76F4\u4F20 MinIO\uFF08\u4E0D\u7ECF\u5E73\u53F0\u670D\u52A1\u5668\uFF09: ${storage.endpoint}:${storage.port}/${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(
551
+ 2
552
+ )} MB)`
553
+ );
554
+ } catch (error) {
555
+ const detail = error instanceof Error ? error.message : String(error);
556
+ console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
557
+ return null;
558
+ }
559
+ if (deploymentRunId) {
560
+ try {
561
+ await api.cli.attachTaskDeploymentArtifact({
562
+ id: deploymentRunId,
563
+ artifactObjectKey: objectKey,
564
+ artifactFileName: fileName
565
+ });
566
+ console.error(
567
+ `[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
568
+ );
569
+ } catch (error) {
570
+ const detail = error instanceof Error ? error.message : String(error);
571
+ console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
572
+ return null;
573
+ }
574
+ }
575
+ return {
576
+ objectKey,
577
+ fileName,
578
+ bucket: storage.bucket
579
+ };
580
+ }
581
+ var APM_DEPLOYMENT_RUN_ID_ENV;
582
+ var init_deploy_artifact_minio = __esm({
583
+ "src/commands/deploy/internal/deploy-artifact-minio.ts"() {
584
+ "use strict";
585
+ init_client();
586
+ init_config();
587
+ init_minio();
588
+ init_minio();
589
+ APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
590
+ }
591
+ });
592
+
593
+ // src/commands/connect/webide-terminal-registry.ts
594
+ var webide_terminal_registry_exports = {};
595
+ __export(webide_terminal_registry_exports, {
596
+ WEBIDE_TERMINAL_LINES_PER_CHUNK: () => WEBIDE_TERMINAL_LINES_PER_CHUNK,
597
+ WebIdeTerminalRegistry: () => WebIdeTerminalRegistry,
598
+ getWebIdeTerminalRegistry: () => getWebIdeTerminalRegistry
599
+ });
600
+ import { spawn } from "node:child_process";
601
+ import { execFile as execFile3 } from "node:child_process";
602
+ import { promisify as promisify3 } from "node:util";
603
+ function taskKey(taskId, key) {
604
+ return `${taskId}\0${key}`;
605
+ }
606
+ function chunkKey(prefix, chunkIndex) {
607
+ return `${prefix}chunks/${String(chunkIndex).padStart(6, "0")}.json`;
608
+ }
609
+ function extractPortsAndUrl(text) {
610
+ const ports = /* @__PURE__ */ new Set();
611
+ let primaryUrl = null;
612
+ let m;
613
+ const re = new RegExp(PORT_URL_RE.source, "gi");
614
+ while ((m = re.exec(text)) !== null) {
615
+ const urlCandidate = m[2];
616
+ if (urlCandidate) {
617
+ primaryUrl = primaryUrl ?? urlCandidate.replace(/[),.;]+$/, "");
618
+ try {
619
+ const u = new URL(primaryUrl);
620
+ if (u.port) ports.add(Number(u.port));
621
+ else if (u.protocol === "https:") ports.add(443);
622
+ else ports.add(80);
623
+ } catch {
624
+ }
625
+ }
626
+ for (const g of [m[1], m[3], m[4]]) {
627
+ if (g) {
628
+ const p = Number(g);
629
+ if (p > 0 && p < 65536) ports.add(p);
630
+ }
631
+ }
632
+ }
633
+ const httpMatch = text.match(/https?:\/\/[^\s)'"]+/i);
634
+ if (httpMatch && !primaryUrl) {
635
+ primaryUrl = httpMatch[0].replace(/[),.;]+$/, "");
636
+ }
637
+ return { ports: [...ports].sort((a, b) => a - b), primaryUrl };
638
+ }
639
+ function killProcessTree(pid) {
640
+ try {
641
+ process.kill(-pid, "SIGTERM");
642
+ } catch {
643
+ try {
644
+ process.kill(pid, "SIGTERM");
645
+ } catch {
646
+ }
647
+ }
648
+ }
649
+ function forceKillProcessTree(pid) {
650
+ try {
651
+ process.kill(-pid, "SIGKILL");
652
+ } catch {
653
+ try {
654
+ process.kill(pid, "SIGKILL");
655
+ } catch {
656
+ }
657
+ }
658
+ }
659
+ async function listProcessTreePids(rootPid) {
660
+ const pids = [rootPid];
661
+ const queue = [rootPid];
662
+ while (queue.length > 0) {
663
+ const parent = queue.shift();
664
+ try {
665
+ const { stdout } = await execFileAsync3("pgrep", ["-P", String(parent)], {
666
+ timeout: 2e3
667
+ });
668
+ for (const line of stdout.split("\n")) {
669
+ const child = Number(line.trim());
670
+ if (child > 0 && !pids.includes(child)) {
671
+ pids.push(child);
672
+ queue.push(child);
673
+ }
674
+ }
675
+ } catch {
676
+ }
677
+ }
678
+ return pids;
679
+ }
680
+ async function probeListenPorts(pid) {
681
+ try {
682
+ const treePids = await listProcessTreePids(pid);
683
+ const { stdout } = await execFileAsync3(
684
+ "lsof",
685
+ ["-nP", "-a", `-p${treePids.join(",")}`, "-iTCP", "-sTCP:LISTEN"],
686
+ { timeout: 3e3, maxBuffer: 1024 * 1024 }
687
+ );
688
+ const ports = /* @__PURE__ */ new Set();
689
+ for (const line of stdout.split("\n")) {
690
+ const m = line.match(/:(\d+)\s+\(LISTEN\)/);
691
+ if (m) {
692
+ const p = Number(m[1]);
693
+ if (p > 0 && p < 65536) ports.add(p);
694
+ }
695
+ }
696
+ return [...ports].sort((a, b) => a - b);
697
+ } catch {
698
+ return [];
699
+ }
700
+ }
701
+ function getWebIdeTerminalRegistry(cfg) {
702
+ if (!registrySingleton) {
703
+ registrySingleton = new WebIdeTerminalRegistry(cfg);
704
+ }
705
+ return registrySingleton;
706
+ }
707
+ var execFileAsync3, WEBIDE_TERMINAL_LINES_PER_CHUNK, SYNC_INTERVAL_MS, PORT_PROBE_INTERVAL_MS, STOP_SIGKILL_MS, PORT_URL_RE, WebIdeTerminalRegistry, registrySingleton;
708
+ var init_webide_terminal_registry = __esm({
709
+ "src/commands/connect/webide-terminal-registry.ts"() {
710
+ "use strict";
711
+ init_client();
712
+ init_deploy_artifact_minio();
713
+ init_minio();
714
+ execFileAsync3 = promisify3(execFile3);
715
+ WEBIDE_TERMINAL_LINES_PER_CHUNK = 50;
716
+ SYNC_INTERVAL_MS = 400;
717
+ PORT_PROBE_INTERVAL_MS = 2e3;
718
+ STOP_SIGKILL_MS = 5e3;
719
+ PORT_URL_RE = /(?:https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d+))?|(?:Local|Network):\s*(https?:\/\/\S+)|listening\s+(?:on\s+)?(?:port\s+)?(\d+)|(?:port|PORT)\s*[:=]\s*(\d+))/gi;
720
+ WebIdeTerminalRegistry = class {
721
+ constructor(cfg) {
722
+ this.cfg = cfg;
723
+ }
724
+ cfg;
725
+ byId = /* @__PURE__ */ new Map();
726
+ byTaskKey = /* @__PURE__ */ new Map();
727
+ minio;
728
+ bucket = "";
729
+ async ensureMinio() {
730
+ if (this.minio) return;
731
+ const storage = await fetchDeployArtifactStorage(
732
+ createApmApiClient(this.cfg)
733
+ );
734
+ this.minio = new MinioClient({
735
+ endPoint: storage.endpoint,
736
+ port: storage.port,
737
+ useSSL: storage.useSsl,
738
+ accessKey: storage.accessKey,
739
+ secretKey: storage.secretKey
740
+ });
741
+ this.bucket = storage.bucket;
742
+ await this.minio.ensureBucket(this.bucket);
743
+ }
744
+ listLocal(taskId) {
745
+ const alive = [
746
+ "STARTING",
747
+ "RUNNING",
748
+ "READY",
749
+ "STOPPING"
750
+ ];
751
+ const sessions = [...this.byId.values()].filter(
752
+ (s) => alive.includes(s.status)
753
+ );
754
+ return (taskId ? sessions.filter((s) => s.taskId === taskId) : sessions).map((s) => ({
755
+ id: s.id,
756
+ taskId: s.taskId,
757
+ key: s.key,
758
+ name: s.name,
759
+ status: s.status,
760
+ pid: s.pid,
761
+ ports: s.ports,
762
+ primaryUrl: s.primaryUrl,
763
+ nextSeq: s.nextSeq,
764
+ command: s.command,
765
+ cwd: s.cwd
766
+ }));
767
+ }
768
+ async start(opts) {
769
+ const key = opts.key.trim();
770
+ const existingId = this.byTaskKey.get(taskKey(opts.taskId, key));
771
+ const existing = existingId ? this.byId.get(existingId) : void 0;
772
+ if (existing?.child && (existing.status === "RUNNING" || existing.status === "READY" || existing.status === "STARTING")) {
773
+ return {
774
+ ok: true,
775
+ idempotent: true,
776
+ terminalId: existing.id,
777
+ status: existing.status,
778
+ pid: existing.pid,
779
+ ports: existing.ports,
780
+ primaryUrl: existing.primaryUrl
781
+ };
782
+ }
783
+ const api = createApmApiClient(this.cfg);
784
+ const upserted = await api.cli.webideUpsertTerminal({
785
+ taskId: opts.taskId,
786
+ key,
787
+ name: opts.name ?? key,
788
+ cwd: opts.cwd,
789
+ command: opts.command,
790
+ readyPattern: opts.readyPattern,
791
+ portsHint: opts.portsHint
792
+ });
793
+ const terminalId = upserted.id;
794
+ const objectPrefix = upserted.objectPrefix?.trim() || `terminals/${opts.taskId}/${terminalId}/`;
795
+ if (existing?.child) {
796
+ await this.stop(terminalId, { reason: "restart" });
797
+ }
798
+ const session = {
799
+ id: terminalId,
800
+ taskId: opts.taskId,
801
+ key,
802
+ name: (opts.name ?? key).trim() || key,
803
+ cwd: opts.cwd,
804
+ command: opts.command,
805
+ readyPattern: opts.readyPattern?.trim() || null,
806
+ status: "STARTING",
807
+ pid: null,
808
+ ports: opts.portsHint ? [...opts.portsHint] : [],
809
+ primaryUrl: null,
810
+ nextSeq: typeof upserted.nextSeq === "number" ? upserted.nextSeq : 0,
811
+ chunkCount: typeof upserted.chunkCount === "number" ? upserted.chunkCount : 0,
812
+ objectPrefix,
813
+ child: null,
814
+ stopping: false,
815
+ pendingLines: [],
816
+ syncChain: Promise.resolve()
817
+ };
818
+ this.byId.set(terminalId, session);
819
+ this.byTaskKey.set(taskKey(opts.taskId, key), terminalId);
820
+ this.pushSystemLine(
821
+ session,
822
+ `[apm] starting: ${opts.command} (cwd=${opts.cwd})`
823
+ );
824
+ const child = spawn(opts.command, {
825
+ cwd: opts.cwd,
826
+ shell: true,
827
+ detached: true,
828
+ env: process.env,
829
+ stdio: ["ignore", "pipe", "pipe"]
830
+ });
831
+ session.child = child;
832
+ session.pid = child.pid ?? null;
833
+ session.status = "RUNNING";
834
+ await api.cli.webideUpdateTerminalStatus({
835
+ id: terminalId,
836
+ status: "RUNNING",
837
+ pid: session.pid,
838
+ ports: session.ports,
839
+ primaryUrl: null
840
+ });
841
+ const onData = (stream) => (buf) => {
842
+ const text = buf.toString("utf8");
843
+ for (const line of text.split(/\r?\n/)) {
844
+ if (!line.length && text.endsWith("\n") === false) continue;
845
+ if (line.length === 0) continue;
846
+ this.pushLine(session, stream, line);
847
+ }
848
+ this.scheduleSync(session);
849
+ this.maybeUpdatePortsFromText(session, text);
850
+ };
851
+ child.stdout?.on("data", onData("stdout"));
852
+ child.stderr?.on("data", onData("stderr"));
853
+ child.on("exit", (code, signal) => {
854
+ void this.onExit(session, code, signal);
855
+ });
856
+ session.portTimer = setInterval(() => {
857
+ void this.probePorts(session);
858
+ }, PORT_PROBE_INTERVAL_MS);
859
+ this.scheduleSync(session);
860
+ return {
861
+ ok: true,
862
+ idempotent: false,
863
+ terminalId,
864
+ status: session.status,
865
+ pid: session.pid,
866
+ ports: session.ports,
867
+ primaryUrl: session.primaryUrl
868
+ };
869
+ }
870
+ async stop(terminalId, opts) {
871
+ const session = this.byId.get(terminalId);
872
+ const api = createApmApiClient(this.cfg);
873
+ if (!session) {
874
+ const pid2 = opts?.pid;
875
+ if (typeof pid2 === "number" && pid2 > 0) {
876
+ killProcessTree(pid2);
877
+ setTimeout(() => forceKillProcessTree(pid2), STOP_SIGKILL_MS);
878
+ }
879
+ try {
880
+ await api.cli.webideUpdateTerminalStatus({
881
+ id: terminalId,
882
+ status: "EXITED",
883
+ lastError: opts?.reason ?? "stopped"
884
+ });
885
+ } catch {
886
+ }
887
+ return { ok: true, terminalId, status: "EXITED" };
888
+ }
889
+ if (session.status === "EXITED" || session.status === "FAILED" || session.stopping) {
890
+ return { ok: true, terminalId, status: session.status };
891
+ }
892
+ session.stopping = true;
893
+ session.status = "STOPPING";
894
+ this.pushSystemLine(
895
+ session,
896
+ `[apm] stopping${opts?.reason ? `: ${opts.reason}` : ""}`
897
+ );
898
+ this.scheduleSync(session);
899
+ try {
900
+ await api.cli.webideUpdateTerminalStatus({
901
+ id: terminalId,
902
+ status: "STOPPING",
903
+ pid: session.pid
904
+ });
905
+ } catch {
906
+ }
907
+ const pid = session.pid ?? opts?.pid ?? null;
908
+ if (session.child && !session.child.killed) {
909
+ try {
910
+ if (session.child.pid) killProcessTree(session.child.pid);
911
+ } catch {
912
+ }
913
+ } else if (typeof pid === "number" && pid > 0) {
914
+ killProcessTree(pid);
915
+ }
916
+ const killPid = session.child?.pid ?? pid;
917
+ if (typeof killPid === "number" && killPid > 0) {
918
+ setTimeout(() => {
919
+ if (session.child && !session.child.killed) {
920
+ forceKillProcessTree(killPid);
921
+ }
922
+ }, STOP_SIGKILL_MS);
923
+ }
924
+ return { ok: true, terminalId, status: "STOPPING" };
925
+ }
926
+ async stopAllForTask(taskId, reason = "cache-cleanup") {
927
+ const ids = [...this.byId.values()].filter((s) => s.taskId === taskId).map((s) => s.id);
928
+ for (const id of ids) {
929
+ await this.stop(id, { reason });
930
+ }
931
+ return { ok: true, stopped: ids.length };
932
+ }
933
+ pushSystemLine(session, text) {
934
+ this.pushLine(session, "system", text);
935
+ }
936
+ pushLine(session, stream, text) {
937
+ session.nextSeq += 1;
938
+ session.pendingLines.push({
939
+ seq: session.nextSeq,
940
+ stream,
941
+ text,
942
+ ts: (/* @__PURE__ */ new Date()).toISOString()
943
+ });
944
+ }
945
+ maybeUpdatePortsFromText(session, text) {
946
+ const { ports, primaryUrl } = extractPortsAndUrl(text);
947
+ let changed = false;
948
+ for (const p of ports) {
949
+ if (!session.ports.includes(p)) {
950
+ session.ports.push(p);
951
+ changed = true;
952
+ }
953
+ }
954
+ session.ports.sort((a, b) => a - b);
955
+ if (primaryUrl && !session.primaryUrl) {
956
+ session.primaryUrl = primaryUrl;
957
+ changed = true;
958
+ }
959
+ if (changed) {
960
+ void this.syncStatus(session);
961
+ }
962
+ if (session.readyPattern && session.status === "RUNNING" && new RegExp(session.readyPattern).test(text)) {
963
+ session.status = "READY";
964
+ void this.syncStatus(session);
965
+ } else if (session.status === "RUNNING" && (session.ports.length > 0 || session.primaryUrl)) {
966
+ session.status = "READY";
967
+ void this.syncStatus(session);
968
+ }
969
+ }
970
+ async probePorts(session) {
971
+ if (session.status !== "RUNNING" && session.status !== "STARTING" && session.status !== "READY") {
972
+ return;
973
+ }
974
+ if (!session.pid) return;
975
+ const ports = await probeListenPorts(session.pid);
976
+ if (ports.length === 0) return;
977
+ const same = ports.length === session.ports.length && ports.every((p, i) => p === session.ports[i]);
978
+ if (same) return;
979
+ session.ports = ports;
980
+ if (!session.primaryUrl && session.ports[0]) {
981
+ session.primaryUrl = `http://127.0.0.1:${session.ports[0]}`;
982
+ }
983
+ if (session.status === "RUNNING" || session.status === "STARTING") {
984
+ session.status = "READY";
985
+ }
986
+ void this.syncStatus(session);
987
+ }
988
+ async syncStatus(session) {
989
+ const api = createApmApiClient(this.cfg);
990
+ try {
991
+ await api.cli.webideUpdateTerminalStatus({
992
+ id: session.id,
993
+ status: session.status,
994
+ pid: session.pid,
995
+ ports: session.ports,
996
+ primaryUrl: session.primaryUrl
997
+ });
998
+ } catch (err) {
999
+ console.warn(
1000
+ "[apm] terminal status sync failed:",
1001
+ err instanceof Error ? err.message : err
1002
+ );
1003
+ }
1004
+ }
1005
+ scheduleSync(session) {
1006
+ if (session.syncTimer) return;
1007
+ session.syncTimer = setTimeout(() => {
1008
+ session.syncTimer = void 0;
1009
+ session.syncChain = session.syncChain.then(() => this.flushPending(session)).catch((err) => {
1010
+ console.warn(
1011
+ "[apm] terminal log flush failed:",
1012
+ err instanceof Error ? err.message : err
1013
+ );
1014
+ });
1015
+ }, SYNC_INTERVAL_MS);
1016
+ }
1017
+ async flushPending(session) {
1018
+ if (session.pendingLines.length === 0) return;
1019
+ const batch = session.pendingLines.splice(0, session.pendingLines.length);
1020
+ await this.ensureMinio();
1021
+ const seqStart = batch[0].seq;
1022
+ const seqEnd = batch.at(-1).seq;
1023
+ const ci = session.chunkCount;
1024
+ const key = chunkKey(session.objectPrefix, ci);
1025
+ const body = Buffer.from(
1026
+ JSON.stringify({ seqStart, seqEnd, lines: batch }),
1027
+ "utf8"
1028
+ );
1029
+ await this.minio.putObject(this.bucket, key, body, {
1030
+ "Content-Type": "application/json"
1031
+ });
1032
+ session.chunkCount = ci + 1;
1033
+ const api = createApmApiClient(this.cfg);
1034
+ await api.cli.webideUpdateTerminalMeta({
1035
+ id: session.id,
1036
+ nextSeq: session.nextSeq,
1037
+ chunkCount: session.chunkCount
1038
+ });
1039
+ if (!session.stopping && (session.status === "RUNNING" || session.status === "READY")) {
1040
+ try {
1041
+ const listed = await api.cli.webideListTerminals({
1042
+ taskId: session.taskId
1043
+ });
1044
+ const remote = listed.terminals?.find((t) => t.id === session.id);
1045
+ if (remote?.status === "STOPPING") {
1046
+ await this.stop(session.id, {
1047
+ pid: remote.pid ?? session.pid,
1048
+ reason: "server STOPPING"
1049
+ });
1050
+ }
1051
+ } catch {
1052
+ }
1053
+ }
1054
+ }
1055
+ async onExit(session, code, signal) {
1056
+ if (session.portTimer) {
1057
+ clearInterval(session.portTimer);
1058
+ session.portTimer = void 0;
1059
+ }
1060
+ session.child = null;
1061
+ const exitCode = code ?? (signal ? 1 : 0);
1062
+ session.status = session.stopping || exitCode === 0 ? "EXITED" : "FAILED";
1063
+ this.pushSystemLine(
1064
+ session,
1065
+ `[apm] exited code=${exitCode}${signal ? ` signal=${signal}` : ""}`
1066
+ );
1067
+ await session.syncChain;
1068
+ await this.flushPending(session);
1069
+ const api = createApmApiClient(this.cfg);
1070
+ try {
1071
+ await api.cli.webideUpdateTerminalStatus({
1072
+ id: session.id,
1073
+ status: session.status,
1074
+ pid: session.pid,
1075
+ exitCode,
1076
+ ports: session.ports,
1077
+ primaryUrl: session.primaryUrl
1078
+ });
1079
+ } catch (err) {
1080
+ console.warn(
1081
+ "[apm] terminal exit status sync failed:",
1082
+ err instanceof Error ? err.message : err
1083
+ );
1084
+ }
1085
+ }
1086
+ };
1087
+ registrySingleton = null;
1088
+ }
1089
+ });
1090
+
1091
+ // src/index.ts
1092
+ import { Command } from "commander";
1093
+ init_config();
81
1094
 
82
1095
  // src/commands/init.ts
83
1096
  import { join as join5 } from "path";
84
1097
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
85
1098
 
86
1099
  // src/command-utils.ts
1100
+ init_config();
87
1101
  import {
88
1102
  copyFileSync,
89
1103
  existsSync,
@@ -367,204 +1381,10 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
367
1381
  }
368
1382
 
369
1383
  // src/deployment-config-sync.ts
1384
+ init_client();
370
1385
  import { join as join3 } from "path";
371
1386
  import { writeFileSync as writeFileSync3 } from "fs";
372
1387
 
373
- // src/api/client.ts
374
- import { createApiClient } from "listpage-http";
375
-
376
- // src/api/request-config.ts
377
- import { defineEndpoint } from "listpage-http";
378
- var requestConfig = {
379
- cli: {
380
- me: defineEndpoint(
381
- {
382
- method: "GET",
383
- path: "/cli/me"
384
- }
385
- ),
386
- sessionDetail: defineEndpoint({
387
- method: "GET",
388
- path: "/cli/sessions/detail"
389
- }),
390
- sessionMembers: defineEndpoint({
391
- method: "GET",
392
- path: "/cli/sessions/members"
393
- }),
394
- listSessionMessages: defineEndpoint({
395
- method: "GET",
396
- path: "/cli/messages"
397
- }),
398
- listDocuments: defineEndpoint({
399
- method: "GET",
400
- path: "/cli/documents"
401
- }),
402
- getDevGate: defineEndpoint({
403
- method: "GET",
404
- path: "/cli/dev-gate"
405
- }),
406
- listAttachments: defineEndpoint(
407
- {
408
- method: "GET",
409
- path: "/cli/attachments"
410
- }
411
- ),
412
- upsertDocument: defineEndpoint({
413
- method: "PUT",
414
- path: "/cli/documents/upsert"
415
- }),
416
- appendMessageContent: defineEndpoint({
417
- method: "PUT",
418
- path: "/cli/messages/content"
419
- }),
420
- setMessageError: defineEndpoint({
421
- method: "PUT",
422
- path: "/cli/messages/error"
423
- }),
424
- upsertCursorMessageLog: defineEndpoint({
425
- method: "PUT",
426
- path: "/cli/cursor-message-logs"
427
- }),
428
- updateMessageStatus: defineEndpoint({
429
- method: "PUT",
430
- path: "/cli/messages/status"
431
- }),
432
- webideAppendMessageContent: defineEndpoint({
433
- method: "PUT",
434
- path: "/cli/webide/messages/content"
435
- }),
436
- webideEnsureMessageContent: defineEndpoint({
437
- method: "PUT",
438
- path: "/cli/webide/messages/ensure-content"
439
- }),
440
- webideSetMessageError: defineEndpoint({
441
- method: "PUT",
442
- path: "/cli/webide/messages/error"
443
- }),
444
- webideUpdateMessageStatus: defineEndpoint({
445
- method: "PUT",
446
- path: "/cli/webide/messages/status"
447
- }),
448
- webideUpsertMessageLog: defineEndpoint({
449
- method: "PUT",
450
- path: "/cli/webide/message-logs"
451
- }),
452
- webideReplaceAssumptions: defineEndpoint({
453
- method: "PUT",
454
- path: "/cli/webide/assumptions"
455
- }),
456
- webideListAssumptions: defineEndpoint({
457
- method: "GET",
458
- path: "/cli/webide/assumptions"
459
- }),
460
- webideRecommendPlan: defineEndpoint({
461
- method: "PUT",
462
- path: "/cli/webide/plan-recommendation"
463
- }),
464
- webideUpsertPlan: defineEndpoint({
465
- method: "PUT",
466
- path: "/cli/webide/plan"
467
- }),
468
- webideReplaceTestCases: defineEndpoint({
469
- method: "PUT",
470
- path: "/cli/webide/test-cases"
471
- }),
472
- branchBaseline: defineEndpoint({
473
- method: "GET",
474
- path: "/cli/tasks/branch-baseline"
475
- }),
476
- listSessionsForBranchCleanup: defineEndpoint({
477
- method: "GET",
478
- path: "/cli/sessions/branch-cleanup"
479
- }),
480
- workspaceBaseline: defineEndpoint({
481
- method: "GET",
482
- path: "/cli/workspaces/baseline"
483
- }),
484
- getDeploymentConfiguration: defineEndpoint({
485
- method: "GET",
486
- path: "/cli/deployment-configurations"
487
- }),
488
- matchRepository: defineEndpoint({
489
- method: "GET",
490
- path: "/cli/repositories/match"
491
- }),
492
- listSkills: defineEndpoint({
493
- method: "GET",
494
- path: "/cli/skills"
495
- }),
496
- listRules: defineEndpoint({
497
- method: "GET",
498
- path: "/cli/rules"
499
- }),
500
- createPullRequest: defineEndpoint({
501
- method: "POST",
502
- path: "/cli/pull-requests"
503
- }),
504
- createWebIdeDraftPullRequest: defineEndpoint({
505
- method: "POST",
506
- path: "/cli/webide/draft-pull-requests"
507
- }),
508
- webideMergePullRequests: defineEndpoint({
509
- method: "POST",
510
- path: "/cli/webide/pull-requests/merge"
511
- }),
512
- getRepositoryProjectDocumentManifest: defineEndpoint({
513
- method: "GET",
514
- path: "/cli/repository-project-documents/manifest"
515
- }),
516
- listRepositoryProjectDocuments: defineEndpoint({
517
- method: "GET",
518
- path: "/cli/repository-project-documents"
519
- }),
520
- upsertRepositoryProjectDocument: defineEndpoint({
521
- method: "PUT",
522
- path: "/cli/repository-project-documents/upsert"
523
- }),
524
- removeRepositoryProjectDocument: defineEndpoint({
525
- method: "DELETE",
526
- path: "/cli/repository-project-documents"
527
- }),
528
- createTaskDeployment: defineEndpoint({
529
- method: "POST",
530
- path: "/cli/task-deployments"
531
- }),
532
- updateTaskDeploymentStatus: defineEndpoint({
533
- method: "PUT",
534
- path: "/cli/task-deployments/status"
535
- }),
536
- syncTaskDeploymentLog: defineEndpoint({
537
- method: "PUT",
538
- path: "/cli/task-deployments/log"
539
- }),
540
- completeTaskDeployment: defineEndpoint({
541
- method: "PUT",
542
- path: "/cli/task-deployments/complete"
543
- }),
544
- getDeployArtifactStorage: defineEndpoint(
545
- {
546
- method: "GET",
547
- path: "/cli/deploy-artifact-storage"
548
- }
549
- ),
550
- attachTaskDeploymentArtifact: defineEndpoint({
551
- method: "PUT",
552
- path: "/cli/task-deployments/artifact"
553
- })
554
- }
555
- };
556
-
557
- // src/api/client.ts
558
- function createApmApiClient(cfg) {
559
- const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
560
- return createApiClient(requestConfig, {
561
- baseURL,
562
- getToken: () => resolveApiKey(cfg) || void 0,
563
- successCodes: [0],
564
- unauthorizedCodes: [401]
565
- });
566
- }
567
-
568
1388
  // src/git-remote.ts
569
1389
  import { execFile } from "child_process";
570
1390
  import { promisify } from "util";
@@ -769,6 +1589,7 @@ async function resolveBranchBaseline(api, sessionId, workdirPath) {
769
1589
  }
770
1590
 
771
1591
  // src/deployment-config-sync.ts
1592
+ init_config();
772
1593
  var TEMPLATE_HINT = "\u4FDD\u7559\u6A21\u677F .apm/apm.config.json";
773
1594
  var SYNC_HINT = "\u767B\u8BB0\u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u3001\u7ED1\u5B9A\u4ED3\u5E93\u540E\uFF0C\u53EF\u6267\u884C: apm sync-deploy-config";
774
1595
  async function resolveRepositoryIdForSync(api, workdirPath) {
@@ -831,7 +1652,9 @@ ${diagnostic ?? ""}
831
1652
  return { synced: true, repositoryId, configName: config.name };
832
1653
  }
833
1654
 
834
- // src/repository-project-documents-sync.ts
1655
+ // src/project-documents-sync.ts
1656
+ init_client();
1657
+ init_config();
835
1658
  import {
836
1659
  existsSync as existsSync2,
837
1660
  readdirSync as readdirSync2,
@@ -843,7 +1666,7 @@ import { createHash } from "crypto";
843
1666
  import { dirname as dirname2, join as join4, relative, sep } from "path";
844
1667
  var MANIFEST_FILE = "manifest.json";
845
1668
  function projectDocumentsDir(apmRoot) {
846
- return join4(apmRoot ?? workspaceApmDir(), "project");
1669
+ return join4(apmRoot ?? workspaceApmDir(), "docs");
847
1670
  }
848
1671
  function projectDocumentLocalPath(apmRoot, documentPath) {
849
1672
  const normalized = normalizeLocalDocumentPath(documentPath);
@@ -918,43 +1741,67 @@ function diffManifestPaths(remote, local) {
918
1741
  deleteLocal.push(path19);
919
1742
  }
920
1743
  }
921
- return { download, deleteLocal };
1744
+ return { download, deleteLocal };
1745
+ }
1746
+ async function resolveProjectIdForSync(api, workdirPath) {
1747
+ try {
1748
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
1749
+ const projectId = baseline.projectId?.trim() || null;
1750
+ if (projectId) {
1751
+ return { projectId, diagnostic: null };
1752
+ }
1753
+ const projectIds = baseline.projectIds ?? [];
1754
+ if (projectIds.length === 0) {
1755
+ return {
1756
+ projectId: null,
1757
+ diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
1758
+ \u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
1759
+ };
1760
+ }
1761
+ return {
1762
+ projectId: null,
1763
+ diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
1764
+ \u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
1765
+ };
1766
+ } catch (err) {
1767
+ const detail = err instanceof Error ? err.message : String(err);
1768
+ return {
1769
+ projectId: null,
1770
+ diagnostic: detail.replace(/^\[apm\]\s*/, "")
1771
+ };
1772
+ }
922
1773
  }
923
- async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
1774
+ async function syncProjectDocumentsPull(workdirPath, apmDir) {
924
1775
  const empty = {
925
1776
  synced: false,
926
- repositoryId: null,
1777
+ projectId: null,
927
1778
  downloaded: 0,
928
1779
  deleted: 0
929
1780
  };
930
1781
  const cfg = await tryReadApmConfig();
931
1782
  if (!cfg || !resolveApiKey(cfg)) {
932
1783
  console.log(
933
- "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
1784
+ "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
934
1785
  );
935
1786
  return empty;
936
1787
  }
937
1788
  const api = createApmApiClient(cfg);
938
- const { repositoryId, diagnostic } = await resolveRepositoryIdForSync(
1789
+ const { projectId, diagnostic } = await resolveProjectIdForSync(
939
1790
  api,
940
1791
  workdirPath
941
1792
  );
942
- if (!repositoryId) {
943
- console.log(
944
- `[apm] \u672A\u80FD\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u3002
945
- ${diagnostic ?? ""}`
946
- );
1793
+ if (!projectId) {
1794
+ console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
1795
+ ${diagnostic ?? ""}`);
947
1796
  return empty;
948
1797
  }
949
1798
  const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
950
- const projectDir = projectDocumentsDir(targetApmDir);
951
- await ensureDirExists(projectDir);
952
- const { manifest: remoteManifest } = await api.cli.getRepositoryProjectDocumentManifest({ repositoryId });
1799
+ const docsDir = projectDocumentsDir(targetApmDir);
1800
+ await ensureDirExists(docsDir);
1801
+ const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest({ projectId });
953
1802
  if (!remoteManifest) {
954
- console.log(
955
- `[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`
956
- );
957
- return { ...empty, repositoryId };
1803
+ console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
1804
+ return { ...empty, projectId };
958
1805
  }
959
1806
  const localManifest = readLocalManifest(targetApmDir);
960
1807
  const { download, deleteLocal } = diffManifestPaths(
@@ -963,12 +1810,14 @@ ${diagnostic ?? ""}`
963
1810
  );
964
1811
  let downloaded = 0;
965
1812
  if (download.length > 0) {
966
- const { list } = await api.cli.listRepositoryProjectDocuments({
967
- repositoryId,
1813
+ const { list } = await api.cli.listProjectDocuments({
1814
+ projectId,
968
1815
  paths: download.join(",")
969
1816
  });
970
1817
  for (const doc of list) {
971
- const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, doc.path));
1818
+ const absPath = toFsPath(
1819
+ projectDocumentLocalPath(targetApmDir, doc.path)
1820
+ );
972
1821
  await ensureDirExists(dirname2(absPath));
973
1822
  writeFileSync4(absPath, doc.content, "utf8");
974
1823
  downloaded += 1;
@@ -983,34 +1832,34 @@ ${diagnostic ?? ""}`
983
1832
  }
984
1833
  }
985
1834
  writeFileSync4(
986
- toFsPath(join4(projectDir, MANIFEST_FILE)),
1835
+ toFsPath(join4(docsDir, MANIFEST_FILE)),
987
1836
  `${JSON.stringify(remoteManifest, null, 2)}
988
1837
  `,
989
1838
  "utf8"
990
1839
  );
991
1840
  console.log(
992
- `[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
1841
+ `[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
993
1842
  );
994
1843
  return {
995
1844
  synced: true,
996
- repositoryId,
1845
+ projectId,
997
1846
  downloaded,
998
1847
  deleted
999
1848
  };
1000
1849
  }
1001
- async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
1850
+ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
1002
1851
  const api = createApmApiClient(cfg);
1003
- const { repositoryId } = await resolveRepositoryIdForSync(api, workdirPath);
1004
- if (!repositoryId) {
1852
+ const { projectId } = await resolveProjectIdForSync(api, workdirPath);
1853
+ if (!projectId) {
1005
1854
  return 0;
1006
1855
  }
1007
1856
  const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
1008
1857
  const localPaths = listLocalDocumentPaths(targetApmDir);
1009
1858
  if (localPaths.length === 0) {
1010
- console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1859
+ console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1011
1860
  return 0;
1012
1861
  }
1013
- const remoteManifest = (await api.cli.getRepositoryProjectDocumentManifest({ repositoryId })).manifest ?? null;
1862
+ const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
1014
1863
  const remoteHashByPath = new Map(
1015
1864
  (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1016
1865
  );
@@ -1028,17 +1877,17 @@ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
1028
1877
  if (remoteHashByPath.get(path19) === contentHash) {
1029
1878
  continue;
1030
1879
  }
1031
- await api.cli.upsertRepositoryProjectDocument({
1032
- repositoryId,
1880
+ await api.cli.upsertProjectDocument({
1881
+ projectId,
1033
1882
  path: path19,
1034
1883
  content,
1035
1884
  description: remoteDescriptionByPath.get(path19) ?? void 0
1036
1885
  });
1037
1886
  synced += 1;
1038
- console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path19}`);
1887
+ console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
1039
1888
  }
1040
1889
  if (synced === 0) {
1041
- console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1890
+ console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1042
1891
  }
1043
1892
  return synced;
1044
1893
  }
@@ -1059,7 +1908,7 @@ async function ensureWorkspaceInitialized(workdir, options) {
1059
1908
  const apmDir = workspaceApmDir(workdir);
1060
1909
  await copyTemplateFiles(apmDir, workdir);
1061
1910
  const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
1062
- await syncRepositoryProjectDocumentsPull(workdir, apmDir);
1911
+ await syncProjectDocumentsPull(workdir, apmDir);
1063
1912
  const trimmedName = options?.name?.trim();
1064
1913
  if (trimmedName) {
1065
1914
  const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
@@ -1097,6 +1946,8 @@ async function runInit(name) {
1097
1946
  }
1098
1947
 
1099
1948
  // src/commands/login.ts
1949
+ init_config();
1950
+ init_client();
1100
1951
  import { existsSync as existsSync3 } from "fs";
1101
1952
  import { ApiError } from "listpage-http";
1102
1953
  async function runLogin(opts) {
@@ -1158,6 +2009,7 @@ async function runLogin(opts) {
1158
2009
  }
1159
2010
 
1160
2011
  // src/commands/branch.ts
2012
+ init_client();
1161
2013
  var SESSION_BRANCH_PREFIX = "feat/session-";
1162
2014
  function branchNameForSession(sessionId) {
1163
2015
  const id = sessionId.trim();
@@ -1281,6 +2133,9 @@ async function runBranch(sessionId, options = {}) {
1281
2133
  return ensureFeatureBranch(branch, baseline.defaultBranch, options);
1282
2134
  }
1283
2135
 
2136
+ // src/commands/clean-branches.ts
2137
+ init_client();
2138
+
1284
2139
  // src/commands/session-branch-prune.ts
1285
2140
  var TASK_STATUS_LABEL = {
1286
2141
  OPEN: "\u5F85\u5F00\u59CB",
@@ -1603,6 +2458,7 @@ async function runCleanBranches(options = {}) {
1603
2458
  }
1604
2459
 
1605
2460
  // src/commands/pull.ts
2461
+ init_client();
1606
2462
  import { writeFileSync as writeFileSync9 } from "fs";
1607
2463
  import { join as join9 } from "path";
1608
2464
  import { stringify as yamlStringify } from "yaml";
@@ -1713,6 +2569,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
1713
2569
  }
1714
2570
 
1715
2571
  // src/rules-sync.ts
2572
+ init_client();
1716
2573
  import { basename as basename2, extname as extname2, join as join8 } from "path";
1717
2574
  import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
1718
2575
 
@@ -1965,7 +2822,7 @@ async function runPull(sessionId, remoteWorkdir) {
1965
2822
  await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
1966
2823
  await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
1967
2824
  await syncRemoteDeploymentConfig(workdir, apmRoot);
1968
- await syncRepositoryProjectDocumentsPull(workdir, apmRoot);
2825
+ await syncProjectDocumentsPull(workdir, apmRoot);
1969
2826
  console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
1970
2827
  return dir;
1971
2828
  }
@@ -2135,6 +2992,7 @@ async function runUpdate(options = {}) {
2135
2992
  }
2136
2993
 
2137
2994
  // src/commands/update-skills.ts
2995
+ init_client();
2138
2996
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
2139
2997
  import { join as join11 } from "path";
2140
2998
  async function syncWorkspaceSkills(cfg, workdir) {
@@ -2217,17 +3075,21 @@ async function runSyncDeployConfig() {
2217
3075
  }
2218
3076
 
2219
3077
  // src/commands/sync-project-documents.ts
3078
+ init_config();
2220
3079
  async function runSyncProjectDocuments(options) {
2221
- const pull = options?.pull ?? !options?.push;
2222
- const push = options?.push ?? false;
2223
3080
  const workdir = resolveWorkdirPath();
2224
3081
  const apmRoot = workspaceApmDir(workdir);
2225
- if (pull) {
2226
- await syncRepositoryProjectDocumentsPull(workdir, apmRoot);
3082
+ const doPush = Boolean(options.push);
3083
+ const doPull = Boolean(options.pull) || !doPush;
3084
+ if (doPull) {
3085
+ await syncProjectDocumentsPull(workdir, apmRoot);
2227
3086
  }
2228
- if (push) {
2229
- const cfg = await ensureLoggedConfig();
2230
- await syncRepositoryProjectDocumentsPush(cfg, workdir, apmRoot);
3087
+ if (doPush) {
3088
+ const cfg = await tryReadApmConfig();
3089
+ if (!cfg) {
3090
+ throw new Error("[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8BF7\u5148\u6267\u884C apm login");
3091
+ }
3092
+ await syncProjectDocumentsPush(cfg, workdir, apmRoot);
2231
3093
  }
2232
3094
  }
2233
3095
 
@@ -2398,7 +3260,11 @@ function validateAssumptionsMarkdown(markdown, source) {
2398
3260
  };
2399
3261
  }
2400
3262
 
3263
+ // src/commands/sync-document.ts
3264
+ init_client();
3265
+
2401
3266
  // src/commands/sync-session-documents.ts
3267
+ init_client();
2402
3268
  import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
2403
3269
  import { join as join12 } from "path";
2404
3270
  function listLocalMarkdownFiles(docsDir) {
@@ -2519,6 +3385,7 @@ async function runSyncDocument(sessionId, options) {
2519
3385
  }
2520
3386
 
2521
3387
  // src/commands/dev-gate.ts
3388
+ init_client();
2522
3389
  async function runDevGate(sessionId, source) {
2523
3390
  const trimmedId = sessionId.trim();
2524
3391
  if (!trimmedId) {
@@ -2538,6 +3405,7 @@ async function runDevGate(sessionId, source) {
2538
3405
  }
2539
3406
 
2540
3407
  // src/commands/append-message.ts
3408
+ init_client();
2541
3409
  async function appendMessageContent(cfg, messageId, content) {
2542
3410
  const trimmedId = messageId.trim();
2543
3411
  if (!trimmedId) {
@@ -2566,6 +3434,7 @@ async function runAppendMessage(options) {
2566
3434
  }
2567
3435
 
2568
3436
  // src/commands/update-message-status.ts
3437
+ init_client();
2569
3438
  var VALID_STATUSES = [
2570
3439
  "CREATED",
2571
3440
  "QUEUED",
@@ -2594,8 +3463,10 @@ async function runUpdateMessageStatus(options) {
2594
3463
  }
2595
3464
 
2596
3465
  // src/commands/connect.ts
3466
+ init_client();
2597
3467
  import WebSocket from "ws";
2598
3468
  import { setTimeout as delay3 } from "node:timers/promises";
3469
+ init_config();
2599
3470
 
2600
3471
  // src/ws/protocol.ts
2601
3472
  function nonEmptyString(v) {
@@ -2777,13 +3648,38 @@ function validateWebIdeCacheCleanupPush(o) {
2777
3648
  }
2778
3649
  };
2779
3650
  }
3651
+ function validateWebIdeTerminalStopPush(o) {
3652
+ if (o.type !== "webide-terminal-stop") {
3653
+ return { ok: false, reason: "\u671F\u671B webide-terminal-stop" };
3654
+ }
3655
+ if (!nonEmptyString(o.taskId)) {
3656
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 taskId" };
3657
+ }
3658
+ if (!nonEmptyString(o.terminalId)) {
3659
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 terminalId" };
3660
+ }
3661
+ if (!nonEmptyString(o.key)) {
3662
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 key" };
3663
+ }
3664
+ const pid = typeof o.pid === "number" && Number.isFinite(o.pid) ? Math.trunc(o.pid) : o.pid === null ? null : void 0;
3665
+ return {
3666
+ ok: true,
3667
+ data: {
3668
+ type: "webide-terminal-stop",
3669
+ taskId: o.taskId.trim(),
3670
+ terminalId: o.terminalId.trim(),
3671
+ key: o.key.trim(),
3672
+ ...pid !== void 0 ? { pid } : {}
3673
+ }
3674
+ };
3675
+ }
2780
3676
  function validateAgentWsMessage(value, kind) {
2781
3677
  if (typeof value !== "object" || value === null) {
2782
3678
  return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
2783
3679
  }
2784
3680
  const o = value;
2785
3681
  const type = o.type;
2786
- if (type !== "heartbeat" && type !== "message" && type !== "webide-message" && type !== "cancel" && type !== "deploy" && type !== "session-cache-cleanup" && type !== "webide-cache-cleanup") {
3682
+ if (type !== "heartbeat" && type !== "message" && type !== "webide-message" && type !== "cancel" && type !== "deploy" && type !== "session-cache-cleanup" && type !== "webide-cache-cleanup" && type !== "webide-terminal-stop") {
2787
3683
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
2788
3684
  }
2789
3685
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -2801,12 +3697,18 @@ function validateAgentWsMessage(value, kind) {
2801
3697
  if (type === "webide-cache-cleanup") {
2802
3698
  return validateWebIdeCacheCleanupPush(o);
2803
3699
  }
3700
+ if (type === "webide-terminal-stop") {
3701
+ return validateWebIdeTerminalStopPush(o);
3702
+ }
2804
3703
  if (type === "webide-message") {
2805
3704
  return validateWebIdeMessagePush(o);
2806
3705
  }
2807
3706
  return validateMessagePush(o);
2808
3707
  }
2809
3708
 
3709
+ // src/commands/connect/deploy-run.ts
3710
+ init_client();
3711
+
2810
3712
  // src/commands/deploy/deploy-execute.ts
2811
3713
  import path11 from "node:path";
2812
3714
 
@@ -3054,366 +3956,119 @@ function resolveWisdomBackendPackOnlyFromApmConfig(cfg) {
3054
3956
  mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3055
3957
  mode: resolveWisdomDeployMode(w.mode)
3056
3958
  };
3057
- }
3058
- function resolveWisdomBackendDeployFromApmConfig(cfg) {
3059
- const projectName = reqTopLevelName(cfg);
3060
- const w = cfg.wisdomDeploy ?? {};
3061
- const h = cfg.healthCheck ?? {};
3062
- const jarPath = req(w.jarPath, "jarPath", "wisdomDeploy").trim();
3063
- const remoteAppDir = posixDirname(jarPath);
3064
- const healthPort = Number(req(h.port, "port", "healthCheck"));
3065
- const healthTimeout = Number(req(h.timeout, "timeout", "healthCheck"));
3066
- if (!Number.isFinite(healthPort) || !Number.isInteger(healthPort) || healthPort < 1) {
3067
- console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
3068
- process.exit(1);
3069
- }
3070
- if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
3071
- console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
3072
- process.exit(1);
3073
- }
3074
- const mavenLocalRepo = resolveMavenLocalRepoWithSource();
3075
- return {
3076
- projectName,
3077
- host: req(w.host, "host", "wisdomDeploy").trim(),
3078
- port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
3079
- username: req(w.username, "username", "wisdomDeploy").trim(),
3080
- password: req(w.password, "password", "wisdomDeploy").trim(),
3081
- remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
3082
- remoteAppDir,
3083
- remoteLibDir: `${remoteAppDir}/lib`,
3084
- startupJar: posixBasename(jarPath),
3085
- packageName: `${projectName}.jar.zip`,
3086
- mavenLocalRepo: mavenLocalRepo.path,
3087
- mavenLocalRepoSource: mavenLocalRepo.source,
3088
- mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3089
- healthCheckPort: healthPort,
3090
- healthCheckContext: req(h.context, "context", "healthCheck").trim(),
3091
- healthCheckTimeout: healthTimeout,
3092
- mode: resolveWisdomDeployMode(w.mode)
3093
- };
3094
- }
3095
-
3096
- // src/commands/deploy/internal/apm-config.ts
3097
- function loadApmConfig(options) {
3098
- const p = resolve4(
3099
- process.cwd(),
3100
- options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3101
- );
3102
- if (!existsSync12(p)) {
3103
- console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3104
- process.exit(1);
3105
- }
3106
- try {
3107
- const raw = readFileSync11(p, "utf8");
3108
- return JSON.parse(raw);
3109
- } catch (e) {
3110
- console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
3111
- process.exit(1);
3112
- }
3113
- }
3114
-
3115
- // src/commands/deploy/internal/deploy-hints.ts
3116
- function isProductionDeployEnv(env) {
3117
- const normalized = env.trim().toLowerCase();
3118
- return normalized === "online" || normalized === "production" || normalized === "prod";
3119
- }
3120
- function formatMissingWisdomDeployLines() {
3121
- return [
3122
- "[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
3123
- ];
3124
- }
3125
- function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
3126
- if (isProductionDeployEnv(env)) {
3127
- return [
3128
- `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
3129
- "[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
3130
- ];
3131
- }
3132
- return [
3133
- `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
3134
- "[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
3135
- ];
3136
- }
3137
-
3138
- // src/commands/deploy/deploy-errors.ts
3139
- var DeployExecutionError = class extends Error {
3140
- exitCode;
3141
- output;
3142
- constructor(message, exitCode, output = "") {
3143
- super(message);
3144
- this.name = "DeployExecutionError";
3145
- this.exitCode = exitCode;
3146
- this.output = output;
3147
- }
3148
- };
3149
-
3150
- // src/commands/deploy/deploy-debug-log.ts
3151
- import { spawnSync as spawnSync4 } from "node:child_process";
3152
- import { existsSync as existsSync16 } from "node:fs";
3153
- import { dirname as dirname5, join as join17 } from "node:path";
3154
-
3155
- // src/commands/deploy/internal/deploy-artifact-minio.ts
3156
- import { readFile as readFile2 } from "node:fs/promises";
3157
-
3158
- // src/commands/deploy/internal/minio.ts
3159
- import { statSync as statSync5 } from "node:fs";
3160
- import { readdir, readFile } from "node:fs/promises";
3161
- import path from "node:path";
3162
- import * as Minio from "minio";
3163
- var DEFAULT_MAX_FILE_SIZE_MB = 50;
3164
- async function isDirectoryPath(dir) {
3165
- try {
3166
- const st = statSync5(dir);
3167
- return st.isDirectory();
3168
- } catch {
3169
- return false;
3170
- }
3171
- }
3172
- function sanitizeRelativePath(rel) {
3173
- const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
3174
- const segments = norm.split("/").filter(Boolean);
3175
- for (const s of segments) {
3176
- if (s === "." || s === "..") {
3177
- throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
3178
- }
3179
- }
3180
- return segments.join("/");
3181
- }
3182
- async function collectFiles(root) {
3183
- const out = [];
3184
- async function walk(dir, prefix) {
3185
- const entries = await readdir(dir, { withFileTypes: true });
3186
- for (const e of entries) {
3187
- const name = e.name;
3188
- if (name === "." || name === "..") {
3189
- continue;
3190
- }
3191
- const abs = path.join(dir, name);
3192
- const rel = prefix ? `${prefix}/${name}` : name;
3193
- if (e.isDirectory()) {
3194
- await walk(abs, rel);
3195
- } else if (e.isFile()) {
3196
- const st = statSync5(abs);
3197
- out.push({
3198
- absPath: abs,
3199
- relativePath: rel.replace(/\\/g, "/"),
3200
- size: st.size
3201
- });
3202
- }
3203
- }
3204
- }
3205
- await walk(root, "");
3206
- out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
3207
- return out;
3208
- }
3209
- async function readArtifactFile(absPath) {
3210
- return readFile(absPath);
3211
- }
3212
- function toMB(bytes) {
3213
- return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
3214
- }
3215
- var MIME = {
3216
- ".html": "text/html; charset=utf-8",
3217
- ".css": "text/css; charset=utf-8",
3218
- ".js": "application/javascript; charset=utf-8",
3219
- ".json": "application/json; charset=utf-8",
3220
- ".svg": "image/svg+xml",
3221
- ".png": "image/png",
3222
- ".jpg": "image/jpeg",
3223
- ".jpeg": "image/jpeg",
3224
- ".gif": "image/gif",
3225
- ".webp": "image/webp",
3226
- ".woff": "font/woff",
3227
- ".woff2": "font/woff2",
3228
- ".ttf": "font/ttf",
3229
- ".ico": "image/x-icon",
3230
- ".txt": "text/plain; charset=utf-8",
3231
- ".map": "application/json"
3232
- };
3233
- function detectMimeType(filePath) {
3234
- const ext = path.extname(filePath).toLowerCase();
3235
- return MIME[ext] ?? "";
3236
- }
3237
- var MinioClient = class {
3238
- inner;
3239
- constructor(opts) {
3240
- const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
3241
- this.inner = new Minio.Client({
3242
- endPoint,
3243
- port: opts.port,
3244
- useSSL: opts.useSSL,
3245
- accessKey: opts.accessKey,
3246
- secretKey: opts.secretKey
3247
- });
3248
- }
3249
- async ensureBucket(bucket) {
3250
- const exists = await this.inner.bucketExists(bucket);
3251
- if (!exists) {
3252
- await this.inner.makeBucket(bucket);
3253
- }
3254
- }
3255
- async deleteObjectsByPrefix(bucket, prefix) {
3256
- const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
3257
- const keys = [];
3258
- await new Promise((resolve7, reject) => {
3259
- objectsStream.on("data", (obj) => {
3260
- if (obj.name) {
3261
- keys.push(obj.name);
3262
- }
3263
- });
3264
- objectsStream.on("error", reject);
3265
- objectsStream.on("end", resolve7);
3266
- });
3267
- const chunkSize = 500;
3268
- for (let i = 0; i < keys.length; i += chunkSize) {
3269
- const chunk = keys.slice(i, i + chunkSize);
3270
- await this.inner.removeObjects(
3271
- bucket,
3272
- chunk.map((name) => name)
3273
- );
3274
- }
3275
- }
3276
- async putObject(bucket, objectKey, body, meta) {
3277
- await this.inner.putObject(bucket, objectKey, body, body.length, meta);
3278
- }
3279
- /** 匿名可读当前桶全部对象(便于静态站点直链) */
3280
- async setBucketPublicRead(bucket) {
3281
- const policy = {
3282
- Version: "2012-10-17",
3283
- Statement: [
3284
- {
3285
- Effect: "Allow",
3286
- Principal: { AWS: ["*"] },
3287
- Action: ["s3:GetObject"],
3288
- Resource: [`arn:aws:s3:::${bucket}/*`]
3289
- }
3290
- ]
3291
- };
3292
- await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
3293
- }
3294
- };
3295
-
3296
- // src/commands/deploy/internal/deploy-artifact-minio.ts
3297
- var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
3298
- function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
3299
- const pad2 = (n) => String(n).padStart(2, "0");
3300
- return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}${pad2(date.getHours())}${pad2(date.getMinutes())}${pad2(date.getSeconds())}`;
3301
- }
3302
- function sanitizeDeployProjectName(name) {
3303
- const trimmed = name.trim().replace(/\\/g, "/");
3304
- const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
3305
- try {
3306
- return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
3307
- } catch {
3308
- return "project";
3309
- }
3310
- }
3311
- function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
3312
- const safeName = sanitizeDeployProjectName(projectName);
3313
- const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
3314
- return `${timestamp}-${safeName}.${suffix}`;
3315
- }
3316
- function buildDeployArtifactObjectKey(projectName, fileName) {
3317
- const safeProject = sanitizeDeployProjectName(projectName);
3318
- return `deploy/${safeProject}/${fileName}`;
3319
- }
3320
- function resolveDeploymentRunIdFromEnv() {
3321
- const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
3322
- return raw || null;
3323
- }
3324
- async function fetchDeployArtifactStorage(api) {
3325
- return api.cli.getDeployArtifactStorage(void 0);
3326
- }
3327
- async function uploadDeployArtifactZip(options) {
3328
- const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
3329
- if (!deploymentRunId && !options.uploadWithoutRunId) {
3330
- return null;
3331
- }
3332
- const cfg = await tryReadApmConfig();
3333
- if (!cfg || !resolveApiKey(cfg)) {
3334
- console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
3335
- return null;
3959
+ }
3960
+ function resolveWisdomBackendDeployFromApmConfig(cfg) {
3961
+ const projectName = reqTopLevelName(cfg);
3962
+ const w = cfg.wisdomDeploy ?? {};
3963
+ const h = cfg.healthCheck ?? {};
3964
+ const jarPath = req(w.jarPath, "jarPath", "wisdomDeploy").trim();
3965
+ const remoteAppDir = posixDirname(jarPath);
3966
+ const healthPort = Number(req(h.port, "port", "healthCheck"));
3967
+ const healthTimeout = Number(req(h.timeout, "timeout", "healthCheck"));
3968
+ if (!Number.isFinite(healthPort) || !Number.isInteger(healthPort) || healthPort < 1) {
3969
+ console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
3970
+ process.exit(1);
3336
3971
  }
3337
- const api = options.api ?? createApmApiClient(cfg);
3338
- let storage;
3339
- try {
3340
- storage = await fetchDeployArtifactStorage(api);
3341
- } catch (error) {
3342
- const detail = error instanceof Error ? error.message : String(error);
3343
- console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
3344
- return null;
3972
+ if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
3973
+ console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
3974
+ process.exit(1);
3345
3975
  }
3346
- const fileName = buildDeployArtifactFileName(
3347
- options.kind,
3348
- options.projectName,
3349
- options.timestamp
3976
+ const mavenLocalRepo = resolveMavenLocalRepoWithSource();
3977
+ return {
3978
+ projectName,
3979
+ host: req(w.host, "host", "wisdomDeploy").trim(),
3980
+ port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
3981
+ username: req(w.username, "username", "wisdomDeploy").trim(),
3982
+ password: req(w.password, "password", "wisdomDeploy").trim(),
3983
+ remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
3984
+ remoteAppDir,
3985
+ remoteLibDir: `${remoteAppDir}/lib`,
3986
+ startupJar: posixBasename(jarPath),
3987
+ packageName: `${projectName}.jar.zip`,
3988
+ mavenLocalRepo: mavenLocalRepo.path,
3989
+ mavenLocalRepoSource: mavenLocalRepo.source,
3990
+ mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3991
+ healthCheckPort: healthPort,
3992
+ healthCheckContext: req(h.context, "context", "healthCheck").trim(),
3993
+ healthCheckTimeout: healthTimeout,
3994
+ mode: resolveWisdomDeployMode(w.mode)
3995
+ };
3996
+ }
3997
+
3998
+ // src/commands/deploy/internal/apm-config.ts
3999
+ function loadApmConfig(options) {
4000
+ const p = resolve4(
4001
+ process.cwd(),
4002
+ options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3350
4003
  );
3351
- const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
3352
- let body;
3353
- try {
3354
- body = await readFile2(options.zipPath);
3355
- } catch (error) {
3356
- const detail = error instanceof Error ? error.message : String(error);
3357
- console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
3358
- return null;
4004
+ if (!existsSync12(p)) {
4005
+ console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
4006
+ process.exit(1);
3359
4007
  }
3360
4008
  try {
3361
- const client = new MinioClient({
3362
- endPoint: storage.endpoint,
3363
- port: storage.port,
3364
- useSSL: storage.useSsl,
3365
- accessKey: storage.accessKey,
3366
- secretKey: storage.secretKey
3367
- });
3368
- await client.ensureBucket(storage.bucket);
3369
- await client.putObject(storage.bucket, objectKey, body, {
3370
- "Content-Type": "application/zip"
3371
- });
3372
- console.error(
3373
- `[apm] \u672C\u5730\u76F4\u4F20 MinIO\uFF08\u4E0D\u7ECF\u5E73\u53F0\u670D\u52A1\u5668\uFF09: ${storage.endpoint}:${storage.port}/${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(
3374
- 2
3375
- )} MB)`
3376
- );
3377
- } catch (error) {
3378
- const detail = error instanceof Error ? error.message : String(error);
3379
- console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
3380
- return null;
4009
+ const raw = readFileSync11(p, "utf8");
4010
+ return JSON.parse(raw);
4011
+ } catch (e) {
4012
+ console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
4013
+ process.exit(1);
3381
4014
  }
3382
- if (deploymentRunId) {
3383
- try {
3384
- await api.cli.attachTaskDeploymentArtifact({
3385
- id: deploymentRunId,
3386
- artifactObjectKey: objectKey,
3387
- artifactFileName: fileName
3388
- });
3389
- console.error(
3390
- `[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
3391
- );
3392
- } catch (error) {
3393
- const detail = error instanceof Error ? error.message : String(error);
3394
- console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
3395
- return null;
3396
- }
4015
+ }
4016
+
4017
+ // src/commands/deploy/internal/deploy-hints.ts
4018
+ function isProductionDeployEnv(env) {
4019
+ const normalized = env.trim().toLowerCase();
4020
+ return normalized === "online" || normalized === "production" || normalized === "prod";
4021
+ }
4022
+ function formatMissingWisdomDeployLines() {
4023
+ return [
4024
+ "[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
4025
+ ];
4026
+ }
4027
+ function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
4028
+ if (isProductionDeployEnv(env)) {
4029
+ return [
4030
+ `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
4031
+ "[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
4032
+ ];
3397
4033
  }
3398
- return {
3399
- objectKey,
3400
- fileName,
3401
- bucket: storage.bucket
3402
- };
4034
+ return [
4035
+ `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
4036
+ "[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
4037
+ ];
3403
4038
  }
3404
4039
 
4040
+ // src/commands/deploy/deploy-errors.ts
4041
+ var DeployExecutionError = class extends Error {
4042
+ exitCode;
4043
+ output;
4044
+ constructor(message, exitCode, output = "") {
4045
+ super(message);
4046
+ this.name = "DeployExecutionError";
4047
+ this.exitCode = exitCode;
4048
+ this.output = output;
4049
+ }
4050
+ };
4051
+
4052
+ // src/commands/deploy/deploy-debug-log.ts
4053
+ init_deploy_artifact_minio();
4054
+ import { spawnSync as spawnSync4 } from "node:child_process";
4055
+ import { existsSync as existsSync16 } from "node:fs";
4056
+ import { dirname as dirname5, join as join17 } from "node:path";
4057
+
3405
4058
  // src/commands/deploy/internal/deploy-shell-env.ts
3406
4059
  import { spawnSync as spawnSync3 } from "node:child_process";
3407
4060
  import { existsSync as existsSync15 } from "node:fs";
3408
4061
  import { dirname as dirname4, join as join16 } from "node:path";
3409
4062
 
3410
4063
  // src/commands/daemon.ts
4064
+ init_config();
3411
4065
  import { spawnSync as spawnSync2 } from "child_process";
3412
4066
  import { setTimeout as delay } from "node:timers/promises";
3413
4067
  import { existsSync as existsSync14, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
3414
4068
  import { join as join15 } from "path";
3415
4069
 
3416
4070
  // src/commands/connect-lock.ts
4071
+ init_config();
3417
4072
  import {
3418
4073
  existsSync as existsSync13,
3419
4074
  mkdirSync as mkdirSync5,
@@ -4548,6 +5203,7 @@ function runDeployShellCommand(command, cwd, captureOutput) {
4548
5203
 
4549
5204
  // src/commands/deploy/internal/wisdom-backend/index.ts
4550
5205
  import path9 from "node:path";
5206
+ init_deploy_artifact_minio();
4551
5207
 
4552
5208
  // src/commands/deploy/internal/wisdom-backend/jar-incremental.ts
4553
5209
  import {
@@ -4616,6 +5272,8 @@ import SftpClient from "ssh2-sftp-client";
4616
5272
  import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
4617
5273
  import path3 from "node:path";
4618
5274
  import JSZip from "jszip";
5275
+ init_deploy_artifact_minio();
5276
+ init_minio();
4619
5277
  async function addDirToZip(dir, zipFolder) {
4620
5278
  const entries = await readdir2(dir, { withFileTypes: true });
4621
5279
  for (const entry of entries) {
@@ -4704,6 +5362,7 @@ async function packDirectoryWithMinioArchive(input) {
4704
5362
  }
4705
5363
 
4706
5364
  // src/commands/deploy/internal/wisdom-sftp.ts
5365
+ init_deploy_artifact_minio();
4707
5366
  var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
4708
5367
  var SFTP_FAST_PUT_OPTIONS = {
4709
5368
  chunkSize: 64 * 1024,
@@ -5703,6 +6362,8 @@ async function executeWisdomDeploy(options) {
5703
6362
  }
5704
6363
 
5705
6364
  // src/commands/deploy/internal/deploy-baseline-sync.ts
6365
+ init_client();
6366
+ init_config();
5706
6367
  var LOG_PREFIX2 = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
5707
6368
  function formatGitError(error) {
5708
6369
  return error instanceof Error ? error.message : String(error);
@@ -5910,6 +6571,9 @@ function printDeployExecutionError(error) {
5910
6571
  console.error(error.message);
5911
6572
  }
5912
6573
 
6574
+ // src/commands/deploy/platform/deploy-run-coordinator.ts
6575
+ init_deploy_artifact_minio();
6576
+
5913
6577
  // src/commands/deploy/platform/deploy-console-capture.ts
5914
6578
  function captureDeployConsole(logSyncer) {
5915
6579
  const chunks = [];
@@ -6638,6 +7302,7 @@ function clearSessionAgentId(workdir, sessionId, user) {
6638
7302
  }
6639
7303
 
6640
7304
  // src/commands/connect/cursor-message-log.ts
7305
+ init_client();
6641
7306
  var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
6642
7307
  function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
6643
7308
  let lastRunAt = 0;
@@ -6806,16 +7471,17 @@ ${payload}`;
6806
7471
  }
6807
7472
 
6808
7473
  // src/commands/connect/webide-merge-tools.ts
7474
+ init_client();
6809
7475
  function createMergeWebIdePullRequestsTool(options) {
6810
7476
  const { cfg, taskId } = options;
6811
7477
  return {
6812
- description: "Merge all open pull requests for this WebIDE task after conflicts are resolved locally and pushed. Call once with no arguments. Do NOT call if conflicts contradict the task requirements \u2014 report the conflict and fail instead.",
7478
+ description: "Merge all open pull requests for this WebIDE task. Call once with confirmedConflictFree=true. No baseline sync or review is required before calling.",
6813
7479
  inputSchema: {
6814
7480
  type: "object",
6815
7481
  properties: {
6816
7482
  confirmedConflictFree: {
6817
7483
  type: "boolean",
6818
- description: "Must be true: you have checked each repo, resolved mergeable conflicts consistently with requirements, and pushed. If conflicts contradict requirements, do not call this tool."
7484
+ description: "Must be true to confirm you intend to merge this task's open PRs now."
6819
7485
  }
6820
7486
  },
6821
7487
  required: ["confirmedConflictFree"]
@@ -6823,7 +7489,7 @@ function createMergeWebIdePullRequestsTool(options) {
6823
7489
  execute: async (args) => {
6824
7490
  if (args.confirmedConflictFree !== true) {
6825
7491
  throw new Error(
6826
- "MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002\u82E5\u5B58\u5728\u4E0E\u9700\u6C42\u76F8\u6096\u7684\u51B2\u7A81\uFF0C\u8BF7\u52FF\u8C03\u7528\u672C\u5DE5\u5177\uFF0C\u7528 AppendMessage \u8BF4\u660E\u5E76\u5931\u8D25\u7ED3\u675F\u3002"
7492
+ "MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
6827
7493
  );
6828
7494
  }
6829
7495
  const api = createApmApiClient(cfg);
@@ -6835,6 +7501,7 @@ function createMergeWebIdePullRequestsTool(options) {
6835
7501
  }
6836
7502
 
6837
7503
  // src/commands/connect/webide-plan-tools.ts
7504
+ init_client();
6838
7505
  function asString(value) {
6839
7506
  return typeof value === "string" ? value.trim() : "";
6840
7507
  }
@@ -6877,7 +7544,7 @@ function createRecommendPlanTool(options) {
6877
7544
  function createUpsertWebIdePlanTool(options) {
6878
7545
  const { cfg, taskId } = options;
6879
7546
  return {
6880
- description: "Persist the implementation plan markdown for this WebIDE task and mark the plan as ready for human confirmation. Call once with the full plan document.",
7547
+ description: "Persist or replace the implementation plan markdown for this WebIDE task and mark it ready for human confirmation. Call with the full plan document (not a diff). Use after generating or revising the plan.",
6881
7548
  inputSchema: {
6882
7549
  type: "object",
6883
7550
  properties: {
@@ -6904,6 +7571,7 @@ function createUpsertWebIdePlanTool(options) {
6904
7571
  }
6905
7572
 
6906
7573
  // src/commands/connect/webide-test-case-tools.ts
7574
+ init_client();
6907
7575
  function asString2(value) {
6908
7576
  return typeof value === "string" ? value.trim() : "";
6909
7577
  }
@@ -6988,6 +7656,165 @@ function createUpsertWebIdeTestCasesTool(options) {
6988
7656
  };
6989
7657
  }
6990
7658
 
7659
+ // src/commands/connect/webide-terminal-tools.ts
7660
+ init_client();
7661
+ function asString3(value) {
7662
+ return typeof value === "string" ? value.trim() : "";
7663
+ }
7664
+ function asPorts(value) {
7665
+ if (!Array.isArray(value)) return void 0;
7666
+ const ports = value.map((p) => typeof p === "number" ? p : Number(p)).filter((p) => Number.isFinite(p) && p > 0 && p < 65536).map((p) => Math.trunc(p));
7667
+ return ports.length ? ports : void 0;
7668
+ }
7669
+ function createWebIdeTerminalTools(options) {
7670
+ const { cfg, taskId, workdir, rpc } = options;
7671
+ const StartWebIdeTerminal = {
7672
+ description: "Start a long-running WebIDE terminal (e.g. frontend/backend dev server). Process outlives this message. Same key already RUNNING/READY returns idempotently. Do NOT use Shell for long-lived dev servers. Returns terminalId, pid, ports.",
7673
+ inputSchema: {
7674
+ type: "object",
7675
+ properties: {
7676
+ key: {
7677
+ type: "string",
7678
+ description: "Stable key, e.g. frontend / backend"
7679
+ },
7680
+ name: { type: "string", description: "Display name" },
7681
+ cwd: {
7682
+ type: "string",
7683
+ description: "Working directory (absolute or under workspace)"
7684
+ },
7685
+ command: {
7686
+ type: "string",
7687
+ description: "Shell command to run, e.g. pnpm dev"
7688
+ },
7689
+ readyPattern: {
7690
+ type: "string",
7691
+ description: "Optional regex; when matched in logs, status becomes READY"
7692
+ },
7693
+ portsHint: {
7694
+ type: "array",
7695
+ items: { type: "number" },
7696
+ description: "Optional expected listen ports"
7697
+ }
7698
+ },
7699
+ required: ["key", "cwd", "command"]
7700
+ },
7701
+ execute: async (args) => {
7702
+ const key = asString3(args.key);
7703
+ const cwd = asString3(args.cwd) || workdir;
7704
+ const command = asString3(args.command);
7705
+ if (!key || !command) {
7706
+ return {
7707
+ content: [{ type: "text", text: "key / command \u4E0D\u80FD\u4E3A\u7A7A" }],
7708
+ isError: true
7709
+ };
7710
+ }
7711
+ try {
7712
+ const result = rpc ? await rpc.start({
7713
+ taskId,
7714
+ key,
7715
+ name: asString3(args.name) || void 0,
7716
+ cwd,
7717
+ command,
7718
+ readyPattern: asString3(args.readyPattern) || void 0,
7719
+ portsHint: asPorts(args.portsHint)
7720
+ }) : await (async () => {
7721
+ throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
7722
+ })();
7723
+ return JSON.stringify(result, null, 2);
7724
+ } catch (err) {
7725
+ const detail = err instanceof Error ? err.message : String(err);
7726
+ return {
7727
+ content: [{ type: "text", text: `\u542F\u52A8\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7728
+ isError: true
7729
+ };
7730
+ }
7731
+ }
7732
+ };
7733
+ const StopWebIdeTerminal = {
7734
+ description: "Stop a WebIDE terminal and kill its process tree. Soft-terminates (status EXITED, record kept).",
7735
+ inputSchema: {
7736
+ type: "object",
7737
+ properties: {
7738
+ key: { type: "string", description: "Terminal key" },
7739
+ terminalId: { type: "string", description: "Server terminal id" }
7740
+ }
7741
+ },
7742
+ execute: async (args) => {
7743
+ const key = asString3(args.key) || void 0;
7744
+ const terminalId = asString3(args.terminalId) || void 0;
7745
+ if (!key && !terminalId) {
7746
+ return {
7747
+ content: [{ type: "text", text: "\u9700\u8981 key \u6216 terminalId" }],
7748
+ isError: true
7749
+ };
7750
+ }
7751
+ try {
7752
+ const result = rpc ? await rpc.stop({ taskId, key, terminalId }) : await (async () => {
7753
+ throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
7754
+ })();
7755
+ return JSON.stringify(result, null, 2);
7756
+ } catch (err) {
7757
+ const detail = err instanceof Error ? err.message : String(err);
7758
+ return {
7759
+ content: [{ type: "text", text: `\u505C\u6B62\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7760
+ isError: true
7761
+ };
7762
+ }
7763
+ }
7764
+ };
7765
+ const StopAllWebIdeTerminals = {
7766
+ description: "Stop all living WebIDE terminals for the current task (kill process trees). Use before re-running local dev to avoid port conflicts. Soft-terminates records.",
7767
+ inputSchema: {
7768
+ type: "object",
7769
+ properties: {}
7770
+ },
7771
+ execute: async () => {
7772
+ try {
7773
+ const result = rpc ? await rpc.stopAll({ taskId }) : await (async () => {
7774
+ throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
7775
+ })();
7776
+ return JSON.stringify(result, null, 2);
7777
+ } catch (err) {
7778
+ const detail = err instanceof Error ? err.message : String(err);
7779
+ return {
7780
+ content: [{ type: "text", text: `\u5168\u90E8\u505C\u6B62\u5931\u8D25: ${detail}` }],
7781
+ isError: true
7782
+ };
7783
+ }
7784
+ }
7785
+ };
7786
+ const ListWebIdeTerminals = {
7787
+ description: "List living WebIDE terminals for the current task (STARTING/RUNNING/READY/STOPPING only). Does NOT include EXITED/FAILED. Returns id, key, status, pid, ports.",
7788
+ inputSchema: {
7789
+ type: "object",
7790
+ properties: {}
7791
+ },
7792
+ execute: async () => {
7793
+ try {
7794
+ if (rpc) {
7795
+ const result2 = await rpc.list({ taskId });
7796
+ return JSON.stringify(result2, null, 2);
7797
+ }
7798
+ const api = createApmApiClient(cfg);
7799
+ const result = await api.cli.webideListTerminals({ taskId });
7800
+ return JSON.stringify(result, null, 2);
7801
+ } catch (err) {
7802
+ const detail = err instanceof Error ? err.message : String(err);
7803
+ return {
7804
+ content: [{ type: "text", text: `\u5217\u51FA\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7805
+ isError: true
7806
+ };
7807
+ }
7808
+ }
7809
+ };
7810
+ return {
7811
+ StartWebIdeTerminal,
7812
+ StopWebIdeTerminal,
7813
+ StopAllWebIdeTerminals,
7814
+ ListWebIdeTerminals
7815
+ };
7816
+ }
7817
+
6991
7818
  // src/commands/connect/cursor-custom-tools.ts
6992
7819
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
6993
7820
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -7025,6 +7852,17 @@ function createCursorCustomTools(cfg, messageId, options) {
7025
7852
  cfg,
7026
7853
  taskId: options.taskId
7027
7854
  });
7855
+ if (options.workdir) {
7856
+ Object.assign(
7857
+ tools,
7858
+ createWebIdeTerminalTools({
7859
+ cfg,
7860
+ taskId: options.taskId,
7861
+ workdir: options.workdir,
7862
+ rpc: options.terminalRpc
7863
+ })
7864
+ );
7865
+ }
7028
7866
  }
7029
7867
  return tools;
7030
7868
  }
@@ -7093,8 +7931,8 @@ async function obtainAgent(ctx) {
7093
7931
  settingSources: ["project"]
7094
7932
  } : {}
7095
7933
  },
7096
- ...ctx.mode ? { mode: ctx.mode } : {}
7097
- // mcpServers: createPlaywrightMcpServers(),
7934
+ ...ctx.mode ? { mode: ctx.mode } : {},
7935
+ ...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
7098
7936
  };
7099
7937
  const explicitAgentId = ctx.resumeAgentId?.trim();
7100
7938
  const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
@@ -7148,7 +7986,9 @@ async function runCursorAgent(cfg, ctx, options) {
7148
7986
  appendMessageContent: options?.appendMessageContent,
7149
7987
  askQuestionExecute: options?.askQuestionExecute,
7150
7988
  enableWebIdePlanTools: options?.enableWebIdePlanTools,
7151
- taskId: options?.taskId
7989
+ taskId: options?.taskId,
7990
+ workdir,
7991
+ terminalRpc: options?.terminalRpc
7152
7992
  });
7153
7993
  const enableSandbox = Boolean(options?.enableSandbox);
7154
7994
  let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
@@ -7189,7 +8029,6 @@ async function runCursorAgent(cfg, ctx, options) {
7189
8029
  try {
7190
8030
  const run = await agent.send(prompt, {
7191
8031
  ...ctx.mode ? { mode: ctx.mode } : {},
7192
- // mcpServers: createPlaywrightMcpServers(),
7193
8032
  local: {
7194
8033
  ...options?.forceSend ? { force: true } : {},
7195
8034
  customTools
@@ -7290,6 +8129,7 @@ async function runCursorAgent(cfg, ctx, options) {
7290
8129
  }
7291
8130
 
7292
8131
  // src/commands/connect/ensure-message-reply.ts
8132
+ init_client();
7293
8133
  var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
7294
8134
  function resolveMessageReplyFallback(fallback) {
7295
8135
  for (const candidate of [
@@ -7433,6 +8273,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
7433
8273
  }
7434
8274
 
7435
8275
  // src/commands/connect/webide-worker-pool.ts
8276
+ init_webide_terminal_registry();
7436
8277
  import { Worker } from "node:worker_threads";
7437
8278
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7438
8279
  import { dirname as dirname7, join as join20 } from "node:path";
@@ -7444,23 +8285,78 @@ function spawnWebIdeMessageWorker(cfg, msg) {
7444
8285
  const worker = new Worker(workerFile, {
7445
8286
  workerData: { cfg, msg }
7446
8287
  });
8288
+ const registry = getWebIdeTerminalRegistry(cfg);
7447
8289
  let resolveDone;
7448
8290
  const done = new Promise((resolve7) => {
7449
8291
  resolveDone = resolve7;
7450
8292
  });
7451
- worker.on(
7452
- "message",
7453
- (m) => {
7454
- if (m?.type === "done" || m?.type === "error") {
7455
- if (m.type === "error") {
7456
- console.error(
7457
- `[apm] webide worker error messageId=${m.messageId}: ${m.error}`
7458
- );
8293
+ worker.on("message", (m) => {
8294
+ if (m?.type === "terminal-rpc") {
8295
+ const rpc = m;
8296
+ void (async () => {
8297
+ try {
8298
+ let result;
8299
+ if (rpc.op === "start") {
8300
+ result = await registry.start({
8301
+ taskId: String(rpc.args.taskId ?? msg.taskId),
8302
+ key: String(rpc.args.key ?? ""),
8303
+ name: typeof rpc.args.name === "string" ? rpc.args.name : void 0,
8304
+ cwd: String(rpc.args.cwd ?? msg.workdir),
8305
+ command: String(rpc.args.command ?? ""),
8306
+ readyPattern: typeof rpc.args.readyPattern === "string" ? rpc.args.readyPattern : void 0,
8307
+ portsHint: Array.isArray(rpc.args.portsHint) ? rpc.args.portsHint : void 0
8308
+ });
8309
+ } else if (rpc.op === "stop") {
8310
+ const terminalId = typeof rpc.args.terminalId === "string" ? rpc.args.terminalId : void 0;
8311
+ const key = typeof rpc.args.key === "string" ? rpc.args.key : void 0;
8312
+ const taskId = String(rpc.args.taskId ?? msg.taskId);
8313
+ let id = terminalId;
8314
+ if (!id && key) {
8315
+ const local = registry.listLocal(taskId).find((t) => t.key === key);
8316
+ id = local?.id;
8317
+ }
8318
+ if (!id) {
8319
+ throw new Error("\u672A\u627E\u5230\u7EC8\u7AEF");
8320
+ }
8321
+ result = await registry.stop(id, { reason: "ai-stop" });
8322
+ } else if (rpc.op === "stopAll") {
8323
+ result = await registry.stopAllForTask(
8324
+ String(rpc.args.taskId ?? msg.taskId),
8325
+ "ai-stop-all"
8326
+ );
8327
+ } else {
8328
+ result = {
8329
+ terminals: registry.listLocal(
8330
+ String(rpc.args.taskId ?? msg.taskId)
8331
+ )
8332
+ };
8333
+ }
8334
+ worker.postMessage({
8335
+ type: "terminal-rpc-result",
8336
+ id: rpc.id,
8337
+ ok: true,
8338
+ result
8339
+ });
8340
+ } catch (err) {
8341
+ worker.postMessage({
8342
+ type: "terminal-rpc-result",
8343
+ id: rpc.id,
8344
+ ok: false,
8345
+ error: err instanceof Error ? err.message : String(err)
8346
+ });
7459
8347
  }
7460
- void worker.terminate().finally(() => resolveDone());
8348
+ })();
8349
+ return;
8350
+ }
8351
+ if (m?.type === "done" || m?.type === "error") {
8352
+ if (m.type === "error") {
8353
+ console.error(
8354
+ `[apm] webide worker error messageId=${m.messageId}: ${m.error}`
8355
+ );
7461
8356
  }
8357
+ void worker.terminate().finally(() => resolveDone());
7462
8358
  }
7463
- );
8359
+ });
7464
8360
  worker.on("error", (err) => {
7465
8361
  console.error("[apm] webide worker crashed:", err);
7466
8362
  resolveDone();
@@ -7565,7 +8461,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
7565
8461
  if (!pullRan) {
7566
8462
  await runStep(
7567
8463
  "sync-project-documents-pull",
7568
- () => syncRepositoryProjectDocumentsPull(workdir, apmRoot)
8464
+ () => syncProjectDocumentsPull(workdir, apmRoot)
7569
8465
  );
7570
8466
  }
7571
8467
  const agentResult = await runStep(
@@ -7598,7 +8494,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
7598
8494
  );
7599
8495
  await runStep(
7600
8496
  "sync-project-documents",
7601
- () => syncRepositoryProjectDocumentsPush(cfg, workdir, apmRoot)
8497
+ () => syncProjectDocumentsPush(cfg, workdir, apmRoot)
7602
8498
  );
7603
8499
  await runStep(
7604
8500
  "commit-files",
@@ -7737,6 +8633,11 @@ function attachWsHandlers(ws, ctx, onOpen) {
7737
8633
  const msg2 = validated.data;
7738
8634
  const task2 = (async () => {
7739
8635
  const workdir = requireRemoteWorkdir(msg2.workdir);
8636
+ const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
8637
+ await getWebIdeTerminalRegistry2(cfg).stopAllForTask(
8638
+ msg2.taskId,
8639
+ "cache-cleanup"
8640
+ );
7740
8641
  cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
7741
8642
  })();
7742
8643
  activeTasks.add(task2);
@@ -7745,6 +8646,21 @@ function attachWsHandlers(ws, ctx, onOpen) {
7745
8646
  });
7746
8647
  return;
7747
8648
  }
8649
+ if (validated.data.type === "webide-terminal-stop") {
8650
+ const msg2 = validated.data;
8651
+ const task2 = (async () => {
8652
+ const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
8653
+ await getWebIdeTerminalRegistry2(cfg).stop(msg2.terminalId, {
8654
+ pid: msg2.pid,
8655
+ reason: "web-stop"
8656
+ });
8657
+ })();
8658
+ activeTasks.add(task2);
8659
+ void task2.finally(() => {
8660
+ activeTasks.delete(task2);
8661
+ });
8662
+ return;
8663
+ }
7748
8664
  if (validated.data.type === "webide-message") {
7749
8665
  const msg2 = validated.data;
7750
8666
  const perMessageController2 = new AbortController();
@@ -8009,6 +8925,10 @@ async function runConnect(options) {
8009
8925
  }
8010
8926
  }
8011
8927
 
8928
+ // src/commands/create-pr.ts
8929
+ init_client();
8930
+ init_config();
8931
+
8012
8932
  // src/commands/create-pr-errors.ts
8013
8933
  import { ApiError as ApiError2 } from "listpage-http";
8014
8934
  function extractMessage(error) {
@@ -8136,6 +9056,8 @@ async function runCreatePr(options) {
8136
9056
  }
8137
9057
 
8138
9058
  // src/commands/deploy/deploy-tracking.ts
9059
+ init_client();
9060
+ init_config();
8139
9061
  function normalizeDeployEnvironment(env) {
8140
9062
  const normalized = env.trim().toLowerCase();
8141
9063
  if (normalized === "test") return "TEST";
@@ -8805,6 +9727,7 @@ function registerDeployBackendCommands(program) {
8805
9727
  // src/commands/deploy/frontend.ts
8806
9728
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
8807
9729
  import path17 from "node:path";
9730
+ init_minio();
8808
9731
  function artifactObjectKey(namePrefix, branchSegment, relativePath) {
8809
9732
  const base = sanitizeRelativePath(namePrefix);
8810
9733
  const rel = sanitizeRelativePath(relativePath);
@@ -8982,6 +9905,7 @@ function registerDeployFrontendCommands(program) {
8982
9905
 
8983
9906
  // src/commands/deploy/sftp.ts
8984
9907
  import path18 from "node:path";
9908
+ init_minio();
8985
9909
  function registerDeploySftpCommands(program) {
8986
9910
  program.command("deploy-sftp").description(
8987
9911
  "\u5C06\u6307\u5B9A\u76EE\u5F55\u538B\u7F29\u4E3A zip \u540E\u901A\u8FC7 SFTP \u4E0A\u4F20\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u8FDE\u63A5\u4FE1\u606F\u89C1 apm.config.json wisdomDeploy\uFF09"
@@ -9107,12 +10031,12 @@ function buildProgram() {
9107
10031
  await runSyncDeployConfig();
9108
10032
  });
9109
10033
  program.command("sync-project-documents").description(
9110
- "\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/project/\uFF1B\u52A0 --push \u5C06\u672C\u5730\u53D8\u66F4\u63A8\u9001\u5230\u5E73\u53F0"
9111
- ).option("--push", "\u63A8\u9001\u672C\u5730 .apm/project/ \u5230\u5E73\u53F0").option("--pull", "\u4ECE\u5E73\u53F0\u62C9\u53D6\u5230 .apm/project/").action(async (opts) => {
10034
+ "\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/docs/\uFF1B\u52A0 --push \u5C06\u672C\u5730\u53D8\u66F4\u63A8\u9001\u5230\u5E73\u53F0"
10035
+ ).option("--push", "\u63A8\u9001\u672C\u5730 .apm/docs/ \u5230\u5E73\u53F0").option("--pull", "\u4ECE\u5E73\u53F0\u62C9\u53D6\u5230 .apm/docs/").action(async (opts) => {
9112
10036
  await runSyncProjectDocuments(opts);
9113
10037
  });
9114
10038
  program.command("pull").description(
9115
- "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF0C\u5E76\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u4E0E\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u5230 .apm/project/"
10039
+ "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF0C\u5E76\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u4E0E\u9879\u76EE\u6587\u6863\u5230 .apm/docs/"
9116
10040
  ).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
9117
10041
  await runPull(sessionId);
9118
10042
  });