@stage-labs/metro 0.1.0-beta.201 → 0.1.0-beta.202
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.js +4 -15
- package/dist/cli.js +5 -3
- package/dist/hold.js +1 -2
- package/dist/runtime-install.js +4 -5
- package/dist/runtime.js +1 -4
- package/dist/serve.js +4 -5
- package/dist/service.js +82 -21
- package/dist/update.js +1 -1
- package/package.json +2 -2
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +1 -1
- package/runtime/node_modules/@metro-labs/core/src/events.ts +4 -2
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +12 -25
- package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +45 -22
- package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +0 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +8 -80
- package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +3 -5
- package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +4 -9
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +9 -39
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +9 -28
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +8 -116
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +11 -35
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +7 -9
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +3 -29
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +5 -8
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +5 -8
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +3 -6
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +25 -3
- package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +2 -3
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +6 -56
- package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +7 -6
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +7 -7
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +2 -4
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +19 -22
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +1 -2
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +6 -8
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +3 -6
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +2 -3
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +28 -1
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +6 -6
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +7 -7
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +10 -31
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +1 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +5 -6
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +1 -4
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +3 -8
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +6 -7
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +5 -44
- package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +6 -7
- package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +12 -13
- package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +5 -22
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +1 -19
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +7 -35
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +5 -10
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +1 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +1 -1
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +11 -12
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +2 -5
- package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +0 -1
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +1 -15
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +0 -6
- package/runtime/node_modules/@metro-labs/outlook/src/format.ts +0 -1
- package/runtime/node_modules/@metro-labs/outlook/src/state.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +4 -22
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +2 -2
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +1 -3
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +1 -2
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +4 -37
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +7 -23
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +1 -4
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +4 -9
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +3 -3
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +0 -11
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +2 -12
- package/runtime/node_modules/@metro-labs/threema/src/files.ts +4 -15
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +2 -7
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +1 -2
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +5 -26
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +5 -6
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +1 -3
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +1 -12
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +14 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +31 -78
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +3 -4
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +1 -6
- package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +0 -2
- package/runtime/runtime.json +1 -1
- package/runtime/node_modules/@metro-labs/core/src/identity.ts +0 -7
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +0 -2
- package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +0 -12
- package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +0 -13
- package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +0 -29
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +0 -43
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +0 -165
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -1
|
@@ -4,8 +4,8 @@ import { ApiError } from '@metro-labs/http/api-error';
|
|
|
4
4
|
import { claudeDir } from './files.js';
|
|
5
5
|
import { removeHome, writeHomeText } from '../agent-user/home-fs.js';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const SKILL_MAX = 256 * 1024;
|
|
8
|
+
const SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
9
9
|
const FILE = 'SKILL.md';
|
|
10
10
|
const DEFAULT_MODE = 0o644;
|
|
11
11
|
const SUMMARY_MAX = 300;
|
|
@@ -44,7 +44,7 @@ function entryOf(name: string, path: string): ClaudeSkill {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
const userSkillsRoot = (dir: string): string => join(dir, 'skills');
|
|
48
48
|
|
|
49
49
|
function isFolder(path: string): boolean {
|
|
50
50
|
try {
|
|
@@ -97,7 +97,7 @@ export function writeClaudeSkill(
|
|
|
97
97
|
return entryOf(skill.name, skill.path);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
const skillTemplate = (name: string): string =>
|
|
101
101
|
['---', `name: ${name}`, 'description: what this skill does, and when Claude should reach for it', '---', '', `# ${name}`, '', 'Write the instructions here.', ''].join('\n');
|
|
102
102
|
|
|
103
103
|
export function createClaudeSkill(name: string, text: string | undefined, dir = claudeDir()): ClaudeSkill {
|
|
@@ -61,7 +61,7 @@ export function newerThan(a: string, b: string): boolean {
|
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
async function latestClaudeVersion(deps: VersionDeps = {}): Promise<string | null> {
|
|
65
65
|
const now = (deps.now ?? Date.now)();
|
|
66
66
|
if (cached !== null && now - cached.at < CACHE_MS) return cached.latest;
|
|
67
67
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
cors,
|
|
11
11
|
readJsonBody,
|
|
12
12
|
sendJson,
|
|
13
|
+
stringOf,
|
|
13
14
|
} from '@metro-labs/http/api-http';
|
|
14
15
|
import { parseId } from '@metro-labs/core/ids';
|
|
15
16
|
import {
|
|
@@ -29,9 +30,6 @@ import type {
|
|
|
29
30
|
|
|
30
31
|
const PREFIX = '/api/connectors';
|
|
31
32
|
|
|
32
|
-
const asText = (value: unknown): string =>
|
|
33
|
-
typeof value === 'string' ? value : '';
|
|
34
|
-
|
|
35
33
|
export interface ConnectorApiDeps extends OAuthRouteDeps {
|
|
36
34
|
listConnectors: () => Promise<Connector[]>;
|
|
37
35
|
createConnector: (input: ConnectorInput) => Promise<Connector>;
|
|
@@ -112,7 +110,7 @@ async function handleCreate(
|
|
|
112
110
|
deps: ConnectorApiDeps,
|
|
113
111
|
): Promise<void> {
|
|
114
112
|
const body = await readJsonBody(req);
|
|
115
|
-
const offered =
|
|
113
|
+
const offered = stringOf(bodyField(body, 'value')).trim() !== '';
|
|
116
114
|
try {
|
|
117
115
|
const created = await deps.createConnector({
|
|
118
116
|
name: bodyField(body, 'name'),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiError } from '@metro-labs/http/api-error';
|
|
2
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
2
3
|
import type {
|
|
3
4
|
ConnectorAuth,
|
|
4
5
|
VerifiedRecord,
|
|
@@ -30,10 +31,6 @@ export interface ConnectorConfig {
|
|
|
30
31
|
toolGroups?: Record<string, ToolGroup>;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
function text(value: unknown): string {
|
|
34
|
-
return typeof value === 'string' ? value : '';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
34
|
function hasControlChar(value: string): boolean {
|
|
38
35
|
for (const ch of value) {
|
|
39
36
|
const code = ch.codePointAt(0) ?? 0;
|
|
@@ -62,8 +59,8 @@ export function connectorAuth(
|
|
|
62
59
|
rawHeader: unknown,
|
|
63
60
|
rawValue: unknown,
|
|
64
61
|
): ConnectorAuth {
|
|
65
|
-
const header =
|
|
66
|
-
const value =
|
|
62
|
+
const header = stringOf(rawHeader).trim();
|
|
63
|
+
const value = stringOf(rawValue).trim();
|
|
67
64
|
if (header === '' && value === '') return { kind: 'none' };
|
|
68
65
|
if (value === '')
|
|
69
66
|
throw new ConnectorError(
|
|
@@ -82,8 +79,8 @@ export function connectorClient(
|
|
|
82
79
|
rawId: unknown,
|
|
83
80
|
rawSecret: unknown,
|
|
84
81
|
): OAuthClient | null {
|
|
85
|
-
const clientId =
|
|
86
|
-
const clientSecret =
|
|
82
|
+
const clientId = stringOf(rawId).trim();
|
|
83
|
+
const clientSecret = stringOf(rawSecret).trim();
|
|
87
84
|
if (clientId === '' && clientSecret === '') return null;
|
|
88
85
|
if (clientId === '')
|
|
89
86
|
throw new ConnectorError('a client secret needs the client ID it belongs to', 400);
|
|
@@ -96,27 +93,27 @@ export function connectorClient(
|
|
|
96
93
|
|
|
97
94
|
function readClient(raw: unknown): OAuthClient | null {
|
|
98
95
|
if (!isRecord(raw)) return null;
|
|
99
|
-
const clientId =
|
|
96
|
+
const clientId = stringOf(raw.clientId);
|
|
100
97
|
if (clientId === '') return null;
|
|
101
|
-
const clientSecret =
|
|
98
|
+
const clientSecret = stringOf(raw.clientSecret);
|
|
102
99
|
return { clientId, ...(clientSecret === '' ? {} : { clientSecret }) };
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
function readOAuth(raw: Record<string, unknown>): ConnectorAuth {
|
|
106
|
-
const accessToken =
|
|
107
|
-
const clientId =
|
|
108
|
-
const tokenEndpoint =
|
|
103
|
+
const accessToken = stringOf(raw.accessToken);
|
|
104
|
+
const clientId = stringOf(raw.clientId);
|
|
105
|
+
const tokenEndpoint = stringOf(raw.tokenEndpoint);
|
|
109
106
|
if (accessToken === '' || clientId === '' || tokenEndpoint === '')
|
|
110
107
|
return { kind: 'none' };
|
|
111
|
-
const refreshToken =
|
|
112
|
-
const clientSecret =
|
|
113
|
-
const scope =
|
|
108
|
+
const refreshToken = stringOf(raw.refreshToken);
|
|
109
|
+
const clientSecret = stringOf(raw.clientSecret);
|
|
110
|
+
const scope = stringOf(raw.scope);
|
|
114
111
|
return {
|
|
115
112
|
kind: 'oauth',
|
|
116
113
|
accessToken,
|
|
117
114
|
clientId,
|
|
118
115
|
tokenEndpoint,
|
|
119
|
-
issuer:
|
|
116
|
+
issuer: stringOf(raw.issuer),
|
|
120
117
|
...(refreshToken === '' ? {} : { refreshToken }),
|
|
121
118
|
...(clientSecret === '' ? {} : { clientSecret }),
|
|
122
119
|
...(scope === '' ? {} : { scope }),
|
|
@@ -128,21 +125,21 @@ function readAuth(raw: unknown): ConnectorAuth {
|
|
|
128
125
|
if (!isRecord(raw)) return { kind: 'none' };
|
|
129
126
|
if (raw.kind === 'oauth') return readOAuth(raw);
|
|
130
127
|
if (raw.kind !== 'header') return { kind: 'none' };
|
|
131
|
-
const name =
|
|
132
|
-
const value =
|
|
128
|
+
const name = stringOf(raw.name);
|
|
129
|
+
const value = stringOf(raw.value);
|
|
133
130
|
if (name === '' || value === '') return { kind: 'none' };
|
|
134
131
|
return { kind: 'header', name, value };
|
|
135
132
|
}
|
|
136
133
|
|
|
137
134
|
function readVerified(raw: unknown): VerifiedRecord {
|
|
138
135
|
const record = isRecord(raw) ? raw : {};
|
|
139
|
-
return { at:
|
|
136
|
+
return { at: stringOf(record.at), server: stringOf(record.server) };
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
const TOOL_NAME_MAX = 128;
|
|
143
140
|
const TOOL_GROUPS_MAX = 1000;
|
|
144
141
|
|
|
145
|
-
|
|
142
|
+
function readToolGroups(raw: unknown): Record<string, ToolGroup> | undefined {
|
|
146
143
|
if (!isRecord(raw)) return undefined;
|
|
147
144
|
const kept = Object.entries(raw)
|
|
148
145
|
.filter((entry): entry is [string, ToolGroup] => entry[0].length <= TOOL_NAME_MAX && (entry[1] === 'read' || entry[1] === 'write'))
|
|
@@ -157,7 +154,7 @@ export function readConfig(raw: unknown): ConnectorConfig {
|
|
|
157
154
|
const toolGroups = readToolGroups(record.toolGroups);
|
|
158
155
|
return {
|
|
159
156
|
auth,
|
|
160
|
-
createdAt:
|
|
157
|
+
createdAt: stringOf(record.createdAt),
|
|
161
158
|
verified: readVerified(record.verified),
|
|
162
159
|
oauth: record.oauth === true || auth.kind === 'oauth',
|
|
163
160
|
client: readClient(record.client),
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { ToolGroup } from '@metro-labs/core/stations/types';
|
|
2
2
|
import { policyFor, setPolicies, type PolicyTarget, type ToolPolicy } from '../policy/policy.js';
|
|
3
3
|
import { serverKeysOf } from './plugin-sync.js';
|
|
4
|
-
import { takeGrant } from '../approvals/pending.js';
|
|
5
|
-
import { NEEDS_APPROVAL } from '../approvals/needs.js';
|
|
4
|
+
import { NEEDS_APPROVAL, takeGrant } from '../approvals/pending.js';
|
|
6
5
|
|
|
7
6
|
interface GateRow {
|
|
8
7
|
id: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseConnectorUrl, refused } from './verify.js';
|
|
2
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
2
3
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
3
4
|
|
|
4
5
|
const DISCOVERY_TIMEOUT_MS = 10_000;
|
|
@@ -17,9 +18,6 @@ interface ResourceMetadata {
|
|
|
17
18
|
scopes: string[];
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const str = (value: unknown): string =>
|
|
21
|
-
typeof value === 'string' ? value : '';
|
|
22
|
-
|
|
23
21
|
async function getJson(url: URL): Promise<Record<string, unknown> | null> {
|
|
24
22
|
let res: Response;
|
|
25
23
|
try {
|
|
@@ -72,7 +70,7 @@ function scopesOf(body: Record<string, unknown> | null): string[] {
|
|
|
72
70
|
if (!Array.isArray(raw)) return [];
|
|
73
71
|
const out: string[] = [];
|
|
74
72
|
for (const entry of raw) {
|
|
75
|
-
const scope =
|
|
73
|
+
const scope = stringOf(entry).trim();
|
|
76
74
|
if (scope !== '' && !/\s/.test(scope) && !out.includes(scope)) out.push(scope);
|
|
77
75
|
}
|
|
78
76
|
return out;
|
|
@@ -94,16 +92,16 @@ function toServer(
|
|
|
94
92
|
issuer: URL,
|
|
95
93
|
scopes: string[],
|
|
96
94
|
): OAuthServer {
|
|
97
|
-
const authorizationEndpoint =
|
|
98
|
-
const tokenEndpoint =
|
|
95
|
+
const authorizationEndpoint = stringOf(body.authorization_endpoint);
|
|
96
|
+
const tokenEndpoint = stringOf(body.token_endpoint);
|
|
99
97
|
if (authorizationEndpoint === '' || tokenEndpoint === '')
|
|
100
98
|
throw refused(
|
|
101
99
|
`${issuer.hostname} advertises OAuth but not where to sign in.`,
|
|
102
100
|
);
|
|
103
101
|
const methods = body.code_challenge_methods_supported;
|
|
104
|
-
const registration =
|
|
102
|
+
const registration = stringOf(body.registration_endpoint);
|
|
105
103
|
return {
|
|
106
|
-
issuer:
|
|
104
|
+
issuer: stringOf(body.issuer) === '' ? issuer.origin : stringOf(body.issuer),
|
|
107
105
|
authorizationEndpoint,
|
|
108
106
|
tokenEndpoint,
|
|
109
107
|
registrationEndpoint: registration === '' ? null : registration,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
import { errMsg, log } from '@metro-labs/core/log';
|
|
3
|
-
import { bodyField, readJsonBody, sendJson } from '@metro-labs/http/api-http';
|
|
3
|
+
import { bodyField, readJsonBody, sendJson, stringOf } from '@metro-labs/http/api-http';
|
|
4
4
|
import {
|
|
5
5
|
beginOAuth,
|
|
6
6
|
completeOAuth,
|
|
@@ -13,9 +13,6 @@ import { parseConnectorUrl } from './verify.js';
|
|
|
13
13
|
import type { OAuthAuth } from './verify.js';
|
|
14
14
|
import type { Connector, PendingConnectorInput } from './store.js';
|
|
15
15
|
|
|
16
|
-
const asText = (value: unknown): string =>
|
|
17
|
-
typeof value === 'string' ? value : '';
|
|
18
|
-
|
|
19
16
|
export function hostOf(url: string): string {
|
|
20
17
|
try {
|
|
21
18
|
return new URL(url).hostname;
|
|
@@ -38,7 +35,7 @@ export async function startOAuth(
|
|
|
38
35
|
payload: (row: Connector) => Record<string, unknown>,
|
|
39
36
|
): Promise<void> {
|
|
40
37
|
const url = parseConnectorUrl(bodyField(body, 'url'));
|
|
41
|
-
const returnTo =
|
|
38
|
+
const returnTo = stringOf(bodyField(body, 'returnTo'));
|
|
42
39
|
const clientId = bodyField(body, 'clientId');
|
|
43
40
|
const clientSecret = bodyField(body, 'clientSecret');
|
|
44
41
|
const prepared = await prepareOAuth({
|
|
@@ -74,7 +71,7 @@ export async function handleConnect(
|
|
|
74
71
|
const row = await deps.getConnector(id);
|
|
75
72
|
const url = parseConnectorUrl(row.url);
|
|
76
73
|
const body = await readJsonBody(req);
|
|
77
|
-
const returnTo =
|
|
74
|
+
const returnTo = stringOf(bodyField(body, 'returnTo'));
|
|
78
75
|
const prepared = await prepareOAuth({ url, client: row.client, returnTo });
|
|
79
76
|
const authorize = beginOAuth(prepared, {
|
|
80
77
|
name: row.name,
|
|
@@ -69,7 +69,7 @@ function pluginRootsUnder(dir: string, depth: number, out: string[]): void {
|
|
|
69
69
|
pluginRootsUnder(join(dir, entry.name), depth - 1, out);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
function stagedPluginDir(env: NodeJS.ProcessEnv = process.env): string | null {
|
|
73
73
|
const store = env.METRO_RUNTIME_STORE?.trim() ?? '';
|
|
74
74
|
if (store === '') return null;
|
|
75
75
|
const user = agentUser();
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
import { whyUnreachable } from './reach.js';
|
|
3
3
|
import { noteHealth } from './health.js';
|
|
4
|
-
import type { RelayTarget } from './
|
|
5
|
-
import type { AgentIdentity } from '@metro-labs/http/api-http';
|
|
4
|
+
import type { RelayTarget } from './store.js';
|
|
6
5
|
import { ApiError } from '@metro-labs/http/api-error';
|
|
7
6
|
import { errMsg, log } from '@metro-labs/core/log';
|
|
8
7
|
import { answerBlocked, mergeBlocked, screenCalls, type BlockedReason } from './relay-policy.js';
|
|
9
8
|
|
|
10
9
|
export interface RelayApiDeps {
|
|
11
10
|
target: (connectorId: string, force: boolean) => Promise<RelayTarget>;
|
|
12
|
-
identify: (req: IncomingMessage) =>
|
|
11
|
+
identify: (req: IncomingMessage) => { subject: string; agentId: string } | null;
|
|
13
12
|
signedOut: (connectorId: string) => void;
|
|
14
13
|
blocked?: BlockedReason;
|
|
15
14
|
}
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
type ConnectorSignIn,
|
|
31
31
|
} from './config.js';
|
|
32
32
|
import type { OAuthClient } from './oauth-client.js';
|
|
33
|
-
import { fixedTarget, unrefreshedTarget, type RelayTarget } from './relay-target.js';
|
|
34
33
|
import { agentsDir } from '../agents/files.js';
|
|
35
34
|
import { newId } from '@metro-labs/core/ids';
|
|
36
35
|
import type { LoadedConnector } from '../stations/materialize.js';
|
|
@@ -333,6 +332,34 @@ function refreshOnce(row: LocalConnectorRow, auth: OAuthAuth, dir: string): Prom
|
|
|
333
332
|
return job;
|
|
334
333
|
}
|
|
335
334
|
|
|
335
|
+
export type RelayTarget =
|
|
336
|
+
| { kind: 'ok'; url: string; headers: Record<string, string> }
|
|
337
|
+
| { kind: 'missing' }
|
|
338
|
+
| { kind: 'signin' };
|
|
339
|
+
|
|
340
|
+
function staleUsable(auth: OAuthAuth, now = Date.now()): boolean {
|
|
341
|
+
return auth.expiresAt === undefined || auth.expiresAt > now;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function unrefreshedTarget(
|
|
345
|
+
url: string,
|
|
346
|
+
auth: OAuthAuth,
|
|
347
|
+
force: boolean,
|
|
348
|
+
now = Date.now(),
|
|
349
|
+
): RelayTarget {
|
|
350
|
+
if (force) return { kind: 'signin' };
|
|
351
|
+
return { kind: 'ok', url, headers: staleUsable(auth, now) ? authHeaders(auth) : {} };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function fixedTarget(
|
|
355
|
+
url: string,
|
|
356
|
+
auth: Exclude<ConnectorAuth, OAuthAuth>,
|
|
357
|
+
force: boolean,
|
|
358
|
+
): RelayTarget {
|
|
359
|
+
if (force) return { kind: 'signin' };
|
|
360
|
+
return { kind: 'ok', url, headers: authHeaders(auth) };
|
|
361
|
+
}
|
|
362
|
+
|
|
336
363
|
async function oauthTarget(row: LocalConnectorRow, auth: OAuthAuth, force: boolean, dir: string): Promise<RelayTarget> {
|
|
337
364
|
if (!force && !oauthExpired(auth)) return { kind: 'ok', url: row.url, headers: authHeaders(auth) };
|
|
338
365
|
try {
|
|
@@ -56,7 +56,7 @@ export function attachmentEventUrl(
|
|
|
56
56
|
): string | null {
|
|
57
57
|
if (payload.contentType !== 'attachmentSaved') return null;
|
|
58
58
|
if (typeof payload.url === 'string' && payload.url.length > 0) return null;
|
|
59
|
-
const p = payload.attachmentPath
|
|
59
|
+
const p = payload.attachmentPath;
|
|
60
60
|
return typeof p === 'string' ? attachmentUrl(p, agentId) : null;
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -43,12 +43,12 @@ const PREFIXES: [RegExp, string][] = [
|
|
|
43
43
|
[/^ap-/, 'apac'],
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
function regionPrefix(region: string): string {
|
|
47
47
|
for (const [pattern, prefix] of PREFIXES) if (pattern.test(region)) return prefix;
|
|
48
48
|
return 'global';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
function bedrockModelId(requested: string, region: string): string {
|
|
52
52
|
if (requested.includes('anthropic.')) return requested;
|
|
53
53
|
return `${regionPrefix(region)}.anthropic.${requested}`;
|
|
54
54
|
}
|
|
@@ -63,7 +63,7 @@ function splitBetas(header: string | string[] | undefined): string[] {
|
|
|
63
63
|
.filter((b) => b !== '');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
function rewriteForBedrock(
|
|
67
67
|
body: Record<string, unknown>,
|
|
68
68
|
model: string,
|
|
69
69
|
betaHeader: string | string[] | undefined,
|
|
@@ -78,7 +78,7 @@ export function rewriteForBedrock(
|
|
|
78
78
|
return { modelId: bedrockModelId(model, region), stream: body.stream === true, body: out, betas };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
function errorKind(status: number, errorType: string | null): string {
|
|
82
82
|
if (status === 429 || errorType === 'ThrottlingException') return 'rate_limit_error';
|
|
83
83
|
if (status === 400) return 'invalid_request_error';
|
|
84
84
|
if (status === 401 || status === 403) return 'permission_error';
|
|
@@ -10,7 +10,7 @@ export const CODEX_ISSUER = 'https://auth.openai.com';
|
|
|
10
10
|
export const CODEX_REDIRECT = 'http://localhost:1455/auth/callback';
|
|
11
11
|
const SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke';
|
|
12
12
|
const AUTH_CLAIM = 'https://api.openai.com/auth';
|
|
13
|
-
|
|
13
|
+
const ACCESS_TOKEN_TTL_MS = 55 * 60_000;
|
|
14
14
|
|
|
15
15
|
export interface CodexTokens {
|
|
16
16
|
accessToken: string;
|
|
@@ -2,7 +2,7 @@ import { randomBytes } from 'node:crypto';
|
|
|
2
2
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
3
3
|
import { errMsg } from '@metro-labs/core/log';
|
|
4
4
|
import { CODEX_CLIENT_ID, CODEX_ISSUER, CodexAuthError, exchangeCode, type CodexTokens } from './codex-auth.js';
|
|
5
|
-
import { stringOf } from '
|
|
5
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
6
6
|
|
|
7
7
|
const VERIFY_PATH = '/codex/device';
|
|
8
8
|
const DEVICE_TTL_MS = 15 * 60_000;
|
|
@@ -2,7 +2,7 @@ import { isRecord } from '@metro-labs/core/is-record';
|
|
|
2
2
|
import { encodeSignature } from './codex-translate.js';
|
|
3
3
|
import { errorFrame } from './forward.js';
|
|
4
4
|
import { frame, messageEnd, messageStart, type Usage } from './frames.js';
|
|
5
|
-
import { stringOf } from '
|
|
5
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
6
6
|
|
|
7
7
|
type Item = Record<string, unknown>;
|
|
8
8
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
3
|
-
import { resultText
|
|
3
|
+
import { resultText } from './text.js';
|
|
4
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const SIGNATURE_PREFIX = 'metro-codex:';
|
|
6
7
|
const DEFAULT_EFFORT = 'medium';
|
|
7
8
|
const EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
8
9
|
const EFFORT_ALIAS: Record<string, string> = { max: 'xhigh' };
|
|
@@ -11,7 +11,7 @@ import type { Connection } from './model-config.js';
|
|
|
11
11
|
import { answerWhole, currentOf, errorKind, reach, refreshed, relayTranslated, sessionHeader, type TokenSource, type TokenState } from './subscription.js';
|
|
12
12
|
import { noteUsageHeaders } from './usage.js';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const CODEX_BASE = 'https://chatgpt.com/backend-api/codex';
|
|
15
15
|
const CODEX_VERSION = '0.153.4';
|
|
16
16
|
const VERSION_RE = /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.]+)?$/;
|
|
17
17
|
const INVALID = [400, 404, 422];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
2
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const MAIN_EFFORT = 'low';
|
|
5
|
+
const SUBAGENT_EFFORT = 'max';
|
|
6
6
|
export const BINDING_BETA = 'thinking-binding-controls-2026-08-01';
|
|
7
7
|
|
|
8
8
|
const SUBAGENT_HEADER = 'x-claude-code-agent-id';
|
|
@@ -56,7 +56,7 @@ function readHeaders(buf: Buffer): Record<string, string> {
|
|
|
56
56
|
return out;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
function decodeFrame(buf: Buffer, offset: number): { message: EventStreamMessage; end: number } | null {
|
|
60
60
|
if (buf.length - offset < PRELUDE_BYTES) return null;
|
|
61
61
|
const total = buf.readUInt32BE(offset);
|
|
62
62
|
const headersLen = buf.readUInt32BE(offset + 4);
|
|
@@ -11,7 +11,7 @@ export class GatewayError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const BODY_MAX = 64 * 1024 * 1024;
|
|
15
15
|
const DRAIN_FACTOR = 2;
|
|
16
16
|
const PING_MS = 25_000;
|
|
17
17
|
const PING_FRAME = 'event: ping\ndata: {"type":"ping"}\n\n';
|
|
@@ -40,9 +40,9 @@ export const errorFrame = (kind: string, message: string): string => `event: err
|
|
|
40
40
|
|
|
41
41
|
export const providerStatus = (status: number): number => (status === 401 ? 403 : status);
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
const idleMs = (): number => Number(process.env.METRO_GATEWAY_IDLE_MS) || IDLE_MS;
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const idleMessage = (ms: number): string => `metro gateway: the provider sent nothing for ${String(Math.round(ms / 1000))}s; giving up on this request`;
|
|
46
46
|
|
|
47
47
|
export interface Watch {
|
|
48
48
|
signal: AbortSignal;
|
|
@@ -24,8 +24,8 @@ import { forgetServed, noteServed } from './served.js';
|
|
|
24
24
|
import { forgetUsage, noteUsageHeaders, UsageScanner } from './usage.js';
|
|
25
25
|
import type { CodexTokens } from './codex-auth.js';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const GATEWAY_PREFIX = '/gateway';
|
|
28
|
+
const ANTHROPIC_BASE = 'https://api.anthropic.com';
|
|
29
29
|
const MESSAGES = '/v1/messages';
|
|
30
30
|
const COUNT = '/v1/messages/count_tokens';
|
|
31
31
|
const MODELS = '/v1/models';
|
|
@@ -178,7 +178,7 @@ async function toOpenRouter(
|
|
|
178
178
|
|
|
179
179
|
const thinkingOff = (body: Record<string, unknown>): boolean => isRecord(body.thinking) && body.thinking.type === 'disabled';
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
function openrouterBody(body: Record<string, unknown>, model: string, zdr: boolean): Record<string, unknown> {
|
|
182
182
|
const sent: Record<string, unknown> = { ...body, model };
|
|
183
183
|
if (thinkingOff(body)) delete sent.thinking;
|
|
184
184
|
const effort = effortToApply(body);
|
|
@@ -2,11 +2,11 @@ import { isRecord } from '@metro-labs/core/is-record';
|
|
|
2
2
|
import { PendingLogins } from './pkce.js';
|
|
3
3
|
import { nonEmpty } from './text.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const GEMINI_CLIENT_ID = '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';
|
|
6
|
+
const GEMINI_CLIENT_SECRET = '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';
|
|
7
|
+
const GEMINI_AUTH_BASE = 'https://accounts.google.com';
|
|
8
|
+
const GEMINI_TOKEN_BASE = 'https://oauth2.googleapis.com';
|
|
9
|
+
const GEMINI_REDIRECT = 'http://localhost:51121/oauth-callback';
|
|
10
10
|
const SCOPE = [
|
|
11
11
|
'https://www.googleapis.com/auth/cloud-platform',
|
|
12
12
|
'https://www.googleapis.com/auth/userinfo.email',
|
|
@@ -93,7 +93,7 @@ async function tokenCall(form: Record<string, string>, base: string, fetchImpl:
|
|
|
93
93
|
return body;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
function codeIn(pasted: string, state: string): string {
|
|
97
97
|
const trimmed = pasted.trim();
|
|
98
98
|
if (trimmed === '') throw new GeminiAuthError('paste the address the browser landed on, or the code in it');
|
|
99
99
|
if (!/^https?:\/\//i.test(trimmed)) return trimmed;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const CODE_ASSIST_BASE = 'https://cloudcode-pa.googleapis.com';
|
|
4
|
+
const CODE_ASSIST_DAILY = 'https://daily-cloudcode-pa.googleapis.com';
|
|
5
5
|
export const API = 'v1internal';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const APP_VERSION = '2.0.3';
|
|
7
|
+
const CLIENT_VERSION = '1.110.0';
|
|
8
8
|
export const CLIENT_NAME = 'antigravity';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const IDE_TYPE = 9;
|
|
10
|
+
const PLUGIN_TYPE = 2;
|
|
11
11
|
export const SYSTEM_PREFIX =
|
|
12
12
|
'You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Absolute paths only****Proactiveness**';
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ const PLATFORMS: Record<string, number> = { 'darwin-x64': 1, 'darwin-arm64': 2,
|
|
|
15
15
|
|
|
16
16
|
const osName = (): string => (process.platform === 'darwin' ? 'darwin' : process.platform === 'win32' ? 'win32' : 'linux');
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const platformCode = (): number => PLATFORMS[`${process.platform}-${process.arch}`] ?? 0;
|
|
19
19
|
|
|
20
20
|
export const clientMetadata = (project: string | null): Record<string, unknown> => ({
|
|
21
21
|
ideType: IDE_TYPE,
|
|
@@ -2,7 +2,7 @@ import { isRecord } from '@metro-labs/core/is-record';
|
|
|
2
2
|
import { errorFrame } from './forward.js';
|
|
3
3
|
import { frame, messageEnd, messageStart, type Usage } from './frames.js';
|
|
4
4
|
import { encodeSignature, newCallId, rememberSignature } from './gemini-translate.js';
|
|
5
|
-
import { stringOf } from '
|
|
5
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
6
6
|
|
|
7
7
|
type Item = Record<string, unknown>;
|
|
8
8
|
|
|
@@ -3,11 +3,12 @@ import { isRecord } from '@metro-labs/core/is-record';
|
|
|
3
3
|
import { ToolNames } from './codex-translate.js';
|
|
4
4
|
import { CLIENT_NAME, requestId, SYSTEM_PREFIX } from './gemini-client.js';
|
|
5
5
|
import { cappedEffort, effortToApply } from './effort.js';
|
|
6
|
-
import { resultText
|
|
6
|
+
import { resultText } from './text.js';
|
|
7
|
+
import { stringOf } from '@metro-labs/http/api-http';
|
|
7
8
|
|
|
8
9
|
type Item = Record<string, unknown>;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const SIGNATURE_PREFIX = 'metro-gemini:';
|
|
11
12
|
export const SKIP_SIGNATURE = 'skip_thought_signature_validator';
|
|
12
13
|
const SCHEMA_KEEP = new Set([
|
|
13
14
|
'type',
|
|
@@ -91,7 +91,7 @@ const detailText = (detail: Record<string, unknown>): string[] => {
|
|
|
91
91
|
return out.filter((x) => x !== '');
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
function refusalMessage(text: string, status: number): string {
|
|
95
95
|
const message = upstreamMessage(text, `Gemini answered ${String(status)}`);
|
|
96
96
|
let details: unknown = null;
|
|
97
97
|
try {
|