@ai21/gateway 0.5.3

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.
Files changed (44) hide show
  1. package/README.md +91 -0
  2. package/dist/args.d.ts +74 -0
  3. package/dist/args.js +304 -0
  4. package/dist/args.js.map +1 -0
  5. package/dist/cli.d.ts +10 -0
  6. package/dist/cli.js +140 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.d.ts +76 -0
  9. package/dist/config.js +187 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/login/browser.d.ts +7 -0
  12. package/dist/login/browser.js +41 -0
  13. package/dist/login/browser.js.map +1 -0
  14. package/dist/login/contract.d.ts +42 -0
  15. package/dist/login/contract.js +60 -0
  16. package/dist/login/contract.js.map +1 -0
  17. package/dist/login/loopback.d.ts +42 -0
  18. package/dist/login/loopback.js +192 -0
  19. package/dist/login/loopback.js.map +1 -0
  20. package/dist/login/session.d.ts +36 -0
  21. package/dist/login/session.js +75 -0
  22. package/dist/login/session.js.map +1 -0
  23. package/dist/prompt.d.ts +32 -0
  24. package/dist/prompt.js +72 -0
  25. package/dist/prompt.js.map +1 -0
  26. package/dist/report.d.ts +50 -0
  27. package/dist/report.js +214 -0
  28. package/dist/report.js.map +1 -0
  29. package/dist/result.d.ts +26 -0
  30. package/dist/result.js +22 -0
  31. package/dist/result.js.map +1 -0
  32. package/dist/revert.d.ts +60 -0
  33. package/dist/revert.js +230 -0
  34. package/dist/revert.js.map +1 -0
  35. package/dist/router.d.ts +21 -0
  36. package/dist/router.js +271 -0
  37. package/dist/router.js.map +1 -0
  38. package/dist/snippets.d.ts +52 -0
  39. package/dist/snippets.js +71 -0
  40. package/dist/snippets.js.map +1 -0
  41. package/dist/verify.d.ts +96 -0
  42. package/dist/verify.js +309 -0
  43. package/dist/verify.js.map +1 -0
  44. package/package.json +61 -0
package/dist/verify.js ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * One real request through the gateway, to prove the written config works and flip
3
+ * the agent's `used` latch. Three tiers, because the CLI holds no Anthropic
4
+ * credential and the gateway requires one: `claude -p` if it is on PATH, else a
5
+ * direct ping if a key is available, else skip.
6
+ *
7
+ * Two invariants: verification never changes the exit code, and exactly one
8
+ * request is fired — a winning tier means no later tier runs.
9
+ */
10
+ import { spawn } from "node:child_process";
11
+ import { accessSync, constants, statSync } from "node:fs";
12
+ import { DEFAULT_URLS, isInsecureGateway } from "./args.js";
13
+ export const CLAUDE_COMMAND = "claude";
14
+ /* An instruction with a trivial answer, and it names itself so a gateway trace shows
15
+ * why the request happened. Both halves matter: "ai21-gateway CLI verification" alone
16
+ * reads as a task label, and Claude Code answered it by exploring the repo and hunting
17
+ * for tools — measured at >90s against ~5s for this. */
18
+ export const CLAUDE_PROMPT = "ai21-gateway CLI connectivity check — reply with OK and nothing else.";
19
+ export const PING_PATH = "/v1/messages";
20
+ /** Names the directory, since `--repo` and `--user` verify from different ones. Ends
21
+ * without a full stop: a `reason` must not, and report.ts punctuates for it. */
22
+ function manualHint(cwd) {
23
+ return `Run \`${CLAUDE_COMMAND} -p "${CLAUDE_PROMPT}"\` in ${cwd} to verify by hand`;
24
+ }
25
+ /**
26
+ * Both tiers are bounded; the spawn gets longer because it waits on a full model
27
+ * round trip. 20s is measured, not guessed: a working `claude -p` returns in ~5s,
28
+ * and a rejected one hangs rather than failing, so this budget is what turns that
29
+ * hang into a message.
30
+ */
31
+ export const PING_TIMEOUT_MS = 10_000;
32
+ export const CLAUDE_TIMEOUT_MS = 20_000;
33
+ /** Mirrors the `genericCurl` snippet; a contract test holds the two together. */
34
+ export const PING_BODY = {
35
+ model: "claude-sonnet-4-5",
36
+ max_tokens: 64,
37
+ messages: [{ role: "user", content: "Hello through the gateway" }],
38
+ };
39
+ /** npm installs the Claude Code CLI as `claude.cmd`, so this must not be empty. */
40
+ const DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
41
+ /**
42
+ * Hand-rolled rather than shelling out to `which`/`where`, which is slower and
43
+ * absent from stripped Windows images. `node:path`'s `join` and `delimiter` follow
44
+ * the *host* platform, so they cannot resolve a Windows PATH under test on macOS.
45
+ */
46
+ export function findOnPath(command, lookup) {
47
+ const windows = lookup.platform === "win32";
48
+ const separator = windows ? ";" : ":";
49
+ const raw = lookup.processEnv.PATH ?? (windows ? lookup.processEnv.Path : undefined) ?? "";
50
+ const extensions = windows ? (lookup.processEnv.PATHEXT ?? DEFAULT_PATHEXT).split(";") : [""];
51
+ for (const dir of raw.split(separator)) {
52
+ if (dir === "")
53
+ continue;
54
+ for (const extension of extensions) {
55
+ const candidate = `${dir.replace(/[\\/]+$/, "")}${windows ? "\\" : "/"}${command}${extension}`;
56
+ if (lookup.isExecutableFile(candidate))
57
+ return candidate;
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ /** Blanks every credential in play, wherever it appears. */
63
+ function redactSecrets(text, ctx) {
64
+ let redacted = text;
65
+ for (const secret of [ctx.ai21Key, resolveAnthropicKey(ctx)]) {
66
+ if (secret === undefined || secret === "")
67
+ continue;
68
+ redacted = redacted.split(secret).join("****");
69
+ }
70
+ return redacted;
71
+ }
72
+ const MAX_REASON_LENGTH = 200;
73
+ /** First line only, scrubbed and bounded — a reason, not a log dump. */
74
+ function quoteOutput(output, ctx) {
75
+ const [firstLine = ""] = redactSecrets(output, ctx).trim().split("\n");
76
+ const trimmed = firstLine.trim();
77
+ if (trimmed === "")
78
+ return "";
79
+ return trimmed.length > MAX_REASON_LENGTH ? `: ${trimmed.slice(0, MAX_REASON_LENGTH)}…` : `: ${trimmed}`;
80
+ }
81
+ function resolveAnthropicKey(ctx) {
82
+ if (ctx.anthropicKey !== undefined && ctx.anthropicKey !== "")
83
+ return ctx.anthropicKey;
84
+ // Set-but-empty is how people disable it; treating that as a credential would
85
+ // turn a clean skip into a guaranteed 401.
86
+ const fromEnv = ctx.processEnv.ANTHROPIC_API_KEY;
87
+ return fromEnv !== undefined && fromEnv !== "" ? fromEnv : undefined;
88
+ }
89
+ /**
90
+ * The spawned `claude` inherits our environment, but settings.json still wins:
91
+ * measured with two loopback servers, every model request used the file's
92
+ * ANTHROPIC_BASE_URL and headers, not exported ones. So no warning is owed for
93
+ * those two variables — only for ANTHROPIC_API_KEY, which we never write.
94
+ */
95
+ async function viaClaude(executable, ctx, deps) {
96
+ const result = await deps.runCommand(executable, ["-p", CLAUDE_PROMPT], {
97
+ cwd: ctx.cwd,
98
+ timeoutMs: CLAUDE_TIMEOUT_MS,
99
+ });
100
+ if (result.code === 0)
101
+ return { kind: "passed", via: "claude-cli" };
102
+ const failed = (reason) => ({ kind: "failed", via: "claude-cli", reason });
103
+ if (result.timedOut) {
104
+ // `claude` prints nothing while retrying, so the timeout alone cannot say which
105
+ // credential failed; one probe can. A second request is allowed here only because
106
+ // it follows a failure and cannot authenticate, so it never flips `used`.
107
+ const timedOut = `\`${CLAUDE_COMMAND} -p\` did not answer within ${CLAUDE_TIMEOUT_MS / 1000}s`;
108
+ switch (await probeCredentials(ctx, deps)) {
109
+ case "ai21-rejected":
110
+ return failed(`${timedOut}, and the gateway rejected the AI21 key${environmentNote(ctx)}`);
111
+ case "ai21-accepted":
112
+ /* Deliberately not "check the Anthropic credential": the gateway answering means
113
+ * the config is right, and the likeliest cause is `claude` itself being slow or
114
+ * busy in this directory. Running the prompt by hand settles it in one step. */
115
+ return failed(`${timedOut}, but the gateway accepted the AI21 key — so the configuration is good and ` +
116
+ `\`${CLAUDE_COMMAND}\` itself did not answer in time. ${manualHint(ctx.cwd)}`);
117
+ case "inconclusive":
118
+ return failed(`${timedOut}, which usually means the gateway rejected the credentials and \`${CLAUDE_COMMAND}\` is ` +
119
+ `retrying silently — check the AI21 key and the agent id${environmentNote(ctx)}`);
120
+ }
121
+ }
122
+ if (result.code === null) {
123
+ return failed(`\`${CLAUDE_COMMAND}\` could not be run${quoteOutput(result.output, ctx)}`);
124
+ }
125
+ return failed(`\`${CLAUDE_COMMAND} -p\` exited ${result.code}${quoteOutput(result.output, ctx)}`);
126
+ }
127
+ /** A production key sent to staging is refused indistinguishably from a typo. */
128
+ function environmentNote(ctx) {
129
+ return ctx.urls.gateway === DEFAULT_URLS.gateway
130
+ ? ""
131
+ : /* Carries its own leading separator and no trailing full stop, because it is
132
+ * optional and `report.ts` composes the reason into a sentence with a full stop of
133
+ * its own. A call site that punctuates *before* this note prints "the AI21 key.."
134
+ * whenever the note is empty — which is every production run. */
135
+ ". AI21 keys are per-environment: a production key will not work against this gateway";
136
+ }
137
+ /** Explanations for the statuses users actually hit, keyed by what to check. */
138
+ const STATUS_HINTS = {
139
+ 401: "the AI21 key or the Anthropic credential was rejected",
140
+ 403: "the key is not allowed to use this agent",
141
+ 404: "check the gateway URL",
142
+ };
143
+ /** A value that cannot be a real credential, so the probe can never succeed. */
144
+ const PROBE_ANTHROPIC_KEY = "sk-ant-invalid-credential-probe";
145
+ function classifyRejection(body) {
146
+ let parsed;
147
+ try {
148
+ parsed = JSON.parse(body);
149
+ }
150
+ catch {
151
+ return "inconclusive";
152
+ }
153
+ if (typeof parsed !== "object" || parsed === null)
154
+ return "inconclusive";
155
+ const shape = parsed;
156
+ // Anthropic answers `{"type":"error","error":{…}}`, the gateway `{"detail":"…"}`.
157
+ // Reaching Anthropic at all proves the gateway forwarded us.
158
+ if (shape.error !== undefined || shape.type === "error")
159
+ return "ai21-accepted";
160
+ if (shape.detail !== undefined)
161
+ return "ai21-rejected";
162
+ return "inconclusive";
163
+ }
164
+ async function probeCredentials(ctx, deps) {
165
+ // Diagnosing is not a reason to put the key on the wire in the clear.
166
+ if (isInsecureGateway(ctx.urls.gateway))
167
+ return "inconclusive";
168
+ try {
169
+ const response = await deps.fetchImpl(`${ctx.urls.gateway}${PING_PATH}`, {
170
+ method: "POST",
171
+ headers: pingHeaders(PROBE_ANTHROPIC_KEY, ctx),
172
+ body: JSON.stringify(PING_BODY),
173
+ signal: AbortSignal.timeout(PING_TIMEOUT_MS),
174
+ });
175
+ // Cannot happen with an invalid key, but if it did the AI21 side is fine.
176
+ if (response.ok)
177
+ return "ai21-accepted";
178
+ return classifyRejection((await response.text?.()) ?? "");
179
+ }
180
+ catch {
181
+ return "inconclusive";
182
+ }
183
+ }
184
+ function pingHeaders(anthropicKey, ctx) {
185
+ const headers = {
186
+ "x-ai21-key": ctx.ai21Key,
187
+ "x-agent-id": ctx.agentId,
188
+ "x-api-key": anthropicKey,
189
+ "anthropic-version": "2023-06-01",
190
+ "content-type": "application/json",
191
+ };
192
+ if (ctx.userId !== undefined)
193
+ headers["x-ai21-user-id"] = ctx.userId;
194
+ return headers;
195
+ }
196
+ function isAbort(err) {
197
+ return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
198
+ }
199
+ async function viaDirectPing(anthropicKey, ctx, deps) {
200
+ const failed = (reason) => ({ kind: "failed", via: "direct-ping", reason });
201
+ // This carries both credentials, so an off-box plain-http endpoint would put them
202
+ // on the wire in the clear. Refused rather than sent; the write still stands.
203
+ if (isInsecureGateway(ctx.urls.gateway)) {
204
+ return failed(`refusing to send credentials to ${ctx.urls.gateway} over plain http; use https, or verify by hand`);
205
+ }
206
+ try {
207
+ const response = await deps.fetchImpl(`${ctx.urls.gateway}${PING_PATH}`, {
208
+ method: "POST",
209
+ headers: pingHeaders(anthropicKey, ctx),
210
+ body: JSON.stringify(PING_BODY),
211
+ signal: AbortSignal.timeout(PING_TIMEOUT_MS),
212
+ });
213
+ if (response.ok)
214
+ return { kind: "passed", via: "direct-ping" };
215
+ const hint = STATUS_HINTS[response.status];
216
+ const note = response.status === 401 ? environmentNote(ctx) : "";
217
+ return failed(`the gateway answered ${response.status}${hint === undefined ? "" : ` (${hint})`}${note}`);
218
+ }
219
+ catch (err) {
220
+ if (isAbort(err)) {
221
+ return failed(`the gateway did not answer within ${PING_TIMEOUT_MS / 1000}s`);
222
+ }
223
+ // The cause of a fetch rejection is a DNS/TLS/socket detail that reads as
224
+ // noise and can carry an internal hostname, so it is named, not quoted.
225
+ return failed(`the gateway could not be reached at ${ctx.urls.gateway}`);
226
+ }
227
+ }
228
+ export async function verify(ctx, deps) {
229
+ const executable = findOnPath(CLAUDE_COMMAND, deps.lookup);
230
+ if (executable !== undefined)
231
+ return viaClaude(executable, ctx, deps);
232
+ const anthropicKey = resolveAnthropicKey(ctx);
233
+ if (anthropicKey !== undefined)
234
+ return viaDirectPing(anthropicKey, ctx, deps);
235
+ return { kind: "skipped", hint: `${manualHint(ctx.cwd)}.` };
236
+ }
237
+ /** Cap on captured subprocess output — only the first line is ever quoted. */
238
+ const MAX_CAPTURED_OUTPUT = 4_096;
239
+ function runCommand(executable, args, opts) {
240
+ return new Promise((resolve) => {
241
+ // A .cmd cannot be spawned without a shell since the CVE-2024-27980 fix. Safe
242
+ // here because every argument is a literal. Killing the shell may orphan the
243
+ // real `claude`, which exits on its own shortly after.
244
+ const shell = /\.(cmd|bat)$/i.test(executable);
245
+ const child = spawn(shell ? `"${executable}"` : executable, [...args], {
246
+ cwd: opts.cwd,
247
+ shell,
248
+ // stdin closed: `claude` must never sit waiting on input we will not send.
249
+ stdio: ["ignore", "pipe", "pipe"],
250
+ });
251
+ let output = "";
252
+ const capture = (chunk) => {
253
+ if (output.length < MAX_CAPTURED_OUTPUT)
254
+ output += chunk.toString("utf8");
255
+ };
256
+ child.stdout?.on("data", capture);
257
+ child.stderr?.on("data", capture);
258
+ let done = false;
259
+ const settle = (result) => {
260
+ if (done)
261
+ return;
262
+ done = true;
263
+ clearTimeout(timer);
264
+ resolve(result);
265
+ };
266
+ /**
267
+ * Our own timer, not spawn's `timeout`: Windows reports a killed process as an
268
+ * ordinary non-zero exit, so an inferred timeout would be wrong there.
269
+ *
270
+ * It resolves the promise itself rather than killing and waiting for `close`.
271
+ * `close` fires when the *stdio* closes, and anything `claude` spawned inherits
272
+ * those pipes — an MCP server or a hook outliving its parent holds them open, so
273
+ * the kill lands and `close` never comes. That hung the CLI indefinitely after a
274
+ * write it had already completed. The budget is now a guarantee, not a hope.
275
+ */
276
+ let timedOut = false;
277
+ const timer = setTimeout(() => {
278
+ timedOut = true;
279
+ child.kill("SIGKILL");
280
+ settle({ code: null, timedOut, output: output.trim() });
281
+ }, opts.timeoutMs);
282
+ // A spawn error may never emit `close`, so both paths settle. Only the errno is
283
+ // kept — the message carries an absolute path.
284
+ child.once("error", (err) => settle({ code: null, timedOut, output: err.code ?? "" }));
285
+ child.once("close", (code) => settle({ code, timedOut, output: output.trim() }));
286
+ });
287
+ }
288
+ function isExecutableFile(candidate) {
289
+ try {
290
+ if (!statSync(candidate).isFile())
291
+ return false;
292
+ // The execute bit is meaningless on Windows, where the extension decides.
293
+ if (process.platform !== "win32")
294
+ accessSync(candidate, constants.X_OK);
295
+ return true;
296
+ }
297
+ catch {
298
+ return false;
299
+ }
300
+ }
301
+ /** The real-world adapters, kept here so `cli.ts` stays a thin edge. */
302
+ export function nodeVerifyDeps(processEnv) {
303
+ return {
304
+ lookup: { platform: process.platform, processEnv, isExecutableFile },
305
+ runCommand,
306
+ fetchImpl: fetch,
307
+ };
308
+ }
309
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@ai21/gateway",
3
+ "//version": "Placeholder. The source of truth is the Artifact Registry — CI sets this from NODE_PACKAGE_VERSION at publish time (see the Semantic Release user guide).",
4
+ "version": "0.5.3",
5
+ "description": "One command to point a local coding agent at the AI21 Intelligent Gateway",
6
+ "type": "module",
7
+ "bin": {
8
+ "ai21-gateway": "dist/cli.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "lint": "eslint src test --ext .ts --report-unused-disable-directives --max-warnings 0",
19
+ "lint:fix": "eslint src test --ext .ts --fix",
20
+ "typecheck": "tsc --noEmit",
21
+ "format": "prettier --write \"(src|test)/**\" --no-error-on-unmatched-pattern",
22
+ "format:check": "prettier --check \"(src|test)/**\" --no-error-on-unmatched-pattern",
23
+ "quality": "npm audit --omit=dev --audit-level=moderate && npm run lint && npm run typecheck && npm run format:check",
24
+ "test": "npm run build && npm run typecheck:test && node --disable-warning=ExperimentalWarning --experimental-strip-types --test",
25
+ "precommit": "npm run format:check && npm run lint && npm run typecheck && npm test",
26
+ "pack": "npm pack",
27
+ "prepack": "npm run build",
28
+ "typecheck:test": "tsc -p tsconfig.test.json"
29
+ },
30
+ "prettier": {
31
+ "printWidth": 120,
32
+ "trailingComma": "all"
33
+ },
34
+ "keywords": [
35
+ "ai21",
36
+ "gateway",
37
+ "claude-code",
38
+ "cli"
39
+ ],
40
+ "license": "UNLICENSED",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/AI21Labs/ai21-intelligent-gateway-cli.git"
44
+ },
45
+ "//overrides": "eslint 8 (required by @ai21/eslint-plugin's peer range) reaches brace-expansion <=5.0.7 via minimatch, which carries a DoS advisory with no patch in the 1.x/2.x lines. 5.0.8 is the only patched release and is dual CJS/ESM, so it drops in. Dev-only, but this keeps `npm audit` clean rather than muted.",
46
+ "overrides": {
47
+ "brace-expansion": "5.0.8"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "20.19.9",
51
+ "@typescript-eslint/eslint-plugin": "8.46.2",
52
+ "@typescript-eslint/parser": "8.46.2",
53
+ "eslint": "8.57.1",
54
+ "eslint-config-prettier": "9.1.0",
55
+ "prettier": "3.6.2",
56
+ "typescript": "5.9.2"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ }
61
+ }