theclawbay 0.3.58 → 0.3.60

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 CHANGED
@@ -29,3 +29,7 @@ Saves the API key without writing local client config.
29
29
  `theclawbay logout`
30
30
 
31
31
  Removes The Claw Bay-managed local config from this machine.
32
+
33
+ `theclawbay usage`
34
+
35
+ Shows your current shared 5-hour and weekly usage windows.
@@ -49,6 +49,8 @@ const CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING = "claudeCode.disableLogin
49
49
  const ANTHROPIC_PROVIDER_ID = "anthropic";
50
50
  const MANAGED_EDITOR_TERMINAL_ENV_NAMES = [
51
51
  ENV_KEY_NAME,
52
+ ];
53
+ const MANAGED_CLAUDE_ENV_NAMES = [
52
54
  CLAUDE_ENV_API_KEY_NAME,
53
55
  CLAUDE_ENV_BASE_URL_NAME,
54
56
  CLAUDE_ENV_SIMPLE_MODE_NAME,
@@ -736,6 +738,18 @@ async function readFileIfExists(filePath) {
736
738
  throw error;
737
739
  }
738
740
  }
741
+ async function removeFileIfExists(filePath) {
742
+ try {
743
+ await promises_1.default.unlink(filePath);
744
+ return true;
745
+ }
746
+ catch (error) {
747
+ const err = error;
748
+ if (err.code === "ENOENT")
749
+ return false;
750
+ throw error;
751
+ }
752
+ }
739
753
  async function readJsonObjectFile(filePath) {
740
754
  const existingRaw = await readFileIfExists(filePath);
741
755
  if (existingRaw === null || !existingRaw.trim())
@@ -1948,14 +1962,9 @@ function terminalIntegratedEnvSettingsKey() {
1948
1962
  return "terminal.integrated.env.linux";
1949
1963
  }
1950
1964
  function buildManagedEditorTerminalEnv(params) {
1951
- const env = {
1965
+ return {
1952
1966
  [ENV_KEY_NAME]: params.apiKey,
1953
1967
  };
1954
- if (params.claudeEnabled) {
1955
- env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
1956
- env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
1957
- }
1958
- return env;
1959
1968
  }
1960
1969
  function buildManagedClaudeEditorEnv(params) {
1961
1970
  return {
@@ -2069,6 +2078,12 @@ async function persistClaudeEditorSettings(params) {
2069
2078
  nextEnv[name] = value;
2070
2079
  changed = true;
2071
2080
  }
2081
+ for (const name of MANAGED_CLAUDE_ENV_NAMES) {
2082
+ if (name in desiredEnv || !(name in nextEnv))
2083
+ continue;
2084
+ delete nextEnv[name];
2085
+ changed = true;
2086
+ }
2072
2087
  settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = nextEnv;
2073
2088
  if (settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] !== true) {
2074
2089
  settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = true;
@@ -2093,6 +2108,126 @@ async function persistClaudeEditorSettings(params) {
2093
2108
  await writeJsonObjectFile(CLAUDE_EDITOR_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
2094
2109
  return managedPaths;
2095
2110
  }
2111
+ async function cleanupClaudeEditorSettings() {
2112
+ const snapshotRaw = await readFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
2113
+ if (snapshotRaw === null || !snapshotRaw.trim())
2114
+ return [];
2115
+ let entries = [];
2116
+ try {
2117
+ entries = JSON.parse(snapshotRaw);
2118
+ }
2119
+ catch {
2120
+ return [];
2121
+ }
2122
+ const updated = [];
2123
+ for (const entry of entries) {
2124
+ const settings = await readJsonObjectFile(entry.settingsPath);
2125
+ if (!entry.envHadKey) {
2126
+ delete settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2127
+ }
2128
+ else {
2129
+ settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = entry.envPreviousValue;
2130
+ }
2131
+ if (!entry.disableLoginPromptHadKey) {
2132
+ delete settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING];
2133
+ }
2134
+ else {
2135
+ settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = entry.disableLoginPromptPreviousValue;
2136
+ }
2137
+ if (entry.existed || Object.keys(settings).length > 0) {
2138
+ await writeJsonObjectFile(entry.settingsPath, settings);
2139
+ updated.push(entry.settingsPath);
2140
+ continue;
2141
+ }
2142
+ if (await removeFileIfExists(entry.settingsPath)) {
2143
+ updated.push(entry.settingsPath);
2144
+ }
2145
+ }
2146
+ await removeFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
2147
+ return updated;
2148
+ }
2149
+ async function cleanupClaudeDesktop3pConfig() {
2150
+ const snapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2151
+ if (snapshotRaw === null || !snapshotRaw.trim())
2152
+ return false;
2153
+ let snapshot = null;
2154
+ try {
2155
+ snapshot = JSON.parse(snapshotRaw);
2156
+ }
2157
+ catch {
2158
+ snapshot = null;
2159
+ }
2160
+ if (!snapshot?.configPath) {
2161
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2162
+ return false;
2163
+ }
2164
+ if (!snapshot.existed) {
2165
+ const removed = await removeFileIfExists(snapshot.configPath);
2166
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2167
+ return removed;
2168
+ }
2169
+ const previousRaw = snapshot.previousRaw ?? "";
2170
+ const existingRaw = await readFileIfExists(snapshot.configPath);
2171
+ if (existingRaw === previousRaw) {
2172
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2173
+ return false;
2174
+ }
2175
+ await promises_1.default.mkdir(node_path_1.default.dirname(snapshot.configPath), { recursive: true });
2176
+ await promises_1.default.writeFile(snapshot.configPath, previousRaw, "utf8");
2177
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2178
+ return true;
2179
+ }
2180
+ async function cleanupLegacyClaudeEditorTerminalEnvSettings(params) {
2181
+ const snapshotRaw = await readFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
2182
+ if (snapshotRaw === null || !snapshotRaw.trim())
2183
+ return [];
2184
+ let entries = [];
2185
+ try {
2186
+ entries = JSON.parse(snapshotRaw);
2187
+ }
2188
+ catch {
2189
+ return [];
2190
+ }
2191
+ const settingsKey = terminalIntegratedEnvSettingsKey();
2192
+ const managedClaudeEnv = {
2193
+ [CLAUDE_ENV_API_KEY_NAME]: params.apiKey,
2194
+ [CLAUDE_ENV_BASE_URL_NAME]: anthropicCompatibleProxyUrl(params.backendUrl),
2195
+ };
2196
+ const updated = [];
2197
+ for (const entry of entries) {
2198
+ const settings = await readJsonObjectFile(entry.settingsPath);
2199
+ const currentValue = settings[settingsKey];
2200
+ if (typeof currentValue !== "object" || currentValue === null || Array.isArray(currentValue))
2201
+ continue;
2202
+ const currentEnv = { ...currentValue };
2203
+ const previousEnv = typeof entry.previousValue === "object" &&
2204
+ entry.previousValue !== null &&
2205
+ !Array.isArray(entry.previousValue)
2206
+ ? { ...entry.previousValue }
2207
+ : {};
2208
+ let changed = false;
2209
+ for (const name of MANAGED_CLAUDE_ENV_NAMES) {
2210
+ const expectedManagedValue = managedClaudeEnv[name];
2211
+ const currentManagedValue = currentEnv[name];
2212
+ const currentMatchesManaged = typeof expectedManagedValue === "string" ? currentManagedValue === expectedManagedValue : false;
2213
+ if (!currentMatchesManaged)
2214
+ continue;
2215
+ if (Object.prototype.hasOwnProperty.call(previousEnv, name)) {
2216
+ currentEnv[name] = previousEnv[name];
2217
+ }
2218
+ else {
2219
+ delete currentEnv[name];
2220
+ }
2221
+ changed = true;
2222
+ }
2223
+ if (!changed)
2224
+ continue;
2225
+ settings[settingsKey] = currentEnv;
2226
+ await writeJsonObjectFile(entry.settingsPath, settings);
2227
+ updated.push(entry.settingsPath);
2228
+ }
2229
+ return updated;
2230
+ }
2096
2231
  function expandClaudeDesktopInferenceModels(modelIds) {
2097
2232
  const expanded = [];
2098
2233
  const add = (value) => {
@@ -2846,6 +2981,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2846
2981
  let updatedVsCodeEnvFiles = [];
2847
2982
  let updatedEditorTerminalSettings = [];
2848
2983
  let updatedClaudeEditorSettings = [];
2984
+ let restoredClaudeDesktop3pConfig = false;
2849
2985
  let claudeDesktop3pConfigPathManaged = null;
2850
2986
  let sessionMigration = null;
2851
2987
  let authSeed = null;
@@ -2908,18 +3044,34 @@ class SetupCommand extends base_command_1.BaseCommand {
2908
3044
  backendUrl,
2909
3045
  claudeEnabled: claudeEnvEnabled,
2910
3046
  });
2911
- if (selectedSetupClients.has("claude")) {
2912
- updatedClaudeEditorSettings = await persistClaudeEditorSettings({
2913
- apiKey: authCredential,
2914
- backendUrl,
2915
- claudeEnabled: claudeEnvEnabled,
2916
- });
3047
+ }
3048
+ if (selectedSetupClients.has("claude")) {
3049
+ updatedClaudeEditorSettings = await persistClaudeEditorSettings({
3050
+ apiKey: authCredential,
3051
+ backendUrl,
3052
+ claudeEnabled: claudeEnvEnabled,
3053
+ });
3054
+ if (claudeEnvEnabled) {
2917
3055
  claudeDesktop3pConfigPathManaged = await writeClaudeDesktop3pConfig({
2918
3056
  backendUrl,
2919
3057
  apiKey: authCredential,
2920
3058
  claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2921
3059
  });
2922
3060
  }
3061
+ else {
3062
+ restoredClaudeDesktop3pConfig = await cleanupClaudeDesktop3pConfig();
3063
+ }
3064
+ }
3065
+ else {
3066
+ updatedEditorTerminalSettings = [
3067
+ ...updatedEditorTerminalSettings,
3068
+ ...await cleanupLegacyClaudeEditorTerminalEnvSettings({
3069
+ apiKey: authCredential,
3070
+ backendUrl,
3071
+ }),
3072
+ ];
3073
+ updatedClaudeEditorSettings = await cleanupClaudeEditorSettings();
3074
+ restoredClaudeDesktop3pConfig = await cleanupClaudeDesktop3pConfig();
2923
3075
  }
2924
3076
  if (selectedSetupClients.has("codex")) {
2925
3077
  progress.update("Configuring Codex");
@@ -3137,6 +3289,9 @@ class SetupCommand extends base_command_1.BaseCommand {
3137
3289
  if (updatedClaudeEditorSettings.length > 0) {
3138
3290
  this.log(`- Claude editor settings updated: ${updatedClaudeEditorSettings.join(", ")}`);
3139
3291
  }
3292
+ if (restoredClaudeDesktop3pConfig) {
3293
+ this.log("- Claude Desktop 3P config: restored the pre-The Claw Bay config.");
3294
+ }
3140
3295
  if (selectedSetupClients.has("codex")) {
3141
3296
  this.log(`- Codex: configured (${codexConfigPath})`);
3142
3297
  if (resolved)
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from "../lib/base-command";
2
+ export default class UsageCommand extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ backend: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
+ "api-key": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../lib/base-command");
5
+ const api_key_1 = require("../lib/managed/api-key");
6
+ const config_1 = require("../lib/managed/config");
7
+ const errors_1 = require("../lib/managed/errors");
8
+ const DEFAULT_BACKEND_URL = "https://theclawbay.com";
9
+ function formatPercent(value) {
10
+ if (typeof value !== "number" || !Number.isFinite(value))
11
+ return "n/a";
12
+ return `${value.toFixed(value >= 10 ? 1 : 2).replace(/\.0+$/u, "").replace(/(\.\d*?)0+$/u, "$1")}%`;
13
+ }
14
+ function formatUsd(value) {
15
+ if (typeof value !== "number" || !Number.isFinite(value))
16
+ return "n/a";
17
+ return new Intl.NumberFormat("en-US", {
18
+ style: "currency",
19
+ currency: "USD",
20
+ minimumFractionDigits: 2,
21
+ maximumFractionDigits: 2,
22
+ }).format(value);
23
+ }
24
+ function formatTimestamp(value) {
25
+ if (!value)
26
+ return "n/a";
27
+ const parsed = Date.parse(value);
28
+ if (!Number.isFinite(parsed))
29
+ return value;
30
+ return new Date(parsed).toISOString();
31
+ }
32
+ function formatDuration(seconds) {
33
+ const totalSeconds = Math.max(0, Math.round(seconds));
34
+ const days = Math.floor(totalSeconds / 86400);
35
+ const hours = Math.floor((totalSeconds % 86400) / 3600);
36
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
37
+ const remainingSeconds = totalSeconds % 60;
38
+ const parts = [];
39
+ if (days > 0)
40
+ parts.push(`${days}d`);
41
+ if (hours > 0)
42
+ parts.push(`${hours}h`);
43
+ if (minutes > 0)
44
+ parts.push(`${minutes}m`);
45
+ if (parts.length === 0 || (parts.length < 2 && remainingSeconds > 0)) {
46
+ parts.push(`${remainingSeconds}s`);
47
+ }
48
+ return parts.slice(0, 2).join(" ");
49
+ }
50
+ function printWindow(log, label, window, showCosts) {
51
+ log(`${label}`);
52
+ log(` Used: ${formatPercent(window.progressPercentUsed ?? window.percentUsed)}`);
53
+ log(` Remaining: ${formatPercent(window.percentRemaining)}`);
54
+ log(` Requests: ${window.requestCount} total, ${window.successCount} succeeded, ${window.failedCount} failed`);
55
+ log(` Runtime: ${window.runtimeSecondsUsed.toFixed(1)}s`);
56
+ log(` Resets: ${formatDuration(window.secondsUntilReset)} (${formatTimestamp(window.windowEnd)})`);
57
+ if (showCosts) {
58
+ log(` Cost: ${formatUsd(window.estimatedCostUsdUsed)} used / ${formatUsd(window.costUsdLimit)} limit / ${formatUsd(window.costUsdRemaining)} remaining`);
59
+ }
60
+ log(` Last request: ${formatTimestamp(window.lastRequestAt)}`);
61
+ if (window.limitReached) {
62
+ log(" Status: limit reached");
63
+ }
64
+ }
65
+ class UsageCommand extends base_command_1.BaseCommand {
66
+ async run() {
67
+ await this.runSafe(async () => {
68
+ const { flags } = await this.parse(UsageCommand);
69
+ let managed = null;
70
+ try {
71
+ managed = await (0, config_1.readManagedConfig)();
72
+ }
73
+ catch (error) {
74
+ if (!(error instanceof errors_1.ManagedConfigMissingError))
75
+ throw error;
76
+ }
77
+ const credential = (flags["api-key"] ?? managed?.credential ?? "").trim();
78
+ if (!credential) {
79
+ this.error('No saved credential found. Run "theclawbay setup" or pass --api-key.');
80
+ }
81
+ const backendRaw = flags.backend ??
82
+ (0, api_key_1.tryInferBackendUrlFromApiKey)(credential) ??
83
+ managed?.backendUrl ??
84
+ DEFAULT_BACKEND_URL;
85
+ const backendUrl = backendRaw.replace(/\/+$/u, "");
86
+ const response = await fetch(`${backendUrl}/api/codex-auth/v1/quota`, {
87
+ headers: {
88
+ Authorization: `Bearer ${credential}`,
89
+ Accept: "application/json",
90
+ },
91
+ signal: AbortSignal.timeout(20000),
92
+ });
93
+ const body = (await response.json().catch(() => ({})));
94
+ if (!response.ok) {
95
+ this.error(body.error || `Failed to load usage (HTTP ${response.status})`);
96
+ }
97
+ if (flags.json) {
98
+ this.log(JSON.stringify(body, null, 2));
99
+ return;
100
+ }
101
+ const showCosts = body.usageLimitPresentation !== "percent_only";
102
+ this.log("The Claw Bay usage");
103
+ this.log(`Observed: ${formatTimestamp(body.observedAt)}`);
104
+ this.log(`Auth scope: ${body.pooled ? "shared pooled usage" : body.mode}`);
105
+ if (body.mode === "direct" && typeof body.planMultiplier === "number" && Number.isFinite(body.planMultiplier)) {
106
+ this.log(`Plan multiplier: ${body.planMultiplier}x`);
107
+ }
108
+ if (body.anyLimitReached) {
109
+ this.log("Status: at least one usage window has been reached");
110
+ }
111
+ this.log("");
112
+ printWindow((message) => this.log(message), "5-hour window", body.usage.fiveHour, showCosts);
113
+ this.log("");
114
+ printWindow((message) => this.log(message), "Weekly window", body.usage.weekly, showCosts);
115
+ });
116
+ }
117
+ }
118
+ UsageCommand.description = "Show your current The Claw Bay pooled 5-hour and weekly usage";
119
+ UsageCommand.flags = {
120
+ backend: core_1.Flags.string({
121
+ required: false,
122
+ description: "Backend base URL override (default: saved managed config or https://theclawbay.com)",
123
+ }),
124
+ "api-key": core_1.Flags.string({
125
+ required: false,
126
+ aliases: ["apiKey"],
127
+ description: "API key or linked device-session token override",
128
+ }),
129
+ json: core_1.Flags.boolean({
130
+ required: false,
131
+ default: false,
132
+ description: "Print the raw quota response as JSON",
133
+ }),
134
+ };
135
+ exports.default = UsageCommand;
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "theclawbay",
3
- "version": "0.3.58",
3
+ "version": "0.3.60",
4
4
  "description": "CLI for connecting Codex, Continue, Cline, GSD, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/RCRTCBHAL900/TheClawBay"
9
+ },
6
10
  "bin": {
7
11
  "theclawbay": "dist/index.js"
8
12
  },
@@ -17,6 +17,15 @@
17
17
  "cachedInputPer1M": 0.125,
18
18
  "outputPer1M": 10.0
19
19
  },
20
+ {
21
+ "id": "gpt-image-1.5",
22
+ "label": "GPT Image 1.5",
23
+ "note": "Native image generation model for direct image outputs.",
24
+ "tone": "coral",
25
+ "inputPer1M": 8.0,
26
+ "cachedInputPer1M": 2.0,
27
+ "outputPer1M": 32.0
28
+ },
20
29
  {
21
30
  "id": "gpt-5.3-codex",
22
31
  "label": "GPT-5.3 Codex",