theclawbay 0.3.44 → 0.3.45
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/logout.js +1 -16
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +39 -60
- package/dist/lib/device-session-auth.d.ts +0 -1
- package/dist/lib/device-session-auth.js +108 -26
- package/package.json +7 -2
- package/dist/lib/codex-vscode-patch.d.ts +0 -14
- package/dist/lib/codex-vscode-patch.js +0 -510
package/dist/commands/logout.js
CHANGED
|
@@ -15,7 +15,6 @@ const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migratio
|
|
|
15
15
|
const config_1 = require("../lib/managed/config");
|
|
16
16
|
const errors_1 = require("../lib/managed/errors");
|
|
17
17
|
const supported_models_1 = require("../lib/supported-models");
|
|
18
|
-
const codex_vscode_patch_1 = require("../lib/codex-vscode-patch");
|
|
19
18
|
const paths_1 = require("../lib/config/paths");
|
|
20
19
|
const OPENAI_PROVIDER_ID = "openai";
|
|
21
20
|
const DEFAULT_PROVIDER_ID = "theclawbay";
|
|
@@ -738,7 +737,6 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
738
737
|
let stateDbMigration;
|
|
739
738
|
let authSeedCleanup;
|
|
740
739
|
let modelCacheCleanup;
|
|
741
|
-
let codexVsCodePatchCleanup;
|
|
742
740
|
let remoteRevokeWarning = null;
|
|
743
741
|
try {
|
|
744
742
|
if (managed?.authType === "device-session" && managed.credential.trim()) {
|
|
@@ -788,7 +786,6 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
788
786
|
authSeedCleanup = await (0, codex_auth_seeding_1.cleanupSeededCodexAuth)({
|
|
789
787
|
codexHome: paths_1.codexDir,
|
|
790
788
|
});
|
|
791
|
-
codexVsCodePatchCleanup = await (0, codex_vscode_patch_1.cleanupCodexVsCodeExtensions)();
|
|
792
789
|
modelCacheCleanup = await (0, codex_model_cache_migration_1.cleanupSeededCodexModelCache)({
|
|
793
790
|
codexHome: paths_1.codexDir,
|
|
794
791
|
});
|
|
@@ -828,7 +825,6 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
828
825
|
stateDbMigration.updated > 0 ||
|
|
829
826
|
authSeedCleanup.action === "removed" ||
|
|
830
827
|
authSeedCleanup.action === "restored_backup" ||
|
|
831
|
-
codexVsCodePatchCleanup.action === "removed" ||
|
|
832
828
|
modelCacheCleanup.action === "removed";
|
|
833
829
|
if (codexChanged)
|
|
834
830
|
revertedTargets.push("Codex");
|
|
@@ -856,8 +852,6 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
856
852
|
}
|
|
857
853
|
if (authSeedCleanup.warning)
|
|
858
854
|
summaryNotes.add(authSeedCleanup.warning);
|
|
859
|
-
if (codexVsCodePatchCleanup.warning)
|
|
860
|
-
summaryNotes.add(codexVsCodePatchCleanup.warning);
|
|
861
855
|
if (modelCacheCleanup.warning)
|
|
862
856
|
summaryNotes.add(modelCacheCleanup.warning);
|
|
863
857
|
if (remoteRevokeWarning) {
|
|
@@ -903,7 +897,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
903
897
|
this.log(`- Conversation cache: could not update local Codex history DB.${detail}`);
|
|
904
898
|
}
|
|
905
899
|
if (authSeedCleanup.action === "restored_backup") {
|
|
906
|
-
this.log("- Codex login state: restored the auth that existed before
|
|
900
|
+
this.log("- Codex login state: restored the auth that existed before an older The Claw Bay Codex auth override.");
|
|
907
901
|
}
|
|
908
902
|
else if (authSeedCleanup.action === "removed") {
|
|
909
903
|
this.log("- Codex login state: removed the Claw Bay auth seed.");
|
|
@@ -919,15 +913,6 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
919
913
|
else {
|
|
920
914
|
this.log("- Codex login state: no cleanup needed.");
|
|
921
915
|
}
|
|
922
|
-
if (codexVsCodePatchCleanup.action === "removed") {
|
|
923
|
-
this.log(`- Codex VS Code extension: removed the The Claw Bay patch from ${codexVsCodePatchCleanup.restoredRoots.join(", ")}.`);
|
|
924
|
-
}
|
|
925
|
-
else if (codexVsCodePatchCleanup.warning) {
|
|
926
|
-
this.log(`- Codex VS Code extension: ${codexVsCodePatchCleanup.warning}`);
|
|
927
|
-
}
|
|
928
|
-
else {
|
|
929
|
-
this.log("- Codex VS Code extension: no cleanup needed.");
|
|
930
|
-
}
|
|
931
916
|
if (modelCacheCleanup.action === "removed") {
|
|
932
917
|
this.log("- Codex model cache: removed the Claw Bay GPT-5.4 / GPT-5.4 Mini seed.");
|
|
933
918
|
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export default class SetupCommand extends BaseCommand {
|
|
|
7
7
|
"device-name": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
8
|
clients: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
"
|
|
10
|
+
"migrate-conversations": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
"debug-output": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
12
|
};
|
|
13
13
|
run(): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -17,7 +17,6 @@ const codex_auth_seeding_1 = require("../lib/codex-auth-seeding");
|
|
|
17
17
|
const codex_history_migration_1 = require("../lib/codex-history-migration");
|
|
18
18
|
const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migration");
|
|
19
19
|
const paths_1 = require("../lib/config/paths");
|
|
20
|
-
const codex_vscode_patch_1 = require("../lib/codex-vscode-patch");
|
|
21
20
|
const api_key_1 = require("../lib/managed/api-key");
|
|
22
21
|
const config_1 = require("../lib/managed/config");
|
|
23
22
|
const errors_1 = require("../lib/managed/errors");
|
|
@@ -849,7 +848,7 @@ function resolveSetupClientSelection(params) {
|
|
|
849
848
|
return Promise.resolve(defaults);
|
|
850
849
|
return promptForSetupClients(setupClients);
|
|
851
850
|
}
|
|
852
|
-
async function
|
|
851
|
+
async function promptForCodexConversationMigration() {
|
|
853
852
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
854
853
|
return false;
|
|
855
854
|
const stdin = process.stdin;
|
|
@@ -879,20 +878,20 @@ async function promptForCodexUsageUiPatch() {
|
|
|
879
878
|
finish(false);
|
|
880
879
|
}
|
|
881
880
|
};
|
|
882
|
-
process.stdout.write("\
|
|
881
|
+
process.stdout.write("\nConvert old Codex conversations so they stay visible under The Claw Bay? [y/N] ");
|
|
883
882
|
if (stdin.isTTY)
|
|
884
883
|
stdin.setRawMode(true);
|
|
885
884
|
stdin.on("data", onData);
|
|
886
885
|
});
|
|
887
886
|
}
|
|
888
|
-
async function
|
|
887
|
+
async function resolveCodexConversationMigrationSelection(params) {
|
|
889
888
|
if (!params.codexSelected)
|
|
890
889
|
return false;
|
|
891
890
|
if (params.flagValue !== undefined)
|
|
892
891
|
return params.flagValue;
|
|
893
892
|
if (params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY)
|
|
894
893
|
return false;
|
|
895
|
-
return
|
|
894
|
+
return promptForCodexConversationMigration();
|
|
896
895
|
}
|
|
897
896
|
async function resolveDeviceLabel(params) {
|
|
898
897
|
const fallback = node_os_1.default.hostname().trim() || "This device";
|
|
@@ -1097,7 +1096,7 @@ async function writeCodexConfig(params) {
|
|
|
1097
1096
|
next = upsertFirstKeyLine(next, "model", `"${params.model}"`);
|
|
1098
1097
|
const managedBlock = appendManagedBlock("", [
|
|
1099
1098
|
MANAGED_START,
|
|
1100
|
-
|
|
1099
|
+
`chatgpt_base_url = "${proxyRoot}"`,
|
|
1101
1100
|
`[model_providers.${DEFAULT_PROVIDER_ID}]`,
|
|
1102
1101
|
'name = "OpenAI"',
|
|
1103
1102
|
`base_url = "${proxyRoot}/backend-api/codex"`,
|
|
@@ -1869,13 +1868,13 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1869
1868
|
flagSelection: parseSetupClientFlags(flags.clients),
|
|
1870
1869
|
skipPrompt: flags.yes,
|
|
1871
1870
|
});
|
|
1872
|
-
const
|
|
1871
|
+
const migrateCodexConversations = await resolveCodexConversationMigrationSelection({
|
|
1873
1872
|
codexSelected: selectedSetupClients.has("codex"),
|
|
1874
|
-
flagValue: flags["
|
|
1873
|
+
flagValue: flags["migrate-conversations"],
|
|
1875
1874
|
skipPrompt: flags.yes,
|
|
1876
1875
|
});
|
|
1877
|
-
if (flags["
|
|
1878
|
-
throw new Error("--
|
|
1876
|
+
if (flags["migrate-conversations"] !== undefined && !selectedSetupClients.has("codex")) {
|
|
1877
|
+
throw new Error("--migrate-conversations requires Codex to be selected for setup.");
|
|
1879
1878
|
}
|
|
1880
1879
|
if (!authCredential) {
|
|
1881
1880
|
deviceLabel = await resolveDeviceLabel({
|
|
@@ -1887,7 +1886,6 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1887
1886
|
backendUrl,
|
|
1888
1887
|
deviceLabel,
|
|
1889
1888
|
selectedClients: Array.from(selectedSetupClients),
|
|
1890
|
-
usagePatchRequested: codexUsageUiEnabled,
|
|
1891
1889
|
log: (message) => this.log(message),
|
|
1892
1890
|
});
|
|
1893
1891
|
authType = browserAuth.authType;
|
|
@@ -1905,7 +1903,6 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1905
1903
|
let authSeedCleanup = null;
|
|
1906
1904
|
let stateDbMigration = null;
|
|
1907
1905
|
let modelCacheMigration = null;
|
|
1908
|
-
let codexVsCodeCleanup = null;
|
|
1909
1906
|
let continueConfigPath = null;
|
|
1910
1907
|
let clineConfigPaths = [];
|
|
1911
1908
|
let openClawConfigPath = null;
|
|
@@ -1936,36 +1933,29 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1936
1933
|
backendUrl,
|
|
1937
1934
|
model: resolved?.model ?? DEFAULT_CODEX_MODEL,
|
|
1938
1935
|
apiKey: authCredential,
|
|
1939
|
-
enableUsageUiPatch: codexUsageUiEnabled,
|
|
1940
1936
|
});
|
|
1941
1937
|
updatedVsCodeEnvFiles = await persistVsCodeServerEnvSource();
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
migrationStateFile: MIGRATION_STATE_FILE,
|
|
1945
|
-
neutralizeSources: HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
|
|
1946
|
-
});
|
|
1947
|
-
if (codexUsageUiEnabled) {
|
|
1948
|
-
authSeed = await (0, codex_auth_seeding_1.ensureCodexUsageLimitAuth)({
|
|
1938
|
+
if (migrateCodexConversations) {
|
|
1939
|
+
sessionMigration = await (0, codex_history_migration_1.migrateSessionProviders)({
|
|
1949
1940
|
codexHome: paths_1.codexDir,
|
|
1950
|
-
|
|
1941
|
+
migrationStateFile: MIGRATION_STATE_FILE,
|
|
1942
|
+
neutralizeSources: HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
|
|
1951
1943
|
});
|
|
1952
|
-
codexVsCodeCleanup = await (0, codex_vscode_patch_1.cleanupCodexVsCodeExtensions)();
|
|
1953
1944
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1945
|
+
authSeedCleanup = await (0, codex_auth_seeding_1.cleanupSeededCodexAuth)({
|
|
1946
|
+
codexHome: paths_1.codexDir,
|
|
1947
|
+
});
|
|
1948
|
+
authSeed = await (0, codex_auth_seeding_1.ensureCodexUsageLimitAuth)({
|
|
1949
|
+
codexHome: paths_1.codexDir,
|
|
1950
|
+
apiKey: authCredential,
|
|
1951
|
+
});
|
|
1952
|
+
if (migrateCodexConversations) {
|
|
1953
|
+
stateDbMigration = await (0, codex_history_migration_1.migrateStateDbProviders)({
|
|
1959
1954
|
codexHome: paths_1.codexDir,
|
|
1960
|
-
|
|
1955
|
+
targetProvider: DEFAULT_PROVIDER_ID,
|
|
1956
|
+
sourceProviders: HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
|
|
1961
1957
|
});
|
|
1962
|
-
codexVsCodeCleanup = await (0, codex_vscode_patch_1.cleanupCodexVsCodeExtensions)();
|
|
1963
1958
|
}
|
|
1964
|
-
stateDbMigration = await (0, codex_history_migration_1.migrateStateDbProviders)({
|
|
1965
|
-
codexHome: paths_1.codexDir,
|
|
1966
|
-
targetProvider: DEFAULT_PROVIDER_ID,
|
|
1967
|
-
sourceProviders: HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
|
|
1968
|
-
});
|
|
1969
1959
|
modelCacheMigration = await (0, codex_model_cache_migration_1.ensureCodexModelCacheHasGpt54)({
|
|
1970
1960
|
codexHome: paths_1.codexDir,
|
|
1971
1961
|
});
|
|
@@ -2081,13 +2071,11 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2081
2071
|
summaryNotes.add(authSeed.warning);
|
|
2082
2072
|
if (authSeedCleanup?.warning)
|
|
2083
2073
|
summaryNotes.add(authSeedCleanup.warning);
|
|
2084
|
-
if (codexVsCodeCleanup?.warning)
|
|
2085
|
-
summaryNotes.add(codexVsCodeCleanup.warning);
|
|
2086
2074
|
if (authSeed?.replacedExistingAuth) {
|
|
2087
|
-
summaryNotes.add("Codex usage
|
|
2075
|
+
summaryNotes.add("Codex usage visibility temporarily replaced your existing local Codex auth. `theclawbay logout` restores it.");
|
|
2088
2076
|
}
|
|
2089
|
-
if (
|
|
2090
|
-
summaryNotes.add("
|
|
2077
|
+
if (selectedSetupClients.has("codex") && !migrateCodexConversations) {
|
|
2078
|
+
summaryNotes.add("Left existing Codex conversations untouched.");
|
|
2091
2079
|
}
|
|
2092
2080
|
if (authType === "device-session" && deviceLabel) {
|
|
2093
2081
|
summaryNotes.add(`This machine is linked as "${deviceLabel}". You can revoke it from the dashboard Devices section or with \`theclawbay logout\`.`);
|
|
@@ -2129,6 +2117,9 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2129
2117
|
else if (sessionMigration) {
|
|
2130
2118
|
this.log("- Conversations: no local sessions required migration.");
|
|
2131
2119
|
}
|
|
2120
|
+
if (!migrateCodexConversations) {
|
|
2121
|
+
this.log("- Conversations: left existing local Codex history untouched.");
|
|
2122
|
+
}
|
|
2132
2123
|
if ((sessionMigration?.retimed ?? 0) > 0) {
|
|
2133
2124
|
this.log(`- Conversation ordering: repaired timestamps on ${sessionMigration?.retimed ?? 0} local sessions.`);
|
|
2134
2125
|
}
|
|
@@ -2160,18 +2151,6 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2160
2151
|
else if (modelCacheMigration) {
|
|
2161
2152
|
this.log("- Codex model cache: no change.");
|
|
2162
2153
|
}
|
|
2163
|
-
if (codexUsageUiEnabled) {
|
|
2164
|
-
this.log("- Codex usage UI: patched to show The Claw Bay usage remaining.");
|
|
2165
|
-
}
|
|
2166
|
-
if (codexVsCodeCleanup?.action === "removed") {
|
|
2167
|
-
this.log(`- Codex VS Code extension: removed the prior The Claw Bay patch (${codexVsCodeCleanup.restoredRoots.join(", ")})`);
|
|
2168
|
-
}
|
|
2169
|
-
else if (codexVsCodeCleanup?.warning) {
|
|
2170
|
-
this.log(`- Codex VS Code extension: ${codexVsCodeCleanup.warning}`);
|
|
2171
|
-
}
|
|
2172
|
-
else {
|
|
2173
|
-
this.log("- Codex VS Code extension: no cleanup needed.");
|
|
2174
|
-
}
|
|
2175
2154
|
}
|
|
2176
2155
|
else if (codexDetected) {
|
|
2177
2156
|
this.log("- Codex: detected but skipped");
|
|
@@ -2281,22 +2260,22 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2281
2260
|
this.log("- Zo: not detected (skipped)");
|
|
2282
2261
|
}
|
|
2283
2262
|
if (authSeed?.action === "seeded" && authSeed.mode === "chatgpt-auth-tokens") {
|
|
2284
|
-
this.log("- Codex login state: seeded
|
|
2263
|
+
this.log("- Codex login state: seeded local Codex auth so remaining usage can be shown.");
|
|
2285
2264
|
}
|
|
2286
2265
|
else if (authSeed?.action === "updated" && authSeed.mode === "chatgpt-auth-tokens") {
|
|
2287
|
-
this.log("- Codex login state: refreshed the
|
|
2266
|
+
this.log("- Codex login state: refreshed the local usage-limit auth seed.");
|
|
2288
2267
|
}
|
|
2289
2268
|
else if (authSeed?.action === "already_seeded" && authSeed.mode === "chatgpt-auth-tokens") {
|
|
2290
|
-
this.log("- Codex login state: the
|
|
2269
|
+
this.log("- Codex login state: the local usage-limit auth seed was already present.");
|
|
2291
2270
|
}
|
|
2292
2271
|
else if (authSeed?.action === "seeded") {
|
|
2293
|
-
this.log("- Codex login state: seeded local API-key auth
|
|
2272
|
+
this.log("- Codex login state: seeded local API-key auth.");
|
|
2294
2273
|
}
|
|
2295
2274
|
else if (authSeed?.action === "updated") {
|
|
2296
|
-
this.log("- Codex login state: refreshed the
|
|
2275
|
+
this.log("- Codex login state: refreshed the local API-key auth seed.");
|
|
2297
2276
|
}
|
|
2298
2277
|
else if (authSeed?.action === "already_seeded") {
|
|
2299
|
-
this.log("- Codex login state: the
|
|
2278
|
+
this.log("- Codex login state: the local API-key auth seed was already present.");
|
|
2300
2279
|
}
|
|
2301
2280
|
else if (authSeed?.action === "already_present") {
|
|
2302
2281
|
this.log("- Codex login state: preserved existing local Codex auth.");
|
|
@@ -2308,7 +2287,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2308
2287
|
this.log("- Codex login state: no change.");
|
|
2309
2288
|
}
|
|
2310
2289
|
if (authSeedCleanup?.action === "restored_backup") {
|
|
2311
|
-
this.log("- Codex login state: restored the auth that existed before
|
|
2290
|
+
this.log("- Codex login state: restored the auth that existed before an older The Claw Bay Codex auth override.");
|
|
2312
2291
|
}
|
|
2313
2292
|
this.log("Next: restart Continue, Cline, Roo Code, Trae, or Zo only if they were already open during setup.");
|
|
2314
2293
|
});
|
|
@@ -2340,10 +2319,10 @@ SetupCommand.flags = {
|
|
|
2340
2319
|
default: false,
|
|
2341
2320
|
description: "Accept the recommended detected setup targets without prompting",
|
|
2342
2321
|
}),
|
|
2343
|
-
"
|
|
2322
|
+
"migrate-conversations": core_1.Flags.boolean({
|
|
2344
2323
|
required: false,
|
|
2345
2324
|
allowNo: true,
|
|
2346
|
-
description: "
|
|
2325
|
+
description: "Convert old Codex conversations so they stay visible under The Claw Bay.",
|
|
2347
2326
|
}),
|
|
2348
2327
|
"debug-output": core_1.Flags.boolean({
|
|
2349
2328
|
required: false,
|
|
@@ -8,6 +8,8 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
8
8
|
const node_http_1 = __importDefault(require("node:http"));
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
10
10
|
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
const BROWSER_SETUP_TIMEOUT_MS = 15 * 60000;
|
|
12
|
+
const BROWSER_SETUP_POLL_INTERVAL_MS = 2000;
|
|
11
13
|
function preferredBrowserCallbackPort() {
|
|
12
14
|
const parsed = Number(process.env.THECLAWBAY_CALLBACK_PORT?.trim() || "");
|
|
13
15
|
if (Number.isFinite(parsed) && parsed > 0 && parsed < 65536) {
|
|
@@ -124,6 +126,66 @@ async function createLocalCallbackServer(expectedState) {
|
|
|
124
126
|
}),
|
|
125
127
|
};
|
|
126
128
|
}
|
|
129
|
+
async function exchangeBrowserSetupSession(params) {
|
|
130
|
+
const exchangeResponse = await fetch(`${params.backendUrl}/api/setup/device-session/exchange`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: { "Content-Type": "application/json" },
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
sessionId: params.sessionId,
|
|
135
|
+
state: params.state,
|
|
136
|
+
...(params.code ? { code: params.code } : {}),
|
|
137
|
+
}),
|
|
138
|
+
signal: AbortSignal.timeout(20000),
|
|
139
|
+
});
|
|
140
|
+
const exchangeBody = (await exchangeResponse.json().catch(() => ({})));
|
|
141
|
+
if (!exchangeResponse.ok ||
|
|
142
|
+
exchangeBody.authType !== "device-session" ||
|
|
143
|
+
!exchangeBody.credential ||
|
|
144
|
+
!exchangeBody.deviceSessionId) {
|
|
145
|
+
throw new Error(exchangeBody.error ??
|
|
146
|
+
`failed to exchange browser setup session (HTTP ${exchangeResponse.status})`);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
credential: exchangeBody.credential,
|
|
150
|
+
authType: "device-session",
|
|
151
|
+
deviceSessionId: exchangeBody.deviceSessionId,
|
|
152
|
+
deviceLabel: exchangeBody.deviceLabel,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function delay(ms) {
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
157
|
+
}
|
|
158
|
+
async function waitForFirstSuccessfulExchange(promises) {
|
|
159
|
+
return await new Promise((resolve, reject) => {
|
|
160
|
+
const errors = [];
|
|
161
|
+
let pending = promises.length;
|
|
162
|
+
let settled = false;
|
|
163
|
+
const fail = (error) => {
|
|
164
|
+
if (settled)
|
|
165
|
+
return;
|
|
166
|
+
if (error instanceof Error) {
|
|
167
|
+
errors.push(error);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
errors.push(new Error(String(error)));
|
|
171
|
+
}
|
|
172
|
+
pending -= 1;
|
|
173
|
+
if (pending <= 0) {
|
|
174
|
+
reject(errors[0] ?? new Error("browser setup timed out"));
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
for (const promise of promises) {
|
|
178
|
+
promise.then((value) => {
|
|
179
|
+
if (settled)
|
|
180
|
+
return;
|
|
181
|
+
settled = true;
|
|
182
|
+
resolve(value);
|
|
183
|
+
}, (error) => {
|
|
184
|
+
fail(error);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
127
189
|
async function createBrowserLinkedDeviceSession(params) {
|
|
128
190
|
const label = (params.deviceLabel ?? "").trim() || node_os_1.default.hostname() || "This device";
|
|
129
191
|
const state = node_crypto_1.default.randomUUID();
|
|
@@ -137,7 +199,6 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
137
199
|
state,
|
|
138
200
|
deviceLabel: label,
|
|
139
201
|
selectedClients: params.selectedClients,
|
|
140
|
-
usagePatchRequested: params.usagePatchRequested,
|
|
141
202
|
}),
|
|
142
203
|
signal: AbortSignal.timeout(20000),
|
|
143
204
|
});
|
|
@@ -145,39 +206,60 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
145
206
|
if (!startResponse.ok || !startBody.authUrl || !startBody.sessionId) {
|
|
146
207
|
throw new Error(startBody.error ?? `failed to start browser auth (HTTP ${startResponse.status})`);
|
|
147
208
|
}
|
|
209
|
+
const setupSessionId = startBody.sessionId;
|
|
148
210
|
const opened = openUrlInBrowser(startBody.authUrl);
|
|
149
211
|
params.log(opened
|
|
150
212
|
? `Open browser auth if it did not appear automatically: ${startBody.authUrl}`
|
|
151
213
|
: `Open this URL to continue setup: ${startBody.authUrl}`);
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
214
|
+
const deadlineMs = Date.now() + BROWSER_SETUP_TIMEOUT_MS;
|
|
215
|
+
let fallbackNoticeShown = false;
|
|
216
|
+
const timeoutError = new Error("browser setup timed out");
|
|
217
|
+
const waitForLocalCallbackExchange = async () => {
|
|
218
|
+
const callback = await Promise.race([
|
|
219
|
+
callbackServer.waitForCallback(),
|
|
220
|
+
new Promise((_, reject) => {
|
|
221
|
+
setTimeout(() => reject(timeoutError), BROWSER_SETUP_TIMEOUT_MS);
|
|
222
|
+
}),
|
|
223
|
+
]);
|
|
224
|
+
return exchangeBrowserSetupSession({
|
|
225
|
+
backendUrl: params.backendUrl,
|
|
162
226
|
sessionId: callback.sessionId,
|
|
163
227
|
state: callback.state,
|
|
164
228
|
code: callback.code,
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
const waitForPolledExchange = async () => {
|
|
232
|
+
while (Date.now() < deadlineMs) {
|
|
233
|
+
try {
|
|
234
|
+
return await exchangeBrowserSetupSession({
|
|
235
|
+
backendUrl: params.backendUrl,
|
|
236
|
+
sessionId: setupSessionId,
|
|
237
|
+
state,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
const message = error.message || "";
|
|
242
|
+
if (!message.includes("setup session is not approved")) {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!fallbackNoticeShown) {
|
|
247
|
+
params.log("Waiting for browser approval. If the browser cannot reach localhost, setup will still finish here after you connect the device.");
|
|
248
|
+
fallbackNoticeShown = true;
|
|
249
|
+
}
|
|
250
|
+
await delay(BROWSER_SETUP_POLL_INTERVAL_MS);
|
|
251
|
+
}
|
|
252
|
+
throw timeoutError;
|
|
253
|
+
};
|
|
254
|
+
const linked = await waitForFirstSuccessfulExchange([
|
|
255
|
+
waitForLocalCallbackExchange(),
|
|
256
|
+
waitForPolledExchange(),
|
|
257
|
+
]);
|
|
176
258
|
return {
|
|
177
|
-
credential:
|
|
178
|
-
authType:
|
|
179
|
-
deviceSessionId:
|
|
180
|
-
deviceLabel:
|
|
259
|
+
credential: linked.credential,
|
|
260
|
+
authType: linked.authType,
|
|
261
|
+
deviceSessionId: linked.deviceSessionId,
|
|
262
|
+
deviceLabel: linked.deviceLabel?.trim() || label,
|
|
181
263
|
};
|
|
182
264
|
}
|
|
183
265
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theclawbay",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.45",
|
|
4
4
|
"description": "CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node -e \"require('node:fs').chmodSync('dist/index.js',0o755)\"",
|
|
13
|
-
"
|
|
13
|
+
"verify:npm-package": "node ./scripts/verify-npm-package.js",
|
|
14
|
+
"prepublishOnly": "npm run build && npm run verify:npm-package",
|
|
14
15
|
"release:patch": "./scripts/release-npm.sh patch",
|
|
15
16
|
"release:minor": "./scripts/release-npm.sh minor",
|
|
16
17
|
"release:major": "./scripts/release-npm.sh major",
|
|
@@ -45,6 +46,10 @@
|
|
|
45
46
|
"preferGlobal": true,
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@oclif/core": "^4.8.0",
|
|
49
|
+
"lucide-react": "^1.7.0",
|
|
50
|
+
"next": "^16.2.1",
|
|
51
|
+
"react": "^19.2.4",
|
|
52
|
+
"react-dom": "^19.2.4",
|
|
48
53
|
"tslib": "^2.8.1",
|
|
49
54
|
"yaml": "^2.8.2"
|
|
50
55
|
},
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type PatchCodexVsCodeExtensionsResult = {
|
|
2
|
-
action: "patched" | "already_patched" | "none";
|
|
3
|
-
patchedRoots: string[];
|
|
4
|
-
warning?: string;
|
|
5
|
-
};
|
|
6
|
-
export type CleanupCodexVsCodeExtensionsResult = {
|
|
7
|
-
action: "removed" | "none";
|
|
8
|
-
restoredRoots: string[];
|
|
9
|
-
warning?: string;
|
|
10
|
-
};
|
|
11
|
-
export declare function patchCodexVsCodeExtensions(params: {
|
|
12
|
-
logoutCommandPath?: string | null;
|
|
13
|
-
}): Promise<PatchCodexVsCodeExtensionsResult>;
|
|
14
|
-
export declare function cleanupCodexVsCodeExtensions(): Promise<CleanupCodexVsCodeExtensionsResult>;
|
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.patchCodexVsCodeExtensions = patchCodexVsCodeExtensions;
|
|
7
|
-
exports.cleanupCodexVsCodeExtensions = cleanupCodexVsCodeExtensions;
|
|
8
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
-
const EXTENSION_PREFIX = "openai.chatgpt-";
|
|
12
|
-
const BACKUP_SUFFIX = ".theclawbay-managed-backup";
|
|
13
|
-
const EXTENSION_PATCH_MARKER = "theclawbay-codex-extension-host-patch";
|
|
14
|
-
const WEBVIEW_PATCH_MARKER = "theclawbay-codex-webview-patch";
|
|
15
|
-
const LOGOUT_COMMAND_ID = "chatgpt.theClawBayLogout";
|
|
16
|
-
function uniqueStrings(values) {
|
|
17
|
-
const output = [];
|
|
18
|
-
const seen = new Set();
|
|
19
|
-
for (const value of values) {
|
|
20
|
-
const normalized = value.trim();
|
|
21
|
-
if (!normalized || seen.has(normalized))
|
|
22
|
-
continue;
|
|
23
|
-
seen.add(normalized);
|
|
24
|
-
output.push(normalized);
|
|
25
|
-
}
|
|
26
|
-
return output;
|
|
27
|
-
}
|
|
28
|
-
async function pathExists(filePath) {
|
|
29
|
-
try {
|
|
30
|
-
await promises_1.default.access(filePath);
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function readFileIfExists(filePath) {
|
|
38
|
-
try {
|
|
39
|
-
return await promises_1.default.readFile(filePath, "utf8");
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
const err = error;
|
|
43
|
-
if (err.code === "ENOENT")
|
|
44
|
-
return null;
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function extensionSearchRoots() {
|
|
49
|
-
const home = node_os_1.default.homedir();
|
|
50
|
-
return uniqueStrings([
|
|
51
|
-
node_path_1.default.join(home, ".vscode", "extensions"),
|
|
52
|
-
node_path_1.default.join(home, ".vscode-insiders", "extensions"),
|
|
53
|
-
node_path_1.default.join(home, ".vscode-server", "extensions"),
|
|
54
|
-
node_path_1.default.join(home, ".vscode-server-insiders", "extensions"),
|
|
55
|
-
node_path_1.default.join(home, ".cursor", "extensions"),
|
|
56
|
-
node_path_1.default.join(home, ".windsurf", "extensions"),
|
|
57
|
-
node_path_1.default.join(home, ".vscode-oss", "extensions"),
|
|
58
|
-
]);
|
|
59
|
-
}
|
|
60
|
-
async function findCodexExtensionRoots() {
|
|
61
|
-
const roots = [];
|
|
62
|
-
for (const extensionsDir of extensionSearchRoots()) {
|
|
63
|
-
if (!(await pathExists(extensionsDir)))
|
|
64
|
-
continue;
|
|
65
|
-
const entries = await promises_1.default.readdir(extensionsDir, { withFileTypes: true });
|
|
66
|
-
for (const entry of entries) {
|
|
67
|
-
if (!entry.isDirectory())
|
|
68
|
-
continue;
|
|
69
|
-
if (!entry.name.toLowerCase().startsWith(EXTENSION_PREFIX))
|
|
70
|
-
continue;
|
|
71
|
-
roots.push(node_path_1.default.join(extensionsDir, entry.name));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return uniqueStrings(roots).sort();
|
|
75
|
-
}
|
|
76
|
-
async function resolveWebviewEntryScriptPath(extensionRoot) {
|
|
77
|
-
const indexHtmlPath = node_path_1.default.join(extensionRoot, "webview", "index.html");
|
|
78
|
-
const indexHtml = await readFileIfExists(indexHtmlPath);
|
|
79
|
-
if (!indexHtml)
|
|
80
|
-
return null;
|
|
81
|
-
const match = indexHtml.match(/<script[^>]+src="\.\/([^"]+index-[^"]+\.js)"/i);
|
|
82
|
-
if (!match?.[1])
|
|
83
|
-
return null;
|
|
84
|
-
return node_path_1.default.join(extensionRoot, "webview", match[1]);
|
|
85
|
-
}
|
|
86
|
-
function backupPath(filePath) {
|
|
87
|
-
return `${filePath}${BACKUP_SUFFIX}`;
|
|
88
|
-
}
|
|
89
|
-
async function patchFile(params) {
|
|
90
|
-
const existing = await readFileIfExists(params.filePath);
|
|
91
|
-
if (existing === null) {
|
|
92
|
-
throw new Error(`required Codex extension file is missing: ${params.filePath}`);
|
|
93
|
-
}
|
|
94
|
-
const fileBackupPath = backupPath(params.filePath);
|
|
95
|
-
const backupExists = await pathExists(fileBackupPath);
|
|
96
|
-
const baseSource = backupExists ? ((await readFileIfExists(fileBackupPath)) ?? existing) : existing;
|
|
97
|
-
const next = params.transform(baseSource);
|
|
98
|
-
if (!backupExists) {
|
|
99
|
-
await promises_1.default.writeFile(fileBackupPath, existing, "utf8");
|
|
100
|
-
}
|
|
101
|
-
if (next === existing && existing.includes(params.marker)) {
|
|
102
|
-
return "already_patched";
|
|
103
|
-
}
|
|
104
|
-
await promises_1.default.writeFile(params.filePath, next, "utf8");
|
|
105
|
-
return "patched";
|
|
106
|
-
}
|
|
107
|
-
function buildExtensionHostPatch(logoutCommandPath) {
|
|
108
|
-
const embeddedCommandPath = JSON.stringify(logoutCommandPath);
|
|
109
|
-
return `
|
|
110
|
-
;/* ${EXTENSION_PATCH_MARKER}:start */
|
|
111
|
-
(()=> {
|
|
112
|
-
const vscode = require("vscode");
|
|
113
|
-
const childProcess = require("node:child_process");
|
|
114
|
-
const os = require("node:os");
|
|
115
|
-
const commandId = ${JSON.stringify(LOGOUT_COMMAND_ID)};
|
|
116
|
-
const embeddedCommandPath = ${embeddedCommandPath};
|
|
117
|
-
|
|
118
|
-
function quoteShellArg(value) {
|
|
119
|
-
if (process.platform === "win32") {
|
|
120
|
-
return '"' + String(value).replace(/"/g, '""') + '"';
|
|
121
|
-
}
|
|
122
|
-
return "'" + String(value).replace(/'/g, "'\\\\''") + "'";
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function buildLogoutCommands() {
|
|
126
|
-
const commands = [];
|
|
127
|
-
if (embeddedCommandPath) {
|
|
128
|
-
commands.push(\`\${quoteShellArg(embeddedCommandPath)} logout\`);
|
|
129
|
-
}
|
|
130
|
-
commands.push("theclawbay logout");
|
|
131
|
-
return commands;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function runLogoutCommand(commandLine, cwd, outputChannel) {
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
childProcess.exec(
|
|
137
|
-
commandLine,
|
|
138
|
-
{
|
|
139
|
-
cwd,
|
|
140
|
-
windowsHide: true,
|
|
141
|
-
maxBuffer: 1024 * 1024,
|
|
142
|
-
shell: process.platform === "win32" ? (process.env.ComSpec || "cmd.exe") : (process.env.SHELL || "/bin/sh"),
|
|
143
|
-
},
|
|
144
|
-
(error, stdout, stderr) => {
|
|
145
|
-
if (stdout) outputChannel.append(stdout);
|
|
146
|
-
if (stderr) outputChannel.append(stderr);
|
|
147
|
-
if (error) {
|
|
148
|
-
reject(error);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
resolve();
|
|
152
|
-
},
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async function executeLogout(outputChannel) {
|
|
158
|
-
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
|
159
|
-
const cwd = workspaceFolder || os.homedir();
|
|
160
|
-
const errors = [];
|
|
161
|
-
for (const commandLine of buildLogoutCommands()) {
|
|
162
|
-
outputChannel.appendLine(\`$ \${commandLine}\`);
|
|
163
|
-
try {
|
|
164
|
-
await runLogoutCommand(commandLine, cwd, outputChannel);
|
|
165
|
-
return;
|
|
166
|
-
} catch (error) {
|
|
167
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
168
|
-
errors.push(\`\${commandLine}: \${message}\`);
|
|
169
|
-
outputChannel.appendLine(message);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
throw new Error(errors.join("\\n"));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function patchActivate(exportsObject) {
|
|
176
|
-
if (!exportsObject || typeof exportsObject.activate !== "function" || exportsObject.__theclawbayPatched) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const originalActivate = exportsObject.activate;
|
|
180
|
-
exportsObject.activate = async function patchedActivate(context, ...rest) {
|
|
181
|
-
const result = await originalActivate.apply(this, [context, ...rest]);
|
|
182
|
-
if (context && !context.__theclawbayLogoutRegistered) {
|
|
183
|
-
context.__theclawbayLogoutRegistered = true;
|
|
184
|
-
const outputChannel = vscode.window.createOutputChannel("The Claw Bay");
|
|
185
|
-
context.subscriptions.push(outputChannel);
|
|
186
|
-
context.subscriptions.push(
|
|
187
|
-
vscode.commands.registerCommand(commandId, async () => {
|
|
188
|
-
const confirmed = await vscode.window.showWarningMessage(
|
|
189
|
-
"Run The Claw Bay logout and remove the Codex usage/UI patch?",
|
|
190
|
-
{ modal: true },
|
|
191
|
-
"Log out",
|
|
192
|
-
);
|
|
193
|
-
if (confirmed !== "Log out") return;
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
await vscode.window.withProgress(
|
|
197
|
-
{
|
|
198
|
-
location: vscode.ProgressLocation.Notification,
|
|
199
|
-
title: "Removing The Claw Bay Codex patch…",
|
|
200
|
-
},
|
|
201
|
-
async () => {
|
|
202
|
-
await executeLogout(outputChannel);
|
|
203
|
-
},
|
|
204
|
-
);
|
|
205
|
-
const reloadChoice = await vscode.window.showInformationMessage(
|
|
206
|
-
"The Claw Bay logout completed. Reload this window to finish removing the Codex patch.",
|
|
207
|
-
"Reload Window",
|
|
208
|
-
"Show Output",
|
|
209
|
-
);
|
|
210
|
-
if (reloadChoice === "Reload Window") {
|
|
211
|
-
await vscode.commands.executeCommand("workbench.action.reloadWindow");
|
|
212
|
-
} else if (reloadChoice === "Show Output") {
|
|
213
|
-
outputChannel.show(true);
|
|
214
|
-
}
|
|
215
|
-
} catch (error) {
|
|
216
|
-
outputChannel.show(true);
|
|
217
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
218
|
-
await vscode.window.showErrorMessage(\`The Claw Bay logout failed: \${message}\`, "Show Output");
|
|
219
|
-
}
|
|
220
|
-
}),
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
return result;
|
|
224
|
-
};
|
|
225
|
-
exportsObject.__theclawbayPatched = true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
patchActivate(module.exports);
|
|
229
|
-
})();
|
|
230
|
-
/* ${EXTENSION_PATCH_MARKER}:end */
|
|
231
|
-
`;
|
|
232
|
-
}
|
|
233
|
-
function buildWebviewPatch() {
|
|
234
|
-
return `
|
|
235
|
-
;/* ${WEBVIEW_PATCH_MARKER}:start */
|
|
236
|
-
(()=> {
|
|
237
|
-
const cardId = "theclawbay-codex-settings-card";
|
|
238
|
-
const styleId = "theclawbay-codex-settings-style";
|
|
239
|
-
const commandId = ${JSON.stringify(LOGOUT_COMMAND_ID)};
|
|
240
|
-
const vscodeApi = typeof acquireVsCodeApi === "function" ? acquireVsCodeApi() : null;
|
|
241
|
-
|
|
242
|
-
function currentRoute() {
|
|
243
|
-
return \`\${window.location.pathname}\${window.location.hash}\`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function isAgentSettingsRoute() {
|
|
247
|
-
return currentRoute().includes("/settings/general") || currentRoute().includes("/settings/agent");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function ensureStyle() {
|
|
251
|
-
if (document.getElementById(styleId)) return;
|
|
252
|
-
const style = document.createElement("style");
|
|
253
|
-
style.id = styleId;
|
|
254
|
-
style.textContent = \`
|
|
255
|
-
#\${cardId} {
|
|
256
|
-
margin: 20px 0 28px;
|
|
257
|
-
border: 1px solid var(--vscode-panel-border, rgba(255,255,255,0.12));
|
|
258
|
-
border-radius: 12px;
|
|
259
|
-
background: var(--vscode-editorWidget-background, rgba(255,255,255,0.03));
|
|
260
|
-
color: var(--vscode-foreground, inherit);
|
|
261
|
-
overflow: hidden;
|
|
262
|
-
}
|
|
263
|
-
#\${cardId} .theclawbay-header {
|
|
264
|
-
display: flex;
|
|
265
|
-
align-items: center;
|
|
266
|
-
justify-content: space-between;
|
|
267
|
-
gap: 12px;
|
|
268
|
-
padding: 14px 16px;
|
|
269
|
-
}
|
|
270
|
-
#\${cardId} .theclawbay-title {
|
|
271
|
-
font-size: 14px;
|
|
272
|
-
font-weight: 700;
|
|
273
|
-
color: var(--vscode-foreground, inherit);
|
|
274
|
-
}
|
|
275
|
-
#\${cardId} .theclawbay-topline {
|
|
276
|
-
display: flex;
|
|
277
|
-
align-items: center;
|
|
278
|
-
gap: 10px;
|
|
279
|
-
}
|
|
280
|
-
#\${cardId} .theclawbay-badge {
|
|
281
|
-
border-radius: 999px;
|
|
282
|
-
padding: 2px 8px;
|
|
283
|
-
font-size: 12px;
|
|
284
|
-
font-weight: 600;
|
|
285
|
-
color: var(--vscode-button-foreground, white);
|
|
286
|
-
background: var(--vscode-button-background, #0e639c);
|
|
287
|
-
}
|
|
288
|
-
#\${cardId} .theclawbay-body {
|
|
289
|
-
padding: 0 16px 16px;
|
|
290
|
-
display: flex;
|
|
291
|
-
flex-direction: column;
|
|
292
|
-
gap: 12px;
|
|
293
|
-
}
|
|
294
|
-
#\${cardId} .theclawbay-copy {
|
|
295
|
-
font-size: 13px;
|
|
296
|
-
line-height: 1.5;
|
|
297
|
-
color: var(--vscode-descriptionForeground, var(--vscode-foreground, inherit));
|
|
298
|
-
}
|
|
299
|
-
#\${cardId} .theclawbay-button {
|
|
300
|
-
width: fit-content;
|
|
301
|
-
border: 1px solid transparent;
|
|
302
|
-
border-radius: 8px;
|
|
303
|
-
padding: 8px 12px;
|
|
304
|
-
cursor: pointer;
|
|
305
|
-
font: inherit;
|
|
306
|
-
font-weight: 600;
|
|
307
|
-
color: var(--vscode-button-foreground, white);
|
|
308
|
-
background: var(--vscode-button-background, #0e639c);
|
|
309
|
-
}
|
|
310
|
-
#\${cardId} .theclawbay-button:hover {
|
|
311
|
-
background: var(--vscode-button-hoverBackground, #1177bb);
|
|
312
|
-
}
|
|
313
|
-
#\${cardId} .theclawbay-row {
|
|
314
|
-
display: flex;
|
|
315
|
-
align-items: center;
|
|
316
|
-
justify-content: space-between;
|
|
317
|
-
gap: 16px;
|
|
318
|
-
flex-wrap: wrap;
|
|
319
|
-
}
|
|
320
|
-
#\${cardId} .theclawbay-toggle {
|
|
321
|
-
display: inline-flex;
|
|
322
|
-
align-items: center;
|
|
323
|
-
gap: 10px;
|
|
324
|
-
font-size: 13px;
|
|
325
|
-
font-weight: 600;
|
|
326
|
-
color: var(--vscode-foreground, inherit);
|
|
327
|
-
}
|
|
328
|
-
#\${cardId} .theclawbay-toggle input {
|
|
329
|
-
width: 34px;
|
|
330
|
-
height: 18px;
|
|
331
|
-
accent-color: var(--vscode-button-background, #0e639c);
|
|
332
|
-
cursor: pointer;
|
|
333
|
-
}
|
|
334
|
-
\`;
|
|
335
|
-
document.head.appendChild(style);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function findMount() {
|
|
339
|
-
const selectors = [
|
|
340
|
-
"div.min-w-0.flex-1.overflow-visible",
|
|
341
|
-
"div.min-w-0.flex-1",
|
|
342
|
-
"main",
|
|
343
|
-
];
|
|
344
|
-
for (const selector of selectors) {
|
|
345
|
-
const matches = Array.from(document.querySelectorAll(selector));
|
|
346
|
-
for (let index = matches.length - 1; index >= 0; index -= 1) {
|
|
347
|
-
const match = matches[index];
|
|
348
|
-
if (!(match instanceof HTMLElement)) continue;
|
|
349
|
-
if (match.closest("#root") || match.id === "root") {
|
|
350
|
-
return match;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return document.getElementById("root");
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function buildCard() {
|
|
358
|
-
const wrapper = document.createElement("section");
|
|
359
|
-
wrapper.id = cardId;
|
|
360
|
-
|
|
361
|
-
const header = document.createElement("div");
|
|
362
|
-
header.className = "theclawbay-header";
|
|
363
|
-
|
|
364
|
-
const topline = document.createElement("div");
|
|
365
|
-
topline.className = "theclawbay-topline";
|
|
366
|
-
const title = document.createElement("span");
|
|
367
|
-
title.className = "theclawbay-title";
|
|
368
|
-
title.textContent = "The Claw Bay";
|
|
369
|
-
const badge = document.createElement("span");
|
|
370
|
-
badge.className = "theclawbay-badge";
|
|
371
|
-
badge.textContent = "Patched";
|
|
372
|
-
topline.append(title, badge);
|
|
373
|
-
header.appendChild(topline);
|
|
374
|
-
|
|
375
|
-
const body = document.createElement("div");
|
|
376
|
-
body.className = "theclawbay-body";
|
|
377
|
-
|
|
378
|
-
const copy = document.createElement("p");
|
|
379
|
-
copy.className = "theclawbay-copy";
|
|
380
|
-
copy.textContent = "Shows The Claw Bay usage remaining inside Codex.";
|
|
381
|
-
|
|
382
|
-
const row = document.createElement("div");
|
|
383
|
-
row.className = "theclawbay-row";
|
|
384
|
-
|
|
385
|
-
const toggleLabel = document.createElement("label");
|
|
386
|
-
toggleLabel.className = "theclawbay-toggle";
|
|
387
|
-
|
|
388
|
-
const toggle = document.createElement("input");
|
|
389
|
-
toggle.type = "checkbox";
|
|
390
|
-
toggle.checked = true;
|
|
391
|
-
toggle.addEventListener("change", () => {
|
|
392
|
-
if (!toggle.checked) {
|
|
393
|
-
vscodeApi?.postMessage({ type: "open-vscode-command", command: commandId, args: [] });
|
|
394
|
-
}
|
|
395
|
-
window.setTimeout(() => {
|
|
396
|
-
toggle.checked = true;
|
|
397
|
-
}, 0);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
const toggleText = document.createElement("span");
|
|
401
|
-
toggleText.textContent = "Usage remaining patch";
|
|
402
|
-
toggleLabel.append(toggle, toggleText);
|
|
403
|
-
|
|
404
|
-
const button = document.createElement("button");
|
|
405
|
-
button.type = "button";
|
|
406
|
-
button.className = "theclawbay-button";
|
|
407
|
-
button.textContent = "Remove patch";
|
|
408
|
-
button.addEventListener("click", () => {
|
|
409
|
-
vscodeApi?.postMessage({ type: "open-vscode-command", command: commandId, args: [] });
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
row.append(toggleLabel, button);
|
|
413
|
-
body.append(copy, row);
|
|
414
|
-
wrapper.append(header, body);
|
|
415
|
-
return wrapper;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function syncCard() {
|
|
419
|
-
const existing = document.getElementById(cardId);
|
|
420
|
-
if (!isAgentSettingsRoute()) {
|
|
421
|
-
existing?.remove();
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
ensureStyle();
|
|
425
|
-
if (existing) return;
|
|
426
|
-
const mount = findMount();
|
|
427
|
-
if (!mount) return;
|
|
428
|
-
mount.appendChild(buildCard());
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
let lastRoute = currentRoute();
|
|
432
|
-
const observer = new MutationObserver(() => {
|
|
433
|
-
const nextRoute = currentRoute();
|
|
434
|
-
if (nextRoute !== lastRoute) {
|
|
435
|
-
lastRoute = nextRoute;
|
|
436
|
-
}
|
|
437
|
-
syncCard();
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
if (document.body) {
|
|
441
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
442
|
-
} else {
|
|
443
|
-
window.addEventListener("DOMContentLoaded", () => {
|
|
444
|
-
if (document.body) observer.observe(document.body, { childList: true, subtree: true });
|
|
445
|
-
syncCard();
|
|
446
|
-
}, { once: true });
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
window.addEventListener("popstate", syncCard);
|
|
450
|
-
window.setInterval(syncCard, 1500);
|
|
451
|
-
syncCard();
|
|
452
|
-
})();
|
|
453
|
-
/* ${WEBVIEW_PATCH_MARKER}:end */
|
|
454
|
-
`;
|
|
455
|
-
}
|
|
456
|
-
async function patchCodexVsCodeExtensions(params) {
|
|
457
|
-
void params;
|
|
458
|
-
return {
|
|
459
|
-
action: "none",
|
|
460
|
-
patchedRoots: [],
|
|
461
|
-
warning: "The Codex VS Code bundle patch has been disabled for safety. Run `theclawbay setup` or `theclawbay logout` to remove any older The Claw Bay VS Code patch.",
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
async function cleanupCodexVsCodeExtensions() {
|
|
465
|
-
try {
|
|
466
|
-
const extensionRoots = await findCodexExtensionRoots();
|
|
467
|
-
if (extensionRoots.length === 0) {
|
|
468
|
-
return { action: "none", restoredRoots: [] };
|
|
469
|
-
}
|
|
470
|
-
const restoredRoots = new Set();
|
|
471
|
-
for (const extensionRoot of extensionRoots) {
|
|
472
|
-
const candidates = [
|
|
473
|
-
node_path_1.default.join(extensionRoot, "out", "extension.js"),
|
|
474
|
-
];
|
|
475
|
-
const webviewAssetsDir = node_path_1.default.join(extensionRoot, "webview", "assets");
|
|
476
|
-
if (await pathExists(webviewAssetsDir)) {
|
|
477
|
-
const webviewFiles = await promises_1.default.readdir(webviewAssetsDir);
|
|
478
|
-
for (const fileName of webviewFiles) {
|
|
479
|
-
if (!fileName.startsWith("index-") || !fileName.endsWith(".js"))
|
|
480
|
-
continue;
|
|
481
|
-
candidates.push(node_path_1.default.join(webviewAssetsDir, fileName));
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
for (const filePath of candidates) {
|
|
485
|
-
const fileBackupPath = backupPath(filePath);
|
|
486
|
-
const backup = await readFileIfExists(fileBackupPath);
|
|
487
|
-
if (backup === null)
|
|
488
|
-
continue;
|
|
489
|
-
const existing = await readFileIfExists(filePath);
|
|
490
|
-
if (existing !== null &&
|
|
491
|
-
(existing.includes(EXTENSION_PATCH_MARKER) || existing.includes(WEBVIEW_PATCH_MARKER))) {
|
|
492
|
-
await promises_1.default.writeFile(filePath, backup, "utf8");
|
|
493
|
-
restoredRoots.add(extensionRoot);
|
|
494
|
-
}
|
|
495
|
-
await promises_1.default.unlink(fileBackupPath).catch(() => { });
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return {
|
|
499
|
-
action: restoredRoots.size > 0 ? "removed" : "none",
|
|
500
|
-
restoredRoots: Array.from(restoredRoots).sort(),
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
catch (error) {
|
|
504
|
-
return {
|
|
505
|
-
action: "none",
|
|
506
|
-
restoredRoots: [],
|
|
507
|
-
warning: `Could not clean up the Codex VS Code extension patch: ${error.message}`,
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
}
|