blink 1.1.33 → 1.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/browser/control/index.d.cts +28 -20
  2. package/dist/browser/control/index.d.ts +28 -20
  3. package/dist/cli/chat-BfOX7Zpn.js +1 -0
  4. package/dist/cli/chat-manager-cUXPlLU4.js +21 -0
  5. package/dist/cli/compute-server-BFNeHOmk.js +1 -0
  6. package/dist/cli/connect-3pBvxI6u.js +1 -0
  7. package/dist/cli/connect-XRcF_tn8.js +1 -0
  8. package/dist/cli/create-github-app-CaCP607a.js +16 -0
  9. package/dist/cli/{dev-DTCSw4OQ.js → dev-DOvBAIjm.js} +222 -236
  10. package/dist/cli/{getMachineId-bsd-BECgXpZb.js → getMachineId-bsd-BfwVPdTh.js} +1 -1
  11. package/dist/cli/{getMachineId-bsd-BgAyii4H.js → getMachineId-bsd-BmhpEQU_.js} +1 -1
  12. package/dist/cli/{getMachineId-bsd-Cc73dhdJ.js → getMachineId-bsd-C1eeoY31.js} +1 -1
  13. package/dist/cli/{getMachineId-bsd-DJn61I3m.js → getMachineId-bsd-CO9ne--Y.js} +1 -1
  14. package/dist/cli/{getMachineId-bsd-Doah6QJP.js → getMachineId-bsd-D4uBPoNF.js} +1 -1
  15. package/dist/cli/getMachineId-bsd-DVGtITCk.js +1 -0
  16. package/dist/cli/getMachineId-bsd-MA4oFaCa.js +1 -0
  17. package/dist/cli/getMachineId-bsd-dbToLPez.js +1 -0
  18. package/dist/cli/{getMachineId-darwin-BMnMBte0.js → getMachineId-darwin-C0EZ3sY3.js} +1 -1
  19. package/dist/cli/{getMachineId-darwin-BP9BSYcd.js → getMachineId-darwin-CSfD7gS4.js} +1 -1
  20. package/dist/cli/{getMachineId-darwin-Bs3mlKrs.js → getMachineId-darwin-CgQ9n4TU.js} +1 -1
  21. package/dist/cli/{getMachineId-darwin-DFronuaU.js → getMachineId-darwin-Cpq0Lloc.js} +1 -1
  22. package/dist/cli/{getMachineId-darwin-D-l4_nyT.js → getMachineId-darwin-CuAzoP7O.js} +1 -1
  23. package/dist/cli/getMachineId-darwin-D6_tdcZj.js +2 -0
  24. package/dist/cli/getMachineId-darwin-DkExT2qu.js +2 -0
  25. package/dist/cli/getMachineId-darwin-xoAzTD7x.js +2 -0
  26. package/dist/cli/{getMachineId-linux-B2r_HEgX.js → getMachineId-linux-B1XJTHUh.js} +1 -1
  27. package/dist/cli/{getMachineId-linux-Bj52Hdvy.js → getMachineId-linux-CRUR6LbQ.js} +1 -1
  28. package/dist/cli/{getMachineId-linux-C9o1dYwI.js → getMachineId-linux-CVQNNGui.js} +1 -1
  29. package/dist/cli/{getMachineId-linux-CGcBOg7J.js → getMachineId-linux-CtdZRCaX.js} +1 -1
  30. package/dist/cli/getMachineId-linux-DMILcDJR.js +1 -0
  31. package/dist/cli/getMachineId-linux-Deja7F2k.js +1 -0
  32. package/dist/cli/getMachineId-linux-DqOGXYgv.js +1 -0
  33. package/dist/cli/getMachineId-linux-FRx9q8NV.js +1 -0
  34. package/dist/cli/{getMachineId-unsupported-BtmyS2eg.js → getMachineId-unsupported-B-xvzil1.js} +1 -1
  35. package/dist/cli/{getMachineId-unsupported-C8az6JKm.js → getMachineId-unsupported-BA3eQjiV.js} +1 -1
  36. package/dist/cli/{getMachineId-unsupported-CBS2qXAs.js → getMachineId-unsupported-BZCOxs7n.js} +1 -1
  37. package/dist/cli/{getMachineId-unsupported-Cu0DvxaU.js → getMachineId-unsupported-BjGFauqb.js} +1 -1
  38. package/dist/cli/getMachineId-unsupported-CLWxRb7f.js +1 -0
  39. package/dist/cli/getMachineId-unsupported-CSVFDvWp.js +1 -0
  40. package/dist/cli/getMachineId-unsupported-Cp_95R-6.js +1 -0
  41. package/dist/cli/getMachineId-unsupported-Nd0_Z7NT.js +1 -0
  42. package/dist/cli/{getMachineId-win-BCAO7ld1.js → getMachineId-win-AW-Mv9PG.js} +1 -1
  43. package/dist/cli/{getMachineId-win-BDQszRKi.js → getMachineId-win-B_2C1cwE.js} +1 -1
  44. package/dist/cli/{getMachineId-win-BOnF6rL7.js → getMachineId-win-BbYxddZ9.js} +1 -1
  45. package/dist/cli/{getMachineId-win-BOuIHcz_.js → getMachineId-win-BkozcdSh.js} +1 -1
  46. package/dist/cli/getMachineId-win-CPhFagnx.js +1 -0
  47. package/dist/cli/{getMachineId-win-pnMl0wak.js → getMachineId-win-CUeKGXdr.js} +1 -1
  48. package/dist/cli/getMachineId-win-D3-sMfSu.js +1 -0
  49. package/dist/cli/getMachineId-win-DFVpvzl3.js +1 -0
  50. package/dist/cli/index.js +7 -7
  51. package/dist/cli/init-LtTUYXzn.js +23 -0
  52. package/dist/cli/{init-templates-D4lwX_8M.js → init-templates-jGRvqLQ6.js} +614 -1
  53. package/dist/cli/run-BHKBjgkK.js +1 -0
  54. package/dist/cli/setup-github-app-DBiuq7cy.js +9 -0
  55. package/dist/cli/token-util-3NkPUSqu.js +1 -0
  56. package/dist/cli/{token-util-BKLboTb2.js → token-util-BgEcL2NM.js} +1 -1
  57. package/dist/cli/{token-Bk68OrJt.js → token-v4NJW7VU.js} +1 -1
  58. package/dist/cli/undici-JD4hDNWZ.js +1 -0
  59. package/dist/cli/utils-B3iC7vdW.js +1 -0
  60. package/dist/node/agent/index.node.cjs +1 -1
  61. package/dist/node/agent/index.node.js +1 -1
  62. package/dist/node/{index.node-Bvp48I1R.cjs → index.node-CFR7FTuY.cjs} +4 -4
  63. package/dist/node/{index.node-ppzOWVGE.js → index.node-CITcyk6b.js} +4 -4
  64. package/dist/node/react/index.node.cjs +621 -8
  65. package/dist/node/react/index.node.js +621 -8
  66. package/dist/node/{token-CI2_C_5D.js → token-B2MtKe6w.js} +1 -1
  67. package/dist/node/{token-D4jFpAk0.cjs → token-BQ-EqDMB.cjs} +1 -1
  68. package/dist/node/{token-util-BxSJFXkT.cjs → token-util--zh4M6Ze.cjs} +1 -1
  69. package/dist/node/{token-util-idU3T5iM.js → token-util-CtaB7NzM.js} +1 -1
  70. package/dist/node/token-util-P8_cV3Yu.cjs +1 -0
  71. package/dist/node/{token-util-ChGcRyiF.js → token-util-RdQsj3MK.js} +1 -1
  72. package/package.json +10 -8
  73. package/dist/cli/chat-C_rQ9SRr.js +0 -1
  74. package/dist/cli/chat-manager-bLgTuAnK.js +0 -21
  75. package/dist/cli/compute-server-CyGc-fpM.js +0 -1
  76. package/dist/cli/connect-CgLoRyDv.js +0 -1
  77. package/dist/cli/connect-t56RPJRB.js +0 -1
  78. package/dist/cli/create-slack-app-CyVPwWsG.js +0 -1
  79. package/dist/cli/events-DKgZ2LNM.js +0 -1
  80. package/dist/cli/getMachineId-bsd-aoAkh_W6.js +0 -1
  81. package/dist/cli/getMachineId-bsd-iPOb0TqF.js +0 -1
  82. package/dist/cli/getMachineId-bsd-io5tPlFg.js +0 -1
  83. package/dist/cli/getMachineId-darwin-DJO6fOdc.js +0 -2
  84. package/dist/cli/getMachineId-darwin-DlRRGl6M.js +0 -2
  85. package/dist/cli/getMachineId-darwin-Wp0usMIp.js +0 -2
  86. package/dist/cli/getMachineId-linux-CV2HmJXY.js +0 -1
  87. package/dist/cli/getMachineId-linux-Cv44Y8ZY.js +0 -1
  88. package/dist/cli/getMachineId-linux-CzNEh3DJ.js +0 -1
  89. package/dist/cli/getMachineId-linux-_RiwpX2J.js +0 -1
  90. package/dist/cli/getMachineId-unsupported-Dni2ujA-.js +0 -1
  91. package/dist/cli/getMachineId-unsupported-MPNVP8rP.js +0 -1
  92. package/dist/cli/getMachineId-unsupported-mBJd3oBN.js +0 -1
  93. package/dist/cli/getMachineId-unsupported-rdNf8ej6.js +0 -1
  94. package/dist/cli/getMachineId-win-BQ_C7kS3.js +0 -1
  95. package/dist/cli/getMachineId-win-Bw0-Mlkn.js +0 -1
  96. package/dist/cli/getMachineId-win-DrJTv2qi.js +0 -1
  97. package/dist/cli/init-DXBledyD.js +0 -23
  98. package/dist/cli/run-CRrWbMEw.js +0 -1
  99. package/dist/cli/setup-slack-app-C9L0iIDZ.js +0 -1
  100. package/dist/cli/token-util-Dui5nOLw.js +0 -1
  101. package/dist/cli/undici-mM2IfLwA.js +0 -1
  102. package/dist/node/token-util-BENsR1Bn.cjs +0 -1
  103. /package/dist/cli/{devtools-CS3ukAFj.js → devtools-CwQnB6v7.js} +0 -0
  104. /package/dist/cli/{esm-CYkSi8_o.js → esm-BiWxO8S7.js} +0 -0
  105. /package/dist/cli/{execAsync-9jtK4I3l.js → execAsync-4IyNgpoN.js} +0 -0
  106. /package/dist/cli/{execAsync-B4_pEoEJ.js → execAsync-B6xSGvuk.js} +0 -0
  107. /package/dist/cli/{execAsync-BdK7RB6d.js → execAsync-B8_ZTafx.js} +0 -0
  108. /package/dist/cli/{execAsync-Bjxs-bO2.js → execAsync-B8nelpRN.js} +0 -0
  109. /package/dist/cli/{execAsync-CM_C8SBr.js → execAsync-Cj_jF8MF.js} +0 -0
  110. /package/dist/cli/{execAsync-CNFaXJjR.js → execAsync-Ckbi9Yqe.js} +0 -0
  111. /package/dist/cli/{execAsync-Dl3hbWiU.js → execAsync-CvOpnFex.js} +0 -0
  112. /package/dist/cli/{execAsync-qGIZ8XA8.js → execAsync-DDBH3pWg.js} +0 -0
  113. /package/dist/cli/{token-error-CGIVaIg6.js → token-error-BHDo_48c.js} +0 -0
  114. /package/dist/cli/{undici-BiTrd9-R.js → undici-B5tM_IT9.js} +0 -0
@@ -1,4 +1,617 @@
1
- const e={scratch:{".env.local.hbs":`# Store local environment variables here.
1
+ const e={scout:{".env.local.hbs":`# Store local environment variables here.
2
+ # They will be used by blink dev for development.
3
+ {{#each envLocal}}
4
+ {{this.[0]}}={{this.[1]}}
5
+ {{/each}}
6
+ {{#unless (hasAnyEnvVar envLocal "OPENAI_API_KEY" "ANTHROPIC_API_KEY" "AI_GATEWAY_API_KEY")}}
7
+ # OPENAI_API_KEY=
8
+ # ANTHROPIC_API_KEY=
9
+ # AI_GATEWAY_API_KEY=
10
+ {{/unless}}
11
+
12
+ # Web search (optional - get an API key from https://exa.ai)
13
+ # EXA_API_KEY=
14
+
15
+ # Coder credentials (optional - for production compute)
16
+ # CODER_URL=
17
+ # CODER_SESSION_TOKEN=
18
+ # CODER_TEMPLATE=
19
+ # CODER_PRESET_NAME=
20
+ `,".env.production":`# Store production environment variables here.
21
+ # They will be upserted as secrets on blink deploy.
22
+ # OPENAI_API_KEY=
23
+ # ANTHROPIC_API_KEY=
24
+ # AI_GATEWAY_API_KEY=
25
+
26
+ # Slack App credentials
27
+ # SLACK_BOT_TOKEN=
28
+ # SLACK_SIGNING_SECRET=
29
+
30
+ # GitHub App credentials
31
+ # GITHUB_APP_ID=
32
+ # GITHUB_CLIENT_ID=
33
+ # GITHUB_CLIENT_SECRET=
34
+ # GITHUB_WEBHOOK_SECRET=
35
+ # GITHUB_PRIVATE_KEY=
36
+
37
+ # Web search
38
+ # EXA_API_KEY=
39
+
40
+ # Coder credentials (recommended for production)
41
+ # CODER_URL=
42
+ # CODER_SESSION_TOKEN=
43
+ # CODER_TEMPLATE=
44
+ # CODER_PRESET_NAME=
45
+ `,".gitignore":`# dependencies
46
+ node_modules
47
+
48
+ # config and build
49
+ .blink/*
50
+ !.blink/config.json
51
+
52
+ # dotenv environment variables file
53
+ .env
54
+ .env.*
55
+
56
+ # Finder (MacOS) folder config
57
+ .DS_Store
58
+ `,"AGENTS.md":`This project is a Blink agent.
59
+
60
+ You are an expert software engineer, which makes you an expert agent developer. You are highly idiomatic, opinionated, concise, and precise. The user prefers accuracy over speed.
61
+
62
+ <communication>
63
+ 1. Be concise, direct, and to the point.
64
+ 2. You are communicating via a terminal interface, so avoid verbosity, preambles, postambles, and unnecessary whitespace.
65
+ 3. NEVER use emojis unless the user explicitly asks for them.
66
+ 4. You must avoid text before/after your response, such as "The answer is" or "Short answer:", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...".
67
+ 5. Mimic the style of the user's messages.
68
+ 6. Do not remind the user you are happy to help.
69
+ 7. Do not act with sycophantic flattery or over-the-top enthusiasm.
70
+ 8. Do not regurgitate tool output. e.g. if a command succeeds, acknowledge briefly (e.g. "Done" or "Formatted").
71
+ 9. *NEVER* create markdown files for the user - *always* guide the user through your efforts.
72
+ 10. *NEVER* create example scripts for the user, or examples scripts for you to run. Leverage your tools to accomplish the user's goals.
73
+ </communication>
74
+
75
+ <goals>
76
+ Your method of assisting the user is by iterating their agent using the context provided by the user in run mode.
77
+
78
+ You can obtain additional context by leveraging web search and compute tools to read files, run commands, and search the web.
79
+
80
+ The user is _extremely happy_ to provide additional context. They prefer this over you guessing, and then potentially getting it wrong.
81
+
82
+ <example>
83
+ user: i want a coding agent
84
+ assistant: Let me take a look at your codebase...
85
+ ... tool calls to investigate the codebase...
86
+ assistant: I've created tools for linting, testing, and formatting. Hop back in run mode to use your agent! If you ever encounter undesired behavior from your agent, switch back to edit mode to refine your agent.
87
+ </example>
88
+
89
+ Always investigate the current state of the agent before assisting the user.
90
+ </goals>
91
+
92
+ <agent_development>
93
+ Agents are written in TypeScript, and mostly stored in a single \`agent.ts\` file. Complex agents will have multiple files, like a proper codebase.
94
+
95
+ Environment variables are stored in \`.env.local\` and \`.env.production\`. \`blink dev\` will hot-reload environment variable changes in \`.env.local\`.
96
+
97
+ Changes to the agent are hot-reloaded. As you make edits, the user can immediately try them in run mode.
98
+
99
+ 1. _ALWAYS_ use the package manager the user is using (inferred from lock files or \`process.argv\`).
100
+ 2. You _MUST_ use \`agent.store\` to persist state. The agent process is designed to be stateless.
101
+ 3. Test your changes to the user's agent by using the \`message_user_agent\` tool. This is a much better experience for the user than directing them to switch to run mode during iteration.
102
+ 4. Use console.log for debugging. The console output appears for the user.
103
+ 5. Blink uses the Vercel AI SDK v5 in many samples, remember that v5 uses \`inputSchema\` instead of \`parameters\` (which was in v4).
104
+ 6. Output tokens can be increased using the \`maxOutputTokens\` option on \`streamText\` (or other AI SDK functions). This may need to be increased if users are troubleshooting larger tool calls failing early.
105
+ 7. Use the TypeScript language service tools (\`typescript_completions\`, \`typescript_quickinfo\`, \`typescript_definition\`, \`typescript_diagnostics\`) to understand APIs, discover available methods, check types, and debug errors. These tools use tsserver to provide IDE-like intelligence.
106
+
107
+ If the user is asking for a behavioral change, you should update the agent's system prompt.
108
+ This will not ensure the behavior, but it will guide the agent towards the desired behavior.
109
+ If the user needs 100% behavioral certainty, adjust tool behavior instead.
110
+ </agent_development>
111
+
112
+ <agent_web_requests>
113
+ Agents are HTTP servers, so they can handle web requests. This is commonly used to async-invoke an agent. e.g. for a Slack bot, messages are sent to the agent via a webhook.
114
+
115
+ Blink automatically creates a reverse-tunnel to your local machine for simple local development with external services (think Slack Bot, GitHub Bot, etc.).
116
+
117
+ To trigger chats based on web requests, use the \`agent.chat.upsert\` and \`agent.chat.message\` APIs.
118
+ </agent_web_requests>
119
+
120
+ <technical_knowledge>
121
+ Blink agents are Node.js HTTP servers built on the Vercel AI SDK:
122
+
123
+ \`\`\`typescript
124
+ import { convertToModelMessages, streamText } from "ai";
125
+ import * as blink from "blink";
126
+
127
+ const agent = new blink.Agent();
128
+
129
+ agent.on("chat", async ({ messages, chat, abortSignal }) => {
130
+ return streamText({
131
+ model: "anthropic/claude-sonnet-4.5",
132
+ system: "You are a helpful assistant.",
133
+ messages: convertToModelMessages(messages, {
134
+ ignoreIncompleteToolCalls: true,
135
+ }),
136
+ tools: {
137
+ /* your tools */
138
+ },
139
+ });
140
+ });
141
+
142
+ agent.on("request", async (request) => {
143
+ // Handle webhooks, OAuth callbacks, etc.
144
+ });
145
+
146
+ agent.serve();
147
+ \`\`\`
148
+
149
+ Event Handlers:
150
+
151
+ **\`agent.on("chat", handler)\`**
152
+
153
+ 1. Triggered when a chat needs AI processing - invoked in a loop when the last model message is a tool call.
154
+ 2. Must return: \`streamText()\` result, \`Response\`, \`ReadableStream<UIMessageChunk>\`, or \`void\`
155
+ 3. Parameters: \`messages\`, \`id\`, \`abortSignal\`
156
+
157
+ _NEVER_ use "maxSteps" from the Vercel AI SDK. It is unnecessary and will cause a worse experience for the user.
158
+
159
+ **\`agent.on("request", handler)\`**
160
+ • Handles raw HTTP requests before Blink processes them
161
+ • Use for: OAuth callbacks, webhook verification, custom endpoints
162
+ • Return \`Response\` to handle, or \`void\` to pass through
163
+
164
+ **\`agent.on("ui", handler)\`**
165
+ • Provides dynamic UI options for chat interfaces
166
+ • Returns schema defining user-selectable options
167
+
168
+ **\`agent.on("error", handler)\`**
169
+ • Global error handler for the agent
170
+
171
+ Chat Management:
172
+
173
+ Blink automatically manages chat state:
174
+
175
+ \`\`\`typescript
176
+ // Create or get existing chat
177
+ // The parameter can be any JSON-serializable value.
178
+ // e.g. for a Slack bot to preserve context in a thread, you might use: ["slack", teamId, channelId, threadTs]
179
+ const chat = await agent.chat.upsert("unique-key");
180
+
181
+ // Send a message to a chat
182
+ await agent.chat.sendMessages(
183
+ chat.id,
184
+ [
185
+ {
186
+ role: "user",
187
+ parts: [{ type: "text", text: "Message" }],
188
+ },
189
+ ],
190
+ {
191
+ behavior: "interrupt" | "enqueue" | "append",
192
+ }
193
+ );
194
+
195
+ // When sending messages, feel free to inject additional parts to direct the model.
196
+ // e.g. if the user is asking for specific behavior in specific scenarios, the simplest
197
+ // answer is to append a text part: "always do X when Y".
198
+ \`\`\`
199
+
200
+ Behaviors:
201
+ • "interrupt": Stop current processing and handle immediately
202
+ • "enqueue": Queue message, process when current chat finishes
203
+ • "append": Add to history without triggering processing
204
+
205
+ Chat keys: Use structured keys like \`"slack-\${teamId}-\${channelId}-\${threadTs}"\` for uniqueness.
206
+
207
+ Storage API:
208
+
209
+ Persistent key-value storage per agent:
210
+
211
+ \`\`\`typescript
212
+ // Store data
213
+ await agent.store.set("key", "value", { ttl: 3600 });
214
+
215
+ // Retrieve data
216
+ const value = await agent.store.get("key");
217
+
218
+ // Delete data
219
+ await agent.store.delete("key");
220
+
221
+ // List keys by prefix
222
+ const result = await agent.store.list("prefix-", { limit: 100 });
223
+ \`\`\`
224
+
225
+ Common uses: OAuth tokens, user preferences, caching, chat-resource associations.
226
+
227
+ Tools:
228
+
229
+ Tools follow Vercel AI SDK patterns with Zod validation:
230
+
231
+ \`\`\`typescript
232
+ import { tool } from "ai";
233
+ import { z } from "zod";
234
+
235
+ const myTool = tool({
236
+ description: "Clear description of what this tool does",
237
+ inputSchema: z.object({
238
+ param: z.string().describe("Parameter description"),
239
+ }),
240
+ execute: async (args, opts) => {
241
+ // opts.abortSignal for cancellation
242
+ // opts.toolCallId for unique identification
243
+ return result;
244
+ },
245
+ });
246
+ \`\`\`
247
+
248
+ Tool Approvals for destructive operations:
249
+
250
+ \`\`\`typescript
251
+ ...await blink.tools.withApproval({
252
+ messages,
253
+ tools: {
254
+ delete_database: tool({ /* ... */ }),
255
+ },
256
+ })
257
+ \`\`\`
258
+
259
+ Tool Context for dependency injection:
260
+
261
+ \`\`\`typescript
262
+ ...blink.tools.withContext(github.tools, {
263
+ accessToken: process.env.GITHUB_TOKEN,
264
+ })
265
+ \`\`\`
266
+
267
+ Tool Prefixing to avoid collisions:
268
+
269
+ \`\`\`typescript
270
+ ...blink.tools.prefix(github.tools, "github_")
271
+ \`\`\`
272
+
273
+ LLM Models:
274
+
275
+ \`\`\`typescript
276
+ import { anthropic } from "@ai-sdk/anthropic";
277
+ import { openai } from "@ai-sdk/openai";
278
+
279
+ model: anthropic("claude-sonnet-4.5", {
280
+ apiKey: process.env.ANTHROPIC_API_KEY,
281
+ });
282
+ // Use chat API for OpenAI models - it's more reliable than the responses API
283
+ model: openai.chat("gpt-5", { apiKey: process.env.OPENAI_API_KEY });
284
+ \`\`\`
285
+
286
+ **Note about Edit Mode:** Edit mode (this agent) automatically selects models in this priority:
287
+
288
+ 1. If \`ANTHROPIC_API_KEY\` is set: uses \`claude-sonnet-4.5\` via \`@ai-sdk/anthropic\`
289
+ 2. If \`OPENAI_API_KEY\` is set: uses \`gpt-5\` via \`@ai-sdk/openai\`
290
+
291
+ Available SDKs:
292
+
293
+ **@blink-sdk/compute**
294
+
295
+ \`\`\`typescript
296
+ import * as compute from "@blink-sdk/compute";
297
+
298
+ tools: {
299
+ ...compute.tools, // execute_bash, read_file, write_file, edit_file, process management
300
+ }
301
+ \`\`\`
302
+
303
+ **@blink-sdk/github**
304
+
305
+ \`\`\`typescript
306
+ import * as github from "@blink-sdk/github";
307
+
308
+ tools: {
309
+ ...blink.tools.withContext(github.tools, {
310
+ accessToken: process.env.GITHUB_TOKEN,
311
+ }),
312
+ }
313
+ \`\`\`
314
+
315
+ **@blink-sdk/slack**
316
+
317
+ \`\`\`typescript
318
+ import * as slack from "@blink-sdk/slack";
319
+ import { App } from "@slack/bolt";
320
+
321
+ const receiver = new slack.Receiver();
322
+ const app = new App({
323
+ token: process.env.SLACK_BOT_TOKEN,
324
+ signingSecret: process.env.SLACK_SIGNING_SECRET,
325
+ receiver,
326
+ });
327
+
328
+ // This will trigger when the bot is @mentioned.
329
+ app.event("app_mention", async ({ event }) => {
330
+ // The argument here is a JSON-serializable value.
331
+ // To maintain the same chat context, use the same key.
332
+ const chat = await agent.chat.upsert([
333
+ "slack",
334
+ event.channel,
335
+ event.thread_ts ?? event.ts,
336
+ ]);
337
+ const { message } = await slack.createMessageFromEvent({
338
+ client: app.client,
339
+ event,
340
+ });
341
+ await agent.chat.sendMessages(chat.id, [message]);
342
+ // This is a nice immediate indicator for the user.
343
+ await app.client.assistant.threads.setStatus({
344
+ channel_id: event.channel,
345
+ status: "is typing...",
346
+ thread_ts: event.thread_ts ?? event.ts,
347
+ });
348
+ });
349
+
350
+ const agent = new blink.Agent();
351
+
352
+ agent.on("request", async (request) => {
353
+ return receiver.handle(app, request);
354
+ });
355
+
356
+ agent.on("chat", async ({ messages }) => {
357
+ const tools = slack.createTools({ client: app.client });
358
+ return streamText({
359
+ model: "anthropic/claude-sonnet-4.5",
360
+ system: "You chatting with users in Slack.",
361
+ messages: convertToModelMessages(messages, {
362
+ ignoreIncompleteToolCalls: true,
363
+ tools,
364
+ }),
365
+ });
366
+ });
367
+ \`\`\`
368
+
369
+ Slack SDK Notes:
370
+
371
+ - "app_mention" event is triggered in both private channels and public channels.
372
+ - "message" event is triggered regardless of being mentioned or not, and will _also_ be fired when "app_mention" is triggered.
373
+ - _NEVER_ register app event listeners in the "on" handler of the agent. This will cause the handler to be called multiple times.
374
+ - Think about how you scope chats - for example, in IMs or if the user wants to make a bot for a whole channel, you would not want to add "ts" or "thread_ts" to the chat key.
375
+ - When using "assistant.threads.setStatus", you need to ensure the status of that same "thread_ts" is cleared. You can do this by inserting a message part that directs the agent to clear the status (there is a tool if using @blink-sdk/slack called "reportStatus" that does this). e.g. \`message.parts.push({ type: "text", text: "*INTERNAL INSTRUCTION*: Clear the status of this thread after you finish: channel=\${channel} thread_ts=\${thread_ts}" })\`
376
+ - The Slack SDK has many functions that allow users to completely customize the message format. If the user asks for customization, look at the types for @blink-sdk/slack - specifically: "createPartsFromMessageMetadata", "createMessageFromEvent", and "extractMessagesMetadata".
377
+
378
+ Slack App Manifest:
379
+
380
+ - _ALWAYS_ include the "assistant:write" scope unless the user explicitly states otherwise - this allows Slack apps to set their status, which makes for a significantly better user experience. You _MUST_ provide "assistant_view" if you provide this scope.
381
+ - The user can always edit the manifest after creation, but you'd have to suggest it to them.
382
+ - "oauth_config" MUST BE PROVIDED - otherwise the app will have NO ACCESS.
383
+ - _ALWAYS_ default \`token_rotation_enabled\` to false unless the user explicitly asks for it. It is a _much_ simpler user-experience to not rotate tokens.
384
+ - For the best user experience, default to the following bot scopes (in the "oauth_config" > "scopes" > "bot"):
385
+ - "app_mentions:read"
386
+ - "reactions:write"
387
+ - "reactions:read"
388
+ - "channels:history"
389
+ - "chat:write"
390
+ - "groups:history"
391
+ - "groups:read"
392
+ - "files:read"
393
+ - "im:history"
394
+ - "im:read"
395
+ - "im:write"
396
+ - "mpim:history"
397
+ - "mpim:read"
398
+ - "users:read"
399
+ - "links:read"
400
+ - "commands"
401
+ - For the best user experience, default to the following bot events (in the "settings" > "event_subscriptions" > "bot_events"):
402
+ - "app_mention"
403
+ - "message.channels",
404
+ - "message.groups",
405
+ - "message.im",
406
+ - "reaction_added"
407
+ - "reaction_removed"
408
+ - "assistant_thread_started"
409
+ - "member_joined_channel"
410
+ - _NEVER_ include USER SCOPES unless the user explicitly asks for them.
411
+
412
+ WARNING: Beware of attaching multiple event listeners to the same chat. This could cause the agent to respond multiple times.
413
+
414
+ State Management:
415
+
416
+ Blink agents are short-lived HTTP servers that restart on code changes and do not persist in-memory state between requests.
417
+
418
+ _NEVER_ use module-level Maps, Sets, or variables to store state (e.g. \`const activeBots = new Map()\`).
419
+
420
+ For global state persistence, you can use the agent store:
421
+
422
+ - Use \`agent.store\` for persistent key-value storage
423
+ - Query external APIs to fetch current state
424
+ - Use webhooks to trigger actions rather than polling in-memory state
425
+
426
+ For message-level state persistence, use message metadata:
427
+
428
+ \`\`\`typescript
429
+ import { UIMessage } from "blink";
430
+ import * as blink from "blink";
431
+
432
+ const agent = new blink.Agent<
433
+ UIMessage<{
434
+ source: "github";
435
+ associated_id: string;
436
+ }>
437
+ >();
438
+
439
+ agent.on("request", async (request) => {
440
+ // comes from github, we want to do something deterministic in the chat loop with that ID...
441
+ // insert a message with that metadata into the chat
442
+ const chat = await agent.chat.upsert("some-github-key");
443
+ await agent.chat.sendMessages(request.chat.id, [
444
+ {
445
+ role: "user",
446
+ parts: [
447
+ {
448
+ type: "text",
449
+ text: "example",
450
+ },
451
+ ],
452
+ metadata: {
453
+ source: "github",
454
+ associated_id: "some-github-id",
455
+ },
456
+ },
457
+ ]);
458
+ });
459
+
460
+ agent.on("chat", async ({ messages }) => {
461
+ const message = messages.find(
462
+ (message) => message.metadata?.source === "github"
463
+ );
464
+
465
+ // Now we can use that metadata...
466
+ });
467
+ \`\`\`
468
+
469
+ The agent process can restart at any time, so all important state must be externalized.
470
+ </technical_knowledge>
471
+
472
+ <code_quality>
473
+
474
+ - Never use "as any" type assertions. Always figure out the correct typings.
475
+ </code_quality>
476
+ `,"agent.ts.hbs":`{{#if (eq aiProvider "anthropic")}}
477
+ import { anthropic } from "@ai-sdk/anthropic";
478
+ {{else if (eq aiProvider "openai")}}
479
+ import { openai } from "@ai-sdk/openai";
480
+ {{/if}}
481
+ import { type Message, Scout } from "@blink-sdk/scout-agent";
482
+ import { streamText, tool } from "ai";
483
+ import * as blink from "blink";
484
+ import { z } from "zod";
485
+
486
+ export const agent = new blink.Agent<Message>();
487
+
488
+ const scout = new Scout({
489
+ agent,
490
+ // GitHub integration (optional).
491
+ // Run \`blink setup github-app\` to set up your GitHub App, or remove this section if not needed.
492
+ github: {
493
+ appID: process.env.GITHUB_APP_ID,
494
+ privateKey: process.env.GITHUB_PRIVATE_KEY,
495
+ webhookSecret: process.env.GITHUB_WEBHOOK_SECRET,
496
+ },
497
+ // Slack integration (optional).
498
+ // Run \`blink setup slack-app\` to set up your Slack App, or remove this section if not needed.
499
+ slack: {
500
+ botToken: process.env.SLACK_BOT_TOKEN,
501
+ signingSecret: process.env.SLACK_SIGNING_SECRET,
502
+ },
503
+ // Web search integration (optional). Visit https://exa.ai to get an API key.
504
+ webSearch: {
505
+ exaApiKey: process.env.EXA_API_KEY,
506
+ },
507
+ // Compute environment for running code.
508
+ // Using Docker by default for local development.
509
+ // For production, you can use Coder (https://coder.com) instead:
510
+ // compute: {
511
+ // type: "coder",
512
+ // options: {
513
+ // url: process.env.CODER_URL,
514
+ // sessionToken: process.env.CODER_SESSION_TOKEN,
515
+ // template: process.env.CODER_TEMPLATE,
516
+ // presetName: process.env.CODER_PRESET_NAME,
517
+ // },
518
+ // },
519
+ compute: {
520
+ type: "docker",
521
+ },
522
+ });
523
+
524
+ agent.on("request", async (request) => {
525
+ const url = new URL(request.url);
526
+ if (url.pathname.startsWith("/slack")) {
527
+ return scout.handleSlackWebhook(request);
528
+ }
529
+ if (url.pathname.startsWith("/github")) {
530
+ return scout.handleGitHubWebhook(request);
531
+ }
532
+ return new Response("Hey there!", { status: 200 });
533
+ });
534
+
535
+ agent.on("chat", async ({ id, messages }) => {
536
+ const params = await scout.buildStreamTextParams({
537
+ messages,
538
+ chatID: id,
539
+ {{#if (eq aiProvider "anthropic")}}
540
+ model: anthropic("claude-opus-4-5"),
541
+ {{else if (eq aiProvider "openai")}}
542
+ model: openai.chat("gpt-5"),
543
+ {{else if (eq aiProvider "vercel")}}
544
+ model: "anthropic/claude-opus-4.5",
545
+ {{else}}
546
+ // Unknown provider: {{aiProvider}}. Defaulting to Vercel AI Gateway syntax.
547
+ model: "anthropic/claude-opus-4.5",
548
+ {{/if}}
549
+ providerOptions: { anthropic: { cacheControl: { type: "ephemeral" } } },
550
+ tools: {
551
+ // Add your custom tools here
552
+ get_favorite_color: tool({
553
+ description: "Get your favorite color",
554
+ inputSchema: z.object({}),
555
+ execute() {
556
+ return "blue";
557
+ },
558
+ }),
559
+ },
560
+ });
561
+ return streamText(params);
562
+ });
563
+
564
+ agent.serve();
565
+ `,"package.json.hbs":`{
566
+ "name": "{{packageName}}",
567
+ "main": "agent.ts",
568
+ "type": "module",
569
+ "private": true,
570
+ "scripts": {
571
+ "dev": "blink dev",
572
+ "deploy": "blink deploy"
573
+ },
574
+ "devDependencies": {
575
+ "zod": "latest",
576
+ "ai": "latest",
577
+ {{#if (eq aiProvider "anthropic")}}
578
+ "@ai-sdk/anthropic": "latest",
579
+ {{else if (eq aiProvider "openai")}}
580
+ "@ai-sdk/openai": "latest",
581
+ {{/if}}
582
+ "blink": "latest",
583
+ "esbuild": "latest",
584
+ "@types/node": "latest",
585
+ "typescript": "latest",
586
+ "@blink-sdk/scout-agent": "latest"
587
+ }
588
+ }
589
+ `,"tsconfig.json":`{
590
+ "compilerOptions": {
591
+ "lib": ["ESNext"],
592
+ "target": "ESNext",
593
+ "module": "Preserve",
594
+ "moduleDetection": "force",
595
+
596
+ "moduleResolution": "bundler",
597
+ "allowImportingTsExtensions": true,
598
+ "verbatimModuleSyntax": true,
599
+ "resolveJsonModule": true,
600
+ "noEmit": true,
601
+
602
+ "strict": true,
603
+ "skipLibCheck": true,
604
+ "noFallthroughCasesInSwitch": true,
605
+ "noUncheckedIndexedAccess": true,
606
+ "noImplicitOverride": true,
607
+
608
+ "noUnusedLocals": false,
609
+ "noUnusedParameters": false,
610
+
611
+ "types": ["node"]
612
+ }
613
+ }
614
+ `},scratch:{".env.local.hbs":`# Store local environment variables here.
2
615
  # They will be used by blink dev for development.
3
616
  {{#each envLocal}}
4
617
  {{this.[0]}}={{this.[1]}}
@@ -0,0 +1 @@
1
+ import{__toESM as e}from"./chunk-D9KrCrVq.js";import{findNearestEntry as t,migrateDataToBlink as n,require_main as r,resolveConfig as i}from"./util-k3x8m_i5.js";import"./dist-Ceoe3h7v.js";import{getAuthToken as a}from"./auth-DbpOUCTO.js";import"./util-CUfifM0g.js";import"./usingCtx-B1FpoePI.js";import"./utils-B3iC7vdW.js";import{ChatManager as o,Client as s,RWLock as c}from"./chat-manager-cUXPlLU4.js";import{spawn as l}from"node:child_process";import{join as u,resolve as d}from"node:path";import{existsSync as f}from"node:fs";import{readFile as p}from"node:fs/promises";import{createServer as m}from"node:net";async function h(e){let t=e.env?.PORT??await g(),n=e.env?.HOST??`127.0.0.1`,r=`http://${n}:${t}`,i={...e.env??process.env,PORT:t.toString(),HOST:n},a=l(e.command,e.args,{stdio:`pipe`,env:i});e.signal?.addEventListener(`abort`,()=>{try{a.kill()}catch{}},{once:!0});let o=new AbortController,c=[o.signal];e.signal&&c.push(e.signal);let u=AbortSignal.any(c);a.stdout.on(`data`,t=>{e.onStdout?.(Buffer.from(t).toString(`utf-8`))});let d=``;a.stderr.on(`data`,t=>{o.signal.aborted||(d+=Buffer.from(t).toString(`utf-8`)),e.onStderr?.(Buffer.from(t).toString(`utf-8`))}),a.on(`error`,e=>{o.abort(e)}),a.on(`exit`,(t,n)=>{o.signal.aborted?e.onExit?.(t,n):o.abort()});let f=new s({baseUrl:r}),p=0;for(;!u.aborted;){try{await f.health();break}catch{}if(await new Promise(e=>setTimeout(e,p*5)),p++,p>100)throw Error(`Health endpoint timed out`)}if(u.aborted)throw u.reason;return o.abort(),{client:f,dispose:()=>{a.kill()}}}async function g(){let e=m();return new Promise((t,n)=>{e.listen(0,()=>{let n=e.address().port;t(n)}).on(`error`,e=>{n(e)})}).finally(()=>{e.close()})}var _=e(r(),1);async function v(e,r={}){if(!r.directory){let e=process.cwd();try{i(e),r.directory=e}catch{let n=await t(e,`.blink`);n&&f(u(n,`build`))&&(n=void 0),n?r.directory=n:r.directory=e}}await n(r.directory);let s=i(r.directory),l={};try{l=(0,_.parse)(await p(u(r.directory,`.env.local`),`utf-8`))}catch{}let m=await a(),g=await h({command:`node`,args:[`--experimental-strip-types`,`--no-deprecation`,s.entry],env:{...process.env,...l,BLINK_TOKEN:m}});console.log(`Agent spawned`);let v=d(r?.directory??process.cwd(),`.blink`,`chats`),y=new o({chatId:r?.chat,chatsDirectory:v,onError:e=>{console.error(`Error:`,e)}});y.setAgent({client:g.client,lock:new c});try{let t=new Promise(e=>{let t=y.subscribe(n=>{(n.status===`idle`||n.status===`error`)&&(t(),e())})});await y.sendMessages([{id:crypto.randomUUID(),created_at:new Date().toISOString(),metadata:void 0,parts:[{type:`text`,text:e.join(` `)}],role:`user`,mode:`run`}]),await t;let n=y.getState();console.log(`Final state:`,n.messages.pop())}finally{y.dispose(),g.dispose()}}export{v as default};
@@ -0,0 +1,9 @@
1
+ import{Client as e,Ie as t,M as n,Se as r,Y as i,ge as a,he as o,pD as s,ye as c}from"./dist-Ceoe3h7v.js";import{openUrl as l,source_default as u}from"./util-CUfifM0g.js";import{createDevhookID as d,createGithubApp as f,createSlackApp as p,getDevhookID as m,hasDevhook as h}from"./create-github-app-CaCP607a.js";import{_usingCtx as g}from"./usingCtx-B1FpoePI.js";import{basename as _,join as v}from"node:path";import{access as y,readFile as b,readdir as x,writeFile as S}from"node:fs/promises";import C from"node:util";import w from"node:crypto";async function T(e){try{let t=await(await fetch(`https://slack.com/api/auth.test`,{method:`POST`,headers:{Authorization:`Bearer ${e}`,"Content-Type":`application/json`}})).json();return t.ok?{valid:!0,botName:t.user}:{valid:!1,error:t.error||`Invalid bot token`}}catch(e){return{valid:!1,error:`Failed to verify credentials: ${e}`}}}function E(e,t,n,r){let i=Math.floor(Date.now()/1e3),a=parseInt(t,10);if(Math.abs(i-a)>300)return!1;let o=w.createHmac(`sha256`,e),s=`v0:${t}:${n}`;o.update(s);let c=`v0=${o.digest(`hex`)}`;return w.timingSafeEqual(Buffer.from(c),Buffer.from(r))}const D=e=>{if(!(typeof e==`object`&&e))throw Error(`Unable to make value disposable, it's not an object`);if(Symbol.dispose in e)return e;if(`dispose`in e&&typeof e.dispose==`function`){let t=e;return t[Symbol.dispose]=()=>t.dispose(),t}throw Error(`Unable to make value disposable`)};async function O(e,t,n){let r=``;try{r=await b(e,`utf-8`)}catch{}r=r.replace(/^(SLACK_BOT_TOKEN=.*)/gm,`# $1`).replace(/^(SLACK_SIGNING_SECRET=.*)/gm,`# $1`),r=`${r.trimEnd()}\n`;let i=[];i.push(``),i.push(`# Slack App credentials`),t&&i.push(`SLACK_BOT_TOKEN=${t}`),n&&i.push(`SLACK_SIGNING_SECRET=${n}`),i.push(``),await S(e,r+i.join(`
2
+ `),`utf-8`)}async function k(e){let t=await x(e);for(let[e,n]of[[`bun`,`bun.lock`],[`npm`,`package-lock.json`],[`pnpm`,`pnpm-lock.yaml`],[`yarn`,`yarn.lock`]])for(let r of t)if(r.includes(n))return e;return`npm`}async function A(t,f){try{var b=g();let x=f?.name||_(t).replace(/[^a-zA-Z0-9]/g,`-`),S=f?.packageManager||await k(t),w=v(t,`.env.local`);try{await y(w)}catch{n.error(`No .env.local file found in this directory. Please run this command from a Blink agent directory.`),r(`Slack app setup cancelled`);return}let A=await o({message:`What should your Slack app be called? This will be the name displayed in Slack. You can change it later.`,placeholder:x,defaultValue:x,validate:e=>{if(!e||e.trim().length===0)return`App name cannot be empty`}});if(s(A))return;let j=h(t)?m(t):d(t),M=`https://${j}.blink.host`;if(!j)throw Error(`Failed to obtain devhook ID`);n.info(`Starting webhook listener...`);let N=``,P=``,F=!1,I=``,L=``,R=!1,z,B=new e({baseURL:`https://blink.coder.com`}),V=()=>{},H=e=>{},U=new Promise((e,t)=>{V=e,H=t}),W=B.devhook.listen({id:j,onRequest:async e=>{let t=await e.text(),r;try{r=JSON.parse(t)}catch{return new Response(`Invalid JSON`,{status:400})}if(r.type===`url_verification`)return n.info(`✓ Webhook challenge received`),new Response(JSON.stringify({challenge:r.challenge}),{headers:{"Content-Type":`application/json`}});if(N){let n=e.headers.get(`x-slack-signature`),i=e.headers.get(`x-slack-request-timestamp`);if(!n||!i)return new Response(`Missing signature`,{status:401});if(!E(N,i,t,n))return R=!0,r.event?.type===`message`&&r.event.channel_type===`im`&&!r.event.bot_id&&(z=r.event.channel),new Response(`Invalid signature`,{status:401})}return r.event?.type===`message`&&r.event.channel_type===`im`&&!r.event.bot_id&&(F=!0,I=r.event.channel??``,L=r.event.ts??``),new Response(`OK`)},onConnect:()=>{V()},onDisconnect:()=>{},onError:e=>{H(e)}});b.u(D(W));let G={display_information:{name:A.toString(),description:`A Blink agent for Slack`,background_color:`#4A154B`},features:{bot_user:{display_name:A.toString(),always_online:!0},app_home:{home_tab_enabled:!1,messages_tab_enabled:!0,messages_tab_read_only_enabled:!1},assistant_view:{assistant_description:`A helpful assistant powered by Blink`}},oauth_config:{scopes:{bot:[`app_mentions:read`,`assistant:write`,`reactions:write`,`reactions:read`,`channels:history`,`chat:write`,`groups:history`,`groups:read`,`files:read`,`im:history`,`im:read`,`im:write`,`mpim:history`,`mpim:read`,`users:read`,`links:read`,`commands`]}},settings:{event_subscriptions:{request_url:M,bot_events:[`app_mention`,`message.channels`,`message.groups`,`message.im`,`reaction_added`,`reaction_removed`,`assistant_thread_started`,`member_joined_channel`]},interactivity:{is_enabled:!0,request_url:M},token_rotation_enabled:!1,org_deploy_enabled:!1,socket_mode_enabled:!1}},K=p(G);n.info(`Please visit this URL to create your Slack app and return here after finishing:\n\n${u.gray(K)}\n`);let q=await c({message:`Open this URL in your browser automatically?`,initialValue:!0});if(s(q)){n.warn(`Skipping Slack app setup`);return}q&&await l(K,`Could not open the browser. Please visit the URL manually.`);let J=await o({message:`After creating the app, paste the App ID from the "Basic Information" page:`,placeholder:`A01234567AB`,validate:e=>{if(!e||e.trim().length===0)return`App ID is required`}});if(s(J)){n.warn(`Skipping Slack app setup`);return}if(N=await a({message:`Paste your Signing Secret from the same page:`,validate:e=>{if(!e||e.trim().length===0)return`Signing secret is required`}}),s(N)){n.warn(`Skipping Slack app setup`);return}let Y=!1;for(;!Y;){if(P=await a({message:`Install your app and paste your Bot Token from ${u.cyan(`https://api.slack.com/apps/${J}/install-on-team`)}`,validate:e=>{if(!e||e.trim().length===0)return`Bot token is required`}}),s(P)){n.warn(`Skipping Slack app setup`);return}let e=i();e.start(`Verifying bot token...`);let t=await T(P);if(t.valid)e.stop(`✓ Bot token verified!`),Y=!0;else{e.stop(`✗ Failed to verify bot token: ${t.error}`);let r=await c({message:`Would you like to try again?`,initialValue:!0});if(s(r)||!r){n.warn(`Skipping Slack app setup`);return}}}await O(w,P,N),n.success(`Credentials saved to .env.local`),await U;let X=i();X.start(`Try sending a DM to the bot on Slack - it's ${u.bold(u.cyan(A))} in the search bar.`);let Z={bun:`bun run dev`,npm:`npm run dev`,pnpm:`pnpm run dev`,yarn:`yarn dev`}[S];for(;!F;){if(R){if(X.stop(`✗ Invalid signing secret detected`),z&&P)try{await fetch(`https://slack.com/api/chat.postMessage`,{method:`POST`,headers:{Authorization:`Bearer ${P}`,"Content-Type":`application/json`},body:JSON.stringify({channel:z,text:`⚠️ There seems to be a problem with the signing secret. Please check the CLI for instructions on how to fix it.`})})}catch{}let e=await a({message:`The signing secret appears to be incorrect. Please paste the correct Signing Secret from ${u.cyan(`https://api.slack.com/apps/${J}/general`)}`,validate:e=>{if(!e||e.trim().length===0)return`Signing secret is required`}});if(s(e)){n.warn(`Skipping Slack app setup`);return}N=e,R=!1,z=void 0,await O(w,void 0,N),X.start(`Please try sending a DM to the bot again on Slack...`)}await new Promise(e=>setTimeout(e,500))}X.stop(u.green(`✓ DM received!`));try{await fetch(`https://slack.com/api/chat.postMessage`,{method:`POST`,headers:{Authorization:`Bearer ${P}`,"Content-Type":`application/json`},body:JSON.stringify({channel:I,thread_ts:L,text:`Congrats, your app is now installed and ready to use! Run \`${Z}\` to use your agent.`})})}catch(e){n.warn(`Could not send message to Slack: ${C.inspect(e)}`)}n.success(`Slack app setup complete!`)}catch(e){b.e=e}finally{b.d()}}async function j(e){e||=process.cwd(),t(`Setting up Slack app`),await A(e),process.exit(0)}async function M(e,t){let n=``;try{n=await b(e,`utf-8`)}catch{}n=n.replace(/^(GITHUB_APP_ID=.*)/gm,`# $1`).replace(/^(GITHUB_CLIENT_ID=.*)/gm,`# $1`).replace(/^(GITHUB_CLIENT_SECRET=.*)/gm,`# $1`).replace(/^(GITHUB_WEBHOOK_SECRET=.*)/gm,`# $1`).replace(/^(GITHUB_PRIVATE_KEY=.*)/gm,`# $1`),n=`${n.trimEnd()}\n`;let r=`
3
+ # GitHub App credentials
4
+ GITHUB_APP_ID=${t.id}
5
+ GITHUB_CLIENT_ID=${t.client_id}
6
+ GITHUB_CLIENT_SECRET=${t.client_secret}
7
+ GITHUB_WEBHOOK_SECRET=${t.webhook_secret}
8
+ GITHUB_PRIVATE_KEY="${btoa(t.pem)}"
9
+ `;await S(e,n+r,`utf-8`)}async function N(e,t){let a=t?.name||_(e).replace(/[^a-zA-Z0-9]/g,`-`),p=v(e,`.env.local`);try{await y(p)}catch{n.error(`No .env.local file found in this directory. Please run this command from a Blink agent directory.`),r(`GitHub App setup cancelled`);return}let g=await o({message:`What should your GitHub App be called? This will be the name displayed on GitHub. You can change it later.`,placeholder:a,defaultValue:a,validate:e=>{if(!e||e.trim().length===0)return`App name cannot be empty`}});if(s(g))return;let b=await o({message:`Enter a GitHub organization name to create the app under, or leave blank for a personal app:`,placeholder:`Leave blank for personal app`,defaultValue:``});if(s(b))return;let x=b&&b.trim().length>0?b.trim():void 0,S=h(e)?m(e):d(e);if(!S)throw Error(`Failed to obtain devhook ID`);let C=`https://${S}.blink.host`,w={name:g.toString(),url:`https://blink.coder.com`,description:`A Blink agent for GitHub`,public:!1,hook_attributes:{url:C,active:!0},default_events:[`issues`,`issue_comment`,`pull_request`,`pull_request_review`,`pull_request_review_comment`,`push`],default_permissions:{contents:`write`,issues:`write`,pull_requests:`write`,metadata:`read`}},T,E,D=new Promise((e,t)=>{T=e,E=t}),O=i(),k=await f(w,x,async(e,t)=>{if(e){E(e);return}t&&T(t)});n.info(`Please visit this URL to create your GitHub App and return here after finishing:\n\n${u.gray(k)}\n`);let A=await c({message:`Open this URL in your browser automatically?`,initialValue:!0});if(s(A)){n.warn(`Skipping GitHub App setup`);return}A&&await l(k,`Could not open the browser. Please visit the URL manually.`),O.start(`Waiting for GitHub App creation to complete...`);let j;try{j=await D}catch(e){O.stop(`Failed to create GitHub App: ${e instanceof Error?e.message:String(e)}`);return}O.stop(u.green(`✓ GitHub App "${j.name}" created!`)),await M(p,j),n.success(`Credentials saved to .env.local`),n.info(`\nYour GitHub App is available at: ${u.cyan(j.html_url)}`),n.info(`\nTo install the app on repositories, visit: ${u.cyan(`${j.html_url}/installations/new`)}`),n.success(`GitHub App setup complete!`)}async function P(e){e||=process.cwd(),t(`Setting up GitHub App`),await N(e),process.exit(0)}export{N as setupGithubApp,P as setupGithubAppCommand,A as setupSlackApp,j as setupSlackAppCommand};
@@ -0,0 +1 @@
1
+ import"./token-error-BHDo_48c.js";import{require_token_util as e}from"./token-util-BgEcL2NM.js";export default e();export{};
@@ -1 +1 @@
1
- import{__commonJSMin as e,__require as t}from"./chunk-D9KrCrVq.js";import{require_token_error as n}from"./token-error-CGIVaIg6.js";var r=e(((e,r)=>{var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t)=>{for(var n in t)a(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of s(t))!l.call(e,i)&&i!==n&&a(e,i,{get:()=>t[i],enumerable:!(r=o(t,i))||r.enumerable});return e},f=(e,t,n)=>(n=e==null?{}:i(c(e)),d(t||!e||!e.__esModule?a(n,`default`,{value:e,enumerable:!0}):n,e)),p=e=>d(a({},`__esModule`,{value:!0}),e),m={};u(m,{findRootDir:()=>y,getUserDataDir:()=>b}),r.exports=p(m);var h=f(t(`path`)),g=f(t(`fs`)),_=f(t(`os`)),v=n();function y(){try{let e=process.cwd();for(;e!==h.default.dirname(e);){let t=h.default.join(e,`.vercel`);if(g.default.existsSync(t))return e;e=h.default.dirname(e)}}catch{throw new v.VercelOidcTokenError(`Token refresh only supported in node server environments`)}throw new v.VercelOidcTokenError(`Unable to find root directory`)}function b(){if(process.env.XDG_DATA_HOME)return process.env.XDG_DATA_HOME;switch(_.default.platform()){case`darwin`:return h.default.join(_.default.homedir(),`Library/Application Support`);case`linux`:return h.default.join(_.default.homedir(),`.local/share`);case`win32`:return process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:null;default:return null}}})),i=e(((e,i)=>{var a=Object.create,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty,d=(e,t)=>{for(var n in t)o(e,n,{get:t[n],enumerable:!0})},f=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of c(t))!u.call(e,i)&&i!==n&&o(e,i,{get:()=>t[i],enumerable:!(r=s(t,i))||r.enumerable});return e},p=(e,t,n)=>(n=e==null?{}:a(l(e)),f(t||!e||!e.__esModule?o(n,`default`,{value:e,enumerable:!0}):n,e)),m=e=>f(o({},`__esModule`,{value:!0}),e),h={};d(h,{assertVercelOidcTokenResponse:()=>C,findProjectInfo:()=>w,getTokenPayload:()=>D,getVercelCliToken:()=>x,getVercelDataDir:()=>b,getVercelOidcToken:()=>S,isExpired:()=>O,loadToken:()=>E,saveToken:()=>T}),i.exports=m(h);var g=p(t(`path`)),_=p(t(`fs`)),v=n(),y=r();function b(){let e=(0,y.getUserDataDir)();return e?g.join(e,`com.vercel.cli`):null}function x(){let e=b();if(!e)return null;let t=g.join(e,`auth.json`);if(!_.existsSync(t))return null;let n=_.readFileSync(t,`utf8`);return n?JSON.parse(n).token:null}async function S(e,t,n){try{let r=`https://api.vercel.com/v1/projects/${t}/token?source=vercel-oidc-refresh${n?`&teamId=${n}`:``}`,i=await fetch(r,{method:`POST`,headers:{Authorization:`Bearer ${e}`}});if(!i.ok)throw new v.VercelOidcTokenError(`Failed to refresh OIDC token: ${i.statusText}`);let a=await i.json();return C(a),a}catch(e){throw new v.VercelOidcTokenError(`Failed to refresh OIDC token`,e)}}function C(e){if(!e||typeof e!=`object`)throw TypeError(`Expected an object`);if(!(`token`in e)||typeof e.token!=`string`)throw TypeError(`Expected a string-valued token property`)}function w(){let e=(0,y.findRootDir)();if(!e)throw new v.VercelOidcTokenError(`Unable to find root directory`);try{let t=g.join(e,`.vercel`,`project.json`);if(!_.existsSync(t))throw new v.VercelOidcTokenError(`project.json not found`);let n=JSON.parse(_.readFileSync(t,`utf8`));if(typeof n.projectId!=`string`&&typeof n.orgId!=`string`)throw TypeError(`Expected a string-valued projectId property`);return{projectId:n.projectId,teamId:n.orgId}}catch(e){throw new v.VercelOidcTokenError(`Unable to find project ID`,e)}}function T(e,t){try{let n=(0,y.getUserDataDir)();if(!n)throw new v.VercelOidcTokenError(`Unable to find user data directory`);let r=g.join(n,`com.vercel.token`,`${t}.json`),i=JSON.stringify(e);_.mkdirSync(g.dirname(r),{mode:432,recursive:!0}),_.writeFileSync(r,i),_.chmodSync(r,432);return}catch(e){throw new v.VercelOidcTokenError(`Failed to save token`,e)}}function E(e){try{let t=(0,y.getUserDataDir)();if(!t)return null;let n=g.join(t,`com.vercel.token`,`${e}.json`);if(!_.existsSync(n))return null;let r=JSON.parse(_.readFileSync(n,`utf8`));return C(r),r}catch(e){throw new v.VercelOidcTokenError(`Failed to load token`,e)}}function D(e){let t=e.split(`.`);if(t.length!==3)throw new v.VercelOidcTokenError(`Invalid token`);let n=t[1].replace(/-/g,`+`).replace(/_/g,`/`),r=n.padEnd(n.length+(4-n.length%4)%4,`=`);return JSON.parse(Buffer.from(r,`base64`).toString(`utf8`))}function O(e){return e.exp*1e3<Date.now()+9e5}}));export{i as require_token_util};
1
+ import{__commonJSMin as e,__require as t}from"./chunk-D9KrCrVq.js";import{require_token_error as n}from"./token-error-BHDo_48c.js";var r=e(((e,r)=>{var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t)=>{for(var n in t)a(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of s(t))!l.call(e,i)&&i!==n&&a(e,i,{get:()=>t[i],enumerable:!(r=o(t,i))||r.enumerable});return e},f=(e,t,n)=>(n=e==null?{}:i(c(e)),d(t||!e||!e.__esModule?a(n,`default`,{value:e,enumerable:!0}):n,e)),p=e=>d(a({},`__esModule`,{value:!0}),e),m={};u(m,{findRootDir:()=>y,getUserDataDir:()=>b}),r.exports=p(m);var h=f(t(`path`)),g=f(t(`fs`)),_=f(t(`os`)),v=n();function y(){try{let e=process.cwd();for(;e!==h.default.dirname(e);){let t=h.default.join(e,`.vercel`);if(g.default.existsSync(t))return e;e=h.default.dirname(e)}}catch{throw new v.VercelOidcTokenError(`Token refresh only supported in node server environments`)}throw new v.VercelOidcTokenError(`Unable to find root directory`)}function b(){if(process.env.XDG_DATA_HOME)return process.env.XDG_DATA_HOME;switch(_.default.platform()){case`darwin`:return h.default.join(_.default.homedir(),`Library/Application Support`);case`linux`:return h.default.join(_.default.homedir(),`.local/share`);case`win32`:return process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:null;default:return null}}})),i=e(((e,i)=>{var a=Object.create,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty,d=(e,t)=>{for(var n in t)o(e,n,{get:t[n],enumerable:!0})},f=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of c(t))!u.call(e,i)&&i!==n&&o(e,i,{get:()=>t[i],enumerable:!(r=s(t,i))||r.enumerable});return e},p=(e,t,n)=>(n=e==null?{}:a(l(e)),f(t||!e||!e.__esModule?o(n,`default`,{value:e,enumerable:!0}):n,e)),m=e=>f(o({},`__esModule`,{value:!0}),e),h={};d(h,{assertVercelOidcTokenResponse:()=>C,findProjectInfo:()=>w,getTokenPayload:()=>D,getVercelCliToken:()=>x,getVercelDataDir:()=>b,getVercelOidcToken:()=>S,isExpired:()=>O,loadToken:()=>E,saveToken:()=>T}),i.exports=m(h);var g=p(t(`path`)),_=p(t(`fs`)),v=n(),y=r();function b(){let e=(0,y.getUserDataDir)();return e?g.join(e,`com.vercel.cli`):null}function x(){let e=b();if(!e)return null;let t=g.join(e,`auth.json`);if(!_.existsSync(t))return null;let n=_.readFileSync(t,`utf8`);return n?JSON.parse(n).token:null}async function S(e,t,n){try{let r=`https://api.vercel.com/v1/projects/${t}/token?source=vercel-oidc-refresh${n?`&teamId=${n}`:``}`,i=await fetch(r,{method:`POST`,headers:{Authorization:`Bearer ${e}`}});if(!i.ok)throw new v.VercelOidcTokenError(`Failed to refresh OIDC token: ${i.statusText}`);let a=await i.json();return C(a),a}catch(e){throw new v.VercelOidcTokenError(`Failed to refresh OIDC token`,e)}}function C(e){if(!e||typeof e!=`object`)throw TypeError(`Expected an object`);if(!(`token`in e)||typeof e.token!=`string`)throw TypeError(`Expected a string-valued token property`)}function w(){let e=(0,y.findRootDir)();if(!e)throw new v.VercelOidcTokenError(`Unable to find root directory`);try{let t=g.join(e,`.vercel`,`project.json`);if(!_.existsSync(t))throw new v.VercelOidcTokenError(`project.json not found`);let n=JSON.parse(_.readFileSync(t,`utf8`));if(typeof n.projectId!=`string`&&typeof n.orgId!=`string`)throw TypeError(`Expected a string-valued projectId property`);return{projectId:n.projectId,teamId:n.orgId}}catch(e){throw new v.VercelOidcTokenError(`Unable to find project ID`,e)}}function T(e,t){try{let n=(0,y.getUserDataDir)();if(!n)throw new v.VercelOidcTokenError(`Unable to find user data directory`);let r=g.join(n,`com.vercel.token`,`${t}.json`),i=JSON.stringify(e);_.mkdirSync(g.dirname(r),{mode:504,recursive:!0}),_.writeFileSync(r,i),_.chmodSync(r,432);return}catch(e){throw new v.VercelOidcTokenError(`Failed to save token`,e)}}function E(e){try{let t=(0,y.getUserDataDir)();if(!t)return null;let n=g.join(t,`com.vercel.token`,`${e}.json`);if(!_.existsSync(n))return null;let r=JSON.parse(_.readFileSync(n,`utf8`));return C(r),r}catch(e){throw new v.VercelOidcTokenError(`Failed to load token`,e)}}function D(e){let t=e.split(`.`);if(t.length!==3)throw new v.VercelOidcTokenError(`Invalid token`);let n=t[1].replace(/-/g,`+`).replace(/_/g,`/`),r=n.padEnd(n.length+(4-n.length%4)%4,`=`);return JSON.parse(Buffer.from(r,`base64`).toString(`utf8`))}function O(e){return e.exp*1e3<Date.now()}}));export{i as require_token_util};