@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 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.123",
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), { packageDir, packageVersion: packageJson.version, webAppUrl: webLaunch.ok ? webLaunch.url : "" });
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.",
@@ -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 || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
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({ packageVersion = "", frame = 2, width = terminalWidth(), animated = true, language = cliLanguage, webAppUrl = "" } = {}) {
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 tagline = webAppUrl ? `webapp: ${webAppUrl}` : t("launchTagline", language);
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 ? row(centerLine(tagline, contentWidth), ansi.cyan) : "",
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"));
@@ -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