@myapihq/cli 1.3.13 → 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.
- package/dist/commands/account.d.ts +7 -1
- package/dist/commands/account.js +356 -18
- package/dist/commands/authproduct.d.ts +7 -0
- package/dist/commands/authproduct.js +286 -0
- package/dist/commands/billing.js +1 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/container.d.ts +1 -0
- package/dist/commands/container.js +115 -11
- package/dist/commands/domain.js +2 -2
- package/dist/commands/email/index.js +0 -13
- package/dist/commands/fn.d.ts +1 -0
- package/dist/commands/fn.js +64 -12
- package/dist/commands/keys.js +3 -3
- package/dist/commands/queue.d.ts +1 -0
- package/dist/commands/queue.js +10 -0
- package/dist/commands/setup.js +8 -8
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.js +11 -27
- package/dist/commands/storage.js +3 -1
- package/dist/commands/workflow.js +21 -6
- package/dist/completion.js +5 -4
- package/dist/config.js +1 -1
- package/dist/exposes.test.js +1 -2
- package/dist/index.js +31 -28
- package/dist/registrant.js +5 -5
- package/dist/sdk-queue.test.js +3 -2
- package/dist/skills/my-api-hq/SKILL.md +15 -12
- package/dist/skills/my-auth-api/README.md +33 -0
- package/dist/skills/my-auth-api/SKILL.md +112 -0
- package/dist/skills/my-auth-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-auth-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-crm-api/SKILL.md +1 -1
- package/dist/skills/my-domain-api/SKILL.md +4 -4
- package/dist/skills/my-email-api/README.md +1 -1
- package/dist/skills/my-email-api/SKILL.md +7 -18
- package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +1 -1
- package/dist/skills/my-email-verify-api/SKILL.md +1 -1
- package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +1 -1
- package/dist/skills/my-git-api/README.md +43 -0
- package/dist/skills/my-git-api/SKILL.md +115 -0
- package/dist/skills/my-git-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-git-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +1 -1
- package/package.json +2 -2
- package/dist/commands/auth.d.ts +0 -11
- package/dist/commands/auth.js +0 -345
- package/dist/commands/email/campaign.d.ts +0 -4
- 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.
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-llm-api",
|
|
3
|
-
"description": "
|
|
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": "
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"description": "MyAPI command-line interface",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@myapihq/sdk": "^
|
|
34
|
+
"@myapihq/sdk": "^2.0.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.6.0",
|
package/dist/commands/auth.d.ts
DELETED
|
@@ -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>;
|
package/dist/commands/auth.js
DELETED
|
@@ -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
|
-
}
|