@blinkdotnew/cli 0.3.2 → 0.3.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
@@ -1127,6 +1127,7 @@ The email is sent from your project's configured sender address (set in blink.ne
1127
1127
  }
1128
1128
 
1129
1129
  // src/commands/connector.ts
1130
+ init_agent();
1130
1131
  import chalk6 from "chalk";
1131
1132
  var PROVIDERS = [
1132
1133
  // Communication
@@ -1233,8 +1234,10 @@ ${filtered.length} providers total. Connect at blink.new/settings?tab=connectors
1233
1234
  if (d.metadata?.email) console.log(chalk6.dim(` Email: ${d.metadata.email}`));
1234
1235
  if (d.metadata?.name) console.log(chalk6.dim(` Name: ${d.metadata.name}`));
1235
1236
  } else {
1237
+ const agentUrl = process.env.BLINK_WORKSPACE_SLUG && process.env.BLINK_AGENT_ID ? `https://blink.new/${process.env.BLINK_WORKSPACE_SLUG}/claw/${process.env.BLINK_AGENT_ID}` : "https://blink.new/settings?tab=connectors";
1236
1238
  console.log(chalk6.red("\u2717 Not connected"));
1237
- console.log(chalk6.dim(` Connect at blink.new/settings?tab=connectors`));
1239
+ console.log(chalk6.dim(` Fix: blink connector link ${provider}`));
1240
+ console.log(chalk6.dim(` Or visit: ${agentUrl}`));
1238
1241
  }
1239
1242
  } else {
1240
1243
  const result = await withSpinner(
@@ -1257,6 +1260,40 @@ ${filtered.length} providers total. Connect at blink.new/settings?tab=connectors
1257
1260
  ${connected.length} provider(s) connected`));
1258
1261
  }
1259
1262
  });
1263
+ connector.command("link <provider>").description("Auto-link a workspace connector to the current agent").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1264
+ Links the workspace's connected account for <provider> to this agent automatically.
1265
+ The workspace must already have the account connected at blink.new/settings?tab=connectors.
1266
+
1267
+ This is the self-service fix when an agent gets "not linked" errors \u2014 run this once
1268
+ and the connector will be available for all subsequent commands.
1269
+
1270
+ Examples:
1271
+ $ blink connector link linkedin Link LinkedIn to this agent
1272
+ $ blink connector link notion Link Notion to this agent
1273
+ $ blink connector link slack Link Slack to this agent
1274
+ $ blink connector link linkedin --json Machine-readable output
1275
+ `).action(async (provider, opts) => {
1276
+ requireToken();
1277
+ const agentId = requireAgentId(opts.agent);
1278
+ const result = await withSpinner(
1279
+ `Linking ${provider} to agent ${agentId}...`,
1280
+ () => resourcesRequest(`/v1/connectors/${provider}/link`, {
1281
+ method: "POST",
1282
+ headers: { "x-blink-agent-id": agentId },
1283
+ body: {}
1284
+ })
1285
+ );
1286
+ if (!result?.success) {
1287
+ const agentUrl = process.env.BLINK_WORKSPACE_SLUG && process.env.BLINK_AGENT_ID ? `https://blink.new/${process.env.BLINK_WORKSPACE_SLUG}/claw/${process.env.BLINK_AGENT_ID}` : "https://blink.new/settings?tab=connectors";
1288
+ console.error(chalk6.red(`\u2717 ${result?.error ?? `No ${provider} account in workspace`}`));
1289
+ console.error(chalk6.dim(` Connect one first: ${agentUrl}`));
1290
+ process.exit(1);
1291
+ }
1292
+ if (isJsonMode()) return printJson(result.data);
1293
+ const label = result.data?.account_label ?? provider;
1294
+ console.log(chalk6.green(`\u2713 ${provider} linked`) + chalk6.dim(` (${label})`));
1295
+ console.log(chalk6.dim(` Agent ${agentId} can now use blink connector exec ${provider}`));
1296
+ });
1260
1297
  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", `
1261
1298
  <endpoint> is the API path relative to the provider's base URL, OR a GraphQL query string for Linear.
1262
1299
 
@@ -1332,13 +1369,24 @@ Provider base URLs used:
1332
1369
  // src/commands/linkedin.ts
1333
1370
  init_agent();
1334
1371
  import chalk7 from "chalk";
1335
- var NOT_LINKED = "LinkedIn not linked. Link it in the Agent Integrations tab at blink.new/claw";
1372
+ function getAgentPageUrl() {
1373
+ const slug = process.env.BLINK_WORKSPACE_SLUG;
1374
+ const agentId = process.env.BLINK_AGENT_ID;
1375
+ if (slug && agentId) return `https://blink.new/${slug}/claw/${agentId}`;
1376
+ return "https://blink.new/claw";
1377
+ }
1378
+ function notLinkedError() {
1379
+ const url = getAgentPageUrl();
1380
+ return `LinkedIn not linked to this agent.
1381
+ Fix: blink connector link linkedin
1382
+ Or connect manually: ${url}`;
1383
+ }
1336
1384
  async function liExec(method, httpMethod, params, agentId) {
1337
1385
  const result = await resourcesRequest("/v1/connectors/linkedin/execute", {
1338
1386
  body: { method, http_method: httpMethod, params },
1339
1387
  headers: { "x-blink-agent-id": agentId }
1340
1388
  });
1341
- if (!result?.success) throw new Error(result?.error ?? NOT_LINKED);
1389
+ if (!result?.success) throw new Error(result?.error ?? notLinkedError());
1342
1390
  return result.data;
1343
1391
  }
1344
1392
  async function getPersonId(agentId) {
@@ -1642,7 +1690,8 @@ Examples:
1642
1690
  The number is permanently returned to the carrier pool. This action cannot be undone.
1643
1691
  `).action(async (id, opts) => {
1644
1692
  requireToken();
1645
- if (!opts.yes && !isJsonMode()) {
1693
+ const skipConfirm = process.argv.includes("--yes") || process.argv.includes("-y") || isJsonMode();
1694
+ if (!skipConfirm) {
1646
1695
  const { confirm } = await import("@clack/prompts");
1647
1696
  const yes = await confirm({ message: `Release ${id}? This cannot be undone.` });
1648
1697
  if (!yes) {
@@ -2050,6 +2099,41 @@ Examples:
2050
2099
  console.log(chalk11.bold("Machine ") + (a.machine_size ?? "-"));
2051
2100
  if (a.fly_app_name) console.log(chalk11.bold("Fly App ") + a.fly_app_name);
2052
2101
  });
2102
+ agent.command("url [agent_id]").description("Print the blink.new page URL for this agent (for sharing / setup links)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
2103
+ Returns the direct URL to this agent's page on blink.new.
2104
+ On Claw machines, uses BLINK_WORKSPACE_SLUG + BLINK_AGENT_ID env vars (no API call).
2105
+ Otherwise fetches workspace slug from the API.
2106
+
2107
+ Examples:
2108
+ $ blink agent url
2109
+ https://blink.new/kai/claw/clw_xxx
2110
+
2111
+ $ blink agent url --json
2112
+ {"url":"https://blink.new/kai/claw/clw_xxx","agent_id":"clw_xxx","workspace_slug":"kai"}
2113
+ `).action(async (agentIdArg, opts) => {
2114
+ requireToken();
2115
+ const agentId = requireAgentId(opts.agent ?? agentIdArg);
2116
+ const envSlug = process.env.BLINK_WORKSPACE_SLUG;
2117
+ if (envSlug) {
2118
+ const url2 = `https://blink.new/${envSlug}/claw/${agentId}`;
2119
+ if (isJsonMode()) return printJson({ url: url2, agent_id: agentId, workspace_slug: envSlug });
2120
+ console.log(url2);
2121
+ return;
2122
+ }
2123
+ const result = await withSpinner(
2124
+ "Fetching agent details...",
2125
+ () => appRequest(`/api/claw/agents/${agentId}`)
2126
+ );
2127
+ const a = result?.agent ?? result;
2128
+ const workspaceSlug = a?.workspace_slug ?? a?.workspace_id;
2129
+ if (!workspaceSlug) {
2130
+ console.error(chalk11.red("Could not determine workspace slug"));
2131
+ process.exit(1);
2132
+ }
2133
+ const url = `https://blink.new/${workspaceSlug}/claw/${agentId}`;
2134
+ if (isJsonMode()) return printJson({ url, agent_id: agentId, workspace_slug: workspaceSlug });
2135
+ console.log(url);
2136
+ });
2053
2137
  }
2054
2138
 
2055
2139
  // src/commands/secrets.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Blink platform CLI — deploy apps, manage databases, generate AI content",
5
5
  "bin": {
6
6
  "blink": "dist/cli.js"
@@ -86,4 +86,48 @@ Examples:
86
86
  console.log(chalk.bold('Machine ') + (a.machine_size ?? '-'))
87
87
  if (a.fly_app_name) console.log(chalk.bold('Fly App ') + a.fly_app_name)
88
88
  })
89
+
90
+ // blink agent url
91
+ agent.command('url [agent_id]')
92
+ .description('Print the blink.new page URL for this agent (for sharing / setup links)')
93
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)')
94
+ .addHelpText('after', `
95
+ Returns the direct URL to this agent's page on blink.new.
96
+ On Claw machines, uses BLINK_WORKSPACE_SLUG + BLINK_AGENT_ID env vars (no API call).
97
+ Otherwise fetches workspace slug from the API.
98
+
99
+ Examples:
100
+ $ blink agent url
101
+ https://blink.new/kai/claw/clw_xxx
102
+
103
+ $ blink agent url --json
104
+ {"url":"https://blink.new/kai/claw/clw_xxx","agent_id":"clw_xxx","workspace_slug":"kai"}
105
+ `)
106
+ .action(async (agentIdArg: string | undefined, opts) => {
107
+ requireToken()
108
+ const agentId = requireAgentId(opts.agent ?? agentIdArg)
109
+
110
+ // Fast path: env vars already set on Claw machines (no API call needed)
111
+ const envSlug = process.env.BLINK_WORKSPACE_SLUG
112
+ if (envSlug) {
113
+ const url = `https://blink.new/${envSlug}/claw/${agentId}`
114
+ if (isJsonMode()) return printJson({ url, agent_id: agentId, workspace_slug: envSlug })
115
+ console.log(url)
116
+ return
117
+ }
118
+
119
+ // Fallback: fetch workspace slug from API
120
+ const result = await withSpinner('Fetching agent details...', () =>
121
+ appRequest(`/api/claw/agents/${agentId}`)
122
+ )
123
+ const a = result?.agent ?? result
124
+ const workspaceSlug = a?.workspace_slug ?? a?.workspace_id
125
+ if (!workspaceSlug) {
126
+ console.error(chalk.red('Could not determine workspace slug'))
127
+ process.exit(1)
128
+ }
129
+ const url = `https://blink.new/${workspaceSlug}/claw/${agentId}`
130
+ if (isJsonMode()) return printJson({ url, agent_id: agentId, workspace_slug: workspaceSlug })
131
+ console.log(url)
132
+ })
89
133
  }
@@ -1,6 +1,7 @@
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 { requireAgentId } from '../lib/agent.js'
4
5
  import { printJson, isJsonMode, withSpinner, createTable } from '../lib/output.js'
5
6
  import chalk from 'chalk'
6
7
 
@@ -124,8 +125,12 @@ Use --account <id> if you have multiple linked accounts for the same provider.
124
125
  if (d.metadata?.email) console.log(chalk.dim(` Email: ${d.metadata.email}`))
125
126
  if (d.metadata?.name) console.log(chalk.dim(` Name: ${d.metadata.name}`))
126
127
  } else {
128
+ const agentUrl = process.env.BLINK_WORKSPACE_SLUG && process.env.BLINK_AGENT_ID
129
+ ? `https://blink.new/${process.env.BLINK_WORKSPACE_SLUG}/claw/${process.env.BLINK_AGENT_ID}`
130
+ : 'https://blink.new/settings?tab=connectors'
127
131
  console.log(chalk.red('✗ Not connected'))
128
- console.log(chalk.dim(` Connect at blink.new/settings?tab=connectors`))
132
+ console.log(chalk.dim(` Fix: blink connector link ${provider}`))
133
+ console.log(chalk.dim(` Or visit: ${agentUrl}`))
129
134
  }
130
135
  } else {
131
136
  // List all connected providers
@@ -149,6 +154,47 @@ Use --account <id> if you have multiple linked accounts for the same provider.
149
154
  }
150
155
  })
151
156
 
157
+ // blink connector link <provider>
158
+ connector.command('link <provider>')
159
+ .description('Auto-link a workspace connector to the current agent')
160
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
161
+ .addHelpText('after', `
162
+ Links the workspace's connected account for <provider> to this agent automatically.
163
+ The workspace must already have the account connected at blink.new/settings?tab=connectors.
164
+
165
+ This is the self-service fix when an agent gets "not linked" errors — run this once
166
+ and the connector will be available for all subsequent commands.
167
+
168
+ Examples:
169
+ $ blink connector link linkedin Link LinkedIn to this agent
170
+ $ blink connector link notion Link Notion to this agent
171
+ $ blink connector link slack Link Slack to this agent
172
+ $ blink connector link linkedin --json Machine-readable output
173
+ `)
174
+ .action(async (provider: string, opts) => {
175
+ requireToken()
176
+ const agentId = requireAgentId(opts.agent)
177
+ const result = await withSpinner(`Linking ${provider} to agent ${agentId}...`, () =>
178
+ resourcesRequest(`/v1/connectors/${provider}/link`, {
179
+ method: 'POST',
180
+ headers: { 'x-blink-agent-id': agentId },
181
+ body: {},
182
+ })
183
+ )
184
+ if (!result?.success) {
185
+ const agentUrl = process.env.BLINK_WORKSPACE_SLUG && process.env.BLINK_AGENT_ID
186
+ ? `https://blink.new/${process.env.BLINK_WORKSPACE_SLUG}/claw/${process.env.BLINK_AGENT_ID}`
187
+ : 'https://blink.new/settings?tab=connectors'
188
+ console.error(chalk.red(`✗ ${result?.error ?? `No ${provider} account in workspace`}`))
189
+ console.error(chalk.dim(` Connect one first: ${agentUrl}`))
190
+ process.exit(1)
191
+ }
192
+ if (isJsonMode()) return printJson(result.data)
193
+ const label = result.data?.account_label ?? provider
194
+ console.log(chalk.green(`✓ ${provider} linked`) + chalk.dim(` (${label})`))
195
+ console.log(chalk.dim(` Agent ${agentId} can now use blink connector exec ${provider}`))
196
+ })
197
+
152
198
  // blink connector exec <provider> <endpoint> [method-or-params] [params]
153
199
  // Supports both patterns:
154
200
  // blink connector exec github /user/repos GET
@@ -5,7 +5,19 @@ import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
5
5
  import { resourcesRequest } from '../lib/api-resources.js'
6
6
  import chalk from 'chalk'
7
7
 
8
- const NOT_LINKED = 'LinkedIn not linked. Link it in the Agent Integrations tab at blink.new/claw'
8
+ // Build agent page URL from env vars (set at machine creation via BLINK_WORKSPACE_SLUG).
9
+ // Falls back to generic URL if env vars aren't set (e.g. local dev).
10
+ function getAgentPageUrl(): string {
11
+ const slug = process.env.BLINK_WORKSPACE_SLUG
12
+ const agentId = process.env.BLINK_AGENT_ID
13
+ if (slug && agentId) return `https://blink.new/${slug}/claw/${agentId}`
14
+ return 'https://blink.new/claw'
15
+ }
16
+
17
+ function notLinkedError(): string {
18
+ const url = getAgentPageUrl()
19
+ return `LinkedIn not linked to this agent.\n Fix: blink connector link linkedin\n Or connect manually: ${url}`
20
+ }
9
21
 
10
22
  async function liExec(
11
23
  method: string,
@@ -17,7 +29,7 @@ async function liExec(
17
29
  body: { method, http_method: httpMethod, params },
18
30
  headers: { 'x-blink-agent-id': agentId },
19
31
  })
20
- if (!result?.success) throw new Error(result?.error ?? NOT_LINKED)
32
+ if (!result?.success) throw new Error(result?.error ?? notLinkedError())
21
33
  return result.data
22
34
  }
23
35
 
@@ -163,7 +163,8 @@ The number is permanently returned to the carrier pool. This action cannot be un
163
163
  `)
164
164
  .action(async (id: string, opts) => {
165
165
  requireToken()
166
- if (!opts.yes && !isJsonMode()) {
166
+ const skipConfirm = process.argv.includes('--yes') || process.argv.includes('-y') || isJsonMode()
167
+ if (!skipConfirm) {
167
168
  const { confirm } = await import('@clack/prompts')
168
169
  const yes = await confirm({ message: `Release ${id}? This cannot be undone.` })
169
170
  if (!yes) { console.log('Cancelled.'); return }