@blinkdotnew/cli 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -478,7 +478,7 @@ Supported formats: mp3, wav, m4a, ogg
478
478
  if (isJsonMode()) return printJson(result);
479
479
  console.log(result?.text ?? result?.transcript ?? JSON.stringify(result));
480
480
  });
481
- ai.command("call <phone-number> <system-prompt>").description("Make an AI phone call to any number (US/International)").option("--voice <voice>", "Voice: openai:alloy | openai:nova | cartesia:sonic-english", "openai:alloy").option("--max-duration <seconds>", "Max call duration in seconds", "300").option("--no-wait", "Return call_id immediately without waiting for completion").addHelpText("after", `
481
+ ai.command("call <phone-number> <system-prompt>").description("Make an AI phone call to any number (US/International)").option("--voice <voice>", "Voice: openai:alloy | openai:nova | cartesia:sonic-english", "openai:alloy").option("--max-duration <seconds>", "Max call duration in seconds", "300").option("--no-wait", "Return call_id immediately without waiting for completion").option("--from <number>", "Phone number to call from (E.164 format, e.g. +14155551234). Uses primary number if omitted.").addHelpText("after", `
482
482
  Examples:
483
483
  $ blink ai call "+14155551234" "You are collecting a payment of $240 from John Smith. Be polite but firm."
484
484
  $ blink ai call "+14155551234" "Confirm John's appointment for tomorrow at 3pm" --voice openai:nova
@@ -496,6 +496,7 @@ Voices:
496
496
  cartesia:sonic-english Low-latency, natural
497
497
 
498
498
  Call is charged to your workspace credits after completion (~1 credit/min).
499
+ Use --from to specify which of your workspace numbers to call from.
499
500
  `).action(async (phoneNumber, systemPrompt, opts) => {
500
501
  requireToken();
501
502
  const result = await withSpinner(
@@ -505,13 +506,14 @@ Call is charged to your workspace credits after completion (~1 credit/min).
505
506
  phone_number: phoneNumber,
506
507
  system_prompt: systemPrompt,
507
508
  voice: opts.voice,
508
- max_duration_seconds: parseInt(opts.maxDuration)
509
+ max_duration_seconds: parseInt(opts.maxDuration),
510
+ ...opts.from ? { from_number: opts.from } : {}
509
511
  }
510
512
  })
511
513
  );
512
514
  if (isJsonMode()) return printJson(result);
513
515
  const callId = result?.call_id;
514
- if (opts.noWait) {
516
+ if (!opts.wait) {
515
517
  console.log(`Call initiated: ${callId}`);
516
518
  console.log(`Poll status: blink ai call-status ${callId}`);
517
519
  return;
@@ -1227,7 +1229,7 @@ ${filtered.length} providers total. Connect at blink.new/settings?tab=connectors
1227
1229
  ${connected.length} provider(s) connected`));
1228
1230
  }
1229
1231
  });
1230
- connector.command("exec <provider> <endpoint> [params]").description("Execute a call on a connected OAuth provider").option("--account <id>", "Specific account ID (if you have multiple accounts)").option("--method <method>", "HTTP method: GET | POST | PUT | PATCH | DELETE (default: POST)", "POST").addHelpText("after", `
1232
+ connector.command("exec <provider> <endpoint> [method-or-params] [params]").description("Execute a call on a connected OAuth provider").option("--account <id>", "Specific account ID (if you have multiple accounts)").option("--method <method>", "HTTP method: GET | POST | PUT | PATCH | DELETE (default: POST)", "POST").addHelpText("after", `
1231
1233
  <endpoint> is the API path relative to the provider's base URL, OR a GraphQL query string for Linear.
1232
1234
 
1233
1235
  Examples (REST):
@@ -1260,22 +1262,35 @@ Provider base URLs used:
1260
1262
  jira https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/
1261
1263
  shopify https://{shop}.myshopify.com/admin/api/2024-10/
1262
1264
  ... run \`blink connector providers\` for all 38 providers
1263
- `).action(async (provider, endpoint, paramsArg, opts) => {
1265
+ `).action(async (provider, endpoint, arg1, arg2, opts) => {
1264
1266
  requireToken();
1267
+ const HTTP_METHODS = /^(GET|POST|PUT|PATCH|DELETE|HEAD)$/i;
1268
+ let httpMethod = opts.method;
1265
1269
  let params = {};
1266
- if (paramsArg) {
1267
- try {
1268
- params = JSON.parse(paramsArg);
1269
- } catch {
1270
- params = {};
1270
+ if (arg1) {
1271
+ if (HTTP_METHODS.test(arg1)) {
1272
+ httpMethod = arg1.toUpperCase();
1273
+ if (arg2) {
1274
+ try {
1275
+ params = JSON.parse(arg2);
1276
+ } catch {
1277
+ params = {};
1278
+ }
1279
+ }
1280
+ } else {
1281
+ try {
1282
+ params = JSON.parse(arg1);
1283
+ } catch {
1284
+ params = {};
1285
+ }
1271
1286
  }
1272
1287
  }
1273
1288
  const result = await withSpinner(
1274
- `${opts.method} ${provider}${endpoint}...`,
1289
+ `${httpMethod} ${provider}${endpoint}...`,
1275
1290
  () => resourcesRequest(`/v1/connectors/${provider}/execute`, {
1276
1291
  body: {
1277
1292
  method: endpoint,
1278
- http_method: opts.method,
1293
+ http_method: httpMethod,
1279
1294
  params,
1280
1295
  ...opts.account ? { account_id: opts.account } : {}
1281
1296
  }
@@ -1380,6 +1395,30 @@ Examples:
1380
1395
  console.log(chalk7.green("\u2713 Post published"));
1381
1396
  if (data?.id) console.log(chalk7.dim(` URN: ${data.id}`));
1382
1397
  });
1398
+ li.command("upload-media <media-url>").description("Upload an image or video to LinkedIn storage, returns asset URN for use in posts").option("--type <type>", "Media type: image | video (default: image)", "image").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1399
+ Returns an asset URN to use when composing a post with media via blink connector exec.
1400
+
1401
+ Examples:
1402
+ $ blink linkedin upload-media https://example.com/photo.jpg
1403
+ $ blink linkedin upload-media https://example.com/demo.mp4 --type video
1404
+ $ ASSET_URN=$(blink linkedin upload-media https://example.com/photo.jpg --json | python3 -c "import json,sys; print(json.load(sys.stdin)['asset_urn'])")
1405
+ `).action(async (mediaUrl, opts) => {
1406
+ requireToken();
1407
+ const agentId = requireAgentId(opts.agent);
1408
+ const result = await withSpinner(
1409
+ `Uploading ${opts.type}...`,
1410
+ () => resourcesRequest("/v1/connectors/linkedin/upload-media", {
1411
+ body: { media_url: mediaUrl, media_type: opts.type },
1412
+ headers: { "x-blink-agent-id": agentId }
1413
+ })
1414
+ );
1415
+ if (isJsonMode()) return printJson(result?.data ?? result);
1416
+ const assetUrn = result?.data?.asset_urn;
1417
+ if (assetUrn) {
1418
+ console.log(chalk7.green("\u2713 Upload complete"));
1419
+ console.log(chalk7.dim(` Asset URN: ${assetUrn}`));
1420
+ }
1421
+ });
1383
1422
  li.command("delete <postUrn>").description("Delete one of your LinkedIn posts").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1384
1423
  <postUrn> is the LinkedIn post URN returned when the post was created.
1385
1424
  e.g. urn:li:ugcPost:1234567890
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "description": "Blink platform CLI — deploy apps, manage databases, generate AI content",
5
5
  "bin": {
6
6
  "blink": "dist/cli.js"
@@ -290,6 +290,7 @@ Supported formats: mp3, wav, m4a, ogg
290
290
  .option('--voice <voice>', 'Voice: openai:alloy | openai:nova | cartesia:sonic-english', 'openai:alloy')
291
291
  .option('--max-duration <seconds>', 'Max call duration in seconds', '300')
292
292
  .option('--no-wait', 'Return call_id immediately without waiting for completion')
293
+ .option('--from <number>', 'Phone number to call from (E.164 format, e.g. +14155551234). Uses primary number if omitted.')
293
294
  .addHelpText('after', `
294
295
  Examples:
295
296
  $ blink ai call "+14155551234" "You are collecting a payment of $240 from John Smith. Be polite but firm."
@@ -308,6 +309,7 @@ Voices:
308
309
  cartesia:sonic-english Low-latency, natural
309
310
 
310
311
  Call is charged to your workspace credits after completion (~1 credit/min).
312
+ Use --from to specify which of your workspace numbers to call from.
311
313
  `)
312
314
  .action(async (phoneNumber, systemPrompt, opts) => {
313
315
  requireToken()
@@ -318,6 +320,7 @@ Call is charged to your workspace credits after completion (~1 credit/min).
318
320
  system_prompt: systemPrompt,
319
321
  voice: opts.voice,
320
322
  max_duration_seconds: parseInt(opts.maxDuration),
323
+ ...(opts.from ? { from_number: opts.from } : {}),
321
324
  },
322
325
  })
323
326
  )
@@ -325,7 +328,7 @@ Call is charged to your workspace credits after completion (~1 credit/min).
325
328
 
326
329
  const callId = result?.call_id as string
327
330
 
328
- if (opts.noWait) {
331
+ if (!opts.wait) { // Commander --no-wait sets opts.wait=false (not opts.noWait)
329
332
  console.log(`Call initiated: ${callId}`)
330
333
  console.log(`Poll status: blink ai call-status ${callId}`)
331
334
  return
@@ -149,8 +149,12 @@ Use --account <id> if you have multiple linked accounts for the same provider.
149
149
  }
150
150
  })
151
151
 
152
- // blink connector exec <provider> <endpoint> [params]
153
- connector.command('exec <provider> <endpoint> [params]')
152
+ // blink connector exec <provider> <endpoint> [method-or-params] [params]
153
+ // Supports both patterns:
154
+ // blink connector exec github /user/repos GET
155
+ // blink connector exec notion /search POST '{"query":"notes"}'
156
+ // blink connector exec notion /search '{"query":"notes"}'
157
+ connector.command('exec <provider> <endpoint> [method-or-params] [params]')
154
158
  .description('Execute a call on a connected OAuth provider')
155
159
  .option('--account <id>', 'Specific account ID (if you have multiple accounts)')
156
160
  .option('--method <method>', 'HTTP method: GET | POST | PUT | PATCH | DELETE (default: POST)', 'POST')
@@ -188,17 +192,28 @@ Provider base URLs used:
188
192
  shopify https://{shop}.myshopify.com/admin/api/2024-10/
189
193
  ... run \`blink connector providers\` for all 38 providers
190
194
  `)
191
- .action(async (provider: string, endpoint: string, paramsArg: string | undefined, opts) => {
195
+ .action(async (provider: string, endpoint: string, arg1: string | undefined, arg2: string | undefined, opts) => {
192
196
  requireToken()
197
+ const HTTP_METHODS = /^(GET|POST|PUT|PATCH|DELETE|HEAD)$/i
198
+ let httpMethod = opts.method
193
199
  let params: Record<string, unknown> = {}
194
- if (paramsArg) {
195
- try { params = JSON.parse(paramsArg) } catch { params = {} }
200
+
201
+ if (arg1) {
202
+ if (HTTP_METHODS.test(arg1)) {
203
+ // Pattern: exec provider endpoint GET ['{"key":"val"}']
204
+ httpMethod = arg1.toUpperCase()
205
+ if (arg2) { try { params = JSON.parse(arg2) } catch { params = {} } }
206
+ } else {
207
+ // Pattern: exec provider endpoint '{"key":"val"}'
208
+ try { params = JSON.parse(arg1) } catch { params = {} }
209
+ }
196
210
  }
197
- const result = await withSpinner(`${opts.method} ${provider}${endpoint}...`, () =>
211
+
212
+ const result = await withSpinner(`${httpMethod} ${provider}${endpoint}...`, () =>
198
213
  resourcesRequest(`/v1/connectors/${provider}/execute`, {
199
214
  body: {
200
215
  method: endpoint,
201
- http_method: opts.method,
216
+ http_method: httpMethod,
202
217
  params,
203
218
  ...(opts.account ? { account_id: opts.account } : {}),
204
219
  }
@@ -117,6 +117,36 @@ Examples:
117
117
  if (data?.id) console.log(chalk.dim(` URN: ${data.id}`))
118
118
  })
119
119
 
120
+ // blink linkedin upload-media <media-url>
121
+ li.command('upload-media <media-url>')
122
+ .description('Upload an image or video to LinkedIn storage, returns asset URN for use in posts')
123
+ .option('--type <type>', 'Media type: image | video (default: image)', 'image')
124
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
125
+ .addHelpText('after', `
126
+ Returns an asset URN to use when composing a post with media via blink connector exec.
127
+
128
+ Examples:
129
+ $ blink linkedin upload-media https://example.com/photo.jpg
130
+ $ blink linkedin upload-media https://example.com/demo.mp4 --type video
131
+ $ ASSET_URN=$(blink linkedin upload-media https://example.com/photo.jpg --json | python3 -c "import json,sys; print(json.load(sys.stdin)['asset_urn'])")
132
+ `)
133
+ .action(async (mediaUrl: string, opts) => {
134
+ requireToken()
135
+ const agentId = requireAgentId(opts.agent)
136
+ const result = await withSpinner(`Uploading ${opts.type}...`, () =>
137
+ resourcesRequest('/v1/connectors/linkedin/upload-media', {
138
+ body: { media_url: mediaUrl, media_type: opts.type },
139
+ headers: { 'x-blink-agent-id': agentId },
140
+ })
141
+ )
142
+ if (isJsonMode()) return printJson(result?.data ?? result)
143
+ const assetUrn = result?.data?.asset_urn
144
+ if (assetUrn) {
145
+ console.log(chalk.green('✓ Upload complete'))
146
+ console.log(chalk.dim(` Asset URN: ${assetUrn}`))
147
+ }
148
+ })
149
+
120
150
  // blink linkedin delete <postUrn>
121
151
  li.command('delete <postUrn>')
122
152
  .description('Delete one of your LinkedIn posts')