@wjarka/cezarion 0.14.9-dev.888 → 0.14.9-dev.901
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 +12 -0
- package/dist/contract/index.js +50 -5
- package/dist/contract/projects.d.ts +40 -0
- package/dist/contract/runs.d.ts +43 -0
- package/dist/contract/skills.d.ts +5 -0
- package/dist/core/cursor-acp-runner.js +43 -0
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/runs/store.d.ts +12 -0
- package/dist/runs/store.js +5 -0
- package/dist/runs/store.js.map +1 -1
- package/dist/runs/webhook.d.ts +125 -0
- package/dist/runs/webhook.js +256 -0
- package/dist/runs/webhook.js.map +1 -0
- package/dist/server/server.d.ts +2634 -1403
- package/dist/server/server.js +131 -7
- package/dist/server/server.js.map +1 -1
- package/dist/task-cli/cli.js +51 -4
- package/dist/task-cli/cli.js.map +1 -1
- package/dist/task-cli/discovery.js +6 -5
- package/dist/task-cli/discovery.js.map +1 -1
- package/dist/task-cli/http.d.ts +3 -0
- package/dist/task-cli/http.js.map +1 -1
- package/dist/workflows/run.d.ts +2 -0
- package/dist/workflows/run.js +1 -0
- package/dist/workflows/run.js.map +1 -1
- package/dist/workspace/config.d.ts +9 -0
- package/dist/workspace/config.js +13 -1
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/projects.d.ts +24 -1
- package/dist/workspace/projects.js +14 -1
- package/dist/workspace/projects.js.map +1 -1
- package/package.json +3 -3
- package/scripts/mock-cursor-hang-stdin-late.mjs +35 -0
- package/scripts/mock-cursor-hang-stdin.mjs +19 -5
- package/web/dist/assets/agent-accounts-DOFEs7OK.js +1 -0
- package/web/dist/assets/centered-state-BrRPODjj.js +43 -0
- package/web/dist/assets/{collapsible-D3Lh_nlG.js → collapsible-CH3UzMt8.js} +1 -1
- package/web/dist/assets/{commit-list-CgoIXaVd.js → commit-list-BF4myGkq.js} +1 -1
- package/web/dist/assets/{compare-variants-2AnPP6ez.js → compare-variants-DNX6SniB.js} +1 -1
- package/web/dist/assets/{diff-CZcD4D1r2.js → diff-Jd8igxRb2.js} +2 -2
- package/web/dist/assets/{diff-controls-BCazakYu.js → diff-controls-1iZN3L9z.js} +1 -1
- package/web/dist/assets/{diff-stat-Cn7rzy1H.js → diff-stat-C3Id3W_C.js} +1 -1
- package/web/dist/assets/{diff-view-C46nHiP2.js → diff-view-BK1wJX0D.js} +1 -1
- package/web/dist/assets/{dropdown-menu-CyFz8H2A.js → dropdown-menu-JK2oEKtU.js} +1 -1
- package/web/dist/assets/{git-toolbar-DT4Q02mZ.js → git-toolbar-Czc2eDts.js} +1 -1
- package/web/dist/assets/github-BOYBHSuU.js +1 -0
- package/web/dist/assets/{image-preview-B618kDBH.js → image-preview-BEOsw8qX.js} +1 -1
- package/web/dist/assets/index-CaQiR7tg.js +7 -0
- package/web/dist/assets/index-Cp146nCD.css +1 -0
- package/web/dist/assets/{markdown-0VE-LMFx.js → markdown-CbMH76QP.js} +1 -1
- package/web/dist/assets/pill-i0LDDqPl.js +1 -0
- package/web/dist/assets/{project-router-EEnXzYjj.js → project-router-5lOBnHn-.js} +1 -1
- package/web/dist/assets/{prompt-templates-D8lCwJds.js → prompt-templates-CinGNbS4.js} +3 -3
- package/web/dist/assets/repo-git-CwEH2IXE.js +1 -0
- package/web/dist/assets/{run-diff-C-Ij6Y04.js → run-diff-3peOAX9l.js} +2 -2
- package/web/dist/assets/run-header-DDuQGG2F.js +1 -0
- package/web/dist/assets/{skills-CH6TkMy9.js → skills-Cd35zfc2.js} +1 -1
- package/web/dist/assets/{tab-link-CQc65wEy.js → tab-link-KwnZekGX.js} +1 -1
- package/web/dist/assets/task-changes-BRAnOBnv.js +1 -0
- package/web/dist/assets/{task-commits-BYdWmsx1.js → task-commits-CLHTP-nL.js} +1 -1
- package/web/dist/assets/{task-files-DHUEF1Fh.js → task-files-BbQ3zTG5.js} +2 -2
- package/web/dist/assets/task-thread-BzPJy_YC.js +9 -0
- package/web/dist/assets/{textarea-BOXhPgVD.js → textarea-CP5yQwcP.js} +1 -1
- package/web/dist/assets/thread-loading-DRqt03Zs.js +1 -0
- package/web/dist/assets/{token-metrics-DJeOZ5n-.js → token-metrics-BjlJ1WUf.js} +1 -1
- package/web/dist/assets/{utils-DKheTxGD.js → utils-BW1-Ibnd.js} +1 -1
- package/web/dist/assets/{workflows-CpHaJbUe.js → workflows-BPYflHS1.js} +4 -4
- package/web/dist/assets/{zoomable-image-YW3Qdrr_.js → zoomable-image-DVvXID3t.js} +1 -1
- package/web/dist/index.html +14 -14
- package/web/dist/assets/agent-accounts-tNk0wiKj.js +0 -1
- package/web/dist/assets/centered-state-DXxIJA15.js +0 -43
- package/web/dist/assets/github-vwoJvCTz.js +0 -1
- package/web/dist/assets/index-1YNk4P3I.js +0 -7
- package/web/dist/assets/index-D0WAgmTQ.css +0 -1
- package/web/dist/assets/pill-BU5y1CTd.js +0 -1
- package/web/dist/assets/repo-git-CYDGngEk.js +0 -1
- package/web/dist/assets/run-header-DYiqx0qE.js +0 -1
- package/web/dist/assets/task-changes-CEi-l2GZ.js +0 -1
- package/web/dist/assets/task-thread-DAxvkDvr.js +0 -9
- package/web/dist/assets/thread-loading-D9dDLmql.js +0 -1
package/dist/server/server.js
CHANGED
|
@@ -17,9 +17,10 @@ import { serve } from '@hono/node-server';
|
|
|
17
17
|
import { bodyLimit } from 'hono/body-limit';
|
|
18
18
|
import { streamSSE } from 'hono/streaming';
|
|
19
19
|
import { jsonZodValidator, paramZodValidator, queryZodValidator } from './validators.js';
|
|
20
|
+
import { deliverOnce, TaskWebhooks } from '../runs/webhook.js';
|
|
20
21
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
21
22
|
import { z } from 'zod';
|
|
22
|
-
import { repoPullInputSchema, pinRunInputSchema, githubSearchQuerySchema, setWorkspaceUiStateInputSchema, } from '../contract/index.js';
|
|
23
|
+
import { repoPullInputSchema, pinRunInputSchema, notifyRunInputSchema, githubSearchQuerySchema, setWorkspaceUiStateInputSchema, } from '../contract/index.js';
|
|
23
24
|
// A contract VALUE, like `workspaceUiStateSchema` in workspace/migrations.ts — the request
|
|
24
25
|
// schema this route validates with is the same one the client compiles against.
|
|
25
26
|
import { MODEL_DISCOVERY_RUNNERS, attachmentInputSchema, attachmentFileParamSchema, modelDiscoveryRunnerSchema, openProjectInSchema, runnerDiscoversModels, updateProjectInputSchema, } from '../contract/index.js';
|
|
@@ -68,7 +69,7 @@ import { defaultAgentProfile, listAgentProfiles, profileDirState, resolveProfile
|
|
|
68
69
|
import { PROFILE_CAPABLE_PROVIDERS, profileEnv, supportsProfiles } from '../core/agent-profiles.js';
|
|
69
70
|
import { quoteExecutable, withEnvPrefix } from '../core/shell-env.js';
|
|
70
71
|
import { resolveCursorExecutable } from '../core/cursor-model-catalog.js';
|
|
71
|
-
import { allocateProjectSlug, listProjects, normalizeProjectTags, probeProjectStatus, registerProject, removeProject, shouldRegisterProject, } from '../workspace/projects.js';
|
|
72
|
+
import { allocateProjectSlug, listProjects, normalizeProjectTags, probeProjectStatus, toProjectListEntry, registerProject, removeProject, shouldRegisterProject, } from '../workspace/projects.js';
|
|
72
73
|
import { mergeWriteWorkspaceUiState, readWorkspaceUiState } from '../workspace/ui-state.js';
|
|
73
74
|
import { checkoutRepo } from './checkout.js';
|
|
74
75
|
import { ProjectContextError, ProjectContexts } from './project-context.js';
|
|
@@ -312,6 +313,8 @@ const startRunSchema = z
|
|
|
312
313
|
todoId: z.string().min(1).max(200, 'must be at most 200 characters').optional(),
|
|
313
314
|
// Idempotent start (#504): the same id and payload answer 200 with the existing run.
|
|
314
315
|
clientRequestId: z.string().uuid().optional(),
|
|
316
|
+
// Task webhook opt-in (#589). Absent = off; not part of the idempotency hash.
|
|
317
|
+
notify: z.boolean().optional(),
|
|
315
318
|
})
|
|
316
319
|
.refine((b) => Boolean(b.workflow) !== Boolean(b.steps), {
|
|
317
320
|
message: 'provide either "workflow" or "steps", not both',
|
|
@@ -822,6 +825,11 @@ export function createApp(deps) {
|
|
|
822
825
|
automationStore: deps.automationStore ?? AutomationStore.open(bootDataDir),
|
|
823
826
|
launchKey: ensureLaunchKey(bootDataDir), // bookmarklet auto-start secret (spec 011)
|
|
824
827
|
};
|
|
828
|
+
// The task webhook (#589): attached before any recovery — the boot store by `index.ts`, lazy
|
|
829
|
+
// projects through `prepareManager` below. Attaching is idempotent, so the calls here only
|
|
830
|
+
// cover an app built without boot's wiring (tests, embedded servers).
|
|
831
|
+
const taskWebhooks = deps.taskWebhooks ?? new TaskWebhooks({ fetch: deps.taskWebhookFetch, origin: '' });
|
|
832
|
+
taskWebhooks.attach(bootContext);
|
|
825
833
|
// Non-boot projects build lazily on first scoped request; their managers
|
|
826
834
|
// count against the same workspace semaphore as the boot manager (step 2.5).
|
|
827
835
|
const contexts = deps.contexts ?? new ProjectContexts({
|
|
@@ -832,7 +840,10 @@ export function createApp(deps) {
|
|
|
832
840
|
return listProjects(selector);
|
|
833
841
|
},
|
|
834
842
|
semaphore: deps.semaphore,
|
|
835
|
-
prepareManager: project =>
|
|
843
|
+
prepareManager: (project) => {
|
|
844
|
+
taskWebhooks.attach(project);
|
|
845
|
+
return deps.delegation?.attachProject(project);
|
|
846
|
+
},
|
|
836
847
|
});
|
|
837
848
|
// Workspace-level SSE bus (step 2.8) — the registry mutators and the
|
|
838
849
|
// checkout flow (Phase 4) emit here; /api/workspace/events relays.
|
|
@@ -856,6 +867,17 @@ export function createApp(deps) {
|
|
|
856
867
|
}
|
|
857
868
|
contexts.onStoreCreated((store) => providerRuntimeAuth.watch(store));
|
|
858
869
|
contexts.onContextBuilt((ctx) => providerRuntimeAuth.watch(ctx.store));
|
|
870
|
+
// Injected contexts (tests) skip `prepareManager`; attach those when they are built.
|
|
871
|
+
for (const id of contexts.ids()) {
|
|
872
|
+
const ctx = contexts.peek(id);
|
|
873
|
+
if (ctx)
|
|
874
|
+
taskWebhooks.attach(ctx);
|
|
875
|
+
}
|
|
876
|
+
contexts.onContextBuilt((ctx) => taskWebhooks.attach(ctx));
|
|
877
|
+
const projectRegistryEntry = async (projectId) => {
|
|
878
|
+
const id = projectId === 'default' ? await resolveBootProject() : projectId;
|
|
879
|
+
return (await loadWorkspaceConfig()).projects.find((entry) => entry.id === id);
|
|
880
|
+
};
|
|
859
881
|
const app = new Hono();
|
|
860
882
|
// Reject oversized request bodies before they reach any handler (#429). GETs
|
|
861
883
|
// and SSE carry no body, so this only ever gates the mutating routes.
|
|
@@ -2057,7 +2079,7 @@ export function createApp(deps) {
|
|
|
2057
2079
|
const parsed = { data: c.req.valid('json') };
|
|
2058
2080
|
// `default` is the boot alias the cockpit is allowed to use everywhere else.
|
|
2059
2081
|
const id = raw === 'default' ? await resolveBootProject() : raw;
|
|
2060
|
-
const { maxParallel, tags } = parsed.data;
|
|
2082
|
+
const { maxParallel, tags, webhook } = parsed.data;
|
|
2061
2083
|
// Read-first (mirroring DELETE, server.ts:1252-1258): a well-formed but
|
|
2062
2084
|
// unknown id must 404 WITHOUT rewriting the config — otherwise it would both
|
|
2063
2085
|
// do a needless full-config tmp+rename and, on a read-only home, surface the
|
|
@@ -2099,6 +2121,16 @@ export function createApp(deps) {
|
|
|
2099
2121
|
else
|
|
2100
2122
|
entry.tags = normalized;
|
|
2101
2123
|
}
|
|
2124
|
+
if (webhook !== undefined) {
|
|
2125
|
+
// The token is write-only (#589): the form never has it back, so an omitted token
|
|
2126
|
+
// keeps the stored one and only `''` clears it.
|
|
2127
|
+
if (webhook === null)
|
|
2128
|
+
delete entry.webhook;
|
|
2129
|
+
else {
|
|
2130
|
+
const token = webhook.token === undefined ? entry.webhook?.token : webhook.token || undefined;
|
|
2131
|
+
entry.webhook = { url: webhook.url, ...(token ? { token } : {}) };
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2102
2134
|
updated = entry;
|
|
2103
2135
|
});
|
|
2104
2136
|
}
|
|
@@ -2114,7 +2146,41 @@ export function createApp(deps) {
|
|
|
2114
2146
|
// `PUT /api/workspace/config` fires for a workspace-cap change.
|
|
2115
2147
|
await deps.semaphore?.refresh();
|
|
2116
2148
|
const body = {
|
|
2117
|
-
project:
|
|
2149
|
+
project: toProjectListEntry(updated, await probeProjectStatus(updated.root)),
|
|
2150
|
+
};
|
|
2151
|
+
return c.json(body);
|
|
2152
|
+
})
|
|
2153
|
+
// "Send test" (#589): one `task.test` delivery to the STORED webhook, now, with the timeout
|
|
2154
|
+
// the real deliveries use and no retries — the settings page wants the answer, not a queue.
|
|
2155
|
+
// Refused in single-project mode like every other registry edit route.
|
|
2156
|
+
.post('/projects/:projectId/webhook/test', async (c) => {
|
|
2157
|
+
if (capabilities().singleProject) {
|
|
2158
|
+
return c.json(singleProjectRefusal('editing projects'), 409);
|
|
2159
|
+
}
|
|
2160
|
+
const raw = c.req.param('projectId');
|
|
2161
|
+
if (!projectIdSchema.safeParse(raw).success) {
|
|
2162
|
+
return c.json({ error: `unknown project: ${raw}` }, 404);
|
|
2163
|
+
}
|
|
2164
|
+
const entry = await projectRegistryEntry(raw).catch(() => undefined);
|
|
2165
|
+
if (!entry)
|
|
2166
|
+
return c.json({ error: `unknown project: ${raw}` }, 404);
|
|
2167
|
+
if (!entry.webhook)
|
|
2168
|
+
return c.json({ error: NO_WEBHOOK_ERROR }, 400);
|
|
2169
|
+
const payload = {
|
|
2170
|
+
event: 'task.test',
|
|
2171
|
+
deliveryId: randomUUID(),
|
|
2172
|
+
projectId: entry.id,
|
|
2173
|
+
occurredAt: new Date().toISOString(),
|
|
2174
|
+
};
|
|
2175
|
+
if (process.env.CEZ_DRY_RUN === '1') {
|
|
2176
|
+
const body = { ok: true, dryRun: true };
|
|
2177
|
+
return c.json(body);
|
|
2178
|
+
}
|
|
2179
|
+
const result = await deliverOnce(entry.webhook, payload, { fetch: deps.taskWebhookFetch });
|
|
2180
|
+
const body = {
|
|
2181
|
+
ok: result.ok,
|
|
2182
|
+
...(result.status === undefined ? {} : { status: result.status }),
|
|
2183
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
2118
2184
|
};
|
|
2119
2185
|
return c.json(body);
|
|
2120
2186
|
})
|
|
@@ -2250,7 +2316,7 @@ export function createApp(deps) {
|
|
|
2250
2316
|
let project;
|
|
2251
2317
|
try {
|
|
2252
2318
|
const entry = await registerProject(requested, source);
|
|
2253
|
-
project =
|
|
2319
|
+
project = toProjectListEntry(entry, await probeProjectStatus(entry.root));
|
|
2254
2320
|
}
|
|
2255
2321
|
catch (err) {
|
|
2256
2322
|
// e.g. a read-only home — nothing was persisted (atomic tmp+rename).
|
|
@@ -3149,6 +3215,34 @@ export function createApp(deps) {
|
|
|
3149
3215
|
const { store } = c.get('project');
|
|
3150
3216
|
const run = store.setPinned(c.req.valid('param').id, c.req.valid('json').pinned !== false);
|
|
3151
3217
|
return run ? c.json(run) : c.json({ error: 'not found' }, 404);
|
|
3218
|
+
})
|
|
3219
|
+
// Task webhook opt-in for one run (#589), valid in every state — handing a finished task to a
|
|
3220
|
+
// bot for follow-up is a real case. Turning it on sends one `task.subscribed` delivery with
|
|
3221
|
+
// the note; the note never reaches the agent session. Either direction leaves a durable
|
|
3222
|
+
// `handoff` event so the thread shows when and why. Idempotent: repeating `true` with a
|
|
3223
|
+
// new note is how "Send a note…" works.
|
|
3224
|
+
.post('/runs/:id/notify', paramZodValidator(runIdParamSchema), jsonZodValidator(notifyRunInputSchema), async (c) => {
|
|
3225
|
+
const project = c.get('project');
|
|
3226
|
+
const { id } = c.req.valid('param');
|
|
3227
|
+
const { notify, message } = c.req.valid('json');
|
|
3228
|
+
if (!project.store.getRun(id))
|
|
3229
|
+
return c.json({ error: 'not found' }, 404);
|
|
3230
|
+
if (notify) {
|
|
3231
|
+
const entry = await projectRegistryEntry(project.id).catch(() => undefined);
|
|
3232
|
+
if (!entry?.webhook)
|
|
3233
|
+
return c.json({ error: NO_WEBHOOK_ERROR }, 400);
|
|
3234
|
+
}
|
|
3235
|
+
const run = project.store.updateRun(id, { notify: notify ? true : undefined });
|
|
3236
|
+
if (!run)
|
|
3237
|
+
return c.json({ error: 'not found' }, 404);
|
|
3238
|
+
project.store.appendEvent(id, {
|
|
3239
|
+
type: 'handoff',
|
|
3240
|
+
notify,
|
|
3241
|
+
...(message === undefined ? {} : { message }),
|
|
3242
|
+
});
|
|
3243
|
+
if (notify)
|
|
3244
|
+
taskWebhooks.attach(project).subscribed(id, message);
|
|
3245
|
+
return c.json(run);
|
|
3152
3246
|
})
|
|
3153
3247
|
// The per-task off switch for that resume (the workspace setting is Settings → Resources).
|
|
3154
3248
|
// Idempotent: a run with nothing pending answers 200 too, because "this task will not
|
|
@@ -3225,9 +3319,15 @@ export function createApp(deps) {
|
|
|
3225
3319
|
if ('error' in account)
|
|
3226
3320
|
return c.json({ error: account.error }, 400);
|
|
3227
3321
|
}
|
|
3322
|
+
// Task webhook (#589): an explicit `true` on a project with no webhook is refused, so a
|
|
3323
|
+
// bot learns in one round trip. `cez task start`'s implicit default never sends it there.
|
|
3324
|
+
if (parsed.data.notify === true && !(await projectRegistryEntry(c.get('project').id).catch(() => undefined))?.webhook) {
|
|
3325
|
+
return c.json({ error: NO_WEBHOOK_ERROR }, 400);
|
|
3326
|
+
}
|
|
3228
3327
|
const images = parsed.data.images?.map(toPastedContent);
|
|
3229
3328
|
const input = {
|
|
3230
3329
|
task: parsed.data.task,
|
|
3330
|
+
notify: parsed.data.notify,
|
|
3231
3331
|
model: parsed.data.model,
|
|
3232
3332
|
effort: parsed.data.effort,
|
|
3233
3333
|
runner: parsed.data.runner,
|
|
@@ -5247,6 +5347,13 @@ export function createApp(deps) {
|
|
|
5247
5347
|
routed.get('*', (c) => serveShell(c) ?? c.notFound());
|
|
5248
5348
|
return routed;
|
|
5249
5349
|
}
|
|
5350
|
+
/** The host a thread link names (#589): the bound one, or loopback for a wildcard bind. */
|
|
5351
|
+
function taskWebhookHost(bindHost) {
|
|
5352
|
+
if (!bindHost || bindHost === '0.0.0.0' || bindHost === '::')
|
|
5353
|
+
return '127.0.0.1';
|
|
5354
|
+
return bindHost.includes(':') ? `[${bindHost}]` : bindHost;
|
|
5355
|
+
}
|
|
5356
|
+
const NO_WEBHOOK_ERROR = 'this project has no task webhook — set one in Settings → General → Task webhook, or start without notify';
|
|
5250
5357
|
export function startServer(deps, port) {
|
|
5251
5358
|
const workspaceEvents = deps.workspaceEvents ?? new WorkspaceEventBus();
|
|
5252
5359
|
const skillsUpdate = deps.skillsUpdate ?? new SkillsUpdateService({ invalidateCatalog: refreshTeamSkills });
|
|
@@ -5256,11 +5363,15 @@ export function startServer(deps, port) {
|
|
|
5256
5363
|
const automationCoordinator = new AutomationCoordinator({ listProjects });
|
|
5257
5364
|
const bootProjectId = deps.bootProjectId ?? 'default';
|
|
5258
5365
|
const bootAutomationStore = automationCoordinator.store(bootProjectId, deps.repoRoot);
|
|
5366
|
+
const taskWebhooks = deps.taskWebhooks ?? new TaskWebhooks({ fetch: deps.taskWebhookFetch });
|
|
5259
5367
|
const sharedContexts = deps.contexts ?? new ProjectContexts({
|
|
5260
5368
|
listProjects,
|
|
5261
5369
|
semaphore: deps.semaphore,
|
|
5262
5370
|
automationStore: (projectId, root) => automationCoordinator.store(projectId, root),
|
|
5263
|
-
prepareManager: project =>
|
|
5371
|
+
prepareManager: (project) => {
|
|
5372
|
+
taskWebhooks.attach(project);
|
|
5373
|
+
return deps.delegation?.attachProject(project);
|
|
5374
|
+
},
|
|
5264
5375
|
});
|
|
5265
5376
|
// #801: GitHub automations are opt-in. Off, the flag must remove the BEHAVIOR and not merely
|
|
5266
5377
|
// the UI — no scheduler, no GitHub polling, no launched runs — so every entry point into the
|
|
@@ -5270,6 +5381,7 @@ export function startServer(deps, port) {
|
|
|
5270
5381
|
let rescheduleAutomations = () => { };
|
|
5271
5382
|
const app = createApp({
|
|
5272
5383
|
...deps,
|
|
5384
|
+
taskWebhooks,
|
|
5273
5385
|
contexts: sharedContexts,
|
|
5274
5386
|
automationStore: bootAutomationStore,
|
|
5275
5387
|
workspaceEvents,
|
|
@@ -5408,6 +5520,18 @@ export function startServer(deps, port) {
|
|
|
5408
5520
|
server.closeAllConnections();
|
|
5409
5521
|
});
|
|
5410
5522
|
};
|
|
5523
|
+
// The task webhook's thread links name the port the listener BOUND (#594 review): `--port 0`
|
|
5524
|
+
// asks for an ephemeral one, so the requested number is not an address. Deliveries queue
|
|
5525
|
+
// behind this, so none goes out before it is set.
|
|
5526
|
+
const setTaskWebhookOrigin = () => {
|
|
5527
|
+
const address = server.address();
|
|
5528
|
+
const bound = address && typeof address === 'object' ? address.port : port;
|
|
5529
|
+
taskWebhooks.setOrigin(`http://${taskWebhookHost(deps.bindHost)}:${bound}`);
|
|
5530
|
+
};
|
|
5531
|
+
if (server.listening)
|
|
5532
|
+
setTaskWebhookOrigin();
|
|
5533
|
+
else
|
|
5534
|
+
server.once('listening', setTaskWebhookOrigin);
|
|
5411
5535
|
return Object.assign(server, { shutdownForRestart });
|
|
5412
5536
|
}
|
|
5413
5537
|
/**
|