@treeseed/core 0.8.2 → 0.8.4

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.
Files changed (133) hide show
  1. package/README.md +7 -11
  2. package/dist/dev-watch.js +1 -1
  3. package/dist/dev.d.ts +2 -4
  4. package/dist/dev.js +4 -124
  5. package/dist/env.yaml +23 -175
  6. package/dist/index.d.ts +0 -4
  7. package/dist/index.js +0 -6
  8. package/dist/scripts/build-dist.js +3 -3
  9. package/dist/scripts/dev-platform.js +1 -7
  10. package/dist/scripts/run-fixture-astro-command.js +25 -51
  11. package/dist/scripts/test-smoke.js +50 -7
  12. package/package.json +5 -78
  13. package/templates/github/deploy-web.workflow.yml +106 -0
  14. package/templates/github/hosted-project.workflow.yml +4 -4
  15. package/dist/agent-runtime.d.ts +0 -17
  16. package/dist/agent-runtime.js +0 -117
  17. package/dist/agent.d.ts +0 -11
  18. package/dist/agent.js +0 -25
  19. package/dist/agents/adapters/execution.d.ts +0 -41
  20. package/dist/agents/adapters/execution.js +0 -73
  21. package/dist/agents/adapters/mutations.d.ts +0 -22
  22. package/dist/agents/adapters/mutations.js +0 -30
  23. package/dist/agents/adapters/notification.d.ts +0 -26
  24. package/dist/agents/adapters/notification.js +0 -46
  25. package/dist/agents/adapters/repository.d.ts +0 -23
  26. package/dist/agents/adapters/repository.js +0 -61
  27. package/dist/agents/adapters/research.d.ts +0 -26
  28. package/dist/agents/adapters/research.js +0 -59
  29. package/dist/agents/adapters/verification.d.ts +0 -36
  30. package/dist/agents/adapters/verification.js +0 -62
  31. package/dist/agents/cli-tools.d.ts +0 -1
  32. package/dist/agents/cli-tools.js +0 -5
  33. package/dist/agents/cli.d.ts +0 -15
  34. package/dist/agents/cli.js +0 -109
  35. package/dist/agents/contracts/messages.d.ts +0 -88
  36. package/dist/agents/contracts/messages.js +0 -138
  37. package/dist/agents/contracts/run.d.ts +0 -21
  38. package/dist/agents/contracts/run.js +0 -0
  39. package/dist/agents/index.d.ts +0 -1
  40. package/dist/agents/index.js +0 -5
  41. package/dist/agents/kernel/agent-kernel.d.ts +0 -51
  42. package/dist/agents/kernel/agent-kernel.js +0 -292
  43. package/dist/agents/kernel/trigger-resolver.d.ts +0 -19
  44. package/dist/agents/kernel/trigger-resolver.js +0 -157
  45. package/dist/agents/registry-helper.d.ts +0 -4
  46. package/dist/agents/registry-helper.js +0 -14
  47. package/dist/agents/registry.d.ts +0 -6
  48. package/dist/agents/registry.js +0 -98
  49. package/dist/agents/runtime-types.d.ts +0 -118
  50. package/dist/agents/runtime-types.js +0 -0
  51. package/dist/agents/spec-loader.d.ts +0 -18
  52. package/dist/agents/spec-loader.js +0 -55
  53. package/dist/agents/spec-normalizer.d.ts +0 -2
  54. package/dist/agents/spec-normalizer.js +0 -327
  55. package/dist/agents/spec-types.d.ts +0 -64
  56. package/dist/agents/spec-types.js +0 -0
  57. package/dist/agents/testing/agents-smoke.d.ts +0 -1
  58. package/dist/agents/testing/agents-smoke.js +0 -32
  59. package/dist/agents/testing/e2e-harness.d.ts +0 -44
  60. package/dist/agents/testing/e2e-harness.js +0 -504
  61. package/dist/api/agent-routes.d.ts +0 -13
  62. package/dist/api/agent-routes.js +0 -327
  63. package/dist/api/app.d.ts +0 -5
  64. package/dist/api/app.js +0 -361
  65. package/dist/api/auth/d1-database.d.ts +0 -3
  66. package/dist/api/auth/d1-database.js +0 -20
  67. package/dist/api/auth/d1-provider.d.ts +0 -79
  68. package/dist/api/auth/d1-provider.js +0 -92
  69. package/dist/api/auth/d1-store.d.ts +0 -114
  70. package/dist/api/auth/d1-store.js +0 -895
  71. package/dist/api/auth/memory-provider.d.ts +0 -77
  72. package/dist/api/auth/memory-provider.js +0 -249
  73. package/dist/api/auth/rbac.d.ts +0 -22
  74. package/dist/api/auth/rbac.js +0 -162
  75. package/dist/api/auth/tokens.d.ts +0 -18
  76. package/dist/api/auth/tokens.js +0 -56
  77. package/dist/api/capabilities.d.ts +0 -9
  78. package/dist/api/capabilities.js +0 -33
  79. package/dist/api/config.d.ts +0 -2
  80. package/dist/api/config.js +0 -77
  81. package/dist/api/http.d.ts +0 -28
  82. package/dist/api/http.js +0 -51
  83. package/dist/api/index.d.ts +0 -9
  84. package/dist/api/index.js +0 -18
  85. package/dist/api/operations-routes.d.ts +0 -11
  86. package/dist/api/operations-routes.js +0 -87
  87. package/dist/api/operations.d.ts +0 -3
  88. package/dist/api/operations.js +0 -26
  89. package/dist/api/project-routes.d.ts +0 -8
  90. package/dist/api/project-routes.js +0 -586
  91. package/dist/api/providers.d.ts +0 -2
  92. package/dist/api/providers.js +0 -62
  93. package/dist/api/railway.d.ts +0 -50
  94. package/dist/api/railway.js +0 -69
  95. package/dist/api/sdk-dispatch.d.ts +0 -5
  96. package/dist/api/sdk-dispatch.js +0 -13
  97. package/dist/api/sdk-routes.d.ts +0 -11
  98. package/dist/api/sdk-routes.js +0 -29
  99. package/dist/api/server.d.ts +0 -2
  100. package/dist/api/server.js +0 -10
  101. package/dist/api/templates.d.ts +0 -3
  102. package/dist/api/templates.js +0 -31
  103. package/dist/api/types.d.ts +0 -231
  104. package/dist/api/types.js +0 -0
  105. package/dist/api.d.ts +0 -1
  106. package/dist/api.js +0 -1
  107. package/dist/railway.d.ts +0 -1
  108. package/dist/railway.js +0 -4
  109. package/dist/services/agents.d.ts +0 -11
  110. package/dist/services/agents.js +0 -48
  111. package/dist/services/common.d.ts +0 -66
  112. package/dist/services/common.js +0 -212
  113. package/dist/services/index.d.ts +0 -6
  114. package/dist/services/index.js +0 -19
  115. package/dist/services/manager.d.ts +0 -267
  116. package/dist/services/manager.js +0 -1368
  117. package/dist/services/remote-runner.d.ts +0 -30
  118. package/dist/services/remote-runner.js +0 -230
  119. package/dist/services/workday-content.d.ts +0 -53
  120. package/dist/services/workday-content.js +0 -190
  121. package/dist/services/workday-manager.d.ts +0 -279
  122. package/dist/services/workday-manager.js +0 -163
  123. package/dist/services/workday-report.d.ts +0 -195
  124. package/dist/services/workday-report.js +0 -17
  125. package/dist/services/workday-start.d.ts +0 -195
  126. package/dist/services/workday-start.js +0 -17
  127. package/dist/services/worker-capacity.d.ts +0 -58
  128. package/dist/services/worker-capacity.js +0 -208
  129. package/dist/services/worker-pool-scaler.d.ts +0 -27
  130. package/dist/services/worker-pool-scaler.js +0 -127
  131. package/dist/services/worker.d.ts +0 -19
  132. package/dist/services/worker.js +0 -436
  133. package/templates/github/deploy.workflow.yml +0 -577
@@ -1,436 +0,0 @@
1
- #!/usr/bin/env node
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { AgentKernel } from "../agents/kernel/agent-kernel.js";
6
- import { createControlPlaneReporter } from "@treeseed/sdk";
7
- import { buildTaskContext, createQueueClient, createServiceSdk, resolveServiceRepoRoot, resolveWorkerConfig } from "./common.js";
8
- function parseTaskPayload(task) {
9
- const raw = typeof task?.payloadJson === "string" ? task.payloadJson : "{}";
10
- try {
11
- return JSON.parse(raw);
12
- } catch {
13
- return {};
14
- }
15
- }
16
- function asRecord(value) {
17
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
18
- }
19
- function readCapacityEnvelope(payload) {
20
- const envelope = asRecord(payload.capacityEnvelope);
21
- return Object.keys(envelope).length > 0 ? envelope : null;
22
- }
23
- function runnerRepositoryPath(volumeRoot, repositoryId, taskId) {
24
- const repositoryRoot = join(volumeRoot, "repositories", repositoryId);
25
- return {
26
- repositoryRoot,
27
- bareGit: join(repositoryRoot, "bare.git"),
28
- worktree: join(repositoryRoot, "worktrees", taskId)
29
- };
30
- }
31
- function runnerComposedWorkspacePath(volumeRoot, hubId) {
32
- const workspaceRoot = join(volumeRoot, "workspaces", hubId);
33
- return {
34
- root: workspaceRoot,
35
- parent: join(workspaceRoot, "workspace-root"),
36
- site: join(workspaceRoot, "site"),
37
- content: join(workspaceRoot, "content"),
38
- manifest: join(workspaceRoot, ".treeseed", "workspace.json")
39
- };
40
- }
41
- async function ensureRunnerComposedWorkspace(volumeRoot, task) {
42
- const payload = parseTaskPayload(task);
43
- const workspace = asRecord(payload.workspace);
44
- const hubId = String(workspace.hubId ?? payload.projectId ?? task.projectId ?? "").trim();
45
- if (!hubId) return null;
46
- const paths = runnerComposedWorkspacePath(volumeRoot, hubId);
47
- await mkdir(paths.parent, { recursive: true });
48
- await mkdir(paths.site, { recursive: true });
49
- await mkdir(paths.content, { recursive: true });
50
- await mkdir(join(paths.root, ".treeseed"), { recursive: true });
51
- await writeFile(paths.manifest, `${JSON.stringify({
52
- schemaVersion: 1,
53
- kind: "treeseed_composed_workspace",
54
- hubId,
55
- softwareRepository: workspace.softwareRepository ?? null,
56
- contentRepository: workspace.contentRepository ?? null,
57
- parentRepository: workspace.parentRepository ?? null,
58
- paths: {
59
- workspaceRoot: paths.parent,
60
- site: paths.site,
61
- content: paths.content
62
- },
63
- allowedWriteTargets: Array.isArray(workspace.allowedWriteTargets) ? workspace.allowedWriteTargets : ["content"],
64
- credentialSessionScopes: workspace.credentialSessionScopes ?? {
65
- software: ["repository:software"],
66
- content: ["repository:content"],
67
- parentWorkspace: []
68
- },
69
- credentialScopes: workspace.credentialScopes ?? {
70
- software: ["repository:software"],
71
- content: ["repository:content"],
72
- parentWorkspace: []
73
- },
74
- contentOverlay: workspace.contentOverlay ?? "src_content_when_present"
75
- }, null, 2)}
76
- `, "utf8");
77
- return paths;
78
- }
79
- class WorkerPausedForApproval extends Error {
80
- constructor(request) {
81
- super(String(request.summary ?? request.title ?? "Task paused for approval."));
82
- this.request = request;
83
- }
84
- request;
85
- }
86
- async function executeQueuedTask(options) {
87
- const context = await buildTaskContext(options.sdk, options.taskId);
88
- const task = context.task;
89
- const payload = parseTaskPayload(task);
90
- await ensureRunnerComposedWorkspace(options.volumeRoot, {
91
- ...task ?? {},
92
- payloadJson: JSON.stringify(payload)
93
- });
94
- const capacityEnvelope = readCapacityEnvelope(payload);
95
- const explicitApproval = asRecord(payload.approvalRequest);
96
- if (Object.keys(explicitApproval).length > 0 || capacityEnvelope?.maxCredits === 0) {
97
- throw new WorkerPausedForApproval({
98
- kind: String(explicitApproval.kind ?? "capacity_boundary"),
99
- title: String(explicitApproval.title ?? "Task paused for approval"),
100
- summary: String(explicitApproval.summary ?? "The task reached a boundary outside its approved execution envelope."),
101
- severity: explicitApproval.severity ?? "medium",
102
- workDayId: task?.workDayId ?? task?.work_day_id ?? null,
103
- taskId: options.taskId,
104
- options: Array.isArray(explicitApproval.options) ? explicitApproval.options : [],
105
- recommendation: asRecord(explicitApproval.recommendation),
106
- policySnapshot: {
107
- capacityEnvelope,
108
- ...asRecord(explicitApproval.policySnapshot)
109
- }
110
- });
111
- }
112
- const executionKind = typeof payload.executionKind === "string" ? payload.executionKind : null;
113
- if (String(task?.type ?? task?.taskType ?? "") === "refresh_project_graph") {
114
- const config = resolveWorkerConfig();
115
- const projectId = typeof payload.projectId === "string" ? payload.projectId : config.projectId;
116
- const repositoryId = typeof payload.repositoryId === "string" ? payload.repositoryId : projectId;
117
- const paths = runnerRepositoryPath(config.volumeRoot, repositoryId, options.taskId);
118
- await mkdir(paths.bareGit, { recursive: true });
119
- await mkdir(paths.worktree, { recursive: true });
120
- const graphRefresh = await options.sdk.refreshGraph();
121
- const graphVersion = graphRefresh.snapshotRoot;
122
- await options.sdk.create({
123
- model: "graph_run",
124
- data: {
125
- id: `${options.taskId}:graph`,
126
- workDayId: String(task?.workDayId ?? task?.work_day_id ?? ""),
127
- corpusHash: graphVersion,
128
- graphVersion,
129
- statsJson: JSON.stringify(graphRefresh),
130
- snapshotRef: graphVersion
131
- },
132
- actor: "worker"
133
- });
134
- if (typeof options.sdk.updateWorkDayGraph === "function") {
135
- await options.sdk.updateWorkDayGraph({
136
- id: String(task?.workDayId ?? task?.work_day_id ?? ""),
137
- graphVersion,
138
- summaryPatch: {
139
- graphRefresh: {
140
- state: "completed",
141
- graphVersion,
142
- snapshotRef: graphVersion,
143
- runnerId: config.workerId
144
- }
145
- }
146
- });
147
- }
148
- if (typeof options.sdk.recordRepositoryClaim === "function") {
149
- await options.sdk.recordRepositoryClaim({
150
- projectId,
151
- repositoryId,
152
- runnerId: config.workerId,
153
- runnerServiceName: config.runnerServiceName,
154
- volumeIdentity: config.volumeIdentity,
155
- lastSeenCommit: typeof payload.commitSha === "string" ? payload.commitSha : null,
156
- metadata: {
157
- bareGit: paths.bareGit,
158
- worktree: paths.worktree
159
- }
160
- });
161
- }
162
- return {
163
- workerId: options.workerId,
164
- queueAttempt: options.queueAttempt,
165
- graphVersion,
166
- snapshotRef: graphVersion,
167
- repositoryId,
168
- paths,
169
- summary: {
170
- status: "completed",
171
- workerId: options.workerId,
172
- summary: `Refreshed project graph ${graphVersion}`
173
- }
174
- };
175
- }
176
- if (executionKind === "workflow_dispatch" || executionKind === "sdk_dispatch") {
177
- const namespace = typeof payload.namespace === "string" ? payload.namespace : "workflow";
178
- const operation = typeof payload.operation === "string" ? payload.operation : "";
179
- if (!operation) {
180
- throw new Error(`Task ${options.taskId} does not define a dispatch operation.`);
181
- }
182
- const input = payload.input && typeof payload.input === "object" ? payload.input : {};
183
- const result = await options.sdk.dispatch({
184
- namespace,
185
- operation,
186
- input,
187
- preferredMode: "prefer_local"
188
- });
189
- return {
190
- workerId: options.workerId,
191
- queueAttempt: options.queueAttempt,
192
- executionKind,
193
- namespace,
194
- operation,
195
- result,
196
- summary: {
197
- status: "completed",
198
- workerId: options.workerId,
199
- summary: `Executed ${namespace}:${operation}`
200
- }
201
- };
202
- }
203
- const agentSlug = typeof payload.agentSlug === "string" && payload.agentSlug ? payload.agentSlug : typeof context.agent?.slug === "string" && context.agent.slug ? context.agent.slug : typeof task?.agentId === "string" && task.agentId ? task.agentId : typeof task?.agent_id === "string" && task.agent_id ? task.agent_id : "";
204
- if (!agentSlug) {
205
- throw new Error(`Task ${options.taskId} does not resolve to an agent slug.`);
206
- }
207
- const invocation = payload.invocation && typeof payload.invocation === "object" ? payload.invocation : null;
208
- const agentResult = await options.kernel.runAgent(agentSlug, invocation ? "manual" : "auto", invocation);
209
- return {
210
- workerId: options.workerId,
211
- queueAttempt: options.queueAttempt,
212
- agentSlug,
213
- result: agentResult,
214
- summary: {
215
- status: agentResult.status,
216
- workerId: options.workerId,
217
- summary: agentResult.summary
218
- },
219
- capacityUsage: capacityEnvelope?.providerId && capacityEnvelope?.laneId ? {
220
- capacityProviderId: capacityEnvelope.providerId,
221
- laneId: capacityEnvelope.laneId,
222
- reservationId: capacityEnvelope.reservationIds?.[0] ?? null,
223
- credits: Number(payload.estimatedCredits ?? capacityEnvelope.maxCredits ?? 1),
224
- source: "worker"
225
- } : null
226
- };
227
- }
228
- async function runWorkerCycle() {
229
- const sdk = createServiceSdk();
230
- const queue = createQueueClient();
231
- const config = resolveWorkerConfig();
232
- const kernel = new AgentKernel(sdk, resolveServiceRepoRoot());
233
- if (typeof sdk.recordWorkerRunner === "function") {
234
- await sdk.recordWorkerRunner({
235
- projectId: config.projectId,
236
- environment: config.environment,
237
- runnerId: config.workerId,
238
- runnerServiceName: config.runnerServiceName,
239
- volumeIdentity: config.volumeIdentity,
240
- state: "active",
241
- maxLocalWorkers: config.maxLocalWorkers,
242
- activeLocalWorkers: 0,
243
- metadata: {
244
- volumeRoot: config.volumeRoot
245
- }
246
- }).catch(() => null);
247
- }
248
- if (!queue) {
249
- if (process.env.TREESEED_LOCAL_DEV_MODE?.trim()) {
250
- return { ok: true, processed: 0, idle: true, reason: "queue_unconfigured" };
251
- }
252
- throw new Error("Worker requires CLOUDFLARE_ACCOUNT_ID, TREESEED_QUEUE_ID, and TREESEED_QUEUE_PULL_TOKEN.");
253
- }
254
- const pulled = await queue.pull({
255
- batchSize: config.batchSize,
256
- visibilityTimeoutMs: config.visibilityTimeoutMs
257
- });
258
- if (pulled.messages.length === 0) {
259
- return { ok: true, processed: 0 };
260
- }
261
- const maxLocalWorkers = Number.isFinite(Number(config.maxLocalWorkers)) ? Math.max(1, Number(config.maxLocalWorkers)) : 1;
262
- const selectedMessages = pulled.messages.slice(0, maxLocalWorkers);
263
- const results = await Promise.all(selectedMessages.map(async (message) => {
264
- try {
265
- await sdk.claimTask({
266
- id: message.body.taskId,
267
- workerId: config.workerId,
268
- leaseSeconds: config.leaseSeconds,
269
- actor: "worker"
270
- });
271
- await sdk.recordTaskProgress({
272
- id: message.body.taskId,
273
- workerId: config.workerId,
274
- state: "running",
275
- appendEvent: {
276
- kind: "worker_started",
277
- data: { workerId: config.workerId, queueAttempt: message.attempts }
278
- },
279
- actor: "worker"
280
- });
281
- let output;
282
- try {
283
- output = await executeQueuedTask({
284
- sdk,
285
- kernel,
286
- taskId: message.body.taskId,
287
- workerId: config.workerId,
288
- queueAttempt: message.attempts,
289
- volumeRoot: config.volumeRoot
290
- });
291
- } catch (error) {
292
- if (error instanceof WorkerPausedForApproval) {
293
- const reporter = createControlPlaneReporter();
294
- const context = await buildTaskContext(sdk, message.body.taskId);
295
- const task = context.task;
296
- const projectId = String(process.env.TREESEED_PROJECT_ID ?? "");
297
- await reporter.createApprovalRequest({
298
- projectId,
299
- teamId: String(error.request.teamId ?? process.env.TREESEED_TEAM_ID ?? ""),
300
- workDayId: typeof error.request.workDayId === "string" ? error.request.workDayId : String(task?.workDayId ?? task?.work_day_id ?? ""),
301
- taskId: message.body.taskId,
302
- kind: String(error.request.kind ?? "capacity_boundary"),
303
- severity: error.request.severity === "high" || error.request.severity === "low" ? error.request.severity : "medium",
304
- requestedByType: "worker",
305
- requestedById: config.workerId,
306
- title: String(error.request.title ?? "Task paused for approval"),
307
- summary: String(error.request.summary ?? error.message),
308
- options: Array.isArray(error.request.options) ? error.request.options : [],
309
- recommendation: asRecord(error.request.recommendation),
310
- policySnapshot: asRecord(error.request.policySnapshot)
311
- }).catch(() => null);
312
- await sdk.recordTaskProgress({
313
- id: message.body.taskId,
314
- workerId: config.workerId,
315
- state: "paused_for_approval",
316
- appendEvent: {
317
- kind: "paused_for_approval",
318
- data: error.request
319
- },
320
- actor: "worker"
321
- });
322
- await queue.ack([message.leaseId]);
323
- return 1;
324
- }
325
- throw error;
326
- }
327
- await sdk.completeTask({
328
- id: message.body.taskId,
329
- output,
330
- summary: output.summary,
331
- actor: "worker"
332
- });
333
- if (output.capacityUsage?.capacityProviderId && output.capacityUsage?.laneId) {
334
- const reporter = createControlPlaneReporter();
335
- await reporter.reportCapacityUsage({
336
- capacityProviderId: output.capacityUsage.capacityProviderId,
337
- laneId: output.capacityUsage.laneId,
338
- reservationId: output.capacityUsage.reservationId,
339
- teamId: String(process.env.TREESEED_TEAM_ID ?? ""),
340
- projectId: String(process.env.TREESEED_PROJECT_ID ?? ""),
341
- workDayId: message.body.workDayId,
342
- taskId: message.body.taskId,
343
- phase: "consume",
344
- credits: output.capacityUsage.credits,
345
- source: "worker",
346
- metadata: {
347
- workerId: config.workerId,
348
- queueAttempt: message.attempts
349
- }
350
- }).catch(() => null);
351
- }
352
- await queue.ack([message.leaseId]);
353
- return 1;
354
- } catch (error) {
355
- const retryDelaySeconds = Math.min(300, Math.max(15, message.attempts * 30));
356
- await sdk.failTask({
357
- id: message.body.taskId,
358
- errorMessage: error instanceof Error ? error.message : String(error),
359
- retryable: true,
360
- nextVisibleAt: new Date(Date.now() + retryDelaySeconds * 1e3).toISOString(),
361
- actor: "worker"
362
- }).catch(() => null);
363
- await queue.retry([{ leaseId: message.leaseId, delaySeconds: retryDelaySeconds }]);
364
- return 0;
365
- }
366
- }));
367
- return { ok: true, processed: results.reduce((sum, value) => sum + value, 0) };
368
- }
369
- function shouldExitWorkerLoopAfterIdle(options) {
370
- const idleExitMs = Number(options.idleExitMs ?? 0);
371
- if (!Number.isFinite(idleExitMs) || idleExitMs <= 0) {
372
- return false;
373
- }
374
- if (options.processed > 0 || options.idleSince === null) {
375
- return false;
376
- }
377
- return options.now - options.idleSince >= idleExitMs;
378
- }
379
- async function recordWorkerLoopExitState(config) {
380
- const sdk = createServiceSdk();
381
- if (typeof sdk.recordWorkerRunner !== "function") {
382
- return;
383
- }
384
- await sdk.recordWorkerRunner({
385
- projectId: config.projectId,
386
- environment: config.environment,
387
- runnerId: config.workerId,
388
- runnerServiceName: config.runnerServiceName,
389
- volumeIdentity: config.volumeIdentity,
390
- state: "sleeping",
391
- maxLocalWorkers: config.maxLocalWorkers,
392
- activeLocalWorkers: 0,
393
- metadata: {
394
- volumeRoot: config.volumeRoot,
395
- reason: "idle_exit"
396
- }
397
- }).catch(() => null);
398
- }
399
- async function startWorkerLoop() {
400
- const config = resolveWorkerConfig();
401
- let idleSince = null;
402
- for (; ; ) {
403
- try {
404
- const result = await runWorkerCycle();
405
- const processed = Number(result.processed ?? 0);
406
- if (processed > 0) {
407
- idleSince = null;
408
- } else {
409
- idleSince ??= Date.now();
410
- if (shouldExitWorkerLoopAfterIdle({
411
- idleExitMs: config.idleExitMs,
412
- idleSince,
413
- now: Date.now(),
414
- processed
415
- })) {
416
- await recordWorkerLoopExitState(config);
417
- return;
418
- }
419
- }
420
- } catch (error) {
421
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}
422
- `);
423
- }
424
- await new Promise((resolve) => setTimeout(resolve, config.pollIntervalMs));
425
- }
426
- }
427
- const currentFile = fileURLToPath(import.meta.url);
428
- const entryFile = process.argv[1] ?? "";
429
- if (entryFile === currentFile) {
430
- await startWorkerLoop();
431
- }
432
- export {
433
- runWorkerCycle,
434
- shouldExitWorkerLoopAfterIdle,
435
- startWorkerLoop
436
- };