@lazyingart/agintiflow 0.20.130 → 0.20.131

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,7 @@ 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 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 by another project. The active URL is shown in the CLI launch header. If startup is blocked, stale, or unavailable, the same header row shows the recovery hint; run `/webapp [port]` inside the CLI to retry, `/webapp stop [port]` to stop the compatible local webapp, or `/webapp restart [port]` to stop and relaunch the local webapp with the current project and canonical `~/.agintiflow` session home. After a successful `aginti update` or accepted startup auto-update, AgInTiFlow also restarts the compatible local webapp so artifact serving uses the updated package.
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 by another project. The active URL is shown in the CLI launch header. If startup is blocked, stale, or unavailable, the same header row shows the recovery hint; run `/webapp [port]` inside the CLI to retry, `/webapp stop [port]` to stop the compatible local webapp, or `/webapp restart [port]` to stop and relaunch the local webapp with the current project and canonical `~/.agintiflow` session home. Use `/webapp disable` or `aginti webapp disable` to persistently disable automatic webapp startup and update-time restarts; use `/webapp enable` or `aginti webapp enable` to restore them. After a successful `aginti update` or accepted startup auto-update, AgInTiFlow restarts the compatible local webapp only when webapp auto-start is enabled.
107
107
 
108
108
  Package installation also makes a best-effort, non-blocking webapp initialization. Install never fails because the optional local webapp could not start.
109
109
 
@@ -111,6 +111,8 @@ Launch the web UI explicitly when you want a foreground web server:
111
111
 
112
112
  ```bash
113
113
  aginti webapp
114
+ aginti webapp disable
115
+ aginti webapp enable
114
116
  aginti webapp stop
115
117
  aginti webapp restart
116
118
  aginti web --port 3210
@@ -146,7 +148,7 @@ aginti --language de
146
148
  | Goal | Command |
147
149
  | --- | --- |
148
150
  | Start interactive chat | `aginti` or `aginti chat` |
149
- | Start local web app | Auto-starts with `aginti`; detached command is `aginti webapp`; stop with `aginti webapp stop`; restart with `aginti webapp restart`; foreground mode is `aginti web --port 3210` |
151
+ | Start local web app | Auto-starts with `aginti`; detached command is `aginti webapp`; disable/enable auto-start with `aginti webapp disable` / `aginti webapp enable`; stop with `aginti webapp stop`; restart with `aginti webapp restart`; foreground mode is `aginti web --port 3210` |
150
152
  | Save provider keys | `aginti auth`, `/auth`, `/login` |
151
153
  | Review current repo | `/review [focus]` |
152
154
  | Toggle SCS quality gate | `/scs` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.130",
3
+ "version": "0.20.131",
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",
@@ -14,6 +14,10 @@ function delay(ms) {
14
14
  return new Promise((resolve) => setTimeout(resolve, ms));
15
15
  }
16
16
 
17
+ function occurrenceCount(value, pattern) {
18
+ return String(value || "").split(pattern).length - 1;
19
+ }
20
+
17
21
  async function waitFor(predicate, child, label, output) {
18
22
  const deadline = Date.now() + 12000;
19
23
  while (Date.now() < deadline) {
@@ -74,6 +78,13 @@ async function runCase({ port, env = {}, expectHeader, label }) {
74
78
  if (path.resolve(health.agintiflowHome) !== path.resolve(path.join(runtimeDir, `.agintiflow-web-home-${label}`))) {
75
79
  throw new Error(`webapp command inherited the wrong home for ${label}: ${JSON.stringify(health)}`);
76
80
  }
81
+ child.stdin.write(`/webapp disable\n`);
82
+ await waitFor(() => output.stdout.includes("webapp auto-start=disabled"), child, `${label} /webapp disable command`, output);
83
+ const disabledCount = occurrenceCount(output.stdout, "webapp auto-start=disabled");
84
+ child.stdin.write(`/webapp status\n`);
85
+ await waitFor(() => occurrenceCount(output.stdout, "webapp auto-start=disabled") > disabledCount, child, `${label} /webapp status disabled command`, output);
86
+ child.stdin.write(`/webapp enable\n`);
87
+ await waitFor(() => output.stdout.includes("webapp auto-start=enabled"), child, `${label} /webapp enable command`, output);
77
88
  child.stdin.write(`/webapp stop ${port}\n`);
78
89
  await waitFor(() => output.stdout.includes(`webapp=http://127.0.0.1:${port} stopped`), child, `${label} /webapp stop command`, output);
79
90
  let stopped = false;
package/src/cli.js CHANGED
@@ -37,7 +37,7 @@ import { handleSkillMeshCommand } from "./skillmesh.js";
37
37
  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
- import { ensureAgintiWebApp, stopAgintiWebApp } from "./web-autostart.js";
40
+ import { ensureAgintiWebApp, readWebAppPreference, stopAgintiWebApp, writeWebAppPreference } from "./web-autostart.js";
41
41
  import { dockerHostInstallPlan, formatDockerSetupText, summarizeDockerSetup } from "./docker-setup.js";
42
42
  import fs from "node:fs/promises";
43
43
  import path from "node:path";
@@ -403,7 +403,7 @@ export function parseArgs(argv) {
403
403
  parts.push(...argv.slice(i + 1));
404
404
  break;
405
405
  }
406
- if ((arg === "web" || arg === "--web") && ["restart", "stop"].includes(String(argv[i + 1] || "").toLowerCase())) {
406
+ if ((arg === "web" || arg === "--web") && ["restart", "stop", "enable", "disable", "status"].includes(String(argv[i + 1] || "").toLowerCase())) {
407
407
  result.webapp = true;
408
408
  result.webAction = String(argv[i + 1] || "").toLowerCase();
409
409
  i += 1;
@@ -902,7 +902,7 @@ async function restartWebAppAfterUpdate({ commandCwd = process.cwd(), language =
902
902
  preferredPort: process.env.AGINTI_WEB_PORT || process.env.PORT || 3210,
903
903
  language,
904
904
  restart: true,
905
- respectAutoStartDisable: false,
905
+ respectAutoStartDisable: true,
906
906
  }).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), url: "" }));
907
907
  }
908
908
 
@@ -1949,8 +1949,8 @@ export async function main(argv = process.argv.slice(2)) {
1949
1949
 
1950
1950
  if (args.webapp) {
1951
1951
  const action = String(args.webAction || "start").toLowerCase();
1952
- if (!["start", "stop", "restart", "reuse"].includes(action)) {
1953
- console.error("Usage: aginti webapp [start|stop|restart|reuse] [--port 3210] [--host 127.0.0.1]");
1952
+ if (!["start", "stop", "restart", "reuse", "enable", "disable", "status"].includes(action)) {
1953
+ console.error("Usage: aginti webapp [start|stop|restart|reuse|enable|disable|status] [--port 3210] [--host 127.0.0.1]");
1954
1954
  process.exit(1);
1955
1955
  }
1956
1956
  const webOptions = {
@@ -1960,6 +1960,16 @@ export async function main(argv = process.argv.slice(2)) {
1960
1960
  host: args.host || process.env.AGINTI_WEB_HOST || "127.0.0.1",
1961
1961
  preferredPort: args.port || process.env.AGINTI_WEB_PORT || 3210,
1962
1962
  };
1963
+ if (action === "enable" || action === "disable" || action === "status") {
1964
+ const preference =
1965
+ action === "status"
1966
+ ? await readWebAppPreference({ home: webOptions.home })
1967
+ : await writeWebAppPreference({ home: webOptions.home, autoStart: action === "enable" });
1968
+ console.log(`webapp auto-start: ${preference.autoStart ? "enabled" : "disabled"}`);
1969
+ console.log(`preference: ${preference.path}`);
1970
+ if (!preference.autoStart) console.log("Use `aginti webapp stop` or `/webapp stop` to stop a currently running webapp.");
1971
+ return;
1972
+ }
1963
1973
  const result = await (action === "stop"
1964
1974
  ? stopAgintiWebApp(webOptions)
1965
1975
  : ensureAgintiWebApp({
package/src/i18n.js CHANGED
@@ -139,7 +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, reuse, stop, or restart the local webapp and print its URL.",
142
+ helpWebapp: "Start, reuse, stop, restart, or configure the local webapp.",
143
143
  helpWebSearch: "Enable or disable the web_search tool.",
144
144
  helpEnableScs: "Toggle Student-Committee-Supervisor gated execution.",
145
145
  helpScouts: "Enable parallel DeepSeek scouts and set scout count.",
@@ -45,7 +45,7 @@ import {
45
45
  permissionModeForApprovalCategory,
46
46
  permissionModeLabel,
47
47
  } from "./permission-modes.js";
48
- import { ensureAgintiWebApp, stopAgintiWebApp } from "./web-autostart.js";
48
+ import { ensureAgintiWebApp, readWebAppPreference, stopAgintiWebApp, writeWebAppPreference } from "./web-autostart.js";
49
49
 
50
50
  const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
51
51
  const ansi = {
@@ -881,7 +881,7 @@ function printHelp() {
881
881
  ` ${command("/skills [query]", "List Markdown skills selected for a topic.", "helpSkills")}`,
882
882
  ` ${command("/skillmesh [status|off|record|share|sync]", "Manage strict reviewed skill sharing.", "helpSkillMesh")}`,
883
883
  ` ${command("/profile <name>", "Set task profile, e.g. code, website, latex, maintenance.", "helpProfile")}`,
884
- ` ${command("/webapp [port|start|stop|restart|reuse]", "Start, reuse, stop, or restart the local webapp and print its URL.", "helpWebapp")}`,
884
+ ` ${command("/webapp [port|start|stop|restart|reuse|enable|disable|status]", "Start, reuse, stop, restart, or configure the local webapp.", "helpWebapp")}`,
885
885
  ` ${command("/web-search on|off", "Enable or disable the web_search tool.", "helpWebSearch")}`,
886
886
  ` ${command("/web-research <query>", "Run a sourced web_research turn with persisted evidence.", "helpWebSearch")}`,
887
887
  ` ${command("/image-read <path> [question]", "Run read_image on a workspace screenshot/image.", "helpWebSearch")}`,
@@ -3011,17 +3011,26 @@ async function handleCommand(line, state, packageDir) {
3011
3011
  }
3012
3012
  if (command === "webapp" || command === "web") {
3013
3013
  const words = value.split(/\s+/).filter(Boolean);
3014
- const action = words.find((word) => ["start", "stop", "restart", "reuse"].includes(word.toLowerCase()))?.toLowerCase() || "start";
3014
+ const action = words.find((word) => ["start", "stop", "restart", "reuse", "enable", "disable", "status"].includes(word.toLowerCase()))?.toLowerCase() || "start";
3015
3015
  const restart = words.some((word) => word.toLowerCase() === "restart");
3016
3016
  const portValue = words.find((word) => /^\d+$/.test(word));
3017
3017
  const port = Number(portValue) || Number(process.env.AGINTI_WEB_PORT || process.env.PORT || 3210);
3018
- printSystemLine(action === "stop" ? "webapp=stopping" : restart ? "webapp=restarting" : "webapp=starting");
3019
3018
  const webOptions = {
3020
3019
  packageDir,
3021
3020
  cwd: state.commandCwd || process.cwd(),
3022
3021
  host: process.env.AGINTI_WEB_HOST || process.env.HOST || "127.0.0.1",
3023
3022
  preferredPort: port,
3024
3023
  };
3024
+ if (action === "enable" || action === "disable" || action === "status") {
3025
+ const preference =
3026
+ action === "status"
3027
+ ? await readWebAppPreference()
3028
+ : await writeWebAppPreference({ autoStart: action === "enable" });
3029
+ printSystemLine(`webapp auto-start=${preference.autoStart ? "enabled" : "disabled"}`);
3030
+ if (!preference.autoStart) printSystemLine("webapp disabled for future starts; use /webapp stop to stop a running webapp");
3031
+ return true;
3032
+ }
3033
+ printSystemLine(action === "stop" ? "webapp=stopping" : restart ? "webapp=restarting" : "webapp=starting");
3025
3034
  const result = await (action === "stop"
3026
3035
  ? stopAgintiWebApp(webOptions)
3027
3036
  : ensureAgintiWebApp({
@@ -1,4 +1,5 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
+ import fs from "node:fs/promises";
2
3
  import http from "node:http";
3
4
  import net from "node:net";
4
5
  import os from "node:os";
@@ -54,6 +55,32 @@ function resolveRuntimeDir(cwd = "") {
54
55
  return path.resolve(process.env.AGINTIFLOW_WEB_RUNTIME_DIR || cwd || process.cwd());
55
56
  }
56
57
 
58
+ function webPreferencePath(home = "") {
59
+ return path.join(resolveWebHome(home), "webapp.json");
60
+ }
61
+
62
+ export async function readWebAppPreference({ home = "" } = {}) {
63
+ const file = webPreferencePath(home);
64
+ try {
65
+ const data = JSON.parse(await fs.readFile(file, "utf8"));
66
+ return { autoStart: data.autoStart !== false, path: file };
67
+ } catch {
68
+ return { autoStart: true, path: file };
69
+ }
70
+ }
71
+
72
+ export async function writeWebAppPreference({ home = "", autoStart = true } = {}) {
73
+ const file = webPreferencePath(home);
74
+ await fs.mkdir(path.dirname(file), { recursive: true });
75
+ await fs.writeFile(file, `${JSON.stringify({ autoStart: Boolean(autoStart), updatedAt: new Date().toISOString() }, null, 2)}\n`, "utf8");
76
+ return await readWebAppPreference({ home });
77
+ }
78
+
79
+ async function webAutoStartDisabled(home = "") {
80
+ if (process.env.AGINTI_NO_WEB_AUTO_START === "1" || process.env.AGINTIFLOW_NO_WEB_AUTO_START === "1") return true;
81
+ return (await readWebAppPreference({ home })).autoStart === false;
82
+ }
83
+
57
84
  function fetchHealthDetails(host, port, timeoutMs = 450) {
58
85
  return new Promise((resolve) => {
59
86
  const req = http.get(`${webUrl(host, port)}/health`, { timeout: timeoutMs }, (res) => {
@@ -274,7 +301,7 @@ export async function ensureAgintiWebApp({
274
301
  restart = false,
275
302
  respectAutoStartDisable = true,
276
303
  } = {}) {
277
- if (respectAutoStartDisable && (process.env.AGINTI_NO_WEB_AUTO_START === "1" || process.env.AGINTIFLOW_NO_WEB_AUTO_START === "1")) {
304
+ if (respectAutoStartDisable && (await webAutoStartDisabled(home))) {
278
305
  return { ok: false, disabled: true, url: "" };
279
306
  }
280
307