@blinkdotnew/cli 0.1.6 → 0.1.7

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
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __esm = (fn, res) => function __init() {
5
7
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
8
  };
@@ -8,6 +10,15 @@ var __export = (target, all) => {
8
10
  for (var name in all)
9
11
  __defProp(target, name, { get: all[name], enumerable: true });
10
12
  };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
11
22
 
12
23
  // src/lib/project.ts
13
24
  var project_exports = {};
@@ -53,6 +64,34 @@ var init_project = __esm({
53
64
  }
54
65
  });
55
66
 
67
+ // src/lib/agent.ts
68
+ var agent_exports = {};
69
+ __export(agent_exports, {
70
+ requireAgentId: () => requireAgentId,
71
+ resolveAgentId: () => resolveAgentId
72
+ });
73
+ function resolveAgentId(explicitId) {
74
+ if (explicitId) return explicitId;
75
+ if (process.env.BLINK_AGENT_ID) return process.env.BLINK_AGENT_ID;
76
+ if (process.env.BLINK_ACTIVE_AGENT) return process.env.BLINK_ACTIVE_AGENT;
77
+ return void 0;
78
+ }
79
+ function requireAgentId(explicitId) {
80
+ const id = resolveAgentId(explicitId);
81
+ if (!id) {
82
+ process.stderr.write(
83
+ "\nError: No agent set.\n On Claw machines this is automatic (BLINK_AGENT_ID is injected).\n Otherwise:\n blink agent list # find your agent ID\n eval $(blink agent use clw_xxx --export) # set it for this session\n blink secrets set --agent clw_xxx KEY val # or pass --agent directly\n"
84
+ );
85
+ process.exit(1);
86
+ }
87
+ return id;
88
+ }
89
+ var init_agent = __esm({
90
+ "src/lib/agent.ts"() {
91
+ "use strict";
92
+ }
93
+ });
94
+
56
95
  // src/cli.ts
57
96
  import { Command } from "commander";
58
97
  import { createRequire } from "module";
@@ -135,8 +174,12 @@ function requireToken() {
135
174
 
136
175
  // src/lib/api-resources.ts
137
176
  var BASE_URL = process.env.BLINK_APIS_URL ?? "https://core.blink.new";
177
+ function resolveResourceToken() {
178
+ if (process.env.BLINK_PROJECT_KEY) return process.env.BLINK_PROJECT_KEY;
179
+ return resolveToken();
180
+ }
138
181
  async function resourcesRequest(path, opts = {}) {
139
- const token = resolveToken();
182
+ const token = resolveResourceToken();
140
183
  const headers = {
141
184
  ...token ? { Authorization: `Bearer ${token}` } : {},
142
185
  ...process.env.BLINK_AGENT_ID ? { "x-blink-agent-id": process.env.BLINK_AGENT_ID } : {},
@@ -1204,16 +1247,30 @@ After linking, most commands work without specifying a project_id:
1204
1247
  clearProjectConfig();
1205
1248
  console.log("Unlinked.");
1206
1249
  });
1207
- program2.command("status").description("Show linked project and auth status").action(() => {
1250
+ program2.command("status").description("Show current agent, project, and auth context").action(() => {
1208
1251
  const config = readProjectConfig();
1209
- const token = process.env.BLINK_API_KEY ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
1252
+ const { resolveAgentId: resolveAgentId3 } = (init_agent(), __toCommonJS(agent_exports));
1253
+ const agentId = resolveAgentId3();
1254
+ const agentSource = process.env.BLINK_AGENT_ID ? "BLINK_AGENT_ID env" : process.env.BLINK_ACTIVE_AGENT ? "BLINK_ACTIVE_AGENT env" : null;
1255
+ if (agentId) {
1256
+ console.log(chalk7.bold("Agent ") + agentId + chalk7.dim(" (" + agentSource + ")"));
1257
+ } else {
1258
+ console.log(chalk7.dim("Agent not set (use: eval $(blink agent use clw_xxx --export))"));
1259
+ }
1210
1260
  if (config) {
1211
- console.log(chalk7.bold("Project ") + config.projectId);
1212
- if (config.workspaceId) console.log(chalk7.bold("Workspace") + " " + config.workspaceId);
1261
+ const projectSource = process.env.BLINK_ACTIVE_PROJECT ? "BLINK_ACTIVE_PROJECT env" : ".blink/project.json";
1262
+ console.log(chalk7.bold("Project ") + config.projectId + chalk7.dim(" (" + projectSource + ")"));
1263
+ } else if (process.env.BLINK_ACTIVE_PROJECT) {
1264
+ console.log(chalk7.bold("Project ") + process.env.BLINK_ACTIVE_PROJECT + chalk7.dim(" (BLINK_ACTIVE_PROJECT env)"));
1213
1265
  } else {
1214
- console.log(chalk7.dim("No project linked (run `blink link`)"));
1266
+ console.log(chalk7.dim("Project not linked (use: blink link or eval $(blink use proj_xxx --export))"));
1267
+ }
1268
+ const authSource = process.env.BLINK_API_KEY ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
1269
+ const hasProjectKey = !!process.env.BLINK_PROJECT_KEY;
1270
+ console.log(chalk7.bold("Auth ") + authSource);
1271
+ if (hasProjectKey) {
1272
+ console.log(chalk7.bold("ProjKey ") + "BLINK_PROJECT_KEY env" + chalk7.dim(" (used for db/storage/rag)"));
1215
1273
  }
1216
- console.log(chalk7.bold("Auth ") + token);
1217
1274
  });
1218
1275
  }
1219
1276
 
@@ -1267,6 +1324,173 @@ For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
1267
1324
  });
1268
1325
  }
1269
1326
 
1327
+ // src/commands/agent.ts
1328
+ init_agent();
1329
+ import chalk9 from "chalk";
1330
+ function registerAgentCommands(program2) {
1331
+ const agent = program2.command("agent").description("Manage Blink Claw agents in your workspace").addHelpText("after", `
1332
+ Examples:
1333
+ $ blink agent list List all agents in workspace
1334
+ $ blink agent use clw_xxx Show how to set active agent
1335
+ $ eval $(blink agent use clw_xxx --export) Set active agent for this session
1336
+ $ blink agent status Show current agent details
1337
+
1338
+ Agent ID resolution for all agent/secrets commands (priority: high \u2192 low):
1339
+ 1. --agent <id> flag
1340
+ 2. BLINK_AGENT_ID env var \u2190 always set on Claw Fly machines (zero config)
1341
+ 3. BLINK_ACTIVE_AGENT env \u2190 set via eval $(blink agent use clw_xxx --export)
1342
+ `);
1343
+ agent.command("list").description("List all Claw agents in the workspace").action(async () => {
1344
+ requireToken();
1345
+ const result = await withSpinner("Loading agents...", () => appRequest("/api/claw/agents"));
1346
+ if (isJsonMode()) return printJson(result);
1347
+ const agents = result?.agents ?? result ?? [];
1348
+ if (!agents.length) {
1349
+ console.log(chalk9.dim("No agents found. Deploy one at blink.new/claw"));
1350
+ return;
1351
+ }
1352
+ const table = createTable(["ID", "Name", "Status", "Size", "Model"]);
1353
+ for (const a of agents) {
1354
+ table.push([a.id, a.name, a.status, a.machine_size ?? "-", a.model ?? "-"]);
1355
+ }
1356
+ console.log(table.toString());
1357
+ });
1358
+ agent.command("use <agent_id>").description("Set active agent for this shell session").option("--export", "Output shell export statement for eval").addHelpText("after", `
1359
+ Examples:
1360
+ $ blink agent use clw_xxx Prints the export command to run
1361
+ $ eval $(blink agent use clw_xxx --export) Actually sets it in your shell
1362
+ $ export BLINK_ACTIVE_AGENT=clw_xxx Same thing, manual
1363
+
1364
+ After setting, secrets commands use this agent automatically:
1365
+ $ blink secrets list
1366
+ $ blink secrets set GITHUB_TOKEN ghp_xxx
1367
+ `).action(async (agentId, opts) => {
1368
+ if (opts.export) {
1369
+ process.stdout.write(`export BLINK_ACTIVE_AGENT=${agentId}
1370
+ `);
1371
+ } else {
1372
+ console.log(chalk9.bold("Active agent: ") + agentId);
1373
+ console.log(chalk9.dim(`Run: export BLINK_ACTIVE_AGENT=${agentId}`));
1374
+ console.log(chalk9.dim(`Or: eval $(blink agent use ${agentId} --export)`));
1375
+ }
1376
+ });
1377
+ 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", `
1378
+ Examples:
1379
+ $ blink agent status Show current agent (from BLINK_AGENT_ID)
1380
+ $ blink agent status clw_xxx Show specific agent
1381
+ `).action(async (agentIdArg, opts) => {
1382
+ requireToken();
1383
+ const agentId = requireAgentId(opts.agent ?? agentIdArg);
1384
+ const result = await withSpinner(
1385
+ "Loading agent...",
1386
+ () => appRequest(`/api/claw/agents/${agentId}`)
1387
+ );
1388
+ if (isJsonMode()) return printJson(result);
1389
+ const a = result?.agent ?? result;
1390
+ console.log(chalk9.bold("ID ") + a.id);
1391
+ console.log(chalk9.bold("Name ") + a.name);
1392
+ console.log(chalk9.bold("Status ") + a.status);
1393
+ console.log(chalk9.bold("Model ") + (a.model ?? "-"));
1394
+ console.log(chalk9.bold("Machine ") + (a.machine_size ?? "-"));
1395
+ if (a.fly_app_name) console.log(chalk9.bold("Fly App ") + a.fly_app_name);
1396
+ });
1397
+ }
1398
+
1399
+ // src/commands/secrets.ts
1400
+ init_agent();
1401
+ import chalk10 from "chalk";
1402
+ function registerSecretsCommands(program2) {
1403
+ const secrets = program2.command("secrets").description("Manage encrypted secrets vault for a Claw agent").addHelpText("after", `
1404
+ Secrets are encrypted key-value pairs stored in the agent's vault.
1405
+ They are available as $KEY_NAME in all shell commands run by the agent.
1406
+ Values are NEVER shown after being set \u2014 only key names are listed.
1407
+
1408
+ Agent ID resolution (priority: high \u2192 low):
1409
+ 1. --agent <id> flag
1410
+ 2. BLINK_AGENT_ID env var \u2190 always set on Claw Fly machines (zero config)
1411
+ 3. BLINK_ACTIVE_AGENT env \u2190 set via eval $(blink agent use clw_xxx --export)
1412
+
1413
+ Examples:
1414
+ $ blink secrets list List key names for current agent
1415
+ $ blink secrets set GITHUB_TOKEN ghp_xxx Add/update a secret
1416
+ $ blink secrets delete OLD_KEY Remove a secret
1417
+
1418
+ Cross-agent management (an agent manager setting secrets on another agent):
1419
+ $ blink secrets set --agent clw_other OPENAI_KEY sk-xxx
1420
+ $ blink secrets list --agent clw_other
1421
+ `);
1422
+ secrets.command("list").description("List secret key names (values are never displayed)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
1423
+ Examples:
1424
+ $ blink secrets list List current agent's secrets
1425
+ $ blink secrets list --agent clw_xxx List another agent's secrets
1426
+ $ blink secrets list --json Machine-readable output
1427
+ `).action(async (opts) => {
1428
+ requireToken();
1429
+ const agentId = requireAgentId(opts.agent);
1430
+ const result = await withSpinner(
1431
+ "Loading secrets...",
1432
+ () => appRequest(`/api/claw/agents/${agentId}/secrets`)
1433
+ );
1434
+ if (isJsonMode()) return printJson(result);
1435
+ const keys = result?.keys ?? result?.secrets?.map((s) => s.key) ?? result ?? [];
1436
+ if (!keys.length) {
1437
+ console.log(chalk10.dim("(no secrets set \u2014 use `blink secrets set KEY value`)"));
1438
+ return;
1439
+ }
1440
+ for (const k of keys) console.log(chalk10.bold(k));
1441
+ console.log(chalk10.dim(`
1442
+ ${keys.length} secret${keys.length === 1 ? "" : "s"} (values hidden)`));
1443
+ });
1444
+ 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", `
1445
+ Examples:
1446
+ $ blink secrets set GITHUB_TOKEN ghp_xxx
1447
+ $ blink secrets set OPENAI_KEY sk-xxx
1448
+ $ blink secrets set --agent clw_other DATABASE_URL postgres://...
1449
+
1450
+ Key naming convention: UPPER_SNAKE_CASE (e.g. GITHUB_TOKEN, OPENAI_KEY)
1451
+ After setting, the secret is available as $KEY_NAME in agent shell commands.
1452
+ `).action(async (key, value, opts) => {
1453
+ requireToken();
1454
+ const agentId = requireAgentId(opts.agent);
1455
+ await withSpinner(
1456
+ `Setting ${key}...`,
1457
+ () => appRequest(`/api/claw/agents/${agentId}/secrets`, {
1458
+ method: "POST",
1459
+ body: { key: key.toUpperCase(), value }
1460
+ })
1461
+ );
1462
+ if (!isJsonMode()) {
1463
+ console.log(chalk10.green("\u2713") + ` ${key.toUpperCase()} saved`);
1464
+ console.log(chalk10.dim(" Value hidden. Use $" + key.toUpperCase() + " in shell commands."));
1465
+ } else {
1466
+ printJson({ status: "ok", key: key.toUpperCase(), agent_id: agentId });
1467
+ }
1468
+ });
1469
+ secrets.command("delete <key>").description("Delete a secret from the agent vault").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").option("--yes", "Skip confirmation").addHelpText("after", `
1470
+ Examples:
1471
+ $ blink secrets delete OLD_KEY
1472
+ $ blink secrets delete OLD_KEY --yes
1473
+ $ blink secrets delete --agent clw_xxx OLD_KEY
1474
+ `).action(async (key, opts) => {
1475
+ requireToken();
1476
+ const agentId = requireAgentId(opts.agent);
1477
+ if (!opts.yes && !isJsonMode()) {
1478
+ const { confirm } = await import("@clack/prompts");
1479
+ const ok = await confirm({ message: `Delete secret "${key}" from agent ${agentId}?` });
1480
+ if (!ok) {
1481
+ console.log("Cancelled.");
1482
+ return;
1483
+ }
1484
+ }
1485
+ await withSpinner(
1486
+ `Deleting ${key}...`,
1487
+ () => appRequest(`/api/claw/agents/${agentId}/secrets/${key}`, { method: "DELETE" })
1488
+ );
1489
+ if (!isJsonMode()) console.log("Deleted: " + key);
1490
+ else printJson({ status: "ok", key, agent_id: agentId });
1491
+ });
1492
+ }
1493
+
1270
1494
  // src/cli.ts
1271
1495
  var require2 = createRequire(import.meta.url);
1272
1496
  var pkg = require2("../package.json");
@@ -1322,6 +1546,20 @@ Connectors (Notion, Slack, Google, HubSpot, etc.):
1322
1546
  $ blink connector exec notion search_pages '{"query":"meeting notes"}'
1323
1547
  $ blink connector exec slack post_message '{"channel":"C123","text":"hi"}' --method POST
1324
1548
 
1549
+ Agents (Claw \u2014 zero config on Fly machines, BLINK_AGENT_ID is already set):
1550
+ $ blink agent list List all agents in workspace
1551
+ $ blink agent status Show current agent details
1552
+ $ eval $(blink agent use clw_xxx --export) Set active agent for session
1553
+
1554
+ Secrets (encrypted agent vault \u2014 values never shown):
1555
+ $ blink secrets list List secret key names
1556
+ $ blink secrets set GITHUB_TOKEN ghp_xxx Add/update a secret
1557
+ $ blink secrets delete OLD_KEY Remove a secret
1558
+ $ blink secrets set --agent clw_other KEY v Set secret on another agent (agent manager pattern)
1559
+
1560
+ Tip \u2014 check full context (agent + project + auth):
1561
+ $ blink status
1562
+
1325
1563
  Scripting tip \u2014 add --json to any command for machine-readable output:
1326
1564
  $ blink deploy ./dist --prod --json | jq '.url'
1327
1565
  $ blink db query "SELECT email FROM users" --json | jq '.[].email'
@@ -1334,6 +1572,8 @@ Docs: https://blink.new/docs/cli
1334
1572
  if (opts.profile) setProfile(opts.profile);
1335
1573
  });
1336
1574
  registerAuthCommands(program);
1575
+ registerAgentCommands(program);
1576
+ registerSecretsCommands(program);
1337
1577
  registerProjectCommands(program);
1338
1578
  registerDeployCommands(program);
1339
1579
  registerAiCommands(program);
@@ -1359,10 +1599,10 @@ After setting:
1359
1599
  process.stdout.write(`export BLINK_ACTIVE_PROJECT=${projectId}
1360
1600
  `);
1361
1601
  } else {
1362
- const { default: chalk9 } = await import("chalk");
1363
- console.log(chalk9.bold("Active project: ") + projectId);
1364
- console.log(chalk9.dim(`Run: export BLINK_ACTIVE_PROJECT=${projectId}`));
1365
- console.log(chalk9.dim(`Or: eval $(blink use ${projectId} --export)`));
1602
+ const { default: chalk11 } = await import("chalk");
1603
+ console.log(chalk11.bold("Active project: ") + projectId);
1604
+ console.log(chalk11.dim(`Run: export BLINK_ACTIVE_PROJECT=${projectId}`));
1605
+ console.log(chalk11.dim(`Or: eval $(blink use ${projectId} --export)`));
1366
1606
  }
1367
1607
  });
1368
1608
  program.action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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
@@ -12,6 +12,8 @@ import { registerConnectorCommands } from './commands/connector.js'
12
12
  import { registerDeployCommands } from './commands/deploy.js'
13
13
  import { registerProjectCommands } from './commands/project.js'
14
14
  import { registerAuthCommands } from './commands/auth.js'
15
+ import { registerAgentCommands } from './commands/agent.js'
16
+ import { registerSecretsCommands } from './commands/secrets.js'
15
17
 
16
18
  const require = createRequire(import.meta.url)
17
19
  const pkg = require('../package.json') as { version: string }
@@ -78,6 +80,20 @@ Connectors (Notion, Slack, Google, HubSpot, etc.):
78
80
  $ blink connector exec notion search_pages '{"query":"meeting notes"}'
79
81
  $ blink connector exec slack post_message '{"channel":"C123","text":"hi"}' --method POST
80
82
 
83
+ Agents (Claw — zero config on Fly machines, BLINK_AGENT_ID is already set):
84
+ $ blink agent list List all agents in workspace
85
+ $ blink agent status Show current agent details
86
+ $ eval $(blink agent use clw_xxx --export) Set active agent for session
87
+
88
+ Secrets (encrypted agent vault — values never shown):
89
+ $ blink secrets list List secret key names
90
+ $ blink secrets set GITHUB_TOKEN ghp_xxx Add/update a secret
91
+ $ blink secrets delete OLD_KEY Remove a secret
92
+ $ blink secrets set --agent clw_other KEY v Set secret on another agent (agent manager pattern)
93
+
94
+ Tip — check full context (agent + project + auth):
95
+ $ blink status
96
+
81
97
  Scripting tip — add --json to any command for machine-readable output:
82
98
  $ blink deploy ./dist --prod --json | jq '.url'
83
99
  $ blink db query "SELECT email FROM users" --json | jq '.[].email'
@@ -92,6 +108,8 @@ Docs: https://blink.new/docs/cli
92
108
  })
93
109
 
94
110
  registerAuthCommands(program)
111
+ registerAgentCommands(program)
112
+ registerSecretsCommands(program)
95
113
  registerProjectCommands(program)
96
114
  registerDeployCommands(program)
97
115
  registerAiCommands(program)
@@ -0,0 +1,89 @@
1
+ import { Command } from 'commander'
2
+ import { appRequest } from '../lib/api-app.js'
3
+ import { requireToken } from '../lib/auth.js'
4
+ import { requireAgentId, resolveAgentId } from '../lib/agent.js'
5
+ import { printJson, isJsonMode, withSpinner, createTable } from '../lib/output.js'
6
+ import chalk from 'chalk'
7
+
8
+ export function registerAgentCommands(program: Command) {
9
+ const agent = program.command('agent')
10
+ .description('Manage Blink Claw agents in your workspace')
11
+ .addHelpText('after', `
12
+ Examples:
13
+ $ blink agent list List all agents in workspace
14
+ $ blink agent use clw_xxx Show how to set active agent
15
+ $ eval $(blink agent use clw_xxx --export) Set active agent for this session
16
+ $ blink agent status Show current agent details
17
+
18
+ Agent ID resolution for all agent/secrets commands (priority: high → low):
19
+ 1. --agent <id> flag
20
+ 2. BLINK_AGENT_ID env var ← always set on Claw Fly machines (zero config)
21
+ 3. BLINK_ACTIVE_AGENT env ← set via eval \$(blink agent use clw_xxx --export)
22
+ `)
23
+
24
+ agent.command('list')
25
+ .description('List all Claw agents in the workspace')
26
+ .action(async () => {
27
+ requireToken()
28
+ const result = await withSpinner('Loading agents...', () => appRequest('/api/claw/agents'))
29
+ if (isJsonMode()) return printJson(result)
30
+ const agents: Array<{ id: string; name: string; status: string; model?: string; machine_size?: string }> =
31
+ result?.agents ?? result ?? []
32
+ if (!agents.length) {
33
+ console.log(chalk.dim('No agents found. Deploy one at blink.new/claw'))
34
+ return
35
+ }
36
+ const table = createTable(['ID', 'Name', 'Status', 'Size', 'Model'])
37
+ for (const a of agents) {
38
+ table.push([a.id, a.name, a.status, a.machine_size ?? '-', a.model ?? '-'])
39
+ }
40
+ console.log(table.toString())
41
+ })
42
+
43
+ agent.command('use <agent_id>')
44
+ .description('Set active agent for this shell session')
45
+ .option('--export', 'Output shell export statement for eval')
46
+ .addHelpText('after', `
47
+ Examples:
48
+ $ blink agent use clw_xxx Prints the export command to run
49
+ $ eval $(blink agent use clw_xxx --export) Actually sets it in your shell
50
+ $ export BLINK_ACTIVE_AGENT=clw_xxx Same thing, manual
51
+
52
+ After setting, secrets commands use this agent automatically:
53
+ $ blink secrets list
54
+ $ blink secrets set GITHUB_TOKEN ghp_xxx
55
+ `)
56
+ .action(async (agentId: string, opts) => {
57
+ if (opts.export) {
58
+ process.stdout.write(`export BLINK_ACTIVE_AGENT=${agentId}\n`)
59
+ } else {
60
+ console.log(chalk.bold('Active agent: ') + agentId)
61
+ console.log(chalk.dim(`Run: export BLINK_ACTIVE_AGENT=${agentId}`))
62
+ console.log(chalk.dim(`Or: eval $(blink agent use ${agentId} --export)`))
63
+ }
64
+ })
65
+
66
+ agent.command('status [agent_id]')
67
+ .description('Show details for an agent')
68
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)')
69
+ .addHelpText('after', `
70
+ Examples:
71
+ $ blink agent status Show current agent (from BLINK_AGENT_ID)
72
+ $ blink agent status clw_xxx Show specific agent
73
+ `)
74
+ .action(async (agentIdArg: string | undefined, opts) => {
75
+ requireToken()
76
+ const agentId = requireAgentId(opts.agent ?? agentIdArg)
77
+ const result = await withSpinner('Loading agent...', () =>
78
+ appRequest(`/api/claw/agents/${agentId}`)
79
+ )
80
+ if (isJsonMode()) return printJson(result)
81
+ const a = result?.agent ?? result
82
+ console.log(chalk.bold('ID ') + a.id)
83
+ console.log(chalk.bold('Name ') + a.name)
84
+ console.log(chalk.bold('Status ') + a.status)
85
+ console.log(chalk.bold('Model ') + (a.model ?? '-'))
86
+ console.log(chalk.bold('Machine ') + (a.machine_size ?? '-'))
87
+ if (a.fly_app_name) console.log(chalk.bold('Fly App ') + a.fly_app_name)
88
+ })
89
+ }
@@ -99,16 +99,42 @@ After linking, most commands work without specifying a project_id:
99
99
  })
100
100
 
101
101
  program.command('status')
102
- .description('Show linked project and auth status')
102
+ .description('Show current agent, project, and auth context')
103
103
  .action(() => {
104
104
  const config = readProjectConfig()
105
- const token = process.env.BLINK_API_KEY ? 'BLINK_API_KEY env' : '~/.config/blink/config.toml'
105
+ const { resolveAgentId } = require('../lib/agent.js') as typeof import('../lib/agent.js')
106
+
107
+ // Agent context
108
+ const agentId = resolveAgentId()
109
+ const agentSource = process.env.BLINK_AGENT_ID
110
+ ? 'BLINK_AGENT_ID env'
111
+ : process.env.BLINK_ACTIVE_AGENT
112
+ ? 'BLINK_ACTIVE_AGENT env'
113
+ : null
114
+ if (agentId) {
115
+ console.log(chalk.bold('Agent ') + agentId + chalk.dim(' (' + agentSource + ')'))
116
+ } else {
117
+ console.log(chalk.dim('Agent not set (use: eval $(blink agent use clw_xxx --export))'))
118
+ }
119
+
120
+ // Project context
106
121
  if (config) {
107
- console.log(chalk.bold('Project ') + config.projectId)
108
- if (config.workspaceId) console.log(chalk.bold('Workspace') + ' ' + config.workspaceId)
122
+ const projectSource = process.env.BLINK_ACTIVE_PROJECT
123
+ ? 'BLINK_ACTIVE_PROJECT env'
124
+ : '.blink/project.json'
125
+ console.log(chalk.bold('Project ') + config.projectId + chalk.dim(' (' + projectSource + ')'))
126
+ } else if (process.env.BLINK_ACTIVE_PROJECT) {
127
+ console.log(chalk.bold('Project ') + process.env.BLINK_ACTIVE_PROJECT + chalk.dim(' (BLINK_ACTIVE_PROJECT env)'))
109
128
  } else {
110
- console.log(chalk.dim('No project linked (run `blink link`)'))
129
+ console.log(chalk.dim('Project not linked (use: blink link or eval $(blink use proj_xxx --export))'))
130
+ }
131
+
132
+ // Auth
133
+ const authSource = process.env.BLINK_API_KEY ? 'BLINK_API_KEY env' : '~/.config/blink/config.toml'
134
+ const hasProjectKey = !!process.env.BLINK_PROJECT_KEY
135
+ console.log(chalk.bold('Auth ') + authSource)
136
+ if (hasProjectKey) {
137
+ console.log(chalk.bold('ProjKey ') + 'BLINK_PROJECT_KEY env' + chalk.dim(' (used for db/storage/rag)'))
111
138
  }
112
- console.log(chalk.bold('Auth ') + token)
113
139
  })
114
140
  }
@@ -0,0 +1,109 @@
1
+ import { Command } from 'commander'
2
+ import { appRequest } from '../lib/api-app.js'
3
+ import { requireToken } from '../lib/auth.js'
4
+ import { requireAgentId } from '../lib/agent.js'
5
+ import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
6
+ import chalk from 'chalk'
7
+
8
+ export function registerSecretsCommands(program: Command) {
9
+ const secrets = program.command('secrets')
10
+ .description('Manage encrypted secrets vault for a Claw agent')
11
+ .addHelpText('after', `
12
+ Secrets are encrypted key-value pairs stored in the agent's vault.
13
+ They are available as $KEY_NAME in all shell commands run by the agent.
14
+ Values are NEVER shown after being set — only key names are listed.
15
+
16
+ Agent ID resolution (priority: high → low):
17
+ 1. --agent <id> flag
18
+ 2. BLINK_AGENT_ID env var ← always set on Claw Fly machines (zero config)
19
+ 3. BLINK_ACTIVE_AGENT env ← set via eval \$(blink agent use clw_xxx --export)
20
+
21
+ Examples:
22
+ $ blink secrets list List key names for current agent
23
+ $ blink secrets set GITHUB_TOKEN ghp_xxx Add/update a secret
24
+ $ blink secrets delete OLD_KEY Remove a secret
25
+
26
+ Cross-agent management (an agent manager setting secrets on another agent):
27
+ $ blink secrets set --agent clw_other OPENAI_KEY sk-xxx
28
+ $ blink secrets list --agent clw_other
29
+ `)
30
+
31
+ secrets.command('list')
32
+ .description('List secret key names (values are never displayed)')
33
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)')
34
+ .addHelpText('after', `
35
+ Examples:
36
+ $ blink secrets list List current agent's secrets
37
+ $ blink secrets list --agent clw_xxx List another agent's secrets
38
+ $ blink secrets list --json Machine-readable output
39
+ `)
40
+ .action(async (opts) => {
41
+ requireToken()
42
+ const agentId = requireAgentId(opts.agent)
43
+ const result = await withSpinner('Loading secrets...', () =>
44
+ appRequest(`/api/claw/agents/${agentId}/secrets`)
45
+ )
46
+ if (isJsonMode()) return printJson(result)
47
+ const keys: string[] = result?.keys ?? result?.secrets?.map((s: { key: string }) => s.key) ?? result ?? []
48
+ if (!keys.length) {
49
+ console.log(chalk.dim('(no secrets set — use `blink secrets set KEY value`)'))
50
+ return
51
+ }
52
+ for (const k of keys) console.log(chalk.bold(k))
53
+ console.log(chalk.dim(`\n${keys.length} secret${keys.length === 1 ? '' : 's'} (values hidden)`))
54
+ })
55
+
56
+ secrets.command('set <key> <value>')
57
+ .description('Add or update a secret (stored encrypted, value never shown again)')
58
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)')
59
+ .addHelpText('after', `
60
+ Examples:
61
+ $ blink secrets set GITHUB_TOKEN ghp_xxx
62
+ $ blink secrets set OPENAI_KEY sk-xxx
63
+ $ blink secrets set --agent clw_other DATABASE_URL postgres://...
64
+
65
+ Key naming convention: UPPER_SNAKE_CASE (e.g. GITHUB_TOKEN, OPENAI_KEY)
66
+ After setting, the secret is available as $KEY_NAME in agent shell commands.
67
+ `)
68
+ .action(async (key: string, value: string, opts) => {
69
+ requireToken()
70
+ const agentId = requireAgentId(opts.agent)
71
+ await withSpinner(`Setting ${key}...`, () =>
72
+ appRequest(`/api/claw/agents/${agentId}/secrets`, {
73
+ method: 'POST',
74
+ body: { key: key.toUpperCase(), value },
75
+ })
76
+ )
77
+ if (!isJsonMode()) {
78
+ console.log(chalk.green('✓') + ` ${key.toUpperCase()} saved`)
79
+ console.log(chalk.dim(' Value hidden. Use $' + key.toUpperCase() + ' in shell commands.'))
80
+ } else {
81
+ printJson({ status: 'ok', key: key.toUpperCase(), agent_id: agentId })
82
+ }
83
+ })
84
+
85
+ secrets.command('delete <key>')
86
+ .description('Delete a secret from the agent vault')
87
+ .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)')
88
+ .option('--yes', 'Skip confirmation')
89
+ .addHelpText('after', `
90
+ Examples:
91
+ $ blink secrets delete OLD_KEY
92
+ $ blink secrets delete OLD_KEY --yes
93
+ $ blink secrets delete --agent clw_xxx OLD_KEY
94
+ `)
95
+ .action(async (key: string, opts) => {
96
+ requireToken()
97
+ const agentId = requireAgentId(opts.agent)
98
+ if (!opts.yes && !isJsonMode()) {
99
+ const { confirm } = await import('@clack/prompts')
100
+ const ok = await confirm({ message: `Delete secret "${key}" from agent ${agentId}?` })
101
+ if (!ok) { console.log('Cancelled.'); return }
102
+ }
103
+ await withSpinner(`Deleting ${key}...`, () =>
104
+ appRequest(`/api/claw/agents/${agentId}/secrets/${key}`, { method: 'DELETE' })
105
+ )
106
+ if (!isJsonMode()) console.log('Deleted: ' + key)
107
+ else printJson({ status: 'ok', key, agent_id: agentId })
108
+ })
109
+ }
@@ -0,0 +1,29 @@
1
+ // Agent ID resolution — parallel to lib/project.ts for project ID resolution
2
+ //
3
+ // Priority (high → low):
4
+ // 1. Explicit --agent <id> flag passed to the command
5
+ // 2. BLINK_AGENT_ID env var ← always set on Claw Fly machines (zero config)
6
+ // 3. BLINK_ACTIVE_AGENT env ← set via `eval $(blink agent use clw_xxx --export)`
7
+
8
+ export function resolveAgentId(explicitId?: string): string | undefined {
9
+ if (explicitId) return explicitId
10
+ if (process.env.BLINK_AGENT_ID) return process.env.BLINK_AGENT_ID
11
+ if (process.env.BLINK_ACTIVE_AGENT) return process.env.BLINK_ACTIVE_AGENT
12
+ return undefined
13
+ }
14
+
15
+ export function requireAgentId(explicitId?: string): string {
16
+ const id = resolveAgentId(explicitId)
17
+ if (!id) {
18
+ process.stderr.write(
19
+ '\nError: No agent set.\n' +
20
+ ' On Claw machines this is automatic (BLINK_AGENT_ID is injected).\n' +
21
+ ' Otherwise:\n' +
22
+ ' blink agent list # find your agent ID\n' +
23
+ ' eval $(blink agent use clw_xxx --export) # set it for this session\n' +
24
+ ' blink secrets set --agent clw_xxx KEY val # or pass --agent directly\n'
25
+ )
26
+ process.exit(1)
27
+ }
28
+ return id
29
+ }
@@ -2,6 +2,14 @@ import { resolveToken } from './auth.js'
2
2
 
3
3
  const BASE_URL = process.env.BLINK_APIS_URL ?? 'https://core.blink.new'
4
4
 
5
+ // BLINK_PROJECT_KEY overrides the workspace key for project-scoped endpoints
6
+ // (db, storage, realtime, rag, notifications). Set it per-command:
7
+ // BLINK_PROJECT_KEY=$BLINK_PROJECT_KEY_1 blink db query "SELECT * FROM users"
8
+ function resolveResourceToken(): string | undefined {
9
+ if (process.env.BLINK_PROJECT_KEY) return process.env.BLINK_PROJECT_KEY
10
+ return resolveToken()
11
+ }
12
+
5
13
  interface RequestOptions {
6
14
  method?: string
7
15
  body?: unknown
@@ -10,7 +18,7 @@ interface RequestOptions {
10
18
  }
11
19
 
12
20
  export async function resourcesRequest(path: string, opts: RequestOptions = {}) {
13
- const token = resolveToken()
21
+ const token = resolveResourceToken()
14
22
  const headers: Record<string, string> = {
15
23
  ...(token ? { Authorization: `Bearer ${token}` } : {}),
16
24
  ...(process.env.BLINK_AGENT_ID ? { 'x-blink-agent-id': process.env.BLINK_AGENT_ID } : {}),