@electric-ax/agents-server 0.4.6 → 0.4.7
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/entrypoint.js +207 -35
- package/dist/index.cjs +228 -35
- package/dist/index.d.cts +52 -4
- package/dist/index.d.ts +52 -4
- package/dist/index.js +223 -36
- package/drizzle/0009_entity_signal_statuses.sql +3 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/src/db/schema.ts +1 -1
- package/src/electric-agents-types.ts +76 -1
- package/src/entity-manager.ts +256 -33
- package/src/entity-registry.ts +40 -16
- package/src/index.ts +20 -0
- package/src/routing/dispatch-policy.ts +6 -0
- package/src/routing/entities-router.ts +57 -1
- package/src/routing/internal-router.ts +6 -3
- package/src/routing/runners-router.ts +1 -1
- package/src/runtime.ts +5 -1
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
backfillEntityDispatchPolicy,
|
|
19
19
|
linkEntityDispatchSubscription,
|
|
20
20
|
resolveEffectiveDispatchPolicyForSpawn,
|
|
21
|
+
shouldLinkDispatchBeforeInitialMessage,
|
|
21
22
|
unlinkEntityDispatchSubscription,
|
|
22
23
|
} from './dispatch-policy.js'
|
|
23
24
|
import { routeBody, withSchema } from './schema.js'
|
|
@@ -133,6 +134,22 @@ const setTagBodySchema = Type.Object({
|
|
|
133
134
|
value: Type.String(),
|
|
134
135
|
})
|
|
135
136
|
|
|
137
|
+
const entitySignalSchema = Type.Union([
|
|
138
|
+
Type.Literal(`SIGINT`),
|
|
139
|
+
Type.Literal(`SIGHUP`),
|
|
140
|
+
Type.Literal(`SIGTERM`),
|
|
141
|
+
Type.Literal(`SIGKILL`),
|
|
142
|
+
Type.Literal(`SIGSTOP`),
|
|
143
|
+
Type.Literal(`SIGCONT`),
|
|
144
|
+
Type.Literal(`SIGUSR`),
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
const signalBodySchema = Type.Object({
|
|
148
|
+
signal: entitySignalSchema,
|
|
149
|
+
reason: Type.Optional(Type.String()),
|
|
150
|
+
payload: Type.Optional(Type.Unknown()),
|
|
151
|
+
})
|
|
152
|
+
|
|
136
153
|
const scheduleBodySchema = Type.Union([
|
|
137
154
|
Type.Object({
|
|
138
155
|
scheduleType: Type.Literal(`cron`),
|
|
@@ -161,6 +178,7 @@ type SendBody = Static<typeof sendBodySchema>
|
|
|
161
178
|
type InboxMessageBody = Static<typeof inboxMessageBodySchema>
|
|
162
179
|
type ForkBody = Static<typeof forkBodySchema>
|
|
163
180
|
type SetTagBody = Static<typeof setTagBodySchema>
|
|
181
|
+
type SignalBody = Static<typeof signalBodySchema>
|
|
164
182
|
type ScheduleBody = Static<typeof scheduleBodySchema>
|
|
165
183
|
type EntitiesRegisterBody = Static<typeof entitiesRegisterBodySchema>
|
|
166
184
|
|
|
@@ -187,6 +205,12 @@ entitiesRouter.put(
|
|
|
187
205
|
entitiesRouter.get(`/:type/:instanceId`, withExistingEntity, getEntity)
|
|
188
206
|
entitiesRouter.head(`/:type/:instanceId`, withExistingEntity, headEntity)
|
|
189
207
|
entitiesRouter.delete(`/:type/:instanceId`, withExistingEntity, killEntity)
|
|
208
|
+
entitiesRouter.post(
|
|
209
|
+
`/:type/:instanceId/signal`,
|
|
210
|
+
withExistingEntity,
|
|
211
|
+
withSchema(signalBodySchema),
|
|
212
|
+
signalEntity
|
|
213
|
+
)
|
|
190
214
|
entitiesRouter.post(
|
|
191
215
|
`/:type/:instanceId/send`,
|
|
192
216
|
withExistingEntity,
|
|
@@ -614,13 +638,21 @@ async function spawnEntity(
|
|
|
614
638
|
wake: parsed.wake,
|
|
615
639
|
created_by: principal.url,
|
|
616
640
|
})
|
|
617
|
-
|
|
641
|
+
const linkBeforeInitialMessage =
|
|
642
|
+
parsed.initialMessage !== undefined &&
|
|
643
|
+
shouldLinkDispatchBeforeInitialMessage(dispatchPolicy)
|
|
644
|
+
if (linkBeforeInitialMessage) {
|
|
645
|
+
await linkEntityDispatchSubscription(ctx, entity)
|
|
646
|
+
}
|
|
618
647
|
if (parsed.initialMessage !== undefined) {
|
|
619
648
|
await ctx.entityManager.send(entity.url, {
|
|
620
649
|
from: principal.url,
|
|
621
650
|
payload: parsed.initialMessage,
|
|
622
651
|
})
|
|
623
652
|
}
|
|
653
|
+
if (!linkBeforeInitialMessage) {
|
|
654
|
+
await linkEntityDispatchSubscription(ctx, entity)
|
|
655
|
+
}
|
|
624
656
|
|
|
625
657
|
return json(
|
|
626
658
|
{ ...toPublicEntity(entity), txid: entity.txid },
|
|
@@ -655,3 +687,27 @@ async function killEntity(
|
|
|
655
687
|
ctx.runtime.claimWriteTokens.clearStream(ctx.service, entity.streams.main)
|
|
656
688
|
return json(result)
|
|
657
689
|
}
|
|
690
|
+
|
|
691
|
+
async function signalEntity(
|
|
692
|
+
request: AgentsRouteRequest,
|
|
693
|
+
ctx: TenantContext
|
|
694
|
+
): Promise<Response> {
|
|
695
|
+
const principalMutationError = rejectPrincipalEntityMutation(
|
|
696
|
+
request,
|
|
697
|
+
`signaled`
|
|
698
|
+
)
|
|
699
|
+
if (principalMutationError) return principalMutationError
|
|
700
|
+
|
|
701
|
+
const parsed = routeBody<SignalBody>(request)
|
|
702
|
+
const { entityUrl, entity } = requireExistingEntityRoute(request)
|
|
703
|
+
const result = await ctx.entityManager.signal(entityUrl, {
|
|
704
|
+
signal: parsed.signal,
|
|
705
|
+
reason: parsed.reason,
|
|
706
|
+
payload: parsed.payload,
|
|
707
|
+
})
|
|
708
|
+
if (result.new_state === `stopped` || result.new_state === `killed`) {
|
|
709
|
+
await unlinkEntityDispatchSubscription(ctx, entity)
|
|
710
|
+
ctx.runtime.claimWriteTokens.clearStream(ctx.service, entity.streams.main)
|
|
711
|
+
}
|
|
712
|
+
return json(result)
|
|
713
|
+
}
|
|
@@ -479,7 +479,7 @@ async function webhookForward(
|
|
|
479
479
|
enrichPromise,
|
|
480
480
|
])
|
|
481
481
|
|
|
482
|
-
if (entity?.status === `stopped`) {
|
|
482
|
+
if (entity?.status === `stopped` || entity?.status === `paused`) {
|
|
483
483
|
if (upsertPromise) await upsertPromise
|
|
484
484
|
return json({ done: true })
|
|
485
485
|
}
|
|
@@ -764,10 +764,13 @@ async function callbackForward(
|
|
|
764
764
|
// the token is intact even though entityDispatchState may diverge.
|
|
765
765
|
// If both are false, a newer wake owns the entity — leave status as-is.
|
|
766
766
|
if (entity && (entityCleared || stillOwnsClaim)) {
|
|
767
|
-
await ctx.entityManager.registry.updateStatus(
|
|
767
|
+
await ctx.entityManager.registry.updateStatus(
|
|
768
|
+
entity.url,
|
|
769
|
+
entity.status === `stopping` ? `stopped` : `idle`
|
|
770
|
+
)
|
|
768
771
|
await ctx.entityBridgeManager.onEntityChanged(entity.url)
|
|
769
772
|
serverLog.info(
|
|
770
|
-
`[callback-forward] status updated
|
|
773
|
+
`[callback-forward] status updated after done for ${entity.url}`
|
|
771
774
|
)
|
|
772
775
|
} else if (!entity) {
|
|
773
776
|
serverLog.warn(
|
|
@@ -575,7 +575,7 @@ async function notificationFromClaim(
|
|
|
575
575
|
404
|
|
576
576
|
)
|
|
577
577
|
}
|
|
578
|
-
if (entity.status === `stopped`) {
|
|
578
|
+
if (entity.status === `stopped` || entity.status === `paused`) {
|
|
579
579
|
await ctx.streamClient.releaseSubscription(
|
|
580
580
|
input.subscriptionId,
|
|
581
581
|
input.claim.token,
|
package/src/runtime.ts
CHANGED
|
@@ -533,7 +533,11 @@ export class ElectricAgentsTenantRuntime {
|
|
|
533
533
|
return
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
-
await this.manager.registry.
|
|
536
|
+
const entity = await this.manager.registry.getEntity(entityUrl)
|
|
537
|
+
await this.manager.registry.updateStatus(
|
|
538
|
+
entityUrl,
|
|
539
|
+
entity?.status === `stopping` ? `stopped` : `idle`
|
|
540
|
+
)
|
|
537
541
|
await this.entityBridgeManager.onEntityChanged(entityUrl)
|
|
538
542
|
}
|
|
539
543
|
}
|