@fitlab-ai/agent-infra 0.8.2 → 0.8.4
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/README.zh-CN.md +10 -0
- package/bin/cli.ts +1 -1
- package/dist/bin/cli.js +2 -2
- package/dist/lib/cp.js +57 -16
- package/dist/lib/decide.js +2 -2
- package/dist/lib/defaults.json +2 -0
- package/dist/lib/init.js +7 -7
- package/dist/lib/merge.js +1 -1
- package/dist/lib/prompt.js +1 -1
- package/dist/lib/run/index.js +7 -7
- package/dist/lib/run/prompt.js +1 -1
- package/dist/lib/sandbox/capture.js +10 -8
- package/dist/lib/sandbox/clipboard/bridge.js +59 -21
- package/dist/lib/sandbox/clipboard/inbox.js +82 -0
- package/dist/lib/sandbox/clipboard/index.js +5 -6
- package/dist/lib/sandbox/clipboard/linux.js +91 -0
- package/dist/lib/sandbox/clipboard/paths.js +1 -1
- package/dist/lib/sandbox/clipboard/win32.js +144 -0
- package/dist/lib/sandbox/commands/create.js +106 -88
- package/dist/lib/sandbox/commands/enter.js +9 -9
- package/dist/lib/sandbox/commands/list-running.js +2 -2
- package/dist/lib/sandbox/commands/ls.js +7 -7
- package/dist/lib/sandbox/commands/prune.js +8 -8
- package/dist/lib/sandbox/commands/rebuild.js +38 -50
- package/dist/lib/sandbox/commands/refresh.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +11 -11
- package/dist/lib/sandbox/commands/show.js +4 -4
- package/dist/lib/sandbox/commands/start.js +4 -4
- package/dist/lib/sandbox/commands/vm.js +4 -4
- package/dist/lib/sandbox/config.js +10 -4
- package/dist/lib/sandbox/constants.js +4 -1
- package/dist/lib/sandbox/credentials.js +1 -1
- package/dist/lib/sandbox/dotfiles.js +1 -1
- package/dist/lib/sandbox/engine.js +3 -3
- package/dist/lib/sandbox/engines/index.js +5 -5
- package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
- package/dist/lib/sandbox/image-build.js +80 -0
- package/dist/lib/sandbox/image-prune.js +2 -2
- package/dist/lib/sandbox/index.js +10 -10
- package/dist/lib/sandbox/managed-fs.js +1 -1
- package/dist/lib/sandbox/readme-scaffold.js +1 -1
- package/dist/lib/sandbox/runtimes/base.dockerfile +3 -0
- package/dist/lib/sandbox/shell.js +9 -1
- package/dist/lib/sandbox/tools.js +4 -4
- package/dist/lib/server/adapters/feishu/index.js +10 -3
- package/dist/lib/server/adapters/feishu/renderer.js +88 -0
- package/dist/lib/server/adapters/feishu/transport.js +2 -13
- package/dist/lib/server/daemon.js +64 -43
- package/dist/lib/server/display.js +83 -0
- package/dist/lib/server/index.js +2 -2
- package/dist/lib/server/process-control.js +2 -2
- package/dist/lib/server/protocol.js +2 -2
- package/dist/lib/server/streamer.js +5 -4
- package/dist/lib/task/commands/cat.js +2 -2
- package/dist/lib/task/commands/decisions.js +4 -4
- package/dist/lib/task/commands/files.js +3 -3
- package/dist/lib/task/commands/grep.js +3 -3
- package/dist/lib/task/commands/issue-body.js +4 -4
- package/dist/lib/task/commands/log.js +5 -5
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/commands/show.js +1 -1
- package/dist/lib/task/commands/status.js +71 -25
- package/dist/lib/task/index.js +9 -9
- package/dist/lib/task/resolve-ref.js +1 -1
- package/dist/lib/task/workflow-warnings.js +94 -0
- package/dist/lib/update.js +4 -4
- package/lib/cp.ts +56 -15
- package/lib/defaults.json +2 -0
- package/lib/sandbox/capture.ts +5 -3
- package/lib/sandbox/clipboard/bridge.ts +62 -18
- package/lib/sandbox/clipboard/inbox.ts +83 -0
- package/lib/sandbox/clipboard/index.ts +5 -6
- package/lib/sandbox/clipboard/linux.ts +124 -0
- package/lib/sandbox/clipboard/win32.ts +173 -0
- package/lib/sandbox/commands/create.ts +132 -88
- package/lib/sandbox/commands/rebuild.ts +42 -54
- package/lib/sandbox/config.ts +13 -1
- package/lib/sandbox/constants.ts +4 -0
- package/lib/sandbox/image-build.ts +134 -0
- package/lib/sandbox/runtimes/base.dockerfile +3 -0
- package/lib/sandbox/shell.ts +11 -2
- package/lib/sandbox/tools.ts +5 -5
- package/lib/server/adapters/_contract.ts +3 -0
- package/lib/server/adapters/feishu/index.ts +11 -3
- package/lib/server/adapters/feishu/renderer.ts +99 -0
- package/lib/server/adapters/feishu/transport.ts +5 -18
- package/lib/server/daemon.ts +74 -38
- package/lib/server/display.ts +136 -0
- package/lib/server/streamer.ts +5 -4
- package/lib/task/commands/log.ts +3 -3
- package/lib/task/commands/status.ts +102 -21
- package/lib/task/workflow-warnings.ts +121 -0
- package/package.json +2 -2
- package/templates/.agents/README.en.md +3 -0
- package/templates/.agents/README.zh-CN.md +3 -0
- package/templates/.agents/rules/create-issue.github.en.md +4 -4
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -4
- package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
- package/templates/.agents/rules/issue-sync.github.en.md +13 -0
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
- package/templates/.agents/rules/next-step-output.en.md +15 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
- package/templates/.agents/rules/pr-sync.github.en.md +17 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
- package/templates/.agents/rules/review-handshake.en.md +2 -2
- package/templates/.agents/rules/review-handshake.zh-CN.md +2 -2
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/rules/testing-discipline.en.md +3 -41
- package/templates/.agents/rules/testing-discipline.zh-CN.md +3 -41
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
- package/templates/.agents/scripts/validate-artifact.js +213 -0
- package/templates/.agents/scripts/workflow-warnings.js +290 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +9 -9
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/branch-management.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +6 -6
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +6 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +142 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +142 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -2
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +165 -41
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.en.md +8 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/test/SKILL.zh-CN.md +26 -8
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +36 -6
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +8 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +8 -1
- package/templates/.agents/templates/task.en.md +7 -0
- package/templates/.agents/templates/task.zh-CN.md +7 -0
- package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
- package/templates/.github/workflows/metadata-sync.yml +5 -0
- package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
|
@@ -1,58 +1,37 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
2
|
import * as p from '@clack/prompts';
|
|
4
3
|
import pc from 'picocolors';
|
|
5
|
-
import { loadConfig } from
|
|
6
|
-
import { prepareDockerfile } from
|
|
7
|
-
import {
|
|
8
|
-
import { detectEngine, ensureDocker } from
|
|
9
|
-
import { runEngine, runSafeEngine, runVerboseEngine } from
|
|
10
|
-
import { pruneSandboxDanglingImages } from
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { resolveBuildUid } from "../engines/native.js";
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
import { prepareDockerfile } from '../dockerfile.js';
|
|
6
|
+
import { sandboxImageRefreshLabel } from '../constants.js';
|
|
7
|
+
import { detectEngine, ensureDocker } from '../engine.js';
|
|
8
|
+
import { runEngine, runOkEngine, runSafeEngine, runVerboseEngine } from '../shell.js';
|
|
9
|
+
import { pruneSandboxDanglingImages } from '../image-prune.js';
|
|
10
|
+
import { resolveTools } from '../tools.js';
|
|
11
|
+
import { buildImageSignature, buildSandboxImageArgs, parseImageLabels, parseRefreshTimestamp } from '../image-build.js';
|
|
14
12
|
const USAGE = `Usage: ai sandbox rebuild [--quiet] [--refresh]`;
|
|
15
|
-
function
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
dockerfile: preparedDockerfile.signature,
|
|
19
|
-
tools: imageSignatureFields(tools)
|
|
20
|
-
}))
|
|
21
|
-
.digest('hex')
|
|
22
|
-
.slice(0, 12);
|
|
23
|
-
}
|
|
24
|
-
export function buildArgs(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, env = process.env, refresh = false } = {}) {
|
|
25
|
-
const selectedEngine = engine ?? detectEngine(config);
|
|
26
|
-
const { uid: hostUid, gid: hostGid } = resolveBuildUid({
|
|
27
|
-
engine: selectedEngine,
|
|
13
|
+
export function buildArgs(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, env = process.env, refresh = false, lastRefresh } = {}) {
|
|
14
|
+
return buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, {
|
|
15
|
+
engine: engine ?? detectEngine(config),
|
|
28
16
|
runFn,
|
|
29
17
|
runSafeFn,
|
|
30
|
-
env
|
|
18
|
+
env,
|
|
19
|
+
refresh,
|
|
20
|
+
lastRefresh
|
|
31
21
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'--build-arg',
|
|
37
|
-
`HOST_UID=${hostUid}`,
|
|
38
|
-
'--build-arg',
|
|
39
|
-
`HOST_GID=${hostGid}`,
|
|
40
|
-
'--build-arg',
|
|
41
|
-
`AI_TOOL_PACKAGES=${toolNpmPackagesArg(tools)}`,
|
|
42
|
-
'--build-arg',
|
|
43
|
-
`AI_TOOLS_SHELL_INSTALL_B64=${toolShellInstallScriptBase64(tools)}`,
|
|
44
|
-
'--label',
|
|
45
|
-
sandboxLabel(config),
|
|
46
|
-
'--label',
|
|
47
|
-
`${sandboxImageConfigLabel(config)}=${imageSignature}`,
|
|
48
|
-
'-f',
|
|
49
|
-
toEnginePath(selectedEngine, dockerfilePath),
|
|
50
|
-
toEnginePath(selectedEngine, config.repoRoot)
|
|
51
|
-
];
|
|
52
|
-
if (refresh) {
|
|
53
|
-
args.splice(1, 0, '--no-cache', '--pull');
|
|
22
|
+
}
|
|
23
|
+
function readExistingLastRefresh(config, engine) {
|
|
24
|
+
if (!runOkEngine(engine, 'docker', ['image', 'inspect', config.imageName])) {
|
|
25
|
+
return 0;
|
|
54
26
|
}
|
|
55
|
-
|
|
27
|
+
const labels = parseImageLabels(runSafeEngine(engine, 'docker', [
|
|
28
|
+
'image',
|
|
29
|
+
'inspect',
|
|
30
|
+
'--format',
|
|
31
|
+
'{{ json .Config.Labels }}',
|
|
32
|
+
config.imageName
|
|
33
|
+
]));
|
|
34
|
+
return parseRefreshTimestamp(labels[sandboxImageRefreshLabel(config)] ?? '');
|
|
56
35
|
}
|
|
57
36
|
export async function rebuild(args) {
|
|
58
37
|
const { values } = parseArgs({
|
|
@@ -72,24 +51,33 @@ export async function rebuild(args) {
|
|
|
72
51
|
const config = loadConfig();
|
|
73
52
|
const tools = resolveTools(config);
|
|
74
53
|
const preparedDockerfile = prepareDockerfile(config);
|
|
75
|
-
const imageSignature =
|
|
54
|
+
const imageSignature = buildImageSignature(preparedDockerfile, tools);
|
|
76
55
|
const quiet = values.quiet ?? false;
|
|
77
56
|
const refresh = values.refresh ?? false;
|
|
78
57
|
const engine = detectEngine(config);
|
|
79
58
|
await ensureDocker(config, undefined);
|
|
59
|
+
const lastRefresh = refresh ? Date.now() : readExistingLastRefresh(config, engine);
|
|
80
60
|
p.intro(pc.cyan('Rebuilding sandbox image'));
|
|
81
61
|
try {
|
|
82
62
|
if (quiet) {
|
|
83
63
|
const spinner = p.spinner();
|
|
84
64
|
spinner.start('Building image...');
|
|
85
|
-
runEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, {
|
|
65
|
+
runEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, {
|
|
66
|
+
engine,
|
|
67
|
+
refresh,
|
|
68
|
+
lastRefresh
|
|
69
|
+
}), {
|
|
86
70
|
cwd: config.repoRoot
|
|
87
71
|
});
|
|
88
72
|
spinner.stop(pc.green('Sandbox image rebuilt'));
|
|
89
73
|
}
|
|
90
74
|
else {
|
|
91
75
|
p.log.step('Building image');
|
|
92
|
-
runVerboseEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, {
|
|
76
|
+
runVerboseEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, {
|
|
77
|
+
engine,
|
|
78
|
+
refresh,
|
|
79
|
+
lastRefresh
|
|
80
|
+
}), { cwd: config.repoRoot });
|
|
93
81
|
p.log.success(pc.green('Sandbox image rebuilt'));
|
|
94
82
|
}
|
|
95
83
|
pruneSandboxDanglingImages(config, engine);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
|
-
import { buildLockedGuidance, discoverProjects, formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from
|
|
4
|
-
import { runProbe } from
|
|
3
|
+
import { buildLockedGuidance, discoverProjects, formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
|
|
4
|
+
import { runProbe } from '../shell.js';
|
|
5
5
|
const USAGE = 'Usage: ai sandbox refresh';
|
|
6
6
|
export function probeClaudeStatus(spawnFn = runProbe) {
|
|
7
7
|
const result = spawnFn('claude', ['/status'], {
|
|
@@ -3,21 +3,21 @@ import path from 'node:path';
|
|
|
3
3
|
import { parseArgs } from 'node:util';
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import pc from 'picocolors';
|
|
6
|
-
import { loadConfig } from
|
|
7
|
-
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, shareBranchDir, shellConfigDirCandidates, worktreeDirCandidates } from
|
|
8
|
-
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, stopManagedVm } from
|
|
9
|
-
import { pruneSandboxDanglingImages } from
|
|
10
|
-
import { removeManagedDir, removeWorktreeDir } from
|
|
11
|
-
import { runOk, runSafe, runSafeEngine } from
|
|
12
|
-
import { resolveTaskBranch } from
|
|
13
|
-
import { resolveTools, toolConfigDirCandidates, toolProjectDirCandidates } from
|
|
14
|
-
import { fetchSandboxRows } from
|
|
15
|
-
import { lookupShortIdByBranch } from
|
|
6
|
+
import { loadConfig } from '../config.js';
|
|
7
|
+
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, shareBranchDir, shellConfigDirCandidates, worktreeDirCandidates } from '../constants.js';
|
|
8
|
+
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, stopManagedVm } from '../engine.js';
|
|
9
|
+
import { pruneSandboxDanglingImages } from '../image-prune.js';
|
|
10
|
+
import { removeManagedDir, removeWorktreeDir } from '../managed-fs.js';
|
|
11
|
+
import { runOk, runSafe, runSafeEngine } from '../shell.js';
|
|
12
|
+
import { resolveTaskBranch } from '../task-resolver.js';
|
|
13
|
+
import { resolveTools, toolConfigDirCandidates, toolProjectDirCandidates } from '../tools.js';
|
|
14
|
+
import { fetchSandboxRows } from './list-running.js';
|
|
15
|
+
import { lookupShortIdByBranch } from '../../task/short-id.js';
|
|
16
16
|
const USAGE = `Usage:
|
|
17
17
|
ai sandbox rm <branch> Remove one sandbox (branch | TASK-id | short id)
|
|
18
18
|
ai sandbox rm --all [--dry-run] [--yes] Remove every sandbox not bound to an active task
|
|
19
19
|
ai sandbox rm --purge Tear down ALL sandboxes for the project (containers, worktrees, image, VM)`;
|
|
20
|
-
export { assertManagedPath } from
|
|
20
|
+
export { assertManagedPath } from '../managed-fs.js';
|
|
21
21
|
function projectToolDirs(config, tools) {
|
|
22
22
|
return tools.flatMap((tool) => toolProjectDirCandidates(tool, config.project));
|
|
23
23
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
|
-
import { loadConfig } from
|
|
5
|
-
import { assertValidBranchName, worktreeDirCandidates } from
|
|
6
|
-
import { resolveBranchArg } from
|
|
7
|
-
import { resolveTools, toolConfigDirCandidates } from
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
import { assertValidBranchName, worktreeDirCandidates } from '../constants.js';
|
|
6
|
+
import { resolveBranchArg } from './list-running.js';
|
|
7
|
+
import { resolveTools, toolConfigDirCandidates } from '../tools.js';
|
|
8
8
|
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N | '#N'>
|
|
9
9
|
|
|
10
10
|
Shows one sandbox's worktree path and per-tool state paths (Claude Code,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { loadConfig } from
|
|
2
|
-
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from
|
|
3
|
-
import { detectEngine } from
|
|
4
|
-
import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, startSandboxContainer } from
|
|
1
|
+
import { loadConfig } from '../config.js';
|
|
2
|
+
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
|
|
3
|
+
import { detectEngine } from '../engine.js';
|
|
4
|
+
import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, startSandboxContainer } from './list-running.js';
|
|
5
5
|
const USAGE = `Usage: ai sandbox start <branch | TASK-id | N | '#N'>
|
|
6
6
|
|
|
7
7
|
Start an existing sandbox container that has stopped (for example after the
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
|
-
import { loadConfig } from
|
|
5
|
-
import { parsePositiveIntegerOption } from
|
|
6
|
-
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, startManagedVm, stopManagedVm } from
|
|
7
|
-
import { runOk, runSafe } from
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
import { parsePositiveIntegerOption } from '../constants.js';
|
|
6
|
+
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, startManagedVm, stopManagedVm } from '../engine.js';
|
|
7
|
+
import { runOk, runSafe } from '../shell.js';
|
|
8
8
|
const USAGE = `Usage: ai sandbox vm <status|start|stop> [--cpu <n>] [--memory <n>]`;
|
|
9
9
|
export function ensureManagedVm(engine) {
|
|
10
10
|
if (engine === ENGINES.NATIVE) {
|
|
@@ -3,14 +3,15 @@ import path from 'node:path';
|
|
|
3
3
|
import { homedir, platform } from 'node:os';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
5
|
import pc from 'picocolors';
|
|
6
|
-
import { validateSandboxEngine } from
|
|
7
|
-
import { hostJoin } from
|
|
8
|
-
import { findRuntimeEngineMismatches } from
|
|
9
|
-
import { parseCustomTools } from
|
|
6
|
+
import { validateSandboxEngine } from './engine.js';
|
|
7
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
8
|
+
import { findRuntimeEngineMismatches } from './runtime-engines.js';
|
|
9
|
+
import { parseCustomTools } from './tools.js';
|
|
10
10
|
const DEFAULTS = Object.freeze({
|
|
11
11
|
engine: null,
|
|
12
12
|
runtimes: ['node22'],
|
|
13
13
|
tools: ['agent-infra', 'claude-code', 'codex', 'gemini-cli', 'opencode'],
|
|
14
|
+
refreshIntervalDays: 7,
|
|
14
15
|
dockerfile: null,
|
|
15
16
|
vm: {
|
|
16
17
|
cpu: null,
|
|
@@ -32,11 +33,15 @@ function detectRepoRoot() {
|
|
|
32
33
|
function asPositiveNumberOrNull(value) {
|
|
33
34
|
return typeof value === 'number' ? value : null;
|
|
34
35
|
}
|
|
36
|
+
function asNonNegativeIntegerOrDefault(value, fallback) {
|
|
37
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
38
|
+
}
|
|
35
39
|
function cloneDefaults() {
|
|
36
40
|
return {
|
|
37
41
|
engine: DEFAULTS.engine,
|
|
38
42
|
runtimes: [...DEFAULTS.runtimes],
|
|
39
43
|
tools: [...DEFAULTS.tools],
|
|
44
|
+
refreshIntervalDays: DEFAULTS.refreshIntervalDays,
|
|
40
45
|
dockerfile: DEFAULTS.dockerfile,
|
|
41
46
|
vm: { ...DEFAULTS.vm }
|
|
42
47
|
};
|
|
@@ -96,6 +101,7 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
|
|
|
96
101
|
? [...sandbox.tools]
|
|
97
102
|
: defaults.tools,
|
|
98
103
|
customTools,
|
|
104
|
+
refreshIntervalDays: asNonNegativeIntegerOrDefault(sandbox.refreshIntervalDays, defaults.refreshIntervalDays),
|
|
99
105
|
dockerfile,
|
|
100
106
|
vm: {
|
|
101
107
|
cpu: asPositiveNumberOrNull(sandbox.vm?.cpu) ?? defaults.vm.cpu,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import { hostJoin } from
|
|
3
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
4
4
|
const validatedBranches = new Set();
|
|
5
5
|
function dedupe(items) {
|
|
6
6
|
return [...new Set(items)];
|
|
@@ -72,6 +72,9 @@ export function sandboxBranchLabel(config) {
|
|
|
72
72
|
export function sandboxImageConfigLabel(config) {
|
|
73
73
|
return `${sandboxLabel(config)}.image-config`;
|
|
74
74
|
}
|
|
75
|
+
export function sandboxImageRefreshLabel(config) {
|
|
76
|
+
return `${sandboxLabel(config)}.last-refresh`;
|
|
77
|
+
}
|
|
75
78
|
export function parsePositiveIntegerOption(value, optionName) {
|
|
76
79
|
if (value === undefined || value === null) {
|
|
77
80
|
return undefined;
|
|
@@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { hostJoin } from
|
|
5
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
6
6
|
const LOCKED_PATTERN = /errSecInteractionNotAllowed|User interaction is not allowed/i;
|
|
7
7
|
const NOT_FOUND_PATTERN = /errSecItemNotFound|specified item could not be found/i;
|
|
8
8
|
const REDACTION_PATTERNS = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { hostJoin } from
|
|
3
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
4
4
|
export function dotfilesCacheDir(home, project) {
|
|
5
5
|
return hostJoin(home, '.agent-infra', '.cache', 'dotfiles-resolved', project);
|
|
6
6
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { platform } from 'node:os';
|
|
2
|
-
import { detectHostResources } from
|
|
3
|
-
import { ADAPTERS, enginesForPlatform, getAdapter } from
|
|
4
|
-
import { run, runOk, runSafe, runVerbose } from
|
|
2
|
+
import { detectHostResources } from './constants.js';
|
|
3
|
+
import { ADAPTERS, enginesForPlatform, getAdapter } from './engines/index.js';
|
|
4
|
+
import { run, runOk, runSafe, runVerbose } from './shell.js';
|
|
5
5
|
export const ENGINES = Object.freeze({
|
|
6
6
|
COLIMA: 'colima',
|
|
7
7
|
ORBSTACK: 'orbstack',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { colimaAdapter } from
|
|
2
|
-
import { dockerDesktopAdapter } from
|
|
3
|
-
import { nativeAdapter } from
|
|
4
|
-
import { orbstackAdapter } from
|
|
5
|
-
import { wsl2Adapter } from
|
|
1
|
+
import { colimaAdapter } from './colima.js';
|
|
2
|
+
import { dockerDesktopAdapter } from './docker-desktop.js';
|
|
3
|
+
import { nativeAdapter } from './native.js';
|
|
4
|
+
import { orbstackAdapter } from './orbstack.js';
|
|
5
|
+
import { wsl2Adapter } from './wsl2.js';
|
|
6
6
|
export const ADAPTERS = Object.freeze({
|
|
7
7
|
colima: colimaAdapter,
|
|
8
8
|
orbstack: orbstackAdapter,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { selinuxLabelForMount } from
|
|
2
|
+
import { selinuxLabelForMount } from './selinux.js';
|
|
3
3
|
const WINDOWS_DRIVE_PATH_PATTERN = /^([A-Za-z]):[\\/](.*)$/;
|
|
4
4
|
const UNC_PATH_PATTERN = /^(?:\\\\|\/\/)[^\\/]+[\\/][^\\/]+/;
|
|
5
5
|
export function hostJoin(basePath, ...segments) {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { detectEngine } from './engine.js';
|
|
3
|
+
import { resolveBuildUid } from './engines/native.js';
|
|
4
|
+
import { toEnginePath } from './engines/wsl2-paths.js';
|
|
5
|
+
import { sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel } from './constants.js';
|
|
6
|
+
import { runEngine, runSafeEngine } from './shell.js';
|
|
7
|
+
import { imageSignatureFields, toolNpmPackagesArg, toolShellInstallScriptBase64 } from './tools.js';
|
|
8
|
+
export function buildImageSignature(preparedDockerfile, tools) {
|
|
9
|
+
return createHash('sha256')
|
|
10
|
+
.update(JSON.stringify({
|
|
11
|
+
dockerfile: preparedDockerfile.signature,
|
|
12
|
+
tools: imageSignatureFields(tools)
|
|
13
|
+
}))
|
|
14
|
+
.digest('hex')
|
|
15
|
+
.slice(0, 12);
|
|
16
|
+
}
|
|
17
|
+
export function buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, env = process.env, refresh = false, lastRefresh } = {}) {
|
|
18
|
+
const selectedEngine = engine ?? detectEngine({ engine: config.engine });
|
|
19
|
+
const { uid: hostUid, gid: hostGid } = resolveBuildUid({
|
|
20
|
+
engine: selectedEngine,
|
|
21
|
+
runFn,
|
|
22
|
+
runSafeFn,
|
|
23
|
+
env
|
|
24
|
+
});
|
|
25
|
+
const args = [
|
|
26
|
+
'build',
|
|
27
|
+
'-t',
|
|
28
|
+
config.imageName,
|
|
29
|
+
'--build-arg',
|
|
30
|
+
`HOST_UID=${hostUid}`,
|
|
31
|
+
'--build-arg',
|
|
32
|
+
`HOST_GID=${hostGid}`,
|
|
33
|
+
'--build-arg',
|
|
34
|
+
`AI_TOOL_PACKAGES=${toolNpmPackagesArg(tools)}`,
|
|
35
|
+
'--build-arg',
|
|
36
|
+
`AI_TOOLS_SHELL_INSTALL_B64=${toolShellInstallScriptBase64(tools)}`,
|
|
37
|
+
'--label',
|
|
38
|
+
sandboxLabel(config),
|
|
39
|
+
'--label',
|
|
40
|
+
`${sandboxImageConfigLabel(config)}=${imageSignature}`
|
|
41
|
+
];
|
|
42
|
+
if (lastRefresh !== undefined) {
|
|
43
|
+
args.push('--label', `${sandboxImageRefreshLabel(config)}=${lastRefresh}`);
|
|
44
|
+
}
|
|
45
|
+
args.push('-f', toEnginePath(selectedEngine, dockerfilePath), toEnginePath(selectedEngine, config.repoRoot));
|
|
46
|
+
if (refresh) {
|
|
47
|
+
args.splice(1, 0, '--no-cache', '--pull');
|
|
48
|
+
}
|
|
49
|
+
return args;
|
|
50
|
+
}
|
|
51
|
+
export function parseRefreshTimestamp(value) {
|
|
52
|
+
const parsed = Number(value);
|
|
53
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : 0;
|
|
54
|
+
}
|
|
55
|
+
export function parseImageLabels(raw) {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === 'string'));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function isRefreshDisabled(env, noRefreshFlag) {
|
|
68
|
+
if (noRefreshFlag) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const value = env.AI_SANDBOX_NO_REFRESH?.trim().toLowerCase();
|
|
72
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
73
|
+
}
|
|
74
|
+
export function isRefreshDue(lastRefresh, now, intervalDays) {
|
|
75
|
+
if (intervalDays <= 0 || lastRefresh > now) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return now - lastRefresh >= intervalDays * 24 * 60 * 60 * 1000;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=image-build.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
|
-
import { sandboxLabel } from
|
|
3
|
-
import { runEngine } from
|
|
2
|
+
import { sandboxLabel } from './constants.js';
|
|
3
|
+
import { runEngine } from './shell.js';
|
|
4
4
|
export function pruneSandboxDanglingImages(config, engine) {
|
|
5
5
|
try {
|
|
6
6
|
runEngine(engine, 'docker', [
|
|
@@ -37,12 +37,12 @@ export async function runSandbox(args) {
|
|
|
37
37
|
}
|
|
38
38
|
switch (subcommand) {
|
|
39
39
|
case 'create': {
|
|
40
|
-
const { create } = await import(
|
|
40
|
+
const { create } = await import('./commands/create.js');
|
|
41
41
|
await create(rest);
|
|
42
42
|
break;
|
|
43
43
|
}
|
|
44
44
|
case 'exec': {
|
|
45
|
-
const { enter } = await import(
|
|
45
|
+
const { enter } = await import('./commands/enter.js');
|
|
46
46
|
const exitCode = await enter(rest);
|
|
47
47
|
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
48
48
|
process.exitCode = exitCode;
|
|
@@ -50,27 +50,27 @@ export async function runSandbox(args) {
|
|
|
50
50
|
break;
|
|
51
51
|
}
|
|
52
52
|
case 'ls': {
|
|
53
|
-
const { ls } = await import(
|
|
53
|
+
const { ls } = await import('./commands/ls.js');
|
|
54
54
|
ls(rest);
|
|
55
55
|
break;
|
|
56
56
|
}
|
|
57
57
|
case 'show': {
|
|
58
|
-
const { show } = await import(
|
|
58
|
+
const { show } = await import('./commands/show.js');
|
|
59
59
|
show(rest);
|
|
60
60
|
break;
|
|
61
61
|
}
|
|
62
62
|
case 'prune': {
|
|
63
|
-
const { prune } = await import(
|
|
63
|
+
const { prune } = await import('./commands/prune.js');
|
|
64
64
|
await prune(rest);
|
|
65
65
|
break;
|
|
66
66
|
}
|
|
67
67
|
case 'rebuild': {
|
|
68
|
-
const { rebuild } = await import(
|
|
68
|
+
const { rebuild } = await import('./commands/rebuild.js');
|
|
69
69
|
await rebuild(rest);
|
|
70
70
|
break;
|
|
71
71
|
}
|
|
72
72
|
case 'refresh': {
|
|
73
|
-
const { refresh } = await import(
|
|
73
|
+
const { refresh } = await import('./commands/refresh.js');
|
|
74
74
|
const exitCode = await refresh(rest);
|
|
75
75
|
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
76
76
|
process.exitCode = exitCode;
|
|
@@ -78,17 +78,17 @@ export async function runSandbox(args) {
|
|
|
78
78
|
break;
|
|
79
79
|
}
|
|
80
80
|
case 'rm': {
|
|
81
|
-
const { rm } = await import(
|
|
81
|
+
const { rm } = await import('./commands/rm.js');
|
|
82
82
|
await rm(rest);
|
|
83
83
|
break;
|
|
84
84
|
}
|
|
85
85
|
case 'start': {
|
|
86
|
-
const { start } = await import(
|
|
86
|
+
const { start } = await import('./commands/start.js');
|
|
87
87
|
await start(rest);
|
|
88
88
|
break;
|
|
89
89
|
}
|
|
90
90
|
case 'vm': {
|
|
91
|
-
const { vm } = await import(
|
|
91
|
+
const { vm } = await import('./commands/vm.js');
|
|
92
92
|
await vm(rest);
|
|
93
93
|
break;
|
|
94
94
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { run } from
|
|
3
|
+
import { run } from './shell.js';
|
|
4
4
|
export function assertManagedPath(root, target) {
|
|
5
5
|
const resolvedRoot = path.resolve(root);
|
|
6
6
|
const resolvedTarget = path.resolve(target);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { shareBranchDir, shareCommonDir } from
|
|
3
|
+
import { shareBranchDir, shareCommonDir } from './constants.js';
|
|
4
4
|
const DOTFILES_README = `# User-level dotfiles channel
|
|
5
5
|
|
|
6
6
|
This directory is mounted **read-only** into every sandbox container at
|
|
@@ -12,6 +12,9 @@ RUN if [ "${HOST_UID}" = "0" ]; then \
|
|
|
12
12
|
(groupadd -o -g ${HOST_GID} devuser || true) && \
|
|
13
13
|
useradd -o -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser; \
|
|
14
14
|
else \
|
|
15
|
+
if ubuntu_uid="$(id -u ubuntu 2>/dev/null)" && [ "${ubuntu_uid}" = "${HOST_UID}" ]; then \
|
|
16
|
+
userdel -r ubuntu || exit 1; \
|
|
17
|
+
fi; \
|
|
15
18
|
(groupadd -g ${HOST_GID} devuser || true) && \
|
|
16
19
|
useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser; \
|
|
17
20
|
fi
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { getAdapter } from './engines/index.js';
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
5
6
|
function normalizeOptions(opts = {}, stdio) {
|
|
6
7
|
return {
|
|
@@ -114,6 +115,12 @@ export function commandForEngine(engine, cmd, args = []) {
|
|
|
114
115
|
const resolvedWrapper = resolveCommand('wsl.exe');
|
|
115
116
|
return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
|
|
116
117
|
}
|
|
118
|
+
if (cmd === 'docker') {
|
|
119
|
+
const context = getAdapter(engine).dockerContext;
|
|
120
|
+
if (context) {
|
|
121
|
+
return { cmd, args: ['--context', context, ...args] };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
117
124
|
return { cmd, args };
|
|
118
125
|
}
|
|
119
126
|
export function runEngine(engine, cmd, args, opts = {}) {
|
|
@@ -122,7 +129,8 @@ export function runEngine(engine, cmd, args, opts = {}) {
|
|
|
122
129
|
}
|
|
123
130
|
export function execEngine(engine, cmd, args, opts = {}) {
|
|
124
131
|
const command = commandForEngine(engine, cmd, args);
|
|
125
|
-
|
|
132
|
+
const resolved = resolveCommand(command.cmd);
|
|
133
|
+
return execFileSync(resolved, command.args, commandOptions(resolved, opts));
|
|
126
134
|
}
|
|
127
135
|
export function runOkEngine(engine, cmd, args, opts = {}) {
|
|
128
136
|
const command = commandForEngine(engine, cmd, args);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { safeNameCandidates, sanitizeBranchName } from
|
|
2
|
-
import { hostJoin } from
|
|
1
|
+
import { safeNameCandidates, sanitizeBranchName } from './constants.js';
|
|
2
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
3
3
|
const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
4
4
|
function createBuiltinTools(home, project) {
|
|
5
5
|
return {
|
|
@@ -44,8 +44,8 @@ function createBuiltinTools(home, project) {
|
|
|
44
44
|
// codex churns ~/.codex/logs_2.sqlite heavily (upstream openai/codex#24275);
|
|
45
45
|
// a bind-mount would write-amplify onto the host SSD via virtiofs. Mount the
|
|
46
46
|
// codex home as tmpfs so those logs stay in RAM and die with the container.
|
|
47
|
-
//
|
|
48
|
-
//
|
|
47
|
+
// Seeded config (config.toml, model-catalogs) is copied into the tmpfs at
|
|
48
|
+
// startup; runtime files like logs_2.sqlite stay in RAM.
|
|
49
49
|
tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'] },
|
|
50
50
|
hostLiveMounts: [
|
|
51
51
|
{ hostPath: hostJoin(home, '.codex', 'auth.json'), containerSubpath: 'auth.json' }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderFeishuMessage } from './renderer.js';
|
|
2
|
+
import { createFeishuTransport, normalizeMessage } from './transport.js';
|
|
2
3
|
// Assemble the feishu adapter. The transport is injectable so unit tests can
|
|
3
4
|
// drive start/dispatch/reply/sendMessage against a fake transport without the
|
|
4
5
|
// real SDK; production uses the default SDK-backed transport.
|
|
@@ -19,7 +20,10 @@ export function createFeishuAdapter(config, transport = createFeishuTransport(co
|
|
|
19
20
|
messageId: normalized.messageId,
|
|
20
21
|
raw: normalized.raw,
|
|
21
22
|
reply: async (text) => {
|
|
22
|
-
await transport.send(normalized.chatId,
|
|
23
|
+
await transport.send(normalized.chatId, renderFeishuMessage(text));
|
|
24
|
+
},
|
|
25
|
+
replyDisplay: async (message) => {
|
|
26
|
+
await transport.send(normalized.chatId, renderFeishuMessage(message));
|
|
23
27
|
}
|
|
24
28
|
};
|
|
25
29
|
await adapterCtx.dispatch(message);
|
|
@@ -33,7 +37,10 @@ export function createFeishuAdapter(config, transport = createFeishuTransport(co
|
|
|
33
37
|
await transport.stop();
|
|
34
38
|
},
|
|
35
39
|
async sendMessage(target, text) {
|
|
36
|
-
await transport.send(target.chatId,
|
|
40
|
+
await transport.send(target.chatId, renderFeishuMessage(text));
|
|
41
|
+
},
|
|
42
|
+
async sendDisplayMessage(target, message) {
|
|
43
|
+
await transport.send(target.chatId, renderFeishuMessage(message));
|
|
37
44
|
}
|
|
38
45
|
};
|
|
39
46
|
}
|