aws-runtime-bridge 1.4.0 → 1.5.0
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/adapter/OpencodeSdkAdapter.d.ts +13 -1
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +56 -4
- package/dist/adapter/OpencodeSdkAdapter.test.js +57 -1
- package/dist/routes/file-browser.d.ts +10 -0
- package/dist/routes/file-browser.d.ts.map +1 -1
- package/dist/routes/file-browser.js +206 -4
- package/dist/routes/file-browser.test.js +22 -0
- package/dist/services/session-output.d.ts +12 -0
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +15 -0
- package/dist/services/workspace-files.d.ts +72 -0
- package/dist/services/workspace-files.d.ts.map +1 -1
- package/dist/services/workspace-files.js +519 -21
- package/dist/services/workspace-files.test.js +387 -11
- package/dist/services/workspace-watch.d.ts +21 -0
- package/dist/services/workspace-watch.d.ts.map +1 -0
- package/dist/services/workspace-watch.js +123 -0
- package/dist/services/workspace-watch.test.d.ts +2 -0
- package/dist/services/workspace-watch.test.d.ts.map +1 -0
- package/dist/services/workspace-watch.test.js +38 -0
- package/package.json +8 -1
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 参考:spectrai-community/src/main/adapter/OpenCodeSdkAdapter.ts
|
|
12
12
|
*/
|
|
13
13
|
import { EventEmitter } from 'node:events';
|
|
14
|
-
import type {
|
|
14
|
+
import type { AdapterSessionConfig, BaseProviderAdapter, ConversationMessage, SessionStatus } from './types.js';
|
|
15
15
|
export declare class OpencodeSdkAdapter extends EventEmitter implements BaseProviderAdapter {
|
|
16
16
|
readonly providerId = "opencode";
|
|
17
17
|
readonly displayName = "OpenCode";
|
|
@@ -39,6 +39,18 @@ export declare class OpencodeSdkAdapter extends EventEmitter implements BaseProv
|
|
|
39
39
|
*/
|
|
40
40
|
private loadSdk;
|
|
41
41
|
private waitForServer;
|
|
42
|
+
/**
|
|
43
|
+
* 记录 OpenCode serve 启动阶段输出,用于进程提前退出时返回可诊断错误。
|
|
44
|
+
*/
|
|
45
|
+
private recordStartupOutput;
|
|
46
|
+
/**
|
|
47
|
+
* 将 OpenCode serve 提前退出信息转成前端可直接展示的错误文本。
|
|
48
|
+
*/
|
|
49
|
+
private formatStartupExitMessage;
|
|
50
|
+
/**
|
|
51
|
+
* 异步投递启动提示,避免首轮 OpenCode 处理耗时导致 /runtime/start 被前端误判为启动失败。
|
|
52
|
+
*/
|
|
53
|
+
private dispatchInitialPrompt;
|
|
42
54
|
private startSseLoop;
|
|
43
55
|
private runSseLoop;
|
|
44
56
|
private handleOpenCodeEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAc3C,OAAO,KAAK,EACV,oBAAoB,EAEpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EACd,MAAM,YAAY,CAAC;AA4GpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,SAAS,CAAC,CAAoB;IAEhC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+I5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,OAAO;YA6BP,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAahC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
|
|
@@ -132,11 +132,16 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
132
132
|
shell: useShell,
|
|
133
133
|
});
|
|
134
134
|
session.serverProcess = proc;
|
|
135
|
+
session.startupOutput = [];
|
|
135
136
|
proc.stdout?.on('data', (d) => {
|
|
136
|
-
|
|
137
|
+
const text = d.toString();
|
|
138
|
+
this.recordStartupOutput(session, text);
|
|
139
|
+
console.debug(`[OpencodeSdkAdapter] serve stdout: ${text.slice(0, 200)}`);
|
|
137
140
|
});
|
|
138
141
|
proc.stderr?.on('data', (d) => {
|
|
139
|
-
|
|
142
|
+
const text = d.toString();
|
|
143
|
+
this.recordStartupOutput(session, text);
|
|
144
|
+
console.debug(`[OpencodeSdkAdapter] serve stderr: ${text.slice(0, 200)}`);
|
|
140
145
|
});
|
|
141
146
|
proc.on('error', (err) => {
|
|
142
147
|
console.error(`[OpencodeSdkAdapter] Server process error:`, err);
|
|
@@ -155,6 +160,9 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
155
160
|
console.log(`[OpencodeSdkAdapter] Server exited with code ${code}`);
|
|
156
161
|
session.sseActive = false;
|
|
157
162
|
});
|
|
163
|
+
const exitDuringStartup = new Promise((resolve) => {
|
|
164
|
+
proc.once('exit', (code, signal) => resolve({ code, signal }));
|
|
165
|
+
});
|
|
158
166
|
// 步骤 3:创建 SDK 客户端
|
|
159
167
|
const sdk = await this.loadSdk();
|
|
160
168
|
const client = sdk.createOpencodeClient({
|
|
@@ -162,7 +170,12 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
162
170
|
});
|
|
163
171
|
session.client = client;
|
|
164
172
|
// 步骤 4:等待服务器就绪
|
|
165
|
-
await
|
|
173
|
+
await Promise.race([
|
|
174
|
+
this.waitForServer(client, config.workingDirectory, 10_000, port),
|
|
175
|
+
exitDuringStartup.then((exitInfo) => {
|
|
176
|
+
throw new Error(this.formatStartupExitMessage(exitInfo, session.startupOutput));
|
|
177
|
+
}),
|
|
178
|
+
]);
|
|
166
179
|
console.log(`[OpencodeSdkAdapter] ★★★ OpenCode server ready ★★★ port=${port}`);
|
|
167
180
|
// 步骤 5:创建 OpenCode 会话
|
|
168
181
|
const createResult = await client.session.create({
|
|
@@ -182,7 +195,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
182
195
|
this.emit('status-change', sessionId, 'waiting_input');
|
|
183
196
|
// 步骤 7:发送初始 Prompt
|
|
184
197
|
if (config.initialPrompt) {
|
|
185
|
-
|
|
198
|
+
this.dispatchInitialPrompt(sessionId, config.initialPrompt);
|
|
186
199
|
}
|
|
187
200
|
}
|
|
188
201
|
catch (err) {
|
|
@@ -264,6 +277,9 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
264
277
|
const session = this.sessions.get(sessionId);
|
|
265
278
|
if (session) {
|
|
266
279
|
session.idleCommands = commands;
|
|
280
|
+
if (session.adapterSession.status === 'waiting_input' && (commands.idleInputCommand || commands.nonInputCommand)) {
|
|
281
|
+
this.startIdleDetection(sessionId);
|
|
282
|
+
}
|
|
267
283
|
}
|
|
268
284
|
}
|
|
269
285
|
async abortCurrentTurn(sessionId) {
|
|
@@ -382,6 +398,42 @@ export class OpencodeSdkAdapter extends EventEmitter {
|
|
|
382
398
|
}
|
|
383
399
|
throw new Error(`OpenCode server (port ${port}) did not start within ${timeoutMs}ms`);
|
|
384
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* 记录 OpenCode serve 启动阶段输出,用于进程提前退出时返回可诊断错误。
|
|
403
|
+
*/
|
|
404
|
+
recordStartupOutput(session, text) {
|
|
405
|
+
const normalized = text.trim();
|
|
406
|
+
if (!normalized)
|
|
407
|
+
return;
|
|
408
|
+
session.startupOutput = [...(session.startupOutput ?? []), normalized].slice(-8);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 将 OpenCode serve 提前退出信息转成前端可直接展示的错误文本。
|
|
412
|
+
*/
|
|
413
|
+
formatStartupExitMessage(exitInfo, startupOutput) {
|
|
414
|
+
const exitReason = exitInfo.signal
|
|
415
|
+
? `signal ${exitInfo.signal}`
|
|
416
|
+
: `code ${exitInfo.code ?? 'unknown'}`;
|
|
417
|
+
const output = (startupOutput ?? []).join('\n').trim();
|
|
418
|
+
if (!output) {
|
|
419
|
+
return `OpenCode server exited during startup (${exitReason}). Please check whether opencode serve can run in this workspace.`;
|
|
420
|
+
}
|
|
421
|
+
return `OpenCode server exited during startup (${exitReason}): ${output}`;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 异步投递启动提示,避免首轮 OpenCode 处理耗时导致 /runtime/start 被前端误判为启动失败。
|
|
425
|
+
*/
|
|
426
|
+
dispatchInitialPrompt(sessionId, initialPrompt) {
|
|
427
|
+
void this.sendMessage(sessionId, initialPrompt).catch((err) => {
|
|
428
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
429
|
+
this.emitEvent({
|
|
430
|
+
type: 'error',
|
|
431
|
+
sessionId,
|
|
432
|
+
timestamp: new Date().toISOString(),
|
|
433
|
+
data: { text: `OpenCode initial prompt failed: ${msg}` },
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
385
437
|
startSseLoop(sessionId, session) {
|
|
386
438
|
session.sseActive = true;
|
|
387
439
|
const ac = new AbortController();
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { OpencodeSdkAdapter } from './OpencodeSdkAdapter.js';
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
vi.useRealTimers();
|
|
5
|
+
vi.restoreAllMocks();
|
|
6
|
+
});
|
|
3
7
|
describe('OpencodeSdkAdapter', () => {
|
|
4
8
|
it('should expose opencode provider identity', () => {
|
|
5
9
|
const adapter = new OpencodeSdkAdapter();
|
|
@@ -16,4 +20,56 @@ describe('OpencodeSdkAdapter', () => {
|
|
|
16
20
|
const prompt = Reflect.get(adapter, 'toIdlePrompt').call(adapter, 'system: 醒来了吗?使用get_profile获取自己的信息,继续未完成的任务或使用poll_message阻塞获取消息');
|
|
17
21
|
expect(prompt).toBe('请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。');
|
|
18
22
|
});
|
|
23
|
+
it('starts idle detection when commands are set after OpenCode is already waiting for input', async () => {
|
|
24
|
+
vi.useFakeTimers();
|
|
25
|
+
const adapter = new OpencodeSdkAdapter();
|
|
26
|
+
const sentMessages = [];
|
|
27
|
+
Reflect.set(adapter, 'sessions', new Map([
|
|
28
|
+
[
|
|
29
|
+
'session-1',
|
|
30
|
+
{
|
|
31
|
+
adapterSession: {
|
|
32
|
+
sessionId: 'session-1',
|
|
33
|
+
status: 'waiting_input',
|
|
34
|
+
messages: [],
|
|
35
|
+
createdAt: new Date().toISOString(),
|
|
36
|
+
totalUsage: { inputTokens: 0, outputTokens: 0 },
|
|
37
|
+
},
|
|
38
|
+
config: {
|
|
39
|
+
command: 'opencode',
|
|
40
|
+
workingDirectory: process.cwd(),
|
|
41
|
+
autoAccept: true,
|
|
42
|
+
},
|
|
43
|
+
sseActive: false,
|
|
44
|
+
pendingPermissions: new Map(),
|
|
45
|
+
emittedToolStarts: new Set(),
|
|
46
|
+
workingDirectory: process.cwd(),
|
|
47
|
+
userMessageIds: new Set(),
|
|
48
|
+
currentAssistantText: '',
|
|
49
|
+
seenTextPartIds: new Set(),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
]));
|
|
53
|
+
vi.spyOn(adapter, 'sendMessage').mockImplementation(async (_sessionId, message) => {
|
|
54
|
+
sentMessages.push(message);
|
|
55
|
+
});
|
|
56
|
+
adapter.setIdleCommands('session-1', {
|
|
57
|
+
idleInputCommand: 'system: 使用poll_message阻塞获取消息',
|
|
58
|
+
nonInputCommand: '',
|
|
59
|
+
});
|
|
60
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
61
|
+
expect(sentMessages).toEqual(['请调用 poll_message 工具阻塞等待新消息;收到消息后再处理消息内容。']);
|
|
62
|
+
});
|
|
63
|
+
it('includes serve startup output when the OpenCode process exits early', () => {
|
|
64
|
+
const adapter = new OpencodeSdkAdapter();
|
|
65
|
+
const message = Reflect.get(adapter, 'formatStartupExitMessage').call(adapter, { code: 1, signal: null }, ['missing auth configuration']);
|
|
66
|
+
expect(message).toContain('OpenCode server exited during startup (code 1)');
|
|
67
|
+
expect(message).toContain('missing auth configuration');
|
|
68
|
+
});
|
|
69
|
+
it('dispatches the initial prompt asynchronously so launch can return after session creation', () => {
|
|
70
|
+
const adapter = new OpencodeSdkAdapter();
|
|
71
|
+
const sendMessage = vi.spyOn(adapter, 'sendMessage').mockResolvedValue(undefined);
|
|
72
|
+
Reflect.get(adapter, 'dispatchInitialPrompt').call(adapter, 'session-1', 'hello');
|
|
73
|
+
expect(sendMessage).toHaveBeenCalledWith('session-1', 'hello');
|
|
74
|
+
});
|
|
19
75
|
});
|
|
@@ -3,5 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 提供文件系统浏览功能
|
|
5
5
|
*/
|
|
6
|
+
import multer from 'multer';
|
|
6
7
|
export declare const fileBrowserRouter: import("express-serve-static-core").Router;
|
|
8
|
+
export declare const WORKSPACE_UPLOAD_FILE_LIMIT = 2000;
|
|
9
|
+
export declare function createWorkspaceUploadLimitResponse(error: multer.MulterError): {
|
|
10
|
+
status: number;
|
|
11
|
+
body: {
|
|
12
|
+
error: string;
|
|
13
|
+
code: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function parseWorkspaceUploadRelativePaths(body: Record<string, unknown>): string[];
|
|
7
17
|
//# sourceMappingURL=file-browser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-browser.d.ts","sourceRoot":"","sources":["../../src/routes/file-browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"file-browser.d.ts","sourceRoot":"","sources":["../../src/routes/file-browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAsB5B,eAAO,MAAM,iBAAiB,4CAAW,CAAC;AAE1C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAqChD,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAUvI;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAgBzF"}
|
|
@@ -3,15 +3,79 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 提供文件系统浏览功能
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { createReadStream, promises as fs } from 'node:fs';
|
|
7
8
|
import path from 'node:path';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
9
|
+
import { Router } from 'express';
|
|
10
|
+
import multer from 'multer';
|
|
10
11
|
import { allowHostFileBrowser } from '../config.js';
|
|
12
|
+
import { validateToken } from '../middleware/auth.js';
|
|
11
13
|
import { createLogger } from '../utils/logger.js';
|
|
12
|
-
import { createWorkspaceEntry, deleteWorkspaceEntry, listWorkspaceDirectory, readWorkspaceFile, renameWorkspaceEntry, writeWorkspaceFile, } from '../services/workspace-files.js';
|
|
14
|
+
import { createWorkspaceEntry, deleteWorkspaceEntry, extractWorkspaceArchive, listWorkspaceDirectory, previewWorkspaceDocument, readWorkspaceFile, renameWorkspaceEntry, resolveWorkspaceDownloadTarget, streamWorkspaceDirectoryZip, uploadWorkspaceFiles, writeWorkspaceFile, } from '../services/workspace-files.js';
|
|
15
|
+
import { unwatchWorkspaceFile, watchWorkspaceFile } from '../services/workspace-watch.js';
|
|
13
16
|
const log = createLogger('file-browser');
|
|
14
17
|
export const fileBrowserRouter = Router();
|
|
18
|
+
export const WORKSPACE_UPLOAD_FILE_LIMIT = 2000;
|
|
19
|
+
const upload = multer({
|
|
20
|
+
dest: path.join(os.tmpdir(), 'agentswork-runtime-bridge-uploads'),
|
|
21
|
+
limits: {
|
|
22
|
+
files: WORKSPACE_UPLOAD_FILE_LIMIT,
|
|
23
|
+
fileSize: 512 * 1024 * 1024
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
function toBoolean(value) {
|
|
27
|
+
return value === true || value === 'true' || value === '1';
|
|
28
|
+
}
|
|
29
|
+
function contentDispositionFileName(fileName) {
|
|
30
|
+
const fallbackName = fileName
|
|
31
|
+
.replace(/[\\"\r\n]/g, '_')
|
|
32
|
+
.replace(/[^\x20-\x7E]/g, '_');
|
|
33
|
+
return `attachment; filename="${fallbackName}"; filename*=UTF-8''${encodeURIComponent(fileName)}`;
|
|
34
|
+
}
|
|
35
|
+
async function cleanupUploadedFiles(files = []) {
|
|
36
|
+
await Promise.all(files.map((file) => fs.rm(file.path, { force: true }).catch(() => undefined)));
|
|
37
|
+
}
|
|
38
|
+
export function createWorkspaceUploadLimitResponse(error) {
|
|
39
|
+
return {
|
|
40
|
+
status: 400,
|
|
41
|
+
body: {
|
|
42
|
+
error: error.code === 'LIMIT_FILE_COUNT'
|
|
43
|
+
? `一次最多上传 ${WORKSPACE_UPLOAD_FILE_LIMIT} 个文件,请拆分文件夹后重试。`
|
|
44
|
+
: error.message,
|
|
45
|
+
code: error.code
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function parseWorkspaceUploadRelativePaths(body) {
|
|
50
|
+
const relativePathsJson = body.relativePathsJson;
|
|
51
|
+
if (typeof relativePathsJson === 'string' && relativePathsJson.trim()) {
|
|
52
|
+
const parsed = JSON.parse(relativePathsJson);
|
|
53
|
+
if (!Array.isArray(parsed)) {
|
|
54
|
+
throw new Error('relativePathsJson must be an array');
|
|
55
|
+
}
|
|
56
|
+
return parsed.map((relativePath) => String(relativePath || ''));
|
|
57
|
+
}
|
|
58
|
+
const relativePaths = body.relativePaths;
|
|
59
|
+
return Array.isArray(relativePaths)
|
|
60
|
+
? relativePaths.map((relativePath) => String(relativePath || ''))
|
|
61
|
+
: typeof relativePaths === 'string'
|
|
62
|
+
? [relativePaths]
|
|
63
|
+
: [];
|
|
64
|
+
}
|
|
65
|
+
function workspaceUploadMiddleware(req, res, next) {
|
|
66
|
+
upload.array('files')(req, res, (error) => {
|
|
67
|
+
if (!error) {
|
|
68
|
+
next();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (error instanceof multer.MulterError) {
|
|
72
|
+
const response = createWorkspaceUploadLimitResponse(error);
|
|
73
|
+
res.status(response.status).json(response.body);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
next(error);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
15
79
|
async function listHostDirectories(requestPath = '') {
|
|
16
80
|
const platform = process.platform;
|
|
17
81
|
const isWindows = platform === 'win32';
|
|
@@ -155,6 +219,61 @@ fileBrowserRouter.post('/workspace/read', validateToken, async (req, res) => {
|
|
|
155
219
|
res.status(400).json({ error: err.message });
|
|
156
220
|
}
|
|
157
221
|
});
|
|
222
|
+
/**
|
|
223
|
+
* 预览工作区文档内容。
|
|
224
|
+
* POST /api/file-browser/workspace/preview
|
|
225
|
+
*/
|
|
226
|
+
fileBrowserRouter.post('/workspace/preview', validateToken, async (req, res) => {
|
|
227
|
+
try {
|
|
228
|
+
const { workspacePath, filePath } = req.body || {};
|
|
229
|
+
const result = await previewWorkspaceDocument({
|
|
230
|
+
workspacePath: String(workspacePath || ''),
|
|
231
|
+
filePath: String(filePath || '')
|
|
232
|
+
});
|
|
233
|
+
res.json(result);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
const err = error;
|
|
237
|
+
log.error('failed to preview workspace document:', err);
|
|
238
|
+
res.status(400).json({ error: err.message });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
/**
|
|
242
|
+
* 监听工作区文件变化,用于文档预览自动刷新。
|
|
243
|
+
* POST /api/file-browser/workspace/watch
|
|
244
|
+
*/
|
|
245
|
+
fileBrowserRouter.post('/workspace/watch', validateToken, async (req, res) => {
|
|
246
|
+
try {
|
|
247
|
+
const { workspacePath, filePath } = req.body || {};
|
|
248
|
+
res.json(await watchWorkspaceFile({
|
|
249
|
+
workspacePath: String(workspacePath || ''),
|
|
250
|
+
filePath: String(filePath || '')
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
const err = error;
|
|
255
|
+
log.error('failed to watch workspace file:', err);
|
|
256
|
+
res.status(400).json({ error: err.message });
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
/**
|
|
260
|
+
* 取消监听工作区文件变化。
|
|
261
|
+
* POST /api/file-browser/workspace/unwatch
|
|
262
|
+
*/
|
|
263
|
+
fileBrowserRouter.post('/workspace/unwatch', validateToken, async (req, res) => {
|
|
264
|
+
try {
|
|
265
|
+
const { workspacePath, filePath } = req.body || {};
|
|
266
|
+
res.json(await unwatchWorkspaceFile({
|
|
267
|
+
workspacePath: String(workspacePath || ''),
|
|
268
|
+
filePath: String(filePath || '')
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
const err = error;
|
|
273
|
+
log.error('failed to unwatch workspace file:', err);
|
|
274
|
+
res.status(400).json({ error: err.message });
|
|
275
|
+
}
|
|
276
|
+
});
|
|
158
277
|
/**
|
|
159
278
|
* 保存工作区文件内容
|
|
160
279
|
* POST /api/file-browser/workspace/write
|
|
@@ -235,3 +354,86 @@ fileBrowserRouter.post('/workspace/delete', validateToken, async (req, res) => {
|
|
|
235
354
|
res.status(400).json({ error: err.message });
|
|
236
355
|
}
|
|
237
356
|
});
|
|
357
|
+
/**
|
|
358
|
+
* 上传文件到工作区指定目录,可选自动解压常见压缩包。
|
|
359
|
+
* POST /api/file-browser/workspace/upload
|
|
360
|
+
*/
|
|
361
|
+
fileBrowserRouter.post('/workspace/upload', validateToken, workspaceUploadMiddleware, async (req, res) => {
|
|
362
|
+
const files = (req.files || []);
|
|
363
|
+
try {
|
|
364
|
+
const { workspacePath, path: requestPath = '', extractArchives } = req.body || {};
|
|
365
|
+
const relativePaths = parseWorkspaceUploadRelativePaths(req.body || {});
|
|
366
|
+
const result = await uploadWorkspaceFiles({
|
|
367
|
+
workspacePath: String(workspacePath || ''),
|
|
368
|
+
targetPath: String(requestPath || ''),
|
|
369
|
+
extractArchives: toBoolean(extractArchives),
|
|
370
|
+
files: files.map((file, index) => ({
|
|
371
|
+
originalname: file.originalname,
|
|
372
|
+
relativePath: relativePaths[index],
|
|
373
|
+
path: file.path,
|
|
374
|
+
size: file.size
|
|
375
|
+
}))
|
|
376
|
+
});
|
|
377
|
+
res.json(result);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
const err = error;
|
|
381
|
+
log.error('failed to upload workspace files:', err);
|
|
382
|
+
res.status(400).json({ error: err.message });
|
|
383
|
+
}
|
|
384
|
+
finally {
|
|
385
|
+
await cleanupUploadedFiles(files);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
/**
|
|
389
|
+
* 下载工作区文件或目录;目录会实时打包为 zip。
|
|
390
|
+
* POST /api/file-browser/workspace/download
|
|
391
|
+
*/
|
|
392
|
+
fileBrowserRouter.post('/workspace/download', validateToken, async (req, res) => {
|
|
393
|
+
try {
|
|
394
|
+
const { workspacePath, targetPath } = req.body || {};
|
|
395
|
+
const target = await resolveWorkspaceDownloadTarget({
|
|
396
|
+
workspacePath: String(workspacePath || ''),
|
|
397
|
+
targetPath: String(targetPath || '')
|
|
398
|
+
});
|
|
399
|
+
res.setHeader('Content-Type', target.contentType);
|
|
400
|
+
res.setHeader('Content-Disposition', contentDispositionFileName(target.fileName));
|
|
401
|
+
res.setHeader('X-Workspace-Path', encodeURIComponent(target.workspacePath));
|
|
402
|
+
res.setHeader('X-Workspace-Target-Path', encodeURIComponent(target.targetPath));
|
|
403
|
+
if (target.isDirectory) {
|
|
404
|
+
await streamWorkspaceDirectoryZip(target.resolvedTargetPath, res);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
createReadStream(target.resolvedTargetPath).pipe(res);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
const err = error;
|
|
411
|
+
log.error('failed to download workspace entry:', err);
|
|
412
|
+
if (!res.headersSent) {
|
|
413
|
+
res.status(400).json({ error: err.message });
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
res.destroy(err);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
/**
|
|
421
|
+
* 解压工作区内的压缩包到指定目录。
|
|
422
|
+
* POST /api/file-browser/workspace/extract
|
|
423
|
+
*/
|
|
424
|
+
fileBrowserRouter.post('/workspace/extract', validateToken, async (req, res) => {
|
|
425
|
+
try {
|
|
426
|
+
const { workspacePath, archivePath, outputPath = '' } = req.body || {};
|
|
427
|
+
const result = await extractWorkspaceArchive({
|
|
428
|
+
workspacePath: String(workspacePath || ''),
|
|
429
|
+
archivePath: String(archivePath || ''),
|
|
430
|
+
outputPath: String(outputPath || '')
|
|
431
|
+
});
|
|
432
|
+
res.json(result);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
const err = error;
|
|
436
|
+
log.error('failed to extract workspace archive:', err);
|
|
437
|
+
res.status(400).json({ error: err.message });
|
|
438
|
+
}
|
|
439
|
+
});
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* File Browser 路由单元测试
|
|
3
3
|
*/
|
|
4
4
|
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import multer from 'multer';
|
|
6
|
+
import { createWorkspaceUploadLimitResponse, parseWorkspaceUploadRelativePaths, WORKSPACE_UPLOAD_FILE_LIMIT } from './file-browser.js';
|
|
5
7
|
describe('file-browser route validation', () => {
|
|
6
8
|
it('returns root drives on Windows when path is empty', () => {
|
|
7
9
|
const getRootForWindows = () => {
|
|
@@ -85,4 +87,24 @@ describe('file-browser response building', () => {
|
|
|
85
87
|
expect(directories).toEqual(['src']);
|
|
86
88
|
expect(files).toEqual(['package.json']);
|
|
87
89
|
});
|
|
90
|
+
it('returns JSON guidance when folder upload exceeds file count limit', () => {
|
|
91
|
+
const response = createWorkspaceUploadLimitResponse(new multer.MulterError('LIMIT_FILE_COUNT'));
|
|
92
|
+
expect(response.status).toBe(400);
|
|
93
|
+
expect(response.body).toEqual({
|
|
94
|
+
error: `一次最多上传 ${WORKSPACE_UPLOAD_FILE_LIMIT} 个文件,请拆分文件夹后重试。`,
|
|
95
|
+
code: 'LIMIT_FILE_COUNT'
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
it('parses relative paths from a JSON manifest to preserve folder hierarchy', () => {
|
|
99
|
+
const relativePaths = parseWorkspaceUploadRelativePaths({
|
|
100
|
+
relativePathsJson: JSON.stringify(['a/b/c.txt', 'a/d/e.txt'])
|
|
101
|
+
});
|
|
102
|
+
expect(relativePaths).toEqual(['a/b/c.txt', 'a/d/e.txt']);
|
|
103
|
+
});
|
|
104
|
+
it('keeps backward compatibility with repeated relativePaths fields', () => {
|
|
105
|
+
const relativePaths = parseWorkspaceUploadRelativePaths({
|
|
106
|
+
relativePaths: ['a/b/c.txt', 'a/d/e.txt']
|
|
107
|
+
});
|
|
108
|
+
expect(relativePaths).toEqual(['a/b/c.txt', 'a/d/e.txt']);
|
|
109
|
+
});
|
|
88
110
|
});
|
|
@@ -41,6 +41,18 @@ export declare function sendOutput(agentId: string, output: string, sessionId: s
|
|
|
41
41
|
* @param questions - 结构化问题列表
|
|
42
42
|
*/
|
|
43
43
|
export declare function sendQuestionRequest(agentId: string, sessionId: string, questions: unknown[]): Promise<void>;
|
|
44
|
+
export interface RuntimeFileChangedPayload {
|
|
45
|
+
bridgeBaseUrl?: string;
|
|
46
|
+
workspacePath: string;
|
|
47
|
+
filePath: string;
|
|
48
|
+
eventType: string;
|
|
49
|
+
size?: number;
|
|
50
|
+
mtimeMs?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 发送工作区文件变更事件到调度器,由调度器通过 WebSocket 通知前端刷新预览。
|
|
54
|
+
*/
|
|
55
|
+
export declare function sendFileChanged(payload: RuntimeFileChangedPayload): Promise<void>;
|
|
44
56
|
/**
|
|
45
57
|
* 调度输出刷新
|
|
46
58
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-output.d.ts","sourceRoot":"","sources":["../../src/services/session-output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAO3C,aAAa;AACb,eAAO,MAAM,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAa,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,KAAK,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAyBnC;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCzE"}
|
|
1
|
+
{"version":3,"file":"session-output.d.ts","sourceRoot":"","sources":["../../src/services/session-output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAO3C,aAAa;AACb,eAAO,MAAM,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAa,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,KAAK,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAyBnC;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCzE"}
|
|
@@ -81,6 +81,21 @@ export async function sendQuestionRequest(agentId, sessionId, questions) {
|
|
|
81
81
|
// ignore transient callback failures
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* 发送工作区文件变更事件到调度器,由调度器通过 WebSocket 通知前端刷新预览。
|
|
86
|
+
*/
|
|
87
|
+
export async function sendFileChanged(payload) {
|
|
88
|
+
try {
|
|
89
|
+
const headers = runtimeAuthHeaders();
|
|
90
|
+
if (!headers) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await axios.post(`${schedulerBaseUrl}/api/runtime/callback/file-changed`, payload, { headers });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore transient callback failures
|
|
97
|
+
}
|
|
98
|
+
}
|
|
84
99
|
/**
|
|
85
100
|
* 调度输出刷新
|
|
86
101
|
*
|
|
@@ -6,6 +6,7 @@ export interface WorkspaceFileItem {
|
|
|
6
6
|
name: string;
|
|
7
7
|
path: string;
|
|
8
8
|
isDirectory: boolean;
|
|
9
|
+
size?: number;
|
|
9
10
|
}
|
|
10
11
|
export interface ListWorkspaceDirectoryResult {
|
|
11
12
|
currentPath: string;
|
|
@@ -17,6 +18,36 @@ export interface ReadWorkspaceFileResult {
|
|
|
17
18
|
filePath: string;
|
|
18
19
|
content: string;
|
|
19
20
|
}
|
|
21
|
+
export interface WorkspaceDocumentPreviewResult {
|
|
22
|
+
workspacePath: string;
|
|
23
|
+
filePath: string;
|
|
24
|
+
kind: 'html' | 'unsupported';
|
|
25
|
+
content: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
size: number;
|
|
28
|
+
mtimeMs: number;
|
|
29
|
+
}
|
|
30
|
+
export interface WorkspaceDownloadTarget {
|
|
31
|
+
workspacePath: string;
|
|
32
|
+
targetPath: string;
|
|
33
|
+
resolvedTargetPath: string;
|
|
34
|
+
fileName: string;
|
|
35
|
+
contentType: string;
|
|
36
|
+
isDirectory: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface WorkspaceUploadFile {
|
|
39
|
+
originalname: string;
|
|
40
|
+
relativePath?: string;
|
|
41
|
+
path: string;
|
|
42
|
+
size: number;
|
|
43
|
+
}
|
|
44
|
+
export interface WorkspaceArchiveExtractResult {
|
|
45
|
+
ok: true;
|
|
46
|
+
workspacePath: string;
|
|
47
|
+
archivePath: string;
|
|
48
|
+
targetPath: string;
|
|
49
|
+
extractedEntries: number;
|
|
50
|
+
}
|
|
20
51
|
interface WorkspacePathParams {
|
|
21
52
|
workspacePath: string;
|
|
22
53
|
targetPath?: string;
|
|
@@ -39,6 +70,17 @@ interface RenameWorkspaceEntryParams extends WorkspacePathParams {
|
|
|
39
70
|
interface DeleteWorkspaceEntryParams extends WorkspacePathParams {
|
|
40
71
|
targetPath: string;
|
|
41
72
|
}
|
|
73
|
+
interface UploadWorkspaceFilesParams extends WorkspacePathParams {
|
|
74
|
+
files: WorkspaceUploadFile[];
|
|
75
|
+
extractArchives?: boolean;
|
|
76
|
+
}
|
|
77
|
+
interface DownloadWorkspaceEntryParams extends WorkspacePathParams {
|
|
78
|
+
targetPath: string;
|
|
79
|
+
}
|
|
80
|
+
interface ExtractWorkspaceArchiveParams extends WorkspacePathParams {
|
|
81
|
+
archivePath: string;
|
|
82
|
+
outputPath?: string;
|
|
83
|
+
}
|
|
42
84
|
/**
|
|
43
85
|
* 列出工作区目录内容。
|
|
44
86
|
*/
|
|
@@ -47,6 +89,10 @@ export declare function listWorkspaceDirectory(params: WorkspacePathParams): Pro
|
|
|
47
89
|
* 读取工作区中的文本文件。
|
|
48
90
|
*/
|
|
49
91
|
export declare function readWorkspaceFile(params: WorkspaceFileParams): Promise<ReadWorkspaceFileResult>;
|
|
92
|
+
/**
|
|
93
|
+
* 返回工作区内 Word 文档的预览元数据;docx 由前端下载原文件后渲染,旧版 doc 明确提示不支持。
|
|
94
|
+
*/
|
|
95
|
+
export declare function previewWorkspaceDocument(params: WorkspaceFileParams): Promise<WorkspaceDocumentPreviewResult>;
|
|
50
96
|
/**
|
|
51
97
|
* 将文本内容保存到工作区文件中。
|
|
52
98
|
*/
|
|
@@ -81,5 +127,31 @@ export declare function deleteWorkspaceEntry(params: DeleteWorkspaceEntryParams)
|
|
|
81
127
|
workspacePath: string;
|
|
82
128
|
targetPath: string;
|
|
83
129
|
}>;
|
|
130
|
+
/**
|
|
131
|
+
* 上传文件到工作区指定目录,并可在上传后自动解压常见归档。
|
|
132
|
+
*/
|
|
133
|
+
export declare function uploadWorkspaceFiles(params: UploadWorkspaceFilesParams): Promise<{
|
|
134
|
+
ok: true;
|
|
135
|
+
workspacePath: string;
|
|
136
|
+
targetPath: string;
|
|
137
|
+
files: Array<{
|
|
138
|
+
fileName: string;
|
|
139
|
+
targetPath: string;
|
|
140
|
+
size: number;
|
|
141
|
+
extracted?: WorkspaceArchiveExtractResult;
|
|
142
|
+
}>;
|
|
143
|
+
}>;
|
|
144
|
+
/**
|
|
145
|
+
* 解析工作区下载目标;目录由路由层打包为 zip,文件直接流式下载。
|
|
146
|
+
*/
|
|
147
|
+
export declare function resolveWorkspaceDownloadTarget(params: DownloadWorkspaceEntryParams): Promise<WorkspaceDownloadTarget>;
|
|
148
|
+
/**
|
|
149
|
+
* 将目录内容以 zip 格式写入输出流,避免在磁盘生成临时包。
|
|
150
|
+
*/
|
|
151
|
+
export declare function streamWorkspaceDirectoryZip(directoryPath: string, output: NodeJS.WritableStream): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* 解压工作区中的归档文件到指定目录,支持 zip、tar、tar.gz/tgz 与单文件 gz。
|
|
154
|
+
*/
|
|
155
|
+
export declare function extractWorkspaceArchive(params: ExtractWorkspaceArchiveParams): Promise<WorkspaceArchiveExtractResult>;
|
|
84
156
|
export {};
|
|
85
157
|
//# sourceMappingURL=workspace-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-files.d.ts","sourceRoot":"","sources":["../../src/services/workspace-files.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"workspace-files.d.ts","sourceRoot":"","sources":["../../src/services/workspace-files.ts"],"names":[],"mappings":"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"}
|