ai-project-manage-cli 7.1.11 → 7.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1472 -552
- package/dist/webide-message-worker.js +356 -40
- package/package.json +1 -1
- package/template/AGENTS.md +2 -1
- package/template/rules/webide_git_commit.md +4 -1
- package/template/rules/webide_merge.md +16 -0
- 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,
|
|
@@ -361,206 +1352,16 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
|
361
1352
|
copyTemplateEntry(join2(templateDir, name), join2(resolvedTarget, name));
|
|
362
1353
|
}
|
|
363
1354
|
for (const subdir of WORKSPACE_TEMPLATE_SUBDIRS) {
|
|
364
|
-
mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
|
|
365
|
-
}
|
|
366
|
-
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// src/deployment-config-sync.ts
|
|
370
|
-
import { join as join3 } from "path";
|
|
371
|
-
import { writeFileSync as writeFileSync3 } from "fs";
|
|
372
|
-
|
|
373
|
-
// src/api/client.ts
|
|
374
|
-
import { createApiClient } from "listpage-http";
|
|
375
|
-
|
|
376
|
-
// src/api/request-config.ts
|
|
377
|
-
import { defineEndpoint } from "listpage-http";
|
|
378
|
-
var requestConfig = {
|
|
379
|
-
cli: {
|
|
380
|
-
me: defineEndpoint(
|
|
381
|
-
{
|
|
382
|
-
method: "GET",
|
|
383
|
-
path: "/cli/me"
|
|
384
|
-
}
|
|
385
|
-
),
|
|
386
|
-
sessionDetail: defineEndpoint({
|
|
387
|
-
method: "GET",
|
|
388
|
-
path: "/cli/sessions/detail"
|
|
389
|
-
}),
|
|
390
|
-
sessionMembers: defineEndpoint({
|
|
391
|
-
method: "GET",
|
|
392
|
-
path: "/cli/sessions/members"
|
|
393
|
-
}),
|
|
394
|
-
listSessionMessages: defineEndpoint({
|
|
395
|
-
method: "GET",
|
|
396
|
-
path: "/cli/messages"
|
|
397
|
-
}),
|
|
398
|
-
listDocuments: defineEndpoint({
|
|
399
|
-
method: "GET",
|
|
400
|
-
path: "/cli/documents"
|
|
401
|
-
}),
|
|
402
|
-
getDevGate: defineEndpoint({
|
|
403
|
-
method: "GET",
|
|
404
|
-
path: "/cli/dev-gate"
|
|
405
|
-
}),
|
|
406
|
-
listAttachments: defineEndpoint(
|
|
407
|
-
{
|
|
408
|
-
method: "GET",
|
|
409
|
-
path: "/cli/attachments"
|
|
410
|
-
}
|
|
411
|
-
),
|
|
412
|
-
upsertDocument: defineEndpoint({
|
|
413
|
-
method: "PUT",
|
|
414
|
-
path: "/cli/documents/upsert"
|
|
415
|
-
}),
|
|
416
|
-
appendMessageContent: defineEndpoint({
|
|
417
|
-
method: "PUT",
|
|
418
|
-
path: "/cli/messages/content"
|
|
419
|
-
}),
|
|
420
|
-
setMessageError: defineEndpoint({
|
|
421
|
-
method: "PUT",
|
|
422
|
-
path: "/cli/messages/error"
|
|
423
|
-
}),
|
|
424
|
-
upsertCursorMessageLog: defineEndpoint({
|
|
425
|
-
method: "PUT",
|
|
426
|
-
path: "/cli/cursor-message-logs"
|
|
427
|
-
}),
|
|
428
|
-
updateMessageStatus: defineEndpoint({
|
|
429
|
-
method: "PUT",
|
|
430
|
-
path: "/cli/messages/status"
|
|
431
|
-
}),
|
|
432
|
-
webideAppendMessageContent: defineEndpoint({
|
|
433
|
-
method: "PUT",
|
|
434
|
-
path: "/cli/webide/messages/content"
|
|
435
|
-
}),
|
|
436
|
-
webideEnsureMessageContent: defineEndpoint({
|
|
437
|
-
method: "PUT",
|
|
438
|
-
path: "/cli/webide/messages/ensure-content"
|
|
439
|
-
}),
|
|
440
|
-
webideSetMessageError: defineEndpoint({
|
|
441
|
-
method: "PUT",
|
|
442
|
-
path: "/cli/webide/messages/error"
|
|
443
|
-
}),
|
|
444
|
-
webideUpdateMessageStatus: defineEndpoint({
|
|
445
|
-
method: "PUT",
|
|
446
|
-
path: "/cli/webide/messages/status"
|
|
447
|
-
}),
|
|
448
|
-
webideUpsertMessageLog: defineEndpoint({
|
|
449
|
-
method: "PUT",
|
|
450
|
-
path: "/cli/webide/message-logs"
|
|
451
|
-
}),
|
|
452
|
-
webideReplaceAssumptions: defineEndpoint({
|
|
453
|
-
method: "PUT",
|
|
454
|
-
path: "/cli/webide/assumptions"
|
|
455
|
-
}),
|
|
456
|
-
webideListAssumptions: defineEndpoint({
|
|
457
|
-
method: "GET",
|
|
458
|
-
path: "/cli/webide/assumptions"
|
|
459
|
-
}),
|
|
460
|
-
webideRecommendPlan: defineEndpoint({
|
|
461
|
-
method: "PUT",
|
|
462
|
-
path: "/cli/webide/plan-recommendation"
|
|
463
|
-
}),
|
|
464
|
-
webideUpsertPlan: defineEndpoint({
|
|
465
|
-
method: "PUT",
|
|
466
|
-
path: "/cli/webide/plan"
|
|
467
|
-
}),
|
|
468
|
-
webideReplaceTestCases: defineEndpoint({
|
|
469
|
-
method: "PUT",
|
|
470
|
-
path: "/cli/webide/test-cases"
|
|
471
|
-
}),
|
|
472
|
-
branchBaseline: defineEndpoint({
|
|
473
|
-
method: "GET",
|
|
474
|
-
path: "/cli/tasks/branch-baseline"
|
|
475
|
-
}),
|
|
476
|
-
listSessionsForBranchCleanup: defineEndpoint({
|
|
477
|
-
method: "GET",
|
|
478
|
-
path: "/cli/sessions/branch-cleanup"
|
|
479
|
-
}),
|
|
480
|
-
workspaceBaseline: defineEndpoint({
|
|
481
|
-
method: "GET",
|
|
482
|
-
path: "/cli/workspaces/baseline"
|
|
483
|
-
}),
|
|
484
|
-
getDeploymentConfiguration: defineEndpoint({
|
|
485
|
-
method: "GET",
|
|
486
|
-
path: "/cli/deployment-configurations"
|
|
487
|
-
}),
|
|
488
|
-
matchRepository: defineEndpoint({
|
|
489
|
-
method: "GET",
|
|
490
|
-
path: "/cli/repositories/match"
|
|
491
|
-
}),
|
|
492
|
-
listSkills: defineEndpoint({
|
|
493
|
-
method: "GET",
|
|
494
|
-
path: "/cli/skills"
|
|
495
|
-
}),
|
|
496
|
-
listRules: defineEndpoint({
|
|
497
|
-
method: "GET",
|
|
498
|
-
path: "/cli/rules"
|
|
499
|
-
}),
|
|
500
|
-
createPullRequest: defineEndpoint({
|
|
501
|
-
method: "POST",
|
|
502
|
-
path: "/cli/pull-requests"
|
|
503
|
-
}),
|
|
504
|
-
createWebIdeDraftPullRequest: defineEndpoint({
|
|
505
|
-
method: "POST",
|
|
506
|
-
path: "/cli/webide/draft-pull-requests"
|
|
507
|
-
}),
|
|
508
|
-
getRepositoryProjectDocumentManifest: defineEndpoint({
|
|
509
|
-
method: "GET",
|
|
510
|
-
path: "/cli/repository-project-documents/manifest"
|
|
511
|
-
}),
|
|
512
|
-
listRepositoryProjectDocuments: defineEndpoint({
|
|
513
|
-
method: "GET",
|
|
514
|
-
path: "/cli/repository-project-documents"
|
|
515
|
-
}),
|
|
516
|
-
upsertRepositoryProjectDocument: defineEndpoint({
|
|
517
|
-
method: "PUT",
|
|
518
|
-
path: "/cli/repository-project-documents/upsert"
|
|
519
|
-
}),
|
|
520
|
-
removeRepositoryProjectDocument: defineEndpoint({
|
|
521
|
-
method: "DELETE",
|
|
522
|
-
path: "/cli/repository-project-documents"
|
|
523
|
-
}),
|
|
524
|
-
createTaskDeployment: defineEndpoint({
|
|
525
|
-
method: "POST",
|
|
526
|
-
path: "/cli/task-deployments"
|
|
527
|
-
}),
|
|
528
|
-
updateTaskDeploymentStatus: defineEndpoint({
|
|
529
|
-
method: "PUT",
|
|
530
|
-
path: "/cli/task-deployments/status"
|
|
531
|
-
}),
|
|
532
|
-
syncTaskDeploymentLog: defineEndpoint({
|
|
533
|
-
method: "PUT",
|
|
534
|
-
path: "/cli/task-deployments/log"
|
|
535
|
-
}),
|
|
536
|
-
completeTaskDeployment: defineEndpoint({
|
|
537
|
-
method: "PUT",
|
|
538
|
-
path: "/cli/task-deployments/complete"
|
|
539
|
-
}),
|
|
540
|
-
getDeployArtifactStorage: defineEndpoint(
|
|
541
|
-
{
|
|
542
|
-
method: "GET",
|
|
543
|
-
path: "/cli/deploy-artifact-storage"
|
|
544
|
-
}
|
|
545
|
-
),
|
|
546
|
-
attachTaskDeploymentArtifact: defineEndpoint({
|
|
547
|
-
method: "PUT",
|
|
548
|
-
path: "/cli/task-deployments/artifact"
|
|
549
|
-
})
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
// src/api/client.ts
|
|
554
|
-
function createApmApiClient(cfg) {
|
|
555
|
-
const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
|
|
556
|
-
return createApiClient(requestConfig, {
|
|
557
|
-
baseURL,
|
|
558
|
-
getToken: () => resolveApiKey(cfg) || void 0,
|
|
559
|
-
successCodes: [0],
|
|
560
|
-
unauthorizedCodes: [401]
|
|
561
|
-
});
|
|
1355
|
+
mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
|
|
1356
|
+
}
|
|
1357
|
+
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
562
1358
|
}
|
|
563
1359
|
|
|
1360
|
+
// src/deployment-config-sync.ts
|
|
1361
|
+
init_client();
|
|
1362
|
+
import { join as join3 } from "path";
|
|
1363
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1364
|
+
|
|
564
1365
|
// src/git-remote.ts
|
|
565
1366
|
import { execFile } from "child_process";
|
|
566
1367
|
import { promisify } from "util";
|
|
@@ -648,6 +1449,22 @@ async function hasUpstream(cwd) {
|
|
|
648
1449
|
return false;
|
|
649
1450
|
}
|
|
650
1451
|
}
|
|
1452
|
+
async function pushCurrentBranch(cwd) {
|
|
1453
|
+
if (!await isGitRepo(cwd)) {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
const originUrl = await tryReadGitOriginUrl(cwd);
|
|
1457
|
+
if (!originUrl) {
|
|
1458
|
+
console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8DF3\u8FC7 push");
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
if (await hasUpstream(cwd)) {
|
|
1462
|
+
await execGit(cwd, ["push"]);
|
|
1463
|
+
} else {
|
|
1464
|
+
await execGit(cwd, ["push", "-u", "origin", "HEAD"]);
|
|
1465
|
+
}
|
|
1466
|
+
return true;
|
|
1467
|
+
}
|
|
651
1468
|
var GITIGNORE_COMMIT_MESSAGE = "chore(apm): ignore .apm directory";
|
|
652
1469
|
async function commitAndPushGitignore(workdir) {
|
|
653
1470
|
if (!await isGitRepo(workdir)) {
|
|
@@ -657,17 +1474,12 @@ async function commitAndPushGitignore(workdir) {
|
|
|
657
1474
|
await execGit(workdir, ["add", "--", ".gitignore"]);
|
|
658
1475
|
await execGit(workdir, ["commit", "-m", GITIGNORE_COMMIT_MESSAGE]);
|
|
659
1476
|
console.log(`[apm] \u5DF2\u63D0\u4EA4 .gitignore: ${GITIGNORE_COMMIT_MESSAGE}`);
|
|
660
|
-
const
|
|
661
|
-
if (
|
|
662
|
-
console.log("[apm] \
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
if (await hasUpstream(workdir)) {
|
|
666
|
-
await execGit(workdir, ["push"]);
|
|
1477
|
+
const pushed = await pushCurrentBranch(workdir);
|
|
1478
|
+
if (pushed) {
|
|
1479
|
+
console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
|
|
667
1480
|
} else {
|
|
668
|
-
|
|
1481
|
+
console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
|
|
669
1482
|
}
|
|
670
|
-
console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
|
|
671
1483
|
}
|
|
672
1484
|
|
|
673
1485
|
// src/baseline-resolve.ts
|
|
@@ -754,6 +1566,7 @@ async function resolveBranchBaseline(api, sessionId, workdirPath) {
|
|
|
754
1566
|
}
|
|
755
1567
|
|
|
756
1568
|
// src/deployment-config-sync.ts
|
|
1569
|
+
init_config();
|
|
757
1570
|
var TEMPLATE_HINT = "\u4FDD\u7559\u6A21\u677F .apm/apm.config.json";
|
|
758
1571
|
var SYNC_HINT = "\u767B\u8BB0\u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u3001\u7ED1\u5B9A\u4ED3\u5E93\u540E\uFF0C\u53EF\u6267\u884C: apm sync-deploy-config";
|
|
759
1572
|
async function resolveRepositoryIdForSync(api, workdirPath) {
|
|
@@ -817,6 +1630,8 @@ ${diagnostic ?? ""}
|
|
|
817
1630
|
}
|
|
818
1631
|
|
|
819
1632
|
// src/repository-project-documents-sync.ts
|
|
1633
|
+
init_client();
|
|
1634
|
+
init_config();
|
|
820
1635
|
import {
|
|
821
1636
|
existsSync as existsSync2,
|
|
822
1637
|
readdirSync as readdirSync2,
|
|
@@ -1082,6 +1897,8 @@ async function runInit(name) {
|
|
|
1082
1897
|
}
|
|
1083
1898
|
|
|
1084
1899
|
// src/commands/login.ts
|
|
1900
|
+
init_config();
|
|
1901
|
+
init_client();
|
|
1085
1902
|
import { existsSync as existsSync3 } from "fs";
|
|
1086
1903
|
import { ApiError } from "listpage-http";
|
|
1087
1904
|
async function runLogin(opts) {
|
|
@@ -1143,6 +1960,7 @@ async function runLogin(opts) {
|
|
|
1143
1960
|
}
|
|
1144
1961
|
|
|
1145
1962
|
// src/commands/branch.ts
|
|
1963
|
+
init_client();
|
|
1146
1964
|
var SESSION_BRANCH_PREFIX = "feat/session-";
|
|
1147
1965
|
function branchNameForSession(sessionId) {
|
|
1148
1966
|
const id = sessionId.trim();
|
|
@@ -1266,6 +2084,9 @@ async function runBranch(sessionId, options = {}) {
|
|
|
1266
2084
|
return ensureFeatureBranch(branch, baseline.defaultBranch, options);
|
|
1267
2085
|
}
|
|
1268
2086
|
|
|
2087
|
+
// src/commands/clean-branches.ts
|
|
2088
|
+
init_client();
|
|
2089
|
+
|
|
1269
2090
|
// src/commands/session-branch-prune.ts
|
|
1270
2091
|
var TASK_STATUS_LABEL = {
|
|
1271
2092
|
OPEN: "\u5F85\u5F00\u59CB",
|
|
@@ -1588,6 +2409,7 @@ async function runCleanBranches(options = {}) {
|
|
|
1588
2409
|
}
|
|
1589
2410
|
|
|
1590
2411
|
// src/commands/pull.ts
|
|
2412
|
+
init_client();
|
|
1591
2413
|
import { writeFileSync as writeFileSync9 } from "fs";
|
|
1592
2414
|
import { join as join9 } from "path";
|
|
1593
2415
|
import { stringify as yamlStringify } from "yaml";
|
|
@@ -1698,6 +2520,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
1698
2520
|
}
|
|
1699
2521
|
|
|
1700
2522
|
// src/rules-sync.ts
|
|
2523
|
+
init_client();
|
|
1701
2524
|
import { basename as basename2, extname as extname2, join as join8 } from "path";
|
|
1702
2525
|
import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
1703
2526
|
|
|
@@ -2120,6 +2943,7 @@ async function runUpdate(options = {}) {
|
|
|
2120
2943
|
}
|
|
2121
2944
|
|
|
2122
2945
|
// src/commands/update-skills.ts
|
|
2946
|
+
init_client();
|
|
2123
2947
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
|
|
2124
2948
|
import { join as join11 } from "path";
|
|
2125
2949
|
async function syncWorkspaceSkills(cfg, workdir) {
|
|
@@ -2383,7 +3207,11 @@ function validateAssumptionsMarkdown(markdown, source) {
|
|
|
2383
3207
|
};
|
|
2384
3208
|
}
|
|
2385
3209
|
|
|
3210
|
+
// src/commands/sync-document.ts
|
|
3211
|
+
init_client();
|
|
3212
|
+
|
|
2386
3213
|
// src/commands/sync-session-documents.ts
|
|
3214
|
+
init_client();
|
|
2387
3215
|
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
|
|
2388
3216
|
import { join as join12 } from "path";
|
|
2389
3217
|
function listLocalMarkdownFiles(docsDir) {
|
|
@@ -2504,6 +3332,7 @@ async function runSyncDocument(sessionId, options) {
|
|
|
2504
3332
|
}
|
|
2505
3333
|
|
|
2506
3334
|
// src/commands/dev-gate.ts
|
|
3335
|
+
init_client();
|
|
2507
3336
|
async function runDevGate(sessionId, source) {
|
|
2508
3337
|
const trimmedId = sessionId.trim();
|
|
2509
3338
|
if (!trimmedId) {
|
|
@@ -2523,6 +3352,7 @@ async function runDevGate(sessionId, source) {
|
|
|
2523
3352
|
}
|
|
2524
3353
|
|
|
2525
3354
|
// src/commands/append-message.ts
|
|
3355
|
+
init_client();
|
|
2526
3356
|
async function appendMessageContent(cfg, messageId, content) {
|
|
2527
3357
|
const trimmedId = messageId.trim();
|
|
2528
3358
|
if (!trimmedId) {
|
|
@@ -2551,6 +3381,7 @@ async function runAppendMessage(options) {
|
|
|
2551
3381
|
}
|
|
2552
3382
|
|
|
2553
3383
|
// src/commands/update-message-status.ts
|
|
3384
|
+
init_client();
|
|
2554
3385
|
var VALID_STATUSES = [
|
|
2555
3386
|
"CREATED",
|
|
2556
3387
|
"QUEUED",
|
|
@@ -2579,8 +3410,10 @@ async function runUpdateMessageStatus(options) {
|
|
|
2579
3410
|
}
|
|
2580
3411
|
|
|
2581
3412
|
// src/commands/connect.ts
|
|
3413
|
+
init_client();
|
|
2582
3414
|
import WebSocket from "ws";
|
|
2583
3415
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
3416
|
+
init_config();
|
|
2584
3417
|
|
|
2585
3418
|
// src/ws/protocol.ts
|
|
2586
3419
|
function nonEmptyString(v) {
|
|
@@ -2762,13 +3595,38 @@ function validateWebIdeCacheCleanupPush(o) {
|
|
|
2762
3595
|
}
|
|
2763
3596
|
};
|
|
2764
3597
|
}
|
|
3598
|
+
function validateWebIdeTerminalStopPush(o) {
|
|
3599
|
+
if (o.type !== "webide-terminal-stop") {
|
|
3600
|
+
return { ok: false, reason: "\u671F\u671B webide-terminal-stop" };
|
|
3601
|
+
}
|
|
3602
|
+
if (!nonEmptyString(o.taskId)) {
|
|
3603
|
+
return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 taskId" };
|
|
3604
|
+
}
|
|
3605
|
+
if (!nonEmptyString(o.terminalId)) {
|
|
3606
|
+
return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 terminalId" };
|
|
3607
|
+
}
|
|
3608
|
+
if (!nonEmptyString(o.key)) {
|
|
3609
|
+
return { ok: false, reason: "webide-terminal-stop \u7F3A\u5C11 key" };
|
|
3610
|
+
}
|
|
3611
|
+
const pid = typeof o.pid === "number" && Number.isFinite(o.pid) ? Math.trunc(o.pid) : o.pid === null ? null : void 0;
|
|
3612
|
+
return {
|
|
3613
|
+
ok: true,
|
|
3614
|
+
data: {
|
|
3615
|
+
type: "webide-terminal-stop",
|
|
3616
|
+
taskId: o.taskId.trim(),
|
|
3617
|
+
terminalId: o.terminalId.trim(),
|
|
3618
|
+
key: o.key.trim(),
|
|
3619
|
+
...pid !== void 0 ? { pid } : {}
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
2765
3623
|
function validateAgentWsMessage(value, kind) {
|
|
2766
3624
|
if (typeof value !== "object" || value === null) {
|
|
2767
3625
|
return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
2768
3626
|
}
|
|
2769
3627
|
const o = value;
|
|
2770
3628
|
const type = o.type;
|
|
2771
|
-
if (type !== "heartbeat" && type !== "message" && type !== "webide-message" && type !== "cancel" && type !== "deploy" && type !== "session-cache-cleanup" && type !== "webide-cache-cleanup") {
|
|
3629
|
+
if (type !== "heartbeat" && type !== "message" && type !== "webide-message" && type !== "cancel" && type !== "deploy" && type !== "session-cache-cleanup" && type !== "webide-cache-cleanup" && type !== "webide-terminal-stop") {
|
|
2772
3630
|
return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
|
|
2773
3631
|
}
|
|
2774
3632
|
if (kind === "heartbeat" || type === "heartbeat") {
|
|
@@ -2786,12 +3644,18 @@ function validateAgentWsMessage(value, kind) {
|
|
|
2786
3644
|
if (type === "webide-cache-cleanup") {
|
|
2787
3645
|
return validateWebIdeCacheCleanupPush(o);
|
|
2788
3646
|
}
|
|
3647
|
+
if (type === "webide-terminal-stop") {
|
|
3648
|
+
return validateWebIdeTerminalStopPush(o);
|
|
3649
|
+
}
|
|
2789
3650
|
if (type === "webide-message") {
|
|
2790
3651
|
return validateWebIdeMessagePush(o);
|
|
2791
3652
|
}
|
|
2792
3653
|
return validateMessagePush(o);
|
|
2793
3654
|
}
|
|
2794
3655
|
|
|
3656
|
+
// src/commands/connect/deploy-run.ts
|
|
3657
|
+
init_client();
|
|
3658
|
+
|
|
2795
3659
|
// src/commands/deploy/deploy-execute.ts
|
|
2796
3660
|
import path11 from "node:path";
|
|
2797
3661
|
|
|
@@ -3052,353 +3916,106 @@ function resolveWisdomBackendDeployFromApmConfig(cfg) {
|
|
|
3052
3916
|
console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
|
|
3053
3917
|
process.exit(1);
|
|
3054
3918
|
}
|
|
3055
|
-
if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
|
|
3056
|
-
console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
|
|
3057
|
-
process.exit(1);
|
|
3058
|
-
}
|
|
3059
|
-
const mavenLocalRepo = resolveMavenLocalRepoWithSource();
|
|
3060
|
-
return {
|
|
3061
|
-
projectName,
|
|
3062
|
-
host: req(w.host, "host", "wisdomDeploy").trim(),
|
|
3063
|
-
port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
|
|
3064
|
-
username: req(w.username, "username", "wisdomDeploy").trim(),
|
|
3065
|
-
password: req(w.password, "password", "wisdomDeploy").trim(),
|
|
3066
|
-
remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
|
|
3067
|
-
remoteAppDir,
|
|
3068
|
-
remoteLibDir: `${remoteAppDir}/lib`,
|
|
3069
|
-
startupJar: posixBasename(jarPath),
|
|
3070
|
-
packageName: `${projectName}.jar.zip`,
|
|
3071
|
-
mavenLocalRepo: mavenLocalRepo.path,
|
|
3072
|
-
mavenLocalRepoSource: mavenLocalRepo.source,
|
|
3073
|
-
mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
|
|
3074
|
-
healthCheckPort: healthPort,
|
|
3075
|
-
healthCheckContext: req(h.context, "context", "healthCheck").trim(),
|
|
3076
|
-
healthCheckTimeout: healthTimeout,
|
|
3077
|
-
mode: resolveWisdomDeployMode(w.mode)
|
|
3078
|
-
};
|
|
3079
|
-
}
|
|
3080
|
-
|
|
3081
|
-
// src/commands/deploy/internal/apm-config.ts
|
|
3082
|
-
function loadApmConfig(options) {
|
|
3083
|
-
const p = resolve4(
|
|
3084
|
-
process.cwd(),
|
|
3085
|
-
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
3086
|
-
);
|
|
3087
|
-
if (!existsSync12(p)) {
|
|
3088
|
-
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
3089
|
-
process.exit(1);
|
|
3090
|
-
}
|
|
3091
|
-
try {
|
|
3092
|
-
const raw = readFileSync11(p, "utf8");
|
|
3093
|
-
return JSON.parse(raw);
|
|
3094
|
-
} catch (e) {
|
|
3095
|
-
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
3096
|
-
process.exit(1);
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
|
-
// src/commands/deploy/internal/deploy-hints.ts
|
|
3101
|
-
function isProductionDeployEnv(env) {
|
|
3102
|
-
const normalized = env.trim().toLowerCase();
|
|
3103
|
-
return normalized === "online" || normalized === "production" || normalized === "prod";
|
|
3104
|
-
}
|
|
3105
|
-
function formatMissingWisdomDeployLines() {
|
|
3106
|
-
return [
|
|
3107
|
-
"[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
|
|
3108
|
-
];
|
|
3109
|
-
}
|
|
3110
|
-
function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
|
|
3111
|
-
if (isProductionDeployEnv(env)) {
|
|
3112
|
-
return [
|
|
3113
|
-
`[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
|
|
3114
|
-
"[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
|
|
3115
|
-
];
|
|
3116
|
-
}
|
|
3117
|
-
return [
|
|
3118
|
-
`[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
|
|
3119
|
-
"[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
|
|
3120
|
-
];
|
|
3121
|
-
}
|
|
3122
|
-
|
|
3123
|
-
// src/commands/deploy/deploy-errors.ts
|
|
3124
|
-
var DeployExecutionError = class extends Error {
|
|
3125
|
-
exitCode;
|
|
3126
|
-
output;
|
|
3127
|
-
constructor(message, exitCode, output = "") {
|
|
3128
|
-
super(message);
|
|
3129
|
-
this.name = "DeployExecutionError";
|
|
3130
|
-
this.exitCode = exitCode;
|
|
3131
|
-
this.output = output;
|
|
3132
|
-
}
|
|
3133
|
-
};
|
|
3134
|
-
|
|
3135
|
-
// src/commands/deploy/deploy-debug-log.ts
|
|
3136
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
3137
|
-
import { existsSync as existsSync16 } from "node:fs";
|
|
3138
|
-
import { dirname as dirname5, join as join17 } from "node:path";
|
|
3139
|
-
|
|
3140
|
-
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
3141
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
3142
|
-
|
|
3143
|
-
// src/commands/deploy/internal/minio.ts
|
|
3144
|
-
import { statSync as statSync5 } from "node:fs";
|
|
3145
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3146
|
-
import path from "node:path";
|
|
3147
|
-
import * as Minio from "minio";
|
|
3148
|
-
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
3149
|
-
async function isDirectoryPath(dir) {
|
|
3150
|
-
try {
|
|
3151
|
-
const st = statSync5(dir);
|
|
3152
|
-
return st.isDirectory();
|
|
3153
|
-
} catch {
|
|
3154
|
-
return false;
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
function sanitizeRelativePath(rel) {
|
|
3158
|
-
const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
3159
|
-
const segments = norm.split("/").filter(Boolean);
|
|
3160
|
-
for (const s of segments) {
|
|
3161
|
-
if (s === "." || s === "..") {
|
|
3162
|
-
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
return segments.join("/");
|
|
3166
|
-
}
|
|
3167
|
-
async function collectFiles(root) {
|
|
3168
|
-
const out = [];
|
|
3169
|
-
async function walk(dir, prefix) {
|
|
3170
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
3171
|
-
for (const e of entries) {
|
|
3172
|
-
const name = e.name;
|
|
3173
|
-
if (name === "." || name === "..") {
|
|
3174
|
-
continue;
|
|
3175
|
-
}
|
|
3176
|
-
const abs = path.join(dir, name);
|
|
3177
|
-
const rel = prefix ? `${prefix}/${name}` : name;
|
|
3178
|
-
if (e.isDirectory()) {
|
|
3179
|
-
await walk(abs, rel);
|
|
3180
|
-
} else if (e.isFile()) {
|
|
3181
|
-
const st = statSync5(abs);
|
|
3182
|
-
out.push({
|
|
3183
|
-
absPath: abs,
|
|
3184
|
-
relativePath: rel.replace(/\\/g, "/"),
|
|
3185
|
-
size: st.size
|
|
3186
|
-
});
|
|
3187
|
-
}
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3190
|
-
await walk(root, "");
|
|
3191
|
-
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
3192
|
-
return out;
|
|
3193
|
-
}
|
|
3194
|
-
async function readArtifactFile(absPath) {
|
|
3195
|
-
return readFile(absPath);
|
|
3196
|
-
}
|
|
3197
|
-
function toMB(bytes) {
|
|
3198
|
-
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
3199
|
-
}
|
|
3200
|
-
var MIME = {
|
|
3201
|
-
".html": "text/html; charset=utf-8",
|
|
3202
|
-
".css": "text/css; charset=utf-8",
|
|
3203
|
-
".js": "application/javascript; charset=utf-8",
|
|
3204
|
-
".json": "application/json; charset=utf-8",
|
|
3205
|
-
".svg": "image/svg+xml",
|
|
3206
|
-
".png": "image/png",
|
|
3207
|
-
".jpg": "image/jpeg",
|
|
3208
|
-
".jpeg": "image/jpeg",
|
|
3209
|
-
".gif": "image/gif",
|
|
3210
|
-
".webp": "image/webp",
|
|
3211
|
-
".woff": "font/woff",
|
|
3212
|
-
".woff2": "font/woff2",
|
|
3213
|
-
".ttf": "font/ttf",
|
|
3214
|
-
".ico": "image/x-icon",
|
|
3215
|
-
".txt": "text/plain; charset=utf-8",
|
|
3216
|
-
".map": "application/json"
|
|
3217
|
-
};
|
|
3218
|
-
function detectMimeType(filePath) {
|
|
3219
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
3220
|
-
return MIME[ext] ?? "";
|
|
3221
|
-
}
|
|
3222
|
-
var MinioClient = class {
|
|
3223
|
-
inner;
|
|
3224
|
-
constructor(opts) {
|
|
3225
|
-
const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
|
|
3226
|
-
this.inner = new Minio.Client({
|
|
3227
|
-
endPoint,
|
|
3228
|
-
port: opts.port,
|
|
3229
|
-
useSSL: opts.useSSL,
|
|
3230
|
-
accessKey: opts.accessKey,
|
|
3231
|
-
secretKey: opts.secretKey
|
|
3232
|
-
});
|
|
3233
|
-
}
|
|
3234
|
-
async ensureBucket(bucket) {
|
|
3235
|
-
const exists = await this.inner.bucketExists(bucket);
|
|
3236
|
-
if (!exists) {
|
|
3237
|
-
await this.inner.makeBucket(bucket);
|
|
3238
|
-
}
|
|
3239
|
-
}
|
|
3240
|
-
async deleteObjectsByPrefix(bucket, prefix) {
|
|
3241
|
-
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
3242
|
-
const keys = [];
|
|
3243
|
-
await new Promise((resolve7, reject) => {
|
|
3244
|
-
objectsStream.on("data", (obj) => {
|
|
3245
|
-
if (obj.name) {
|
|
3246
|
-
keys.push(obj.name);
|
|
3247
|
-
}
|
|
3248
|
-
});
|
|
3249
|
-
objectsStream.on("error", reject);
|
|
3250
|
-
objectsStream.on("end", resolve7);
|
|
3251
|
-
});
|
|
3252
|
-
const chunkSize = 500;
|
|
3253
|
-
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
3254
|
-
const chunk = keys.slice(i, i + chunkSize);
|
|
3255
|
-
await this.inner.removeObjects(
|
|
3256
|
-
bucket,
|
|
3257
|
-
chunk.map((name) => name)
|
|
3258
|
-
);
|
|
3259
|
-
}
|
|
3260
|
-
}
|
|
3261
|
-
async putObject(bucket, objectKey, body, meta) {
|
|
3262
|
-
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
3263
|
-
}
|
|
3264
|
-
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
3265
|
-
async setBucketPublicRead(bucket) {
|
|
3266
|
-
const policy = {
|
|
3267
|
-
Version: "2012-10-17",
|
|
3268
|
-
Statement: [
|
|
3269
|
-
{
|
|
3270
|
-
Effect: "Allow",
|
|
3271
|
-
Principal: { AWS: ["*"] },
|
|
3272
|
-
Action: ["s3:GetObject"],
|
|
3273
|
-
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
3274
|
-
}
|
|
3275
|
-
]
|
|
3276
|
-
};
|
|
3277
|
-
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
3278
|
-
}
|
|
3279
|
-
};
|
|
3280
|
-
|
|
3281
|
-
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
3282
|
-
var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
|
|
3283
|
-
function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
3284
|
-
const pad2 = (n) => String(n).padStart(2, "0");
|
|
3285
|
-
return `${date.getFullYear()}${pad2(date.getMonth() + 1)}${pad2(date.getDate())}${pad2(date.getHours())}${pad2(date.getMinutes())}${pad2(date.getSeconds())}`;
|
|
3286
|
-
}
|
|
3287
|
-
function sanitizeDeployProjectName(name) {
|
|
3288
|
-
const trimmed = name.trim().replace(/\\/g, "/");
|
|
3289
|
-
const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
|
|
3290
|
-
try {
|
|
3291
|
-
return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
|
|
3292
|
-
} catch {
|
|
3293
|
-
return "project";
|
|
3294
|
-
}
|
|
3295
|
-
}
|
|
3296
|
-
function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
|
|
3297
|
-
const safeName = sanitizeDeployProjectName(projectName);
|
|
3298
|
-
const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
|
|
3299
|
-
return `${timestamp}-${safeName}.${suffix}`;
|
|
3300
|
-
}
|
|
3301
|
-
function buildDeployArtifactObjectKey(projectName, fileName) {
|
|
3302
|
-
const safeProject = sanitizeDeployProjectName(projectName);
|
|
3303
|
-
return `deploy/${safeProject}/${fileName}`;
|
|
3304
|
-
}
|
|
3305
|
-
function resolveDeploymentRunIdFromEnv() {
|
|
3306
|
-
const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
|
|
3307
|
-
return raw || null;
|
|
3308
|
-
}
|
|
3309
|
-
async function fetchDeployArtifactStorage(api) {
|
|
3310
|
-
return api.cli.getDeployArtifactStorage(void 0);
|
|
3311
|
-
}
|
|
3312
|
-
async function uploadDeployArtifactZip(options) {
|
|
3313
|
-
const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
|
|
3314
|
-
if (!deploymentRunId && !options.uploadWithoutRunId) {
|
|
3315
|
-
return null;
|
|
3316
|
-
}
|
|
3317
|
-
const cfg = await tryReadApmConfig();
|
|
3318
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
3319
|
-
console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
|
|
3320
|
-
return null;
|
|
3321
|
-
}
|
|
3322
|
-
const api = options.api ?? createApmApiClient(cfg);
|
|
3323
|
-
let storage;
|
|
3324
|
-
try {
|
|
3325
|
-
storage = await fetchDeployArtifactStorage(api);
|
|
3326
|
-
} catch (error) {
|
|
3327
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
3328
|
-
console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
|
|
3329
|
-
return null;
|
|
3919
|
+
if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
|
|
3920
|
+
console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
|
|
3921
|
+
process.exit(1);
|
|
3330
3922
|
}
|
|
3331
|
-
const
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3923
|
+
const mavenLocalRepo = resolveMavenLocalRepoWithSource();
|
|
3924
|
+
return {
|
|
3925
|
+
projectName,
|
|
3926
|
+
host: req(w.host, "host", "wisdomDeploy").trim(),
|
|
3927
|
+
port: reqPositiveInt(w.port, "port", "wisdomDeploy"),
|
|
3928
|
+
username: req(w.username, "username", "wisdomDeploy").trim(),
|
|
3929
|
+
password: req(w.password, "password", "wisdomDeploy").trim(),
|
|
3930
|
+
remoteVueDistDir: req(w.remotePath, "remotePath", "wisdomDeploy").trim(),
|
|
3931
|
+
remoteAppDir,
|
|
3932
|
+
remoteLibDir: `${remoteAppDir}/lib`,
|
|
3933
|
+
startupJar: posixBasename(jarPath),
|
|
3934
|
+
packageName: `${projectName}.jar.zip`,
|
|
3935
|
+
mavenLocalRepo: mavenLocalRepo.path,
|
|
3936
|
+
mavenLocalRepoSource: mavenLocalRepo.source,
|
|
3937
|
+
mavenLocalRepoSourceDetail: mavenLocalRepo.sourceDetail,
|
|
3938
|
+
healthCheckPort: healthPort,
|
|
3939
|
+
healthCheckContext: req(h.context, "context", "healthCheck").trim(),
|
|
3940
|
+
healthCheckTimeout: healthTimeout,
|
|
3941
|
+
mode: resolveWisdomDeployMode(w.mode)
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
|
|
3945
|
+
// src/commands/deploy/internal/apm-config.ts
|
|
3946
|
+
function loadApmConfig(options) {
|
|
3947
|
+
const p = resolve4(
|
|
3948
|
+
process.cwd(),
|
|
3949
|
+
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
3335
3950
|
);
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
body = await readFile2(options.zipPath);
|
|
3340
|
-
} catch (error) {
|
|
3341
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
3342
|
-
console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
|
|
3343
|
-
return null;
|
|
3951
|
+
if (!existsSync12(p)) {
|
|
3952
|
+
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
3953
|
+
process.exit(1);
|
|
3344
3954
|
}
|
|
3345
3955
|
try {
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
secretKey: storage.secretKey
|
|
3352
|
-
});
|
|
3353
|
-
await client.ensureBucket(storage.bucket);
|
|
3354
|
-
await client.putObject(storage.bucket, objectKey, body, {
|
|
3355
|
-
"Content-Type": "application/zip"
|
|
3356
|
-
});
|
|
3357
|
-
console.error(
|
|
3358
|
-
`[apm] \u672C\u5730\u76F4\u4F20 MinIO\uFF08\u4E0D\u7ECF\u5E73\u53F0\u670D\u52A1\u5668\uFF09: ${storage.endpoint}:${storage.port}/${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(
|
|
3359
|
-
2
|
|
3360
|
-
)} MB)`
|
|
3361
|
-
);
|
|
3362
|
-
} catch (error) {
|
|
3363
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
3364
|
-
console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
|
|
3365
|
-
return null;
|
|
3956
|
+
const raw = readFileSync11(p, "utf8");
|
|
3957
|
+
return JSON.parse(raw);
|
|
3958
|
+
} catch (e) {
|
|
3959
|
+
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
3960
|
+
process.exit(1);
|
|
3366
3961
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
// src/commands/deploy/internal/deploy-hints.ts
|
|
3965
|
+
function isProductionDeployEnv(env) {
|
|
3966
|
+
const normalized = env.trim().toLowerCase();
|
|
3967
|
+
return normalized === "online" || normalized === "production" || normalized === "prod";
|
|
3968
|
+
}
|
|
3969
|
+
function formatMissingWisdomDeployLines() {
|
|
3970
|
+
return [
|
|
3971
|
+
"[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
|
|
3972
|
+
];
|
|
3973
|
+
}
|
|
3974
|
+
function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
|
|
3975
|
+
if (isProductionDeployEnv(env)) {
|
|
3976
|
+
return [
|
|
3977
|
+
`[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u6216 build \u811A\u672C`,
|
|
3978
|
+
"[apm] \u8BF7\u914D\u7F6E npm run build:online \u6216 npm run build \u540E\u518D\u8BD5"
|
|
3979
|
+
];
|
|
3382
3980
|
}
|
|
3383
|
-
return
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
};
|
|
3981
|
+
return [
|
|
3982
|
+
`[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
|
|
3983
|
+
"[apm] \u8BF7\u914D\u7F6E npm run build:test \u540E\u518D\u8BD5"
|
|
3984
|
+
];
|
|
3388
3985
|
}
|
|
3389
3986
|
|
|
3987
|
+
// src/commands/deploy/deploy-errors.ts
|
|
3988
|
+
var DeployExecutionError = class extends Error {
|
|
3989
|
+
exitCode;
|
|
3990
|
+
output;
|
|
3991
|
+
constructor(message, exitCode, output = "") {
|
|
3992
|
+
super(message);
|
|
3993
|
+
this.name = "DeployExecutionError";
|
|
3994
|
+
this.exitCode = exitCode;
|
|
3995
|
+
this.output = output;
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
|
|
3999
|
+
// src/commands/deploy/deploy-debug-log.ts
|
|
4000
|
+
init_deploy_artifact_minio();
|
|
4001
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4002
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
4003
|
+
import { dirname as dirname5, join as join17 } from "node:path";
|
|
4004
|
+
|
|
3390
4005
|
// src/commands/deploy/internal/deploy-shell-env.ts
|
|
3391
4006
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
3392
4007
|
import { existsSync as existsSync15 } from "node:fs";
|
|
3393
4008
|
import { dirname as dirname4, join as join16 } from "node:path";
|
|
3394
4009
|
|
|
3395
4010
|
// src/commands/daemon.ts
|
|
4011
|
+
init_config();
|
|
3396
4012
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3397
4013
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3398
4014
|
import { existsSync as existsSync14, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
3399
4015
|
import { join as join15 } from "path";
|
|
3400
4016
|
|
|
3401
4017
|
// src/commands/connect-lock.ts
|
|
4018
|
+
init_config();
|
|
3402
4019
|
import {
|
|
3403
4020
|
existsSync as existsSync13,
|
|
3404
4021
|
mkdirSync as mkdirSync5,
|
|
@@ -4533,6 +5150,7 @@ function runDeployShellCommand(command, cwd, captureOutput) {
|
|
|
4533
5150
|
|
|
4534
5151
|
// src/commands/deploy/internal/wisdom-backend/index.ts
|
|
4535
5152
|
import path9 from "node:path";
|
|
5153
|
+
init_deploy_artifact_minio();
|
|
4536
5154
|
|
|
4537
5155
|
// src/commands/deploy/internal/wisdom-backend/jar-incremental.ts
|
|
4538
5156
|
import {
|
|
@@ -4601,6 +5219,8 @@ import SftpClient from "ssh2-sftp-client";
|
|
|
4601
5219
|
import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
|
|
4602
5220
|
import path3 from "node:path";
|
|
4603
5221
|
import JSZip from "jszip";
|
|
5222
|
+
init_deploy_artifact_minio();
|
|
5223
|
+
init_minio();
|
|
4604
5224
|
async function addDirToZip(dir, zipFolder) {
|
|
4605
5225
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
4606
5226
|
for (const entry of entries) {
|
|
@@ -4689,6 +5309,7 @@ async function packDirectoryWithMinioArchive(input) {
|
|
|
4689
5309
|
}
|
|
4690
5310
|
|
|
4691
5311
|
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
5312
|
+
init_deploy_artifact_minio();
|
|
4692
5313
|
var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
|
|
4693
5314
|
var SFTP_FAST_PUT_OPTIONS = {
|
|
4694
5315
|
chunkSize: 64 * 1024,
|
|
@@ -5688,6 +6309,8 @@ async function executeWisdomDeploy(options) {
|
|
|
5688
6309
|
}
|
|
5689
6310
|
|
|
5690
6311
|
// src/commands/deploy/internal/deploy-baseline-sync.ts
|
|
6312
|
+
init_client();
|
|
6313
|
+
init_config();
|
|
5691
6314
|
var LOG_PREFIX2 = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
|
|
5692
6315
|
function formatGitError(error) {
|
|
5693
6316
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -5895,6 +6518,9 @@ function printDeployExecutionError(error) {
|
|
|
5895
6518
|
console.error(error.message);
|
|
5896
6519
|
}
|
|
5897
6520
|
|
|
6521
|
+
// src/commands/deploy/platform/deploy-run-coordinator.ts
|
|
6522
|
+
init_deploy_artifact_minio();
|
|
6523
|
+
|
|
5898
6524
|
// src/commands/deploy/platform/deploy-console-capture.ts
|
|
5899
6525
|
function captureDeployConsole(logSyncer) {
|
|
5900
6526
|
const chunks = [];
|
|
@@ -6623,6 +7249,7 @@ function clearSessionAgentId(workdir, sessionId, user) {
|
|
|
6623
7249
|
}
|
|
6624
7250
|
|
|
6625
7251
|
// src/commands/connect/cursor-message-log.ts
|
|
7252
|
+
init_client();
|
|
6626
7253
|
var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
|
|
6627
7254
|
function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
|
|
6628
7255
|
let lastRunAt = 0;
|
|
@@ -6790,7 +7417,38 @@ ${payload}`;
|
|
|
6790
7417
|
};
|
|
6791
7418
|
}
|
|
6792
7419
|
|
|
7420
|
+
// src/commands/connect/webide-merge-tools.ts
|
|
7421
|
+
init_client();
|
|
7422
|
+
function createMergeWebIdePullRequestsTool(options) {
|
|
7423
|
+
const { cfg, taskId } = options;
|
|
7424
|
+
return {
|
|
7425
|
+
description: "Merge all open pull requests for this WebIDE task. Call once with confirmedConflictFree=true. No baseline sync or review is required before calling.",
|
|
7426
|
+
inputSchema: {
|
|
7427
|
+
type: "object",
|
|
7428
|
+
properties: {
|
|
7429
|
+
confirmedConflictFree: {
|
|
7430
|
+
type: "boolean",
|
|
7431
|
+
description: "Must be true to confirm you intend to merge this task's open PRs now."
|
|
7432
|
+
}
|
|
7433
|
+
},
|
|
7434
|
+
required: ["confirmedConflictFree"]
|
|
7435
|
+
},
|
|
7436
|
+
execute: async (args) => {
|
|
7437
|
+
if (args.confirmedConflictFree !== true) {
|
|
7438
|
+
throw new Error(
|
|
7439
|
+
"MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
|
|
7440
|
+
);
|
|
7441
|
+
}
|
|
7442
|
+
const api = createApmApiClient(cfg);
|
|
7443
|
+
console.log(`[apm] MergeWebIdePullRequests taskId=${taskId}`);
|
|
7444
|
+
const result = await api.cli.webideMergePullRequests({ taskId });
|
|
7445
|
+
return JSON.stringify(result, null, 2);
|
|
7446
|
+
}
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
|
|
6793
7450
|
// src/commands/connect/webide-plan-tools.ts
|
|
7451
|
+
init_client();
|
|
6794
7452
|
function asString(value) {
|
|
6795
7453
|
return typeof value === "string" ? value.trim() : "";
|
|
6796
7454
|
}
|
|
@@ -6860,6 +7518,7 @@ function createUpsertWebIdePlanTool(options) {
|
|
|
6860
7518
|
}
|
|
6861
7519
|
|
|
6862
7520
|
// src/commands/connect/webide-test-case-tools.ts
|
|
7521
|
+
init_client();
|
|
6863
7522
|
function asString2(value) {
|
|
6864
7523
|
return typeof value === "string" ? value.trim() : "";
|
|
6865
7524
|
}
|
|
@@ -6944,6 +7603,165 @@ function createUpsertWebIdeTestCasesTool(options) {
|
|
|
6944
7603
|
};
|
|
6945
7604
|
}
|
|
6946
7605
|
|
|
7606
|
+
// src/commands/connect/webide-terminal-tools.ts
|
|
7607
|
+
init_client();
|
|
7608
|
+
function asString3(value) {
|
|
7609
|
+
return typeof value === "string" ? value.trim() : "";
|
|
7610
|
+
}
|
|
7611
|
+
function asPorts(value) {
|
|
7612
|
+
if (!Array.isArray(value)) return void 0;
|
|
7613
|
+
const ports = value.map((p) => typeof p === "number" ? p : Number(p)).filter((p) => Number.isFinite(p) && p > 0 && p < 65536).map((p) => Math.trunc(p));
|
|
7614
|
+
return ports.length ? ports : void 0;
|
|
7615
|
+
}
|
|
7616
|
+
function createWebIdeTerminalTools(options) {
|
|
7617
|
+
const { cfg, taskId, workdir, rpc } = options;
|
|
7618
|
+
const StartWebIdeTerminal = {
|
|
7619
|
+
description: "Start a long-running WebIDE terminal (e.g. frontend/backend dev server). Process outlives this message. Same key already RUNNING/READY returns idempotently. Do NOT use Shell for long-lived dev servers. Returns terminalId, pid, ports.",
|
|
7620
|
+
inputSchema: {
|
|
7621
|
+
type: "object",
|
|
7622
|
+
properties: {
|
|
7623
|
+
key: {
|
|
7624
|
+
type: "string",
|
|
7625
|
+
description: "Stable key, e.g. frontend / backend"
|
|
7626
|
+
},
|
|
7627
|
+
name: { type: "string", description: "Display name" },
|
|
7628
|
+
cwd: {
|
|
7629
|
+
type: "string",
|
|
7630
|
+
description: "Working directory (absolute or under workspace)"
|
|
7631
|
+
},
|
|
7632
|
+
command: {
|
|
7633
|
+
type: "string",
|
|
7634
|
+
description: "Shell command to run, e.g. pnpm dev"
|
|
7635
|
+
},
|
|
7636
|
+
readyPattern: {
|
|
7637
|
+
type: "string",
|
|
7638
|
+
description: "Optional regex; when matched in logs, status becomes READY"
|
|
7639
|
+
},
|
|
7640
|
+
portsHint: {
|
|
7641
|
+
type: "array",
|
|
7642
|
+
items: { type: "number" },
|
|
7643
|
+
description: "Optional expected listen ports"
|
|
7644
|
+
}
|
|
7645
|
+
},
|
|
7646
|
+
required: ["key", "cwd", "command"]
|
|
7647
|
+
},
|
|
7648
|
+
execute: async (args) => {
|
|
7649
|
+
const key = asString3(args.key);
|
|
7650
|
+
const cwd = asString3(args.cwd) || workdir;
|
|
7651
|
+
const command = asString3(args.command);
|
|
7652
|
+
if (!key || !command) {
|
|
7653
|
+
return {
|
|
7654
|
+
content: [{ type: "text", text: "key / command \u4E0D\u80FD\u4E3A\u7A7A" }],
|
|
7655
|
+
isError: true
|
|
7656
|
+
};
|
|
7657
|
+
}
|
|
7658
|
+
try {
|
|
7659
|
+
const result = rpc ? await rpc.start({
|
|
7660
|
+
taskId,
|
|
7661
|
+
key,
|
|
7662
|
+
name: asString3(args.name) || void 0,
|
|
7663
|
+
cwd,
|
|
7664
|
+
command,
|
|
7665
|
+
readyPattern: asString3(args.readyPattern) || void 0,
|
|
7666
|
+
portsHint: asPorts(args.portsHint)
|
|
7667
|
+
}) : await (async () => {
|
|
7668
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
7669
|
+
})();
|
|
7670
|
+
return JSON.stringify(result, null, 2);
|
|
7671
|
+
} catch (err) {
|
|
7672
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7673
|
+
return {
|
|
7674
|
+
content: [{ type: "text", text: `\u542F\u52A8\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
7675
|
+
isError: true
|
|
7676
|
+
};
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
};
|
|
7680
|
+
const StopWebIdeTerminal = {
|
|
7681
|
+
description: "Stop a WebIDE terminal and kill its process tree. Soft-terminates (status EXITED, record kept).",
|
|
7682
|
+
inputSchema: {
|
|
7683
|
+
type: "object",
|
|
7684
|
+
properties: {
|
|
7685
|
+
key: { type: "string", description: "Terminal key" },
|
|
7686
|
+
terminalId: { type: "string", description: "Server terminal id" }
|
|
7687
|
+
}
|
|
7688
|
+
},
|
|
7689
|
+
execute: async (args) => {
|
|
7690
|
+
const key = asString3(args.key) || void 0;
|
|
7691
|
+
const terminalId = asString3(args.terminalId) || void 0;
|
|
7692
|
+
if (!key && !terminalId) {
|
|
7693
|
+
return {
|
|
7694
|
+
content: [{ type: "text", text: "\u9700\u8981 key \u6216 terminalId" }],
|
|
7695
|
+
isError: true
|
|
7696
|
+
};
|
|
7697
|
+
}
|
|
7698
|
+
try {
|
|
7699
|
+
const result = rpc ? await rpc.stop({ taskId, key, terminalId }) : await (async () => {
|
|
7700
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
7701
|
+
})();
|
|
7702
|
+
return JSON.stringify(result, null, 2);
|
|
7703
|
+
} catch (err) {
|
|
7704
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7705
|
+
return {
|
|
7706
|
+
content: [{ type: "text", text: `\u505C\u6B62\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
7707
|
+
isError: true
|
|
7708
|
+
};
|
|
7709
|
+
}
|
|
7710
|
+
}
|
|
7711
|
+
};
|
|
7712
|
+
const StopAllWebIdeTerminals = {
|
|
7713
|
+
description: "Stop all living WebIDE terminals for the current task (kill process trees). Use before re-running local dev to avoid port conflicts. Soft-terminates records.",
|
|
7714
|
+
inputSchema: {
|
|
7715
|
+
type: "object",
|
|
7716
|
+
properties: {}
|
|
7717
|
+
},
|
|
7718
|
+
execute: async () => {
|
|
7719
|
+
try {
|
|
7720
|
+
const result = rpc ? await rpc.stopAll({ taskId }) : await (async () => {
|
|
7721
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
7722
|
+
})();
|
|
7723
|
+
return JSON.stringify(result, null, 2);
|
|
7724
|
+
} catch (err) {
|
|
7725
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7726
|
+
return {
|
|
7727
|
+
content: [{ type: "text", text: `\u5168\u90E8\u505C\u6B62\u5931\u8D25: ${detail}` }],
|
|
7728
|
+
isError: true
|
|
7729
|
+
};
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
};
|
|
7733
|
+
const ListWebIdeTerminals = {
|
|
7734
|
+
description: "List living WebIDE terminals for the current task (STARTING/RUNNING/READY/STOPPING only). Does NOT include EXITED/FAILED. Returns id, key, status, pid, ports.",
|
|
7735
|
+
inputSchema: {
|
|
7736
|
+
type: "object",
|
|
7737
|
+
properties: {}
|
|
7738
|
+
},
|
|
7739
|
+
execute: async () => {
|
|
7740
|
+
try {
|
|
7741
|
+
if (rpc) {
|
|
7742
|
+
const result2 = await rpc.list({ taskId });
|
|
7743
|
+
return JSON.stringify(result2, null, 2);
|
|
7744
|
+
}
|
|
7745
|
+
const api = createApmApiClient(cfg);
|
|
7746
|
+
const result = await api.cli.webideListTerminals({ taskId });
|
|
7747
|
+
return JSON.stringify(result, null, 2);
|
|
7748
|
+
} catch (err) {
|
|
7749
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7750
|
+
return {
|
|
7751
|
+
content: [{ type: "text", text: `\u5217\u51FA\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
7752
|
+
isError: true
|
|
7753
|
+
};
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7756
|
+
};
|
|
7757
|
+
return {
|
|
7758
|
+
StartWebIdeTerminal,
|
|
7759
|
+
StopWebIdeTerminal,
|
|
7760
|
+
StopAllWebIdeTerminals,
|
|
7761
|
+
ListWebIdeTerminals
|
|
7762
|
+
};
|
|
7763
|
+
}
|
|
7764
|
+
|
|
6947
7765
|
// src/commands/connect/cursor-custom-tools.ts
|
|
6948
7766
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
6949
7767
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
@@ -6977,6 +7795,21 @@ function createCursorCustomTools(cfg, messageId, options) {
|
|
|
6977
7795
|
cfg,
|
|
6978
7796
|
taskId: options.taskId
|
|
6979
7797
|
});
|
|
7798
|
+
tools.MergeWebIdePullRequests = createMergeWebIdePullRequestsTool({
|
|
7799
|
+
cfg,
|
|
7800
|
+
taskId: options.taskId
|
|
7801
|
+
});
|
|
7802
|
+
if (options.workdir) {
|
|
7803
|
+
Object.assign(
|
|
7804
|
+
tools,
|
|
7805
|
+
createWebIdeTerminalTools({
|
|
7806
|
+
cfg,
|
|
7807
|
+
taskId: options.taskId,
|
|
7808
|
+
workdir: options.workdir,
|
|
7809
|
+
rpc: options.terminalRpc
|
|
7810
|
+
})
|
|
7811
|
+
);
|
|
7812
|
+
}
|
|
6980
7813
|
}
|
|
6981
7814
|
return tools;
|
|
6982
7815
|
}
|
|
@@ -7100,7 +7933,9 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
7100
7933
|
appendMessageContent: options?.appendMessageContent,
|
|
7101
7934
|
askQuestionExecute: options?.askQuestionExecute,
|
|
7102
7935
|
enableWebIdePlanTools: options?.enableWebIdePlanTools,
|
|
7103
|
-
taskId: options?.taskId
|
|
7936
|
+
taskId: options?.taskId,
|
|
7937
|
+
workdir,
|
|
7938
|
+
terminalRpc: options?.terminalRpc
|
|
7104
7939
|
});
|
|
7105
7940
|
const enableSandbox = Boolean(options?.enableSandbox);
|
|
7106
7941
|
let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
|
|
@@ -7242,6 +8077,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
7242
8077
|
}
|
|
7243
8078
|
|
|
7244
8079
|
// src/commands/connect/ensure-message-reply.ts
|
|
8080
|
+
init_client();
|
|
7245
8081
|
var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
|
|
7246
8082
|
function resolveMessageReplyFallback(fallback) {
|
|
7247
8083
|
for (const candidate of [
|
|
@@ -7385,6 +8221,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
|
|
|
7385
8221
|
}
|
|
7386
8222
|
|
|
7387
8223
|
// src/commands/connect/webide-worker-pool.ts
|
|
8224
|
+
init_webide_terminal_registry();
|
|
7388
8225
|
import { Worker } from "node:worker_threads";
|
|
7389
8226
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7390
8227
|
import { dirname as dirname7, join as join20 } from "node:path";
|
|
@@ -7396,23 +8233,78 @@ function spawnWebIdeMessageWorker(cfg, msg) {
|
|
|
7396
8233
|
const worker = new Worker(workerFile, {
|
|
7397
8234
|
workerData: { cfg, msg }
|
|
7398
8235
|
});
|
|
8236
|
+
const registry = getWebIdeTerminalRegistry(cfg);
|
|
7399
8237
|
let resolveDone;
|
|
7400
8238
|
const done = new Promise((resolve7) => {
|
|
7401
8239
|
resolveDone = resolve7;
|
|
7402
8240
|
});
|
|
7403
|
-
worker.on(
|
|
7404
|
-
"
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
8241
|
+
worker.on("message", (m) => {
|
|
8242
|
+
if (m?.type === "terminal-rpc") {
|
|
8243
|
+
const rpc = m;
|
|
8244
|
+
void (async () => {
|
|
8245
|
+
try {
|
|
8246
|
+
let result;
|
|
8247
|
+
if (rpc.op === "start") {
|
|
8248
|
+
result = await registry.start({
|
|
8249
|
+
taskId: String(rpc.args.taskId ?? msg.taskId),
|
|
8250
|
+
key: String(rpc.args.key ?? ""),
|
|
8251
|
+
name: typeof rpc.args.name === "string" ? rpc.args.name : void 0,
|
|
8252
|
+
cwd: String(rpc.args.cwd ?? msg.workdir),
|
|
8253
|
+
command: String(rpc.args.command ?? ""),
|
|
8254
|
+
readyPattern: typeof rpc.args.readyPattern === "string" ? rpc.args.readyPattern : void 0,
|
|
8255
|
+
portsHint: Array.isArray(rpc.args.portsHint) ? rpc.args.portsHint : void 0
|
|
8256
|
+
});
|
|
8257
|
+
} else if (rpc.op === "stop") {
|
|
8258
|
+
const terminalId = typeof rpc.args.terminalId === "string" ? rpc.args.terminalId : void 0;
|
|
8259
|
+
const key = typeof rpc.args.key === "string" ? rpc.args.key : void 0;
|
|
8260
|
+
const taskId = String(rpc.args.taskId ?? msg.taskId);
|
|
8261
|
+
let id = terminalId;
|
|
8262
|
+
if (!id && key) {
|
|
8263
|
+
const local = registry.listLocal(taskId).find((t) => t.key === key);
|
|
8264
|
+
id = local?.id;
|
|
8265
|
+
}
|
|
8266
|
+
if (!id) {
|
|
8267
|
+
throw new Error("\u672A\u627E\u5230\u7EC8\u7AEF");
|
|
8268
|
+
}
|
|
8269
|
+
result = await registry.stop(id, { reason: "ai-stop" });
|
|
8270
|
+
} else if (rpc.op === "stopAll") {
|
|
8271
|
+
result = await registry.stopAllForTask(
|
|
8272
|
+
String(rpc.args.taskId ?? msg.taskId),
|
|
8273
|
+
"ai-stop-all"
|
|
8274
|
+
);
|
|
8275
|
+
} else {
|
|
8276
|
+
result = {
|
|
8277
|
+
terminals: registry.listLocal(
|
|
8278
|
+
String(rpc.args.taskId ?? msg.taskId)
|
|
8279
|
+
)
|
|
8280
|
+
};
|
|
8281
|
+
}
|
|
8282
|
+
worker.postMessage({
|
|
8283
|
+
type: "terminal-rpc-result",
|
|
8284
|
+
id: rpc.id,
|
|
8285
|
+
ok: true,
|
|
8286
|
+
result
|
|
8287
|
+
});
|
|
8288
|
+
} catch (err) {
|
|
8289
|
+
worker.postMessage({
|
|
8290
|
+
type: "terminal-rpc-result",
|
|
8291
|
+
id: rpc.id,
|
|
8292
|
+
ok: false,
|
|
8293
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8294
|
+
});
|
|
7411
8295
|
}
|
|
7412
|
-
|
|
8296
|
+
})();
|
|
8297
|
+
return;
|
|
8298
|
+
}
|
|
8299
|
+
if (m?.type === "done" || m?.type === "error") {
|
|
8300
|
+
if (m.type === "error") {
|
|
8301
|
+
console.error(
|
|
8302
|
+
`[apm] webide worker error messageId=${m.messageId}: ${m.error}`
|
|
8303
|
+
);
|
|
7413
8304
|
}
|
|
8305
|
+
void worker.terminate().finally(() => resolveDone());
|
|
7414
8306
|
}
|
|
7415
|
-
);
|
|
8307
|
+
});
|
|
7416
8308
|
worker.on("error", (err) => {
|
|
7417
8309
|
console.error("[apm] webide worker crashed:", err);
|
|
7418
8310
|
resolveDone();
|
|
@@ -7689,6 +8581,11 @@ function attachWsHandlers(ws, ctx, onOpen) {
|
|
|
7689
8581
|
const msg2 = validated.data;
|
|
7690
8582
|
const task2 = (async () => {
|
|
7691
8583
|
const workdir = requireRemoteWorkdir(msg2.workdir);
|
|
8584
|
+
const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
|
|
8585
|
+
await getWebIdeTerminalRegistry2(cfg).stopAllForTask(
|
|
8586
|
+
msg2.taskId,
|
|
8587
|
+
"cache-cleanup"
|
|
8588
|
+
);
|
|
7692
8589
|
cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
|
|
7693
8590
|
})();
|
|
7694
8591
|
activeTasks.add(task2);
|
|
@@ -7697,6 +8594,21 @@ function attachWsHandlers(ws, ctx, onOpen) {
|
|
|
7697
8594
|
});
|
|
7698
8595
|
return;
|
|
7699
8596
|
}
|
|
8597
|
+
if (validated.data.type === "webide-terminal-stop") {
|
|
8598
|
+
const msg2 = validated.data;
|
|
8599
|
+
const task2 = (async () => {
|
|
8600
|
+
const { getWebIdeTerminalRegistry: getWebIdeTerminalRegistry2 } = await Promise.resolve().then(() => (init_webide_terminal_registry(), webide_terminal_registry_exports));
|
|
8601
|
+
await getWebIdeTerminalRegistry2(cfg).stop(msg2.terminalId, {
|
|
8602
|
+
pid: msg2.pid,
|
|
8603
|
+
reason: "web-stop"
|
|
8604
|
+
});
|
|
8605
|
+
})();
|
|
8606
|
+
activeTasks.add(task2);
|
|
8607
|
+
void task2.finally(() => {
|
|
8608
|
+
activeTasks.delete(task2);
|
|
8609
|
+
});
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
7700
8612
|
if (validated.data.type === "webide-message") {
|
|
7701
8613
|
const msg2 = validated.data;
|
|
7702
8614
|
const perMessageController2 = new AbortController();
|
|
@@ -7961,6 +8873,10 @@ async function runConnect(options) {
|
|
|
7961
8873
|
}
|
|
7962
8874
|
}
|
|
7963
8875
|
|
|
8876
|
+
// src/commands/create-pr.ts
|
|
8877
|
+
init_client();
|
|
8878
|
+
init_config();
|
|
8879
|
+
|
|
7964
8880
|
// src/commands/create-pr-errors.ts
|
|
7965
8881
|
import { ApiError as ApiError2 } from "listpage-http";
|
|
7966
8882
|
function extractMessage(error) {
|
|
@@ -8088,6 +9004,8 @@ async function runCreatePr(options) {
|
|
|
8088
9004
|
}
|
|
8089
9005
|
|
|
8090
9006
|
// src/commands/deploy/deploy-tracking.ts
|
|
9007
|
+
init_client();
|
|
9008
|
+
init_config();
|
|
8091
9009
|
function normalizeDeployEnvironment(env) {
|
|
8092
9010
|
const normalized = env.trim().toLowerCase();
|
|
8093
9011
|
if (normalized === "test") return "TEST";
|
|
@@ -8757,6 +9675,7 @@ function registerDeployBackendCommands(program) {
|
|
|
8757
9675
|
// src/commands/deploy/frontend.ts
|
|
8758
9676
|
import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
|
|
8759
9677
|
import path17 from "node:path";
|
|
9678
|
+
init_minio();
|
|
8760
9679
|
function artifactObjectKey(namePrefix, branchSegment, relativePath) {
|
|
8761
9680
|
const base = sanitizeRelativePath(namePrefix);
|
|
8762
9681
|
const rel = sanitizeRelativePath(relativePath);
|
|
@@ -8934,6 +9853,7 @@ function registerDeployFrontendCommands(program) {
|
|
|
8934
9853
|
|
|
8935
9854
|
// src/commands/deploy/sftp.ts
|
|
8936
9855
|
import path18 from "node:path";
|
|
9856
|
+
init_minio();
|
|
8937
9857
|
function registerDeploySftpCommands(program) {
|
|
8938
9858
|
program.command("deploy-sftp").description(
|
|
8939
9859
|
"\u5C06\u6307\u5B9A\u76EE\u5F55\u538B\u7F29\u4E3A zip \u540E\u901A\u8FC7 SFTP \u4E0A\u4F20\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u8FDE\u63A5\u4FE1\u606F\u89C1 apm.config.json wisdomDeploy\uFF09"
|