@zaip-ai/cli 4.2.0 → 4.3.0
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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/services/adapters.d.ts +2 -2
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +11 -63
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/agent-adapters.d.ts.map +1 -1
- package/dist/services/agent-adapters.js +3 -7
- package/dist/services/agent-adapters.js.map +1 -1
- package/dist/services/agent-adapters.test.js +43 -11
- package/dist/services/agent-adapters.test.js.map +1 -1
- package/dist/services/codex-config-template.js +2 -2
- package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.js +38 -14
- package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
- package/dist/services/codex-hook-activation.d.ts +4 -0
- package/dist/services/codex-hook-activation.d.ts.map +1 -0
- package/dist/services/codex-hook-activation.js +64 -0
- package/dist/services/codex-hook-activation.js.map +1 -0
- package/dist/services/codex-model-policy.d.ts +4 -0
- package/dist/services/codex-model-policy.d.ts.map +1 -0
- package/dist/services/codex-model-policy.js +24 -0
- package/dist/services/codex-model-policy.js.map +1 -0
- package/dist/services/codex-native-tool-authority-template.d.ts +2 -0
- package/dist/services/codex-native-tool-authority-template.d.ts.map +1 -0
- package/dist/services/codex-native-tool-authority-template.js +89 -0
- package/dist/services/codex-native-tool-authority-template.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +59 -86
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +200 -1
- package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
- package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-support-template.js +303 -12
- package/dist/services/codex-pre-tool-support-template.js.map +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
- package/dist/services/codex-subagent-hook-template.js +11 -4
- package/dist/services/codex-subagent-hook-template.js.map +1 -1
- package/dist/services/codex-validation-runtime-template.d.ts +3 -0
- package/dist/services/codex-validation-runtime-template.d.ts.map +1 -0
- package/dist/services/codex-validation-runtime-template.js +18 -0
- package/dist/services/codex-validation-runtime-template.js.map +1 -0
- package/dist/services/codex-windows-hook-command.js +1 -1
- package/dist/services/codex-windows-hook-command.js.map +1 -1
- package/dist/services/codex-windows-hook-command.test.js +22 -0
- package/dist/services/codex-windows-hook-command.test.js.map +1 -1
- package/dist/services/global-codex-config-fixtures.d.ts +12 -0
- package/dist/services/global-codex-config-fixtures.d.ts.map +1 -0
- package/dist/services/global-codex-config-fixtures.js +38 -0
- package/dist/services/global-codex-config-fixtures.js.map +1 -0
- package/dist/services/global-codex-config-migration.test.d.ts +2 -0
- package/dist/services/global-codex-config-migration.test.d.ts.map +1 -0
- package/dist/services/global-codex-config-migration.test.js +301 -0
- package/dist/services/global-codex-config-migration.test.js.map +1 -0
- package/dist/services/global-codex-config-transaction.d.ts +15 -0
- package/dist/services/global-codex-config-transaction.d.ts.map +1 -0
- package/dist/services/global-codex-config-transaction.js +89 -0
- package/dist/services/global-codex-config-transaction.js.map +1 -0
- package/dist/services/global-codex-config-transaction.test.d.ts +2 -0
- package/dist/services/global-codex-config-transaction.test.d.ts.map +1 -0
- package/dist/services/global-codex-config-transaction.test.js +145 -0
- package/dist/services/global-codex-config-transaction.test.js.map +1 -0
- package/dist/services/global-codex-config.d.ts +10 -11
- package/dist/services/global-codex-config.d.ts.map +1 -1
- package/dist/services/global-codex-config.js +225 -107
- package/dist/services/global-codex-config.js.map +1 -1
- package/dist/services/global-codex-config.test.js +6 -6
- package/dist/services/global-codex-config.test.js.map +1 -1
- package/dist/services/hook-readiness.d.ts +7 -0
- package/dist/services/hook-readiness.d.ts.map +1 -0
- package/dist/services/hook-readiness.js +17 -0
- package/dist/services/hook-readiness.js.map +1 -0
- package/dist/services/logger.d.ts +2 -0
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +15 -1
- package/dist/services/logger.js.map +1 -1
- package/dist/services/logger.test.js +3 -0
- package/dist/services/logger.test.js.map +1 -1
- package/dist/services/managed-adapter.d.ts +6 -0
- package/dist/services/managed-adapter.d.ts.map +1 -0
- package/dist/services/managed-adapter.js +63 -0
- package/dist/services/managed-adapter.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README.md +8 -0
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +1 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -2
- package/templates/.agents/dashboard/.zaip-managed.json +40 -32
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-DlN5EMmD.js → DashboardHelpDialog-CkQ_y8kg.js} +60 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BRdoeTWk.js → ProjectSettingsPage-doIMhei7.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-B_5u4Xnp.js → TraceabilityWorkspace-C_4ftjyB.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-C4rLXrz_.js → index-Rbmpz4UO.js} +2 -2
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +1 -1
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +175 -175
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +135 -135
- package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +175 -175
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operation-detail.test.ts +106 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +0 -13
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-every-root-execution-day-returns.test.ts +17 -230
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-fixture.test-support.ts +239 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +28 -8
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/astra-diagnostic.md +1 -1
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +627 -177
- package/templates/.agents/runtime/checksums.sha256 +5 -5
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/provenance.json +4 -4
- package/templates/.agents/runtime/sbom.cdx.json +1 -1
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-execution/references/correction-cycle.md +26 -0
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +2 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +31 -1
- package/templates/.agents/skills/zaip-skill-setup/SKILL.md +26 -1
- package/templates/.agents/skills/zaip-skill-task/references/commit-policy.md +8 -1
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +52 -1
- package/templates/template-manifest.json +95 -87
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { link, open, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { installSecurityHook, reconcileGlobalCodexConfig, } from "./global-codex-config.js";
|
|
4
|
+
import { cleanupProjectFixtures, nodeFileSystem, projectFixture, } from "./global-codex-config-fixtures.js";
|
|
5
|
+
afterEach(cleanupProjectFixtures);
|
|
6
|
+
describe("global Codex config transaction concurrency", () => {
|
|
7
|
+
it.each([
|
|
8
|
+
"before return",
|
|
9
|
+
"after return",
|
|
10
|
+
])("retains saves through an open original handle %s", async (stage) => {
|
|
11
|
+
const fixture = await projectFixture();
|
|
12
|
+
const original = installSecurityHook('model = "original"\n');
|
|
13
|
+
const newer = 'model = "saved through original handle"\n';
|
|
14
|
+
await writeFile(fixture.configPath, original);
|
|
15
|
+
const handle = await open(fixture.configPath, "r+");
|
|
16
|
+
const save = async () => {
|
|
17
|
+
await handle.truncate(0);
|
|
18
|
+
await handle.write(newer, 0, "utf8");
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
const filesystem = {
|
|
22
|
+
...nodeFileSystem,
|
|
23
|
+
link: async (...args) => {
|
|
24
|
+
if (stage === "before return" && String(args[0]).includes(".zaip-tmp-"))
|
|
25
|
+
await save();
|
|
26
|
+
await link(...args);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const result = await reconcileGlobalCodexConfig(fixture.codexHome, fixture.catalogPath, filesystem);
|
|
30
|
+
if (stage === "after return") {
|
|
31
|
+
expect(result.installed).toBe(true);
|
|
32
|
+
await save();
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
expect(result.installed).toBe(false);
|
|
36
|
+
expect(result.error).toContain("Concurrent save detected");
|
|
37
|
+
}
|
|
38
|
+
const preserved = (await readdir(fixture.codexHome)).filter((name) => name.startsWith("config.toml.zaip-saved-"));
|
|
39
|
+
expect(preserved).toHaveLength(1);
|
|
40
|
+
expect(await readFile(`${fixture.codexHome}/${preserved[0]}`, "utf8")).toBe(newer);
|
|
41
|
+
expect(await readFile(fixture.configPath, "utf8")).toBe('model = "original"\n\n');
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await handle.close();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
it.each([
|
|
48
|
+
"promotion",
|
|
49
|
+
"rollback",
|
|
50
|
+
])("preserves a concurrent editor save during %s", async (stage) => {
|
|
51
|
+
const fixture = await projectFixture();
|
|
52
|
+
const original = installSecurityHook('model = "original"\n');
|
|
53
|
+
const newer = 'model = "newer user setting"\n';
|
|
54
|
+
await writeFile(fixture.configPath, original);
|
|
55
|
+
const filesystem = {
|
|
56
|
+
...nodeFileSystem,
|
|
57
|
+
link: vi.fn(async (...args) => {
|
|
58
|
+
const promoting = String(args[0]).includes(".zaip-tmp-");
|
|
59
|
+
if (stage === "promotion" && promoting)
|
|
60
|
+
await writeFile(fixture.configPath, newer);
|
|
61
|
+
if (stage === "rollback") {
|
|
62
|
+
if (promoting)
|
|
63
|
+
throw new Error("promotion fault");
|
|
64
|
+
await writeFile(fixture.configPath, newer);
|
|
65
|
+
}
|
|
66
|
+
await link(...args);
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
const result = await reconcileGlobalCodexConfig(fixture.codexHome, fixture.catalogPath, filesystem);
|
|
70
|
+
expect(result.installed).toBe(false);
|
|
71
|
+
expect(result.error).toContain("Concurrent global config preserved");
|
|
72
|
+
expect(await readFile(fixture.configPath, "utf8")).toBe(newer);
|
|
73
|
+
expect(await readFile(`${fixture.configPath}.zaip-backup-${process.pid}`, "utf8")).toBe(original);
|
|
74
|
+
expect((await readdir(fixture.codexHome)).sort()).toEqual([
|
|
75
|
+
"config.toml",
|
|
76
|
+
`config.toml.zaip-backup-${process.pid}`,
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
it.each([
|
|
80
|
+
"changed",
|
|
81
|
+
"appeared",
|
|
82
|
+
"disappeared",
|
|
83
|
+
])("preserves a config that %s after the initial read", async (scenario) => {
|
|
84
|
+
const fixture = await projectFixture();
|
|
85
|
+
const original = Buffer.from(installSecurityHook('model = "original"\r\n'));
|
|
86
|
+
const newer = Buffer.from('\uFEFFmodel = "newer"\r\n[desktop]\r\nlabel = "ação concorrente"\r\n');
|
|
87
|
+
if (scenario === "appeared") {
|
|
88
|
+
// Force a fallback write when there is no initial global config to migrate.
|
|
89
|
+
await writeFile(fixture.projectConfigPath, "zaip:\n enabled: false\n");
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
await writeFile(fixture.configPath, original);
|
|
93
|
+
}
|
|
94
|
+
const filesystem = {
|
|
95
|
+
...nodeFileSystem,
|
|
96
|
+
rename: vi.fn(async (...args) => {
|
|
97
|
+
if (args[0] === fixture.configPath) {
|
|
98
|
+
if (scenario === "disappeared")
|
|
99
|
+
await rm(fixture.configPath);
|
|
100
|
+
else
|
|
101
|
+
await writeFile(fixture.configPath, newer);
|
|
102
|
+
}
|
|
103
|
+
await rename(...args);
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
const result = await reconcileGlobalCodexConfig(fixture.codexHome, fixture.catalogPath, filesystem);
|
|
107
|
+
if (scenario === "appeared") {
|
|
108
|
+
// Repository-only policy has no write to race when global config is absent.
|
|
109
|
+
expect(result).toMatchObject({ installed: true, securityHookInstalled: false, error: null });
|
|
110
|
+
expect(filesystem.rename).not.toHaveBeenCalled();
|
|
111
|
+
await expect(readFile(fixture.configPath)).rejects.toMatchObject({ code: "ENOENT" });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
expect(result).toMatchObject({
|
|
115
|
+
installed: false,
|
|
116
|
+
securityHookInstalled: false,
|
|
117
|
+
securityHookOwner: null,
|
|
118
|
+
});
|
|
119
|
+
expect(result.error).toContain("concurrently");
|
|
120
|
+
if (scenario === "disappeared") {
|
|
121
|
+
await expect(readFile(fixture.configPath)).rejects.toMatchObject({ code: "ENOENT" });
|
|
122
|
+
expect(await readdir(fixture.codexHome)).toEqual([]);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
expect(result.error).toContain("newer config restored");
|
|
126
|
+
expect(await readFile(fixture.configPath)).toEqual(newer);
|
|
127
|
+
expect(await readdir(fixture.codexHome)).toEqual(["config.toml"]);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
it("preserves non-UTF-8 user bytes instead of rewriting a lossy decoding", async () => {
|
|
131
|
+
const fixture = await projectFixture();
|
|
132
|
+
const original = Buffer.from([0xff, 0xfe, 0x61, 0x00]);
|
|
133
|
+
await writeFile(fixture.configPath, original);
|
|
134
|
+
const result = await reconcileGlobalCodexConfig(fixture.codexHome, fixture.catalogPath);
|
|
135
|
+
expect(result).toMatchObject({
|
|
136
|
+
installed: false,
|
|
137
|
+
securityHookInstalled: false,
|
|
138
|
+
securityHookOwner: null,
|
|
139
|
+
});
|
|
140
|
+
expect(result.error).not.toBeNull();
|
|
141
|
+
expect(await readFile(fixture.configPath)).toEqual(original);
|
|
142
|
+
expect(await readdir(fixture.codexHome)).toEqual(["config.toml"]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=global-codex-config-transaction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-codex-config-transaction.test.js","sourceRoot":"","sources":["../../src/services/global-codex-config-transaction.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAEL,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,cAAc,GACf,MAAM,mCAAmC,CAAC;AAE3C,SAAS,CAAC,sBAAsB,CAAC,CAAC;AAElC,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,IAAI,CAAC;QACN,eAAe;QACf,cAAc;KACf,CAAC,CAAC,kDAAkD,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACrE,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,2CAA2C,CAAC;QAC1D,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC;QACF,IAAI,CAAC;YACH,MAAM,UAAU,GAAgC;gBAC9C,GAAG,cAAc;gBACjB,IAAI,EAAE,KAAK,EAAE,GAAG,IAA6B,EAAE,EAAE;oBAC/C,IAAI,KAAK,KAAK,eAAe,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;wBAAE,MAAM,IAAI,EAAE,CAAC;oBACtF,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;gBACtB,CAAC;aACF,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAC7C,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,WAAW,EACnB,UAAU,CACX,CAAC;YACF,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;gBAC7B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,IAAI,EAAE,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,SAAS,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACnE,IAAI,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAC3C,CAAC;YACF,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,QAAQ,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnF,MAAM,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACpF,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,IAAI,CAAC;QACN,WAAW;QACX,UAAU;KACX,CAAC,CAAC,8CAA8C,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACjE,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,gCAAgC,CAAC;QAC/C,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAgC;YAC9C,GAAG,cAAc;YACjB,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,IAA6B,EAAE,EAAE;gBACrD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;gBACzD,IAAI,KAAK,KAAK,WAAW,IAAI,SAAS;oBAAE,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBACnF,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;oBACzB,IAAI,SAAS;wBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;oBAClD,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAC7C,CAAC;gBACD,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YACtB,CAAC,CAAC;SACH,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAC7C,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,WAAW,EACnB,UAAU,CACX,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,QAAQ,CAAC,GAAG,OAAO,CAAC,UAAU,gBAAgB,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CACrF,QAAQ,CACT,CAAC;QACF,MAAM,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;YACxD,aAAa;YACb,2BAA2B,OAAO,CAAC,GAAG,EAAE;SACzC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,IAAI,CAAC;QACN,SAAS;QACT,UAAU;QACV,aAAa;KACd,CAAC,CAAC,mDAAmD,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QACzE,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CACvB,sEAAsE,CACvE,CAAC;QACF,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;YAC5B,4EAA4E;YAC5E,MAAM,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,2BAA2B,CAAC,CAAC;QAC1E,CAAC;aAAM,CAAC;YACN,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,UAAU,GAAgC;YAC9C,GAAG,cAAc;YACjB,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,IAA+B,EAAE,EAAE;gBACzD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC;oBACnC,IAAI,QAAQ,KAAK,aAAa;wBAAE,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;;wBACxD,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAClD,CAAC;gBACD,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;YACxB,CAAC,CAAC;SACH,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAC7C,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,WAAW,EACnB,UAAU,CACX,CAAC;QACF,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;YAC5B,4EAA4E;YAC5E,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7F,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACjD,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACrF,OAAO;QACT,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC3B,SAAS,EAAE,KAAK;YAChB,qBAAqB,EAAE,KAAK;YAC5B,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC/C,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;YAC/B,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACrF,MAAM,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC1D,MAAM,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACxF,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC3B,SAAS,EAAE,KAAK;YAChB,qBAAqB,EAAE,KAAK;YAC5B,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type GlobalCodexConfigFileSystem } from "./global-codex-config-transaction.js";
|
|
2
|
+
export type { GlobalCodexConfigFileSystem } from "./global-codex-config-transaction.js";
|
|
2
3
|
export interface GlobalCodexConfigResult {
|
|
3
4
|
path: string;
|
|
4
5
|
sourcePath: string;
|
|
@@ -6,21 +7,19 @@ export interface GlobalCodexConfigResult {
|
|
|
6
7
|
agentFilesInstalled: number;
|
|
7
8
|
removedRegistrations: number;
|
|
8
9
|
securityHookInstalled: boolean;
|
|
10
|
+
securityHookOwner: "project" | "global" | null;
|
|
11
|
+
projectHookFinding?: string | null;
|
|
12
|
+
backupPath: string | null;
|
|
9
13
|
error: string | null;
|
|
10
14
|
}
|
|
11
|
-
export interface GlobalCodexConfigFileSystem {
|
|
12
|
-
mkdir: typeof mkdir;
|
|
13
|
-
readFile: typeof readFile;
|
|
14
|
-
rename: typeof rename;
|
|
15
|
-
rm: typeof rm;
|
|
16
|
-
writeFile: typeof writeFile;
|
|
17
|
-
}
|
|
18
15
|
/**
|
|
19
16
|
* Removes only catalog-declared legacy Codex identity aliases and installs the
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* repository-only hook policy. Never installs a global fallback.
|
|
18
|
+
* All unrelated global configuration remains byte-for-byte intact.
|
|
22
19
|
*/
|
|
23
|
-
export declare function reconcileGlobalCodexConfig(codexHome?: string, sourcePath?: string, filesystem?: GlobalCodexConfigFileSystem
|
|
20
|
+
export declare function reconcileGlobalCodexConfig(codexHome?: string, sourcePath?: string, filesystem?: GlobalCodexConfigFileSystem, options?: {
|
|
21
|
+
migrateLegacyAgents?: boolean;
|
|
22
|
+
}): Promise<GlobalCodexConfigResult>;
|
|
24
23
|
export declare function installSecurityHook(config: string): string;
|
|
25
24
|
export declare function stripLegacyRegistrations(config: string, legacyAgentNames?: readonly string[]): {
|
|
26
25
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-codex-config.d.ts","sourceRoot":"","sources":["../../src/services/global-codex-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"global-codex-config.d.ts","sourceRoot":"","sources":["../../src/services/global-codex-config.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,2BAA2B,EAIjC,MAAM,sCAAsC,CAAC;AAE9C,YAAY,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAExF,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,iBAAiB,EAAE,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAQD;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,SAAS,SAAsD,EAC/D,UAAU,SAAiD,EAC3D,UAAU,GAAE,2BAA4C,EACxD,OAAO,GAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9C,OAAO,CAAC,uBAAuB,CAAC,CAqElC;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAS1D;AAyGD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,gBAAgB,GAAE,SAAS,MAAM,EAAO,GACvC;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAMA"}
|
|
@@ -1,56 +1,73 @@
|
|
|
1
|
-
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
1
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
3
|
import { parse } from "yaml";
|
|
4
|
+
import { adapterFiles } from "./adapters.js";
|
|
5
5
|
import { CODEX_PRE_TOOL_USE_HOOK } from "./codex-config-template.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
".codex/hooks/zaip-conversation-lifecycle.mjs",
|
|
13
|
-
".codex/hooks/zaip-subagent-lifecycle.mjs",
|
|
14
|
-
'statusMessage = "Applying ZAIP workspace safety policy"',
|
|
15
|
-
'statusMessage = "Reconciling ZAIP task closure and telemetry"',
|
|
16
|
-
'statusMessage = "Attributing ZAIP subagent Run"',
|
|
17
|
-
'statusMessage = "Finalizing ZAIP subagent Run"',
|
|
18
|
-
];
|
|
6
|
+
import { codexWindowsHookCommand } from "./codex-windows-hook-command.js";
|
|
7
|
+
import { assertNoConfigBackup, isMissingFile, nodeFileSystem, replaceConfig, } from "./global-codex-config-transaction.js";
|
|
8
|
+
const ANY_TOML_TABLE = /^\s*\[\[?[^\r\n]+\]\]?\s*(?:#.*)?$/;
|
|
9
|
+
const AGENT_TABLE = /^\s*\[agents\."([^"]+)"\]\s*(?:#.*)?$/;
|
|
10
|
+
const HOOK_TABLE = /^\s*\[\[hooks\.(PreToolUse|Stop|SubagentStart|SubagentStop)\]\]\s*(?:#.*)?$/;
|
|
11
|
+
const ZAIP_HOOK_PATH = /(?:^|[/\\])\.codex[/\\]hooks[/\\]zaip-(?:pre-tool-use|conversation-lifecycle|subagent-lifecycle)\.mjs$/;
|
|
19
12
|
/**
|
|
20
13
|
* Removes only catalog-declared legacy Codex identity aliases and installs the
|
|
21
|
-
*
|
|
22
|
-
*
|
|
14
|
+
* repository-only hook policy. Never installs a global fallback.
|
|
15
|
+
* All unrelated global configuration remains byte-for-byte intact.
|
|
23
16
|
*/
|
|
24
|
-
export async function reconcileGlobalCodexConfig(codexHome = process.env.CODEX_HOME ?? join(homedir(), ".codex"), sourcePath = "<project>/.agents/config/agents/catalog.yaml", filesystem = nodeFileSystem) {
|
|
17
|
+
export async function reconcileGlobalCodexConfig(codexHome = process.env.CODEX_HOME ?? join(homedir(), ".codex"), sourcePath = "<project>/.agents/config/agents/catalog.yaml", filesystem = nodeFileSystem, options = {}) {
|
|
25
18
|
const path = join(codexHome, "config.toml");
|
|
26
|
-
const temporary = `${path}.zaip-tmp-${process.pid}`;
|
|
27
19
|
const backup = `${path}.zaip-backup-${process.pid}`;
|
|
28
20
|
try {
|
|
29
|
-
const
|
|
21
|
+
const homeInfo = await filesystem.lstat(codexHome).catch((error) => {
|
|
30
22
|
if (isMissingFile(error))
|
|
31
23
|
return null;
|
|
32
24
|
throw error;
|
|
33
25
|
});
|
|
34
|
-
|
|
26
|
+
if (homeInfo && (!homeInfo.isDirectory() || homeInfo.isSymbolicLink()))
|
|
27
|
+
throw new Error(`ENOTDIR: Codex home must be a real directory: ${codexHome}`);
|
|
28
|
+
const fileInfo = await filesystem.lstat(path).catch((error) => {
|
|
29
|
+
if (isMissingFile(error))
|
|
30
|
+
return null;
|
|
31
|
+
throw error;
|
|
32
|
+
});
|
|
33
|
+
if (fileInfo && (!fileInfo.isFile() || fileInfo.isSymbolicLink() || fileInfo.nlink !== 1))
|
|
34
|
+
throw new Error(`Global Codex config must be a regular unlinked file: ${path}`);
|
|
35
|
+
await assertNoConfigBackup(backup, filesystem);
|
|
36
|
+
const original = await filesystem.readFile(path).catch((error) => {
|
|
37
|
+
if (isMissingFile(error))
|
|
38
|
+
return null;
|
|
39
|
+
throw error;
|
|
40
|
+
});
|
|
41
|
+
const current = original === null
|
|
42
|
+
? null
|
|
43
|
+
: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(original);
|
|
44
|
+
const legacyAgentNames = options.migrateLegacyAgents === false
|
|
45
|
+
? [] : await catalogedCodexAliases(sourcePath, filesystem);
|
|
35
46
|
const { content: stripped, removedRegistrations } = current === null
|
|
36
47
|
? { content: "", removedRegistrations: 0 }
|
|
37
48
|
: stripLegacyRegistrations(current, legacyAgentNames);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
49
|
+
let projectHookFinding = null;
|
|
50
|
+
const projectHooksVerified = await hasCanonicalProjectHooks(sourcePath, filesystem, (reason) => {
|
|
51
|
+
projectHookFinding = reason;
|
|
52
|
+
});
|
|
53
|
+
const content = stripZaipHooks(stripped);
|
|
54
|
+
const backupPath = content !== (current ?? "")
|
|
55
|
+
? await replaceConfig(path, content, original, filesystem, fileInfo?.mode)
|
|
56
|
+
: null;
|
|
42
57
|
return {
|
|
43
58
|
path,
|
|
44
59
|
sourcePath,
|
|
45
60
|
installed: true,
|
|
46
61
|
agentFilesInstalled: 0,
|
|
47
62
|
removedRegistrations,
|
|
48
|
-
securityHookInstalled:
|
|
63
|
+
securityHookInstalled: projectHooksVerified,
|
|
64
|
+
securityHookOwner: projectHooksVerified ? "project" : null,
|
|
65
|
+
projectHookFinding,
|
|
66
|
+
backupPath,
|
|
49
67
|
error: null,
|
|
50
68
|
};
|
|
51
69
|
}
|
|
52
70
|
catch (error) {
|
|
53
|
-
await filesystem.rm(temporary, { force: true }).catch(() => undefined);
|
|
54
71
|
return {
|
|
55
72
|
path,
|
|
56
73
|
sourcePath,
|
|
@@ -58,6 +75,8 @@ export async function reconcileGlobalCodexConfig(codexHome = process.env.CODEX_H
|
|
|
58
75
|
agentFilesInstalled: 0,
|
|
59
76
|
removedRegistrations: 0,
|
|
60
77
|
securityHookInstalled: false,
|
|
78
|
+
securityHookOwner: null,
|
|
79
|
+
backupPath: null,
|
|
61
80
|
error: error instanceof Error ? error.message : String(error),
|
|
62
81
|
};
|
|
63
82
|
}
|
|
@@ -67,68 +86,114 @@ export function installSecurityHook(config) {
|
|
|
67
86
|
if (zaipHookSpans.length === 1 && normalizeNewlines(config).includes(CODEX_PRE_TOOL_USE_HOOK)) {
|
|
68
87
|
return config;
|
|
69
88
|
}
|
|
70
|
-
const stripped =
|
|
89
|
+
const stripped = stripZaipHooks(config);
|
|
71
90
|
const prefix = stripped.length === 0 ? "" : stripped.endsWith("\n") ? `${stripped}\n` : `${stripped}\n\n`;
|
|
72
91
|
return `${prefix}${CODEX_PRE_TOOL_USE_HOOK}\n`;
|
|
73
92
|
}
|
|
93
|
+
function stripZaipHooks(config) {
|
|
94
|
+
return stripOwnedSpans(config, zaipOwnedHookSpans(config));
|
|
95
|
+
}
|
|
96
|
+
function stripOwnedSpans(config, spans) {
|
|
97
|
+
return spans.reduceRight((remaining, span) => {
|
|
98
|
+
const block = config.slice(span.start, span.end);
|
|
99
|
+
const comments = tomlStatements(block)
|
|
100
|
+
.filter(({ text }) => /^\s*#/.test(text))
|
|
101
|
+
.map(({ start, end }) => block.slice(start, end))
|
|
102
|
+
.join("");
|
|
103
|
+
const bom = span.start === 0 && block.startsWith("\uFEFF") ? "\uFEFF" : "";
|
|
104
|
+
return remaining.slice(0, span.start) + bom + comments + remaining.slice(span.end);
|
|
105
|
+
}, config);
|
|
106
|
+
}
|
|
107
|
+
async function hasCanonicalProjectHooks(sourcePath, filesystem, finding) {
|
|
108
|
+
const fail = (reason) => {
|
|
109
|
+
finding(reason);
|
|
110
|
+
return false;
|
|
111
|
+
};
|
|
112
|
+
if (!/[/\\]\.agents[/\\]config[/\\]agents[/\\]catalog\.yaml$/.test(sourcePath))
|
|
113
|
+
return fail("catalog-path");
|
|
114
|
+
const root = resolve(dirname(sourcePath), "../../..");
|
|
115
|
+
try {
|
|
116
|
+
const project = parse(await filesystem.readFile(join(root, ".agents/config/project.yaml"), "utf8"));
|
|
117
|
+
if (project?.zaip?.enabled !== true)
|
|
118
|
+
return fail("project-disabled");
|
|
119
|
+
const config = await filesystem.readFile(join(root, ".codex/config.toml"), "utf8");
|
|
120
|
+
const features = tomlTables(config).filter(({ text }) => /^\s*\[features\]\s*(?:#.*)?$/.test(text));
|
|
121
|
+
const feature = features[0];
|
|
122
|
+
if (features.length !== 1 || feature === undefined)
|
|
123
|
+
return fail("features-table");
|
|
124
|
+
const hooks = tomlStatements(config.slice(feature.start, feature.end)).filter(({ text }) => /^\s*hooks\s*=/.test(text));
|
|
125
|
+
if (hooks.length !== 1 || !/^\s*hooks\s*=\s*true\s*(?:#.*)?$/.test(hooks[0]?.text ?? ""))
|
|
126
|
+
return fail("hooks-disabled-or-ambiguous");
|
|
127
|
+
const expected = adapterFiles(root);
|
|
128
|
+
const expectedConfig = expected.find((file) => file.path === ".codex/config.toml")?.content;
|
|
129
|
+
if (expectedConfig === undefined)
|
|
130
|
+
return fail("expected-config-missing");
|
|
131
|
+
const expectedTables = tomlTables(expectedConfig);
|
|
132
|
+
const installedTables = tomlTables(config);
|
|
133
|
+
const normalizeWindowsCommand = (text) => [
|
|
134
|
+
"zaip-pre-tool-use.mjs",
|
|
135
|
+
"zaip-conversation-lifecycle.mjs",
|
|
136
|
+
"zaip-subagent-lifecycle.mjs",
|
|
137
|
+
].reduce((value, hook) => value.replaceAll(codexWindowsHookCommand(hook), codexWindowsHookCommand(hook, root)), text);
|
|
138
|
+
const tableContent = (source, table) => tomlStatements(source.slice(table.start, table.end))
|
|
139
|
+
.filter(({ text }) => text.trim() !== "" && !/^\s*(?:#|statusMessage\s*=)/.test(text))
|
|
140
|
+
.map(({ text }) => normalizeWindowsCommand(normalizeNewlines(text).trim()))
|
|
141
|
+
.join("\n");
|
|
142
|
+
// Verify every migrated event and its executable dependencies before retiring globals.
|
|
143
|
+
for (const [index, table] of expectedTables.entries()) {
|
|
144
|
+
const eventName = HOOK_TABLE.exec(table.text)?.[1];
|
|
145
|
+
if (eventName === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
const child = expectedTables[index + 1];
|
|
148
|
+
if (child === undefined)
|
|
149
|
+
return fail(`expected-child:${eventName}`);
|
|
150
|
+
const childHeader = new RegExp(`^\\s*\\[\\[hooks\\.${eventName}\\.hooks\\]\\]`);
|
|
151
|
+
if (!installedTables.some((candidate, candidateIndex) => {
|
|
152
|
+
if (tableContent(config, candidate) !== tableContent(expectedConfig, table))
|
|
153
|
+
return false;
|
|
154
|
+
for (let position = candidateIndex + 1; position < installedTables.length; position += 1) {
|
|
155
|
+
const installedChild = installedTables[position];
|
|
156
|
+
if (installedChild === undefined || !childHeader.test(installedChild.text))
|
|
157
|
+
break;
|
|
158
|
+
if (tableContent(config, installedChild) === tableContent(expectedConfig, child))
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}))
|
|
163
|
+
return fail(`event-mismatch:${eventName}`);
|
|
164
|
+
}
|
|
165
|
+
for (const file of expected.filter((file) => file.path.startsWith(".codex/hooks/"))) {
|
|
166
|
+
if (normalizeNewlines(await filesystem.readFile(join(root, file.path), "utf8")) !==
|
|
167
|
+
normalizeNewlines(file.content))
|
|
168
|
+
return fail(`executable-mismatch:${file.path}`);
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Missing/customized hooks require onboarding; never recreate a global fallback.
|
|
174
|
+
return fail("project-config-or-executable-unreadable");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
74
177
|
export function stripLegacyRegistrations(config, legacyAgentNames = []) {
|
|
75
178
|
const spans = legacyAgentTableSpans(config, new Set(legacyAgentNames));
|
|
76
179
|
return {
|
|
77
|
-
content:
|
|
180
|
+
content: stripOwnedSpans(config, spans),
|
|
78
181
|
removedRegistrations: spans.length,
|
|
79
182
|
};
|
|
80
183
|
}
|
|
81
184
|
function legacyAgentTableSpans(config, legacyAgentNames) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for (const line of config.matchAll(/.*(?:\r?\n|$)/g)) {
|
|
87
|
-
const text = line[0];
|
|
88
|
-
if (text.length === 0)
|
|
89
|
-
continue;
|
|
90
|
-
if (!inMultilineBasic && !inMultilineLiteral && ANY_TOML_TABLE.test(text)) {
|
|
91
|
-
const agentName = AGENT_TABLE.exec(text)?.[1];
|
|
92
|
-
tables.push({
|
|
93
|
-
start: offset,
|
|
94
|
-
legacy: agentName !== undefined && legacyAgentNames.has(agentName),
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (!inMultilineLiteral && hasUnescapedTripleQuote(text, '"""')) {
|
|
98
|
-
inMultilineBasic = !inMultilineBasic;
|
|
99
|
-
}
|
|
100
|
-
if (!inMultilineBasic && hasUnescapedTripleQuote(text, "'''")) {
|
|
101
|
-
inMultilineLiteral = !inMultilineLiteral;
|
|
102
|
-
}
|
|
103
|
-
offset += text.length;
|
|
104
|
-
}
|
|
105
|
-
return tables.flatMap((table, index) => table.legacy ? [{ start: table.start, end: tables[index + 1]?.start ?? config.length }] : []);
|
|
185
|
+
return tomlTables(config).filter(({ text }) => {
|
|
186
|
+
const agentName = AGENT_TABLE.exec(text)?.[1];
|
|
187
|
+
return agentName !== undefined && legacyAgentNames.has(agentName);
|
|
188
|
+
});
|
|
106
189
|
}
|
|
107
190
|
function zaipOwnedHookSpans(config) {
|
|
108
|
-
const tables =
|
|
109
|
-
let offset = 0;
|
|
110
|
-
let inMultilineBasic = false;
|
|
111
|
-
let inMultilineLiteral = false;
|
|
112
|
-
for (const line of config.matchAll(/.*(?:\r?\n|$)/g)) {
|
|
113
|
-
const text = line[0];
|
|
114
|
-
if (text.length === 0)
|
|
115
|
-
continue;
|
|
116
|
-
if (!inMultilineBasic && !inMultilineLiteral && ANY_TOML_TABLE.test(text)) {
|
|
117
|
-
tables.push({ start: offset, text });
|
|
118
|
-
}
|
|
119
|
-
if (!inMultilineLiteral && hasUnescapedTripleQuote(text, '"""')) {
|
|
120
|
-
inMultilineBasic = !inMultilineBasic;
|
|
121
|
-
}
|
|
122
|
-
if (!inMultilineBasic && hasUnescapedTripleQuote(text, "'''")) {
|
|
123
|
-
inMultilineLiteral = !inMultilineLiteral;
|
|
124
|
-
}
|
|
125
|
-
offset += text.length;
|
|
126
|
-
}
|
|
191
|
+
const tables = tomlTables(config);
|
|
127
192
|
return tables.flatMap((table, index) => {
|
|
128
193
|
const eventName = HOOK_TABLE.exec(table.text)?.[1];
|
|
129
194
|
if (eventName === undefined)
|
|
130
195
|
return [];
|
|
131
|
-
const childTable = new RegExp(`^\\[\\[hooks\\.${eventName}\\.[^\\]]+\\]\\]`);
|
|
196
|
+
const childTable = new RegExp(`^\\s*\\[\\[hooks\\.${eventName}\\.[^\\]]+\\]\\]`);
|
|
132
197
|
let boundary = index + 1;
|
|
133
198
|
while (boundary < tables.length && childTable.test(tables[boundary]?.text ?? ""))
|
|
134
199
|
boundary += 1;
|
|
@@ -137,7 +202,27 @@ function zaipOwnedHookSpans(config) {
|
|
|
137
202
|
start: child.start,
|
|
138
203
|
end: siblings[childIndex + 1]?.start ?? end,
|
|
139
204
|
}));
|
|
140
|
-
const isZaip = (span) =>
|
|
205
|
+
const isZaip = (span) => {
|
|
206
|
+
const commands = tomlStatements(config.slice(span.start, span.end)).filter(({ text }) => /^\s*command(?:_windows)?\s*=/.test(text));
|
|
207
|
+
return (commands.length > 0 &&
|
|
208
|
+
commands.every(({ text }) => {
|
|
209
|
+
const match = /^\s*command(?:_windows)?\s*=\s*("""[\s\S]*?"""|'''[\s\S]*?'''|"(?:\\.|[^"\\])*"|'[^']*')\s*(?:#.*)?$/.exec(text);
|
|
210
|
+
if (match?.[1] === undefined)
|
|
211
|
+
return false;
|
|
212
|
+
const literal = match[1];
|
|
213
|
+
try {
|
|
214
|
+
const command = literal.startsWith('"""') || literal.startsWith("'''")
|
|
215
|
+
? literal.slice(3, -3)
|
|
216
|
+
: literal.startsWith('"')
|
|
217
|
+
? JSON.parse(literal)
|
|
218
|
+
: literal.slice(1, -1);
|
|
219
|
+
return isZaipCommand(command);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}));
|
|
225
|
+
};
|
|
141
226
|
if (children.length === 0)
|
|
142
227
|
return isZaip({ start: table.start, end }) ? [{ start: table.start, end }] : [];
|
|
143
228
|
const owned = children.filter(isZaip);
|
|
@@ -157,40 +242,73 @@ async function catalogedCodexAliases(sourcePath, filesystem) {
|
|
|
157
242
|
function normalizeNewlines(value) {
|
|
158
243
|
return value.replaceAll("\r\n", "\n");
|
|
159
244
|
}
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
245
|
+
function isZaipCommand(command) {
|
|
246
|
+
const node = /^node\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+([^\r\n]*))?$/.exec(command.trim());
|
|
247
|
+
if (node !== null &&
|
|
248
|
+
ZAIP_HOOK_PATH.test(node[1] ?? node[2] ?? node[3] ?? "") &&
|
|
249
|
+
!/[;&|<>]/.test(node[4] ?? ""))
|
|
250
|
+
return true;
|
|
251
|
+
const encoded = /^powershell(?:\.exe)?\s+-NoProfile\s+-NonInteractive\s+-EncodedCommand\s+([A-Za-z0-9+/=]+)\s*$/i.exec(command.trim());
|
|
252
|
+
if (encoded?.[1] === undefined)
|
|
253
|
+
return false;
|
|
254
|
+
const script = Buffer.from(encoded[1], "base64").toString("utf16le");
|
|
255
|
+
const path = /\$hook\s*=\s*Join-Path\s+\$root\s+'([^']+)'/.exec(script)?.[1];
|
|
256
|
+
if (path === undefined || !ZAIP_HOOK_PATH.test(path))
|
|
257
|
+
return false;
|
|
258
|
+
const hook = path.split(/[/\\]/).at(-1) ?? "";
|
|
259
|
+
const root = /\$search = '((?:''|[^'])*)';/.exec(script)?.[1]?.replaceAll("''", "'");
|
|
260
|
+
// A matching path inside an arbitrary encoded user script is not ownership proof.
|
|
261
|
+
return encoded[1] === codexWindowsHookCommand(hook, root).split(" ").at(-1);
|
|
167
262
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
await filesystem.rename(path, backup);
|
|
176
|
-
previousConfigMoved = true;
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
if (!isMissingFile(error))
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
await filesystem.rename(temporary, path);
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
if (previousConfigMoved)
|
|
187
|
-
await filesystem.rename(backup, path);
|
|
188
|
-
throw error;
|
|
189
|
-
}
|
|
190
|
-
if (previousConfigMoved)
|
|
191
|
-
await filesystem.rm(backup, { force: true });
|
|
263
|
+
function tomlTables(config) {
|
|
264
|
+
const tables = tomlStatements(config).filter(({ text }) => ANY_TOML_TABLE.test(text));
|
|
265
|
+
return tables.map((table, index) => ({
|
|
266
|
+
...table,
|
|
267
|
+
end: tables[index + 1]?.start ?? config.length,
|
|
268
|
+
}));
|
|
192
269
|
}
|
|
193
|
-
|
|
194
|
-
|
|
270
|
+
/** Keep table-looking text, comments and assignments inside strings out of ownership checks. */
|
|
271
|
+
function tomlStatements(config) {
|
|
272
|
+
const statements = [];
|
|
273
|
+
let quote = "";
|
|
274
|
+
let nesting = 0;
|
|
275
|
+
let start = 0;
|
|
276
|
+
let offset = 0;
|
|
277
|
+
for (const match of config.matchAll(/.*(?:\r?\n|$)/g)) {
|
|
278
|
+
const line = match[0];
|
|
279
|
+
if (!line)
|
|
280
|
+
continue;
|
|
281
|
+
if (!quote && nesting === 0)
|
|
282
|
+
start = offset;
|
|
283
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
284
|
+
if (quote) {
|
|
285
|
+
if (quote.startsWith('"') && line[index] === "\\") {
|
|
286
|
+
index += 1;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (line.startsWith(quote, index)) {
|
|
290
|
+
index += quote.length - 1;
|
|
291
|
+
quote = "";
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
if (line[index] === "#")
|
|
296
|
+
break;
|
|
297
|
+
const character = line[index];
|
|
298
|
+
if (character === '"' || character === "'") {
|
|
299
|
+
quote = line.startsWith(character.repeat(3), index) ? character.repeat(3) : character;
|
|
300
|
+
index += quote.length - 1;
|
|
301
|
+
}
|
|
302
|
+
if (line[index] === "[" || line[index] === "{")
|
|
303
|
+
nesting += 1;
|
|
304
|
+
if (line[index] === "]" || line[index] === "}")
|
|
305
|
+
nesting -= 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
offset += line.length;
|
|
309
|
+
if (!quote && nesting === 0)
|
|
310
|
+
statements.push({ start, end: offset, text: config.slice(start, offset).trimEnd() });
|
|
311
|
+
}
|
|
312
|
+
return statements;
|
|
195
313
|
}
|
|
196
314
|
//# sourceMappingURL=global-codex-config.js.map
|