theclawbay 0.3.49 → 0.3.51
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/setup.js +77 -3
- package/package.json +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -970,6 +970,23 @@ function summarizeModelFetchFailure(detail) {
|
|
|
970
970
|
return normalized;
|
|
971
971
|
return `${normalized.slice(0, 137).trimEnd()}...`;
|
|
972
972
|
}
|
|
973
|
+
function isCredentialRejectedFailure(detail) {
|
|
974
|
+
if (!detail)
|
|
975
|
+
return false;
|
|
976
|
+
const normalized = detail.toLowerCase();
|
|
977
|
+
return (normalized.includes("http 401") ||
|
|
978
|
+
normalized.includes("invalid api key") ||
|
|
979
|
+
normalized.includes("missing bearer token") ||
|
|
980
|
+
normalized.includes("device session not found") ||
|
|
981
|
+
normalized.includes("billing access required"));
|
|
982
|
+
}
|
|
983
|
+
function describeCredentialRejection(authType, failure) {
|
|
984
|
+
const detail = failure?.trim() || "credential rejected by backend";
|
|
985
|
+
if (authType === "device-session") {
|
|
986
|
+
return `The saved device-session credential was rejected by the backend (${detail}). Re-link this machine in the browser or run \`theclawbay logout\` before rerunning setup.`;
|
|
987
|
+
}
|
|
988
|
+
return `The provided API key was rejected by the backend (${detail}). Update the key and rerun setup.`;
|
|
989
|
+
}
|
|
973
990
|
async function fetchBackendModelIds(backendUrl, apiKey) {
|
|
974
991
|
const url = `${openAiCompatibleProxyUrl(backendUrl)}/models`;
|
|
975
992
|
try {
|
|
@@ -1194,6 +1211,7 @@ async function resolveModels(backendUrl, apiKey) {
|
|
|
1194
1211
|
const available = new Set(ids ?? []);
|
|
1195
1212
|
let selected = DEFAULT_CODEX_MODEL;
|
|
1196
1213
|
let note;
|
|
1214
|
+
const authRejected = isCredentialRejectedFailure(failure);
|
|
1197
1215
|
for (const preferred of PREFERRED_MODELS) {
|
|
1198
1216
|
if (available.has(preferred)) {
|
|
1199
1217
|
selected = preferred;
|
|
@@ -1224,20 +1242,27 @@ async function resolveModels(backendUrl, apiKey) {
|
|
|
1224
1242
|
model: selected,
|
|
1225
1243
|
models: unique.map((modelId) => ({ id: modelId, name: modelDisplayName(modelId) })),
|
|
1226
1244
|
note,
|
|
1245
|
+
failure,
|
|
1246
|
+
authRejected,
|
|
1227
1247
|
};
|
|
1228
1248
|
}
|
|
1229
1249
|
async function resolveClaudeAccess(backendUrl, apiKey) {
|
|
1230
1250
|
const { ids, failure } = await fetchClaudeModelIds(backendUrl, apiKey);
|
|
1251
|
+
const authRejected = isCredentialRejectedFailure(failure);
|
|
1231
1252
|
if (!ids?.length) {
|
|
1232
1253
|
return {
|
|
1233
1254
|
enabled: false,
|
|
1234
1255
|
models: [],
|
|
1235
1256
|
note: failure ? `Claude Code auto-setup skipped (${failure}).` : undefined,
|
|
1257
|
+
failure,
|
|
1258
|
+
authRejected,
|
|
1236
1259
|
};
|
|
1237
1260
|
}
|
|
1238
1261
|
return {
|
|
1239
1262
|
enabled: true,
|
|
1240
1263
|
models: ids,
|
|
1264
|
+
failure,
|
|
1265
|
+
authRejected,
|
|
1241
1266
|
};
|
|
1242
1267
|
}
|
|
1243
1268
|
async function writeCodexConfig(params) {
|
|
@@ -1429,7 +1454,15 @@ async function persistApiKeyEnv(params) {
|
|
|
1429
1454
|
await promises_1.default.writeFile(ENV_FILE, envContents, "utf8");
|
|
1430
1455
|
await promises_1.default.chmod(ENV_FILE, 0o600);
|
|
1431
1456
|
const sourceLine = `[ -f "$HOME/.config/theclawbay/env" ] && . "$HOME/.config/theclawbay/env"`;
|
|
1432
|
-
const shellRcPaths = [
|
|
1457
|
+
const shellRcPaths = [
|
|
1458
|
+
".bashrc",
|
|
1459
|
+
".bash_profile",
|
|
1460
|
+
".bash_login",
|
|
1461
|
+
".zshrc",
|
|
1462
|
+
".zprofile",
|
|
1463
|
+
".zlogin",
|
|
1464
|
+
".profile",
|
|
1465
|
+
].map((name) => node_path_1.default.join(node_os_1.default.homedir(), name));
|
|
1433
1466
|
const updated = [];
|
|
1434
1467
|
for (const rcPath of shellRcPaths) {
|
|
1435
1468
|
let existing = "";
|
|
@@ -1457,6 +1490,19 @@ async function persistApiKeyEnv(params) {
|
|
|
1457
1490
|
}
|
|
1458
1491
|
return updated;
|
|
1459
1492
|
}
|
|
1493
|
+
async function persistFishEnv(params) {
|
|
1494
|
+
const fishConfPath = node_path_1.default.join(node_os_1.default.homedir(), ".config", "fish", "conf.d", "theclawbay.fish");
|
|
1495
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(fishConfPath), { recursive: true });
|
|
1496
|
+
const lines = [
|
|
1497
|
+
"# Generated by theclawbay setup",
|
|
1498
|
+
`set -gx ${ENV_KEY_NAME} ${shellQuote(params.apiKey)}`,
|
|
1499
|
+
];
|
|
1500
|
+
if (params.claudeEnabled) {
|
|
1501
|
+
lines.push("", "# Official Claude Code CLI", `set -gx ${CLAUDE_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`, `set -gx ${CLAUDE_ENV_BASE_URL_NAME} ${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
|
|
1502
|
+
}
|
|
1503
|
+
await promises_1.default.writeFile(fishConfPath, `${lines.join("\n")}\n`, "utf8");
|
|
1504
|
+
return [fishConfPath];
|
|
1505
|
+
}
|
|
1460
1506
|
function powerShellProfilePaths() {
|
|
1461
1507
|
if (node_os_1.default.platform() !== "win32")
|
|
1462
1508
|
return [];
|
|
@@ -2091,7 +2137,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2091
2137
|
if (flags["migrate-conversations"] !== undefined && !selectedSetupClients.has("codex")) {
|
|
2092
2138
|
throw new Error("--migrate-conversations requires Codex to be selected for setup.");
|
|
2093
2139
|
}
|
|
2094
|
-
|
|
2140
|
+
const linkFreshDeviceSession = async () => {
|
|
2095
2141
|
deviceLabel = await resolveDeviceLabel({
|
|
2096
2142
|
flagValue: flags["device-name"],
|
|
2097
2143
|
managedValue: managed?.deviceLabel,
|
|
@@ -2107,11 +2153,22 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2107
2153
|
authCredential = browserAuth.credential;
|
|
2108
2154
|
deviceSessionId = browserAuth.deviceSessionId;
|
|
2109
2155
|
deviceLabel = browserAuth.deviceLabel;
|
|
2156
|
+
};
|
|
2157
|
+
if (!authCredential) {
|
|
2158
|
+
await linkFreshDeviceSession();
|
|
2159
|
+
}
|
|
2160
|
+
else if (!explicitApiKey && managedAuthType === "device-session") {
|
|
2161
|
+
const managedProbe = await fetchBackendModelIds(backendUrl, authCredential);
|
|
2162
|
+
if (isCredentialRejectedFailure(managedProbe.failure)) {
|
|
2163
|
+
this.log(`Saved device-session credential was rejected by the backend (${managedProbe.failure}). Re-linking this machine now.`);
|
|
2164
|
+
await linkFreshDeviceSession();
|
|
2165
|
+
}
|
|
2110
2166
|
}
|
|
2111
2167
|
const progress = this.createProgressHandle(!debugOutput);
|
|
2112
2168
|
let resolved = null;
|
|
2113
2169
|
let claudeAccess = null;
|
|
2114
2170
|
let updatedShellFiles = [];
|
|
2171
|
+
let updatedFishFiles = [];
|
|
2115
2172
|
let updatedPowerShellProfiles = [];
|
|
2116
2173
|
let codexConfigPath = null;
|
|
2117
2174
|
let updatedVsCodeEnvFiles = [];
|
|
@@ -2134,9 +2191,15 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2134
2191
|
if (selectedSetupClients.size > 0) {
|
|
2135
2192
|
progress.update("Resolving supported models");
|
|
2136
2193
|
resolved = await resolveModels(backendUrl, authCredential);
|
|
2194
|
+
if (resolved.authRejected) {
|
|
2195
|
+
throw new Error(describeCredentialRejection(authType, resolved.failure));
|
|
2196
|
+
}
|
|
2137
2197
|
}
|
|
2138
2198
|
progress.update("Checking Claude access");
|
|
2139
2199
|
claudeAccess = await resolveClaudeAccess(backendUrl, authCredential);
|
|
2200
|
+
if (!resolved?.authRejected && claudeAccess.authRejected) {
|
|
2201
|
+
throw new Error(describeCredentialRejection(authType, claudeAccess.failure));
|
|
2202
|
+
}
|
|
2140
2203
|
progress.update("Saving shared machine config");
|
|
2141
2204
|
await (0, config_1.writeManagedConfig)({
|
|
2142
2205
|
backendUrl,
|
|
@@ -2150,11 +2213,19 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2150
2213
|
backendUrl,
|
|
2151
2214
|
claudeEnabled: claudeAccess.enabled,
|
|
2152
2215
|
});
|
|
2216
|
+
updatedFishFiles = await persistFishEnv({
|
|
2217
|
+
apiKey: authCredential,
|
|
2218
|
+
backendUrl,
|
|
2219
|
+
claudeEnabled: claudeAccess.enabled,
|
|
2220
|
+
});
|
|
2153
2221
|
updatedPowerShellProfiles = await persistPowerShellEnv({
|
|
2154
2222
|
apiKey: authCredential,
|
|
2155
2223
|
backendUrl,
|
|
2156
2224
|
claudeEnabled: claudeAccess.enabled,
|
|
2157
2225
|
});
|
|
2226
|
+
if (selectedSetupClients.has("codex") || selectedSetupClients.has("claude")) {
|
|
2227
|
+
updatedVsCodeEnvFiles = await persistVsCodeServerEnvSource();
|
|
2228
|
+
}
|
|
2158
2229
|
if (selectedSetupClients.has("codex")) {
|
|
2159
2230
|
progress.update("Configuring Codex");
|
|
2160
2231
|
codexConfigPath = await writeCodexConfig({
|
|
@@ -2162,7 +2233,6 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2162
2233
|
model: resolved?.model ?? DEFAULT_CODEX_MODEL,
|
|
2163
2234
|
apiKey: authCredential,
|
|
2164
2235
|
});
|
|
2165
|
-
updatedVsCodeEnvFiles = await persistVsCodeServerEnvSource();
|
|
2166
2236
|
if (migrateCodexConversations) {
|
|
2167
2237
|
sessionMigration = await (0, codex_history_migration_1.migrateSessionProviders)({
|
|
2168
2238
|
codexHome: paths_1.codexDir,
|
|
@@ -2345,6 +2415,9 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2345
2415
|
}
|
|
2346
2416
|
this.log(`- Local credential env: ${ENV_FILE}`);
|
|
2347
2417
|
this.log(`- Shell profiles updated: ${updatedShellFiles.join(", ")}`);
|
|
2418
|
+
if (updatedFishFiles.length > 0) {
|
|
2419
|
+
this.log(`- Fish profiles updated: ${updatedFishFiles.join(", ")}`);
|
|
2420
|
+
}
|
|
2348
2421
|
if (updatedPowerShellProfiles.length > 0) {
|
|
2349
2422
|
this.log(`- PowerShell profiles updated: ${updatedPowerShellProfiles.join(", ")}`);
|
|
2350
2423
|
}
|
|
@@ -2438,6 +2511,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2438
2511
|
const openCodeDetected = setupClients.find((client) => client.id === "opencode")?.detected ?? false;
|
|
2439
2512
|
if (selectedSetupClients.has("opencode")) {
|
|
2440
2513
|
this.log(`- OpenCode: configured (${openCodeConfigPaths.join(", ")})`);
|
|
2514
|
+
this.log("- OpenCode note: plain OpenAI-compatible config is preferred. If you add a quota/auth plugin manually, use /api/codex-auth/v1/quota?format=legacy_codex for legacy Codex-style quota responses.");
|
|
2441
2515
|
const openCodeProjectConfig = findOpenCodeProjectConfigFile();
|
|
2442
2516
|
const openCodeConfigEnv = process.env.OPENCODE_CONFIG?.trim() || "";
|
|
2443
2517
|
if (openCodeConfigEnv) {
|
package/package.json
CHANGED