ai-project-manage-cli 7.1.11 → 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,
@@ -361,206 +1352,16 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
361
1352
  copyTemplateEntry(join2(templateDir, name), join2(resolvedTarget, name));
362
1353
  }
363
1354
  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
- getRepositoryProjectDocumentManifest: defineEndpoint({
509
- method: "GET",
510
- path: "/cli/repository-project-documents/manifest"
511
- }),
512
- listRepositoryProjectDocuments: defineEndpoint({
513
- method: "GET",
514
- path: "/cli/repository-project-documents"
515
- }),
516
- upsertRepositoryProjectDocument: defineEndpoint({
517
- method: "PUT",
518
- path: "/cli/repository-project-documents/upsert"
519
- }),
520
- removeRepositoryProjectDocument: defineEndpoint({
521
- method: "DELETE",
522
- path: "/cli/repository-project-documents"
523
- }),
524
- createTaskDeployment: defineEndpoint({
525
- method: "POST",
526
- path: "/cli/task-deployments"
527
- }),
528
- updateTaskDeploymentStatus: defineEndpoint({
529
- method: "PUT",
530
- path: "/cli/task-deployments/status"
531
- }),
532
- syncTaskDeploymentLog: defineEndpoint({
533
- method: "PUT",
534
- path: "/cli/task-deployments/log"
535
- }),
536
- completeTaskDeployment: defineEndpoint({
537
- method: "PUT",
538
- path: "/cli/task-deployments/complete"
539
- }),
540
- getDeployArtifactStorage: defineEndpoint(
541
- {
542
- method: "GET",
543
- path: "/cli/deploy-artifact-storage"
544
- }
545
- ),
546
- attachTaskDeploymentArtifact: defineEndpoint({
547
- method: "PUT",
548
- path: "/cli/task-deployments/artifact"
549
- })
550
- }
551
- };
552
-
553
- // src/api/client.ts
554
- function createApmApiClient(cfg) {
555
- const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
556
- return createApiClient(requestConfig, {
557
- baseURL,
558
- getToken: () => resolveApiKey(cfg) || void 0,
559
- successCodes: [0],
560
- unauthorizedCodes: [401]
561
- });
1355
+ mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
1356
+ }
1357
+ assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
562
1358
  }
563
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
+
564
1365
  // src/git-remote.ts
565
1366
  import { execFile } from "child_process";
566
1367
  import { promisify } from "util";
@@ -648,6 +1449,22 @@ async function hasUpstream(cwd) {
648
1449
  return false;
649
1450
  }
650
1451
  }
1452
+ async function pushCurrentBranch(cwd) {
1453
+ if (!await isGitRepo(cwd)) {
1454
+ return false;
1455
+ }
1456
+ const originUrl = await tryReadGitOriginUrl(cwd);
1457
+ if (!originUrl) {
1458
+ console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8DF3\u8FC7 push");
1459
+ return false;
1460
+ }
1461
+ if (await hasUpstream(cwd)) {
1462
+ await execGit(cwd, ["push"]);
1463
+ } else {
1464
+ await execGit(cwd, ["push", "-u", "origin", "HEAD"]);
1465
+ }
1466
+ return true;
1467
+ }
651
1468
  var GITIGNORE_COMMIT_MESSAGE = "chore(apm): ignore .apm directory";
652
1469
  async function commitAndPushGitignore(workdir) {
653
1470
  if (!await isGitRepo(workdir)) {
@@ -657,17 +1474,12 @@ async function commitAndPushGitignore(workdir) {
657
1474
  await execGit(workdir, ["add", "--", ".gitignore"]);
658
1475
  await execGit(workdir, ["commit", "-m", GITIGNORE_COMMIT_MESSAGE]);
659
1476
  console.log(`[apm] \u5DF2\u63D0\u4EA4 .gitignore: ${GITIGNORE_COMMIT_MESSAGE}`);
660
- const originUrl = await tryReadGitOriginUrl(workdir);
661
- if (!originUrl) {
662
- console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
663
- return;
664
- }
665
- if (await hasUpstream(workdir)) {
666
- await execGit(workdir, ["push"]);
1477
+ const pushed = await pushCurrentBranch(workdir);
1478
+ if (pushed) {
1479
+ console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
667
1480
  } else {
668
- await execGit(workdir, ["push", "-u", "origin", "HEAD"]);
1481
+ console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
669
1482
  }
670
- console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
671
1483
  }
672
1484
 
673
1485
  // src/baseline-resolve.ts
@@ -754,6 +1566,7 @@ async function resolveBranchBaseline(api, sessionId, workdirPath) {
754
1566
  }
755
1567
 
756
1568
  // src/deployment-config-sync.ts
1569
+ init_config();
757
1570
  var TEMPLATE_HINT = "\u4FDD\u7559\u6A21\u677F .apm/apm.config.json";
758
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";
759
1572
  async function resolveRepositoryIdForSync(api, workdirPath) {
@@ -817,6 +1630,8 @@ ${diagnostic ?? ""}
817
1630
  }
818
1631
 
819
1632
  // src/repository-project-documents-sync.ts
1633
+ init_client();
1634
+ init_config();
820
1635
  import {
821
1636
  existsSync as existsSync2,
822
1637
  readdirSync as readdirSync2,
@@ -1082,6 +1897,8 @@ async function runInit(name) {
1082
1897
  }
1083
1898
 
1084
1899
  // src/commands/login.ts
1900
+ init_config();
1901
+ init_client();
1085
1902
  import { existsSync as existsSync3 } from "fs";
1086
1903
  import { ApiError } from "listpage-http";
1087
1904
  async function runLogin(opts) {
@@ -1143,6 +1960,7 @@ async function runLogin(opts) {
1143
1960
  }
1144
1961
 
1145
1962
  // src/commands/branch.ts
1963
+ init_client();
1146
1964
  var SESSION_BRANCH_PREFIX = "feat/session-";
1147
1965
  function branchNameForSession(sessionId) {
1148
1966
  const id = sessionId.trim();
@@ -1266,6 +2084,9 @@ async function runBranch(sessionId, options = {}) {
1266
2084
  return ensureFeatureBranch(branch, baseline.defaultBranch, options);
1267
2085
  }
1268
2086
 
2087
+ // src/commands/clean-branches.ts
2088
+ init_client();
2089
+
1269
2090
  // src/commands/session-branch-prune.ts
1270
2091
  var TASK_STATUS_LABEL = {
1271
2092
  OPEN: "\u5F85\u5F00\u59CB",
@@ -1588,6 +2409,7 @@ async function runCleanBranches(options = {}) {
1588
2409
  }
1589
2410
 
1590
2411
  // src/commands/pull.ts
2412
+ init_client();
1591
2413
  import { writeFileSync as writeFileSync9 } from "fs";
1592
2414
  import { join as join9 } from "path";
1593
2415
  import { stringify as yamlStringify } from "yaml";
@@ -1698,6 +2520,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
1698
2520
  }
1699
2521
 
1700
2522
  // src/rules-sync.ts
2523
+ init_client();
1701
2524
  import { basename as basename2, extname as extname2, join as join8 } from "path";
1702
2525
  import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
1703
2526
 
@@ -2120,6 +2943,7 @@ async function runUpdate(options = {}) {
2120
2943
  }
2121
2944
 
2122
2945
  // src/commands/update-skills.ts
2946
+ init_client();
2123
2947
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
2124
2948
  import { join as join11 } from "path";
2125
2949
  async function syncWorkspaceSkills(cfg, workdir) {
@@ -2383,7 +3207,11 @@ function validateAssumptionsMarkdown(markdown, source) {
2383
3207
  };
2384
3208
  }
2385
3209
 
3210
+ // src/commands/sync-document.ts
3211
+ init_client();
3212
+
2386
3213
  // src/commands/sync-session-documents.ts
3214
+ init_client();
2387
3215
  import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
2388
3216
  import { join as join12 } from "path";
2389
3217
  function listLocalMarkdownFiles(docsDir) {
@@ -2504,6 +3332,7 @@ async function runSyncDocument(sessionId, options) {
2504
3332
  }
2505
3333
 
2506
3334
  // src/commands/dev-gate.ts
3335
+ init_client();
2507
3336
  async function runDevGate(sessionId, source) {
2508
3337
  const trimmedId = sessionId.trim();
2509
3338
  if (!trimmedId) {
@@ -2523,6 +3352,7 @@ async function runDevGate(sessionId, source) {
2523
3352
  }
2524
3353
 
2525
3354
  // src/commands/append-message.ts
3355
+ init_client();
2526
3356
  async function appendMessageContent(cfg, messageId, content) {
2527
3357
  const trimmedId = messageId.trim();
2528
3358
  if (!trimmedId) {
@@ -2551,6 +3381,7 @@ async function runAppendMessage(options) {
2551
3381
  }
2552
3382
 
2553
3383
  // src/commands/update-message-status.ts
3384
+ init_client();
2554
3385
  var VALID_STATUSES = [
2555
3386
  "CREATED",
2556
3387
  "QUEUED",
@@ -2579,8 +3410,10 @@ async function runUpdateMessageStatus(options) {
2579
3410
  }
2580
3411
 
2581
3412
  // src/commands/connect.ts
3413
+ init_client();
2582
3414
  import WebSocket from "ws";
2583
3415
  import { setTimeout as delay3 } from "node:timers/promises";
3416
+ init_config();
2584
3417
 
2585
3418
  // src/ws/protocol.ts
2586
3419
  function nonEmptyString(v) {
@@ -2762,13 +3595,38 @@ function validateWebIdeCacheCleanupPush(o) {
2762
3595
  }
2763
3596
  };
2764
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
+ }
2765
3623
  function validateAgentWsMessage(value, kind) {
2766
3624
  if (typeof value !== "object" || value === null) {
2767
3625
  return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
2768
3626
  }
2769
3627
  const o = value;
2770
3628
  const type = o.type;
2771
- 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") {
2772
3630
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
2773
3631
  }
2774
3632
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -2786,12 +3644,18 @@ function validateAgentWsMessage(value, kind) {
2786
3644
  if (type === "webide-cache-cleanup") {
2787
3645
  return validateWebIdeCacheCleanupPush(o);
2788
3646
  }
3647
+ if (type === "webide-terminal-stop") {
3648
+ return validateWebIdeTerminalStopPush(o);
3649
+ }
2789
3650
  if (type === "webide-message") {
2790
3651
  return validateWebIdeMessagePush(o);
2791
3652
  }
2792
3653
  return validateMessagePush(o);
2793
3654
  }
2794
3655
 
3656
+ // src/commands/connect/deploy-run.ts
3657
+ init_client();
3658
+
2795
3659
  // src/commands/deploy/deploy-execute.ts
2796
3660
  import path11 from "node:path";
2797
3661
 
@@ -3052,353 +3916,106 @@ function resolveWisdomBackendDeployFromApmConfig(cfg) {
3052
3916
  console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
3053
3917
  process.exit(1);
3054
3918
  }
3055
- if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
3056
- console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
3057
- process.exit(1);
3058
- }
3059
- const mavenLocalRepo = resolveMavenLocalRepoWithSource();
3060
- return {
3061
- projectName,
3062
- host: req(w.host, "host", "wisdomDeploy").trim(),
3063
- port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
3064
- username: req(w.username, "username", "wisdomDeploy").trim(),
3065
- password: req(w.password, "password", "wisdomDeploy").trim(),
3066
- remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
3067
- remoteAppDir,
3068
- remoteLibDir: `${remoteAppDir}/lib`,
3069
- startupJar: posixBasename(jarPath),
3070
- packageName: `${projectName}.jar.zip`,
3071
- mavenLocalRepo: mavenLocalRepo.path,
3072
- mavenLocalRepoSource: mavenLocalRepo.source,
3073
- mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
3074
- healthCheckPort: healthPort,
3075
- healthCheckContext: req(h.context, "context", "healthCheck").trim(),
3076
- healthCheckTimeout: healthTimeout,
3077
- mode: resolveWisdomDeployMode(w.mode)
3078
- };
3079
- }
3080
-
3081
- // src/commands/deploy/internal/apm-config.ts
3082
- function loadApmConfig(options) {
3083
- const p = resolve4(
3084
- process.cwd(),
3085
- options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3086
- );
3087
- if (!existsSync12(p)) {
3088
- console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3089
- process.exit(1);
3090
- }
3091
- try {
3092
- const raw = readFileSync11(p, "utf8");
3093
- return JSON.parse(raw);
3094
- } catch (e) {
3095
- console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
3096
- process.exit(1);
3097
- }
3098
- }
3099
-
3100
- // src/commands/deploy/internal/deploy-hints.ts
3101
- function isProductionDeployEnv(env) {
3102
- const normalized = env.trim().toLowerCase();
3103
- return normalized === "online" || normalized === "production" || normalized === "prod";
3104
- }
3105
- function formatMissingWisdomDeployLines() {
3106
- return [
3107
- "[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
3108
- ];
3109
- }
3110
- function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
3111
- if (isProductionDeployEnv(env)) {
3112
- return [
3113
- `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
3114
- "[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
3115
- ];
3116
- }
3117
- return [
3118
- `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
3119
- "[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
3120
- ];
3121
- }
3122
-
3123
- // src/commands/deploy/deploy-errors.ts
3124
- var DeployExecutionError = class extends Error {
3125
- exitCode;
3126
- output;
3127
- constructor(message, exitCode, output = "") {
3128
- super(message);
3129
- this.name = "DeployExecutionError";
3130
- this.exitCode = exitCode;
3131
- this.output = output;
3132
- }
3133
- };
3134
-
3135
- // src/commands/deploy/deploy-debug-log.ts
3136
- import { spawnSync as spawnSync4 } from "node:child_process";
3137
- import { existsSync as existsSync16 } from "node:fs";
3138
- import { dirname as dirname5, join as join17 } from "node:path";
3139
-
3140
- // src/commands/deploy/internal/deploy-artifact-minio.ts
3141
- import { readFile as readFile2 } from "node:fs/promises";
3142
-
3143
- // src/commands/deploy/internal/minio.ts
3144
- import { statSync as statSync5 } from "node:fs";
3145
- import { readdir, readFile } from "node:fs/promises";
3146
- import path from "node:path";
3147
- import * as Minio from "minio";
3148
- var DEFAULT_MAX_FILE_SIZE_MB = 50;
3149
- async function isDirectoryPath(dir) {
3150
- try {
3151
- const st = statSync5(dir);
3152
- return st.isDirectory();
3153
- } catch {
3154
- return false;
3155
- }
3156
- }
3157
- function sanitizeRelativePath(rel) {
3158
- const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
3159
- const segments = norm.split("/").filter(Boolean);
3160
- for (const s of segments) {
3161
- if (s === "." || s === "..") {
3162
- throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
3163
- }
3164
- }
3165
- return segments.join("/");
3166
- }
3167
- async function collectFiles(root) {
3168
- const out = [];
3169
- async function walk(dir, prefix) {
3170
- const entries = await readdir(dir, { withFileTypes: true });
3171
- for (const e of entries) {
3172
- const name = e.name;
3173
- if (name === "." || name === "..") {
3174
- continue;
3175
- }
3176
- const abs = path.join(dir, name);
3177
- const rel = prefix ? `${prefix}/${name}` : name;
3178
- if (e.isDirectory()) {
3179
- await walk(abs, rel);
3180
- } else if (e.isFile()) {
3181
- const st = statSync5(abs);
3182
- out.push({
3183
- absPath: abs,
3184
- relativePath: rel.replace(/\\/g, "/"),
3185
- size: st.size
3186
- });
3187
- }
3188
- }
3189
- }
3190
- await walk(root, "");
3191
- out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
3192
- return out;
3193
- }
3194
- async function readArtifactFile(absPath) {
3195
- return readFile(absPath);
3196
- }
3197
- function toMB(bytes) {
3198
- return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
3199
- }
3200
- var MIME = {
3201
- ".html": "text/html; charset=utf-8",
3202
- ".css": "text/css; charset=utf-8",
3203
- ".js": "application/javascript; charset=utf-8",
3204
- ".json": "application/json; charset=utf-8",
3205
- ".svg": "image/svg+xml",
3206
- ".png": "image/png",
3207
- ".jpg": "image/jpeg",
3208
- ".jpeg": "image/jpeg",
3209
- ".gif": "image/gif",
3210
- ".webp": "image/webp",
3211
- ".woff": "font/woff",
3212
- ".woff2": "font/woff2",
3213
- ".ttf": "font/ttf",
3214
- ".ico": "image/x-icon",
3215
- ".txt": "text/plain; charset=utf-8",
3216
- ".map": "application/json"
3217
- };
3218
- function detectMimeType(filePath) {
3219
- const ext = path.extname(filePath).toLowerCase();
3220
- return MIME[ext] ?? "";
3221
- }
3222
- var MinioClient = class {
3223
- inner;
3224
- constructor(opts) {
3225
- const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
3226
- this.inner = new Minio.Client({
3227
- endPoint,
3228
- port: opts.port,
3229
- useSSL: opts.useSSL,
3230
- accessKey: opts.accessKey,
3231
- secretKey: opts.secretKey
3232
- });
3233
- }
3234
- async ensureBucket(bucket) {
3235
- const exists = await this.inner.bucketExists(bucket);
3236
- if (!exists) {
3237
- await this.inner.makeBucket(bucket);
3238
- }
3239
- }
3240
- async deleteObjectsByPrefix(bucket, prefix) {
3241
- const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
3242
- const keys = [];
3243
- await new Promise((resolve7, reject) => {
3244
- objectsStream.on("data", (obj) => {
3245
- if (obj.name) {
3246
- keys.push(obj.name);
3247
- }
3248
- });
3249
- objectsStream.on("error", reject);
3250
- objectsStream.on("end", resolve7);
3251
- });
3252
- const chunkSize = 500;
3253
- for (let i = 0; i < keys.length; i += chunkSize) {
3254
- const chunk = keys.slice(i, i + chunkSize);
3255
- await this.inner.removeObjects(
3256
- bucket,
3257
- chunk.map((name) => name)
3258
- );
3259
- }
3260
- }
3261
- async putObject(bucket, objectKey, body, meta) {
3262
- await this.inner.putObject(bucket, objectKey, body, body.length, meta);
3263
- }
3264
- /** 匿名可读当前桶全部对象(便于静态站点直链) */
3265
- async setBucketPublicRead(bucket) {
3266
- const policy = {
3267
- Version: "2012-10-17",
3268
- Statement: [
3269
- {
3270
- Effect: "Allow",
3271
- Principal: { AWS: ["*"] },
3272
- Action: ["s3:GetObject"],
3273
- Resource: [`arn:aws:s3:::${bucket}/*`]
3274
- }
3275
- ]
3276
- };
3277
- await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
3278
- }
3279
- };
3280
-
3281
- // src/commands/deploy/internal/deploy-artifact-minio.ts
3282
- var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
3283
- function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
3284
- const pad2 = (n) => String(n).padStart(2, "0");
3285
- return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}${pad2(date.getHours())}${pad2(date.getMinutes())}${pad2(date.getSeconds())}`;
3286
- }
3287
- function sanitizeDeployProjectName(name) {
3288
- const trimmed = name.trim().replace(/\\/g, "/");
3289
- const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
3290
- try {
3291
- return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
3292
- } catch {
3293
- return "project";
3294
- }
3295
- }
3296
- function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
3297
- const safeName = sanitizeDeployProjectName(projectName);
3298
- const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
3299
- return `${timestamp}-${safeName}.${suffix}`;
3300
- }
3301
- function buildDeployArtifactObjectKey(projectName, fileName) {
3302
- const safeProject = sanitizeDeployProjectName(projectName);
3303
- return `deploy/${safeProject}/${fileName}`;
3304
- }
3305
- function resolveDeploymentRunIdFromEnv() {
3306
- const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
3307
- return raw || null;
3308
- }
3309
- async function fetchDeployArtifactStorage(api) {
3310
- return api.cli.getDeployArtifactStorage(void 0);
3311
- }
3312
- async function uploadDeployArtifactZip(options) {
3313
- const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
3314
- if (!deploymentRunId && !options.uploadWithoutRunId) {
3315
- return null;
3316
- }
3317
- const cfg = await tryReadApmConfig();
3318
- if (!cfg || !resolveApiKey(cfg)) {
3319
- console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
3320
- return null;
3321
- }
3322
- const api = options.api ?? createApmApiClient(cfg);
3323
- let storage;
3324
- try {
3325
- storage = await fetchDeployArtifactStorage(api);
3326
- } catch (error) {
3327
- const detail = error instanceof Error ? error.message : String(error);
3328
- console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
3329
- 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);
3330
3922
  }
3331
- const fileName = buildDeployArtifactFileName(
3332
- options.kind,
3333
- options.projectName,
3334
- 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")
3335
3950
  );
3336
- const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
3337
- let body;
3338
- try {
3339
- body = await readFile2(options.zipPath);
3340
- } catch (error) {
3341
- const detail = error instanceof Error ? error.message : String(error);
3342
- console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
3343
- return null;
3951
+ if (!existsSync12(p)) {
3952
+ console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3953
+ process.exit(1);
3344
3954
  }
3345
3955
  try {
3346
- const client = new MinioClient({
3347
- endPoint: storage.endpoint,
3348
- port: storage.port,
3349
- useSSL: storage.useSsl,
3350
- accessKey: storage.accessKey,
3351
- secretKey: storage.secretKey
3352
- });
3353
- await client.ensureBucket(storage.bucket);
3354
- await client.putObject(storage.bucket, objectKey, body, {
3355
- "Content-Type": "application/zip"
3356
- });
3357
- console.error(
3358
- `[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(
3359
- 2
3360
- )} MB)`
3361
- );
3362
- } catch (error) {
3363
- const detail = error instanceof Error ? error.message : String(error);
3364
- console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
3365
- 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);
3366
3961
  }
3367
- if (deploymentRunId) {
3368
- try {
3369
- await api.cli.attachTaskDeploymentArtifact({
3370
- id: deploymentRunId,
3371
- artifactObjectKey: objectKey,
3372
- artifactFileName: fileName
3373
- });
3374
- console.error(
3375
- `[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
3376
- );
3377
- } catch (error) {
3378
- const detail = error instanceof Error ? error.message : String(error);
3379
- console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
3380
- return null;
3381
- }
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
+ ];
3382
3980
  }
3383
- return {
3384
- objectKey,
3385
- fileName,
3386
- bucket: storage.bucket
3387
- };
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
+ ];
3388
3985
  }
3389
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
+
3390
4005
  // src/commands/deploy/internal/deploy-shell-env.ts
3391
4006
  import { spawnSync as spawnSync3 } from "node:child_process";
3392
4007
  import { existsSync as existsSync15 } from "node:fs";
3393
4008
  import { dirname as dirname4, join as join16 } from "node:path";
3394
4009
 
3395
4010
  // src/commands/daemon.ts
4011
+ init_config();
3396
4012
  import { spawnSync as spawnSync2 } from "child_process";
3397
4013
  import { setTimeout as delay } from "node:timers/promises";
3398
4014
  import { existsSync as existsSync14, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
3399
4015
  import { join as join15 } from "path";
3400
4016
 
3401
4017
  // src/commands/connect-lock.ts
4018
+ init_config();
3402
4019
  import {
3403
4020
  existsSync as existsSync13,
3404
4021
  mkdirSync as mkdirSync5,
@@ -4533,6 +5150,7 @@ function runDeployShellCommand(command, cwd, captureOutput) {
4533
5150
 
4534
5151
  // src/commands/deploy/internal/wisdom-backend/index.ts
4535
5152
  import path9 from "node:path";
5153
+ init_deploy_artifact_minio();
4536
5154
 
4537
5155
  // src/commands/deploy/internal/wisdom-backend/jar-incremental.ts
4538
5156
  import {
@@ -4601,6 +5219,8 @@ import SftpClient from "ssh2-sftp-client";
4601
5219
  import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
4602
5220
  import path3 from "node:path";
4603
5221
  import JSZip from "jszip";
5222
+ init_deploy_artifact_minio();
5223
+ init_minio();
4604
5224
  async function addDirToZip(dir, zipFolder) {
4605
5225
  const entries = await readdir2(dir, { withFileTypes: true });
4606
5226
  for (const entry of entries) {
@@ -4689,6 +5309,7 @@ async function packDirectoryWithMinioArchive(input) {
4689
5309
  }
4690
5310
 
4691
5311
  // src/commands/deploy/internal/wisdom-sftp.ts
5312
+ init_deploy_artifact_minio();
4692
5313
  var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
4693
5314
  var SFTP_FAST_PUT_OPTIONS = {
4694
5315
  chunkSize: 64 * 1024,
@@ -5688,6 +6309,8 @@ async function executeWisdomDeploy(options) {
5688
6309
  }
5689
6310
 
5690
6311
  // src/commands/deploy/internal/deploy-baseline-sync.ts
6312
+ init_client();
6313
+ init_config();
5691
6314
  var LOG_PREFIX2 = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
5692
6315
  function formatGitError(error) {
5693
6316
  return error instanceof Error ? error.message : String(error);
@@ -5895,6 +6518,9 @@ function printDeployExecutionError(error) {
5895
6518
  console.error(error.message);
5896
6519
  }
5897
6520
 
6521
+ // src/commands/deploy/platform/deploy-run-coordinator.ts
6522
+ init_deploy_artifact_minio();
6523
+
5898
6524
  // src/commands/deploy/platform/deploy-console-capture.ts
5899
6525
  function captureDeployConsole(logSyncer) {
5900
6526
  const chunks = [];
@@ -6623,6 +7249,7 @@ function clearSessionAgentId(workdir, sessionId, user) {
6623
7249
  }
6624
7250
 
6625
7251
  // src/commands/connect/cursor-message-log.ts
7252
+ init_client();
6626
7253
  var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
6627
7254
  function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
6628
7255
  let lastRunAt = 0;
@@ -6790,7 +7417,38 @@ ${payload}`;
6790
7417
  };
6791
7418
  }
6792
7419
 
7420
+ // src/commands/connect/webide-merge-tools.ts
7421
+ init_client();
7422
+ function createMergeWebIdePullRequestsTool(options) {
7423
+ const { cfg, taskId } = options;
7424
+ return {
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.",
7426
+ inputSchema: {
7427
+ type: "object",
7428
+ properties: {
7429
+ confirmedConflictFree: {
7430
+ type: "boolean",
7431
+ description: "Must be true to confirm you intend to merge this task's open PRs now."
7432
+ }
7433
+ },
7434
+ required: ["confirmedConflictFree"]
7435
+ },
7436
+ execute: async (args) => {
7437
+ if (args.confirmedConflictFree !== true) {
7438
+ throw new Error(
7439
+ "MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
7440
+ );
7441
+ }
7442
+ const api = createApmApiClient(cfg);
7443
+ console.log(`[apm] MergeWebIdePullRequests taskId=${taskId}`);
7444
+ const result = await api.cli.webideMergePullRequests({ taskId });
7445
+ return JSON.stringify(result, null, 2);
7446
+ }
7447
+ };
7448
+ }
7449
+
6793
7450
  // src/commands/connect/webide-plan-tools.ts
7451
+ init_client();
6794
7452
  function asString(value) {
6795
7453
  return typeof value === "string" ? value.trim() : "";
6796
7454
  }
@@ -6860,6 +7518,7 @@ function createUpsertWebIdePlanTool(options) {
6860
7518
  }
6861
7519
 
6862
7520
  // src/commands/connect/webide-test-case-tools.ts
7521
+ init_client();
6863
7522
  function asString2(value) {
6864
7523
  return typeof value === "string" ? value.trim() : "";
6865
7524
  }
@@ -6944,6 +7603,165 @@ function createUpsertWebIdeTestCasesTool(options) {
6944
7603
  };
6945
7604
  }
6946
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
+
6947
7765
  // src/commands/connect/cursor-custom-tools.ts
6948
7766
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
6949
7767
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -6977,6 +7795,21 @@ function createCursorCustomTools(cfg, messageId, options) {
6977
7795
  cfg,
6978
7796
  taskId: options.taskId
6979
7797
  });
7798
+ tools.MergeWebIdePullRequests = createMergeWebIdePullRequestsTool({
7799
+ cfg,
7800
+ taskId: options.taskId
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
+ }
6980
7813
  }
6981
7814
  return tools;
6982
7815
  }
@@ -7100,7 +7933,9 @@ async function runCursorAgent(cfg, ctx, options) {
7100
7933
  appendMessageContent: options?.appendMessageContent,
7101
7934
  askQuestionExecute: options?.askQuestionExecute,
7102
7935
  enableWebIdePlanTools: options?.enableWebIdePlanTools,
7103
- taskId: options?.taskId
7936
+ taskId: options?.taskId,
7937
+ workdir,
7938
+ terminalRpc: options?.terminalRpc
7104
7939
  });
7105
7940
  const enableSandbox = Boolean(options?.enableSandbox);
7106
7941
  let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
@@ -7242,6 +8077,7 @@ async function runCursorAgent(cfg, ctx, options) {
7242
8077
  }
7243
8078
 
7244
8079
  // src/commands/connect/ensure-message-reply.ts
8080
+ init_client();
7245
8081
  var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
7246
8082
  function resolveMessageReplyFallback(fallback) {
7247
8083
  for (const candidate of [
@@ -7385,6 +8221,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
7385
8221
  }
7386
8222
 
7387
8223
  // src/commands/connect/webide-worker-pool.ts
8224
+ init_webide_terminal_registry();
7388
8225
  import { Worker } from "node:worker_threads";
7389
8226
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7390
8227
  import { dirname as dirname7, join as join20 } from "node:path";
@@ -7396,23 +8233,78 @@ function spawnWebIdeMessageWorker(cfg, msg) {
7396
8233
  const worker = new Worker(workerFile, {
7397
8234
  workerData: { cfg, msg }
7398
8235
  });
8236
+ const registry = getWebIdeTerminalRegistry(cfg);
7399
8237
  let resolveDone;
7400
8238
  const done = new Promise((resolve7) => {
7401
8239
  resolveDone = resolve7;
7402
8240
  });
7403
- worker.on(
7404
- "message",
7405
- (m) => {
7406
- if (m?.type === "done" || m?.type === "error") {
7407
- if (m.type === "error") {
7408
- console.error(
7409
- `[apm] webide worker error messageId=${m.messageId}: ${m.error}`
7410
- );
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
+ });
7411
8295
  }
7412
- 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
+ );
7413
8304
  }
8305
+ void worker.terminate().finally(() => resolveDone());
7414
8306
  }
7415
- );
8307
+ });
7416
8308
  worker.on("error", (err) => {
7417
8309
  console.error("[apm] webide worker crashed:", err);
7418
8310
  resolveDone();
@@ -7689,6 +8581,11 @@ function attachWsHandlers(ws, ctx, onOpen) {
7689
8581
  const msg2 = validated.data;
7690
8582
  const task2 = (async () => {
7691
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
+ );
7692
8589
  cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
7693
8590
  })();
7694
8591
  activeTasks.add(task2);
@@ -7697,6 +8594,21 @@ function attachWsHandlers(ws, ctx, onOpen) {
7697
8594
  });
7698
8595
  return;
7699
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
+ }
7700
8612
  if (validated.data.type === "webide-message") {
7701
8613
  const msg2 = validated.data;
7702
8614
  const perMessageController2 = new AbortController();
@@ -7961,6 +8873,10 @@ async function runConnect(options) {
7961
8873
  }
7962
8874
  }
7963
8875
 
8876
+ // src/commands/create-pr.ts
8877
+ init_client();
8878
+ init_config();
8879
+
7964
8880
  // src/commands/create-pr-errors.ts
7965
8881
  import { ApiError as ApiError2 } from "listpage-http";
7966
8882
  function extractMessage(error) {
@@ -8088,6 +9004,8 @@ async function runCreatePr(options) {
8088
9004
  }
8089
9005
 
8090
9006
  // src/commands/deploy/deploy-tracking.ts
9007
+ init_client();
9008
+ init_config();
8091
9009
  function normalizeDeployEnvironment(env) {
8092
9010
  const normalized = env.trim().toLowerCase();
8093
9011
  if (normalized === "test") return "TEST";
@@ -8757,6 +9675,7 @@ function registerDeployBackendCommands(program) {
8757
9675
  // src/commands/deploy/frontend.ts
8758
9676
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
8759
9677
  import path17 from "node:path";
9678
+ init_minio();
8760
9679
  function artifactObjectKey(namePrefix, branchSegment, relativePath) {
8761
9680
  const base = sanitizeRelativePath(namePrefix);
8762
9681
  const rel = sanitizeRelativePath(relativePath);
@@ -8934,6 +9853,7 @@ function registerDeployFrontendCommands(program) {
8934
9853
 
8935
9854
  // src/commands/deploy/sftp.ts
8936
9855
  import path18 from "node:path";
9856
+ init_minio();
8937
9857
  function registerDeploySftpCommands(program) {
8938
9858
  program.command("deploy-sftp").description(
8939
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"