@lazyingart/agintiflow 0.20.249 → 0.20.250
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/package.json +7 -2
- package/scripts/smoke-coding-tools.js +205 -0
- package/scripts/smoke-docker-command.js +40 -0
- package/scripts/smoke-dynamic-step-budget.js +291 -0
- package/scripts/smoke-execution-policy.js +40 -0
- package/scripts/smoke-local-failure-recovery.js +26 -0
- package/scripts/smoke-model-roles.js +62 -0
- package/scripts/smoke-planning-timeout-recovery.js +572 -0
- package/scripts/smoke-progressive-tool-selection.js +192 -1
- package/scripts/smoke-toolchain-docker.js +10 -0
- package/scripts/smoke-truthful-completion.js +14 -0
- package/scripts/smoke-workspace-secret-false-positive.js +91 -0
- package/src/agent-runner.js +816 -98
- package/src/command-policy.js +67 -2
- package/src/config.js +2 -0
- package/src/docker-sandbox.js +78 -1
- package/src/execution-policy.js +8 -0
- package/src/local-failure-recovery.js +7 -4
- package/src/model-client.js +60 -1
- package/src/permission-advice.js +93 -1
- package/src/progressive-tool-selection.js +27 -0
- package/src/redaction.js +23 -2
- package/src/scs-controller.js +1 -0
- package/src/scs-evidence.js +75 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.250",
|
|
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",
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
"scripts/smoke-localllm-auto-max.js",
|
|
100
100
|
"scripts/smoke-localllm-code-routing.js",
|
|
101
101
|
"scripts/smoke-local-failure-recovery.js",
|
|
102
|
+
"scripts/smoke-planning-timeout-recovery.js",
|
|
102
103
|
"scripts/smoke-local-resource-policy.js",
|
|
103
104
|
"scripts/smoke-localllm-model-tiers.js",
|
|
104
105
|
"scripts/smoke-localllm-provider.js",
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
"scripts/smoke-truthful-completion.js",
|
|
107
108
|
"scripts/smoke-document-artifact-quality.js",
|
|
108
109
|
"scripts/smoke-writing-specialist-routing.js",
|
|
110
|
+
"scripts/smoke-workspace-secret-false-positive.js",
|
|
109
111
|
"scripts/eval-provider-attribution.js",
|
|
110
112
|
"scripts/local-first-agent-eval.mjs",
|
|
111
113
|
"scripts/fixtures/local-first-agent-eval-fixtures.mjs",
|
|
@@ -171,6 +173,7 @@
|
|
|
171
173
|
"smoke:localllm-auto-max": "node scripts/smoke-localllm-auto-max.js",
|
|
172
174
|
"smoke:localllm-code-routing": "node scripts/smoke-localllm-code-routing.js",
|
|
173
175
|
"smoke:local-failure-recovery": "node scripts/smoke-local-failure-recovery.js",
|
|
176
|
+
"smoke:planning-timeout-recovery": "node scripts/smoke-planning-timeout-recovery.js",
|
|
174
177
|
"smoke:local-resource-policy": "node scripts/smoke-local-resource-policy.js",
|
|
175
178
|
"smoke:context-budget-recovery": "node scripts/smoke-context-budget-recovery.js",
|
|
176
179
|
"smoke:localllm-model-tiers": "node scripts/smoke-localllm-model-tiers.js",
|
|
@@ -179,6 +182,7 @@
|
|
|
179
182
|
"smoke:truthful-completion": "node scripts/smoke-truthful-completion.js",
|
|
180
183
|
"smoke:document-artifact-quality": "node scripts/smoke-document-artifact-quality.js",
|
|
181
184
|
"smoke:writing-specialist-routing": "node scripts/smoke-writing-specialist-routing.js",
|
|
185
|
+
"smoke:workspace-secret-false-positive": "node scripts/smoke-workspace-secret-false-positive.js",
|
|
182
186
|
"smoke:mcp": "node scripts/smoke-mcp.js",
|
|
183
187
|
"smoke:model-roles": "node scripts/smoke-model-roles.js",
|
|
184
188
|
"smoke:platform": "node scripts/smoke-platform.js",
|
|
@@ -223,7 +227,8 @@
|
|
|
223
227
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
224
228
|
"publish:env": "node scripts/npm-publish-from-env.js publish --access public",
|
|
225
229
|
"publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
|
|
226
|
-
"
|
|
230
|
+
"pretest": "npm run smoke:planning-timeout-recovery",
|
|
231
|
+
"test": "npm run check && npm run smoke:integration-production-mount && npm run smoke:integration-production-runtime-bundle && npm run smoke:integration-retained-idempotency-store && npm run smoke:integration-storage-authority && npm run smoke:integration-retained-durable-common && npm run smoke:integration-retained-file-lock && npm run smoke:integration-retained-event-ledger && npm run smoke:integration-retained-event-ledger-bundle && npm run smoke:integration-retained-repository-kernel && npm run smoke:integration-retained-session-state-store && npm run smoke:integration-retained-native-session-repository-state && npm run smoke:integration-retained-native-execution-evidence && npm run smoke:integration-retained-runtime-repository-surface && npm run smoke:integration-retained-runtime-repository-phase-b && npm run smoke:integration-runtime-repository-contract && npm run smoke:integration-runtime-authority && npm run smoke:localllm-provider && npm run smoke:localllm-model-tiers && npm run smoke:localllm-code-routing && npm run smoke:local-failure-recovery && npm run smoke:localllm-auto-max && npm run smoke:local-resource-policy && npm run smoke:context-budget-recovery && npm run smoke:session-runtime && npm run smoke:runtime-core && npm run smoke:progressive-tools && npm run smoke:truthful-completion && npm run smoke:document-artifact-quality && npm run smoke:writing-specialist-routing && npm run smoke:workspace-secret-false-positive && npm run eval:local-first-agent && npm run eval:provider-attribution && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:math-rendering && npm run smoke:web-ui && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:execution-policy && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:deep-research && npm run smoke:public-research && npm run smoke:safe-chat && npm run smoke:auth && npm run smoke:agentlink && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:mcp && 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:supervision-ledger && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:run-stdin && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
227
232
|
"pack:dry-run": "npm pack --dry-run",
|
|
228
233
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
229
234
|
},
|
|
@@ -971,6 +971,55 @@ try {
|
|
|
971
971
|
const pythonDemoPolicy = evaluateCommandPolicy("python3 demo.py 2>&1", dockerWorkspaceNoInstallsPolicy);
|
|
972
972
|
assert(pythonDemoPolicy.allowed, "workspace-local python demo script should be allowed without package installs");
|
|
973
973
|
assert(pythonDemoPolicy.category === "toolchain", "workspace-local python demo script should be classified as toolchain");
|
|
974
|
+
const broadLocalProbePolicy = evaluateCommandPolicy(
|
|
975
|
+
'python3 --version; echo "---IMPORT TEST---"; python3 -c "import pathlib; print(pathlib.Path.cwd())" 2>&1; echo "---FILES---"; ls -la 2>&1 | head -20',
|
|
976
|
+
dockerWorkspaceNoInstallsPolicy
|
|
977
|
+
);
|
|
978
|
+
assert(
|
|
979
|
+
broadLocalProbePolicy.allowed && broadLocalProbePolicy.category === "general-shell",
|
|
980
|
+
"networkless general shell inside docker-workspace should not require package-install permission"
|
|
981
|
+
);
|
|
982
|
+
const broadLocalHostProbePolicy = evaluateCommandPolicy(
|
|
983
|
+
'python3 -c "import pathlib; print(pathlib.Path.cwd())"',
|
|
984
|
+
{ ...hostWorkspacePolicy, packageInstallPolicy: "block" }
|
|
985
|
+
);
|
|
986
|
+
assert(
|
|
987
|
+
!broadLocalHostProbePolicy.allowed,
|
|
988
|
+
"general shell on the host should remain blocked without trusted destructive access"
|
|
989
|
+
);
|
|
990
|
+
const explicitBlockedInstallPolicy = evaluateCommandPolicy(
|
|
991
|
+
"python3 -m pip install requests",
|
|
992
|
+
dockerWorkspaceNoInstallsPolicy
|
|
993
|
+
);
|
|
994
|
+
assert(
|
|
995
|
+
!explicitBlockedInstallPolicy.allowed && explicitBlockedInstallPolicy.category === "package-install",
|
|
996
|
+
"decoupling broad shell from install authorization must not allow explicit package installs"
|
|
997
|
+
);
|
|
998
|
+
const destructiveNoInstallsPolicy = evaluateCommandPolicy("rm -rf reports", dockerWorkspaceNoInstallsPolicy);
|
|
999
|
+
assert(
|
|
1000
|
+
!destructiveNoInstallsPolicy.allowed && destructiveNoInstallsPolicy.category === "destructive",
|
|
1001
|
+
"decoupling broad shell from install authorization must not weaken destructive-command guards"
|
|
1002
|
+
);
|
|
1003
|
+
const multiParagraphCommitPolicy = evaluateCommandPolicy(
|
|
1004
|
+
'git commit -m "Harden LabShare" -m "Fix path traversal, avoid shell injection, and preserve normal use."',
|
|
1005
|
+
dockerWorkspaceNoInstallsPolicy
|
|
1006
|
+
);
|
|
1007
|
+
assert(
|
|
1008
|
+
multiParagraphCommitPolicy.allowed &&
|
|
1009
|
+
multiParagraphCommitPolicy.category === "git-workflow" &&
|
|
1010
|
+
multiParagraphCommitPolicy.gitOnly === true,
|
|
1011
|
+
"a bounded multi-paragraph Git commit message should remain a Git-only workflow"
|
|
1012
|
+
);
|
|
1013
|
+
const boundedTaskCommitPolicy = evaluateCommandPolicy(
|
|
1014
|
+
"git add -- 'labshare.py' 'tests/test_labshare.py' 'SECURITY.md' && git commit -m 'Harden LabShare'",
|
|
1015
|
+
dockerWorkspaceNoInstallsPolicy
|
|
1016
|
+
);
|
|
1017
|
+
assert(
|
|
1018
|
+
boundedTaskCommitPolicy.allowed &&
|
|
1019
|
+
boundedTaskCommitPolicy.category === "git-workflow" &&
|
|
1020
|
+
boundedTaskCommitPolicy.gitOnly === true,
|
|
1021
|
+
"the task-owned add-and-commit routine should remain a bounded Git workflow"
|
|
1022
|
+
);
|
|
974
1023
|
const rVersionProbePolicy = evaluateCommandPolicy("R --version 2>&1", dockerWorkspaceNoInstallsPolicy);
|
|
975
1024
|
assert(rVersionProbePolicy.allowed, "R version probe should be allowed without package installs");
|
|
976
1025
|
assert(rVersionProbePolicy.category === "read-only", "R version probe should be classified as read-only");
|
|
@@ -1218,6 +1267,38 @@ try {
|
|
|
1218
1267
|
});
|
|
1219
1268
|
assert(permissionAdvice.suggestedCommand.includes("coding-policy-smoke"), "permission advice did not include resume session id");
|
|
1220
1269
|
assert(permissionAdvice.suggestedCommand.includes("--sandbox-mode docker-workspace"), "permission advice did not suggest docker-workspace recovery");
|
|
1270
|
+
const wrongWorkspaceCdAdvice = buildPermissionAdvice({
|
|
1271
|
+
toolName: "run_command",
|
|
1272
|
+
args: {
|
|
1273
|
+
command:
|
|
1274
|
+
"cd /home/example/project-parent && python3 -m unittest discover -s tests -v",
|
|
1275
|
+
},
|
|
1276
|
+
guard: {
|
|
1277
|
+
category: "blocked",
|
|
1278
|
+
reason:
|
|
1279
|
+
"cd target must be a safe workspace-relative directory: /home/example/project-parent",
|
|
1280
|
+
},
|
|
1281
|
+
config: {
|
|
1282
|
+
...dockerWorkspacePolicy,
|
|
1283
|
+
commandCwd: "/home/example/project-parent/current-project",
|
|
1284
|
+
},
|
|
1285
|
+
state: { sessionId: "coding-wrong-workspace-cd-smoke" },
|
|
1286
|
+
});
|
|
1287
|
+
assert(
|
|
1288
|
+
wrongWorkspaceCdAdvice.autoRecover === true,
|
|
1289
|
+
"an unnecessary outside-workspace cd should be corrected in-turn instead of pausing"
|
|
1290
|
+
);
|
|
1291
|
+
assert(
|
|
1292
|
+
/configured project root/i.test(wrongWorkspaceCdAdvice.instruction),
|
|
1293
|
+
"workspace cd correction did not direct the model back to the configured project root"
|
|
1294
|
+
);
|
|
1295
|
+
assert(
|
|
1296
|
+
!shouldPauseForPermissionAdvice({
|
|
1297
|
+
blocked: true,
|
|
1298
|
+
permissionAdvice: wrongWorkspaceCdAdvice,
|
|
1299
|
+
}),
|
|
1300
|
+
"recoverable workspace cd correction still paused the session"
|
|
1301
|
+
);
|
|
1221
1302
|
const destructiveAdvice = buildPermissionAdvice({
|
|
1222
1303
|
toolName: "run_command",
|
|
1223
1304
|
args: { command: "rm -rf reports && git reset --hard" },
|
|
@@ -1367,6 +1448,32 @@ try {
|
|
|
1367
1448
|
!shouldPauseForPermissionAdvice({ blocked: true, permissionAdvice: dynamicEvidenceAdvice }),
|
|
1368
1449
|
"dynamic evidence filename recovery still produced a permission pause"
|
|
1369
1450
|
);
|
|
1451
|
+
const malformedPatchGuard = {
|
|
1452
|
+
allowed: false,
|
|
1453
|
+
category: "workspace-patch",
|
|
1454
|
+
reason: "Patch did not contain any supported file operations.",
|
|
1455
|
+
permissionAdvice: {
|
|
1456
|
+
category: "workspace-patch",
|
|
1457
|
+
autoRecover: true,
|
|
1458
|
+
summary: "The patch format or exact context was not accepted; this is not a permission blocker.",
|
|
1459
|
+
instruction: "Inspect the exact file and retry with a supported focused edit.",
|
|
1460
|
+
},
|
|
1461
|
+
};
|
|
1462
|
+
const malformedPatchAdvice = buildPermissionAdvice({
|
|
1463
|
+
toolName: "apply_patch",
|
|
1464
|
+
args: { patch: "*** a/labshare.py\n*** b/labshare.py" },
|
|
1465
|
+
guard: malformedPatchGuard,
|
|
1466
|
+
config: dockerWorkspacePolicy,
|
|
1467
|
+
state: { sessionId: "coding-malformed-patch-smoke" },
|
|
1468
|
+
});
|
|
1469
|
+
assert(
|
|
1470
|
+
malformedPatchAdvice === malformedPatchGuard.permissionAdvice,
|
|
1471
|
+
"explicit recoverable workspace-patch advice was replaced by generic permission advice"
|
|
1472
|
+
);
|
|
1473
|
+
assert(
|
|
1474
|
+
!shouldPauseForPermissionAdvice({ blocked: true, permissionAdvice: malformedPatchAdvice }),
|
|
1475
|
+
"recoverable malformed patch advice still paused the session for permission"
|
|
1476
|
+
);
|
|
1370
1477
|
const destructiveDynamicEvidenceAdvice = buildPermissionAdvice({
|
|
1371
1478
|
toolName: "run_command",
|
|
1372
1479
|
args: {
|
|
@@ -1408,6 +1515,104 @@ try {
|
|
|
1408
1515
|
/once for each rendered page/i.test(documentPageBatchAdvice.instruction),
|
|
1409
1516
|
"Word document page batching did not recover into separate visual checks"
|
|
1410
1517
|
);
|
|
1518
|
+
const alreadyCommittedAdvice = buildFailedCommandAdvice({
|
|
1519
|
+
args: { command: "git commit -m 'Focused repair'" },
|
|
1520
|
+
commandPolicy: evaluateCommandPolicy("git commit -m 'Focused repair'", dockerWorkspacePolicy),
|
|
1521
|
+
commandResult: {
|
|
1522
|
+
ok: false,
|
|
1523
|
+
exitCode: 1,
|
|
1524
|
+
stdout: "On branch main\nnothing to commit, working tree clean\n",
|
|
1525
|
+
},
|
|
1526
|
+
config: dockerWorkspacePolicy,
|
|
1527
|
+
state: {
|
|
1528
|
+
meta: {
|
|
1529
|
+
goalContract: { revision: 9 },
|
|
1530
|
+
projectVerification: { mutationRevision: 4 },
|
|
1531
|
+
durableGitEvidence: [{ action: "commit", goalRevision: 9, mutationRevision: 4 }],
|
|
1532
|
+
},
|
|
1533
|
+
},
|
|
1534
|
+
});
|
|
1535
|
+
assert(
|
|
1536
|
+
alreadyCommittedAdvice?.failureKind === "repository-already-committed" &&
|
|
1537
|
+
alreadyCommittedAdvice.autoRecover === true &&
|
|
1538
|
+
/should not be retried/i.test(alreadyCommittedAdvice.summary),
|
|
1539
|
+
"a clean no-op commit with current durable evidence did not converge"
|
|
1540
|
+
);
|
|
1541
|
+
const sameTaskContinuationAdvice = buildFailedCommandAdvice({
|
|
1542
|
+
args: { command: "git commit -m 'Focused repair'" },
|
|
1543
|
+
commandPolicy: evaluateCommandPolicy("git commit -m 'Focused repair'", dockerWorkspacePolicy),
|
|
1544
|
+
commandResult: {
|
|
1545
|
+
ok: false,
|
|
1546
|
+
exitCode: 1,
|
|
1547
|
+
stdout: "On branch main\nnothing to commit, working tree clean\n",
|
|
1548
|
+
},
|
|
1549
|
+
config: dockerWorkspacePolicy,
|
|
1550
|
+
state: {
|
|
1551
|
+
meta: {
|
|
1552
|
+
goalContract: {
|
|
1553
|
+
revision: 10,
|
|
1554
|
+
history: [
|
|
1555
|
+
{ revision: 9, taskHash: "task-a" },
|
|
1556
|
+
{ revision: 10, taskHash: "task-a" },
|
|
1557
|
+
],
|
|
1558
|
+
},
|
|
1559
|
+
projectVerification: { mutationRevision: 4 },
|
|
1560
|
+
durableGitEvidence: [{ action: "commit", goalRevision: 9, mutationRevision: 4 }],
|
|
1561
|
+
},
|
|
1562
|
+
},
|
|
1563
|
+
});
|
|
1564
|
+
assert(
|
|
1565
|
+
sameTaskContinuationAdvice?.failureKind === "repository-already-committed",
|
|
1566
|
+
"a same-task continuation invalidated a clean commit at the current mutation revision"
|
|
1567
|
+
);
|
|
1568
|
+
const newTaskAdvice = buildFailedCommandAdvice({
|
|
1569
|
+
args: { command: "git commit -m 'Focused repair'" },
|
|
1570
|
+
commandPolicy: evaluateCommandPolicy("git commit -m 'Focused repair'", dockerWorkspacePolicy),
|
|
1571
|
+
commandResult: {
|
|
1572
|
+
ok: false,
|
|
1573
|
+
exitCode: 1,
|
|
1574
|
+
stdout: "On branch main\nnothing to commit, working tree clean\n",
|
|
1575
|
+
},
|
|
1576
|
+
config: dockerWorkspacePolicy,
|
|
1577
|
+
state: {
|
|
1578
|
+
meta: {
|
|
1579
|
+
goalContract: {
|
|
1580
|
+
revision: 10,
|
|
1581
|
+
history: [
|
|
1582
|
+
{ revision: 9, taskHash: "task-a" },
|
|
1583
|
+
{ revision: 10, taskHash: "task-b" },
|
|
1584
|
+
],
|
|
1585
|
+
},
|
|
1586
|
+
projectVerification: { mutationRevision: 4 },
|
|
1587
|
+
durableGitEvidence: [{ action: "commit", goalRevision: 9, mutationRevision: 4 }],
|
|
1588
|
+
},
|
|
1589
|
+
},
|
|
1590
|
+
});
|
|
1591
|
+
assert(
|
|
1592
|
+
newTaskAdvice === null,
|
|
1593
|
+
"a commit from a prior task satisfied a genuinely new task"
|
|
1594
|
+
);
|
|
1595
|
+
const staleCommitAdvice = buildFailedCommandAdvice({
|
|
1596
|
+
args: { command: "git commit -m 'Focused repair'" },
|
|
1597
|
+
commandPolicy: evaluateCommandPolicy("git commit -m 'Focused repair'", dockerWorkspacePolicy),
|
|
1598
|
+
commandResult: {
|
|
1599
|
+
ok: false,
|
|
1600
|
+
exitCode: 1,
|
|
1601
|
+
stdout: "On branch main\nnothing to commit, working tree clean\n",
|
|
1602
|
+
},
|
|
1603
|
+
config: dockerWorkspacePolicy,
|
|
1604
|
+
state: {
|
|
1605
|
+
meta: {
|
|
1606
|
+
goalContract: { revision: 9 },
|
|
1607
|
+
projectVerification: { mutationRevision: 4 },
|
|
1608
|
+
durableGitEvidence: [{ action: "commit", goalRevision: 8, mutationRevision: 4 }],
|
|
1609
|
+
},
|
|
1610
|
+
},
|
|
1611
|
+
});
|
|
1612
|
+
assert(
|
|
1613
|
+
staleCommitAdvice === null,
|
|
1614
|
+
"a stale commit from an earlier goal revision satisfied a fresh commit request"
|
|
1615
|
+
);
|
|
1411
1616
|
const failedNetworkAdvice = buildFailedCommandAdvice({
|
|
1412
1617
|
args: { command: "git clone https://github.com/lazyingart/AgInTiFlow.git" },
|
|
1413
1618
|
commandPolicy: clonePolicy,
|
|
@@ -6,12 +6,52 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
|
+
import {
|
|
10
|
+
dockerWorkspaceAliasMounts,
|
|
11
|
+
ensureDockerGitIdentity,
|
|
12
|
+
} from "../src/docker-sandbox.js";
|
|
9
13
|
|
|
10
14
|
const execFileAsync = promisify(execFile);
|
|
11
15
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
16
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-docker-command-"));
|
|
13
17
|
|
|
14
18
|
try {
|
|
19
|
+
const identityRepo = path.join(tempRoot, "identity-repo");
|
|
20
|
+
const dockerState = path.join(tempRoot, "docker-state");
|
|
21
|
+
await fs.mkdir(identityRepo, { recursive: true });
|
|
22
|
+
await fs.mkdir(path.join(dockerState, "home"), { recursive: true });
|
|
23
|
+
await execFileAsync("git", ["init", identityRepo]);
|
|
24
|
+
await execFileAsync("git", ["-C", identityRepo, "config", "user.name", "AgInTi Test Author"]);
|
|
25
|
+
await execFileAsync("git", ["-C", identityRepo, "config", "user.email", "aginti-test@example.invalid"]);
|
|
26
|
+
assert.deepEqual(
|
|
27
|
+
dockerWorkspaceAliasMounts({ commandCwd: identityRepo, sandboxMode: "docker-workspace" }),
|
|
28
|
+
[identityRepo],
|
|
29
|
+
"Docker workspace mode should preserve the exact host workspace path as a scoped writable alias"
|
|
30
|
+
);
|
|
31
|
+
assert.deepEqual(
|
|
32
|
+
dockerWorkspaceAliasMounts({ commandCwd: identityRepo, sandboxMode: "docker-readonly" }),
|
|
33
|
+
[],
|
|
34
|
+
"Read-only Docker mode must not create a writable host-path alias"
|
|
35
|
+
);
|
|
36
|
+
const identity = await ensureDockerGitIdentity(
|
|
37
|
+
{ commandCwd: identityRepo, dockerStateDir: dockerState },
|
|
38
|
+
{
|
|
39
|
+
root: dockerState,
|
|
40
|
+
home: path.join(dockerState, "home"),
|
|
41
|
+
cache: path.join(dockerState, "cache"),
|
|
42
|
+
env: path.join(dockerState, "env"),
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
assert.equal(identity.configured, true, "Docker Git identity should reuse an existing repository author");
|
|
46
|
+
const identityConfig = path.join(dockerState, "home", ".gitconfig.aginti-author");
|
|
47
|
+
const [storedName, storedEmail] = await Promise.all([
|
|
48
|
+
execFileAsync("git", ["config", "--file", identityConfig, "--get", "user.name"]),
|
|
49
|
+
execFileAsync("git", ["config", "--file", identityConfig, "--get", "user.email"]),
|
|
50
|
+
]);
|
|
51
|
+
assert.equal(storedName.stdout.trim(), "AgInTi Test Author");
|
|
52
|
+
assert.equal(storedEmail.stdout.trim(), "aginti-test@example.invalid");
|
|
53
|
+
assert.equal((await fs.stat(identityConfig)).mode & 0o777, 0o600, "Docker author config should be private");
|
|
54
|
+
|
|
15
55
|
const { stdout } = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "docker", "status", "--json", "--cwd", tempRoot], {
|
|
16
56
|
cwd: tempRoot,
|
|
17
57
|
env: {
|