@myapihq/cli 1.3.11 → 1.3.13

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.
@@ -5,6 +5,10 @@ import { success, error, info, printTable, printJson } from '../../output.js';
5
5
  import { requireOrg, requireArg } from '../../helpers.js';
6
6
  export const EXPOSES = [
7
7
  'POST /crm/orgs/{org_id}/companies',
8
+ // Backend (2026-06-06): bare GET list with query-param filters —
9
+ // companion to the POST /search verb. See contacts.ts for the same
10
+ // pattern.
11
+ 'GET /crm/orgs/{org_id}/companies',
8
12
  'POST /crm/orgs/{org_id}/companies/promote',
9
13
  'POST /crm/orgs/{org_id}/companies/search',
10
14
  'GET /crm/orgs/{org_id}/companies/{id}',
@@ -5,6 +5,11 @@ import { success, error, info, printTable, printJson } from '../../output.js';
5
5
  import { requireOrg, requireArg } from '../../helpers.js';
6
6
  export const EXPOSES = [
7
7
  'POST /crm/orgs/{org_id}/contacts',
8
+ // Backend (2026-06-06): bare GET list with query-param filters —
9
+ // companion to the POST /search verb (richer filters via body). The
10
+ // CLI `list` verb still uses /search for forward-compat; the bare GET
11
+ // is declared so it's covered.
12
+ 'GET /crm/orgs/{org_id}/contacts',
8
13
  'POST /crm/orgs/{org_id}/contacts/promote',
9
14
  'POST /crm/orgs/{org_id}/contacts/search',
10
15
  'GET /crm/orgs/{org_id}/contacts/{id}',
@@ -27,6 +27,7 @@ export const SCHEMA = {
27
27
  funnel: 'string',
28
28
  slug: 'string',
29
29
  env: 'string',
30
+ force: 'boolean',
30
31
  'api-fn': 'string',
31
32
  // form
32
33
  fields: 'string',
@@ -44,6 +45,28 @@ function validateFunnelName(name) {
44
45
  error(`Invalid --name "${name}". Lowercase letters, digits, hyphens; 1-50 chars; starts with a letter or digit.`);
45
46
  }
46
47
  }
48
+ // Canonical key for comparing slugs across the wire shapes the backend may
49
+ // return (`/about`, `about`, `/about/`). Home is the empty string whether it
50
+ // arrives as `/` or ``.
51
+ function slugKey(s) {
52
+ return (s.startsWith('/') ? s.slice(1) : s).replace(/\/+$/, '');
53
+ }
54
+ // Resolve human-readable labels for the namespace a destructive write lands
55
+ // on. The whole point of the funnel-write guardrails is that the agent (and
56
+ // the human reading the transcript) can SEE which org + funnel it touched —
57
+ // a bare UUID hides the cross-org mistake this is meant to catch. Best-effort:
58
+ // fall back to ids if either lookup fails so we never block the real work on a
59
+ // cosmetic call.
60
+ async function describeTarget(apiKey, orgId, funnelId) {
61
+ const [org, funnelRes] = await Promise.all([
62
+ hq.getOrg(apiKey, orgId).catch(() => undefined),
63
+ sdkFunnel.getFunnel(apiKey, orgId, funnelId).catch(() => undefined),
64
+ ]);
65
+ const orgLabel = org?.name ? `${org.name} (${orgId})` : orgId;
66
+ const fname = funnelRes?.funnel?.name;
67
+ const funnelLabel = fname ? `${fname} (${funnelId})` : funnelId;
68
+ return { orgLabel, funnelLabel };
69
+ }
47
70
  export async function list(flags) {
48
71
  const config = requireConfig();
49
72
  const orgId = requireOrg(flags, config, 'myapi funnel list [--org <id>]');
@@ -149,6 +172,29 @@ export async function push(slug, flags) {
149
172
  if (process.stdin.isTTY) {
150
173
  error("No content provided via stdin. Use a pipe or file:\n echo '<h1>Hello</h1>' | myapi funnel push /\n cat page.html | myapi funnel push /about");
151
174
  }
175
+ // Overwrite guard. `push` silently replaces whatever is live at the slug,
176
+ // so an agent pushing `/` onto an org's existing site destroys it with no
177
+ // second chance. Mirror the my-domain-api principle ("destructive ops don't
178
+ // ride on silent defaults"): if a page already exists at this slug, refuse
179
+ // and name the org + funnel + slug, unless --force. We do this BEFORE
180
+ // reading stdin so a refused push never consumes the generated HTML.
181
+ const force = flags.force === true;
182
+ const existingPages = await sdkFunnel.listFunnelPages(config.api_key, orgId, funnelId);
183
+ const clash = existingPages.find(p => slugKey(p.slug) === slugKey(finalSlug));
184
+ if (clash && !force) {
185
+ const { orgLabel, funnelLabel } = await describeTarget(config.api_key, orgId, funnelId);
186
+ const when = clash.updated_at ? `, last updated ${formatDate(clash.updated_at)}` : '';
187
+ error(`Refusing to overwrite an existing page.
188
+
189
+ Org: ${orgLabel}
190
+ Funnel: ${funnelLabel}
191
+ Slug: ${finalSlug} (already published${when})
192
+
193
+ This funnel already serves a page at ${finalSlug}; pushing would replace it.
194
+ • Wrong org or funnel? Re-run with --org <id> and/or --funnel <id>.
195
+ • New demo? Create a fresh funnel: myapi funnel create --name <demo> --org <id>
196
+ • Meant to replace it? Re-run the same command with --force.`);
197
+ }
152
198
  const html = await new Promise((resolve, reject) => {
153
199
  let data = '';
154
200
  process.stdin.setEncoding('utf-8');
@@ -159,9 +205,12 @@ export async function push(slug, flags) {
159
205
  if (!html.trim())
160
206
  error("No content provided via stdin. Usage: echo '<h1>Hello</h1>' | myapi funnel push [slug]");
161
207
  const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, funnelId, { slug: finalSlug, html });
162
- success(`Pushed page to ${finalSlug}`);
208
+ const { orgLabel, funnelLabel } = await describeTarget(config.api_key, orgId, funnelId);
209
+ success(`Pushed ${clash ? '(overwrote) ' : ''}page to ${finalSlug}`);
210
+ info(`Org: ${orgLabel}`);
211
+ info(`Funnel: ${funnelLabel}`);
163
212
  if (resolvedFromOrg) {
164
- info(`(Used the org's only funnel: ${funnelId}. Set a default with: myapi config set-funnel ${funnelId})`);
213
+ info(`(Auto-picked the org's only funnel. Pin it explicitly with --funnel ${funnelId} or: myapi config set-funnel ${funnelId})`);
165
214
  }
166
215
  let liveUrl = result?.url;
167
216
  if (!liveUrl) {
@@ -405,6 +454,29 @@ export async function publish(dir, flags) {
405
454
  const files = await collectFiles(dir, dir);
406
455
  if (files.length === 0)
407
456
  error(`No files found under ${dir}.`);
457
+ // Overwrite guard — prod only. Publishing to prod replaces the whole live
458
+ // site; the dev channel is the throwaway preview you re-publish freely, so
459
+ // it stays unguarded. If the funnel already serves pages and this is a prod
460
+ // publish, refuse without --force and name the namespace at risk.
461
+ const force = flags.force === true;
462
+ const channel = env || 'prod';
463
+ if (channel === 'prod' && !force) {
464
+ const existingPages = await sdkFunnel.listFunnelPages(config.api_key, orgId, funnelId);
465
+ if (existingPages.length > 0) {
466
+ const { orgLabel, funnelLabel } = await describeTarget(config.api_key, orgId, funnelId);
467
+ error(`Refusing to replace a published site.
468
+
469
+ Org: ${orgLabel}
470
+ Funnel: ${funnelLabel}
471
+ Live: ${existingPages.length} page(s) currently published
472
+
473
+ Publishing to the prod channel replaces the entire live site.
474
+ • Wrong org or funnel? Re-run with --org <id> and/or --funnel <id>.
475
+ • New demo? Create a fresh funnel: myapi funnel create --name <demo> --org <id>
476
+ • Preview safely first: myapi funnel publish ${dir} --env dev
477
+ • Meant to replace it? Re-run the same command with --force.`);
478
+ }
479
+ }
408
480
  const result = await sdkFunnel.publishFiles(config.api_key, orgId, funnelId, files, {
409
481
  env: env,
410
482
  apiFunctionId: flags['api-fn'],
@@ -413,7 +485,10 @@ export async function publish(dir, flags) {
413
485
  printJson(result);
414
486
  return;
415
487
  }
488
+ const { orgLabel, funnelLabel } = await describeTarget(config.api_key, orgId, funnelId);
416
489
  success(`Published ${result.file_count} file(s) to the ${result.channel} channel`);
490
+ info(`Org: ${orgLabel}`);
491
+ info(`Funnel: ${funnelLabel}`);
417
492
  info(`Size: ${(result.size_bytes / 1024).toFixed(1)} KB`);
418
493
  info(`SPA: ${result.spa_mode ? 'on' : 'off'}`);
419
494
  info(`Live: ${result.published_url}`);
@@ -466,7 +541,7 @@ free.`,
466
541
  'get': 'myapi funnel get <id> [--org <id>] [--json]',
467
542
  'list': 'myapi funnel list [--org <id>] [--json]',
468
543
  'pages': 'myapi funnel pages [funnel_id] [--funnel <id>] [--org <id>] [--json]',
469
- 'publish': `myapi funnel publish <dir> [--funnel <id>] [--env dev|prod] [--api-fn <id>] [--org <id>]
544
+ 'publish': `myapi funnel publish <dir> [--funnel <id>] [--env dev|prod] [--api-fn <id>] [--force] [--org <id>]
470
545
 
471
546
  Uploads a whole local directory as the funnel's site. Each file's path
472
547
  within <dir> becomes its path on the site (e.g. dir/about/index.html →
@@ -475,18 +550,29 @@ within <dir> becomes its path on the site (e.g. dir/about/index.html →
475
550
  --env dev|prod Target channel (default prod). dev publishes to
476
551
  <name>-dev.makeautonomous.com for preview.
477
552
  --api-fn <id> Bind /api/* on the site to a deployed function.
553
+ --force Publish to prod even when the funnel already serves
554
+ pages (replaces the entire live site). Refused without
555
+ it. The dev channel is never guarded.
478
556
 
557
+ Publishing to prod replaces the whole live site. The command prints the
558
+ resolved org + funnel so you can confirm the namespace before it lands.
479
559
  SPA fallback is auto-enabled when the publish has a root index.html.
480
560
 
481
561
  Examples:
482
- myapi funnel publish ./dist
483
- myapi funnel publish ./dist --env dev
562
+ myapi funnel publish ./dist --env dev # safe preview
563
+ myapi funnel publish ./dist # prod — refused if a site exists
564
+ myapi funnel publish ./dist --force # prod — replace existing site
484
565
  myapi funnel publish ./site --api-fn <function_id>`,
485
- 'push': `myapi funnel push [slug] [--funnel <id>] [--slug <path>] [--org <id>] < page.html
566
+ 'push': `myapi funnel push [slug] [--funnel <id>] [--slug <path>] [--force] [--org <id>] < page.html
486
567
 
487
568
  Reads HTML from stdin and publishes to <slug> on your funnel. Funnel is resolved
488
569
  from --funnel, the default funnel, or (only if the org has exactly one) auto-picked.
489
570
 
571
+ Pushing OVERWRITES whatever page is already live at <slug>. If a page already
572
+ exists there, push refuses and names the org + funnel + slug at risk; re-run
573
+ with --force to replace it. On success it prints the resolved org + funnel so
574
+ you can confirm you wrote to the namespace you intended.
575
+
490
576
  By default, your funnel is served on a preview subdomain (*.makeautonomous.com).
491
577
  To serve on your own domain, register and assign one with:
492
578
  myapi domain register <domain>
@@ -495,7 +581,7 @@ To serve on your own domain, register and assign one with:
495
581
  Examples:
496
582
  echo '<h1>Hello</h1>' | myapi funnel push /
497
583
  cat about.html | myapi funnel push /about
498
- myapi funnel push < index.html
584
+ cat new.html | myapi funnel push / --force # replace the live homepage
499
585
  cat p.html | myapi funnel push /pricing --funnel <uuid>`,
500
586
  'verify': `myapi funnel verify [slug] [--funnel <id>] [--org <id>]
501
587
 
@@ -15,6 +15,8 @@ A funnel is a website tied to an org. Push raw HTML pages to slugs and they're s
15
15
  <!-- llm:start -->
16
16
  Funnels are the publishing surface. You create a funnel under an org (one command), then `funnel push` raw HTML to any slug (`/`, `/about`, `/pricing`, etc.). The edge serves the page within seconds — no CI/CD, no build, no deploy queue.
17
17
 
18
+ **Before you push, know where you're writing.** `funnel push` and `funnel publish --env prod` **overwrite** whatever is live at the target — no undo. An org may already host a real site; pushing `/` onto it replaces the homepage. See **Namespace & safety** below — this is the #1 way agents wreck an existing site.
19
+
18
20
  By default, your funnel lives on a free preview subdomain (`*.makeautonomous.com`) you get with every org. To serve on a custom domain, register and assign one via **mydomainapi** first.
19
21
 
20
22
  Every funnel auto-provisions a **webhook** at creation time (`org_webhook_id`). The funnel exposes two public proxy endpoints your HTML can call directly (no API key needed): a **form submit** at `POST /funnel/funnels/{id}/submit/{slug}` that delivers submissions through that webhook (CRM upsert + bound workflows fire automatically), and an **analytics/event ingest** for pageviews and click tracking. For multi-form funnels you can bind individual slugs to specific destinations with `myapi funnel form ... --capture-to webhook:<id>`.
@@ -28,48 +30,61 @@ Every funnel auto-provisions a **webhook** at creation time (`org_webhook_id`).
28
30
  | `myapi funnel list` | List all funnels with preview/domain URLs |
29
31
  | `myapi funnel get <id>` | Inspect a funnel's metadata + preview URL |
30
32
  | `myapi funnel delete <id>` | Delete the funnel and purge its edge pages |
31
- | `myapi funnel push [slug]` | Push HTML from stdin to a slug (default: `/`) |
33
+ | `myapi funnel push [slug]` | Push HTML from stdin to a slug (default: `/`). **Overwrites** an existing page — refused without `--force`, prints the resolved org + funnel on success |
32
34
  | `myapi funnel pages [funnel_id]` | List the pages currently published to a funnel |
33
35
  | `myapi funnel form [funnel_id]` | Emit canonical form HTML (and register a binding with `--capture-to`) |
34
36
  | `myapi funnel verify [slug]` | Verify a published page is reachable + check links/webhooks |
35
37
  <!-- generated:end -->
36
38
 
39
+ ## Namespace & safety (read before any write)
40
+
41
+ A write targets a `(org, funnel, slug)` address. Get all three right *before* pushing — the CLI will help, but the thinking is yours:
42
+
43
+ 1. **Confirm the org.** `--org` (or `default_org`) decides whose namespace you touch. For a demo or a new project, pass `--org <id>` explicitly every time — don't trust the ambient default. `myapi org list` shows the orgs you can reach.
44
+ 2. **Look before you write.** `myapi funnel list --org <id>` shows the org's funnels; `myapi funnel pages --funnel <id>` shows what's already published. If a slug is taken by something real, you're about to replace it.
45
+ 3. **A new demo = a new funnel.** Don't reuse an org's existing funnel for an unrelated demo. `myapi funnel create --name <demo> --org <id>` gives you a clean namespace and its own preview subdomain.
46
+ 4. **Pin the funnel.** When an org has exactly one funnel, `push`/`publish` auto-pick it — convenient, but it's how a demo lands on the wrong site. Pass `--funnel <id>` (or set a default) so the target is explicit, not inferred.
47
+
48
+ The CLI backstops you: `push` refuses to overwrite an existing slug (and `publish --env prod` refuses to replace a live site) without `--force`, and both print the resolved **org + funnel** on success. `--force` is the deliberate "yes, replace it" — never reach for it just to clear the error; first check whether the clash means you're aimed at the wrong place.
49
+
37
50
  ## Examples
38
51
  <!-- llm:start -->
39
52
  ```bash
40
- # Create a funnel + push a homepage
41
- myapi funnel create
42
- echo '<h1>Hello</h1>' | myapi funnel push /
53
+ # New demo, done safely — explicit org, fresh funnel, confirm, then push
54
+ myapi org list # which orgs can I reach?
55
+ myapi funnel list --org <org_id> # what already lives here?
56
+ myapi funnel create --name acme-demo --org <org_id> # clean namespace for the demo
57
+ echo '<h1>Hello</h1>' | myapi funnel push / --funnel <new_funnel_id>
43
58
 
44
- # Push multiple pages
45
- cat about.html | myapi funnel push /about
46
- cat pricing.html | myapi funnel push /pricing
59
+ # Push multiple pages to that funnel
60
+ cat about.html | myapi funnel push /about --funnel <new_funnel_id>
61
+ cat pricing.html | myapi funnel push /pricing --funnel <new_funnel_id>
47
62
 
48
- # Pipe directly from a generator
49
- my-html-gen | myapi funnel push /landing
63
+ # Inspect what's published before touching an existing funnel
64
+ myapi funnel pages --funnel <funnel_id>
65
+ myapi funnel verify /pricing --funnel <funnel_id>
50
66
 
51
- # Inspect what's published
52
- myapi funnel pages
53
- myapi funnel verify /pricing
67
+ # Deliberately replace a live page (only after confirming it's the right target)
68
+ cat new-home.html | myapi funnel push / --funnel <funnel_id> --force
54
69
 
55
70
  # Clean up
56
- myapi funnel delete <funnel_id>
71
+ myapi funnel delete <funnel_id> --org <org_id>
57
72
  ```
58
73
 
59
- Omitting `[slug]` defaults to `/`. The funnel id is resolved from `--funnel`, the saved default funnel, or — only if the org has exactly one funnel — auto-picked.
74
+ Omitting `[slug]` defaults to `/`. The funnel id is resolved from `--funnel`, the saved default funnel, or — only if the org has exactly one funnel — auto-picked (so an unintended push can silently land on an existing site; pass `--funnel` to be sure). `push` refuses to overwrite an occupied slug without `--force`.
60
75
  <!-- llm:end -->
61
76
 
62
77
  ## Form submissions (canonical recipe)
63
78
 
64
79
  The funnel submit proxy is the canonical form-capture path. **Never hardcode `https://api.mywebhookapi.com/webhook/in/<slug>` into funnel HTML** — that URL leaks into your git history, breaks on rotation, gets scraped, and locks the platform out of future form-quality features (rate-limit, captcha, anti-bot, field validation). Use the proxy instead — same plumbing, hidden URL, automatic CRM ingest on `email`.
65
80
 
66
- Zero-config form (the happy path most agents want):
81
+ Zero-config form (the happy path most agents want) — still pin the org + funnel so the snippet and the page land in the namespace you mean (see **Namespace & safety**):
67
82
 
68
83
  ```bash
69
- myapi funnel create
70
- myapi funnel form --slug join --fields email:required,name > snippet.html
84
+ myapi funnel create --name acme-demo --org <org_id>
85
+ myapi funnel form --slug join --fields email:required,name --funnel <funnel_id> > snippet.html
71
86
  # paste snippet.html into your page (or pipe through funnel push):
72
- cat page-with-snippet.html | myapi funnel push /
87
+ cat page-with-snippet.html | myapi funnel push / --funnel <funnel_id>
73
88
  # Submissions land in the funnel's auto-provisioned webhook → CRM upsert on `email` → any bound workflow fires.
74
89
  ```
75
90
 
@@ -97,7 +112,8 @@ myapi funnel form <funnel_id> --slug survey \
97
112
 
98
113
  ## Notes
99
114
 
100
- - Set defaults with `myapi config set-org <id>` and `myapi config set-funnel <id>` to skip flags on every command.
115
+ - Set defaults with `myapi config set-org <id>` and `myapi config set-funnel <id>` to skip flags on every command — but a stale default is exactly how a push lands on the wrong org/funnel. For demos and one-offs, pass `--org`/`--funnel` explicitly instead of relying on whatever default was set last.
116
+ - `push` (any slug) and `publish --env prod` overwrite live content and are refused without `--force` when something already exists at the target. `--force` means "yes, replace the live page/site" — confirm you're aimed at the right `(org, funnel, slug)` before using it, don't use it to silence the error.
101
117
  - Deleting a funnel purges all its edge pages immediately.
102
118
  - `402` errors mean insufficient credits — run `myapi billing topup <amount>`.
103
119
 
@@ -89,6 +89,22 @@ Shared usage block on every verb:
89
89
 
90
90
  The model/provider is **never** named in the verb response. That's the point — the verb is the contract, the model is implementation.
91
91
 
92
+ ### OpenAI-compatible drop-in
93
+
94
+ `POST /llm/orgs/{org_id}/chat/completions` (and `/v1/chat/completions` alias) accepts the OpenAI request shape and returns the OpenAI response shape — **no envelope**. Same catalog and pricing as raw `complete`. Use it when existing OpenAI SDK / LangChain / any `base_url`-configurable tooling should point at MyAPI without rewriting.
95
+
96
+ ```python
97
+ from openai import OpenAI
98
+ client = OpenAI(
99
+ api_key="hq_live_…",
100
+ base_url="https://api.myapihq.com/llm/orgs/<org_id>/v1",
101
+ )
102
+ r = client.chat.completions.create(model="Qwen/Qwen3-Coder-30B-A3B-Instruct",
103
+ messages=[{"role":"user","content":"Hi"}])
104
+ ```
105
+
106
+ Use raw `complete` (MyAPI shape) for first-party integrations; use the OpenAI-compat path for compatibility with existing client code.
107
+
92
108
  <!-- llm:end -->
93
109
 
94
110
  ## Commands
@@ -145,10 +161,7 @@ INTENT=$(printf '%s' "$BODY" | myapi llm classify - \
145
161
 
146
162
  ## Notes
147
163
 
148
- - **`draft` context safety — what the platform does and doesn't do.** Every field in `context` is quoted into the prompt as referent data the model is told to incorporate. Sensitive-named keys (`secret`, `api_key`, `password`, `token`, etc.) are **not** automatically redacted if you pass `{"secret": "abc123"}`, the model may include `"abc123"` in the output. The verb does two things on top:
149
- - **Injection defense.** Keys named `instructions`, `system`, `system_prompt`, `prompt`, `override`, `directives`, and the JS prototype-pollution sentinels are stripped at the boundary and surfaced in `meta.warnings`; the model can't be hijacked through them.
150
- - **Output guardrail.** After generation, every fact value (length ≥ 4) is substring-scanned in the output; matches surface under `meta.guardrails.facts_in_output: ["secret", "api_key", ...]`. This is a **signal, not a redaction** — you see when a value landed in the body.
151
- Rule of thumb: if a value must not appear in the output, **do not put it in `context`**. Pass identifiers (recipient name, account id, topic) and let the prompt reference them indirectly; keep credentials, PII, and any internal-only metadata out of the body entirely.
152
- - **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks (future proprietary models land here, wrapped behind the verb contract).
153
- - **Cost + latency.** `usage.cost_cents` is authoritative — no markup today. Qwen3-Coder-30B-A3B: 200–600ms to first token, 1–3s end-to-end on a few-hundred-token reply.
154
- - **Live catalog, no streaming, no BYOK.** Don't hard-code model ids — `models` is the source of truth (CLI picks if `--model` omitted). Full reply only.
164
+ - **`draft` context safety.** `context` fields are quoted into the prompt verbatim; sensitive-named keys (`secret`, `api_key`, `password`, …) are NOT redacted. Two guards on top: (a) injection-defense strips `instructions`/`system`/`prompt`/`override` keys and surfaces them in `meta.warnings`; (b) output guardrail substring-scans fact values (length ≥ 4) in the response and lists matches in `meta.guardrails.facts_in_output` (signal, not redaction). Rule of thumb: never put credentials, PII, or internal metadata in `context` — pass identifiers, reference them indirectly.
165
+ - **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks.
166
+ - **Cost + latency.** `usage.cost_cents` is authoritative no markup. Qwen3-Coder-30B: 200–600 ms to first token, 1–3 s end-to-end.
167
+ - **Live catalog, no streaming, no BYOK.** Don't hard-code ids `models` is truth (CLI auto-picks if `--model` omitted). Full reply only.
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.11",
4
+ "version": "1.3.13",
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.11"
34
+ "@myapihq/sdk": "^1.3.12"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^25.6.0",