@gakim-digital/dexter-bridge 0.5.20 → 0.11.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 +128 -18
- package/package.json +19 -5
- package/src/agent.js +1500 -335
- package/src/agentOutput.js +209 -0
- package/src/api.js +49 -2
- package/src/cli.js +310 -54
- package/src/config.js +82 -15
- package/src/framerAgentTools.js +1108 -0
- package/src/harnessMcpServer.js +240 -0
- package/src/harnessTools.js +548 -0
- package/src/logger.js +1 -1
- package/src/nativeSkills.js +295 -0
- package/src/outcomeWorkspace.js +351 -0
- package/src/protocol.js +257 -10
- package/src/providers/acp.js +241 -0
- package/src/providers/codexAppServer.js +1074 -170
- package/src/providers/codexStructuredOutput.js +243 -16
- package/src/providers/directByok.js +197 -0
- package/src/providers/index.js +33 -7
- package/src/providers/openCode.js +607 -0
- package/src/runtimeProfiles.js +284 -0
- package/src/providers/claudeAgentSdk.js +0 -507
package/src/config.js
CHANGED
|
@@ -17,6 +17,18 @@ export const BRIDGE_BUILD_FINGERPRINT = crypto
|
|
|
17
17
|
'./cli.js',
|
|
18
18
|
'./config.js',
|
|
19
19
|
'./protocol.js',
|
|
20
|
+
'./nativeSkills.js',
|
|
21
|
+
'./harnessTools.js',
|
|
22
|
+
'./harnessMcpServer.js',
|
|
23
|
+
'./framerAgentTools.js',
|
|
24
|
+
'./outcomeWorkspace.js',
|
|
25
|
+
'./runtimeProfiles.js',
|
|
26
|
+
'./providers/index.js',
|
|
27
|
+
'./providers/codexAppServer.js',
|
|
28
|
+
'./providers/codexStructuredOutput.js',
|
|
29
|
+
'./providers/directByok.js',
|
|
30
|
+
'./providers/openCode.js',
|
|
31
|
+
'./providers/acp.js',
|
|
20
32
|
].map((relativePath) => {
|
|
21
33
|
const url = new URL(relativePath, import.meta.url);
|
|
22
34
|
return `${relativePath}\u0000${fs.readFileSync(url, 'utf8')}`;
|
|
@@ -24,15 +36,34 @@ export const BRIDGE_BUILD_FINGERPRINT = crypto
|
|
|
24
36
|
.digest('hex');
|
|
25
37
|
export const BRIDGE_CAPABILITIES = [
|
|
26
38
|
'model-turn-v1',
|
|
39
|
+
'outcome-run-v1',
|
|
40
|
+
'harness-tools-v1',
|
|
41
|
+
'framer-agent-v1',
|
|
42
|
+
'framer-project-bootstrap-v1',
|
|
43
|
+
'project-shell-v1',
|
|
27
44
|
'build-fingerprint-v1',
|
|
28
45
|
'tool-schema-parity-v1',
|
|
46
|
+
'runtime-profiles-v1',
|
|
47
|
+
'native-skills-v1',
|
|
48
|
+
'model-progress-v1',
|
|
29
49
|
];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
export const BRIDGE_PRODUCTS = {
|
|
51
|
+
dexter: {
|
|
52
|
+
id: 'dexter',
|
|
53
|
+
name: 'Dexter',
|
|
54
|
+
connectionScope: 'framer',
|
|
55
|
+
},
|
|
56
|
+
instawebai: {
|
|
57
|
+
id: 'instawebai',
|
|
58
|
+
name: 'InstaWebAI',
|
|
59
|
+
connectionScope: 'app-builder',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
// Codex remains the default local agent. Claude Code is enabled only when the
|
|
63
|
+
// user explicitly connects the locally installed CLI and chooses one of its
|
|
64
|
+
// reported models.
|
|
34
65
|
export const DEFAULT_BRIDGE_AGENT = 'codex';
|
|
35
|
-
export const BRIDGE_AGENTS = ['claude-code', 'codex', 'dry-run'];
|
|
66
|
+
export const BRIDGE_AGENTS = ['claude-code', 'codex', 'opencode', 'dry-run'];
|
|
36
67
|
export const COMPANION_MODEL_DEFINITIONS = [
|
|
37
68
|
{
|
|
38
69
|
id: 'claude-code:fable',
|
|
@@ -146,17 +177,34 @@ export function defaultConfigDir(env = process.env) {
|
|
|
146
177
|
return env.DEXTER_BRIDGE_CONFIG_DIR || path.join(os.homedir(), '.dexter-bridge');
|
|
147
178
|
}
|
|
148
179
|
|
|
180
|
+
export function normalizeBridgeProduct(value) {
|
|
181
|
+
const id =
|
|
182
|
+
typeof value === 'string'
|
|
183
|
+
? value.trim().toLowerCase()
|
|
184
|
+
: typeof value?.id === 'string'
|
|
185
|
+
? value.id.trim().toLowerCase()
|
|
186
|
+
: '';
|
|
187
|
+
const connectionScope =
|
|
188
|
+
value && typeof value === 'object' && typeof value.connectionScope === 'string'
|
|
189
|
+
? value.connectionScope.trim().toLowerCase()
|
|
190
|
+
: '';
|
|
191
|
+
if (id === 'instawebai' || connectionScope === 'app-builder') {
|
|
192
|
+
return BRIDGE_PRODUCTS.instawebai;
|
|
193
|
+
}
|
|
194
|
+
return BRIDGE_PRODUCTS.dexter;
|
|
195
|
+
}
|
|
196
|
+
|
|
149
197
|
export function configFilePath(configDir = defaultConfigDir()) {
|
|
150
198
|
return path.join(configDir, 'config.json');
|
|
151
199
|
}
|
|
152
200
|
|
|
153
|
-
export function normalizeApiBaseUrl(value) {
|
|
201
|
+
export function normalizeApiBaseUrl(value, { allowInsecureHttp = false } = {}) {
|
|
154
202
|
const raw = String(value || DEFAULT_API_BASE_URL).trim();
|
|
155
203
|
let parsed;
|
|
156
204
|
try {
|
|
157
205
|
parsed = new URL(raw);
|
|
158
206
|
} catch {
|
|
159
|
-
throw new Error('
|
|
207
|
+
throw new Error('Bridge API URL must be a valid absolute URL.');
|
|
160
208
|
}
|
|
161
209
|
const hostname = parsed.hostname.replace(/^\[|\]$/g, '').toLowerCase();
|
|
162
210
|
const loopback =
|
|
@@ -165,13 +213,16 @@ export function normalizeApiBaseUrl(value) {
|
|
|
165
213
|
|| hostname === '127.0.0.1'
|
|
166
214
|
|| hostname === '::1';
|
|
167
215
|
if (parsed.username || parsed.password) {
|
|
168
|
-
throw new Error('
|
|
216
|
+
throw new Error('Bridge API URL must not contain embedded credentials.');
|
|
169
217
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
218
|
+
if (
|
|
219
|
+
parsed.protocol !== 'https:'
|
|
220
|
+
&& !(parsed.protocol === 'http:' && (loopback || allowInsecureHttp))
|
|
221
|
+
) {
|
|
222
|
+
throw new Error('Bridge API URL must use HTTPS. HTTP is allowed only for localhost development.');
|
|
172
223
|
}
|
|
173
224
|
if (parsed.search || parsed.hash) {
|
|
174
|
-
throw new Error('
|
|
225
|
+
throw new Error('Bridge API URL must not contain a query string or fragment.');
|
|
175
226
|
}
|
|
176
227
|
return parsed.toString().replace(/\/+$/, '');
|
|
177
228
|
}
|
|
@@ -180,6 +231,7 @@ export function normalizeAgentName(value, fallback = DEFAULT_BRIDGE_AGENT) {
|
|
|
180
231
|
const raw = String(value || '').trim().toLowerCase();
|
|
181
232
|
if (raw === 'claude' || raw === 'claude_code' || raw === 'claude-code') return 'claude-code';
|
|
182
233
|
if (raw === 'openai-codex' || raw === 'openai_codex' || raw === 'codex') return 'codex';
|
|
234
|
+
if (raw === 'open-code' || raw === 'open_code' || raw === 'opencode') return 'opencode';
|
|
183
235
|
if (raw === 'dryrun' || raw === 'dry_run' || raw === 'dry-run') return 'dry-run';
|
|
184
236
|
return BRIDGE_AGENTS.includes(fallback) ? fallback : DEFAULT_BRIDGE_AGENT;
|
|
185
237
|
}
|
|
@@ -196,12 +248,27 @@ export function companionModelDefinition(value, agent = DEFAULT_BRIDGE_AGENT) {
|
|
|
196
248
|
if (!raw || raw === 'local-companion') {
|
|
197
249
|
return COMPANION_MODEL_DEFINITIONS.find((model) => model.id === DEFAULT_MODEL_BY_AGENT[normalizedAgent]);
|
|
198
250
|
}
|
|
199
|
-
|
|
251
|
+
const known =
|
|
200
252
|
candidates.find((model) => model.id.toLowerCase() === raw) ||
|
|
201
253
|
candidates.find((model) => model.invocationName.toLowerCase() === raw) ||
|
|
202
|
-
candidates.find((model) => model.id.toLowerCase() === `${normalizedAgent}:${raw}`)
|
|
203
|
-
|
|
204
|
-
|
|
254
|
+
candidates.find((model) => model.id.toLowerCase() === `${normalizedAgent}:${raw}`);
|
|
255
|
+
if (known) return known;
|
|
256
|
+
const prefix = `${normalizedAgent}:`;
|
|
257
|
+
if (raw.startsWith(prefix)) {
|
|
258
|
+
const invocationName = raw.slice(prefix.length);
|
|
259
|
+
if (/^[a-z0-9][a-z0-9._-]{0,159}$/.test(invocationName)) {
|
|
260
|
+
return {
|
|
261
|
+
id: `${normalizedAgent}:${invocationName}`,
|
|
262
|
+
agent: normalizedAgent,
|
|
263
|
+
provider: normalizedAgent === 'codex' ? 'openai' : normalizedAgent === 'claude-code' ? 'anthropic' : 'companion',
|
|
264
|
+
displayName: invocationName,
|
|
265
|
+
invocationName,
|
|
266
|
+
costTier: '$$',
|
|
267
|
+
description: `${normalizedAgent === 'codex' ? 'Codex' : 'Claude Code'} model reported by the connected bridge.`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return COMPANION_MODEL_DEFINITIONS.find((model) => model.id === DEFAULT_MODEL_BY_AGENT[normalizedAgent]);
|
|
205
272
|
}
|
|
206
273
|
|
|
207
274
|
export function normalizeCompanionModelName(value, agent = DEFAULT_BRIDGE_AGENT) {
|