@lazyingart/agintiflow 0.20.185 → 0.20.187
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/docs/auxiliary-image-generation.md +15 -0
- package/package.json +1 -1
- package/public/app.js +101 -29
- package/public/styles.css +155 -8
- package/references/agintiflow-npm-publication-memory.md +22 -0
- package/scripts/smoke-auxiliary-tools.js +35 -0
- package/scripts/smoke-web-api.js +18 -0
- package/scripts/smoke-web-ui.js +15 -0
- package/src/cli.js +221 -1
- package/web.js +24 -0
|
@@ -75,6 +75,21 @@ That SVG request returns a PNG fallback contract rather than pretending SVG was
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
The CLI exposes the same direct path for shell scripts and other local apps:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
aginti image --json --dry-run \
|
|
82
|
+
--provider venice \
|
|
83
|
+
--format svg \
|
|
84
|
+
--output-dir artifacts/images/robot-cover \
|
|
85
|
+
--output-stem cover.svg \
|
|
86
|
+
"A cyan robot painting a poster, clean product illustration"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`aginti image ...` returns the same `requestedFormat`, `actualFormat`, and `formatNotice` fields as the web API. Use this direct CLI when
|
|
90
|
+
an app needs a deterministic tool call. Use `aginti --image "..."` when the user wants an agent-mediated image task that may plan, inspect
|
|
91
|
+
references, create files, or send artifacts to the canvas.
|
|
92
|
+
|
|
78
93
|
With GRS AI, the tool uses the Nano Banana API:
|
|
79
94
|
|
|
80
95
|
- `POST https://grsaiapi.com/v1/draw/nano-banana`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.187",
|
|
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",
|
package/public/app.js
CHANGED
|
@@ -1251,6 +1251,37 @@ function workspaceFilesByLane() {
|
|
|
1251
1251
|
return groups;
|
|
1252
1252
|
}
|
|
1253
1253
|
|
|
1254
|
+
function buildWorkspaceFolderTree(files = []) {
|
|
1255
|
+
const root = { name: "", path: "", dirs: new Map(), files: [] };
|
|
1256
|
+
for (const file of files) {
|
|
1257
|
+
const parts = String(file.path || "")
|
|
1258
|
+
.split("/")
|
|
1259
|
+
.filter(Boolean);
|
|
1260
|
+
if (!parts.length) continue;
|
|
1261
|
+
const fileName = parts.pop();
|
|
1262
|
+
let node = root;
|
|
1263
|
+
let currentPath = "";
|
|
1264
|
+
for (const part of parts) {
|
|
1265
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
1266
|
+
if (!node.dirs.has(part)) node.dirs.set(part, { name: part, path: currentPath, dirs: new Map(), files: [] });
|
|
1267
|
+
node = node.dirs.get(part);
|
|
1268
|
+
}
|
|
1269
|
+
node.files.push({ ...file, name: file.name || fileName });
|
|
1270
|
+
}
|
|
1271
|
+
return root;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function workspaceFolderCount(node) {
|
|
1275
|
+
if (!node) return 0;
|
|
1276
|
+
let count = node.files?.length || 0;
|
|
1277
|
+
for (const child of node.dirs?.values?.() || []) count += workspaceFolderCount(child);
|
|
1278
|
+
return count;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function sortWorkspaceFiles(left, right) {
|
|
1282
|
+
return String(left.path || "").localeCompare(String(right.path || ""));
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1254
1285
|
function workspaceActiveFile() {
|
|
1255
1286
|
return workspaceBrowser.files.find((file) => file.path === workspaceBrowser.activePath) || null;
|
|
1256
1287
|
}
|
|
@@ -1279,12 +1310,18 @@ function renderWorkspaceExplorer() {
|
|
|
1279
1310
|
const activeLane = workspaceBrowser.activeLane || "all";
|
|
1280
1311
|
const warnings = workspaceBrowser.warnings?.length ? ` · ${workspaceBrowser.warnings.length} warning(s)` : "";
|
|
1281
1312
|
const truncated = workspaceBrowser.truncated ? " · truncated" : "";
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1313
|
+
if (workspaceBrowser.loading) {
|
|
1314
|
+
workspaceBrowserStatusEl.textContent = "Loading workspace...";
|
|
1315
|
+
} else if (workspaceBrowser.error) {
|
|
1316
|
+
workspaceBrowserStatusEl.textContent = `Workspace unavailable: ${workspaceBrowser.error}`;
|
|
1317
|
+
} else {
|
|
1318
|
+
workspaceBrowserStatusEl.innerHTML = `
|
|
1319
|
+
<span class="workspace-status-chip">${workspaceBrowser.files.length} files</span>
|
|
1320
|
+
<span class="workspace-status-chip">${escapeHtml(activeLane)}</span>
|
|
1321
|
+
<code class="workspace-root-path">${escapeHtml(workspaceBrowser.root || workspaceRoot())}</code>
|
|
1322
|
+
${warnings || truncated ? `<span class="workspace-status-warn">${escapeHtml(`${warnings}${truncated}`.replace(/^ · /, ""))}</span>` : ""}
|
|
1323
|
+
`;
|
|
1324
|
+
}
|
|
1288
1325
|
|
|
1289
1326
|
workspaceLanesEl.innerHTML = WORKSPACE_LANES.map((lane) => {
|
|
1290
1327
|
const count = groups[lane.id]?.length || 0;
|
|
@@ -1302,34 +1339,69 @@ function renderWorkspaceExplorer() {
|
|
|
1302
1339
|
`;
|
|
1303
1340
|
}).join("");
|
|
1304
1341
|
|
|
1305
|
-
const visibleFiles = (groups[activeLane] || []).slice().sort(
|
|
1342
|
+
const visibleFiles = (groups[activeLane] || []).slice().sort(sortWorkspaceFiles);
|
|
1306
1343
|
if (!visibleFiles.length) {
|
|
1307
1344
|
workspaceTreeEl.innerHTML = `<p class="subtle workspace-empty">No files in this category.</p>`;
|
|
1308
1345
|
return;
|
|
1309
1346
|
}
|
|
1310
1347
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1348
|
+
const tree = buildWorkspaceFolderTree(visibleFiles);
|
|
1349
|
+
workspaceTreeEl.innerHTML = `
|
|
1350
|
+
<div class="workspace-tree-toolbar">
|
|
1351
|
+
<span>${escapeHtml(WORKSPACE_LANES.find((lane) => lane.id === activeLane)?.label || "Files")}</span>
|
|
1352
|
+
<strong>${visibleFiles.length}</strong>
|
|
1353
|
+
</div>
|
|
1354
|
+
<div class="workspace-folder-root">
|
|
1355
|
+
${renderWorkspaceFolderNode(tree, 0)}
|
|
1356
|
+
</div>
|
|
1357
|
+
`;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
function renderWorkspaceFileNode(file, depth = 0) {
|
|
1361
|
+
const lane = inferWorkspaceLane(file);
|
|
1362
|
+
const active = file.path === workspaceBrowser.activePath;
|
|
1363
|
+
const meta = [lane, formatBytes(file.size), file.binary ? "binary" : "text"].join(" · ");
|
|
1364
|
+
return `
|
|
1365
|
+
<button
|
|
1366
|
+
type="button"
|
|
1367
|
+
class="workspace-file-row workspace-tree-file"
|
|
1368
|
+
data-workspace-file="${escapeHtml(file.path)}"
|
|
1369
|
+
data-selected="${active}"
|
|
1370
|
+
draggable="true"
|
|
1371
|
+
style="--depth: ${depth};"
|
|
1372
|
+
>
|
|
1373
|
+
<span class="workspace-file-icon" aria-hidden="true">${file.binary ? "bin" : "txt"}</span>
|
|
1374
|
+
<span class="workspace-file-main">
|
|
1375
|
+
<strong>${escapeHtml(workspaceBaseName(file.path))}</strong>
|
|
1376
|
+
<small>${escapeHtml(workspaceDir(file.path) || ".")}</small>
|
|
1377
|
+
</span>
|
|
1378
|
+
<span class="workspace-file-meta">${escapeHtml(meta)}</span>
|
|
1379
|
+
</button>
|
|
1380
|
+
`;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function renderWorkspaceFolderNode(node, depth = 0) {
|
|
1384
|
+
const dirs = [...(node.dirs?.values?.() || [])].sort((left, right) => left.name.localeCompare(right.name));
|
|
1385
|
+
const files = (node.files || []).slice().sort(sortWorkspaceFiles);
|
|
1386
|
+
const children = [
|
|
1387
|
+
...dirs.map((child) => renderWorkspaceFolderNode(child, depth + 1)),
|
|
1388
|
+
...files.map((file) => renderWorkspaceFileNode(file, depth)),
|
|
1389
|
+
].join("");
|
|
1390
|
+
if (depth === 0) return children;
|
|
1391
|
+
const activeInside = workspaceBrowser.activePath && workspaceBrowser.activePath.startsWith(`${node.path}/`);
|
|
1392
|
+
const open = depth <= 2 || activeInside;
|
|
1393
|
+
return `
|
|
1394
|
+
<details class="workspace-folder" ${open ? "open" : ""} style="--depth: ${Math.max(depth - 1, 0)};">
|
|
1395
|
+
<summary>
|
|
1396
|
+
<span class="workspace-folder-icon" aria-hidden="true">></span>
|
|
1397
|
+
<span class="workspace-folder-name">${escapeHtml(node.name)}</span>
|
|
1398
|
+
<small>${workspaceFolderCount(node)}</small>
|
|
1399
|
+
</summary>
|
|
1400
|
+
<div class="workspace-folder-children">
|
|
1401
|
+
${children}
|
|
1402
|
+
</div>
|
|
1403
|
+
</details>
|
|
1404
|
+
`;
|
|
1333
1405
|
}
|
|
1334
1406
|
|
|
1335
1407
|
function renderWorkspaceWorkbench() {
|
package/public/styles.css
CHANGED
|
@@ -959,10 +959,43 @@ button.danger {
|
|
|
959
959
|
}
|
|
960
960
|
|
|
961
961
|
.workspace-browser-status {
|
|
962
|
+
display: flex;
|
|
963
|
+
flex-wrap: wrap;
|
|
964
|
+
align-items: center;
|
|
965
|
+
gap: 7px;
|
|
962
966
|
line-height: 1.35;
|
|
963
967
|
overflow-wrap: anywhere;
|
|
964
968
|
}
|
|
965
969
|
|
|
970
|
+
.workspace-status-chip {
|
|
971
|
+
padding: 4px 8px;
|
|
972
|
+
border: 1px solid rgba(15, 118, 110, 0.16);
|
|
973
|
+
border-radius: 999px;
|
|
974
|
+
background: rgba(255, 253, 250, 0.86);
|
|
975
|
+
color: var(--ink);
|
|
976
|
+
font-size: 0.76rem;
|
|
977
|
+
font-weight: 700;
|
|
978
|
+
text-transform: capitalize;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.workspace-root-path {
|
|
982
|
+
min-width: 0;
|
|
983
|
+
max-width: 100%;
|
|
984
|
+
padding: 5px 8px;
|
|
985
|
+
border: 1px solid rgba(15, 118, 110, 0.14);
|
|
986
|
+
border-radius: 10px;
|
|
987
|
+
background: rgba(13, 148, 136, 0.07);
|
|
988
|
+
color: #155e75;
|
|
989
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
990
|
+
font-size: 0.76rem;
|
|
991
|
+
overflow-wrap: anywhere;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.workspace-status-warn {
|
|
995
|
+
color: #b45309;
|
|
996
|
+
font-size: 0.76rem;
|
|
997
|
+
}
|
|
998
|
+
|
|
966
999
|
.workspace-lanes {
|
|
967
1000
|
display: grid;
|
|
968
1001
|
grid-template-columns: repeat(auto-fit, minmax(82px, 1fr));
|
|
@@ -995,11 +1028,105 @@ button.danger {
|
|
|
995
1028
|
}
|
|
996
1029
|
|
|
997
1030
|
.workspace-tree {
|
|
998
|
-
display: grid;
|
|
999
|
-
gap: 7px;
|
|
1000
1031
|
max-height: 440px;
|
|
1001
1032
|
overflow: auto;
|
|
1002
|
-
padding
|
|
1033
|
+
padding: 8px;
|
|
1034
|
+
border: 1px solid rgba(15, 118, 110, 0.12);
|
|
1035
|
+
border-radius: 16px;
|
|
1036
|
+
background:
|
|
1037
|
+
linear-gradient(180deg, rgba(255, 253, 250, 0.88), rgba(240, 253, 250, 0.58)),
|
|
1038
|
+
rgba(255, 255, 255, 0.72);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.workspace-tree-toolbar {
|
|
1042
|
+
position: sticky;
|
|
1043
|
+
top: 0;
|
|
1044
|
+
z-index: 1;
|
|
1045
|
+
display: flex;
|
|
1046
|
+
justify-content: space-between;
|
|
1047
|
+
align-items: center;
|
|
1048
|
+
gap: 10px;
|
|
1049
|
+
margin: -8px -8px 8px;
|
|
1050
|
+
padding: 8px 10px;
|
|
1051
|
+
border-bottom: 1px solid rgba(15, 118, 110, 0.1);
|
|
1052
|
+
background: rgba(255, 253, 250, 0.95);
|
|
1053
|
+
color: var(--muted);
|
|
1054
|
+
font-size: 0.78rem;
|
|
1055
|
+
font-weight: 700;
|
|
1056
|
+
text-transform: uppercase;
|
|
1057
|
+
letter-spacing: 0.03em;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.workspace-tree-toolbar strong {
|
|
1061
|
+
color: var(--accent);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.workspace-folder-root,
|
|
1065
|
+
.workspace-folder-children {
|
|
1066
|
+
display: grid;
|
|
1067
|
+
gap: 4px;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.workspace-folder {
|
|
1071
|
+
margin-left: calc(var(--depth, 0) * 12px);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
.workspace-folder summary {
|
|
1075
|
+
display: flex;
|
|
1076
|
+
align-items: center;
|
|
1077
|
+
gap: 7px;
|
|
1078
|
+
min-height: 32px;
|
|
1079
|
+
padding: 6px 8px;
|
|
1080
|
+
border: 1px solid transparent;
|
|
1081
|
+
border-radius: 11px;
|
|
1082
|
+
color: var(--ink);
|
|
1083
|
+
cursor: pointer;
|
|
1084
|
+
list-style: none;
|
|
1085
|
+
user-select: none;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.workspace-folder summary::-webkit-details-marker {
|
|
1089
|
+
display: none;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.workspace-folder summary:hover {
|
|
1093
|
+
border-color: rgba(15, 118, 110, 0.16);
|
|
1094
|
+
background: rgba(240, 253, 250, 0.72);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.workspace-folder-icon {
|
|
1098
|
+
display: inline-grid;
|
|
1099
|
+
place-items: center;
|
|
1100
|
+
width: 18px;
|
|
1101
|
+
height: 18px;
|
|
1102
|
+
border: 1px solid rgba(15, 118, 110, 0.18);
|
|
1103
|
+
border-radius: 6px;
|
|
1104
|
+
color: var(--accent);
|
|
1105
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1106
|
+
font-size: 0.68rem;
|
|
1107
|
+
transition: transform 0.16s ease;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.workspace-folder[open] > summary .workspace-folder-icon {
|
|
1111
|
+
transform: rotate(90deg);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.workspace-folder-name {
|
|
1115
|
+
min-width: 0;
|
|
1116
|
+
overflow: hidden;
|
|
1117
|
+
text-overflow: ellipsis;
|
|
1118
|
+
white-space: nowrap;
|
|
1119
|
+
font-weight: 750;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
.workspace-folder summary small {
|
|
1123
|
+
margin-left: auto;
|
|
1124
|
+
color: var(--muted);
|
|
1125
|
+
font-size: 0.72rem;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.workspace-folder-children {
|
|
1129
|
+
margin-top: 4px;
|
|
1003
1130
|
}
|
|
1004
1131
|
|
|
1005
1132
|
.workspace-empty {
|
|
@@ -1011,18 +1138,23 @@ button.danger {
|
|
|
1011
1138
|
|
|
1012
1139
|
.workspace-file-row {
|
|
1013
1140
|
display: grid;
|
|
1014
|
-
grid-template-columns: minmax(0, 1fr);
|
|
1015
|
-
gap:
|
|
1141
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
1142
|
+
gap: 6px 8px;
|
|
1143
|
+
align-items: center;
|
|
1016
1144
|
width: 100%;
|
|
1017
|
-
padding: 10px
|
|
1145
|
+
padding: 8px 10px;
|
|
1018
1146
|
border: 1px solid rgba(217, 119, 6, 0.16);
|
|
1019
|
-
border-radius:
|
|
1147
|
+
border-radius: 12px;
|
|
1020
1148
|
background: rgba(255, 253, 250, 0.72);
|
|
1021
1149
|
color: var(--ink);
|
|
1022
1150
|
cursor: pointer;
|
|
1023
1151
|
text-align: left;
|
|
1024
1152
|
}
|
|
1025
1153
|
|
|
1154
|
+
.workspace-tree-file {
|
|
1155
|
+
margin-left: calc(var(--depth, 0) * 12px);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1026
1158
|
.workspace-file-row:hover {
|
|
1027
1159
|
border-color: rgba(15, 118, 110, 0.34);
|
|
1028
1160
|
background: rgba(240, 253, 250, 0.72);
|
|
@@ -1030,10 +1162,24 @@ button.danger {
|
|
|
1030
1162
|
|
|
1031
1163
|
.workspace-file-row[data-selected="true"] {
|
|
1032
1164
|
border-color: rgba(15, 118, 110, 0.58);
|
|
1033
|
-
background: linear-gradient(135deg, rgba(15, 118, 110, 0.
|
|
1165
|
+
background: linear-gradient(135deg, rgba(15, 118, 110, 0.22), rgba(255, 255, 255, 0.96));
|
|
1034
1166
|
box-shadow: 0 0 0 2px rgba(15, 118, 110, 0.1);
|
|
1035
1167
|
}
|
|
1036
1168
|
|
|
1169
|
+
.workspace-file-icon {
|
|
1170
|
+
display: inline-grid;
|
|
1171
|
+
place-items: center;
|
|
1172
|
+
width: 30px;
|
|
1173
|
+
height: 24px;
|
|
1174
|
+
border-radius: 8px;
|
|
1175
|
+
background: rgba(15, 118, 110, 0.09);
|
|
1176
|
+
color: #155e75;
|
|
1177
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1178
|
+
font-size: 0.62rem;
|
|
1179
|
+
font-weight: 800;
|
|
1180
|
+
text-transform: uppercase;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1037
1183
|
.workspace-file-main {
|
|
1038
1184
|
display: grid;
|
|
1039
1185
|
gap: 1px;
|
|
@@ -1050,6 +1196,7 @@ button.danger {
|
|
|
1050
1196
|
|
|
1051
1197
|
.workspace-file-main small,
|
|
1052
1198
|
.workspace-file-meta {
|
|
1199
|
+
grid-column: 2;
|
|
1053
1200
|
color: var(--muted);
|
|
1054
1201
|
font-size: 0.76rem;
|
|
1055
1202
|
}
|
|
@@ -10,6 +10,28 @@ This note records the working publication route for AgInTiFlow so future release
|
|
|
10
10
|
|
|
11
11
|
Latest verified release:
|
|
12
12
|
|
|
13
|
+
- Version: `0.20.186`
|
|
14
|
+
- Commit: `f32a748`
|
|
15
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26700782132`
|
|
16
|
+
- Workflow result: success
|
|
17
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.186`.
|
|
18
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.186` then `aginti --version` returned `0.20.186`.
|
|
19
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.186"` from the global npm package path.
|
|
20
|
+
- Direct image CLI verification: `aginti image --json --dry-run --format svg` returned `requestedFormat:"svg"`, `actualFormat:"png"`, and a clear raster PNG fallback notice.
|
|
21
|
+
|
|
22
|
+
Previous verified release:
|
|
23
|
+
|
|
24
|
+
- Version: `0.20.185`
|
|
25
|
+
- Commit: `58d6087`
|
|
26
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26700387931`
|
|
27
|
+
- Workflow result: success
|
|
28
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.185`.
|
|
29
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.185` then `aginti --version` returned `0.20.185`.
|
|
30
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.185"` from the global npm package path.
|
|
31
|
+
- Image API verification: `POST /api/auxiliary/generate-image` with `format:"svg"` and `dryRun:true` returned `requestedFormat:"svg"`, `actualFormat:"png"`, and a clear raster PNG fallback notice.
|
|
32
|
+
|
|
33
|
+
Previous verified release:
|
|
34
|
+
|
|
13
35
|
- Version: `0.20.184`
|
|
14
36
|
- Commit: `9b22f4c`
|
|
15
37
|
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26700154894`
|
|
@@ -3,6 +3,8 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
6
8
|
import { runAgent } from "../src/agent-runner.js";
|
|
7
9
|
import { generateImage, listAuxiliarySkills } from "../src/auxiliary-tools.js";
|
|
8
10
|
import { resolveRuntimeConfig } from "../src/config.js";
|
|
@@ -10,6 +12,7 @@ import { providerKeyStatus, setProviderKey } from "../src/project.js";
|
|
|
10
12
|
import { SessionStore } from "../src/session-store.js";
|
|
11
13
|
|
|
12
14
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
+
const execFile = promisify(execFileCallback);
|
|
13
16
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auxiliary-"));
|
|
14
17
|
process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
|
|
15
18
|
const runtimeDir = path.join(tempRoot, "runtime");
|
|
@@ -82,6 +85,37 @@ try {
|
|
|
82
85
|
);
|
|
83
86
|
assert(svgFallbackManifest.requestedFormat === "svg" && svgFallbackManifest.actualFormat === "png", "SVG fallback manifest missing format contract");
|
|
84
87
|
|
|
88
|
+
const cliImage = await execFile(
|
|
89
|
+
process.execPath,
|
|
90
|
+
[
|
|
91
|
+
path.join(repoRoot, "bin/aginti-cli.js"),
|
|
92
|
+
"--no-auto-update",
|
|
93
|
+
"image",
|
|
94
|
+
"--json",
|
|
95
|
+
"--dry-run",
|
|
96
|
+
"--cwd",
|
|
97
|
+
workspace,
|
|
98
|
+
"--provider",
|
|
99
|
+
"venice",
|
|
100
|
+
"--format",
|
|
101
|
+
"svg",
|
|
102
|
+
"--output-dir",
|
|
103
|
+
"artifacts/images/cli-svg-fallback",
|
|
104
|
+
"--output-stem",
|
|
105
|
+
"diagram.svg",
|
|
106
|
+
"A simple geometric diagram requested as SVG.",
|
|
107
|
+
],
|
|
108
|
+
{
|
|
109
|
+
cwd: repoRoot,
|
|
110
|
+
env: { ...process.env, AGINTIFLOW_HOME: process.env.AGINTIFLOW_HOME },
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
const cliImageResult = JSON.parse(cliImage.stdout);
|
|
114
|
+
assert(cliImageResult.ok && cliImageResult.requestedFormat === "svg", "direct image CLI did not record requestedFormat");
|
|
115
|
+
assert(cliImageResult.actualFormat === "png", "direct image CLI did not select PNG fallback");
|
|
116
|
+
assert(/raster PNG/i.test(cliImageResult.formatNotice || ""), "direct image CLI did not explain SVG-to-PNG fallback");
|
|
117
|
+
await fs.access(path.join(workspace, "artifacts/images/cli-svg-fallback/task_manifest.json"));
|
|
118
|
+
|
|
85
119
|
const blocked = await generateImage(
|
|
86
120
|
{
|
|
87
121
|
prompt: "blocked",
|
|
@@ -131,6 +165,7 @@ try {
|
|
|
131
165
|
"generate_image_dry_run",
|
|
132
166
|
"venice_generate_image_dry_run",
|
|
133
167
|
"svg_request_png_fallback",
|
|
168
|
+
"direct_image_cli_svg_request_png_fallback",
|
|
134
169
|
"generate_image_guardrail",
|
|
135
170
|
"mock_agent_image_tool",
|
|
136
171
|
],
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -93,6 +93,7 @@ try {
|
|
|
93
93
|
await fs.mkdir(path.join(runtimeDir, "data"), { recursive: true });
|
|
94
94
|
await fs.writeFile(path.join(runtimeDir, "notes", "workspace-smoke.md"), "# Workspace smoke\n\nEditable text.\n");
|
|
95
95
|
await fs.writeFile(path.join(runtimeDir, "data", "workspace-smoke.csv"), "sample,value\nalpha,1\n");
|
|
96
|
+
await fs.writeFile(path.join(runtimeDir, ".aginti", ".env"), "DEEPSEEK_API_KEY=should-not-render\n");
|
|
96
97
|
|
|
97
98
|
const webAppHtml = await fs.readFile(path.join(repoRoot, "public", "index.html"), "utf8");
|
|
98
99
|
const chatThreadIndex = webAppHtml.indexOf('id="chat-thread"');
|
|
@@ -145,6 +146,9 @@ try {
|
|
|
145
146
|
if (!workspaceSnapshot.files?.some((file) => file.path === "notes/workspace-smoke.md" && file.content.includes("Editable text."))) {
|
|
146
147
|
throw new Error(`workspace snapshot did not include editable note: ${JSON.stringify(workspaceSnapshot.files?.slice(0, 5))}`);
|
|
147
148
|
}
|
|
149
|
+
if (workspaceSnapshot.files?.some((file) => file.path === ".aginti/.env" || file.path === ".aginti/mcp.json" || file.path === ".env")) {
|
|
150
|
+
throw new Error(`workspace snapshot exposed protected internal/secret files: ${JSON.stringify(workspaceSnapshot.files?.slice(0, 10))}`);
|
|
151
|
+
}
|
|
148
152
|
const rawWorkspaceResponse = await fetch(
|
|
149
153
|
`${baseUrl}/api/workspace/raw?commandCwd=${encodeURIComponent(runtimeDir)}&path=${encodeURIComponent("notes/workspace-smoke.md")}`
|
|
150
154
|
);
|
|
@@ -152,6 +156,20 @@ try {
|
|
|
152
156
|
if (!rawWorkspaceResponse.ok || !rawWorkspaceText.includes("Workspace smoke")) {
|
|
153
157
|
throw new Error(`workspace raw endpoint failed: status=${rawWorkspaceResponse.status} body=${rawWorkspaceText.slice(0, 120)}`);
|
|
154
158
|
}
|
|
159
|
+
const protectedRawResponse = await fetch(
|
|
160
|
+
`${baseUrl}/api/workspace/raw?commandCwd=${encodeURIComponent(runtimeDir)}&path=${encodeURIComponent(".aginti/.env")}`
|
|
161
|
+
);
|
|
162
|
+
if (protectedRawResponse.ok) {
|
|
163
|
+
throw new Error("workspace raw endpoint exposed protected .aginti/.env");
|
|
164
|
+
}
|
|
165
|
+
const protectedWriteResponse = await fetch(`${baseUrl}/api/workspace/write`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { "Content-Type": "application/json" },
|
|
168
|
+
body: JSON.stringify({ commandCwd: runtimeDir, path: ".env", content: "SECRET=blocked\n" }),
|
|
169
|
+
});
|
|
170
|
+
if (protectedWriteResponse.ok) {
|
|
171
|
+
throw new Error("workspace write endpoint allowed protected .env");
|
|
172
|
+
}
|
|
155
173
|
const writtenWorkspace = await fetchJson("/api/workspace/write", {
|
|
156
174
|
method: "POST",
|
|
157
175
|
headers: { "Content-Type": "application/json" },
|
package/scripts/smoke-web-ui.js
CHANGED
|
@@ -73,8 +73,10 @@ let browser;
|
|
|
73
73
|
|
|
74
74
|
try {
|
|
75
75
|
await waitForHealth();
|
|
76
|
+
await fs.mkdir(path.join(runtimeDir, ".aginti"), { recursive: true });
|
|
76
77
|
await fs.mkdir(path.join(runtimeDir, "notes"), { recursive: true });
|
|
77
78
|
await fs.mkdir(path.join(runtimeDir, "data"), { recursive: true });
|
|
79
|
+
await fs.writeFile(path.join(runtimeDir, ".aginti", ".env"), "DEEPSEEK_API_KEY=should-not-render\n");
|
|
78
80
|
await fs.writeFile(path.join(runtimeDir, "notes", "workspace-ui.md"), "# Workspace UI\n\ninitial body\n");
|
|
79
81
|
await fs.writeFile(path.join(runtimeDir, "data", "workspace-ui.csv"), "sample,value\nalpha,1\n");
|
|
80
82
|
browser = await chromium.launch({ headless: true });
|
|
@@ -102,6 +104,16 @@ try {
|
|
|
102
104
|
if ((await page.locator("#workspace-lanes [data-workspace-lane]").count()) < 8) {
|
|
103
105
|
throw new Error("workspace explorer did not render general category lanes");
|
|
104
106
|
}
|
|
107
|
+
if ((await page.locator('[data-workspace-file=".aginti/.env"]').count()) !== 0) {
|
|
108
|
+
throw new Error("workspace explorer exposed protected .aginti/.env");
|
|
109
|
+
}
|
|
110
|
+
if ((await page.locator("#workspace-tree details.workspace-folder").count()) < 1) {
|
|
111
|
+
throw new Error("workspace explorer did not render a folder tree");
|
|
112
|
+
}
|
|
113
|
+
const statusText = await page.locator("#workspace-browser-status").innerText();
|
|
114
|
+
if (!statusText.includes(runtimeDir) || !/files/i.test(statusText)) {
|
|
115
|
+
throw new Error(`workspace explorer status did not show file count and root path: ${statusText}`);
|
|
116
|
+
}
|
|
105
117
|
if ((await page.locator('[data-workspace-file="notes/workspace-ui.md"]').getAttribute("draggable")) !== "true") {
|
|
106
118
|
throw new Error("workspace file rows are not draggable");
|
|
107
119
|
}
|
|
@@ -305,6 +317,9 @@ try {
|
|
|
305
317
|
"working-directory-search-top",
|
|
306
318
|
"project-status-chips",
|
|
307
319
|
"workspace-explorer-general-lanes",
|
|
320
|
+
"workspace-explorer-folder-tree",
|
|
321
|
+
"workspace-explorer-protected-files-hidden",
|
|
322
|
+
"workspace-explorer-status-root",
|
|
308
323
|
"workspace-file-selection",
|
|
309
324
|
"workspace-editor-save-persists",
|
|
310
325
|
"workspace-file-drop-to-chat",
|
package/src/cli.js
CHANGED
|
@@ -30,6 +30,7 @@ import { listTaskProfiles } from "./task-profiles.js";
|
|
|
30
30
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
31
31
|
import { normalizeAuthProvider, promptHidden, runAuthWizard, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
32
32
|
import { listSkills, selectSkillsForGoal } from "./skill-library.js";
|
|
33
|
+
import { generateImage } from "./auxiliary-tools.js";
|
|
33
34
|
import { languageLabel, resolveLanguage } from "./i18n.js";
|
|
34
35
|
import { maybeAutoUpdate } from "./auto-update.js";
|
|
35
36
|
import { readHousekeepingSummary } from "./housekeeping.js";
|
|
@@ -761,7 +762,7 @@ function exitOnUnknownOptions(parsed) {
|
|
|
761
762
|
|
|
762
763
|
function printUsage() {
|
|
763
764
|
console.log(
|
|
764
|
-
'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 mcp [status|config|inspect|tools|resources|read|prompts|prompt|call|restart] 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] [--mcp|--no-mcp] [--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"'
|
|
765
|
+
'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 image [--json] [--dry-run] [--format png|webp|svg] "prompt" OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti mcp [status|config|inspect|tools|resources|read|prompts|prompt|call|restart] 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] [--mcp|--no-mcp] [--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"'
|
|
765
766
|
);
|
|
766
767
|
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.");
|
|
767
768
|
console.log(`Languages: ${["en", "ja", "zh-Hans", "zh-Hant", "ko", "fr", "es", "ar", "vi", "de", "ru"].map((code) => `${code}=${languageLabel(code)}`).join(", ")}`);
|
|
@@ -790,6 +791,211 @@ function splitToolCommandArgv(argv = []) {
|
|
|
790
791
|
return { actionArgv, optionArgv, json };
|
|
791
792
|
}
|
|
792
793
|
|
|
794
|
+
function takeImageOptionValue(argv, index, option) {
|
|
795
|
+
const equals = String(option || "").match(/^([^=]+)=(.*)$/);
|
|
796
|
+
if (equals) return { value: equals[2], nextIndex: index + 1 };
|
|
797
|
+
const value = argv[index + 1];
|
|
798
|
+
return { value: value && !String(value).startsWith("--") ? value : "", nextIndex: value && !String(value).startsWith("--") ? index + 2 : index + 1 };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function parseImageCommandArgs(argv = []) {
|
|
802
|
+
const result = {
|
|
803
|
+
prompt: "",
|
|
804
|
+
provider: "",
|
|
805
|
+
model: "",
|
|
806
|
+
format: "",
|
|
807
|
+
outputDir: "",
|
|
808
|
+
outputStem: "",
|
|
809
|
+
aspectRatio: "",
|
|
810
|
+
imageSize: "",
|
|
811
|
+
host: "",
|
|
812
|
+
referenceImages: [],
|
|
813
|
+
commandCwd: "",
|
|
814
|
+
requestTimeoutMs: "",
|
|
815
|
+
pollTimeoutMs: "",
|
|
816
|
+
pollIntervalMs: "",
|
|
817
|
+
dryRun: false,
|
|
818
|
+
json: false,
|
|
819
|
+
stdin: false,
|
|
820
|
+
help: false,
|
|
821
|
+
unknownOptions: [],
|
|
822
|
+
};
|
|
823
|
+
const promptParts = [];
|
|
824
|
+
let index = 0;
|
|
825
|
+
if (["generate", "create", "make"].includes(String(argv[0] || "").toLowerCase())) index = 1;
|
|
826
|
+
|
|
827
|
+
while (index < argv.length) {
|
|
828
|
+
const arg = String(argv[index] || "");
|
|
829
|
+
const name = arg.split("=")[0];
|
|
830
|
+
if (arg === "--") {
|
|
831
|
+
promptParts.push(...argv.slice(index + 1));
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
if (arg === "--help" || arg === "-h") {
|
|
835
|
+
result.help = true;
|
|
836
|
+
index += 1;
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (arg === "--json") {
|
|
840
|
+
result.json = true;
|
|
841
|
+
index += 1;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (arg === "--dry-run" || arg === "--dryrun") {
|
|
845
|
+
result.dryRun = true;
|
|
846
|
+
index += 1;
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (arg === "--stdin") {
|
|
850
|
+
result.stdin = true;
|
|
851
|
+
index += 1;
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const valueOptions = new Map([
|
|
856
|
+
["--provider", "provider"],
|
|
857
|
+
["--aux-provider", "provider"],
|
|
858
|
+
["--auxiliary-provider", "provider"],
|
|
859
|
+
["--model", "model"],
|
|
860
|
+
["--aux-model", "model"],
|
|
861
|
+
["--auxiliary-model", "model"],
|
|
862
|
+
["--format", "format"],
|
|
863
|
+
["--output-dir", "outputDir"],
|
|
864
|
+
["--out-dir", "outputDir"],
|
|
865
|
+
["--dir", "outputDir"],
|
|
866
|
+
["--output-stem", "outputStem"],
|
|
867
|
+
["--stem", "outputStem"],
|
|
868
|
+
["--aspect-ratio", "aspectRatio"],
|
|
869
|
+
["--ratio", "aspectRatio"],
|
|
870
|
+
["--image-size", "imageSize"],
|
|
871
|
+
["--size", "imageSize"],
|
|
872
|
+
["--host", "host"],
|
|
873
|
+
["--cwd", "commandCwd"],
|
|
874
|
+
["--request-timeout-ms", "requestTimeoutMs"],
|
|
875
|
+
["--poll-timeout-ms", "pollTimeoutMs"],
|
|
876
|
+
["--poll-interval-ms", "pollIntervalMs"],
|
|
877
|
+
]);
|
|
878
|
+
if (valueOptions.has(name)) {
|
|
879
|
+
const { value, nextIndex } = takeImageOptionValue(argv, index, arg);
|
|
880
|
+
if (!value) result.unknownOptions.push(arg);
|
|
881
|
+
else result[valueOptions.get(name)] = value;
|
|
882
|
+
index = nextIndex;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (["--reference", "--reference-image", "--ref", "--image-ref"].includes(name)) {
|
|
886
|
+
const { value, nextIndex } = takeImageOptionValue(argv, index, arg);
|
|
887
|
+
if (!value) result.unknownOptions.push(arg);
|
|
888
|
+
else result.referenceImages.push(value);
|
|
889
|
+
index = nextIndex;
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
if (arg.startsWith("--") && promptParts.length === 0) {
|
|
893
|
+
result.unknownOptions.push(arg);
|
|
894
|
+
index += 1;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
promptParts.push(arg);
|
|
898
|
+
index += 1;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
result.prompt = promptParts.join(" ").trim();
|
|
902
|
+
return result;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function printImageCommandUsage() {
|
|
906
|
+
console.log(
|
|
907
|
+
'Usage: aginti image [generate] [--json] [--dry-run] [--provider grsai|venice] [--model MODEL] [--format png|webp|svg] [--output-dir DIR] [--output-stem STEM] [--aspect-ratio 1:1] [--image-size 1K|2K|4K|1024x1024] [--reference path-or-url] "prompt"'
|
|
908
|
+
);
|
|
909
|
+
console.log("Direct image CLI calls the same generate_image tool as the web API. SVG/vector requests return PNG with requestedFormat/actualFormat/formatNotice.");
|
|
910
|
+
console.log('Agent-mediated image work is still available as: aginti --image "draw a poster"');
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function directImageCommandArgv(argv = []) {
|
|
914
|
+
const first = String(argv[0] || "").toLowerCase();
|
|
915
|
+
const second = String(argv[1] || "").toLowerCase();
|
|
916
|
+
if (["image", "imagegen", "image-gen", "generate-image", "image-generate"].includes(first)) return argv.slice(1);
|
|
917
|
+
if (["aux", "auxiliary"].includes(first) && ["image", "imagegen", "image-gen", "generate-image", "generate"].includes(second)) {
|
|
918
|
+
return argv.slice(2);
|
|
919
|
+
}
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function printImageCommandResult(result = {}) {
|
|
924
|
+
const status = result.dryRun ? "prepared" : result.ok ? "generated" : result.blocked ? "blocked" : "failed";
|
|
925
|
+
console.log(`image: ${status}`);
|
|
926
|
+
if (result.summary) console.log(`summary: ${result.summary}`);
|
|
927
|
+
if (result.provider) console.log(`provider: ${result.provider}`);
|
|
928
|
+
if (result.requestedFormat || result.actualFormat) {
|
|
929
|
+
const requested = result.requestedFormat || "auto";
|
|
930
|
+
const actual = result.actualFormat || "unknown";
|
|
931
|
+
console.log(`format: ${requested}${requested === actual ? "" : ` -> ${actual}`}`);
|
|
932
|
+
}
|
|
933
|
+
if (result.formatNotice) console.log(`notice: ${result.formatNotice}`);
|
|
934
|
+
if (result.path) console.log(`path: ${result.path}`);
|
|
935
|
+
if (result.imagePaths?.length) console.log(`images: ${result.imagePaths.join(", ")}`);
|
|
936
|
+
if (result.manifestPath) console.log(`manifest: ${result.manifestPath}`);
|
|
937
|
+
if (result.promptPath) console.log(`prompt: ${result.promptPath}`);
|
|
938
|
+
if (result.requestPayloadPath) console.log(`request: ${result.requestPayloadPath}`);
|
|
939
|
+
if (result.reason) console.log(`reason: ${result.reason}`);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async function handleImageCommand(argv, { commandCwd = process.cwd() } = {}) {
|
|
943
|
+
const parsed = parseImageCommandArgs(argv);
|
|
944
|
+
if (parsed.help) {
|
|
945
|
+
printImageCommandUsage();
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (parsed.unknownOptions.length) {
|
|
949
|
+
printUnknownCliOptions(parsed.unknownOptions);
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
const prompt = parsed.stdin ? await readStdin() : parsed.prompt;
|
|
953
|
+
if (!prompt) {
|
|
954
|
+
printImageCommandUsage();
|
|
955
|
+
process.exit(1);
|
|
956
|
+
}
|
|
957
|
+
const effectiveCommandCwd = path.resolve(parsed.commandCwd || commandCwd || process.cwd());
|
|
958
|
+
const config = loadConfig(
|
|
959
|
+
{
|
|
960
|
+
goal: prompt,
|
|
961
|
+
taskProfile: "image",
|
|
962
|
+
commandCwd: effectiveCommandCwd,
|
|
963
|
+
auxiliaryProvider: parsed.provider,
|
|
964
|
+
auxiliaryModel: parsed.model,
|
|
965
|
+
allowFileTools: true,
|
|
966
|
+
allowAuxiliaryTools: true,
|
|
967
|
+
},
|
|
968
|
+
{ packageDir, baseDir: effectiveCommandCwd }
|
|
969
|
+
);
|
|
970
|
+
const result = await generateImage(
|
|
971
|
+
{
|
|
972
|
+
prompt,
|
|
973
|
+
provider: parsed.provider,
|
|
974
|
+
model: parsed.model,
|
|
975
|
+
format: parsed.format,
|
|
976
|
+
outputDir: parsed.outputDir || undefined,
|
|
977
|
+
outputStem: parsed.outputStem || undefined,
|
|
978
|
+
aspectRatio: parsed.aspectRatio || undefined,
|
|
979
|
+
imageSize: parsed.imageSize || undefined,
|
|
980
|
+
host: parsed.host || undefined,
|
|
981
|
+
referenceImages: parsed.referenceImages,
|
|
982
|
+
requestTimeoutMs: parsed.requestTimeoutMs || undefined,
|
|
983
|
+
pollTimeoutMs: parsed.pollTimeoutMs || undefined,
|
|
984
|
+
pollIntervalMs: parsed.pollIntervalMs || undefined,
|
|
985
|
+
dryRun: parsed.dryRun,
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
...config,
|
|
989
|
+
commandCwd: effectiveCommandCwd,
|
|
990
|
+
allowFileTools: true,
|
|
991
|
+
allowAuxiliaryTools: true,
|
|
992
|
+
}
|
|
993
|
+
);
|
|
994
|
+
if (parsed.json) console.log(JSON.stringify(result, null, 2));
|
|
995
|
+
else printImageCommandResult(result);
|
|
996
|
+
if (!result.ok) process.exit(1);
|
|
997
|
+
}
|
|
998
|
+
|
|
793
999
|
function parseDockerCommandArgs(argv = [], fallbackCwd = process.cwd()) {
|
|
794
1000
|
const result = {
|
|
795
1001
|
action: "status",
|
|
@@ -1856,6 +2062,20 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1856
2062
|
return;
|
|
1857
2063
|
}
|
|
1858
2064
|
|
|
2065
|
+
const imageCommand = directImageCommandArgv(commandArgv);
|
|
2066
|
+
if (imageCommand) {
|
|
2067
|
+
try {
|
|
2068
|
+
await handleImageCommand(imageCommand, { commandCwd });
|
|
2069
|
+
} catch (error) {
|
|
2070
|
+
const wantsJson = imageCommand.includes("--json");
|
|
2071
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2072
|
+
if (wantsJson) console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
2073
|
+
else console.error(message);
|
|
2074
|
+
process.exit(1);
|
|
2075
|
+
}
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
1859
2079
|
if (commandArgv.includes("--remove-empty-sessions") || commandArgv[0] === "remove-empty-sessions") {
|
|
1860
2080
|
await handleRemoveSessionsCommand({ emptyOnly: true });
|
|
1861
2081
|
return;
|
package/web.js
CHANGED
|
@@ -778,8 +778,11 @@ const WORKSPACE_SKIP_NAMES = new Set([
|
|
|
778
778
|
".git",
|
|
779
779
|
".hg",
|
|
780
780
|
".svn",
|
|
781
|
+
".aginti",
|
|
782
|
+
".aginti-work",
|
|
781
783
|
".aginti-sessions",
|
|
782
784
|
".agintiflow-home",
|
|
785
|
+
".sessions",
|
|
783
786
|
".cache",
|
|
784
787
|
".pytest_cache",
|
|
785
788
|
".ruff_cache",
|
|
@@ -790,9 +793,26 @@ const WORKSPACE_SKIP_NAMES = new Set([
|
|
|
790
793
|
"dist",
|
|
791
794
|
"node_modules",
|
|
792
795
|
]);
|
|
796
|
+
const WORKSPACE_PROTECTED_DIRS = new Set([".aginti", ".aginti-work", ".aginti-sessions", ".agintiflow-home", ".sessions", ".git", ".hg", ".svn"]);
|
|
797
|
+
const WORKSPACE_PROTECTED_FILES = new Set([".env", ".npmrc", ".pypirc", ".netrc"]);
|
|
793
798
|
const WORKSPACE_MAX_FILES = 1600;
|
|
794
799
|
const WORKSPACE_MAX_TEXT_BYTES = 512 * 1024;
|
|
795
800
|
|
|
801
|
+
function isProtectedEnvName(name = "") {
|
|
802
|
+
const value = String(name || "");
|
|
803
|
+
if (value === ".env") return true;
|
|
804
|
+
if (!value.startsWith(".env.")) return false;
|
|
805
|
+
return !/\.(example|sample|template)$/i.test(value);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function isProtectedWorkspacePath(relativePath = "") {
|
|
809
|
+
const normalized = String(relativePath || "")
|
|
810
|
+
.replace(/\\/g, "/")
|
|
811
|
+
.replace(/^\/+/, "");
|
|
812
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
813
|
+
return parts.some((part) => WORKSPACE_PROTECTED_DIRS.has(part) || WORKSPACE_PROTECTED_FILES.has(part) || isProtectedEnvName(part));
|
|
814
|
+
}
|
|
815
|
+
|
|
796
816
|
function isInsideDirectory(parent, candidate) {
|
|
797
817
|
const relative = path.relative(parent, candidate);
|
|
798
818
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
@@ -820,6 +840,9 @@ async function resolveWorkspaceFilePath(root, relativePath = "", { mustExist = f
|
|
|
820
840
|
.replace(/\\/g, "/")
|
|
821
841
|
.replace(/^\/+/, "");
|
|
822
842
|
if (!normalizedRelative || normalizedRelative.includes("\0")) throw new Error("Workspace file path is required.");
|
|
843
|
+
if (isProtectedWorkspacePath(normalizedRelative)) {
|
|
844
|
+
throw new Error("Workspace file is protected because it may contain secrets or AgInTiFlow internals.");
|
|
845
|
+
}
|
|
823
846
|
const absolutePath = path.resolve(root, normalizedRelative);
|
|
824
847
|
if (!isInsideDirectory(root, absolutePath)) throw new Error("Workspace file path escapes the selected folder.");
|
|
825
848
|
|
|
@@ -892,6 +915,7 @@ async function buildWorkspaceSnapshot(root) {
|
|
|
892
915
|
|
|
893
916
|
const absolutePath = path.join(currentDir, entry.name);
|
|
894
917
|
const relativePath = relativeWorkspacePath(root, absolutePath);
|
|
918
|
+
if (isProtectedWorkspacePath(relativePath)) continue;
|
|
895
919
|
let stat;
|
|
896
920
|
let realPath = absolutePath;
|
|
897
921
|
let symlink = false;
|