ai-project-manage-cli 7.1.12 → 7.1.13

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,995 @@ 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
+ branchBaseline: defineEndpoint({
216
+ method: "GET",
217
+ path: "/cli/tasks/branch-baseline"
218
+ }),
219
+ listSessionsForBranchCleanup: defineEndpoint({
220
+ method: "GET",
221
+ path: "/cli/sessions/branch-cleanup"
222
+ }),
223
+ workspaceBaseline: defineEndpoint({
224
+ method: "GET",
225
+ path: "/cli/workspaces/baseline"
226
+ }),
227
+ getDeploymentConfiguration: defineEndpoint({
228
+ method: "GET",
229
+ path: "/cli/deployment-configurations"
230
+ }),
231
+ matchRepository: defineEndpoint({
232
+ method: "GET",
233
+ path: "/cli/repositories/match"
234
+ }),
235
+ listSkills: defineEndpoint({
236
+ method: "GET",
237
+ path: "/cli/skills"
238
+ }),
239
+ listRules: defineEndpoint({
240
+ method: "GET",
241
+ path: "/cli/rules"
242
+ }),
243
+ createPullRequest: defineEndpoint({
244
+ method: "POST",
245
+ path: "/cli/pull-requests"
246
+ }),
247
+ createWebIdeDraftPullRequest: defineEndpoint({
248
+ method: "POST",
249
+ path: "/cli/webide/draft-pull-requests"
250
+ }),
251
+ webideMergePullRequests: defineEndpoint({
252
+ method: "POST",
253
+ path: "/cli/webide/pull-requests/merge"
254
+ }),
255
+ getRepositoryProjectDocumentManifest: defineEndpoint({
256
+ method: "GET",
257
+ path: "/cli/repository-project-documents/manifest"
258
+ }),
259
+ listRepositoryProjectDocuments: defineEndpoint({
260
+ method: "GET",
261
+ path: "/cli/repository-project-documents"
262
+ }),
263
+ upsertRepositoryProjectDocument: defineEndpoint({
264
+ method: "PUT",
265
+ path: "/cli/repository-project-documents/upsert"
266
+ }),
267
+ removeRepositoryProjectDocument: defineEndpoint({
268
+ method: "DELETE",
269
+ path: "/cli/repository-project-documents"
270
+ }),
271
+ createTaskDeployment: defineEndpoint({
272
+ method: "POST",
273
+ path: "/cli/task-deployments"
274
+ }),
275
+ updateTaskDeploymentStatus: defineEndpoint({
276
+ method: "PUT",
277
+ path: "/cli/task-deployments/status"
278
+ }),
279
+ syncTaskDeploymentLog: defineEndpoint({
280
+ method: "PUT",
281
+ path: "/cli/task-deployments/log"
282
+ }),
283
+ completeTaskDeployment: defineEndpoint({
284
+ method: "PUT",
285
+ path: "/cli/task-deployments/complete"
286
+ }),
287
+ getDeployArtifactStorage: defineEndpoint(
288
+ {
289
+ method: "GET",
290
+ path: "/cli/deploy-artifact-storage"
291
+ }
292
+ ),
293
+ attachTaskDeploymentArtifact: defineEndpoint({
294
+ method: "PUT",
295
+ path: "/cli/task-deployments/artifact"
296
+ })
297
+ }
298
+ };
299
+ }
300
+ });
301
+
302
+ // src/api/client.ts
303
+ import { createApiClient } from "listpage-http";
304
+ function createApmApiClient(cfg) {
305
+ const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
306
+ return createApiClient(requestConfig, {
307
+ baseURL,
308
+ getToken: () => resolveApiKey(cfg) || void 0,
309
+ successCodes: [0],
310
+ unauthorizedCodes: [401]
311
+ });
312
+ }
313
+ var init_client = __esm({
314
+ "src/api/client.ts"() {
315
+ "use strict";
316
+ init_request_config();
317
+ init_config();
318
+ }
319
+ });
320
+
321
+ // src/commands/deploy/internal/minio.ts
322
+ import { statSync as statSync5 } from "node:fs";
323
+ import { readdir, readFile } from "node:fs/promises";
324
+ import path from "node:path";
325
+ import * as Minio from "minio";
326
+ async function isDirectoryPath(dir) {
327
+ try {
328
+ const st = statSync5(dir);
329
+ return st.isDirectory();
330
+ } catch {
331
+ return false;
332
+ }
333
+ }
334
+ function sanitizeRelativePath(rel) {
335
+ const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
336
+ const segments = norm.split("/").filter(Boolean);
337
+ for (const s of segments) {
338
+ if (s === "." || s === "..") {
339
+ throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
340
+ }
341
+ }
342
+ return segments.join("/");
343
+ }
344
+ async function collectFiles(root) {
345
+ const out = [];
346
+ async function walk(dir, prefix) {
347
+ const entries = await readdir(dir, { withFileTypes: true });
348
+ for (const e of entries) {
349
+ const name = e.name;
350
+ if (name === "." || name === "..") {
351
+ continue;
352
+ }
353
+ const abs = path.join(dir, name);
354
+ const rel = prefix ? `${prefix}/${name}` : name;
355
+ if (e.isDirectory()) {
356
+ await walk(abs, rel);
357
+ } else if (e.isFile()) {
358
+ const st = statSync5(abs);
359
+ out.push({
360
+ absPath: abs,
361
+ relativePath: rel.replace(/\\/g, "/"),
362
+ size: st.size
363
+ });
364
+ }
365
+ }
366
+ }
367
+ await walk(root, "");
368
+ out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
369
+ return out;
370
+ }
371
+ async function readArtifactFile(absPath) {
372
+ return readFile(absPath);
373
+ }
374
+ function toMB(bytes) {
375
+ return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
376
+ }
377
+ function detectMimeType(filePath) {
378
+ const ext = path.extname(filePath).toLowerCase();
379
+ return MIME[ext] ?? "";
380
+ }
381
+ var DEFAULT_MAX_FILE_SIZE_MB, MIME, MinioClient;
382
+ var init_minio = __esm({
383
+ "src/commands/deploy/internal/minio.ts"() {
384
+ "use strict";
385
+ DEFAULT_MAX_FILE_SIZE_MB = 50;
386
+ MIME = {
387
+ ".html": "text/html; charset=utf-8",
388
+ ".css": "text/css; charset=utf-8",
389
+ ".js": "application/javascript; charset=utf-8",
390
+ ".json": "application/json; charset=utf-8",
391
+ ".svg": "image/svg+xml",
392
+ ".png": "image/png",
393
+ ".jpg": "image/jpeg",
394
+ ".jpeg": "image/jpeg",
395
+ ".gif": "image/gif",
396
+ ".webp": "image/webp",
397
+ ".woff": "font/woff",
398
+ ".woff2": "font/woff2",
399
+ ".ttf": "font/ttf",
400
+ ".ico": "image/x-icon",
401
+ ".txt": "text/plain; charset=utf-8",
402
+ ".map": "application/json"
403
+ };
404
+ MinioClient = class {
405
+ inner;
406
+ constructor(opts) {
407
+ const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
408
+ this.inner = new Minio.Client({
409
+ endPoint,
410
+ port: opts.port,
411
+ useSSL: opts.useSSL,
412
+ accessKey: opts.accessKey,
413
+ secretKey: opts.secretKey
414
+ });
415
+ }
416
+ async ensureBucket(bucket) {
417
+ const exists = await this.inner.bucketExists(bucket);
418
+ if (!exists) {
419
+ await this.inner.makeBucket(bucket);
420
+ }
421
+ }
422
+ async deleteObjectsByPrefix(bucket, prefix) {
423
+ const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
424
+ const keys = [];
425
+ await new Promise((resolve7, reject) => {
426
+ objectsStream.on("data", (obj) => {
427
+ if (obj.name) {
428
+ keys.push(obj.name);
429
+ }
430
+ });
431
+ objectsStream.on("error", reject);
432
+ objectsStream.on("end", resolve7);
433
+ });
434
+ const chunkSize = 500;
435
+ for (let i = 0; i < keys.length; i += chunkSize) {
436
+ const chunk = keys.slice(i, i + chunkSize);
437
+ await this.inner.removeObjects(
438
+ bucket,
439
+ chunk.map((name) => name)
440
+ );
441
+ }
442
+ }
443
+ async putObject(bucket, objectKey, body, meta) {
444
+ await this.inner.putObject(bucket, objectKey, body, body.length, meta);
445
+ }
446
+ /** 匿名可读当前桶全部对象(便于静态站点直链) */
447
+ async setBucketPublicRead(bucket) {
448
+ const policy = {
449
+ Version: "2012-10-17",
450
+ Statement: [
451
+ {
452
+ Effect: "Allow",
453
+ Principal: { AWS: ["*"] },
454
+ Action: ["s3:GetObject"],
455
+ Resource: [`arn:aws:s3:::${bucket}/*`]
456
+ }
457
+ ]
458
+ };
459
+ await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
460
+ }
461
+ };
462
+ }
463
+ });
464
+
465
+ // src/commands/deploy/internal/deploy-artifact-minio.ts
466
+ import { readFile as readFile2 } from "node:fs/promises";
467
+ function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
468
+ const pad2 = (n) => String(n).padStart(2, "0");
469
+ return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}${pad2(date.getHours())}${pad2(date.getMinutes())}${pad2(date.getSeconds())}`;
470
+ }
471
+ function sanitizeDeployProjectName(name) {
472
+ const trimmed = name.trim().replace(/\\/g, "/");
473
+ const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
474
+ try {
475
+ return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
476
+ } catch {
477
+ return "project";
478
+ }
479
+ }
480
+ function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
481
+ const safeName = sanitizeDeployProjectName(projectName);
482
+ const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
483
+ return `${timestamp}-${safeName}.${suffix}`;
484
+ }
485
+ function buildDeployArtifactObjectKey(projectName, fileName) {
486
+ const safeProject = sanitizeDeployProjectName(projectName);
487
+ return `deploy/${safeProject}/${fileName}`;
488
+ }
489
+ function resolveDeploymentRunIdFromEnv() {
490
+ const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
491
+ return raw || null;
492
+ }
493
+ async function fetchDeployArtifactStorage(api) {
494
+ return api.cli.getDeployArtifactStorage(void 0);
495
+ }
496
+ async function uploadDeployArtifactZip(options) {
497
+ const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
498
+ if (!deploymentRunId && !options.uploadWithoutRunId) {
499
+ return null;
500
+ }
501
+ const cfg = await tryReadApmConfig();
502
+ if (!cfg || !resolveApiKey(cfg)) {
503
+ console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
504
+ return null;
505
+ }
506
+ const api = options.api ?? createApmApiClient(cfg);
507
+ let storage;
508
+ try {
509
+ storage = await fetchDeployArtifactStorage(api);
510
+ } catch (error) {
511
+ const detail = error instanceof Error ? error.message : String(error);
512
+ console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
513
+ return null;
514
+ }
515
+ const fileName = buildDeployArtifactFileName(
516
+ options.kind,
517
+ options.projectName,
518
+ options.timestamp
519
+ );
520
+ const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
521
+ let body;
522
+ try {
523
+ body = await readFile2(options.zipPath);
524
+ } catch (error) {
525
+ const detail = error instanceof Error ? error.message : String(error);
526
+ console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
527
+ return null;
528
+ }
529
+ try {
530
+ const client = new MinioClient({
531
+ endPoint: storage.endpoint,
532
+ port: storage.port,
533
+ useSSL: storage.useSsl,
534
+ accessKey: storage.accessKey,
535
+ secretKey: storage.secretKey
536
+ });
537
+ await client.ensureBucket(storage.bucket);
538
+ await client.putObject(storage.bucket, objectKey, body, {
539
+ "Content-Type": "application/zip"
540
+ });
541
+ console.error(
542
+ `[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(
543
+ 2
544
+ )} MB)`
545
+ );
546
+ } catch (error) {
547
+ const detail = error instanceof Error ? error.message : String(error);
548
+ console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
549
+ return null;
550
+ }
551
+ if (deploymentRunId) {
552
+ try {
553
+ await api.cli.attachTaskDeploymentArtifact({
554
+ id: deploymentRunId,
555
+ artifactObjectKey: objectKey,
556
+ artifactFileName: fileName
557
+ });
558
+ console.error(
559
+ `[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
560
+ );
561
+ } catch (error) {
562
+ const detail = error instanceof Error ? error.message : String(error);
563
+ console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
564
+ return null;
565
+ }
566
+ }
567
+ return {
568
+ objectKey,
569
+ fileName,
570
+ bucket: storage.bucket
571
+ };
572
+ }
573
+ var APM_DEPLOYMENT_RUN_ID_ENV;
574
+ var init_deploy_artifact_minio = __esm({
575
+ "src/commands/deploy/internal/deploy-artifact-minio.ts"() {
576
+ "use strict";
577
+ init_client();
578
+ init_config();
579
+ init_minio();
580
+ init_minio();
581
+ APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
582
+ }
583
+ });
584
+
585
+ // src/commands/connect/webide-terminal-registry.ts
586
+ var webide_terminal_registry_exports = {};
587
+ __export(webide_terminal_registry_exports, {
588
+ WEBIDE_TERMINAL_LINES_PER_CHUNK: () => WEBIDE_TERMINAL_LINES_PER_CHUNK,
589
+ WebIdeTerminalRegistry: () => WebIdeTerminalRegistry,
590
+ getWebIdeTerminalRegistry: () => getWebIdeTerminalRegistry
591
+ });
592
+ import { spawn } from "node:child_process";
593
+ import { execFile as execFile3 } from "node:child_process";
594
+ import { promisify as promisify3 } from "node:util";
595
+ function taskKey(taskId, key) {
596
+ return `${taskId}\0${key}`;
597
+ }
598
+ function chunkKey(prefix, chunkIndex) {
599
+ return `${prefix}chunks/${String(chunkIndex).padStart(6, "0")}.json`;
600
+ }
601
+ function extractPortsAndUrl(text) {
602
+ const ports = /* @__PURE__ */ new Set();
603
+ let primaryUrl = null;
604
+ let m;
605
+ const re = new RegExp(PORT_URL_RE.source, "gi");
606
+ while ((m = re.exec(text)) !== null) {
607
+ const urlCandidate = m[2];
608
+ if (urlCandidate) {
609
+ primaryUrl = primaryUrl ?? urlCandidate.replace(/[),.;]+$/, "");
610
+ try {
611
+ const u = new URL(primaryUrl);
612
+ if (u.port) ports.add(Number(u.port));
613
+ else if (u.protocol === "https:") ports.add(443);
614
+ else ports.add(80);
615
+ } catch {
616
+ }
617
+ }
618
+ for (const g of [m[1], m[3], m[4]]) {
619
+ if (g) {
620
+ const p = Number(g);
621
+ if (p > 0 && p < 65536) ports.add(p);
622
+ }
623
+ }
624
+ }
625
+ const httpMatch = text.match(/https?:\/\/[^\s)'"]+/i);
626
+ if (httpMatch && !primaryUrl) {
627
+ primaryUrl = httpMatch[0].replace(/[),.;]+$/, "");
628
+ }
629
+ return { ports: [...ports].sort((a, b) => a - b), primaryUrl };
630
+ }
631
+ function killProcessTree(pid) {
632
+ try {
633
+ process.kill(-pid, "SIGTERM");
634
+ } catch {
635
+ try {
636
+ process.kill(pid, "SIGTERM");
637
+ } catch {
638
+ }
639
+ }
640
+ }
641
+ function forceKillProcessTree(pid) {
642
+ try {
643
+ process.kill(-pid, "SIGKILL");
644
+ } catch {
645
+ try {
646
+ process.kill(pid, "SIGKILL");
647
+ } catch {
648
+ }
649
+ }
650
+ }
651
+ async function probeListenPorts(pid) {
652
+ try {
653
+ const { stdout } = await execFileAsync3(
654
+ "lsof",
655
+ ["-nP", `-p${pid}`, "-iTCP", "-sTCP:LISTEN"],
656
+ { timeout: 3e3, maxBuffer: 1024 * 1024 }
657
+ );
658
+ const ports = /* @__PURE__ */ new Set();
659
+ for (const line of stdout.split("\n")) {
660
+ const m = line.match(/:(\d+)\s+\(LISTEN\)/);
661
+ if (m) {
662
+ const p = Number(m[1]);
663
+ if (p > 0 && p < 65536) ports.add(p);
664
+ }
665
+ }
666
+ return [...ports].sort((a, b) => a - b);
667
+ } catch {
668
+ return [];
669
+ }
670
+ }
671
+ function getWebIdeTerminalRegistry(cfg) {
672
+ if (!registrySingleton) {
673
+ registrySingleton = new WebIdeTerminalRegistry(cfg);
674
+ }
675
+ return registrySingleton;
676
+ }
677
+ var execFileAsync3, WEBIDE_TERMINAL_LINES_PER_CHUNK, SYNC_INTERVAL_MS, PORT_PROBE_INTERVAL_MS, STOP_SIGKILL_MS, PORT_URL_RE, WebIdeTerminalRegistry, registrySingleton;
678
+ var init_webide_terminal_registry = __esm({
679
+ "src/commands/connect/webide-terminal-registry.ts"() {
680
+ "use strict";
681
+ init_client();
682
+ init_deploy_artifact_minio();
683
+ init_minio();
684
+ execFileAsync3 = promisify3(execFile3);
685
+ WEBIDE_TERMINAL_LINES_PER_CHUNK = 50;
686
+ SYNC_INTERVAL_MS = 400;
687
+ PORT_PROBE_INTERVAL_MS = 2e3;
688
+ STOP_SIGKILL_MS = 5e3;
689
+ 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;
690
+ WebIdeTerminalRegistry = class {
691
+ constructor(cfg) {
692
+ this.cfg = cfg;
693
+ }
694
+ cfg;
695
+ byId = /* @__PURE__ */ new Map();
696
+ byTaskKey = /* @__PURE__ */ new Map();
697
+ minio;
698
+ bucket = "";
699
+ async ensureMinio() {
700
+ if (this.minio) return;
701
+ const storage = await fetchDeployArtifactStorage(
702
+ createApmApiClient(this.cfg)
703
+ );
704
+ this.minio = new MinioClient({
705
+ endPoint: storage.endpoint,
706
+ port: storage.port,
707
+ useSSL: storage.useSsl,
708
+ accessKey: storage.accessKey,
709
+ secretKey: storage.secretKey
710
+ });
711
+ this.bucket = storage.bucket;
712
+ await this.minio.ensureBucket(this.bucket);
713
+ }
714
+ listLocal(taskId) {
715
+ const alive = [
716
+ "STARTING",
717
+ "RUNNING",
718
+ "READY",
719
+ "STOPPING"
720
+ ];
721
+ const sessions = [...this.byId.values()].filter(
722
+ (s) => alive.includes(s.status)
723
+ );
724
+ return (taskId ? sessions.filter((s) => s.taskId === taskId) : sessions).map((s) => ({
725
+ id: s.id,
726
+ taskId: s.taskId,
727
+ key: s.key,
728
+ name: s.name,
729
+ status: s.status,
730
+ pid: s.pid,
731
+ ports: s.ports,
732
+ primaryUrl: s.primaryUrl,
733
+ nextSeq: s.nextSeq,
734
+ command: s.command,
735
+ cwd: s.cwd
736
+ }));
737
+ }
738
+ async start(opts) {
739
+ const key = opts.key.trim();
740
+ const existingId = this.byTaskKey.get(taskKey(opts.taskId, key));
741
+ const existing = existingId ? this.byId.get(existingId) : void 0;
742
+ if (existing?.child && (existing.status === "RUNNING" || existing.status === "READY" || existing.status === "STARTING")) {
743
+ return {
744
+ ok: true,
745
+ idempotent: true,
746
+ terminalId: existing.id,
747
+ status: existing.status,
748
+ pid: existing.pid,
749
+ ports: existing.ports,
750
+ primaryUrl: existing.primaryUrl
751
+ };
752
+ }
753
+ const api = createApmApiClient(this.cfg);
754
+ const upserted = await api.cli.webideUpsertTerminal({
755
+ taskId: opts.taskId,
756
+ key,
757
+ name: opts.name ?? key,
758
+ cwd: opts.cwd,
759
+ command: opts.command,
760
+ readyPattern: opts.readyPattern,
761
+ portsHint: opts.portsHint
762
+ });
763
+ const terminalId = upserted.id;
764
+ const objectPrefix = upserted.objectPrefix?.trim() || `terminals/${opts.taskId}/${terminalId}/`;
765
+ if (existing?.child) {
766
+ await this.stop(terminalId, { reason: "restart" });
767
+ }
768
+ const session = {
769
+ id: terminalId,
770
+ taskId: opts.taskId,
771
+ key,
772
+ name: (opts.name ?? key).trim() || key,
773
+ cwd: opts.cwd,
774
+ command: opts.command,
775
+ readyPattern: opts.readyPattern?.trim() || null,
776
+ status: "STARTING",
777
+ pid: null,
778
+ ports: opts.portsHint ? [...opts.portsHint] : [],
779
+ primaryUrl: null,
780
+ nextSeq: typeof upserted.nextSeq === "number" ? upserted.nextSeq : 0,
781
+ chunkCount: typeof upserted.chunkCount === "number" ? upserted.chunkCount : 0,
782
+ objectPrefix,
783
+ child: null,
784
+ stopping: false,
785
+ pendingLines: [],
786
+ syncChain: Promise.resolve()
787
+ };
788
+ this.byId.set(terminalId, session);
789
+ this.byTaskKey.set(taskKey(opts.taskId, key), terminalId);
790
+ this.pushSystemLine(
791
+ session,
792
+ `[apm] starting: ${opts.command} (cwd=${opts.cwd})`
793
+ );
794
+ const child = spawn(opts.command, {
795
+ cwd: opts.cwd,
796
+ shell: true,
797
+ detached: true,
798
+ env: process.env,
799
+ stdio: ["ignore", "pipe", "pipe"]
800
+ });
801
+ session.child = child;
802
+ session.pid = child.pid ?? null;
803
+ session.status = "RUNNING";
804
+ await api.cli.webideUpdateTerminalStatus({
805
+ id: terminalId,
806
+ status: "RUNNING",
807
+ pid: session.pid,
808
+ ports: session.ports,
809
+ primaryUrl: null
810
+ });
811
+ const onData = (stream) => (buf) => {
812
+ const text = buf.toString("utf8");
813
+ for (const line of text.split(/\r?\n/)) {
814
+ if (!line.length && text.endsWith("\n") === false) continue;
815
+ if (line.length === 0) continue;
816
+ this.pushLine(session, stream, line);
817
+ }
818
+ this.scheduleSync(session);
819
+ this.maybeUpdatePortsFromText(session, text);
820
+ };
821
+ child.stdout?.on("data", onData("stdout"));
822
+ child.stderr?.on("data", onData("stderr"));
823
+ child.on("exit", (code, signal) => {
824
+ void this.onExit(session, code, signal);
825
+ });
826
+ session.portTimer = setInterval(() => {
827
+ void this.probePorts(session);
828
+ }, PORT_PROBE_INTERVAL_MS);
829
+ this.scheduleSync(session);
830
+ return {
831
+ ok: true,
832
+ idempotent: false,
833
+ terminalId,
834
+ status: session.status,
835
+ pid: session.pid,
836
+ ports: session.ports,
837
+ primaryUrl: session.primaryUrl
838
+ };
839
+ }
840
+ async stop(terminalId, opts) {
841
+ const session = this.byId.get(terminalId);
842
+ const api = createApmApiClient(this.cfg);
843
+ if (!session) {
844
+ const pid2 = opts?.pid;
845
+ if (typeof pid2 === "number" && pid2 > 0) {
846
+ killProcessTree(pid2);
847
+ setTimeout(() => forceKillProcessTree(pid2), STOP_SIGKILL_MS);
848
+ }
849
+ try {
850
+ await api.cli.webideUpdateTerminalStatus({
851
+ id: terminalId,
852
+ status: "EXITED",
853
+ lastError: opts?.reason ?? "stopped"
854
+ });
855
+ } catch {
856
+ }
857
+ return { ok: true, terminalId, status: "EXITED" };
858
+ }
859
+ if (session.status === "EXITED" || session.status === "FAILED" || session.stopping) {
860
+ return { ok: true, terminalId, status: session.status };
861
+ }
862
+ session.stopping = true;
863
+ session.status = "STOPPING";
864
+ this.pushSystemLine(
865
+ session,
866
+ `[apm] stopping${opts?.reason ? `: ${opts.reason}` : ""}`
867
+ );
868
+ this.scheduleSync(session);
869
+ try {
870
+ await api.cli.webideUpdateTerminalStatus({
871
+ id: terminalId,
872
+ status: "STOPPING",
873
+ pid: session.pid
874
+ });
875
+ } catch {
876
+ }
877
+ const pid = session.pid ?? opts?.pid ?? null;
878
+ if (session.child && !session.child.killed) {
879
+ try {
880
+ if (session.child.pid) killProcessTree(session.child.pid);
881
+ } catch {
882
+ }
883
+ } else if (typeof pid === "number" && pid > 0) {
884
+ killProcessTree(pid);
885
+ }
886
+ const killPid = session.child?.pid ?? pid;
887
+ if (typeof killPid === "number" && killPid > 0) {
888
+ setTimeout(() => {
889
+ if (session.child && !session.child.killed) {
890
+ forceKillProcessTree(killPid);
891
+ }
892
+ }, STOP_SIGKILL_MS);
893
+ }
894
+ return { ok: true, terminalId, status: "STOPPING" };
895
+ }
896
+ async stopAllForTask(taskId, reason = "cache-cleanup") {
897
+ const ids = [...this.byId.values()].filter((s) => s.taskId === taskId).map((s) => s.id);
898
+ for (const id of ids) {
899
+ await this.stop(id, { reason });
900
+ }
901
+ return { ok: true, stopped: ids.length };
902
+ }
903
+ pushSystemLine(session, text) {
904
+ this.pushLine(session, "system", text);
905
+ }
906
+ pushLine(session, stream, text) {
907
+ session.nextSeq += 1;
908
+ session.pendingLines.push({
909
+ seq: session.nextSeq,
910
+ stream,
911
+ text,
912
+ ts: (/* @__PURE__ */ new Date()).toISOString()
913
+ });
914
+ }
915
+ maybeUpdatePortsFromText(session, text) {
916
+ const { ports, primaryUrl } = extractPortsAndUrl(text);
917
+ let changed = false;
918
+ for (const p of ports) {
919
+ if (!session.ports.includes(p)) {
920
+ session.ports.push(p);
921
+ changed = true;
922
+ }
923
+ }
924
+ session.ports.sort((a, b) => a - b);
925
+ if (primaryUrl && !session.primaryUrl) {
926
+ session.primaryUrl = primaryUrl;
927
+ changed = true;
928
+ }
929
+ if (changed) {
930
+ void this.syncStatus(session);
931
+ }
932
+ if (session.readyPattern && session.status === "RUNNING" && new RegExp(session.readyPattern).test(text)) {
933
+ session.status = "READY";
934
+ void this.syncStatus(session);
935
+ } else if (session.status === "RUNNING" && (session.ports.length > 0 || session.primaryUrl)) {
936
+ session.status = "READY";
937
+ void this.syncStatus(session);
938
+ }
939
+ }
940
+ async probePorts(session) {
941
+ if (session.status !== "RUNNING" && session.status !== "STARTING" && session.status !== "READY") {
942
+ return;
943
+ }
944
+ if (!session.pid) return;
945
+ const ports = await probeListenPorts(session.pid);
946
+ if (ports.length === 0) return;
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
+ if (changed) {
955
+ session.ports.sort((a, b) => a - b);
956
+ if (!session.primaryUrl && session.ports[0]) {
957
+ session.primaryUrl = `http://127.0.0.1:${session.ports[0]}`;
958
+ }
959
+ if (session.status === "RUNNING" || session.status === "STARTING") {
960
+ session.status = "READY";
961
+ }
962
+ void this.syncStatus(session);
963
+ }
964
+ }
965
+ async syncStatus(session) {
966
+ const api = createApmApiClient(this.cfg);
967
+ try {
968
+ await api.cli.webideUpdateTerminalStatus({
969
+ id: session.id,
970
+ status: session.status,
971
+ pid: session.pid,
972
+ ports: session.ports,
973
+ primaryUrl: session.primaryUrl
974
+ });
975
+ } catch (err) {
976
+ console.warn(
977
+ "[apm] terminal status sync failed:",
978
+ err instanceof Error ? err.message : err
979
+ );
980
+ }
981
+ }
982
+ scheduleSync(session) {
983
+ if (session.syncTimer) return;
984
+ session.syncTimer = setTimeout(() => {
985
+ session.syncTimer = void 0;
986
+ session.syncChain = session.syncChain.then(() => this.flushPending(session)).catch((err) => {
987
+ console.warn(
988
+ "[apm] terminal log flush failed:",
989
+ err instanceof Error ? err.message : err
990
+ );
991
+ });
992
+ }, SYNC_INTERVAL_MS);
993
+ }
994
+ async flushPending(session) {
995
+ if (session.pendingLines.length === 0) return;
996
+ const batch = session.pendingLines.splice(0, session.pendingLines.length);
997
+ await this.ensureMinio();
998
+ const seqStart = batch[0].seq;
999
+ const seqEnd = batch.at(-1).seq;
1000
+ const ci = session.chunkCount;
1001
+ const key = chunkKey(session.objectPrefix, ci);
1002
+ const body = Buffer.from(
1003
+ JSON.stringify({ seqStart, seqEnd, lines: batch }),
1004
+ "utf8"
1005
+ );
1006
+ await this.minio.putObject(this.bucket, key, body, {
1007
+ "Content-Type": "application/json"
1008
+ });
1009
+ session.chunkCount = ci + 1;
1010
+ const api = createApmApiClient(this.cfg);
1011
+ await api.cli.webideUpdateTerminalMeta({
1012
+ id: session.id,
1013
+ nextSeq: session.nextSeq,
1014
+ chunkCount: session.chunkCount
1015
+ });
1016
+ if (!session.stopping && (session.status === "RUNNING" || session.status === "READY")) {
1017
+ try {
1018
+ const listed = await api.cli.webideListTerminals({
1019
+ taskId: session.taskId
1020
+ });
1021
+ const remote = listed.terminals?.find((t) => t.id === session.id);
1022
+ if (remote?.status === "STOPPING") {
1023
+ await this.stop(session.id, {
1024
+ pid: remote.pid ?? session.pid,
1025
+ reason: "server STOPPING"
1026
+ });
1027
+ }
1028
+ } catch {
1029
+ }
1030
+ }
1031
+ }
1032
+ async onExit(session, code, signal) {
1033
+ if (session.portTimer) {
1034
+ clearInterval(session.portTimer);
1035
+ session.portTimer = void 0;
1036
+ }
1037
+ session.child = null;
1038
+ const exitCode = code ?? (signal ? 1 : 0);
1039
+ session.status = session.stopping || exitCode === 0 ? "EXITED" : "FAILED";
1040
+ this.pushSystemLine(
1041
+ session,
1042
+ `[apm] exited code=${exitCode}${signal ? ` signal=${signal}` : ""}`
1043
+ );
1044
+ await session.syncChain;
1045
+ await this.flushPending(session);
1046
+ const api = createApmApiClient(this.cfg);
1047
+ try {
1048
+ await api.cli.webideUpdateTerminalStatus({
1049
+ id: session.id,
1050
+ status: session.status,
1051
+ pid: session.pid,
1052
+ exitCode,
1053
+ ports: session.ports,
1054
+ primaryUrl: session.primaryUrl
1055
+ });
1056
+ } catch (err) {
1057
+ console.warn(
1058
+ "[apm] terminal exit status sync failed:",
1059
+ err instanceof Error ? err.message : err
1060
+ );
1061
+ }
1062
+ }
1063
+ };
1064
+ registrySingleton = null;
1065
+ }
1066
+ });
1067
+
1068
+ // src/index.ts
1069
+ import { Command } from "commander";
1070
+ init_config();
81
1071
 
82
1072
  // src/commands/init.ts
83
1073
  import { join as join5 } from "path";
84
1074
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
85
1075
 
86
1076
  // src/command-utils.ts
1077
+ init_config();
87
1078
  import {
88
1079
  copyFileSync,
89
1080
  existsSync,
@@ -360,211 +1351,17 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
360
1351
  for (const name of entries) {
361
1352
  copyTemplateEntry(join2(templateDir, name), join2(resolvedTarget, name));
362
1353
  }
363
- for (const subdir of WORKSPACE_TEMPLATE_SUBDIRS) {
364
- mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
365
- }
366
- assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
367
- }
368
-
369
- // src/deployment-config-sync.ts
370
- import { join as join3 } from "path";
371
- import { writeFileSync as writeFileSync3 } from "fs";
372
-
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
- })
1354
+ for (const subdir of WORKSPACE_TEMPLATE_SUBDIRS) {
1355
+ mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
554
1356
  }
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
- });
1357
+ assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
566
1358
  }
567
1359
 
1360
+ // src/deployment-config-sync.ts
1361
+ init_client();
1362
+ import { join as join3 } from "path";
1363
+ import { writeFileSync as writeFileSync3 } from "fs";
1364
+
568
1365
  // src/git-remote.ts
569
1366
  import { execFile } from "child_process";
570
1367
  import { promisify } from "util";
@@ -769,6 +1566,7 @@ async function resolveBranchBaseline(api, sessionId, workdirPath) {
769
1566
  }
770
1567
 
771
1568
  // src/deployment-config-sync.ts
1569
+ init_config();
772
1570
  var TEMPLATE_HINT = "\u4FDD\u7559\u6A21\u677F .apm/apm.config.json";
773
1571
  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
1572
  async function resolveRepositoryIdForSync(api, workdirPath) {
@@ -832,6 +1630,8 @@ ${diagnostic ?? ""}
832
1630
  }
833
1631
 
834
1632
  // src/repository-project-documents-sync.ts
1633
+ init_client();
1634
+ init_config();
835
1635
  import {
836
1636
  existsSync as existsSync2,
837
1637
  readdirSync as readdirSync2,
@@ -1097,6 +1897,8 @@ async function runInit(name) {
1097
1897
  }
1098
1898
 
1099
1899
  // src/commands/login.ts
1900
+ init_config();
1901
+ init_client();
1100
1902
  import { existsSync as existsSync3 } from "fs";
1101
1903
  import { ApiError } from "listpage-http";
1102
1904
  async function runLogin(opts) {
@@ -1158,6 +1960,7 @@ async function runLogin(opts) {
1158
1960
  }
1159
1961
 
1160
1962
  // src/commands/branch.ts
1963
+ init_client();
1161
1964
  var SESSION_BRANCH_PREFIX = "feat/session-";
1162
1965
  function branchNameForSession(sessionId) {
1163
1966
  const id = sessionId.trim();
@@ -1281,6 +2084,9 @@ async function runBranch(sessionId, options = {}) {
1281
2084
  return ensureFeatureBranch(branch, baseline.defaultBranch, options);
1282
2085
  }
1283
2086
 
2087
+ // src/commands/clean-branches.ts
2088
+ init_client();
2089
+
1284
2090
  // src/commands/session-branch-prune.ts
1285
2091
  var TASK_STATUS_LABEL = {
1286
2092
  OPEN: "\u5F85\u5F00\u59CB",
@@ -1603,6 +2409,7 @@ async function runCleanBranches(options = {}) {
1603
2409
  }
1604
2410
 
1605
2411
  // src/commands/pull.ts
2412
+ init_client();
1606
2413
  import { writeFileSync as writeFileSync9 } from "fs";
1607
2414
  import { join as join9 } from "path";
1608
2415
  import { stringify as yamlStringify } from "yaml";
@@ -1713,6 +2520,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
1713
2520
  }
1714
2521
 
1715
2522
  // src/rules-sync.ts
2523
+ init_client();
1716
2524
  import { basename as basename2, extname as extname2, join as join8 } from "path";
1717
2525
  import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
1718
2526
 
@@ -2135,6 +2943,7 @@ async function runUpdate(options = {}) {
2135
2943
  }
2136
2944
 
2137
2945
  // src/commands/update-skills.ts
2946
+ init_client();
2138
2947
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
2139
2948
  import { join as join11 } from "path";
2140
2949
  async function syncWorkspaceSkills(cfg, workdir) {
@@ -2398,7 +3207,11 @@ function validateAssumptionsMarkdown(markdown, source) {
2398
3207
  };
2399
3208
  }
2400
3209
 
3210
+ // src/commands/sync-document.ts
3211
+ init_client();
3212
+
2401
3213
  // src/commands/sync-session-documents.ts
3214
+ init_client();
2402
3215
  import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
2403
3216
  import { join as join12 } from "path";
2404
3217
  function listLocalMarkdownFiles(docsDir) {
@@ -2519,6 +3332,7 @@ async function runSyncDocument(sessionId, options) {
2519
3332
  }
2520
3333
 
2521
3334
  // src/commands/dev-gate.ts
3335
+ init_client();
2522
3336
  async function runDevGate(sessionId, source) {
2523
3337
  const trimmedId = sessionId.trim();
2524
3338
  if (!trimmedId) {
@@ -2538,6 +3352,7 @@ async function runDevGate(sessionId, source) {
2538
3352
  }
2539
3353
 
2540
3354
  // src/commands/append-message.ts
3355
+ init_client();
2541
3356
  async function appendMessageContent(cfg, messageId, content) {
2542
3357
  const trimmedId = messageId.trim();
2543
3358
  if (!trimmedId) {
@@ -2566,6 +3381,7 @@ async function runAppendMessage(options) {
2566
3381
  }
2567
3382
 
2568
3383
  // src/commands/update-message-status.ts
3384
+ init_client();
2569
3385
  var VALID_STATUSES = [
2570
3386
  "CREATED",
2571
3387
  "QUEUED",
@@ -2594,8 +3410,10 @@ async function runUpdateMessageStatus(options) {
2594
3410
  }
2595
3411
 
2596
3412
  // src/commands/connect.ts
3413
+ init_client();
2597
3414
  import WebSocket from "ws";
2598
3415
  import { setTimeout as delay3 } from "node:timers/promises";
3416
+ init_config();
2599
3417
 
2600
3418
  // src/ws/protocol.ts
2601
3419
  function nonEmptyString(v) {
@@ -2777,13 +3595,38 @@ function validateWebIdeCacheCleanupPush(o) {
2777
3595
  }
2778
3596
  };
2779
3597
  }
3598
+ function validateWebIdeTerminalStopPush(o) {
3599
+ if (o.type !== "webide-terminal-stop") {
3600
+ return { ok: false, reason: "\u671F\u671B webide-terminal-stop" };
3601
+ }
3602
+ if (!nonEmptyString(o.taskId)) {
3603
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 taskId" };
3604
+ }
3605
+ if (!nonEmptyString(o.terminalId)) {
3606
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 terminalId" };
3607
+ }
3608
+ if (!nonEmptyString(o.key)) {
3609
+ return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 key" };
3610
+ }
3611
+ const pid = typeof o.pid === "number" && Number.isFinite(o.pid) ? Math.trunc(o.pid) : o.pid === null ? null : void 0;
3612
+ return {
3613
+ ok: true,
3614
+ data: {
3615
+ type: "webide-terminal-stop",
3616
+ taskId: o.taskId.trim(),
3617
+ terminalId: o.terminalId.trim(),
3618
+ key: o.key.trim(),
3619
+ ...pid !== void 0 ? { pid } : {}
3620
+ }
3621
+ };
3622
+ }
2780
3623
  function validateAgentWsMessage(value, kind) {
2781
3624
  if (typeof value !== "object" || value === null) {
2782
3625
  return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
2783
3626
  }
2784
3627
  const o = value;
2785
3628
  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") {
3629
+ 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
3630
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
2788
3631
  }
2789
3632
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -2801,12 +3644,18 @@ function validateAgentWsMessage(value, kind) {
2801
3644
  if (type === "webide-cache-cleanup") {
2802
3645
  return validateWebIdeCacheCleanupPush(o);
2803
3646
  }
3647
+ if (type === "webide-terminal-stop") {
3648
+ return validateWebIdeTerminalStopPush(o);
3649
+ }
2804
3650
  if (type === "webide-message") {
2805
3651
  return validateWebIdeMessagePush(o);
2806
3652
  }
2807
3653
  return validateMessagePush(o);
2808
3654
  }
2809
3655
 
3656
+ // src/commands/connect/deploy-run.ts
3657
+ init_client();
3658
+
2810
3659
  // src/commands/deploy/deploy-execute.ts
2811
3660
  import path11 from "node:path";
2812
3661
 
@@ -3054,366 +3903,119 @@ function resolveWisdomBackendPackOnlyFromApmConfig(cfg) {
3054
3903
  mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3055
3904
  mode: resolveWisdomDeployMode(w.mode)
3056
3905
  };
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;
3906
+ }
3907
+ function resolveWisdomBackendDeployFromApmConfig(cfg) {
3908
+ const projectName = reqTopLevelName(cfg);
3909
+ const w = cfg.wisdomDeploy ?? {};
3910
+ const h = cfg.healthCheck ?? {};
3911
+ const jarPath = req(w.jarPath, "jarPath", "wisdomDeploy").trim();
3912
+ const remoteAppDir = posixDirname(jarPath);
3913
+ const healthPort = Number(req(h.port, "port", "healthCheck"));
3914
+ const healthTimeout = Number(req(h.timeout, "timeout", "healthCheck"));
3915
+ if (!Number.isFinite(healthPort) || !Number.isInteger(healthPort) || healthPort < 1) {
3916
+ console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
3917
+ process.exit(1);
3336
3918
  }
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;
3919
+ if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
3920
+ console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
3921
+ process.exit(1);
3345
3922
  }
3346
- const fileName = buildDeployArtifactFileName(
3347
- options.kind,
3348
- options.projectName,
3349
- options.timestamp
3923
+ const mavenLocalRepo = resolveMavenLocalRepoWithSource();
3924
+ return {
3925
+ projectName,
3926
+ host: req(w.host, "host", "wisdomDeploy").trim(),
3927
+ port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
3928
+ username: req(w.username, "username", "wisdomDeploy").trim(),
3929
+ password: req(w.password, "password", "wisdomDeploy").trim(),
3930
+ remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
3931
+ remoteAppDir,
3932
+ remoteLibDir: `${remoteAppDir}/lib`,
3933
+ startupJar: posixBasename(jarPath),
3934
+ packageName: `${projectName}.jar.zip`,
3935
+ mavenLocalRepo: mavenLocalRepo.path,
3936
+ mavenLocalRepoSource: mavenLocalRepo.source,
3937
+ mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3938
+ healthCheckPort: healthPort,
3939
+ healthCheckContext: req(h.context, "context", "healthCheck").trim(),
3940
+ healthCheckTimeout: healthTimeout,
3941
+ mode: resolveWisdomDeployMode(w.mode)
3942
+ };
3943
+ }
3944
+
3945
+ // src/commands/deploy/internal/apm-config.ts
3946
+ function loadApmConfig(options) {
3947
+ const p = resolve4(
3948
+ process.cwd(),
3949
+ options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3350
3950
  );
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;
3951
+ if (!existsSync12(p)) {
3952
+ console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3953
+ process.exit(1);
3359
3954
  }
3360
3955
  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;
3956
+ const raw = readFileSync11(p, "utf8");
3957
+ return JSON.parse(raw);
3958
+ } catch (e) {
3959
+ console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
3960
+ process.exit(1);
3381
3961
  }
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
- }
3962
+ }
3963
+
3964
+ // src/commands/deploy/internal/deploy-hints.ts
3965
+ function isProductionDeployEnv(env) {
3966
+ const normalized = env.trim().toLowerCase();
3967
+ return normalized === "online" || normalized === "production" || normalized === "prod";
3968
+ }
3969
+ function formatMissingWisdomDeployLines() {
3970
+ return [
3971
+ "[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
3972
+ ];
3973
+ }
3974
+ function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
3975
+ if (isProductionDeployEnv(env)) {
3976
+ return [
3977
+ `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
3978
+ "[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
3979
+ ];
3397
3980
  }
3398
- return {
3399
- objectKey,
3400
- fileName,
3401
- bucket: storage.bucket
3402
- };
3981
+ return [
3982
+ `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
3983
+ "[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
3984
+ ];
3403
3985
  }
3404
3986
 
3987
+ // src/commands/deploy/deploy-errors.ts
3988
+ var DeployExecutionError = class extends Error {
3989
+ exitCode;
3990
+ output;
3991
+ constructor(message, exitCode, output = "") {
3992
+ super(message);
3993
+ this.name = "DeployExecutionError";
3994
+ this.exitCode = exitCode;
3995
+ this.output = output;
3996
+ }
3997
+ };
3998
+
3999
+ // src/commands/deploy/deploy-debug-log.ts
4000
+ init_deploy_artifact_minio();
4001
+ import { spawnSync as spawnSync4 } from "node:child_process";
4002
+ import { existsSync as existsSync16 } from "node:fs";
4003
+ import { dirname as dirname5, join as join17 } from "node:path";
4004
+
3405
4005
  // src/commands/deploy/internal/deploy-shell-env.ts
3406
4006
  import { spawnSync as spawnSync3 } from "node:child_process";
3407
4007
  import { existsSync as existsSync15 } from "node:fs";
3408
4008
  import { dirname as dirname4, join as join16 } from "node:path";
3409
4009
 
3410
4010
  // src/commands/daemon.ts
4011
+ init_config();
3411
4012
  import { spawnSync as spawnSync2 } from "child_process";
3412
4013
  import { setTimeout as delay } from "node:timers/promises";
3413
4014
  import { existsSync as existsSync14, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
3414
4015
  import { join as join15 } from "path";
3415
4016
 
3416
4017
  // src/commands/connect-lock.ts
4018
+ init_config();
3417
4019
  import {
3418
4020
  existsSync as existsSync13,
3419
4021
  mkdirSync as mkdirSync5,
@@ -4548,6 +5150,7 @@ function runDeployShellCommand(command, cwd, captureOutput) {
4548
5150
 
4549
5151
  // src/commands/deploy/internal/wisdom-backend/index.ts
4550
5152
  import path9 from "node:path";
5153
+ init_deploy_artifact_minio();
4551
5154
 
4552
5155
  // src/commands/deploy/internal/wisdom-backend/jar-incremental.ts
4553
5156
  import {
@@ -4616,6 +5219,8 @@ import SftpClient from "ssh2-sftp-client";
4616
5219
  import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
4617
5220
  import path3 from "node:path";
4618
5221
  import JSZip from "jszip";
5222
+ init_deploy_artifact_minio();
5223
+ init_minio();
4619
5224
  async function addDirToZip(dir, zipFolder) {
4620
5225
  const entries = await readdir2(dir, { withFileTypes: true });
4621
5226
  for (const entry of entries) {
@@ -4704,6 +5309,7 @@ async function packDirectoryWithMinioArchive(input) {
4704
5309
  }
4705
5310
 
4706
5311
  // src/commands/deploy/internal/wisdom-sftp.ts
5312
+ init_deploy_artifact_minio();
4707
5313
  var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
4708
5314
  var SFTP_FAST_PUT_OPTIONS = {
4709
5315
  chunkSize: 64 * 1024,
@@ -5703,6 +6309,8 @@ async function executeWisdomDeploy(options) {
5703
6309
  }
5704
6310
 
5705
6311
  // src/commands/deploy/internal/deploy-baseline-sync.ts
6312
+ init_client();
6313
+ init_config();
5706
6314
  var LOG_PREFIX2 = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
5707
6315
  function formatGitError(error) {
5708
6316
  return error instanceof Error ? error.message : String(error);
@@ -5910,6 +6518,9 @@ function printDeployExecutionError(error) {
5910
6518
  console.error(error.message);
5911
6519
  }
5912
6520
 
6521
+ // src/commands/deploy/platform/deploy-run-coordinator.ts
6522
+ init_deploy_artifact_minio();
6523
+
5913
6524
  // src/commands/deploy/platform/deploy-console-capture.ts
5914
6525
  function captureDeployConsole(logSyncer) {
5915
6526
  const chunks = [];
@@ -6638,6 +7249,7 @@ function clearSessionAgentId(workdir, sessionId, user) {
6638
7249
  }
6639
7250
 
6640
7251
  // src/commands/connect/cursor-message-log.ts
7252
+ init_client();
6641
7253
  var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
6642
7254
  function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
6643
7255
  let lastRunAt = 0;
@@ -6806,16 +7418,17 @@ ${payload}`;
6806
7418
  }
6807
7419
 
6808
7420
  // src/commands/connect/webide-merge-tools.ts
7421
+ init_client();
6809
7422
  function createMergeWebIdePullRequestsTool(options) {
6810
7423
  const { cfg, taskId } = options;
6811
7424
  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.",
7425
+ 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
7426
  inputSchema: {
6814
7427
  type: "object",
6815
7428
  properties: {
6816
7429
  confirmedConflictFree: {
6817
7430
  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."
7431
+ description: "Must be true to confirm you intend to merge this task's open PRs now."
6819
7432
  }
6820
7433
  },
6821
7434
  required: ["confirmedConflictFree"]
@@ -6823,7 +7436,7 @@ function createMergeWebIdePullRequestsTool(options) {
6823
7436
  execute: async (args) => {
6824
7437
  if (args.confirmedConflictFree !== true) {
6825
7438
  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"
7439
+ "MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
6827
7440
  );
6828
7441
  }
6829
7442
  const api = createApmApiClient(cfg);
@@ -6835,6 +7448,7 @@ function createMergeWebIdePullRequestsTool(options) {
6835
7448
  }
6836
7449
 
6837
7450
  // src/commands/connect/webide-plan-tools.ts
7451
+ init_client();
6838
7452
  function asString(value) {
6839
7453
  return typeof value === "string" ? value.trim() : "";
6840
7454
  }
@@ -6904,6 +7518,7 @@ function createUpsertWebIdePlanTool(options) {
6904
7518
  }
6905
7519
 
6906
7520
  // src/commands/connect/webide-test-case-tools.ts
7521
+ init_client();
6907
7522
  function asString2(value) {
6908
7523
  return typeof value === "string" ? value.trim() : "";
6909
7524
  }
@@ -6988,6 +7603,165 @@ function createUpsertWebIdeTestCasesTool(options) {
6988
7603
  };
6989
7604
  }
6990
7605
 
7606
+ // src/commands/connect/webide-terminal-tools.ts
7607
+ init_client();
7608
+ function asString3(value) {
7609
+ return typeof value === "string" ? value.trim() : "";
7610
+ }
7611
+ function asPorts(value) {
7612
+ if (!Array.isArray(value)) return void 0;
7613
+ 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));
7614
+ return ports.length ? ports : void 0;
7615
+ }
7616
+ function createWebIdeTerminalTools(options) {
7617
+ const { cfg, taskId, workdir, rpc } = options;
7618
+ const StartWebIdeTerminal = {
7619
+ 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.",
7620
+ inputSchema: {
7621
+ type: "object",
7622
+ properties: {
7623
+ key: {
7624
+ type: "string",
7625
+ description: "Stable key, e.g. frontend / backend"
7626
+ },
7627
+ name: { type: "string", description: "Display name" },
7628
+ cwd: {
7629
+ type: "string",
7630
+ description: "Working directory (absolute or under workspace)"
7631
+ },
7632
+ command: {
7633
+ type: "string",
7634
+ description: "Shell command to run, e.g. pnpm dev"
7635
+ },
7636
+ readyPattern: {
7637
+ type: "string",
7638
+ description: "Optional regex; when matched in logs, status becomes READY"
7639
+ },
7640
+ portsHint: {
7641
+ type: "array",
7642
+ items: { type: "number" },
7643
+ description: "Optional expected listen ports"
7644
+ }
7645
+ },
7646
+ required: ["key", "cwd", "command"]
7647
+ },
7648
+ execute: async (args) => {
7649
+ const key = asString3(args.key);
7650
+ const cwd = asString3(args.cwd) || workdir;
7651
+ const command = asString3(args.command);
7652
+ if (!key || !command) {
7653
+ return {
7654
+ content: [{ type: "text", text: "key / command \u4E0D\u80FD\u4E3A\u7A7A" }],
7655
+ isError: true
7656
+ };
7657
+ }
7658
+ try {
7659
+ const result = rpc ? await rpc.start({
7660
+ taskId,
7661
+ key,
7662
+ name: asString3(args.name) || void 0,
7663
+ cwd,
7664
+ command,
7665
+ readyPattern: asString3(args.readyPattern) || void 0,
7666
+ portsHint: asPorts(args.portsHint)
7667
+ }) : await (async () => {
7668
+ throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
7669
+ })();
7670
+ return JSON.stringify(result, null, 2);
7671
+ } catch (err) {
7672
+ const detail = err instanceof Error ? err.message : String(err);
7673
+ return {
7674
+ content: [{ type: "text", text: `\u542F\u52A8\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7675
+ isError: true
7676
+ };
7677
+ }
7678
+ }
7679
+ };
7680
+ const StopWebIdeTerminal = {
7681
+ description: "Stop a WebIDE terminal and kill its process tree. Soft-terminates (status EXITED, record kept).",
7682
+ inputSchema: {
7683
+ type: "object",
7684
+ properties: {
7685
+ key: { type: "string", description: "Terminal key" },
7686
+ terminalId: { type: "string", description: "Server terminal id" }
7687
+ }
7688
+ },
7689
+ execute: async (args) => {
7690
+ const key = asString3(args.key) || void 0;
7691
+ const terminalId = asString3(args.terminalId) || void 0;
7692
+ if (!key && !terminalId) {
7693
+ return {
7694
+ content: [{ type: "text", text: "\u9700\u8981 key \u6216 terminalId" }],
7695
+ isError: true
7696
+ };
7697
+ }
7698
+ try {
7699
+ const result = rpc ? await rpc.stop({ taskId, key, terminalId }) : await (async () => {
7700
+ throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
7701
+ })();
7702
+ return JSON.stringify(result, null, 2);
7703
+ } catch (err) {
7704
+ const detail = err instanceof Error ? err.message : String(err);
7705
+ return {
7706
+ content: [{ type: "text", text: `\u505C\u6B62\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7707
+ isError: true
7708
+ };
7709
+ }
7710
+ }
7711
+ };
7712
+ const StopAllWebIdeTerminals = {
7713
+ 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.",
7714
+ inputSchema: {
7715
+ type: "object",
7716
+ properties: {}
7717
+ },
7718
+ execute: async () => {
7719
+ try {
7720
+ const result = rpc ? await rpc.stopAll({ taskId }) : 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: `\u5168\u90E8\u505C\u6B62\u5931\u8D25: ${detail}` }],
7728
+ isError: true
7729
+ };
7730
+ }
7731
+ }
7732
+ };
7733
+ const ListWebIdeTerminals = {
7734
+ 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.",
7735
+ inputSchema: {
7736
+ type: "object",
7737
+ properties: {}
7738
+ },
7739
+ execute: async () => {
7740
+ try {
7741
+ if (rpc) {
7742
+ const result2 = await rpc.list({ taskId });
7743
+ return JSON.stringify(result2, null, 2);
7744
+ }
7745
+ const api = createApmApiClient(cfg);
7746
+ const result = await api.cli.webideListTerminals({ taskId });
7747
+ return JSON.stringify(result, null, 2);
7748
+ } catch (err) {
7749
+ const detail = err instanceof Error ? err.message : String(err);
7750
+ return {
7751
+ content: [{ type: "text", text: `\u5217\u51FA\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
7752
+ isError: true
7753
+ };
7754
+ }
7755
+ }
7756
+ };
7757
+ return {
7758
+ StartWebIdeTerminal,
7759
+ StopWebIdeTerminal,
7760
+ StopAllWebIdeTerminals,
7761
+ ListWebIdeTerminals
7762
+ };
7763
+ }
7764
+
6991
7765
  // src/commands/connect/cursor-custom-tools.ts
6992
7766
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
6993
7767
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -7025,6 +7799,17 @@ function createCursorCustomTools(cfg, messageId, options) {
7025
7799
  cfg,
7026
7800
  taskId: options.taskId
7027
7801
  });
7802
+ if (options.workdir) {
7803
+ Object.assign(
7804
+ tools,
7805
+ createWebIdeTerminalTools({
7806
+ cfg,
7807
+ taskId: options.taskId,
7808
+ workdir: options.workdir,
7809
+ rpc: options.terminalRpc
7810
+ })
7811
+ );
7812
+ }
7028
7813
  }
7029
7814
  return tools;
7030
7815
  }
@@ -7148,7 +7933,9 @@ async function runCursorAgent(cfg, ctx, options) {
7148
7933
  appendMessageContent: options?.appendMessageContent,
7149
7934
  askQuestionExecute: options?.askQuestionExecute,
7150
7935
  enableWebIdePlanTools: options?.enableWebIdePlanTools,
7151
- taskId: options?.taskId
7936
+ taskId: options?.taskId,
7937
+ workdir,
7938
+ terminalRpc: options?.terminalRpc
7152
7939
  });
7153
7940
  const enableSandbox = Boolean(options?.enableSandbox);
7154
7941
  let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
@@ -7290,6 +8077,7 @@ async function runCursorAgent(cfg, ctx, options) {
7290
8077
  }
7291
8078
 
7292
8079
  // src/commands/connect/ensure-message-reply.ts
8080
+ init_client();
7293
8081
  var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
7294
8082
  function resolveMessageReplyFallback(fallback) {
7295
8083
  for (const candidate of [
@@ -7433,6 +8221,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
7433
8221
  }
7434
8222
 
7435
8223
  // src/commands/connect/webide-worker-pool.ts
8224
+ init_webide_terminal_registry();
7436
8225
  import { Worker } from "node:worker_threads";
7437
8226
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7438
8227
  import { dirname as dirname7, join as join20 } from "node:path";
@@ -7444,23 +8233,78 @@ function spawnWebIdeMessageWorker(cfg, msg) {
7444
8233
  const worker = new Worker(workerFile, {
7445
8234
  workerData: { cfg, msg }
7446
8235
  });
8236
+ const registry = getWebIdeTerminalRegistry(cfg);
7447
8237
  let resolveDone;
7448
8238
  const done = new Promise((resolve7) => {
7449
8239
  resolveDone = resolve7;
7450
8240
  });
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
- );
8241
+ worker.on("message", (m) => {
8242
+ if (m?.type === "terminal-rpc") {
8243
+ const rpc = m;
8244
+ void (async () => {
8245
+ try {
8246
+ let result;
8247
+ if (rpc.op === "start") {
8248
+ result = await registry.start({
8249
+ taskId: String(rpc.args.taskId ?? msg.taskId),
8250
+ key: String(rpc.args.key ?? ""),
8251
+ name: typeof rpc.args.name === "string" ? rpc.args.name : void 0,
8252
+ cwd: String(rpc.args.cwd ?? msg.workdir),
8253
+ command: String(rpc.args.command ?? ""),
8254
+ readyPattern: typeof rpc.args.readyPattern === "string" ? rpc.args.readyPattern : void 0,
8255
+ portsHint: Array.isArray(rpc.args.portsHint) ? rpc.args.portsHint : void 0
8256
+ });
8257
+ } else if (rpc.op === "stop") {
8258
+ const terminalId = typeof rpc.args.terminalId === "string" ? rpc.args.terminalId : void 0;
8259
+ const key = typeof rpc.args.key === "string" ? rpc.args.key : void 0;
8260
+ const taskId = String(rpc.args.taskId ?? msg.taskId);
8261
+ let id = terminalId;
8262
+ if (!id && key) {
8263
+ const local = registry.listLocal(taskId).find((t) => t.key === key);
8264
+ id = local?.id;
8265
+ }
8266
+ if (!id) {
8267
+ throw new Error("\u672A\u627E\u5230\u7EC8\u7AEF");
8268
+ }
8269
+ result = await registry.stop(id, { reason: "ai-stop" });
8270
+ } else if (rpc.op === "stopAll") {
8271
+ result = await registry.stopAllForTask(
8272
+ String(rpc.args.taskId ?? msg.taskId),
8273
+ "ai-stop-all"
8274
+ );
8275
+ } else {
8276
+ result = {
8277
+ terminals: registry.listLocal(
8278
+ String(rpc.args.taskId ?? msg.taskId)
8279
+ )
8280
+ };
8281
+ }
8282
+ worker.postMessage({
8283
+ type: "terminal-rpc-result",
8284
+ id: rpc.id,
8285
+ ok: true,
8286
+ result
8287
+ });
8288
+ } catch (err) {
8289
+ worker.postMessage({
8290
+ type: "terminal-rpc-result",
8291
+ id: rpc.id,
8292
+ ok: false,
8293
+ error: err instanceof Error ? err.message : String(err)
8294
+ });
7459
8295
  }
7460
- void worker.terminate().finally(() => resolveDone());
8296
+ })();
8297
+ return;
8298
+ }
8299
+ if (m?.type === "done" || m?.type === "error") {
8300
+ if (m.type === "error") {
8301
+ console.error(
8302
+ `[apm] webide worker error messageId=${m.messageId}: ${m.error}`
8303
+ );
7461
8304
  }
8305
+ void worker.terminate().finally(() => resolveDone());
7462
8306
  }
7463
- );
8307
+ });
7464
8308
  worker.on("error", (err) => {
7465
8309
  console.error("[apm] webide worker crashed:", err);
7466
8310
  resolveDone();
@@ -7737,6 +8581,11 @@ function attachWsHandlers(ws, ctx, onOpen) {
7737
8581
  const msg2 = validated.data;
7738
8582
  const task2 = (async () => {
7739
8583
  const workdir = requireRemoteWorkdir(msg2.workdir);
8584
+ const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
8585
+ await getWebIdeTerminalRegistry2(cfg).stopAllForTask(
8586
+ msg2.taskId,
8587
+ "cache-cleanup"
8588
+ );
7740
8589
  cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
7741
8590
  })();
7742
8591
  activeTasks.add(task2);
@@ -7745,6 +8594,21 @@ function attachWsHandlers(ws, ctx, onOpen) {
7745
8594
  });
7746
8595
  return;
7747
8596
  }
8597
+ if (validated.data.type === "webide-terminal-stop") {
8598
+ const msg2 = validated.data;
8599
+ const task2 = (async () => {
8600
+ const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
8601
+ await getWebIdeTerminalRegistry2(cfg).stop(msg2.terminalId, {
8602
+ pid: msg2.pid,
8603
+ reason: "web-stop"
8604
+ });
8605
+ })();
8606
+ activeTasks.add(task2);
8607
+ void task2.finally(() => {
8608
+ activeTasks.delete(task2);
8609
+ });
8610
+ return;
8611
+ }
7748
8612
  if (validated.data.type === "webide-message") {
7749
8613
  const msg2 = validated.data;
7750
8614
  const perMessageController2 = new AbortController();
@@ -8009,6 +8873,10 @@ async function runConnect(options) {
8009
8873
  }
8010
8874
  }
8011
8875
 
8876
+ // src/commands/create-pr.ts
8877
+ init_client();
8878
+ init_config();
8879
+
8012
8880
  // src/commands/create-pr-errors.ts
8013
8881
  import { ApiError as ApiError2 } from "listpage-http";
8014
8882
  function extractMessage(error) {
@@ -8136,6 +9004,8 @@ async function runCreatePr(options) {
8136
9004
  }
8137
9005
 
8138
9006
  // src/commands/deploy/deploy-tracking.ts
9007
+ init_client();
9008
+ init_config();
8139
9009
  function normalizeDeployEnvironment(env) {
8140
9010
  const normalized = env.trim().toLowerCase();
8141
9011
  if (normalized === "test") return "TEST";
@@ -8805,6 +9675,7 @@ function registerDeployBackendCommands(program) {
8805
9675
  // src/commands/deploy/frontend.ts
8806
9676
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
8807
9677
  import path17 from "node:path";
9678
+ init_minio();
8808
9679
  function artifactObjectKey(namePrefix, branchSegment, relativePath) {
8809
9680
  const base = sanitizeRelativePath(namePrefix);
8810
9681
  const rel = sanitizeRelativePath(relativePath);
@@ -8982,6 +9853,7 @@ function registerDeployFrontendCommands(program) {
8982
9853
 
8983
9854
  // src/commands/deploy/sftp.ts
8984
9855
  import path18 from "node:path";
9856
+ init_minio();
8985
9857
  function registerDeploySftpCommands(program) {
8986
9858
  program.command("deploy-sftp").description(
8987
9859
  "\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"