@atolis-hq/wake 0.4.37 → 0.4.39
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/src/bootstrap/surface-api-applications.js +13 -0
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/integrations/contracts/provider.d.ts +14 -2
- package/dist/src/integrations/contracts/provider.js +4 -0
- package/dist/src/integrations/github/infrastructure/webhook.js +11 -2
- package/dist/src/integrations/github/provider.d.ts +1 -1
- package/dist/src/integrations/github/provider.js +10 -1
- package/dist/src/orchestration/application/orchestration-service.js +1 -1
- package/dist/src/orchestration/application/watch-reactor.js +11 -0
- package/dist/src/orchestration/contracts/event-decoder.d.ts +1 -0
- package/dist/src/orchestration/contracts/event-decoder.js +1 -0
- package/dist/src/orchestration/contracts/events.d.ts +2 -0
- package/dist/src/orchestration/contracts/views.d.ts +1 -0
- package/dist/src/orchestration/domain/workflow-instance-events.js +1 -0
- package/dist/src/surfaces/api/contracts/system.d.ts +18 -1
- package/dist/src/surfaces/api/contracts/system.js +4 -1
- package/dist/src/surfaces/api/routes/applications.d.ts +2 -1
- package/dist/src/surfaces/api/routes/read.js +4 -0
- package/dist/src/surfaces/api/routes/system.d.ts +1 -1
- package/dist/src/surfaces/api/routes/system.js +1 -0
- package/dist/src/surfaces/web-assets/assets/index-DkmZZW60.js +1179 -0
- package/dist/src/surfaces/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/dist/src/surfaces/web-assets/assets/index-aL1g5gVQ.js +0 -1179
|
@@ -279,6 +279,19 @@ function createSystemApplications(root, now) {
|
|
|
279
279
|
meta: sampledMeta(sampledAt),
|
|
280
280
|
};
|
|
281
281
|
},
|
|
282
|
+
async webhooks() {
|
|
283
|
+
const sampledAt = now();
|
|
284
|
+
const adapters = await Promise.all(root.providers.flatMap((instance) => instance.webhook === undefined
|
|
285
|
+
? []
|
|
286
|
+
: [
|
|
287
|
+
instance.webhook.setupInstructions().then((hooks) => ({
|
|
288
|
+
adapter: instance.adapter,
|
|
289
|
+
provider: instance.provider,
|
|
290
|
+
hooks,
|
|
291
|
+
})),
|
|
292
|
+
]));
|
|
293
|
+
return { data: { adapters }, meta: sampledMeta(sampledAt) };
|
|
294
|
+
},
|
|
282
295
|
};
|
|
283
296
|
}
|
|
284
297
|
function commandAccepted(command, acceptedAt) {
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.39";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -2,7 +2,7 @@ import type { CheckpointStore, EventJournal } from '@atolis-hq/eventing';
|
|
|
2
2
|
import type { PullRequestService } from '../../activities/index.js';
|
|
3
3
|
import type { ConversationService } from '../../conversations/index.js';
|
|
4
4
|
import type { RunRepository } from '../../execution/index.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type Clock, type IdGenerator, type ValueOf } from '../../kernel/index.js';
|
|
6
6
|
import type { OrchestrationService, SurfaceCapability, WorkflowCandidate, WorkflowName } from '../../orchestration/index.js';
|
|
7
7
|
import type { ExternalResourceKey, ResourceCapability, ResourceKind, ResourceLookup, ResourceService } from '../../resources/index.js';
|
|
8
8
|
import type { WorkItemId, WorkItemView, WorkService } from '../../work/index.js';
|
|
@@ -44,10 +44,14 @@ export interface VerifiedArtifact {
|
|
|
44
44
|
readonly capabilities: readonly ResourceCapability[];
|
|
45
45
|
readonly revision?: string | undefined;
|
|
46
46
|
}
|
|
47
|
+
export declare const AdapterHealthStatus: {
|
|
48
|
+
readonly Unknown: "unknown";
|
|
49
|
+
};
|
|
50
|
+
export type AdapterHealthStatus = 'ok' | 'degraded' | ValueOf<typeof AdapterHealthStatus>;
|
|
47
51
|
export interface AdapterHealthCheck {
|
|
48
52
|
readonly scope: string;
|
|
49
53
|
readonly channel: string;
|
|
50
|
-
readonly status:
|
|
54
|
+
readonly status: AdapterHealthStatus;
|
|
51
55
|
readonly detail?: string;
|
|
52
56
|
readonly successCount: number;
|
|
53
57
|
readonly failureCount: number;
|
|
@@ -79,8 +83,16 @@ export interface ProviderInstance {
|
|
|
79
83
|
}
|
|
80
84
|
export interface ProviderWebhook {
|
|
81
85
|
provision(): Promise<void>;
|
|
86
|
+
setupInstructions(): Promise<readonly ProviderWebhookSetup[]>;
|
|
82
87
|
receive(body: Buffer, headers: Readonly<Record<string, string | string[] | undefined>>, trigger: () => void): Promise<404 | 400 | 401 | 202>;
|
|
83
88
|
}
|
|
89
|
+
/** Operator-only material for configuring a provider webhook outside Wake. */
|
|
90
|
+
export interface ProviderWebhookSetup {
|
|
91
|
+
readonly scope: string;
|
|
92
|
+
readonly endpoint: string;
|
|
93
|
+
readonly events: readonly string[];
|
|
94
|
+
readonly secret: string;
|
|
95
|
+
}
|
|
84
96
|
export type ComposedProviderInstance = Omit<ProviderInstance, 'provider'>;
|
|
85
97
|
export interface ProviderDefinition<Config = unknown> {
|
|
86
98
|
readonly provider: string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { defineClosedVocabulary, } from '../../kernel/index.js';
|
|
1
2
|
import { adapterId } from './identifiers.js';
|
|
3
|
+
export const AdapterHealthStatus = defineClosedVocabulary({
|
|
4
|
+
Unknown: 'unknown',
|
|
5
|
+
});
|
|
2
6
|
export class ProviderRegistry {
|
|
3
7
|
definitions = new Map();
|
|
4
8
|
register(definition) {
|
|
@@ -55,10 +55,21 @@ export function createGitHubWebhook(adapter, config, publicUrl, root, client, he
|
|
|
55
55
|
return 401;
|
|
56
56
|
if (!events.has(event))
|
|
57
57
|
return 404;
|
|
58
|
+
health.recordSuccess(`${owner}/${repo}`, 'webhook');
|
|
58
59
|
trigger();
|
|
59
60
|
return 202;
|
|
60
61
|
});
|
|
61
62
|
},
|
|
63
|
+
async setupInstructions() {
|
|
64
|
+
if (!config.webhooks.enabled || endpoint === undefined)
|
|
65
|
+
return [];
|
|
66
|
+
return Promise.all(config.repositories.map(({ owner, repo }) => state.serialise(owner, repo, async () => ({
|
|
67
|
+
scope: `${owner}/${repo}`,
|
|
68
|
+
endpoint,
|
|
69
|
+
events: [...events],
|
|
70
|
+
secret: (await state.load(owner, repo)).secret,
|
|
71
|
+
}))));
|
|
72
|
+
},
|
|
62
73
|
};
|
|
63
74
|
}
|
|
64
75
|
async function provisionRepository(repository, endpoint, state, client, health) {
|
|
@@ -67,7 +78,6 @@ async function provisionRepository(repository, endpoint, state, client, health)
|
|
|
67
78
|
const saved = await state.load(repository.owner, repository.repo);
|
|
68
79
|
if (saved.hookId !== undefined &&
|
|
69
80
|
(await updateManagedHook(repository, endpoint, saved, client))) {
|
|
70
|
-
health.recordSuccess(scope, 'webhook');
|
|
71
81
|
return;
|
|
72
82
|
}
|
|
73
83
|
const hookId = await client.createHook({
|
|
@@ -77,7 +87,6 @@ async function provisionRepository(repository, endpoint, state, client, health)
|
|
|
77
87
|
events: [...events],
|
|
78
88
|
});
|
|
79
89
|
await state.save(repository.owner, repository.repo, { ...saved, hookId });
|
|
80
|
-
health.recordSuccess(scope, 'webhook');
|
|
81
90
|
}
|
|
82
91
|
catch (error) {
|
|
83
92
|
health.recordFailure(scope, 'webhook', error);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ProviderDefinition } from '../contracts/provider.js';
|
|
2
2
|
import { type GitHubConfig } from './contracts/config.js';
|
|
3
3
|
export declare const gitHubProviderDefinition: ProviderDefinition<GitHubConfig>;
|
|
@@ -2,6 +2,7 @@ import { BuiltInActivityName, PullRequestState } from '../../activities/index.js
|
|
|
2
2
|
import { ConversationSurfaceCapability } from '../../orchestration/index.js';
|
|
3
3
|
import { BuiltInResourceCapability, BuiltInResourceKind, ResourceCorrelationRole, resourceId, resourceKind, } from '../../resources/index.js';
|
|
4
4
|
import { ArtifactVerificationResult } from '../contracts/artifact-vocabulary.js';
|
|
5
|
+
import { AdapterHealthStatus } from '../contracts/provider.js';
|
|
5
6
|
import { InboundTranslator } from './application/inbound-translator.js';
|
|
6
7
|
import { translateGitHubOutbound } from './application/outbound-translator.js';
|
|
7
8
|
import { createGitHubWakeLabelReconciler } from './application/wake-labels.js';
|
|
@@ -59,7 +60,15 @@ export const gitHubProviderDefinition = {
|
|
|
59
60
|
setLabels: client.setIssueLabels,
|
|
60
61
|
requests,
|
|
61
62
|
}),
|
|
62
|
-
health: () => health.snapshotAll()
|
|
63
|
+
health: () => health.snapshotAll().flatMap((check) => {
|
|
64
|
+
if (check.channel !== 'webhook')
|
|
65
|
+
return [check];
|
|
66
|
+
if (!config.webhooks.enabled)
|
|
67
|
+
return [];
|
|
68
|
+
return [
|
|
69
|
+
check.successCount === 0 ? { ...check, status: AdapterHealthStatus.Unknown } : check,
|
|
70
|
+
];
|
|
71
|
+
}),
|
|
63
72
|
webhook: createGitHubWebhook(adapter, config, services.publicUiUrl, services.providerStateRoot, client, health),
|
|
64
73
|
commands: () => [
|
|
65
74
|
...Object.values(GitHubBuiltInCommand).map((syntax) => ({ syntax })),
|
|
@@ -154,7 +154,7 @@ export class OrchestrationService {
|
|
|
154
154
|
throw new GroupBudgetExtensionIneligibleError('Workflow has no durable gate definition to extend');
|
|
155
155
|
const group = childOrchestrationGroupStream(parent.orchestrationGroupId, exhausted.watchId);
|
|
156
156
|
await this.claims.grantBudget(group, exhausted.requestId, context, authority);
|
|
157
|
-
await this.waitForSignal(parent.workflowInstanceId, parent.waitingFor, context);
|
|
157
|
+
await this.waitForSignal(parent.workflowInstanceId, { ...parent.waitingFor, suppressWatchDispatch: true }, context);
|
|
158
158
|
return this.requestChild({
|
|
159
159
|
...exhausted,
|
|
160
160
|
workflowName,
|
|
@@ -11,6 +11,8 @@ const reconciliationCheckpoint = 'reconciler:orchestration.watch';
|
|
|
11
11
|
const noWatchEventTypes = () => [];
|
|
12
12
|
export function createWatchReactor(orchestration, watchEventTypes = noWatchEventTypes, runs) {
|
|
13
13
|
const react = async (event, context) => {
|
|
14
|
+
if (suppressesWatchDispatch(event))
|
|
15
|
+
return;
|
|
14
16
|
await dispatch(orchestration, runs, event, context, await orchestration.listWatchMatches(event, context));
|
|
15
17
|
};
|
|
16
18
|
return {
|
|
@@ -45,6 +47,10 @@ export function createWatchReconciler(orchestration, journal, checkpoints, runs)
|
|
|
45
47
|
]));
|
|
46
48
|
let reconciled = 0;
|
|
47
49
|
for (const event of events) {
|
|
50
|
+
if (suppressesWatchDispatch(event)) {
|
|
51
|
+
await checkpoints.save(reconciliationCheckpoint, event.globalPosition);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
48
54
|
const context = reactorCommandContext(event, ApprovalAuthorityKind.Watch, 'watch-reactor');
|
|
49
55
|
const matches = (await Promise.all((await orchestration.listWatchMatches(event, context)).map(async (match) => {
|
|
50
56
|
const watches = waiting.get(match.parent.workflowInstanceId);
|
|
@@ -63,6 +69,11 @@ export function createWatchReconciler(orchestration, journal, checkpoints, runs)
|
|
|
63
69
|
},
|
|
64
70
|
};
|
|
65
71
|
}
|
|
72
|
+
function suppressesWatchDispatch(event) {
|
|
73
|
+
const owned = selectOrchestrationEvent(event);
|
|
74
|
+
return (owned?.event.eventType === OrchestrationEventType.SignalWaitStarted &&
|
|
75
|
+
owned.event.payload.suppressWatchDispatch === true);
|
|
76
|
+
}
|
|
66
77
|
async function dispatch(orchestration, runs, event, context, matches) {
|
|
67
78
|
const causalCycle = orchestrationCausalCycleId(selectOrchestrationEvent(event));
|
|
68
79
|
const sourceWorkflowInstanceId = await resolveTriggerWorkflowInstanceId(event, runs);
|
|
@@ -71,6 +71,7 @@ export declare const activityRequestedSchema: z.ZodObject<{
|
|
|
71
71
|
}, z.core.$strict>;
|
|
72
72
|
export declare const expectationSchema: z.ZodObject<{
|
|
73
73
|
signalKind: z.ZodPipe<z.ZodString, z.ZodTransform<import("./identifiers.js").SignalName, string>>;
|
|
74
|
+
suppressWatchDispatch: z.ZodOptional<z.ZodLiteral<true>>;
|
|
74
75
|
resourceId: z.ZodOptional<z.ZodString>;
|
|
75
76
|
revision: z.ZodOptional<z.ZodString>;
|
|
76
77
|
from: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -121,6 +121,7 @@ const resourceTransitionSchema = z
|
|
|
121
121
|
export const expectationSchema = z
|
|
122
122
|
.object({
|
|
123
123
|
signalKind: brandedStringSchema(signalName),
|
|
124
|
+
suppressWatchDispatch: z.literal(true).optional(),
|
|
124
125
|
resourceId: z.string().optional(),
|
|
125
126
|
revision: z.string().optional(),
|
|
126
127
|
from: z.array(approvalAuthoritySchema).min(1).optional(),
|
|
@@ -187,6 +187,8 @@ export interface ActivityRequestedPayload {
|
|
|
187
187
|
}
|
|
188
188
|
export interface SignalExpectation {
|
|
189
189
|
readonly signalKind: SignalName;
|
|
190
|
+
/** Re-arm a blocked gate without treating its wait event as a new watch trigger. */
|
|
191
|
+
readonly suppressWatchDispatch?: true | undefined;
|
|
190
192
|
readonly resourceId?: string | undefined;
|
|
191
193
|
readonly revision?: string | undefined;
|
|
192
194
|
readonly from?: readonly ApprovalAuthority[] | undefined;
|
|
@@ -17,6 +17,7 @@ export interface ActivityActivationView {
|
|
|
17
17
|
}
|
|
18
18
|
export interface SignalExpectationView {
|
|
19
19
|
readonly signalKind: SignalName;
|
|
20
|
+
readonly suppressWatchDispatch?: true;
|
|
20
21
|
readonly intentEventId?: string;
|
|
21
22
|
readonly resourceId?: string;
|
|
22
23
|
readonly revision?: string;
|
|
@@ -177,6 +177,7 @@ function applySignalWaitStarted(state, event) {
|
|
|
177
177
|
state.status = orchestrationStatusTransitions[OrchestrationEventType.SignalWaitStarted];
|
|
178
178
|
state.waitingFor = {
|
|
179
179
|
signalKind: event.payload.signalKind,
|
|
180
|
+
...(event.payload.suppressWatchDispatch === true ? { suppressWatchDispatch: true } : {}),
|
|
180
181
|
...(event.payload.resourceId === undefined ? {} : { resourceId: event.payload.resourceId }),
|
|
181
182
|
...(event.payload.revision === undefined ? {} : { revision: event.payload.revision }),
|
|
182
183
|
...(event.payload.from === undefined ? {} : { from: event.payload.from }),
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { type ValueOf } from '../../../kernel/index.js';
|
|
2
|
+
export declare const ApiAdapterHealthStatus: {
|
|
3
|
+
readonly Unknown: "unknown";
|
|
4
|
+
};
|
|
5
|
+
export type ApiAdapterHealthStatus = 'ok' | 'degraded' | ValueOf<typeof ApiAdapterHealthStatus>;
|
|
1
6
|
export interface HealthResponse {
|
|
2
7
|
readonly status: 'ok' | 'degraded';
|
|
3
8
|
readonly version: string;
|
|
@@ -12,7 +17,7 @@ export interface HealthResponse {
|
|
|
12
17
|
readonly provider: string;
|
|
13
18
|
readonly scope: string;
|
|
14
19
|
readonly channel: string;
|
|
15
|
-
readonly status:
|
|
20
|
+
readonly status: ApiAdapterHealthStatus;
|
|
16
21
|
readonly detail?: string;
|
|
17
22
|
readonly successCount: number;
|
|
18
23
|
readonly failureCount: number;
|
|
@@ -30,3 +35,15 @@ export interface CommandsResponse {
|
|
|
30
35
|
}[];
|
|
31
36
|
}[];
|
|
32
37
|
}
|
|
38
|
+
export interface WebhookSetupResponse {
|
|
39
|
+
readonly adapters: readonly {
|
|
40
|
+
readonly adapter: string;
|
|
41
|
+
readonly provider: string;
|
|
42
|
+
readonly hooks: readonly {
|
|
43
|
+
readonly scope: string;
|
|
44
|
+
readonly endpoint: string;
|
|
45
|
+
readonly events: readonly string[];
|
|
46
|
+
readonly secret: string;
|
|
47
|
+
}[];
|
|
48
|
+
}[];
|
|
49
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunStatus } from '../../../execution/index.js';
|
|
2
|
-
import type { ApiCommandResult, AuditEventResponse, BoardCardResponse, BoardResponse, CommandsResponse, ConfigurationResponse, ControlPlaneStatusResponse, ConversationMessageRequest, HealthResponse, MetricsResponse, ResourceItemResponse, ResourceResponse, ResponseMeta, RunnerResponse, RunResponse, RunTranscriptResponse, StatusResponse, WorkDetailResponse, WorkflowDiagramsResponse, WorkflowInstanceResponse, WorkItemResponse, WorkItemTranscriptResponse } from '../contracts/index.js';
|
|
2
|
+
import type { ApiCommandResult, AuditEventResponse, BoardCardResponse, BoardResponse, CommandsResponse, ConfigurationResponse, ControlPlaneStatusResponse, ConversationMessageRequest, HealthResponse, MetricsResponse, ResourceItemResponse, ResourceResponse, ResponseMeta, RunnerResponse, RunResponse, RunTranscriptResponse, StatusResponse, WebhookSetupResponse, WorkDetailResponse, WorkflowDiagramsResponse, WorkflowInstanceResponse, WorkItemResponse, WorkItemTranscriptResponse } from '../contracts/index.js';
|
|
3
3
|
export interface CollectionQuery {
|
|
4
4
|
readonly cursor?: {
|
|
5
5
|
readonly position: number;
|
|
@@ -34,6 +34,7 @@ export interface ApiSystemApplications {
|
|
|
34
34
|
health(): Promise<ApiResourceResult<HealthResponse>>;
|
|
35
35
|
configuration(): Promise<ApiResourceResult<ConfigurationResponse>>;
|
|
36
36
|
commands(): Promise<ApiResourceResult<CommandsResponse>>;
|
|
37
|
+
webhooks?(): Promise<ApiResourceResult<WebhookSetupResponse>>;
|
|
37
38
|
}
|
|
38
39
|
export interface ApiApplications {
|
|
39
40
|
readonly now: () => string;
|
|
@@ -38,6 +38,10 @@ async function readSingleton(applications, url) {
|
|
|
38
38
|
return noQuery(url, async () => ok(await applications.system.configuration()));
|
|
39
39
|
case '/api/v1/system/commands':
|
|
40
40
|
return noQuery(url, async () => ok(await applications.system.commands()));
|
|
41
|
+
case '/api/v1/system/webhooks':
|
|
42
|
+
return applications.system.webhooks === undefined
|
|
43
|
+
? unavailable('webhooks')
|
|
44
|
+
: noQuery(url, async () => ok(await applications.system.webhooks()));
|
|
41
45
|
case '/api/v1/workflow-diagrams':
|
|
42
46
|
return readWorkflowDiagrams(applications, url);
|
|
43
47
|
default:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const systemRoutes: readonly ["/api/v1/system/health", "/api/v1/system/configuration", "/api/v1/system/commands"];
|
|
1
|
+
export declare const systemRoutes: readonly ["/api/v1/system/health", "/api/v1/system/configuration", "/api/v1/system/commands", "/api/v1/system/webhooks"];
|