@picahq/cli 1.4.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.
Files changed (3) hide show
  1. package/README.md +0 -47
  2. package/dist/index.js +35 -432
  3. 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(p5) {
50
- if (p5.startsWith("~/")) {
51
- return path2.join(os2.homedir(), p5.slice(2));
49
+ function expandPath(p4) {
50
+ if (p4.startsWith("~/")) {
51
+ return path2.join(os2.homedir(), p4.slice(2));
52
52
  }
53
- return p5;
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 text4 = await response.text();
260
- throw new ApiError(response.status, text4 || `HTTP ${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();
@@ -525,16 +452,16 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
525
452
  p.cancel("Cancelled.");
526
453
  process.exit(0);
527
454
  }
528
- const spinner5 = p.spinner();
529
- spinner5.start("Validating API key...");
455
+ const spinner4 = p.spinner();
456
+ spinner4.start("Validating API key...");
530
457
  const api = new PicaApi(newKey);
531
458
  const isValid = await api.validateApiKey();
532
459
  if (!isValid) {
533
- spinner5.stop("Invalid API key");
460
+ spinner4.stop("Invalid API key");
534
461
  p.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
535
462
  process.exit(1);
536
463
  }
537
- spinner5.stop("API key validated");
464
+ spinner4.stop("API key validated");
538
465
  const reinstalled = [];
539
466
  for (const s of statuses) {
540
467
  if (s.globalMcp) {
@@ -666,16 +593,16 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
666
593
  p.cancel("Setup cancelled.");
667
594
  process.exit(0);
668
595
  }
669
- const spinner5 = p.spinner();
670
- spinner5.start("Validating API key...");
596
+ const spinner4 = p.spinner();
597
+ spinner4.start("Validating API key...");
671
598
  const api = new PicaApi(apiKey);
672
599
  const isValid = await api.validateApiKey();
673
600
  if (!isValid) {
674
- spinner5.stop("Invalid API key");
601
+ spinner4.stop("Invalid API key");
675
602
  p.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
676
603
  process.exit(1);
677
604
  }
678
- spinner5.stop("API key validated");
605
+ spinner4.stop("API key validated");
679
606
  writeConfig({
680
607
  apiKey,
681
608
  installedAgents: [],
@@ -875,16 +802,16 @@ async function promptConnectIntegrations(apiKey) {
875
802
  p.log.warn("Could not open browser automatically.");
876
803
  p.note(url, "Open manually");
877
804
  }
878
- const spinner5 = p.spinner();
879
- spinner5.start("Waiting for connection... (complete auth in browser)");
805
+ const spinner4 = p.spinner();
806
+ spinner4.start("Waiting for connection... (complete auth in browser)");
880
807
  try {
881
808
  await api.waitForConnection(platform, 5 * 60 * 1e3, 5e3);
882
- spinner5.stop(`${label} connected!`);
809
+ spinner4.stop(`${label} connected!`);
883
810
  p.log.success(`${pc2.green("\u2713")} ${label} is now available to your AI agents`);
884
811
  connected.push(platform);
885
812
  first = false;
886
813
  } catch (error) {
887
- spinner5.stop("Connection timed out");
814
+ spinner4.stop("Connection timed out");
888
815
  if (error instanceof TimeoutError) {
889
816
  p.log.warn(`No worries. Connect later with: ${pc2.cyan(`pica add ${platform}`)}`);
890
817
  }
@@ -917,16 +844,16 @@ import pc3 from "picocolors";
917
844
  function findPlatform(platforms, query) {
918
845
  const normalizedQuery = query.toLowerCase().trim();
919
846
  const exact = platforms.find(
920
- (p5) => p5.platform.toLowerCase() === normalizedQuery || p5.name.toLowerCase() === normalizedQuery
847
+ (p4) => p4.platform.toLowerCase() === normalizedQuery || p4.name.toLowerCase() === normalizedQuery
921
848
  );
922
849
  if (exact) return exact;
923
850
  return null;
924
851
  }
925
852
  function findSimilarPlatforms(platforms, query, limit = 3) {
926
853
  const normalizedQuery = query.toLowerCase().trim();
927
- const scored = platforms.map((p5) => {
928
- const name = p5.name.toLowerCase();
929
- const slug = p5.platform.toLowerCase();
854
+ const scored = platforms.map((p4) => {
855
+ const name = p4.name.toLowerCase();
856
+ const slug = p4.platform.toLowerCase();
930
857
  let score = 0;
931
858
  if (name.includes(normalizedQuery) || slug.includes(normalizedQuery)) {
932
859
  score = 10;
@@ -935,7 +862,7 @@ function findSimilarPlatforms(platforms, query, limit = 3) {
935
862
  } else {
936
863
  score = countMatchingChars(normalizedQuery, slug);
937
864
  }
938
- return { platform: p5, score };
865
+ return { platform: p4, score };
939
866
  }).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
940
867
  return scored.map((item) => item.platform);
941
868
  }
@@ -957,14 +884,14 @@ async function connectionAddCommand(platformArg) {
957
884
  process.exit(1);
958
885
  }
959
886
  const api = new PicaApi(apiKey);
960
- const spinner5 = p2.spinner();
961
- spinner5.start("Loading platforms...");
887
+ const spinner4 = p2.spinner();
888
+ spinner4.start("Loading platforms...");
962
889
  let platforms;
963
890
  try {
964
891
  platforms = await api.listPlatforms();
965
- spinner5.stop(`${platforms.length} platforms available`);
892
+ spinner4.stop(`${platforms.length} platforms available`);
966
893
  } catch (error) {
967
- spinner5.stop("Failed to load platforms");
894
+ spinner4.stop("Failed to load platforms");
968
895
  p2.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
969
896
  process.exit(1);
970
897
  }
@@ -1062,11 +989,11 @@ async function connectionListCommand() {
1062
989
  process.exit(1);
1063
990
  }
1064
991
  const api = new PicaApi(apiKey);
1065
- const spinner5 = p2.spinner();
1066
- spinner5.start("Loading connections...");
992
+ const spinner4 = p2.spinner();
993
+ spinner4.start("Loading connections...");
1067
994
  try {
1068
995
  const connections = await api.listConnections();
1069
- spinner5.stop(`${connections.length} connection${connections.length === 1 ? "" : "s"} found`);
996
+ spinner4.stop(`${connections.length} connection${connections.length === 1 ? "" : "s"} found`);
1070
997
  if (connections.length === 0) {
1071
998
  p2.note(
1072
999
  `No connections yet.
@@ -1095,7 +1022,7 @@ Add one with: ${pc3.cyan("pica connection add gmail")}`,
1095
1022
  console.log();
1096
1023
  p2.note(`Add more with: ${pc3.cyan("pica connection add <platform>")}`, "Tip");
1097
1024
  } catch (error) {
1098
- spinner5.stop("Failed to load connections");
1025
+ spinner4.stop("Failed to load connections");
1099
1026
  p2.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1100
1027
  process.exit(1);
1101
1028
  }
@@ -1123,11 +1050,11 @@ async function platformsCommand(options) {
1123
1050
  process.exit(1);
1124
1051
  }
1125
1052
  const api = new PicaApi(apiKey);
1126
- const spinner5 = p3.spinner();
1127
- spinner5.start("Loading platforms...");
1053
+ const spinner4 = p3.spinner();
1054
+ spinner4.start("Loading platforms...");
1128
1055
  try {
1129
1056
  const platforms = await api.listPlatforms();
1130
- spinner5.stop(`${platforms.length} platforms available`);
1057
+ spinner4.stop(`${platforms.length} platforms available`);
1131
1058
  if (options.json) {
1132
1059
  console.log(JSON.stringify(platforms, null, 2));
1133
1060
  return;
@@ -1179,317 +1106,12 @@ async function platformsCommand(options) {
1179
1106
  console.log();
1180
1107
  p3.note(`Connect with: ${pc4.cyan("pica connection add <platform>")}`, "Tip");
1181
1108
  } catch (error) {
1182
- spinner5.stop("Failed to load platforms");
1109
+ spinner4.stop("Failed to load platforms");
1183
1110
  p3.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1184
1111
  process.exit(1);
1185
1112
  }
1186
1113
  }
1187
1114
 
1188
- // src/commands/actions.ts
1189
- import * as p4 from "@clack/prompts";
1190
- import pc5 from "picocolors";
1191
- function getApi() {
1192
- const apiKey = getApiKey();
1193
- if (!apiKey) {
1194
- p4.cancel("Not configured. Run `pica init` first.");
1195
- process.exit(1);
1196
- }
1197
- return new PicaApi(apiKey);
1198
- }
1199
- function colorMethod(method) {
1200
- const m = method.toUpperCase();
1201
- switch (m) {
1202
- case "GET":
1203
- return pc5.green(m);
1204
- case "POST":
1205
- return pc5.yellow(m);
1206
- case "PUT":
1207
- return pc5.blue(m);
1208
- case "PATCH":
1209
- return pc5.cyan(m);
1210
- case "DELETE":
1211
- return pc5.red(m);
1212
- default:
1213
- return pc5.dim(m);
1214
- }
1215
- }
1216
- function padMethod(method) {
1217
- return method.toUpperCase().padEnd(7);
1218
- }
1219
- async function actionsSearchCommand(platform, query, options = {}) {
1220
- const api = getApi();
1221
- if (!query) {
1222
- const input = await p4.text({
1223
- message: `Search actions on ${pc5.cyan(platform)}:`,
1224
- placeholder: "send email, create contact, list orders..."
1225
- });
1226
- if (p4.isCancel(input)) {
1227
- p4.cancel("Cancelled.");
1228
- process.exit(0);
1229
- }
1230
- query = input;
1231
- }
1232
- const spinner5 = p4.spinner();
1233
- spinner5.start(`Searching ${platform} actions...`);
1234
- try {
1235
- const limit = options.limit ? parseInt(options.limit, 10) : 10;
1236
- const actions2 = await api.searchActions(platform, query, limit);
1237
- spinner5.stop(`${actions2.length} action${actions2.length === 1 ? "" : "s"} found`);
1238
- if (options.json) {
1239
- console.log(JSON.stringify(actions2, null, 2));
1240
- return;
1241
- }
1242
- if (actions2.length === 0) {
1243
- p4.note(`No actions found for "${query}" on ${platform}.`, "No Results");
1244
- return;
1245
- }
1246
- console.log();
1247
- const rows = actions2.map((action) => ({
1248
- method: colorMethod(padMethod(action.method)),
1249
- path: action.path,
1250
- title: action.title,
1251
- id: action._id
1252
- }));
1253
- printTable(
1254
- [
1255
- { key: "method", label: "Method" },
1256
- { key: "title", label: "Title" },
1257
- { key: "path", label: "Path", color: pc5.dim },
1258
- { key: "id", label: "Action ID", color: pc5.dim }
1259
- ],
1260
- rows
1261
- );
1262
- console.log();
1263
- p4.note(
1264
- `Get docs: ${pc5.cyan("pica actions knowledge <actionId>")}
1265
- Execute: ${pc5.cyan("pica actions execute <actionId>")}`,
1266
- "Next Steps"
1267
- );
1268
- } catch (error) {
1269
- spinner5.stop("Search failed");
1270
- p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1271
- process.exit(1);
1272
- }
1273
- }
1274
- async function actionsKnowledgeCommand(actionId, options = {}) {
1275
- const api = getApi();
1276
- const spinner5 = p4.spinner();
1277
- spinner5.start("Loading action knowledge...");
1278
- try {
1279
- const knowledge = await api.getActionKnowledge(actionId);
1280
- spinner5.stop("Action knowledge loaded");
1281
- if (!knowledge) {
1282
- p4.cancel(`No knowledge found for action: ${actionId}`);
1283
- process.exit(1);
1284
- }
1285
- if (options.json) {
1286
- console.log(JSON.stringify(knowledge, null, 2));
1287
- return;
1288
- }
1289
- printKnowledge(knowledge, options.full);
1290
- } catch (error) {
1291
- spinner5.stop("Failed to load knowledge");
1292
- p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1293
- process.exit(1);
1294
- }
1295
- }
1296
- function printKnowledge(k, full) {
1297
- console.log();
1298
- console.log(pc5.bold(` ${k.title}`));
1299
- console.log();
1300
- console.log(` Platform: ${pc5.cyan(k.connectionPlatform)}`);
1301
- console.log(` Method: ${colorMethod(k.method)}`);
1302
- console.log(` Path: ${k.path}`);
1303
- console.log(` Base URL: ${pc5.dim(k.baseUrl)}`);
1304
- if (k.tags?.length) {
1305
- console.log(` Tags: ${k.tags.map((t) => pc5.dim(t)).join(", ")}`);
1306
- }
1307
- const pathVars = extractPathVariables(k.path);
1308
- if (pathVars.length > 0) {
1309
- console.log(` Path Vars: ${pathVars.map((v) => pc5.yellow(`{{${v}}}`)).join(", ")}`);
1310
- }
1311
- console.log(` Active: ${k.active ? pc5.green("yes") : pc5.red("no")}`);
1312
- console.log(` ID: ${pc5.dim(k._id)}`);
1313
- if (k.knowledge) {
1314
- console.log();
1315
- console.log(pc5.bold(" API Documentation"));
1316
- console.log(pc5.dim(" " + "\u2500".repeat(40)));
1317
- console.log();
1318
- const lines = k.knowledge.split("\n");
1319
- const displayLines = full ? lines : lines.slice(0, 50);
1320
- for (const line of displayLines) {
1321
- console.log(` ${line}`);
1322
- }
1323
- if (!full && lines.length > 50) {
1324
- console.log();
1325
- console.log(pc5.dim(` ... ${lines.length - 50} more lines. Use --full to see all.`));
1326
- }
1327
- }
1328
- console.log();
1329
- }
1330
- async function actionsExecuteCommand(actionId, options = {}) {
1331
- const api = getApi();
1332
- const spinner5 = p4.spinner();
1333
- spinner5.start("Loading action details...");
1334
- let knowledge;
1335
- try {
1336
- const k = await api.getActionKnowledge(actionId);
1337
- if (!k) {
1338
- spinner5.stop("Action not found");
1339
- p4.cancel(`No action found for: ${actionId}`);
1340
- process.exit(1);
1341
- }
1342
- knowledge = k;
1343
- spinner5.stop(`${colorMethod(knowledge.method)} ${knowledge.path}`);
1344
- } catch (error) {
1345
- spinner5.stop("Failed to load action");
1346
- p4.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1347
- process.exit(1);
1348
- }
1349
- let connectionKey = options.connection;
1350
- if (!connectionKey) {
1351
- connectionKey = await resolveConnection(api, knowledge.connectionPlatform);
1352
- }
1353
- const pathVarMap = parseKeyValuePairs(options.pathVar || []);
1354
- const pathVars = extractPathVariables(knowledge.path);
1355
- for (const v of pathVars) {
1356
- if (!pathVarMap[v]) {
1357
- const input = await p4.text({
1358
- message: `Value for path variable ${pc5.yellow(`{{${v}}}`)}:`,
1359
- validate: (val) => val.trim() ? void 0 : "Value is required"
1360
- });
1361
- if (p4.isCancel(input)) {
1362
- p4.cancel("Cancelled.");
1363
- process.exit(0);
1364
- }
1365
- pathVarMap[v] = input;
1366
- }
1367
- }
1368
- let bodyData = {};
1369
- if (options.data) {
1370
- try {
1371
- bodyData = JSON.parse(options.data);
1372
- } catch {
1373
- p4.cancel("Invalid JSON in --data flag.");
1374
- process.exit(1);
1375
- }
1376
- } else if (!["GET", "DELETE", "HEAD"].includes(knowledge.method.toUpperCase())) {
1377
- const input = await p4.text({
1378
- message: "Request body (JSON):",
1379
- placeholder: '{"key": "value"} or leave empty'
1380
- });
1381
- if (p4.isCancel(input)) {
1382
- p4.cancel("Cancelled.");
1383
- process.exit(0);
1384
- }
1385
- if (input.trim()) {
1386
- try {
1387
- bodyData = JSON.parse(input);
1388
- } catch {
1389
- p4.cancel("Invalid JSON.");
1390
- process.exit(1);
1391
- }
1392
- }
1393
- }
1394
- const queryParams = parseKeyValuePairs(options.query || []);
1395
- const { resolvedPath, remainingData } = resolveTemplateVariables(
1396
- knowledge.path,
1397
- bodyData,
1398
- pathVarMap
1399
- );
1400
- console.log();
1401
- console.log(pc5.bold(" Request Summary"));
1402
- console.log(` ${colorMethod(knowledge.method)} ${knowledge.baseUrl}${resolvedPath}`);
1403
- console.log(` Connection: ${pc5.dim(connectionKey)}`);
1404
- if (Object.keys(remainingData).length > 0) {
1405
- console.log(` Body: ${pc5.dim(JSON.stringify(remainingData))}`);
1406
- }
1407
- if (Object.keys(queryParams).length > 0) {
1408
- console.log(` Query: ${pc5.dim(JSON.stringify(queryParams))}`);
1409
- }
1410
- console.log();
1411
- const execSpinner = p4.spinner();
1412
- execSpinner.start("Executing...");
1413
- try {
1414
- const result = await api.executeAction({
1415
- method: knowledge.method,
1416
- path: resolvedPath,
1417
- actionId,
1418
- connectionKey,
1419
- data: Object.keys(remainingData).length > 0 ? remainingData : void 0,
1420
- queryParams: Object.keys(queryParams).length > 0 ? queryParams : void 0,
1421
- isFormData: options.formData,
1422
- isFormUrlEncoded: options.formUrlencoded
1423
- });
1424
- execSpinner.stop(pc5.green("Success"));
1425
- if (options.json) {
1426
- console.log(JSON.stringify(result, null, 2));
1427
- } else {
1428
- console.log();
1429
- console.log(pc5.bold(" Response"));
1430
- console.log(pc5.dim(" " + "\u2500".repeat(40)));
1431
- console.log();
1432
- console.log(formatResponse(result));
1433
- console.log();
1434
- }
1435
- } catch (error) {
1436
- execSpinner.stop(pc5.red("Failed"));
1437
- const msg = error instanceof Error ? error.message : "Unknown error";
1438
- p4.cancel(`Execution failed: ${msg}`);
1439
- process.exit(1);
1440
- }
1441
- }
1442
- async function resolveConnection(api, platform) {
1443
- const spinner5 = p4.spinner();
1444
- spinner5.start("Loading connections...");
1445
- const connections = await api.listConnections();
1446
- const matching = connections.filter(
1447
- (c) => c.platform.toLowerCase() === platform.toLowerCase()
1448
- );
1449
- spinner5.stop(`${matching.length} ${platform} connection${matching.length === 1 ? "" : "s"} found`);
1450
- if (matching.length === 0) {
1451
- p4.cancel(
1452
- `No ${platform} connections found.
1453
-
1454
- Add one with: ${pc5.cyan(`pica connection add ${platform}`)}`
1455
- );
1456
- process.exit(1);
1457
- }
1458
- if (matching.length === 1) {
1459
- p4.log.info(`Using connection: ${pc5.dim(matching[0].key)}`);
1460
- return matching[0].key;
1461
- }
1462
- const selected = await p4.select({
1463
- message: `Multiple ${platform} connections found. Which one?`,
1464
- options: matching.map((c) => ({
1465
- value: c.key,
1466
- label: `${c.key}`,
1467
- hint: c.state
1468
- }))
1469
- });
1470
- if (p4.isCancel(selected)) {
1471
- p4.cancel("Cancelled.");
1472
- process.exit(0);
1473
- }
1474
- return selected;
1475
- }
1476
- function parseKeyValuePairs(pairs) {
1477
- const result = {};
1478
- for (const pair of pairs) {
1479
- const eqIdx = pair.indexOf("=");
1480
- if (eqIdx === -1) continue;
1481
- const key = pair.slice(0, eqIdx);
1482
- const value = pair.slice(eqIdx + 1);
1483
- result[key] = value;
1484
- }
1485
- return result;
1486
- }
1487
- function formatResponse(data, indent = 2) {
1488
- const prefix = " ".repeat(indent);
1489
- const json = JSON.stringify(data, null, 2);
1490
- return json.split("\n").map((line) => `${prefix}${line}`).join("\n");
1491
- }
1492
-
1493
1115
  // src/index.ts
1494
1116
  var require2 = createRequire(import.meta.url);
1495
1117
  var { version } = require2("../package.json");
@@ -1514,23 +1136,4 @@ program.command("add [platform]").description("Shortcut for: connection add").ac
1514
1136
  program.command("list").alias("ls").description("Shortcut for: connection list").action(async () => {
1515
1137
  await connectionListCommand();
1516
1138
  });
1517
- var actions = program.command("actions").alias("a").description("Discover and execute platform actions");
1518
- 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) => {
1519
- await actionsSearchCommand(platform, query, options);
1520
- });
1521
- 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) => {
1522
- await actionsKnowledgeCommand(actionId, options);
1523
- });
1524
- 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) => {
1525
- await actionsExecuteCommand(actionId, options);
1526
- });
1527
- 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) => {
1528
- await actionsSearchCommand(platform, query, options);
1529
- });
1530
- 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) => {
1531
- await actionsExecuteCommand(actionId, options);
1532
- });
1533
- function collectValues(value, previous) {
1534
- return (previous || []).concat([value]);
1535
- }
1536
1139
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picahq/cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "CLI for managing Pica",
5
5
  "type": "module",
6
6
  "files": [