@lazyingart/agintiflow 0.20.123 → 0.20.125
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 +3 -1
- package/package.json +6 -2
- package/scripts/postinstall-webapp.js +23 -0
- package/scripts/smoke-cli-chat.js +10 -0
- package/scripts/smoke-webapp-command.js +97 -0
- package/src/cli.js +15 -1
- package/src/i18n.js +1 -0
- package/src/interactive-cli.js +65 -16
- package/src/web-autostart.js +2 -1
package/README.md
CHANGED
|
@@ -103,7 +103,9 @@ Provider signup and key pages:
|
|
|
103
103
|
| Qwen / DashScope | [https://bailian.console.aliyun.com/](https://bailian.console.aliyun.com/) | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` |
|
|
104
104
|
| GRS AI image tools | [https://grsai.ai/dashboard/api-keys](https://grsai.ai/dashboard/api-keys) | Configure with `/auxiliary grsai` or `aginti login grsai` |
|
|
105
105
|
|
|
106
|
-
The CLI auto-starts or reuses the local web UI from the same project. It tries `http://127.0.0.1:3210` first, then `3211`, `3212`, and so on if the port is already occupied. The active URL is shown in the CLI launch header.
|
|
106
|
+
The CLI quietly auto-starts or reuses the local web UI from the same project. It tries `http://127.0.0.1:3210` first, then `3211`, `3212`, and so on if the port is already occupied. The active URL is shown in the CLI launch header. If startup is blocked or unavailable, the same header row shows the recovery hint; run `/webapp [port]` inside the CLI to retry.
|
|
107
|
+
|
|
108
|
+
Package installation also makes a best-effort, non-blocking webapp initialization. Install never fails because the optional local webapp could not start.
|
|
107
109
|
|
|
108
110
|
Launch the web UI explicitly when you want a foreground web server:
|
|
109
111
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.125",
|
|
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",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"scripts/install-docker-ubuntu.sh",
|
|
51
51
|
"scripts/setup-agent-toolchain-docker.sh",
|
|
52
52
|
"scripts/real-deepseek-capabilities.js",
|
|
53
|
+
"scripts/postinstall-webapp.js",
|
|
53
54
|
"scripts/smoke-auxiliary-tools.js",
|
|
54
55
|
"scripts/smoke-auth.js",
|
|
55
56
|
"scripts/smoke-aaps-adapter.js",
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"scripts/smoke-toolchain-docker.js",
|
|
71
72
|
"scripts/smoke-web-autostart.js",
|
|
72
73
|
"scripts/smoke-web-api.js",
|
|
74
|
+
"scripts/smoke-webapp-command.js",
|
|
73
75
|
"scripts/smoke-web-port-fallback.js",
|
|
74
76
|
"src/",
|
|
75
77
|
"run.js",
|
|
@@ -103,12 +105,14 @@
|
|
|
103
105
|
"smoke:tmux-tools": "node scripts/smoke-tmux-tools.js",
|
|
104
106
|
"smoke:web-api": "node scripts/smoke-web-api.js",
|
|
105
107
|
"smoke:web-autostart": "node scripts/smoke-web-autostart.js",
|
|
108
|
+
"smoke:webapp-command": "node scripts/smoke-webapp-command.js",
|
|
106
109
|
"smoke:web-port-fallback": "node scripts/smoke-web-port-fallback.js",
|
|
107
110
|
"smoke:autoupdate": "node scripts/smoke-auto-update.js",
|
|
108
111
|
"real:deepseek": "node scripts/real-deepseek-capabilities.js",
|
|
112
|
+
"postinstall": "node scripts/postinstall-webapp.js",
|
|
109
113
|
"supervision:seed": "node scripts/seed-supervised-homework.js",
|
|
110
114
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
111
|
-
"test": "npm run check && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:web-autostart && 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",
|
|
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",
|
|
112
116
|
"pack:dry-run": "npm pack --dry-run",
|
|
113
117
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
114
118
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { ensureAgintiWebApp } from "../src/web-autostart.js";
|
|
5
|
+
|
|
6
|
+
const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
8
|
+
|
|
9
|
+
if (process.env.CI === "true" || process.env.AGINTIFLOW_SKIP_POSTINSTALL_WEBAPP === "1") {
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
await ensureAgintiWebApp({
|
|
15
|
+
packageDir,
|
|
16
|
+
cwd,
|
|
17
|
+
host: process.env.AGINTI_WEB_HOST || "127.0.0.1",
|
|
18
|
+
preferredPort: process.env.AGINTI_WEB_PORT || 3210,
|
|
19
|
+
language: process.env.AGINTI_LANGUAGE || "",
|
|
20
|
+
});
|
|
21
|
+
} catch {
|
|
22
|
+
// Installation must not fail because the optional local webapp could not be started.
|
|
23
|
+
}
|
|
@@ -326,6 +326,15 @@ try {
|
|
|
326
326
|
if (!launchHeaderWithWeb.includes("webapp: http://127.0.0.1:3210")) {
|
|
327
327
|
throw new Error("launch header did not render the active webapp URL in the tagline row");
|
|
328
328
|
}
|
|
329
|
+
const launchHeaderWithWebError = buildLaunchHeaderLines({
|
|
330
|
+
width: 120,
|
|
331
|
+
packageVersion: "0.0.0",
|
|
332
|
+
animated: false,
|
|
333
|
+
webAppNotice: "webapp unavailable - use /webapp to retry; error: smoke failure",
|
|
334
|
+
}).join("\n");
|
|
335
|
+
if (!launchHeaderWithWebError.includes("webapp unavailable - use /webapp to retry")) {
|
|
336
|
+
throw new Error("launch header did not render webapp recovery advice in the tagline row");
|
|
337
|
+
}
|
|
329
338
|
if (
|
|
330
339
|
formatElapsedDuration(0) !== "00:00" ||
|
|
331
340
|
formatElapsedDuration(65_000) !== "01:05" ||
|
|
@@ -650,6 +659,7 @@ try {
|
|
|
650
659
|
!helpResult.stdout.includes("/auxiliary") ||
|
|
651
660
|
!helpResult.stdout.includes("/review") ||
|
|
652
661
|
!helpResult.stdout.includes("/scs") ||
|
|
662
|
+
!helpResult.stdout.includes("/webapp") ||
|
|
653
663
|
!helpResult.stdout.includes("/safe") ||
|
|
654
664
|
helpResult.stdout.includes("helpReview") ||
|
|
655
665
|
helpResult.stdout.includes("helpRename") ||
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const runtimeDir = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-webapp-command-"));
|
|
11
|
+
const execFile = promisify(execFileCallback);
|
|
12
|
+
|
|
13
|
+
function delay(ms) {
|
|
14
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function waitFor(predicate, child, label, output) {
|
|
18
|
+
const deadline = Date.now() + 12000;
|
|
19
|
+
while (Date.now() < deadline) {
|
|
20
|
+
if (predicate()) return true;
|
|
21
|
+
if (child.exitCode !== null) break;
|
|
22
|
+
await delay(150);
|
|
23
|
+
}
|
|
24
|
+
const { stdout, stderr } = output;
|
|
25
|
+
throw new Error(`${label} timed out. stdout=${stdout.slice(-1000)} stderr=${stderr.slice(-1000)}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function killPort(port) {
|
|
29
|
+
try {
|
|
30
|
+
const { stdout: pids } = await execFile("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"]);
|
|
31
|
+
for (const pid of pids.split(/\s+/).filter(Boolean)) {
|
|
32
|
+
try {
|
|
33
|
+
process.kill(Number(pid), "SIGTERM");
|
|
34
|
+
} catch {
|
|
35
|
+
// Ignore already-exited listeners.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// lsof may be unavailable; the temporary port listener will otherwise be reusable by later tests.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function runCase({ port, env = {}, expectHeader, label }) {
|
|
44
|
+
const output = { stdout: "", stderr: "" };
|
|
45
|
+
const child = spawn(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "chat", "--provider", "mock", "--routing", "manual", "--port", String(port)], {
|
|
46
|
+
cwd: runtimeDir,
|
|
47
|
+
env: {
|
|
48
|
+
...process.env,
|
|
49
|
+
AGINTIFLOW_NO_ANIMATION: "1",
|
|
50
|
+
AGINTIFLOW_HOME: path.join(runtimeDir, `.agintiflow-home-${label}`),
|
|
51
|
+
...env,
|
|
52
|
+
},
|
|
53
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.stdout.on("data", (chunk) => {
|
|
57
|
+
output.stdout += chunk.toString();
|
|
58
|
+
});
|
|
59
|
+
child.stderr.on("data", (chunk) => {
|
|
60
|
+
output.stderr += chunk.toString();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await waitFor(() => output.stdout.includes(expectHeader), child, `${label} launch header`, output);
|
|
65
|
+
child.stdin.write(`/webapp ${port}\n`);
|
|
66
|
+
await waitFor(() => output.stdout.includes(`webapp=http://127.0.0.1:${port}`), child, `${label} /webapp command`, output);
|
|
67
|
+
const health = await fetch(`http://127.0.0.1:${port}/health`).then((response) => response.json());
|
|
68
|
+
if (!health.ok || health.app !== "agintiflow" || Number(health.port) !== port) {
|
|
69
|
+
throw new Error(`invalid /webapp health response for ${label}: ${JSON.stringify(health)}`);
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
child.kill("SIGTERM");
|
|
73
|
+
await killPort(port);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const autoPort = 45600 + Math.floor(Math.random() * 300);
|
|
78
|
+
const manualPort = autoPort + 301;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await runCase({
|
|
82
|
+
port: autoPort,
|
|
83
|
+
expectHeader: `webapp: http://127.0.0.1:${autoPort}`,
|
|
84
|
+
label: "auto-start",
|
|
85
|
+
});
|
|
86
|
+
await runCase({
|
|
87
|
+
port: manualPort,
|
|
88
|
+
env: { AGINTIFLOW_NO_WEB_AUTO_START: "1" },
|
|
89
|
+
expectHeader: "webapp auto-start disabled - use /webapp to start manually",
|
|
90
|
+
label: "manual-disabled",
|
|
91
|
+
});
|
|
92
|
+
console.log(`webapp slash command smoke passed: http://127.0.0.1:${autoPort}, http://127.0.0.1:${manualPort}`);
|
|
93
|
+
} finally {
|
|
94
|
+
await killPort(autoPort);
|
|
95
|
+
await killPort(manualPort);
|
|
96
|
+
await fs.rm(runtimeDir, { recursive: true, force: true });
|
|
97
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -107,6 +107,14 @@ async function maybeEnsureDefaultWebApp(args = {}, { commandCwd = process.cwd()
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function webAppNotice(result) {
|
|
111
|
+
if (!result || result.ok) return "";
|
|
112
|
+
if (result.disabled) return "webapp auto-start disabled - use /webapp to start manually";
|
|
113
|
+
const error = String(result.error || "unknown").replace(/\s+/g, " ").trim();
|
|
114
|
+
const compact = error.length > 84 ? `${error.slice(0, 83)}…` : error;
|
|
115
|
+
return `webapp unavailable - use /webapp to retry; error: ${compact}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
const CLI_VALUE_OPTIONS = new Set([
|
|
111
119
|
"--port",
|
|
112
120
|
"--host",
|
|
@@ -1755,6 +1763,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1755
1763
|
packageDir,
|
|
1756
1764
|
packageVersion: packageJson.version,
|
|
1757
1765
|
webAppUrl: webLaunch.ok ? webLaunch.url : "",
|
|
1766
|
+
webAppNotice: webAppNotice(webLaunch),
|
|
1758
1767
|
});
|
|
1759
1768
|
return;
|
|
1760
1769
|
}
|
|
@@ -1783,7 +1792,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1783
1792
|
|
|
1784
1793
|
if (args.interactive || (!args.goal && !args.resume && process.stdin.isTTY)) {
|
|
1785
1794
|
const webLaunch = await maybeEnsureDefaultWebApp(args, { commandCwd });
|
|
1786
|
-
await startInteractiveCli(agentDefaults(args), {
|
|
1795
|
+
await startInteractiveCli(agentDefaults(args), {
|
|
1796
|
+
packageDir,
|
|
1797
|
+
packageVersion: packageJson.version,
|
|
1798
|
+
webAppUrl: webLaunch.ok ? webLaunch.url : "",
|
|
1799
|
+
webAppNotice: webAppNotice(webLaunch),
|
|
1800
|
+
});
|
|
1787
1801
|
return;
|
|
1788
1802
|
}
|
|
1789
1803
|
|
package/src/i18n.js
CHANGED
|
@@ -139,6 +139,7 @@ const TRANSLATIONS = {
|
|
|
139
139
|
helpSkills: "List Markdown skills selected for a topic.",
|
|
140
140
|
helpSkillMesh: "Manage strict reviewed skill sharing.",
|
|
141
141
|
helpProfile: "Set task profile, e.g. code, website, latex, maintenance.",
|
|
142
|
+
helpWebapp: "Start or reuse the local webapp and print its URL.",
|
|
142
143
|
helpWebSearch: "Enable or disable the web_search tool.",
|
|
143
144
|
helpEnableScs: "Toggle Student-Committee-Supervisor gated execution.",
|
|
144
145
|
helpScouts: "Enable parallel DeepSeek scouts and set scout count.",
|
package/src/interactive-cli.js
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
permissionModeForApprovalCategory,
|
|
44
44
|
permissionModeLabel,
|
|
45
45
|
} from "./permission-modes.js";
|
|
46
|
+
import { ensureAgintiWebApp } from "./web-autostart.js";
|
|
46
47
|
|
|
47
48
|
const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
|
|
48
49
|
const ansi = {
|
|
@@ -95,6 +96,8 @@ const SLASH_COMMANDS = [
|
|
|
95
96
|
"/skillsync",
|
|
96
97
|
"/profile",
|
|
97
98
|
"/web-search",
|
|
99
|
+
"/webapp",
|
|
100
|
+
"/web",
|
|
98
101
|
"/web-research",
|
|
99
102
|
"/image-read",
|
|
100
103
|
"/research-wrapper",
|
|
@@ -291,7 +294,23 @@ function promptViewportRows(height = terminalHeight()) {
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
function stripAnsi(value) {
|
|
294
|
-
return String(value || "")
|
|
297
|
+
return String(value || "")
|
|
298
|
+
.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "")
|
|
299
|
+
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function terminalHyperlink(url = "", text = url) {
|
|
303
|
+
if (!useColor || process.env.AGINTIFLOW_NO_HYPERLINK === "1") return String(text || "");
|
|
304
|
+
const safeUrl = String(url || "").replace(/[\x00-\x1f\x7f]/g, "");
|
|
305
|
+
if (!safeUrl) return String(text || "");
|
|
306
|
+
return `\x1b]8;;${safeUrl}\x07${text}\x1b]8;;\x07`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function linkifyTerminalUrl(line = "", url = "") {
|
|
310
|
+
const visibleLine = String(line || "");
|
|
311
|
+
const visibleUrl = String(url || "");
|
|
312
|
+
if (!visibleLine || !visibleUrl || !visibleLine.includes(visibleUrl)) return visibleLine;
|
|
313
|
+
return visibleLine.replace(visibleUrl, terminalHyperlink(visibleUrl, visibleUrl));
|
|
295
314
|
}
|
|
296
315
|
|
|
297
316
|
function charCellWidth(char = "") {
|
|
@@ -772,10 +791,19 @@ function launchTitleLines(contentWidth) {
|
|
|
772
791
|
return largeWidth <= contentWidth ? LARGE_LAUNCH_TITLE : COMPACT_LAUNCH_TITLE;
|
|
773
792
|
}
|
|
774
793
|
|
|
775
|
-
export function buildLaunchHeaderLines({
|
|
794
|
+
export function buildLaunchHeaderLines({
|
|
795
|
+
packageVersion = "",
|
|
796
|
+
frame = 2,
|
|
797
|
+
width = terminalWidth(),
|
|
798
|
+
animated = true,
|
|
799
|
+
language = cliLanguage,
|
|
800
|
+
webAppUrl = "",
|
|
801
|
+
webAppNotice = "",
|
|
802
|
+
} = {}) {
|
|
776
803
|
const subtitle = t("launchSubtitle", language);
|
|
777
804
|
const credit = t("launchCredit", language);
|
|
778
|
-
const
|
|
805
|
+
const webappLine = webAppUrl ? `webapp: ${webAppUrl}` : webAppNotice;
|
|
806
|
+
const tagline = webappLine || t("launchTagline", language);
|
|
779
807
|
const version = packageVersion ? `v${packageVersion}` : "";
|
|
780
808
|
const terminalColumns = Math.max(Number(width) || 80, 50);
|
|
781
809
|
const contentWidth = Math.min(Math.max(terminalColumns - 8, 58), 112);
|
|
@@ -794,16 +822,18 @@ export function buildLaunchHeaderLines({ packageVersion = "", frame = 2, width =
|
|
|
794
822
|
row(centerLine(subtitle, contentWidth), ansi.dim),
|
|
795
823
|
row(centerLine(credit, contentWidth), ansi.dim),
|
|
796
824
|
tagline ? mid : "",
|
|
797
|
-
tagline
|
|
825
|
+
tagline
|
|
826
|
+
? row(centerLine(linkifyTerminalUrl(compactLine(tagline, contentWidth), webAppUrl), contentWidth), webAppUrl ? ansi.cyan : ansi.yellow)
|
|
827
|
+
: "",
|
|
798
828
|
bottom,
|
|
799
829
|
].filter(Boolean);
|
|
800
830
|
const indent = " ".repeat(Math.max(Math.floor((terminalColumns - Math.max(...boxLines.map((line) => visualLength(line)))) / 2), 0));
|
|
801
831
|
return boxLines.map((line) => `${indent}${line}`);
|
|
802
832
|
}
|
|
803
833
|
|
|
804
|
-
async function renderLaunchHeader(packageVersion = "", language = cliLanguage, webAppUrl = "") {
|
|
834
|
+
async function renderLaunchHeader(packageVersion = "", language = cliLanguage, webAppUrl = "", webAppNotice = "") {
|
|
805
835
|
if (!useColor || process.env.AGINTIFLOW_NO_ANIMATION === "1") {
|
|
806
|
-
console.log(buildLaunchHeaderLines({ packageVersion, animated: false, language, webAppUrl }).join("\n"));
|
|
836
|
+
console.log(buildLaunchHeaderLines({ packageVersion, animated: false, language, webAppUrl, webAppNotice }).join("\n"));
|
|
807
837
|
return;
|
|
808
838
|
}
|
|
809
839
|
|
|
@@ -812,7 +842,7 @@ async function renderLaunchHeader(packageVersion = "", language = cliLanguage, w
|
|
|
812
842
|
let previousLineCount = 0;
|
|
813
843
|
output.write(ansi.cursorHide);
|
|
814
844
|
for (let frame = 0; frame < 18; frame += 1) {
|
|
815
|
-
const lines = [...paddingLines, ...buildLaunchHeaderLines({ packageVersion, frame, animated: true, language, webAppUrl })];
|
|
845
|
+
const lines = [...paddingLines, ...buildLaunchHeaderLines({ packageVersion, frame, animated: true, language, webAppUrl, webAppNotice })];
|
|
816
846
|
if (previousLineCount > 0) output.write(`\x1b[${previousLineCount}A`);
|
|
817
847
|
output.write(lines.map((line) => `\r${ansi.clearLine}${line}`).join("\n"));
|
|
818
848
|
output.write("\n");
|
|
@@ -849,6 +879,7 @@ function printHelp() {
|
|
|
849
879
|
` ${command("/skills [query]", "List Markdown skills selected for a topic.", "helpSkills")}`,
|
|
850
880
|
` ${command("/skillmesh [status|off|record|share|sync]", "Manage strict reviewed skill sharing.", "helpSkillMesh")}`,
|
|
851
881
|
` ${command("/profile <name>", "Set task profile, e.g. code, website, latex, maintenance.", "helpProfile")}`,
|
|
882
|
+
` ${command("/webapp [port]", "Start or reuse the local webapp and print its URL.", "helpWebapp")}`,
|
|
852
883
|
` ${command("/web-search on|off", "Enable or disable the web_search tool.", "helpWebSearch")}`,
|
|
853
884
|
` ${command("/web-research <query>", "Run a sourced web_research turn with persisted evidence.", "helpWebSearch")}`,
|
|
854
885
|
` ${command("/image-read <path> [question]", "Run read_image on a workspace screenshot/image.", "helpWebSearch")}`,
|
|
@@ -2170,6 +2201,8 @@ function createState(args = {}) {
|
|
|
2170
2201
|
dynamicStepHardCap: args.dynamicStepHardCap,
|
|
2171
2202
|
dynamicStepExtensionSize: args.dynamicStepExtensionSize,
|
|
2172
2203
|
sessionId: args.resume || "",
|
|
2204
|
+
webAppUrl: args.webAppUrl || "",
|
|
2205
|
+
webAppNotice: args.webAppNotice || "",
|
|
2173
2206
|
};
|
|
2174
2207
|
}
|
|
2175
2208
|
|
|
@@ -2975,6 +3008,28 @@ async function handleCommand(line, state, packageDir) {
|
|
|
2975
3008
|
);
|
|
2976
3009
|
return true;
|
|
2977
3010
|
}
|
|
3011
|
+
if (command === "webapp" || command === "web") {
|
|
3012
|
+
const port = Number(value) || Number(process.env.AGINTI_WEB_PORT || process.env.PORT || 3210);
|
|
3013
|
+
printSystemLine("webapp=starting");
|
|
3014
|
+
const result = await ensureAgintiWebApp({
|
|
3015
|
+
packageDir,
|
|
3016
|
+
cwd: state.commandCwd || process.cwd(),
|
|
3017
|
+
host: process.env.AGINTI_WEB_HOST || process.env.HOST || "127.0.0.1",
|
|
3018
|
+
preferredPort: port,
|
|
3019
|
+
language: state.language,
|
|
3020
|
+
respectAutoStartDisable: false,
|
|
3021
|
+
}).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), url: "" }));
|
|
3022
|
+
if (result.ok) {
|
|
3023
|
+
state.webAppUrl = result.url;
|
|
3024
|
+
state.webAppNotice = "";
|
|
3025
|
+
printSystemLine(`webapp=${result.url} ${result.reused ? "reused" : "started"}`);
|
|
3026
|
+
} else {
|
|
3027
|
+
state.webAppUrl = "";
|
|
3028
|
+
state.webAppNotice = `webapp unavailable - use /webapp to retry; error: ${compactLine(result.error || "unknown", 72)}`;
|
|
3029
|
+
printSystemLine(state.webAppNotice);
|
|
3030
|
+
}
|
|
3031
|
+
return true;
|
|
3032
|
+
}
|
|
2978
3033
|
if (command === "language" || command === "lang") {
|
|
2979
3034
|
if (!value) {
|
|
2980
3035
|
printSystemLine(tr("languageSet", { language: state.language || cliLanguage, label: languageLabel(state.language || cliLanguage) }));
|
|
@@ -3599,12 +3654,6 @@ async function handleCommand(line, state, packageDir) {
|
|
|
3599
3654
|
printAgentMessage(`initialized project=${result.projectRoot}\nAGINTI.md=${result.instructionsPath}\ntemplate=${result.template}`);
|
|
3600
3655
|
return true;
|
|
3601
3656
|
}
|
|
3602
|
-
if (command === "web") {
|
|
3603
|
-
const port = value || "3220";
|
|
3604
|
-
printAgentMessage(`Run in another terminal: aginti web --port ${port}`);
|
|
3605
|
-
return true;
|
|
3606
|
-
}
|
|
3607
|
-
|
|
3608
3657
|
const typed = `/${command}`;
|
|
3609
3658
|
const suggestions = SLASH_COMMANDS.filter((candidate) => candidate.startsWith(typed));
|
|
3610
3659
|
printAgentMessage(
|
|
@@ -3808,8 +3857,8 @@ async function runPrompt(prompt, state, packageDir, { approvalDepth = 0 } = {})
|
|
|
3808
3857
|
return queuedAfterFinish;
|
|
3809
3858
|
}
|
|
3810
3859
|
|
|
3811
|
-
export async function startInteractiveCli(args = {}, { packageDir, packageVersion, webAppUrl = "" } = {}) {
|
|
3812
|
-
const state = createState(args);
|
|
3860
|
+
export async function startInteractiveCli(args = {}, { packageDir, packageVersion, webAppUrl = "", webAppNotice = "" } = {}) {
|
|
3861
|
+
const state = createState({ ...args, webAppUrl, webAppNotice });
|
|
3813
3862
|
const detachProcessInterrupts = installProcessInterruptHandlers();
|
|
3814
3863
|
setCliLanguage(state.language);
|
|
3815
3864
|
const rl =
|
|
@@ -3822,7 +3871,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
3822
3871
|
completer: commandCompleter,
|
|
3823
3872
|
});
|
|
3824
3873
|
|
|
3825
|
-
await renderLaunchHeader(packageVersion, state.language, webAppUrl);
|
|
3874
|
+
await renderLaunchHeader(packageVersion, state.language, state.webAppUrl, state.webAppNotice);
|
|
3826
3875
|
printSystemLine(`${tr("project")}: ${process.cwd()}`);
|
|
3827
3876
|
await maybeOnboardDeepSeekKey(state);
|
|
3828
3877
|
printAgentMessage(tr("interactiveIntro"));
|
package/src/web-autostart.js
CHANGED
|
@@ -86,8 +86,9 @@ export async function ensureAgintiWebApp({
|
|
|
86
86
|
host = DEFAULT_HOST,
|
|
87
87
|
preferredPort = DEFAULT_PORT,
|
|
88
88
|
language = "",
|
|
89
|
+
respectAutoStartDisable = true,
|
|
89
90
|
} = {}) {
|
|
90
|
-
if (process.env.AGINTI_NO_WEB_AUTO_START === "1" || process.env.AGINTIFLOW_NO_WEB_AUTO_START === "1") {
|
|
91
|
+
if (respectAutoStartDisable && (process.env.AGINTI_NO_WEB_AUTO_START === "1" || process.env.AGINTIFLOW_NO_WEB_AUTO_START === "1")) {
|
|
91
92
|
return { ok: false, disabled: true, url: "" };
|
|
92
93
|
}
|
|
93
94
|
|