@desplega.ai/agent-swarm 1.129.0 → 1.131.0
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/README.md +2 -2
- package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
- package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
- package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
- package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
- package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
- package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
- package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
- package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
- package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
- package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
- package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
- package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
- package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
- package/dist/cli-3fj60etf.js +153 -0
- package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
- package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
- package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
- package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
- package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
- package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
- package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
- package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
- package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
- package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
- package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
- package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
- package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
- package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
- package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
- package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
- package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
- package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
- package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
- package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
- package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
- package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
- package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
- package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
- package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
- package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
- package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
- package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
- package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
- package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
- package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
- package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
- package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
- package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
- package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
- package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
- package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
- package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
- package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
- package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
- package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
- package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
- package/dist/cli-zsspvvm2.js +490 -0
- package/dist/cli.js +27 -25
- package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
- package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
- package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
- package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
- package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
- package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
- package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
- package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
- package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
- package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
- package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
- package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
- package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
- package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
- package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
- package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
- package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
- package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
- package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
- package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
- package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
- package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
- package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
- package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
- package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
- package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
- package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
- package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
- package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
- package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
- package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
- package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
- package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
- package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
- package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
- package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
- package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
- package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
- package/openapi.json +34039 -11036
- package/package.json +8 -6
- package/src/apps/definition.ts +368 -26
- package/src/apps/row-store.ts +66 -10
- package/src/apps/schema-migrate.ts +95 -1
- package/src/apps/store.ts +13 -4
- package/src/apps/sync-run-as.ts +18 -0
- package/src/apps/sync.ts +934 -0
- package/src/apps/version.ts +16 -2
- package/src/assets/key.ts +8 -1
- package/src/be/asset-key-audit.ts +3 -1
- package/src/be/db-queries/mcp-oauth.ts +297 -24
- package/src/be/db.ts +42 -1
- package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
- package/src/be/scripts/db.ts +5 -2
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
- package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
- package/src/be/seed-scripts/index.ts +18 -0
- package/src/be/seed-skills/index.ts +7 -0
- package/src/http/active-sessions.ts +26 -18
- package/src/http/agents.ts +94 -27
- package/src/http/api-keys.ts +91 -17
- package/src/http/approval-requests.ts +87 -10
- package/src/http/apps.ts +361 -53
- package/src/http/assets.ts +165 -31
- package/src/http/budgets.ts +7 -5
- package/src/http/codex-oauth-keep-warm.ts +21 -3
- package/src/http/config.ts +46 -16
- package/src/http/context.ts +33 -6
- package/src/http/ecosystem.ts +20 -6
- package/src/http/events.ts +24 -9
- package/src/http/favorites.ts +20 -6
- package/src/http/fs.ts +60 -17
- package/src/http/heartbeat.ts +18 -4
- package/src/http/inbox-state.ts +12 -6
- package/src/http/integrations.ts +25 -5
- package/src/http/kv.ts +36 -15
- package/src/http/mcp-bridge.ts +5 -1
- package/src/http/mcp-oauth.ts +332 -17
- package/src/http/mcp-servers.ts +56 -18
- package/src/http/memory.ts +282 -29
- package/src/http/metrics.ts +80 -17
- package/src/http/oauth-callback.ts +10 -4
- package/src/http/oauth-generic.ts +5 -1
- package/src/http/oauth-locks.ts +6 -3
- package/src/http/openapi.ts +57 -9
- package/src/http/page-proxy.ts +25 -5
- package/src/http/pages-public.ts +37 -20
- package/src/http/pages.ts +93 -28
- package/src/http/poll.ts +89 -5
- package/src/http/pricing.ts +9 -6
- package/src/http/prompt-templates.ts +85 -21
- package/src/http/route-def.ts +67 -4
- package/src/http/schedules.ts +63 -14
- package/src/http/script-connection-proxy.ts +30 -4
- package/src/http/script-connections.ts +393 -43
- package/src/http/script-runs.ts +73 -19
- package/src/http/scripts.ts +257 -39
- package/src/http/session-data.ts +87 -14
- package/src/http/sessions.ts +80 -5
- package/src/http/skills.ts +115 -35
- package/src/http/stats.ts +94 -13
- package/src/http/task-templates.ts +6 -4
- package/src/http/tasks.ts +230 -61
- package/src/http/trackers/jira.ts +90 -30
- package/src/http/trackers/linear.ts +46 -12
- package/src/http/users.ts +162 -25
- package/src/http/utils.ts +12 -2
- package/src/http/webhooks.ts +30 -16
- package/src/http/workflow-events.ts +18 -5
- package/src/http/workflows.ts +128 -39
- package/src/http/x.ts +37 -6
- package/src/oauth/ensure-mcp-token.ts +16 -1
- package/src/oauth/mcp-wrapper.ts +281 -26
- package/src/providers/pi-mono-adapter.ts +1 -1
- package/src/scripts-runtime/credential-broker/README.md +1 -1
- package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/stdlib/fetch.ts +6 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +2 -0
- package/src/tests/apps-rbac.test.ts +110 -0
- package/src/tests/apps-spike2.test.ts +4 -1
- package/src/tests/apps-sync-engine.test.ts +2540 -0
- package/src/tests/apps-sync.test.ts +1627 -0
- package/src/tests/asset-key-api.test.ts +48 -2
- package/src/tests/asset-key-invariants.test.ts +92 -0
- package/src/tests/asset-key-migration.test.ts +174 -8
- package/src/tests/asset-key-normalization.test.ts +2 -0
- package/src/tests/credential-broker.test.ts +27 -2
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
- package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
- package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
- package/src/tests/mcp-oauth-queries.test.ts +153 -0
- package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
- package/src/tests/memory-http-recall-gating.test.ts +2 -0
- package/src/tests/openapi-response-contract.test.ts +138 -0
- package/src/tests/rbac-admission-e2e.test.ts +68 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/scripts-runtime-fetch.test.ts +121 -0
- package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/swarm-tool-result-gate.test.ts +32 -0
- package/src/tests/task-working-dir.test.ts +8 -4
- package/src/tests/whoami-route.test.ts +127 -0
- package/src/tests/workflow-engine-v2.test.ts +80 -7
- package/src/tests/workflow-swarm-script.test.ts +104 -0
- package/src/tools/app-get.ts +10 -2
- package/src/tools/app-rollback.ts +1 -0
- package/src/tools/app-sync.ts +112 -0
- package/src/tools/script-common.ts +20 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +11 -0
- package/src/tools/workflows/create-workflow.ts +4 -1
- package/src/types.ts +1394 -1206
- package/src/utils/template.ts +8 -1
- package/src/utils/zod-openapi.ts +11 -0
- package/src/workflows/engine.ts +27 -4
- package/src/workflows/executors/swarm-script.ts +28 -0
- package/src/workflows/retry-poller.ts +19 -2
- package/templates/skills/apps/content.md +94 -5
- package/templates/skills/asset-namespaces/content.md +2 -2
- package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
- package/templates/skills/scheduled-task-resilience/config.json +14 -0
- package/templates/skills/scheduled-task-resilience/content.md +97 -0
- package/dist/cli-7wmybrt5.js +0 -115
- package/dist/cli-d91pvmq1.js +0 -14
package/src/http/users.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Endpoint set:
|
|
11
11
|
*
|
|
12
|
+
* GET /api/whoami
|
|
12
13
|
* GET /api/users
|
|
13
14
|
* POST /api/users
|
|
14
15
|
* GET /api/users/unmapped (must precede /:id)
|
|
@@ -47,9 +48,11 @@ import {
|
|
|
47
48
|
revokeToken,
|
|
48
49
|
unlinkIdentity,
|
|
49
50
|
} from "../be/users";
|
|
51
|
+
import { UserSchema } from "../types";
|
|
52
|
+
import { getRequestAuth } from "../utils/request-auth-context";
|
|
50
53
|
import { getOperatorActor } from "./operator-actor";
|
|
51
54
|
import { route } from "./route-def";
|
|
52
|
-
import {
|
|
55
|
+
import { jsonError } from "./utils";
|
|
53
56
|
|
|
54
57
|
// ─── Response composition ────────────────────────────────────────────────────
|
|
55
58
|
|
|
@@ -78,8 +81,85 @@ function syncUserBudgetMirror(userId: string, dailyBudgetUsd: number | null | un
|
|
|
78
81
|
upsertBudget("user", userId, dailyBudgetUsd);
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
// ─── Response schemas ────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
const UserIdentityLinkSchema = z.object({
|
|
87
|
+
kind: z.string(),
|
|
88
|
+
externalId: z.string(),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const UserTokenSummarySchema = z.object({
|
|
92
|
+
id: z.string(),
|
|
93
|
+
userId: z.string(),
|
|
94
|
+
label: z.string().nullable(),
|
|
95
|
+
tokenPreview: z.string(),
|
|
96
|
+
createdAt: z.string(),
|
|
97
|
+
lastUsedAt: z.string().nullable(),
|
|
98
|
+
revokedAt: z.string().nullable(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const IdentityEventSchema = z.object({
|
|
102
|
+
id: z.string(),
|
|
103
|
+
userId: z.string(),
|
|
104
|
+
// `IdentityEvent.eventType` is read back from the DB as a plain `string`
|
|
105
|
+
// (the CHECK constraint guarantees membership in `IdentityEventTypeSchema`,
|
|
106
|
+
// but the read path's static type is not narrowed to the enum).
|
|
107
|
+
eventType: z.string(),
|
|
108
|
+
actor: z.string(),
|
|
109
|
+
before: z.unknown().nullable(),
|
|
110
|
+
after: z.unknown().nullable(),
|
|
111
|
+
createdAt: z.string(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/** `composeUser()`'s shape — base user row + identities + token summaries + recent events. */
|
|
115
|
+
const ComposedUserSchema = UserSchema.extend({
|
|
116
|
+
identities: z.array(UserIdentityLinkSchema),
|
|
117
|
+
tokens: z.array(UserTokenSummarySchema),
|
|
118
|
+
recentEvents: z.array(IdentityEventSchema),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/** `collectUnmappedForKind()`'s per-externalId grouped shape. */
|
|
122
|
+
const UnmappedIdentitySchema = z.object({
|
|
123
|
+
kind: z.string(),
|
|
124
|
+
externalId: z.string(),
|
|
125
|
+
lastSeenAt: z.string().nullable(),
|
|
126
|
+
count: z.number(),
|
|
127
|
+
sampleEventType: z.string().nullable(),
|
|
128
|
+
sampleContext: z.unknown().nullable(),
|
|
129
|
+
});
|
|
130
|
+
|
|
81
131
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
82
132
|
|
|
133
|
+
/**
|
|
134
|
+
* DES-771: identity resolution for embedded dashboards. A client holding a
|
|
135
|
+
* user-bound `aswt_` token learns which user the server attributes its
|
|
136
|
+
* requests to (the token forces requester/audit attribution server-side, so
|
|
137
|
+
* this is the only identity the client can act as). The operator key resolves
|
|
138
|
+
* to `kind: "operator"` with no bound user.
|
|
139
|
+
*/
|
|
140
|
+
const whoamiRoute = route({
|
|
141
|
+
method: "get",
|
|
142
|
+
path: "/api/whoami",
|
|
143
|
+
pattern: ["api", "whoami"],
|
|
144
|
+
summary: "Resolve the authenticated principal behind the presented bearer",
|
|
145
|
+
tags: ["Users"],
|
|
146
|
+
responses: {
|
|
147
|
+
200: {
|
|
148
|
+
description: "Authenticated principal",
|
|
149
|
+
schema: z.object({
|
|
150
|
+
kind: z.enum(["operator", "user"]),
|
|
151
|
+
// `z.union([UserSchema, z.null()])`, NOT `UserSchema.nullable()`:
|
|
152
|
+
// .nullable() on a registered schema rewrites the shared
|
|
153
|
+
// `components.schemas.User` to `object | null` for every consumer;
|
|
154
|
+
// the union keeps nullability local to this response field.
|
|
155
|
+
user: z.union([UserSchema, z.null()]),
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
401: { description: "Unauthorized" },
|
|
159
|
+
},
|
|
160
|
+
auth: { apiKey: true },
|
|
161
|
+
});
|
|
162
|
+
|
|
83
163
|
const listUsers = route({
|
|
84
164
|
method: "get",
|
|
85
165
|
path: "/api/users",
|
|
@@ -90,7 +170,10 @@ const listUsers = route({
|
|
|
90
170
|
recentEvents: z.coerce.number().int().min(0).max(50).optional(),
|
|
91
171
|
}),
|
|
92
172
|
responses: {
|
|
93
|
-
200: {
|
|
173
|
+
200: {
|
|
174
|
+
description: "List of users",
|
|
175
|
+
schema: z.object({ users: z.array(ComposedUserSchema.nullable()) }),
|
|
176
|
+
},
|
|
94
177
|
401: { description: "Unauthorized" },
|
|
95
178
|
},
|
|
96
179
|
auth: { apiKey: true },
|
|
@@ -118,7 +201,10 @@ const createUserRoute = route({
|
|
|
118
201
|
.optional(),
|
|
119
202
|
}),
|
|
120
203
|
responses: {
|
|
121
|
-
200: {
|
|
204
|
+
200: {
|
|
205
|
+
description: "User created",
|
|
206
|
+
schema: z.object({ user: ComposedUserSchema.nullable() }),
|
|
207
|
+
},
|
|
122
208
|
400: { description: "Validation error" },
|
|
123
209
|
401: { description: "Unauthorized" },
|
|
124
210
|
},
|
|
@@ -136,7 +222,10 @@ const listUnmapped = route({
|
|
|
136
222
|
limit: z.coerce.number().int().min(1).max(1000).optional(),
|
|
137
223
|
}),
|
|
138
224
|
responses: {
|
|
139
|
-
200: {
|
|
225
|
+
200: {
|
|
226
|
+
description: "List of unmapped identities sorted by count DESC, lastSeenAt DESC",
|
|
227
|
+
schema: z.object({ unmapped: z.array(UnmappedIdentitySchema) }),
|
|
228
|
+
},
|
|
140
229
|
401: { description: "Unauthorized" },
|
|
141
230
|
},
|
|
142
231
|
auth: { apiKey: true },
|
|
@@ -158,7 +247,10 @@ const resolveUnmapped = route({
|
|
|
158
247
|
}),
|
|
159
248
|
]),
|
|
160
249
|
responses: {
|
|
161
|
-
200: {
|
|
250
|
+
200: {
|
|
251
|
+
description: "Identity linked + kv entries cleared",
|
|
252
|
+
schema: z.object({ user: ComposedUserSchema.nullable() }),
|
|
253
|
+
},
|
|
162
254
|
400: { description: "Validation error" },
|
|
163
255
|
401: { description: "Unauthorized" },
|
|
164
256
|
404: { description: "Target user not found" },
|
|
@@ -177,7 +269,10 @@ const getUserRoute = route({
|
|
|
177
269
|
recentEvents: z.coerce.number().int().min(0).max(200).optional(),
|
|
178
270
|
}),
|
|
179
271
|
responses: {
|
|
180
|
-
200: {
|
|
272
|
+
200: {
|
|
273
|
+
description: "User row",
|
|
274
|
+
schema: z.object({ user: ComposedUserSchema }),
|
|
275
|
+
},
|
|
181
276
|
401: { description: "Unauthorized" },
|
|
182
277
|
404: { description: "User not found" },
|
|
183
278
|
},
|
|
@@ -213,7 +308,10 @@ const updateUserRoute = route({
|
|
|
213
308
|
message: "At least one field must be provided",
|
|
214
309
|
}),
|
|
215
310
|
responses: {
|
|
216
|
-
200: {
|
|
311
|
+
200: {
|
|
312
|
+
description: "User updated",
|
|
313
|
+
schema: z.object({ user: ComposedUserSchema.nullable() }),
|
|
314
|
+
},
|
|
217
315
|
400: { description: "Validation error or empty body" },
|
|
218
316
|
401: { description: "Unauthorized" },
|
|
219
317
|
404: { description: "User not found" },
|
|
@@ -234,7 +332,14 @@ const mintUserMcpTokenRoute = route({
|
|
|
234
332
|
label: z.string().nullable().optional(),
|
|
235
333
|
}),
|
|
236
334
|
responses: {
|
|
237
|
-
200: {
|
|
335
|
+
200: {
|
|
336
|
+
description: "Minted token plaintext, token summary and composed user",
|
|
337
|
+
schema: z.object({
|
|
338
|
+
plaintext: z.string(),
|
|
339
|
+
token: UserTokenSummarySchema.optional(),
|
|
340
|
+
user: ComposedUserSchema.nullable(),
|
|
341
|
+
}),
|
|
342
|
+
},
|
|
238
343
|
401: { description: "Unauthorized" },
|
|
239
344
|
404: { description: "User not found" },
|
|
240
345
|
},
|
|
@@ -249,7 +354,10 @@ const revokeUserMcpTokenRoute = route({
|
|
|
249
354
|
tags: ["Users"],
|
|
250
355
|
params: z.object({ id: z.string(), tokenId: z.string() }),
|
|
251
356
|
responses: {
|
|
252
|
-
200: {
|
|
357
|
+
200: {
|
|
358
|
+
description: "Composed user after token revocation",
|
|
359
|
+
schema: z.object({ user: ComposedUserSchema.nullable() }),
|
|
360
|
+
},
|
|
253
361
|
401: { description: "Unauthorized" },
|
|
254
362
|
404: { description: "User or token not found" },
|
|
255
363
|
},
|
|
@@ -265,7 +373,10 @@ const mergeUsersRoute = route({
|
|
|
265
373
|
params: z.object({ id: z.string() }),
|
|
266
374
|
body: z.object({ sourceUserId: z.string().min(1) }),
|
|
267
375
|
responses: {
|
|
268
|
-
200: {
|
|
376
|
+
200: {
|
|
377
|
+
description: "Merged user",
|
|
378
|
+
schema: z.object({ user: ComposedUserSchema.nullable() }),
|
|
379
|
+
},
|
|
269
380
|
400: { description: "Validation error (e.g. target == source)" },
|
|
270
381
|
401: { description: "Unauthorized" },
|
|
271
382
|
404: { description: "Target or source user not found" },
|
|
@@ -285,7 +396,10 @@ const listEventsRoute = route({
|
|
|
285
396
|
before: z.string().optional(),
|
|
286
397
|
}),
|
|
287
398
|
responses: {
|
|
288
|
-
200: {
|
|
399
|
+
200: {
|
|
400
|
+
description: "Array of identity events",
|
|
401
|
+
schema: z.object({ events: z.array(IdentityEventSchema) }),
|
|
402
|
+
},
|
|
289
403
|
401: { description: "Unauthorized" },
|
|
290
404
|
404: { description: "User not found" },
|
|
291
405
|
},
|
|
@@ -301,7 +415,10 @@ const addIdentityRoute = route({
|
|
|
301
415
|
params: z.object({ id: z.string() }),
|
|
302
416
|
body: z.object({ kind: z.string().min(1), externalId: z.string().min(1) }),
|
|
303
417
|
responses: {
|
|
304
|
-
200: {
|
|
418
|
+
200: {
|
|
419
|
+
description: "Updated identity list",
|
|
420
|
+
schema: z.object({ identities: z.array(UserIdentityLinkSchema) }),
|
|
421
|
+
},
|
|
305
422
|
400: { description: "Validation error or PK collision" },
|
|
306
423
|
401: { description: "Unauthorized" },
|
|
307
424
|
404: { description: "User not found" },
|
|
@@ -317,7 +434,10 @@ const deleteIdentityRoute = route({
|
|
|
317
434
|
tags: ["Users"],
|
|
318
435
|
params: z.object({ id: z.string(), kind: z.string(), externalId: z.string() }),
|
|
319
436
|
responses: {
|
|
320
|
-
200: {
|
|
437
|
+
200: {
|
|
438
|
+
description: "Updated identity list",
|
|
439
|
+
schema: z.object({ identities: z.array(UserIdentityLinkSchema) }),
|
|
440
|
+
},
|
|
321
441
|
401: { description: "Unauthorized" },
|
|
322
442
|
404: { description: "User not found" },
|
|
323
443
|
},
|
|
@@ -403,13 +523,26 @@ export async function handleUsers(
|
|
|
403
523
|
pathSegments: string[],
|
|
404
524
|
queryParams: URLSearchParams,
|
|
405
525
|
): Promise<boolean> {
|
|
526
|
+
// ─── GET /api/whoami ───────────────────────────────────────────────────────
|
|
527
|
+
if (whoamiRoute.match(req.method, pathSegments)) {
|
|
528
|
+
const parsed = await whoamiRoute.parse(req, res, pathSegments, queryParams);
|
|
529
|
+
if (!parsed) return true;
|
|
530
|
+
const auth = getRequestAuth(req);
|
|
531
|
+
if (auth?.kind === "user") {
|
|
532
|
+
whoamiRoute.respond(res, 200, { kind: "user", user: auth.user });
|
|
533
|
+
} else {
|
|
534
|
+
whoamiRoute.respond(res, 200, { kind: "operator", user: null });
|
|
535
|
+
}
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
406
539
|
// ─── GET /api/users ────────────────────────────────────────────────────────
|
|
407
540
|
if (listUsers.match(req.method, pathSegments)) {
|
|
408
541
|
const parsed = await listUsers.parse(req, res, pathSegments, queryParams);
|
|
409
542
|
if (!parsed) return true;
|
|
410
543
|
const recentLimit = parsed.query.recentEvents ?? 5;
|
|
411
544
|
const users = getAllUsers().map((u) => composeUser(u.id, recentLimit));
|
|
412
|
-
|
|
545
|
+
listUsers.respond(res, 200, { users });
|
|
413
546
|
return true;
|
|
414
547
|
}
|
|
415
548
|
|
|
@@ -433,7 +566,7 @@ export async function handleUsers(
|
|
|
433
566
|
});
|
|
434
567
|
}
|
|
435
568
|
const composed = composeUser(user.id);
|
|
436
|
-
|
|
569
|
+
createUserRoute.respond(res, 200, { user: composed });
|
|
437
570
|
} catch (err) {
|
|
438
571
|
jsonError(res, err instanceof Error ? err.message : "Failed to create user", 500);
|
|
439
572
|
}
|
|
@@ -454,7 +587,7 @@ export async function handleUsers(
|
|
|
454
587
|
const bl = b.lastSeenAt ?? "";
|
|
455
588
|
return bl.localeCompare(al);
|
|
456
589
|
});
|
|
457
|
-
|
|
590
|
+
listUnmapped.respond(res, 200, { unmapped: rows.slice(0, limit) });
|
|
458
591
|
return true;
|
|
459
592
|
}
|
|
460
593
|
|
|
@@ -494,7 +627,7 @@ export async function handleUsers(
|
|
|
494
627
|
deleteKv(ns, `${externalId}:meta`);
|
|
495
628
|
deleteKv(ns, `${externalId}:count`);
|
|
496
629
|
const user = composeUser(targetUserId);
|
|
497
|
-
|
|
630
|
+
resolveUnmapped.respond(res, 200, { user });
|
|
498
631
|
} catch (err) {
|
|
499
632
|
jsonError(res, err instanceof Error ? err.message : "Failed to resolve unmapped", 500);
|
|
500
633
|
}
|
|
@@ -513,7 +646,7 @@ export async function handleUsers(
|
|
|
513
646
|
limit: parsed.query.limit,
|
|
514
647
|
before: parsed.query.before,
|
|
515
648
|
});
|
|
516
|
-
|
|
649
|
+
listEventsRoute.respond(res, 200, { events });
|
|
517
650
|
return true;
|
|
518
651
|
}
|
|
519
652
|
|
|
@@ -531,7 +664,11 @@ export async function handleUsers(
|
|
|
531
664
|
try {
|
|
532
665
|
const { tokenId, plaintext } = mintToken(parsed.params.id, parsed.body.label ?? null, actor);
|
|
533
666
|
const token = listUserTokens(parsed.params.id).find((t) => t.id === tokenId);
|
|
534
|
-
|
|
667
|
+
mintUserMcpTokenRoute.respond(res, 200, {
|
|
668
|
+
plaintext,
|
|
669
|
+
token,
|
|
670
|
+
user: composeUser(parsed.params.id),
|
|
671
|
+
});
|
|
535
672
|
} catch (err) {
|
|
536
673
|
jsonError(res, err instanceof Error ? err.message : "Failed to mint token", 500);
|
|
537
674
|
}
|
|
@@ -559,7 +696,7 @@ export async function handleUsers(
|
|
|
559
696
|
|
|
560
697
|
try {
|
|
561
698
|
revokeToken(parsed.params.tokenId, actor);
|
|
562
|
-
|
|
699
|
+
revokeUserMcpTokenRoute.respond(res, 200, { user: composeUser(parsed.params.id) });
|
|
563
700
|
} catch (err) {
|
|
564
701
|
const message = err instanceof Error ? err.message : "Failed to revoke token";
|
|
565
702
|
if (message.includes("Token not found")) {
|
|
@@ -583,7 +720,7 @@ export async function handleUsers(
|
|
|
583
720
|
}
|
|
584
721
|
try {
|
|
585
722
|
linkIdentity(parsed.params.id, parsed.body.kind, parsed.body.externalId, actor);
|
|
586
|
-
|
|
723
|
+
addIdentityRoute.respond(res, 200, { identities: getUserIdentities(parsed.params.id) });
|
|
587
724
|
} catch (err) {
|
|
588
725
|
jsonError(res, err instanceof Error ? err.message : "Failed to link identity", 400);
|
|
589
726
|
}
|
|
@@ -607,7 +744,7 @@ export async function handleUsers(
|
|
|
607
744
|
const kind = decodeURIComponent(parsed.params.kind);
|
|
608
745
|
const externalId = decodeURIComponent(parsed.params.externalId);
|
|
609
746
|
unlinkIdentity(parsed.params.id, kind, externalId, actor);
|
|
610
|
-
|
|
747
|
+
deleteIdentityRoute.respond(res, 200, { identities: getUserIdentities(parsed.params.id) });
|
|
611
748
|
} catch (err) {
|
|
612
749
|
jsonError(res, err instanceof Error ? err.message : "Failed to unlink identity", 500);
|
|
613
750
|
}
|
|
@@ -692,7 +829,7 @@ export async function handleUsers(
|
|
|
692
829
|
|
|
693
830
|
// Re-compose AFTER the event so the response surfaces the merge event in
|
|
694
831
|
// recentEvents (otherwise the timeline is missing the event we just wrote).
|
|
695
|
-
|
|
832
|
+
mergeUsersRoute.respond(res, 200, { user: composeUser(targetId) });
|
|
696
833
|
} catch (err) {
|
|
697
834
|
jsonError(res, err instanceof Error ? err.message : "Failed to merge users", 500);
|
|
698
835
|
}
|
|
@@ -708,7 +845,7 @@ export async function handleUsers(
|
|
|
708
845
|
jsonError(res, "User not found", 404);
|
|
709
846
|
return true;
|
|
710
847
|
}
|
|
711
|
-
|
|
848
|
+
getUserRoute.respond(res, 200, { user: composed });
|
|
712
849
|
return true;
|
|
713
850
|
}
|
|
714
851
|
|
|
@@ -826,7 +963,7 @@ export async function handleUsers(
|
|
|
826
963
|
}
|
|
827
964
|
|
|
828
965
|
const composed = composeUser(parsed.params.id);
|
|
829
|
-
|
|
966
|
+
updateUserRoute.respond(res, 200, { user: composed });
|
|
830
967
|
} catch (err) {
|
|
831
968
|
jsonError(res, err instanceof Error ? err.message : "Failed to update user", 500);
|
|
832
969
|
}
|
package/src/http/utils.ts
CHANGED
|
@@ -79,13 +79,23 @@ export function agentWithCapacity<T extends { id: string; maxTasks?: number }>(
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Parse the JSON body of an incoming request.
|
|
84
|
+
*
|
|
85
|
+
* An empty (or whitespace-only) body parses as `{}` rather than throwing:
|
|
86
|
+
* `JSON.parse("")` raises a SyntaxError, and the `route()` factory only
|
|
87
|
+
* converts ZodErrors — so a bodyless POST used to escape as a 500. Yielding
|
|
88
|
+
* `{}` lets an all-optional body schema accept a bodyless request and turns a
|
|
89
|
+
* required-body schema's failure into the honest 400 its validation produces.
|
|
90
|
+
*/
|
|
83
91
|
export async function parseBody<T = unknown>(req: IncomingMessage): Promise<T> {
|
|
84
92
|
const chunks: Buffer[] = [];
|
|
85
93
|
for await (const chunk of req) {
|
|
86
94
|
chunks.push(chunk as Buffer);
|
|
87
95
|
}
|
|
88
|
-
|
|
96
|
+
const raw = Buffer.concat(chunks).toString();
|
|
97
|
+
if (raw.trim() === "") return {} as T;
|
|
98
|
+
return JSON.parse(raw) as T;
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
/**
|
package/src/http/webhooks.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
import type { AgentMailWebhookPayload } from "../agentmail";
|
|
3
4
|
import {
|
|
4
5
|
handleMessageReceived,
|
|
@@ -57,6 +58,16 @@ import { route } from "./route-def";
|
|
|
57
58
|
|
|
58
59
|
// ─── Route Definitions (documentation only — webhooks handle their own body parsing) ─
|
|
59
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Shared shape returned by the GitHub/GitLab dispatch handlers in
|
|
63
|
+
* ../github/handlers.ts and ../gitlab/handlers.ts — `{ created: boolean;
|
|
64
|
+
* taskId?: string }`, echoed back verbatim as the webhook ack body.
|
|
65
|
+
*/
|
|
66
|
+
const WebhookDispatchResultSchema = z.object({
|
|
67
|
+
created: z.boolean(),
|
|
68
|
+
taskId: z.string().optional(),
|
|
69
|
+
});
|
|
70
|
+
|
|
60
71
|
const githubWebhook = route({
|
|
61
72
|
method: "post",
|
|
62
73
|
path: "/api/github/webhook",
|
|
@@ -65,7 +76,10 @@ const githubWebhook = route({
|
|
|
65
76
|
tags: ["Webhooks"],
|
|
66
77
|
auth: { apiKey: false },
|
|
67
78
|
responses: {
|
|
68
|
-
200: {
|
|
79
|
+
200: {
|
|
80
|
+
description: "Event processed",
|
|
81
|
+
schema: z.union([z.object({ message: z.literal("pong") }), WebhookDispatchResultSchema]),
|
|
82
|
+
},
|
|
69
83
|
401: { description: "Invalid signature" },
|
|
70
84
|
503: { description: "GitHub integration not configured" },
|
|
71
85
|
},
|
|
@@ -79,7 +93,7 @@ const gitlabWebhook = route({
|
|
|
79
93
|
tags: ["Webhooks"],
|
|
80
94
|
auth: { apiKey: false },
|
|
81
95
|
responses: {
|
|
82
|
-
200: { description: "Event processed" },
|
|
96
|
+
200: { description: "Event processed", schema: WebhookDispatchResultSchema },
|
|
83
97
|
401: { description: "Invalid token" },
|
|
84
98
|
503: { description: "GitLab integration not configured" },
|
|
85
99
|
},
|
|
@@ -93,7 +107,7 @@ const agentmailWebhook = route({
|
|
|
93
107
|
tags: ["Webhooks"],
|
|
94
108
|
auth: { apiKey: false },
|
|
95
109
|
responses: {
|
|
96
|
-
200: { description: "Event received" },
|
|
110
|
+
200: { description: "Event received", schema: z.object({ received: z.literal(true) }) },
|
|
97
111
|
401: { description: "Invalid signature" },
|
|
98
112
|
503: { description: "AgentMail integration not configured" },
|
|
99
113
|
},
|
|
@@ -107,7 +121,13 @@ const kapsoWebhook = route({
|
|
|
107
121
|
tags: ["Webhooks"],
|
|
108
122
|
auth: { apiKey: false },
|
|
109
123
|
responses: {
|
|
110
|
-
200: {
|
|
124
|
+
200: {
|
|
125
|
+
description: "Event received",
|
|
126
|
+
schema: z.object({
|
|
127
|
+
received: z.literal(true),
|
|
128
|
+
routing: z.enum(["skip", "duplicate", "workflow", "task", "no_mapping", "error"]),
|
|
129
|
+
}),
|
|
130
|
+
},
|
|
111
131
|
401: { description: "Invalid signature" },
|
|
112
132
|
503: { description: "Kapso integration not configured" },
|
|
113
133
|
},
|
|
@@ -213,8 +233,7 @@ export async function handleWebhooks(
|
|
|
213
233
|
|
|
214
234
|
if (eventType === "ping") {
|
|
215
235
|
console.log("[GitHub] Received ping event - webhook configured successfully");
|
|
216
|
-
|
|
217
|
-
res.end(JSON.stringify({ message: "pong" }));
|
|
236
|
+
githubWebhook.respond(res, 200, { message: "pong" });
|
|
218
237
|
return true;
|
|
219
238
|
}
|
|
220
239
|
|
|
@@ -309,8 +328,7 @@ export async function handleWebhooks(
|
|
|
309
328
|
}
|
|
310
329
|
}
|
|
311
330
|
|
|
312
|
-
|
|
313
|
-
res.end(JSON.stringify(result));
|
|
331
|
+
githubWebhook.respond(res, 200, result);
|
|
314
332
|
} catch (err) {
|
|
315
333
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
316
334
|
console.error(`[GitHub] Error handling ${eventType} event: ${errorMessage}`);
|
|
@@ -425,8 +443,7 @@ export async function handleWebhooks(
|
|
|
425
443
|
}
|
|
426
444
|
}
|
|
427
445
|
|
|
428
|
-
|
|
429
|
-
res.end(JSON.stringify(result));
|
|
446
|
+
gitlabWebhook.respond(res, 200, result);
|
|
430
447
|
} catch (err) {
|
|
431
448
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
432
449
|
console.error(`[GitLab] Error handling ${objectKind} event: ${errorMessage}`);
|
|
@@ -471,8 +488,7 @@ export async function handleWebhooks(
|
|
|
471
488
|
|
|
472
489
|
// Return 200 immediately — Svix best practice to avoid retries.
|
|
473
490
|
// Processing happens asynchronously below; dedup is handled in handlers.ts.
|
|
474
|
-
|
|
475
|
-
res.end(JSON.stringify({ received: true }));
|
|
491
|
+
agentmailWebhook.respond(res, 200, { received: true });
|
|
476
492
|
|
|
477
493
|
const payload = verified as AgentMailWebhookPayload;
|
|
478
494
|
|
|
@@ -602,8 +618,7 @@ export async function handleWebhooks(
|
|
|
602
618
|
console.log(`[Kapso] Skipping event: ${routing.reason}`);
|
|
603
619
|
break;
|
|
604
620
|
}
|
|
605
|
-
|
|
606
|
-
res.end(JSON.stringify({ received: true, routing: routing.kind }));
|
|
621
|
+
kapsoWebhook.respond(res, 200, { received: true, routing: routing.kind });
|
|
607
622
|
} catch (err) {
|
|
608
623
|
// Never fail the delivery on a downstream dispatch error — we already
|
|
609
624
|
// verified + deduped. Log and ack so Kapso doesn't hammer retries.
|
|
@@ -613,8 +628,7 @@ export async function handleWebhooks(
|
|
|
613
628
|
} else {
|
|
614
629
|
console.error(`[Kapso] Error handling inbound event: ${errorMessage}`);
|
|
615
630
|
}
|
|
616
|
-
|
|
617
|
-
res.end(JSON.stringify({ received: true, routing: "error" }));
|
|
631
|
+
kapsoWebhook.respond(res, 200, { received: true, routing: "error" });
|
|
618
632
|
}
|
|
619
633
|
return true;
|
|
620
634
|
}
|
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { getWorkflowRun } from "../be/db";
|
|
4
4
|
import { workflowEventBus } from "../workflows/event-bus";
|
|
5
5
|
import { route } from "./route-def";
|
|
6
|
-
import {
|
|
6
|
+
import { jsonError } from "./utils";
|
|
7
7
|
|
|
8
8
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
9
9
|
|
|
@@ -30,7 +30,14 @@ const runScopedSignalRoute = route({
|
|
|
30
30
|
payload: z.record(z.string(), z.unknown()).optional(),
|
|
31
31
|
}),
|
|
32
32
|
responses: {
|
|
33
|
-
200: {
|
|
33
|
+
200: {
|
|
34
|
+
description: "Event emitted",
|
|
35
|
+
schema: z.object({
|
|
36
|
+
ok: z.literal(true),
|
|
37
|
+
name: z.string(),
|
|
38
|
+
runId: z.string(),
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
34
41
|
404: { description: "Workflow run not found" },
|
|
35
42
|
400: { description: "Validation error" },
|
|
36
43
|
},
|
|
@@ -56,7 +63,13 @@ const globalSignalRoute = route({
|
|
|
56
63
|
payload: z.record(z.string(), z.unknown()).optional(),
|
|
57
64
|
}),
|
|
58
65
|
responses: {
|
|
59
|
-
200: {
|
|
66
|
+
200: {
|
|
67
|
+
description: "Event emitted",
|
|
68
|
+
schema: z.object({
|
|
69
|
+
ok: z.literal(true),
|
|
70
|
+
name: z.string(),
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
60
73
|
400: { description: "Validation error" },
|
|
61
74
|
},
|
|
62
75
|
auth: { apiKey: true },
|
|
@@ -84,7 +97,7 @@ export async function handleWorkflowEvents(
|
|
|
84
97
|
const payload = { ...(parsed.body.payload ?? {}), _runId: parsed.params.runId };
|
|
85
98
|
workflowEventBus.emit(parsed.body.name, payload);
|
|
86
99
|
|
|
87
|
-
|
|
100
|
+
runScopedSignalRoute.respond(res, 200, {
|
|
88
101
|
ok: true,
|
|
89
102
|
name: parsed.body.name,
|
|
90
103
|
runId: parsed.params.runId,
|
|
@@ -99,7 +112,7 @@ export async function handleWorkflowEvents(
|
|
|
99
112
|
|
|
100
113
|
workflowEventBus.emit(parsed.body.name, parsed.body.payload ?? {});
|
|
101
114
|
|
|
102
|
-
|
|
115
|
+
globalSignalRoute.respond(res, 200, { ok: true, name: parsed.body.name });
|
|
103
116
|
return true;
|
|
104
117
|
}
|
|
105
118
|
|