impel-cli 0.16.3 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -18
- package/package.json +1 -1
- package/src/apps.js +8 -2
- package/src/claudeSandbox.js +81 -18
- package/src/cli.js +5 -0
- package/src/cliProfiles.js +5 -1
- package/src/codexSecurity.js +43 -1
- package/src/codexSetup.js +146 -3
- package/src/commands/sessions.js +114 -0
- package/src/commands/use.js +14 -0
- package/src/config.js +8 -3
- package/src/gateway/index.js +2 -1
- package/src/sessionCollector.js +996 -0
- package/src/sessionHooks.js +242 -0
package/src/codexSetup.js
CHANGED
|
@@ -49,8 +49,17 @@ const START_MARK = `# >>> impel-cli managed block (model_providers.${PROVIDER_ID
|
|
|
49
49
|
const END_MARK = `# <<< impel-cli managed block <<<`;
|
|
50
50
|
|
|
51
51
|
const PROVIDER_LINE_RE = /^model_provider[ \t]*=[ \t]*"([^"]*)"[ \t]*$/m;
|
|
52
|
+
const NETWORK_TABLE = "sandbox_workspace_write";
|
|
53
|
+
const NETWORK_KEY = "network_access";
|
|
54
|
+
const NETWORK_DOTTED_RE = /^(\s*)sandbox_workspace_write\.network_access\s*=.*$/u;
|
|
55
|
+
const NETWORK_HEADER_RE = /^\s*\[sandbox_workspace_write\]\s*(?:#.*)?$/u;
|
|
56
|
+
const NETWORK_KEY_RE = /^(\s*)network_access\s*=.*$/u;
|
|
57
|
+
const SANDBOX_MODE_RE = /^(\s*)sandbox_mode\s*=.*$/u;
|
|
52
58
|
|
|
53
|
-
function providerTablesBlock(
|
|
59
|
+
function providerTablesBlock(
|
|
60
|
+
gatewayUrl,
|
|
61
|
+
{ includeNetworkAccess = false, includeSandboxMode = false } = {},
|
|
62
|
+
) {
|
|
54
63
|
const baseUrl = impelCodexBaseUrl(gatewayUrl);
|
|
55
64
|
const auth = impelCliInvocation(["token"]);
|
|
56
65
|
const mcp = impelCliInvocation(["mcp"]);
|
|
@@ -58,6 +67,8 @@ function providerTablesBlock(gatewayUrl) {
|
|
|
58
67
|
START_MARK,
|
|
59
68
|
"# Generated by `impel use gateway codex`. Safe to re-run; do not hand-edit",
|
|
60
69
|
"# the lines between the markers above/below, they'll be overwritten.",
|
|
70
|
+
...(includeSandboxMode ? ['sandbox_mode = "workspace-write"'] : []),
|
|
71
|
+
...(includeNetworkAccess ? [`${NETWORK_TABLE}.${NETWORK_KEY} = true`, ""] : []),
|
|
61
72
|
`[model_providers.${PROVIDER_ID}]`,
|
|
62
73
|
`name = "Impel Gateway"`,
|
|
63
74
|
`base_url = "${baseUrl}"`,
|
|
@@ -76,6 +87,123 @@ function providerTablesBlock(gatewayUrl) {
|
|
|
76
87
|
].join("\n");
|
|
77
88
|
}
|
|
78
89
|
|
|
90
|
+
function applyCodexSandboxMode(text, configPath = CODEX_CONFIG_PATH) {
|
|
91
|
+
const lines = String(text || "").replace(/\r\n/gu, "\n").split("\n");
|
|
92
|
+
const firstTable = lines.findIndex((line) => /^\s*\[/u.test(line));
|
|
93
|
+
const preambleEnd = firstTable === -1 ? lines.length : firstTable;
|
|
94
|
+
const indices = lines
|
|
95
|
+
.slice(0, preambleEnd)
|
|
96
|
+
.flatMap((line, index) => (SANDBOX_MODE_RE.test(line) ? [index] : []));
|
|
97
|
+
if (indices.length > 1) {
|
|
98
|
+
throw new Error(`${configPath} defines sandbox_mode more than once; resolve it before re-running.`);
|
|
99
|
+
}
|
|
100
|
+
if (indices.length === 0) {
|
|
101
|
+
return { text, prior: { kind: "absent" }, includeInBlock: true };
|
|
102
|
+
}
|
|
103
|
+
const index = indices[0];
|
|
104
|
+
const line = lines[index];
|
|
105
|
+
const indent = line.match(SANDBOX_MODE_RE)?.[1] || "";
|
|
106
|
+
lines[index] = `${indent}sandbox_mode = "workspace-write"`;
|
|
107
|
+
return {
|
|
108
|
+
text: lines.join("\n"),
|
|
109
|
+
prior: { kind: "root", line },
|
|
110
|
+
includeInBlock: false,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function restoreCodexSandboxMode(text, snapshot) {
|
|
115
|
+
if (!snapshot || snapshot.kind === "absent") return { text, changed: false };
|
|
116
|
+
const lines = String(text || "").replace(/\r\n/gu, "\n").split("\n");
|
|
117
|
+
const firstTable = lines.findIndex((line) => /^\s*\[/u.test(line));
|
|
118
|
+
const preambleEnd = firstTable === -1 ? lines.length : firstTable;
|
|
119
|
+
const indices = lines
|
|
120
|
+
.slice(0, preambleEnd)
|
|
121
|
+
.flatMap((line, index) => (SANDBOX_MODE_RE.test(line) ? [index] : []));
|
|
122
|
+
if (indices.length !== 1 || !/^\s*sandbox_mode\s*=\s*"workspace-write"\s*(?:#.*)?$/u.test(lines[indices[0]])) {
|
|
123
|
+
return { text, changed: false };
|
|
124
|
+
}
|
|
125
|
+
lines[indices[0]] = snapshot.line;
|
|
126
|
+
return { text: lines.join("\n"), changed: true };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function locateCodexNetworkAccess(text, configPath = CODEX_CONFIG_PATH) {
|
|
130
|
+
const lines = String(text || "").replace(/\r\n/gu, "\n").split("\n");
|
|
131
|
+
const firstTable = lines.findIndex((line) => /^\s*\[/u.test(line));
|
|
132
|
+
const preambleEnd = firstTable === -1 ? lines.length : firstTable;
|
|
133
|
+
const dotted = lines
|
|
134
|
+
.slice(0, preambleEnd)
|
|
135
|
+
.flatMap((line, index) => (NETWORK_DOTTED_RE.test(line) ? [index] : []));
|
|
136
|
+
const headers = lines.flatMap((line, index) => (NETWORK_HEADER_RE.test(line) ? [index] : []));
|
|
137
|
+
if (dotted.length > 1 || headers.length > 1 || (dotted.length && headers.length)) {
|
|
138
|
+
throw new Error(`${configPath} defines ${NETWORK_TABLE}.${NETWORK_KEY} more than once; resolve it before re-running.`);
|
|
139
|
+
}
|
|
140
|
+
if (dotted.length === 1) {
|
|
141
|
+
const index = dotted[0];
|
|
142
|
+
return { lines, kind: "dotted", index, line: lines[index] };
|
|
143
|
+
}
|
|
144
|
+
if (headers.length === 1) {
|
|
145
|
+
const header = headers[0];
|
|
146
|
+
const nextTableOffset = lines.slice(header + 1).findIndex((line) => /^\s*\[/u.test(line));
|
|
147
|
+
const end = nextTableOffset === -1 ? lines.length : header + 1 + nextTableOffset;
|
|
148
|
+
const keys = lines
|
|
149
|
+
.slice(header + 1, end)
|
|
150
|
+
.flatMap((line, index) => (NETWORK_KEY_RE.test(line) ? [header + 1 + index] : []));
|
|
151
|
+
if (keys.length > 1) {
|
|
152
|
+
throw new Error(`${configPath} defines ${NETWORK_KEY} more than once in [${NETWORK_TABLE}].`);
|
|
153
|
+
}
|
|
154
|
+
if (keys.length === 1) {
|
|
155
|
+
const index = keys[0];
|
|
156
|
+
return { lines, kind: "table", index, line: lines[index] };
|
|
157
|
+
}
|
|
158
|
+
return { lines, kind: "table-missing", header };
|
|
159
|
+
}
|
|
160
|
+
if (lines.slice(0, preambleEnd).some((line) => /^\s*sandbox_workspace_write\s*=/u.test(line))) {
|
|
161
|
+
throw new Error(`${configPath} defines ${NETWORK_TABLE} as an inline table; convert it to [${NETWORK_TABLE}] before re-running.`);
|
|
162
|
+
}
|
|
163
|
+
return { lines, kind: "absent" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function applyCodexNetworkAccess(text, configPath = CODEX_CONFIG_PATH) {
|
|
167
|
+
const state = locateCodexNetworkAccess(text, configPath);
|
|
168
|
+
if (state.kind === "absent") {
|
|
169
|
+
return { text, prior: { kind: "absent" }, includeInBlock: true };
|
|
170
|
+
}
|
|
171
|
+
if (state.kind === "table-missing") {
|
|
172
|
+
state.lines.splice(state.header + 1, 0, `${NETWORK_KEY} = true`);
|
|
173
|
+
return {
|
|
174
|
+
text: state.lines.join("\n"),
|
|
175
|
+
prior: { kind: "table-missing" },
|
|
176
|
+
includeInBlock: false,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const indent = state.line.match(state.kind === "dotted" ? NETWORK_DOTTED_RE : NETWORK_KEY_RE)?.[1] || "";
|
|
180
|
+
state.lines[state.index] = state.kind === "dotted"
|
|
181
|
+
? `${indent}${NETWORK_TABLE}.${NETWORK_KEY} = true`
|
|
182
|
+
: `${indent}${NETWORK_KEY} = true`;
|
|
183
|
+
return {
|
|
184
|
+
text: state.lines.join("\n"),
|
|
185
|
+
prior: { kind: state.kind, line: state.line },
|
|
186
|
+
includeInBlock: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function restoreCodexNetworkAccess(text, snapshot, configPath = CODEX_CONFIG_PATH) {
|
|
191
|
+
if (!snapshot || snapshot.kind === "absent") return { text, changed: false };
|
|
192
|
+
const state = locateCodexNetworkAccess(text, configPath);
|
|
193
|
+
if (!["dotted", "table"].includes(state.kind) || !/=\s*true\s*(?:#.*)?$/u.test(state.line)) {
|
|
194
|
+
return { text, changed: false };
|
|
195
|
+
}
|
|
196
|
+
if (snapshot.kind === "table-missing" && state.kind === "table") {
|
|
197
|
+
state.lines.splice(state.index, 1);
|
|
198
|
+
return { text: state.lines.join("\n"), changed: true };
|
|
199
|
+
}
|
|
200
|
+
if (snapshot.kind !== state.kind || typeof snapshot.line !== "string") {
|
|
201
|
+
return { text, changed: false };
|
|
202
|
+
}
|
|
203
|
+
state.lines[state.index] = snapshot.line;
|
|
204
|
+
return { text: state.lines.join("\n"), changed: true };
|
|
205
|
+
}
|
|
206
|
+
|
|
79
207
|
function readConfig() {
|
|
80
208
|
const exists = fs.existsSync(CODEX_CONFIG_PATH);
|
|
81
209
|
const text = exists ? fs.readFileSync(CODEX_CONFIG_PATH, "utf8") : "";
|
|
@@ -155,7 +283,10 @@ export function applyCodexGateway(gatewayUrl) {
|
|
|
155
283
|
);
|
|
156
284
|
}
|
|
157
285
|
|
|
158
|
-
const
|
|
286
|
+
const sandboxMode = applyCodexSandboxMode(withoutOurBlock);
|
|
287
|
+
const networkAccess = applyCodexNetworkAccess(sandboxMode.text);
|
|
288
|
+
|
|
289
|
+
const { preamble, rest } = splitPreamble(networkAccess.text);
|
|
159
290
|
const priorProviderValue = preamble.match(PROVIDER_LINE_RE)?.[1] ?? null;
|
|
160
291
|
const newProviderLine = `model_provider = "${PROVIDER_ID}"`;
|
|
161
292
|
|
|
@@ -167,7 +298,10 @@ export function applyCodexGateway(gatewayUrl) {
|
|
|
167
298
|
newPreamble = trimmed ? `${trimmed}\n${newProviderLine}\n` : `${newProviderLine}\n`;
|
|
168
299
|
}
|
|
169
300
|
|
|
170
|
-
const block = providerTablesBlock(gatewayUrl
|
|
301
|
+
const block = providerTablesBlock(gatewayUrl, {
|
|
302
|
+
includeNetworkAccess: networkAccess.includeInBlock,
|
|
303
|
+
includeSandboxMode: sandboxMode.includeInBlock,
|
|
304
|
+
});
|
|
171
305
|
const restTrimmed = rest.trim();
|
|
172
306
|
const newText = `${newPreamble.trimEnd()}\n\n${block}\n` + (restTrimmed ? `\n${restTrimmed}\n` : "");
|
|
173
307
|
|
|
@@ -177,6 +311,8 @@ export function applyCodexGateway(gatewayUrl) {
|
|
|
177
311
|
path: CODEX_CONFIG_PATH,
|
|
178
312
|
baseUrl: impelCodexBaseUrl(gatewayUrl),
|
|
179
313
|
priorProviderValue,
|
|
314
|
+
priorNetworkAccess: networkAccess.prior,
|
|
315
|
+
priorSandboxMode: sandboxMode.prior,
|
|
180
316
|
};
|
|
181
317
|
}
|
|
182
318
|
|
|
@@ -196,6 +332,11 @@ export function revertCodexGateway(backup = {}) {
|
|
|
196
332
|
const removedBlock = original.includes(START_MARK);
|
|
197
333
|
let text = stripManagedBlock(original);
|
|
198
334
|
|
|
335
|
+
const networkAccess = restoreCodexNetworkAccess(text, backup.network_access);
|
|
336
|
+
text = networkAccess.text;
|
|
337
|
+
const sandboxMode = restoreCodexSandboxMode(text, backup.sandbox_mode);
|
|
338
|
+
text = sandboxMode.text;
|
|
339
|
+
|
|
199
340
|
let resetProvider = false;
|
|
200
341
|
let restoredProvider = null;
|
|
201
342
|
if (currentProviderValue(text) === PROVIDER_ID) {
|
|
@@ -220,5 +361,7 @@ export function revertCodexGateway(backup = {}) {
|
|
|
220
361
|
removedBlock,
|
|
221
362
|
resetProvider,
|
|
222
363
|
restoredProvider,
|
|
364
|
+
restoredNetworkAccess: networkAccess.changed,
|
|
365
|
+
restoredSandboxMode: sandboxMode.changed,
|
|
223
366
|
};
|
|
224
367
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
import { parseFlags } from "../args.js";
|
|
4
|
+
import { collectSessionHook, flushCollectedSession, readHookInput, sessionOutboxStatus } from "../sessionCollector.js";
|
|
5
|
+
import { loadConfig, redactSecretText } from "../config.js";
|
|
6
|
+
import { impelCliInvocation } from "../selfInvocation.js";
|
|
7
|
+
|
|
8
|
+
const SPEC = {
|
|
9
|
+
provider: { type: "string" },
|
|
10
|
+
surface: { type: "string" },
|
|
11
|
+
tenant: { type: "string" },
|
|
12
|
+
session: { type: "string" },
|
|
13
|
+
"impel-managed-session-hook-v1": { type: "boolean" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function startDetachedFlush({ provider, tenant, session }) {
|
|
17
|
+
const invocation = impelCliInvocation([
|
|
18
|
+
"sessions",
|
|
19
|
+
"flush",
|
|
20
|
+
"--provider",
|
|
21
|
+
provider,
|
|
22
|
+
"--tenant",
|
|
23
|
+
tenant,
|
|
24
|
+
"--session",
|
|
25
|
+
session,
|
|
26
|
+
"--impel-managed-session-hook-v1",
|
|
27
|
+
]);
|
|
28
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
29
|
+
detached: true,
|
|
30
|
+
stdio: "ignore",
|
|
31
|
+
env: { ...process.env, IMPEL_SESSIONS_FLUSH_CHILD: "1" },
|
|
32
|
+
windowsHide: true,
|
|
33
|
+
});
|
|
34
|
+
child.once("error", () => {});
|
|
35
|
+
child.unref();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function cmdSessions(argv) {
|
|
39
|
+
const [action, ...rest] = argv;
|
|
40
|
+
if (action === "hook") {
|
|
41
|
+
try {
|
|
42
|
+
const { flags, positionals } = parseFlags(rest, SPEC);
|
|
43
|
+
if (positionals.length > 0) throw new Error("unexpected positional arguments");
|
|
44
|
+
const input = await readHookInput();
|
|
45
|
+
let config = null;
|
|
46
|
+
try {
|
|
47
|
+
config = loadConfig();
|
|
48
|
+
} catch {
|
|
49
|
+
// Capture remains available even while the user's config needs repair.
|
|
50
|
+
}
|
|
51
|
+
await collectSessionHook({
|
|
52
|
+
provider: flags.provider,
|
|
53
|
+
surface: flags.surface,
|
|
54
|
+
tenantId: flags.tenant,
|
|
55
|
+
input,
|
|
56
|
+
config,
|
|
57
|
+
flush: false,
|
|
58
|
+
});
|
|
59
|
+
if (config && (config.tenantId === flags.tenant || process.env.IMPEL_SESSIONS_DEV_ORG_ID)) {
|
|
60
|
+
startDetachedFlush({
|
|
61
|
+
provider: flags.provider,
|
|
62
|
+
tenant: flags.tenant,
|
|
63
|
+
session: String(input.session_id || ""),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Session persistence is observational. A collector outage must never
|
|
68
|
+
// block a model turn or change a provider hook's decision semantics.
|
|
69
|
+
if (process.env.IMPEL_SESSIONS_DEBUG === "1") {
|
|
70
|
+
console.error(`impel sessions hook: ${redactSecretText(error?.message || error)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const { flags, positionals } = parseFlags(rest, SPEC);
|
|
76
|
+
if (positionals.length > 0) {
|
|
77
|
+
console.error("impel sessions: unexpected positional arguments");
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const config = loadConfig();
|
|
82
|
+
if (action === "flush") {
|
|
83
|
+
if (!flags.provider || !flags.session || !flags.tenant) return;
|
|
84
|
+
if (!["claude_code", "codex"].includes(flags.provider)) return;
|
|
85
|
+
if (!config || flags.tenant !== config.tenantId && !process.env.IMPEL_SESSIONS_DEV_ORG_ID) return;
|
|
86
|
+
const deadline = Date.now() + 90_000;
|
|
87
|
+
while (Date.now() < deadline) {
|
|
88
|
+
const result = await flushCollectedSession({
|
|
89
|
+
tenantId: flags.tenant,
|
|
90
|
+
provider: flags.provider,
|
|
91
|
+
sessionKey: flags.session,
|
|
92
|
+
config,
|
|
93
|
+
});
|
|
94
|
+
if (result.pending === 0) break;
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, result.error || result.busy ? 1000 : 250));
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (action === "status") {
|
|
100
|
+
if (!flags.provider || !flags.session || !flags.tenant) {
|
|
101
|
+
console.error("impel sessions status requires --provider, --session, and --tenant");
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(JSON.stringify(sessionOutboxStatus({
|
|
106
|
+
tenantId: flags.tenant,
|
|
107
|
+
provider: flags.provider,
|
|
108
|
+
sessionKey: flags.session,
|
|
109
|
+
}), null, 2));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
console.error("impel sessions: use `hook` (managed internally) or `status`");
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
}
|
package/src/commands/use.js
CHANGED
|
@@ -78,6 +78,11 @@ async function useGateway({ target, app }) {
|
|
|
78
78
|
}
|
|
79
79
|
if (!Object.hasOwn(backup, "sandbox")) {
|
|
80
80
|
backup.sandbox = result.priorSandbox;
|
|
81
|
+
} else if (
|
|
82
|
+
!Object.hasOwn(backup.sandbox, "permissions")
|
|
83
|
+
&& Object.hasOwn(result.priorSandbox, "permissions")
|
|
84
|
+
) {
|
|
85
|
+
backup.sandbox.permissions = result.priorSandbox.permissions;
|
|
81
86
|
}
|
|
82
87
|
console.log(`Claude Code -> GATEWAY (${result.path})`);
|
|
83
88
|
console.log(` apiKeyHelper = "${result.apiKeyHelper}"`);
|
|
@@ -99,12 +104,19 @@ async function useGateway({ target, app }) {
|
|
|
99
104
|
if (result.priorProviderValue !== PROVIDER_ID) {
|
|
100
105
|
backup.model_provider = result.priorProviderValue ?? null;
|
|
101
106
|
}
|
|
107
|
+
if (!Object.hasOwn(backup, "network_access")) {
|
|
108
|
+
backup.network_access = result.priorNetworkAccess;
|
|
109
|
+
}
|
|
110
|
+
if (!Object.hasOwn(backup, "sandbox_mode")) {
|
|
111
|
+
backup.sandbox_mode = result.priorSandboxMode;
|
|
112
|
+
}
|
|
102
113
|
|
|
103
114
|
console.log(`Codex ${app ? "app/IDE" : "CLI"} -> GATEWAY (${result.path})`);
|
|
104
115
|
console.log(` model_provider = "${PROVIDER_ID}"`);
|
|
105
116
|
console.log(` [model_providers.${PROVIDER_ID}] base_url = "${result.baseUrl}"`);
|
|
106
117
|
console.log(` [model_providers.${PROVIDER_ID}.auth] = direct Impel CLI invocation`);
|
|
107
118
|
console.log(` [mcp_servers.${PROVIDER_ID}] = direct Impel CLI invocation`);
|
|
119
|
+
console.log(` sandbox workspace-write network = unrestricted`);
|
|
108
120
|
}
|
|
109
121
|
|
|
110
122
|
saveConfig(config);
|
|
@@ -194,6 +206,8 @@ function useAccount({ target, app }) {
|
|
|
194
206
|
: ` removed model_provider line (Codex falls back to its default)`
|
|
195
207
|
);
|
|
196
208
|
}
|
|
209
|
+
if (result.restoredNetworkAccess) console.log(` restored prior Codex sandbox network setting`);
|
|
210
|
+
if (result.restoredSandboxMode) console.log(` restored prior Codex sandbox mode`);
|
|
197
211
|
}
|
|
198
212
|
if (config?.backups) delete config.backups.codex;
|
|
199
213
|
}
|
package/src/config.js
CHANGED
|
@@ -89,11 +89,16 @@ const PAT_RE = /impel_pat_[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)?/gu;
|
|
|
89
89
|
const ANSI_ESCAPE_RE = /\u001B(?:\][^\u0007\u001B]*(?:\u0007|\u001B\\)|\[[0-?]*[ -/]*[@-~]|[@-_])/gu;
|
|
90
90
|
const TERMINAL_CONTROL_RE = /[\u0000-\u001F\u007F-\u009F]/gu;
|
|
91
91
|
|
|
92
|
-
/**
|
|
93
|
-
export function
|
|
92
|
+
/** Redact Impel bearer credentials without changing JSON/NDJSON whitespace. */
|
|
93
|
+
export function redactCredentialText(value) {
|
|
94
94
|
return String(value ?? "")
|
|
95
95
|
.replace(TENANT_CREDENTIAL_RE, "[REDACTED IMPEL CREDENTIAL]")
|
|
96
|
-
.replace(PAT_RE, "[REDACTED IMPEL CREDENTIAL]")
|
|
96
|
+
.replace(PAT_RE, "[REDACTED IMPEL CREDENTIAL]");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Remove credentials and terminal control sequences from untrusted text. */
|
|
100
|
+
export function redactSecretText(value) {
|
|
101
|
+
return redactCredentialText(value)
|
|
97
102
|
.replace(ANSI_ESCAPE_RE, "")
|
|
98
103
|
.replace(TERMINAL_CONTROL_RE, " ");
|
|
99
104
|
}
|
package/src/gateway/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import readline from "node:readline";
|
|
|
6
6
|
|
|
7
7
|
import { nativeCommandInvocation } from "../nativeProcess.js";
|
|
8
8
|
import { applyImpelClaudeSandbox } from "../claudeSandbox.js";
|
|
9
|
+
import { enableManagedCodexNetworkAccess } from "../codexSecurity.js";
|
|
9
10
|
|
|
10
11
|
const SAFE_ID = /^[a-z][a-z0-9-]{2,31}$/u;
|
|
11
12
|
const SAFE_NAMESPACE = /^[a-z][a-z0-9_-]{1,31}$/u;
|
|
@@ -355,7 +356,7 @@ export function createGatewayCli(options) {
|
|
|
355
356
|
const withoutProvider = outside.replace(/^\s*model_provider\s*=.*$/mu, "").trim();
|
|
356
357
|
const next = [`model_provider = ${JSON.stringify(brand.cli.providerId)}`, codexManagedBlock(gatewayUrl), withoutProvider]
|
|
357
358
|
.filter(Boolean).join("\n\n").concat("\n");
|
|
358
|
-
writePrivateFile(filePath, next);
|
|
359
|
+
writePrivateFile(filePath, enableManagedCodexNetworkAccess(next, filePath));
|
|
359
360
|
return { codexHome, filePath };
|
|
360
361
|
}
|
|
361
362
|
|