@ai21/gateway 1.4.0 → 1.5.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 CHANGED
@@ -37,8 +37,8 @@ check and verification are skipped under `--no-verify`. For Claude Code an Anthr
37
37
  key (`--anthropic-key` or `ANTHROPIC_API_KEY`) can stand in for the login.
38
38
 
39
39
  Without the agent's CLI on PATH, `install` stops before signing in, writes nothing,
40
- and exits 10, even under `--no-verify` (or, for Claude Code, without an Anthropic
41
- key). For Codex there is no stand-in: only the Codex CLI can send the ChatGPT login
40
+ and exits 10, even under `--no-verify` (or, for Claude Code, with neither an Anthropic
41
+ key nor Claude Desktop). For Codex there is no stand-in: only the Codex CLI can send the ChatGPT login
42
42
  the gateway forwards.
43
43
 
44
44
  The dashboard prints this command for you (Add agent, or Reconnect on an existing
@@ -104,6 +104,37 @@ lists the rest.
104
104
  the wrong agent's file by mistake. `uninstall` also takes `--client` to know whose
105
105
  file to revert.
106
106
 
107
+ ### Claude Desktop
108
+
109
+ `claude-code` covers every Claude app on the machine: `install` configures the Claude
110
+ Code CLI and Claude Desktop, whichever are installed, in one run, and `uninstall`
111
+ reverts both. It is one setup for analytics, with `agentKind: claude_code`. Desktop
112
+ is configured only for a user-level install; `--repo` leaves it alone.
113
+
114
+ Desktop uses its own third-party inference mode rather than a patched app: a gateway
115
+ profile in `~/Library/Application Support/Claude-3p/configLibrary/`, applied through
116
+ `_meta.json`, with `deploymentMode: "3p"` in both Desktop configs. Its 1P/3P chooser
117
+ and its telemetry are turned off.
118
+
119
+ It runs on the user's own Claude subscription. The first install runs `claude
120
+ setup-token`, which opens the browser once and returns a token that lasts about a
121
+ year; if the token cannot be read off the screen, it is pasted into a hidden prompt.
122
+ The token is kept in the login keychain (`ai21-gateway-claude-desktop`), and a helper
123
+ script at `~/.config/ai21-gateway/claude-desktop-credential` hands it to Desktop, which
124
+ sends it as `Authorization: Bearer`. A re-install reuses it, and
125
+ `CLAUDE_CODE_OAUTH_TOKEN` supplies one without the browser step.
126
+
127
+ Desktop is verified through the gateway before its profile is written. It reads the
128
+ profile only at launch, so `install` quits it if it is running and opens it again;
129
+ run from inside Desktop's own Claude Code, it asks you to reopen it instead.
130
+
131
+ On Windows the profile lives under `%LOCALAPPDATA%\Claude-3p` (or `Claude Nest-3p`), and
132
+ the token in an owner-only file, `~/.config/ai21-gateway/claude-desktop-token`, which
133
+ Desktop reads by running `cmd.exe /d /c type <file>`: Electron cannot spawn a `.cmd`.
134
+ There is no `script` to record the approval screen, so you paste the token into a hidden
135
+ prompt. Closing Desktop only sends it to the tray, so `install` stops its processes before
136
+ reopening it; a Microsoft Store install is reopened by its app id.
137
+
107
138
  `install` reports onboarding events to AI21's product analytics (Amplitude), so we can
108
139
  see where setup fails: when it started, then whether it completed or at which step it
109
140
  failed, with your AI21 user id, the CLI version, OS, workspace id and a random per-run
@@ -0,0 +1,81 @@
1
+ import type { AgentEnv, AgentSurface, SnippetInputs } from "../types.js";
2
+ /** Fixed, so a re-install replaces our profile instead of stacking a second one. */
3
+ export declare const DESKTOP_PROFILE_ID = "00000000-0000-4000-8000-00000000a121";
4
+ export declare const DESKTOP_PROFILE_NAME = "AI21 Gateway";
5
+ export declare const DESKTOP_BUNDLE_ID = "com.anthropic.claudefordesktop";
6
+ /** Where the subscription token lives. Read raw by the helper, so it is not base64 like the login secrets. */
7
+ export declare const TOKEN_SERVICE = "ai21-gateway-claude-desktop";
8
+ export declare const TOKEN_ACCOUNT = "claude-oauth-token";
9
+ /** A subscription token, or undefined: it is quoted into `security -i` and sent as a header, so nothing else may pass. */
10
+ export declare function oauthToken(value: string | undefined): string | undefined;
11
+ export type DesktopPlatform = "darwin" | "win32";
12
+ export interface DesktopPaths {
13
+ readonly platform: DesktopPlatform;
14
+ /** The regular Claude profile's config; its `deploymentMode` picks 1P or 3P at launch. */
15
+ readonly normalConfig: string;
16
+ readonly thirdPartyConfig: string;
17
+ readonly meta: string;
18
+ readonly profile: string;
19
+ /** What was applied before us, so uninstall can put it back. */
20
+ readonly restore: string;
21
+ /** macOS: the helper script over the keychain; Windows: the owner-only token file that `cmd /c type` prints. */
22
+ readonly helper: string;
23
+ readonly helperCommand: {
24
+ readonly path: string;
25
+ readonly args?: readonly string[];
26
+ };
27
+ }
28
+ export declare function desktopPaths(home: string, platform?: NodeJS.Platform, env?: Record<string, string | undefined>): DesktopPaths;
29
+ export declare function desktopHeaders({ agentId, ai21Key, userId, client }: SnippetInputs): Record<string, string>;
30
+ export declare function helperScript(): string;
31
+ export declare function desktopProfile(inputs: SnippetInputs, paths: DesktopPaths): Record<string, unknown>;
32
+ export type DesktopOutcome = {
33
+ readonly kind: "written";
34
+ readonly path: string;
35
+ } | {
36
+ readonly kind: "reverted";
37
+ readonly path: string;
38
+ } | {
39
+ readonly kind: "not-configured";
40
+ readonly path: string;
41
+ } | {
42
+ readonly kind: "malformed";
43
+ readonly path: string;
44
+ readonly reason: string;
45
+ } | {
46
+ readonly kind: "failed";
47
+ readonly path: string;
48
+ readonly reason: string;
49
+ };
50
+ /** Every file is parsed before any is written, so a malformed one leaves all untouched. */
51
+ export declare function writeDesktopConfig(paths: DesktopPaths, inputs: SnippetInputs): DesktopOutcome;
52
+ export declare function revertDesktopConfig(paths: DesktopPaths): DesktopOutcome;
53
+ /** Everything outside this process, injected so tests never touch the real app or keychain. */
54
+ export interface DesktopDeps {
55
+ readonly platform: NodeJS.Platform;
56
+ readonly installed: () => boolean;
57
+ readonly isRunning: () => Promise<boolean>;
58
+ readonly quit: () => Promise<void>;
59
+ /** For an app that closes to the tray instead of exiting; absent where quit really quits. */
60
+ readonly forceQuit?: () => Promise<void>;
61
+ readonly open: () => Promise<void>;
62
+ readonly sleep: (ms: number) => Promise<void>;
63
+ readonly readToken: () => Promise<string | undefined>;
64
+ readonly storeToken: (token: string) => Promise<void>;
65
+ readonly forgetToken: () => Promise<void>;
66
+ /** Runs `claude setup-token` in the user's terminal; the token it printed, if one could be read. */
67
+ readonly setupToken: () => Promise<string | undefined>;
68
+ readonly pasteToken: () => Promise<string | undefined>;
69
+ }
70
+ export declare function run(file: string, args: readonly string[]): Promise<string>;
71
+ export declare function macDesktopDeps(): DesktopDeps;
72
+ /** Replays a terminal transcript onto a grid: Claude Code draws with cursor moves, so the token is never in one piece. */
73
+ export declare function screenFromTranscript(transcript: string): string;
74
+ /** The token on the final screen, joined across the lines the terminal wrapped it onto. */
75
+ export declare function tokenFromTranscript(transcript: string): string | undefined;
76
+ export declare function promptToken(): Promise<string | undefined>;
77
+ export type LaunchOutcome = "opened" | "restarted" | "still-running" | "open-failed";
78
+ /** Desktop reads the profile only at launch: quit it if running, then open it either way. */
79
+ export declare function relaunchDesktop(deps: DesktopDeps): Promise<LaunchOutcome>;
80
+ /** Tests replace the app and keychain; everything else about the surface is real. */
81
+ export declare function claudeDesktopSurface(desktopDeps?: (env: AgentEnv) => DesktopDeps): AgentSurface;