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 +1434 -562
- package/dist/webide-message-worker.js +260 -101
- package/package.json +1 -1
- package/template/AGENTS.md +1 -1
- package/template/rules/webide_merge.md +5 -28
- package/template/rules/webide_terminal.md +35 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
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
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
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
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
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
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
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
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
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
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
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
|
-
|
|
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"
|