@myapihq/cli 2.9.0 → 2.11.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/dist/commands/container.js +35 -14
- package/dist/commands/flag-reachability.test.js +14 -4
- package/dist/commands/import-key.test.d.ts +1 -0
- package/dist/commands/import-key.test.js +69 -0
- package/dist/commands/org.js +5 -1
- package/dist/commands/setup.d.ts +5 -1
- package/dist/commands/setup.js +67 -6
- package/dist/commands/storage.js +151 -14
- package/dist/config.d.ts +2 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +36 -3
- package/dist/helpers.d.ts +20 -1
- package/dist/helpers.js +79 -1
- package/dist/index.js +6 -1
- package/dist/org-not-found.test.d.ts +1 -0
- package/dist/org-not-found.test.js +42 -0
- package/dist/org-notice.test.d.ts +1 -0
- package/dist/org-notice.test.js +37 -0
- package/dist/output.d.ts +6 -0
- package/dist/output.js +8 -1
- package/dist/skills/my-api-hq/SKILL.md +34 -14
- package/dist/skills/my-audience-api/SKILL.md +16 -1
- package/dist/skills/my-auth-api/SKILL.md +17 -2
- package/dist/skills/my-company-api/SKILL.md +16 -1
- package/dist/skills/my-container-api/SKILL.md +45 -24
- package/dist/skills/my-crm-api/SKILL.md +16 -1
- package/dist/skills/my-database-api/SKILL.md +13 -17
- package/dist/skills/my-domain-api/SKILL.md +16 -1
- package/dist/skills/my-email-api/SKILL.md +12 -16
- package/dist/skills/my-email-verify-api/SKILL.md +16 -1
- package/dist/skills/my-feedback-api/README.md +33 -0
- package/dist/skills/my-feedback-api/SKILL.md +118 -0
- package/dist/skills/my-feedback-api/claude/.claude-plugin/plugin.json +7 -0
- package/dist/skills/my-function-api/README.md +34 -0
- package/dist/skills/my-function-api/SKILL.md +16 -1
- package/dist/skills/my-funnel-api/SKILL.md +16 -1
- package/dist/skills/my-git-api/SKILL.md +17 -1
- package/dist/skills/my-image-api/SKILL.md +17 -1
- package/dist/skills/my-llm-api/SKILL.md +20 -6
- package/dist/skills/my-payments-api/README.md +33 -0
- package/dist/skills/my-payments-api/SKILL.md +16 -1
- package/dist/skills/my-people-api/SKILL.md +16 -1
- package/dist/skills/my-pixel-api/SKILL.md +26 -3
- package/dist/skills/my-queue-api/SKILL.md +35 -2
- package/dist/skills/my-storage-api/SKILL.md +37 -29
- package/dist/skills/my-task-api/SKILL.md +35 -2
- package/dist/skills/my-url-to/SKILL.md +16 -1
- package/dist/skills/my-webhook-api/SKILL.md +23 -3
- package/dist/skills/my-workflow-api/SKILL.md +32 -6
- package/package.json +2 -2
package/dist/helpers.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { error } from './output.js';
|
|
1
|
+
import { error, banner } from './output.js';
|
|
2
|
+
import { saveConfig } from './config.js';
|
|
2
3
|
import { confirm, isNonInteractive } from './prompt.js';
|
|
3
4
|
// error() returns `never`, so after `if (!x) error(...)` TS narrows x to a
|
|
4
5
|
// non-falsy value and the casts disappear.
|
|
@@ -13,8 +14,85 @@ export function requireOrg(flags, config, usage) {
|
|
|
13
14
|
if (!orgId) {
|
|
14
15
|
error(`Missing required arguments.\nUsage: ${usage}\n(Or set default: myapi config set-org <id>)`);
|
|
15
16
|
}
|
|
17
|
+
noticeOrgChanged(orgId, config);
|
|
18
|
+
recordResolvedOrg(orgId, config);
|
|
16
19
|
return orgId;
|
|
17
20
|
}
|
|
21
|
+
// ── Which org am I in? ───────────────────────────────────────────────────────
|
|
22
|
+
//
|
|
23
|
+
// The org is ambient, sticky and invisible: it comes from a saved default that
|
|
24
|
+
// survives across turns, tasks and days, and a stale one looks exactly like a
|
|
25
|
+
// correct one. Agents lose whole writes to this — a demo pushed onto a live
|
|
26
|
+
// site, a domain reassigned away from the org that was serving it.
|
|
27
|
+
//
|
|
28
|
+
// The fix is NOT to announce the org on every command. A line that is always
|
|
29
|
+
// there carries no information, gets filtered out within a day, and costs
|
|
30
|
+
// tokens on every call in an agent loop. It only carries signal when it could
|
|
31
|
+
// have been different — so it fires on CHANGE and is silent otherwise. Steady
|
|
32
|
+
// single-org work never sees it; the moment the target moves, it says so once.
|
|
33
|
+
//
|
|
34
|
+
// Goes to stderr, like the spinner, so `--json | jq` and piped output stay
|
|
35
|
+
// clean.
|
|
36
|
+
let resolvedOrgId;
|
|
37
|
+
let resolvedOrgName;
|
|
38
|
+
/** The notice for a move, or undefined when there is nothing to say.
|
|
39
|
+
*
|
|
40
|
+
* Pure and exported because both failure directions are silent in production:
|
|
41
|
+
* return a string always and it becomes noise everyone filters out; return
|
|
42
|
+
* undefined always and the wrong-org write it exists to catch goes through
|
|
43
|
+
* unannounced. Neither shows up as a broken build, so it is pinned by a test.
|
|
44
|
+
*/
|
|
45
|
+
export function orgChangeNotice(previous, next, names = {}) {
|
|
46
|
+
if (!previous || previous === next)
|
|
47
|
+
return undefined;
|
|
48
|
+
const label = (id) => (names[id] ? `${names[id]} ` : '') + short(id);
|
|
49
|
+
return `→ org changed: ${label(previous)} → ${label(next)}`;
|
|
50
|
+
}
|
|
51
|
+
function noticeOrgChanged(orgId, config) {
|
|
52
|
+
const msg = orgChangeNotice(config.last_org_used, orgId, config.org_names ?? {});
|
|
53
|
+
if (msg)
|
|
54
|
+
banner(msg);
|
|
55
|
+
}
|
|
56
|
+
function recordResolvedOrg(orgId, config) {
|
|
57
|
+
resolvedOrgId = orgId;
|
|
58
|
+
resolvedOrgName = config.org_names?.[orgId];
|
|
59
|
+
if (config.last_org_used === orgId)
|
|
60
|
+
return;
|
|
61
|
+
// Written only on change, so the common path does no disk I/O. Failure here
|
|
62
|
+
// must never break the command the user actually asked for — the worst case
|
|
63
|
+
// is that the next run re-notices the same change.
|
|
64
|
+
try {
|
|
65
|
+
config.last_org_used = orgId;
|
|
66
|
+
saveConfig(config);
|
|
67
|
+
}
|
|
68
|
+
catch { /* advisory only */ }
|
|
69
|
+
}
|
|
70
|
+
function short(id) {
|
|
71
|
+
return id.length > 12 ? `${id.slice(0, 8)}…` : id;
|
|
72
|
+
}
|
|
73
|
+
/** The org this invocation resolved to, for `--json` consumers. */
|
|
74
|
+
export function currentOrg() {
|
|
75
|
+
return resolvedOrgId ? { org_id: resolvedOrgId, org_name: resolvedOrgName } : undefined;
|
|
76
|
+
}
|
|
77
|
+
/** Cache id → name so later output can name an org instead of printing a UUID.
|
|
78
|
+
* Called by the commands that already hold the mapping (`org list`, `status`). */
|
|
79
|
+
export function rememberOrgNames(config, orgs) {
|
|
80
|
+
const names = { ...(config.org_names ?? {}) };
|
|
81
|
+
let changed = false;
|
|
82
|
+
for (const o of orgs) {
|
|
83
|
+
if (!o?.id || !o?.name || names[o.id] === o.name)
|
|
84
|
+
continue;
|
|
85
|
+
names[o.id] = o.name;
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
88
|
+
if (!changed)
|
|
89
|
+
return;
|
|
90
|
+
try {
|
|
91
|
+
config.org_names = names;
|
|
92
|
+
saveConfig(config);
|
|
93
|
+
}
|
|
94
|
+
catch { /* advisory only */ }
|
|
95
|
+
}
|
|
18
96
|
export function requireDomain(arg, flags, config, usage) {
|
|
19
97
|
const fromFlag = typeof flags.domain === 'string' ? flags.domain : '';
|
|
20
98
|
const fromConfig = typeof config.default_domain === 'string' ? config.default_domain : '';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { error, info, success, banner } from './output.js';
|
|
2
|
+
import { error, info, success, banner, setResolvedContextSource } from './output.js';
|
|
3
3
|
import { loadConfig } from './config.js';
|
|
4
4
|
import { MyApiError, setUserAgent } from '@myapihq/sdk';
|
|
5
5
|
import { friendlyError } from './errors.js';
|
|
6
|
+
import { currentOrg } from './helpers.js';
|
|
6
7
|
import * as fs from 'fs';
|
|
7
8
|
import { parseFlags } from './flags.js';
|
|
8
9
|
const pkgPath = new URL('../package.json', import.meta.url);
|
|
@@ -12,6 +13,10 @@ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
|
12
13
|
// tell which client versions are still in the wild — which is what you need
|
|
13
14
|
// before you can deprecate anything server-side.
|
|
14
15
|
setUserAgent(`myapi-cli/${pkg.version} node/${process.versions.node}`);
|
|
16
|
+
// Let `--json` output carry the org the call actually resolved to. Wired here
|
|
17
|
+
// rather than imported inside output.ts so that module stays free of command
|
|
18
|
+
// and config state. See printJson / requireOrg.
|
|
19
|
+
setResolvedContextSource(currentOrg);
|
|
15
20
|
import * as accountCmd from './commands/account.js';
|
|
16
21
|
import * as keysCmd from './commands/keys.js';
|
|
17
22
|
import * as billingCmd from './commands/billing.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// A resource sitting in another org answers exactly like a typo'd id. "Not
|
|
2
|
+
// found" sends the reader to check the id — which is correct, and which is why
|
|
3
|
+
// the org never gets questioned. Naming the org that was searched is what
|
|
4
|
+
// turns the commonest org-drift symptom into its own diagnosis.
|
|
5
|
+
//
|
|
6
|
+
// The risk on the other side is appending it to everything, which is the
|
|
7
|
+
// always-on banner in a different costume. These pin both edges.
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { withOrgContext } from './errors.js';
|
|
10
|
+
const ORG = { org_id: 'org-aaaaaaaa-1111', org_name: 'Acme' };
|
|
11
|
+
describe('withOrgContext', () => {
|
|
12
|
+
it('names the org searched on a 404', () => {
|
|
13
|
+
const out = withOrgContext('Funnel not found.', { status: 404, code: 'FUNNEL_NOT_FOUND' }, ORG);
|
|
14
|
+
expect(out).toContain('Funnel not found.');
|
|
15
|
+
expect(out).toContain('Acme');
|
|
16
|
+
expect(out).toContain('myapi org list');
|
|
17
|
+
});
|
|
18
|
+
it('fires on a not-found CODE even when the status is not 404', () => {
|
|
19
|
+
const out = withOrgContext('Resource not found.', { status: 400, code: 'not_found' }, ORG);
|
|
20
|
+
expect(out).toContain('Acme');
|
|
21
|
+
});
|
|
22
|
+
it('stays out of the way on unrelated errors', () => {
|
|
23
|
+
const msg = 'Too many requests. Please wait a moment and try again.';
|
|
24
|
+
expect(withOrgContext(msg, { status: 429, code: 'RATE_LIMITED' }, ORG)).toBe(msg);
|
|
25
|
+
});
|
|
26
|
+
it('says nothing when the org itself is what was not found', () => {
|
|
27
|
+
// "Looked in org X" where X is the id that does not exist restates the
|
|
28
|
+
// failure as though it were a clue.
|
|
29
|
+
const msg = 'Organization not found.';
|
|
30
|
+
expect(withOrgContext(msg, { status: 404, code: 'ORG_NOT_FOUND' }, ORG)).toBe(msg);
|
|
31
|
+
});
|
|
32
|
+
it('says nothing when no org was resolved', () => {
|
|
33
|
+
// Account-level commands (whoami, billing) never resolve an org; there is
|
|
34
|
+
// no org to blame and claiming one would be a fabrication.
|
|
35
|
+
const msg = 'Resource not found.';
|
|
36
|
+
expect(withOrgContext(msg, { status: 404, code: 'NOT_FOUND' }, undefined)).toBe(msg);
|
|
37
|
+
});
|
|
38
|
+
it('does not repeat an org the message already names', () => {
|
|
39
|
+
const msg = `Funnel not found in org ${ORG.org_id}.`;
|
|
40
|
+
expect(withOrgContext(msg, { status: 404, code: 'FUNNEL_NOT_FOUND' }, ORG)).toBe(msg);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The org-change notice has two silent failure modes and no build breaks for
|
|
2
|
+
// either: always-speak turns it into noise that gets filtered out within a day
|
|
3
|
+
// (and costs tokens on every call in an agent loop), always-silent lets the
|
|
4
|
+
// wrong-org write it exists to catch go through unannounced. Pin both edges.
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { orgChangeNotice } from './helpers.js';
|
|
7
|
+
const NAMES = { 'org-a': 'Acme', 'org-b': 'Demo Corp' };
|
|
8
|
+
describe('orgChangeNotice', () => {
|
|
9
|
+
it('says nothing in steady state — the same org twice', () => {
|
|
10
|
+
expect(orgChangeNotice('org-a', 'org-a', NAMES)).toBeUndefined();
|
|
11
|
+
});
|
|
12
|
+
it('says nothing on the first ever resolution', () => {
|
|
13
|
+
// No prior means nothing has moved; announcing here would fire on every
|
|
14
|
+
// fresh config and train the reader to ignore the line.
|
|
15
|
+
expect(orgChangeNotice(undefined, 'org-a', NAMES)).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
it('speaks when the org moves, naming both sides', () => {
|
|
18
|
+
const msg = orgChangeNotice('org-a', 'org-b', NAMES);
|
|
19
|
+
expect(msg).toBeDefined();
|
|
20
|
+
expect(msg).toContain('Acme');
|
|
21
|
+
expect(msg).toContain('Demo Corp');
|
|
22
|
+
});
|
|
23
|
+
it('still speaks when no name is cached, falling back to the id', () => {
|
|
24
|
+
// The cache is populated opportunistically by `org list` / `status`, so an
|
|
25
|
+
// agent that never ran either still has to be told the target moved.
|
|
26
|
+
const msg = orgChangeNotice('org-a', 'org-b', {});
|
|
27
|
+
expect(msg).toBeDefined();
|
|
28
|
+
expect(msg).toContain('org-a');
|
|
29
|
+
expect(msg).toContain('org-b');
|
|
30
|
+
});
|
|
31
|
+
it('shortens long ids so the line stays scannable', () => {
|
|
32
|
+
const long = 'org-7f1aa7f1-f6eb-4a6d-87b7-1bbe6584f5d6';
|
|
33
|
+
const msg = orgChangeNotice(long, 'org-b', {});
|
|
34
|
+
expect(msg).not.toContain(long);
|
|
35
|
+
expect(msg).toContain('org-7f1a');
|
|
36
|
+
});
|
|
37
|
+
});
|
package/dist/output.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export declare function success(message: string): void;
|
|
|
5
5
|
export declare function error(message: string): never;
|
|
6
6
|
export declare function info(message: string): void;
|
|
7
7
|
export declare function banner(message: string): void;
|
|
8
|
+
type ResolvedContext = {
|
|
9
|
+
org_id: string;
|
|
10
|
+
org_name?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function setResolvedContextSource(fn: () => ResolvedContext | undefined): void;
|
|
8
13
|
export declare function printJson(data: unknown): void;
|
|
9
14
|
export declare function spinnerFrame(i: number): string;
|
|
10
15
|
export declare function spinnerWrite(s: string): void;
|
|
@@ -16,3 +21,4 @@ export interface PrintTableOptions {
|
|
|
16
21
|
empty?: string;
|
|
17
22
|
}
|
|
18
23
|
export declare function printTable<T extends object>(rows: T[], opts?: PrintTableOptions): void;
|
|
24
|
+
export {};
|
package/dist/output.js
CHANGED
|
@@ -12,8 +12,15 @@ export function info(message) {
|
|
|
12
12
|
export function banner(message) {
|
|
13
13
|
process.stderr.write(message + '\n');
|
|
14
14
|
}
|
|
15
|
+
let resolvedContext;
|
|
16
|
+
export function setResolvedContextSource(fn) {
|
|
17
|
+
resolvedContext = fn;
|
|
18
|
+
}
|
|
15
19
|
export function printJson(data) {
|
|
16
|
-
|
|
20
|
+
const ctx = resolvedContext?.();
|
|
21
|
+
const stampable = ctx && data !== null && typeof data === 'object' && !Array.isArray(data);
|
|
22
|
+
const payload = stampable ? { ...data, _resolved: ctx } : data;
|
|
23
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
17
24
|
}
|
|
18
25
|
// Spinner / line-clear primitives. Used by polling helpers (utils.pollJob)
|
|
19
26
|
// and any handler that wants its own progress UI.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: my-api-hq
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.1.0
|
|
4
4
|
description: >
|
|
5
5
|
Auth, organizations, and billing hub. Start here to get an api_key and org_id — every other service depends on both.
|
|
6
6
|
triggers: [api key, account, organization, org, billing, balance, topup, credits, setup, defaults, brand, sync brand, doctor, health check, is my org healthy]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-6f54c719facde1ddcdee04ffcf9527ed42550cbfc67679ced5aea55fc7adba7d
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyApiHQ
|
|
@@ -15,14 +15,6 @@ The root service. It manages accounts, API keys, organizations, and billing. No
|
|
|
15
15
|
<!-- llm:start -->
|
|
16
16
|
MyApiHQ is the platform's foundation. Every other service (domain, funnel, auth, payments, fn, workflow, database, storage, email, webhook, crm, llm, image, pixel, url) requires both an `api_key` and (for org-scoped resources) an `org_id` minted here. Setup is one command — `myapi account setup` — which provisions an account, generates an api_key, creates a default org, and stores everything in `~/.myapi/config.json`. Subsequent commands pick up those defaults automatically.
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
myapihq ──► org_id + api_key
|
|
20
|
-
│
|
|
21
|
-
┌───────┴────────┐
|
|
22
|
-
mydomainapi myfunnelapi
|
|
23
|
-
(domains) (websites)
|
|
24
|
-
```
|
|
25
|
-
|
|
26
18
|
### Anonymous vs registered accounts
|
|
27
19
|
|
|
28
20
|
Two tiers, chosen at setup time:
|
|
@@ -42,6 +34,7 @@ An anonymous account can upgrade at any time via `myapi account link <email>`
|
|
|
42
34
|
| Command | What it does |
|
|
43
35
|
|---|---|
|
|
44
36
|
| `myapi account setup` | Interactive setup: creates account, generates api_key, sets defaults |
|
|
37
|
+
| `myapi status` | Single-screen orientation: account + every resource in the default org. Start here when you don't know what already exists |
|
|
45
38
|
| `myapi account whoami` | Show current account, default org/funnel, balance, free-tier usage |
|
|
46
39
|
| `myapi account link [email]` | Upgrade anonymous account to registered (or add a second session) |
|
|
47
40
|
| `myapi account switch [index]` | Switch active account |
|
|
@@ -72,6 +65,7 @@ myapi account setup
|
|
|
72
65
|
myapi org create "Acme" --yes
|
|
73
66
|
|
|
74
67
|
# Day-to-day
|
|
68
|
+
myapi status # what exists already, in one screen
|
|
75
69
|
myapi account whoami # confirm what's active
|
|
76
70
|
myapi billing balance # before doing anything that costs credits
|
|
77
71
|
myapi billing topup 20 # add $20
|
|
@@ -98,28 +92,54 @@ Each org gets a free preview subdomain (`*.makeautonomous.com`) usable before re
|
|
|
98
92
|
- API keys have format `hq_live_...` and are sent as `Authorization: Bearer <key>`.
|
|
99
93
|
- `org sync-brand` is async (scrapes the site, polls the job).
|
|
100
94
|
|
|
101
|
-
##
|
|
95
|
+
## Scoping a key — and why `--org` is the one that saves you
|
|
102
96
|
|
|
103
97
|
A key's authority is inline and always a subset of the key that mints it, so
|
|
104
98
|
you can hand work a key that cannot exceed its job:
|
|
105
99
|
|
|
106
100
|
```bash
|
|
101
|
+
myapi keys create --name agent --org <id> --grant '*' # locked to ONE org
|
|
107
102
|
myapi keys create --name ci --grant funnel:write,storage:read
|
|
108
103
|
myapi keys create --name readonly --grant '*:read' # read anything, write nothing
|
|
109
104
|
myapi keys create --name billing-fn --org <id> --grant email --spend-cap 25
|
|
110
105
|
```
|
|
111
106
|
|
|
107
|
+
- `--org <id>` — **lock it to one org.** Omit for account-wide.
|
|
112
108
|
- `--grant <list>` — `slot:read` / `slot:write`; a bare slot means write, `*`
|
|
113
109
|
means all. **Omitting `--grant` mints an unrestricted key.**
|
|
114
|
-
- `--org <id>` — lock it to one org. Omit for account-wide.
|
|
115
110
|
- `--spend-cap <usd>` — hard ceiling; `0` means the key cannot spend at all.
|
|
116
111
|
- `keys revoke-all --kind function|manual|account` narrows the kill switch.
|
|
117
112
|
|
|
113
|
+
**Agents working for one org: use an org-locked key.** The org is otherwise
|
|
114
|
+
ambient — a saved default that survives across turns, where a stale one looks
|
|
115
|
+
exactly like a correct one. That is how a demo lands on a live site. Locking
|
|
116
|
+
turns a wrong-org write into a `403`. Keep your *own* key account-wide though:
|
|
117
|
+
an org-locked one cannot create orgs or manage billing.
|
|
118
|
+
|
|
119
|
+
Two more signals, no flags needed: resolving to a different org than last time
|
|
120
|
+
prints `→ org changed: <from> → <to>` on stderr, once — silence means it has
|
|
121
|
+
not moved. Under `--json`, object responses carry `_resolved: {org_id,
|
|
122
|
+
org_name}`; assert on that rather than assuming.
|
|
123
|
+
|
|
118
124
|
## Org profile fields
|
|
119
125
|
|
|
120
126
|
`myapi org create <name>` also takes `--tagline`, `--description`,
|
|
121
127
|
`--business-sector` and `--logo-url`. They populate the org's public profile
|
|
122
|
-
and the funnel created alongside it
|
|
123
|
-
|
|
128
|
+
and the funnel created alongside it.
|
|
129
|
+
|
|
130
|
+
## HTTP (from deployed code)
|
|
131
|
+
|
|
132
|
+
<!-- http:start -->
|
|
133
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
134
|
+
```
|
|
135
|
+
base https://api.myapihq.com
|
|
136
|
+
path GET /hq/orgs/{org_id}
|
|
137
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
138
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
142
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
143
|
+
<!-- http:end -->
|
|
124
144
|
|
|
125
145
|
Run `myapi --help` or `myapi <command> --help` for full flag reference.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Saved audiences = named Goldfox-filter snapshots over the people or company database. Build a target list once, name it, reuse it across campaigns, refresh to re-evaluate against current data. The persistence layer on top of my-people-api + my-company-api.
|
|
6
6
|
triggers: [audience, segment, target list, saved filter, goldfox, lead list, abm list, refresh, members, prospect database]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-3fc9f2966804381cb9e2b5f52d44a0c12c857f243b151a93a813c6345574f07f
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyAudienceAPI
|
|
@@ -136,4 +136,19 @@ myapi audience create "EU growth-stage SaaS accounts" \
|
|
|
136
136
|
- Audiences are per-org. Two orgs can have audiences with the same name; ids are globally unique.
|
|
137
137
|
- Look before you delete: confirm the target with `myapi audience get <id>`, pass `--org` explicitly; delete verbs require `--yes` in non-interactive runs.
|
|
138
138
|
|
|
139
|
+
## HTTP (from deployed code)
|
|
140
|
+
|
|
141
|
+
<!-- http:start -->
|
|
142
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
143
|
+
```
|
|
144
|
+
base https://api.myapihq.com
|
|
145
|
+
path POST /audience/orgs/{org_id}/audiences
|
|
146
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
147
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
151
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
152
|
+
<!-- http:end -->
|
|
153
|
+
|
|
139
154
|
Run `myapi audience --help` for full flag reference.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Add authentication to apps you build on MyAPI — a managed OIDC identity provider for your app's END USERS (à la Kinde/Auth0). One auth tenant per org; register OIDC clients; sign users in with managed Google or the hosted login page; verify RS256 tokens against the tenant JWKS.
|
|
6
6
|
triggers: [auth, authentication, login, sign-in, oidc, oauth, jwt, jwks, sso, google sign-in, user accounts, identity provider, kinde, auth0, clerk]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-07690e940717e8f65c195f9fbecdc1def9372edfd52dea19f6570227008d28ad
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyAuthAPI
|
|
@@ -121,4 +121,19 @@ with no email — for throwaway or machine-owned orgs. It cannot receive
|
|
|
121
121
|
password resets or magic links, so attach a real identity before anything
|
|
122
122
|
depends on it. `myapi status` shows `Type: anonymous`.
|
|
123
123
|
|
|
124
|
-
**End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (verify the id_token for identity; the access token is a bearer credential for `<issuer>/userinfo`).
|
|
124
|
+
**End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (verify the id_token for identity; the access token is a bearer credential for `<issuer>/userinfo`).
|
|
125
|
+
|
|
126
|
+
## HTTP (from deployed code)
|
|
127
|
+
|
|
128
|
+
<!-- http:start -->
|
|
129
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
130
|
+
```
|
|
131
|
+
base https://api.myapihq.com
|
|
132
|
+
path POST /auth/orgs/{org_id}/tenant
|
|
133
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
134
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
138
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
139
|
+
<!-- http:end -->
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Company database backed by the Goldfox crawl. Filter companies by Goldfox confidence tier, country/TLD consistency, behavioral page signals (has_careers_page, has_investors_page, has_shop_page, has_c_level, has_decision_maker), legal-entity status, headcount, and source-URL count. Account-based targeting and B2B firmographics.
|
|
6
6
|
triggers: [companies, accounts, firmographics, abm, search, filter, goldfox, careers signal, investors, c-level, shop, b2b targeting]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-5204c6218af2d4c52b1b81aa0d917b69c863be1d3c0384d2d2f7d9dda1587534
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyCompanyAPI
|
|
@@ -107,4 +107,19 @@ myapi audience members $AID --limit 50 --json > accounts.json
|
|
|
107
107
|
- `keyword` is a substring match on the company's **domain** — use `--keyword stripe` to find domains containing "stripe".
|
|
108
108
|
- For a persistent account list, use `my-audience-api` with `--from company`.
|
|
109
109
|
|
|
110
|
+
## HTTP (from deployed code)
|
|
111
|
+
|
|
112
|
+
<!-- http:start -->
|
|
113
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
114
|
+
```
|
|
115
|
+
base https://api.myapihq.com
|
|
116
|
+
path POST /company/orgs/{org_id}/search
|
|
117
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
118
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
122
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
123
|
+
<!-- http:end -->
|
|
124
|
+
|
|
110
125
|
Run `myapi company --help` for full flag reference.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Run containers on demand — long-running services, background workers, and scheduled jobs. The heavier-duty sibling of edge functions, for native deps and long execution.
|
|
6
6
|
triggers: [container, cloud run, dynamic app, custom domain app, service, worker, scheduled job, deploy container, docker image]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-a5aabfe95b85f1e6ceb2b9254441183d540e81f888cefbdcf302385b95db2fd0
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyContainerAPI
|
|
@@ -23,26 +23,33 @@ The lifecycle is **create → deploy → (optionally) bind a custom domain**.
|
|
|
23
23
|
can deploy; you do not need to be able to run one.
|
|
24
24
|
- `domain` puts the container on a **custom domain** — how you serve a dynamic app at `app.yourbrand.com`.
|
|
25
25
|
|
|
26
|
-
### Deploying safely
|
|
26
|
+
### Deploying safely
|
|
27
27
|
|
|
28
|
-
A deploy takes 100% of traffic the moment it lands.
|
|
29
|
-
definition of correct beyond "something is listening on the port", and no way
|
|
30
|
-
back. Plan for that.
|
|
28
|
+
A plain deploy takes 100% of traffic the moment it lands.
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
not appear on the container afterwards, so do not rely on it either.
|
|
30
|
+
**`--smoke '<assertion>'`** deploys the revision with no traffic, checks the
|
|
31
|
+
assertion, and promotes only if it holds. A failure returns `SMOKE_FAILED`,
|
|
32
|
+
leaves the previous revision serving, and returns a revision URL to inspect.
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
```bash
|
|
35
|
+
myapi container deploy <id> --source ./app --smoke 'GET / contains assets/'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Assert on content, not status.** A build whose frontend never bundled still
|
|
39
|
+
binds its port and answers `200` — that is how a placeholder page reached
|
|
40
|
+
production and served a dead page for fifteen minutes.
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
**`--no-promote`** holds the revision back and prints a URL to test yourself,
|
|
43
|
+
then `myapi container promote <id> <revision>`. Use it when the check is more
|
|
44
|
+
than one assertion. On a container's *first* deploy nothing is serving yet, so
|
|
45
|
+
traffic is not withheld and the output says so.
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
**`myapi container rollback <id>`** returns traffic to the previous ready
|
|
48
|
+
revision in seconds, no rebuild.
|
|
49
|
+
|
|
50
|
+
`--health-check /livez` at create makes the startup probe an HTTP request
|
|
51
|
+
rather than a bare TCP connect. `/healthz` is refused — the runtime intercepts
|
|
52
|
+
it, so the probe would never reach your container.
|
|
46
53
|
|
|
47
54
|
### Custom domains (dynamic apps)
|
|
48
55
|
|
|
@@ -85,14 +92,14 @@ myapi container deploy <id> --source ./my-app
|
|
|
85
92
|
# 2b. Or ship an image you already built and pushed.
|
|
86
93
|
myapi container deploy <id> registry.example.com/my-app:v1
|
|
87
94
|
|
|
88
|
-
#
|
|
89
|
-
#
|
|
95
|
+
# A plain deploy takes 100% of traffic the moment it lands. Gate it with
|
|
96
|
+
# --smoke, or hold it back with --no-promote and test the revision yourself.
|
|
90
97
|
|
|
91
|
-
# 3.
|
|
92
|
-
#
|
|
98
|
+
# 3. Verify content, not status: a build whose frontend never bundled
|
|
99
|
+
# still binds its port and returns 200.
|
|
93
100
|
curl -s https://<your-domain>/ | grep -q 'assets/' || echo "BROKEN BUILD"
|
|
94
101
|
|
|
95
|
-
#
|
|
102
|
+
# 4. Serve it on a custom domain. The parent domain must already be
|
|
96
103
|
# registered: myapi domain register synthesisdaily.com
|
|
97
104
|
myapi container domain <id> app.synthesisdaily.com
|
|
98
105
|
# → app.synthesisdaily.com now serves the container over HTTPS
|
|
@@ -121,8 +128,7 @@ way that looks like an application bug.
|
|
|
121
128
|
error envelope will not reach the caller. Return a 4xx if the reason has to
|
|
122
129
|
survive.
|
|
123
130
|
- **`--env`, `--cpu`, `--memory`, `--max-instances` and `--cron` are set at
|
|
124
|
-
`create` and cannot be changed by `deploy`.**
|
|
125
|
-
nothing. Recreate the container to change them.
|
|
131
|
+
`create` and cannot be changed by `deploy`.** Recreate to change them.
|
|
126
132
|
|
|
127
133
|
### Keeping a service warm
|
|
128
134
|
|
|
@@ -144,6 +150,21 @@ service costs nothing while idle.
|
|
|
144
150
|
`invalid_json_response`. Build the tarball yourself and pass
|
|
145
151
|
`--source ctx.tar.gz`, or keep the directory clean.
|
|
146
152
|
|
|
153
|
+
## HTTP (from deployed code)
|
|
154
|
+
|
|
155
|
+
<!-- http:start -->
|
|
156
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
157
|
+
```
|
|
158
|
+
base https://api.myapihq.com
|
|
159
|
+
path POST /container/orgs/{org_id}/containers
|
|
160
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
161
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
165
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
166
|
+
<!-- http:end -->
|
|
167
|
+
|
|
147
168
|
Run `myapi container --help` for the full flag reference.
|
|
148
169
|
|
|
149
|
-
**End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container
|
|
170
|
+
**End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi walks the full seam — hosted login → token verification → per-user KV record → deployed container. Every constraint it hits is in **Runtime constraints** above.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
The canonical store of engaged contacts + companies for an org. Auto-ingests from inbound webhooks via a configurable dot-path. Fixed lifecycle_stage enum (cold | warm | qualified | customer | churned). Append-only event timeline with reserved kinds. Soft delete + restore. Promote-from-Goldfox closes the discovery → engagement loop.
|
|
6
6
|
triggers: [crm, contact, company, lead, engagement, pipeline, lifecycle, qualified, customer, webhook ingest, promote]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-3f840c8d7205d6a3eacc7c2be3ba2580a52a1964c085af241fd9ad67d48e19b6
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyCRMAPI
|
|
@@ -167,4 +167,19 @@ myapi crm contacts events <id> --kind webhook_received
|
|
|
167
167
|
- **Only `webhook_received` fires today.** Email and pixel auto-ingest are coming; the CLI surface will not change.
|
|
168
168
|
- **Free in v1.** Metered later if usage warrants.
|
|
169
169
|
|
|
170
|
+
## HTTP (from deployed code)
|
|
171
|
+
|
|
172
|
+
<!-- http:start -->
|
|
173
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
174
|
+
```
|
|
175
|
+
base https://api.myapihq.com
|
|
176
|
+
path POST /crm/orgs/{org_id}/contacts
|
|
177
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
178
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
182
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
183
|
+
<!-- http:end -->
|
|
184
|
+
|
|
170
185
|
Run `myapi crm --help` or `myapi crm <namespace> --help` for inline reference.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Per-org KV store with named namespaces, JSON values up to 256 KB, prefix-scan listing, and compare-and-swap via etag. The substrate for any stateful agent-built app on MyAPI — user tables, session stores, idempotency keys, per-user lookup maps.
|
|
6
6
|
triggers: [database, kv, key value, namespace, store, state, etag, cas, session, idempotency]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-9b6f14fa5b0115ce042c71f861cb71ec8993aa68447d740b63f5b3689c23b629
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyDatabaseAPI
|
|
@@ -105,26 +105,22 @@ myapi database get "by-email:$EMAIL" --ns users --json | jq -r .value
|
|
|
105
105
|
- **Free in v1.** Metered later if usage shows a need. Cost discipline still applies — store data, not blobs.
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
##
|
|
109
|
-
|
|
110
|
-
The CLI is not what runs in production — a deployed function or container calls
|
|
111
|
-
the HTTP API directly. That surface was previously only discoverable by
|
|
112
|
-
grepping the CLI bundle, which cost one team an hour per slot.
|
|
108
|
+
## HTTP (from deployed code)
|
|
113
109
|
|
|
110
|
+
<!-- http:start -->
|
|
111
|
+
<!-- generated by `npm run canonical-sync` — do not edit -->
|
|
114
112
|
```
|
|
115
|
-
base
|
|
116
|
-
path
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
121
|
-
Unwrap `data`. On failure `success` is false and `error` is
|
|
122
|
-
{ code, message }.
|
|
113
|
+
base https://api.myapihq.com
|
|
114
|
+
path PUT /database/orgs/{org_id}/namespaces/{ns}/keys/{key}
|
|
115
|
+
body writes are WRAPPED: {"value": <json>} — not the bare value
|
|
116
|
+
auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
|
|
117
|
+
reply { "success": true, "data": …, "error": null, "meta": {…} }
|
|
123
118
|
```
|
|
124
119
|
|
|
125
|
-
**
|
|
126
|
-
**
|
|
120
|
+
- **Per-slot host** — do not assume one host serves every slot.
|
|
121
|
+
- **Org id goes in the PATH** — there is no `X-Org-Id` header.
|
|
122
|
+
<!-- http:end -->
|
|
127
123
|
|
|
128
124
|
Run `myapi database --help` for inline reference.
|
|
129
125
|
|
|
130
|
-
**End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (KV writes must be wrapped as `{"value": …}`).
|
|
126
|
+
**End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (KV writes must be wrapped as `{"value": …}`).
|