@meterapp/car-image 1.0.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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/agent-config.js +104 -0
- package/dist/agent-config.js.map +1 -0
- package/dist/args.js +145 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/browser.js +20 -0
- package/dist/browser.js.map +1 -0
- package/dist/client.js +38 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/agents.js +38 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/billing.js +41 -0
- package/dist/commands/billing.js.map +1 -0
- package/dist/commands/catalog.js +169 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/describe.js +52 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/doctor.js +53 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/help.js +58 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/images.js +168 -0
- package/dist/commands/images.js.map +1 -0
- package/dist/commands/index.js +44 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/login.js +64 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/maintenance.js +156 -0
- package/dist/commands/maintenance.js.map +1 -0
- package/dist/commands/onboard.js +42 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/commands/whoami.js +41 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config.js +84 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/describe.js +226 -0
- package/dist/describe.js.map +1 -0
- package/dist/device-login.js +42 -0
- package/dist/device-login.js.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.js +54 -0
- package/dist/errors.js.map +1 -0
- package/dist/main.js +170 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp/server.js +149 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/output.js +90 -0
- package/dist/output.js.map +1 -0
- package/dist/prompts.js +29 -0
- package/dist/prompts.js.map +1 -0
- package/dist/telemetry.js +50 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/update.js +142 -0
- package/dist/update.js.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Meter
|
|
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,93 @@
|
|
|
1
|
+
# @meterapp/car-image
|
|
2
|
+
|
|
3
|
+
CLI and MCP server for the **Car Image API** by Meter — studio-quality, transparent-background renders of any vehicle in the open [`@meterapp/vehicle-db`](https://github.com/MeterApp/vehicle-db) catalog (1,145 makes, 14,841 models, 1990–2026). Six camera views, fifteen preset colors, PNG/WebP/JPG up to 1024 px.
|
|
4
|
+
|
|
5
|
+
**Pricing:** 1 credit per image (cached or generated) · $1 = 1,000 credits · 100 free credits · no subscription.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @meterapp/car-image # no install
|
|
11
|
+
npm install -g @meterapp/car-image # or pnpm add -g / bun add -g
|
|
12
|
+
curl -fsSL https://car-imgs.vercel.app/install.sh | sh
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Node.js 20+.
|
|
16
|
+
|
|
17
|
+
## 60-second quickstart
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
car-image # onboarding: login → account → quick check → next steps
|
|
21
|
+
car-image login # browser device flow; key stored with mode 0600
|
|
22
|
+
car-image get --make Porsche --model 911 --year 2024 --view side --color red --out porsche.png
|
|
23
|
+
car-image url --make BMW --model M3 --year 2022 --ttl 86400 # signed, key-free URL for a web page
|
|
24
|
+
car-image resolve red 2024 porsche 911 side view # free text → parameters + ready command
|
|
25
|
+
car-image agent-config --host claude-code # MCP setup for agents
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
| Command | What it does |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `car-image` | Interactive onboarding on a TTY (update check → login if needed → `whoami` → free quick check → next steps). Prints help when not interactive. |
|
|
33
|
+
| `login [--no-browser] [--client-name <name>]` | Device-flow sign-in. Opens the browser, shows the code, polls, stores the key at `$CAR_IMAGE_CONFIG` or `$XDG_CONFIG_HOME/car-image-api/config.json` (default `~/.config/…`) with mode `0600`. The key is printed once. |
|
|
34
|
+
| `logout` | Removes the stored key. |
|
|
35
|
+
| `whoami` (alias `status`) | Credits (≈ $), auto-reload, payment method, 30-day usage, masked key prefix and scopes, base URL. |
|
|
36
|
+
| `doctor [--json] [--yes] [--no-image]` | Smoke-tests every endpoint: health, options, vehicles years/makes/models, search, resolve, account, signed-URL mint (1 credit) + HEAD/GET redeem, optional keyed image GET (1 credit, asks unless `--yes`), feedback. Prints check / status / latency / cache / credits / notes and fix hints; exit 1 on any failure. |
|
|
37
|
+
| `get --make --model --year [--view] [--color] [--size\|--width --height] [--format] [--out file\|-] [--url] [--json]` | Downloads one image (1 credit). `--url` prints a signed URL instead. |
|
|
38
|
+
| `url --make … [--ttl 3600] [--max-uses 0] [--batch file.json] [--json]` | Creates signed delivery URLs (1 credit each, up to 50 per batch). |
|
|
39
|
+
| `search <query…> [--limit] [--year]` | Fuzzy catalog search (free, no key). |
|
|
40
|
+
| `resolve <free text…>` | Free text → exact parameters, candidates, confidence and a ready-to-run `car-image get …`. |
|
|
41
|
+
| `options` | Views (with yaw), colors (hex), sizes, formats, pricing, catalog coverage. |
|
|
42
|
+
| `describe [operationId\|path\|all] [--json]` | Explains the REST API from `/openapi.json`: parameters with enums/defaults, response headers, error codes, agent notes. |
|
|
43
|
+
| `feedback (--request-id ID \| --make … --model … --year … [--view] [--color]) (--rating 1-5 \| --good \| --bad) [--reason]` | Rates a delivered image. |
|
|
44
|
+
| `billing [--credits 5000\|10000\|25000\|100000] [--portal] [--no-browser]` | Opens hosted Stripe Checkout or the billing portal. The CLI never touches card data. |
|
|
45
|
+
| `mcp` | Runs the stdio MCP server (see below). |
|
|
46
|
+
| `agent-config [--host claude-code\|claude-desktop\|cursor\|chatgpt\|generic] [--remote] [--json]` | Ready-to-paste MCP configuration. |
|
|
47
|
+
| `update [--check]` | Compares with the npm registry and updates via npm/pnpm/bun (detected). |
|
|
48
|
+
| `config path \| get <key> \| set <key> <value> \| list` | Keys: `autoUpdate` (`ask`\|`always`\|`never`), `telemetry` (`on`\|`off`), `baseUrl`. |
|
|
49
|
+
| `telemetry on\|off\|status` | Anonymous usage events (see below). |
|
|
50
|
+
| `help [command]` | Help. |
|
|
51
|
+
|
|
52
|
+
Global options: `--json`, `--quiet`, `--base-url <url>`, `--api-key <key>`, `--no-color`, `-h/--help`, `-v/--version`.
|
|
53
|
+
Environment: `CAR_IMAGE_API_KEY`, `CAR_IMAGE_API_URL`, `CAR_IMAGE_CONFIG`, `CAR_IMAGE_TELEMETRY=0`.
|
|
54
|
+
Exit codes: `0` ok · `1` failure · `2` usage error. API errors are shown as `Title: detail`, a fix hint (`401 → car-image login`, `402 → car-image billing`, `429 → wait Retry-After`) and the `request_id`.
|
|
55
|
+
|
|
56
|
+
## Agents (MCP)
|
|
57
|
+
|
|
58
|
+
Two servers expose the same tools — `get_car_image`, `create_car_image_urls`, `search_vehicles`, `resolve_vehicle`, `list_image_options`, `get_account`, `rate_image`, `describe_api`:
|
|
59
|
+
|
|
60
|
+
- **Remote (recommended):** `https://car-imgs.vercel.app/api/mcp` with header `Authorization: Bearer ${CAR_IMAGE_API_KEY}`.
|
|
61
|
+
- **Local stdio:** `car-image mcp` (reads `CAR_IMAGE_API_KEY` or the stored key).
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Claude Code
|
|
65
|
+
claude mcp add --transport http car-image https://car-imgs.vercel.app/api/mcp --header "Authorization: Bearer ${CAR_IMAGE_API_KEY}"
|
|
66
|
+
claude mcp add car-image-local --env CAR_IMAGE_API_KEY=${CAR_IMAGE_API_KEY} -- npx -y @meterapp/car-image mcp
|
|
67
|
+
|
|
68
|
+
# Cursor (.cursor/mcp.json), Claude Desktop, ChatGPT, generic:
|
|
69
|
+
car-image agent-config --host cursor|claude-desktop|chatgpt|generic
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The server instructions tell agents that every image costs 1 credit, to keep keys out of URLs, and to ask a human before buying credits on `402`.
|
|
73
|
+
|
|
74
|
+
## Updates
|
|
75
|
+
|
|
76
|
+
On interactive runs the CLI checks npm at most once a day (cached next to the config file). `autoUpdate` = `ask` (default, y/N prompt), `always` (silent install) or `never` (notice only). Runs through `npx`/`pnpm dlx` are never auto-updated.
|
|
77
|
+
|
|
78
|
+
## Telemetry
|
|
79
|
+
|
|
80
|
+
The CLI sends anonymous usage events (`cli_install`, `cli_login`, `cli_doctor`, `cli_command` with command name, version, OS, Node version and exit code) to `POST /api/v1/events`, fire-and-forget with a 1.5 s timeout — never keys, vehicles, file names or output. A one-line disclosure is printed on first run. Disable with `car-image telemetry off` or `CAR_IMAGE_TELEMETRY=0`.
|
|
81
|
+
|
|
82
|
+
## Uninstall
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm uninstall -g @meterapp/car-image # or pnpm remove -g / bun remove -g
|
|
86
|
+
rm -rf ~/.config/car-image-api # stored key + update cache
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Revoke keys any time at https://car-imgs.vercel.app/dashboard/keys.
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT © Meter
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { DEFAULT_BASE_URL } from "@meterapp/car-image-sdk";
|
|
2
|
+
import { PACKAGE_NAME } from "./version.js";
|
|
3
|
+
export const AGENT_HOSTS = ["claude-code", "claude-desktop", "cursor", "chatgpt", "generic"];
|
|
4
|
+
export const KEY_PLACEHOLDER = "${CAR_IMAGE_API_KEY}";
|
|
5
|
+
export function mcpUrl(baseUrl) {
|
|
6
|
+
return `${baseUrl.replace(/\/+$/, "")}/api/mcp`;
|
|
7
|
+
}
|
|
8
|
+
/** Stdio server entry for `mcpServers` maps (Claude Desktop, Cursor, Claude Code). */
|
|
9
|
+
export function stdioServerConfig(baseUrl) {
|
|
10
|
+
const env = { CAR_IMAGE_API_KEY: KEY_PLACEHOLDER };
|
|
11
|
+
if (baseUrl.replace(/\/+$/, "") !== DEFAULT_BASE_URL)
|
|
12
|
+
env.CAR_IMAGE_API_URL = baseUrl;
|
|
13
|
+
return { command: "npx", args: ["-y", PACKAGE_NAME, "mcp"], env };
|
|
14
|
+
}
|
|
15
|
+
/** Remote Streamable HTTP entry with the key in a header (never in the URL). */
|
|
16
|
+
export function remoteServerConfig(baseUrl) {
|
|
17
|
+
return { type: "http", url: mcpUrl(baseUrl), headers: { Authorization: `Bearer ${KEY_PLACEHOLDER}` } };
|
|
18
|
+
}
|
|
19
|
+
function json(value) {
|
|
20
|
+
return JSON.stringify(value, null, 2);
|
|
21
|
+
}
|
|
22
|
+
const KEY_NOTE = `Set CAR_IMAGE_API_KEY in the environment that launches the agent (or replace ${KEY_PLACEHOLDER} with the key in a file that is not committed). Never paste a key into a URL, prompt or repository.`;
|
|
23
|
+
function servers(baseUrl, remoteOnly) {
|
|
24
|
+
return remoteOnly
|
|
25
|
+
? { "car-image": remoteServerConfig(baseUrl) }
|
|
26
|
+
: { "car-image": remoteServerConfig(baseUrl), "car-image-local": stdioServerConfig(baseUrl) };
|
|
27
|
+
}
|
|
28
|
+
export function renderAgentConfig(options) {
|
|
29
|
+
const { host, remoteOnly, baseUrl } = options;
|
|
30
|
+
const url = mcpUrl(baseUrl);
|
|
31
|
+
const envArg = baseUrl.replace(/\/+$/, "") === DEFAULT_BASE_URL ? "" : ` --env CAR_IMAGE_API_URL=${baseUrl}`;
|
|
32
|
+
switch (host) {
|
|
33
|
+
case "claude-code": {
|
|
34
|
+
const lines = [
|
|
35
|
+
"# Claude Code",
|
|
36
|
+
"",
|
|
37
|
+
"# Remote MCP server (recommended; nothing to install):",
|
|
38
|
+
`claude mcp add --transport http car-image ${url} --header "Authorization: Bearer ${KEY_PLACEHOLDER}"`,
|
|
39
|
+
];
|
|
40
|
+
if (!remoteOnly) {
|
|
41
|
+
lines.push("", "# Local stdio server (runs `car-image mcp` through npx):", `claude mcp add car-image-local --env CAR_IMAGE_API_KEY=${KEY_PLACEHOLDER}${envArg} -- npx -y ${PACKAGE_NAME} mcp`);
|
|
42
|
+
}
|
|
43
|
+
lines.push("", "# Project-scoped alternative: .mcp.json in the repository root", json({ mcpServers: servers(baseUrl, remoteOnly) }), "", `# ${KEY_NOTE}`);
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
case "claude-desktop": {
|
|
47
|
+
const lines = [
|
|
48
|
+
"# Claude Desktop",
|
|
49
|
+
"",
|
|
50
|
+
"# Remote connector: Settings → Connectors → Add custom connector →",
|
|
51
|
+
`# URL: ${url}`,
|
|
52
|
+
"# Claude Desktop's connector dialog cannot send a static API-key header; use the local stdio server",
|
|
53
|
+
"# below (or Claude Code with --header) for key-based access.",
|
|
54
|
+
];
|
|
55
|
+
if (!remoteOnly) {
|
|
56
|
+
lines.push("", "# Local stdio server: add to claude_desktop_config.json", "# macOS: ~/Library/Application Support/Claude/claude_desktop_config.json", "# Windows: %APPDATA%\\Claude\\claude_desktop_config.json", json({ mcpServers: { "car-image": stdioServerConfig(baseUrl) } }));
|
|
57
|
+
}
|
|
58
|
+
lines.push("", `# ${KEY_NOTE}`);
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
}
|
|
61
|
+
case "cursor": {
|
|
62
|
+
return [
|
|
63
|
+
"# Cursor — save as .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)",
|
|
64
|
+
json({ mcpServers: servers(baseUrl, remoteOnly) }),
|
|
65
|
+
"",
|
|
66
|
+
`# ${KEY_NOTE}`,
|
|
67
|
+
].join("\n");
|
|
68
|
+
}
|
|
69
|
+
case "chatgpt": {
|
|
70
|
+
return [
|
|
71
|
+
"# ChatGPT",
|
|
72
|
+
"",
|
|
73
|
+
"# Apps SDK / connector (developer mode): Settings → Connectors → Create →",
|
|
74
|
+
`# MCP server URL: ${url}`,
|
|
75
|
+
"# Authentication: none is supported without a header; ask us for OAuth if you need ChatGPT connector access.",
|
|
76
|
+
"",
|
|
77
|
+
"# Custom GPT with Actions (works today):",
|
|
78
|
+
"# 1. Create a GPT → Configure → Create new action → Import from URL",
|
|
79
|
+
`# ${baseUrl}/openapi.json`,
|
|
80
|
+
"# 2. Authentication: API Key → Auth type: Bearer → paste your cimg_… key",
|
|
81
|
+
"# 3. Privacy policy URL: " + `${baseUrl}/privacy`,
|
|
82
|
+
"# Legacy manifest for reference: " + `${baseUrl}/.well-known/ai-plugin.json`,
|
|
83
|
+
"",
|
|
84
|
+
`# ${KEY_NOTE}`,
|
|
85
|
+
].join("\n");
|
|
86
|
+
}
|
|
87
|
+
default: {
|
|
88
|
+
return [
|
|
89
|
+
"# Generic MCP client configuration",
|
|
90
|
+
"",
|
|
91
|
+
"# Remote Streamable HTTP (MCP 2025-06-18 and later):",
|
|
92
|
+
json(remoteServerConfig(baseUrl)),
|
|
93
|
+
]
|
|
94
|
+
.concat(remoteOnly ? [] : ["", "# Local stdio:", json(stdioServerConfig(baseUrl))])
|
|
95
|
+
.concat([
|
|
96
|
+
"",
|
|
97
|
+
"# Tools: get_car_image, create_car_image_urls, search_vehicles, resolve_vehicle, list_image_options, get_account, rate_image, describe_api",
|
|
98
|
+
`# ${KEY_NOTE}`,
|
|
99
|
+
])
|
|
100
|
+
.join("\n");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=agent-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-config.js","sourceRoot":"","sources":["../src/agent-config.ts"],"names":[],"mappings":"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"}
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { CliError } from "./errors.js";
|
|
2
|
+
/** Flags that never take a value. `--no-<flag>` sets them to false. */
|
|
3
|
+
export const BOOLEAN_FLAGS = new Set([
|
|
4
|
+
"json",
|
|
5
|
+
"quiet",
|
|
6
|
+
"help",
|
|
7
|
+
"version",
|
|
8
|
+
"yes",
|
|
9
|
+
"portal",
|
|
10
|
+
"good",
|
|
11
|
+
"bad",
|
|
12
|
+
"url",
|
|
13
|
+
"check",
|
|
14
|
+
"remote",
|
|
15
|
+
"browser",
|
|
16
|
+
"image",
|
|
17
|
+
"color-output",
|
|
18
|
+
"force",
|
|
19
|
+
]);
|
|
20
|
+
const SHORT_FLAGS = {
|
|
21
|
+
h: "help",
|
|
22
|
+
v: "version",
|
|
23
|
+
q: "quiet",
|
|
24
|
+
y: "yes",
|
|
25
|
+
o: "out",
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Hand-rolled argv parser: `--flag`, `--flag value`, `--flag=value`,
|
|
29
|
+
* `--no-flag`, `-h`, `-v`, and `--` to end flag parsing. Boolean flags never
|
|
30
|
+
* consume the following token, so `car-image get --json --make Porsche` works.
|
|
31
|
+
*/
|
|
32
|
+
export function parseArgs(argv, booleanFlags = BOOLEAN_FLAGS) {
|
|
33
|
+
const flags = {};
|
|
34
|
+
const positionals = [];
|
|
35
|
+
let onlyPositionals = false;
|
|
36
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
37
|
+
const token = argv[index] ?? "";
|
|
38
|
+
if (onlyPositionals || token === "-" || !token.startsWith("-")) {
|
|
39
|
+
positionals.push(token);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (token === "--") {
|
|
43
|
+
onlyPositionals = true;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (token.startsWith("--")) {
|
|
47
|
+
const body = token.slice(2);
|
|
48
|
+
const equals = body.indexOf("=");
|
|
49
|
+
if (equals >= 0) {
|
|
50
|
+
flags[body.slice(0, equals)] = body.slice(equals + 1);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (body.startsWith("no-") && body.length > 3) {
|
|
54
|
+
flags[body.slice(3)] = false;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (booleanFlags.has(body)) {
|
|
58
|
+
flags[body] = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const next = argv[index + 1];
|
|
62
|
+
if (next !== undefined && (!next.startsWith("-") || /^-\d/.test(next))) {
|
|
63
|
+
flags[body] = next;
|
|
64
|
+
index += 1;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
flags[body] = true;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Short flags: -h, -v, -q, -y, -o file
|
|
72
|
+
const letters = token.slice(1);
|
|
73
|
+
for (let position = 0; position < letters.length; position += 1) {
|
|
74
|
+
const letter = letters[position] ?? "";
|
|
75
|
+
const name = SHORT_FLAGS[letter];
|
|
76
|
+
if (!name)
|
|
77
|
+
throw new CliError(`Unknown option -${letter}`, 2, "Run `car-image --help` for usage.");
|
|
78
|
+
if (booleanFlags.has(name)) {
|
|
79
|
+
flags[name] = true;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const rest = letters.slice(position + 1);
|
|
83
|
+
const next = rest || argv[index + 1];
|
|
84
|
+
if (next === undefined)
|
|
85
|
+
throw new CliError(`Option -${letter} requires a value`, 2);
|
|
86
|
+
flags[name] = next;
|
|
87
|
+
if (!rest)
|
|
88
|
+
index += 1;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const [command = null, ...rest] = positionals;
|
|
94
|
+
return { command, positionals: rest, flags };
|
|
95
|
+
}
|
|
96
|
+
export function flagString(flags, name) {
|
|
97
|
+
const value = flags[name];
|
|
98
|
+
if (value === undefined || value === false)
|
|
99
|
+
return undefined;
|
|
100
|
+
if (value === true)
|
|
101
|
+
throw new CliError(`Option --${name} requires a value`, 2);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
export function requireString(flags, name, hint) {
|
|
105
|
+
const value = flagString(flags, name);
|
|
106
|
+
if (value === undefined || value === "") {
|
|
107
|
+
throw new CliError(`Missing required option --${name}`, 2, hint ?? "Run `car-image --help` for usage.");
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
export function flagNumber(flags, name) {
|
|
112
|
+
const raw = flagString(flags, name);
|
|
113
|
+
if (raw === undefined)
|
|
114
|
+
return undefined;
|
|
115
|
+
const value = Number(raw);
|
|
116
|
+
if (!Number.isFinite(value))
|
|
117
|
+
throw new CliError(`Option --${name} must be a number (received "${raw}")`, 2);
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
export function flagInteger(flags, name, bounds) {
|
|
121
|
+
const value = flagNumber(flags, name);
|
|
122
|
+
if (value === undefined)
|
|
123
|
+
return undefined;
|
|
124
|
+
if (!Number.isInteger(value))
|
|
125
|
+
throw new CliError(`Option --${name} must be an integer`, 2);
|
|
126
|
+
if (bounds?.min !== undefined && value < bounds.min)
|
|
127
|
+
throw new CliError(`Option --${name} must be at least ${bounds.min}`, 2);
|
|
128
|
+
if (bounds?.max !== undefined && value > bounds.max)
|
|
129
|
+
throw new CliError(`Option --${name} must be at most ${bounds.max}`, 2);
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
export function flagBoolean(flags, name) {
|
|
133
|
+
return flags[name] === true;
|
|
134
|
+
}
|
|
135
|
+
export function flagChoice(flags, name, choices) {
|
|
136
|
+
const value = flagString(flags, name);
|
|
137
|
+
if (value === undefined)
|
|
138
|
+
return undefined;
|
|
139
|
+
const normalized = value.trim().toLowerCase();
|
|
140
|
+
if (!choices.includes(normalized)) {
|
|
141
|
+
throw new CliError(`Invalid --${name} "${value}". Choose one of: ${choices.join(", ")}`, 2);
|
|
142
|
+
}
|
|
143
|
+
return normalized;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { main } from "./main.js";
|
|
3
|
+
main(process.argv.slice(2)).then((code) => {
|
|
4
|
+
process.exitCode = code;
|
|
5
|
+
}, (error) => {
|
|
6
|
+
process.stderr.write(`car-image: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9G,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/** Opens a URL in the default browser without blocking; returns false when no opener exists. */
|
|
3
|
+
export function openBrowser(url, platform = process.platform) {
|
|
4
|
+
if (!/^https?:\/\//i.test(url))
|
|
5
|
+
return false;
|
|
6
|
+
const executable = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
7
|
+
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
8
|
+
try {
|
|
9
|
+
const child = spawn(executable, args, { detached: true, stdio: "ignore" });
|
|
10
|
+
child.on("error", () => {
|
|
11
|
+
// The opener is missing (headless server); the caller already printed the URL.
|
|
12
|
+
});
|
|
13
|
+
child.unref();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,gGAAgG;AAChG,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,WAA4B,OAAO,CAAC,QAAQ;IACnF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,UAAU,GAAG,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;IAC9F,MAAM,IAAI,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,+EAA+E;QACjF,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { CarImageClient, DEFAULT_BASE_URL } from "@meterapp/car-image-sdk";
|
|
2
|
+
import { flagString } from "./args.js";
|
|
3
|
+
import { CLI_VERSION } from "./version.js";
|
|
4
|
+
/** `car-image-cli/1.0.0 (darwin; node/v22.1.0)`; the MCP server identifies as `car-image-mcp`. */
|
|
5
|
+
export function userAgent(kind = "cli") {
|
|
6
|
+
return `car-image-${kind}/${CLI_VERSION} (${process.platform}; node/${process.version})`;
|
|
7
|
+
}
|
|
8
|
+
/** `--base-url` > `CAR_IMAGE_API_URL` > config > default. */
|
|
9
|
+
export function resolveBaseUrl(flags, env, config) {
|
|
10
|
+
const raw = flagString(flags, "base-url") ?? env.CAR_IMAGE_API_URL?.trim() ?? config.baseUrl ?? DEFAULT_BASE_URL;
|
|
11
|
+
return raw.replace(/\/+$/, "");
|
|
12
|
+
}
|
|
13
|
+
/** `--api-key` > `CAR_IMAGE_API_KEY` > config file. */
|
|
14
|
+
export function resolveApiKey(flags, env, config) {
|
|
15
|
+
const fromFlag = flagString(flags, "api-key")?.trim();
|
|
16
|
+
if (fromFlag)
|
|
17
|
+
return { apiKey: fromFlag, source: "flag" };
|
|
18
|
+
const fromEnv = env.CAR_IMAGE_API_KEY?.trim();
|
|
19
|
+
if (fromEnv)
|
|
20
|
+
return { apiKey: fromEnv, source: "env" };
|
|
21
|
+
if (config.apiKey)
|
|
22
|
+
return { apiKey: config.apiKey, source: "config" };
|
|
23
|
+
return { apiKey: undefined, source: null };
|
|
24
|
+
}
|
|
25
|
+
export function createClientContext(flags, env, config, options = {}) {
|
|
26
|
+
const baseUrl = resolveBaseUrl(flags, env, config);
|
|
27
|
+
const { apiKey, source } = resolveApiKey(flags, env, config);
|
|
28
|
+
const agent = userAgent(options.kind ?? "cli");
|
|
29
|
+
const client = new CarImageClient({
|
|
30
|
+
apiKey: apiKey ?? "",
|
|
31
|
+
baseUrl,
|
|
32
|
+
userAgent: agent,
|
|
33
|
+
fetch: options.fetch,
|
|
34
|
+
maxRetries: options.maxRetries,
|
|
35
|
+
});
|
|
36
|
+
return { baseUrl, apiKey, keySource: source, client, userAgent: agent };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAkB,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAY3C,kGAAkG;AAClG,MAAM,UAAU,SAAS,CAAC,OAAsB,KAAK;IACnD,OAAO,aAAa,IAAI,IAAI,WAAW,KAAK,OAAO,CAAC,QAAQ,UAAU,OAAO,CAAC,OAAO,GAAG,CAAC;AAC3F,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,KAAgC,EAAE,GAAQ,EAAE,MAAiB;IAC1F,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACjH,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,aAAa,CAAC,KAAgC,EAAE,GAAQ,EAAE,MAAiB;IACzF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,QAAQ;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1D,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,OAAO;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACvD,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACtE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAAgC,EAChC,GAAQ,EACR,MAAiB,EACjB,UAA4E,EAAE;IAE9E,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;QAChC,MAAM,EAAE,MAAM,IAAI,EAAE;QACpB,OAAO;QACP,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AGENT_HOSTS, remoteServerConfig, renderAgentConfig, stdioServerConfig } from "../agent-config.js";
|
|
2
|
+
import { flagChoice } from "../args.js";
|
|
3
|
+
import { createClientContext } from "../client.js";
|
|
4
|
+
import { serveMcpStdio } from "../mcp/server.js";
|
|
5
|
+
import { printJson } from "../output.js";
|
|
6
|
+
export const agentConfigCommand = {
|
|
7
|
+
name: "agent-config",
|
|
8
|
+
summary: "Print ready-to-paste MCP configuration for Claude Code, Claude Desktop, Cursor, ChatGPT or any client",
|
|
9
|
+
usage: "car-image agent-config [--host claude-code|claude-desktop|cursor|chatgpt|generic] [--remote] [--json]",
|
|
10
|
+
async run(ctx) {
|
|
11
|
+
const host = flagChoice(ctx.args.flags, "host", AGENT_HOSTS) ?? "generic";
|
|
12
|
+
const remoteOnly = ctx.args.flags.remote === true;
|
|
13
|
+
const baseUrl = ctx.clientContext.baseUrl;
|
|
14
|
+
if (ctx.io.json) {
|
|
15
|
+
printJson(ctx.io, {
|
|
16
|
+
mcpServers: remoteOnly ? { "car-image": remoteServerConfig(baseUrl) } : { "car-image": remoteServerConfig(baseUrl), "car-image-local": stdioServerConfig(baseUrl) },
|
|
17
|
+
});
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
ctx.io.stdout(`${renderAgentConfig({ host, remoteOnly, baseUrl })}\n`);
|
|
21
|
+
return 0;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export const mcpCommand = {
|
|
25
|
+
name: "mcp",
|
|
26
|
+
summary: "Run the stdio MCP server (tools: get_car_image, create_car_image_urls, search_vehicles, …)",
|
|
27
|
+
usage: "car-image mcp\n Reads CAR_IMAGE_API_KEY or the stored key. Configure with `car-image agent-config`.",
|
|
28
|
+
async run(ctx) {
|
|
29
|
+
// A dedicated client so the REST API can attribute traffic to the MCP server.
|
|
30
|
+
const mcpContext = createClientContext(ctx.args.flags, ctx.env, ctx.config, { kind: "mcp" });
|
|
31
|
+
if (!mcpContext.apiKey) {
|
|
32
|
+
ctx.io.stderr("car-image mcp: no API key configured; tools will return authentication errors until CAR_IMAGE_API_KEY is set or `car-image login` has run.\n");
|
|
33
|
+
}
|
|
34
|
+
await serveMcpStdio(mcpContext.client, (line) => ctx.io.stderr(line));
|
|
35
|
+
return 0;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAkB,MAAM,oBAAoB,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,uGAAuG;IAChH,KAAK,EAAE,uGAAuG;IAC9G,KAAK,CAAC,GAAG,CAAC,GAAmB;QAC3B,MAAM,IAAI,GAAc,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,SAAS,CAAC;QACrF,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;QAClD,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC;QAC1C,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;YAChB,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE;gBAChB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE;aACpK,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;QACX,CAAC;QACD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QACvE,OAAO,CAAC,CAAC;IACX,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAsB;IAC3C,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,4FAA4F;IACrG,KAAK,EAAE,sGAAsG;IAC7G,KAAK,CAAC,GAAG,CAAC,GAAmB;QAC3B,8EAA8E;QAC9E,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7F,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACvB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,8IAA8I,CAAC,CAAC;QAChK,CAAC;QACD,MAAM,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,CAAC;IACX,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { flagInteger } from "../args.js";
|
|
2
|
+
import { openBrowser } from "../browser.js";
|
|
3
|
+
import { CliError } from "../errors.js";
|
|
4
|
+
import { paint, printJson } from "../output.js";
|
|
5
|
+
export const CREDIT_PACKS = [5000, 10000, 25000, 100000];
|
|
6
|
+
export const billingCommand = {
|
|
7
|
+
name: "billing",
|
|
8
|
+
summary: "Open hosted Stripe Checkout for a credit pack, or the billing portal (--portal)",
|
|
9
|
+
usage: "car-image billing [--credits 5000|10000|25000|100000] [--portal] [--no-browser] [--json]",
|
|
10
|
+
async run(ctx) {
|
|
11
|
+
const { io, clientContext } = ctx;
|
|
12
|
+
if (!clientContext.apiKey)
|
|
13
|
+
throw new CliError("Not signed in.", 1, "Run `car-image login` first (CLI keys include the billing:write scope).");
|
|
14
|
+
const openInBrowser = ctx.args.flags.browser !== false;
|
|
15
|
+
if (ctx.args.flags.portal === true) {
|
|
16
|
+
const portal = await clientContext.client.billingPortal();
|
|
17
|
+
if (io.json) {
|
|
18
|
+
printJson(io, portal);
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
io.stdout(`Stripe billing portal (invoices, cards, auto-reload):\n ${paint(io, "underline", portal.data.url)}\n`);
|
|
22
|
+
if (openInBrowser)
|
|
23
|
+
openBrowser(portal.data.url);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
const credits = flagInteger(ctx.args.flags, "credits") ?? 5000;
|
|
27
|
+
if (!CREDIT_PACKS.includes(credits)) {
|
|
28
|
+
throw new CliError(`Unknown pack ${credits}. Choose one of: ${CREDIT_PACKS.join(", ")} credits ($${CREDIT_PACKS.map((pack) => pack / 1000).join(", $")})`, 2);
|
|
29
|
+
}
|
|
30
|
+
const checkout = await clientContext.client.createCheckout(credits);
|
|
31
|
+
if (io.json) {
|
|
32
|
+
printJson(io, checkout);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
io.stdout(`Stripe Checkout for ${credits.toLocaleString("en-US")} credits ($${(checkout.data.amount_cents / 100).toFixed(2)}):\n ${paint(io, "underline", checkout.data.url)}\n\nComplete the purchase in your browser; the CLI never sees card details. Credits appear within seconds of payment.\n`);
|
|
36
|
+
if (openInBrowser)
|
|
37
|
+
openBrowser(checkout.data.url);
|
|
38
|
+
return 0;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAU,CAAC;AAElE,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,iFAAiF;IAC1F,KAAK,EAAE,0FAA0F;IACjG,KAAK,CAAC,GAAG,CAAC,GAAmB;QAC3B,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC;QAClC,IAAI,CAAC,aAAa,CAAC,MAAM;YAAE,MAAM,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC,EAAE,yEAAyE,CAAC,CAAC;QAC9I,MAAM,aAAa,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC;QAEvD,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1D,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;gBACZ,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBACtB,OAAO,CAAC,CAAC;YACX,CAAC;YACD,EAAE,CAAC,MAAM,CAAC,4DAA4D,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnH,IAAI,aAAa;gBAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChD,OAAO,CAAC,CAAC;QACX,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC;QAC/D,IAAI,CAAE,YAAkC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,QAAQ,CAAC,gBAAgB,OAAO,oBAAoB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAChK,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACZ,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YACxB,OAAO,CAAC,CAAC;QACX,CAAC;QACD,EAAE,CAAC,MAAM,CACP,uBAAuB,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,yHAAyH,CAC7R,CAAC;QACF,IAAI,aAAa;YAAE,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;CACF,CAAC"}
|