theclawbay 0.1.15 → 0.2.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.
- package/README.md +35 -45
- package/dist/commands/link.d.ts +1 -4
- package/dist/commands/link.js +13 -47
- package/dist/commands/proxy.d.ts +12 -0
- package/dist/commands/proxy.js +179 -0
- package/dist/lib/base-command.d.ts +0 -1
- package/dist/lib/base-command.js +2 -6
- package/dist/lib/config/paths.d.ts +0 -13
- package/dist/lib/config/paths.js +1 -84
- package/dist/lib/errors.d.ts +3 -0
- package/dist/lib/errors.js +10 -0
- package/dist/lib/managed/config.d.ts +1 -5
- package/dist/lib/managed/config.js +8 -47
- package/dist/lib/managed/errors.d.ts +4 -4
- package/dist/lib/managed/errors.js +4 -4
- package/dist/lib/update-check.js +10 -13
- package/package.json +6 -10
- package/dist/commands/agent.d.ts +0 -12
- package/dist/commands/agent.js +0 -545
- package/dist/commands/current.d.ts +0 -8
- package/dist/commands/current.js +0 -29
- package/dist/commands/list.d.ts +0 -8
- package/dist/commands/list.js +0 -36
- package/dist/commands/save.d.ts +0 -11
- package/dist/commands/save.js +0 -30
- package/dist/commands/usage.d.ts +0 -5
- package/dist/commands/usage.js +0 -68
- package/dist/commands/use.d.ts +0 -12
- package/dist/commands/use.js +0 -91
- package/dist/lib/accounts/account-service.d.ts +0 -21
- package/dist/lib/accounts/account-service.js +0 -201
- package/dist/lib/accounts/errors.d.ts +0 -24
- package/dist/lib/accounts/errors.js +0 -61
- package/dist/lib/accounts/index.d.ts +0 -7
- package/dist/lib/accounts/index.js +0 -24
- package/dist/lib/accounts/runtime.d.ts +0 -9
- package/dist/lib/accounts/runtime.js +0 -62
- package/dist/lib/agent/account-login.d.ts +0 -22
- package/dist/lib/agent/account-login.js +0 -1408
- package/dist/lib/agent/rollout-log.d.ts +0 -1
- package/dist/lib/agent/rollout-log.js +0 -72
- package/dist/lib/agent/round-robin.d.ts +0 -2
- package/dist/lib/agent/round-robin.js +0 -28
- package/dist/lib/agent/sync-accounts.d.ts +0 -15
- package/dist/lib/agent/sync-accounts.js +0 -199
- package/dist/lib/agent/token-count-watcher.d.ts +0 -33
- package/dist/lib/agent/token-count-watcher.js +0 -224
- package/dist/lib/agent/usage-store.d.ts +0 -23
- package/dist/lib/agent/usage-store.js +0 -125
- package/dist/lib/managed/backend-client.d.ts +0 -54
- package/dist/lib/managed/backend-client.js +0 -90
package/README.md
CHANGED
|
@@ -1,77 +1,67 @@
|
|
|
1
1
|
# The Claw Bay (`theclawbay`)
|
|
2
2
|
|
|
3
|
-
`theclawbay` is a CLI for
|
|
4
|
-
Its main job is to auto-switch between Codex accounts assigned from your web app, so users can keep working without manual login/logout churn.
|
|
3
|
+
`theclawbay` is a customer CLI for The Claw Bay.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
> Not affiliated with OpenAI or Codex. Not an official tool.
|
|
8
|
-
|
|
9
|
-
## Main Functionality
|
|
5
|
+
It now does one job:
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
- link your purchased API key
|
|
8
|
+
- run a local relay that forwards Codex/OpenAI requests to your The Claw Bay backend
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
2. Link with API key from `theclawbay.com`
|
|
15
|
-
3. Run `theclawbay agent`
|
|
16
|
-
4. Agent auto-manages account sessions and switches accounts locally when limits are reached
|
|
10
|
+
This package no longer runs local account login automation or local account switching flows.
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
> Not affiliated with OpenAI or Codex. Not an official tool.
|
|
19
13
|
|
|
20
14
|
## Requirements
|
|
21
15
|
|
|
22
16
|
- Node.js 18+
|
|
23
|
-
- Codex CLI
|
|
24
|
-
- Python 3
|
|
25
|
-
- Chrome/Chromium browser (or set `THECLAWBAY_ZENDRIVER_BROWSER_BIN`)
|
|
17
|
+
- Codex CLI installed (`@openai/codex`)
|
|
26
18
|
|
|
27
19
|
## Install
|
|
28
20
|
|
|
29
|
-
### Linux/macOS (recommended)
|
|
30
|
-
|
|
31
21
|
```sh
|
|
32
|
-
curl -fsSL https://theclawbay.com/install.sh | bash
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Run this as a regular user (not `root`/`sudo`).
|
|
36
|
-
The installer uses deterministic user-owned paths:
|
|
37
|
-
|
|
38
|
-
- npm global prefix: `~/.local/share/theclawbay/npm`
|
|
39
|
-
- Python env: `~/.local/share/theclawbay/py-venv`
|
|
40
|
-
- CLI launch path: `~/.local/bin`
|
|
41
|
-
|
|
42
|
-
### Windows (PowerShell)
|
|
43
|
-
|
|
44
|
-
```powershell
|
|
45
|
-
# Run as Administrator
|
|
46
|
-
winget install -e --id OpenJS.NodeJS.LTS
|
|
47
|
-
winget install -e --id Python.Python.3.12
|
|
48
|
-
winget install -e --id Google.Chrome
|
|
49
|
-
npm i -g @openai/codex
|
|
50
|
-
python -m pip install --user --upgrade pip zendriver
|
|
51
22
|
npm i -g theclawbay
|
|
52
23
|
```
|
|
53
24
|
|
|
54
|
-
## Link
|
|
25
|
+
## Link
|
|
55
26
|
|
|
56
|
-
|
|
57
|
-
2. Link your machine:
|
|
27
|
+
Use your purchased API key from your dashboard:
|
|
58
28
|
|
|
59
29
|
```sh
|
|
60
30
|
theclawbay link --api-key <apiKey>
|
|
61
31
|
```
|
|
62
32
|
|
|
63
|
-
|
|
33
|
+
This defaults to `https://theclawbay.com`.
|
|
34
|
+
|
|
35
|
+
If you operate a custom backend, pass it explicitly:
|
|
64
36
|
|
|
65
37
|
```sh
|
|
66
|
-
theclawbay
|
|
38
|
+
theclawbay link --api-key <apiKey> --backend https://your-domain.com
|
|
67
39
|
```
|
|
68
40
|
|
|
69
|
-
|
|
41
|
+
## Run Relay
|
|
70
42
|
|
|
71
43
|
```sh
|
|
72
|
-
theclawbay
|
|
44
|
+
theclawbay proxy
|
|
73
45
|
```
|
|
74
46
|
|
|
75
|
-
|
|
47
|
+
By default this starts a local relay on `http://127.0.0.1:2455` and forwards to:
|
|
48
|
+
|
|
49
|
+
- `https://theclawbay.com/api/codex-auth/v1/proxy/...`
|
|
50
|
+
|
|
51
|
+
The command prints a ready-to-paste Codex provider snippet.
|
|
52
|
+
|
|
53
|
+
## Common Flags
|
|
54
|
+
|
|
55
|
+
`theclawbay proxy` supports:
|
|
56
|
+
|
|
57
|
+
- `--host` local bind host (default `127.0.0.1`)
|
|
58
|
+
- `--port` local bind port (default `2455`)
|
|
59
|
+
- `--backend` override backend URL from linked config
|
|
60
|
+
- `--api-key` override API key from linked config
|
|
61
|
+
- `--base-path` override server proxy path (default `/api/codex-auth/v1/proxy`)
|
|
62
|
+
|
|
63
|
+
## Notes
|
|
76
64
|
|
|
77
|
-
- End
|
|
65
|
+
- End users only need API keys. They do not need upstream account credentials.
|
|
66
|
+
- Keep your backend on HTTPS for WAN usage.
|
|
67
|
+
- Linked config is stored at `~/.codex/theclawbay.managed.json`.
|
package/dist/commands/link.d.ts
CHANGED
|
@@ -3,10 +3,7 @@ export default class LinkCommand extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
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
|
|
7
|
-
"seat-id": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
-
"device-token": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
-
"poll-interval-ms": import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
"api-key": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
7
|
};
|
|
11
8
|
run(): Promise<void>;
|
|
12
9
|
}
|
package/dist/commands/link.js
CHANGED
|
@@ -5,69 +5,35 @@ const base_command_1 = require("../lib/base-command");
|
|
|
5
5
|
const config_1 = require("../lib/managed/config");
|
|
6
6
|
const paths_1 = require("../lib/config/paths");
|
|
7
7
|
const api_key_1 = require("../lib/managed/api-key");
|
|
8
|
+
const DEFAULT_BACKEND_URL = "https://theclawbay.com";
|
|
8
9
|
class LinkCommand extends base_command_1.BaseCommand {
|
|
9
10
|
async run() {
|
|
10
11
|
await this.runSafe(async () => {
|
|
11
12
|
const { flags } = await this.parse(LinkCommand);
|
|
12
13
|
const apiKey = flags["api-key"];
|
|
13
|
-
const seatId = flags["seat-id"];
|
|
14
|
-
const deviceToken = flags["device-token"];
|
|
15
|
-
const pollIntervalMs = flags["poll-interval-ms"];
|
|
16
14
|
const backendUrlFlag = flags.backend;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
backendUrl,
|
|
24
|
-
authType: "apiKey",
|
|
25
|
-
apiKey,
|
|
26
|
-
pollIntervalMs,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
if (!backendUrlFlag) {
|
|
31
|
-
throw new Error('Backend URL is required for seat mode. Pass "--backend https://YOUR_ADMIN_APP".');
|
|
32
|
-
}
|
|
33
|
-
await (0, config_1.writeManagedConfig)({
|
|
34
|
-
backendUrl: backendUrlFlag,
|
|
35
|
-
authType: "seat",
|
|
36
|
-
seatId,
|
|
37
|
-
deviceToken,
|
|
38
|
-
pollIntervalMs,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
15
|
+
const inferredBackendUrl = (0, api_key_1.tryInferBackendUrlFromApiKey)(apiKey);
|
|
16
|
+
const backendUrl = backendUrlFlag ?? inferredBackendUrl ?? DEFAULT_BACKEND_URL;
|
|
17
|
+
await (0, config_1.writeManagedConfig)({
|
|
18
|
+
backendUrl,
|
|
19
|
+
apiKey,
|
|
20
|
+
});
|
|
41
21
|
this.log(`Linked. Managed config written to ${paths_1.managedConfigPath}`);
|
|
42
|
-
this.log(`
|
|
22
|
+
this.log(`Backend: ${backendUrl}`);
|
|
23
|
+
this.log(`Run "theclawbay proxy" to start the local relay.`);
|
|
43
24
|
});
|
|
44
25
|
}
|
|
45
26
|
}
|
|
46
|
-
LinkCommand.description = "Link this
|
|
27
|
+
LinkCommand.description = "Link this machine to your The Claw Bay API backend using an API key";
|
|
47
28
|
LinkCommand.flags = {
|
|
48
29
|
backend: core_1.Flags.string({
|
|
49
30
|
required: false,
|
|
50
|
-
description: "Backend base URL (
|
|
31
|
+
description: "Backend base URL override (default: https://theclawbay.com)",
|
|
51
32
|
}),
|
|
52
33
|
"api-key": core_1.Flags.string({
|
|
53
|
-
required:
|
|
34
|
+
required: true,
|
|
54
35
|
aliases: ["apiKey"],
|
|
55
|
-
description: "API key issued by your
|
|
56
|
-
}),
|
|
57
|
-
"seat-id": core_1.Flags.string({
|
|
58
|
-
required: false,
|
|
59
|
-
aliases: ["seatId"],
|
|
60
|
-
description: "[legacy] Seat ID issued by your admin web app",
|
|
61
|
-
}),
|
|
62
|
-
"device-token": core_1.Flags.string({
|
|
63
|
-
required: false,
|
|
64
|
-
aliases: ["deviceToken"],
|
|
65
|
-
description: "[legacy] Device token issued by your admin web app",
|
|
66
|
-
}),
|
|
67
|
-
"poll-interval-ms": core_1.Flags.integer({
|
|
68
|
-
required: false,
|
|
69
|
-
aliases: ["pollIntervalMs"],
|
|
70
|
-
description: "Override backend poll interval (ms)",
|
|
36
|
+
description: "API key issued by your The Claw Bay dashboard",
|
|
71
37
|
}),
|
|
72
38
|
};
|
|
73
39
|
exports.default = LinkCommand;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from "../lib/base-command";
|
|
2
|
+
export default class ProxyCommand extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
host: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
port: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
backend: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
"api-key": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
"base-path": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
7
|
+
const node_stream_1 = require("node:stream");
|
|
8
|
+
const core_1 = require("@oclif/core");
|
|
9
|
+
const base_command_1 = require("../lib/base-command");
|
|
10
|
+
const config_1 = require("../lib/managed/config");
|
|
11
|
+
function trimTrailingSlash(value) {
|
|
12
|
+
return value.replace(/\/+$/g, "");
|
|
13
|
+
}
|
|
14
|
+
function ensureLeadingSlash(value) {
|
|
15
|
+
return value.startsWith("/") ? value : `/${value}`;
|
|
16
|
+
}
|
|
17
|
+
function joinUrl(base, pathname) {
|
|
18
|
+
const url = new URL(base);
|
|
19
|
+
url.pathname = `${url.pathname.replace(/\/+$/g, "")}${pathname.startsWith("/") ? "" : "/"}${pathname}`;
|
|
20
|
+
return url.toString();
|
|
21
|
+
}
|
|
22
|
+
function normalizeUrl(raw, label) {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = new URL(raw.trim());
|
|
25
|
+
return trimTrailingSlash(parsed.toString());
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error(`invalid ${label} URL: ${raw}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function headersFromIncoming(source) {
|
|
32
|
+
const headers = new Headers();
|
|
33
|
+
for (const [key, value] of Object.entries(source)) {
|
|
34
|
+
if (!value)
|
|
35
|
+
continue;
|
|
36
|
+
const lower = key.toLowerCase();
|
|
37
|
+
if (lower === "host" || lower === "content-length" || lower === "authorization" || lower === "connection") {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
headers.set(key, value.join(", "));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
headers.set(key, value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return headers;
|
|
48
|
+
}
|
|
49
|
+
async function readIncomingBody(req) {
|
|
50
|
+
if (!req.method || req.method === "GET" || req.method === "HEAD")
|
|
51
|
+
return null;
|
|
52
|
+
const chunks = [];
|
|
53
|
+
for await (const chunk of req) {
|
|
54
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
55
|
+
}
|
|
56
|
+
if (!chunks.length)
|
|
57
|
+
return null;
|
|
58
|
+
return Buffer.concat(chunks);
|
|
59
|
+
}
|
|
60
|
+
function bufferToArrayBuffer(buffer) {
|
|
61
|
+
const bytes = new Uint8Array(buffer.length);
|
|
62
|
+
bytes.set(buffer);
|
|
63
|
+
return bytes.buffer;
|
|
64
|
+
}
|
|
65
|
+
function writeUpstreamHeaders(res, source) {
|
|
66
|
+
source.forEach((value, key) => {
|
|
67
|
+
const lower = key.toLowerCase();
|
|
68
|
+
if (lower === "connection" || lower === "transfer-encoding" || lower === "keep-alive")
|
|
69
|
+
return;
|
|
70
|
+
res.setHeader(key, value);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
class ProxyCommand extends base_command_1.BaseCommand {
|
|
74
|
+
async run() {
|
|
75
|
+
await this.runSafe(async () => {
|
|
76
|
+
const { flags } = await this.parse(ProxyCommand);
|
|
77
|
+
const managed = await (0, config_1.readManagedConfig)();
|
|
78
|
+
if (!managed.apiKey?.trim()) {
|
|
79
|
+
throw new Error('API key auth is required. Re-link using "theclawbay link --api-key <key>".');
|
|
80
|
+
}
|
|
81
|
+
const backendUrl = normalizeUrl(flags.backend ?? managed.backendUrl, "--backend");
|
|
82
|
+
const apiKey = (flags["api-key"] ?? managed.apiKey ?? "").trim();
|
|
83
|
+
if (!apiKey) {
|
|
84
|
+
throw new Error("API key is required for WAN proxy mode.");
|
|
85
|
+
}
|
|
86
|
+
const basePath = ensureLeadingSlash(flags["base-path"].trim()).replace(/\/+$/g, "");
|
|
87
|
+
const proxyBase = `${trimTrailingSlash(backendUrl)}${basePath}`;
|
|
88
|
+
const server = node_http_1.default.createServer(async (req, res) => {
|
|
89
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
90
|
+
const incomingUrl = new URL(req.url || "/", "http://local-proxy.invalid");
|
|
91
|
+
const routePath = incomingUrl.pathname.replace(/^\/+/, "");
|
|
92
|
+
const upstreamUrl = joinUrl(proxyBase, routePath);
|
|
93
|
+
const urlWithQuery = `${upstreamUrl}${incomingUrl.search || ""}`;
|
|
94
|
+
try {
|
|
95
|
+
const headers = headersFromIncoming(req.headers);
|
|
96
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
97
|
+
const bodyBuffer = await readIncomingBody(req);
|
|
98
|
+
const body = bodyBuffer ? bufferToArrayBuffer(bodyBuffer) : undefined;
|
|
99
|
+
const upstream = await fetch(urlWithQuery, {
|
|
100
|
+
method,
|
|
101
|
+
headers,
|
|
102
|
+
body,
|
|
103
|
+
redirect: "manual",
|
|
104
|
+
});
|
|
105
|
+
res.statusCode = upstream.status;
|
|
106
|
+
res.statusMessage = upstream.statusText;
|
|
107
|
+
writeUpstreamHeaders(res, upstream.headers);
|
|
108
|
+
if (!upstream.body || method === "HEAD") {
|
|
109
|
+
res.end();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const stream = node_stream_1.Readable.fromWeb(upstream.body);
|
|
113
|
+
stream.on("error", () => {
|
|
114
|
+
res.destroy();
|
|
115
|
+
});
|
|
116
|
+
stream.pipe(res);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
res.statusCode = 502;
|
|
121
|
+
res.setHeader("Content-Type", "application/json");
|
|
122
|
+
res.end(JSON.stringify({ error: `proxy relay failed: ${message}` }));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
await new Promise((resolve, reject) => {
|
|
126
|
+
server.once("error", reject);
|
|
127
|
+
server.listen(flags.port, flags.host, () => {
|
|
128
|
+
server.off("error", reject);
|
|
129
|
+
resolve();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
const localBase = `http://${flags.host}:${flags.port}`;
|
|
133
|
+
this.log(`theclawbay WAN relay listening at ${localBase}`);
|
|
134
|
+
this.log(`Forwarding to ${proxyBase}`);
|
|
135
|
+
this.log("");
|
|
136
|
+
this.log("Codex CLI config snippet:");
|
|
137
|
+
this.log('model_provider = "theclawbay-wan"');
|
|
138
|
+
this.log("");
|
|
139
|
+
this.log("[model_providers.theclawbay-wan]");
|
|
140
|
+
this.log('name = "OpenAI"');
|
|
141
|
+
this.log(`base_url = "${localBase}/backend-api/codex"`);
|
|
142
|
+
this.log('wire_api = "responses"');
|
|
143
|
+
this.log(`chatgpt_base_url = "${localBase}"`);
|
|
144
|
+
this.log("requires_openai_auth = true");
|
|
145
|
+
await new Promise((resolve) => {
|
|
146
|
+
const stop = () => {
|
|
147
|
+
server.close(() => resolve());
|
|
148
|
+
};
|
|
149
|
+
process.once("SIGINT", stop);
|
|
150
|
+
process.once("SIGTERM", stop);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
ProxyCommand.description = "Run a local relay that forwards Codex/OpenAI requests to your The Claw Bay backend using API-key auth";
|
|
156
|
+
ProxyCommand.flags = {
|
|
157
|
+
host: core_1.Flags.string({
|
|
158
|
+
default: "127.0.0.1",
|
|
159
|
+
description: "Local bind host",
|
|
160
|
+
}),
|
|
161
|
+
port: core_1.Flags.integer({
|
|
162
|
+
default: 2455,
|
|
163
|
+
description: "Local bind port",
|
|
164
|
+
}),
|
|
165
|
+
backend: core_1.Flags.string({
|
|
166
|
+
required: false,
|
|
167
|
+
description: "Override backend URL (defaults to linked managed backend)",
|
|
168
|
+
}),
|
|
169
|
+
"api-key": core_1.Flags.string({
|
|
170
|
+
required: false,
|
|
171
|
+
aliases: ["apiKey"],
|
|
172
|
+
description: "Override API key (defaults to linked managed API key)",
|
|
173
|
+
}),
|
|
174
|
+
"base-path": core_1.Flags.string({
|
|
175
|
+
default: "/api/codex-auth/v1/proxy",
|
|
176
|
+
description: "Server proxy base path",
|
|
177
|
+
}),
|
|
178
|
+
};
|
|
179
|
+
exports.default = ProxyCommand;
|
package/dist/lib/base-command.js
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseCommand = void 0;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
|
-
const
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
6
|
const update_check_1 = require("./update-check");
|
|
7
7
|
class BaseCommand extends core_1.Command {
|
|
8
|
-
constructor() {
|
|
9
|
-
super(...arguments);
|
|
10
|
-
this.accounts = accounts_1.accountService;
|
|
11
|
-
}
|
|
12
8
|
async runSafe(action) {
|
|
13
9
|
try {
|
|
14
10
|
await (0, update_check_1.maybeNotifyUpdate)({
|
|
@@ -23,7 +19,7 @@ class BaseCommand extends core_1.Command {
|
|
|
23
19
|
}
|
|
24
20
|
}
|
|
25
21
|
handleError(error) {
|
|
26
|
-
if (error instanceof
|
|
22
|
+
if (error instanceof errors_1.ClawBayError) {
|
|
27
23
|
this.error(error.message);
|
|
28
24
|
}
|
|
29
25
|
if (error instanceof Error) {
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
export declare const codexDir: string;
|
|
2
|
-
export declare const accountsDir: string;
|
|
3
|
-
export declare const authPath: string;
|
|
4
|
-
export declare const currentNamePath: string;
|
|
5
|
-
export declare const sessionsDir: string;
|
|
6
2
|
export declare const managedConfigPath: string;
|
|
7
|
-
export declare const managedAccountsStatePath: string;
|
|
8
|
-
export declare const usageStatePath: string;
|
|
9
|
-
export declare const usageHistoryPath: string;
|
|
10
3
|
export declare const updateCheckStatePath: string;
|
|
11
4
|
export declare const legacyManagedConfigPathClayBay: string;
|
|
12
5
|
export declare const legacyManagedConfigPathCodexAuth: string;
|
|
13
|
-
export declare function resolveOpenClawStateDir(): string;
|
|
14
|
-
export declare function resolveOpenClawAgentDir(): string;
|
|
15
|
-
export declare function resolveOpenClawAuthStorePath(): string;
|
|
16
|
-
export declare function resolveOpenClawLegacyAuthPath(): string;
|
|
17
|
-
export declare function resolveOpenClawAccountsDir(): string;
|
|
18
|
-
export declare function resolveOpenClawCurrentNamePath(): string;
|
package/dist/lib/config/paths.js
CHANGED
|
@@ -3,94 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.legacyManagedConfigPathCodexAuth = exports.legacyManagedConfigPathClayBay = exports.updateCheckStatePath = exports.
|
|
7
|
-
exports.resolveOpenClawStateDir = resolveOpenClawStateDir;
|
|
8
|
-
exports.resolveOpenClawAgentDir = resolveOpenClawAgentDir;
|
|
9
|
-
exports.resolveOpenClawAuthStorePath = resolveOpenClawAuthStorePath;
|
|
10
|
-
exports.resolveOpenClawLegacyAuthPath = resolveOpenClawLegacyAuthPath;
|
|
11
|
-
exports.resolveOpenClawAccountsDir = resolveOpenClawAccountsDir;
|
|
12
|
-
exports.resolveOpenClawCurrentNamePath = resolveOpenClawCurrentNamePath;
|
|
13
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
6
|
+
exports.legacyManagedConfigPathCodexAuth = exports.legacyManagedConfigPathClayBay = exports.updateCheckStatePath = exports.managedConfigPath = exports.codexDir = void 0;
|
|
14
7
|
const node_os_1 = __importDefault(require("node:os"));
|
|
15
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
9
|
exports.codexDir = node_path_1.default.join(node_os_1.default.homedir(), ".codex");
|
|
17
|
-
exports.accountsDir = node_path_1.default.join(exports.codexDir, "accounts");
|
|
18
|
-
exports.authPath = node_path_1.default.join(exports.codexDir, "auth.json");
|
|
19
|
-
exports.currentNamePath = node_path_1.default.join(exports.codexDir, "current");
|
|
20
|
-
exports.sessionsDir = node_path_1.default.join(exports.codexDir, "sessions");
|
|
21
10
|
exports.managedConfigPath = node_path_1.default.join(exports.codexDir, "theclawbay.managed.json");
|
|
22
|
-
exports.managedAccountsStatePath = node_path_1.default.join(exports.codexDir, "theclawbay.accounts-state.json");
|
|
23
|
-
exports.usageStatePath = node_path_1.default.join(exports.codexDir, "theclawbay.usage-state.json");
|
|
24
|
-
exports.usageHistoryPath = node_path_1.default.join(exports.codexDir, "theclawbay.usage-history.jsonl");
|
|
25
11
|
exports.updateCheckStatePath = node_path_1.default.join(exports.codexDir, "theclawbay.update-check.json");
|
|
26
12
|
exports.legacyManagedConfigPathClayBay = node_path_1.default.join(exports.codexDir, "theclaybay.managed.json");
|
|
27
13
|
exports.legacyManagedConfigPathCodexAuth = node_path_1.default.join(exports.codexDir, "codex-auth.managed.json");
|
|
28
|
-
const OPENCLAW_LEGACY_STATE_DIRNAMES = [".clawdbot", ".moltbot", ".moldbot"];
|
|
29
|
-
function normalizeEnvPath(rawValue) {
|
|
30
|
-
if (typeof rawValue !== "string")
|
|
31
|
-
return null;
|
|
32
|
-
const trimmed = rawValue.trim();
|
|
33
|
-
return trimmed.length ? trimmed : null;
|
|
34
|
-
}
|
|
35
|
-
function resolveEffectiveHomeDir() {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
const explicitHome = normalizeEnvPath(process.env.OPENCLAW_HOME);
|
|
38
|
-
const systemHome = (_b = (_a = normalizeEnvPath(process.env.HOME)) !== null && _a !== void 0 ? _a : normalizeEnvPath(process.env.USERPROFILE)) !== null && _b !== void 0 ? _b : node_os_1.default.homedir();
|
|
39
|
-
if (!explicitHome) {
|
|
40
|
-
return node_path_1.default.resolve(systemHome);
|
|
41
|
-
}
|
|
42
|
-
if (explicitHome === "~" || explicitHome.startsWith("~/") || explicitHome.startsWith("~\\")) {
|
|
43
|
-
return node_path_1.default.resolve(explicitHome.replace(/^~(?=$|[\\/])/, systemHome));
|
|
44
|
-
}
|
|
45
|
-
return node_path_1.default.resolve(explicitHome);
|
|
46
|
-
}
|
|
47
|
-
function resolveUserPath(rawPath) {
|
|
48
|
-
const trimmed = rawPath.trim();
|
|
49
|
-
if (trimmed.startsWith("~")) {
|
|
50
|
-
return node_path_1.default.resolve(trimmed.replace(/^~(?=$|[\\/])/, resolveEffectiveHomeDir()));
|
|
51
|
-
}
|
|
52
|
-
return node_path_1.default.resolve(trimmed);
|
|
53
|
-
}
|
|
54
|
-
function pathExistsSync(targetPath) {
|
|
55
|
-
try {
|
|
56
|
-
return node_fs_1.default.existsSync(targetPath);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function resolveOpenClawStateDir() {
|
|
63
|
-
var _a;
|
|
64
|
-
const override = (_a = normalizeEnvPath(process.env.OPENCLAW_STATE_DIR)) !== null && _a !== void 0 ? _a : normalizeEnvPath(process.env.CLAWDBOT_STATE_DIR);
|
|
65
|
-
if (override)
|
|
66
|
-
return resolveUserPath(override);
|
|
67
|
-
const home = resolveEffectiveHomeDir();
|
|
68
|
-
const openClawDir = node_path_1.default.join(home, ".openclaw");
|
|
69
|
-
if (pathExistsSync(openClawDir))
|
|
70
|
-
return openClawDir;
|
|
71
|
-
for (const legacyDirName of OPENCLAW_LEGACY_STATE_DIRNAMES) {
|
|
72
|
-
const legacyDir = node_path_1.default.join(home, legacyDirName);
|
|
73
|
-
if (pathExistsSync(legacyDir))
|
|
74
|
-
return legacyDir;
|
|
75
|
-
}
|
|
76
|
-
return openClawDir;
|
|
77
|
-
}
|
|
78
|
-
function resolveOpenClawAgentDir() {
|
|
79
|
-
var _a;
|
|
80
|
-
const override = (_a = normalizeEnvPath(process.env.OPENCLAW_AGENT_DIR)) !== null && _a !== void 0 ? _a : normalizeEnvPath(process.env.PI_CODING_AGENT_DIR);
|
|
81
|
-
if (override)
|
|
82
|
-
return resolveUserPath(override);
|
|
83
|
-
return node_path_1.default.join(resolveOpenClawStateDir(), "agents", "default", "agent");
|
|
84
|
-
}
|
|
85
|
-
function resolveOpenClawAuthStorePath() {
|
|
86
|
-
return node_path_1.default.join(resolveOpenClawAgentDir(), "auth-profiles.json");
|
|
87
|
-
}
|
|
88
|
-
function resolveOpenClawLegacyAuthPath() {
|
|
89
|
-
return node_path_1.default.join(resolveOpenClawAgentDir(), "auth.json");
|
|
90
|
-
}
|
|
91
|
-
function resolveOpenClawAccountsDir() {
|
|
92
|
-
return node_path_1.default.join(resolveOpenClawStateDir(), "accounts");
|
|
93
|
-
}
|
|
94
|
-
function resolveOpenClawCurrentNamePath() {
|
|
95
|
-
return node_path_1.default.join(resolveOpenClawStateDir(), "current");
|
|
96
|
-
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClawBayError = void 0;
|
|
4
|
+
class ClawBayError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ClawBayError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.ClawBayError = ClawBayError;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
export type ManagedConfig = {
|
|
2
2
|
backendUrl: string;
|
|
3
|
-
|
|
4
|
-
seatId?: string;
|
|
5
|
-
deviceToken?: string;
|
|
6
|
-
apiKey?: string;
|
|
7
|
-
pollIntervalMs?: number;
|
|
3
|
+
apiKey: string;
|
|
8
4
|
};
|
|
9
5
|
export declare function readManagedConfig(): Promise<ManagedConfig>;
|
|
10
6
|
export declare function writeManagedConfig(config: ManagedConfig): Promise<void>;
|
|
@@ -72,70 +72,31 @@ async function readManagedConfig() {
|
|
|
72
72
|
throw new errors_1.ManagedConfigInvalidError("backendUrl is required");
|
|
73
73
|
}
|
|
74
74
|
const backendUrl = normalizeAndValidateBackendUrl(obj.backendUrl);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new errors_1.ManagedConfigInvalidError('authType must be "seat" or "apiKey"');
|
|
75
|
+
if (obj.authType === "seat") {
|
|
76
|
+
throw new errors_1.ManagedConfigInvalidError('legacy seat-mode config is no longer supported; run "theclawbay link --api-key <key>" to relink');
|
|
78
77
|
}
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
throw new errors_1.ManagedConfigInvalidError("seatId is required for authType=seat");
|
|
82
|
-
}
|
|
83
|
-
if (typeof obj.deviceToken !== "string" || !obj.deviceToken.trim()) {
|
|
84
|
-
throw new errors_1.ManagedConfigInvalidError("deviceToken is required for authType=seat");
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
if (typeof obj.apiKey !== "string" || !obj.apiKey.trim()) {
|
|
89
|
-
throw new errors_1.ManagedConfigInvalidError("apiKey is required for authType=apiKey");
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
const pollIntervalMs = obj.pollIntervalMs === undefined ? undefined : Number(obj.pollIntervalMs);
|
|
93
|
-
if (pollIntervalMs !== undefined && (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 250)) {
|
|
94
|
-
throw new errors_1.ManagedConfigInvalidError("pollIntervalMs must be >= 250");
|
|
78
|
+
if (typeof obj.apiKey !== "string" || !obj.apiKey.trim()) {
|
|
79
|
+
throw new errors_1.ManagedConfigInvalidError("apiKey is required");
|
|
95
80
|
}
|
|
96
81
|
if (loadedFromPath !== paths_1.managedConfigPath) {
|
|
97
82
|
// Best-effort migration to the new config filename.
|
|
98
83
|
await promises_1.default.mkdir(paths_1.codexDir, { recursive: true });
|
|
99
84
|
await promises_1.default.writeFile(paths_1.managedConfigPath, raw, "utf8").catch(() => { });
|
|
100
85
|
}
|
|
101
|
-
return {
|
|
102
|
-
backendUrl,
|
|
103
|
-
authType,
|
|
104
|
-
seatId: obj.seatId,
|
|
105
|
-
deviceToken: obj.deviceToken,
|
|
106
|
-
apiKey: obj.apiKey,
|
|
107
|
-
pollIntervalMs,
|
|
108
|
-
};
|
|
86
|
+
return { backendUrl, apiKey: obj.apiKey.trim() };
|
|
109
87
|
}
|
|
110
88
|
async function writeManagedConfig(config) {
|
|
111
89
|
if (typeof config.backendUrl !== "string" || !config.backendUrl.trim()) {
|
|
112
90
|
throw new errors_1.ManagedConfigInvalidError("backendUrl is required");
|
|
113
91
|
}
|
|
114
92
|
const backendUrl = normalizeAndValidateBackendUrl(config.backendUrl);
|
|
115
|
-
if (config.
|
|
116
|
-
throw new errors_1.ManagedConfigInvalidError(
|
|
117
|
-
}
|
|
118
|
-
if (config.authType === "seat") {
|
|
119
|
-
if (typeof config.seatId !== "string" || !config.seatId.trim()) {
|
|
120
|
-
throw new errors_1.ManagedConfigInvalidError("seatId is required for authType=seat");
|
|
121
|
-
}
|
|
122
|
-
if (typeof config.deviceToken !== "string" || !config.deviceToken.trim()) {
|
|
123
|
-
throw new errors_1.ManagedConfigInvalidError("deviceToken is required for authType=seat");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
if (typeof config.apiKey !== "string" || !config.apiKey.trim()) {
|
|
128
|
-
throw new errors_1.ManagedConfigInvalidError("apiKey is required for authType=apiKey");
|
|
129
|
-
}
|
|
93
|
+
if (typeof config.apiKey !== "string" || !config.apiKey.trim()) {
|
|
94
|
+
throw new errors_1.ManagedConfigInvalidError("apiKey is required");
|
|
130
95
|
}
|
|
131
96
|
await promises_1.default.mkdir(paths_1.codexDir, { recursive: true });
|
|
132
97
|
const contents = JSON.stringify({
|
|
133
98
|
backendUrl,
|
|
134
|
-
|
|
135
|
-
seatId: config.seatId,
|
|
136
|
-
deviceToken: config.deviceToken,
|
|
137
|
-
apiKey: config.apiKey,
|
|
138
|
-
pollIntervalMs: config.pollIntervalMs,
|
|
99
|
+
apiKey: config.apiKey.trim(),
|
|
139
100
|
}, null, 2);
|
|
140
101
|
await promises_1.default.writeFile(paths_1.managedConfigPath, `${contents}\n`, "utf8");
|
|
141
102
|
}
|