oh-my-codex 0.21.30 → 0.21.31
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/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/dist/cli/__tests__/setup-symlink-destinations.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-symlink-destinations.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-symlink-destinations.test.js +180 -0
- package/dist/cli/__tests__/setup-symlink-destinations.test.js.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +140 -25
- package/dist/cli/setup.js.map +1 -1
- package/dist/team/__tests__/delivery-recovery.test.js +61 -1
- package/dist/team/__tests__/delivery-recovery.test.js.map +1 -1
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +47 -12
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +43 -16
- package/dist/team/state.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
package/Cargo.lock
CHANGED
|
@@ -32,7 +32,7 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
|
32
32
|
|
|
33
33
|
[[package]]
|
|
34
34
|
name = "omx-api"
|
|
35
|
-
version = "0.21.
|
|
35
|
+
version = "0.21.31"
|
|
36
36
|
dependencies = [
|
|
37
37
|
"serde",
|
|
38
38
|
"serde_json",
|
|
@@ -40,14 +40,14 @@ dependencies = [
|
|
|
40
40
|
|
|
41
41
|
[[package]]
|
|
42
42
|
name = "omx-explore-harness"
|
|
43
|
-
version = "0.21.
|
|
43
|
+
version = "0.21.31"
|
|
44
44
|
dependencies = [
|
|
45
45
|
"libc",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
48
|
[[package]]
|
|
49
49
|
name = "omx-mux"
|
|
50
|
-
version = "0.21.
|
|
50
|
+
version = "0.21.31"
|
|
51
51
|
dependencies = [
|
|
52
52
|
"serde",
|
|
53
53
|
"serde_json",
|
|
@@ -55,7 +55,7 @@ dependencies = [
|
|
|
55
55
|
|
|
56
56
|
[[package]]
|
|
57
57
|
name = "omx-runtime"
|
|
58
|
-
version = "0.21.
|
|
58
|
+
version = "0.21.31"
|
|
59
59
|
dependencies = [
|
|
60
60
|
"fs2",
|
|
61
61
|
"libc",
|
|
@@ -67,7 +67,7 @@ dependencies = [
|
|
|
67
67
|
|
|
68
68
|
[[package]]
|
|
69
69
|
name = "omx-runtime-core"
|
|
70
|
-
version = "0.21.
|
|
70
|
+
version = "0.21.31"
|
|
71
71
|
dependencies = [
|
|
72
72
|
"fs2",
|
|
73
73
|
"serde",
|
|
@@ -76,7 +76,7 @@ dependencies = [
|
|
|
76
76
|
|
|
77
77
|
[[package]]
|
|
78
78
|
name = "omx-sparkshell"
|
|
79
|
-
version = "0.21.
|
|
79
|
+
version = "0.21.31"
|
|
80
80
|
dependencies = [
|
|
81
81
|
"omx-mux",
|
|
82
82
|
]
|
package/Cargo.toml
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-symlink-destinations.test.d.ts","sourceRoot":"","sources":["../../../src/cli/__tests__/setup-symlink-destinations.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, readFile, rm, symlink, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { setup } from '../setup.js';
|
|
8
|
+
function setMockHome(home) {
|
|
9
|
+
const previousHome = process.env.HOME;
|
|
10
|
+
const previousCodexHome = process.env.CODEX_HOME;
|
|
11
|
+
process.env.HOME = home;
|
|
12
|
+
process.env.CODEX_HOME = join(home, '.codex');
|
|
13
|
+
return () => {
|
|
14
|
+
if (typeof previousHome === 'string')
|
|
15
|
+
process.env.HOME = previousHome;
|
|
16
|
+
else
|
|
17
|
+
delete process.env.HOME;
|
|
18
|
+
if (typeof previousCodexHome === 'string')
|
|
19
|
+
process.env.CODEX_HOME = previousCodexHome;
|
|
20
|
+
else
|
|
21
|
+
delete process.env.CODEX_HOME;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function withRepo(run) {
|
|
25
|
+
const parent = await mkdtemp(join(tmpdir(), 'omx-setup-symlink-'));
|
|
26
|
+
const repo = join(parent, 'repo');
|
|
27
|
+
await mkdir(repo);
|
|
28
|
+
const restoreHome = setMockHome(join(parent, 'home'));
|
|
29
|
+
const previousCwd = process.cwd();
|
|
30
|
+
try {
|
|
31
|
+
process.chdir(repo);
|
|
32
|
+
await run(repo, parent);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
process.chdir(previousCwd);
|
|
36
|
+
restoreHome();
|
|
37
|
+
await rm(parent, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
describe('omx setup symlink destinations', () => {
|
|
41
|
+
it('refuses a symlinked project .gitignore and leaves the target untouched', async () => {
|
|
42
|
+
await withRepo(async (repo, parent) => {
|
|
43
|
+
const victim = join(parent, 'victim.txt');
|
|
44
|
+
await writeFile(victim, 'ORIGINAL-SECRET-CONTENT\n');
|
|
45
|
+
await symlink('../victim.txt', join(repo, '.gitignore'));
|
|
46
|
+
const gitignorePath = join(repo, '.gitignore');
|
|
47
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
48
|
+
const msg = err?.message || '';
|
|
49
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
50
|
+
msg.includes(gitignorePath) &&
|
|
51
|
+
msg.includes('Either remove the symlink'));
|
|
52
|
+
});
|
|
53
|
+
assert.equal(await readFile(victim, 'utf8'), 'ORIGINAL-SECRET-CONTENT\n');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it('refuses a symlinked prompt destination and leaves the target untouched', async () => {
|
|
57
|
+
await withRepo(async (repo, parent) => {
|
|
58
|
+
await mkdir(join(repo, '.codex', 'prompts'), { recursive: true });
|
|
59
|
+
const victim = join(parent, 'victim-prompt.txt');
|
|
60
|
+
await writeFile(victim, 'ORIGINAL-PROMPT-CONTENT\n');
|
|
61
|
+
const analystPath = join(repo, '.codex', 'prompts', 'analyst.md');
|
|
62
|
+
await symlink('../../../victim-prompt.txt', analystPath);
|
|
63
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
64
|
+
const msg = err?.message || '';
|
|
65
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
66
|
+
msg.includes(analystPath) &&
|
|
67
|
+
msg.includes('Either remove the symlink'));
|
|
68
|
+
});
|
|
69
|
+
assert.equal(await readFile(victim, 'utf8'), 'ORIGINAL-PROMPT-CONTENT\n');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
it('rejects a symlinked ancestor directory (.codex)', async () => {
|
|
73
|
+
await withRepo(async (repo, parent) => {
|
|
74
|
+
const targetDir = join(parent, 'external-codex');
|
|
75
|
+
await mkdir(targetDir);
|
|
76
|
+
const victim = join(targetDir, 'secret.txt');
|
|
77
|
+
await writeFile(victim, 'EXTERNAL-SECRET\n');
|
|
78
|
+
// Create .codex as a symlink to external directory
|
|
79
|
+
await symlink(targetDir, join(repo, '.codex'));
|
|
80
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
81
|
+
const msg = err?.message || '';
|
|
82
|
+
return ((msg.includes('Refusing to install through symlink') || msg.includes('Refusing native hook transaction')) &&
|
|
83
|
+
msg.includes('symlink') &&
|
|
84
|
+
(msg.includes('Either remove the symlink') || msg.includes('is a symbolic link')));
|
|
85
|
+
});
|
|
86
|
+
assert.equal(await readFile(victim, 'utf8'), 'EXTERNAL-SECRET\n');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
it('rejects a symlinked auth directory', async () => {
|
|
90
|
+
await withRepo(async (repo, parent) => {
|
|
91
|
+
const targetDir = join(parent, 'external-auth');
|
|
92
|
+
await mkdir(targetDir);
|
|
93
|
+
const victim = join(targetDir, 'token.txt');
|
|
94
|
+
await writeFile(victim, 'SECRET-TOKEN\n');
|
|
95
|
+
// Create .omx as a symlink to external directory
|
|
96
|
+
await symlink(targetDir, join(repo, '.omx'));
|
|
97
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
98
|
+
const msg = err?.message || '';
|
|
99
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
100
|
+
msg.includes('.omx') &&
|
|
101
|
+
msg.includes('Either remove the symlink'));
|
|
102
|
+
});
|
|
103
|
+
assert.equal(await readFile(victim, 'utf8'), 'SECRET-TOKEN\n');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
it('rejects a symlinked state directory that setup manages', async () => {
|
|
107
|
+
await withRepo(async (repo, parent) => {
|
|
108
|
+
const targetDir = join(parent, 'external-state');
|
|
109
|
+
await mkdir(targetDir);
|
|
110
|
+
const victim = join(targetDir, 'installed-skills.json');
|
|
111
|
+
await writeFile(victim, '{"version": 1}\n');
|
|
112
|
+
// Create .omx/state as a symlink to external directory, which setup writes to for installed-skills.json
|
|
113
|
+
await mkdir(join(repo, '.omx'), { recursive: true });
|
|
114
|
+
await symlink(targetDir, join(repo, '.omx', 'state'));
|
|
115
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
116
|
+
const msg = err?.message || '';
|
|
117
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
118
|
+
msg.includes('.omx') &&
|
|
119
|
+
msg.includes('Either remove the symlink'));
|
|
120
|
+
});
|
|
121
|
+
assert.equal(await readFile(victim, 'utf8'), '{"version": 1}\n');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it('rejects a symlinked .omx directory even when hud-config.json would already exist', async () => {
|
|
125
|
+
await withRepo(async (repo, parent) => {
|
|
126
|
+
const targetDir = join(parent, 'external-omx');
|
|
127
|
+
await mkdir(targetDir);
|
|
128
|
+
// Create hud-config.json in the target so that the old code would skip validation
|
|
129
|
+
await mkdir(join(targetDir), { recursive: true });
|
|
130
|
+
await writeFile(join(targetDir, 'hud-config.json'), '{"preset": "focused"}\n');
|
|
131
|
+
// Create .omx as a symlink to external directory
|
|
132
|
+
await symlink(targetDir, join(repo, '.omx'));
|
|
133
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
134
|
+
const msg = err?.message || '';
|
|
135
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
136
|
+
msg.includes('.omx') &&
|
|
137
|
+
msg.includes('Either remove the symlink'));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
it('rejects a symlinked .omx/backups when backing up a managed file', async () => {
|
|
142
|
+
await withRepo(async (repo, parent) => {
|
|
143
|
+
const targetDir = join(parent, 'external-backups');
|
|
144
|
+
await mkdir(targetDir);
|
|
145
|
+
const victim = join(targetDir, 'secret-backup.txt');
|
|
146
|
+
// Create .omx/backups as a symlink to external directory
|
|
147
|
+
await mkdir(join(repo, '.omx'), { recursive: true });
|
|
148
|
+
await symlink(targetDir, join(repo, '.omx', 'backups'));
|
|
149
|
+
// Create an existing .gitignore to trigger the backup code path
|
|
150
|
+
await writeFile(join(repo, '.gitignore'), 'original-gitignore\n');
|
|
151
|
+
await assert.rejects(setup({ scope: 'project' }), (err) => {
|
|
152
|
+
const msg = err?.message || '';
|
|
153
|
+
return (msg.includes('Refusing to install through symlink') &&
|
|
154
|
+
msg.includes('backups') &&
|
|
155
|
+
msg.includes('Either remove the symlink'));
|
|
156
|
+
});
|
|
157
|
+
// Verify nothing was written to the victim directory
|
|
158
|
+
assert.equal(existsSync(victim), false, 'Backup should not be written outside the scope');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
it('succeeds when scope root is under a symlinked parent (like /tmp on macOS) while still rejecting symlinks inside the managed tree', async () => {
|
|
162
|
+
// Regression test for issue #3887: On macOS, /tmp is a symlink to /private/tmp,
|
|
163
|
+
// so the setup should not reject paths just because they're under /tmp,
|
|
164
|
+
// but should still reject symlinks inside the managed scope (.codex, .omx, etc).
|
|
165
|
+
await withRepo(async (repo, parent) => {
|
|
166
|
+
// The repo is already under /tmp (symlinked parent), so this tests that
|
|
167
|
+
// OS-level symlinks are not rejected.
|
|
168
|
+
// Create a file to install
|
|
169
|
+
const skillDir = join(repo, '.codex', 'skills', 'test-skill');
|
|
170
|
+
await mkdir(skillDir, { recursive: true });
|
|
171
|
+
await writeFile(join(skillDir, 'SKILL.md'), '# Test Skill\n\nA test skill');
|
|
172
|
+
// Setup should succeed even though the scope root is under the /tmp symlink
|
|
173
|
+
await assert.doesNotReject(setup({ scope: 'project' }), 'Setup should succeed when scope root is under /tmp (symlinked OS directory)');
|
|
174
|
+
// Verify the setup actually completed
|
|
175
|
+
assert(existsSync(join(repo, '.gitignore')), '.gitignore should be created');
|
|
176
|
+
assert(existsSync(join(repo, '.codex', 'config.toml')), 'config.toml should be created');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=setup-symlink-destinations.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-symlink-destinations.test.js","sourceRoot":"","sources":["../../../src/cli/__tests__/setup-symlink-destinations.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/setup.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* omx setup - Automated installation of oh-my-codex
|
|
3
3
|
* Installs skills, prompts, MCP servers config, and AGENTS.md
|
|
4
4
|
*/
|
|
5
|
-
import { mkdir, cp, copyFile, readdir, readFile, rename, writeFile, stat, lstat, rm, open, chmod, } from "fs/promises";
|
|
5
|
+
import { mkdir, cp, copyFile, readdir, readFile, rename, writeFile, stat, lstat, realpath, rm, open, chmod, } from "fs/promises";
|
|
6
6
|
import { join, dirname, relative, basename, isAbsolute, sep, win32 } from "path";
|
|
7
7
|
import { constants, existsSync } from "fs";
|
|
8
8
|
import { spawnSync } from "child_process";
|
|
@@ -1241,7 +1241,88 @@ async function commitNativeHookTransaction(artifacts, preconditions, ancestorPre
|
|
|
1241
1241
|
throw new Error(`Native hook transaction failed and was rolled back: ${message}`);
|
|
1242
1242
|
}
|
|
1243
1243
|
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Validates that a destination path is safe for managed setup operations (install, read, compare).
|
|
1246
|
+
* Refuses if the destination is a symlink or lies outside the specified scope root,
|
|
1247
|
+
* or if any ancestor component within the scope is a symlink.
|
|
1248
|
+
*
|
|
1249
|
+
* This guard prevents symlink attacks that would redirect reads, backups, and writes outside
|
|
1250
|
+
* the managed tree. It validates every path component without following symlinks, but only
|
|
1251
|
+
* checks ancestors within the scope root—never rejects OS-level symlinks above the managed
|
|
1252
|
+
* scope (e.g., /var, /tmp on macOS which may be systemwide symlinks).
|
|
1253
|
+
*
|
|
1254
|
+
* MUST be called BEFORE reading/comparing contents and BEFORE mkdir of any intermediate dir.
|
|
1255
|
+
* It is the single authoritative source of symlink validation for all managed scope writes.
|
|
1256
|
+
*
|
|
1257
|
+
* Addresses Codex findings:
|
|
1258
|
+
* - 4351464979: validate before compare (filesDiffer) ✓
|
|
1259
|
+
* - 4351464989: validate before compare (readFile) ✓
|
|
1260
|
+
* - 4351560195: respects CODEX_HOME via codexHome() ✓
|
|
1261
|
+
* - 4351560207: validate before compare ✓
|
|
1262
|
+
* - 4351560219: validate before mkdir ✓
|
|
1263
|
+
*/
|
|
1264
|
+
async function assertManagedDestination(destinationPath, scopeRoot) {
|
|
1265
|
+
// Resolve paths to absolute forms
|
|
1266
|
+
const absolutePath = isAbsolute(destinationPath) ? destinationPath : join(process.cwd(), destinationPath);
|
|
1267
|
+
const absoluteScopeRoot = isAbsolute(scopeRoot) ? scopeRoot : join(process.cwd(), scopeRoot);
|
|
1268
|
+
// Resolve scope root to its real path (following all symlinks) to properly handle
|
|
1269
|
+
// OS-level symlinks like /tmp -> /private/tmp on macOS. This allows us to detect when
|
|
1270
|
+
// a destination lies outside the real managed scope.
|
|
1271
|
+
let realScopeRoot;
|
|
1272
|
+
try {
|
|
1273
|
+
realScopeRoot = await realpath(absoluteScopeRoot);
|
|
1274
|
+
}
|
|
1275
|
+
catch (error) {
|
|
1276
|
+
if (isMissingPathError(error)) {
|
|
1277
|
+
// Scope root doesn't exist yet; use absolute path
|
|
1278
|
+
realScopeRoot = absoluteScopeRoot;
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
throw error;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
// Verify destination is within the managed scope by checking relative path.
|
|
1285
|
+
// Using realScopeRoot ensures we detect when symlinks in ancestors would escape the scope.
|
|
1286
|
+
const relFromScope = relative(realScopeRoot, absolutePath);
|
|
1287
|
+
if (relFromScope === '..' || relFromScope.startsWith(`..${sep}`)) {
|
|
1288
|
+
throw new Error(`Destination ${absolutePath} is outside managed scope ${realScopeRoot}.`);
|
|
1289
|
+
}
|
|
1290
|
+
// Walk from destination up to and including the scope root, checking each component
|
|
1291
|
+
// for symlinks. We do NOT resolve symlinks while walking (to catch them).
|
|
1292
|
+
// Stop at the scope root (don't check parents of the scope root).
|
|
1293
|
+
let currentPath = absolutePath;
|
|
1294
|
+
const visited = new Set();
|
|
1295
|
+
while (currentPath && currentPath !== dirname(currentPath)) {
|
|
1296
|
+
if (visited.has(currentPath))
|
|
1297
|
+
break; // Avoid infinite loops
|
|
1298
|
+
visited.add(currentPath);
|
|
1299
|
+
// Stop before checking ancestors beyond the scope root
|
|
1300
|
+
if (currentPath === realScopeRoot)
|
|
1301
|
+
break;
|
|
1302
|
+
// Check if this path component is a symlink (using lstat, not stat)
|
|
1303
|
+
let stats;
|
|
1304
|
+
try {
|
|
1305
|
+
stats = await lstat(currentPath);
|
|
1306
|
+
}
|
|
1307
|
+
catch (error) {
|
|
1308
|
+
if (isMissingPathError(error)) {
|
|
1309
|
+
// Path doesn't exist yet, move to parent
|
|
1310
|
+
currentPath = dirname(currentPath);
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
throw error;
|
|
1314
|
+
}
|
|
1315
|
+
if (stats.isSymbolicLink()) {
|
|
1316
|
+
const msg = `Refusing to install through symlink ${currentPath}. ` +
|
|
1317
|
+
`Either remove the symlink (rm ${currentPath}), ` +
|
|
1318
|
+
`or move the target to the expected location before running setup again.`;
|
|
1319
|
+
throw new Error(msg);
|
|
1320
|
+
}
|
|
1321
|
+
currentPath = dirname(currentPath);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1244
1324
|
async function ensureBackup(destinationPath, contentChanged, backupContext, options) {
|
|
1325
|
+
await assertManagedDestination(destinationPath, backupContext.baseRoot);
|
|
1245
1326
|
if (!contentChanged || !existsSync(destinationPath))
|
|
1246
1327
|
return false;
|
|
1247
1328
|
const relativePath = relative(backupContext.baseRoot, destinationPath);
|
|
@@ -1250,6 +1331,7 @@ async function ensureBackup(destinationPath, contentChanged, backupContext, opti
|
|
|
1250
1331
|
: relativePath;
|
|
1251
1332
|
const backupPath = join(backupContext.backupRoot, safeRelativePath);
|
|
1252
1333
|
if (!options.dryRun) {
|
|
1334
|
+
await assertManagedDestination(backupPath, backupContext.baseRoot);
|
|
1253
1335
|
await mkdir(dirname(backupPath), { recursive: true });
|
|
1254
1336
|
await copyFile(destinationPath, backupPath);
|
|
1255
1337
|
}
|
|
@@ -1435,7 +1517,7 @@ async function isUnmodifiedRecordedInstall(receiptPath, skillName, skillDir) {
|
|
|
1435
1517
|
* The receipt therefore describes the installer's own output, and any extra entry present at
|
|
1436
1518
|
* retirement time makes the comparison fail, which retains the directory.
|
|
1437
1519
|
*/
|
|
1438
|
-
async function writeInstalledSkillReceipt(receiptPath, skillsDir, writtenFilesBySkill, options) {
|
|
1520
|
+
async function writeInstalledSkillReceipt(receiptPath, skillsDir, writtenFilesBySkill, options, scopeRoot) {
|
|
1439
1521
|
if (options.dryRun)
|
|
1440
1522
|
return;
|
|
1441
1523
|
const receipt = await readInstalledSkillReceipt(receiptPath);
|
|
@@ -1458,11 +1540,16 @@ async function writeInstalledSkillReceipt(receiptPath, skillsDir, writtenFilesBy
|
|
|
1458
1540
|
delete receipt.skills[name];
|
|
1459
1541
|
}
|
|
1460
1542
|
try {
|
|
1543
|
+
await assertManagedDestination(receiptPath, scopeRoot);
|
|
1461
1544
|
await mkdir(dirname(receiptPath), { recursive: true });
|
|
1462
1545
|
await writeFile(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
1463
1546
|
}
|
|
1464
|
-
catch {
|
|
1465
|
-
//
|
|
1547
|
+
catch (error) {
|
|
1548
|
+
// Symlink assertions must propagate to prevent writes through symlinks
|
|
1549
|
+
if (error instanceof Error && error.message.includes('Refusing to install through symlink')) {
|
|
1550
|
+
throw error;
|
|
1551
|
+
}
|
|
1552
|
+
// A receipt we cannot persist for other reasons simply means the next refresh retains conservatively.
|
|
1466
1553
|
}
|
|
1467
1554
|
}
|
|
1468
1555
|
async function isOmxManagedInstalledSkillDir(skillDir) {
|
|
@@ -1945,6 +2032,7 @@ function stripLegacyGitignoreEntries(content, legacyEntries) {
|
|
|
1945
2032
|
}
|
|
1946
2033
|
async function ensureProjectGitignore(projectRoot, backupContext, options) {
|
|
1947
2034
|
const gitignorePath = join(projectRoot, ".gitignore");
|
|
2035
|
+
await assertManagedDestination(gitignorePath, projectRoot);
|
|
1948
2036
|
const destinationExists = existsSync(gitignorePath);
|
|
1949
2037
|
const existing = destinationExists
|
|
1950
2038
|
? await readFile(gitignorePath, "utf-8")
|
|
@@ -2557,6 +2645,21 @@ export async function setup(options = {}) {
|
|
|
2557
2645
|
const { disableHooks = false, force = false, repairPluginCache = false, dryRun = false, installMode: requestedInstallMode, mcpMode: requestedMcpMode, teamMode: requestedTeamMode, scope: requestedScope, verbose = false, skipNativeAgentRefresh: requestedSkipNativeAgentRefresh = false, setupScopePrompt, persistedSetupReviewPrompt, installModePrompt, modelUpgradePrompt, pluginAgentsMdPrompt, pluginDeveloperInstructionsPrompt, firstPartyMcpRemovalPrompt, } = options;
|
|
2558
2646
|
const pkgRoot = getPackageRoot();
|
|
2559
2647
|
const projectRoot = process.cwd();
|
|
2648
|
+
// Validate .omx is never a symlink before any operations touch it
|
|
2649
|
+
if (!dryRun) {
|
|
2650
|
+
const omxPath = join(projectRoot, ".omx");
|
|
2651
|
+
try {
|
|
2652
|
+
await assertManagedDestination(omxPath, projectRoot);
|
|
2653
|
+
}
|
|
2654
|
+
catch (error) {
|
|
2655
|
+
// Missing .omx is fine, but symlinks must be refused
|
|
2656
|
+
if (error instanceof Error && error.message.includes("Refusing to install through symlink")) {
|
|
2657
|
+
throw error;
|
|
2658
|
+
}
|
|
2659
|
+
// Any other error is unexpected
|
|
2660
|
+
throw error;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2560
2663
|
const persistedPreferences = await readPersistedSetupPreferences(projectRoot, { warnOnLegacyScope: true });
|
|
2561
2664
|
let persistedReviewDecision = "keep";
|
|
2562
2665
|
const effectiveScopeForInstallMode = requestedScope ?? persistedPreferences?.scope ?? DEFAULT_SETUP_SCOPE;
|
|
@@ -2788,25 +2891,23 @@ export async function setup(options = {}) {
|
|
|
2788
2891
|
}
|
|
2789
2892
|
// Step 1: Ensure directories exist
|
|
2790
2893
|
console.log("[1/8] Creating directories...");
|
|
2791
|
-
const
|
|
2792
|
-
? [
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
omxLogsDir(projectRoot),
|
|
2807
|
-
];
|
|
2808
|
-
for (const dir of dirs) {
|
|
2894
|
+
const userScopeDirs = isPluginInstallMode
|
|
2895
|
+
? [scopeDirs.codexHomeDir, scopeDirs.nativeAgentsDir]
|
|
2896
|
+
: [scopeDirs.codexHomeDir, scopeDirs.promptsDir, scopeDirs.skillsDir, scopeDirs.nativeAgentsDir];
|
|
2897
|
+
const projectScopeDirs = [omxStateDir(projectRoot), omxPlansDir(projectRoot), omxLogsDir(projectRoot)];
|
|
2898
|
+
// Validate and create user-scope directories against user authority root
|
|
2899
|
+
for (const dir of userScopeDirs) {
|
|
2900
|
+
if (!dryRun) {
|
|
2901
|
+
await assertManagedDestination(dir, backupContext.baseRoot);
|
|
2902
|
+
await mkdir(dir, { recursive: true });
|
|
2903
|
+
}
|
|
2904
|
+
if (verbose)
|
|
2905
|
+
console.log(` mkdir ${dir}`);
|
|
2906
|
+
}
|
|
2907
|
+
// Validate and create project-scope directories against project authority root
|
|
2908
|
+
for (const dir of projectScopeDirs) {
|
|
2809
2909
|
if (!dryRun) {
|
|
2910
|
+
await assertManagedDestination(dir, projectRoot);
|
|
2810
2911
|
await mkdir(dir, { recursive: true });
|
|
2811
2912
|
}
|
|
2812
2913
|
if (verbose)
|
|
@@ -3305,6 +3406,10 @@ export async function setup(options = {}) {
|
|
|
3305
3406
|
// Step 8: Configure HUD
|
|
3306
3407
|
console.log("[8/8] Configuring HUD...");
|
|
3307
3408
|
const hudConfigPath = join(projectRoot, ".omx", "hud-config.json");
|
|
3409
|
+
// Validate .omx even when hud-config.json already exists to catch symlinks
|
|
3410
|
+
if (!dryRun) {
|
|
3411
|
+
await assertManagedDestination(hudConfigPath, projectRoot);
|
|
3412
|
+
}
|
|
3308
3413
|
if (force || !existsSync(hudConfigPath)) {
|
|
3309
3414
|
if (!dryRun) {
|
|
3310
3415
|
const defaultHudConfig = { preset: "focused" };
|
|
@@ -3415,6 +3520,7 @@ function isGitHubCliConfigured() {
|
|
|
3415
3520
|
return cachedGitHubCliConfigured;
|
|
3416
3521
|
}
|
|
3417
3522
|
async function syncManagedFileFromDisk(srcPath, dstPath, summary, backupContext, options, verboseLabel) {
|
|
3523
|
+
await assertManagedDestination(dstPath, backupContext.baseRoot);
|
|
3418
3524
|
const destinationExists = existsSync(dstPath);
|
|
3419
3525
|
const changed = !destinationExists || (await filesDiffer(srcPath, dstPath));
|
|
3420
3526
|
if (!changed) {
|
|
@@ -3434,6 +3540,7 @@ async function syncManagedFileFromDisk(srcPath, dstPath, summary, backupContext,
|
|
|
3434
3540
|
}
|
|
3435
3541
|
}
|
|
3436
3542
|
async function syncManagedContent(content, dstPath, summary, backupContext, options, verboseLabel) {
|
|
3543
|
+
await assertManagedDestination(dstPath, backupContext.baseRoot);
|
|
3437
3544
|
const destinationExists = existsSync(dstPath);
|
|
3438
3545
|
let changed = true;
|
|
3439
3546
|
if (destinationExists) {
|
|
@@ -3490,8 +3597,9 @@ async function readNativeAgentInstallManifest(agentsDir) {
|
|
|
3490
3597
|
return { version: 1, files: {} };
|
|
3491
3598
|
}
|
|
3492
3599
|
}
|
|
3493
|
-
async function writeNativeAgentInstallManifest(agentsDir, manifest) {
|
|
3600
|
+
async function writeNativeAgentInstallManifest(agentsDir, manifest, scopeRoot) {
|
|
3494
3601
|
const manifestPath = nativeAgentInstallManifestPath(agentsDir);
|
|
3602
|
+
await assertManagedDestination(manifestPath, scopeRoot);
|
|
3495
3603
|
await mkdir(dirname(manifestPath), { recursive: true });
|
|
3496
3604
|
const sortedFiles = Object.fromEntries(Object.entries(manifest.files).sort(([left], [right]) => left.localeCompare(right)));
|
|
3497
3605
|
await writeFile(manifestPath, JSON.stringify({ version: 1, files: sortedFiles }, null, 2) + "\n");
|
|
@@ -3499,6 +3607,7 @@ async function writeNativeAgentInstallManifest(agentsDir, manifest) {
|
|
|
3499
3607
|
async function syncNativeAgentToml(content, dstPath, summary, backupContext, options, verboseLabel, manifest) {
|
|
3500
3608
|
const fileName = basename(dstPath);
|
|
3501
3609
|
const nextHash = hashContent(content);
|
|
3610
|
+
await assertManagedDestination(dstPath, backupContext.baseRoot);
|
|
3502
3611
|
const destinationExists = existsSync(dstPath);
|
|
3503
3612
|
if (!destinationExists) {
|
|
3504
3613
|
if (!options.dryRun) {
|
|
@@ -3718,6 +3827,7 @@ async function cleanupGeneratedNonInstallableNativeAgents(agentsDir, manifest, b
|
|
|
3718
3827
|
async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, options) {
|
|
3719
3828
|
const summary = createEmptyCategorySummary();
|
|
3720
3829
|
if (!options.dryRun) {
|
|
3830
|
+
await assertManagedDestination(agentsDir, backupContext.baseRoot);
|
|
3721
3831
|
await mkdir(agentsDir, { recursive: true });
|
|
3722
3832
|
}
|
|
3723
3833
|
const nativeAgentManifest = await readNativeAgentInstallManifest(agentsDir);
|
|
@@ -3800,7 +3910,7 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
|
|
|
3800
3910
|
}
|
|
3801
3911
|
}
|
|
3802
3912
|
if (!options.dryRun) {
|
|
3803
|
-
await writeNativeAgentInstallManifest(agentsDir, nativeAgentManifest);
|
|
3913
|
+
await writeNativeAgentInstallManifest(agentsDir, nativeAgentManifest, backupContext.baseRoot);
|
|
3804
3914
|
}
|
|
3805
3915
|
return summary;
|
|
3806
3916
|
}
|
|
@@ -3850,6 +3960,10 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
|
|
|
3850
3960
|
const summary = createEmptyCategorySummary();
|
|
3851
3961
|
if (!existsSync(srcDir))
|
|
3852
3962
|
return summary;
|
|
3963
|
+
// Validate the skills directory before creating any skill subdirectories
|
|
3964
|
+
if (!options.dryRun) {
|
|
3965
|
+
await assertManagedDestination(dstDir, backupContext.baseRoot);
|
|
3966
|
+
}
|
|
3853
3967
|
const installableSkillNames = getSetupInstallableSkillNames();
|
|
3854
3968
|
const installableSkills = [];
|
|
3855
3969
|
const manifest = tryReadCatalogManifest();
|
|
@@ -3906,6 +4020,7 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
|
|
|
3906
4020
|
const skillSrc = skill.sourceDir;
|
|
3907
4021
|
const skillDst = skill.destinationDir;
|
|
3908
4022
|
if (!options.dryRun) {
|
|
4023
|
+
await assertManagedDestination(skillDst, backupContext.baseRoot);
|
|
3909
4024
|
await mkdir(skillDst, { recursive: true });
|
|
3910
4025
|
}
|
|
3911
4026
|
const skillFiles = await readdir(skillSrc);
|
|
@@ -3929,7 +4044,7 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
|
|
|
3929
4044
|
// Without an authority path, refuse to create a receipt rather than placing deletion authority
|
|
3930
4045
|
// in the user-writable skills directory.
|
|
3931
4046
|
if (options.skillReceiptPath) {
|
|
3932
|
-
await writeInstalledSkillReceipt(options.skillReceiptPath, dstDir, writtenFilesBySkill, options);
|
|
4047
|
+
await writeInstalledSkillReceipt(options.skillReceiptPath, dstDir, writtenFilesBySkill, options, backupContext.baseRoot);
|
|
3933
4048
|
}
|
|
3934
4049
|
if (manifest && existsSync(dstDir)) {
|
|
3935
4050
|
for (const staleSkill of staleCandidateSkillNames) {
|