openbot 0.5.10 → 0.5.12
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/app/cli.js +1 -1
- package/dist/app/server.js +6 -10
- package/dist/app/webhook-routing.js +1 -1
- package/dist/plugins/storage/service.js +9 -11
- package/docs/plugins.md +14 -2
- package/package.json +2 -2
- package/src/app/cli.ts +1 -1
- package/src/app/server.ts +9 -13
- package/src/app/types.ts +2 -1
- package/src/app/webhook-routing.ts +1 -1
- package/src/plugins/storage/service.ts +8 -10
package/dist/app/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ function checkNodeVersion() {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
checkNodeVersion();
|
|
21
|
-
program.name('openbot').description('OpenBot CLI').version('0.5.
|
|
21
|
+
program.name('openbot').description('OpenBot CLI').version('0.5.12');
|
|
22
22
|
program
|
|
23
23
|
.command('start')
|
|
24
24
|
.description('Start the OpenBot harness')
|
package/dist/app/server.js
CHANGED
|
@@ -447,7 +447,7 @@ export async function startServer(options = {}) {
|
|
|
447
447
|
}
|
|
448
448
|
});
|
|
449
449
|
const WEBHOOK_TIMEOUT_MS = Number(process.env.OPENBOT_WEBHOOK_TIMEOUT_MS ?? 30000);
|
|
450
|
-
|
|
450
|
+
const handleInboundWebhook = async (req, res) => {
|
|
451
451
|
const provider = req.params.provider?.trim();
|
|
452
452
|
if (!provider || !/^[a-z0-9][a-z0-9_-]*$/i.test(provider)) {
|
|
453
453
|
res.status(400).json({ error: 'Invalid webhook provider' });
|
|
@@ -471,6 +471,7 @@ export async function startServer(options = {}) {
|
|
|
471
471
|
type: 'action:webhook',
|
|
472
472
|
data: {
|
|
473
473
|
provider,
|
|
474
|
+
method: req.method,
|
|
474
475
|
rawBody: rawBody.toString('base64'),
|
|
475
476
|
headers: pickWebhookHeaders(req),
|
|
476
477
|
query: req.query,
|
|
@@ -478,14 +479,6 @@ export async function startServer(options = {}) {
|
|
|
478
479
|
};
|
|
479
480
|
try {
|
|
480
481
|
ensureEventId(event);
|
|
481
|
-
console.log('runAgent', {
|
|
482
|
-
runId,
|
|
483
|
-
agentId,
|
|
484
|
-
event,
|
|
485
|
-
persistEvents: true,
|
|
486
|
-
publicBaseUrl: resolvePublicBaseUrl(),
|
|
487
|
-
onEvent,
|
|
488
|
-
});
|
|
489
482
|
const runPromise = runAgent({
|
|
490
483
|
runId,
|
|
491
484
|
agentId,
|
|
@@ -511,6 +504,7 @@ export async function startServer(options = {}) {
|
|
|
511
504
|
const isTimeout = error instanceof Error && error.message === 'webhook_timeout';
|
|
512
505
|
console.error('[webhook] Failed to process inbound webhook', {
|
|
513
506
|
provider,
|
|
507
|
+
method: req.method,
|
|
514
508
|
agentId,
|
|
515
509
|
error,
|
|
516
510
|
});
|
|
@@ -520,7 +514,9 @@ export async function startServer(options = {}) {
|
|
|
520
514
|
}
|
|
521
515
|
respondAgentDispatchError(res, error, 'Failed to process webhook');
|
|
522
516
|
}
|
|
523
|
-
}
|
|
517
|
+
};
|
|
518
|
+
app.get('/api/webhooks/:provider', handleInboundWebhook);
|
|
519
|
+
app.post('/api/webhooks/:provider', handleInboundWebhook);
|
|
524
520
|
app.get('/api/state', async (req, res) => {
|
|
525
521
|
let event;
|
|
526
522
|
try {
|
|
@@ -14,7 +14,7 @@ export function respondAgentDispatchError(res, error, fallbackMessage) {
|
|
|
14
14
|
res.status(500).json({ error: fallbackMessage });
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Routes `POST /api/webhooks/:provider` to agent `<provider>` when that agent exists,
|
|
17
|
+
* Routes `GET`/`POST /api/webhooks/:provider` to agent `<provider>` when that agent exists,
|
|
18
18
|
* otherwise falls back to {@link WEBHOOK_AGENT_FALLBACK_ID}.
|
|
19
19
|
*/
|
|
20
20
|
export async function resolveWebhookAgentId(provider) {
|
|
@@ -4,11 +4,11 @@ import { DEFAULT_PLUGINS_DIR, DEFAULT_AGENTS_DIR, DEFAULT_CHANNELS_DIR, getBaseD
|
|
|
4
4
|
import fs from 'node:fs/promises';
|
|
5
5
|
import { readFileSync } from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { fileURLToPath
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import crypto from 'node:crypto';
|
|
9
9
|
import matter from 'gray-matter';
|
|
10
10
|
import { OPENBOT_PLUGIN_ID } from '../../app/openbot-plugin.js';
|
|
11
|
-
import { listBuiltInPlugins,
|
|
11
|
+
import { listBuiltInPlugins, resolvePlugin } from '../../services/plugins/registry.js';
|
|
12
12
|
import { enrichOpenbotPluginDescriptor } from '../../services/plugins/enrich-openbot-plugin.js';
|
|
13
13
|
import { processService } from '../../services/process.js';
|
|
14
14
|
import { memoryService } from '../memory/service.js';
|
|
@@ -372,24 +372,22 @@ const listPluginsFromDisk = async () => {
|
|
|
372
372
|
const ids = await listInstalledPluginIds(pluginsDir);
|
|
373
373
|
const descriptors = await Promise.all(ids.map(async (id) => {
|
|
374
374
|
try {
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
const module = await import(pathToFileURL(distPath).href);
|
|
378
|
-
const parsed = parsePluginModule(module);
|
|
379
|
-
if (!parsed)
|
|
375
|
+
const plugin = await resolvePlugin(id);
|
|
376
|
+
if (!plugin)
|
|
380
377
|
return null;
|
|
378
|
+
const pluginDir = path.join(pluginsDir, id);
|
|
381
379
|
const [image, version] = await Promise.all([
|
|
382
380
|
resolveEntityImageDataUrl(pluginDir),
|
|
383
381
|
readPluginPackageVersion(pluginDir),
|
|
384
382
|
]);
|
|
385
383
|
return {
|
|
386
384
|
id,
|
|
387
|
-
name:
|
|
388
|
-
description:
|
|
385
|
+
name: plugin.name || id,
|
|
386
|
+
description: plugin.description || '',
|
|
389
387
|
builtIn: false,
|
|
390
388
|
version,
|
|
391
|
-
image:
|
|
392
|
-
configSchema:
|
|
389
|
+
image: plugin.image || image,
|
|
390
|
+
configSchema: plugin.configSchema,
|
|
393
391
|
createdAt: new Date(),
|
|
394
392
|
updatedAt: new Date(),
|
|
395
393
|
};
|
package/docs/plugins.md
CHANGED
|
@@ -62,16 +62,28 @@ model as the tool result (`history.ts`). Structured fields (e.g. `list`,
|
|
|
62
62
|
|
|
63
63
|
## Inbound webhooks
|
|
64
64
|
|
|
65
|
-
`POST /api/webhooks/:provider`
|
|
66
|
-
`action:webhook`. The URL segment is used as the target agent id when
|
|
65
|
+
`GET` and `POST /api/webhooks/:provider` forward third-party HTTP requests onto the
|
|
66
|
+
bus as `action:webhook`. The URL segment is used as the target agent id when
|
|
67
67
|
`~/.openbot/agents/<provider>/AGENT.md` exists; otherwise the request falls back
|
|
68
68
|
to the `system` agent. Attach a plugin to the routed agent and handle
|
|
69
69
|
verification, channel/thread routing, transforms, and HTTP responses there.
|
|
70
70
|
|
|
71
|
+
GET requests have an empty `rawBody`; use `event.data.method` and `event.data.query`
|
|
72
|
+
for URL verification challenges (for example Meta `hub.challenge`).
|
|
73
|
+
|
|
71
74
|
```ts
|
|
72
75
|
builder.on('action:webhook', async function* (event) {
|
|
73
76
|
if (event.data?.provider !== 'slack') return;
|
|
74
77
|
|
|
78
|
+
if (event.data.method === 'GET') {
|
|
79
|
+
const challenge = event.data.query?.['hub.challenge'];
|
|
80
|
+
yield {
|
|
81
|
+
type: 'action:webhook:http-response',
|
|
82
|
+
data: { status: 200, body: String(challenge) },
|
|
83
|
+
};
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
const rawBody = Buffer.from(event.data.rawBody, 'base64');
|
|
76
88
|
|
|
77
89
|
// Optional: control the HTTP response (URL challenges, 401s, custom acks)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbot",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"build": "tsc && mkdir -p dist/assets && cp src/assets/icon.svg dist/assets/icon.svg",
|
|
12
12
|
"start": "node dist/app/cli.js start",
|
|
13
13
|
"format": "prettier --write .",
|
|
14
|
-
"push": "fly deploy --build-only --push --config deploy/fly.toml -a openbot-runtime --image-label v0.5.
|
|
14
|
+
"push": "fly deploy --build-only --push --config deploy/fly.toml -a openbot-runtime --image-label v0.5.12"
|
|
15
15
|
},
|
|
16
16
|
"bin": {
|
|
17
17
|
"openbot": "./dist/app/cli.js"
|
package/src/app/cli.ts
CHANGED
package/src/app/server.ts
CHANGED
|
@@ -127,7 +127,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
127
127
|
const threadId = parts[1]; // undefined if no ":"
|
|
128
128
|
storageService
|
|
129
129
|
.setLastRead({ channelId, threadId, lastReadEventId: chunk.id })
|
|
130
|
-
.catch(() => {
|
|
130
|
+
.catch(() => {});
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
threadClients.forEach((client) => {
|
|
@@ -266,7 +266,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
266
266
|
return storageService.setLastRead({ channelId, threadId, lastReadEventId: latestId });
|
|
267
267
|
}
|
|
268
268
|
})
|
|
269
|
-
.catch(() => {
|
|
269
|
+
.catch(() => {});
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
if (channelId === GLOBAL_CHANNEL_ID) {
|
|
@@ -539,7 +539,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
539
539
|
|
|
540
540
|
const WEBHOOK_TIMEOUT_MS = Number(process.env.OPENBOT_WEBHOOK_TIMEOUT_MS ?? 30_000);
|
|
541
541
|
|
|
542
|
-
|
|
542
|
+
const handleInboundWebhook = async (req: express.Request, res: express.Response) => {
|
|
543
543
|
const provider = req.params.provider?.trim();
|
|
544
544
|
if (!provider || !/^[a-z0-9][a-z0-9_-]*$/i.test(provider)) {
|
|
545
545
|
res.status(400).json({ error: 'Invalid webhook provider' });
|
|
@@ -567,6 +567,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
567
567
|
type: 'action:webhook',
|
|
568
568
|
data: {
|
|
569
569
|
provider,
|
|
570
|
+
method: req.method,
|
|
570
571
|
rawBody: rawBody.toString('base64'),
|
|
571
572
|
headers: pickWebhookHeaders(req),
|
|
572
573
|
query: req.query as Record<string, unknown>,
|
|
@@ -576,15 +577,6 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
576
577
|
try {
|
|
577
578
|
ensureEventId(event);
|
|
578
579
|
|
|
579
|
-
console.log('runAgent', {
|
|
580
|
-
runId,
|
|
581
|
-
agentId,
|
|
582
|
-
event,
|
|
583
|
-
persistEvents: true,
|
|
584
|
-
publicBaseUrl: resolvePublicBaseUrl(),
|
|
585
|
-
onEvent,
|
|
586
|
-
});
|
|
587
|
-
|
|
588
580
|
const runPromise = runAgent({
|
|
589
581
|
runId,
|
|
590
582
|
agentId,
|
|
@@ -610,6 +602,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
610
602
|
const isTimeout = error instanceof Error && error.message === 'webhook_timeout';
|
|
611
603
|
console.error('[webhook] Failed to process inbound webhook', {
|
|
612
604
|
provider,
|
|
605
|
+
method: req.method,
|
|
613
606
|
agentId,
|
|
614
607
|
error,
|
|
615
608
|
});
|
|
@@ -621,7 +614,10 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
621
614
|
|
|
622
615
|
respondAgentDispatchError(res, error, 'Failed to process webhook');
|
|
623
616
|
}
|
|
624
|
-
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
app.get('/api/webhooks/:provider', handleInboundWebhook);
|
|
620
|
+
app.post('/api/webhooks/:provider', handleInboundWebhook);
|
|
625
621
|
|
|
626
622
|
app.get('/api/state', async (req, res) => {
|
|
627
623
|
let event: OpenBotEvent;
|
package/src/app/types.ts
CHANGED
|
@@ -148,11 +148,12 @@ export type GetActiveRunsEvent = BaseEvent & {
|
|
|
148
148
|
type: 'action:storage:get-active-runs';
|
|
149
149
|
};
|
|
150
150
|
|
|
151
|
-
/** Inbound third-party webhook forwarded from `POST /api/webhooks/:provider`. */
|
|
151
|
+
/** Inbound third-party webhook forwarded from `GET`/`POST /api/webhooks/:provider`. */
|
|
152
152
|
export type WebhookEvent = BaseEvent & {
|
|
153
153
|
type: 'action:webhook';
|
|
154
154
|
data: {
|
|
155
155
|
provider: string;
|
|
156
|
+
method: string;
|
|
156
157
|
/** Request body encoded as base64 so plugins can verify HMACs on the raw bytes. */
|
|
157
158
|
rawBody: string;
|
|
158
159
|
headers: Record<string, string | string[] | undefined>;
|
|
@@ -24,7 +24,7 @@ export function respondAgentDispatchError(
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Routes `POST /api/webhooks/:provider` to agent `<provider>` when that agent exists,
|
|
27
|
+
* Routes `GET`/`POST /api/webhooks/:provider` to agent `<provider>` when that agent exists,
|
|
28
28
|
* otherwise falls back to {@link WEBHOOK_AGENT_FALLBACK_ID}.
|
|
29
29
|
*/
|
|
30
30
|
export async function resolveWebhookAgentId(provider: string): Promise<string> {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import fs from 'node:fs/promises';
|
|
18
18
|
import { readFileSync } from 'node:fs';
|
|
19
19
|
import path from 'node:path';
|
|
20
|
-
import { fileURLToPath
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
21
|
import crypto from 'node:crypto';
|
|
22
22
|
import matter from 'gray-matter';
|
|
23
23
|
import {
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from '../../services/plugins/domain.js';
|
|
33
33
|
import type { PluginRef } from '../../services/plugins/types.js';
|
|
34
34
|
import { OPENBOT_PLUGIN_ID } from '../../app/openbot-plugin.js';
|
|
35
|
-
import { listBuiltInPlugins,
|
|
35
|
+
import { listBuiltInPlugins, resolvePlugin } from '../../services/plugins/registry.js';
|
|
36
36
|
import { enrichOpenbotPluginDescriptor } from '../../services/plugins/enrich-openbot-plugin.js';
|
|
37
37
|
import { OpenBotEvent, OpenBotState } from '../../app/types.js';
|
|
38
38
|
import { processService } from '../../services/process.js';
|
|
@@ -456,23 +456,21 @@ const listPluginsFromDisk = async (): Promise<PluginDescriptor[]> => {
|
|
|
456
456
|
const descriptors = await Promise.all(
|
|
457
457
|
ids.map(async (id): Promise<PluginDescriptor | null> => {
|
|
458
458
|
try {
|
|
459
|
+
const plugin = await resolvePlugin(id);
|
|
460
|
+
if (!plugin) return null;
|
|
459
461
|
const pluginDir = path.join(pluginsDir, id);
|
|
460
|
-
const distPath = path.join(pluginDir, 'dist', 'index.js');
|
|
461
|
-
const module = await import(pathToFileURL(distPath).href);
|
|
462
|
-
const parsed = parsePluginModule(module as Record<string, unknown>);
|
|
463
|
-
if (!parsed) return null;
|
|
464
462
|
const [image, version] = await Promise.all([
|
|
465
463
|
resolveEntityImageDataUrl(pluginDir),
|
|
466
464
|
readPluginPackageVersion(pluginDir),
|
|
467
465
|
]);
|
|
468
466
|
return {
|
|
469
467
|
id,
|
|
470
|
-
name:
|
|
471
|
-
description:
|
|
468
|
+
name: plugin.name || id,
|
|
469
|
+
description: plugin.description || '',
|
|
472
470
|
builtIn: false,
|
|
473
471
|
version,
|
|
474
|
-
image:
|
|
475
|
-
configSchema:
|
|
472
|
+
image: plugin.image || image,
|
|
473
|
+
configSchema: plugin.configSchema,
|
|
476
474
|
createdAt: new Date(),
|
|
477
475
|
updatedAt: new Date(),
|
|
478
476
|
};
|