@lazyingart/agintiflow 0.20.14 → 0.20.16

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
@@ -21,6 +21,10 @@ AgInTiFlow is AgInTi's web-first agent platform for controlled website automatio
21
21
 
22
22
  It is designed for workflows where an AI agent should act, but every tool, log, and session state should remain inspectable.
23
23
 
24
+ <p align="center">
25
+ <img src="./demos/agintiflow-cli-launch.jpg" alt="AgInTiFlow interactive CLI launch screen with colorful terminal banner, Docker workspace status, and chat input panel" width="960" />
26
+ </p>
27
+
24
28
  ## Product Snapshot
25
29
 
26
30
  | Area | Direction |
@@ -0,0 +1,6 @@
1
+ # AgInTiFlow Demos
2
+
3
+ This folder stores shareable screenshots and visual demos used by the repository README.
4
+
5
+ - `agintiflow-cli-launch.png`: interactive CLI launch screen with the AgInTi Flow terminal banner, Docker workspace status, and input panel.
6
+ - `agintiflow-cli-launch.jpg`: optimized README/web preview derived from the same screenshot.
Binary file
@@ -46,7 +46,7 @@ Interactive commands:
46
46
  /auxiliary model grsai/nano-banana-2
47
47
  ```
48
48
 
49
- In the interactive CLI, `/provider`, `/route`, `/model`, `/spare`, and `/auxiliary model` without arguments open selectors. Use Up/Down/Left/Right to move through choices, Enter to confirm, and Esc to cancel. Slash-command hints use the same arrow selection behavior: type a prefix such as `/mo`, use arrows to choose `/model` or `/models`, then press Enter or Tab.
49
+ In the interactive CLI, `/provider`, `/route`, `/model`, `/spare`, and `/auxiliary model` without arguments open selectors. Use Up/Down/Left/Right to move through choices and Enter to confirm. Esc cancels at the top level; inside multi-level selectors it goes back one level. Slash-command hints use the same arrow selection behavior: type a prefix such as `/mo`, use arrows to choose `/model` or `/models`, then press Enter or Tab.
50
50
 
51
51
  `/route`, `/model`, and `/spare` intentionally share the same text-model selector so users do not need to learn three different catalogs. The selector is hierarchical:
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.14",
3
+ "version": "0.20.16",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a web-first coding agent and CLI with DeepSeek routing, sandboxed tools, model providers, canvas artifacts, and optional wrappers.",
6
6
  "license": "Apache-2.0",
@@ -37,6 +37,7 @@
37
37
  "bin/",
38
38
  "docs/",
39
39
  "docker/",
40
+ "demos/agintiflow-cli-launch.jpg",
40
41
  "i18n/",
41
42
  "logos/banner-opaque.png",
42
43
  "logos/logo.png",
package/public/index.html CHANGED
@@ -36,10 +36,13 @@
36
36
  <p class="subtle" data-i18n="intro">
37
37
  Browser agent with provider selection, resumable runs, persisted settings, and an optional guarded shell tool with Docker sandbox support.
38
38
  </p>
39
- <section class="project-card">
40
- <strong data-i18n="projectStatusTitle">Project folder</strong>
39
+ <details class="project-card">
40
+ <summary>
41
+ <strong data-i18n="projectStatusTitle">Project folder</strong>
42
+ <span class="project-card-toggle" aria-hidden="true"></span>
43
+ </summary>
41
44
  <p id="project-status" class="subtle">Loading project context...</p>
42
- </section>
45
+ </details>
43
46
 
44
47
  <section id="setup-card" class="setup-card" hidden>
45
48
  <div>
package/public/styles.css CHANGED
@@ -290,9 +290,7 @@ h1 {
290
290
 
291
291
  .project-card,
292
292
  .setup-card {
293
- display: grid;
294
293
  min-width: 0;
295
- gap: 10px;
296
294
  margin: 14px 0;
297
295
  padding: 13px;
298
296
  border: 1px solid rgba(15, 118, 110, 0.18);
@@ -300,11 +298,73 @@ h1 {
300
298
  background: linear-gradient(135deg, rgba(204, 251, 241, 0.44), rgba(255, 247, 237, 0.9));
301
299
  }
302
300
 
301
+ .setup-card {
302
+ display: grid;
303
+ gap: 10px;
304
+ }
305
+
306
+ .project-card {
307
+ display: block;
308
+ }
309
+
310
+ .project-card summary {
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: space-between;
314
+ gap: 12px;
315
+ min-width: 0;
316
+ list-style: none;
317
+ cursor: pointer;
318
+ }
319
+
320
+ .project-card summary::-webkit-details-marker {
321
+ display: none;
322
+ }
323
+
324
+ .project-card summary strong {
325
+ min-width: 0;
326
+ }
327
+
328
+ .project-card summary:focus-visible {
329
+ border-radius: 10px;
330
+ outline: 3px solid rgba(15, 118, 110, 0.22);
331
+ outline-offset: 4px;
332
+ }
333
+
334
+ .project-card-toggle {
335
+ display: inline-grid;
336
+ width: 30px;
337
+ height: 30px;
338
+ flex: 0 0 auto;
339
+ place-items: center;
340
+ border: 1px solid rgba(15, 118, 110, 0.22);
341
+ border-radius: 999px;
342
+ background: rgba(255, 253, 250, 0.78);
343
+ color: var(--accent);
344
+ font-size: 0.88rem;
345
+ font-weight: 900;
346
+ }
347
+
348
+ .project-card-toggle::before {
349
+ content: "⌄";
350
+ transform: rotate(-90deg);
351
+ transition: transform 160ms ease;
352
+ }
353
+
354
+ .project-card[open] .project-card-toggle::before {
355
+ transform: rotate(0deg);
356
+ }
357
+
303
358
  .project-card p,
304
359
  .setup-card p {
305
360
  overflow-wrap: anywhere;
306
361
  }
307
362
 
363
+ .project-card p {
364
+ margin: 10px 0 0;
365
+ line-height: 1.55;
366
+ }
367
+
308
368
  .setup-card .key-links {
309
369
  display: flex;
310
370
  flex-wrap: wrap;
@@ -55,6 +55,8 @@ Interactive equivalents:
55
55
 
56
56
  `/route`, `/model`, and `/spare` share a single text-model selector. The selector is grouped as DeepSeek, Venice Uncensored, Venice GPT/Claude/Gemma/Qwen, OpenAI, Qwen, and Mock so the user sees the same union of route/main/spare-capable models in every role.
57
57
 
58
+ Interactive selectors are hierarchical. Enter moves forward or confirms. Esc cancels only at the first level; at model/reasoning levels it returns to the previous selector instead of dropping the whole flow.
59
+
58
60
  `/venice` opens a route/main selector for Venice text models. The selector includes `venice/venice-uncensored-1-2` (Venice 1.2), `venice/venice-uncensored` (Venice 1.1), `venice/gemma-4-uncensored` (Gemma 4), and a Disable Venice option. In non-interactive shells, `/venice` keeps script compatibility by selecting `venice/venice-uncensored-1-2` for both roles. `/venice 1.2 gemma` sets route to Venice 1.2 and main to Gemma 4; `/venice off` or the Disable Venice selector option restores `deepseek/deepseek-v4-flash` for route and `deepseek/deepseek-v4-pro` for main.
59
61
 
60
62
  The web UI should expose model names as dropdowns, not free-text fields. The left panel should stay focused on common daily controls, while model-role editing and less-used switches live in an Advanced settings modal. The terminal-like capability panels belong after the runtime log so the left control panel remains short.
@@ -2079,7 +2079,13 @@ function renderSelector({ title, subtitle, options, selectedIndex, lineCount = 0
2079
2079
  return rows.length;
2080
2080
  }
2081
2081
 
2082
- function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
2082
+ const SELECTOR_BACK = Symbol("selector-back");
2083
+
2084
+ function isSelectorBack(value) {
2085
+ return value === SELECTOR_BACK;
2086
+ }
2087
+
2088
+ function selectModelChoice({ title, subtitle, options, initialIndex = 0, escapeValue = null }) {
2083
2089
  if (!input.isTTY || !output.isTTY || typeof input.setRawMode !== "function") return Promise.resolve(null);
2084
2090
  return new Promise((resolve) => {
2085
2091
  emitKeypressEvents(input);
@@ -2107,7 +2113,7 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
2107
2113
  return;
2108
2114
  }
2109
2115
  if (key.name === "escape") {
2110
- finish(null);
2116
+ finish(escapeValue);
2111
2117
  return;
2112
2118
  }
2113
2119
  if (key.name === "up" || key.name === "left") {
@@ -2157,73 +2163,95 @@ async function pickModelRole(role, state) {
2157
2163
  ? modelRoleChoices("auxiliary").find((item) => item.provider === currentProvider && item.model === currentModel)
2158
2164
  : modelRoleChoices(role).find((item) => item.provider === currentProvider && item.model === currentModel);
2159
2165
  const currentGroup = currentModelEntry?.group || currentProvider;
2160
- const group = await selectModelChoice({
2161
- title:
2162
- role === "route"
2163
- ? "Select route model family"
2164
- : role === "spare"
2165
- ? "Select spare model family"
2166
- : role === "auxiliary"
2167
- ? "Select auxiliary provider"
2168
- : "Select main model family",
2169
- subtitle: "First choose a provider family. Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
2170
- options: groupOptions,
2171
- initialIndex: Math.max(
2172
- groupOptions.findIndex((item) => item.groupId === currentGroup),
2173
- 0
2174
- ),
2175
- });
2176
- if (!group) return false;
2177
-
2178
- const options = role === "auxiliary" ? auxiliaryModelRoleChoices(group.groupId) : textModelRoleChoices(group.groupId);
2179
- const initialIndex = Math.max(
2180
- options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
2166
+ let selected = null;
2167
+ let selectedReasoning = "";
2168
+ let initialGroupIndex = Math.max(
2169
+ groupOptions.findIndex((item) => item.groupId === currentGroup),
2181
2170
  0
2182
2171
  );
2183
- const selected = await selectModelChoice({
2184
- title:
2185
- role === "route"
2186
- ? `Select route model: ${group.label}`
2187
- : role === "spare"
2188
- ? `Select spare model: ${group.label}`
2189
- : role === "auxiliary"
2190
- ? `Select auxiliary model: ${group.label}`
2191
- : `Select main model: ${group.label}`,
2192
- subtitle:
2193
- role === "auxiliary"
2194
- ? "Choose the image/edit auxiliary model. Enter confirms, Esc cancels."
2195
- : "Choose the text model. OpenAI choices ask for reasoning next.",
2196
- options,
2197
- initialIndex,
2198
- });
2199
- if (!selected) return false;
2200
2172
 
2201
- let selectedReasoning = selected.reasoningDefault || "";
2202
- if (Array.isArray(selected.reasoningOptions) && selected.reasoningOptions.length > 0) {
2203
- const currentReasoning =
2204
- role === "route"
2205
- ? state.routeReasoning || selectedReasoning || "medium"
2206
- : role === "spare"
2207
- ? state.spareReasoning || selectedReasoning || "medium"
2208
- : state.mainReasoning || selectedReasoning || "medium";
2209
- const reasoningOptions = selected.reasoningOptions.map((level) => ({
2210
- action: "reasoning",
2211
- label: level,
2212
- value: level,
2213
- description: `${selected.label} with ${level} reasoning`,
2214
- }));
2215
- const reasoning = await selectModelChoice({
2216
- title: `Select reasoning: ${selected.label}`,
2217
- subtitle: "Up/Down selects reasoning effort. Enter confirms, Esc cancels.",
2218
- options: reasoningOptions,
2219
- initialIndex: Math.max(
2220
- reasoningOptions.findIndex((item) => item.value === currentReasoning),
2221
- reasoningOptions.findIndex((item) => item.value === selectedReasoning),
2222
- 0
2223
- ),
2173
+ while (!selected) {
2174
+ const group = await selectModelChoice({
2175
+ title:
2176
+ role === "route"
2177
+ ? "Select route model family"
2178
+ : role === "spare"
2179
+ ? "Select spare model family"
2180
+ : role === "auxiliary"
2181
+ ? "Select auxiliary provider"
2182
+ : "Select main model family",
2183
+ subtitle: "First choose a provider family. Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
2184
+ options: groupOptions,
2185
+ initialIndex: initialGroupIndex,
2224
2186
  });
2225
- if (!reasoning) return false;
2226
- selectedReasoning = reasoning.value;
2187
+ if (!group) return false;
2188
+ initialGroupIndex = Math.max(
2189
+ groupOptions.findIndex((item) => item.groupId === group.groupId),
2190
+ 0
2191
+ );
2192
+
2193
+ const options = role === "auxiliary" ? auxiliaryModelRoleChoices(group.groupId) : textModelRoleChoices(group.groupId);
2194
+ let initialModelIndex = Math.max(
2195
+ options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
2196
+ 0
2197
+ );
2198
+
2199
+ while (!selected) {
2200
+ const modelSelection = await selectModelChoice({
2201
+ title:
2202
+ role === "route"
2203
+ ? `Select route model: ${group.label}`
2204
+ : role === "spare"
2205
+ ? `Select spare model: ${group.label}`
2206
+ : role === "auxiliary"
2207
+ ? `Select auxiliary model: ${group.label}`
2208
+ : `Select main model: ${group.label}`,
2209
+ subtitle:
2210
+ role === "auxiliary"
2211
+ ? "Choose the image/edit auxiliary model. Enter confirms, Esc goes back."
2212
+ : "Choose the text model. OpenAI choices ask for reasoning next. Esc goes back.",
2213
+ options,
2214
+ initialIndex: initialModelIndex,
2215
+ escapeValue: SELECTOR_BACK,
2216
+ });
2217
+ if (isSelectorBack(modelSelection)) break;
2218
+ if (!modelSelection) return false;
2219
+ initialModelIndex = Math.max(
2220
+ options.findIndex((item) => item.provider === modelSelection.provider && item.model === modelSelection.model),
2221
+ 0
2222
+ );
2223
+
2224
+ selectedReasoning = modelSelection.reasoningDefault || "";
2225
+ if (Array.isArray(modelSelection.reasoningOptions) && modelSelection.reasoningOptions.length > 0) {
2226
+ const currentReasoning =
2227
+ role === "route"
2228
+ ? state.routeReasoning || selectedReasoning || "medium"
2229
+ : role === "spare"
2230
+ ? state.spareReasoning || selectedReasoning || "medium"
2231
+ : state.mainReasoning || selectedReasoning || "medium";
2232
+ const reasoningOptions = modelSelection.reasoningOptions.map((level) => ({
2233
+ action: "reasoning",
2234
+ label: level,
2235
+ value: level,
2236
+ description: `${modelSelection.label} with ${level} reasoning`,
2237
+ }));
2238
+ const reasoning = await selectModelChoice({
2239
+ title: `Select reasoning: ${modelSelection.label}`,
2240
+ subtitle: "Up/Down selects reasoning effort. Enter confirms, Esc goes back.",
2241
+ options: reasoningOptions,
2242
+ initialIndex: Math.max(
2243
+ reasoningOptions.findIndex((item) => item.value === currentReasoning),
2244
+ reasoningOptions.findIndex((item) => item.value === selectedReasoning),
2245
+ 0
2246
+ ),
2247
+ escapeValue: SELECTOR_BACK,
2248
+ });
2249
+ if (isSelectorBack(reasoning)) continue;
2250
+ if (!reasoning) return false;
2251
+ selectedReasoning = reasoning.value;
2252
+ }
2253
+ selected = modelSelection;
2254
+ }
2227
2255
  }
2228
2256
 
2229
2257
  state.routingMode = "smart";
@@ -2255,44 +2283,52 @@ async function pickModelRole(role, state) {
2255
2283
 
2256
2284
  async function pickVeniceRouteAndMain(state) {
2257
2285
  const options = veniceTextModelChoices();
2258
- const routeIndex = Math.max(
2286
+ let routeIndex = Math.max(
2259
2287
  options.findIndex((item) => item.provider === state.routeProvider && item.model === state.routeModel),
2260
2288
  0
2261
2289
  );
2262
- const route = await selectModelChoice({
2263
- title: "Select Venice route model",
2264
- subtitle: "Route handles planning and short turns. Up/Down selects, Enter confirms, Esc cancels.",
2265
- options,
2266
- initialIndex: routeIndex,
2267
- });
2268
- if (!route) return false;
2269
- if (route.action === "disable") {
2270
- useDeepSeekDefaults(state);
2271
- printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
2272
- return true;
2273
- }
2290
+ while (true) {
2291
+ const route = await selectModelChoice({
2292
+ title: "Select Venice route model",
2293
+ subtitle: "Route handles planning and short turns. Up/Down selects, Enter confirms, Esc cancels.",
2294
+ options,
2295
+ initialIndex: routeIndex,
2296
+ });
2297
+ if (!route) return false;
2298
+ routeIndex = Math.max(
2299
+ options.findIndex((item) => item.provider === route.provider && item.model === route.model),
2300
+ 0
2301
+ );
2302
+ if (route.action === "disable") {
2303
+ useDeepSeekDefaults(state);
2304
+ printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
2305
+ return true;
2306
+ }
2274
2307
 
2275
- const mainIndex = Math.max(
2276
- options.findIndex((item) => item.action !== "disable" && item.provider === state.mainProvider && item.model === state.mainModel),
2277
- options.findIndex((item) => item.action !== "disable" && item.model === route.model),
2278
- 0
2279
- );
2280
- const main = await selectModelChoice({
2281
- title: "Select Venice main model",
2282
- subtitle: "Main handles complex work. Up/Down selects, Enter confirms, Esc cancels.",
2283
- options,
2284
- initialIndex: mainIndex,
2285
- });
2286
- if (!main) return false;
2287
- if (main.action === "disable") {
2288
- useDeepSeekDefaults(state);
2289
- printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
2308
+ const mainIndex = Math.max(
2309
+ options.findIndex((item) => item.action !== "disable" && item.provider === state.mainProvider && item.model === state.mainModel),
2310
+ options.findIndex((item) => item.action !== "disable" && item.model === route.model),
2311
+ 0
2312
+ );
2313
+ const main = await selectModelChoice({
2314
+ title: "Select Venice main model",
2315
+ subtitle: "Main handles complex work. Up/Down selects, Enter confirms, Esc goes back.",
2316
+ options,
2317
+ initialIndex: mainIndex,
2318
+ escapeValue: SELECTOR_BACK,
2319
+ });
2320
+ if (isSelectorBack(main)) continue;
2321
+ if (!main) return false;
2322
+ if (main.action === "disable") {
2323
+ useDeepSeekDefaults(state);
2324
+ printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
2325
+ return true;
2326
+ }
2327
+
2328
+ useVeniceModels(state, route.model, main.model);
2329
+ printSystemLine(`venice=on routing=smart route=venice/${state.routeModel} main=venice/${state.mainModel}`);
2290
2330
  return true;
2291
2331
  }
2292
-
2293
- useVeniceModels(state, route.model, main.model);
2294
- printSystemLine(`venice=on routing=smart route=venice/${state.routeModel} main=venice/${state.mainModel}`);
2295
- return true;
2296
2332
  }
2297
2333
 
2298
2334
  async function pickProvider(state) {