@lazyingart/agintiflow 0.20.124 → 0.20.126
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.md +10 -0
- package/docs/runtime-modes-and-autonomy.md +9 -0
- package/package.json +4 -2
- package/scripts/smoke-cli-chat.js +1 -0
- package/scripts/smoke-docker-command.js +42 -0
- package/src/cli.js +100 -1
- package/src/docker-setup.js +107 -0
- package/src/i18n.js +11 -0
- package/src/interactive-cli.js +51 -5
package/README.md
CHANGED
|
@@ -114,6 +114,16 @@ aginti web --port 3210
|
|
|
114
114
|
# opens http://127.0.0.1:3210, or the next available port
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
Check or prepare the Docker/LaTeX sandbox explicitly:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
aginti docker status
|
|
121
|
+
aginti docker setup
|
|
122
|
+
aginti docker install-host --yes # Ubuntu host only; explicit opt-in
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
AgInTiFlow does not silently install Docker during npm postinstall. Docker changes host services and permissions, so host installation is explicit. Normal mode can install packages and TeX/Python dependencies inside the Docker sandbox. Danger mode is trusted host mode for tasks that really need host package managers or system services.
|
|
126
|
+
|
|
117
127
|
Run without live model credentials for smoke tests:
|
|
118
128
|
|
|
119
129
|
```bash
|
|
@@ -43,6 +43,15 @@ Docker package installs are safe when they match the sandbox contract.
|
|
|
43
43
|
- OS package installs such as `apt-get install htop` affect only the current short-lived command container unless the Docker image is rebuilt.
|
|
44
44
|
- To make OS packages portable, add them to `docker/sandbox.Dockerfile` and rebuild with `scripts/setup-agent-toolchain-docker.sh`.
|
|
45
45
|
|
|
46
|
+
Use the built-in Docker setup command for the normal sandbox/toolchain path:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
aginti docker status
|
|
50
|
+
aginti docker setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`aginti docker setup` builds or verifies the companion sandbox image and checks Node, npm, Python, matplotlib/numpy, `latexmk`, `pdflatex`, git, and ripgrep inside Docker. If host Docker itself is missing, AgInTiFlow prints platform-specific install guidance. `aginti docker install-host --yes` is only an explicit opt-in helper for supported Ubuntu hosts; npm postinstall does not silently install Docker or mutate host services.
|
|
54
|
+
|
|
46
55
|
This is why a task can safely install Python packages in Docker, but cannot keep an interactive tmux server alive inside a one-shot Docker command.
|
|
47
56
|
|
|
48
57
|
## Permission Contract
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.126",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"scripts/smoke-canvas-artifacts.js",
|
|
58
58
|
"scripts/smoke-cli-chat.js",
|
|
59
59
|
"scripts/smoke-coding-tools.js",
|
|
60
|
+
"scripts/smoke-docker-command.js",
|
|
60
61
|
"scripts/smoke-dynamic-step-budget.js",
|
|
61
62
|
"scripts/smoke-capabilities.js",
|
|
62
63
|
"scripts/smoke-auto-update.js",
|
|
@@ -94,6 +95,7 @@
|
|
|
94
95
|
"smoke:auth": "node scripts/smoke-auth.js",
|
|
95
96
|
"smoke:canvas-artifacts": "node scripts/smoke-canvas-artifacts.js",
|
|
96
97
|
"smoke:cli-chat": "node scripts/smoke-cli-chat.js",
|
|
98
|
+
"smoke:docker-command": "node scripts/smoke-docker-command.js",
|
|
97
99
|
"smoke:skills": "node scripts/smoke-skills.js",
|
|
98
100
|
"smoke:skillmesh": "node scripts/smoke-skillmesh.js",
|
|
99
101
|
"smoke:toolchain-docker": "node scripts/smoke-toolchain-docker.js",
|
|
@@ -112,7 +114,7 @@
|
|
|
112
114
|
"postinstall": "node scripts/postinstall-webapp.js",
|
|
113
115
|
"supervision:seed": "node scripts/seed-supervised-homework.js",
|
|
114
116
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
115
|
-
"test": "npm run check && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:auth && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
117
|
+
"test": "npm run check && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:auth && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
116
118
|
"pack:dry-run": "npm pack --dry-run",
|
|
117
119
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
118
120
|
},
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-docker-command-"));
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const { stdout } = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "docker", "status", "--json", "--cwd", tempRoot], {
|
|
16
|
+
cwd: tempRoot,
|
|
17
|
+
env: {
|
|
18
|
+
...process.env,
|
|
19
|
+
AGINTIFLOW_NO_WEB_AUTO_START: "1",
|
|
20
|
+
},
|
|
21
|
+
timeout: 20000,
|
|
22
|
+
maxBuffer: 1024 * 1024,
|
|
23
|
+
});
|
|
24
|
+
const parsed = JSON.parse(stdout);
|
|
25
|
+
assert.equal(parsed.ok, true, "docker status command should return ok envelope");
|
|
26
|
+
assert.equal(parsed.summary.workspace, tempRoot, "docker status should honor --cwd");
|
|
27
|
+
assert.equal(typeof parsed.summary.dockerAvailable, "boolean", "docker status should include docker availability");
|
|
28
|
+
assert(parsed.summary.install?.command, "docker status should include install guidance");
|
|
29
|
+
|
|
30
|
+
const install = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "docker", "install-host", "--json"], {
|
|
31
|
+
cwd: tempRoot,
|
|
32
|
+
timeout: 20000,
|
|
33
|
+
maxBuffer: 1024 * 1024,
|
|
34
|
+
});
|
|
35
|
+
const installPlan = JSON.parse(install.stdout);
|
|
36
|
+
assert.equal(typeof installPlan.supported, "boolean", "install-host plan should state support");
|
|
37
|
+
assert(installPlan.command, "install-host plan should include a command or guidance");
|
|
38
|
+
|
|
39
|
+
console.log("docker command smoke ok");
|
|
40
|
+
} finally {
|
|
41
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
42
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -38,9 +38,11 @@ import { handleAapsCliCommand } from "./aaps-adapter.js";
|
|
|
38
38
|
import { formatInstructionTemplateList, normalizeInstructionTemplate } from "./behavior-contract.js";
|
|
39
39
|
import { applyPermissionMode, normalizePermissionMode } from "./permission-modes.js";
|
|
40
40
|
import { ensureAgintiWebApp } from "./web-autostart.js";
|
|
41
|
+
import { dockerHostInstallPlan, formatDockerSetupText, summarizeDockerSetup } from "./docker-setup.js";
|
|
41
42
|
import fs from "node:fs/promises";
|
|
42
43
|
import path from "node:path";
|
|
43
44
|
import { fileURLToPath } from "node:url";
|
|
45
|
+
import { spawn } from "node:child_process";
|
|
44
46
|
import readline from "node:readline/promises";
|
|
45
47
|
import * as readlineRaw from "node:readline";
|
|
46
48
|
|
|
@@ -716,12 +718,104 @@ function exitOnUnknownOptions(parsed) {
|
|
|
716
718
|
|
|
717
719
|
function printUsage() {
|
|
718
720
|
console.log(
|
|
719
|
-
'Usage: aginti [chat] OR aginti init [--template minimal|disciplined|coding|research|writing|design|aaps|supervision] OR aginti web [--port 3210] OR aginti update OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [-s safe|normal|danger] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--scs|--scs auto|--no-scs] [--dynamic-steps auto|on|off] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
721
|
+
'Usage: aginti [chat] OR aginti init [--template minimal|disciplined|coding|research|writing|design|aaps|supervision] OR aginti web [--port 3210] OR aginti docker [status|setup|install-host] OR aginti update OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [-s safe|normal|danger] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--scs|--scs auto|--no-scs] [--dynamic-steps auto|on|off] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
720
722
|
);
|
|
721
723
|
console.log("Permission shortcuts: -s safe asks before writes/setup; -s normal allows current-project writes and Docker setup; -s danger enables trusted host/full-access mode.");
|
|
722
724
|
console.log(`Languages: ${["en", "ja", "zh-Hans", "zh-Hant", "ko", "fr", "es", "ar", "vi", "de", "ru"].map((code) => `${code}=${languageLabel(code)}`).join(", ")}`);
|
|
723
725
|
}
|
|
724
726
|
|
|
727
|
+
function parseDockerCommandArgs(argv = [], fallbackCwd = process.cwd()) {
|
|
728
|
+
const result = {
|
|
729
|
+
action: "status",
|
|
730
|
+
json: false,
|
|
731
|
+
yes: false,
|
|
732
|
+
commandCwd: fallbackCwd,
|
|
733
|
+
};
|
|
734
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
735
|
+
const arg = argv[index];
|
|
736
|
+
if (arg === "--json") {
|
|
737
|
+
result.json = true;
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
if (arg === "--yes" || arg === "-y") {
|
|
741
|
+
result.yes = true;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (arg === "--cwd") {
|
|
745
|
+
result.commandCwd = readOption(argv, index) || result.commandCwd;
|
|
746
|
+
index += 1;
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (!arg.startsWith("-") && result.action === "status") {
|
|
750
|
+
result.action = arg;
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function runInherited(command, args = [], options = {}) {
|
|
758
|
+
return new Promise((resolve) => {
|
|
759
|
+
const child = spawn(command, args, { stdio: "inherit", ...options });
|
|
760
|
+
child.on("close", (code) => resolve(Number(code || 0)));
|
|
761
|
+
child.on("error", (error) => {
|
|
762
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
763
|
+
resolve(1);
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async function handleDockerCommand(argv = [], { commandCwd = process.cwd() } = {}) {
|
|
769
|
+
const parsed = parseDockerCommandArgs(argv, commandCwd);
|
|
770
|
+
const config = loadConfig(
|
|
771
|
+
{
|
|
772
|
+
goal: "docker setup",
|
|
773
|
+
commandCwd: parsed.commandCwd,
|
|
774
|
+
sandboxMode: "docker-workspace",
|
|
775
|
+
packageInstallPolicy: "allow",
|
|
776
|
+
provider: "mock",
|
|
777
|
+
routingMode: "manual",
|
|
778
|
+
},
|
|
779
|
+
{ packageDir, baseDir: parsed.commandCwd }
|
|
780
|
+
);
|
|
781
|
+
const action = String(parsed.action || "status").toLowerCase();
|
|
782
|
+
|
|
783
|
+
if (["install", "install-host", "host-install"].includes(action)) {
|
|
784
|
+
const plan = dockerHostInstallPlan(packageDir);
|
|
785
|
+
if (parsed.json) {
|
|
786
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (!plan.supported) {
|
|
790
|
+
console.log(formatDockerSetupText(summarizeDockerSetup({ status: await getDockerSandboxStatus(config), packageDir })));
|
|
791
|
+
console.log("");
|
|
792
|
+
console.log("Host Docker auto-install is intentionally not run on this platform.");
|
|
793
|
+
console.log(`Recommended path: ${plan.command}`);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (!parsed.yes) {
|
|
797
|
+
console.log("Host Docker install requires explicit confirmation because it changes system packages/services.");
|
|
798
|
+
console.log(`Run: aginti docker install-host --yes`);
|
|
799
|
+
console.log(`Script: ${plan.command}`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const exitCode = await runInherited("bash", [plan.command], { cwd: packageDir });
|
|
803
|
+
process.exitCode = exitCode;
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const buildImage = ["setup", "build", "preflight", "latex", "toolchain"].includes(action);
|
|
808
|
+
const result = buildImage
|
|
809
|
+
? await runDockerPreflight(config, { buildImage: true })
|
|
810
|
+
: { ok: true, status: await getDockerSandboxStatus(config), checks: [] };
|
|
811
|
+
const summary = summarizeDockerSetup({ status: result.status, preflight: buildImage ? result : null, packageDir });
|
|
812
|
+
if (parsed.json) {
|
|
813
|
+
console.log(JSON.stringify({ ok: buildImage ? result.ok : true, summary, result }, null, 2));
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
console.log(formatDockerSetupText(summary));
|
|
817
|
+
}
|
|
818
|
+
|
|
725
819
|
function stripLeadingGlobalOptions(argv = []) {
|
|
726
820
|
let index = 0;
|
|
727
821
|
const options = {
|
|
@@ -1607,6 +1701,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1607
1701
|
return;
|
|
1608
1702
|
}
|
|
1609
1703
|
|
|
1704
|
+
if (commandArgv[0] === "docker") {
|
|
1705
|
+
await handleDockerCommand(commandArgv.slice(1), { commandCwd });
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1610
1709
|
if (commandArgv.includes("--remove-empty-sessions") || commandArgv[0] === "remove-empty-sessions") {
|
|
1611
1710
|
await handleRemoveSessionsCommand({ emptyOnly: true });
|
|
1612
1711
|
return;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { platformInfo, platformLabel, platformSetupHints } from "./platform.js";
|
|
4
|
+
|
|
5
|
+
function latexStatusFromChecks(checks = []) {
|
|
6
|
+
const latexChecks = checks.filter((check) => /latexmk|pdflatex/i.test(check.command || ""));
|
|
7
|
+
if (!latexChecks.length) return "unchecked";
|
|
8
|
+
return latexChecks.every((check) => check.ok) ? "ready" : "missing";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function dockerHostInstallPlan(packageDir = process.cwd(), info = platformInfo()) {
|
|
12
|
+
const hints = platformSetupHints(info);
|
|
13
|
+
const script = path.join(packageDir, "scripts", "install-docker-ubuntu.sh");
|
|
14
|
+
if (info.isMac) {
|
|
15
|
+
return {
|
|
16
|
+
supported: false,
|
|
17
|
+
platform: platformLabel(info),
|
|
18
|
+
command: "Install Docker Desktop or Colima, then run: aginti docker setup",
|
|
19
|
+
hints,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (info.isWindows && !info.isWsl) {
|
|
23
|
+
return {
|
|
24
|
+
supported: false,
|
|
25
|
+
platform: platformLabel(info),
|
|
26
|
+
command: "Use WSL2 with Docker Desktop WSL integration, then run: aginti docker setup inside WSL",
|
|
27
|
+
hints,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (info.isLinux && info.linuxFamily === "debian" && /ubuntu/i.test(`${info.linuxId} ${info.distro}`)) {
|
|
31
|
+
return {
|
|
32
|
+
supported: true,
|
|
33
|
+
platform: platformLabel(info),
|
|
34
|
+
command: script,
|
|
35
|
+
hints,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
supported: false,
|
|
40
|
+
platform: platformLabel(info),
|
|
41
|
+
command: "Install a Docker-compatible engine with your OS package manager, then run: aginti docker setup",
|
|
42
|
+
hints,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function summarizeDockerSetup({ status, preflight = null, packageDir = process.cwd() } = {}) {
|
|
47
|
+
const info = platformInfo();
|
|
48
|
+
const install = dockerHostInstallPlan(packageDir, info);
|
|
49
|
+
const checks = preflight?.checks || [];
|
|
50
|
+
const failedChecks = checks.filter((check) => !check.ok);
|
|
51
|
+
const latex = latexStatusFromChecks(checks);
|
|
52
|
+
return {
|
|
53
|
+
platform: platformLabel(info),
|
|
54
|
+
sandboxMode: status?.sandboxMode || "unknown",
|
|
55
|
+
packageInstallPolicy: status?.packageInstallPolicy || "unknown",
|
|
56
|
+
dockerAvailable: Boolean(status?.dockerAvailable),
|
|
57
|
+
image: status?.image || "agintiflow-sandbox:latest",
|
|
58
|
+
imageReady: Boolean(status?.imageReady),
|
|
59
|
+
dockerfileExists: Boolean(status?.dockerfileExists),
|
|
60
|
+
workspace: status?.workspace || process.cwd(),
|
|
61
|
+
persistentDocker: status?.persistentDocker || {},
|
|
62
|
+
latex,
|
|
63
|
+
preflightOk: preflight ? Boolean(preflight.ok && failedChecks.length === 0) : undefined,
|
|
64
|
+
failedChecks: failedChecks.map((check) => check.command || check.error || "unknown"),
|
|
65
|
+
install,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function formatDockerSetupText(summary = {}) {
|
|
70
|
+
const lines = [
|
|
71
|
+
"AgInTiFlow Docker setup",
|
|
72
|
+
`platform=${summary.platform || `${os.type()} (${process.arch})`}`,
|
|
73
|
+
`sandbox=${summary.sandboxMode || "unknown"} installs=${summary.packageInstallPolicy || "unknown"}`,
|
|
74
|
+
`docker=${summary.dockerAvailable ? "available" : "missing"} image=${summary.image || "agintiflow-sandbox:latest"} ${
|
|
75
|
+
summary.imageReady ? "ready" : "not-ready"
|
|
76
|
+
}`,
|
|
77
|
+
`dockerfile=${summary.dockerfileExists ? "present" : "missing"}`,
|
|
78
|
+
`workspace=${summary.workspace || process.cwd()}`,
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
if (summary.persistentDocker?.root || summary.persistentDocker?.env) {
|
|
82
|
+
lines.push(`persistent=${summary.persistentDocker.root || path.dirname(summary.persistentDocker.env)}`);
|
|
83
|
+
}
|
|
84
|
+
if (summary.latex && summary.latex !== "unchecked") lines.push(`latex=${summary.latex}`);
|
|
85
|
+
if (summary.preflightOk !== undefined) lines.push(`preflight=${summary.preflightOk ? "passed" : "failed"}`);
|
|
86
|
+
if (summary.failedChecks?.length) {
|
|
87
|
+
lines.push(`failedChecks=${summary.failedChecks.join(", ")}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
lines.push("");
|
|
91
|
+
if (!summary.dockerAvailable) {
|
|
92
|
+
lines.push("Docker is not available.");
|
|
93
|
+
lines.push(`Install path: ${summary.install?.command || "Install Docker, then run: aginti docker setup"}`);
|
|
94
|
+
} else if (!summary.imageReady || summary.preflightOk === false) {
|
|
95
|
+
lines.push("Next: aginti docker setup");
|
|
96
|
+
} else {
|
|
97
|
+
lines.push("Docker sandbox is ready for normal-mode package setup and LaTeX/PDF work.");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (summary.install?.hints?.length) {
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("Platform notes:");
|
|
103
|
+
for (const hint of summary.install.hints) lines.push(`- ${hint}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return lines.join("\n");
|
|
107
|
+
}
|
package/src/i18n.js
CHANGED
|
@@ -146,6 +146,7 @@ const TRANSLATIONS = {
|
|
|
146
146
|
helpRouting: "Set routing: smart, fast, complex, manual.",
|
|
147
147
|
helpProvider: "Open provider selector, or set deepseek/openai/qwen/venice/mock.",
|
|
148
148
|
helpPermissionMode: "Switch safe/normal/danger permission posture for this session.",
|
|
149
|
+
helpDocker: "Inspect or prepare the Docker sandbox/toolchain.",
|
|
149
150
|
helpDockerOn: "Use docker-workspace with approved package installs.",
|
|
150
151
|
helpDockerOff: "Use host shell policy.",
|
|
151
152
|
helpLatex: "Use the LaTeX/PDF profile in Docker with a larger step budget.",
|
|
@@ -197,6 +198,7 @@ const TRANSLATIONS = {
|
|
|
197
198
|
helpScouts: "並列 DeepSeek スカウトを有効化し数を設定します。",
|
|
198
199
|
helpRouting: "routing を smart、fast、complex、manual に設定します。",
|
|
199
200
|
helpProvider: "プロバイダ選択を開くか deepseek/openai/qwen/venice/mock を設定します。",
|
|
201
|
+
helpDocker: "Docker サンドボックス/ツールチェーンを確認または準備します。",
|
|
200
202
|
helpDockerOn: "承認済みパッケージインストール付き docker-workspace を使います。",
|
|
201
203
|
helpDockerOff: "ホストシェルポリシーを使います。",
|
|
202
204
|
helpLatex: "Docker で LaTeX/PDF プロファイルと大きめのステップ数を使います。",
|
|
@@ -248,6 +250,7 @@ const TRANSLATIONS = {
|
|
|
248
250
|
helpScouts: "启用并设置并行 DeepSeek scouts 数量。",
|
|
249
251
|
helpRouting: "设置 routing: smart、fast、complex、manual。",
|
|
250
252
|
helpProvider: "打开 provider 选择器,或设置 deepseek/openai/qwen/venice/mock。",
|
|
253
|
+
helpDocker: "检查或准备 Docker 沙箱/工具链。",
|
|
251
254
|
helpDockerOn: "使用允许安装包的 docker-workspace。",
|
|
252
255
|
helpDockerOff: "使用主机 shell 策略。",
|
|
253
256
|
helpLatex: "在 Docker 中使用 LaTeX/PDF profile 和更大步骤数。",
|
|
@@ -299,6 +302,7 @@ const TRANSLATIONS = {
|
|
|
299
302
|
helpScouts: "啟用並設定並行 DeepSeek scouts 數量。",
|
|
300
303
|
helpRouting: "設定 routing: smart、fast、complex、manual。",
|
|
301
304
|
helpProvider: "開啟 provider 選擇器,或設定 deepseek/openai/qwen/venice/mock。",
|
|
305
|
+
helpDocker: "檢查或準備 Docker 沙箱/工具鏈。",
|
|
302
306
|
helpDockerOn: "使用允許安裝套件的 docker-workspace。",
|
|
303
307
|
helpDockerOff: "使用主機 shell 策略。",
|
|
304
308
|
helpLatex: "在 Docker 中使用 LaTeX/PDF profile 和較大步數。",
|
|
@@ -353,6 +357,7 @@ const FALLBACKS = {
|
|
|
353
357
|
helpScouts: "병렬 DeepSeek 스카우트를 켜고 수를 설정합니다.",
|
|
354
358
|
helpRouting: "routing 을 smart, fast, complex, manual 로 설정합니다.",
|
|
355
359
|
helpProvider: "provider 선택기를 열거나 deepseek/openai/qwen/venice/mock 을 설정합니다.",
|
|
360
|
+
helpDocker: "Docker 샌드박스/툴체인을 확인하거나 준비합니다.",
|
|
356
361
|
helpDockerOn: "승인된 패키지 설치가 가능한 docker-workspace 를 사용합니다.",
|
|
357
362
|
helpDockerOff: "호스트 shell 정책을 사용합니다.",
|
|
358
363
|
helpLatex: "Docker 에서 LaTeX/PDF 프로필과 더 큰 단계 예산을 사용합니다.",
|
|
@@ -404,6 +409,7 @@ const FALLBACKS = {
|
|
|
404
409
|
helpScouts: "Activer les scouts DeepSeek parallèles et régler leur nombre.",
|
|
405
410
|
helpRouting: "Définir le routage: smart, fast, complex, manual.",
|
|
406
411
|
helpProvider: "Ouvrir le sélecteur provider, ou définir deepseek/openai/qwen/venice/mock.",
|
|
412
|
+
helpDocker: "Inspecter ou préparer le sandbox/la toolchain Docker.",
|
|
407
413
|
helpDockerOn: "Utiliser docker-workspace avec installations de paquets approuvées.",
|
|
408
414
|
helpDockerOff: "Utiliser la politique shell hôte.",
|
|
409
415
|
helpLatex: "Utiliser le profil LaTeX/PDF dans Docker avec plus d'étapes.",
|
|
@@ -455,6 +461,7 @@ const FALLBACKS = {
|
|
|
455
461
|
helpScouts: "Activar scouts DeepSeek paralelos y configurar cantidad.",
|
|
456
462
|
helpRouting: "Configurar routing: smart, fast, complex, manual.",
|
|
457
463
|
helpProvider: "Abrir selector provider, o configurar deepseek/openai/qwen/venice/mock.",
|
|
464
|
+
helpDocker: "Inspeccionar o preparar el sandbox/toolchain Docker.",
|
|
458
465
|
helpDockerOn: "Usar docker-workspace con instalaciones aprobadas.",
|
|
459
466
|
helpDockerOff: "Usar política de shell host.",
|
|
460
467
|
helpLatex: "Usar perfil LaTeX/PDF en Docker con más pasos.",
|
|
@@ -506,6 +513,7 @@ const FALLBACKS = {
|
|
|
506
513
|
helpScouts: "تفعيل كشافات DeepSeek المتوازية وتحديد عددها.",
|
|
507
514
|
helpRouting: "تعيين routing: smart أو fast أو complex أو manual.",
|
|
508
515
|
helpProvider: "فتح محدد provider أو تعيين deepseek/openai/qwen/venice/mock.",
|
|
516
|
+
helpDocker: "فحص أو تجهيز صندوق Docker والأدوات.",
|
|
509
517
|
helpDockerOn: "استخدام docker-workspace مع تثبيت حزم معتمد.",
|
|
510
518
|
helpDockerOff: "استخدام سياسة shell المضيف.",
|
|
511
519
|
helpLatex: "استخدام ملف LaTeX/PDF في Docker مع ميزانية خطوات أكبر.",
|
|
@@ -557,6 +565,7 @@ const FALLBACKS = {
|
|
|
557
565
|
helpScouts: "Bật DeepSeek scouts song song và đặt số lượng.",
|
|
558
566
|
helpRouting: "Đặt routing: smart, fast, complex, manual.",
|
|
559
567
|
helpProvider: "Mở bộ chọn provider, hoặc đặt deepseek/openai/qwen/venice/mock.",
|
|
568
|
+
helpDocker: "Kiểm tra hoặc chuẩn bị sandbox/toolchain Docker.",
|
|
560
569
|
helpDockerOn: "Dùng docker-workspace với cài package đã duyệt.",
|
|
561
570
|
helpDockerOff: "Dùng chính sách shell host.",
|
|
562
571
|
helpLatex: "Dùng profile LaTeX/PDF trong Docker với ngân sách bước lớn hơn.",
|
|
@@ -608,6 +617,7 @@ const FALLBACKS = {
|
|
|
608
617
|
helpScouts: "Parallele DeepSeek-Scouts aktivieren und Anzahl setzen.",
|
|
609
618
|
helpRouting: "Routing setzen: smart, fast, complex, manual.",
|
|
610
619
|
helpProvider: "Provider-Auswahl öffnen oder deepseek/openai/qwen/venice/mock setzen.",
|
|
620
|
+
helpDocker: "Docker-Sandbox/Toolchain prüfen oder vorbereiten.",
|
|
611
621
|
helpDockerOn: "docker-workspace mit genehmigten Paketinstallationen verwenden.",
|
|
612
622
|
helpDockerOff: "Host-Shell-Policy verwenden.",
|
|
613
623
|
helpLatex: "LaTeX/PDF-Profil in Docker mit größerem Schrittbudget verwenden.",
|
|
@@ -659,6 +669,7 @@ const FALLBACKS = {
|
|
|
659
669
|
helpScouts: "Включить параллельных DeepSeek scouts и задать количество.",
|
|
660
670
|
helpRouting: "Задать routing: smart, fast, complex, manual.",
|
|
661
671
|
helpProvider: "Открыть выбор provider или задать deepseek/openai/qwen/venice/mock.",
|
|
672
|
+
helpDocker: "Проверить или подготовить Docker sandbox/toolchain.",
|
|
662
673
|
helpDockerOn: "Использовать docker-workspace с одобренными установками пакетов.",
|
|
663
674
|
helpDockerOff: "Использовать политику host shell.",
|
|
664
675
|
helpLatex: "Использовать профиль LaTeX/PDF в Docker с большим числом шагов.",
|
package/src/interactive-cli.js
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
sessionStoreOptions,
|
|
14
14
|
} from "./project.js";
|
|
15
15
|
import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
|
|
16
|
+
import { getDockerSandboxStatus, runDockerPreflight } from "./docker-sandbox.js";
|
|
17
|
+
import { formatDockerSetupText, summarizeDockerSetup } from "./docker-setup.js";
|
|
16
18
|
import { defaultMaxStepsForProfile, normalizeTaskProfile } from "./task-profiles.js";
|
|
17
19
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
18
20
|
import { normalizeAuthProvider, runAuthWizard, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
@@ -294,7 +296,23 @@ function promptViewportRows(height = terminalHeight()) {
|
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
function stripAnsi(value) {
|
|
297
|
-
return String(value || "")
|
|
299
|
+
return String(value || "")
|
|
300
|
+
.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "")
|
|
301
|
+
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function terminalHyperlink(url = "", text = url) {
|
|
305
|
+
if (!useColor || process.env.AGINTIFLOW_NO_HYPERLINK === "1") return String(text || "");
|
|
306
|
+
const safeUrl = String(url || "").replace(/[\x00-\x1f\x7f]/g, "");
|
|
307
|
+
if (!safeUrl) return String(text || "");
|
|
308
|
+
return `\x1b]8;;${safeUrl}\x07${text}\x1b]8;;\x07`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function linkifyTerminalUrl(line = "", url = "") {
|
|
312
|
+
const visibleLine = String(line || "");
|
|
313
|
+
const visibleUrl = String(url || "");
|
|
314
|
+
if (!visibleLine || !visibleUrl || !visibleLine.includes(visibleUrl)) return visibleLine;
|
|
315
|
+
return visibleLine.replace(visibleUrl, terminalHyperlink(visibleUrl, visibleUrl));
|
|
298
316
|
}
|
|
299
317
|
|
|
300
318
|
function charCellWidth(char = "") {
|
|
@@ -806,7 +824,9 @@ export function buildLaunchHeaderLines({
|
|
|
806
824
|
row(centerLine(subtitle, contentWidth), ansi.dim),
|
|
807
825
|
row(centerLine(credit, contentWidth), ansi.dim),
|
|
808
826
|
tagline ? mid : "",
|
|
809
|
-
tagline
|
|
827
|
+
tagline
|
|
828
|
+
? row(centerLine(linkifyTerminalUrl(compactLine(tagline, contentWidth), webAppUrl), contentWidth), webAppUrl ? ansi.cyan : ansi.yellow)
|
|
829
|
+
: "",
|
|
810
830
|
bottom,
|
|
811
831
|
].filter(Boolean);
|
|
812
832
|
const indent = " ".repeat(Math.max(Math.floor((terminalColumns - Math.max(...boxLines.map((line) => visualLength(line)))) / 2), 0));
|
|
@@ -871,8 +891,7 @@ function printHelp() {
|
|
|
871
891
|
` ${command("/routing <mode>", "Set routing: smart, fast, complex, manual.", "helpRouting")}`,
|
|
872
892
|
` ${command("/provider [name]", "Open provider selector, or set deepseek/openai/qwen/venice/mock.", "helpProvider")}`,
|
|
873
893
|
` ${command("/safe | /normal | /danger", "Switch permission posture for this session.", "helpPermissionMode")}`,
|
|
874
|
-
` ${command("/docker on", "
|
|
875
|
-
` ${command("/docker off", "Use host shell policy.", "helpDockerOff")}`,
|
|
894
|
+
` ${command("/docker [status|setup|on|off]", "Inspect or prepare the Docker sandbox/toolchain.", "helpDocker")}`,
|
|
876
895
|
` ${command("/latex on", "Use the LaTeX/PDF profile in Docker with a larger step budget.", "helpLatex")}`,
|
|
877
896
|
` ${command("/installs block|prompt|allow", "Set package install policy.", "helpInstalls")}`,
|
|
878
897
|
` ${command("/cwd <path>", "Change command workspace.", "helpCwd")}`,
|
|
@@ -3601,8 +3620,35 @@ async function handleCommand(line, state, packageDir) {
|
|
|
3601
3620
|
state.sandboxMode = "host";
|
|
3602
3621
|
state.packageInstallPolicy = "prompt";
|
|
3603
3622
|
printSystemLine("docker=off sandbox=host installs=prompt");
|
|
3623
|
+
} else if (value === "status" || value === "") {
|
|
3624
|
+
const config = loadConfig(
|
|
3625
|
+
{
|
|
3626
|
+
...state,
|
|
3627
|
+
goal: "docker status",
|
|
3628
|
+
commandCwd: state.commandCwd || process.cwd(),
|
|
3629
|
+
sandboxMode: state.sandboxMode || "docker-workspace",
|
|
3630
|
+
packageInstallPolicy: state.packageInstallPolicy || "allow",
|
|
3631
|
+
},
|
|
3632
|
+
{ packageDir, baseDir: state.commandCwd || process.cwd() }
|
|
3633
|
+
);
|
|
3634
|
+
const status = await getDockerSandboxStatus(config);
|
|
3635
|
+
printAgentMessage(formatDockerSetupText(summarizeDockerSetup({ status, packageDir })));
|
|
3636
|
+
} else if (value === "setup" || value === "preflight" || value === "build" || value === "latex") {
|
|
3637
|
+
const config = loadConfig(
|
|
3638
|
+
{
|
|
3639
|
+
...state,
|
|
3640
|
+
goal: "docker setup",
|
|
3641
|
+
commandCwd: state.commandCwd || process.cwd(),
|
|
3642
|
+
sandboxMode: "docker-workspace",
|
|
3643
|
+
packageInstallPolicy: "allow",
|
|
3644
|
+
},
|
|
3645
|
+
{ packageDir, baseDir: state.commandCwd || process.cwd() }
|
|
3646
|
+
);
|
|
3647
|
+
printSystemLine("docker=setup starting");
|
|
3648
|
+
const result = await runDockerPreflight(config, { buildImage: true });
|
|
3649
|
+
printAgentMessage(formatDockerSetupText(summarizeDockerSetup({ status: result.status, preflight: result, packageDir })));
|
|
3604
3650
|
} else {
|
|
3605
|
-
printAgentMessage("Usage: /docker on OR /docker off");
|
|
3651
|
+
printAgentMessage("Usage: /docker status OR /docker setup OR /docker on OR /docker off");
|
|
3606
3652
|
}
|
|
3607
3653
|
return true;
|
|
3608
3654
|
}
|