@pix3/agent-bridge 0.2.0 → 0.2.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,38 +1,47 @@
1
- {
2
- "name": "@pix3/agent-bridge",
3
- "version": "0.2.0",
4
- "description": "Local bridge for the Pix3 editor's in-editor agent: a Claude Agent SDK (Pro/MAX subscription) lane plus a credential-injecting proxy for OpenAI / Anthropic / OpenCode Zen / custom OpenAI-compatible providers. Keys stay on your machine.",
5
- "type": "module",
6
- "bin": {
7
- "pix3-agent-bridge": "src/index.ts"
8
- },
9
- "files": [
10
- "src",
11
- "tsconfig.json",
12
- "README.md"
13
- ],
14
- "engines": {
15
- "node": ">=24"
16
- },
17
- "scripts": {
18
- "start": "node src/index.ts",
19
- "type-check": "tsc --noEmit"
20
- },
21
- "keywords": [
22
- "pix3",
23
- "llm",
24
- "proxy",
25
- "claude",
26
- "openai",
27
- "agent"
28
- ],
29
- "license": "MIT",
30
- "dependencies": {
31
- "@anthropic-ai/claude-agent-sdk": "^0.3.211",
32
- "@modelcontextprotocol/sdk": "^1.29.0"
33
- },
34
- "devDependencies": {
35
- "@types/node": "^24.5.2",
36
- "typescript": "~5.8.3"
37
- }
38
- }
1
+ {
2
+ "name": "@pix3/agent-bridge",
3
+ "version": "0.2.1",
4
+ "description": "Local bridge for the Pix3 editor's in-editor agent: a Claude Agent SDK (Pro/MAX subscription) lane plus a credential-injecting proxy for OpenAI / Anthropic / OpenCode Zen / custom OpenAI-compatible providers. Keys stay on your machine.",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/pix3dev/pix3.git",
9
+ "directory": "tools/pix3-agent-bridge"
10
+ },
11
+ "bin": {
12
+ "pix3-agent-bridge": "dist/index.js"
13
+ },
14
+ "main": "dist/index.js",
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "tsconfig.json",
19
+ "README.md"
20
+ ],
21
+ "engines": {
22
+ "node": ">=24"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.build.json",
26
+ "start": "node src/index.ts",
27
+ "type-check": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "pix3",
32
+ "llm",
33
+ "proxy",
34
+ "claude",
35
+ "openai",
36
+ "agent"
37
+ ],
38
+ "license": "MIT",
39
+ "dependencies": {
40
+ "@anthropic-ai/claude-agent-sdk": "^0.3.211",
41
+ "@modelcontextprotocol/sdk": "^1.29.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^24.5.2",
45
+ "typescript": "~5.8.3"
46
+ }
47
+ }
package/src/cli.ts CHANGED
@@ -1,195 +1,195 @@
1
- /**
2
- * CLI for Pix3AgentBridge. Running the bridge with no subcommand starts the server (see index.ts);
3
- * the `provider` subcommands below manage the provider table in `~/.pix3/agent-bridge.json` — adding
4
- * a key, enabling/disabling, or removing a provider — without touching a running server (the editor
5
- * re-reads availability from `GET /v1/providers` on its next probe).
6
- *
7
- * pix3-agent-bridge provider list
8
- * pix3-agent-bridge provider add openai --key sk-...
9
- * pix3-agent-bridge provider add anthropic --key sk-ant-...
10
- * pix3-agent-bridge provider add opencode-zen --key ...
11
- * pix3-agent-bridge provider add my-router --base-url https://openrouter.ai/api/v1 --key ... --kind openai
12
- * pix3-agent-bridge provider enable|disable|remove <id>
13
- * pix3-agent-bridge provider set-key <id> <key>
14
- */
15
-
16
- import {
17
- PROVIDER_PRESETS,
18
- RESERVED_PROVIDER_IDS,
19
- configPath,
20
- loadConfig,
21
- saveConfig,
22
- type ProviderConfig,
23
- } from './config.ts';
24
-
25
- /** Parse `--flag value` / `--flag=value` pairs, returning [positional[], flags]. */
26
- const parseArgs = (args: string[]): { positional: string[]; flags: Record<string, string> } => {
27
- const positional: string[] = [];
28
- const flags: Record<string, string> = {};
29
- for (let i = 0; i < args.length; i += 1) {
30
- const arg = args[i];
31
- if (arg.startsWith('--')) {
32
- const eq = arg.indexOf('=');
33
- if (eq >= 0) {
34
- flags[arg.slice(2, eq)] = arg.slice(eq + 1);
35
- } else {
36
- flags[arg.slice(2)] = args[i + 1] ?? '';
37
- i += 1;
38
- }
39
- } else {
40
- positional.push(arg);
41
- }
42
- }
43
- return { positional, flags };
44
- };
45
-
46
- const maskKey = (key: string): string =>
47
- key.length <= 8 ? (key ? '••••' : '(none)') : `${key.slice(0, 4)}…${key.slice(-4)}`;
48
-
49
- const printList = (providers: Record<string, ProviderConfig>): void => {
50
- const ids = Object.keys(providers);
51
- if (ids.length === 0) {
52
- console.log('No providers configured. Add one, e.g.: pix3-agent-bridge provider add openai --key sk-...');
53
- console.log(`\nBuilt-in presets: ${Object.keys(PROVIDER_PRESETS).join(', ')}`);
54
- return;
55
- }
56
- console.log(`Providers (from ${configPath()}):\n`);
57
- for (const id of ids) {
58
- const p = providers[id];
59
- const state = p.enabled ? 'enabled' : 'disabled';
60
- const key = p.apiKey ? maskKey(p.apiKey) : '(no key — add one with `provider set-key`)';
61
- console.log(` ${id}`);
62
- console.log(` kind: ${p.kind}`);
63
- console.log(` label: ${p.label}`);
64
- console.log(` baseUrl: ${p.baseUrl}`);
65
- console.log(` key: ${key}`);
66
- console.log(` status: ${state}`);
67
- console.log('');
68
- }
69
- };
70
-
71
- const usage = (): void => {
72
- console.log(
73
- [
74
- 'Usage:',
75
- ' pix3-agent-bridge start the bridge server',
76
- ' pix3-agent-bridge provider list',
77
- ' pix3-agent-bridge provider add <id> [--key <k>] [--base-url <url>] [--kind openai|anthropic] [--label <l>]',
78
- ' pix3-agent-bridge provider set-key <id> <key>',
79
- ' pix3-agent-bridge provider enable <id>',
80
- ' pix3-agent-bridge provider disable <id>',
81
- ' pix3-agent-bridge provider remove <id>',
82
- '',
83
- `Built-in presets (add with just --key): ${Object.keys(PROVIDER_PRESETS).join(', ')}`,
84
- ].join('\n')
85
- );
86
- };
87
-
88
- const addProvider = (
89
- config: ReturnType<typeof loadConfig>,
90
- id: string,
91
- flags: Record<string, string>
92
- ): void => {
93
- if ((RESERVED_PROVIDER_IDS as readonly string[]).includes(id)) {
94
- console.error(
95
- `Provider id "${id}" is reserved by the bridge and cannot be added. Pick a different id.`
96
- );
97
- process.exitCode = 1;
98
- return;
99
- }
100
- const preset = PROVIDER_PRESETS[id];
101
- const baseUrl = (flags['base-url'] ?? preset?.baseUrl ?? '').replace(/\/$/, '');
102
- if (!baseUrl) {
103
- console.error(
104
- `Provider "${id}" is not a built-in preset, so it needs an explicit --base-url ` +
105
- `(e.g. --base-url https://openrouter.ai/api/v1).`
106
- );
107
- process.exitCode = 1;
108
- return;
109
- }
110
- const kindFlag = flags.kind === 'anthropic' ? 'anthropic' : flags.kind === 'openai' ? 'openai' : undefined;
111
- const existing = config.providers[id];
112
- config.providers[id] = {
113
- kind: kindFlag ?? preset?.kind ?? existing?.kind ?? 'openai',
114
- label: flags.label ?? preset?.label ?? existing?.label ?? id,
115
- baseUrl,
116
- apiKey: flags.key ?? existing?.apiKey ?? '',
117
- enabled: true,
118
- builtin: preset?.builtin ?? existing?.builtin ?? false,
119
- };
120
- saveConfig(config);
121
- console.log(`Provider "${id}" added/updated and enabled.`);
122
- if (!config.providers[id].apiKey) {
123
- console.log(' Note: no API key set yet — add one with `provider set-key ' + id + ' <key>`.');
124
- }
125
- };
126
-
127
- /** Run a `provider …` subcommand. Returns true if it was handled (so the server should not start). */
128
- export const runProviderCommand = (args: string[]): void => {
129
- const { positional, flags } = parseArgs(args);
130
- const [sub, id] = positional;
131
- const config = loadConfig();
132
-
133
- switch (sub) {
134
- case undefined:
135
- case 'list':
136
- printList(config.providers);
137
- return;
138
- case 'add':
139
- if (!id) {
140
- console.error('Usage: provider add <id> [--key <k>] [--base-url <url>] [--kind openai|anthropic]');
141
- process.exitCode = 1;
142
- return;
143
- }
144
- addProvider(config, id, flags);
145
- return;
146
- case 'set-key': {
147
- const key = positional[2];
148
- if (!id || !key) {
149
- console.error('Usage: provider set-key <id> <key>');
150
- process.exitCode = 1;
151
- return;
152
- }
153
- const provider = config.providers[id];
154
- if (!provider) {
155
- console.error(`No such provider "${id}". Add it first with \`provider add ${id}\`.`);
156
- process.exitCode = 1;
157
- return;
158
- }
159
- provider.apiKey = key;
160
- saveConfig(config);
161
- console.log(`Key updated for "${id}".`);
162
- return;
163
- }
164
- case 'enable':
165
- case 'disable': {
166
- const provider = id ? config.providers[id] : undefined;
167
- if (!provider) {
168
- console.error(`No such provider "${id ?? ''}".`);
169
- process.exitCode = 1;
170
- return;
171
- }
172
- provider.enabled = sub === 'enable';
173
- saveConfig(config);
174
- console.log(`Provider "${id}" ${provider.enabled ? 'enabled' : 'disabled'}.`);
175
- return;
176
- }
177
- case 'remove': {
178
- if (!id || !config.providers[id]) {
179
- console.error(`No such provider "${id ?? ''}".`);
180
- process.exitCode = 1;
181
- return;
182
- }
183
- delete config.providers[id];
184
- saveConfig(config);
185
- console.log(`Provider "${id}" removed.`);
186
- return;
187
- }
188
- default:
189
- console.error(`Unknown provider subcommand: ${sub}\n`);
190
- usage();
191
- process.exitCode = 1;
192
- }
193
- };
194
-
195
- export { usage };
1
+ /**
2
+ * CLI for Pix3AgentBridge. Running the bridge with no subcommand starts the server (see index.ts);
3
+ * the `provider` subcommands below manage the provider table in `~/.pix3/agent-bridge.json` — adding
4
+ * a key, enabling/disabling, or removing a provider — without touching a running server (the editor
5
+ * re-reads availability from `GET /v1/providers` on its next probe).
6
+ *
7
+ * pix3-agent-bridge provider list
8
+ * pix3-agent-bridge provider add openai --key sk-...
9
+ * pix3-agent-bridge provider add anthropic --key sk-ant-...
10
+ * pix3-agent-bridge provider add opencode-zen --key ...
11
+ * pix3-agent-bridge provider add my-router --base-url https://openrouter.ai/api/v1 --key ... --kind openai
12
+ * pix3-agent-bridge provider enable|disable|remove <id>
13
+ * pix3-agent-bridge provider set-key <id> <key>
14
+ */
15
+
16
+ import {
17
+ PROVIDER_PRESETS,
18
+ RESERVED_PROVIDER_IDS,
19
+ configPath,
20
+ loadConfig,
21
+ saveConfig,
22
+ type ProviderConfig,
23
+ } from './config.ts';
24
+
25
+ /** Parse `--flag value` / `--flag=value` pairs, returning [positional[], flags]. */
26
+ const parseArgs = (args: string[]): { positional: string[]; flags: Record<string, string> } => {
27
+ const positional: string[] = [];
28
+ const flags: Record<string, string> = {};
29
+ for (let i = 0; i < args.length; i += 1) {
30
+ const arg = args[i];
31
+ if (arg.startsWith('--')) {
32
+ const eq = arg.indexOf('=');
33
+ if (eq >= 0) {
34
+ flags[arg.slice(2, eq)] = arg.slice(eq + 1);
35
+ } else {
36
+ flags[arg.slice(2)] = args[i + 1] ?? '';
37
+ i += 1;
38
+ }
39
+ } else {
40
+ positional.push(arg);
41
+ }
42
+ }
43
+ return { positional, flags };
44
+ };
45
+
46
+ const maskKey = (key: string): string =>
47
+ key.length <= 8 ? (key ? '••••' : '(none)') : `${key.slice(0, 4)}…${key.slice(-4)}`;
48
+
49
+ const printList = (providers: Record<string, ProviderConfig>): void => {
50
+ const ids = Object.keys(providers);
51
+ if (ids.length === 0) {
52
+ console.log('No providers configured. Add one, e.g.: pix3-agent-bridge provider add openai --key sk-...');
53
+ console.log(`\nBuilt-in presets: ${Object.keys(PROVIDER_PRESETS).join(', ')}`);
54
+ return;
55
+ }
56
+ console.log(`Providers (from ${configPath()}):\n`);
57
+ for (const id of ids) {
58
+ const p = providers[id];
59
+ const state = p.enabled ? 'enabled' : 'disabled';
60
+ const key = p.apiKey ? maskKey(p.apiKey) : '(no key — add one with `provider set-key`)';
61
+ console.log(` ${id}`);
62
+ console.log(` kind: ${p.kind}`);
63
+ console.log(` label: ${p.label}`);
64
+ console.log(` baseUrl: ${p.baseUrl}`);
65
+ console.log(` key: ${key}`);
66
+ console.log(` status: ${state}`);
67
+ console.log('');
68
+ }
69
+ };
70
+
71
+ const usage = (): void => {
72
+ console.log(
73
+ [
74
+ 'Usage:',
75
+ ' pix3-agent-bridge start the bridge server',
76
+ ' pix3-agent-bridge provider list',
77
+ ' pix3-agent-bridge provider add <id> [--key <k>] [--base-url <url>] [--kind openai|anthropic] [--label <l>]',
78
+ ' pix3-agent-bridge provider set-key <id> <key>',
79
+ ' pix3-agent-bridge provider enable <id>',
80
+ ' pix3-agent-bridge provider disable <id>',
81
+ ' pix3-agent-bridge provider remove <id>',
82
+ '',
83
+ `Built-in presets (add with just --key): ${Object.keys(PROVIDER_PRESETS).join(', ')}`,
84
+ ].join('\n')
85
+ );
86
+ };
87
+
88
+ const addProvider = (
89
+ config: ReturnType<typeof loadConfig>,
90
+ id: string,
91
+ flags: Record<string, string>
92
+ ): void => {
93
+ if ((RESERVED_PROVIDER_IDS as readonly string[]).includes(id)) {
94
+ console.error(
95
+ `Provider id "${id}" is reserved by the bridge and cannot be added. Pick a different id.`
96
+ );
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ const preset = PROVIDER_PRESETS[id];
101
+ const baseUrl = (flags['base-url'] ?? preset?.baseUrl ?? '').replace(/\/$/, '');
102
+ if (!baseUrl) {
103
+ console.error(
104
+ `Provider "${id}" is not a built-in preset, so it needs an explicit --base-url ` +
105
+ `(e.g. --base-url https://openrouter.ai/api/v1).`
106
+ );
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+ const kindFlag = flags.kind === 'anthropic' ? 'anthropic' : flags.kind === 'openai' ? 'openai' : undefined;
111
+ const existing = config.providers[id];
112
+ config.providers[id] = {
113
+ kind: kindFlag ?? preset?.kind ?? existing?.kind ?? 'openai',
114
+ label: flags.label ?? preset?.label ?? existing?.label ?? id,
115
+ baseUrl,
116
+ apiKey: flags.key ?? existing?.apiKey ?? '',
117
+ enabled: true,
118
+ builtin: preset?.builtin ?? existing?.builtin ?? false,
119
+ };
120
+ saveConfig(config);
121
+ console.log(`Provider "${id}" added/updated and enabled.`);
122
+ if (!config.providers[id].apiKey) {
123
+ console.log(' Note: no API key set yet — add one with `provider set-key ' + id + ' <key>`.');
124
+ }
125
+ };
126
+
127
+ /** Run a `provider …` subcommand. Returns true if it was handled (so the server should not start). */
128
+ export const runProviderCommand = (args: string[]): void => {
129
+ const { positional, flags } = parseArgs(args);
130
+ const [sub, id] = positional;
131
+ const config = loadConfig();
132
+
133
+ switch (sub) {
134
+ case undefined:
135
+ case 'list':
136
+ printList(config.providers);
137
+ return;
138
+ case 'add':
139
+ if (!id) {
140
+ console.error('Usage: provider add <id> [--key <k>] [--base-url <url>] [--kind openai|anthropic]');
141
+ process.exitCode = 1;
142
+ return;
143
+ }
144
+ addProvider(config, id, flags);
145
+ return;
146
+ case 'set-key': {
147
+ const key = positional[2];
148
+ if (!id || !key) {
149
+ console.error('Usage: provider set-key <id> <key>');
150
+ process.exitCode = 1;
151
+ return;
152
+ }
153
+ const provider = config.providers[id];
154
+ if (!provider) {
155
+ console.error(`No such provider "${id}". Add it first with \`provider add ${id}\`.`);
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+ provider.apiKey = key;
160
+ saveConfig(config);
161
+ console.log(`Key updated for "${id}".`);
162
+ return;
163
+ }
164
+ case 'enable':
165
+ case 'disable': {
166
+ const provider = id ? config.providers[id] : undefined;
167
+ if (!provider) {
168
+ console.error(`No such provider "${id ?? ''}".`);
169
+ process.exitCode = 1;
170
+ return;
171
+ }
172
+ provider.enabled = sub === 'enable';
173
+ saveConfig(config);
174
+ console.log(`Provider "${id}" ${provider.enabled ? 'enabled' : 'disabled'}.`);
175
+ return;
176
+ }
177
+ case 'remove': {
178
+ if (!id || !config.providers[id]) {
179
+ console.error(`No such provider "${id ?? ''}".`);
180
+ process.exitCode = 1;
181
+ return;
182
+ }
183
+ delete config.providers[id];
184
+ saveConfig(config);
185
+ console.log(`Provider "${id}" removed.`);
186
+ return;
187
+ }
188
+ default:
189
+ console.error(`Unknown provider subcommand: ${sub}\n`);
190
+ usage();
191
+ process.exitCode = 1;
192
+ }
193
+ };
194
+
195
+ export { usage };