@picahq/cli 1.3.0 → 1.5.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 +0 -47
- package/dist/index.js +134 -439
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,45 +96,6 @@ pica platforms
|
|
|
96
96
|
pica platforms -c "CRM"
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
### Search actions
|
|
100
|
-
|
|
101
|
-
Find available API actions on any connected platform:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
pica search gmail "send email"
|
|
105
|
-
pica search slack "post message"
|
|
106
|
-
pica search stripe "list payments"
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
POST /gmail/v1/users/{{userId}}/messages/send
|
|
111
|
-
Send Message
|
|
112
|
-
conn_mod_def::ABC123::XYZ789
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Read API docs
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
pica actions knowledge <actionId>
|
|
119
|
-
pica actions k <actionId> --full # no truncation
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Shows method, path, path variables, parameter schemas, and request/response examples.
|
|
123
|
-
|
|
124
|
-
### Execute an action
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
pica exec <actionId> \
|
|
128
|
-
-c live::gmail::default::abc123 \
|
|
129
|
-
-d '{"to": "test@example.com", "subject": "Hello", "body": "Hi there"}' \
|
|
130
|
-
-p userId=me
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
If you omit flags, the CLI prompts interactively:
|
|
134
|
-
- Auto-selects the connection if only one exists for the platform
|
|
135
|
-
- Prompts for each `{{path variable}}` not provided via `-p`
|
|
136
|
-
- Prompts for the request body on POST/PUT/PATCH if `-d` is missing
|
|
137
|
-
|
|
138
99
|
## Commands
|
|
139
100
|
|
|
140
101
|
| Command | Description |
|
|
@@ -143,9 +104,6 @@ If you omit flags, the CLI prompts interactively:
|
|
|
143
104
|
| `pica add <platform>` | Connect a platform via OAuth |
|
|
144
105
|
| `pica list` | List connections with keys |
|
|
145
106
|
| `pica platforms` | Browse available platforms |
|
|
146
|
-
| `pica search <platform> [query]` | Search for actions |
|
|
147
|
-
| `pica actions knowledge <id>` | Get API docs for an action |
|
|
148
|
-
| `pica exec <id>` | Execute an action |
|
|
149
107
|
|
|
150
108
|
Every command supports `--json` for machine-readable output.
|
|
151
109
|
|
|
@@ -155,9 +113,6 @@ Every command supports `--json` for machine-readable output.
|
|
|
155
113
|
|-------|------|
|
|
156
114
|
| `pica ls` | `pica list` |
|
|
157
115
|
| `pica p` | `pica platforms` |
|
|
158
|
-
| `pica a search` | `pica actions search` |
|
|
159
|
-
| `pica a k` | `pica actions knowledge` |
|
|
160
|
-
| `pica a x` | `pica actions execute` |
|
|
161
116
|
|
|
162
117
|
## How it works
|
|
163
118
|
|
|
@@ -193,11 +148,9 @@ src/
|
|
|
193
148
|
init.ts # pica init (setup, status display, targeted actions)
|
|
194
149
|
connection.ts # pica add, pica list
|
|
195
150
|
platforms.ts # pica platforms
|
|
196
|
-
actions.ts # pica search, actions knowledge, exec
|
|
197
151
|
lib/
|
|
198
152
|
api.ts # HTTP client for Pica API
|
|
199
153
|
types.ts # TypeScript interfaces
|
|
200
|
-
actions.ts # Action ID normalization, path variable helpers
|
|
201
154
|
config.ts # ~/.pica/config.json read/write
|
|
202
155
|
agents.ts # Agent detection, MCP config, status reporting
|
|
203
156
|
platforms.ts # Platform search and fuzzy matching
|
package/dist/index.js
CHANGED
|
@@ -46,11 +46,11 @@ function getApiKey() {
|
|
|
46
46
|
import fs2 from "fs";
|
|
47
47
|
import path2 from "path";
|
|
48
48
|
import os2 from "os";
|
|
49
|
-
function expandPath(
|
|
50
|
-
if (
|
|
51
|
-
return path2.join(os2.homedir(),
|
|
49
|
+
function expandPath(p4) {
|
|
50
|
+
if (p4.startsWith("~/")) {
|
|
51
|
+
return path2.join(os2.homedir(), p4.slice(2));
|
|
52
52
|
}
|
|
53
|
-
return
|
|
53
|
+
return p4;
|
|
54
54
|
}
|
|
55
55
|
function getClaudeDesktopConfigPath() {
|
|
56
56
|
switch (process.platform) {
|
|
@@ -186,40 +186,6 @@ function getAgentStatuses() {
|
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
// src/lib/actions.ts
|
|
190
|
-
var ACTION_ID_PREFIX = "conn_mod_def::";
|
|
191
|
-
function normalizeActionId(id) {
|
|
192
|
-
if (id.startsWith(ACTION_ID_PREFIX)) return id;
|
|
193
|
-
return `${ACTION_ID_PREFIX}${id}`;
|
|
194
|
-
}
|
|
195
|
-
function extractPathVariables(path3) {
|
|
196
|
-
const matches = path3.match(/\{\{(\w+)\}\}/g);
|
|
197
|
-
if (!matches) return [];
|
|
198
|
-
return matches.map((m) => m.replace(/\{\{|\}\}/g, ""));
|
|
199
|
-
}
|
|
200
|
-
function replacePathVariables(path3, vars) {
|
|
201
|
-
let result = path3;
|
|
202
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
203
|
-
result = result.replace(`{{${key}}}`, encodeURIComponent(value));
|
|
204
|
-
}
|
|
205
|
-
return result;
|
|
206
|
-
}
|
|
207
|
-
function resolveTemplateVariables(path3, data, pathVars) {
|
|
208
|
-
const variables = extractPathVariables(path3);
|
|
209
|
-
const merged = { ...pathVars };
|
|
210
|
-
const remaining = { ...data };
|
|
211
|
-
for (const v of variables) {
|
|
212
|
-
if (!merged[v] && data[v] != null) {
|
|
213
|
-
merged[v] = String(data[v]);
|
|
214
|
-
delete remaining[v];
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
resolvedPath: replacePathVariables(path3, merged),
|
|
219
|
-
remainingData: remaining
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
189
|
// src/lib/api.ts
|
|
224
190
|
var API_BASE = "https://api.picaos.com/v1";
|
|
225
191
|
var ApiError = class extends Error {
|
|
@@ -256,8 +222,8 @@ var PicaApi = class {
|
|
|
256
222
|
}
|
|
257
223
|
const response = await fetch(url, fetchOpts);
|
|
258
224
|
if (!response.ok) {
|
|
259
|
-
const
|
|
260
|
-
throw new ApiError(response.status,
|
|
225
|
+
const text3 = await response.text();
|
|
226
|
+
throw new ApiError(response.status, text3 || `HTTP ${response.status}`);
|
|
261
227
|
}
|
|
262
228
|
return response.json();
|
|
263
229
|
}
|
|
@@ -288,45 +254,6 @@ var PicaApi = class {
|
|
|
288
254
|
} while (page <= totalPages);
|
|
289
255
|
return allPlatforms;
|
|
290
256
|
}
|
|
291
|
-
async searchActions(platform, query, limit = 10) {
|
|
292
|
-
const queryParams = {
|
|
293
|
-
limit: String(limit),
|
|
294
|
-
executeAgent: "true"
|
|
295
|
-
};
|
|
296
|
-
if (query) queryParams.query = query;
|
|
297
|
-
const response = await this.requestFull({
|
|
298
|
-
path: `/available-actions/search/${encodeURIComponent(platform)}`,
|
|
299
|
-
queryParams
|
|
300
|
-
});
|
|
301
|
-
return response.rows || [];
|
|
302
|
-
}
|
|
303
|
-
async getActionKnowledge(actionId) {
|
|
304
|
-
const normalized = normalizeActionId(actionId);
|
|
305
|
-
const response = await this.requestFull({
|
|
306
|
-
path: "/knowledge",
|
|
307
|
-
queryParams: { _id: normalized }
|
|
308
|
-
});
|
|
309
|
-
return response.rows?.[0] ?? null;
|
|
310
|
-
}
|
|
311
|
-
async executeAction(opts) {
|
|
312
|
-
const headers = {
|
|
313
|
-
"x-pica-connection-key": opts.connectionKey,
|
|
314
|
-
"x-pica-action-id": normalizeActionId(opts.actionId),
|
|
315
|
-
...opts.headers
|
|
316
|
-
};
|
|
317
|
-
if (opts.isFormData) {
|
|
318
|
-
headers["Content-Type"] = "multipart/form-data";
|
|
319
|
-
} else if (opts.isFormUrlEncoded) {
|
|
320
|
-
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
321
|
-
}
|
|
322
|
-
return this.requestFull({
|
|
323
|
-
path: `/passthrough${opts.path}`,
|
|
324
|
-
method: opts.method.toUpperCase(),
|
|
325
|
-
body: opts.data,
|
|
326
|
-
headers,
|
|
327
|
-
queryParams: opts.queryParams
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
257
|
async waitForConnection(platform, timeoutMs = 5 * 60 * 1e3, pollIntervalMs = 5e3, onPoll) {
|
|
331
258
|
const startTime = Date.now();
|
|
332
259
|
const existingConnections = await this.listConnections();
|
|
@@ -372,6 +299,9 @@ async function openApiKeyPage() {
|
|
|
372
299
|
await open(getApiKeyUrl());
|
|
373
300
|
}
|
|
374
301
|
|
|
302
|
+
// src/commands/init.ts
|
|
303
|
+
import open2 from "open";
|
|
304
|
+
|
|
375
305
|
// src/lib/table.ts
|
|
376
306
|
import pc from "picocolors";
|
|
377
307
|
function printTable(columns, rows) {
|
|
@@ -410,12 +340,13 @@ function stripAnsi(str) {
|
|
|
410
340
|
|
|
411
341
|
// src/commands/init.ts
|
|
412
342
|
async function initCommand(options) {
|
|
413
|
-
p.intro(pc2.bgCyan(pc2.black(" Pica ")));
|
|
414
343
|
const existingConfig = readConfig();
|
|
415
344
|
if (existingConfig) {
|
|
345
|
+
p.intro(pc2.bgCyan(pc2.black(" Pica ")));
|
|
416
346
|
await handleExistingConfig(existingConfig.apiKey, options);
|
|
417
347
|
return;
|
|
418
348
|
}
|
|
349
|
+
printBanner();
|
|
419
350
|
await freshSetup(options);
|
|
420
351
|
}
|
|
421
352
|
async function handleExistingConfig(apiKey, options) {
|
|
@@ -521,16 +452,16 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
521
452
|
p.cancel("Cancelled.");
|
|
522
453
|
process.exit(0);
|
|
523
454
|
}
|
|
524
|
-
const
|
|
525
|
-
|
|
455
|
+
const spinner4 = p.spinner();
|
|
456
|
+
spinner4.start("Validating API key...");
|
|
526
457
|
const api = new PicaApi(newKey);
|
|
527
458
|
const isValid = await api.validateApiKey();
|
|
528
459
|
if (!isValid) {
|
|
529
|
-
|
|
460
|
+
spinner4.stop("Invalid API key");
|
|
530
461
|
p.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
|
|
531
462
|
process.exit(1);
|
|
532
463
|
}
|
|
533
|
-
|
|
464
|
+
spinner4.stop("API key validated");
|
|
534
465
|
const reinstalled = [];
|
|
535
466
|
for (const s of statuses) {
|
|
536
467
|
if (s.globalMcp) {
|
|
@@ -662,16 +593,16 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
662
593
|
p.cancel("Setup cancelled.");
|
|
663
594
|
process.exit(0);
|
|
664
595
|
}
|
|
665
|
-
const
|
|
666
|
-
|
|
596
|
+
const spinner4 = p.spinner();
|
|
597
|
+
spinner4.start("Validating API key...");
|
|
667
598
|
const api = new PicaApi(apiKey);
|
|
668
599
|
const isValid = await api.validateApiKey();
|
|
669
600
|
if (!isValid) {
|
|
670
|
-
|
|
601
|
+
spinner4.stop("Invalid API key");
|
|
671
602
|
p.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
|
|
672
603
|
process.exit(1);
|
|
673
604
|
}
|
|
674
|
-
|
|
605
|
+
spinner4.stop("API key validated");
|
|
675
606
|
writeConfig({
|
|
676
607
|
apiKey,
|
|
677
608
|
installedAgents: [],
|
|
@@ -773,11 +704,10 @@ ${globalPaths}
|
|
|
773
704
|
|
|
774
705
|
`;
|
|
775
706
|
}
|
|
776
|
-
summary += pc2.yellow("Note: Project config files can be committed to share with your team.\n") + pc2.yellow("Team members will need their own API key
|
|
777
|
-
${pc2.cyan("pica add gmail")} - Connect Gmail
|
|
778
|
-
${pc2.cyan("pica platforms")} - See all 200+ integrations`;
|
|
707
|
+
summary += pc2.yellow("Note: Project config files can be committed to share with your team.\n") + pc2.yellow("Team members will need their own API key.");
|
|
779
708
|
p.note(summary, "Setup Complete");
|
|
780
|
-
|
|
709
|
+
await promptConnectIntegrations(apiKey);
|
|
710
|
+
p.outro("Your AI agents now have access to Pica integrations!");
|
|
781
711
|
return;
|
|
782
712
|
}
|
|
783
713
|
const installedAgentIds = [];
|
|
@@ -794,16 +724,105 @@ ${globalPaths}
|
|
|
794
724
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
795
725
|
});
|
|
796
726
|
p.note(
|
|
797
|
-
`Config saved to: ${pc2.dim(getConfigPath())}
|
|
798
|
-
|
|
799
|
-
Next steps:
|
|
800
|
-
${pc2.cyan("pica add gmail")} - Connect Gmail
|
|
801
|
-
${pc2.cyan("pica platforms")} - See all 200+ integrations
|
|
802
|
-
${pc2.cyan("pica connection list")} - View your connections`,
|
|
727
|
+
`Config saved to: ${pc2.dim(getConfigPath())}`,
|
|
803
728
|
"Setup Complete"
|
|
804
729
|
);
|
|
730
|
+
await promptConnectIntegrations(apiKey);
|
|
805
731
|
p.outro("Your AI agents now have access to Pica integrations!");
|
|
806
732
|
}
|
|
733
|
+
function printBanner() {
|
|
734
|
+
console.log();
|
|
735
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588"));
|
|
736
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588"));
|
|
737
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
738
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
739
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588"));
|
|
740
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588"));
|
|
741
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
742
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
743
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
744
|
+
console.log(pc2.cyan(" \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588"));
|
|
745
|
+
console.log();
|
|
746
|
+
console.log(pc2.dim(" U N I V E R S A L I N T E G R A T I O N S F O R A I"));
|
|
747
|
+
console.log();
|
|
748
|
+
}
|
|
749
|
+
var TOP_INTEGRATIONS = [
|
|
750
|
+
{ value: "gmail", label: "Gmail", hint: "Read and send emails" },
|
|
751
|
+
{ value: "google-calendar", label: "Google Calendar", hint: "Manage events and schedules" },
|
|
752
|
+
{ value: "notion", label: "Notion", hint: "Access pages, databases, and docs" }
|
|
753
|
+
];
|
|
754
|
+
async function promptConnectIntegrations(apiKey) {
|
|
755
|
+
const api = new PicaApi(apiKey);
|
|
756
|
+
const connected = [];
|
|
757
|
+
try {
|
|
758
|
+
const existing = await api.listConnections();
|
|
759
|
+
for (const conn of existing) {
|
|
760
|
+
const match = TOP_INTEGRATIONS.find(
|
|
761
|
+
(i) => i.value === conn.platform.toLowerCase()
|
|
762
|
+
);
|
|
763
|
+
if (match) connected.push(match.value);
|
|
764
|
+
}
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
let first = true;
|
|
768
|
+
while (true) {
|
|
769
|
+
const available = TOP_INTEGRATIONS.filter((i) => !connected.includes(i.value));
|
|
770
|
+
const options = [
|
|
771
|
+
...available.map((i) => ({
|
|
772
|
+
value: i.value,
|
|
773
|
+
label: i.label,
|
|
774
|
+
hint: i.hint
|
|
775
|
+
})),
|
|
776
|
+
{ value: "more", label: "Browse all 200+ platforms" },
|
|
777
|
+
{ value: "skip", label: "Skip for now", hint: "you can always run pica add later" }
|
|
778
|
+
];
|
|
779
|
+
const message = first ? "Connect your first integration?" : "Connect another?";
|
|
780
|
+
const choice = await p.select({ message, options });
|
|
781
|
+
if (p.isCancel(choice) || choice === "skip") {
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
if (choice === "more") {
|
|
785
|
+
try {
|
|
786
|
+
await open2("https://app.picaos.com/connections");
|
|
787
|
+
p.log.info("Opened Pica dashboard in browser.");
|
|
788
|
+
} catch {
|
|
789
|
+
p.note("https://app.picaos.com/connections", "Open in browser");
|
|
790
|
+
}
|
|
791
|
+
p.log.info(`Connect from the dashboard, or use ${pc2.cyan("pica add <platform>")}`);
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
const platform = choice;
|
|
795
|
+
const integration = TOP_INTEGRATIONS.find((i) => i.value === platform);
|
|
796
|
+
const label = integration?.label ?? platform;
|
|
797
|
+
p.log.info(`Opening browser to connect ${pc2.cyan(label)}...`);
|
|
798
|
+
try {
|
|
799
|
+
await openConnectionPage(platform);
|
|
800
|
+
} catch {
|
|
801
|
+
const url = getConnectionUrl(platform);
|
|
802
|
+
p.log.warn("Could not open browser automatically.");
|
|
803
|
+
p.note(url, "Open manually");
|
|
804
|
+
}
|
|
805
|
+
const spinner4 = p.spinner();
|
|
806
|
+
spinner4.start("Waiting for connection... (complete auth in browser)");
|
|
807
|
+
try {
|
|
808
|
+
await api.waitForConnection(platform, 5 * 60 * 1e3, 5e3);
|
|
809
|
+
spinner4.stop(`${label} connected!`);
|
|
810
|
+
p.log.success(`${pc2.green("\u2713")} ${label} is now available to your AI agents`);
|
|
811
|
+
connected.push(platform);
|
|
812
|
+
first = false;
|
|
813
|
+
} catch (error) {
|
|
814
|
+
spinner4.stop("Connection timed out");
|
|
815
|
+
if (error instanceof TimeoutError) {
|
|
816
|
+
p.log.warn(`No worries. Connect later with: ${pc2.cyan(`pica add ${platform}`)}`);
|
|
817
|
+
}
|
|
818
|
+
first = false;
|
|
819
|
+
}
|
|
820
|
+
if (TOP_INTEGRATIONS.every((i) => connected.includes(i.value))) {
|
|
821
|
+
p.log.success("All top integrations connected!");
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
807
826
|
function maskApiKey(key) {
|
|
808
827
|
if (key.length <= 12) return key.slice(0, 8) + "...";
|
|
809
828
|
return key.slice(0, 8) + "..." + key.slice(-4);
|
|
@@ -825,16 +844,16 @@ import pc3 from "picocolors";
|
|
|
825
844
|
function findPlatform(platforms, query) {
|
|
826
845
|
const normalizedQuery = query.toLowerCase().trim();
|
|
827
846
|
const exact = platforms.find(
|
|
828
|
-
(
|
|
847
|
+
(p4) => p4.platform.toLowerCase() === normalizedQuery || p4.name.toLowerCase() === normalizedQuery
|
|
829
848
|
);
|
|
830
849
|
if (exact) return exact;
|
|
831
850
|
return null;
|
|
832
851
|
}
|
|
833
852
|
function findSimilarPlatforms(platforms, query, limit = 3) {
|
|
834
853
|
const normalizedQuery = query.toLowerCase().trim();
|
|
835
|
-
const scored = platforms.map((
|
|
836
|
-
const name =
|
|
837
|
-
const slug =
|
|
854
|
+
const scored = platforms.map((p4) => {
|
|
855
|
+
const name = p4.name.toLowerCase();
|
|
856
|
+
const slug = p4.platform.toLowerCase();
|
|
838
857
|
let score = 0;
|
|
839
858
|
if (name.includes(normalizedQuery) || slug.includes(normalizedQuery)) {
|
|
840
859
|
score = 10;
|
|
@@ -843,7 +862,7 @@ function findSimilarPlatforms(platforms, query, limit = 3) {
|
|
|
843
862
|
} else {
|
|
844
863
|
score = countMatchingChars(normalizedQuery, slug);
|
|
845
864
|
}
|
|
846
|
-
return { platform:
|
|
865
|
+
return { platform: p4, score };
|
|
847
866
|
}).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
848
867
|
return scored.map((item) => item.platform);
|
|
849
868
|
}
|
|
@@ -865,14 +884,14 @@ async function connectionAddCommand(platformArg) {
|
|
|
865
884
|
process.exit(1);
|
|
866
885
|
}
|
|
867
886
|
const api = new PicaApi(apiKey);
|
|
868
|
-
const
|
|
869
|
-
|
|
887
|
+
const spinner4 = p2.spinner();
|
|
888
|
+
spinner4.start("Loading platforms...");
|
|
870
889
|
let platforms;
|
|
871
890
|
try {
|
|
872
891
|
platforms = await api.listPlatforms();
|
|
873
|
-
|
|
892
|
+
spinner4.stop(`${platforms.length} platforms available`);
|
|
874
893
|
} catch (error) {
|
|
875
|
-
|
|
894
|
+
spinner4.stop("Failed to load platforms");
|
|
876
895
|
p2.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
877
896
|
process.exit(1);
|
|
878
897
|
}
|
|
@@ -970,11 +989,11 @@ async function connectionListCommand() {
|
|
|
970
989
|
process.exit(1);
|
|
971
990
|
}
|
|
972
991
|
const api = new PicaApi(apiKey);
|
|
973
|
-
const
|
|
974
|
-
|
|
992
|
+
const spinner4 = p2.spinner();
|
|
993
|
+
spinner4.start("Loading connections...");
|
|
975
994
|
try {
|
|
976
995
|
const connections = await api.listConnections();
|
|
977
|
-
|
|
996
|
+
spinner4.stop(`${connections.length} connection${connections.length === 1 ? "" : "s"} found`);
|
|
978
997
|
if (connections.length === 0) {
|
|
979
998
|
p2.note(
|
|
980
999
|
`No connections yet.
|
|
@@ -1003,7 +1022,7 @@ Add one with: ${pc3.cyan("pica connection add gmail")}`,
|
|
|
1003
1022
|
console.log();
|
|
1004
1023
|
p2.note(`Add more with: ${pc3.cyan("pica connection add <platform>")}`, "Tip");
|
|
1005
1024
|
} catch (error) {
|
|
1006
|
-
|
|
1025
|
+
spinner4.stop("Failed to load connections");
|
|
1007
1026
|
p2.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1008
1027
|
process.exit(1);
|
|
1009
1028
|
}
|
|
@@ -1031,11 +1050,11 @@ async function platformsCommand(options) {
|
|
|
1031
1050
|
process.exit(1);
|
|
1032
1051
|
}
|
|
1033
1052
|
const api = new PicaApi(apiKey);
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1053
|
+
const spinner4 = p3.spinner();
|
|
1054
|
+
spinner4.start("Loading platforms...");
|
|
1036
1055
|
try {
|
|
1037
1056
|
const platforms = await api.listPlatforms();
|
|
1038
|
-
|
|
1057
|
+
spinner4.stop(`${platforms.length} platforms available`);
|
|
1039
1058
|
if (options.json) {
|
|
1040
1059
|
console.log(JSON.stringify(platforms, null, 2));
|
|
1041
1060
|
return;
|
|
@@ -1087,317 +1106,12 @@ async function platformsCommand(options) {
|
|
|
1087
1106
|
console.log();
|
|
1088
1107
|
p3.note(`Connect with: ${pc4.cyan("pica connection add <platform>")}`, "Tip");
|
|
1089
1108
|
} catch (error) {
|
|
1090
|
-
|
|
1109
|
+
spinner4.stop("Failed to load platforms");
|
|
1091
1110
|
p3.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1092
1111
|
process.exit(1);
|
|
1093
1112
|
}
|
|
1094
1113
|
}
|
|
1095
1114
|
|
|
1096
|
-
// src/commands/actions.ts
|
|
1097
|
-
import * as p4 from "@clack/prompts";
|
|
1098
|
-
import pc5 from "picocolors";
|
|
1099
|
-
function getApi() {
|
|
1100
|
-
const apiKey = getApiKey();
|
|
1101
|
-
if (!apiKey) {
|
|
1102
|
-
p4.cancel("Not configured. Run `pica init` first.");
|
|
1103
|
-
process.exit(1);
|
|
1104
|
-
}
|
|
1105
|
-
return new PicaApi(apiKey);
|
|
1106
|
-
}
|
|
1107
|
-
function colorMethod(method) {
|
|
1108
|
-
const m = method.toUpperCase();
|
|
1109
|
-
switch (m) {
|
|
1110
|
-
case "GET":
|
|
1111
|
-
return pc5.green(m);
|
|
1112
|
-
case "POST":
|
|
1113
|
-
return pc5.yellow(m);
|
|
1114
|
-
case "PUT":
|
|
1115
|
-
return pc5.blue(m);
|
|
1116
|
-
case "PATCH":
|
|
1117
|
-
return pc5.cyan(m);
|
|
1118
|
-
case "DELETE":
|
|
1119
|
-
return pc5.red(m);
|
|
1120
|
-
default:
|
|
1121
|
-
return pc5.dim(m);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
function padMethod(method) {
|
|
1125
|
-
return method.toUpperCase().padEnd(7);
|
|
1126
|
-
}
|
|
1127
|
-
async function actionsSearchCommand(platform, query, options = {}) {
|
|
1128
|
-
const api = getApi();
|
|
1129
|
-
if (!query) {
|
|
1130
|
-
const input = await p4.text({
|
|
1131
|
-
message: `Search actions on ${pc5.cyan(platform)}:`,
|
|
1132
|
-
placeholder: "send email, create contact, list orders..."
|
|
1133
|
-
});
|
|
1134
|
-
if (p4.isCancel(input)) {
|
|
1135
|
-
p4.cancel("Cancelled.");
|
|
1136
|
-
process.exit(0);
|
|
1137
|
-
}
|
|
1138
|
-
query = input;
|
|
1139
|
-
}
|
|
1140
|
-
const spinner5 = p4.spinner();
|
|
1141
|
-
spinner5.start(`Searching ${platform} actions...`);
|
|
1142
|
-
try {
|
|
1143
|
-
const limit = options.limit ? parseInt(options.limit, 10) : 10;
|
|
1144
|
-
const actions2 = await api.searchActions(platform, query, limit);
|
|
1145
|
-
spinner5.stop(`${actions2.length} action${actions2.length === 1 ? "" : "s"} found`);
|
|
1146
|
-
if (options.json) {
|
|
1147
|
-
console.log(JSON.stringify(actions2, null, 2));
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
if (actions2.length === 0) {
|
|
1151
|
-
p4.note(`No actions found for "${query}" on ${platform}.`, "No Results");
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
console.log();
|
|
1155
|
-
const rows = actions2.map((action) => ({
|
|
1156
|
-
method: colorMethod(padMethod(action.method)),
|
|
1157
|
-
path: action.path,
|
|
1158
|
-
title: action.title,
|
|
1159
|
-
id: action._id
|
|
1160
|
-
}));
|
|
1161
|
-
printTable(
|
|
1162
|
-
[
|
|
1163
|
-
{ key: "method", label: "Method" },
|
|
1164
|
-
{ key: "title", label: "Title" },
|
|
1165
|
-
{ key: "path", label: "Path", color: pc5.dim },
|
|
1166
|
-
{ key: "id", label: "Action ID", color: pc5.dim }
|
|
1167
|
-
],
|
|
1168
|
-
rows
|
|
1169
|
-
);
|
|
1170
|
-
console.log();
|
|
1171
|
-
p4.note(
|
|
1172
|
-
`Get docs: ${pc5.cyan("pica actions knowledge <actionId>")}
|
|
1173
|
-
Execute: ${pc5.cyan("pica actions execute <actionId>")}`,
|
|
1174
|
-
"Next Steps"
|
|
1175
|
-
);
|
|
1176
|
-
} catch (error) {
|
|
1177
|
-
spinner5.stop("Search failed");
|
|
1178
|
-
p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1179
|
-
process.exit(1);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
async function actionsKnowledgeCommand(actionId, options = {}) {
|
|
1183
|
-
const api = getApi();
|
|
1184
|
-
const spinner5 = p4.spinner();
|
|
1185
|
-
spinner5.start("Loading action knowledge...");
|
|
1186
|
-
try {
|
|
1187
|
-
const knowledge = await api.getActionKnowledge(actionId);
|
|
1188
|
-
spinner5.stop("Action knowledge loaded");
|
|
1189
|
-
if (!knowledge) {
|
|
1190
|
-
p4.cancel(`No knowledge found for action: ${actionId}`);
|
|
1191
|
-
process.exit(1);
|
|
1192
|
-
}
|
|
1193
|
-
if (options.json) {
|
|
1194
|
-
console.log(JSON.stringify(knowledge, null, 2));
|
|
1195
|
-
return;
|
|
1196
|
-
}
|
|
1197
|
-
printKnowledge(knowledge, options.full);
|
|
1198
|
-
} catch (error) {
|
|
1199
|
-
spinner5.stop("Failed to load knowledge");
|
|
1200
|
-
p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1201
|
-
process.exit(1);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
function printKnowledge(k, full) {
|
|
1205
|
-
console.log();
|
|
1206
|
-
console.log(pc5.bold(` ${k.title}`));
|
|
1207
|
-
console.log();
|
|
1208
|
-
console.log(` Platform: ${pc5.cyan(k.connectionPlatform)}`);
|
|
1209
|
-
console.log(` Method: ${colorMethod(k.method)}`);
|
|
1210
|
-
console.log(` Path: ${k.path}`);
|
|
1211
|
-
console.log(` Base URL: ${pc5.dim(k.baseUrl)}`);
|
|
1212
|
-
if (k.tags?.length) {
|
|
1213
|
-
console.log(` Tags: ${k.tags.map((t) => pc5.dim(t)).join(", ")}`);
|
|
1214
|
-
}
|
|
1215
|
-
const pathVars = extractPathVariables(k.path);
|
|
1216
|
-
if (pathVars.length > 0) {
|
|
1217
|
-
console.log(` Path Vars: ${pathVars.map((v) => pc5.yellow(`{{${v}}}`)).join(", ")}`);
|
|
1218
|
-
}
|
|
1219
|
-
console.log(` Active: ${k.active ? pc5.green("yes") : pc5.red("no")}`);
|
|
1220
|
-
console.log(` ID: ${pc5.dim(k._id)}`);
|
|
1221
|
-
if (k.knowledge) {
|
|
1222
|
-
console.log();
|
|
1223
|
-
console.log(pc5.bold(" API Documentation"));
|
|
1224
|
-
console.log(pc5.dim(" " + "\u2500".repeat(40)));
|
|
1225
|
-
console.log();
|
|
1226
|
-
const lines = k.knowledge.split("\n");
|
|
1227
|
-
const displayLines = full ? lines : lines.slice(0, 50);
|
|
1228
|
-
for (const line of displayLines) {
|
|
1229
|
-
console.log(` ${line}`);
|
|
1230
|
-
}
|
|
1231
|
-
if (!full && lines.length > 50) {
|
|
1232
|
-
console.log();
|
|
1233
|
-
console.log(pc5.dim(` ... ${lines.length - 50} more lines. Use --full to see all.`));
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
console.log();
|
|
1237
|
-
}
|
|
1238
|
-
async function actionsExecuteCommand(actionId, options = {}) {
|
|
1239
|
-
const api = getApi();
|
|
1240
|
-
const spinner5 = p4.spinner();
|
|
1241
|
-
spinner5.start("Loading action details...");
|
|
1242
|
-
let knowledge;
|
|
1243
|
-
try {
|
|
1244
|
-
const k = await api.getActionKnowledge(actionId);
|
|
1245
|
-
if (!k) {
|
|
1246
|
-
spinner5.stop("Action not found");
|
|
1247
|
-
p4.cancel(`No action found for: ${actionId}`);
|
|
1248
|
-
process.exit(1);
|
|
1249
|
-
}
|
|
1250
|
-
knowledge = k;
|
|
1251
|
-
spinner5.stop(`${colorMethod(knowledge.method)} ${knowledge.path}`);
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
spinner5.stop("Failed to load action");
|
|
1254
|
-
p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1255
|
-
process.exit(1);
|
|
1256
|
-
}
|
|
1257
|
-
let connectionKey = options.connection;
|
|
1258
|
-
if (!connectionKey) {
|
|
1259
|
-
connectionKey = await resolveConnection(api, knowledge.connectionPlatform);
|
|
1260
|
-
}
|
|
1261
|
-
const pathVarMap = parseKeyValuePairs(options.pathVar || []);
|
|
1262
|
-
const pathVars = extractPathVariables(knowledge.path);
|
|
1263
|
-
for (const v of pathVars) {
|
|
1264
|
-
if (!pathVarMap[v]) {
|
|
1265
|
-
const input = await p4.text({
|
|
1266
|
-
message: `Value for path variable ${pc5.yellow(`{{${v}}}`)}:`,
|
|
1267
|
-
validate: (val) => val.trim() ? void 0 : "Value is required"
|
|
1268
|
-
});
|
|
1269
|
-
if (p4.isCancel(input)) {
|
|
1270
|
-
p4.cancel("Cancelled.");
|
|
1271
|
-
process.exit(0);
|
|
1272
|
-
}
|
|
1273
|
-
pathVarMap[v] = input;
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
let bodyData = {};
|
|
1277
|
-
if (options.data) {
|
|
1278
|
-
try {
|
|
1279
|
-
bodyData = JSON.parse(options.data);
|
|
1280
|
-
} catch {
|
|
1281
|
-
p4.cancel("Invalid JSON in --data flag.");
|
|
1282
|
-
process.exit(1);
|
|
1283
|
-
}
|
|
1284
|
-
} else if (!["GET", "DELETE", "HEAD"].includes(knowledge.method.toUpperCase())) {
|
|
1285
|
-
const input = await p4.text({
|
|
1286
|
-
message: "Request body (JSON):",
|
|
1287
|
-
placeholder: '{"key": "value"} or leave empty'
|
|
1288
|
-
});
|
|
1289
|
-
if (p4.isCancel(input)) {
|
|
1290
|
-
p4.cancel("Cancelled.");
|
|
1291
|
-
process.exit(0);
|
|
1292
|
-
}
|
|
1293
|
-
if (input.trim()) {
|
|
1294
|
-
try {
|
|
1295
|
-
bodyData = JSON.parse(input);
|
|
1296
|
-
} catch {
|
|
1297
|
-
p4.cancel("Invalid JSON.");
|
|
1298
|
-
process.exit(1);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
const queryParams = parseKeyValuePairs(options.query || []);
|
|
1303
|
-
const { resolvedPath, remainingData } = resolveTemplateVariables(
|
|
1304
|
-
knowledge.path,
|
|
1305
|
-
bodyData,
|
|
1306
|
-
pathVarMap
|
|
1307
|
-
);
|
|
1308
|
-
console.log();
|
|
1309
|
-
console.log(pc5.bold(" Request Summary"));
|
|
1310
|
-
console.log(` ${colorMethod(knowledge.method)} ${knowledge.baseUrl}${resolvedPath}`);
|
|
1311
|
-
console.log(` Connection: ${pc5.dim(connectionKey)}`);
|
|
1312
|
-
if (Object.keys(remainingData).length > 0) {
|
|
1313
|
-
console.log(` Body: ${pc5.dim(JSON.stringify(remainingData))}`);
|
|
1314
|
-
}
|
|
1315
|
-
if (Object.keys(queryParams).length > 0) {
|
|
1316
|
-
console.log(` Query: ${pc5.dim(JSON.stringify(queryParams))}`);
|
|
1317
|
-
}
|
|
1318
|
-
console.log();
|
|
1319
|
-
const execSpinner = p4.spinner();
|
|
1320
|
-
execSpinner.start("Executing...");
|
|
1321
|
-
try {
|
|
1322
|
-
const result = await api.executeAction({
|
|
1323
|
-
method: knowledge.method,
|
|
1324
|
-
path: resolvedPath,
|
|
1325
|
-
actionId,
|
|
1326
|
-
connectionKey,
|
|
1327
|
-
data: Object.keys(remainingData).length > 0 ? remainingData : void 0,
|
|
1328
|
-
queryParams: Object.keys(queryParams).length > 0 ? queryParams : void 0,
|
|
1329
|
-
isFormData: options.formData,
|
|
1330
|
-
isFormUrlEncoded: options.formUrlencoded
|
|
1331
|
-
});
|
|
1332
|
-
execSpinner.stop(pc5.green("Success"));
|
|
1333
|
-
if (options.json) {
|
|
1334
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1335
|
-
} else {
|
|
1336
|
-
console.log();
|
|
1337
|
-
console.log(pc5.bold(" Response"));
|
|
1338
|
-
console.log(pc5.dim(" " + "\u2500".repeat(40)));
|
|
1339
|
-
console.log();
|
|
1340
|
-
console.log(formatResponse(result));
|
|
1341
|
-
console.log();
|
|
1342
|
-
}
|
|
1343
|
-
} catch (error) {
|
|
1344
|
-
execSpinner.stop(pc5.red("Failed"));
|
|
1345
|
-
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
1346
|
-
p4.cancel(`Execution failed: ${msg}`);
|
|
1347
|
-
process.exit(1);
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
async function resolveConnection(api, platform) {
|
|
1351
|
-
const spinner5 = p4.spinner();
|
|
1352
|
-
spinner5.start("Loading connections...");
|
|
1353
|
-
const connections = await api.listConnections();
|
|
1354
|
-
const matching = connections.filter(
|
|
1355
|
-
(c) => c.platform.toLowerCase() === platform.toLowerCase()
|
|
1356
|
-
);
|
|
1357
|
-
spinner5.stop(`${matching.length} ${platform} connection${matching.length === 1 ? "" : "s"} found`);
|
|
1358
|
-
if (matching.length === 0) {
|
|
1359
|
-
p4.cancel(
|
|
1360
|
-
`No ${platform} connections found.
|
|
1361
|
-
|
|
1362
|
-
Add one with: ${pc5.cyan(`pica connection add ${platform}`)}`
|
|
1363
|
-
);
|
|
1364
|
-
process.exit(1);
|
|
1365
|
-
}
|
|
1366
|
-
if (matching.length === 1) {
|
|
1367
|
-
p4.log.info(`Using connection: ${pc5.dim(matching[0].key)}`);
|
|
1368
|
-
return matching[0].key;
|
|
1369
|
-
}
|
|
1370
|
-
const selected = await p4.select({
|
|
1371
|
-
message: `Multiple ${platform} connections found. Which one?`,
|
|
1372
|
-
options: matching.map((c) => ({
|
|
1373
|
-
value: c.key,
|
|
1374
|
-
label: `${c.key}`,
|
|
1375
|
-
hint: c.state
|
|
1376
|
-
}))
|
|
1377
|
-
});
|
|
1378
|
-
if (p4.isCancel(selected)) {
|
|
1379
|
-
p4.cancel("Cancelled.");
|
|
1380
|
-
process.exit(0);
|
|
1381
|
-
}
|
|
1382
|
-
return selected;
|
|
1383
|
-
}
|
|
1384
|
-
function parseKeyValuePairs(pairs) {
|
|
1385
|
-
const result = {};
|
|
1386
|
-
for (const pair of pairs) {
|
|
1387
|
-
const eqIdx = pair.indexOf("=");
|
|
1388
|
-
if (eqIdx === -1) continue;
|
|
1389
|
-
const key = pair.slice(0, eqIdx);
|
|
1390
|
-
const value = pair.slice(eqIdx + 1);
|
|
1391
|
-
result[key] = value;
|
|
1392
|
-
}
|
|
1393
|
-
return result;
|
|
1394
|
-
}
|
|
1395
|
-
function formatResponse(data, indent = 2) {
|
|
1396
|
-
const prefix = " ".repeat(indent);
|
|
1397
|
-
const json = JSON.stringify(data, null, 2);
|
|
1398
|
-
return json.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
1115
|
// src/index.ts
|
|
1402
1116
|
var require2 = createRequire(import.meta.url);
|
|
1403
1117
|
var { version } = require2("../package.json");
|
|
@@ -1422,23 +1136,4 @@ program.command("add [platform]").description("Shortcut for: connection add").ac
|
|
|
1422
1136
|
program.command("list").alias("ls").description("Shortcut for: connection list").action(async () => {
|
|
1423
1137
|
await connectionListCommand();
|
|
1424
1138
|
});
|
|
1425
|
-
var actions = program.command("actions").alias("a").description("Discover and execute platform actions");
|
|
1426
|
-
actions.command("search <platform> [query]").description("Search actions on a platform").option("--json", "Output as JSON").option("-l, --limit <limit>", "Max results", "10").action(async (platform, query, options) => {
|
|
1427
|
-
await actionsSearchCommand(platform, query, options);
|
|
1428
|
-
});
|
|
1429
|
-
actions.command("knowledge <actionId>").alias("k").description("Get API docs for an action").option("--json", "Output as JSON").option("--full", "Show full knowledge (no truncation)").action(async (actionId, options) => {
|
|
1430
|
-
await actionsKnowledgeCommand(actionId, options);
|
|
1431
|
-
});
|
|
1432
|
-
actions.command("execute <actionId>").alias("x").description("Execute an action").option("-c, --connection <key>", "Connection key to use").option("-d, --data <json>", "Request body as JSON").option("-p, --path-var <key=value...>", "Path variable", collectValues).option("-q, --query <key=value...>", "Query parameter", collectValues).option("--form-data", "Send as multipart/form-data").option("--form-urlencoded", "Send as application/x-www-form-urlencoded").option("--json", "Output as JSON").action(async (actionId, options) => {
|
|
1433
|
-
await actionsExecuteCommand(actionId, options);
|
|
1434
|
-
});
|
|
1435
|
-
program.command("search <platform> [query]").description("Shortcut for: actions search").option("--json", "Output as JSON").option("-l, --limit <limit>", "Max results", "10").action(async (platform, query, options) => {
|
|
1436
|
-
await actionsSearchCommand(platform, query, options);
|
|
1437
|
-
});
|
|
1438
|
-
program.command("exec <actionId>").description("Shortcut for: actions execute").option("-c, --connection <key>", "Connection key to use").option("-d, --data <json>", "Request body as JSON").option("-p, --path-var <key=value...>", "Path variable", collectValues).option("-q, --query <key=value...>", "Query parameter", collectValues).option("--form-data", "Send as multipart/form-data").option("--form-urlencoded", "Send as application/x-www-form-urlencoded").option("--json", "Output as JSON").action(async (actionId, options) => {
|
|
1439
|
-
await actionsExecuteCommand(actionId, options);
|
|
1440
|
-
});
|
|
1441
|
-
function collectValues(value, previous) {
|
|
1442
|
-
return (previous || []).concat([value]);
|
|
1443
|
-
}
|
|
1444
1139
|
program.parse();
|