Skip to content

Commit ce58cda

Browse files
committed
local devhook
1 parent da1bcdc commit ce58cda

16 files changed

Lines changed: 400 additions & 52 deletions

bun.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/api/src/routes/devhook.client.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export interface DevhookListenOptions {
77
readonly id: string;
88

99
readonly onRequest: (req: Request) => Promise<Response>;
10-
readonly onConnect?: (url?: string) => void;
10+
readonly onConnect?: () => void;
1111
readonly onDisconnect?: () => void;
1212
readonly onError?: (error: unknown) => void;
1313
}
@@ -117,16 +117,7 @@ export default class Devhook {
117117
ws.addEventListener("open", () => {
118118
if (disposed) return;
119119
resetBackoff();
120-
const activeSocket = ws;
121-
void this.getUrl(options.id)
122-
.then((url) => {
123-
if (disposed || socket !== activeSocket) return;
124-
options.onConnect?.(url);
125-
})
126-
.catch(() => {
127-
if (disposed || socket !== activeSocket) return;
128-
options.onConnect?.();
129-
});
120+
options.onConnect?.();
130121
});
131122

132123
ws.addEventListener("close", () => {

packages/blink/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
"typecheck": "tsgo --noEmit"
8282
},
8383
"dependencies": {
84-
"@blink-sdk/compute-protocol": "^0.0.7"
84+
"@blink-sdk/compute-protocol": "^0.0.7",
85+
"@blink.so/api": "/home/hugodutka/dev/blink/internal/api/dist/blink.so-api-1.1.0.tgz"
8586
},
8687
"devDependencies": {
8788
"@ai-sdk/anthropic": "^2.0.15",
@@ -93,7 +94,6 @@
9394
"@ai-sdk/xai": "^2.0.16",
9495
"@blink-sdk/compute": "^0.0.15",
9596
"@blink-sdk/events": "workspace:*",
96-
"@blink.so/api": "^1.0.0",
9797
"@clack/prompts": "^0.11.0",
9898
"@hono/node-server": "^1.19.3",
9999
"@jaaydenh/gemini-cli": "0.11.0-nightly-20251022-2",

packages/blink/src/cli/deploy.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,26 @@ export default async function deploy(
327327
chalk.dim(" Skipping webhook tunnel migration in CI mode")
328328
);
329329
} else {
330-
const productionUrl = `https://${devhookID}.blink.host`;
330+
let productionUrl: string | undefined;
331+
try {
332+
productionUrl = await client.devhook.getUrl(devhookID);
333+
} catch (error) {
334+
const message =
335+
error instanceof Error ? error.message : "Unknown error";
336+
console.log(
337+
chalk.yellow(
338+
`Warning: unable to determine webhook tunnel URL (${message})`
339+
)
340+
);
341+
}
331342
console.log("\n" + chalk.cyan("Webhook Tunnel"));
332-
console.log(chalk.dim(` Current: ${productionUrl} → local dev`));
333-
console.log(chalk.dim(` After: ${productionUrl} → production`));
343+
if (productionUrl) {
344+
console.log(chalk.dim(` Current: ${productionUrl} → local dev`));
345+
console.log(chalk.dim(` After: ${productionUrl} → production`));
346+
} else {
347+
console.log(chalk.dim(" Current: (unknown URL) → local dev"));
348+
console.log(chalk.dim(" After: (unknown URL) → production"));
349+
}
334350
console.log(
335351
chalk.dim(" Migrating will keep your webhooks working in production")
336352
);

packages/blink/src/cli/setup-github-app.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ GITHUB_CLIENT_ID=active_client_id
146146
describe("setup github-app command", () => {
147147
function callSetupGithubApp(directory: string) {
148148
const client = createMockClient();
149+
client.devhook.getUrl.mockResolvedValue("https://test.blink.so/devhook");
149150
return setupGithubApp(directory, {
150151
_deps: {
151152
authenticate: async () => {},

packages/blink/src/cli/setup-github-app.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { access, readFile, writeFile } from "node:fs/promises";
22
import { basename, join } from "node:path";
3-
import type Client from "@blink.so/api";
3+
import Client from "@blink.so/api";
44
import {
55
confirm,
66
intro,
@@ -133,12 +133,17 @@ export async function setupGithubApp(
133133
if (!devhookId) {
134134
throw new Error("Failed to obtain devhook ID");
135135
}
136-
const webhookUrl = `https://${devhookId}.blink.host`;
137136

138137
const host = getHostFn();
139138
if (!host) {
140139
throw new Error("No Blink host configured.");
141140
}
141+
const client =
142+
options?._deps?.client ??
143+
new Client({
144+
baseURL: host,
145+
});
146+
const webhookUrl = await client.devhook.getUrl(devhookId);
142147

143148
// Create manifest with sensible defaults for a typical GitHub App
144149
const manifest = {

packages/blink/src/cli/setup-slack-app.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,11 @@ SLACK_SIGNING_SECRET=active_secret
248248
describe("setup slack-app command", () => {
249249
function callSetupSlackApp(directory: string) {
250250
const client = createMockClient();
251+
client.devhook.getUrl.mockResolvedValue("https://test.blink.so/devhook");
251252
// Mock devhook.listen to avoid WebSocket connections
252253
client.devhook.listen.mockImplementation(
253-
({ onConnect }: { onConnect?: () => void }) => {
254-
setTimeout(() => onConnect?.(), 0);
254+
({ onConnect }: { onConnect?: (url?: string) => void }) => {
255+
setTimeout(() => onConnect?.("https://test.blink.so/api/webhook/id"), 0);
255256
return { dispose: () => {}, [Symbol.dispose]: () => {} };
256257
}
257258
);

packages/blink/src/cli/setup-slack-app.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -218,22 +218,10 @@ export async function setupSlackApp(
218218
const devhookId = hasDevhook(directory)
219219
? getDevhookID(directory)
220220
: createDevhookID(directory);
221-
const webhookUrl = `https://${devhookId}.blink.host`;
222221
if (!devhookId) {
223222
throw new Error("Failed to obtain devhook ID");
224223
}
225224

226-
log.info("Starting webhook listener...");
227-
228-
// State for handling Slack events
229-
let signingSecret = "";
230-
let botToken = "";
231-
let dmReceived = false;
232-
let dmChannel = "";
233-
let dmTimestamp = "";
234-
let signatureFailureDetected = false;
235-
let lastFailedChannel: string | undefined;
236-
237225
const host = getHostFn();
238226
if (!host) {
239227
throw new Error(
@@ -245,6 +233,18 @@ export async function setupSlackApp(
245233
new Client({
246234
baseURL: host,
247235
});
236+
const webhookUrl = await client.devhook.getUrl(devhookId);
237+
238+
log.info("Starting webhook listener...");
239+
240+
// State for handling Slack events
241+
let signingSecret = "";
242+
let botToken = "";
243+
let dmReceived = false;
244+
let dmChannel = "";
245+
let dmTimestamp = "";
246+
let signatureFailureDetected = false;
247+
let lastFailedChannel: string | undefined;
248248

249249
let resolveConnected = () => {};
250250
let rejectConnected = (_error: unknown) => {};
@@ -320,7 +320,7 @@ export async function setupSlackApp(
320320

321321
return new Response("OK");
322322
},
323-
onConnect: () => {
323+
onConnect: (_url?: string) => {
324324
resolveConnected();
325325
},
326326
onDisconnect: () => {

packages/blink/src/edit/agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export interface EditAgent {
3636
export function createEditAgent(options: {
3737
directory: string;
3838
env: Record<string, string>;
39-
getDevhookUrl: () => string;
39+
getDevhookUrl: () => Promise<string>;
4040
}): EditAgent {
4141
const agent = new Agent();
4242

packages/blink/src/react/use-dev-mode.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { isToolOrDynamicToolUIPart } from "ai";
44
import { isToolApprovalOutput } from "../agent/tools";
55
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
66
import { join } from "path";
7-
import type { Client, CapabilitiesResponse } from "../agent/client";
7+
import Client from "@blink.so/api";
8+
import type { CapabilitiesResponse } from "../agent/client";
9+
import { getHost } from "../cli/lib/auth";
810
import { getDevhookID, createDevhookID, hasDevhook } from "../cli/lib/devhook";
911
import { createLocalServer, type LocalServer } from "../local/server";
1012
import { isLogMessage, isStoredMessageMetadata } from "../local/types";
@@ -230,10 +232,12 @@ export default function useDevMode(options: UseDevModeOptions): UseDevMode {
230232
directory,
231233
apiServerUrl: server.url,
232234
env,
233-
getDevhookUrl: useCallback(() => {
235+
getDevhookUrl: useCallback(async () => {
234236
const id = getDevhookID(directory) ?? createDevhookID(directory);
235237
setDevhookID(id);
236-
return `https://${id}.blink.host`;
238+
const host = getHost();
239+
const client = host ? new Client({ baseURL: host }) : new Client();
240+
return client.devhook.getUrl(id);
237241
}, [directory]),
238242
});
239243

0 commit comments

Comments
 (0)