@runcofactor/cli 0.10.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/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Canary CLI
|
|
2
|
+
|
|
3
|
+
Run tests, query issues, and manage AI agent skill templates from the command line.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @canaryai/cli
|
|
9
|
+
# or
|
|
10
|
+
bun add -g @canaryai/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
> **Rebranding note:** the same CLI is also published as
|
|
14
|
+
> [`@runcofactor/cli`](https://www.npmjs.com/package/@runcofactor/cli), which installs
|
|
15
|
+
> the `cofactor` binary instead of `canary`. Both packages ship the identical
|
|
16
|
+
> build at the same version — install whichever brand you prefer, and substitute
|
|
17
|
+
> `cofactor` for `canary` in the commands below if you installed `@runcofactor/cli`.
|
|
18
|
+
|
|
19
|
+
## Login
|
|
20
|
+
|
|
21
|
+
Authenticate with your Canary account before using the CLI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
canary login
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
- `--org <name>` - Select organization by name or ID (for multi-org users)
|
|
29
|
+
- `--app-url <url>` - Custom app URL (e.g. `http://localhost:5173` for local dev)
|
|
30
|
+
- `--api-url <url>` - Custom API URL (e.g. `http://localhost:3000` for local dev)
|
|
31
|
+
- `--no-open` - Don't auto-open browser
|
|
32
|
+
|
|
33
|
+
To switch environments, set `CANARY_API_URL`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export CANARY_API_URL=http://localhost:3000
|
|
37
|
+
canary login --app-url http://localhost:5173
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Run Tests
|
|
41
|
+
|
|
42
|
+
### Local Playwright tests
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
canary test
|
|
46
|
+
canary test --grep "login" --headed --workers 1
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
All standard Playwright options are passed through.
|
|
50
|
+
|
|
51
|
+
### Remote workflow tests
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
canary test --remote
|
|
55
|
+
canary test --remote --tag smoke
|
|
56
|
+
canary test --remote --property "My App" --environment staging
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Options:
|
|
60
|
+
- `--token <key>` - API key (or set `CANARY_API_TOKEN`)
|
|
61
|
+
- `--api-url <url>` - API URL (default: `https://api.trycanary.ai`)
|
|
62
|
+
- `--property <name|id>` - Target a specific property
|
|
63
|
+
- `--environment <name|id>` - Target a specific environment
|
|
64
|
+
- `--tag <tag>` - Filter workflows by tag
|
|
65
|
+
- `--name-pattern <pat>` - Filter workflows by name pattern
|
|
66
|
+
- `--verbose, -v` - Show all events
|
|
67
|
+
|
|
68
|
+
## Issues
|
|
69
|
+
|
|
70
|
+
Search and inspect QA issues detected by Canary.
|
|
71
|
+
|
|
72
|
+
### List issues
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
canary issues list
|
|
76
|
+
canary issues list --severity high --status open
|
|
77
|
+
canary issues list --search "timeout" --format markdown
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
- `--search <query>` - Full-text search
|
|
82
|
+
- `--severity <level>` - Filter: `low`, `medium`, `high`, `unknown`
|
|
83
|
+
- `--status <statuses>` - Filter: `open`, `closed`, `not_a_bug` (comma-separated)
|
|
84
|
+
- `--property-id <uuid>` - Filter by property
|
|
85
|
+
- `--page <n>` - Page number (default: 1)
|
|
86
|
+
- `--page-size <n>` - Page size (default: 25)
|
|
87
|
+
- `--json` - Output raw JSON
|
|
88
|
+
- `--format markdown` - Output as markdown
|
|
89
|
+
|
|
90
|
+
### Get issue details
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
canary issues get <issue-id>
|
|
94
|
+
canary issues get <issue-id> --format markdown
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## AI Agent Skill Templates
|
|
98
|
+
|
|
99
|
+
Output or install reusable skill templates that teach AI agents (Claude Code, Codex, Copilot, etc.) how to use the Canary CLI:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
canary skill help # list available templates
|
|
103
|
+
canary skill issue-log-xref # output a template to stdout
|
|
104
|
+
canary skill install issue-log-xref # auto-detect agent and install it
|
|
105
|
+
canary skill install issue-log-xref --agent codex # install to .codex/skills/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Release QA (CI/CD)
|
|
109
|
+
|
|
110
|
+
Trigger and monitor Release QA runs from CI pipelines:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
canary release trigger --property-id <uuid>
|
|
114
|
+
canary release status <run-id>
|
|
115
|
+
canary release run --property-id <uuid> --timeout 600
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Environment Variables
|
|
119
|
+
|
|
120
|
+
| Variable | Default | Description |
|
|
121
|
+
|----------|---------|-------------|
|
|
122
|
+
| `CANARY_API_URL` | `https://api.trycanary.ai` | API endpoint |
|
|
123
|
+
| `CANARY_APP_URL` | `https://app.trycanary.ai` | App URL for login |
|
|
124
|
+
| `CANARY_API_TOKEN` | — | API key (alternative to `canary login`) |
|
|
125
|
+
| `CANARY_LOCAL_PORT` | — | Default port for `canary run` / `canary tunnel` |
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
apiRequest
|
|
4
|
+
} from "./chunk-WOHL4FTB.js";
|
|
5
|
+
import "./chunk-SJJW77WN.js";
|
|
6
|
+
import "./chunk-3CZU7XCS.js";
|
|
7
|
+
import {
|
|
8
|
+
getArgValue,
|
|
9
|
+
hasFlag
|
|
10
|
+
} from "./chunk-A4OFTUVV.js";
|
|
11
|
+
import "./chunk-JWHYXEA2.js";
|
|
12
|
+
import "./chunk-VKVL7WBN.js";
|
|
13
|
+
|
|
14
|
+
// src/api-catalog.ts
|
|
15
|
+
import fs from "fs/promises";
|
|
16
|
+
import process from "process";
|
|
17
|
+
function getArg(argv, key) {
|
|
18
|
+
const prefix = `${key}=`;
|
|
19
|
+
const eq = argv.find((a) => a.startsWith(prefix));
|
|
20
|
+
if (eq) return eq.slice(prefix.length);
|
|
21
|
+
return getArgValue(argv, key);
|
|
22
|
+
}
|
|
23
|
+
function wantsJson(argv) {
|
|
24
|
+
if (hasFlag(argv, "--json")) return true;
|
|
25
|
+
if (hasFlag(argv, "--text")) return false;
|
|
26
|
+
return !process.stdout.isTTY;
|
|
27
|
+
}
|
|
28
|
+
function emit(argv, textFallback, json) {
|
|
29
|
+
if (wantsJson(argv)) {
|
|
30
|
+
console.log(JSON.stringify(json, null, 2));
|
|
31
|
+
} else {
|
|
32
|
+
console.log(textFallback);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function readJsonInput(argv) {
|
|
36
|
+
const fromFile = getArg(argv, "--from-file");
|
|
37
|
+
let raw;
|
|
38
|
+
if (fromFile) {
|
|
39
|
+
raw = await fs.readFile(fromFile, "utf-8");
|
|
40
|
+
} else if (hasFlag(argv, "--from-stdin") || !process.stdin.isTTY) {
|
|
41
|
+
raw = await readStdin();
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error("Provide --from-file <path> or pipe JSON on stdin.");
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(raw);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error("Invalid JSON input.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function readStdin() {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
process.stdin.on("data", (chunk) => {
|
|
55
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
56
|
+
});
|
|
57
|
+
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
58
|
+
process.stdin.on("error", reject);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function positional(argv) {
|
|
62
|
+
return argv.filter((a) => !a.startsWith("-"));
|
|
63
|
+
}
|
|
64
|
+
async function runApiCatalog(argv, apiUrl, token) {
|
|
65
|
+
const [group, ...rest] = argv;
|
|
66
|
+
if (!group || group === "help" || hasFlag(argv, "--help", "-h")) {
|
|
67
|
+
printHelp();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
switch (group) {
|
|
71
|
+
case "sources":
|
|
72
|
+
await runSources(rest, apiUrl, token);
|
|
73
|
+
break;
|
|
74
|
+
case "endpoints":
|
|
75
|
+
await runEndpoints(rest, apiUrl, token);
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
console.error(`Unknown group: ${group}`);
|
|
79
|
+
printHelp();
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function printHelp() {
|
|
84
|
+
console.error(
|
|
85
|
+
[
|
|
86
|
+
"Usage: canary api-catalog <sources|endpoints> <verb> [options]",
|
|
87
|
+
"",
|
|
88
|
+
"Sources (OpenAPI / Postman imports):",
|
|
89
|
+
" sources list list catalog sources",
|
|
90
|
+
" sources get <id> fetch a source",
|
|
91
|
+
" sources create --from-file <f> create source + run initial import",
|
|
92
|
+
" sources patch <id> --from-file <f> update source metadata",
|
|
93
|
+
" sources refresh <id> re-sync a source",
|
|
94
|
+
" sources delete <id> delete a source",
|
|
95
|
+
"",
|
|
96
|
+
"Endpoints (individual requests):",
|
|
97
|
+
" endpoints list [--search <q>] [--application <id>] [--method <m>] [--page <n>]",
|
|
98
|
+
" list/search endpoints",
|
|
99
|
+
" endpoints get <id> fetch one endpoint",
|
|
100
|
+
" endpoints create --from-file <f> create a manual endpoint",
|
|
101
|
+
" body: { method, url, propertyId, name? }",
|
|
102
|
+
" endpoints patch <id> --from-file <f> update endpoint fields (name, schemas)",
|
|
103
|
+
" endpoints delete <id> delete one endpoint",
|
|
104
|
+
" endpoints delete-many --ids a,b,c delete many endpoints",
|
|
105
|
+
" endpoints test <id> run the endpoint against its application",
|
|
106
|
+
" endpoints domains list distinct domains",
|
|
107
|
+
"",
|
|
108
|
+
"Global options:",
|
|
109
|
+
" --json force JSON output",
|
|
110
|
+
" --text force pretty-text output",
|
|
111
|
+
"",
|
|
112
|
+
"Input for create/patch: JSON via --from-file or stdin. Field shapes match the API route",
|
|
113
|
+
"schemas (see apps/api/src/api-catalog/*.ts for the authoritative definitions)."
|
|
114
|
+
].join("\n")
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
async function runSources(argv, apiUrl, token) {
|
|
118
|
+
const [verb, ...rest] = argv;
|
|
119
|
+
switch (verb) {
|
|
120
|
+
case "list":
|
|
121
|
+
await sourcesList(rest, apiUrl, token);
|
|
122
|
+
break;
|
|
123
|
+
case "get":
|
|
124
|
+
await sourcesGet(rest, apiUrl, token);
|
|
125
|
+
break;
|
|
126
|
+
case "create":
|
|
127
|
+
await sourcesCreate(rest, apiUrl, token);
|
|
128
|
+
break;
|
|
129
|
+
case "patch":
|
|
130
|
+
await sourcesPatch(rest, apiUrl, token);
|
|
131
|
+
break;
|
|
132
|
+
case "refresh":
|
|
133
|
+
await sourcesRefresh(rest, apiUrl, token);
|
|
134
|
+
break;
|
|
135
|
+
case "delete":
|
|
136
|
+
await sourcesDelete(rest, apiUrl, token);
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
printHelp();
|
|
140
|
+
process.exit(verb ? 1 : 0);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function sourcesList(argv, apiUrl, token) {
|
|
144
|
+
const result = await apiRequest(
|
|
145
|
+
apiUrl,
|
|
146
|
+
token,
|
|
147
|
+
"GET",
|
|
148
|
+
"/api-catalog-sources"
|
|
149
|
+
);
|
|
150
|
+
if (!result.ok) exitErr(result.error);
|
|
151
|
+
const sources = result.data ?? [];
|
|
152
|
+
const text = sources.length === 0 ? "No sources." : sources.map((s) => {
|
|
153
|
+
const id = String(s.id ?? "?");
|
|
154
|
+
const name = String(s.name ?? "(unnamed)");
|
|
155
|
+
const format = String(s.format ?? "?");
|
|
156
|
+
const count = s.lastEndpointCount ?? "-";
|
|
157
|
+
const status = String(s.lastSyncStatus ?? "?");
|
|
158
|
+
return `${id} ${name.padEnd(30)} ${format.padEnd(10)} ${count} endpoints (${status})`;
|
|
159
|
+
}).join("\n");
|
|
160
|
+
emit(argv, text, result);
|
|
161
|
+
}
|
|
162
|
+
async function sourcesGet(argv, apiUrl, token) {
|
|
163
|
+
const [id] = positional(argv);
|
|
164
|
+
if (!id) exitErr("Missing source ID.");
|
|
165
|
+
const result = await apiRequest(
|
|
166
|
+
apiUrl,
|
|
167
|
+
token,
|
|
168
|
+
"GET",
|
|
169
|
+
`/api-catalog-sources/${id}`
|
|
170
|
+
);
|
|
171
|
+
if (!result.ok) exitErr(result.error);
|
|
172
|
+
emit(argv, JSON.stringify(result.data, null, 2), result);
|
|
173
|
+
}
|
|
174
|
+
async function sourcesCreate(argv, apiUrl, token) {
|
|
175
|
+
let body;
|
|
176
|
+
try {
|
|
177
|
+
body = await readJsonInput(argv);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
exitErr(err instanceof Error ? err.message : String(err));
|
|
180
|
+
}
|
|
181
|
+
const result = await apiRequest(
|
|
182
|
+
apiUrl,
|
|
183
|
+
token,
|
|
184
|
+
"POST",
|
|
185
|
+
"/api-catalog-sources",
|
|
186
|
+
body
|
|
187
|
+
);
|
|
188
|
+
if (!result.ok) exitErr(result.error);
|
|
189
|
+
emit(argv, "Source created.", result);
|
|
190
|
+
}
|
|
191
|
+
async function sourcesPatch(argv, apiUrl, token) {
|
|
192
|
+
const [id] = positional(argv);
|
|
193
|
+
if (!id) exitErr("Missing source ID.");
|
|
194
|
+
let body;
|
|
195
|
+
try {
|
|
196
|
+
body = await readJsonInput(argv);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
exitErr(err instanceof Error ? err.message : String(err));
|
|
199
|
+
}
|
|
200
|
+
const result = await apiRequest(
|
|
201
|
+
apiUrl,
|
|
202
|
+
token,
|
|
203
|
+
"PATCH",
|
|
204
|
+
`/api-catalog-sources/${id}`,
|
|
205
|
+
body
|
|
206
|
+
);
|
|
207
|
+
if (!result.ok) exitErr(result.error);
|
|
208
|
+
emit(argv, `Source ${id} patched.`, result);
|
|
209
|
+
}
|
|
210
|
+
async function sourcesRefresh(argv, apiUrl, token) {
|
|
211
|
+
const [id] = positional(argv);
|
|
212
|
+
if (!id) exitErr("Missing source ID.");
|
|
213
|
+
const result = await apiRequest(
|
|
214
|
+
apiUrl,
|
|
215
|
+
token,
|
|
216
|
+
"POST",
|
|
217
|
+
`/api-catalog-sources/${id}/refresh`
|
|
218
|
+
);
|
|
219
|
+
if (!result.ok) exitErr(result.error);
|
|
220
|
+
emit(argv, `Source ${id} refreshed.`, result);
|
|
221
|
+
}
|
|
222
|
+
async function sourcesDelete(argv, apiUrl, token) {
|
|
223
|
+
const [id] = positional(argv);
|
|
224
|
+
if (!id) exitErr("Missing source ID.");
|
|
225
|
+
const result = await apiRequest(
|
|
226
|
+
apiUrl,
|
|
227
|
+
token,
|
|
228
|
+
"DELETE",
|
|
229
|
+
`/api-catalog-sources/${id}`
|
|
230
|
+
);
|
|
231
|
+
if (!result.ok) exitErr(result.error);
|
|
232
|
+
emit(argv, `Source ${id} deleted.`, result);
|
|
233
|
+
}
|
|
234
|
+
async function runEndpoints(argv, apiUrl, token) {
|
|
235
|
+
const [verb, ...rest] = argv;
|
|
236
|
+
switch (verb) {
|
|
237
|
+
case "list":
|
|
238
|
+
await endpointsList(rest, apiUrl, token);
|
|
239
|
+
break;
|
|
240
|
+
case "get":
|
|
241
|
+
await endpointsGet(rest, apiUrl, token);
|
|
242
|
+
break;
|
|
243
|
+
case "create":
|
|
244
|
+
await endpointsCreate(rest, apiUrl, token);
|
|
245
|
+
break;
|
|
246
|
+
case "patch":
|
|
247
|
+
await endpointsPatch(rest, apiUrl, token);
|
|
248
|
+
break;
|
|
249
|
+
case "delete":
|
|
250
|
+
await endpointsDelete(rest, apiUrl, token);
|
|
251
|
+
break;
|
|
252
|
+
case "delete-many":
|
|
253
|
+
await endpointsDeleteMany(rest, apiUrl, token);
|
|
254
|
+
break;
|
|
255
|
+
case "test":
|
|
256
|
+
await endpointsTest(rest, apiUrl, token);
|
|
257
|
+
break;
|
|
258
|
+
case "domains":
|
|
259
|
+
await endpointsDomains(rest, apiUrl, token);
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
262
|
+
printHelp();
|
|
263
|
+
process.exit(verb ? 1 : 0);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function endpointsList(argv, apiUrl, token) {
|
|
267
|
+
const params = new URLSearchParams();
|
|
268
|
+
const search = getArg(argv, "--search");
|
|
269
|
+
const propertyId = getArg(argv, "--application") ?? getArg(argv, "--property");
|
|
270
|
+
const method = getArg(argv, "--method");
|
|
271
|
+
const page = getArg(argv, "--page");
|
|
272
|
+
const pageSize = getArg(argv, "--page-size");
|
|
273
|
+
if (search) params.set("search", search);
|
|
274
|
+
if (propertyId) params.set("propertyId", propertyId);
|
|
275
|
+
if (method) params.set("method", method);
|
|
276
|
+
if (page) params.set("page", page);
|
|
277
|
+
if (pageSize) params.set("pageSize", pageSize);
|
|
278
|
+
const qs = params.toString();
|
|
279
|
+
const result = await apiRequest(
|
|
280
|
+
apiUrl,
|
|
281
|
+
token,
|
|
282
|
+
"GET",
|
|
283
|
+
`/api-requests${qs ? `?${qs}` : ""}`
|
|
284
|
+
);
|
|
285
|
+
if (!result.ok) exitErr(result.error);
|
|
286
|
+
const items = result.data ?? [];
|
|
287
|
+
const text = items.length === 0 ? "No endpoints." : items.map((ep) => {
|
|
288
|
+
const id = String(ep.id ?? "?");
|
|
289
|
+
const method2 = String(ep.method ?? "?").toUpperCase().padEnd(6);
|
|
290
|
+
const url = String(ep.url ?? ep.path ?? "");
|
|
291
|
+
const name = ep.name ? ` \u2014 ${String(ep.name)}` : "";
|
|
292
|
+
return `${id} ${method2} ${url}${name}`;
|
|
293
|
+
}).join("\n");
|
|
294
|
+
emit(argv, text, result);
|
|
295
|
+
}
|
|
296
|
+
async function endpointsGet(argv, apiUrl, token) {
|
|
297
|
+
const [id] = positional(argv);
|
|
298
|
+
if (!id) exitErr("Missing endpoint ID.");
|
|
299
|
+
const result = await apiRequest(
|
|
300
|
+
apiUrl,
|
|
301
|
+
token,
|
|
302
|
+
"GET",
|
|
303
|
+
`/api-requests/${id}`
|
|
304
|
+
);
|
|
305
|
+
if (!result.ok) exitErr(result.error);
|
|
306
|
+
emit(argv, JSON.stringify(result.data, null, 2), result);
|
|
307
|
+
}
|
|
308
|
+
async function endpointsCreate(argv, apiUrl, token) {
|
|
309
|
+
let body;
|
|
310
|
+
try {
|
|
311
|
+
body = await readJsonInput(argv);
|
|
312
|
+
} catch (err) {
|
|
313
|
+
exitErr(err instanceof Error ? err.message : String(err));
|
|
314
|
+
}
|
|
315
|
+
const result = await apiRequest(
|
|
316
|
+
apiUrl,
|
|
317
|
+
token,
|
|
318
|
+
"POST",
|
|
319
|
+
"/api-requests",
|
|
320
|
+
body
|
|
321
|
+
);
|
|
322
|
+
if (!result.ok) exitErr(result.error);
|
|
323
|
+
emit(argv, "Endpoint created.", result);
|
|
324
|
+
}
|
|
325
|
+
async function endpointsPatch(argv, apiUrl, token) {
|
|
326
|
+
const [id] = positional(argv);
|
|
327
|
+
if (!id) exitErr("Missing endpoint ID.");
|
|
328
|
+
let body;
|
|
329
|
+
try {
|
|
330
|
+
body = await readJsonInput(argv);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
exitErr(err instanceof Error ? err.message : String(err));
|
|
333
|
+
}
|
|
334
|
+
const result = await apiRequest(
|
|
335
|
+
apiUrl,
|
|
336
|
+
token,
|
|
337
|
+
"PATCH",
|
|
338
|
+
`/api-requests/${id}`,
|
|
339
|
+
body
|
|
340
|
+
);
|
|
341
|
+
if (!result.ok) exitErr(result.error);
|
|
342
|
+
emit(argv, `Endpoint ${id} patched.`, result);
|
|
343
|
+
}
|
|
344
|
+
async function endpointsDelete(argv, apiUrl, token) {
|
|
345
|
+
const [id] = positional(argv);
|
|
346
|
+
if (!id) exitErr("Missing endpoint ID.");
|
|
347
|
+
const result = await apiRequest(
|
|
348
|
+
apiUrl,
|
|
349
|
+
token,
|
|
350
|
+
"DELETE",
|
|
351
|
+
"/api-requests/bulk",
|
|
352
|
+
{ ids: [id] }
|
|
353
|
+
);
|
|
354
|
+
if (!result.ok) exitErr(result.error);
|
|
355
|
+
emit(argv, `Endpoint ${id} deleted.`, result);
|
|
356
|
+
}
|
|
357
|
+
async function endpointsDeleteMany(argv, apiUrl, token) {
|
|
358
|
+
const idsArg = getArg(argv, "--ids");
|
|
359
|
+
if (!idsArg) exitErr("Missing --ids <a,b,c>.");
|
|
360
|
+
const ids = idsArg.split(",").map((s) => s.trim()).filter(Boolean);
|
|
361
|
+
if (ids.length === 0) exitErr("No IDs provided.");
|
|
362
|
+
const result = await apiRequest(
|
|
363
|
+
apiUrl,
|
|
364
|
+
token,
|
|
365
|
+
"DELETE",
|
|
366
|
+
"/api-requests/bulk",
|
|
367
|
+
{ ids }
|
|
368
|
+
);
|
|
369
|
+
if (!result.ok) exitErr(result.error);
|
|
370
|
+
emit(argv, `Deleted ${ids.length} endpoint(s).`, result);
|
|
371
|
+
}
|
|
372
|
+
async function endpointsTest(argv, apiUrl, token) {
|
|
373
|
+
const [id] = positional(argv);
|
|
374
|
+
if (!id) exitErr("Missing endpoint ID.");
|
|
375
|
+
const result = await apiRequest(
|
|
376
|
+
apiUrl,
|
|
377
|
+
token,
|
|
378
|
+
"POST",
|
|
379
|
+
`/api-requests/${id}/test`
|
|
380
|
+
);
|
|
381
|
+
if (!result.ok) exitErr(result.error);
|
|
382
|
+
emit(argv, JSON.stringify(result.data, null, 2), result);
|
|
383
|
+
}
|
|
384
|
+
async function endpointsDomains(argv, apiUrl, token) {
|
|
385
|
+
const result = await apiRequest(
|
|
386
|
+
apiUrl,
|
|
387
|
+
token,
|
|
388
|
+
"GET",
|
|
389
|
+
"/api-requests/domains"
|
|
390
|
+
);
|
|
391
|
+
if (!result.ok) exitErr(result.error);
|
|
392
|
+
emit(argv, JSON.stringify(result.data, null, 2), result);
|
|
393
|
+
}
|
|
394
|
+
function exitErr(msg) {
|
|
395
|
+
console.error(`Error: ${msg ?? "Unknown error"}`);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
export {
|
|
399
|
+
runApiCatalog
|
|
400
|
+
};
|
|
401
|
+
//# sourceMappingURL=api-catalog-GFGCLVS4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-catalog.ts"],"sourcesContent":["/**\n * `canary api-catalog <sources|endpoints> <verb>` — CRUD for API catalog\n * sources (OpenAPI/Postman imports) and endpoints (aka api_requests).\n *\n * Lets a coding agent seed a catalog from scratch, search for endpoints to\n * wire into API sequences, and curate existing entries.\n *\n * @module ApiCatalogCLI\n */\n\nimport fs from 'node:fs/promises';\nimport process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\n\n/* ── Arg helpers ───────────────────────────────────────────────────────── */\n\nfunction getArg(argv: string[], key: string): string | undefined {\n const prefix = `${key}=`;\n const eq = argv.find((a) => a.startsWith(prefix));\n if (eq) return eq.slice(prefix.length);\n return getArgValue(argv, key);\n}\n\nfunction wantsJson(argv: string[]): boolean {\n if (hasFlag(argv, '--json')) return true;\n if (hasFlag(argv, '--text')) return false;\n return !process.stdout.isTTY;\n}\n\nfunction emit(argv: string[], textFallback: string, json: unknown): void {\n if (wantsJson(argv)) {\n console.log(JSON.stringify(json, null, 2));\n } else {\n console.log(textFallback);\n }\n}\n\nasync function readJsonInput(argv: string[]): Promise<unknown> {\n const fromFile = getArg(argv, '--from-file');\n let raw: string;\n if (fromFile) {\n raw = await fs.readFile(fromFile, 'utf-8');\n } else if (hasFlag(argv, '--from-stdin') || !process.stdin.isTTY) {\n raw = await readStdin();\n } else {\n throw new Error('Provide --from-file <path> or pipe JSON on stdin.');\n }\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error('Invalid JSON input.');\n }\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', (chunk) => {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n });\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n process.stdin.on('error', reject);\n });\n}\n\nfunction positional(argv: string[]): string[] {\n return argv.filter((a) => !a.startsWith('-'));\n}\n\n/* ── Types ─────────────────────────────────────────────────────────────── */\n\ninterface GenericResponse<T = unknown> {\n ok: boolean;\n error?: string;\n data?: T;\n pagination?: unknown;\n filters?: unknown;\n meta?: unknown;\n}\n\n/* ── Entry point ───────────────────────────────────────────────────────── */\n\nexport async function runApiCatalog(\n argv: string[],\n apiUrl: string,\n token: string,\n): Promise<void> {\n const [group, ...rest] = argv;\n\n if (!group || group === 'help' || hasFlag(argv, '--help', '-h')) {\n printHelp();\n return;\n }\n\n switch (group) {\n case 'sources':\n await runSources(rest, apiUrl, token);\n break;\n case 'endpoints':\n await runEndpoints(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown group: ${group}`);\n printHelp();\n process.exit(1);\n }\n}\n\nfunction printHelp() {\n console.error(\n [\n 'Usage: canary api-catalog <sources|endpoints> <verb> [options]',\n '',\n 'Sources (OpenAPI / Postman imports):',\n ' sources list list catalog sources',\n ' sources get <id> fetch a source',\n ' sources create --from-file <f> create source + run initial import',\n ' sources patch <id> --from-file <f> update source metadata',\n ' sources refresh <id> re-sync a source',\n ' sources delete <id> delete a source',\n '',\n 'Endpoints (individual requests):',\n ' endpoints list [--search <q>] [--application <id>] [--method <m>] [--page <n>]',\n ' list/search endpoints',\n ' endpoints get <id> fetch one endpoint',\n ' endpoints create --from-file <f> create a manual endpoint',\n ' body: { method, url, propertyId, name? }',\n ' endpoints patch <id> --from-file <f> update endpoint fields (name, schemas)',\n ' endpoints delete <id> delete one endpoint',\n ' endpoints delete-many --ids a,b,c delete many endpoints',\n ' endpoints test <id> run the endpoint against its application',\n ' endpoints domains list distinct domains',\n '',\n 'Global options:',\n ' --json force JSON output',\n ' --text force pretty-text output',\n '',\n 'Input for create/patch: JSON via --from-file or stdin. Field shapes match the API route',\n 'schemas (see apps/api/src/api-catalog/*.ts for the authoritative definitions).',\n ].join('\\n'),\n );\n}\n\n/* ── Sources ───────────────────────────────────────────────────────────── */\n\nasync function runSources(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [verb, ...rest] = argv;\n switch (verb) {\n case 'list':\n await sourcesList(rest, apiUrl, token);\n break;\n case 'get':\n await sourcesGet(rest, apiUrl, token);\n break;\n case 'create':\n await sourcesCreate(rest, apiUrl, token);\n break;\n case 'patch':\n await sourcesPatch(rest, apiUrl, token);\n break;\n case 'refresh':\n await sourcesRefresh(rest, apiUrl, token);\n break;\n case 'delete':\n await sourcesDelete(rest, apiUrl, token);\n break;\n default:\n printHelp();\n process.exit(verb ? 1 : 0);\n }\n}\n\nasync function sourcesList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const result = await apiRequest<GenericResponse<unknown[]>>(\n apiUrl,\n token,\n 'GET',\n '/api-catalog-sources',\n );\n if (!result.ok) exitErr(result.error);\n const sources = (result.data ?? []) as Array<Record<string, unknown>>;\n const text =\n sources.length === 0\n ? 'No sources.'\n : sources\n .map((s) => {\n const id = String(s.id ?? '?');\n const name = String(s.name ?? '(unnamed)');\n const format = String(s.format ?? '?');\n const count = s.lastEndpointCount ?? '-';\n const status = String(s.lastSyncStatus ?? '?');\n return `${id} ${name.padEnd(30)} ${format.padEnd(10)} ${count} endpoints (${status})`;\n })\n .join('\\n');\n emit(argv, text, result);\n}\n\nasync function sourcesGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing source ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'GET',\n `/api-catalog-sources/${id}`,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, JSON.stringify(result.data, null, 2), result);\n}\n\nasync function sourcesCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonInput(argv);\n } catch (err) {\n exitErr(err instanceof Error ? err.message : String(err));\n }\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'POST',\n '/api-catalog-sources',\n body as Record<string, unknown>,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, 'Source created.', result);\n}\n\nasync function sourcesPatch(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing source ID.');\n let body: unknown;\n try {\n body = await readJsonInput(argv);\n } catch (err) {\n exitErr(err instanceof Error ? err.message : String(err));\n }\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'PATCH',\n `/api-catalog-sources/${id}`,\n body as Record<string, unknown>,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Source ${id} patched.`, result);\n}\n\nasync function sourcesRefresh(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing source ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'POST',\n `/api-catalog-sources/${id}/refresh`,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Source ${id} refreshed.`, result);\n}\n\nasync function sourcesDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing source ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'DELETE',\n `/api-catalog-sources/${id}`,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Source ${id} deleted.`, result);\n}\n\n/* ── Endpoints ─────────────────────────────────────────────────────────── */\n\nasync function runEndpoints(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [verb, ...rest] = argv;\n switch (verb) {\n case 'list':\n await endpointsList(rest, apiUrl, token);\n break;\n case 'get':\n await endpointsGet(rest, apiUrl, token);\n break;\n case 'create':\n await endpointsCreate(rest, apiUrl, token);\n break;\n case 'patch':\n await endpointsPatch(rest, apiUrl, token);\n break;\n case 'delete':\n await endpointsDelete(rest, apiUrl, token);\n break;\n case 'delete-many':\n await endpointsDeleteMany(rest, apiUrl, token);\n break;\n case 'test':\n await endpointsTest(rest, apiUrl, token);\n break;\n case 'domains':\n await endpointsDomains(rest, apiUrl, token);\n break;\n default:\n printHelp();\n process.exit(verb ? 1 : 0);\n }\n}\n\nasync function endpointsList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const params = new URLSearchParams();\n const search = getArg(argv, '--search');\n const propertyId = getArg(argv, '--application') ?? getArg(argv, '--property');\n const method = getArg(argv, '--method');\n const page = getArg(argv, '--page');\n const pageSize = getArg(argv, '--page-size');\n if (search) params.set('search', search);\n if (propertyId) params.set('propertyId', propertyId);\n if (method) params.set('method', method);\n if (page) params.set('page', page);\n if (pageSize) params.set('pageSize', pageSize);\n\n const qs = params.toString();\n const result = await apiRequest<GenericResponse<Array<Record<string, unknown>>>>(\n apiUrl,\n token,\n 'GET',\n `/api-requests${qs ? `?${qs}` : ''}`,\n );\n if (!result.ok) exitErr(result.error);\n\n const items = (result.data ?? []) as Array<Record<string, unknown>>;\n const text =\n items.length === 0\n ? 'No endpoints.'\n : items\n .map((ep) => {\n const id = String(ep.id ?? '?');\n const method = String(ep.method ?? '?').toUpperCase().padEnd(6);\n const url = String(ep.url ?? ep.path ?? '');\n const name = ep.name ? ` — ${String(ep.name)}` : '';\n return `${id} ${method} ${url}${name}`;\n })\n .join('\\n');\n emit(argv, text, result);\n}\n\nasync function endpointsGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing endpoint ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'GET',\n `/api-requests/${id}`,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, JSON.stringify(result.data, null, 2), result);\n}\n\nasync function endpointsCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonInput(argv);\n } catch (err) {\n exitErr(err instanceof Error ? err.message : String(err));\n }\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'POST',\n '/api-requests',\n body as Record<string, unknown>,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, 'Endpoint created.', result);\n}\n\nasync function endpointsPatch(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing endpoint ID.');\n let body: unknown;\n try {\n body = await readJsonInput(argv);\n } catch (err) {\n exitErr(err instanceof Error ? err.message : String(err));\n }\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'PATCH',\n `/api-requests/${id}`,\n body as Record<string, unknown>,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Endpoint ${id} patched.`, result);\n}\n\nasync function endpointsDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing endpoint ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'DELETE',\n '/api-requests/bulk',\n { ids: [id] },\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Endpoint ${id} deleted.`, result);\n}\n\nasync function endpointsDeleteMany(\n argv: string[],\n apiUrl: string,\n token: string,\n): Promise<void> {\n const idsArg = getArg(argv, '--ids');\n if (!idsArg) exitErr('Missing --ids <a,b,c>.');\n const ids = idsArg\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n if (ids.length === 0) exitErr('No IDs provided.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'DELETE',\n '/api-requests/bulk',\n { ids },\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, `Deleted ${ids.length} endpoint(s).`, result);\n}\n\nasync function endpointsTest(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = positional(argv);\n if (!id) exitErr('Missing endpoint ID.');\n const result = await apiRequest<GenericResponse>(\n apiUrl,\n token,\n 'POST',\n `/api-requests/${id}/test`,\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, JSON.stringify(result.data, null, 2), result);\n}\n\nasync function endpointsDomains(argv: string[], apiUrl: string, token: string): Promise<void> {\n const result = await apiRequest<GenericResponse<unknown>>(\n apiUrl,\n token,\n 'GET',\n '/api-requests/domains',\n );\n if (!result.ok) exitErr(result.error);\n emit(argv, JSON.stringify(result.data, null, 2), result);\n}\n\n/* ── Error helper ──────────────────────────────────────────────────────── */\n\nfunction exitErr(msg: string | undefined): never {\n console.error(`Error: ${msg ?? 'Unknown error'}`);\n process.exit(1);\n}\n"],"mappings":"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","names":["method"]}
|