@blinkdotnew/cli 0.2.2 → 0.2.3

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
@@ -1009,35 +1009,170 @@ The email is sent from your project's configured sender address (set in blink.ne
1009
1009
  }
1010
1010
 
1011
1011
  // src/commands/connector.ts
1012
+ import chalk6 from "chalk";
1013
+ var PROVIDERS = [
1014
+ // Communication
1015
+ { key: "slack", label: "Slack", category: "Communication" },
1016
+ { key: "discord", label: "Discord", category: "Communication" },
1017
+ // Email & Calendar
1018
+ { key: "google_gmail", label: "Gmail", category: "Email & Calendar" },
1019
+ { key: "google_calendar", label: "Google Calendar", category: "Email & Calendar" },
1020
+ { key: "microsoft_outlook", label: "Outlook", category: "Email & Calendar" },
1021
+ { key: "microsoft_calendar", label: "Microsoft Calendar", category: "Email & Calendar" },
1022
+ // Files & Docs
1023
+ { key: "google_drive", label: "Google Drive", category: "Files & Docs" },
1024
+ { key: "google_docs", label: "Google Docs", category: "Files & Docs" },
1025
+ { key: "google_sheets", label: "Google Sheets", category: "Files & Docs" },
1026
+ { key: "google_slides", label: "Google Slides", category: "Files & Docs" },
1027
+ { key: "microsoft_onedrive", label: "OneDrive", category: "Files & Docs" },
1028
+ { key: "notion", label: "Notion", category: "Files & Docs" },
1029
+ // Dev Tools
1030
+ { key: "github", label: "GitHub", category: "Dev Tools" },
1031
+ { key: "linear", label: "Linear", category: "Dev Tools" },
1032
+ { key: "jira", label: "Jira", category: "Dev Tools" },
1033
+ { key: "vercel", label: "Vercel", category: "Dev Tools" },
1034
+ { key: "figma", label: "Figma", category: "Dev Tools" },
1035
+ // CRM & Sales
1036
+ { key: "hubspot", label: "HubSpot", category: "CRM & Sales" },
1037
+ { key: "salesforce", label: "Salesforce", category: "CRM & Sales" },
1038
+ { key: "pipedrive", label: "Pipedrive", category: "CRM & Sales" },
1039
+ { key: "attio", label: "Attio", category: "CRM & Sales" },
1040
+ // Marketing
1041
+ { key: "mailchimp", label: "Mailchimp", category: "Marketing" },
1042
+ { key: "typeform", label: "Typeform", category: "Marketing" },
1043
+ // Data & Productivity
1044
+ { key: "airtable", label: "Airtable", category: "Data & Productivity" },
1045
+ { key: "asana", label: "Asana", category: "Data & Productivity" },
1046
+ { key: "calendly", label: "Calendly", category: "Data & Productivity" },
1047
+ // Social & Creator
1048
+ { key: "twitter", label: "X (Twitter)", category: "Social & Creator" },
1049
+ { key: "instagram", label: "Instagram", category: "Social & Creator" },
1050
+ { key: "tiktok", label: "TikTok", category: "Social & Creator" },
1051
+ { key: "youtube", label: "YouTube", category: "Social & Creator" },
1052
+ { key: "linkedin", label: "LinkedIn", category: "Social & Creator" },
1053
+ { key: "reddit", label: "Reddit", category: "Social & Creator" },
1054
+ { key: "loom", label: "Loom", category: "Social & Creator" },
1055
+ // Meetings
1056
+ { key: "zoom", label: "Zoom", category: "Meetings" },
1057
+ { key: "microsoft_teams", label: "Microsoft Teams", category: "Meetings" },
1058
+ // Commerce
1059
+ { key: "stripe", label: "Stripe", category: "Commerce" },
1060
+ { key: "shopify", label: "Shopify", category: "Commerce" },
1061
+ { key: "etsy", label: "Etsy", category: "Commerce" }
1062
+ ];
1012
1063
  function registerConnectorCommands(program2) {
1013
- const connector = program2.command("connector").description("Execute actions on connected OAuth apps (Notion, Slack, Google, HubSpot, etc.)").addHelpText("after", `
1014
- Connectors are OAuth accounts linked to your workspace in blink.new \u2192 Settings \u2192 Integrations.
1015
- Once linked, use blink connector exec to call their APIs without managing tokens yourself.
1016
-
1017
- Supported providers: notion, slack, discord, google_drive, google_calendar, google_sheets,
1018
- google_docs, gmail, hubspot, airtable, microsoft, linkedin, salesforce
1019
-
1020
- Examples:
1021
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
1022
- $ blink connector exec slack post_message '{"channel":"C123","text":"Hello"}' --method POST
1023
- $ blink connector exec google_calendar list_events '{}'
1024
- $ blink connector exec google_sheets get_values '{"spreadsheet_id":"xxx","range":"A1:D10"}'
1025
- $ blink connector exec hubspot get_contacts '{}'
1026
- $ blink connector exec notion get_page '{"page_id":"abc-123"}' --method GET
1027
- $ blink connector exec notion create_page '{"title":"New"}' --account acct_xxx
1028
-
1029
- Use --method GET for read operations, POST/PUT for writes (default: POST).
1064
+ const connector = program2.command("connector").description("Execute actions on connected OAuth apps (GitHub, Notion, Slack, Reddit, Google, and 30+ more)").addHelpText("after", `
1065
+ Connectors are OAuth accounts linked to your workspace at blink.new \u2192 Settings \u2192 Integrations.
1066
+ Once linked, use \`blink connector exec\` to call their APIs without managing tokens yourself.
1067
+
1068
+ Connect accounts at: https://blink.new/settings?tab=connectors
1069
+
1070
+ Run \`blink connector providers\` to see all 39 supported providers.
1071
+
1072
+ Quick examples:
1073
+ $ blink connector exec github /user/repos GET
1074
+ $ blink connector exec notion /search POST '{"query":"meeting notes"}'
1075
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"Hello"}'
1076
+ $ blink connector exec google_calendar /calendars/primary/events GET
1077
+ $ blink connector exec jira /issue POST '{"fields":{"project":{"key":"PROJ"},"summary":"Bug"}}'
1078
+ $ blink connector exec stripe /customers GET '{"limit":10}'
1079
+ $ blink connector exec shopify /orders.json GET
1080
+ $ blink connector exec linear '{ viewer { id name } }' POST # GraphQL
1081
+
1082
+ Use --method GET for reads, POST for writes (default: POST).
1083
+ Use --account <id> if you have multiple linked accounts for the same provider.
1030
1084
  `);
1031
- connector.command("exec <provider> <action> [params]").description("Execute an action on a connected OAuth provider").option("--account <id>", "Specific account ID (if you have multiple accounts for a provider)").option("--method <method>", "HTTP method: GET | POST | PUT | DELETE (default: POST)", "POST").addHelpText("after", `
1032
- Examples:
1033
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
1034
- $ blink connector exec notion create_page '{"title":"My Page"}' --method POST
1035
- $ blink connector exec slack post_message '{"channel":"C123","text":"Hello"}' --method POST
1036
- $ blink connector exec google_drive list_files '{"folder_id":"root"}' --method GET
1037
- $ blink connector exec hubspot get_contacts '{}' --method GET
1038
- $ blink connector exec linkedin get_profile '{}' --method GET
1039
- $ blink connector exec notion search_pages '{"query":"docs"}' --json | jq '.results[].title'
1040
- `).action(async (provider, action, paramsArg, opts) => {
1085
+ connector.command("providers").description("List all supported OAuth connector providers").option("--category <cat>", "Filter by category").action((opts) => {
1086
+ const filtered = opts.category ? PROVIDERS.filter((p) => p.category.toLowerCase().includes(opts.category.toLowerCase())) : PROVIDERS;
1087
+ if (isJsonMode()) return printJson(filtered);
1088
+ const categories = [...new Set(filtered.map((p) => p.category))];
1089
+ for (const cat of categories) {
1090
+ console.log(chalk6.bold.cyan(`
1091
+ ${cat}`));
1092
+ const catProviders = filtered.filter((p) => p.category === cat);
1093
+ for (const p of catProviders) {
1094
+ console.log(` ${chalk6.green(p.key.padEnd(22))} ${chalk6.dim(p.label)}`);
1095
+ }
1096
+ }
1097
+ console.log(chalk6.dim(`
1098
+ ${filtered.length} providers total. Connect at blink.new/settings?tab=connectors`));
1099
+ });
1100
+ connector.command("status [provider]").description("Check connection status for a provider (or all providers)").option("--account <id>", "Check a specific account ID").action(async (provider, opts) => {
1101
+ requireToken();
1102
+ if (provider) {
1103
+ const result = await withSpinner(
1104
+ `Checking ${provider} status...`,
1105
+ () => resourcesRequest(`/v1/connectors/${provider}/status`, {
1106
+ method: "GET",
1107
+ ...opts.account ? { query: { account_id: opts.account } } : {}
1108
+ })
1109
+ );
1110
+ if (isJsonMode()) return printJson(result);
1111
+ const d = result?.data ?? result;
1112
+ if (d?.connected) {
1113
+ console.log(chalk6.green("\u2713 Connected"));
1114
+ if (d.account_id) console.log(chalk6.dim(` Account: ${d.account_id}`));
1115
+ if (d.metadata?.email) console.log(chalk6.dim(` Email: ${d.metadata.email}`));
1116
+ if (d.metadata?.name) console.log(chalk6.dim(` Name: ${d.metadata.name}`));
1117
+ } else {
1118
+ console.log(chalk6.red("\u2717 Not connected"));
1119
+ console.log(chalk6.dim(` Connect at blink.new/settings?tab=connectors`));
1120
+ }
1121
+ } else {
1122
+ const result = await withSpinner(
1123
+ "Checking all connectors...",
1124
+ () => resourcesRequest("/v1/connectors/linked", { method: "GET" })
1125
+ );
1126
+ if (isJsonMode()) return printJson(result);
1127
+ const connected = result?.data ?? [];
1128
+ if (!connected.length) {
1129
+ console.log(chalk6.dim("No connectors linked. Connect at blink.new/settings?tab=connectors"));
1130
+ return;
1131
+ }
1132
+ const table = createTable(["Provider", "Accounts"]);
1133
+ for (const c of connected) {
1134
+ const accounts = c.accounts.map((a) => a.label || a.account_id).join(", ");
1135
+ table.push([chalk6.green(c.provider), accounts]);
1136
+ }
1137
+ console.log(table.toString());
1138
+ console.log(chalk6.dim(`
1139
+ ${connected.length} provider(s) connected`));
1140
+ }
1141
+ });
1142
+ 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", `
1143
+ <endpoint> is the API path relative to the provider's base URL, OR a GraphQL query string for Linear.
1144
+
1145
+ Examples (REST):
1146
+ $ blink connector exec github /user/repos GET
1147
+ $ blink connector exec github /repos/owner/repo/issues POST '{"title":"Bug","body":"..."}'
1148
+ $ blink connector exec notion /search POST '{"query":"meeting notes"}'
1149
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"Hello"}'
1150
+ $ blink connector exec google_drive /files GET '{"q":"mimeType=\\'application/pdf\\'"}'
1151
+ $ blink connector exec jira /search GET '{"jql":"assignee=currentUser()","maxResults":20}'
1152
+ $ blink connector exec stripe /customers GET '{"limit":5}'
1153
+ $ blink connector exec shopify /orders.json GET
1154
+ $ blink connector exec zoom /users/me/meetings GET
1155
+ $ blink connector exec github /user/repos GET --json | jq '.[].full_name'
1156
+
1157
+ Examples (GraphQL \u2014 Linear only):
1158
+ $ blink connector exec linear '{ viewer { id name email } }' POST
1159
+ $ blink connector exec linear 'mutation { issueCreate(input: { title: "Bug", teamId: "xxx" }) { issue { id } } }' POST
1160
+
1161
+ Multiple accounts:
1162
+ $ blink connector exec github /user/repos GET --account acct_xxx
1163
+
1164
+ Provider base URLs used:
1165
+ github https://api.github.com/
1166
+ stripe https://api.stripe.com/v1/
1167
+ notion https://api.notion.com/v1/
1168
+ slack https://slack.com/api/
1169
+ google_gmail https://gmail.googleapis.com/gmail/v1/
1170
+ google_drive https://www.googleapis.com/drive/v3/
1171
+ google_calendar https://www.googleapis.com/calendar/v3/
1172
+ jira https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/
1173
+ shopify https://{shop}.myshopify.com/admin/api/2024-10/
1174
+ ... run \`blink connector providers\` for all 38 providers
1175
+ `).action(async (provider, endpoint, paramsArg, opts) => {
1041
1176
  requireToken();
1042
1177
  let params = {};
1043
1178
  if (paramsArg) {
@@ -1048,10 +1183,10 @@ Examples:
1048
1183
  }
1049
1184
  }
1050
1185
  const result = await withSpinner(
1051
- `Executing ${provider}/${action}...`,
1186
+ `${opts.method} ${provider}${endpoint}...`,
1052
1187
  () => resourcesRequest(`/v1/connectors/${provider}/execute`, {
1053
1188
  body: {
1054
- method: action,
1189
+ method: endpoint,
1055
1190
  http_method: opts.method,
1056
1191
  params,
1057
1192
  ...opts.account ? { account_id: opts.account } : {}
@@ -1059,7 +1194,7 @@ Examples:
1059
1194
  })
1060
1195
  );
1061
1196
  if (isJsonMode()) return printJson(result);
1062
- console.log(JSON.stringify(result, null, 2));
1197
+ console.log(JSON.stringify(result?.data ?? result, null, 2));
1063
1198
  });
1064
1199
  }
1065
1200
 
@@ -1101,7 +1236,7 @@ async function appRequest(path, opts = {}) {
1101
1236
  init_project();
1102
1237
  import { readdirSync, readFileSync as readFileSync8 } from "fs";
1103
1238
  import { join as join3, relative } from "path";
1104
- import chalk6 from "chalk";
1239
+ import chalk7 from "chalk";
1105
1240
  function collectFiles(dir) {
1106
1241
  const files = [];
1107
1242
  function walk(current) {
@@ -1174,7 +1309,7 @@ Project resolution:
1174
1309
  const production = opts.prod === true;
1175
1310
  if (!isJsonMode()) {
1176
1311
  console.log();
1177
- console.log(chalk6.bold(" Blink Deploy"));
1312
+ console.log(chalk7.bold(" Blink Deploy"));
1178
1313
  console.log();
1179
1314
  }
1180
1315
  const files = await withSpinner(`Packaging ${buildDir}...`, async () => collectFiles(buildDir));
@@ -1228,7 +1363,7 @@ Project resolution:
1228
1363
 
1229
1364
  // src/commands/project.ts
1230
1365
  init_project();
1231
- import chalk7 from "chalk";
1366
+ import chalk8 from "chalk";
1232
1367
  function registerProjectCommands(program2) {
1233
1368
  const project = program2.command("project").description("Create, list, and delete Blink projects").addHelpText("after", `
1234
1369
  Examples:
@@ -1257,8 +1392,8 @@ After creating a project, link it to your current directory:
1257
1392
  );
1258
1393
  if (isJsonMode()) return printJson(result);
1259
1394
  const proj = result?.project ?? result;
1260
- console.log(chalk7.green("\u2713") + ` Created: ${proj.id}`);
1261
- console.log(chalk7.dim(" Run `blink link " + proj.id + "` to use it"));
1395
+ console.log(chalk8.green("\u2713") + ` Created: ${proj.id}`);
1396
+ console.log(chalk8.dim(" Run `blink link " + proj.id + "` to use it"));
1262
1397
  });
1263
1398
  project.command("delete <project_id>").description("Delete a project").option("--yes", "Skip confirmation").action(async (projectId, opts) => {
1264
1399
  requireToken();
@@ -1301,7 +1436,7 @@ After linking, most commands work without specifying a project_id:
1301
1436
  });
1302
1437
  }
1303
1438
  writeProjectConfig({ projectId: id });
1304
- console.log(chalk7.green("\u2713") + " Linked to " + id);
1439
+ console.log(chalk8.green("\u2713") + " Linked to " + id);
1305
1440
  });
1306
1441
  program2.command("unlink").description("Remove project link from current directory").action(() => {
1307
1442
  clearProjectConfig();
@@ -1313,29 +1448,29 @@ After linking, most commands work without specifying a project_id:
1313
1448
  const agentId = resolveAgentId3();
1314
1449
  const agentSource = process.env.BLINK_AGENT_ID ? "BLINK_AGENT_ID env" : process.env.BLINK_ACTIVE_AGENT ? "BLINK_ACTIVE_AGENT env" : null;
1315
1450
  if (agentId) {
1316
- console.log(chalk7.bold("Agent ") + agentId + chalk7.dim(" (" + agentSource + ")"));
1451
+ console.log(chalk8.bold("Agent ") + agentId + chalk8.dim(" (" + agentSource + ")"));
1317
1452
  } else {
1318
- console.log(chalk7.dim("Agent not set (use: eval $(blink agent use clw_xxx --export))"));
1453
+ console.log(chalk8.dim("Agent not set (use: eval $(blink agent use clw_xxx --export))"));
1319
1454
  }
1320
1455
  if (config) {
1321
1456
  const projectSource = process.env.BLINK_ACTIVE_PROJECT ? "BLINK_ACTIVE_PROJECT env" : ".blink/project.json";
1322
- console.log(chalk7.bold("Project ") + config.projectId + chalk7.dim(" (" + projectSource + ")"));
1457
+ console.log(chalk8.bold("Project ") + config.projectId + chalk8.dim(" (" + projectSource + ")"));
1323
1458
  } else if (process.env.BLINK_ACTIVE_PROJECT) {
1324
- console.log(chalk7.bold("Project ") + process.env.BLINK_ACTIVE_PROJECT + chalk7.dim(" (BLINK_ACTIVE_PROJECT env)"));
1459
+ console.log(chalk8.bold("Project ") + process.env.BLINK_ACTIVE_PROJECT + chalk8.dim(" (BLINK_ACTIVE_PROJECT env)"));
1325
1460
  } else {
1326
- console.log(chalk7.dim("Project not linked (use: blink link or eval $(blink use proj_xxx --export))"));
1461
+ console.log(chalk8.dim("Project not linked (use: blink link or eval $(blink use proj_xxx --export))"));
1327
1462
  }
1328
1463
  const authSource = process.env.BLINK_API_KEY ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
1329
1464
  const hasProjectKey = !!process.env.BLINK_PROJECT_KEY;
1330
- console.log(chalk7.bold("Auth ") + authSource);
1465
+ console.log(chalk8.bold("Auth ") + authSource);
1331
1466
  if (hasProjectKey) {
1332
- console.log(chalk7.bold("ProjKey ") + "BLINK_PROJECT_KEY env" + chalk7.dim(" (used for db/storage/rag)"));
1467
+ console.log(chalk8.bold("ProjKey ") + "BLINK_PROJECT_KEY env" + chalk8.dim(" (used for db/storage/rag)"));
1333
1468
  }
1334
1469
  });
1335
1470
  }
1336
1471
 
1337
1472
  // src/commands/auth.ts
1338
- import chalk8 from "chalk";
1473
+ import chalk9 from "chalk";
1339
1474
  function registerAuthCommands(program2) {
1340
1475
  program2.command("login").description("Authenticate with your Blink API key").option("--interactive", "Prompt for API key (for headless/SSH/CI environments)").addHelpText("after", `
1341
1476
  Get your API key at: blink.new \u2192 Settings \u2192 API Keys (starts with blnk_ak_)
@@ -1348,7 +1483,7 @@ In Blink Claw agents: BLINK_API_KEY is already set \u2014 login is not needed.
1348
1483
  For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
1349
1484
  `).action(async (opts) => {
1350
1485
  if (process.env.BLINK_API_KEY && !opts.interactive) {
1351
- console.log(chalk8.green("\u2713") + " Already authenticated via BLINK_API_KEY env var.");
1486
+ console.log(chalk9.green("\u2713") + " Already authenticated via BLINK_API_KEY env var.");
1352
1487
  return;
1353
1488
  }
1354
1489
  const { password } = await import("@clack/prompts");
@@ -1358,7 +1493,7 @@ For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
1358
1493
  process.exit(1);
1359
1494
  }
1360
1495
  writeConfig({ api_key: apiKey });
1361
- console.log(chalk8.green("\u2713") + " Saved to ~/.config/blink/config.toml");
1496
+ console.log(chalk9.green("\u2713") + " Saved to ~/.config/blink/config.toml");
1362
1497
  });
1363
1498
  program2.command("logout").description("Remove stored credentials").action(() => {
1364
1499
  clearConfig();
@@ -1378,15 +1513,15 @@ For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
1378
1513
  });
1379
1514
  }
1380
1515
  const source = process.env.BLINK_API_KEY === token ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
1381
- console.log(chalk8.green("\u2713") + " Authenticated");
1382
- console.log(chalk8.bold("Key ") + token.slice(0, 20) + chalk8.dim("..."));
1383
- console.log(chalk8.bold("Source ") + chalk8.dim(source));
1516
+ console.log(chalk9.green("\u2713") + " Authenticated");
1517
+ console.log(chalk9.bold("Key ") + token.slice(0, 20) + chalk9.dim("..."));
1518
+ console.log(chalk9.bold("Source ") + chalk9.dim(source));
1384
1519
  });
1385
1520
  }
1386
1521
 
1387
1522
  // src/commands/agent.ts
1388
1523
  init_agent();
1389
- import chalk9 from "chalk";
1524
+ import chalk10 from "chalk";
1390
1525
  function registerAgentCommands(program2) {
1391
1526
  const agent = program2.command("agent").description("Manage Blink Claw agents in your workspace").addHelpText("after", `
1392
1527
  Examples:
@@ -1406,7 +1541,7 @@ Agent ID resolution for all agent/secrets commands (priority: high \u2192 low):
1406
1541
  if (isJsonMode()) return printJson(result);
1407
1542
  const agents = Array.isArray(result) ? result : result?.agents ?? [];
1408
1543
  if (!agents.length) {
1409
- console.log(chalk9.dim("No agents found. Deploy one at blink.new/claw"));
1544
+ console.log(chalk10.dim("No agents found. Deploy one at blink.new/claw"));
1410
1545
  return;
1411
1546
  }
1412
1547
  const table = createTable(["ID", "Name", "Status", "Size", "Model"]);
@@ -1429,9 +1564,9 @@ After setting, secrets commands use this agent automatically:
1429
1564
  process.stdout.write(`export BLINK_ACTIVE_AGENT=${agentId}
1430
1565
  `);
1431
1566
  } else {
1432
- console.log(chalk9.bold("Active agent: ") + agentId);
1433
- console.log(chalk9.dim(`Run: export BLINK_ACTIVE_AGENT=${agentId}`));
1434
- console.log(chalk9.dim(`Or: eval $(blink agent use ${agentId} --export)`));
1567
+ console.log(chalk10.bold("Active agent: ") + agentId);
1568
+ console.log(chalk10.dim(`Run: export BLINK_ACTIVE_AGENT=${agentId}`));
1569
+ console.log(chalk10.dim(`Or: eval $(blink agent use ${agentId} --export)`));
1435
1570
  }
1436
1571
  });
1437
1572
  agent.command("status [agent_id]").description("Show details for an agent").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
@@ -1447,18 +1582,18 @@ Examples:
1447
1582
  );
1448
1583
  if (isJsonMode()) return printJson(result);
1449
1584
  const a = result?.agent ?? result;
1450
- console.log(chalk9.bold("ID ") + a.id);
1451
- console.log(chalk9.bold("Name ") + a.name);
1452
- console.log(chalk9.bold("Status ") + a.status);
1453
- console.log(chalk9.bold("Model ") + (a.model ?? "-"));
1454
- console.log(chalk9.bold("Machine ") + (a.machine_size ?? "-"));
1455
- if (a.fly_app_name) console.log(chalk9.bold("Fly App ") + a.fly_app_name);
1585
+ console.log(chalk10.bold("ID ") + a.id);
1586
+ console.log(chalk10.bold("Name ") + a.name);
1587
+ console.log(chalk10.bold("Status ") + a.status);
1588
+ console.log(chalk10.bold("Model ") + (a.model ?? "-"));
1589
+ console.log(chalk10.bold("Machine ") + (a.machine_size ?? "-"));
1590
+ if (a.fly_app_name) console.log(chalk10.bold("Fly App ") + a.fly_app_name);
1456
1591
  });
1457
1592
  }
1458
1593
 
1459
1594
  // src/commands/secrets.ts
1460
1595
  init_agent();
1461
- import chalk10 from "chalk";
1596
+ import chalk11 from "chalk";
1462
1597
  function registerSecretsCommands(program2) {
1463
1598
  const secrets = program2.command("secrets").description("Manage encrypted secrets vault for a Claw agent").addHelpText("after", `
1464
1599
  Secrets are encrypted key-value pairs stored in the agent's vault.
@@ -1494,11 +1629,11 @@ Examples:
1494
1629
  if (isJsonMode()) return printJson(result);
1495
1630
  const keys = result?.secrets?.map((s) => s.key) ?? result?.keys ?? [];
1496
1631
  if (!keys.length) {
1497
- console.log(chalk10.dim("(no secrets set \u2014 use `blink secrets set KEY value`)"));
1632
+ console.log(chalk11.dim("(no secrets set \u2014 use `blink secrets set KEY value`)"));
1498
1633
  return;
1499
1634
  }
1500
- for (const k of keys) console.log(chalk10.bold(k));
1501
- console.log(chalk10.dim(`
1635
+ for (const k of keys) console.log(chalk11.bold(k));
1636
+ console.log(chalk11.dim(`
1502
1637
  ${keys.length} secret${keys.length === 1 ? "" : "s"} (values hidden)`));
1503
1638
  });
1504
1639
  secrets.command("set <key> <value>").description("Add or update a secret (stored encrypted, value never shown again)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
@@ -1521,8 +1656,8 @@ After setting, the secret is available as $KEY_NAME in agent shell commands.
1521
1656
  );
1522
1657
  const normalised = key.toUpperCase();
1523
1658
  if (!isJsonMode()) {
1524
- console.log(chalk10.green("\u2713") + ` ${normalised} saved`);
1525
- console.log(chalk10.dim(" Value hidden. Use $" + normalised + " in shell commands."));
1659
+ console.log(chalk11.green("\u2713") + ` ${normalised} saved`);
1660
+ console.log(chalk11.dim(" Value hidden. Use $" + normalised + " in shell commands."));
1526
1661
  } else {
1527
1662
  printJson({ status: "ok", key: normalised, agent_id: agentId });
1528
1663
  }
@@ -1606,9 +1741,17 @@ Realtime / RAG / Notify:
1606
1741
  $ blink rag search "how does billing work" --ai
1607
1742
  $ blink notify email user@example.com "Subject" "Body"
1608
1743
 
1609
- Connectors (Notion, Slack, Google, HubSpot, etc.):
1610
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
1611
- $ blink connector exec slack post_message '{"channel":"C123","text":"hi"}' --method POST
1744
+ Connectors (38 OAuth providers \u2014 GitHub, Notion, Slack, Stripe, Shopify, Jira, Linear, and more):
1745
+ $ blink connector providers List all 38 providers
1746
+ $ blink connector status Show all connected accounts
1747
+ $ blink connector status github Check a specific provider
1748
+ $ blink connector exec github /user/repos GET Call any REST endpoint
1749
+ $ blink connector exec notion /search POST '{"query":"notes"}' Notion search
1750
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"hi"}'
1751
+ $ blink connector exec stripe /customers GET '{"limit":5}'
1752
+ $ blink connector exec jira /search GET '{"jql":"assignee=currentUser()"}'
1753
+ $ blink connector exec linear '{ viewer { id name } }' POST GraphQL (Linear)
1754
+ Connect accounts at: blink.new/settings?tab=connectors
1612
1755
 
1613
1756
  Agents (Claw \u2014 zero config on Fly machines, BLINK_AGENT_ID is already set):
1614
1757
  $ blink agent list List all agents in workspace
@@ -1663,10 +1806,10 @@ After setting:
1663
1806
  process.stdout.write(`export BLINK_ACTIVE_PROJECT=${projectId}
1664
1807
  `);
1665
1808
  } else {
1666
- const { default: chalk11 } = await import("chalk");
1667
- console.log(chalk11.bold("Active project: ") + projectId);
1668
- console.log(chalk11.dim(`Run: export BLINK_ACTIVE_PROJECT=${projectId}`));
1669
- console.log(chalk11.dim(`Or: eval $(blink use ${projectId} --export)`));
1809
+ const { default: chalk12 } = await import("chalk");
1810
+ console.log(chalk12.bold("Active project: ") + projectId);
1811
+ console.log(chalk12.dim(`Run: export BLINK_ACTIVE_PROJECT=${projectId}`));
1812
+ console.log(chalk12.dim(`Or: eval $(blink use ${projectId} --export)`));
1670
1813
  }
1671
1814
  });
1672
1815
  program.action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Blink platform CLI — deploy apps, manage databases, generate AI content",
5
5
  "bin": {
6
6
  "blink": "dist/cli.js"
package/src/cli.ts CHANGED
@@ -78,9 +78,17 @@ Realtime / RAG / Notify:
78
78
  $ blink rag search "how does billing work" --ai
79
79
  $ blink notify email user@example.com "Subject" "Body"
80
80
 
81
- Connectors (Notion, Slack, Google, HubSpot, etc.):
82
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
83
- $ blink connector exec slack post_message '{"channel":"C123","text":"hi"}' --method POST
81
+ Connectors (38 OAuth providers — GitHub, Notion, Slack, Stripe, Shopify, Jira, Linear, and more):
82
+ $ blink connector providers List all 38 providers
83
+ $ blink connector status Show all connected accounts
84
+ $ blink connector status github Check a specific provider
85
+ $ blink connector exec github /user/repos GET Call any REST endpoint
86
+ $ blink connector exec notion /search POST '{"query":"notes"}' Notion search
87
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"hi"}'
88
+ $ blink connector exec stripe /customers GET '{"limit":5}'
89
+ $ blink connector exec jira /search GET '{"jql":"assignee=currentUser()"}'
90
+ $ blink connector exec linear '{ viewer { id name } }' POST GraphQL (Linear)
91
+ Connect accounts at: blink.new/settings?tab=connectors
84
92
 
85
93
  Agents (Claw — zero config on Fly machines, BLINK_AGENT_ID is already set):
86
94
  $ blink agent list List all agents in workspace
@@ -1,54 +1,203 @@
1
1
  import { Command } from 'commander'
2
2
  import { resourcesRequest } from '../lib/api-resources.js'
3
3
  import { requireToken } from '../lib/auth.js'
4
- import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
4
+ import { printJson, isJsonMode, withSpinner, createTable } from '../lib/output.js'
5
+ import chalk from 'chalk'
6
+
7
+ // All supported OAuth connector providers
8
+ const PROVIDERS = [
9
+ // Communication
10
+ { key: 'slack', label: 'Slack', category: 'Communication' },
11
+ { key: 'discord', label: 'Discord', category: 'Communication' },
12
+ // Email & Calendar
13
+ { key: 'google_gmail', label: 'Gmail', category: 'Email & Calendar' },
14
+ { key: 'google_calendar', label: 'Google Calendar', category: 'Email & Calendar' },
15
+ { key: 'microsoft_outlook', label: 'Outlook', category: 'Email & Calendar' },
16
+ { key: 'microsoft_calendar', label: 'Microsoft Calendar', category: 'Email & Calendar' },
17
+ // Files & Docs
18
+ { key: 'google_drive', label: 'Google Drive', category: 'Files & Docs' },
19
+ { key: 'google_docs', label: 'Google Docs', category: 'Files & Docs' },
20
+ { key: 'google_sheets', label: 'Google Sheets', category: 'Files & Docs' },
21
+ { key: 'google_slides', label: 'Google Slides', category: 'Files & Docs' },
22
+ { key: 'microsoft_onedrive', label: 'OneDrive', category: 'Files & Docs' },
23
+ { key: 'notion', label: 'Notion', category: 'Files & Docs' },
24
+ // Dev Tools
25
+ { key: 'github', label: 'GitHub', category: 'Dev Tools' },
26
+ { key: 'linear', label: 'Linear', category: 'Dev Tools' },
27
+ { key: 'jira', label: 'Jira', category: 'Dev Tools' },
28
+ { key: 'vercel', label: 'Vercel', category: 'Dev Tools' },
29
+ { key: 'figma', label: 'Figma', category: 'Dev Tools' },
30
+ // CRM & Sales
31
+ { key: 'hubspot', label: 'HubSpot', category: 'CRM & Sales' },
32
+ { key: 'salesforce', label: 'Salesforce', category: 'CRM & Sales' },
33
+ { key: 'pipedrive', label: 'Pipedrive', category: 'CRM & Sales' },
34
+ { key: 'attio', label: 'Attio', category: 'CRM & Sales' },
35
+ // Marketing
36
+ { key: 'mailchimp', label: 'Mailchimp', category: 'Marketing' },
37
+ { key: 'typeform', label: 'Typeform', category: 'Marketing' },
38
+ // Data & Productivity
39
+ { key: 'airtable', label: 'Airtable', category: 'Data & Productivity' },
40
+ { key: 'asana', label: 'Asana', category: 'Data & Productivity' },
41
+ { key: 'calendly', label: 'Calendly', category: 'Data & Productivity' },
42
+ // Social & Creator
43
+ { key: 'twitter', label: 'X (Twitter)', category: 'Social & Creator' },
44
+ { key: 'instagram', label: 'Instagram', category: 'Social & Creator' },
45
+ { key: 'tiktok', label: 'TikTok', category: 'Social & Creator' },
46
+ { key: 'youtube', label: 'YouTube', category: 'Social & Creator' },
47
+ { key: 'linkedin', label: 'LinkedIn', category: 'Social & Creator' },
48
+ { key: 'reddit', label: 'Reddit', category: 'Social & Creator' },
49
+ { key: 'loom', label: 'Loom', category: 'Social & Creator' },
50
+ // Meetings
51
+ { key: 'zoom', label: 'Zoom', category: 'Meetings' },
52
+ { key: 'microsoft_teams', label: 'Microsoft Teams', category: 'Meetings' },
53
+ // Commerce
54
+ { key: 'stripe', label: 'Stripe', category: 'Commerce' },
55
+ { key: 'shopify', label: 'Shopify', category: 'Commerce' },
56
+ { key: 'etsy', label: 'Etsy', category: 'Commerce' },
57
+ ] as const
5
58
 
6
59
  export function registerConnectorCommands(program: Command) {
7
60
  const connector = program.command('connector')
8
- .description('Execute actions on connected OAuth apps (Notion, Slack, Google, HubSpot, etc.)')
61
+ .description('Execute actions on connected OAuth apps (GitHub, Notion, Slack, Reddit, Google, and 30+ more)')
9
62
  .addHelpText('after', `
10
- Connectors are OAuth accounts linked to your workspace in blink.new → Settings → Integrations.
11
- Once linked, use blink connector exec to call their APIs without managing tokens yourself.
12
-
13
- Supported providers: notion, slack, discord, google_drive, google_calendar, google_sheets,
14
- google_docs, gmail, hubspot, airtable, microsoft, linkedin, salesforce
15
-
16
- Examples:
17
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
18
- $ blink connector exec slack post_message '{"channel":"C123","text":"Hello"}' --method POST
19
- $ blink connector exec google_calendar list_events '{}'
20
- $ blink connector exec google_sheets get_values '{"spreadsheet_id":"xxx","range":"A1:D10"}'
21
- $ blink connector exec hubspot get_contacts '{}'
22
- $ blink connector exec notion get_page '{"page_id":"abc-123"}' --method GET
23
- $ blink connector exec notion create_page '{"title":"New"}' --account acct_xxx
24
-
25
- Use --method GET for read operations, POST/PUT for writes (default: POST).
63
+ Connectors are OAuth accounts linked to your workspace at blink.new → Settings → Integrations.
64
+ Once linked, use \`blink connector exec\` to call their APIs without managing tokens yourself.
65
+
66
+ Connect accounts at: https://blink.new/settings?tab=connectors
67
+
68
+ Run \`blink connector providers\` to see all 39 supported providers.
69
+
70
+ Quick examples:
71
+ $ blink connector exec github /user/repos GET
72
+ $ blink connector exec notion /search POST '{"query":"meeting notes"}'
73
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"Hello"}'
74
+ $ blink connector exec google_calendar /calendars/primary/events GET
75
+ $ blink connector exec jira /issue POST '{"fields":{"project":{"key":"PROJ"},"summary":"Bug"}}'
76
+ $ blink connector exec stripe /customers GET '{"limit":10}'
77
+ $ blink connector exec shopify /orders.json GET
78
+ $ blink connector exec linear '{ viewer { id name } }' POST # GraphQL
79
+
80
+ Use --method GET for reads, POST for writes (default: POST).
81
+ Use --account <id> if you have multiple linked accounts for the same provider.
26
82
  `)
27
83
 
28
- connector.command('exec <provider> <action> [params]')
29
- .description('Execute an action on a connected OAuth provider')
30
- .option('--account <id>', 'Specific account ID (if you have multiple accounts for a provider)')
31
- .option('--method <method>', 'HTTP method: GET | POST | PUT | DELETE (default: POST)', 'POST')
84
+ // blink connector providers
85
+ connector.command('providers')
86
+ .description('List all supported OAuth connector providers')
87
+ .option('--category <cat>', 'Filter by category')
88
+ .action((opts) => {
89
+ const filtered = opts.category
90
+ ? PROVIDERS.filter(p => p.category.toLowerCase().includes(opts.category.toLowerCase()))
91
+ : PROVIDERS
92
+
93
+ if (isJsonMode()) return printJson(filtered)
94
+
95
+ const categories = [...new Set(filtered.map(p => p.category))]
96
+ for (const cat of categories) {
97
+ console.log(chalk.bold.cyan(`\n${cat}`))
98
+ const catProviders = filtered.filter(p => p.category === cat)
99
+ for (const p of catProviders) {
100
+ console.log(` ${chalk.green(p.key.padEnd(22))} ${chalk.dim(p.label)}`)
101
+ }
102
+ }
103
+ console.log(chalk.dim(`\n${filtered.length} providers total. Connect at blink.new/settings?tab=connectors`))
104
+ })
105
+
106
+ // blink connector status [provider]
107
+ connector.command('status [provider]')
108
+ .description('Check connection status for a provider (or all providers)')
109
+ .option('--account <id>', 'Check a specific account ID')
110
+ .action(async (provider: string | undefined, opts) => {
111
+ requireToken()
112
+ if (provider) {
113
+ const result = await withSpinner(`Checking ${provider} status...`, () =>
114
+ resourcesRequest(`/v1/connectors/${provider}/status`, {
115
+ method: 'GET',
116
+ ...(opts.account ? { query: { account_id: opts.account } } : {})
117
+ })
118
+ )
119
+ if (isJsonMode()) return printJson(result)
120
+ const d = result?.data ?? result
121
+ if (d?.connected) {
122
+ console.log(chalk.green('✓ Connected'))
123
+ if (d.account_id) console.log(chalk.dim(` Account: ${d.account_id}`))
124
+ if (d.metadata?.email) console.log(chalk.dim(` Email: ${d.metadata.email}`))
125
+ if (d.metadata?.name) console.log(chalk.dim(` Name: ${d.metadata.name}`))
126
+ } else {
127
+ console.log(chalk.red('✗ Not connected'))
128
+ console.log(chalk.dim(` Connect at blink.new/settings?tab=connectors`))
129
+ }
130
+ } else {
131
+ // List all connected providers
132
+ const result = await withSpinner('Checking all connectors...', () =>
133
+ resourcesRequest('/v1/connectors/linked', { method: 'GET' })
134
+ )
135
+ if (isJsonMode()) return printJson(result)
136
+ const connected: Array<{ provider: string; accounts: Array<{ account_id: string; label: string }> }> =
137
+ result?.data ?? []
138
+ if (!connected.length) {
139
+ console.log(chalk.dim('No connectors linked. Connect at blink.new/settings?tab=connectors'))
140
+ return
141
+ }
142
+ const table = createTable(['Provider', 'Accounts'])
143
+ for (const c of connected) {
144
+ const accounts = c.accounts.map(a => a.label || a.account_id).join(', ')
145
+ table.push([chalk.green(c.provider), accounts])
146
+ }
147
+ console.log(table.toString())
148
+ console.log(chalk.dim(`\n${connected.length} provider(s) connected`))
149
+ }
150
+ })
151
+
152
+ // blink connector exec <provider> <endpoint> [params]
153
+ connector.command('exec <provider> <endpoint> [params]')
154
+ .description('Execute a call on a connected OAuth provider')
155
+ .option('--account <id>', 'Specific account ID (if you have multiple accounts)')
156
+ .option('--method <method>', 'HTTP method: GET | POST | PUT | PATCH | DELETE (default: POST)', 'POST')
32
157
  .addHelpText('after', `
33
- Examples:
34
- $ blink connector exec notion search_pages '{"query":"meeting notes"}'
35
- $ blink connector exec notion create_page '{"title":"My Page"}' --method POST
36
- $ blink connector exec slack post_message '{"channel":"C123","text":"Hello"}' --method POST
37
- $ blink connector exec google_drive list_files '{"folder_id":"root"}' --method GET
38
- $ blink connector exec hubspot get_contacts '{}' --method GET
39
- $ blink connector exec linkedin get_profile '{}' --method GET
40
- $ blink connector exec notion search_pages '{"query":"docs"}' --json | jq '.results[].title'
158
+ <endpoint> is the API path relative to the provider's base URL, OR a GraphQL query string for Linear.
159
+
160
+ Examples (REST):
161
+ $ blink connector exec github /user/repos GET
162
+ $ blink connector exec github /repos/owner/repo/issues POST '{"title":"Bug","body":"..."}'
163
+ $ blink connector exec notion /search POST '{"query":"meeting notes"}'
164
+ $ blink connector exec slack /chat.postMessage POST '{"channel":"C123","text":"Hello"}'
165
+ $ blink connector exec google_drive /files GET '{"q":"mimeType=\\'application/pdf\\'"}'
166
+ $ blink connector exec jira /search GET '{"jql":"assignee=currentUser()","maxResults":20}'
167
+ $ blink connector exec stripe /customers GET '{"limit":5}'
168
+ $ blink connector exec shopify /orders.json GET
169
+ $ blink connector exec zoom /users/me/meetings GET
170
+ $ blink connector exec github /user/repos GET --json | jq '.[].full_name'
171
+
172
+ Examples (GraphQL — Linear only):
173
+ $ blink connector exec linear '{ viewer { id name email } }' POST
174
+ $ blink connector exec linear 'mutation { issueCreate(input: { title: "Bug", teamId: "xxx" }) { issue { id } } }' POST
175
+
176
+ Multiple accounts:
177
+ $ blink connector exec github /user/repos GET --account acct_xxx
178
+
179
+ Provider base URLs used:
180
+ github https://api.github.com/
181
+ stripe https://api.stripe.com/v1/
182
+ notion https://api.notion.com/v1/
183
+ slack https://slack.com/api/
184
+ google_gmail https://gmail.googleapis.com/gmail/v1/
185
+ google_drive https://www.googleapis.com/drive/v3/
186
+ google_calendar https://www.googleapis.com/calendar/v3/
187
+ jira https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/
188
+ shopify https://{shop}.myshopify.com/admin/api/2024-10/
189
+ ... run \`blink connector providers\` for all 38 providers
41
190
  `)
42
- .action(async (provider: string, action: string, paramsArg: string | undefined, opts) => {
191
+ .action(async (provider: string, endpoint: string, paramsArg: string | undefined, opts) => {
43
192
  requireToken()
44
193
  let params: Record<string, unknown> = {}
45
194
  if (paramsArg) {
46
195
  try { params = JSON.parse(paramsArg) } catch { params = {} }
47
196
  }
48
- const result = await withSpinner(`Executing ${provider}/${action}...`, () =>
197
+ const result = await withSpinner(`${opts.method} ${provider}${endpoint}...`, () =>
49
198
  resourcesRequest(`/v1/connectors/${provider}/execute`, {
50
199
  body: {
51
- method: action,
200
+ method: endpoint,
52
201
  http_method: opts.method,
53
202
  params,
54
203
  ...(opts.account ? { account_id: opts.account } : {}),
@@ -56,6 +205,6 @@ Examples:
56
205
  })
57
206
  )
58
207
  if (isJsonMode()) return printJson(result)
59
- console.log(JSON.stringify(result, null, 2))
208
+ console.log(JSON.stringify(result?.data ?? result, null, 2))
60
209
  })
61
210
  }