latchkey 2.17.2 → 2.19.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.
Files changed (59) hide show
  1. package/README.md +1 -1
  2. package/dist/skills/generic/latchkey/SKILL.md +1 -1
  3. package/dist/skills/openclaw/latchkey/SKILL.md +1 -1
  4. package/dist/src/cli.js +0 -0
  5. package/dist/src/cliCommands.d.ts.map +1 -1
  6. package/dist/src/cliCommands.js +36 -4
  7. package/dist/src/cliCommands.js.map +1 -1
  8. package/dist/src/gateway/latchkeyEndpoint.d.ts +25 -0
  9. package/dist/src/gateway/latchkeyEndpoint.d.ts.map +1 -1
  10. package/dist/src/gateway/latchkeyEndpoint.js +13 -2
  11. package/dist/src/gateway/latchkeyEndpoint.js.map +1 -1
  12. package/dist/src/serviceRegistry.d.ts.map +1 -1
  13. package/dist/src/serviceRegistry.js +2 -1
  14. package/dist/src/serviceRegistry.js.map +1 -1
  15. package/dist/src/services/core/base.d.ts +44 -0
  16. package/dist/src/services/core/base.d.ts.map +1 -1
  17. package/dist/src/services/core/base.js +55 -0
  18. package/dist/src/services/core/base.js.map +1 -1
  19. package/dist/src/services/google/base.d.ts +21 -0
  20. package/dist/src/services/google/base.d.ts.map +1 -1
  21. package/dist/src/services/google/base.js +34 -6
  22. package/dist/src/services/google/base.js.map +1 -1
  23. package/dist/src/services/index.d.ts +2 -1
  24. package/dist/src/services/index.d.ts.map +1 -1
  25. package/dist/src/services/index.js +2 -1
  26. package/dist/src/services/index.js.map +1 -1
  27. package/dist/src/services/notion-mcp.d.ts +21 -0
  28. package/dist/src/services/notion-mcp.d.ts.map +1 -1
  29. package/dist/src/services/notion-mcp.js +21 -1
  30. package/dist/src/services/notion-mcp.js.map +1 -1
  31. package/dist/src/services/ramp.d.ts +46 -0
  32. package/dist/src/services/ramp.d.ts.map +1 -0
  33. package/dist/src/services/ramp.js +185 -0
  34. package/dist/src/services/ramp.js.map +1 -0
  35. package/dist/src/sharedOperations.d.ts +11 -0
  36. package/dist/src/sharedOperations.d.ts.map +1 -1
  37. package/dist/src/sharedOperations.js +28 -1
  38. package/dist/src/sharedOperations.js.map +1 -1
  39. package/dist/src/version.d.ts +1 -1
  40. package/dist/src/version.js +1 -1
  41. package/dist/tests/cli.test.js +81 -0
  42. package/dist/tests/cli.test.js.map +1 -1
  43. package/dist/tests/latchkeyEndpoint.test.js +57 -0
  44. package/dist/tests/latchkeyEndpoint.test.js.map +1 -1
  45. package/dist/tests/oauthUtils.test.js +15 -4
  46. package/dist/tests/oauthUtils.test.js.map +1 -1
  47. package/dist/tests/ramp-session.test.d.ts +10 -0
  48. package/dist/tests/ramp-session.test.d.ts.map +1 -0
  49. package/dist/tests/ramp-session.test.js +43 -0
  50. package/dist/tests/ramp-session.test.js.map +1 -0
  51. package/dist/tests/serviceErrors.test.d.ts +2 -0
  52. package/dist/tests/serviceErrors.test.d.ts.map +1 -0
  53. package/dist/tests/serviceErrors.test.js +19 -0
  54. package/dist/tests/serviceErrors.test.js.map +1 -0
  55. package/dist/tests/serviceRegistry.test.js +3 -1
  56. package/dist/tests/serviceRegistry.test.js.map +1 -1
  57. package/dist/tests/sharedOperations.test.js +88 -3
  58. package/dist/tests/sharedOperations.test.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Ramp service implementation (browser / AI agent-key pathway, production only).
3
+ *
4
+ * `latchkey auth browser ramp` runs the OAuth 2.0 authorization-code + PKCE flow
5
+ * against Ramp's public client (a fixed client ID, no secret). The hosted consent
6
+ * screen (auth_level=auto) mints an "AI agent key"; latchkey catches the loopback
7
+ * callback, exchanges the code for a bearer + refresh token at
8
+ * `.../developer/v1/token/pkce`, and stores them as OAuthCredentials (auto-refreshed).
9
+ *
10
+ * Agent keys use the agent-tools endpoints -- POST https://api.ramp.com/developer/v1/
11
+ * agent-tools/<tool> with a {"rationale": ...} body (they are auth-level barred from
12
+ * the standard REST endpoints). Spec: https://api.ramp.com/v1/public/agent-tools/spec/.
13
+ */
14
+ import { randomUUID } from 'node:crypto';
15
+ import { OAuthCredentials } from '../apiCredentials/base.js';
16
+ import { exchangeCodeForTokens, generateCodeChallenge, generateCodeVerifier, refreshAccessToken, startOAuthCallbackServer, } from '../oauthUtils.js';
17
+ import { isBrowserClosedError, LoginCancelledError, Service, ServiceSession } from './core/base.js';
18
+ /** Ramp's public OAuth client (PKCE, no secret), from ramp-cli. */
19
+ const RAMP_OAUTH_CLIENT_ID = 'ramp_id_6pKvd0IR3d8Kuzp82SV6YgpVCZOlz68Px6s3wVsr';
20
+ /** Hosted authorize endpoint (where the user signs in / approves the agent key). */
21
+ const RAMP_AUTHORIZE_URL = 'https://app.ramp.com/v1/authorize';
22
+ /** PKCE token endpoint (code exchange + refresh). */
23
+ const RAMP_PKCE_TOKEN_ENDPOINT = 'https://api.ramp.com/developer/v1/token/pkce';
24
+ /** Loopback callback path; matches ramp-cli's `/callback`. */
25
+ const RAMP_OAUTH_CALLBACK_PATH = '/callback';
26
+ /** Time to wait for the user to finish the hosted login + agent-key approval. */
27
+ const RAMP_LOGIN_TIMEOUT_MS = 300_000;
28
+ /**
29
+ * Scopes requested on the authorize URL: exactly the scopes Ramp's agent-tools
30
+ * OpenAPI declares (no regular-REST-only scopes -- agent keys can't use the standard
31
+ * REST API anyway). Ramp grants only the subset the signed-in user is entitled to
32
+ * (returned in the token's `scope`), so over-requesting is harmless, but omitting a
33
+ * scope an endpoint needs fails at call time with DEVELOPER_7100.
34
+ */
35
+ const RAMP_OAUTH_SCOPES = [
36
+ 'accounting:read',
37
+ 'ai_spend:read',
38
+ 'approvals:write',
39
+ 'bills:read',
40
+ 'cards:read_agentic',
41
+ 'cards:write',
42
+ 'comments:write',
43
+ 'funds:write',
44
+ 'limits:read',
45
+ 'limits:write',
46
+ 'memos:read',
47
+ 'purchase_orders:read',
48
+ 'receipts:write',
49
+ 'reimbursements:read',
50
+ 'reimbursements:write',
51
+ 'tasks:read',
52
+ 'transactions:read',
53
+ 'transactions:write',
54
+ 'treasury:read',
55
+ 'trips:read',
56
+ 'trips:write',
57
+ 'unified_requests:read',
58
+ 'users:read',
59
+ 'vendors:read',
60
+ 'vendors:write',
61
+ 'x402:write',
62
+ ].join(' ');
63
+ /**
64
+ * Browser login session: runs the OAuth authorization-code + PKCE flow in a
65
+ * Playwright browser and returns OAuthCredentials. login() is overridden wholesale
66
+ * (the base template's static loginUrl + response-watching model doesn't fit a
67
+ * per-session authorize URL with a localhost callback), mirroring NotionMcpSession.
68
+ */
69
+ class RampOAuthServiceSession extends ServiceSession {
70
+ onResponse(_response) {
71
+ // Not used -- login completion is signalled by the OAuth callback, not by
72
+ // inspecting page responses.
73
+ }
74
+ isLoginComplete() {
75
+ // Not used -- login() is overridden entirely.
76
+ return false;
77
+ }
78
+ finalizeCredentials(_browser, _context, _oldCredentials) {
79
+ // Not used -- login() is overridden entirely.
80
+ return Promise.resolve(null);
81
+ }
82
+ async login(encryptedStorage, launchOptions = {}, _oldCredentials) {
83
+ const { withTempBrowserContext } = await import('../playwrightUtils.js');
84
+ const clientId = RAMP_OAUTH_CLIENT_ID;
85
+ return withTempBrowserContext(encryptedStorage, launchOptions, async ({ context }) => {
86
+ const page = await context.newPage();
87
+ const abortController = new AbortController();
88
+ const closeHandler = () => {
89
+ abortController.abort();
90
+ };
91
+ page.on('close', closeHandler);
92
+ context.on('close', closeHandler);
93
+ try {
94
+ // 1. Stand up the localhost callback server (random port; Ramp's public
95
+ // client allows arbitrary loopback ports per RFC 8252).
96
+ const { port, codePromise } = await startOAuthCallbackServer(RAMP_LOGIN_TIMEOUT_MS, abortController.signal, RAMP_OAUTH_CALLBACK_PATH);
97
+ const redirectUri = `http://localhost:${port.toString()}${RAMP_OAUTH_CALLBACK_PATH}`;
98
+ // 2. PKCE verifier/challenge.
99
+ const codeVerifier = generateCodeVerifier();
100
+ const codeChallenge = generateCodeChallenge(codeVerifier);
101
+ // 3. Open Ramp's hosted authorize screen. auth_level=auto triggers the
102
+ // "create/approve an AI agent key" prompt.
103
+ const authUrl = new URL(RAMP_AUTHORIZE_URL);
104
+ authUrl.searchParams.set('response_type', 'code');
105
+ authUrl.searchParams.set('client_id', clientId);
106
+ authUrl.searchParams.set('redirect_uri', redirectUri);
107
+ authUrl.searchParams.set('scope', RAMP_OAUTH_SCOPES);
108
+ authUrl.searchParams.set('state', randomUUID());
109
+ authUrl.searchParams.set('code_challenge', codeChallenge);
110
+ authUrl.searchParams.set('code_challenge_method', 'S256');
111
+ authUrl.searchParams.set('auth_level', 'auto');
112
+ await page.goto(authUrl.toString());
113
+ // 4. Wait for the user to finish and the callback to deliver the code.
114
+ const code = await codePromise;
115
+ // 5. Exchange the code for tokens (public client: no secret).
116
+ const tokens = exchangeCodeForTokens(RAMP_PKCE_TOKEN_ENDPOINT, code, clientId, '', redirectUri, codeVerifier);
117
+ const accessTokenExpiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
118
+ await page.close();
119
+ // Public client: clientSecret is stored as '' so refresh sends client_id only.
120
+ return new OAuthCredentials(clientId, '', tokens.access_token, tokens.refresh_token, accessTokenExpiresAt);
121
+ }
122
+ catch (error) {
123
+ if (error instanceof Error && isBrowserClosedError(error)) {
124
+ throw new LoginCancelledError();
125
+ }
126
+ throw error;
127
+ }
128
+ finally {
129
+ page.off('close', closeHandler);
130
+ context.off('close', closeHandler);
131
+ }
132
+ });
133
+ }
134
+ }
135
+ export class Ramp extends Service {
136
+ name = 'ramp';
137
+ displayName = 'Ramp';
138
+ baseApiUrls = ['https://api.ramp.com/'];
139
+ loginUrl = 'https://app.ramp.com/';
140
+ info = 'Ramp agent-tools API; the REST API is not supported. ' +
141
+ 'Docs: https://api.ramp.com/v1/public/agent-tools/spec/.';
142
+ // Validate credentials against `search-help-center-snippets`: the one agent-tools
143
+ // endpoint that requires only a valid token and no specific scope (`security:
144
+ // [{oauth2: []}]` in the spec), so the check works regardless of which scopes the
145
+ // signed-in user's agent key was granted. It's a POST taking a required
146
+ // {query, rationale} body; a bad token returns a non-200 (404 DEVELOPER_7002).
147
+ credentialCheckCurlArguments = [
148
+ '-X',
149
+ 'POST',
150
+ '-H',
151
+ 'Content-Type: application/json',
152
+ '-d',
153
+ '{"query":"ping","rationale":"latchkey credential check"}',
154
+ 'https://api.ramp.com/developer/v1/agent-tools/search-help-center-snippets',
155
+ ];
156
+ setCredentialsExample(serviceName) {
157
+ return `latchkey auth browser ${serviceName}`;
158
+ }
159
+ /**
160
+ * Browser login: run the OAuth authorization-code + PKCE flow and store the
161
+ * resulting bearer + refresh token.
162
+ */
163
+ getSession(appNamePrefix) {
164
+ return new RampOAuthServiceSession(this, appNamePrefix);
165
+ }
166
+ refreshCredentials(apiCredentials) {
167
+ // Refresh the PKCE access token with the (rotating) refresh token against the
168
+ // `/token/pkce` endpoint, mirroring ramp-cli.
169
+ if (!(apiCredentials instanceof OAuthCredentials)) {
170
+ return Promise.resolve(null);
171
+ }
172
+ if (apiCredentials.refreshToken === undefined || apiCredentials.refreshToken === '') {
173
+ return Promise.resolve(null);
174
+ }
175
+ const tokens = refreshAccessToken(RAMP_PKCE_TOKEN_ENDPOINT, apiCredentials.refreshToken, apiCredentials.clientId, apiCredentials.clientSecret);
176
+ if (tokens === null) {
177
+ return Promise.resolve(null);
178
+ }
179
+ const accessTokenExpiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
180
+ // Ramp rotates refresh tokens; keep the old one only if none came back.
181
+ return Promise.resolve(new OAuthCredentials(apiCredentials.clientId, apiCredentials.clientSecret, tokens.access_token, tokens.refresh_token ?? apiCredentials.refreshToken, accessTokenExpiresAt, apiCredentials.refreshTokenExpiresAt));
182
+ }
183
+ }
184
+ export const RAMP = new Ramp();
185
+ //# sourceMappingURL=ramp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ramp.js","sourceRoot":"","sources":["../../../src/services/ramp.ts"],"names":[],"mappings":"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"}
@@ -41,4 +41,15 @@ export interface AuthBrowserPrepareResult {
41
41
  readonly alreadyPrepared: boolean;
42
42
  }
43
43
  export declare function authBrowserPrepare(registry: ServiceRegistry, apiCredentialStore: ApiCredentialStore, encryptedStorage: EncryptedStorage, config: Config, serviceName: string): Promise<AuthBrowserPrepareResult>;
44
+ export interface PrepareServiceResult {
45
+ readonly serviceName: string;
46
+ readonly credentialType: string;
47
+ }
48
+ /**
49
+ * Store credentials for a service from a validated JSON payload
50
+ * (`latchkey auth prepare <service> <json>`). The whole operation is rejected — and
51
+ * nothing is stored — if the JSON is malformed, fails the service's schema, or
52
+ * the service does not support prepare.
53
+ */
54
+ export declare function prepareService(registry: ServiceRegistry, apiCredentialStore: ApiCredentialStore, serviceName: string, json: string): PrepareServiceResult;
44
55
  //# sourceMappingURL=sharedOperations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sharedOperations.d.ts","sourceRoot":"","sources":["../../src/sharedOperations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAO9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAO5D,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,WAAW,EAAE,MAAM;CAMhC;AAED,qBAAa,yBAA0B,SAAQ,KAAK;;CAKnD;AAED,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,WAAW,EAAE,MAAM;CAOhC;AAkCD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,SAAS,MAAM,EAAE,CAoBnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,CAAC;IAC/C,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,UAAQ,GACd,OAAO,CAAC,kBAAkB,CAAC,CAyB7B;AAED,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC,CAwB5F;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,wBAAwB,CAAC,CA8BnC"}
1
+ {"version":3,"file":"sharedOperations.d.ts","sourceRoot":"","sources":["../../src/sharedOperations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAO9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAY5D,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,WAAW,EAAE,MAAM;CAMhC;AAED,qBAAa,yBAA0B,SAAQ,KAAK;;CAKnD;AAED,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,WAAW,EAAE,MAAM;CAOhC;AAkCD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,SAAS,MAAM,EAAE,CAoBnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,CAAC;IAC/C,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,UAAQ,GACd,OAAO,CAAC,kBAAkB,CAAC,CAyB7B;AAED,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC,CAwB5F;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,wBAAwB,CAAC,CA8BnC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EACzB,kBAAkB,EAAE,kBAAkB,EACtC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,oBAAoB,CAyBtB"}
@@ -9,7 +9,7 @@ import { ApiCredentialStatus } from './apiCredentials/base.js';
9
9
  import { getCredentialStatus } from './apiCredentials/utils.js';
10
10
  import { loadBrowserConfig } from './configDataStore.js';
11
11
  import { BrowserDisabledError, BrowserFlowsNotSupportedError, GraphicalEnvironmentNotFoundError, hasGraphicalEnvironment, } from './playwrightUtils.js';
12
- import { isBrowserClosedError, LoginCancelledError } from './services/core/base.js';
12
+ import { isBrowserClosedError, LoginCancelledError, PrepareInputInvalidError, PrepareNotSupportedError, } from './services/core/base.js';
13
13
  import { RegisteredService } from './services/core/registered.js';
14
14
  // Error classes
15
15
  export class UnknownServiceError extends Error {
@@ -142,4 +142,31 @@ export async function authBrowserPrepare(registry, apiCredentialStore, encrypted
142
142
  apiCredentialStore.save(service.name, apiCredentials);
143
143
  return { alreadyPrepared: false };
144
144
  }
145
+ /**
146
+ * Store credentials for a service from a validated JSON payload
147
+ * (`latchkey auth prepare <service> <json>`). The whole operation is rejected — and
148
+ * nothing is stored — if the JSON is malformed, fails the service's schema, or
149
+ * the service does not support prepare.
150
+ */
151
+ export function prepareService(registry, apiCredentialStore, serviceName, json) {
152
+ const service = lookupService(registry, serviceName);
153
+ // Services opt in to prepare by implementing prepareFromJson; absence is the
154
+ // default "not supported" state.
155
+ if (service.prepareFromJson === undefined) {
156
+ throw new PrepareNotSupportedError(serviceName);
157
+ }
158
+ let parsedJson;
159
+ try {
160
+ parsedJson = JSON.parse(json);
161
+ }
162
+ catch (error) {
163
+ throw new PrepareInputInvalidError(serviceName, `not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
164
+ }
165
+ // prepareFromJson validates against the service's schema and throws
166
+ // PrepareInputInvalidError on any mismatch, so a store only happens once the
167
+ // input is fully valid.
168
+ const credentials = service.prepareFromJson(parsedJson);
169
+ apiCredentialStore.save(service.name, credentials);
170
+ return { serviceName: service.name, credentialType: credentials.objectType };
171
+ }
145
172
  //# sourceMappingURL=sharedOperations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sharedOperations.js","sourceRoot":"","sources":["../../src/sharedOperations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sharedOperations.js","sourceRoot":"","sources":["../../src/sharedOperations.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "2.17.2";
1
+ export declare const VERSION = "2.19.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Auto-generated from package.json by scripts/generateVersion.js.
2
2
  // Do not edit by hand; run `node scripts/generateVersion.js` to refresh.
3
- export const VERSION = "2.17.2";
3
+ export const VERSION = "2.19.0";
4
4
  //# sourceMappingURL=version.js.map
@@ -18,6 +18,7 @@ import { GITLAB } from '../src/services/gitlab.js';
18
18
  import { GITHUB } from '../src/services/github.js';
19
19
  import { derivePermissionsOverrideSigningKey, verifyPermissionsOverrideJwt, } from '../src/gateway/permissionsOverride.js';
20
20
  import { TELEGRAM } from '../src/services/telegram.js';
21
+ import { GOOGLE_GMAIL } from '../src/services/google/gmail.js';
21
22
  import { deleteRegisteredService, loadRegisteredServices, saveRegisteredService, } from '../src/configDataStore.js';
22
23
  import { loadRegisteredServicesIntoServiceRegistry } from '../src/serviceRegistry.js';
23
24
  // Use a fixed test key for deterministic test behavior (32 bytes = 256 bits, base64 encoded)
@@ -948,6 +949,55 @@ describe('CLI commands with dependency injection', () => {
948
949
  expect(exitCode).toBe(1);
949
950
  });
950
951
  });
952
+ describe('prepare command', () => {
953
+ it('stores an OAuth client for a Google service and reports success', async () => {
954
+ const storePath = join(tempDir, 'credentials.json');
955
+ writeSecureFile(storePath, '{}');
956
+ const deps = createMockDependencies({
957
+ registry: new ServiceRegistry([GOOGLE_GMAIL]),
958
+ });
959
+ await runCommand(['auth', 'prepare', 'google-gmail', '{"clientId":"cid","clientSecret":"csecret"}'], deps);
960
+ expect(logs).toContain('Done');
961
+ const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
962
+ expect(storedData['google-gmail']).toEqual({
963
+ objectType: 'oauth',
964
+ clientId: 'cid',
965
+ clientSecret: 'csecret',
966
+ });
967
+ });
968
+ it('returns an error for an unknown service', async () => {
969
+ const deps = createMockDependencies();
970
+ await runCommand(['auth', 'prepare', 'unknown-service', '{}'], deps);
971
+ expect(exitCode).toBe(1);
972
+ });
973
+ it('returns an error for a service that does not support prepare', async () => {
974
+ const deps = createMockDependencies();
975
+ await runCommand(['auth', 'prepare', 'slack', '{"clientId":"a","clientSecret":"b"}'], deps);
976
+ expect(exitCode).toBe(1);
977
+ });
978
+ it('returns an error and stores nothing for malformed JSON', async () => {
979
+ const storePath = join(tempDir, 'credentials.json');
980
+ writeSecureFile(storePath, '{}');
981
+ const deps = createMockDependencies({
982
+ registry: new ServiceRegistry([GOOGLE_GMAIL]),
983
+ });
984
+ await runCommand(['auth', 'prepare', 'google-gmail', '{not valid'], deps);
985
+ expect(exitCode).toBe(1);
986
+ const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
987
+ expect(storedData['google-gmail']).toBeUndefined();
988
+ });
989
+ it('returns an error for input that fails the schema', async () => {
990
+ const storePath = join(tempDir, 'credentials.json');
991
+ writeSecureFile(storePath, '{}');
992
+ const deps = createMockDependencies({
993
+ registry: new ServiceRegistry([GOOGLE_GMAIL]),
994
+ });
995
+ await runCommand(['auth', 'prepare', 'google-gmail', '{"clientId":"only-id"}'], deps);
996
+ expect(exitCode).toBe(1);
997
+ const storedData = JSON.parse(readSecureFile(storePath) ?? '{}');
998
+ expect(storedData['google-gmail']).toBeUndefined();
999
+ });
1000
+ });
951
1001
  describe('curl command', () => {
952
1002
  it('should pass arguments to subprocess', async () => {
953
1003
  const storePath = join(tempDir, 'credentials.json');
@@ -1018,6 +1068,18 @@ describe('CLI commands with dependency injection', () => {
1018
1068
  await runCommand(['curl', 'https://unknown-api.example.com'], deps);
1019
1069
  expect(exitCode).toBe(1);
1020
1070
  });
1071
+ it('should return error when OAuth credentials lack an access token', async () => {
1072
+ const storePath = join(tempDir, 'credentials.json');
1073
+ writeSecureFile(storePath, JSON.stringify({
1074
+ 'google-gmail': { objectType: 'oauth', clientId: 'cid', clientSecret: 'csecret' },
1075
+ }));
1076
+ const deps = createMockDependencies({
1077
+ registry: new ServiceRegistry([GOOGLE_GMAIL]),
1078
+ });
1079
+ await runCommand(['curl', 'https://gmail.googleapis.com/gmail/v1/users/me/profile'], deps);
1080
+ expect(exitCode).toBe(1);
1081
+ expect(errorLogs.length).toBeGreaterThan(0);
1082
+ });
1021
1083
  it('should pass through unknown service when passthroughUnknown is enabled', async () => {
1022
1084
  const deps = createMockDependencies({
1023
1085
  config: createMockConfig({ passthroughUnknown: true }),
@@ -1860,12 +1922,31 @@ describe('CLI commands with dependency injection', () => {
1860
1922
  await runCommand(['auth', 'browser-prepare', 'slack'], deps);
1861
1923
  expect(logs).toContain('Already prepared.');
1862
1924
  });
1925
+ it('forwards `prepare` to the gateway /latchkey endpoint', async () => {
1926
+ const fetchMock = makeFetchMock(new Response(JSON.stringify({ result: { serviceName: 'google-gmail', credentialType: 'oauth' } }), { status: 200 }));
1927
+ const deps = createMockDependencies({
1928
+ config: createMockConfig({ gatewayUrl: GATEWAY_URL }),
1929
+ });
1930
+ await runCommand(['auth', 'prepare', 'google-gmail', '{"clientId":"cid","clientSecret":"csecret"}'], deps);
1931
+ expect(fetchMock).toHaveBeenCalledTimes(1);
1932
+ const [url, init] = fetchMock.mock.calls[0];
1933
+ expect(url).toBe(`${GATEWAY_URL}/latchkey`);
1934
+ expect(JSON.parse(init.body)).toEqual({
1935
+ command: 'auth prepare',
1936
+ params: {
1937
+ serviceName: 'google-gmail',
1938
+ json: '{"clientId":"cid","clientSecret":"csecret"}',
1939
+ },
1940
+ });
1941
+ expect(logs).toContain('Done');
1942
+ });
1863
1943
  it.each([
1864
1944
  ['services list', ['services', 'list']],
1865
1945
  ['services info', ['services', 'info', 'foo']],
1866
1946
  ['auth list', ['auth', 'list']],
1867
1947
  ['auth browser', ['auth', 'browser', 'slack']],
1868
1948
  ['auth browser-prepare', ['auth', 'browser-prepare', 'slack']],
1949
+ ['auth prepare', ['auth', 'prepare', 'foo', '{}']],
1869
1950
  ])('reports gateway errors on stderr (not stdout) for `%s`', async (_name, argv) => {
1870
1951
  makeFetchMock(new Response(JSON.stringify({ error: 'Unknown service: foo.' }), { status: 400 }));
1871
1952
  const deps = createMockDependencies({