theclawbay 0.3.87 → 0.4.0

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.
Files changed (86) hide show
  1. package/README.md +3 -2
  2. package/dist/commands/logout.js +82 -1466
  3. package/dist/commands/setup.js +143 -3604
  4. package/dist/lib/base-command.js +7 -5
  5. package/dist/lib/clients/aider-cleanup.d.ts +1 -0
  6. package/dist/lib/clients/aider-cleanup.js +15 -0
  7. package/dist/lib/clients/aider.d.ts +14 -0
  8. package/dist/lib/clients/aider.js +57 -0
  9. package/dist/lib/clients/claude-cleanup.d.ts +2 -0
  10. package/dist/lib/clients/claude-cleanup.js +88 -0
  11. package/dist/lib/clients/claude.d.ts +49 -0
  12. package/dist/lib/clients/claude.js +400 -0
  13. package/dist/lib/clients/cline-cleanup.d.ts +1 -0
  14. package/dist/lib/clients/cline-cleanup.js +80 -0
  15. package/dist/lib/clients/cline.d.ts +15 -0
  16. package/dist/lib/clients/cline.js +73 -0
  17. package/dist/lib/clients/codex-cleanup.d.ts +7 -0
  18. package/dist/lib/clients/codex-cleanup.js +145 -0
  19. package/dist/lib/clients/codex.d.ts +28 -0
  20. package/dist/lib/clients/codex.js +176 -0
  21. package/dist/lib/clients/continue-cleanup.d.ts +1 -0
  22. package/dist/lib/clients/continue-cleanup.js +40 -0
  23. package/dist/lib/clients/continue.d.ts +22 -0
  24. package/dist/lib/clients/continue.js +81 -0
  25. package/dist/lib/clients/gsd-cleanup.d.ts +4 -0
  26. package/dist/lib/clients/gsd-cleanup.js +168 -0
  27. package/dist/lib/clients/gsd.d.ts +30 -0
  28. package/dist/lib/clients/gsd.js +136 -0
  29. package/dist/lib/clients/hermes-cleanup.d.ts +2 -0
  30. package/dist/lib/clients/hermes-cleanup.js +112 -0
  31. package/dist/lib/clients/hermes.d.ts +18 -0
  32. package/dist/lib/clients/hermes.js +145 -0
  33. package/dist/lib/clients/openclaw-cleanup.d.ts +1 -0
  34. package/dist/lib/clients/openclaw-cleanup.js +52 -0
  35. package/dist/lib/clients/openclaw.d.ts +34 -0
  36. package/dist/lib/clients/openclaw.js +100 -0
  37. package/dist/lib/clients/opencode-family-cleanup.d.ts +12 -0
  38. package/dist/lib/clients/opencode-family-cleanup.js +201 -0
  39. package/dist/lib/clients/opencode-family.d.ts +86 -0
  40. package/dist/lib/clients/opencode-family.js +427 -0
  41. package/dist/lib/clients/roo-cleanup.d.ts +1 -0
  42. package/dist/lib/clients/roo-cleanup.js +48 -0
  43. package/dist/lib/clients/roo.d.ts +21 -0
  44. package/dist/lib/clients/roo.js +91 -0
  45. package/dist/lib/clients/trae-cleanup.d.ts +2 -0
  46. package/dist/lib/clients/trae-cleanup.js +40 -0
  47. package/dist/lib/clients/trae.d.ts +27 -0
  48. package/dist/lib/clients/trae.js +118 -0
  49. package/dist/lib/clients/zo.d.ts +39 -0
  50. package/dist/lib/clients/zo.js +210 -0
  51. package/dist/lib/shared/backend.d.ts +31 -0
  52. package/dist/lib/shared/backend.js +273 -0
  53. package/dist/lib/shared/client-registry.d.ts +23 -0
  54. package/dist/lib/shared/client-registry.js +68 -0
  55. package/dist/lib/shared/editor-env-cleanup.d.ts +2 -0
  56. package/dist/lib/shared/editor-env-cleanup.js +86 -0
  57. package/dist/lib/shared/editor-env.d.ts +27 -0
  58. package/dist/lib/shared/editor-env.js +224 -0
  59. package/dist/lib/shared/env-persist-cleanup.d.ts +4 -0
  60. package/dist/lib/shared/env-persist-cleanup.js +77 -0
  61. package/dist/lib/shared/env-persist.d.ts +26 -0
  62. package/dist/lib/shared/env-persist.js +179 -0
  63. package/dist/lib/shared/format.d.ts +5 -0
  64. package/dist/lib/shared/format.js +48 -0
  65. package/dist/lib/shared/fsx-cleanup.d.ts +3 -0
  66. package/dist/lib/shared/fsx-cleanup.js +36 -0
  67. package/dist/lib/shared/fsx.d.ts +9 -0
  68. package/dist/lib/shared/fsx.js +103 -0
  69. package/dist/lib/shared/models.d.ts +60 -0
  70. package/dist/lib/shared/models.js +124 -0
  71. package/dist/lib/shared/platform.d.ts +16 -0
  72. package/dist/lib/shared/platform.js +116 -0
  73. package/dist/lib/shared/prompts.d.ts +29 -0
  74. package/dist/lib/shared/prompts.js +504 -0
  75. package/dist/lib/shared/providers.d.ts +3 -0
  76. package/dist/lib/shared/providers.js +8 -0
  77. package/dist/lib/shared/toml.d.ts +7 -0
  78. package/dist/lib/shared/toml.js +157 -0
  79. package/dist/lib/shared/tui.d.ts +42 -0
  80. package/dist/lib/shared/tui.js +173 -0
  81. package/dist/lib/shared/urls.d.ts +18 -0
  82. package/dist/lib/shared/urls.js +149 -0
  83. package/dist/lib/supported-models.d.ts +1 -1
  84. package/dist/lib/supported-models.js +1 -1
  85. package/package.json +1 -6
  86. package/theclawbay-supported-models.json +202 -5
@@ -0,0 +1,30 @@
1
+ export declare const GSD_AGENT_DIR: string;
2
+ export declare const GSD_MODELS_PATH: string;
3
+ export declare const GSD_AUTH_PATH: string;
4
+ export declare const GSD_SETTINGS_PATH: string;
5
+ export declare const GSD_RESTORE_STATE_PATH: string;
6
+ export type GsdRestoreSnapshot = {
7
+ version: 1;
8
+ modelsExisted: boolean;
9
+ providerConfig: Record<string, unknown> | null;
10
+ authExisted: boolean;
11
+ authEntry: Record<string, unknown> | null;
12
+ settingsExisted: boolean;
13
+ defaultProvider: string | null;
14
+ defaultModel: string | null;
15
+ };
16
+ export declare function detectGsdClient(): Promise<boolean>;
17
+ export declare function buildGsdModels(models: Array<{
18
+ id: string;
19
+ name: string;
20
+ }>): Array<Record<string, unknown>>;
21
+ export declare function normalizeGsdCredential(value: unknown): Record<string, unknown> | null;
22
+ export declare function writeGsdConfig(params: {
23
+ backendUrl: string;
24
+ model: string;
25
+ models: Array<{
26
+ id: string;
27
+ name: string;
28
+ }>;
29
+ apiKey: string;
30
+ }): Promise<string[]>;
@@ -0,0 +1,136 @@
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.GSD_RESTORE_STATE_PATH = exports.GSD_SETTINGS_PATH = exports.GSD_AUTH_PATH = exports.GSD_MODELS_PATH = exports.GSD_AGENT_DIR = void 0;
7
+ exports.detectGsdClient = detectGsdClient;
8
+ exports.buildGsdModels = buildGsdModels;
9
+ exports.normalizeGsdCredential = normalizeGsdCredential;
10
+ exports.writeGsdConfig = writeGsdConfig;
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const node_os_1 = __importDefault(require("node:os"));
13
+ const paths_1 = require("../config/paths");
14
+ const platform_1 = require("../shared/platform");
15
+ const fsx_1 = require("../shared/fsx");
16
+ const supported_models_1 = require("../supported-models");
17
+ const models_1 = require("../shared/models");
18
+ const providers_1 = require("../shared/providers");
19
+ const urls_1 = require("../shared/urls");
20
+ const env_persist_1 = require("../shared/env-persist");
21
+ // Extracted from src/commands/setup.ts (theclawbay CLI restructure).
22
+ // Code motion only: bodies are verbatim from the original file.
23
+ exports.GSD_AGENT_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".gsd", "agent");
24
+ exports.GSD_MODELS_PATH = node_path_1.default.join(exports.GSD_AGENT_DIR, "models.json");
25
+ exports.GSD_AUTH_PATH = node_path_1.default.join(exports.GSD_AGENT_DIR, "auth.json");
26
+ exports.GSD_SETTINGS_PATH = node_path_1.default.join(exports.GSD_AGENT_DIR, "settings.json");
27
+ exports.GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "gsd.restore.json");
28
+ async function detectGsdClient() {
29
+ if ((0, platform_1.hasCommand)("gsd"))
30
+ return true;
31
+ const candidates = [
32
+ exports.GSD_AGENT_DIR,
33
+ exports.GSD_MODELS_PATH,
34
+ exports.GSD_AUTH_PATH,
35
+ exports.GSD_SETTINGS_PATH,
36
+ ];
37
+ for (const candidate of candidates) {
38
+ if (await (0, fsx_1.pathExists)(candidate))
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ function buildGsdModels(models) {
44
+ const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
45
+ return models
46
+ .filter((model) => Boolean(model.id))
47
+ .map((model) => {
48
+ const pricing = supportedModelMap.get(model.id);
49
+ return {
50
+ id: model.id,
51
+ name: model.name || model.id,
52
+ reasoning: true,
53
+ input: ["text", "image"],
54
+ cost: {
55
+ input: pricing?.inputPer1M ?? 0,
56
+ output: pricing?.outputPer1M ?? 0,
57
+ cacheRead: pricing?.cachedInputPer1M ?? 0,
58
+ cacheWrite: pricing?.inputPer1M ?? 0,
59
+ },
60
+ contextWindow: (0, models_1.modelContextLimit)(model.id),
61
+ maxTokens: (0, models_1.modelOutputLimit)(model.id),
62
+ };
63
+ });
64
+ }
65
+ function normalizeGsdCredential(value) {
66
+ if (typeof value !== "object" || value === null || Array.isArray(value))
67
+ return null;
68
+ return { ...value };
69
+ }
70
+ async function writeGsdConfig(params) {
71
+ const existingModelsRaw = await (0, fsx_1.readFileIfExists)(exports.GSD_MODELS_PATH);
72
+ const existingAuthRaw = await (0, fsx_1.readFileIfExists)(exports.GSD_AUTH_PATH);
73
+ const existingSettingsRaw = await (0, fsx_1.readFileIfExists)(exports.GSD_SETTINGS_PATH);
74
+ const existingSnapshotRaw = await (0, fsx_1.readFileIfExists)(exports.GSD_RESTORE_STATE_PATH);
75
+ let modelsDoc = {};
76
+ if (existingModelsRaw?.trim()) {
77
+ try {
78
+ modelsDoc = (0, fsx_1.objectRecordOr)(JSON.parse(existingModelsRaw), {});
79
+ }
80
+ catch {
81
+ throw new Error(`invalid JSON in GSD config: ${exports.GSD_MODELS_PATH}`);
82
+ }
83
+ }
84
+ let authDoc = {};
85
+ if (existingAuthRaw?.trim()) {
86
+ try {
87
+ authDoc = (0, fsx_1.objectRecordOr)(JSON.parse(existingAuthRaw), {});
88
+ }
89
+ catch {
90
+ throw new Error(`invalid JSON in GSD auth: ${exports.GSD_AUTH_PATH}`);
91
+ }
92
+ }
93
+ let settingsDoc = {};
94
+ if (existingSettingsRaw?.trim()) {
95
+ try {
96
+ settingsDoc = (0, fsx_1.objectRecordOr)(JSON.parse(existingSettingsRaw), {});
97
+ }
98
+ catch {
99
+ throw new Error(`invalid JSON in GSD settings: ${exports.GSD_SETTINGS_PATH}`);
100
+ }
101
+ }
102
+ if (!existingSnapshotRaw?.trim()) {
103
+ const providersRoot = (0, fsx_1.objectRecordOr)(modelsDoc.providers, {});
104
+ const snapshot = {
105
+ version: 1,
106
+ modelsExisted: existingModelsRaw !== null,
107
+ providerConfig: (0, fsx_1.objectRecordOr)(providersRoot[providers_1.DEFAULT_PROVIDER_ID], {}),
108
+ authExisted: existingAuthRaw !== null,
109
+ authEntry: normalizeGsdCredential(authDoc[providers_1.DEFAULT_PROVIDER_ID]),
110
+ settingsExisted: existingSettingsRaw !== null,
111
+ defaultProvider: typeof settingsDoc.defaultProvider === "string" ? settingsDoc.defaultProvider : null,
112
+ defaultModel: typeof settingsDoc.defaultModel === "string" ? settingsDoc.defaultModel : null,
113
+ };
114
+ if (Object.keys(snapshot.providerConfig ?? {}).length === 0)
115
+ snapshot.providerConfig = null;
116
+ await (0, fsx_1.writeJsonObjectFile)(exports.GSD_RESTORE_STATE_PATH, snapshot, 0o600);
117
+ }
118
+ const providersRoot = (0, fsx_1.objectRecordOr)(modelsDoc.providers, {});
119
+ providersRoot[providers_1.DEFAULT_PROVIDER_ID] = {
120
+ baseUrl: (0, urls_1.openAiCompatibleProxyUrl)(params.backendUrl),
121
+ apiKey: env_persist_1.ENV_KEY_NAME,
122
+ api: "openai-responses",
123
+ models: buildGsdModels(params.models),
124
+ };
125
+ modelsDoc.providers = providersRoot;
126
+ authDoc[providers_1.DEFAULT_PROVIDER_ID] = {
127
+ type: "api_key",
128
+ key: params.apiKey,
129
+ };
130
+ settingsDoc.defaultProvider = providers_1.DEFAULT_PROVIDER_ID;
131
+ settingsDoc.defaultModel = params.model.trim() || models_1.DEFAULT_CODEX_MODEL;
132
+ await (0, fsx_1.writeJsonObjectFile)(exports.GSD_MODELS_PATH, modelsDoc);
133
+ await (0, fsx_1.writeJsonObjectFile)(exports.GSD_AUTH_PATH, authDoc, 0o600);
134
+ await (0, fsx_1.writeJsonObjectFile)(exports.GSD_SETTINGS_PATH, settingsDoc);
135
+ return [exports.GSD_MODELS_PATH, exports.GSD_AUTH_PATH, exports.GSD_SETTINGS_PATH];
136
+ }
@@ -0,0 +1,2 @@
1
+ export declare function removeDotEnvValue(filePath: string, key: string): Promise<boolean>;
2
+ export declare function cleanupHermesConfig(): Promise<boolean>;
@@ -0,0 +1,112 @@
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.removeDotEnvValue = removeDotEnvValue;
7
+ exports.cleanupHermesConfig = cleanupHermesConfig;
8
+ const fsx_1 = require("../shared/fsx");
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const hermes_1 = require("./hermes");
12
+ const yaml_1 = require("yaml");
13
+ const urls_1 = require("../shared/urls");
14
+ // Extracted from src/commands/logout.ts (theclawbay CLI restructure).
15
+ // Code motion only: bodies are verbatim from the original file.
16
+ async function removeDotEnvValue(filePath, key) {
17
+ const existing = await (0, fsx_1.readFileIfExists)(filePath);
18
+ if (existing === null)
19
+ return false;
20
+ const nextLines = existing
21
+ .split(/\r?\n/)
22
+ .filter((line) => !new RegExp(`^\\s*${key}\\s*=`).test(line));
23
+ const next = `${nextLines.join("\n").trimEnd()}\n`;
24
+ const normalizedExisting = `${existing.trimEnd()}\n`;
25
+ if (next === normalizedExisting)
26
+ return false;
27
+ if (!next.trim()) {
28
+ return (0, fsx_1.removeFileIfExists)(filePath);
29
+ }
30
+ await promises_1.default.mkdir(node_path_1.default.dirname(filePath), { recursive: true });
31
+ await promises_1.default.writeFile(filePath, next, "utf8");
32
+ return true;
33
+ }
34
+ async function cleanupHermesConfig() {
35
+ const configPath = (0, hermes_1.hermesConfigPath)();
36
+ const envPath = (0, hermes_1.hermesEnvPath)();
37
+ const existingRaw = await (0, fsx_1.readFileIfExists)(configPath);
38
+ let configChanged = false;
39
+ let removedManagedProvider = false;
40
+ const removedProviderBaseUrls = new Set();
41
+ if (existingRaw !== null && existingRaw.trim()) {
42
+ try {
43
+ const existingConfig = (0, fsx_1.objectRecordOr)((0, yaml_1.parseDocument)(existingRaw).toJS(), {});
44
+ const nextConfig = { ...existingConfig };
45
+ const rawCustomProviders = Array.isArray(existingConfig.custom_providers)
46
+ ? existingConfig.custom_providers
47
+ : [];
48
+ const nextCustomProviders = rawCustomProviders.filter((entry) => {
49
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
50
+ return true;
51
+ const record = entry;
52
+ const name = typeof record.name === "string" ? record.name.trim() : "";
53
+ const providerBaseUrl = typeof record.base_url === "string" ? record.base_url.trim().replace(/\/+$/g, "") : "";
54
+ const keyEnv = typeof record.key_env === "string" ? record.key_env.trim() : "";
55
+ if (keyEnv === hermes_1.HERMES_KEY_ENV_NAME) {
56
+ removedManagedProvider = true;
57
+ if (providerBaseUrl)
58
+ removedProviderBaseUrls.add(providerBaseUrl);
59
+ return false;
60
+ }
61
+ if (name === hermes_1.HERMES_PROVIDER_NAME && (0, urls_1.isTheClawBayOpenAiCompatibleBaseUrl)(providerBaseUrl)) {
62
+ removedManagedProvider = true;
63
+ if (providerBaseUrl)
64
+ removedProviderBaseUrls.add(providerBaseUrl);
65
+ return false;
66
+ }
67
+ return true;
68
+ });
69
+ if (nextCustomProviders.length !== rawCustomProviders.length) {
70
+ configChanged = true;
71
+ }
72
+ if (nextCustomProviders.length > 0) {
73
+ nextConfig.custom_providers = nextCustomProviders;
74
+ }
75
+ else if ("custom_providers" in nextConfig) {
76
+ delete nextConfig.custom_providers;
77
+ configChanged = true;
78
+ }
79
+ const modelConfig = (0, fsx_1.objectRecordOr)(existingConfig.model, {});
80
+ const provider = typeof modelConfig.provider === "string" ? modelConfig.provider.trim() : "";
81
+ const baseUrl = typeof modelConfig.base_url === "string" ? modelConfig.base_url.trim().replace(/\/+$/g, "") : "";
82
+ const apiMode = typeof modelConfig.api_mode === "string" ? modelConfig.api_mode.trim() : "";
83
+ const shouldRemoveManagedModel = provider === "custom" &&
84
+ ((0, urls_1.isTheClawBayOpenAiCompatibleBaseUrl)(baseUrl) ||
85
+ (removedManagedProvider && apiMode === "codex_responses" && removedProviderBaseUrls.has(baseUrl)));
86
+ if (shouldRemoveManagedModel) {
87
+ for (const key of ["provider", "default", "base_url", "api_mode", "api_key", "context_length"]) {
88
+ if (key in modelConfig) {
89
+ delete modelConfig[key];
90
+ configChanged = true;
91
+ }
92
+ }
93
+ if (Object.keys(modelConfig).length > 0) {
94
+ nextConfig.model = modelConfig;
95
+ }
96
+ else if ("model" in nextConfig) {
97
+ delete nextConfig.model;
98
+ configChanged = true;
99
+ }
100
+ }
101
+ if (configChanged) {
102
+ await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
103
+ await promises_1.default.writeFile(configPath, (0, yaml_1.stringify)(nextConfig), "utf8");
104
+ }
105
+ }
106
+ catch {
107
+ // Leave invalid user config untouched; still remove our dedicated env key below.
108
+ }
109
+ }
110
+ const envChanged = await removeDotEnvValue(envPath, hermes_1.HERMES_KEY_ENV_NAME);
111
+ return configChanged || envChanged;
112
+ }
@@ -0,0 +1,18 @@
1
+ export declare const DEFAULT_HERMES_MODEL: string;
2
+ export declare const HERMES_PROVIDER_NAME = "The Claw Bay";
3
+ export declare const HERMES_KEY_ENV_NAME = "HERMES_THECLAWBAY_API_KEY";
4
+ export declare function hermesHomeDir(): string;
5
+ export declare function hermesConfigPath(): string;
6
+ export declare function hermesEnvPath(): string;
7
+ export declare function detectHermesClient(): Promise<boolean>;
8
+ export declare function formatDotEnvValue(value: string): string;
9
+ export declare function upsertDotEnvValue(filePath: string, key: string, value: string): Promise<boolean>;
10
+ export declare function writeHermesConfig(params: {
11
+ backendUrl: string;
12
+ model: string;
13
+ models?: Array<{
14
+ id: string;
15
+ name: string;
16
+ }>;
17
+ apiKey: string;
18
+ }): Promise<string[]>;
@@ -0,0 +1,145 @@
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.HERMES_KEY_ENV_NAME = exports.HERMES_PROVIDER_NAME = exports.DEFAULT_HERMES_MODEL = void 0;
7
+ exports.hermesHomeDir = hermesHomeDir;
8
+ exports.hermesConfigPath = hermesConfigPath;
9
+ exports.hermesEnvPath = hermesEnvPath;
10
+ exports.detectHermesClient = detectHermesClient;
11
+ exports.formatDotEnvValue = formatDotEnvValue;
12
+ exports.upsertDotEnvValue = upsertDotEnvValue;
13
+ exports.writeHermesConfig = writeHermesConfig;
14
+ const models_1 = require("../shared/models");
15
+ const node_path_1 = __importDefault(require("node:path"));
16
+ const node_os_1 = __importDefault(require("node:os"));
17
+ const platform_1 = require("../shared/platform");
18
+ const fsx_1 = require("../shared/fsx");
19
+ const promises_1 = __importDefault(require("node:fs/promises"));
20
+ const urls_1 = require("../shared/urls");
21
+ const yaml_1 = require("yaml");
22
+ // Extracted from src/commands/setup.ts (theclawbay CLI restructure).
23
+ // Code motion only: bodies are verbatim from the original file.
24
+ exports.DEFAULT_HERMES_MODEL = models_1.DEFAULT_CODEX_MODEL;
25
+ exports.HERMES_PROVIDER_NAME = "The Claw Bay";
26
+ exports.HERMES_KEY_ENV_NAME = "HERMES_THECLAWBAY_API_KEY";
27
+ function hermesHomeDir() {
28
+ const raw = process.env.HERMES_HOME?.trim();
29
+ if (raw)
30
+ return raw;
31
+ return node_path_1.default.join(node_os_1.default.homedir(), ".hermes");
32
+ }
33
+ function hermesConfigPath() {
34
+ return node_path_1.default.join(hermesHomeDir(), "config.yaml");
35
+ }
36
+ function hermesEnvPath() {
37
+ return node_path_1.default.join(hermesHomeDir(), ".env");
38
+ }
39
+ async function detectHermesClient() {
40
+ if ((0, platform_1.hasCommand)("hermes"))
41
+ return true;
42
+ const candidates = [
43
+ hermesHomeDir(),
44
+ hermesConfigPath(),
45
+ hermesEnvPath(),
46
+ ];
47
+ for (const candidate of candidates) {
48
+ if (await (0, fsx_1.pathExists)(candidate))
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ function formatDotEnvValue(value) {
54
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
55
+ }
56
+ async function upsertDotEnvValue(filePath, key, value) {
57
+ const existing = (await (0, fsx_1.readFileIfExists)(filePath)) ?? "";
58
+ const lines = existing ? existing.split(/\r?\n/) : [];
59
+ const assignment = `${key}=${formatDotEnvValue(value)}`;
60
+ let changed = false;
61
+ let replaced = false;
62
+ const nextLines = lines.map((line) => {
63
+ if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
64
+ replaced = true;
65
+ if (line === assignment)
66
+ return line;
67
+ changed = true;
68
+ return assignment;
69
+ }
70
+ return line;
71
+ });
72
+ if (!replaced) {
73
+ if (nextLines.length > 0 && nextLines[nextLines.length - 1]?.trim()) {
74
+ nextLines.push("");
75
+ }
76
+ nextLines.push(assignment);
77
+ changed = true;
78
+ }
79
+ if (!changed)
80
+ return false;
81
+ await promises_1.default.mkdir(node_path_1.default.dirname(filePath), { recursive: true });
82
+ await promises_1.default.writeFile(filePath, `${nextLines.join("\n").trimEnd()}\n`, "utf8");
83
+ return true;
84
+ }
85
+ async function writeHermesConfig(params) {
86
+ const configPath = hermesConfigPath();
87
+ const envPath = hermesEnvPath();
88
+ const baseUrl = (0, urls_1.openAiCompatibleProxyUrl)(params.backendUrl);
89
+ const selectedModel = params.model.trim() || exports.DEFAULT_HERMES_MODEL;
90
+ const existingRaw = await (0, fsx_1.readFileIfExists)(configPath);
91
+ const existingConfig = existingRaw?.trim()
92
+ ? (0, fsx_1.objectRecordOr)((0, yaml_1.parseDocument)(existingRaw).toJS(), {})
93
+ : {};
94
+ const nextConfig = { ...existingConfig };
95
+ const modelConfig = (0, fsx_1.objectRecordOr)(existingConfig.model, {});
96
+ modelConfig.provider = "custom";
97
+ modelConfig.default = selectedModel;
98
+ modelConfig.base_url = baseUrl;
99
+ modelConfig.api_mode = "codex_responses";
100
+ delete modelConfig.api_key;
101
+ delete modelConfig.context_length;
102
+ nextConfig.model = modelConfig;
103
+ const rawCustomProviders = Array.isArray(existingConfig.custom_providers)
104
+ ? existingConfig.custom_providers
105
+ : [];
106
+ const nextCustomProviders = rawCustomProviders.filter((entry) => {
107
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
108
+ return true;
109
+ const record = entry;
110
+ const name = typeof record.name === "string" ? record.name.trim() : "";
111
+ const providerBaseUrl = typeof record.base_url === "string" ? record.base_url.trim() : "";
112
+ const keyEnv = typeof record.key_env === "string" ? record.key_env.trim() : "";
113
+ if (keyEnv === exports.HERMES_KEY_ENV_NAME)
114
+ return false;
115
+ if (name === exports.HERMES_PROVIDER_NAME && providerBaseUrl === baseUrl)
116
+ return false;
117
+ return true;
118
+ });
119
+ nextCustomProviders.push({
120
+ name: exports.HERMES_PROVIDER_NAME,
121
+ base_url: baseUrl,
122
+ key_env: exports.HERMES_KEY_ENV_NAME,
123
+ model: selectedModel,
124
+ api_mode: "codex_responses",
125
+ });
126
+ // One named provider per additional selected model so Hermes can switch
127
+ // models without editing config. All share the same key env, so re-runs
128
+ // and logout (which filter on key_env) clean every managed entry.
129
+ for (const model of params.models ?? []) {
130
+ if (!model.id || model.id === selectedModel)
131
+ continue;
132
+ nextCustomProviders.push({
133
+ name: `${exports.HERMES_PROVIDER_NAME} · ${model.name || model.id}`,
134
+ base_url: baseUrl,
135
+ key_env: exports.HERMES_KEY_ENV_NAME,
136
+ model: model.id,
137
+ api_mode: "codex_responses",
138
+ });
139
+ }
140
+ nextConfig.custom_providers = nextCustomProviders;
141
+ await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
142
+ await promises_1.default.writeFile(configPath, (0, yaml_1.stringify)(nextConfig), "utf8");
143
+ await upsertDotEnvValue(envPath, exports.HERMES_KEY_ENV_NAME, params.apiKey);
144
+ return [configPath, envPath];
145
+ }
@@ -0,0 +1 @@
1
+ export declare function cleanupOpenClawConfig(): Promise<boolean>;
@@ -0,0 +1,52 @@
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.cleanupOpenClawConfig = cleanupOpenClawConfig;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const fsx_1 = require("../shared/fsx");
10
+ const providers_1 = require("../shared/providers");
11
+ const promises_1 = __importDefault(require("node:fs/promises"));
12
+ // Extracted from src/commands/logout.ts (theclawbay CLI restructure).
13
+ // Code motion only: bodies are verbatim from the original file.
14
+ async function cleanupOpenClawConfig() {
15
+ const configPath = node_path_1.default.join(node_os_1.default.homedir(), ".openclaw", "openclaw.json");
16
+ const existingRaw = await (0, fsx_1.readFileIfExists)(configPath);
17
+ if (existingRaw === null || !existingRaw.trim())
18
+ return false;
19
+ let doc;
20
+ try {
21
+ doc = (0, fsx_1.objectRecordOr)(JSON.parse(existingRaw), {});
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ let changed = false;
27
+ const modelsRoot = (0, fsx_1.objectRecordOr)(doc.models, {});
28
+ const providersRoot = (0, fsx_1.objectRecordOr)(modelsRoot.providers, {});
29
+ for (const id of [providers_1.DEFAULT_PROVIDER_ID]) {
30
+ if (id in providersRoot) {
31
+ delete providersRoot[id];
32
+ changed = true;
33
+ }
34
+ }
35
+ modelsRoot.providers = providersRoot;
36
+ doc.models = modelsRoot;
37
+ const agentsRoot = (0, fsx_1.objectRecordOr)(doc.agents, {});
38
+ const defaultsRoot = (0, fsx_1.objectRecordOr)(agentsRoot.defaults, {});
39
+ const modelRoot = (0, fsx_1.objectRecordOr)(defaultsRoot.model, {});
40
+ const primary = modelRoot.primary;
41
+ if (typeof primary === "string" && primary.startsWith(`${providers_1.DEFAULT_PROVIDER_ID}/`)) {
42
+ delete modelRoot.primary;
43
+ changed = true;
44
+ }
45
+ defaultsRoot.model = modelRoot;
46
+ agentsRoot.defaults = defaultsRoot;
47
+ doc.agents = agentsRoot;
48
+ if (!changed)
49
+ return false;
50
+ await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
51
+ return true;
52
+ }
@@ -0,0 +1,34 @@
1
+ export declare const DEFAULT_OPENCLAW_MODEL: string;
2
+ export declare const OPENCLAW_PROVIDER_ID: string;
3
+ export declare function resolveOpenClawPrimaryModel(params: {
4
+ model: string;
5
+ models: Array<{
6
+ id: string;
7
+ name: string;
8
+ }>;
9
+ }): string;
10
+ export declare function patchOpenClawConfigFile(params: {
11
+ backendUrl: string;
12
+ model: string;
13
+ models: Array<{
14
+ id: string;
15
+ name: string;
16
+ }>;
17
+ apiKey: string;
18
+ }): Promise<string>;
19
+ export declare function setupOpenClaw(params: {
20
+ backendUrl: string;
21
+ model: string;
22
+ models: Array<{
23
+ id: string;
24
+ name: string;
25
+ }>;
26
+ apiKey: string;
27
+ }): Promise<{
28
+ configPath: string;
29
+ cliWarning?: string;
30
+ }>;
31
+ export declare function buildOpenClawModels(models: Array<{
32
+ id: string;
33
+ name: string;
34
+ }>): Array<Record<string, unknown>>;
@@ -0,0 +1,100 @@
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.OPENCLAW_PROVIDER_ID = exports.DEFAULT_OPENCLAW_MODEL = void 0;
7
+ exports.resolveOpenClawPrimaryModel = resolveOpenClawPrimaryModel;
8
+ exports.patchOpenClawConfigFile = patchOpenClawConfigFile;
9
+ exports.setupOpenClaw = setupOpenClaw;
10
+ exports.buildOpenClawModels = buildOpenClawModels;
11
+ const models_1 = require("../shared/models");
12
+ const providers_1 = require("../shared/providers");
13
+ const urls_1 = require("../shared/urls");
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const node_os_1 = __importDefault(require("node:os"));
16
+ const promises_1 = __importDefault(require("node:fs/promises"));
17
+ const fsx_1 = require("../shared/fsx");
18
+ const supported_models_1 = require("../supported-models");
19
+ // Extracted from src/commands/setup.ts (theclawbay CLI restructure).
20
+ // Code motion only: bodies are verbatim from the original file.
21
+ exports.DEFAULT_OPENCLAW_MODEL = models_1.DEFAULT_CODEX_MODEL;
22
+ exports.OPENCLAW_PROVIDER_ID = providers_1.DEFAULT_PROVIDER_ID;
23
+ function resolveOpenClawPrimaryModel(params) {
24
+ const hasStableDefault = params.models.some((entry) => entry.id === exports.DEFAULT_OPENCLAW_MODEL);
25
+ const modelId = hasStableDefault ? exports.DEFAULT_OPENCLAW_MODEL : params.model.trim() || models_1.DEFAULT_CODEX_MODEL;
26
+ return `${exports.OPENCLAW_PROVIDER_ID}/${modelId}`;
27
+ }
28
+ async function patchOpenClawConfigFile(params) {
29
+ const provider = {
30
+ baseUrl: (0, urls_1.openAiCompatibleProxyUrl)(params.backendUrl),
31
+ apiKey: params.apiKey,
32
+ api: "openai-responses",
33
+ models: buildOpenClawModels(params.models),
34
+ };
35
+ const configPath = node_path_1.default.join(node_os_1.default.homedir(), ".openclaw", "openclaw.json");
36
+ await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
37
+ let existingRaw = "";
38
+ try {
39
+ existingRaw = await promises_1.default.readFile(configPath, "utf8");
40
+ }
41
+ catch (error) {
42
+ const err = error;
43
+ if (err.code !== "ENOENT")
44
+ throw error;
45
+ }
46
+ let doc = {};
47
+ if (existingRaw.trim()) {
48
+ try {
49
+ doc = (0, fsx_1.objectRecordOr)(JSON.parse(existingRaw), {});
50
+ }
51
+ catch {
52
+ throw new Error(`invalid JSON in OpenClaw config: ${configPath}`);
53
+ }
54
+ }
55
+ const modelsRoot = (0, fsx_1.objectRecordOr)(doc.models, {});
56
+ modelsRoot.mode = "merge";
57
+ const providersRoot = (0, fsx_1.objectRecordOr)(modelsRoot.providers, {});
58
+ providersRoot[exports.OPENCLAW_PROVIDER_ID] = provider;
59
+ modelsRoot.providers = providersRoot;
60
+ doc.models = modelsRoot;
61
+ const agentsRoot = (0, fsx_1.objectRecordOr)(doc.agents, {});
62
+ const defaultsRoot = (0, fsx_1.objectRecordOr)(agentsRoot.defaults, {});
63
+ const modelRoot = (0, fsx_1.objectRecordOr)(defaultsRoot.model, {});
64
+ modelRoot.primary = resolveOpenClawPrimaryModel(params);
65
+ if (typeof defaultsRoot.thinkingDefault !== "string") {
66
+ defaultsRoot.thinkingDefault = "medium";
67
+ }
68
+ defaultsRoot.model = modelRoot;
69
+ agentsRoot.defaults = defaultsRoot;
70
+ doc.agents = agentsRoot;
71
+ await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
72
+ return configPath;
73
+ }
74
+ async function setupOpenClaw(params) {
75
+ const configPath = await patchOpenClawConfigFile(params);
76
+ return { configPath };
77
+ }
78
+ function buildOpenClawModels(models) {
79
+ const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
80
+ return models
81
+ .filter((model) => Boolean(model.id))
82
+ .map((model) => {
83
+ const pricing = supportedModelMap.get(model.id);
84
+ return {
85
+ id: model.id,
86
+ name: model.name || model.id,
87
+ api: "openai-responses",
88
+ reasoning: true,
89
+ input: ["text", "image"],
90
+ cost: {
91
+ input: pricing?.inputPer1M ?? 0,
92
+ output: pricing?.outputPer1M ?? 0,
93
+ cacheRead: pricing?.cachedInputPer1M ?? 0,
94
+ cacheWrite: pricing?.inputPer1M ?? 0,
95
+ },
96
+ contextWindow: (0, models_1.modelContextLimit)(model.id),
97
+ maxTokens: (0, models_1.modelOutputLimit)(model.id),
98
+ };
99
+ });
100
+ }
@@ -0,0 +1,12 @@
1
+ export declare const SUPPORTED_MODEL_IDS: Set<string>;
2
+ export declare function openCodeConfigCleanupTargets(): string[];
3
+ export declare function kiloConfigCleanupTargets(): string[];
4
+ export declare function isManagedOpenCodeModel(value: unknown): boolean;
5
+ export declare function isManagedOpenCodeProvider(value: unknown): boolean;
6
+ export declare function isManagedOpenCodeAnthropicProvider(value: unknown): boolean;
7
+ export declare function cleanupOpenCodeFamilyConfigs(params: {
8
+ configPaths: string[];
9
+ restoreStatePath: string;
10
+ }): Promise<boolean>;
11
+ export declare function cleanupOpenCodeConfig(): Promise<boolean>;
12
+ export declare function cleanupKiloConfig(): Promise<boolean>;