@ai21/gateway 1.1.0 → 1.3.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 +16 -3
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +38 -4
- package/dist/cli.js.map +1 -1
- package/dist/login/lock.d.ts +6 -0
- package/dist/login/lock.js +16 -1
- package/dist/login/lock.js.map +1 -1
- package/dist/login/session.d.ts +12 -0
- package/dist/login/session.js +23 -5
- package/dist/login/session.js.map +1 -1
- package/dist/precheck.d.ts +1 -0
- package/dist/precheck.js +1 -1
- package/dist/precheck.js.map +1 -1
- package/dist/prompt.d.ts +5 -7
- package/dist/prompt.js +23 -1
- package/dist/prompt.js.map +1 -1
- package/dist/report.d.ts +4 -0
- package/dist/report.js +59 -17
- package/dist/report.js.map +1 -1
- package/dist/result.d.ts +2 -0
- package/dist/result.js +2 -0
- package/dist/result.js.map +1 -1
- package/dist/router.d.ts +6 -0
- package/dist/router.js +157 -31
- package/dist/router.js.map +1 -1
- package/dist/status.d.ts +31 -0
- package/dist/status.js +107 -0
- package/dist/status.js.map +1 -0
- package/dist/telemetry/amplitudeHttp.d.ts +28 -0
- package/dist/telemetry/amplitudeHttp.js +53 -0
- package/dist/telemetry/amplitudeHttp.js.map +1 -0
- package/dist/telemetry/events.d.ts +46 -0
- package/dist/telemetry/events.js +10 -0
- package/dist/telemetry/events.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +27 -0
- package/dist/telemetry/telemetry.js +78 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/telemetry/transport.d.ts +10 -0
- package/dist/telemetry/transport.js +2 -0
- package/dist/telemetry/transport.js.map +1 -0
- package/dist/verify.d.ts +2 -0
- package/dist/verify.js +2 -1
- package/dist/verify.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,14 +15,14 @@ sends one request to prove it works. With no key stored it signs you in through
|
|
|
15
15
|
browser first and creates an AI21 key for you, all in the one command — pass `--key`
|
|
16
16
|
only for scripting, since a key in argv stays in your shell history.
|
|
17
17
|
|
|
18
|
-
The key is checked against the gateway
|
|
18
|
+
The key is checked against the gateway _before_ anything is written, so a key the
|
|
19
19
|
gateway will not take — or an agent id the key does not carry — stops the command
|
|
20
20
|
instead of leaving a config that fails every later request. There is no flag to skip
|
|
21
21
|
that check, and it needs none: only a definite rejection stops the install. If the
|
|
22
22
|
gateway cannot be reached, or answers something we cannot read, the config is written
|
|
23
23
|
and a warning says the key went unchecked.
|
|
24
24
|
|
|
25
|
-
`--no-verify` turns off the
|
|
25
|
+
`--no-verify` turns off the _post-write_ check — the one that proves end to end that
|
|
26
26
|
Claude Code reaches the gateway. It does not affect the pre-write key check.
|
|
27
27
|
|
|
28
28
|
The gateway passes your own Claude credential through, so without a Claude login it
|
|
@@ -32,6 +32,11 @@ leaves `settings.json` as it was before the run, and exits 9. A timeout or any o
|
|
|
32
32
|
verification failure only warns and keeps the config. `--no-verify` skips this too, for
|
|
33
33
|
machines where Claude Code is signed in later.
|
|
34
34
|
|
|
35
|
+
The gateway currently supports only the Claude Code CLI. With no `claude` on PATH and
|
|
36
|
+
no Anthropic key (`--anthropic-key` or `ANTHROPIC_API_KEY`), `install` stops before
|
|
37
|
+
signing in, writes nothing, and exits 10, even under `--no-verify`. With an Anthropic
|
|
38
|
+
key it goes ahead and verifies with a direct request instead.
|
|
39
|
+
|
|
35
40
|
The dashboard prints this command for you (Add agent, or Reconnect on an existing
|
|
36
41
|
agent) and offers the key as a separate copy.
|
|
37
42
|
|
|
@@ -95,6 +100,14 @@ the only one supported so far — an unsupported name is refused rather than
|
|
|
95
100
|
defaulted, so a `--client` we do not implement never writes Claude Code's file by
|
|
96
101
|
mistake.
|
|
97
102
|
|
|
103
|
+
`install` reports onboarding events to AI21's product analytics (Amplitude), so we can
|
|
104
|
+
see where setup fails: when it started, then whether it completed or at which step it
|
|
105
|
+
failed, with the CLI version, OS, workspace id and a random per-run id. A failure is
|
|
106
|
+
reported as a fixed reason, never the message you saw, so no key, email, path, agent id
|
|
107
|
+
or argument is ever sent. Setup counts as completed only once the written config has
|
|
108
|
+
been verified. Only the production and staging gateways report; any other `--gateway`
|
|
109
|
+
sends nothing. Reporting never delays or fails a command (`src/telemetry/`).
|
|
110
|
+
|
|
98
111
|
> **Internal only.** The package is published to AI21's Artifact Registry, not
|
|
99
112
|
> public npm, so a bare `npx` cannot reach it yet — see below.
|
|
100
113
|
|
|
@@ -116,7 +129,7 @@ last tag. `package.json` stays at `0.0.0`; the git tag is the record.
|
|
|
116
129
|
`403` misleads: it reads as though the AI21 gateway rejected you, but it is npm
|
|
117
130
|
failing to reach the registry — before the CLI has run at all.
|
|
118
131
|
|
|
119
|
-
The last one catches people out: this project
|
|
132
|
+
The last one catches people out: this project _is_ `@ai21/gateway`, so npx
|
|
120
133
|
resolves the name to the working directory instead of the registry, finds no
|
|
121
134
|
linked binary, and fails. The published artifact can never be exercised from its
|
|
122
135
|
own source tree — use a scratch directory.
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type RunResult } from "./result.js";
|
|
3
3
|
export { EXIT, type RunResult } from "./result.js";
|
|
4
|
-
export declare const USAGE = "ai21-gateway \u2014 point a local coding agent at the AI21 Intelligent Gateway\n\nUsage:\n ai21-gateway install --agent-id <id> configure this machine (default command)\n ai21-gateway login sign in and create an AI21 key\n ai21-gateway uninstall revert what this tool wrote\n ai21-gateway --help\n ai21-gateway --version\n\nInstall flags:\n --agent-id <id> agent to attribute requests to (required)\n --key <key> AI21 key. Omit it and the CLI uses the key from your signed-in\n session, signing you in through the browser if you are not\n already. A key passed here is kept in your shell history\n --workspace <id> workspace to create the key in; the default is chosen for you\n --user-id <id> adds the x-ai21-user-id attribution header\n --client <name> which agent to configure; 'claude-code' (default) is the only\n one supported so far\n --gateway <url> override the gateway URL outright\n --user | --repo write to the home directory (default) or repo-local\n --yes overwrite an existing conflicting value\n --print render the resolved configuration instead of writing it\n (the key is always masked)\n --json machine-readable output\n\nLogin flags:\n --workspace <id> workspace to create the key in. Without it one is chosen for\n you and named in the output\n --gateway <url> override the gateway URL outright\n --app <url> the webapp origin serving the sign-in page\n\n'login' opens your browser, creates an AI21 key in your workspace and stores it\nunder ~/.config/ai21-gateway. 'install' does this for you when you have no key\nyet, so it is only needed on its own to sign in ahead of time or to move to a\ndifferent workspace.\n\nUninstall flags:\n --agent-id <id> guard: refuse unless the stored agent matches\n --from-backup restore settings.json.bak verbatim instead of removing\n only this tool's keys \u2014 discards later edits\n --yes proceed with a --from-backup restore that discards changes\n --user | --repo which settings.json to revert (home by default)\n\nBy default 'uninstall' removes only the keys this tool wrote, leaving unrelated\nsettings and any later edits of your own intact.\n\nVerification flags:\n --anthropic-key <key> Anthropic credential for the verifying request; used\n only when 'claude' is not on PATH, never written to disk\n --no-verify write the configuration without verifying it\n\nAfter a successful write the CLI sends one request through the gateway to confirm\nthe configuration works: 'claude -p' when it is on PATH, otherwise a direct\nrequest if an Anthropic credential is available. With neither,
|
|
4
|
+
export declare const USAGE = "ai21-gateway \u2014 point a local coding agent at the AI21 Intelligent Gateway\n\nUsage:\n ai21-gateway install --agent-id <id> configure this machine (default command)\n ai21-gateway login sign in and create an AI21 key\n ai21-gateway uninstall revert what this tool wrote\n ai21-gateway --help\n ai21-gateway --version\n\nInstall flags:\n --agent-id <id> agent to attribute requests to (required)\n --key <key> AI21 key. Omit it and the CLI uses the key from your signed-in\n session, signing you in through the browser if you are not\n already. A key passed here is kept in your shell history\n --workspace <id> workspace to create the key in; the default is chosen for you\n --user-id <id> adds the x-ai21-user-id attribution header\n --client <name> which agent to configure; 'claude-code' (default) is the only\n one supported so far\n --gateway <url> override the gateway URL outright\n --user | --repo write to the home directory (default) or repo-local\n --yes overwrite an existing conflicting value\n --print render the resolved configuration instead of writing it\n (the key is always masked)\n --json machine-readable output\n\nLogin flags:\n --workspace <id> workspace to create the key in. Without it one is chosen for\n you and named in the output\n --gateway <url> override the gateway URL outright\n --app <url> the webapp origin serving the sign-in page\n\n'login' opens your browser, creates an AI21 key in your workspace and stores it\nunder ~/.config/ai21-gateway. 'install' does this for you when you have no key\nyet, so it is only needed on its own to sign in ahead of time or to move to a\ndifferent workspace.\n\nUninstall flags:\n --agent-id <id> guard: refuse unless the stored agent matches\n --from-backup restore settings.json.bak verbatim instead of removing\n only this tool's keys \u2014 discards later edits\n --yes proceed with a --from-backup restore that discards changes\n --user | --repo which settings.json to revert (home by default)\n\nBy default 'uninstall' removes only the keys this tool wrote, leaving unrelated\nsettings and any later edits of your own intact.\n\nVerification flags:\n --anthropic-key <key> Anthropic credential for the verifying request; used\n only when 'claude' is not on PATH, never written to disk\n --no-verify write the configuration without verifying it\n\nAfter a successful write the CLI sends one request through the gateway to confirm\nthe configuration works: 'claude -p' when it is on PATH, otherwise a direct\nrequest if an Anthropic credential is available. With neither, 'install' stops\nbefore signing in and writes nothing: only the Claude Code CLI is supported.\n\nRe-running the same command is a no-op. An existing settings.json is backed up\nto settings.json.bak before it is changed, and is left untouched if the CLI\nrefuses.";
|
|
5
5
|
export declare function readVersion(): string;
|
|
6
6
|
/**
|
|
7
7
|
* Pure entry point: argv in, streams-and-exit-code out. Keeping I/O at the edge
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,12 @@ import { nodeBrowserDeps, openBrowser } from "./login/browser.js";
|
|
|
12
12
|
import { selectSecretStore } from "./login/keychain/select.js";
|
|
13
13
|
import { runCredentialCommand } from "./login/keychain/runner.js";
|
|
14
14
|
import { startLoopback } from "./login/loopback.js";
|
|
15
|
+
import { createStatusLine, isInteractive } from "./status.js";
|
|
16
|
+
import { waitForEnter } from "./prompt.js";
|
|
17
|
+
import { FLUSH_CAP_MS, nodeTelemetry } from "./telemetry/telemetry.js";
|
|
15
18
|
export { EXIT } from "./result.js";
|
|
19
|
+
/** 128 + SIGINT: what a shell reports for Ctrl-C, so scripts see the usual code. */
|
|
20
|
+
const EXIT_INTERRUPTED = 130;
|
|
16
21
|
export const USAGE = `ai21-gateway — point a local coding agent at the AI21 Intelligent Gateway
|
|
17
22
|
|
|
18
23
|
Usage:
|
|
@@ -66,8 +71,8 @@ Verification flags:
|
|
|
66
71
|
|
|
67
72
|
After a successful write the CLI sends one request through the gateway to confirm
|
|
68
73
|
the configuration works: 'claude -p' when it is on PATH, otherwise a direct
|
|
69
|
-
request if an Anthropic credential is available. With neither,
|
|
70
|
-
|
|
74
|
+
request if an Anthropic credential is available. With neither, 'install' stops
|
|
75
|
+
before signing in and writes nothing: only the Claude Code CLI is supported.
|
|
71
76
|
|
|
72
77
|
Re-running the same command is a no-op. An existing settings.json is backed up
|
|
73
78
|
to settings.json.bak before it is changed, and is left untouched if the CLI
|
|
@@ -84,6 +89,15 @@ export function readVersion() {
|
|
|
84
89
|
}
|
|
85
90
|
return version;
|
|
86
91
|
}
|
|
92
|
+
/** A corrupt install still reports, rather than failing a command over analytics. */
|
|
93
|
+
function versionForTelemetry() {
|
|
94
|
+
try {
|
|
95
|
+
return readVersion();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return "unknown";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
87
101
|
function versionResult() {
|
|
88
102
|
try {
|
|
89
103
|
return { code: EXIT.OK, out: `${readVersion()}\n`, err: "" };
|
|
@@ -116,12 +130,22 @@ export async function run(argv) {
|
|
|
116
130
|
case "error":
|
|
117
131
|
return { code: EXIT.USAGE, out: "", err: `${outcome.message}\n\n${USAGE}\n` };
|
|
118
132
|
case "args": {
|
|
133
|
+
// `--json` keeps stderr to plain lines too: a consumer may be capturing both streams.
|
|
134
|
+
const status = createStatusLine(process.stderr, !outcome.args.json && isInteractive(process.stderr, process.env));
|
|
119
135
|
// One stream for login's progress and credential-file warnings, so neither is missed.
|
|
120
|
-
|
|
136
|
+
// Routed through the status line so a message never lands in the middle of the spinner.
|
|
137
|
+
const notify = (message) => status.log(message);
|
|
121
138
|
// Everything the CLI needs from the outside world is gathered here, at the
|
|
122
139
|
// edge, so every layer below stays pure and testable against a temp
|
|
123
140
|
// directory and a pair of fake streams.
|
|
124
|
-
|
|
141
|
+
const telemetry = nodeTelemetry(outcome.args.urls.gateway, versionForTelemetry(), fetch);
|
|
142
|
+
// Ctrl-C would otherwise leave a half-drawn spinner on the prompt line.
|
|
143
|
+
const interrupt = () => {
|
|
144
|
+
status.stop();
|
|
145
|
+
process.exit(EXIT_INTERRUPTED);
|
|
146
|
+
};
|
|
147
|
+
process.once("SIGINT", interrupt);
|
|
148
|
+
const result = await dispatch(outcome.args, {
|
|
125
149
|
paths: { cwd: process.cwd(), home: homedir() },
|
|
126
150
|
prompt: { input: process.stdin, output: process.stderr },
|
|
127
151
|
processEnv: process.env,
|
|
@@ -130,6 +154,7 @@ export async function run(argv) {
|
|
|
130
154
|
login: {
|
|
131
155
|
startListener: startLoopback,
|
|
132
156
|
openBrowser: (url) => openBrowser(url, nodeBrowserDeps()),
|
|
157
|
+
waitForEnter: () => waitForEnter(process.stdin),
|
|
133
158
|
fetchImpl: fetch,
|
|
134
159
|
secrets: selectSecretStore({
|
|
135
160
|
paths: { home: homedir(), warn: notify },
|
|
@@ -142,7 +167,16 @@ export async function run(argv) {
|
|
|
142
167
|
// stderr, so `--json` on stdout stays a single parseable object even while
|
|
143
168
|
// login narrates its progress.
|
|
144
169
|
notify,
|
|
170
|
+
status,
|
|
171
|
+
telemetry,
|
|
172
|
+
}).finally(() => {
|
|
173
|
+
// Cleared before the caller writes the result, and on a throw, so no frame survives.
|
|
174
|
+
status.stop();
|
|
175
|
+
process.off("SIGINT", interrupt);
|
|
145
176
|
});
|
|
177
|
+
// The caller exits right after this returns, which would cut off a send in flight.
|
|
178
|
+
await telemetry.flush(FLUSH_CAP_MS);
|
|
179
|
+
return result;
|
|
146
180
|
}
|
|
147
181
|
}
|
|
148
182
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,IAAI,EAAkB,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/login/lock.d.ts
CHANGED
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* within microseconds of a crash.
|
|
8
8
|
*/
|
|
9
9
|
export declare function breakStale(lockPath: string, judgedIno: number): void;
|
|
10
|
+
/**
|
|
11
|
+
* On Windows a lock another process has just deleted lingers, "delete pending", until the
|
|
12
|
+
* last handle on it closes (a waiter's `inspect` is one). Creating it again meanwhile
|
|
13
|
+
* fails with EPERM rather than EEXIST, so there it means "still held, retry".
|
|
14
|
+
*/
|
|
15
|
+
export declare function isDeletePending(err: unknown, platform?: NodeJS.Platform): boolean;
|
|
10
16
|
/**
|
|
11
17
|
* Runs `change` with the lock held, and releases it however that turns out. The lock's
|
|
12
18
|
* directory must exist. Throws if the lock is not free within the budget.
|
package/dist/login/lock.js
CHANGED
|
@@ -95,6 +95,15 @@ function release(lockPath, ownIno) {
|
|
|
95
95
|
throw err;
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* On Windows a lock another process has just deleted lingers, "delete pending", until the
|
|
100
|
+
* last handle on it closes (a waiter's `inspect` is one). Creating it again meanwhile
|
|
101
|
+
* fails with EPERM rather than EEXIST, so there it means "still held, retry".
|
|
102
|
+
*/
|
|
103
|
+
export function isDeletePending(err, platform = process.platform) {
|
|
104
|
+
const code = err.code;
|
|
105
|
+
return platform === "win32" && (code === "EPERM" || code === "EACCES");
|
|
106
|
+
}
|
|
98
107
|
/**
|
|
99
108
|
* Runs `change` with the lock held, and releases it however that turns out. The lock's
|
|
100
109
|
* directory must exist. Throws if the lock is not free within the budget.
|
|
@@ -103,6 +112,7 @@ export function withLock(lockPath, change, options = {}) {
|
|
|
103
112
|
// Overridable so the test for the refusal does not spend the real budget waiting.
|
|
104
113
|
const deadline = Date.now() + (options.waitBudgetMs ?? WAIT_BUDGET_MS);
|
|
105
114
|
let fd;
|
|
115
|
+
let deletePending;
|
|
106
116
|
for (;;) {
|
|
107
117
|
try {
|
|
108
118
|
// "wx" is O_CREAT|O_EXCL: the create either wins or fails, with no window between
|
|
@@ -111,7 +121,9 @@ export function withLock(lockPath, change, options = {}) {
|
|
|
111
121
|
break;
|
|
112
122
|
}
|
|
113
123
|
catch (err) {
|
|
114
|
-
if (err
|
|
124
|
+
if (isDeletePending(err))
|
|
125
|
+
deletePending = err;
|
|
126
|
+
else if (err.code !== "EEXIST")
|
|
115
127
|
throw err;
|
|
116
128
|
}
|
|
117
129
|
const holder = inspect(lockPath);
|
|
@@ -120,6 +132,9 @@ export function withLock(lockPath, change, options = {}) {
|
|
|
120
132
|
continue;
|
|
121
133
|
}
|
|
122
134
|
if (Date.now() >= deadline) {
|
|
135
|
+
// Never contended, only refused: a real permission error, so report it as one.
|
|
136
|
+
if (holder === undefined && deletePending !== undefined)
|
|
137
|
+
throw deletePending;
|
|
123
138
|
throw new Error(`another ai21-gateway command is writing credentials (${lockPath} held for ${holder?.ageMs ?? "?"}ms). ` +
|
|
124
139
|
"Wait for it to finish, or delete that file if nothing is running.");
|
|
125
140
|
}
|
package/dist/login/lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/login/lock.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/login/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,MAAM,EACN,QAAQ,EACR,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,uFAAuF;AACvF,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B;;;;GAIG;AACH,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB,sFAAsF;AACtF,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAQD,uGAAuG;AACvG,SAAS,OAAO,CAAC,QAAgB;IAC/B,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;QAEvC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IAC5F,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAQ,GAA6B,CAAC,IAAI,KAAK,OAAO,CAAC;IACzD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,SAAiB;IAC5D,MAAM,OAAO,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7C,IAAI,CAAC;QACH,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QAE7D,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,SAAS,OAAO,CAAC,QAAgB,EAAE,MAAc;IAC/C,IAAI,CAAC;QACH,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,MAAM;YAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY,EAAE,WAA4B,OAAO,CAAC,QAAQ;IACxF,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;IAEjD,OAAO,QAAQ,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAI,QAAgB,EAAE,MAAe,EAAE,UAAqC,EAAE;IACpG,kFAAkF;IAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,cAAc,CAAC,CAAC;IACvE,IAAI,EAAU,CAAC;IACf,IAAI,aAAsB,CAAC;IAE3B,SAAS,CAAC;QACR,IAAI,CAAC;YACH,kFAAkF;YAClF,2BAA2B;YAC3B,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YACrC,MAAM;QACR,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,eAAe,CAAC,GAAG,CAAC;gBAAE,aAAa,GAAG,GAAG,CAAC;iBACzC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QACvE,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,GAAG,cAAc,CAAC,EAAE,CAAC;YAC/E,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YACjC,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC3B,+EAA+E;YAC/E,IAAI,MAAM,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS;gBAAE,MAAM,aAAa,CAAC;YAE7E,MAAM,IAAI,KAAK,CACb,wDAAwD,QAAQ,aAAa,MAAM,EAAE,KAAK,IAAI,GAAG,OAAO;gBACtG,mEAAmE,CACtE,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;IACjC,IAAI,CAAC;QACH,6DAA6D;QAC7D,SAAS,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAElC,OAAO,MAAM,EAAE,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
package/dist/login/session.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ import type { SecretStore } from "./secretStore.js";
|
|
|
6
6
|
export interface LoginDeps {
|
|
7
7
|
readonly startListener: (options: LoopbackOptions) => Promise<Listener>;
|
|
8
8
|
readonly openBrowser: (url: string) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Holds the flow until the user presses Enter; false if input ended instead. Absent,
|
|
11
|
+
* the browser opens straight away, which is what tests and non-interactive callers get.
|
|
12
|
+
*/
|
|
13
|
+
readonly waitForEnter?: () => Promise<boolean>;
|
|
9
14
|
/** For the token exchange and workspace listing; injected so tests make no calls. */
|
|
10
15
|
readonly fetchImpl: FetchLike;
|
|
11
16
|
/** Where the refresh token and the minted key are kept; injected so tests use neither. */
|
|
@@ -26,7 +31,14 @@ export type LoginOutcome = {
|
|
|
26
31
|
export interface LoginProgress {
|
|
27
32
|
/** The caller decides which stream this lands on. */
|
|
28
33
|
readonly notify: (message: string) => void;
|
|
34
|
+
/** Called once the browser has been sent to the sign-in page and the wait begins. */
|
|
35
|
+
readonly waiting?: () => void;
|
|
29
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The code framed so the eye lands on it: it is the whole defence against another
|
|
39
|
+
* process asking to sign in, and in running prose it was easy to skim past.
|
|
40
|
+
*/
|
|
41
|
+
export declare function codeBox(code: string): string;
|
|
30
42
|
/**
|
|
31
43
|
* The consent screen's URL.
|
|
32
44
|
*
|
package/dist/login/session.js
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
import { randomInt } from "node:crypto";
|
|
7
7
|
import { CLIENT_ID, CLIENT_PARAM, CLI_LOGIN_PATH, CODE_PARAM, EXPIRES_PARAM, REDIRECT_PARAM, STATE_PARAM, verificationCode, } from "./contract.js";
|
|
8
8
|
import { LOGIN_TIMEOUT_MS } from "./loopback.js";
|
|
9
|
+
/**
|
|
10
|
+
* The code framed so the eye lands on it: it is the whole defence against another
|
|
11
|
+
* process asking to sign in, and in running prose it was easy to skim past.
|
|
12
|
+
*/
|
|
13
|
+
export function codeBox(code) {
|
|
14
|
+
const inner = ` verification code: ${code} `;
|
|
15
|
+
const rule = "─".repeat([...inner].length);
|
|
16
|
+
return ` ╭${rule}╮\n │${inner}│\n ╰${rule}╯`;
|
|
17
|
+
}
|
|
9
18
|
/**
|
|
10
19
|
* The consent screen's URL.
|
|
11
20
|
*
|
|
@@ -25,7 +34,18 @@ export function loginUrl(urls, callbackUrl, state, code, expiresAtSeconds) {
|
|
|
25
34
|
}
|
|
26
35
|
export async function runLogin(urls, deps, io) {
|
|
27
36
|
const code = verificationCode((max) => randomInt(max));
|
|
28
|
-
|
|
37
|
+
/* The code comes first and the browser only on Enter, so it is read before the browser
|
|
38
|
+
* takes focus. The listener starts after that too: its clock must not run while the
|
|
39
|
+
* user is still reading. */
|
|
40
|
+
io.notify(`Sign in to AI21 to continue.\n\n${codeBox(code)}\n\n` +
|
|
41
|
+
`The sign-in page will show this code. If it shows anything else, choose Deny —\n` +
|
|
42
|
+
`something other than this terminal is asking to sign in.`);
|
|
43
|
+
if (deps.waitForEnter !== undefined) {
|
|
44
|
+
io.notify(`\nPress Enter to open ${new URL(urls.app).host} in your browser…`);
|
|
45
|
+
if (!(await deps.waitForEnter()))
|
|
46
|
+
return { kind: "declined", reason: "it was cancelled in the terminal" };
|
|
47
|
+
}
|
|
48
|
+
// Stamped here, so the page gives up a moment before the listener, not after.
|
|
29
49
|
const expiresAtSeconds = Math.floor((Date.now() + LOGIN_TIMEOUT_MS) / 1000);
|
|
30
50
|
let listener;
|
|
31
51
|
try {
|
|
@@ -40,10 +60,8 @@ export async function runLogin(urls, deps, io) {
|
|
|
40
60
|
/* Printed as well as opened: `openBrowser` cannot tell us a browser appeared, and over
|
|
41
61
|
* SSH the URL is the only way in. */
|
|
42
62
|
deps.openBrowser(listener.signInUrl);
|
|
43
|
-
io.notify(
|
|
44
|
-
|
|
45
|
-
`The page will show this code. If it shows anything else, choose Deny — something\n` +
|
|
46
|
-
`other than this terminal is asking to sign in.\n\nWaiting…`);
|
|
63
|
+
io.notify(`\nIf the browser did not open, visit:\n\n ${listener.signInUrl}`);
|
|
64
|
+
io.waiting?.();
|
|
47
65
|
return settle(await listener.result);
|
|
48
66
|
}
|
|
49
67
|
function settle(outcome) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/login/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,OAAO,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EAEd,UAAU,EACV,aAAa,EACb,cAAc,EACd,WAAW,EACX,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAA6D,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/login/session.ts"],"names":[],"mappings":"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"}
|
package/dist/precheck.d.ts
CHANGED
package/dist/precheck.js
CHANGED
|
@@ -67,7 +67,7 @@ export async function precheckKey(ctx, deps) {
|
|
|
67
67
|
}
|
|
68
68
|
if (!response.ok) {
|
|
69
69
|
return REJECTION_STATUSES.has(response.status)
|
|
70
|
-
? { kind: "rejected", reason: "the gateway rejected it" }
|
|
70
|
+
? { kind: "rejected", reason: "the gateway rejected it", status: response.status }
|
|
71
71
|
: { kind: "inconclusive", reason: `the gateway answered ${response.status}` };
|
|
72
72
|
}
|
|
73
73
|
const ids = agentIdsIn((await response.text?.()) ?? "");
|
package/dist/precheck.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"precheck.js","sourceRoot":"","sources":["../src/precheck.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAU1C,+FAA+F;AAC/F,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAEpD,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,OAAO,CAAC;AAC5G,CAAC;AAED,qEAAqE;AACrE,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAE5C,OAAO,QAAQ,KAAK,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QAElE,iEAAiE;QACjE,MAAM,EAAE,GAAI,KAAiC,CAAC,UAAU,CAAC,CAAC;QAC1D,IAAI,OAAO,EAAE,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAE7C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAoB,EAAE,IAAkB;IACxE,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,kEAAkE,EAAE,CAAC;IACzG,CAAC;IAED,IAAI,QAAuE,CAAC;IAC5E,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE;YAC7C,MAAM,EAAE,KAAK;YACb,uFAAuF;YACvF,OAAO,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,OAAO,EAAE;YACrC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,mBAAmB,CAAC;SACjD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sEAAsE;QACtE,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC;gBAClB,CAAC,CAAC,qCAAqC,mBAAmB,GAAG,IAAI,GAAG;gBACpE,CAAC,CAAC,kCAAkC;SACvC,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,yBAAyB,EAAE;
|
|
1
|
+
{"version":3,"file":"precheck.js","sourceRoot":"","sources":["../src/precheck.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The terminal the CLI was run from.
|
|
3
|
-
*
|
|
4
|
-
* All that is left of the key prompt: pasting a key is error-prone, so the only ways in
|
|
5
|
-
* are `--key` and signing in through the browser. What survives is the tty check, which
|
|
6
|
-
* decides whether a sign-in can be offered at all.
|
|
7
|
-
*/
|
|
8
1
|
import type { Readable, Writable } from "node:stream";
|
|
9
2
|
export interface PromptIo {
|
|
10
3
|
readonly input: Readable & {
|
|
@@ -15,3 +8,8 @@ export interface PromptIo {
|
|
|
15
8
|
/** Overrides the stream's own flag; tests drive a pipe as though it were a tty. */
|
|
16
9
|
readonly isTTY?: boolean;
|
|
17
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolves true on the next Enter, false if the input ends first (Ctrl-D, a closed pipe).
|
|
13
|
+
* The interface is closed either way, so stdin does not keep the process alive.
|
|
14
|
+
*/
|
|
15
|
+
export declare function waitForEnter(input: Readable): Promise<boolean>;
|
package/dist/prompt.js
CHANGED
|
@@ -1,2 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The terminal the CLI was run from.
|
|
3
|
+
*
|
|
4
|
+
* All that is left of the key prompt: pasting a key is error-prone, so the only ways in
|
|
5
|
+
* are `--key` and signing in through the browser. What survives is the tty check, which
|
|
6
|
+
* decides whether a sign-in can be offered at all, and the Enter that opens the browser.
|
|
7
|
+
*/
|
|
8
|
+
import { createInterface } from "node:readline";
|
|
9
|
+
/**
|
|
10
|
+
* Resolves true on the next Enter, false if the input ends first (Ctrl-D, a closed pipe).
|
|
11
|
+
* The interface is closed either way, so stdin does not keep the process alive.
|
|
12
|
+
*/
|
|
13
|
+
export function waitForEnter(input) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const rl = createInterface({ input });
|
|
16
|
+
let answered = false;
|
|
17
|
+
rl.once("line", () => {
|
|
18
|
+
answered = true;
|
|
19
|
+
rl.close();
|
|
20
|
+
});
|
|
21
|
+
rl.once("close", () => resolve(answered));
|
|
22
|
+
});
|
|
23
|
+
}
|
|
2
24
|
//# sourceMappingURL=prompt.js.map
|
package/dist/prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAUhD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACtC,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACnB,QAAQ,GAAG,IAAI,CAAC;YAChB,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/report.d.ts
CHANGED
|
@@ -80,3 +80,7 @@ export interface NoCredentialInput {
|
|
|
80
80
|
* gateway can never answer, so a config left in place only breaks Claude Code.
|
|
81
81
|
*/
|
|
82
82
|
export declare function reportNoClaudeCredential(input: NoCredentialInput): RunResult;
|
|
83
|
+
/** `login`'s success: who is signed in, and the workspace any key was minted in. */
|
|
84
|
+
export declare function reportSignedIn(email: string, workspace: string, streams?: ColorTargets): RunResult;
|
|
85
|
+
/** Refused before anything else runs: without `claude` there is nothing to configure. */
|
|
86
|
+
export declare function reportNoClaudeCli(json: boolean, streams?: ColorTargets): RunResult;
|
package/dist/report.js
CHANGED
|
@@ -57,6 +57,17 @@ function painterFor(target) {
|
|
|
57
57
|
return PLAIN;
|
|
58
58
|
return (format, text) => styleText(format, text, { validateStream: false });
|
|
59
59
|
}
|
|
60
|
+
const MARK = { green: "✔", red: "✖", yellow: "⚠" };
|
|
61
|
+
/**
|
|
62
|
+
* A result line in its tone, led by a symbol so pass and fail read apart at a glance.
|
|
63
|
+
* The symbol rides with colour: plain output stays byte-identical, which is the
|
|
64
|
+
* contract every existing output assertion and log scraper relies on.
|
|
65
|
+
*/
|
|
66
|
+
function headline(paint, tone, text) {
|
|
67
|
+
if (paint === PLAIN)
|
|
68
|
+
return text;
|
|
69
|
+
return `${paint(tone, MARK[tone])} ${paint(tone, text)}`;
|
|
70
|
+
}
|
|
60
71
|
/**
|
|
61
72
|
* The sentence a refusal exists to deliver: it answers the only question a refusal
|
|
62
73
|
* raises — what happened to my file? — so it is painted like a `warning:` label
|
|
@@ -70,19 +81,20 @@ const NOTHING_WRITTEN = "Nothing was written.";
|
|
|
70
81
|
export function nothingWasWritten(streams = PROCESS_STREAMS) {
|
|
71
82
|
return painterFor(streams.err)("yellow", NOTHING_WRITTEN);
|
|
72
83
|
}
|
|
73
|
-
function revertLine(outcome) {
|
|
84
|
+
function revertLine(outcome, paint) {
|
|
85
|
+
const path = paint("bold", outcome.path);
|
|
74
86
|
switch (outcome.kind) {
|
|
75
87
|
case "reverted": {
|
|
76
88
|
const parts = [
|
|
77
89
|
outcome.restored.length > 0 ? `restored ${outcome.restored.join(", ")} to previous values` : undefined,
|
|
78
90
|
outcome.removed.length > 0 ? `removed ${outcome.removed.join(", ")}` : undefined,
|
|
79
91
|
].filter((part) => part !== undefined);
|
|
80
|
-
return `Reverted ${
|
|
92
|
+
return `Reverted ${path} — ${parts.join("; ")}`;
|
|
81
93
|
}
|
|
82
94
|
case "restored":
|
|
83
|
-
return `Restored ${
|
|
95
|
+
return `Restored ${path} from ${outcome.backup}`;
|
|
84
96
|
case "not-configured":
|
|
85
|
-
return `Nothing to revert — ${
|
|
97
|
+
return `Nothing to revert — ${path} does not carry this tool's settings.`;
|
|
86
98
|
case "agent-mismatch":
|
|
87
99
|
return `${outcome.path} is configured for agent '${outcome.actual}', not '${outcome.expected}'.`;
|
|
88
100
|
case "drifted":
|
|
@@ -129,9 +141,9 @@ export function reportRevert(outcome, json, streams = PROCESS_STREAMS) {
|
|
|
129
141
|
const ok = code === EXIT.OK;
|
|
130
142
|
// Colour tracks the exit code, with one exception: `not-configured` is a state,
|
|
131
143
|
// not a refusal, and painting "nothing to revert" red reads like a crash.
|
|
132
|
-
const
|
|
144
|
+
const tone = outcome.kind === "not-configured" ? "yellow" : ok ? "green" : "red";
|
|
133
145
|
const paint = painterFor(ok ? streams.out : streams.err);
|
|
134
|
-
const body = `${paint
|
|
146
|
+
const body = `${headline(paint, tone, revertLine(outcome, paint))}\n${revertDetail(outcome, paint)}`;
|
|
135
147
|
return ok ? { code, out: body, err: "" } : { code, out: "", err: body };
|
|
136
148
|
}
|
|
137
149
|
/**
|
|
@@ -176,14 +188,15 @@ export function collectWarnings(ctx) {
|
|
|
176
188
|
return warnings;
|
|
177
189
|
}
|
|
178
190
|
/** Human-readable line for a config outcome. Never includes the key. */
|
|
179
|
-
function humanLine(outcome) {
|
|
191
|
+
function humanLine(outcome, paint) {
|
|
192
|
+
const path = paint("bold", outcome.path);
|
|
180
193
|
switch (outcome.kind) {
|
|
181
194
|
case "created":
|
|
182
|
-
return `Wrote ${
|
|
195
|
+
return `Wrote ${path}`;
|
|
183
196
|
case "updated":
|
|
184
|
-
return `Updated ${
|
|
197
|
+
return `Updated ${path} (${outcome.changed.join(", ")})`;
|
|
185
198
|
case "unchanged":
|
|
186
|
-
return `Already configured — ${
|
|
199
|
+
return `Already configured — ${path} is up to date.`;
|
|
187
200
|
case "conflict":
|
|
188
201
|
return `${outcome.path} already sets values this command would change.`;
|
|
189
202
|
case "malformed":
|
|
@@ -228,10 +241,16 @@ function precheckWarning(outcome) {
|
|
|
228
241
|
};
|
|
229
242
|
}
|
|
230
243
|
/** The stdout line for a successful verification; nothing for any other outcome. */
|
|
231
|
-
function verificationLine(outcome) {
|
|
244
|
+
function verificationLine(outcome, paint) {
|
|
232
245
|
if (outcome?.kind !== "passed")
|
|
233
246
|
return "";
|
|
234
|
-
return `Verified through the gateway with ${VERIFIED_VIA[outcome.via]}
|
|
247
|
+
return `${headline(paint, "green", `Verified through the gateway with ${VERIFIED_VIA[outcome.via]}.`)}\n`;
|
|
248
|
+
}
|
|
249
|
+
/** An aside under the headline, dimmed so the headline stays the thing read first. */
|
|
250
|
+
function backupLine(outcome, paint) {
|
|
251
|
+
if (outcome.kind !== "updated")
|
|
252
|
+
return "";
|
|
253
|
+
return `${paint("dim", `Backed up the previous file to ${outcome.backup}`)}\n`;
|
|
235
254
|
}
|
|
236
255
|
/**
|
|
237
256
|
* One JSON object with what happened, where, why it exited that way, and any
|
|
@@ -290,9 +309,10 @@ export function report(input) {
|
|
|
290
309
|
const paintWarning = painterFor(streams.err);
|
|
291
310
|
// Only the label is coloured: the message is prose, and a paragraph of yellow is
|
|
292
311
|
// harder to read than the plain one it replaced.
|
|
293
|
-
const warningText = warnings.map((w) => `${paintWarning
|
|
294
|
-
const line = paintBody
|
|
295
|
-
const body = `${line}\n${
|
|
312
|
+
const warningText = warnings.map((w) => `${headline(paintWarning, "yellow", "warning:")} ${w.message}\n`).join("");
|
|
313
|
+
const line = headline(paintBody, ok ? "green" : "red", humanLine(input.outcome, paintBody));
|
|
314
|
+
const body = `${line}\n${backupLine(input.outcome, paintBody)}` +
|
|
315
|
+
`${verificationLine(input.verification, paintBody)}${input.detail ?? ""}`;
|
|
296
316
|
return ok ? { code, out: body, err: warningText } : { code, out: "", err: `${body}${warningText}` };
|
|
297
317
|
}
|
|
298
318
|
/** Stable `--json` reasons, spelled for a funnel rather than for a person. */
|
|
@@ -340,11 +360,33 @@ export function reportNoClaudeCredential(input) {
|
|
|
340
360
|
return { code, out: `${JSON.stringify(payload, null, 2)}\n`, err: "" };
|
|
341
361
|
}
|
|
342
362
|
const streams = input.streams ?? PROCESS_STREAMS;
|
|
343
|
-
const
|
|
363
|
+
const title = headline(painterFor(streams.err), "red", NO_CREDENTIAL_HEADLINE[input.reason]);
|
|
344
364
|
return {
|
|
345
365
|
code,
|
|
346
366
|
out: "",
|
|
347
|
-
err: `${
|
|
367
|
+
err: `${title} ${NO_CREDENTIAL_NEXT_STEP[input.reason]}\n${noCredentialStateLine(input, streams)}\n`,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/** `login`'s success: who is signed in, and the workspace any key was minted in. */
|
|
371
|
+
export function reportSignedIn(email, workspace, streams = PROCESS_STREAMS) {
|
|
372
|
+
const paint = painterFor(streams.out);
|
|
373
|
+
return {
|
|
374
|
+
code: EXIT.OK,
|
|
375
|
+
out: `${headline(paint, "green", `Signed in as ${paint("bold", email)}.`)}\n workspace: ${paint("bold", workspace)}\n`,
|
|
376
|
+
err: "",
|
|
348
377
|
};
|
|
349
378
|
}
|
|
379
|
+
/** Stable `--json` reason; kept apart from `no_claude_subscription` so a funnel can split them. */
|
|
380
|
+
const NO_CLAUDE_CLI_REASON = "claude_code_cli_not_installed";
|
|
381
|
+
const NO_CLAUDE_CLI_MESSAGE = "The Claude Code CLI was not found. The AI21 gateway currently supports only the Claude Code CLI; install it to use the gateway, then re-run this command.";
|
|
382
|
+
/** Refused before anything else runs: without `claude` there is nothing to configure. */
|
|
383
|
+
export function reportNoClaudeCli(json, streams = PROCESS_STREAMS) {
|
|
384
|
+
const code = EXIT.NO_CLAUDE_CLI;
|
|
385
|
+
if (json) {
|
|
386
|
+
const payload = { ok: false, outcome: "no-claude-cli", exitCode: code, reason: NO_CLAUDE_CLI_REASON, warnings: [] };
|
|
387
|
+
return { code, out: `${JSON.stringify(payload, null, 2)}\n`, err: "" };
|
|
388
|
+
}
|
|
389
|
+
const headline = painterFor(streams.err)("red", NO_CLAUDE_CLI_MESSAGE);
|
|
390
|
+
return { code, out: "", err: `${headline}\n${nothingWasWritten(streams)}\n` };
|
|
391
|
+
}
|
|
350
392
|
//# sourceMappingURL=report.js.map
|