aws-runtime-bridge 1.9.10 → 1.9.21
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/AcodeSdkAdapter.d.ts +40 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/AcodeSdkAdapter.js +376 -10
- package/dist/adapter/AcodeSdkAdapter.test.js +155 -8
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/ClaudeSdkAdapter.js +17 -1
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/CodexSdkAdapter.js +15 -1
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +56 -4
- package/dist/adapter/instance-ai-config-resolver.d.ts +49 -0
- package/dist/adapter/instance-ai-config-resolver.d.ts.map +1 -0
- package/dist/adapter/instance-ai-config-resolver.js +46 -0
- package/dist/adapter/types.d.ts +29 -2
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/routes/dashboard.d.ts.map +1 -1
- package/dist/routes/dashboard.js +749 -15
- package/dist/routes/events.d.ts +7 -0
- package/dist/routes/events.d.ts.map +1 -0
- package/dist/routes/events.js +22 -0
- package/dist/routes/file-browser.d.ts +1 -1
- package/dist/routes/file-browser.d.ts.map +1 -1
- package/dist/routes/file-browser.js +25 -9
- package/dist/routes/file-snapshot.d.ts +10 -0
- package/dist/routes/file-snapshot.d.ts.map +1 -0
- package/dist/routes/file-snapshot.js +156 -0
- package/dist/routes/git.d.ts +10 -0
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +405 -2
- package/dist/routes/git.test.js +39 -2
- package/dist/routes/instance.js +3 -1
- package/dist/routes/memory.d.ts +3 -0
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +3 -0
- package/dist/routes/terminal.d.ts +17 -3
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +464 -47
- package/dist/routes/terminal.test.js +37 -9
- package/dist/services/agent-event-store.d.ts +60 -0
- package/dist/services/agent-event-store.d.ts.map +1 -0
- package/dist/services/agent-event-store.js +68 -0
- package/dist/services/event-delta-pusher.d.ts +40 -0
- package/dist/services/event-delta-pusher.d.ts.map +1 -0
- package/dist/services/event-delta-pusher.js +95 -0
- package/dist/services/fileSnapshotStore.d.ts +96 -0
- package/dist/services/fileSnapshotStore.d.ts.map +1 -0
- package/dist/services/fileSnapshotStore.js +500 -0
- package/dist/services/instance-ai-config-service.d.ts +49 -0
- package/dist/services/instance-ai-config-service.d.ts.map +1 -0
- package/dist/services/instance-ai-config-service.js +174 -0
- package/dist/services/panel-auth.d.ts +8 -0
- package/dist/services/panel-auth.d.ts.map +1 -1
- package/dist/services/panel-auth.js +8 -0
- package/dist/services/ring-buffer.d.ts +67 -0
- package/dist/services/ring-buffer.d.ts.map +1 -0
- package/dist/services/ring-buffer.js +103 -0
- package/dist/services/session-output.d.ts +49 -3
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +115 -2
- package/dist/utils/instance-ai-config-debug-logger.d.ts +37 -0
- package/dist/utils/instance-ai-config-debug-logger.d.ts.map +1 -0
- package/dist/utils/instance-ai-config-debug-logger.js +63 -0
- package/dist/utils/logger.d.ts +18 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +48 -4
- package/package/acode/dist/built-in-file-tools.d.ts +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.js +5 -3
- package/package/acode/dist/builtins/commandArtifacts.d.ts +8 -1
- package/package/acode/dist/builtins/commandArtifacts.d.ts.map +1 -1
- package/package/acode/dist/builtins/commandArtifacts.js +24 -4
- package/package/acode/dist/builtins/shellCommandResult.d.ts +1 -0
- package/package/acode/dist/builtins/shellCommandResult.d.ts.map +1 -1
- package/package/acode/dist/builtins/shellCommandResult.js +3 -3
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +1 -0
- package/package/acode/dist/builtins/shellCommandRunner.d.ts.map +1 -1
- package/package/acode/dist/builtins/shellCommandRunner.js +1 -0
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts +25 -2
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts.map +1 -1
- package/package/acode/dist/builtins/shellOutputDecoder.js +69 -5
- package/package/acode/dist/cli.js +2 -0
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +156 -5
- package/package/acode/dist/types.d.ts +1 -0
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +10 -3
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +33 -11
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts +136 -0
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/context-manager.js +473 -0
- package/package/aws-client-agent-mcp/dist/context-manager.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +6 -0
- package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +307 -0
- package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +11 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +67 -0
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +166 -10
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +933 -41
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +150 -130
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +15 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +62 -2
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +58 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +246 -15
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +8 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js +106 -2
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +7 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +18 -0
- package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +103 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +27 -2
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +123 -5
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package.json +1 -1
package/dist/routes/git.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { promises as fs } from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
import axios from 'axios';
|
|
9
10
|
import { Router } from 'express';
|
|
10
11
|
import { validateToken } from '../middleware/auth.js';
|
|
11
12
|
import { logger } from '../utils/logger.js';
|
|
@@ -268,9 +269,11 @@ function createScopedGitArgs(baseArgs, context) {
|
|
|
268
269
|
if (!context.relativeWorkspacePath) {
|
|
269
270
|
return [...baseArgs];
|
|
270
271
|
}
|
|
272
|
+
// `git status` does not accept --relative, only a pathspec.
|
|
273
|
+
const supportsRelative = !baseArgs.includes('status');
|
|
271
274
|
return [
|
|
272
275
|
...baseArgs,
|
|
273
|
-
`--relative=${context.relativeWorkspacePath}
|
|
276
|
+
...(supportsRelative ? [`--relative=${context.relativeWorkspacePath}`] : []),
|
|
274
277
|
'--',
|
|
275
278
|
context.relativeWorkspacePath
|
|
276
279
|
];
|
|
@@ -370,6 +373,94 @@ export function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths
|
|
|
370
373
|
}
|
|
371
374
|
return files;
|
|
372
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* 从 git status --porcelain 输出中提取未跟踪 (untracked) 路径。
|
|
378
|
+
* `??` 前缀表示未跟踪文件或目录。
|
|
379
|
+
*/
|
|
380
|
+
export function parseGitStatusUntrackedPaths(output) {
|
|
381
|
+
const paths = [];
|
|
382
|
+
const lines = String(output || '').split('\n').filter(line => line.trim());
|
|
383
|
+
for (const line of lines) {
|
|
384
|
+
if (line.startsWith('?? ')) {
|
|
385
|
+
const rawPath = line.slice(3).trim();
|
|
386
|
+
if (rawPath) {
|
|
387
|
+
// 目录路径通常以 / 结尾,去掉以便统一处理
|
|
388
|
+
paths.push(rawPath.replace(/\/$/, ''));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return paths;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* 遍历未跟踪路径,将目录展开为具体文件列表,返回扁平的 GitDiffSummaryFile 数组。
|
|
396
|
+
* 主流程:对每个路径判断是文件还是目录;目录递归遍历,收集所有文件。
|
|
397
|
+
*/
|
|
398
|
+
async function collectUntrackedFiles(repoRootPath, untrackedPaths, existingFilePaths, maxDepth = 10) {
|
|
399
|
+
const files = [];
|
|
400
|
+
async function walk(dirRelativePath, depth) {
|
|
401
|
+
if (depth > maxDepth)
|
|
402
|
+
return;
|
|
403
|
+
const absDir = path.join(repoRootPath, dirRelativePath);
|
|
404
|
+
let entries;
|
|
405
|
+
try {
|
|
406
|
+
entries = await fs.readdir(absDir, { withFileTypes: true });
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
for (const entry of entries) {
|
|
412
|
+
// 跳过常见的 VCS 与工具元数据目录,避免扫描噪声
|
|
413
|
+
if (entry.name === '.git' || entry.name === 'node_modules')
|
|
414
|
+
continue;
|
|
415
|
+
const entryRelativePath = dirRelativePath
|
|
416
|
+
? `${dirRelativePath}/${entry.name}`
|
|
417
|
+
: entry.name;
|
|
418
|
+
if (entry.isDirectory()) {
|
|
419
|
+
await walk(entryRelativePath, depth + 1);
|
|
420
|
+
}
|
|
421
|
+
else if (entry.isFile()) {
|
|
422
|
+
const normalizedPath = normalizeGitPath(entryRelativePath);
|
|
423
|
+
if (normalizedPath && !existingFilePaths.has(normalizedPath)) {
|
|
424
|
+
files.push({
|
|
425
|
+
path: normalizedPath,
|
|
426
|
+
status: 'added',
|
|
427
|
+
additions: 0,
|
|
428
|
+
deletions: 0,
|
|
429
|
+
staged: false,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
for (const untrackedPath of untrackedPaths) {
|
|
436
|
+
const normalizedPath = normalizeGitPath(untrackedPath);
|
|
437
|
+
if (!normalizedPath)
|
|
438
|
+
continue;
|
|
439
|
+
const absPath = path.join(repoRootPath, untrackedPath);
|
|
440
|
+
let stat;
|
|
441
|
+
try {
|
|
442
|
+
stat = await fs.stat(absPath);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (stat.isFile()) {
|
|
448
|
+
if (!existingFilePaths.has(normalizedPath)) {
|
|
449
|
+
files.push({
|
|
450
|
+
path: normalizedPath,
|
|
451
|
+
status: 'added',
|
|
452
|
+
additions: 0,
|
|
453
|
+
deletions: 0,
|
|
454
|
+
staged: false,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else if (stat.isDirectory()) {
|
|
459
|
+
await walk(normalizedPath, 0);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return files;
|
|
463
|
+
}
|
|
373
464
|
/**
|
|
374
465
|
* 合并工作区与暂存区差异汇总。
|
|
375
466
|
* 主流程:以路径去重,保留工作区展示顺序;同一路径若也存在暂存变更则标记为 staged 并累加统计。
|
|
@@ -706,6 +797,13 @@ gitRouter.post('/git/diff', validateToken, async (req, res) => {
|
|
|
706
797
|
}
|
|
707
798
|
const stagedPaths = parseGitStatusStagedPaths(statusResult.stdout);
|
|
708
799
|
const files = mergeGitDiffSummaryFiles(parseGitDiffSummary(nameStatusResult.stdout, result.stdout, stagedPaths), parseGitDiffSummary(stagedNameStatusResult.stdout, stagedNumstatResult.stdout, stagedPaths, true));
|
|
800
|
+
// 补充未跟踪(untracked)文件:git diff 无法感知 untracked,需从 git status 提取并展开目录
|
|
801
|
+
const untrackedPaths = parseGitStatusUntrackedPaths(statusResult.stdout);
|
|
802
|
+
if (untrackedPaths.length > 0) {
|
|
803
|
+
const existingFilePaths = new Set(files.map(f => f.path));
|
|
804
|
+
const untrackedFiles = await collectUntrackedFiles(context.repositoryRootPath, untrackedPaths, existingFilePaths);
|
|
805
|
+
files.push(...untrackedFiles);
|
|
806
|
+
}
|
|
709
807
|
res.json({
|
|
710
808
|
ok: true,
|
|
711
809
|
isGitRepo: context.isGitRepo,
|
|
@@ -723,6 +821,22 @@ gitRouter.post('/git/diff', validateToken, async (req, res) => {
|
|
|
723
821
|
res.status(500).json({ error: err.message || 'git diff failed' });
|
|
724
822
|
}
|
|
725
823
|
});
|
|
824
|
+
/**
|
|
825
|
+
* 检测仓库是否已配置 git author(user.name / user.email),
|
|
826
|
+
* 未配置时返回 -c 参数回退,避免 git commit 因缺少 author 信息而失败。
|
|
827
|
+
*/
|
|
828
|
+
async function buildGitAuthorFallbackArgs(repoRootPath) {
|
|
829
|
+
const authorArgs = [];
|
|
830
|
+
const nameResult = await execGitCommand(repoRootPath, ['config', 'user.name']);
|
|
831
|
+
if (nameResult.exitCode !== 0 || !nameResult.stdout.trim()) {
|
|
832
|
+
authorArgs.push('-c', 'user.name=AgentsWork');
|
|
833
|
+
}
|
|
834
|
+
const emailResult = await execGitCommand(repoRootPath, ['config', 'user.email']);
|
|
835
|
+
if (emailResult.exitCode !== 0 || !emailResult.stdout.trim()) {
|
|
836
|
+
authorArgs.push('-c', 'user.email=agent@agentswork.com');
|
|
837
|
+
}
|
|
838
|
+
return authorArgs;
|
|
839
|
+
}
|
|
726
840
|
/**
|
|
727
841
|
* 提交工作区未提交变更
|
|
728
842
|
* POST /runtime/git/commit
|
|
@@ -759,7 +873,8 @@ gitRouter.post('/git/commit', validateToken, async (req, res) => {
|
|
|
759
873
|
res.status(400).json({ error: stagedResult.stderr || 'git staged diff failed' });
|
|
760
874
|
return;
|
|
761
875
|
}
|
|
762
|
-
const
|
|
876
|
+
const authorArgs = await buildGitAuthorFallbackArgs(context.repositoryRootPath);
|
|
877
|
+
const commitResult = await execGitCommand(context.repositoryRootPath, [...authorArgs, 'commit', '-m', commitMessage, ...pathspecArgs]);
|
|
763
878
|
if (commitResult.exitCode !== 0) {
|
|
764
879
|
res.status(400).json({ error: commitResult.stderr || commitResult.stdout || 'git commit failed' });
|
|
765
880
|
return;
|
|
@@ -817,6 +932,59 @@ gitRouter.post('/git/add-file', validateToken, async (req, res) => {
|
|
|
817
932
|
res.status(500).json({ error: err.message || 'git add file failed' });
|
|
818
933
|
}
|
|
819
934
|
});
|
|
935
|
+
/**
|
|
936
|
+
* 将多个文件批量加入暂存区(单条 git add 命令,避免 index.lock 竞争)
|
|
937
|
+
* POST /runtime/git/add-files
|
|
938
|
+
*/
|
|
939
|
+
gitRouter.post('/git/add-files', validateToken, async (req, res) => {
|
|
940
|
+
const { workspacePath, repositoryPath, filePaths } = req.body || {};
|
|
941
|
+
if (!workspacePath || !String(workspacePath).trim()) {
|
|
942
|
+
res.status(400).json({ error: 'workspacePath is required' });
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
const rawFilePaths = Array.isArray(filePaths) ? filePaths : [];
|
|
946
|
+
const normalizedFilePaths = rawFilePaths
|
|
947
|
+
.map((fp) => normalizeGitPath(String(fp || '').trim()))
|
|
948
|
+
.filter((fp) => fp.length > 0);
|
|
949
|
+
if (normalizedFilePaths.length === 0) {
|
|
950
|
+
res.status(400).json({ error: 'filePaths is required and must be a non-empty array' });
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
try {
|
|
954
|
+
const context = await resolveGitRepositoryContext(String(repositoryPath || workspacePath).trim());
|
|
955
|
+
if (!context.hasRepository || !context.repositoryRootPath) {
|
|
956
|
+
res.status(400).json({ error: 'workspace is not inside a git repository' });
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const pathspecArgs = normalizedFilePaths.flatMap((fp) => createScopedFilePathspecArgs(fp, context));
|
|
960
|
+
// 合并为单条 git add -- path1 path2 ...,避免并发 index.lock 冲突
|
|
961
|
+
const addArgs = ['add'];
|
|
962
|
+
const pathSpecs = [];
|
|
963
|
+
for (let i = 0; i < pathspecArgs.length; i++) {
|
|
964
|
+
// createScopedFilePathspecArgs 返回 ['--', relativePath],只收集 path 部分
|
|
965
|
+
if (pathspecArgs[i] !== '--') {
|
|
966
|
+
pathSpecs.push(pathspecArgs[i]);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
addArgs.push('--', ...pathSpecs);
|
|
970
|
+
const addResult = await execGitCommand(context.repositoryRootPath, addArgs);
|
|
971
|
+
if (addResult.exitCode !== 0) {
|
|
972
|
+
res.status(400).json({ error: addResult.stderr || 'git add files failed' });
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
res.json({
|
|
976
|
+
ok: true,
|
|
977
|
+
filePaths: normalizedFilePaths,
|
|
978
|
+
workspacePath: context.workspacePath,
|
|
979
|
+
repositoryRootPath: context.repositoryRootPath,
|
|
980
|
+
relativeWorkspacePath: context.relativeWorkspacePath,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
catch (error) {
|
|
984
|
+
const err = error;
|
|
985
|
+
res.status(500).json({ error: err.message || 'git add files failed' });
|
|
986
|
+
}
|
|
987
|
+
});
|
|
820
988
|
/**
|
|
821
989
|
* 获取工作区提交列表
|
|
822
990
|
* POST /runtime/git/commits
|
|
@@ -960,6 +1128,32 @@ gitRouter.post('/git/diff-file', validateToken, async (req, res) => {
|
|
|
960
1128
|
}
|
|
961
1129
|
result = stagedResult;
|
|
962
1130
|
}
|
|
1131
|
+
// 未跟踪文件:git diff 无输出时直接读取文件内容生成新增差异
|
|
1132
|
+
if (!normalizedCommitHash && !result.stdout.trim()) {
|
|
1133
|
+
const absFilePath = path.join(context.repositoryRootPath, repositoryRelativeFilePath);
|
|
1134
|
+
try {
|
|
1135
|
+
const fileContent = await fs.readFile(absFilePath, 'utf-8');
|
|
1136
|
+
if (fileContent) {
|
|
1137
|
+
const diffLines = fileContent.split('\n').map(line => `+${line}`).join('\n');
|
|
1138
|
+
const lineCount = fileContent.split('\n').length;
|
|
1139
|
+
result = {
|
|
1140
|
+
stdout: [
|
|
1141
|
+
`diff --git a/${repositoryRelativeFilePath} b/${repositoryRelativeFilePath}`,
|
|
1142
|
+
'new file mode 100644',
|
|
1143
|
+
`--- /dev/null`,
|
|
1144
|
+
`+++ b/${repositoryRelativeFilePath}`,
|
|
1145
|
+
`@@ -0,0 +1,${lineCount} @@`,
|
|
1146
|
+
diffLines,
|
|
1147
|
+
].join('\n'),
|
|
1148
|
+
stderr: '',
|
|
1149
|
+
exitCode: 0,
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
catch {
|
|
1154
|
+
// 文件不可读或不存在,保持空差异
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
963
1157
|
res.json({
|
|
964
1158
|
ok: true,
|
|
965
1159
|
filePath: normalizedFilePath,
|
|
@@ -976,3 +1170,212 @@ gitRouter.post('/git/diff-file', validateToken, async (req, res) => {
|
|
|
976
1170
|
res.status(500).json({ error: err.message || 'git diff file failed' });
|
|
977
1171
|
}
|
|
978
1172
|
});
|
|
1173
|
+
/**
|
|
1174
|
+
* 调用 OpenAI-compatible API 生成 Git 提交信息。
|
|
1175
|
+
* 具体功能:将暂存区 diff 作为上下文发送给 AI,返回生成的提交信息文本。
|
|
1176
|
+
*/
|
|
1177
|
+
export async function callAiForCommitMessage(aiBaseUrl, aiApiKey, aiModel, diffContent) {
|
|
1178
|
+
const url = `${aiBaseUrl.replace(/\/+$/, '')}/chat/completions`;
|
|
1179
|
+
const systemPrompt = [
|
|
1180
|
+
'You are a Git commit message generator. Generate concise, structured commit messages based on code diffs.',
|
|
1181
|
+
'',
|
|
1182
|
+
'Rules:',
|
|
1183
|
+
'- Use conventional commit format: type(scope): description',
|
|
1184
|
+
'- Types: feat, fix, refactor, test, docs, style, chore, perf, ci, build, revert',
|
|
1185
|
+
'- Keep the first line under 72 characters',
|
|
1186
|
+
'- Include a brief body only if the change needs explanation',
|
|
1187
|
+
'- Write in Chinese for the description part',
|
|
1188
|
+
'- Respond with ONLY the commit message, no explanations',
|
|
1189
|
+
].join('\n');
|
|
1190
|
+
const response = await axios.post(url, {
|
|
1191
|
+
model: aiModel,
|
|
1192
|
+
messages: [
|
|
1193
|
+
{ role: 'system', content: systemPrompt },
|
|
1194
|
+
{ role: 'user', content: `以下是暂存区代码变更内容,请生成一条 Git 提交信息:\n\n${diffContent}` },
|
|
1195
|
+
],
|
|
1196
|
+
max_tokens: 300,
|
|
1197
|
+
temperature: 0.3,
|
|
1198
|
+
}, {
|
|
1199
|
+
headers: {
|
|
1200
|
+
'Content-Type': 'application/json',
|
|
1201
|
+
'Authorization': `Bearer ${aiApiKey}`,
|
|
1202
|
+
},
|
|
1203
|
+
timeout: 30000,
|
|
1204
|
+
});
|
|
1205
|
+
const data = response.data;
|
|
1206
|
+
const content = data?.choices?.[0]?.message?.content?.trim() || '';
|
|
1207
|
+
return content || 'AI 生成失败:返回内容为空';
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* 根据暂存区文件列表和 AI 源生成提交信息。
|
|
1211
|
+
*
|
|
1212
|
+
* 主干流程:
|
|
1213
|
+
* 1. 收集暂存文件路径,对每个文件执行 git diff --cached 获取变更摘要
|
|
1214
|
+
* 2. 合并所有文件变更内容
|
|
1215
|
+
* 3. 调用外部 AI API 生成提交信息
|
|
1216
|
+
* 4. 返回生成的提交信息
|
|
1217
|
+
*
|
|
1218
|
+
* POST /runtime/git/generate-commit-message
|
|
1219
|
+
*/
|
|
1220
|
+
gitRouter.post('/git/generate-commit-message', validateToken, async (req, res) => {
|
|
1221
|
+
const { workspacePath, repositoryPath, stagedFiles, aiBaseUrl, aiApiKey, aiModel } = req.body || {};
|
|
1222
|
+
if (!workspacePath || !String(workspacePath).trim()) {
|
|
1223
|
+
res.status(400).json({ error: 'workspacePath is required' });
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (!Array.isArray(stagedFiles) || stagedFiles.length === 0) {
|
|
1227
|
+
res.status(400).json({ error: 'stagedFiles is required and must be a non-empty array' });
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (!aiBaseUrl || !String(aiBaseUrl).trim() || !aiApiKey || !String(aiApiKey).trim() || !aiModel || !String(aiModel).trim()) {
|
|
1231
|
+
res.status(400).json({ error: 'aiBaseUrl, aiApiKey and aiModel are required' });
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
try {
|
|
1235
|
+
const context = await resolveGitRepositoryContext(String(repositoryPath || workspacePath).trim());
|
|
1236
|
+
if (!context.hasRepository || !context.repositoryRootPath) {
|
|
1237
|
+
res.status(400).json({ error: 'workspace is not inside a git repository' });
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
// 为每个暂存文件获取 staged diff
|
|
1241
|
+
const filePaths = stagedFiles
|
|
1242
|
+
.map(f => f.path)
|
|
1243
|
+
.filter(Boolean);
|
|
1244
|
+
const diffParts = [];
|
|
1245
|
+
for (const filePath of filePaths) {
|
|
1246
|
+
const repositoryRelativePath = resolveRepositoryRelativeFilePath(filePath, context);
|
|
1247
|
+
const result = await execGitCommand(context.repositoryRootPath, [
|
|
1248
|
+
'diff', '--cached', '--', repositoryRelativePath,
|
|
1249
|
+
]);
|
|
1250
|
+
if (result.exitCode === 0 && result.stdout.trim()) {
|
|
1251
|
+
diffParts.push(`--- ${filePath} ---\n${result.stdout.trim()}`);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
if (diffParts.length === 0) {
|
|
1255
|
+
// Fallback: 尝试获取所有暂存区变更
|
|
1256
|
+
const result = await execGitCommand(context.repositoryRootPath, [
|
|
1257
|
+
'diff', '--cached',
|
|
1258
|
+
]);
|
|
1259
|
+
if (result.exitCode === 0 && result.stdout.trim()) {
|
|
1260
|
+
diffParts.push(result.stdout.trim());
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
if (diffParts.length === 0) {
|
|
1264
|
+
res.json({ message: '暂存区没有变更内容' });
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const diffContent = diffParts.join('\n\n');
|
|
1268
|
+
// 限制 diff 长度,防止超过 token 限制
|
|
1269
|
+
const maxDiffLength = 15000;
|
|
1270
|
+
const truncatedDiff = diffContent.length > maxDiffLength
|
|
1271
|
+
? diffContent.slice(0, maxDiffLength) + '\n...(diff truncated due to length)'
|
|
1272
|
+
: diffContent;
|
|
1273
|
+
const generatedMessage = await callAiForCommitMessage(aiBaseUrl, aiApiKey, aiModel, truncatedDiff);
|
|
1274
|
+
res.json({ message: generatedMessage });
|
|
1275
|
+
}
|
|
1276
|
+
catch (error) {
|
|
1277
|
+
const err = error;
|
|
1278
|
+
logger.error(`[Git] generate commit message failed: ${err.message}`);
|
|
1279
|
+
res.status(500).json({ error: err.message || 'generate commit message failed' });
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
/**
|
|
1283
|
+
* 将已暂存的文件撤销暂存(unstage)
|
|
1284
|
+
* POST /runtime/git/unstage-files
|
|
1285
|
+
*/
|
|
1286
|
+
gitRouter.post('/git/unstage-files', validateToken, async (req, res) => {
|
|
1287
|
+
const { workspacePath, repositoryPath, filePaths } = req.body || {};
|
|
1288
|
+
if (!workspacePath || !String(workspacePath).trim()) {
|
|
1289
|
+
res.status(400).json({ error: 'workspacePath is required' });
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const rawFilePaths = Array.isArray(filePaths) ? filePaths : [];
|
|
1293
|
+
const normalizedFilePaths = rawFilePaths
|
|
1294
|
+
.map((fp) => normalizeGitPath(String(fp || '').trim()))
|
|
1295
|
+
.filter((fp) => fp.length > 0);
|
|
1296
|
+
if (normalizedFilePaths.length === 0) {
|
|
1297
|
+
res.status(400).json({ error: 'filePaths is required and must be a non-empty array' });
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
try {
|
|
1301
|
+
const context = await resolveGitRepositoryContext(String(repositoryPath || workspacePath).trim());
|
|
1302
|
+
if (!context.hasRepository || !context.repositoryRootPath) {
|
|
1303
|
+
res.status(400).json({ error: 'workspace is not inside a git repository' });
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
// 使用 git restore --staged 撤销暂存,兼容 Git 2.23+
|
|
1307
|
+
let lastError = '';
|
|
1308
|
+
for (const filePath of normalizedFilePaths) {
|
|
1309
|
+
const pathspecArgs = createScopedFilePathspecArgs(filePath, context);
|
|
1310
|
+
const result = await execGitCommand(context.repositoryRootPath, ['restore', '--staged', ...pathspecArgs]);
|
|
1311
|
+
if (result.exitCode !== 0) {
|
|
1312
|
+
lastError = result.stderr || `git restore --staged failed for ${filePath}`;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if (lastError) {
|
|
1316
|
+
res.status(400).json({ error: lastError });
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
res.json({
|
|
1320
|
+
ok: true,
|
|
1321
|
+
filePaths: normalizedFilePaths,
|
|
1322
|
+
workspacePath: context.workspacePath,
|
|
1323
|
+
repositoryRootPath: context.repositoryRootPath,
|
|
1324
|
+
relativeWorkspacePath: context.relativeWorkspacePath,
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
catch (error) {
|
|
1328
|
+
const err = error;
|
|
1329
|
+
res.status(500).json({ error: err.message || 'git unstage files failed' });
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
/**
|
|
1333
|
+
* 丢弃未暂存文件的修改(还原到 HEAD),慎用,不可恢复。
|
|
1334
|
+
* POST /runtime/git/restore-files
|
|
1335
|
+
*/
|
|
1336
|
+
gitRouter.post('/git/restore-files', validateToken, async (req, res) => {
|
|
1337
|
+
const { workspacePath, repositoryPath, filePaths } = req.body || {};
|
|
1338
|
+
if (!workspacePath || !String(workspacePath).trim()) {
|
|
1339
|
+
res.status(400).json({ error: 'workspacePath is required' });
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const rawFilePaths = Array.isArray(filePaths) ? filePaths : [];
|
|
1343
|
+
const normalizedFilePaths = rawFilePaths
|
|
1344
|
+
.map((fp) => normalizeGitPath(String(fp || '').trim()))
|
|
1345
|
+
.filter((fp) => fp.length > 0);
|
|
1346
|
+
if (normalizedFilePaths.length === 0) {
|
|
1347
|
+
res.status(400).json({ error: 'filePaths is required and must be a non-empty array' });
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
try {
|
|
1351
|
+
const context = await resolveGitRepositoryContext(String(repositoryPath || workspacePath).trim());
|
|
1352
|
+
if (!context.hasRepository || !context.repositoryRootPath) {
|
|
1353
|
+
res.status(400).json({ error: 'workspace is not inside a git repository' });
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
// git restore <file> 丢弃工作区未暂存修改
|
|
1357
|
+
let lastError = '';
|
|
1358
|
+
for (const filePath of normalizedFilePaths) {
|
|
1359
|
+
const pathspecArgs = createScopedFilePathspecArgs(filePath, context);
|
|
1360
|
+
const result = await execGitCommand(context.repositoryRootPath, ['restore', ...pathspecArgs]);
|
|
1361
|
+
if (result.exitCode !== 0) {
|
|
1362
|
+
lastError = result.stderr || `git restore failed for ${filePath}`;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (lastError) {
|
|
1366
|
+
res.status(400).json({ error: lastError });
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
res.json({
|
|
1370
|
+
ok: true,
|
|
1371
|
+
filePaths: normalizedFilePaths,
|
|
1372
|
+
workspacePath: context.workspacePath,
|
|
1373
|
+
repositoryRootPath: context.repositoryRootPath,
|
|
1374
|
+
relativeWorkspacePath: context.relativeWorkspacePath,
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
catch (error) {
|
|
1378
|
+
const err = error;
|
|
1379
|
+
res.status(500).json({ error: err.message || 'git restore files failed' });
|
|
1380
|
+
}
|
|
1381
|
+
});
|
package/dist/routes/git.test.js
CHANGED
|
@@ -3,8 +3,8 @@ import { promises as fs } from 'node:fs';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
import { createAgentsWorkStashSnapshotRef, createScopedFilePathspecArgs, createScopedGitPathspecArgs, mergeGitDiffSummaryFiles, normalizeGitCommitMessage, parseGitDiffSummary, parseGitStashListLine, parseGitStatusStagedPaths, parseLatestGitStash, persistAgentsWorkStashSnapshotRef, } from './git.js';
|
|
6
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { callAiForCommitMessage, createAgentsWorkStashSnapshotRef, createScopedFilePathspecArgs, createScopedGitPathspecArgs, mergeGitDiffSummaryFiles, normalizeGitCommitMessage, parseGitDiffSummary, parseGitStashListLine, parseGitStatusStagedPaths, parseLatestGitStash, persistAgentsWorkStashSnapshotRef, } from './git.js';
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
9
|
async function runGit(cwd, args) {
|
|
10
10
|
const result = await execFileAsync('git', [...args], { cwd });
|
|
@@ -126,3 +126,40 @@ describe('git stash reference helpers', () => {
|
|
|
126
126
|
}
|
|
127
127
|
}, 15_000);
|
|
128
128
|
});
|
|
129
|
+
describe('callAiForCommitMessage', () => {
|
|
130
|
+
it('should return trimmed content from AI response', async () => {
|
|
131
|
+
const mockResponse = { data: { choices: [{ message: { content: ' feat(readme): add new section ' } }] } };
|
|
132
|
+
const axiosModule = await import('axios');
|
|
133
|
+
vi.spyOn(axiosModule.default, 'post').mockResolvedValueOnce(mockResponse);
|
|
134
|
+
const result = await callAiForCommitMessage('https://api.openai.com/v1', 'sk-test-key', 'gpt-4o', 'diff content here');
|
|
135
|
+
expect(result).toBe('feat(readme): add new section');
|
|
136
|
+
});
|
|
137
|
+
it('should return fallback message when AI response content is empty', async () => {
|
|
138
|
+
const mockResponse = { data: { choices: [{ message: { content: '' } }] } };
|
|
139
|
+
const axiosModule = await import('axios');
|
|
140
|
+
vi.spyOn(axiosModule.default, 'post').mockResolvedValueOnce(mockResponse);
|
|
141
|
+
const result = await callAiForCommitMessage('https://api.openai.com/v1', 'sk-test-key', 'gpt-4o', 'diff content here');
|
|
142
|
+
expect(result).toBe('AI 生成失败:返回内容为空');
|
|
143
|
+
});
|
|
144
|
+
it('should use correct API endpoint and headers', async () => {
|
|
145
|
+
const axiosModule = await import('axios');
|
|
146
|
+
const postSpy = vi.spyOn(axiosModule.default, 'post').mockResolvedValueOnce({
|
|
147
|
+
data: { choices: [{ message: { content: 'test message' } }] },
|
|
148
|
+
});
|
|
149
|
+
await callAiForCommitMessage('https://custom.ai.com/api/v1', 'sk-custom-key', 'custom-model', 'test diff');
|
|
150
|
+
expect(postSpy).toHaveBeenCalledWith('https://custom.ai.com/api/v1/chat/completions', expect.objectContaining({
|
|
151
|
+
model: 'custom-model',
|
|
152
|
+
messages: expect.arrayContaining([
|
|
153
|
+
expect.objectContaining({ role: 'system' }),
|
|
154
|
+
expect.objectContaining({ role: 'user' }),
|
|
155
|
+
]),
|
|
156
|
+
max_tokens: 300,
|
|
157
|
+
temperature: 0.3,
|
|
158
|
+
}), expect.objectContaining({
|
|
159
|
+
headers: expect.objectContaining({
|
|
160
|
+
'Content-Type': 'application/json',
|
|
161
|
+
'Authorization': 'Bearer sk-custom-key',
|
|
162
|
+
}),
|
|
163
|
+
}));
|
|
164
|
+
});
|
|
165
|
+
});
|
package/dist/routes/instance.js
CHANGED
|
@@ -724,6 +724,8 @@ instanceRouter.post("/restart-bridge", validateTokenOrPanelSession, async (req,
|
|
|
724
724
|
message: "Bridge 正在优雅退出;下次启动后会通过消息中心通知结果。若未由守护进程管理,请手动重新启动。",
|
|
725
725
|
});
|
|
726
726
|
setTimeout(() => {
|
|
727
|
+
// 用退出码 42 表示"重启请求",配合 systemd Restart=on-failure 触发自动拉起
|
|
728
|
+
process.exitCode = 42;
|
|
727
729
|
if (gracefulShutdownFn) {
|
|
728
730
|
gracefulShutdownFn("PANEL_RESTART", Boolean(preserveSessions)).catch((error) => {
|
|
729
731
|
const err = error;
|
|
@@ -732,7 +734,7 @@ instanceRouter.post("/restart-bridge", validateTokenOrPanelSession, async (req,
|
|
|
732
734
|
});
|
|
733
735
|
return;
|
|
734
736
|
}
|
|
735
|
-
process.exit(
|
|
737
|
+
process.exit(42);
|
|
736
738
|
}, 250);
|
|
737
739
|
}
|
|
738
740
|
catch (error) {
|
package/dist/routes/memory.d.ts
CHANGED
|
@@ -5,11 +5,14 @@ interface MemoryRecord {
|
|
|
5
5
|
uri: string;
|
|
6
6
|
domain: string;
|
|
7
7
|
path: string;
|
|
8
|
+
title?: string;
|
|
8
9
|
content: string;
|
|
9
10
|
memory_type: MemoryType;
|
|
10
11
|
createdAt: string;
|
|
11
12
|
updatedAt: string;
|
|
12
13
|
version: number;
|
|
14
|
+
keywords?: string[];
|
|
15
|
+
bound_files?: string[];
|
|
13
16
|
}
|
|
14
17
|
interface PermanentMemoryStoreCandidate {
|
|
15
18
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/routes/memory.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,4CAAW,CAAC;AAErC,KAAK,UAAU,GAAG,WAAW,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAE7D,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,UAAU,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/routes/memory.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,4CAAW,CAAC;AAErC,KAAK,UAAU,GAAG,WAAW,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAE7D,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,UAAU,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAEhB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAMD,UAAU,6BAA6B;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;CACrB;AAmCD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,EACrB,QAAQ,SAAK,GACZ,MAAM,CAaR;AAED;;;GAGG;AACH,wBAAgB,yCAAyC,CACvD,aAAa,EAAE,MAAM,EACrB,QAAQ,SAAK,GACZ,6BAA6B,EAAE,CAWjC;AA8FD;;;GAGG;AACH,wBAAsB,kCAAkC,CACtD,aAAa,EAAE,MAAM,EACrB,QAAQ,SAAK,GACZ,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB"}
|
package/dist/routes/memory.js
CHANGED
|
@@ -84,11 +84,14 @@ function normalizeMemoryRecord(uri, record) {
|
|
|
84
84
|
uri: normalized.uri,
|
|
85
85
|
domain: normalized.domain,
|
|
86
86
|
path: normalized.path,
|
|
87
|
+
title: record.title || undefined,
|
|
87
88
|
content: String(record.content || ""),
|
|
88
89
|
memory_type: "memory",
|
|
89
90
|
createdAt: String(record.createdAt || record.updatedAt || ""),
|
|
90
91
|
updatedAt: String(record.updatedAt || record.createdAt || ""),
|
|
91
92
|
version: typeof record.version === "number" ? record.version : 1,
|
|
93
|
+
keywords: record.keywords,
|
|
94
|
+
bound_files: record.bound_files,
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
@@ -22,6 +22,7 @@ export interface SdkSessionEntry {
|
|
|
22
22
|
currentRuntimeActionType?: string;
|
|
23
23
|
currentRuntimeActionLabel?: string;
|
|
24
24
|
currentRuntimeActionDetail?: string;
|
|
25
|
+
currentRuntimeActionInput?: string;
|
|
25
26
|
}
|
|
26
27
|
export declare const sdkSessions: Map<string, SdkSessionEntry>;
|
|
27
28
|
export interface RuntimeStopOptions {
|
|
@@ -47,6 +48,7 @@ export declare function shouldProbeSdkProcessPid(providerId: string): boolean;
|
|
|
47
48
|
*/
|
|
48
49
|
export declare function parseRuntimeStopOptions(body: unknown): RuntimeStopOptions;
|
|
49
50
|
export declare function resolveStatusChangeUsage(actionInfo?: RuntimeStatusActionInfo): {
|
|
51
|
+
contextLimit?: number | undefined;
|
|
50
52
|
inputTokens: number;
|
|
51
53
|
outputTokens: number;
|
|
52
54
|
} | undefined;
|
|
@@ -54,7 +56,7 @@ export declare function resolveStatusChangeRuntimeState(status: string, actionIn
|
|
|
54
56
|
status: string;
|
|
55
57
|
actionInfo?: RuntimeStatusActionInfo;
|
|
56
58
|
};
|
|
57
|
-
export declare function buildAcodeCommandOutputPath(
|
|
59
|
+
export declare function buildAcodeCommandOutputPath(agentId: string, now?: Date): string;
|
|
58
60
|
export declare function countTerminalOutputLines(output: string): number;
|
|
59
61
|
export declare function formatCommandOutputSpilloverNotice(outputPath: string, lineLimit?: number): string;
|
|
60
62
|
export declare function formatCommandInactivityTimeoutNotice(timeoutMs?: number): string;
|
|
@@ -67,14 +69,25 @@ export declare function normalizeTerminalCommandInput(value: unknown): string;
|
|
|
67
69
|
* 主流程:优先使用环境变量覆盖 -> Windows 默认 GB18030 -> 其他平台默认 UTF-8。
|
|
68
70
|
*/
|
|
69
71
|
export declare function resolveTerminalOutputEncoding(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
72
|
+
/**
|
|
73
|
+
* 终端输出解码器接口。
|
|
74
|
+
*/
|
|
75
|
+
interface TerminalOutputDecoder {
|
|
76
|
+
decode(chunk?: Uint8Array | ArrayBuffer | null, options?: {
|
|
77
|
+
stream?: boolean;
|
|
78
|
+
}): string;
|
|
79
|
+
}
|
|
70
80
|
/**
|
|
71
81
|
* 创建终端输出流式解码器,避免多字节中文被 chunk 边界截断。
|
|
82
|
+
*
|
|
83
|
+
* 主流程:环境变量显式指定非 utf-8 时固定编码 → 否则自动检测
|
|
84
|
+
* (先 UTF-8,遇替换字符回退 GB18030,覆盖不同程序的不同编码)。
|
|
72
85
|
*/
|
|
73
|
-
export declare function createTerminalOutputDecoder(encoding?: string):
|
|
86
|
+
export declare function createTerminalOutputDecoder(encoding?: string): TerminalOutputDecoder;
|
|
74
87
|
/**
|
|
75
88
|
* 解码终端输出 chunk;end=true 时刷新解码器内部剩余字节。
|
|
76
89
|
*/
|
|
77
|
-
export declare function decodeTerminalOutputChunk(decoder: TextDecoder, chunk?: Buffer, end?: boolean): string;
|
|
90
|
+
export declare function decodeTerminalOutputChunk(decoder: TerminalOutputDecoder | TextDecoder, chunk?: Buffer, end?: boolean): string;
|
|
78
91
|
/**
|
|
79
92
|
* 解析 cd 命令目标;返回 undefined 表示不是目录切换命令,null 表示仅查看当前目录。
|
|
80
93
|
*/
|
|
@@ -125,4 +138,5 @@ export declare function resolveRuntimeStatusFromProviderEvent(event: ProviderEve
|
|
|
125
138
|
actionInfo?: RuntimeStatusActionInfo;
|
|
126
139
|
} | undefined;
|
|
127
140
|
export declare function resolveSdkProviderId(command?: string): string;
|
|
141
|
+
export {};
|
|
128
142
|
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAMxC,OAAO,
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.ts"],"names":[],"mappings":"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"}
|