skalpel 2.0.8 → 2.0.11

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/cli/index.js CHANGED
@@ -578,7 +578,7 @@ async function runReplay(filePaths) {
578
578
 
579
579
  // src/cli/start.ts
580
580
  import { spawn } from "child_process";
581
- import path10 from "path";
581
+ import path12 from "path";
582
582
  import { fileURLToPath as fileURLToPath2 } from "url";
583
583
 
584
584
  // src/proxy/config.ts
@@ -995,10 +995,260 @@ function uninstallService() {
995
995
  }
996
996
  }
997
997
 
998
+ // src/cli/agents/configure.ts
999
+ import fs9 from "fs";
1000
+ import path10 from "path";
1001
+ import os7 from "os";
1002
+ function ensureDir(dir) {
1003
+ fs9.mkdirSync(dir, { recursive: true });
1004
+ }
1005
+ function createBackup(filePath) {
1006
+ if (fs9.existsSync(filePath)) {
1007
+ fs9.copyFileSync(filePath, `${filePath}.skalpel-backup`);
1008
+ }
1009
+ }
1010
+ function readJsonFile(filePath) {
1011
+ try {
1012
+ return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
1013
+ } catch {
1014
+ return null;
1015
+ }
1016
+ }
1017
+ function configureClaudeCode(agent, proxyConfig) {
1018
+ const configPath = agent.configPath ?? path10.join(os7.homedir(), ".claude", "settings.json");
1019
+ const configDir = path10.dirname(configPath);
1020
+ ensureDir(configDir);
1021
+ createBackup(configPath);
1022
+ const config = readJsonFile(configPath) ?? {};
1023
+ if (!config.env || typeof config.env !== "object") {
1024
+ config.env = {};
1025
+ }
1026
+ config.env.ANTHROPIC_BASE_URL = `http://localhost:${proxyConfig.anthropicPort}`;
1027
+ fs9.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1028
+ }
1029
+ function readTomlFile(filePath) {
1030
+ try {
1031
+ return fs9.readFileSync(filePath, "utf-8");
1032
+ } catch {
1033
+ return "";
1034
+ }
1035
+ }
1036
+ function setTomlKey(content, key, value) {
1037
+ const pattern = new RegExp(`^${key.replace(".", "\\.")}\\s*=.*$`, "m");
1038
+ const line = `${key} = "${value}"`;
1039
+ if (pattern.test(content)) {
1040
+ return content.replace(pattern, line);
1041
+ }
1042
+ const sectionMatch = content.match(/^\[/m);
1043
+ if (sectionMatch && sectionMatch.index !== void 0) {
1044
+ return content.slice(0, sectionMatch.index) + line + "\n" + content.slice(sectionMatch.index);
1045
+ }
1046
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
1047
+ return content + separator + line + "\n";
1048
+ }
1049
+ function removeTomlKey(content, key) {
1050
+ const pattern = new RegExp(`^${key.replace(".", "\\.")}\\s*=.*\\n?`, "gm");
1051
+ return content.replace(pattern, "");
1052
+ }
1053
+ function configureCodex(agent, proxyConfig) {
1054
+ const configDir = process.platform === "win32" ? path10.join(os7.homedir(), "AppData", "Roaming", "codex") : path10.join(os7.homedir(), ".codex");
1055
+ const configPath = agent.configPath ?? path10.join(configDir, "config.toml");
1056
+ ensureDir(path10.dirname(configPath));
1057
+ createBackup(configPath);
1058
+ let content = readTomlFile(configPath);
1059
+ content = setTomlKey(content, "openai_base_url", `http://localhost:${proxyConfig.openaiPort}`);
1060
+ fs9.writeFileSync(configPath, content);
1061
+ }
1062
+ function configureAgent(agent, proxyConfig) {
1063
+ switch (agent.name) {
1064
+ case "claude-code":
1065
+ configureClaudeCode(agent, proxyConfig);
1066
+ break;
1067
+ case "codex":
1068
+ configureCodex(agent, proxyConfig);
1069
+ break;
1070
+ }
1071
+ }
1072
+ function unconfigureClaudeCode(agent) {
1073
+ const configPath = agent.configPath ?? path10.join(os7.homedir(), ".claude", "settings.json");
1074
+ if (!fs9.existsSync(configPath)) return;
1075
+ const config = readJsonFile(configPath);
1076
+ if (config === null) {
1077
+ console.warn(` [!] Could not parse ${configPath} \u2014 skipping to avoid data loss. Remove ANTHROPIC_BASE_URL manually if needed.`);
1078
+ return;
1079
+ }
1080
+ if (config.env && typeof config.env === "object") {
1081
+ delete config.env.ANTHROPIC_BASE_URL;
1082
+ if (Object.keys(config.env).length === 0) {
1083
+ delete config.env;
1084
+ }
1085
+ }
1086
+ fs9.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1087
+ const backupPath = `${configPath}.skalpel-backup`;
1088
+ if (fs9.existsSync(backupPath)) {
1089
+ fs9.unlinkSync(backupPath);
1090
+ }
1091
+ }
1092
+ function unconfigureCodex(agent) {
1093
+ const configDir = process.platform === "win32" ? path10.join(os7.homedir(), "AppData", "Roaming", "codex") : path10.join(os7.homedir(), ".codex");
1094
+ const configPath = agent.configPath ?? path10.join(configDir, "config.toml");
1095
+ if (fs9.existsSync(configPath)) {
1096
+ let content = readTomlFile(configPath);
1097
+ content = removeTomlKey(content, "openai_base_url");
1098
+ fs9.writeFileSync(configPath, content);
1099
+ }
1100
+ const backupPath = `${configPath}.skalpel-backup`;
1101
+ if (fs9.existsSync(backupPath)) {
1102
+ fs9.unlinkSync(backupPath);
1103
+ }
1104
+ }
1105
+ function unconfigureAgent(agent) {
1106
+ switch (agent.name) {
1107
+ case "claude-code":
1108
+ unconfigureClaudeCode(agent);
1109
+ break;
1110
+ case "codex":
1111
+ unconfigureCodex(agent);
1112
+ break;
1113
+ }
1114
+ }
1115
+
1116
+ // src/cli/agents/shell.ts
1117
+ import fs10 from "fs";
1118
+ import path11 from "path";
1119
+ import os8 from "os";
1120
+ var BEGIN_MARKER = "# BEGIN SKALPEL PROXY - do not edit manually";
1121
+ var END_MARKER = "# END SKALPEL PROXY";
1122
+ var PS_BEGIN_MARKER = "# BEGIN SKALPEL PROXY - do not edit manually";
1123
+ var PS_END_MARKER = "# END SKALPEL PROXY";
1124
+ function getUnixProfilePaths() {
1125
+ const home = os8.homedir();
1126
+ const candidates = [
1127
+ path11.join(home, ".bashrc"),
1128
+ path11.join(home, ".zshrc"),
1129
+ path11.join(home, ".bash_profile"),
1130
+ path11.join(home, ".profile")
1131
+ ];
1132
+ return candidates.filter((p) => fs10.existsSync(p));
1133
+ }
1134
+ function getPowerShellProfilePath() {
1135
+ if (process.platform !== "win32") return null;
1136
+ if (process.env.PROFILE) return process.env.PROFILE;
1137
+ const docsDir = path11.join(os8.homedir(), "Documents");
1138
+ const psProfile = path11.join(docsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1");
1139
+ const wpProfile = path11.join(docsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
1140
+ if (fs10.existsSync(psProfile)) return psProfile;
1141
+ if (fs10.existsSync(wpProfile)) return wpProfile;
1142
+ return psProfile;
1143
+ }
1144
+ function generateUnixBlock(proxyConfig) {
1145
+ return [
1146
+ BEGIN_MARKER,
1147
+ `export ANTHROPIC_BASE_URL="http://localhost:${proxyConfig.anthropicPort}"`,
1148
+ `export OPENAI_BASE_URL="http://localhost:${proxyConfig.openaiPort}"`,
1149
+ END_MARKER
1150
+ ].join("\n");
1151
+ }
1152
+ function generatePowerShellBlock(proxyConfig) {
1153
+ return [
1154
+ PS_BEGIN_MARKER,
1155
+ `$env:ANTHROPIC_BASE_URL = "http://localhost:${proxyConfig.anthropicPort}"`,
1156
+ `$env:OPENAI_BASE_URL = "http://localhost:${proxyConfig.openaiPort}"`,
1157
+ PS_END_MARKER
1158
+ ].join("\n");
1159
+ }
1160
+ function createBackup2(filePath) {
1161
+ const backupPath = `${filePath}.skalpel-backup`;
1162
+ fs10.copyFileSync(filePath, backupPath);
1163
+ }
1164
+ function updateProfileFile(filePath, block, beginMarker, endMarker) {
1165
+ if (fs10.existsSync(filePath)) {
1166
+ createBackup2(filePath);
1167
+ }
1168
+ let content = fs10.existsSync(filePath) ? fs10.readFileSync(filePath, "utf-8") : "";
1169
+ const beginIdx = content.indexOf(beginMarker);
1170
+ const endIdx = content.indexOf(endMarker);
1171
+ if (beginIdx !== -1 && endIdx !== -1) {
1172
+ content = content.slice(0, beginIdx) + block + content.slice(endIdx + endMarker.length);
1173
+ } else {
1174
+ if (content.length > 0) {
1175
+ const trimmed = content.replace(/\n+$/, "");
1176
+ content = trimmed + "\n\n" + block + "\n";
1177
+ } else {
1178
+ content = block + "\n";
1179
+ }
1180
+ }
1181
+ fs10.writeFileSync(filePath, content);
1182
+ }
1183
+ function configureShellEnvVars(_agents, proxyConfig) {
1184
+ const modified = [];
1185
+ if (process.platform === "win32") {
1186
+ const psProfile = getPowerShellProfilePath();
1187
+ if (psProfile) {
1188
+ const dir = path11.dirname(psProfile);
1189
+ fs10.mkdirSync(dir, { recursive: true });
1190
+ const block = generatePowerShellBlock(proxyConfig);
1191
+ updateProfileFile(psProfile, block, PS_BEGIN_MARKER, PS_END_MARKER);
1192
+ modified.push(psProfile);
1193
+ }
1194
+ } else {
1195
+ const profiles = getUnixProfilePaths();
1196
+ const block = generateUnixBlock(proxyConfig);
1197
+ for (const profilePath of profiles) {
1198
+ updateProfileFile(profilePath, block, BEGIN_MARKER, END_MARKER);
1199
+ modified.push(profilePath);
1200
+ }
1201
+ }
1202
+ return modified;
1203
+ }
1204
+ function removeShellEnvVars() {
1205
+ const restored = [];
1206
+ const home = os8.homedir();
1207
+ const allProfiles = [
1208
+ path11.join(home, ".bashrc"),
1209
+ path11.join(home, ".zshrc"),
1210
+ path11.join(home, ".bash_profile"),
1211
+ path11.join(home, ".profile")
1212
+ ];
1213
+ if (process.platform === "win32") {
1214
+ const psProfile = getPowerShellProfilePath();
1215
+ if (psProfile) allProfiles.push(psProfile);
1216
+ }
1217
+ for (const profilePath of allProfiles) {
1218
+ if (!fs10.existsSync(profilePath)) continue;
1219
+ const content = fs10.readFileSync(profilePath, "utf-8");
1220
+ const beginIdx = content.indexOf(BEGIN_MARKER);
1221
+ const endIdx = content.indexOf(END_MARKER);
1222
+ if (beginIdx === -1 || endIdx === -1) continue;
1223
+ const before = content.slice(0, beginIdx);
1224
+ const after = content.slice(endIdx + END_MARKER.length);
1225
+ const cleaned = (before.replace(/\n+$/, "") + after.replace(/^\n+/, "\n")).trimEnd() + "\n";
1226
+ fs10.writeFileSync(profilePath, cleaned);
1227
+ const backupPath = `${profilePath}.skalpel-backup`;
1228
+ if (fs10.existsSync(backupPath)) {
1229
+ fs10.unlinkSync(backupPath);
1230
+ }
1231
+ restored.push(profilePath);
1232
+ }
1233
+ return restored;
1234
+ }
1235
+
998
1236
  // src/cli/start.ts
999
1237
  function print5(msg) {
1000
1238
  console.log(msg);
1001
1239
  }
1240
+ async function setNormalMode(config) {
1241
+ try {
1242
+ const res = await fetch(`http://localhost:${config.anthropicPort}/admin/mode`, {
1243
+ method: "POST",
1244
+ headers: { "Content-Type": "application/json" },
1245
+ body: JSON.stringify({ mode: "normal" })
1246
+ });
1247
+ return res.ok;
1248
+ } catch {
1249
+ return false;
1250
+ }
1251
+ }
1002
1252
  async function runStart() {
1003
1253
  const config = loadConfig();
1004
1254
  if (!config.apiKey) {
@@ -1007,22 +1257,45 @@ async function runStart() {
1007
1257
  }
1008
1258
  const existingPid = readPid(config.pidFile);
1009
1259
  if (existingPid !== null) {
1010
- print5(` Proxy is already running (pid=${existingPid}).`);
1260
+ const switched = await setNormalMode(config);
1261
+ if (switched) {
1262
+ print5(" Skalpel optimization re-enabled.");
1263
+ } else {
1264
+ print5(` Proxy is already running (pid=${existingPid}).`);
1265
+ }
1266
+ configureAgentsForProxy(config);
1011
1267
  return;
1012
1268
  }
1013
1269
  if (isServiceInstalled()) {
1014
1270
  startService();
1015
1271
  print5(` Skalpel proxy started via system service on ports ${config.anthropicPort} and ${config.openaiPort}`);
1272
+ configureAgentsForProxy(config);
1016
1273
  return;
1017
1274
  }
1018
- const dirname = path10.dirname(fileURLToPath2(import.meta.url));
1019
- const runnerScript = path10.resolve(dirname, "proxy-runner.js");
1275
+ const dirname = path12.dirname(fileURLToPath2(import.meta.url));
1276
+ const runnerScript = path12.resolve(dirname, "proxy-runner.js");
1020
1277
  const child = spawn(process.execPath, [runnerScript], {
1021
1278
  detached: true,
1022
1279
  stdio: "ignore"
1023
1280
  });
1024
1281
  child.unref();
1025
1282
  print5(` Skalpel proxy started on ports ${config.anthropicPort} and ${config.openaiPort}`);
1283
+ configureAgentsForProxy(config);
1284
+ }
1285
+ function configureAgentsForProxy(config) {
1286
+ const agents = detectAgents();
1287
+ for (const agent of agents) {
1288
+ if (agent.installed) {
1289
+ try {
1290
+ configureAgent(agent, config);
1291
+ print5(` Configured ${agent.name}`);
1292
+ } catch (err) {
1293
+ const msg = err instanceof Error ? err.message : String(err);
1294
+ print5(` Could not configure ${agent.name}: ${msg}`);
1295
+ }
1296
+ }
1297
+ }
1298
+ configureShellEnvVars(agents, config);
1026
1299
  }
1027
1300
 
1028
1301
  // src/proxy/server.ts
@@ -1046,8 +1319,8 @@ var STRIP_HEADERS = /* @__PURE__ */ new Set([
1046
1319
  ]);
1047
1320
 
1048
1321
  // src/proxy/logger.ts
1049
- import fs9 from "fs";
1050
- import path11 from "path";
1322
+ import fs11 from "fs";
1323
+ import path13 from "path";
1051
1324
  var MAX_SIZE = 5 * 1024 * 1024;
1052
1325
 
1053
1326
  // src/proxy/server.ts
@@ -1077,8 +1350,33 @@ function getProxyStatus(config) {
1077
1350
  function print6(msg) {
1078
1351
  console.log(msg);
1079
1352
  }
1353
+ async function setPassthroughMode(config) {
1354
+ try {
1355
+ const res = await fetch(`http://localhost:${config.anthropicPort}/admin/mode`, {
1356
+ method: "POST",
1357
+ headers: { "Content-Type": "application/json" },
1358
+ body: JSON.stringify({ mode: "passthrough" })
1359
+ });
1360
+ return res.ok;
1361
+ } catch {
1362
+ return false;
1363
+ }
1364
+ }
1080
1365
  async function runStop() {
1081
1366
  const config = loadConfig();
1367
+ const pid = readPid(config.pidFile);
1368
+ if (pid === null) {
1369
+ print6(" Proxy is not running.");
1370
+ return;
1371
+ }
1372
+ const switched = await setPassthroughMode(config);
1373
+ if (switched) {
1374
+ print6(" Skalpel optimization paused (proxy running in passthrough mode).");
1375
+ print6(" Requests now go directly to provider APIs.");
1376
+ print6(' Run "skalpel start" to re-enable optimization.');
1377
+ return;
1378
+ }
1379
+ print6(" Could not reach proxy \u2014 performing full shutdown...");
1082
1380
  if (isServiceInstalled()) {
1083
1381
  stopService();
1084
1382
  }
@@ -1088,12 +1386,41 @@ async function runStop() {
1088
1386
  } else {
1089
1387
  print6(" Proxy is not running.");
1090
1388
  }
1389
+ const restoredProfiles = removeShellEnvVars();
1390
+ if (restoredProfiles.length > 0) {
1391
+ for (const p of restoredProfiles) {
1392
+ print6(` Restored shell profile: ${p}`);
1393
+ }
1394
+ }
1395
+ const agents = detectAgents();
1396
+ for (const agent of agents) {
1397
+ if (agent.installed) {
1398
+ try {
1399
+ unconfigureAgent(agent);
1400
+ print6(` Restored ${agent.name} config`);
1401
+ } catch (err) {
1402
+ const msg = err instanceof Error ? err.message : String(err);
1403
+ print6(` Could not restore ${agent.name}: ${msg}`);
1404
+ }
1405
+ }
1406
+ }
1091
1407
  }
1092
1408
 
1093
1409
  // src/cli/status.ts
1094
1410
  function print7(msg) {
1095
1411
  console.log(msg);
1096
1412
  }
1413
+ async function fetchProxyMode(port) {
1414
+ try {
1415
+ const res = await fetch(`http://localhost:${port}/health`);
1416
+ if (res.ok) {
1417
+ const data = await res.json();
1418
+ return data.mode ?? null;
1419
+ }
1420
+ } catch {
1421
+ }
1422
+ return null;
1423
+ }
1097
1424
  async function runStatus() {
1098
1425
  const config = loadConfig();
1099
1426
  const status = getProxyStatus(config);
@@ -1104,6 +1431,12 @@ async function runStatus() {
1104
1431
  if (status.pid !== null) {
1105
1432
  print7(` PID: ${status.pid}`);
1106
1433
  }
1434
+ if (status.running) {
1435
+ const mode = await fetchProxyMode(config.anthropicPort);
1436
+ if (mode) {
1437
+ print7(` Mode: ${mode}`);
1438
+ }
1439
+ }
1107
1440
  print7(` Anthropic: port ${status.anthropicPort}`);
1108
1441
  print7(` OpenAI: port ${status.openaiPort}`);
1109
1442
  print7(` Config: ${config.configFile}`);
@@ -1111,7 +1444,7 @@ async function runStatus() {
1111
1444
  }
1112
1445
 
1113
1446
  // src/cli/logs.ts
1114
- import fs10 from "fs";
1447
+ import fs12 from "fs";
1115
1448
  function print8(msg) {
1116
1449
  console.log(msg);
1117
1450
  }
@@ -1119,26 +1452,26 @@ async function runLogs(options) {
1119
1452
  const config = loadConfig();
1120
1453
  const logFile = config.logFile;
1121
1454
  const lineCount = parseInt(options.lines ?? "50", 10);
1122
- if (!fs10.existsSync(logFile)) {
1455
+ if (!fs12.existsSync(logFile)) {
1123
1456
  print8(` No log file found at ${logFile}`);
1124
1457
  return;
1125
1458
  }
1126
- const content = fs10.readFileSync(logFile, "utf-8");
1459
+ const content = fs12.readFileSync(logFile, "utf-8");
1127
1460
  const lines = content.trimEnd().split("\n");
1128
1461
  const tail = lines.slice(-lineCount);
1129
1462
  for (const line of tail) {
1130
1463
  print8(line);
1131
1464
  }
1132
1465
  if (options.follow) {
1133
- let position = fs10.statSync(logFile).size;
1134
- fs10.watchFile(logFile, { interval: 500 }, () => {
1466
+ let position = fs12.statSync(logFile).size;
1467
+ fs12.watchFile(logFile, { interval: 500 }, () => {
1135
1468
  try {
1136
- const stat = fs10.statSync(logFile);
1469
+ const stat = fs12.statSync(logFile);
1137
1470
  if (stat.size > position) {
1138
- const fd = fs10.openSync(logFile, "r");
1471
+ const fd = fs12.openSync(logFile, "r");
1139
1472
  const buf = Buffer.alloc(stat.size - position);
1140
- fs10.readSync(fd, buf, 0, buf.length, position);
1141
- fs10.closeSync(fd);
1473
+ fs12.readSync(fd, buf, 0, buf.length, position);
1474
+ fs12.closeSync(fd);
1142
1475
  process.stdout.write(buf.toString("utf-8"));
1143
1476
  position = stat.size;
1144
1477
  }
@@ -1243,129 +1576,9 @@ async function runUpdate() {
1243
1576
 
1244
1577
  // src/cli/wizard.ts
1245
1578
  import * as readline2 from "readline";
1246
- import * as fs12 from "fs";
1247
- import * as path13 from "path";
1248
- import * as os8 from "os";
1249
-
1250
- // src/cli/agents/configure.ts
1251
- import fs11 from "fs";
1252
- import path12 from "path";
1253
- import os7 from "os";
1254
- function ensureDir(dir) {
1255
- fs11.mkdirSync(dir, { recursive: true });
1256
- }
1257
- function createBackup(filePath) {
1258
- if (fs11.existsSync(filePath)) {
1259
- fs11.copyFileSync(filePath, `${filePath}.skalpel-backup`);
1260
- }
1261
- }
1262
- function readJsonFile(filePath) {
1263
- try {
1264
- return JSON.parse(fs11.readFileSync(filePath, "utf-8"));
1265
- } catch {
1266
- return null;
1267
- }
1268
- }
1269
- function configureClaudeCode(agent, proxyConfig) {
1270
- const configPath = agent.configPath ?? path12.join(os7.homedir(), ".claude", "settings.json");
1271
- const configDir = path12.dirname(configPath);
1272
- ensureDir(configDir);
1273
- createBackup(configPath);
1274
- const config = readJsonFile(configPath) ?? {};
1275
- if (!config.env || typeof config.env !== "object") {
1276
- config.env = {};
1277
- }
1278
- config.env.ANTHROPIC_BASE_URL = `http://localhost:${proxyConfig.anthropicPort}`;
1279
- fs11.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1280
- }
1281
- function readTomlFile(filePath) {
1282
- try {
1283
- return fs11.readFileSync(filePath, "utf-8");
1284
- } catch {
1285
- return "";
1286
- }
1287
- }
1288
- function setTomlKey(content, key, value) {
1289
- const pattern = new RegExp(`^${key.replace(".", "\\.")}\\s*=.*$`, "m");
1290
- const line = `${key} = "${value}"`;
1291
- if (pattern.test(content)) {
1292
- return content.replace(pattern, line);
1293
- }
1294
- const sectionMatch = content.match(/^\[/m);
1295
- if (sectionMatch && sectionMatch.index !== void 0) {
1296
- return content.slice(0, sectionMatch.index) + line + "\n" + content.slice(sectionMatch.index);
1297
- }
1298
- const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
1299
- return content + separator + line + "\n";
1300
- }
1301
- function removeTomlKey(content, key) {
1302
- const pattern = new RegExp(`^${key.replace(".", "\\.")}\\s*=.*\\n?`, "gm");
1303
- return content.replace(pattern, "");
1304
- }
1305
- function configureCodex(agent, proxyConfig) {
1306
- const configDir = process.platform === "win32" ? path12.join(os7.homedir(), "AppData", "Roaming", "codex") : path12.join(os7.homedir(), ".codex");
1307
- const configPath = agent.configPath ?? path12.join(configDir, "config.toml");
1308
- ensureDir(path12.dirname(configPath));
1309
- createBackup(configPath);
1310
- let content = readTomlFile(configPath);
1311
- content = setTomlKey(content, "openai_base_url", `http://localhost:${proxyConfig.openaiPort}`);
1312
- fs11.writeFileSync(configPath, content);
1313
- }
1314
- function configureAgent(agent, proxyConfig) {
1315
- switch (agent.name) {
1316
- case "claude-code":
1317
- configureClaudeCode(agent, proxyConfig);
1318
- break;
1319
- case "codex":
1320
- configureCodex(agent, proxyConfig);
1321
- break;
1322
- }
1323
- }
1324
- function unconfigureClaudeCode(agent) {
1325
- const configPath = agent.configPath ?? path12.join(os7.homedir(), ".claude", "settings.json");
1326
- if (!fs11.existsSync(configPath)) return;
1327
- const config = readJsonFile(configPath);
1328
- if (config === null) {
1329
- console.warn(` [!] Could not parse ${configPath} \u2014 skipping to avoid data loss. Remove ANTHROPIC_BASE_URL manually if needed.`);
1330
- return;
1331
- }
1332
- if (config.env && typeof config.env === "object") {
1333
- delete config.env.ANTHROPIC_BASE_URL;
1334
- if (Object.keys(config.env).length === 0) {
1335
- delete config.env;
1336
- }
1337
- }
1338
- fs11.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1339
- const backupPath = `${configPath}.skalpel-backup`;
1340
- if (fs11.existsSync(backupPath)) {
1341
- fs11.unlinkSync(backupPath);
1342
- }
1343
- }
1344
- function unconfigureCodex(agent) {
1345
- const configDir = process.platform === "win32" ? path12.join(os7.homedir(), "AppData", "Roaming", "codex") : path12.join(os7.homedir(), ".codex");
1346
- const configPath = agent.configPath ?? path12.join(configDir, "config.toml");
1347
- if (fs11.existsSync(configPath)) {
1348
- let content = readTomlFile(configPath);
1349
- content = removeTomlKey(content, "openai_base_url");
1350
- fs11.writeFileSync(configPath, content);
1351
- }
1352
- const backupPath = `${configPath}.skalpel-backup`;
1353
- if (fs11.existsSync(backupPath)) {
1354
- fs11.unlinkSync(backupPath);
1355
- }
1356
- }
1357
- function unconfigureAgent(agent) {
1358
- switch (agent.name) {
1359
- case "claude-code":
1360
- unconfigureClaudeCode(agent);
1361
- break;
1362
- case "codex":
1363
- unconfigureCodex(agent);
1364
- break;
1365
- }
1366
- }
1367
-
1368
- // src/cli/wizard.ts
1579
+ import * as fs13 from "fs";
1580
+ import * as path14 from "path";
1581
+ import * as os9 from "os";
1369
1582
  function print11(msg) {
1370
1583
  console.log(msg);
1371
1584
  }
@@ -1400,8 +1613,8 @@ async function runWizard(options) {
1400
1613
  print11(" Welcome to Skalpel! Let's optimize your coding agent costs.");
1401
1614
  print11(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1402
1615
  print11("");
1403
- const skalpelDir = path13.join(os8.homedir(), ".skalpel");
1404
- const configPath = path13.join(skalpelDir, "config.json");
1616
+ const skalpelDir = path14.join(os9.homedir(), ".skalpel");
1617
+ const configPath = path14.join(skalpelDir, "config.json");
1405
1618
  let apiKey = "";
1406
1619
  if (isAuto && options?.apiKey) {
1407
1620
  apiKey = options.apiKey;
@@ -1414,9 +1627,9 @@ async function runWizard(options) {
1414
1627
  print11(" Error: --api-key is required when using --auto mode.");
1415
1628
  process.exit(1);
1416
1629
  } else {
1417
- if (fs12.existsSync(configPath)) {
1630
+ if (fs13.existsSync(configPath)) {
1418
1631
  try {
1419
- const existing = JSON.parse(fs12.readFileSync(configPath, "utf-8"));
1632
+ const existing = JSON.parse(fs13.readFileSync(configPath, "utf-8"));
1420
1633
  if (existing.apiKey && validateApiKey(existing.apiKey)) {
1421
1634
  const masked = existing.apiKey.slice(0, 14) + "*".repeat(Math.max(0, existing.apiKey.length - 14));
1422
1635
  const useExisting = await ask(` Found existing API key: ${masked}
@@ -1440,7 +1653,7 @@ async function runWizard(options) {
1440
1653
  }
1441
1654
  }
1442
1655
  print11("");
1443
- fs12.mkdirSync(skalpelDir, { recursive: true });
1656
+ fs13.mkdirSync(skalpelDir, { recursive: true });
1444
1657
  const proxyConfig = loadConfig(configPath);
1445
1658
  proxyConfig.apiKey = apiKey;
1446
1659
  saveConfig(proxyConfig);
@@ -1552,56 +1765,6 @@ import * as readline3 from "readline";
1552
1765
  import * as fs14 from "fs";
1553
1766
  import * as path15 from "path";
1554
1767
  import * as os10 from "os";
1555
-
1556
- // src/cli/agents/shell.ts
1557
- import fs13 from "fs";
1558
- import path14 from "path";
1559
- import os9 from "os";
1560
- var BEGIN_MARKER = "# BEGIN SKALPEL PROXY - do not edit manually";
1561
- var END_MARKER = "# END SKALPEL PROXY";
1562
- function getPowerShellProfilePath() {
1563
- if (process.platform !== "win32") return null;
1564
- if (process.env.PROFILE) return process.env.PROFILE;
1565
- const docsDir = path14.join(os9.homedir(), "Documents");
1566
- const psProfile = path14.join(docsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1");
1567
- const wpProfile = path14.join(docsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
1568
- if (fs13.existsSync(psProfile)) return psProfile;
1569
- if (fs13.existsSync(wpProfile)) return wpProfile;
1570
- return psProfile;
1571
- }
1572
- function removeShellEnvVars() {
1573
- const restored = [];
1574
- const home = os9.homedir();
1575
- const allProfiles = [
1576
- path14.join(home, ".bashrc"),
1577
- path14.join(home, ".zshrc"),
1578
- path14.join(home, ".bash_profile"),
1579
- path14.join(home, ".profile")
1580
- ];
1581
- if (process.platform === "win32") {
1582
- const psProfile = getPowerShellProfilePath();
1583
- if (psProfile) allProfiles.push(psProfile);
1584
- }
1585
- for (const profilePath of allProfiles) {
1586
- if (!fs13.existsSync(profilePath)) continue;
1587
- const content = fs13.readFileSync(profilePath, "utf-8");
1588
- const beginIdx = content.indexOf(BEGIN_MARKER);
1589
- const endIdx = content.indexOf(END_MARKER);
1590
- if (beginIdx === -1 || endIdx === -1) continue;
1591
- const before = content.slice(0, beginIdx);
1592
- const after = content.slice(endIdx + END_MARKER.length);
1593
- const cleaned = (before.replace(/\n+$/, "") + after.replace(/^\n+/, "\n")).trimEnd() + "\n";
1594
- fs13.writeFileSync(profilePath, cleaned);
1595
- const backupPath = `${profilePath}.skalpel-backup`;
1596
- if (fs13.existsSync(backupPath)) {
1597
- fs13.unlinkSync(backupPath);
1598
- }
1599
- restored.push(profilePath);
1600
- }
1601
- return restored;
1602
- }
1603
-
1604
- // src/cli/uninstall.ts
1605
1768
  function print12(msg) {
1606
1769
  console.log(msg);
1607
1770
  }