@orcareplay/adapters 0.1.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/dist/claude-code.d.ts +37 -0
- package/dist/claude-code.d.ts.map +1 -0
- package/dist/claude-code.js +103 -0
- package/dist/claude-code.js.map +1 -0
- package/dist/codex.d.ts +36 -0
- package/dist/codex.d.ts.map +1 -0
- package/dist/codex.js +128 -0
- package/dist/codex.js.map +1 -0
- package/dist/contract.d.ts +41 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +373 -0
- package/dist/contract.js.map +1 -0
- package/dist/detect.d.ts +47 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +123 -0
- package/dist/detect.js.map +1 -0
- package/dist/env.d.ts +31 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +54 -0
- package/dist/env.js.map +1 -0
- package/dist/generic-openai.d.ts +8 -0
- package/dist/generic-openai.d.ts.map +1 -0
- package/dist/generic-openai.js +31 -0
- package/dist/generic-openai.js.map +1 -0
- package/dist/grok.d.ts +23 -0
- package/dist/grok.d.ts.map +1 -0
- package/dist/grok.js +50 -0
- package/dist/grok.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-config.d.ts +17 -0
- package/dist/mcp-config.d.ts.map +1 -0
- package/dist/mcp-config.js +62 -0
- package/dist/mcp-config.js.map +1 -0
- package/dist/node.d.ts +20 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +60 -0
- package/dist/node.js.map +1 -0
- package/dist/openclaw.d.ts +19 -0
- package/dist/openclaw.d.ts.map +1 -0
- package/dist/openclaw.js +43 -0
- package/dist/openclaw.js.map +1 -0
- package/dist/opencode.d.ts +9 -0
- package/dist/opencode.d.ts.map +1 -0
- package/dist/opencode.js +52 -0
- package/dist/opencode.js.map +1 -0
- package/dist/registry.d.ts +19 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +82 -0
- package/dist/registry.js.map +1 -0
- package/dist/scaffold.d.ts +26 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +125 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/session.d.ts +47 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +112 -0
- package/dist/session.js.map +1 -0
- package/package.json +18 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Adapter, SessionSupport } from '@orcareplay/plugin-api';
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code names a project directory after the working directory, with every character that is
|
|
4
|
+
* not a letter, a digit or a hyphen replaced by one.
|
|
5
|
+
*
|
|
6
|
+
* Checked against a real install rather than inferred: a directory called `slug_test.dir v2`
|
|
7
|
+
* becomes `slug-test-dir-v2`, so underscores, dots and spaces all fold the same way the path
|
|
8
|
+
* separators do. An earlier version replaced only `\`, `/` and `:`, which was right for every
|
|
9
|
+
* path it happened to be tested against and wrong for the first one with an underscore in it —
|
|
10
|
+
* the computed name missed the real directory by one character, and the session capture then
|
|
11
|
+
* found nothing and said nothing. Hence {@link claudeSession}'s fallback: a guess about someone
|
|
12
|
+
* else's naming should not be the only thing holding the capture up.
|
|
13
|
+
*/
|
|
14
|
+
export declare function claudeProjectSlug(cwd: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Claude Code's transcript.
|
|
17
|
+
*
|
|
18
|
+
* `user` entries carry what the person typed, which is the half of a run orca cannot see from the
|
|
19
|
+
* wire. Entries the harness generates for itself — tool results, the attachments it splices in —
|
|
20
|
+
* arrive with the same `user` role, so they are filtered out: a replay driven by a tool result
|
|
21
|
+
* would ask the model something the recording never asked.
|
|
22
|
+
*/
|
|
23
|
+
export declare const claudeSession: SessionSupport;
|
|
24
|
+
/**
|
|
25
|
+
* Claude Code reads `ANTHROPIC_BASE_URL` for the API origin and appends its own `/v1/...` paths,
|
|
26
|
+
* so the proxy url goes in bare.
|
|
27
|
+
*
|
|
28
|
+
* No placeholder key. The placeholder exists for SDK clients that refuse to construct without one,
|
|
29
|
+
* and Claude Code is not one: it carries its own credential store, and most installations sign in
|
|
30
|
+
* to claude.ai rather than setting `ANTHROPIC_API_KEY` at all. Inventing the variable for those
|
|
31
|
+
* costs the recording twice over — Claude Code stops on an interactive "detected a custom API key"
|
|
32
|
+
* prompt before it will start, and answering yes switches it off the subscription credential it
|
|
33
|
+
* was going to authenticate with and onto a key that is not real. Passing the variable through
|
|
34
|
+
* when it is genuinely set keeps the API-key path exactly as it was.
|
|
35
|
+
*/
|
|
36
|
+
export declare const claudeCodeAdapter: Adapter;
|
|
37
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../src/claude-code.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAyB,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAK7F;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,cA8B3B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAiC/B,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { detectAgent, homeDir } from './detect.js';
|
|
4
|
+
import { passThrough, proxyBase, readEnv } from './env.js';
|
|
5
|
+
import { contentText, parseJsonl } from './session.js';
|
|
6
|
+
/**
|
|
7
|
+
* Claude Code names a project directory after the working directory, with every character that is
|
|
8
|
+
* not a letter, a digit or a hyphen replaced by one.
|
|
9
|
+
*
|
|
10
|
+
* Checked against a real install rather than inferred: a directory called `slug_test.dir v2`
|
|
11
|
+
* becomes `slug-test-dir-v2`, so underscores, dots and spaces all fold the same way the path
|
|
12
|
+
* separators do. An earlier version replaced only `\`, `/` and `:`, which was right for every
|
|
13
|
+
* path it happened to be tested against and wrong for the first one with an underscore in it —
|
|
14
|
+
* the computed name missed the real directory by one character, and the session capture then
|
|
15
|
+
* found nothing and said nothing. Hence {@link claudeSession}'s fallback: a guess about someone
|
|
16
|
+
* else's naming should not be the only thing holding the capture up.
|
|
17
|
+
*/
|
|
18
|
+
export function claudeProjectSlug(cwd) {
|
|
19
|
+
return cwd.replace(/[^A-Za-z0-9-]/g, '-');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Claude Code's transcript.
|
|
23
|
+
*
|
|
24
|
+
* `user` entries carry what the person typed, which is the half of a run orca cannot see from the
|
|
25
|
+
* wire. Entries the harness generates for itself — tool results, the attachments it splices in —
|
|
26
|
+
* arrive with the same `user` role, so they are filtered out: a replay driven by a tool result
|
|
27
|
+
* would ask the model something the recording never asked.
|
|
28
|
+
*/
|
|
29
|
+
export const claudeSession = {
|
|
30
|
+
dir(cwd, env) {
|
|
31
|
+
const home = readEnv(env, 'CLAUDE_CONFIG_DIR') ?? join(homeDir(), '.claude');
|
|
32
|
+
const projects = join(home, 'projects');
|
|
33
|
+
const named = join(projects, claudeProjectSlug(cwd));
|
|
34
|
+
// The slug is a guess about someone else's naming, and a wrong guess loses the capture in
|
|
35
|
+
// silence. When the directory it names is not there, watch the whole projects tree instead:
|
|
36
|
+
// the file that changed during this run is still this run's, whatever it ended up called.
|
|
37
|
+
return existsSync(named) ? named : projects;
|
|
38
|
+
},
|
|
39
|
+
parse(bytes) {
|
|
40
|
+
const prompts = [];
|
|
41
|
+
let id;
|
|
42
|
+
for (const entry of parseJsonl(bytes)) {
|
|
43
|
+
if (typeof entry['sessionId'] === 'string')
|
|
44
|
+
id ??= entry['sessionId'];
|
|
45
|
+
if (entry['type'] !== 'user')
|
|
46
|
+
continue;
|
|
47
|
+
const message = entry['message'];
|
|
48
|
+
if (message === null || typeof message !== 'object')
|
|
49
|
+
continue;
|
|
50
|
+
const text = contentText(message.content).trim();
|
|
51
|
+
// A turn the harness synthesised, not one a person typed.
|
|
52
|
+
if (text === '' || text.startsWith('<'))
|
|
53
|
+
continue;
|
|
54
|
+
prompts.push(text);
|
|
55
|
+
}
|
|
56
|
+
return { id, prompts };
|
|
57
|
+
},
|
|
58
|
+
resumeArgs(id) {
|
|
59
|
+
return ['--resume', id];
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Claude Code reads `ANTHROPIC_BASE_URL` for the API origin and appends its own `/v1/...` paths,
|
|
64
|
+
* so the proxy url goes in bare.
|
|
65
|
+
*
|
|
66
|
+
* No placeholder key. The placeholder exists for SDK clients that refuse to construct without one,
|
|
67
|
+
* and Claude Code is not one: it carries its own credential store, and most installations sign in
|
|
68
|
+
* to claude.ai rather than setting `ANTHROPIC_API_KEY` at all. Inventing the variable for those
|
|
69
|
+
* costs the recording twice over — Claude Code stops on an interactive "detected a custom API key"
|
|
70
|
+
* prompt before it will start, and answering yes switches it off the subscription credential it
|
|
71
|
+
* was going to authenticate with and onto a key that is not real. Passing the variable through
|
|
72
|
+
* when it is genuinely set keeps the API-key path exactly as it was.
|
|
73
|
+
*/
|
|
74
|
+
export const claudeCodeAdapter = {
|
|
75
|
+
id: 'claude-code',
|
|
76
|
+
// What people type. The binary is `claude`, and so is every example in the docs.
|
|
77
|
+
aliases: ['claude'],
|
|
78
|
+
harnessVersions: '>=1.0.0',
|
|
79
|
+
async detect(_cwd) {
|
|
80
|
+
return detectAgent(['claude'], ['.claude']);
|
|
81
|
+
},
|
|
82
|
+
async prepare(ctx) {
|
|
83
|
+
const env = { ANTHROPIC_BASE_URL: proxyBase(ctx.proxyUrl) };
|
|
84
|
+
passThrough(env, ctx.env, 'ANTHROPIC_API_KEY');
|
|
85
|
+
passThrough(env, ctx.env, 'ANTHROPIC_AUTH_TOKEN');
|
|
86
|
+
return { command: 'claude', args: [...ctx.userArgs], env };
|
|
87
|
+
},
|
|
88
|
+
session: claudeSession,
|
|
89
|
+
/**
|
|
90
|
+
* `--strict-mcp-config` alongside it, so the run uses the instrumented copy *instead of* the
|
|
91
|
+
* user's own servers rather than as well as them. Without it a project `.mcp.json` is still
|
|
92
|
+
* loaded, the same server is registered twice, and half its calls go round the shim.
|
|
93
|
+
*/
|
|
94
|
+
mcpConfigArgs(path) {
|
|
95
|
+
return ['--mcp-config', path, '--strict-mcp-config'];
|
|
96
|
+
},
|
|
97
|
+
/** `-p` is how Claude Code takes a prompt without a terminal, which a replay never has. */
|
|
98
|
+
driveArgs(prompts, recorded) {
|
|
99
|
+
const first = prompts[0];
|
|
100
|
+
return first === undefined ? undefined : [...recorded, '-p', first];
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../src/claude-code.ts"],"names":[],"mappings":"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"}
|
package/dist/codex.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Adapter, SessionSupport } from '@orcareplay/plugin-api';
|
|
2
|
+
/**
|
|
3
|
+
* Which provider `config.toml` selects, defaulting to the built-in `openai`.
|
|
4
|
+
*
|
|
5
|
+
* A hand-rolled read of one key rather than a TOML parser: this is a top-level assignment in a
|
|
6
|
+
* file Codex writes itself, and a dependency for it would be the only one in this package.
|
|
7
|
+
* Anything unparseable falls back to the default, because a malformed config must degrade the
|
|
8
|
+
* capture rather than stop the run.
|
|
9
|
+
*/
|
|
10
|
+
export declare function selectedProvider(configToml: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Codex's rollout.
|
|
13
|
+
*
|
|
14
|
+
* The user's turns arrive as `response_item` messages whose content is `input_text`. The harness
|
|
15
|
+
* puts its own preamble through the same channel — skills, environment context, multi-agent
|
|
16
|
+
* instructions — and those are wrapped in a tag, which is what separates them from a typed turn.
|
|
17
|
+
*/
|
|
18
|
+
export declare const codexSession: SessionSupport;
|
|
19
|
+
/**
|
|
20
|
+
* Codex CLI.
|
|
21
|
+
*
|
|
22
|
+
* `OPENAI_BASE_URL` is set for the built-in provider, but it is not enough on its own and never
|
|
23
|
+
* was: Codex resolves its origin from `model_providers.<name>.base_url` in `config.toml`, so an
|
|
24
|
+
* installation pointed at a gateway — which is the reason most people have a `config.toml` at all
|
|
25
|
+
* — ignores the variable completely and talks straight to its own endpoint. The recording then
|
|
26
|
+
* ends with `capture.empty` and nothing to replay, having cost a full run of real tokens.
|
|
27
|
+
*
|
|
28
|
+
* `-c` is Codex's own flag for overriding exactly one config value for one invocation, so the
|
|
29
|
+
* override is scoped to the run and the user's file is never touched. The provider name has to be
|
|
30
|
+
* read from that file, because the key to override is named after whichever provider is selected.
|
|
31
|
+
*
|
|
32
|
+
* No placeholder key: Codex carries its own credential store in `auth.json`, and inventing
|
|
33
|
+
* `OPENAI_API_KEY` overrides it with something that is not real.
|
|
34
|
+
*/
|
|
35
|
+
export declare const codexAdapter: Adapter;
|
|
36
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../src/codex.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAyB,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAU7F;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAU3D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,cAkC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,YAAY,EAAE,OAwC1B,CAAC"}
|
package/dist/codex.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { detectAgent, homeDir } from './detect.js';
|
|
4
|
+
import { passThrough, proxyBase, readEnv } from './env.js';
|
|
5
|
+
import { contentText, parseJsonl } from './session.js';
|
|
6
|
+
/** Where the Codex CLI keeps `config.toml`, honouring its own override. */
|
|
7
|
+
function codexHome(env) {
|
|
8
|
+
return readEnv(env, 'CODEX_HOME') ?? join(homeDir(), '.codex');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Which provider `config.toml` selects, defaulting to the built-in `openai`.
|
|
12
|
+
*
|
|
13
|
+
* A hand-rolled read of one key rather than a TOML parser: this is a top-level assignment in a
|
|
14
|
+
* file Codex writes itself, and a dependency for it would be the only one in this package.
|
|
15
|
+
* Anything unparseable falls back to the default, because a malformed config must degrade the
|
|
16
|
+
* capture rather than stop the run.
|
|
17
|
+
*/
|
|
18
|
+
export function selectedProvider(configToml) {
|
|
19
|
+
for (const line of configToml.split(/\r?\n/)) {
|
|
20
|
+
const text = line.trim();
|
|
21
|
+
// Stop at the first table header: `model_provider` is a top-level key, and a same-named key
|
|
22
|
+
// inside `[model_providers.x]` is a different setting entirely.
|
|
23
|
+
if (text.startsWith('['))
|
|
24
|
+
break;
|
|
25
|
+
const match = /^model_provider\s*=\s*["']([^"']+)["']/.exec(text);
|
|
26
|
+
if (match?.[1])
|
|
27
|
+
return match[1];
|
|
28
|
+
}
|
|
29
|
+
return 'openai';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Codex's rollout.
|
|
33
|
+
*
|
|
34
|
+
* The user's turns arrive as `response_item` messages whose content is `input_text`. The harness
|
|
35
|
+
* puts its own preamble through the same channel — skills, environment context, multi-agent
|
|
36
|
+
* instructions — and those are wrapped in a tag, which is what separates them from a typed turn.
|
|
37
|
+
*/
|
|
38
|
+
export const codexSession = {
|
|
39
|
+
dir(_cwd, env) {
|
|
40
|
+
return join(codexHome(env), 'sessions');
|
|
41
|
+
},
|
|
42
|
+
parse(bytes) {
|
|
43
|
+
const prompts = [];
|
|
44
|
+
let id;
|
|
45
|
+
for (const entry of parseJsonl(bytes)) {
|
|
46
|
+
const payload = entry['payload'];
|
|
47
|
+
if (payload === null || typeof payload !== 'object')
|
|
48
|
+
continue;
|
|
49
|
+
const p = payload;
|
|
50
|
+
if (entry['type'] === 'session_meta' && typeof p['id'] === 'string')
|
|
51
|
+
id ??= p['id'];
|
|
52
|
+
if (entry['type'] !== 'response_item' || p['type'] !== 'message')
|
|
53
|
+
continue;
|
|
54
|
+
if (p['role'] !== undefined && p['role'] !== 'user')
|
|
55
|
+
continue;
|
|
56
|
+
const content = p['content'];
|
|
57
|
+
if (!Array.isArray(content))
|
|
58
|
+
continue;
|
|
59
|
+
const text = content
|
|
60
|
+
.filter((b) => b !== null && typeof b === 'object' && b.type === 'input_text')
|
|
61
|
+
.map((b) => String(b.text ?? ''))
|
|
62
|
+
.join('')
|
|
63
|
+
.trim();
|
|
64
|
+
if (text === '' || text.startsWith('<'))
|
|
65
|
+
continue;
|
|
66
|
+
prompts.push(text);
|
|
67
|
+
}
|
|
68
|
+
return { id, prompts };
|
|
69
|
+
},
|
|
70
|
+
resumeArgs(id) {
|
|
71
|
+
return ['exec', 'resume', id];
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Codex CLI.
|
|
76
|
+
*
|
|
77
|
+
* `OPENAI_BASE_URL` is set for the built-in provider, but it is not enough on its own and never
|
|
78
|
+
* was: Codex resolves its origin from `model_providers.<name>.base_url` in `config.toml`, so an
|
|
79
|
+
* installation pointed at a gateway — which is the reason most people have a `config.toml` at all
|
|
80
|
+
* — ignores the variable completely and talks straight to its own endpoint. The recording then
|
|
81
|
+
* ends with `capture.empty` and nothing to replay, having cost a full run of real tokens.
|
|
82
|
+
*
|
|
83
|
+
* `-c` is Codex's own flag for overriding exactly one config value for one invocation, so the
|
|
84
|
+
* override is scoped to the run and the user's file is never touched. The provider name has to be
|
|
85
|
+
* read from that file, because the key to override is named after whichever provider is selected.
|
|
86
|
+
*
|
|
87
|
+
* No placeholder key: Codex carries its own credential store in `auth.json`, and inventing
|
|
88
|
+
* `OPENAI_API_KEY` overrides it with something that is not real.
|
|
89
|
+
*/
|
|
90
|
+
export const codexAdapter = {
|
|
91
|
+
id: 'codex',
|
|
92
|
+
async detect(_cwd) {
|
|
93
|
+
return detectAgent(['codex'], ['.codex']);
|
|
94
|
+
},
|
|
95
|
+
async prepare(ctx) {
|
|
96
|
+
const base = proxyBase(ctx.proxyUrl, 'v1');
|
|
97
|
+
const env = { OPENAI_BASE_URL: base };
|
|
98
|
+
passThrough(env, ctx.env, 'OPENAI_API_KEY');
|
|
99
|
+
let provider = 'openai';
|
|
100
|
+
try {
|
|
101
|
+
provider = selectedProvider(await readFile(join(codexHome(ctx.env), 'config.toml'), 'utf8'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// No config, or unreadable: the built-in provider is the right guess, and `OPENAI_BASE_URL`
|
|
105
|
+
// covers it as well. Capture must never depend on a file existing.
|
|
106
|
+
}
|
|
107
|
+
// Ahead of the user's own arguments so that anything they pass explicitly still wins.
|
|
108
|
+
const args = ['-c', `model_providers.${provider}.base_url=${base}`, ...ctx.userArgs];
|
|
109
|
+
return { command: 'codex', args, env };
|
|
110
|
+
},
|
|
111
|
+
session: codexSession,
|
|
112
|
+
/**
|
|
113
|
+
* `exec` is Codex's non-interactive form and takes the prompt as its positional argument.
|
|
114
|
+
*
|
|
115
|
+
* A run recorded as `codex exec` with the prompt on stdin already carries the subcommand and its
|
|
116
|
+
* flags, so the prompt is appended to what was recorded; a run recorded bare needs the whole
|
|
117
|
+
* form. `--skip-git-repo-check` because a replay runs in a scratch directory that is not one.
|
|
118
|
+
*/
|
|
119
|
+
driveArgs(prompts, recorded) {
|
|
120
|
+
const first = prompts[0];
|
|
121
|
+
if (first === undefined)
|
|
122
|
+
return undefined;
|
|
123
|
+
if (recorded.includes('exec'))
|
|
124
|
+
return [...recorded, first];
|
|
125
|
+
return ['exec', '--skip-git-repo-check', first];
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../src/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Adapter, RecordContext } from '@orcareplay/plugin-api';
|
|
2
|
+
/**
|
|
3
|
+
* The adapter contract: the behaviour every adapter must have for capture to work at all.
|
|
4
|
+
*
|
|
5
|
+
* Adapters break silently. A harness renames the variable it reads for its API origin, the adapter
|
|
6
|
+
* keeps setting the old one, and every run still *looks* fine — the agent answers, the exit code is
|
|
7
|
+
* zero, and the trace is empty. Each check below exists because breaking it produces exactly that
|
|
8
|
+
* shape of failure: nothing to see until a user files a confusing bug.
|
|
9
|
+
*/
|
|
10
|
+
/** Every check, in the order they run. A failing check never stops the others. */
|
|
11
|
+
export declare const CONTRACT_CHECKS: readonly ["id-format", "detect-resolves", "prepare-shape", "redirects-model-traffic", "no-invented-keys", "no-ctx-mutation", "deterministic", "no-foreign-paths", "harness-versions"];
|
|
12
|
+
export type ContractCheck = (typeof CONTRACT_CHECKS)[number];
|
|
13
|
+
export interface ContractFailure {
|
|
14
|
+
check: ContractCheck;
|
|
15
|
+
detail: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ContractResult {
|
|
18
|
+
adapter: string;
|
|
19
|
+
passed: string[];
|
|
20
|
+
failed: ContractFailure[];
|
|
21
|
+
ok: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ContractOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Overrides for the context the checks prepare against — `userArgs` for an adapter that takes
|
|
26
|
+
* its command from argv, or a `proxyUrl` matching a running proxy. `cwd` and `runDir` default to
|
|
27
|
+
* fresh temp directories that are removed afterwards; `env` is owned by the credential checks,
|
|
28
|
+
* which vary it deliberately.
|
|
29
|
+
*/
|
|
30
|
+
ctx?: Partial<RecordContext>;
|
|
31
|
+
}
|
|
32
|
+
/** At least one of these must be redirected, or the proxy sees nothing at all. */
|
|
33
|
+
export declare const MODEL_BASE_URL_VARS: readonly ["ANTHROPIC_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE"];
|
|
34
|
+
/**
|
|
35
|
+
* Runs one adapter through the contract. Never throws: a runner that dies on a broken adapter is
|
|
36
|
+
* the same class of bug it is trying to catch, and callers batch this over a whole registry.
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkAdapterContract(adapter: Adapter, opts?: ContractOptions): Promise<ContractResult>;
|
|
39
|
+
/** One line for a pass, one line per failure otherwise — the shape a CI log wants. */
|
|
40
|
+
export declare function formatContractResult(result: ContractResult): string;
|
|
41
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAU,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG7E;;;;;;;GAOG;AAEH,kFAAkF;AAClF,eAAO,MAAM,eAAe,uLAUlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CAC9B;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,uEAItB,CAAC;AAoBX;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,cAAc,CAAC,CAkCzB;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAKnE"}
|