@proxy-checkout/cli 0.1.0-prx-128.104.1

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -0
  3. package/dist/cjs/auth.d.cts +38 -0
  4. package/dist/cjs/auth.d.ts +38 -0
  5. package/dist/cjs/auth.js +258 -0
  6. package/dist/cjs/bin.d.cts +2 -0
  7. package/dist/cjs/bin.d.ts +2 -0
  8. package/dist/cjs/bin.js +10 -0
  9. package/dist/cjs/cli.d.cts +10 -0
  10. package/dist/cjs/cli.d.ts +10 -0
  11. package/dist/cjs/cli.js +469 -0
  12. package/dist/cjs/command-schema.d.cts +23 -0
  13. package/dist/cjs/command-schema.d.ts +23 -0
  14. package/dist/cjs/command-schema.js +238 -0
  15. package/dist/cjs/config.d.cts +55 -0
  16. package/dist/cjs/config.d.ts +55 -0
  17. package/dist/cjs/config.js +339 -0
  18. package/dist/cjs/errors.d.cts +20 -0
  19. package/dist/cjs/errors.d.ts +20 -0
  20. package/dist/cjs/errors.js +52 -0
  21. package/dist/cjs/http-client.d.cts +25 -0
  22. package/dist/cjs/http-client.d.ts +25 -0
  23. package/dist/cjs/http-client.js +102 -0
  24. package/dist/cjs/index.d.cts +5 -0
  25. package/dist/cjs/index.d.ts +5 -0
  26. package/dist/cjs/index.js +17 -0
  27. package/dist/cjs/output.d.cts +19 -0
  28. package/dist/cjs/output.d.ts +19 -0
  29. package/dist/cjs/output.js +76 -0
  30. package/dist/cjs/package.json +3 -0
  31. package/dist/cjs/webhooks.d.cts +81 -0
  32. package/dist/cjs/webhooks.d.ts +81 -0
  33. package/dist/cjs/webhooks.js +343 -0
  34. package/dist/esm/auth.d.ts +38 -0
  35. package/dist/esm/auth.js +253 -0
  36. package/dist/esm/bin.d.ts +2 -0
  37. package/dist/esm/bin.js +8 -0
  38. package/dist/esm/cli.d.ts +10 -0
  39. package/dist/esm/cli.js +465 -0
  40. package/dist/esm/command-schema.d.ts +23 -0
  41. package/dist/esm/command-schema.js +232 -0
  42. package/dist/esm/config.d.ts +55 -0
  43. package/dist/esm/config.js +333 -0
  44. package/dist/esm/errors.d.ts +20 -0
  45. package/dist/esm/errors.js +46 -0
  46. package/dist/esm/http-client.d.ts +25 -0
  47. package/dist/esm/http-client.js +98 -0
  48. package/dist/esm/index.d.ts +5 -0
  49. package/dist/esm/index.js +5 -0
  50. package/dist/esm/output.d.ts +19 -0
  51. package/dist/esm/output.js +71 -0
  52. package/dist/esm/webhooks.d.ts +81 -0
  53. package/dist/esm/webhooks.js +337 -0
  54. package/package.json +68 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Linus Labs, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Proxy CLI
2
+
3
+ `@proxy-checkout/cli` publishes the `proxy` binary: a revocable, capability-scoped command foundation for developers and agents. The first workflow relays real, Proxy-signed test webhooks to localhost and can optionally orchestrate Stripe CLI through a temporary Proxy ingress.
4
+
5
+ Requires Node.js 22 or newer and has no production npm dependencies.
6
+
7
+ ## Install and authenticate
8
+
9
+ ```bash
10
+ npm install --global @proxy-checkout/cli
11
+
12
+ proxy auth login \
13
+ --profile my-app-test \
14
+ --merchant merch_... \
15
+ --mode test
16
+
17
+ proxy auth status \
18
+ --profile my-app-test \
19
+ --merchant merch_... \
20
+ --mode test
21
+ ```
22
+
23
+ Authentication uses an RFC 8628 browser/device handoff. Proxy exchanges the short-lived approved browser session for a distinct `pcli_test_...` credential, revokes the temporary session, and stores only the CLI credential. The CLI never persists a Portal session or exposes a merchant `sk_test_*` key.
24
+
25
+ Profiles bind the saved API origin, organization, merchant, and mode. `--merchant` and `--mode` assert the target returned by login or remote credential introspection; they do not select or elevate a merchant client-side. Stored credentials cannot be sent to a different API origin.
26
+
27
+ macOS uses the built-in Security framework and Keychain; supported Linux desktops use Secret Service. Native-store writes pass over stdin, never process arguments or environment. When neither native store is available, the CLI warns and uses an owner-only `0600` file under the user's data directory. Agents and CI can inject `PROXY_CLI_TOKEN` without writing a credential locally.
28
+
29
+ All grants issued in this release and all operational/resource commands are test-only. `auth status` and `auth logout` are mode-neutral safety controls so a future separately issued live grant can still be inspected and revoked. This release does not issue live grants, and `--mode live` on login or a resource command exits `5` with `cli_live_mode_not_enabled` before authentication or resource creation. The credential/verifier, profiles, command registry, and policies model mode explicitly so future live commands can require a separate live-bound grant, capability, and command intent without redesigning auth.
30
+
31
+ ## Listen for Proxy webhooks
32
+
33
+ An active HTTPS source endpoint must already exist. Its selected event types, schema version, and signing-secret identity are cloned server-side; the CLI never receives the signing secret.
34
+
35
+ ```bash
36
+ proxy webhooks endpoints list --profile my-app-test --format json
37
+
38
+ proxy listen \
39
+ --profile my-app-test \
40
+ --merchant merch_... \
41
+ --mode test \
42
+ --endpoint whend_... \
43
+ --forward-to http://127.0.0.1:3000/webhooks
44
+ ```
45
+
46
+ The listener has a two-minute lease renewed every 30 seconds. Proxy prepares the canonical body and signature headers, the CLI forwards those exact bytes to the loopback URL, and the server records the real local outcome in its normal attempt/retry state machine. The source HTTPS endpoint continues to receive its independent normal delivery. Only `localhost`, `127.0.0.1`, and `::1` HTTP(S) destinations are allowed.
47
+
48
+ Control-C closes the listener. Logout closes resources owned by the credential. The server expires abandoned listeners after a crash.
49
+
50
+ ## Optional Stripe CLI orchestration
51
+
52
+ Install and authenticate [Stripe CLI](https://docs.stripe.com/stripe-cli), then run:
53
+
54
+ ```bash
55
+ stripe login
56
+
57
+ proxy listen \
58
+ --stripe \
59
+ --stripe-psp-config pspcfg_... \
60
+ --profile my-app-test \
61
+ --merchant merch_... \
62
+ --mode test \
63
+ --endpoint whend_... \
64
+ --forward-to http://127.0.0.1:3000/webhooks
65
+ ```
66
+
67
+ The Stripe signing secret is captured only in child-process memory, never echoed or placed in argv, and stored only inside Proxy's existing encrypted temporary PSP ingress boundary. The CLI never receives the merchant's Stripe API credential. Both the Stripe ingress and local listener close on exit or expire server-side after a crash.
68
+
69
+ ## Advanced delivery controls
70
+
71
+ Use the one-command listener for normal work. Explicit controls make retry, duplicate, replay, and ordering tests deterministic:
72
+
73
+ ```bash
74
+ proxy webhooks listeners create --endpoint whend_... --profile my-app-test
75
+ proxy webhooks listeners heartbeat --listener whend_... --profile my-app-test
76
+ proxy webhooks deliveries list --listener whend_... --profile my-app-test --format json
77
+
78
+ proxy webhooks deliveries forward \
79
+ --listener whend_... \
80
+ --delivery whdel_... \
81
+ --forward-to http://127.0.0.1:3000/webhooks \
82
+ --profile my-app-test
83
+
84
+ proxy webhooks deliveries replay \
85
+ --listener whend_... \
86
+ --delivery whdel_... \
87
+ --reason "verify duplicate handling" \
88
+ --profile my-app-test
89
+
90
+ # Read replay.outbox_event_id from the previous JSON result.
91
+ proxy webhooks deliveries forward \
92
+ --listener whend_... \
93
+ --replay-outbox out_... \
94
+ --forward-to http://127.0.0.1:3000/webhooks \
95
+ --profile my-app-test
96
+
97
+ proxy webhooks listeners close --listener whend_... --profile my-app-test
98
+ ```
99
+
100
+ Manual listeners require an explicit heartbeat while active. Selecting delivery IDs permits intentional out-of-order claims. Replay creates fresh canonical work/signing while preserving original delivery history and recording the actor/reason; pass its returned outbox ID to `deliveries forward --replay-outbox` when no managed `proxy listen` process is active. Application handlers must remain idempotent.
101
+
102
+ ## Agent and library use
103
+
104
+ Every command uses the same versioned registry exported by this package:
105
+
106
+ ```bash
107
+ proxy commands schema --format json
108
+ proxy auth status --schema --format json
109
+ proxy --llms-full
110
+ ```
111
+
112
+ Non-TTY output defaults to JSON Lines. Pass `--format json` for one-shot JSON, `--format jsonl` for event streams, and `--no-input` with explicit endpoint/merchant/mode flags to prohibit prompts. Errors use stable machine codes; process exit codes are exported as `cliExitCodes`.
113
+
114
+ The package boundary also exports `runCli`, `commandSchemas`, `commandSchemaDocument`, `findCommandSchema`, `llmsFullDocument`, `CliConfigStore`, `CliError`, and their public types. A future MCP or skill adapter can invoke the same command layer and inspect the same schemas instead of scraping terminal prose.
115
+
116
+ | Exit | Meaning |
117
+ | ---: | --- |
118
+ | `0` | Success |
119
+ | `2` | Usage/validation |
120
+ | `3` | Authentication |
121
+ | `4` | Network/API |
122
+ | `5` | Capability/merchant/mode denied |
123
+ | `6` | Conflict/non-claimable state |
124
+ | `7` | Missing or unauthenticated dependency |
125
+ | `8` | Interrupted |
126
+ | `10` | Internal error |
127
+
128
+ For local or preview APIs, pass `--api-base https://...`. Plain HTTP is accepted only for loopback development. See the public [Local webhooks guide](https://docs.proxycheckout.com/guides/local-webhooks/) for handler verification, troubleshooting, and full safety behavior.
@@ -0,0 +1,38 @@
1
+ import type { CliConfigStore, CliProfile } from "./config.js";
2
+ import type { CliOutput } from "./output.js";
3
+ interface CredentialExchangeResponse {
4
+ credential: string;
5
+ profile: {
6
+ api_version: string;
7
+ capability_version: number;
8
+ credential_id: string;
9
+ expires_at: string | null;
10
+ merchant_id: string;
11
+ mode: "test" | "live";
12
+ name: string;
13
+ organization_id: string;
14
+ };
15
+ }
16
+ export declare function login(input: {
17
+ apiBaseUrl: string;
18
+ configStore: CliConfigStore;
19
+ expectedMerchantId?: string;
20
+ expectedMode?: "live" | "test";
21
+ name?: string;
22
+ noBrowser: boolean;
23
+ output: CliOutput;
24
+ profileName: string;
25
+ signal?: AbortSignal;
26
+ }): Promise<CliProfile>;
27
+ export declare function status(input: {
28
+ apiBaseUrl: string;
29
+ token: string;
30
+ }): Promise<CredentialExchangeResponse["profile"]>;
31
+ export declare function logout(input: {
32
+ apiBaseUrl: string;
33
+ configStore: CliConfigStore;
34
+ profileName: string;
35
+ removeLocalProfile: boolean;
36
+ token: string;
37
+ }): Promise<void>;
38
+ export {};
@@ -0,0 +1,38 @@
1
+ import type { CliConfigStore, CliProfile } from "./config.js";
2
+ import type { CliOutput } from "./output.js";
3
+ interface CredentialExchangeResponse {
4
+ credential: string;
5
+ profile: {
6
+ api_version: string;
7
+ capability_version: number;
8
+ credential_id: string;
9
+ expires_at: string | null;
10
+ merchant_id: string;
11
+ mode: "test" | "live";
12
+ name: string;
13
+ organization_id: string;
14
+ };
15
+ }
16
+ export declare function login(input: {
17
+ apiBaseUrl: string;
18
+ configStore: CliConfigStore;
19
+ expectedMerchantId?: string;
20
+ expectedMode?: "live" | "test";
21
+ name?: string;
22
+ noBrowser: boolean;
23
+ output: CliOutput;
24
+ profileName: string;
25
+ signal?: AbortSignal;
26
+ }): Promise<CliProfile>;
27
+ export declare function status(input: {
28
+ apiBaseUrl: string;
29
+ token: string;
30
+ }): Promise<CredentialExchangeResponse["profile"]>;
31
+ export declare function logout(input: {
32
+ apiBaseUrl: string;
33
+ configStore: CliConfigStore;
34
+ profileName: string;
35
+ removeLocalProfile: boolean;
36
+ token: string;
37
+ }): Promise<void>;
38
+ export {};
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.login = login;
4
+ exports.status = status;
5
+ exports.logout = logout;
6
+ const node_child_process_1 = require("node:child_process");
7
+ const node_os_1 = require("node:os");
8
+ const errors_js_1 = require("./errors.js");
9
+ const http_client_js_1 = require("./http-client.js");
10
+ const clientId = "proxy-cli";
11
+ const deviceScope = "proxy.cli.local-webhook-development";
12
+ const deviceGrantType = "urn:ietf:params:oauth:grant-type:device_code";
13
+ async function login(input) {
14
+ const signal = input.signal ?? new AbortController().signal;
15
+ const client = new http_client_js_1.ProxyApiClient(input.apiBaseUrl);
16
+ const codeResult = await client.oauthRequest("/api/auth/device/code", {
17
+ client_id: clientId,
18
+ scope: deviceScope,
19
+ }, { signal });
20
+ const device = requireDeviceCode(codeResult);
21
+ const verificationUrl = validateVerificationUrl(device.verification_uri_complete);
22
+ input.output.event("auth.device_code", {
23
+ expires_in: device.expires_in,
24
+ user_code: device.user_code,
25
+ verification_uri: device.verification_uri,
26
+ verification_uri_complete: verificationUrl,
27
+ }, `Your pairing code is ${device.user_code}. Approve it at ${verificationUrl}`);
28
+ if (!input.noBrowser) {
29
+ const opened = await openBrowser(verificationUrl);
30
+ if (!opened) {
31
+ input.output.notice("Could not open a browser automatically; use the approval URL above.");
32
+ }
33
+ }
34
+ const temporarySession = await pollForDeviceToken(client, device, input.output, signal);
35
+ const approved = parseApprovedScope(temporarySession.scope);
36
+ const exchangeClient = new http_client_js_1.ProxyApiClient(input.apiBaseUrl, temporarySession.access_token);
37
+ const exchanged = await exchangeClient.request("/cli/auth/exchange", {
38
+ method: "POST",
39
+ signal,
40
+ body: {
41
+ approval_id: approved.approvalId,
42
+ merchant_id: approved.merchantId,
43
+ name: normalizeCredentialName(input.name),
44
+ user_code: device.user_code,
45
+ },
46
+ });
47
+ const profile = requireExchangeProfile(exchanged.profile, input.apiBaseUrl);
48
+ if (input.expectedMerchantId && profile.merchantId !== input.expectedMerchantId) {
49
+ const mismatch = new errors_js_1.CliError("The approved CLI credential does not match the requested merchant", "cli_merchant_not_allowed", errors_js_1.cliExitCodes.permission);
50
+ await revokeExchangedCredential(input.apiBaseUrl, exchanged.credential, profile, mismatch);
51
+ throw mismatch;
52
+ }
53
+ if (input.expectedMode && profile.mode !== input.expectedMode) {
54
+ const mismatch = new errors_js_1.CliError("The approved CLI credential does not match the requested mode", input.expectedMode === "live" ? "cli_live_mode_not_enabled" : "cli_mode_not_allowed", errors_js_1.cliExitCodes.permission);
55
+ await revokeExchangedCredential(input.apiBaseUrl, exchanged.credential, profile, mismatch);
56
+ throw mismatch;
57
+ }
58
+ try {
59
+ const saved = await input.configStore.saveProfile(input.profileName, profile, exchanged.credential);
60
+ if (saved.credentialStore === "file") {
61
+ input.output.notice("Warning: native credential storage is unavailable; the CLI credential is in an owner-only (0600) file fallback.");
62
+ }
63
+ return saved;
64
+ }
65
+ catch (error) {
66
+ await revokeExchangedCredential(input.apiBaseUrl, exchanged.credential, profile, error);
67
+ throw error;
68
+ }
69
+ }
70
+ async function revokeExchangedCredential(apiBaseUrl, credential, profile, originalError) {
71
+ try {
72
+ await new http_client_js_1.ProxyApiClient(apiBaseUrl, credential).request("/cli/auth/logout", {
73
+ method: "POST",
74
+ });
75
+ }
76
+ catch (cleanupError) {
77
+ throw new errors_js_1.CliError(`Login did not complete and cleanup of credential ${profile.credentialId} could not be confirmed; revoke it in the Proxy Portal`, "cli_credential_cleanup_failed", errors_js_1.cliExitCodes.network, {
78
+ cleanup_error_class: cleanupError instanceof Error ? cleanupError.name : typeof cleanupError,
79
+ credential_id: profile.credentialId,
80
+ original_error_class: originalError instanceof Error ? originalError.name : typeof originalError,
81
+ original_error_code: originalError instanceof errors_js_1.CliError ? originalError.code : undefined,
82
+ });
83
+ }
84
+ }
85
+ async function status(input) {
86
+ const response = await new http_client_js_1.ProxyApiClient(input.apiBaseUrl, input.token).request("/cli/auth/status");
87
+ return response.profile;
88
+ }
89
+ async function logout(input) {
90
+ await new http_client_js_1.ProxyApiClient(input.apiBaseUrl, input.token).request("/cli/auth/logout", {
91
+ method: "POST",
92
+ });
93
+ if (input.removeLocalProfile) {
94
+ await input.configStore.removeProfile(input.profileName);
95
+ }
96
+ }
97
+ async function pollForDeviceToken(client, device, output, signal) {
98
+ const expiresAt = Date.now() + device.expires_in * 1_000;
99
+ let intervalMs = Math.max(1, device.interval) * 1_000;
100
+ while (Date.now() < expiresAt) {
101
+ await delay(intervalMs, signal);
102
+ const response = await client.oauthRequest("/api/auth/device/token", {
103
+ client_id: clientId,
104
+ device_code: device.device_code,
105
+ grant_type: deviceGrantType,
106
+ }, { signal });
107
+ if (response.ok) {
108
+ return requireDeviceToken(response.body);
109
+ }
110
+ const errorCode = readString(response.body, "error");
111
+ if (errorCode === "authorization_pending") {
112
+ continue;
113
+ }
114
+ if (errorCode === "slow_down") {
115
+ intervalMs += 5_000;
116
+ continue;
117
+ }
118
+ if (errorCode === "access_denied") {
119
+ throw new errors_js_1.CliError("Device authorization was denied", "cli_device_access_denied", errors_js_1.cliExitCodes.authentication);
120
+ }
121
+ if (errorCode === "expired_token") {
122
+ throw deviceExpired();
123
+ }
124
+ output.notice("The authentication service rejected the device exchange.");
125
+ throw new errors_js_1.CliError("Device authorization failed", "cli_device_authorization_failed", errors_js_1.cliExitCodes.authentication, { oauth_error: errorCode });
126
+ }
127
+ throw deviceExpired();
128
+ }
129
+ function requireDeviceCode(result) {
130
+ if (!result.ok ||
131
+ !isRecord(result.body) ||
132
+ !readString(result.body, "device_code") ||
133
+ !readString(result.body, "user_code") ||
134
+ !readString(result.body, "verification_uri") ||
135
+ !readString(result.body, "verification_uri_complete") ||
136
+ !readNumber(result.body, "expires_in") ||
137
+ !readNumber(result.body, "interval")) {
138
+ throw new errors_js_1.CliError("Proxy authentication returned an invalid device code", "cli_invalid_auth_response", errors_js_1.cliExitCodes.network, { status: result.status });
139
+ }
140
+ return result.body;
141
+ }
142
+ function requireDeviceToken(value) {
143
+ if (!isRecord(value) ||
144
+ readString(value, "token_type")?.toLowerCase() !== "bearer" ||
145
+ !readString(value, "access_token") ||
146
+ !readString(value, "scope")) {
147
+ throw new errors_js_1.CliError("Proxy authentication returned an invalid temporary session", "cli_invalid_auth_response", errors_js_1.cliExitCodes.network);
148
+ }
149
+ return value;
150
+ }
151
+ function parseApprovedScope(scope) {
152
+ const parts = new Set(scope.split(/\s+/));
153
+ const approval = [...parts].find((part) => part.startsWith("approval:"));
154
+ const merchant = [...parts].find((part) => part.startsWith("merchant:"));
155
+ if (!parts.has(deviceScope) ||
156
+ !parts.has("mode:test") ||
157
+ !approval?.startsWith("approval:cliappr_") ||
158
+ !merchant?.startsWith("merchant:merch_")) {
159
+ throw new errors_js_1.CliError("Device approval did not contain the required test-merchant capability grant", "cli_invalid_approval_scope", errors_js_1.cliExitCodes.permission);
160
+ }
161
+ return {
162
+ approvalId: approval.slice("approval:".length),
163
+ merchantId: merchant.slice("merchant:".length),
164
+ };
165
+ }
166
+ function requireExchangeProfile(profile, apiBaseUrl) {
167
+ if (profile.mode !== "test" ||
168
+ typeof profile.api_version !== "string" ||
169
+ profile.api_version.length === 0 ||
170
+ !Number.isSafeInteger(profile.capability_version) ||
171
+ profile.capability_version < 1 ||
172
+ !profile.credential_id.startsWith("apk_") ||
173
+ !profile.merchant_id.startsWith("merch_") ||
174
+ !profile.organization_id.startsWith("org_") ||
175
+ typeof profile.name !== "string" ||
176
+ profile.name.length < 1 ||
177
+ profile.name.length > 80 ||
178
+ (profile.expires_at !== null && Number.isNaN(Date.parse(profile.expires_at)))) {
179
+ throw new errors_js_1.CliError("Proxy returned an invalid or non-test CLI grant", "cli_invalid_credential_grant", errors_js_1.cliExitCodes.permission);
180
+ }
181
+ return {
182
+ apiBaseUrl,
183
+ apiVersion: profile.api_version,
184
+ capabilityVersion: profile.capability_version,
185
+ credentialId: profile.credential_id,
186
+ expiresAt: profile.expires_at,
187
+ merchantId: profile.merchant_id,
188
+ mode: profile.mode,
189
+ name: profile.name,
190
+ organizationId: profile.organization_id,
191
+ };
192
+ }
193
+ function normalizeCredentialName(name) {
194
+ const value = name?.trim() || `Proxy CLI on ${(0, node_os_1.hostname)()}`;
195
+ if (value.length > 80) {
196
+ throw new errors_js_1.CliError("Credential name must be at most 80 characters", "cli_credential_name_invalid", errors_js_1.cliExitCodes.usage);
197
+ }
198
+ return value;
199
+ }
200
+ function validateVerificationUrl(value) {
201
+ let url;
202
+ try {
203
+ url = new URL(value);
204
+ }
205
+ catch {
206
+ throw new errors_js_1.CliError("Proxy returned an invalid browser approval URL", "cli_invalid_auth_response", errors_js_1.cliExitCodes.network);
207
+ }
208
+ const loopback = ["127.0.0.1", "::1", "localhost"].includes(url.hostname);
209
+ if ((url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) ||
210
+ url.username ||
211
+ url.password) {
212
+ throw new errors_js_1.CliError("Proxy returned an unsafe browser approval URL", "cli_invalid_auth_response", errors_js_1.cliExitCodes.network);
213
+ }
214
+ return url.toString();
215
+ }
216
+ async function openBrowser(url) {
217
+ const command = (0, node_os_1.platform)() === "darwin" ? "open" : (0, node_os_1.platform)() === "win32" ? "cmd" : "xdg-open";
218
+ const args = (0, node_os_1.platform)() === "win32" ? ["/c", "start", "", url] : [url];
219
+ return new Promise((resolve) => {
220
+ const child = (0, node_child_process_1.spawn)(command, args, { detached: true, shell: false, stdio: "ignore" });
221
+ child.once("error", () => resolve(false));
222
+ child.once("spawn", () => {
223
+ child.unref();
224
+ resolve(true);
225
+ });
226
+ });
227
+ }
228
+ function deviceExpired() {
229
+ return new errors_js_1.CliError("Device authorization expired", "cli_device_authorization_expired", errors_js_1.cliExitCodes.authentication);
230
+ }
231
+ function readString(value, key) {
232
+ return isRecord(value) && typeof value[key] === "string" ? value[key] : undefined;
233
+ }
234
+ function readNumber(value, key) {
235
+ return isRecord(value) && typeof value[key] === "number" ? value[key] : undefined;
236
+ }
237
+ function isRecord(value) {
238
+ return typeof value === "object" && value !== null && !Array.isArray(value);
239
+ }
240
+ function delay(milliseconds, signal) {
241
+ if (signal.aborted)
242
+ return Promise.reject(authInterrupted());
243
+ return new Promise((resolve, reject) => {
244
+ const onAbort = () => {
245
+ clearTimeout(timer);
246
+ signal.removeEventListener("abort", onAbort);
247
+ reject(authInterrupted());
248
+ };
249
+ const timer = setTimeout(() => {
250
+ signal.removeEventListener("abort", onAbort);
251
+ resolve();
252
+ }, milliseconds);
253
+ signal.addEventListener("abort", onAbort, { once: true });
254
+ });
255
+ }
256
+ function authInterrupted() {
257
+ return new errors_js_1.CliError("Device authorization interrupted", "cli_auth_interrupted", errors_js_1.cliExitCodes.interrupted);
258
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const cli_js_1 = require("./cli.js");
5
+ void (0, cli_js_1.runCli)(process.argv.slice(2)).then((exitCode) => {
6
+ process.exitCode = exitCode;
7
+ }, () => {
8
+ process.stderr.write("Proxy CLI failed unexpectedly.\n");
9
+ process.exitCode = 10;
10
+ });
@@ -0,0 +1,10 @@
1
+ import { CliConfigStore } from "./config.js";
2
+ import { type CliIo } from "./output.js";
3
+ export declare const cliVersion = "0.1.0-prx-128.104.1";
4
+ export interface RunCliOptions {
5
+ configStore?: CliConfigStore;
6
+ environment?: NodeJS.ProcessEnv;
7
+ io?: CliIo;
8
+ signal?: AbortSignal;
9
+ }
10
+ export declare function runCli(argv: readonly string[], options?: RunCliOptions): Promise<number>;
@@ -0,0 +1,10 @@
1
+ import { CliConfigStore } from "./config.js";
2
+ import { type CliIo } from "./output.js";
3
+ export declare const cliVersion = "0.1.0-prx-128.104.1";
4
+ export interface RunCliOptions {
5
+ configStore?: CliConfigStore;
6
+ environment?: NodeJS.ProcessEnv;
7
+ io?: CliIo;
8
+ signal?: AbortSignal;
9
+ }
10
+ export declare function runCli(argv: readonly string[], options?: RunCliOptions): Promise<number>;