@myapihq/cli 1.3.12 → 2.0.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 (48) hide show
  1. package/dist/commands/account.d.ts +7 -1
  2. package/dist/commands/account.js +356 -18
  3. package/dist/commands/authproduct.d.ts +7 -0
  4. package/dist/commands/authproduct.js +286 -0
  5. package/dist/commands/billing.js +1 -1
  6. package/dist/commands/config.js +1 -1
  7. package/dist/commands/container.d.ts +1 -0
  8. package/dist/commands/container.js +115 -11
  9. package/dist/commands/domain.js +2 -2
  10. package/dist/commands/email/index.js +0 -13
  11. package/dist/commands/fn.d.ts +1 -0
  12. package/dist/commands/fn.js +64 -12
  13. package/dist/commands/keys.js +3 -3
  14. package/dist/commands/queue.d.ts +1 -0
  15. package/dist/commands/queue.js +10 -0
  16. package/dist/commands/setup.js +8 -8
  17. package/dist/commands/status.d.ts +1 -1
  18. package/dist/commands/status.js +11 -27
  19. package/dist/commands/storage.js +3 -1
  20. package/dist/commands/workflow.js +21 -6
  21. package/dist/completion.js +5 -4
  22. package/dist/config.js +1 -1
  23. package/dist/exposes.test.js +1 -2
  24. package/dist/index.js +31 -28
  25. package/dist/registrant.js +5 -5
  26. package/dist/sdk-queue.test.js +3 -2
  27. package/dist/skills/my-api-hq/SKILL.md +15 -12
  28. package/dist/skills/my-auth-api/README.md +33 -0
  29. package/dist/skills/my-auth-api/SKILL.md +112 -0
  30. package/dist/skills/my-auth-api/claude/.claude-plugin/plugin.json +6 -0
  31. package/dist/skills/my-auth-api/openapi/.gitkeep +0 -0
  32. package/dist/skills/my-crm-api/SKILL.md +1 -1
  33. package/dist/skills/my-domain-api/SKILL.md +4 -4
  34. package/dist/skills/my-email-api/README.md +1 -1
  35. package/dist/skills/my-email-api/SKILL.md +7 -18
  36. package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +1 -1
  37. package/dist/skills/my-email-verify-api/SKILL.md +1 -1
  38. package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +1 -1
  39. package/dist/skills/my-git-api/README.md +43 -0
  40. package/dist/skills/my-git-api/SKILL.md +115 -0
  41. package/dist/skills/my-git-api/claude/.claude-plugin/plugin.json +6 -0
  42. package/dist/skills/my-git-api/openapi/.gitkeep +0 -0
  43. package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +1 -1
  44. package/package.json +5 -4
  45. package/dist/commands/auth.d.ts +0 -11
  46. package/dist/commands/auth.js +0 -345
  47. package/dist/commands/email/campaign.d.ts +0 -4
  48. package/dist/commands/email/campaign.js +0 -200
@@ -0,0 +1,43 @@
1
+ ---
2
+ # my-git-api
3
+
4
+ Hosted git repositories over HTTP. Read history and commit atomically without a clone, or use real `git clone`/`push` over HTTPS with your API key as the password.
5
+
6
+ ## What it does
7
+
8
+ - Repo CRUD: `create` / `list` / `get` / `delete`
9
+ - Clone-free reads: `log` / `show` / `tree` / `blob` / `diff` / `refs`
10
+ - Atomic writes: `commit` (JSON file edits) / `create-branch` / `delete-branch` / `tag`
11
+ - Fast-forward `merge` and `repack`
12
+ - **Real git over HTTPS** — `git clone`/`fetch`/`push` with HTTP Basic auth
13
+
14
+ ## Quickstart
15
+
16
+ ```bash
17
+ # Create a repo and commit a file — no local clone
18
+ myapi git create my-app --default-branch main
19
+ myapi git commit my-app --branch main --message "init" \
20
+ --changes '[{"path":"README.md","content":"# My App"}]'
21
+
22
+ # Or use real git over HTTPS (API key as the password, any username)
23
+ git clone https://x:$MYAPI_KEY@git.mygitapi.com/<org-slug>/my-app.git
24
+ # <org-slug> = your org's preview subdomain (myapi org get)
25
+ git push origin main
26
+ ```
27
+
28
+ ## Authentication
29
+
30
+ ```bash
31
+ export MYAPI_KEY=mak_...
32
+ ```
33
+
34
+ Requires `api_key` and `org_id` from **myapihq**. For real-git clone/push, the
35
+ same key is the HTTP Basic password (clone/fetch needs git read, push needs git
36
+ write).
37
+
38
+ ## Documentation
39
+
40
+ Full command reference, the `--changes` commit format, optimistic-concurrency
41
+ (`--base`), and the smart-HTTP clone/push URL: see `SKILL.md`.
42
+
43
+ Run `myapi git --help` for inline reference.
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: my-git-api
3
+ version: 1.0.0
4
+ description: >
5
+ Hosted git repositories over HTTP. Create repos, read history (log/show/tree/blob/diff), and write atomically (commit/branch/tag/merge) — no clone needed. Real `git clone`/`push` also work over HTTPS with your API key as the password.
6
+ triggers: [git, repo, repository, clone, push, commit, branch, tag, merge, diff, version control, source control, vcs]
7
+ checksum: sha256-pending
8
+ ---
9
+
10
+ # MyGitAPI
11
+
12
+ Per-org hosted git repositories, served by a go-git engine (objects in GCS packfiles, refs in Postgres). Two ways in: the `myapi git` CLI for clone-free reads and atomic writes, **or** a real `git clone`/`push` over HTTPS with HTTP Basic auth.
13
+
14
+ ## Capabilities
15
+ <!-- llm:start -->
16
+ Git is the source-control layer. Each repo lives under your org and is reachable two ways:
17
+
18
+ 1. **CLI / SDK** — no local clone. `commit` applies a JSON array of file edits atomically; reads (`log`, `show`, `tree`, `blob`, `diff`, `refs`) round-trip the API. Perfect for an agent that edits files server-side.
19
+ 2. **Real git over HTTPS** — `git clone`/`fetch`/`push` against `https://git.mygitapi.com/<org-slug>/<repo>.git` with your MyAPI API key as the password. This is how a normal git workflow connects (see *Clone & push with real git*).
20
+
21
+ Both surfaces share one engine: a `git push` and a `myapi git commit` converge on the same refs.
22
+
23
+ `commit --base <tip>` is optimistic concurrency — pass the expected branch tip to guard a write, or `--base ""` to require the branch not exist. `merge` is **fast-forward only**. `repack` compacts incremental packfiles into one.
24
+
25
+ `blob` prints a file's content (decoded) — it's a `cat`. `diff` needs `--base` and `--head`.
26
+ <!-- llm:end -->
27
+
28
+ ## Commands
29
+ <!-- generated:start -->
30
+ | Command | What it does |
31
+ |---|---|
32
+ | `myapi git create <name>` | Create a repository (`--default-branch <b>`) |
33
+ | `myapi git list` | List repositories |
34
+ | `myapi git get <repo>` | Show branch/tag counts + default branch |
35
+ | `myapi git delete <repo>` | Delete a repository |
36
+ | `myapi git refs <repo>` | List HEAD, branches, and tags |
37
+ | `myapi git log <repo>` | List commits (`--ref`, `--limit`) |
38
+ | `myapi git show <repo> <sha>` | Show a single commit |
39
+ | `myapi git tree <repo> <ref>` | List a ref's file tree (`--path`) |
40
+ | `myapi git blob <repo> <ref> <path>` | Print a file's content (decoded) |
41
+ | `myapi git diff <repo>` | Unified diff of two refs (`--base`, `--head`) |
42
+ | `myapi git commit <repo>` | Atomic commit (`--branch`, `--message`, `--changes`) |
43
+ | `myapi git create-branch <repo> <name>` | Create a branch (`--from <ref>`) |
44
+ | `myapi git delete-branch <repo> <branch>` | Delete a branch |
45
+ | `myapi git tag <repo> <name>` | Create a tag (`--ref <ref>`) |
46
+ | `myapi git merge <repo>` | Fast-forward merge (`--target`, `--source`) |
47
+ | `myapi git repack <repo>` | Compact the repository's packfiles |
48
+ <!-- generated:end -->
49
+
50
+ ## Examples
51
+ <!-- llm:start -->
52
+ ```bash
53
+ # Create a repo and inspect it
54
+ myapi git create my-app --default-branch main
55
+ myapi git list
56
+ myapi git refs my-app
57
+
58
+ # Atomic commit — --changes is a JSON array of file edits
59
+ myapi git commit my-app --branch main --message "init" \
60
+ --changes '[{"path":"README.md","content":"# My App"}]'
61
+ # Each entry: {path, content | content_base64 | delete:true, mode?}
62
+ # mode: "100644" (default), "100755" (exec), "120000" (symlink)
63
+
64
+ # Guard against a stale write (optimistic concurrency)
65
+ myapi git commit my-app --branch main --message "fix" \
66
+ --base <expected-tip-sha> \
67
+ --changes '[{"path":"app.js","content":"..."}]'
68
+
69
+ # Read history & content (no clone)
70
+ myapi git log my-app --ref main --limit 20
71
+ myapi git show my-app <sha>
72
+ myapi git tree my-app main --path src
73
+ myapi git blob my-app main README.md # prints file content
74
+ myapi git diff my-app --base main --head feature
75
+
76
+ # Branches, tags, merge
77
+ myapi git create-branch my-app feature --from main
78
+ myapi git tag my-app v1.0.0 --ref main
79
+ myapi git merge my-app --target main --source feature # fast-forward only
80
+ myapi git delete-branch my-app feature
81
+ ```
82
+
83
+ ### Clone & push with real git
84
+ Hosted repos speak the real git smart-HTTP protocol. Clone, fetch, and push
85
+ over HTTPS with **HTTP Basic auth — your MyAPI API key is the password**, any
86
+ username works (git accepts the key in either field).
87
+
88
+ ```bash
89
+ # URL: https://git.mygitapi.com/<org-slug>/<repo>.git
90
+ # <org-slug> = your org's preview subdomain (myapi org get)
91
+ # <repo> = repository name (.git suffix optional)
92
+
93
+ # Clone (key as password; "x" is a throwaway username)
94
+ git clone https://x:$MYAPI_KEY@git.mygitapi.com/my-org-slug/my-app.git
95
+
96
+ # Or set the remote and let git prompt for the password (paste the key)
97
+ git remote add origin https://git.mygitapi.com/my-org-slug/my-app.git
98
+ git push origin main
99
+ ```
100
+
101
+ Capability rules: clone/fetch needs **git read**, push needs **git write**.
102
+ An org-locked key only reaches its own org. Unknown slug/repo → 404 (the
103
+ surface never leaks which repos exist).
104
+ <!-- llm:end -->
105
+
106
+ ## Notes
107
+
108
+ - **Clone-free is the default mode.** The CLI never needs a working copy — `commit` sends file edits as JSON, reads come back over the API. Use real git only when you want a local checkout.
109
+ - **`merge` is fast-forward only.** No merge commits; the target must be an ancestor of the source. Rebase/merge locally and push instead for non-FF cases.
110
+ - **`commit --base`** is the concurrency guard: pass the expected tip SHA to reject a stale write, or `--base ""` to require the branch be newly created. Omit it to create-or-update.
111
+ - **Metering.** A `git push` (and `myapi git commit`) is metered like a commit. Clone/fetch (`upload-pack`) is free.
112
+ - **Limits.** A push body is capped at 100 MiB; per-repo size limits apply (a push past the cap is reported as a receive-pack failure).
113
+ - **Auth field.** git may put the key in the username or password slot — both work. Embedding it in the URL (`https://x:$KEY@…`) avoids the interactive prompt but writes the key into `.git/config`; use a credential helper for anything persistent.
114
+
115
+ Run `myapi git --help` for the full flag reference.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "my-git-api",
3
+ "description": "Hosted git repositories over HTTP — create repos, commit files, manage branches and tags, read trees, blobs, history, and diffs. No local clone required.",
4
+ "version": "1.0.0",
5
+ "published": true
6
+ }
File without changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-llm-api",
3
- "description": "Provider-agnostic LLM completions + embeddings via MyAPI. Routes to managed models (Gemini today, more soon) at upstream cost. Use in workflow steps, not for your own reasoning.",
3
+ "description": "Self-hosted open-source LLM: raw chat completions (you pick the model) + objective verbs (classify/extract/summarize/draft). Billed in cents per 1M tokens from your balance. Use in workflow steps, not for your own reasoning.",
4
4
  "version": "1.0.0",
5
5
  "published": true
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
3
  "license": "Apache-2.0",
4
- "version": "1.3.12",
4
+ "version": "2.0.1",
5
5
  "description": "MyAPI command-line interface",
6
6
  "type": "module",
7
7
  "files": [
@@ -17,6 +17,7 @@
17
17
  "dev": "tsc --watch",
18
18
  "test": "vitest run src test/scripts",
19
19
  "test:smoke": "npm run build && vitest run src test/smoke test/scripts",
20
+ "test:smoke:ci": "vitest run src test/smoke test/scripts",
20
21
  "test:online": "npm run build && MYAPI_RUN_RESET=1 vitest run test/online",
21
22
  "test:realistic": "npm run build && MYAPI_RUN_REALISTIC=1 vitest run test/realistic/autonomous-company.test.ts",
22
23
  "test:realistic:cleanup": "npm run build && vitest run test/realistic/cleanup.test.ts",
@@ -26,11 +27,11 @@
26
27
  "update-schema": "node scripts/update-schema.js",
27
28
  "lint:changelog": "node ../../scripts/lint-changelog.js",
28
29
  "lint:help-order": "node scripts/lint-help-order.js",
29
- "lint:skills": "node scripts/lint-skills.js",
30
- "lint:skills:strict": "node scripts/lint-skills.js --strict"
30
+ "lint:skills": "node scripts/copy-skills.js && node scripts/lint-skills.js",
31
+ "lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
31
32
  },
32
33
  "dependencies": {
33
- "@myapihq/sdk": "^1.3.12"
34
+ "@myapihq/sdk": "^2.0.1"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^25.6.0",
@@ -1,11 +0,0 @@
1
- import type { FlagSchema } from '../flags.js';
2
- import type { Flags } from '../helpers.js';
3
- import type { Exposes } from '../exposes.js';
4
- export declare const EXPOSES: Exposes;
5
- export declare const SCHEMA: FlagSchema;
6
- export declare const HELP = "Usage: myapi auth <subcommand>\n\nSubcommands:\n api-keys Manage API keys \u00B7 list / create / revoke\n config Manage CLI defaults (org, funnel, domain) \u00B7 supports set-org / set-funnel / set-domain\n import-key Import an existing API key non-interactively\n install-skills DEPRECATED \u2014 use `myapi install-skills` instead\n keys Alias for api-keys\n link [email] Upgrade anonymous account to registered (or add a second session)\n Use myapi auth setup to create a completely new account\n registrant Manage stored WHOIS contact info for domain registration \u00B7 set / get / clear\n setup Configure your account\n switch [index] Switch active account by index or email\n whoami Show current account \u00B7 supports --json";
7
- export declare const INSTALL_SKILLS_HELP = "Usage: myapi install-skills\n\nInstalls the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).\n\nThis command writes skill definition files to:\n ~/.agents/skills/myapi/\n\nAnd creates symlinks in the appropriate agent config directories:\n ~/.claude/ (Claude)\n ~/.gemini/ (Gemini)\n ~/.cursor/ (Cursor, if detected)\n\nThese files teach agents how to use the MyAPI CLI and API directly.\nRun this command again to update existing skills to the latest version.\n\nNote: `myapi auth install-skills` is deprecated and will be removed in the next minor.\nUse `myapi install-skills` going forward.";
8
- export declare function link(flags?: Flags, emailArg?: string): Promise<void>;
9
- export declare function whoami(flags?: Flags): Promise<void>;
10
- export declare function switchCmd(flags?: Flags, indexArg?: string): Promise<void>;
11
- export declare function registrant(sub: string | undefined, flags?: Flags): Promise<void>;
@@ -1,345 +0,0 @@
1
- import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts, requireConfig } from '../config.js';
2
- import { info, success, error, printJson } from '../output.js';
3
- import { ask, confirm, withReadline } from '../prompt.js';
4
- import { installSkills } from './setup.js';
5
- import { hq } from '@myapihq/sdk';
6
- import { promptAndSave as promptAndSaveRegistrant } from '../registrant.js';
7
- export const EXPOSES = [
8
- 'PATCH /hq/account/upgrade',
9
- 'POST /hq/account/send-code',
10
- 'POST /hq/account/verify-code',
11
- 'GET /hq/billing/balance',
12
- 'GET /hq/account/free-tier',
13
- ];
14
- export const SCHEMA = {
15
- 'install-skills': 'boolean',
16
- 'no-skills': 'boolean',
17
- anonymous: 'boolean',
18
- anon: 'boolean',
19
- };
20
- export const HELP = `Usage: myapi auth <subcommand>
21
-
22
- Subcommands:
23
- api-keys Manage API keys · list / create / revoke
24
- config Manage CLI defaults (org, funnel, domain) · supports set-org / set-funnel / set-domain
25
- import-key Import an existing API key non-interactively
26
- install-skills DEPRECATED — use \`myapi install-skills\` instead
27
- keys Alias for api-keys
28
- link [email] Upgrade anonymous account to registered (or add a second session)
29
- Use myapi auth setup to create a completely new account
30
- registrant Manage stored WHOIS contact info for domain registration · set / get / clear
31
- setup Configure your account
32
- switch [index] Switch active account by index or email
33
- whoami Show current account · supports --json`;
34
- export const INSTALL_SKILLS_HELP = `Usage: myapi install-skills
35
-
36
- Installs the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).
37
-
38
- This command writes skill definition files to:
39
- ~/.agents/skills/myapi/
40
-
41
- And creates symlinks in the appropriate agent config directories:
42
- ~/.claude/ (Claude)
43
- ~/.gemini/ (Gemini)
44
- ~/.cursor/ (Cursor, if detected)
45
-
46
- These files teach agents how to use the MyAPI CLI and API directly.
47
- Run this command again to update existing skills to the latest version.
48
-
49
- Note: \`myapi auth install-skills\` is deprecated and will be removed in the next minor.
50
- Use \`myapi install-skills\` going forward.`;
51
- const LINK_HELP = `Usage: myapi auth link [email]
52
-
53
- Behavior depends on whether the email is new or already registered:
54
-
55
- New email: Attaches it to your current anonymous account, upgrading it
56
- to a registered account. A one-time code is sent to confirm.
57
-
58
- Existing email: Does NOT merge accounts. The existing account is added as a
59
- second active session — your anonymous account stays separate.
60
- Switch between sessions with: myapi auth switch
61
-
62
- To create a completely new account from scratch, use: myapi auth setup`;
63
- const WHOAMI_HELP = `Usage: myapi auth whoami [--json]
64
-
65
- Displays the active account details: email, account ID, default org,
66
- default funnel, account type, and current balance.
67
-
68
- Flags:
69
- --json Output raw JSON`;
70
- const SWITCH_HELP = `Usage: myapi auth switch [index|email]
71
-
72
- Switches the active account. Run without arguments to see a numbered list
73
- of accounts and choose interactively.
74
-
75
- Pass an index number or email address to switch non-interactively:
76
- myapi auth switch 2
77
- myapi auth switch you@example.com
78
-
79
- Note: index numbers can shift as accounts are added or removed.
80
- Use email as a stable identifier in scripts.`;
81
- // Resolve the user's "install skills?" preference: explicit flag wins, else prompt.
82
- async function resolveSkillsPreference(flags) {
83
- if (flags['install-skills'])
84
- return true;
85
- if (flags['no-skills'])
86
- return false;
87
- return confirm('› Install the MyAPI skills pack? (Y/n) ', true);
88
- }
89
- // myapi auth link — upgrade anonymous session to registered account.
90
- export async function link(flags = {}, emailArg) {
91
- if (flags.help) {
92
- info(LINK_HELP);
93
- return;
94
- }
95
- const config = loadConfig();
96
- if (!config?.api_key)
97
- error('Not configured. Run: myapi auth setup');
98
- if (!config.is_anonymous)
99
- error('Already registered. Use your existing account.');
100
- await withReadline(async () => {
101
- const email = emailArg || await ask('› Email? ');
102
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
103
- error(`Invalid email address: "${email}".`);
104
- let upgradeOk = true;
105
- try {
106
- await hq.upgradeAccount(config.api_key, email);
107
- }
108
- catch (err) {
109
- if (err.message !== 'EMAIL_TAKEN')
110
- throw err;
111
- upgradeOk = false;
112
- info(`› That email already has an account — accounts cannot be merged.`);
113
- info(`› Your anonymous account will be kept and you can switch back to it anytime.`);
114
- if (!await confirm('› Sign in and add it as a second account? (Y/n) ', true))
115
- return;
116
- await hq.sendCode(email);
117
- }
118
- info(`› Sent a code to ${email} · paste it below`);
119
- const code = await ask('› Code? ');
120
- const data = await hq.verifyCode(email, code);
121
- const wantsSkills = await resolveSkillsPreference(flags);
122
- if (upgradeOk) {
123
- saveConfig({
124
- ...config,
125
- api_key: data.api_key,
126
- account_id: data.account_id,
127
- email,
128
- default_org: data.default_org || config.default_org,
129
- default_funnel: data.default_funnel || config.default_funnel,
130
- is_anonymous: false,
131
- skills_installed: wantsSkills,
132
- });
133
- success(`› Welcome! Account upgraded · ${email}`);
134
- }
135
- else {
136
- const idx = addAccount({
137
- api_key: data.api_key,
138
- account_id: data.account_id,
139
- email,
140
- default_org: data.default_org,
141
- default_funnel: data.default_funnel,
142
- is_anonymous: false,
143
- skills_installed: wantsSkills,
144
- });
145
- success(`› Signed in · ${email} (account #${idx + 1})`);
146
- info(`› Use "myapi auth switch" to toggle between accounts.`);
147
- }
148
- if (wantsSkills)
149
- await installSkills();
150
- });
151
- }
152
- // myapi auth whoami — show current session info.
153
- export async function whoami(flags = {}) {
154
- if (flags.help) {
155
- info(WHOAMI_HELP);
156
- return;
157
- }
158
- const config = loadConfig();
159
- if (!config?.api_key)
160
- error('Not configured. Run: myapi auth setup');
161
- let balance = null;
162
- // Backend returns an array of per-service entries, or null. The SDK type
163
- // used to lie about this (claimed scalar) — see hq.ts FreeTierEntry.
164
- let freeTier = null;
165
- // Note any auth-rejection from the balance probe so we don't silently
166
- // print stale cached config when the key has actually been invalidated
167
- // server-side. Other failures (endpoint missing, transient 500s) stay
168
- // swallowed — balance is best-effort, not a key-validation primitive.
169
- let keyRejected = false;
170
- try {
171
- balance = await hq.getBalance(config.api_key);
172
- }
173
- catch (err) {
174
- if (err?.status === 401 || /invalid api key/i.test(err?.code ?? ''))
175
- keyRejected = true;
176
- }
177
- // Free-tier query is best-effort — backend may not have it for every
178
- // account type, and surfacing nothing is better than failing whoami.
179
- try {
180
- freeTier = await hq.getFreeTier(config.api_key);
181
- }
182
- catch (err) {
183
- if (err?.status === 401 || /invalid api key/i.test(err?.code ?? ''))
184
- keyRejected = true;
185
- }
186
- if (flags.json) {
187
- printJson({
188
- email: config.email ?? null,
189
- account_id: config.account_id,
190
- default_org: config.default_org ?? null,
191
- default_funnel: config.default_funnel ?? null,
192
- is_anonymous: config.is_anonymous ?? false,
193
- balance: balance?.balance_display ?? null,
194
- credits: balance?.credits_display ?? null,
195
- free_tier: freeTier ?? null,
196
- key_rejected: keyRejected,
197
- });
198
- return;
199
- }
200
- if (keyRejected) {
201
- info('⚠ The backend rejected your API key. The values below are from local config and may not match server state.');
202
- info(' Run: myapi auth setup');
203
- info('');
204
- }
205
- if (config.email)
206
- info(`Email: ${config.email}`);
207
- info(`Account: ${config.account_id}`);
208
- info(`Org: ${config.default_org ?? '(none)'}`);
209
- info(`Funnel: ${config.default_funnel ?? '(none)'}`);
210
- info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
211
- info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display} (not usable for domains)` : '(unavailable)'}`);
212
- if (Array.isArray(freeTier) && freeTier.length > 0) {
213
- const parts = freeTier
214
- .filter((e) => e && typeof e.service === 'string')
215
- .map((e) => `${e.service} ${e.used ?? 0}/${e.allowance ?? '?'}`);
216
- if (parts.length > 0)
217
- info(`FreeTier: ${parts.join(' · ')}`);
218
- }
219
- }
220
- // myapi auth switch [index] — switch between saved accounts.
221
- export async function switchCmd(flags = {}, indexArg) {
222
- if (flags.help) {
223
- info(SWITCH_HELP);
224
- return;
225
- }
226
- const accounts = listAccounts();
227
- if (accounts.length === 0)
228
- error('No accounts configured. Run: myapi auth setup');
229
- if (accounts.length === 1) {
230
- info(`Only one account configured: ${accounts[0].email ?? accounts[0].account_id}`);
231
- info('To add another account, run: myapi auth setup');
232
- return;
233
- }
234
- info('Accounts:');
235
- for (const a of accounts) {
236
- const label = a.email ?? (a.is_anonymous ? `anonymous · ${a.account_id.slice(0, 8)}` : a.account_id.slice(0, 8));
237
- const marker = a.active ? ' ◀ active' : '';
238
- info(` ${a.index + 1}. ${label}${marker}`);
239
- }
240
- let idxStr = indexArg ?? '';
241
- if (!idxStr) {
242
- idxStr = await ask(`› Switch to account (1-${accounts.length})? `);
243
- }
244
- // Support email as a stable identifier
245
- let idx;
246
- const byEmail = accounts.find(a => a.email && a.email.toLowerCase() === idxStr.toLowerCase());
247
- if (byEmail) {
248
- idx = byEmail.index;
249
- }
250
- else if (idxStr.includes('@')) {
251
- error(`No account found with email "${idxStr}". Run "myapi auth switch" to list configured accounts.`);
252
- }
253
- else {
254
- idx = parseInt(idxStr, 10) - 1;
255
- if (isNaN(idx) || idx < 0 || idx >= accounts.length) {
256
- error(`Invalid selection "${idxStr}". Use an index number (1-${accounts.length}) or an email address.`);
257
- }
258
- }
259
- if (switchAccount(idx)) {
260
- const a = accounts[idx];
261
- success(`› Switched to ${a.email ?? a.account_id}`);
262
- }
263
- }
264
- // ── myapi auth registrant <set|get|clear> ─────────────────────────────────
265
- //
266
- // Manages the locally-stored WHOIS registrant info that `myapi domain
267
- // register` injects into every call. `set` is the one-time interactive
268
- // setup; `--registrant-json` is the agent-friendly bypass. `get` and
269
- // `clear` are housekeeping.
270
- const REGISTRANT_HELP = `Usage: myapi auth registrant <set|get|clear>
271
-
272
- Manages the locally-stored WHOIS contact info that \`myapi domain register\`
273
- requires (ICANN-mandated). Stored in ~/.myapi/config.json under the active
274
- account; never synced to the backend except as a per-request field.
275
-
276
- Subcommands:
277
- clear Remove the stored registrant from the active account.
278
- get Print the stored registrant (or "not set").
279
- set Interactive prompt for all fields. Or pass --registrant-json
280
- '<json>' to set non-interactively (agent-friendly).`;
281
- export async function registrant(sub, flags = {}) {
282
- if (flags.help || !sub) {
283
- info(REGISTRANT_HELP);
284
- return;
285
- }
286
- const config = requireConfig();
287
- switch (sub) {
288
- case 'set': {
289
- // Agent path: --registrant-json '<inline>' writes directly without prompting.
290
- const inlineJson = typeof flags['registrant-json'] === 'string' ? flags['registrant-json'] : null;
291
- if (inlineJson) {
292
- let parsed;
293
- try {
294
- parsed = JSON.parse(inlineJson);
295
- }
296
- catch (e) {
297
- error(`--registrant-json is not valid JSON: ${e.message}`);
298
- }
299
- const required = ['name', 'email', 'phone', 'street', 'city', 'postal_code', 'country_code'];
300
- const missing = required.filter(k => !parsed[k]);
301
- if (missing.length > 0)
302
- error(`Missing required field(s): ${missing.join(', ')}`);
303
- saveConfig({ ...config, registrant: parsed });
304
- success('Saved registrant from --registrant-json.');
305
- return;
306
- }
307
- const r = await promptAndSaveRegistrant(config, config.registrant);
308
- success(`Saved. ${r.name} · ${r.email} · ${r.country_code}`);
309
- return;
310
- }
311
- case 'get': {
312
- if (!config.registrant) {
313
- info('No registrant stored. Set one with: myapi auth registrant set');
314
- return;
315
- }
316
- if (flags.json) {
317
- printJson(config.registrant);
318
- return;
319
- }
320
- const r = config.registrant;
321
- info(`Name: ${r.name}`);
322
- info(`Email: ${r.email}`);
323
- info(`Phone: ${r.phone}`);
324
- info(`Street: ${r.street}`);
325
- info(`City: ${r.city}`);
326
- if (r.state)
327
- info(`State: ${r.state}`);
328
- info(`Postal code: ${r.postal_code}`);
329
- info(`Country: ${r.country_code}`);
330
- return;
331
- }
332
- case 'clear': {
333
- if (!config.registrant) {
334
- info('No registrant stored — nothing to clear.');
335
- return;
336
- }
337
- const { registrant: _drop, ...rest } = config;
338
- saveConfig(rest);
339
- success('Cleared.');
340
- return;
341
- }
342
- default:
343
- error(`Unknown subcommand: ${sub}. Run \`myapi auth registrant --help\` for the list.`);
344
- }
345
- }
@@ -1,4 +0,0 @@
1
- import { type Flags } from '../../helpers.js';
2
- import type { Exposes } from '../../exposes.js';
3
- export declare const EXPOSES: Exposes;
4
- export declare function run(sub: string | undefined, args: string[], flags: Flags): Promise<void>;