create-windy 0.6.14 → 0.6.16
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/cli.js +1008 -61
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +58 -8
- package/template/.windy-template.json +2 -2
- package/template/apps/server/src/agent/client-session/attempt-authority.test.ts +162 -0
- package/template/apps/server/src/agent/client-session/attempt-authority.ts +191 -0
- package/template/apps/server/src/agent/client-session/bootstrap.ts +27 -11
- package/template/apps/server/src/agent/client-session/completion.ts +75 -24
- package/template/apps/server/src/agent/client-session/contracts.ts +23 -0
- package/template/apps/server/src/agent/client-session/creation.ts +103 -0
- package/template/apps/server/src/agent/client-session/digest.ts +11 -1
- package/template/apps/server/src/agent/client-session/drizzle-records.ts +150 -0
- package/template/apps/server/src/agent/client-session/drizzle-repository.integration.test.ts +30 -3
- package/template/apps/server/src/agent/client-session/drizzle-repository.ts +23 -132
- package/template/apps/server/src/agent/client-session/finalization.ts +99 -0
- package/template/apps/server/src/agent/client-session/host-auth.ts +22 -10
- package/template/apps/server/src/agent/client-session/host-revocation-composition.test.ts +77 -0
- package/template/apps/server/src/agent/client-session/host-revocation-composition.ts +28 -0
- package/template/apps/server/src/agent/client-session/launch-authority.contract.test.ts +21 -0
- package/template/apps/server/src/agent/client-session/manifest-admission.ts +11 -0
- package/template/apps/server/src/agent/client-session/service.test-support.ts +22 -0
- package/template/apps/server/src/agent/client-session/service.ts +104 -134
- package/template/apps/server/src/agent/client-session/terminal-digest.test.ts +23 -0
- package/template/apps/server/src/agent/client-workflow-artifact/active-blob-references.ts +8 -0
- package/template/apps/server/src/agent/client-workflow-artifact/artifact-builder.ts +60 -0
- package/template/apps/server/src/agent/client-workflow-artifact/authority-current.ts +59 -0
- package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.test.ts +189 -0
- package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.ts +153 -0
- package/template/apps/server/src/agent/client-workflow-artifact/bootstrap.ts +77 -0
- package/template/apps/server/src/agent/client-workflow-artifact/cancellation.ts +47 -0
- package/template/apps/server/src/agent/client-workflow-artifact/completed-recovery.ts +31 -0
- package/template/apps/server/src/agent/client-workflow-artifact/contracts.ts +104 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-locks.ts +30 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-mapper.ts +138 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-repository.ts +346 -0
- package/template/apps/server/src/agent/client-workflow-artifact/error.ts +21 -0
- package/template/apps/server/src/agent/client-workflow-artifact/handler.ts +167 -0
- package/template/apps/server/src/agent/client-workflow-artifact/host-revocation.ts +29 -0
- package/template/apps/server/src/agent/client-workflow-artifact/in-memory-repository.ts +232 -0
- package/template/apps/server/src/agent/client-workflow-artifact/job-port.ts +101 -0
- package/template/apps/server/src/agent/client-workflow-artifact/presentation.ts +41 -0
- package/template/apps/server/src/agent/client-workflow-artifact/recovery.contract.test.ts +253 -0
- package/template/apps/server/src/agent/client-workflow-artifact/release-binding.ts +85 -0
- package/template/apps/server/src/agent/client-workflow-artifact/repository.test.ts +204 -0
- package/template/apps/server/src/agent/client-workflow-artifact/repository.ts +97 -0
- package/template/apps/server/src/agent/client-workflow-artifact/routes-handler.contract.test.ts +201 -0
- package/template/apps/server/src/agent/client-workflow-artifact/routes.ts +161 -0
- package/template/apps/server/src/agent/client-workflow-artifact/runtime-composition.ts +101 -0
- package/template/apps/server/src/agent/client-workflow-artifact/scope-authority.ts +39 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service-authorization.ts +117 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service-support.ts +28 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service.contract.test.ts +345 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service.ts +350 -0
- package/template/apps/server/src/agent/client-workflow-artifact/session-boundary.ts +39 -0
- package/template/apps/server/src/agent/client-workflow-artifact/source-access.contract.test.ts +98 -0
- package/template/apps/server/src/agent/client-workflow-artifact/source-access.ts +80 -0
- package/template/apps/server/src/agent/client-workflow-artifact/takeover.ts +122 -0
- package/template/apps/server/src/agent/client-workflow-artifact/task-authority.contract.test.ts +97 -0
- package/template/apps/server/src/agent/client-workflow-artifact/task-authority.ts +147 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-authority.contract.test.ts +100 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-replay.ts +31 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-seal.ts +211 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-artifact.test-support.ts +173 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.contract.test.ts +254 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.ts +285 -0
- package/template/apps/server/src/agent/run-facade.test-support.ts +17 -4
- package/template/apps/server/src/agent/run-facade.test.ts +16 -0
- package/template/apps/server/src/agent/run-session-authorization.ts +12 -10
- package/template/apps/server/src/agent/runtime-bootstrap-input.ts +5 -1
- package/template/apps/server/src/agent/runtime-bootstrap.ts +33 -33
- package/template/apps/server/src/agent/runtime-public-ports.ts +40 -0
- package/template/apps/server/src/agent/session-invalidation/contracts.ts +25 -26
- package/template/apps/server/src/agent/session-projection-access.ts +31 -19
- package/template/apps/server/src/agent/unavailable-routes.ts +21 -0
- package/template/apps/server/src/agent/workflow-runtime-bootstrap.ts +2 -0
- package/template/apps/server/src/agent-workflow/artifact-source-authorization.ts +12 -6
- package/template/apps/server/src/agent-workflow/command-facade.ts +70 -11
- package/template/apps/server/src/agent-workflow/drizzle-artifact-port.ts +9 -9
- package/template/apps/server/src/agent-workflow/drizzle-delegation-completion.ts +1 -1
- package/template/apps/server/src/agent-workflow/platform-node-adapters.ts +14 -6
- package/template/apps/server/src/agent-workflow/project-launch-authority.contract.test.ts +53 -0
- package/template/apps/server/src/agent-workflow/project-launch-authority.ts +92 -0
- package/template/apps/server/src/agent-workflow/runtime-composition.ts +30 -14
- package/template/apps/server/src/app/agent-runtime-composition.ts +6 -1
- package/template/apps/server/src/app/client-agent-workflow-artifact-binding.ts +14 -0
- package/template/apps/server/src/application-services.ts +6 -0
- package/template/apps/server/src/data-access/scope-containment.test.ts +31 -0
- package/template/apps/server/src/data-access/scope-containment.ts +48 -0
- package/template/apps/server/src/jobs/artifact-publication.test.ts +192 -0
- package/template/apps/server/src/jobs/artifact-publication.ts +56 -0
- package/template/apps/server/src/jobs/artifact-publisher.ts +101 -0
- package/template/apps/server/src/jobs/drizzle-artifact.ts +73 -1
- package/template/apps/server/src/jobs/drizzle-execution-repository.ts +14 -1
- package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +65 -0
- package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -0
- package/template/apps/server/src/jobs/execution-repository.ts +28 -0
- package/template/apps/server/src/jobs/execution.test.ts +3 -1
- package/template/apps/server/src/jobs/in-memory-execution.ts +16 -0
- package/template/apps/server/src/jobs/in-memory-lease.ts +45 -1
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +2 -0
- package/template/apps/server/src/jobs/postgres-cleanup-contract.test.ts +58 -0
- package/template/apps/server/src/jobs/worker.ts +8 -14
- package/template/apps/server/src/module-host/background-ai-unavailable.ts +18 -0
- package/template/apps/server/src/module-host/durable-context.ts +19 -22
- package/template/apps/server/src/module-host/implementation-completeness.test.ts +80 -1
- package/template/apps/server/src/module-host/implementation-completeness.ts +28 -1
- package/template/apps/server/src/module-host/request-context.ts +32 -8
- package/template/apps/server/src/module-host/storage-port.ts +3 -0
- package/template/apps/server/src/module-host/workflow-input-artifact-port.test.ts +104 -0
- package/template/apps/server/src/module-host/workflow-input-artifact-port.ts +229 -0
- package/template/apps/server/src/module-host/workflow-input-materialization.test.ts +163 -0
- package/template/apps/server/src/module-host/workflow-input-materialization.ts +161 -0
- package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +1 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +2 -1
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +16 -0
- package/template/docs/architecture/agent-workflow.md +13 -2
- package/template/docs/architecture/durable-jobs.md +7 -0
- package/template/docs/operations/agent-session-memory.md +2 -1
- package/template/docs/operations/agent-session-troubleshooting.md +87 -2
- package/template/docs/operations/agent-workflow-recovery.md +25 -14
- package/template/docs/platform/client-agent-session.md +148 -2
- package/template/packages/ai-client/package.json +2 -1
- package/template/packages/ai-client/src/client-agent/index.ts +1 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/contracts.ts +85 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/http.test.ts +49 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/http.ts +82 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/index.ts +3 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.test.ts +154 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.ts +332 -0
- package/template/packages/database/drizzle/0062_client_agent_workflow_artifact.sql +63 -0
- package/template/packages/database/drizzle/meta/0062_snapshot.json +14839 -0
- package/template/packages/database/drizzle/meta/_journal.json +7 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/agent-runtime-schema.test.ts +177 -0
- package/template/packages/database/src/schema/client-agent-workflow-artifact.ts +158 -0
- package/template/packages/database/src/schema/index.ts +1 -0
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/jobs/src/handlers.test.ts +10 -0
- package/template/packages/jobs/src/handlers.ts +28 -11
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/ai-client-execution-policy.ts +6 -0
- package/template/packages/modules/src/manifest.ts +2 -0
- package/template/packages/modules/src/system-agent-audit-actions.ts +3 -0
- package/template/packages/modules/src/system-ai-api-bindings.ts +15 -0
- package/template/packages/modules/src/system-audit-actions.ts +15 -0
- package/template/packages/server-sdk/index.ts +52 -0
- package/template/packages/server-sdk/package.json +5 -1
- package/template/packages/server-sdk/src/client-agent-session.ts +13 -1
- package/template/packages/server-sdk/src/client-agent-workflow-artifact.contract.test.ts +113 -0
- package/template/packages/server-sdk/src/client-agent-workflow-artifact.ts +183 -0
- package/template/packages/server-sdk/src/durable-handler.ts +7 -0
- package/template/packages/server-sdk/src/request-context.ts +7 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-port.ts +104 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-validation.contract.test.ts +124 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-validation.ts +347 -0
- package/template/packages/server-sdk/src/workflow-input-materialization-handler.ts +43 -0
- package/template/packages/storage/package.json +1 -1
- package/template/packages/storage/src/blob-store-put-once.test.ts +70 -0
- package/template/packages/storage/src/blob-store.ts +9 -0
- package/template/packages/storage/src/local-file-blob-put-once.ts +120 -0
- package/template/packages/storage/src/local-file-blob-store.ts +53 -1
- package/template/packages/storage/src/memory-blob-store.ts +73 -33
|
@@ -14,6 +14,7 @@ import { artifactRef } from "@southwind-ai/storage";
|
|
|
14
14
|
import { DrizzleDurableExecutionRepository } from "./drizzle-execution-repository.js";
|
|
15
15
|
import { DrizzleDurableJobRepository } from "./drizzle-repository.js";
|
|
16
16
|
import { DurableJobService } from "./service.js";
|
|
17
|
+
import { DurableArtifactPublicationConflictError } from "./execution-repository.js";
|
|
17
18
|
|
|
18
19
|
const databaseUrl = process.env.JOBS_TEST_DATABASE_URL;
|
|
19
20
|
const describePostgres = databaseUrl ? describe : describe.skip;
|
|
@@ -38,6 +39,7 @@ describePostgres("PostgreSQL durable execution", () => {
|
|
|
38
39
|
await pool.query(`truncate table
|
|
39
40
|
governed_export_requests,
|
|
40
41
|
bulk_data_artifacts, bulk_data_jobs, scheduled_task_runs,
|
|
42
|
+
client_agent_workflow_artifacts,
|
|
41
43
|
durable_job_recoveries, durable_job_failures, durable_job_artifacts,
|
|
42
44
|
durable_job_progress, durable_job_leases, durable_job_attempts,
|
|
43
45
|
durable_job_stages, durable_jobs`);
|
|
@@ -170,6 +172,69 @@ describePostgres("PostgreSQL durable execution", () => {
|
|
|
170
172
|
expect((await service.get(cancelled.job.id))?.artifacts).toEqual([]);
|
|
171
173
|
});
|
|
172
174
|
|
|
175
|
+
test("artifact once 重放首发事实且拒绝覆盖", async () => {
|
|
176
|
+
const service = jobService(pool);
|
|
177
|
+
const submitted = await service.submit(jobInput("pg-artifact-once"));
|
|
178
|
+
const execution = new DrizzleDurableExecutionRepository(drizzle(pool));
|
|
179
|
+
const claim = await execution.claim(claimInput("artifact-once", base));
|
|
180
|
+
if (!claim) throw new Error("artifact once worker 未领取任务");
|
|
181
|
+
const publication = {
|
|
182
|
+
id: "dap_pg_stable",
|
|
183
|
+
jobId: claim.jobId,
|
|
184
|
+
stageId: claim.stageId,
|
|
185
|
+
kind: "result",
|
|
186
|
+
artifactRef: artifactRef("blob_0123456789abcdef0123456789abcdef"),
|
|
187
|
+
filename: "result.json",
|
|
188
|
+
contentType: "application/json",
|
|
189
|
+
byteSize: 10,
|
|
190
|
+
sha256: "a".repeat(64),
|
|
191
|
+
ownerId: "user_pg",
|
|
192
|
+
metadata: { second: 2, first: 1 },
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
await expect(
|
|
196
|
+
execution.publishArtifactOnce(
|
|
197
|
+
claim,
|
|
198
|
+
{ ...publication, jobId: "job_forged" },
|
|
199
|
+
base,
|
|
200
|
+
),
|
|
201
|
+
).rejects.toBeInstanceOf(DurableArtifactPublicationConflictError);
|
|
202
|
+
await expect(
|
|
203
|
+
execution.publishArtifactOnce(
|
|
204
|
+
claim,
|
|
205
|
+
{ ...publication, stageId: "stage_forged" },
|
|
206
|
+
base,
|
|
207
|
+
),
|
|
208
|
+
).rejects.toBeInstanceOf(DurableArtifactPublicationConflictError);
|
|
209
|
+
expect((await service.get(submitted.job.id))?.artifacts).toEqual([]);
|
|
210
|
+
|
|
211
|
+
const first = await execution.publishArtifactOnce(claim, publication, base);
|
|
212
|
+
expect(
|
|
213
|
+
await execution.publishArtifactOnce(
|
|
214
|
+
claim,
|
|
215
|
+
{ ...publication, metadata: { first: 1, second: 2 } },
|
|
216
|
+
new Date(base.getTime() + 1),
|
|
217
|
+
),
|
|
218
|
+
).toEqual(first);
|
|
219
|
+
await expect(
|
|
220
|
+
execution.publishArtifactOnce(
|
|
221
|
+
claim,
|
|
222
|
+
{ ...publication, byteSize: 11 },
|
|
223
|
+
new Date(base.getTime() + 2),
|
|
224
|
+
),
|
|
225
|
+
).rejects.toBeInstanceOf(DurableArtifactPublicationConflictError);
|
|
226
|
+
await expect(
|
|
227
|
+
execution.publishArtifactOnce(
|
|
228
|
+
claim,
|
|
229
|
+
{ ...publication, kind: "other-result" },
|
|
230
|
+
new Date(base.getTime() + 3),
|
|
231
|
+
),
|
|
232
|
+
).rejects.toBeInstanceOf(DurableArtifactPublicationConflictError);
|
|
233
|
+
expect((await service.get(submitted.job.id))?.artifacts).toEqual([
|
|
234
|
+
expect.objectContaining({ id: "dap_pg_stable", byteSize: 10 }),
|
|
235
|
+
]);
|
|
236
|
+
});
|
|
237
|
+
|
|
173
238
|
test("timeout、running cancel 与人工恢复保留最终事实", async () => {
|
|
174
239
|
const service = jobService(pool);
|
|
175
240
|
const submitted = await service.submit({
|
|
@@ -27,6 +27,7 @@ describePostgres("PostgreSQL durable job repository", () => {
|
|
|
27
27
|
await pool.query(`truncate table
|
|
28
28
|
governed_export_requests,
|
|
29
29
|
bulk_data_artifacts, bulk_data_jobs, scheduled_task_runs,
|
|
30
|
+
client_agent_workflow_artifacts,
|
|
30
31
|
durable_job_recoveries, durable_job_failures, durable_job_artifacts, durable_job_progress,
|
|
31
32
|
durable_job_leases, durable_job_attempts, durable_job_stages, durable_jobs`);
|
|
32
33
|
});
|
|
@@ -12,6 +12,29 @@ export type LeaseResult =
|
|
|
12
12
|
| { ok: true; cancelRequested: boolean; expiresAt: string }
|
|
13
13
|
| { ok: false; reason: LeaseRejection };
|
|
14
14
|
|
|
15
|
+
export type DurableArtifactPublication = Omit<DurableJobArtifact, "createdAt">;
|
|
16
|
+
|
|
17
|
+
export type DurableArtifactPublicationResult =
|
|
18
|
+
| Exclude<LeaseResult, { ok: true }>
|
|
19
|
+
| {
|
|
20
|
+
ok: true;
|
|
21
|
+
cancelRequested: true;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
ok: true;
|
|
26
|
+
cancelRequested: false;
|
|
27
|
+
expiresAt: string;
|
|
28
|
+
artifact: DurableJobArtifact;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export class DurableArtifactPublicationConflictError extends Error {
|
|
32
|
+
constructor(message = "同一任务产物发布坐标已绑定不同内容") {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "DurableArtifactPublicationConflictError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
export interface ClaimDurableJob {
|
|
16
39
|
queue: string;
|
|
17
40
|
workerId: string;
|
|
@@ -75,6 +98,11 @@ export interface DurableExecutionRepository {
|
|
|
75
98
|
artifact: DurableJobArtifact,
|
|
76
99
|
now: Date,
|
|
77
100
|
): Promise<LeaseResult>;
|
|
101
|
+
publishArtifactOnce(
|
|
102
|
+
identity: DurableLeaseIdentity,
|
|
103
|
+
artifact: DurableArtifactPublication,
|
|
104
|
+
now: Date,
|
|
105
|
+
): Promise<DurableArtifactPublicationResult>;
|
|
78
106
|
complete(identity: DurableLeaseIdentity, now: Date): Promise<LeaseResult>;
|
|
79
107
|
fail(
|
|
80
108
|
identity: DurableLeaseIdentity,
|
|
@@ -216,7 +216,9 @@ describe("durable job execution", () => {
|
|
|
216
216
|
expiredAt,
|
|
217
217
|
),
|
|
218
218
|
).toMatchObject({ ok: true });
|
|
219
|
-
expect((await service.get(submitted.job.id))?.artifacts).
|
|
219
|
+
expect((await service.get(submitted.job.id))?.artifacts).toEqual([
|
|
220
|
+
expect.objectContaining({ id: "artifact-replay" }),
|
|
221
|
+
]);
|
|
220
222
|
|
|
221
223
|
const cancelled = await service.submit(jobInput("artifact-cancelled"));
|
|
222
224
|
const cancelledClaim = await repository.claim(
|
|
@@ -17,6 +17,7 @@ import { leaseIdentity, startInMemoryAttempt } from "./in-memory-claim.js";
|
|
|
17
17
|
import {
|
|
18
18
|
inMemoryLeaseSuccess,
|
|
19
19
|
publishInMemoryArtifact,
|
|
20
|
+
publishInMemoryArtifactOnce,
|
|
20
21
|
validateInMemoryLease,
|
|
21
22
|
} from "./in-memory-lease.js";
|
|
22
23
|
import {
|
|
@@ -121,6 +122,21 @@ export class InMemoryDurableExecutionRepository
|
|
|
121
122
|
);
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
async publishArtifactOnce(
|
|
126
|
+
identity: DurableLeaseIdentity,
|
|
127
|
+
artifact: Parameters<DurableExecutionRepository["publishArtifactOnce"]>[1],
|
|
128
|
+
now: Date,
|
|
129
|
+
) {
|
|
130
|
+
return publishInMemoryArtifactOnce(
|
|
131
|
+
this.leases,
|
|
132
|
+
this.artifacts,
|
|
133
|
+
this.jobs,
|
|
134
|
+
identity,
|
|
135
|
+
artifact,
|
|
136
|
+
now,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
124
140
|
async complete(identity: DurableLeaseIdentity, now: Date) {
|
|
125
141
|
const validated = validateInMemoryLease(this.leases, identity, now);
|
|
126
142
|
if (!validated.ok) return validated;
|
|
@@ -4,7 +4,16 @@ import type {
|
|
|
4
4
|
DurableLeaseIdentity,
|
|
5
5
|
} from "@southwind-ai/jobs";
|
|
6
6
|
import type { LeaseResult } from "./execution-repository.js";
|
|
7
|
+
import type {
|
|
8
|
+
DurableArtifactPublication,
|
|
9
|
+
DurableArtifactPublicationResult,
|
|
10
|
+
} from "./execution-repository.js";
|
|
11
|
+
import { DurableArtifactPublicationConflictError } from "./execution-repository.js";
|
|
7
12
|
import { hashLeaseToken } from "./in-memory-claim.js";
|
|
13
|
+
import {
|
|
14
|
+
assertArtifactPublicationIdentity,
|
|
15
|
+
sameArtifactPublication,
|
|
16
|
+
} from "./artifact-publication.js";
|
|
8
17
|
|
|
9
18
|
export function validateInMemoryLease(
|
|
10
19
|
leases: Map<string, DurableJobLease>,
|
|
@@ -34,7 +43,7 @@ export function inMemoryLeaseSuccess(
|
|
|
34
43
|
jobs: Map<string, { cancelRequestedAt?: string }>,
|
|
35
44
|
jobId: string,
|
|
36
45
|
expiresAt: string,
|
|
37
|
-
): LeaseResult {
|
|
46
|
+
): Extract<LeaseResult, { ok: true }> {
|
|
38
47
|
return {
|
|
39
48
|
ok: true,
|
|
40
49
|
cancelRequested: Boolean(jobs.get(jobId)?.cancelRequestedAt),
|
|
@@ -64,3 +73,38 @@ export function publishInMemoryArtifact(
|
|
|
64
73
|
);
|
|
65
74
|
return result;
|
|
66
75
|
}
|
|
76
|
+
|
|
77
|
+
export function publishInMemoryArtifactOnce(
|
|
78
|
+
leases: Map<string, DurableJobLease>,
|
|
79
|
+
artifacts: Map<string, DurableJobArtifact>,
|
|
80
|
+
jobs: Map<string, { cancelRequestedAt?: string }>,
|
|
81
|
+
identity: DurableLeaseIdentity,
|
|
82
|
+
artifact: DurableArtifactPublication,
|
|
83
|
+
now: Date,
|
|
84
|
+
): DurableArtifactPublicationResult {
|
|
85
|
+
const validated = validateInMemoryLease(leases, identity, now);
|
|
86
|
+
if (!validated.ok) return validated;
|
|
87
|
+
assertArtifactPublicationIdentity(identity, artifact);
|
|
88
|
+
const lease = inMemoryLeaseSuccess(
|
|
89
|
+
jobs,
|
|
90
|
+
identity.jobId,
|
|
91
|
+
validated.lease.expiresAt,
|
|
92
|
+
);
|
|
93
|
+
if (lease.cancelRequested) {
|
|
94
|
+
return { ...lease, cancelRequested: true };
|
|
95
|
+
}
|
|
96
|
+
const publicationLease = { ...lease, cancelRequested: false } as const;
|
|
97
|
+
const candidate = { ...artifact, createdAt: now.toISOString() };
|
|
98
|
+
const coordinate = `${artifact.jobId}:${artifact.kind}`;
|
|
99
|
+
const existing =
|
|
100
|
+
artifacts.get(coordinate) ||
|
|
101
|
+
[...artifacts.values()].find(({ id }) => id === artifact.id);
|
|
102
|
+
if (existing) {
|
|
103
|
+
if (!sameArtifactPublication(existing, artifact)) {
|
|
104
|
+
throw new DurableArtifactPublicationConflictError();
|
|
105
|
+
}
|
|
106
|
+
return { ...publicationLease, artifact: structuredClone(existing) };
|
|
107
|
+
}
|
|
108
|
+
artifacts.set(coordinate, structuredClone(candidate));
|
|
109
|
+
return { ...publicationLease, artifact: candidate };
|
|
110
|
+
}
|
|
@@ -34,9 +34,11 @@ describePostgres("PostgreSQL legacy durable adapter", () => {
|
|
|
34
34
|
});
|
|
35
35
|
await pool.query(`
|
|
36
36
|
truncate table
|
|
37
|
+
governed_export_requests,
|
|
37
38
|
bulk_data_artifacts,
|
|
38
39
|
bulk_data_jobs,
|
|
39
40
|
scheduled_task_runs,
|
|
41
|
+
client_agent_workflow_artifacts,
|
|
40
42
|
durable_job_recoveries,
|
|
41
43
|
durable_job_failures,
|
|
42
44
|
durable_job_artifacts,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
const integrationFiles = new Bun.Glob("**/*.integration.test.ts");
|
|
4
|
+
const migrationFiles = new Bun.Glob("*.sql");
|
|
5
|
+
|
|
6
|
+
describe("PostgreSQL durable job test cleanup", () => {
|
|
7
|
+
test("所有显式 durable_jobs TRUNCATE 同批清理受限子表且不使用 CASCADE", async () => {
|
|
8
|
+
const serverSourceRoot = new URL("../", import.meta.url).pathname;
|
|
9
|
+
const migrationsRoot = new URL(
|
|
10
|
+
"../../../../packages/database/drizzle/",
|
|
11
|
+
import.meta.url,
|
|
12
|
+
).pathname;
|
|
13
|
+
const durableJobChildTables = new Set<string>();
|
|
14
|
+
const harnesses: string[] = [];
|
|
15
|
+
|
|
16
|
+
for await (const path of migrationFiles.scan(migrationsRoot)) {
|
|
17
|
+
const migration = await Bun.file(`${migrationsRoot}/${path}`).text();
|
|
18
|
+
for (const match of migration.matchAll(
|
|
19
|
+
/ALTER TABLE "([^"]+)" ADD CONSTRAINT [^\n]+ REFERENCES "public"\."durable_jobs"/g,
|
|
20
|
+
)) {
|
|
21
|
+
durableJobChildTables.add(match[1]!);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
expect(durableJobChildTables).toContain("client_agent_workflow_artifacts");
|
|
25
|
+
|
|
26
|
+
for await (const path of integrationFiles.scan(serverSourceRoot)) {
|
|
27
|
+
const source = await Bun.file(`${serverSourceRoot}/${path}`).text();
|
|
28
|
+
if (!source.toLowerCase().includes("truncate table")) continue;
|
|
29
|
+
if (!source.includes("durable_jobs")) continue;
|
|
30
|
+
|
|
31
|
+
harnesses.push(path);
|
|
32
|
+
const cleanup = source.match(
|
|
33
|
+
/`(?=[^`]*truncate table)(?=[^`]*durable_jobs)[^`]+`/is,
|
|
34
|
+
)?.[0];
|
|
35
|
+
expect(cleanup, `${path} 缺少可审计的显式清理 SQL`).toBeDefined();
|
|
36
|
+
for (const childTable of durableJobChildTables) {
|
|
37
|
+
expect(cleanup, `${path} 缺少 ${childTable}`).toContain(childTable);
|
|
38
|
+
}
|
|
39
|
+
expect(cleanup, `${path} 不得用 CASCADE 掩盖清理闭包漂移`).not.toMatch(
|
|
40
|
+
/\bcascade\b/i,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
expect(
|
|
45
|
+
harnesses.length,
|
|
46
|
+
"至少应发现一个 durable_jobs 清理 harness",
|
|
47
|
+
).toBeGreaterThan(0);
|
|
48
|
+
for (const coreHarness of [
|
|
49
|
+
"jobs/drizzle-execution.integration.test.ts",
|
|
50
|
+
"jobs/drizzle-repository.integration.test.ts",
|
|
51
|
+
"workflow/drizzle-repository.integration.test.ts",
|
|
52
|
+
]) {
|
|
53
|
+
expect(harnesses, `缺少核心清理 harness:${coreHarness}`).toContain(
|
|
54
|
+
coreHarness,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
LeaseResult,
|
|
5
5
|
ManualJobRecovery,
|
|
6
6
|
} from "./execution-repository.js";
|
|
7
|
+
import { createDurableArtifactPublisher } from "./artifact-publisher.js";
|
|
7
8
|
import {
|
|
8
9
|
DurableHandlerError,
|
|
9
10
|
type DurableHandlerContext,
|
|
@@ -158,20 +159,13 @@ export class DurableJobWorker {
|
|
|
158
159
|
);
|
|
159
160
|
assertLease(result, abort);
|
|
160
161
|
},
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
...artifact,
|
|
169
|
-
createdAt: this.now().toISOString(),
|
|
170
|
-
},
|
|
171
|
-
this.now(),
|
|
172
|
-
);
|
|
173
|
-
assertLease(result, abort);
|
|
174
|
-
},
|
|
162
|
+
...createDurableArtifactPublisher({
|
|
163
|
+
repository: this.repository,
|
|
164
|
+
identity,
|
|
165
|
+
stageKey: claim.stage.key,
|
|
166
|
+
now: this.now,
|
|
167
|
+
assertLease: (result) => assertLease(result, abort),
|
|
168
|
+
}),
|
|
175
169
|
};
|
|
176
170
|
await Promise.race([
|
|
177
171
|
(async () => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ModuleBackgroundAiError,
|
|
3
|
+
type ModuleBackgroundAiPort,
|
|
4
|
+
} from "@southwind-ai/server-sdk";
|
|
5
|
+
|
|
6
|
+
export function unavailableModuleBackgroundAiPort(
|
|
7
|
+
moduleName: string,
|
|
8
|
+
): ModuleBackgroundAiPort {
|
|
9
|
+
return {
|
|
10
|
+
async enqueueOperation() {
|
|
11
|
+
throw new ModuleBackgroundAiError(
|
|
12
|
+
"AI_OPERATION_UNAVAILABLE",
|
|
13
|
+
`模块 ${moduleName} 的后台 AI Operation Port 未挂载`,
|
|
14
|
+
true,
|
|
15
|
+
);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -14,17 +14,14 @@ import type {
|
|
|
14
14
|
// @windy-module system.storage begin
|
|
15
15
|
import type { ModuleReportStagingPortV1 } from "@southwind-ai/server-sdk";
|
|
16
16
|
// @windy-module system.storage end
|
|
17
|
-
import { ModuleBackgroundAiError } from "@southwind-ai/server-sdk";
|
|
18
17
|
// @windy-module system.storage begin
|
|
19
18
|
import { ModuleReportStagingError } from "@southwind-ai/server-sdk";
|
|
20
19
|
// @windy-module system.storage end
|
|
21
20
|
import { evaluateAccessGuards } from "../access-guards.js";
|
|
22
|
-
import
|
|
23
|
-
captureAuthorizationScope,
|
|
24
|
-
hashAuthorizationScope,
|
|
25
|
-
} from "../data-access/scope-snapshot.js";
|
|
21
|
+
import * as scopeSnapshot from "../data-access/scope-snapshot.js";
|
|
26
22
|
import type { RequestActor, RequestGuardContext } from "../guards.js";
|
|
27
23
|
import type { createServerRuntime } from "../runtime.js";
|
|
24
|
+
import { unavailableModuleBackgroundAiPort } from "./background-ai-unavailable.js";
|
|
28
25
|
import type { InitializedModuleHosts } from "./initialize.js";
|
|
29
26
|
import {
|
|
30
27
|
createModuleStoragePort,
|
|
@@ -36,6 +33,9 @@ import { createModuleStorageWritePort } from "./storage-write-port.js";
|
|
|
36
33
|
import { unavailableModuleStorageWritePort } from "./storage-write-unavailable.js";
|
|
37
34
|
import { toModuleActor } from "./request-context.js";
|
|
38
35
|
import { createModuleSecretPort } from "./module-secret-port.js";
|
|
36
|
+
// @windy-module system.agent begin
|
|
37
|
+
import { moduleWorkflowInputArtifactContext } from "./workflow-input-artifact-port.js";
|
|
38
|
+
// @windy-module system.agent end
|
|
39
39
|
// @windy-module system.storage begin
|
|
40
40
|
import type { ReportStagingService } from "../report-staging/service.js";
|
|
41
41
|
import { createModuleReportStagingPort } from "./report-staging-port.js";
|
|
@@ -87,8 +87,8 @@ export function createModuleDurableContextualizer(
|
|
|
87
87
|
) {
|
|
88
88
|
throw denied("MODULE_ACTOR_UNAVAILABLE");
|
|
89
89
|
}
|
|
90
|
-
const current = captureAuthorizationScope(actor.scopeContext);
|
|
91
|
-
if (current.hash !== hashAuthorizationScope(snapshot)) {
|
|
90
|
+
const current = scopeSnapshot.captureAuthorizationScope(actor.scopeContext);
|
|
91
|
+
if (current.hash !== scopeSnapshot.hashAuthorizationScope(snapshot)) {
|
|
92
92
|
throw denied("MODULE_AUTHORIZATION_SCOPE_CHANGED");
|
|
93
93
|
}
|
|
94
94
|
const request = new Request(
|
|
@@ -216,6 +216,15 @@ export function createModuleDurableContextualizer(
|
|
|
216
216
|
}),
|
|
217
217
|
storage,
|
|
218
218
|
storageWriter,
|
|
219
|
+
// @windy-module system.agent begin
|
|
220
|
+
...moduleWorkflowInputArtifactContext(
|
|
221
|
+
input.moduleName,
|
|
222
|
+
actor.id,
|
|
223
|
+
storage,
|
|
224
|
+
services.storage?.blobStore,
|
|
225
|
+
input.context,
|
|
226
|
+
),
|
|
227
|
+
// @windy-module system.agent end
|
|
219
228
|
// @windy-module system.storage begin
|
|
220
229
|
reportStaging,
|
|
221
230
|
// @windy-module system.storage end
|
|
@@ -224,7 +233,7 @@ export function createModuleDurableContextualizer(
|
|
|
224
233
|
input.moduleName,
|
|
225
234
|
guardContext,
|
|
226
235
|
input.context.signal,
|
|
227
|
-
) ??
|
|
236
|
+
) ?? unavailableModuleBackgroundAiPort(input.moduleName),
|
|
228
237
|
};
|
|
229
238
|
return moduleContext;
|
|
230
239
|
};
|
|
@@ -249,8 +258,8 @@ async function resolveDurableReportAccess(
|
|
|
249
258
|
const snapshot = input.claim.job.authorizationScope;
|
|
250
259
|
if (
|
|
251
260
|
!snapshot ||
|
|
252
|
-
captureAuthorizationScope(actor.scopeContext).hash !==
|
|
253
|
-
hashAuthorizationScope(snapshot)
|
|
261
|
+
scopeSnapshot.captureAuthorizationScope(actor.scopeContext).hash !==
|
|
262
|
+
scopeSnapshot.hashAuthorizationScope(snapshot)
|
|
254
263
|
) {
|
|
255
264
|
return false;
|
|
256
265
|
}
|
|
@@ -322,18 +331,6 @@ function unavailableDurableStoragePort(
|
|
|
322
331
|
};
|
|
323
332
|
}
|
|
324
333
|
|
|
325
|
-
function unavailableBackgroundAi(moduleName: string): ModuleBackgroundAiPort {
|
|
326
|
-
return {
|
|
327
|
-
async enqueueOperation() {
|
|
328
|
-
throw new ModuleBackgroundAiError(
|
|
329
|
-
"AI_OPERATION_UNAVAILABLE",
|
|
330
|
-
`模块 ${moduleName} 的后台 AI Operation Port 未挂载`,
|
|
331
|
-
true,
|
|
332
|
-
);
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
334
|
function denied(code: string, details?: Record<string, unknown>) {
|
|
338
335
|
return new DurableHandlerError(
|
|
339
336
|
code,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
// @windy-module system.agent begin
|
|
4
|
+
defineModuleWorkflowInputMaterializationHandlerV1,
|
|
5
|
+
// @windy-module system.agent end
|
|
6
|
+
type ModuleHost,
|
|
7
|
+
} from "@southwind-ai/server-sdk";
|
|
3
8
|
import { featureKey } from "@southwind-ai/shared";
|
|
4
9
|
import { initializeModuleHosts } from "./initialize.js";
|
|
5
10
|
import {
|
|
@@ -130,6 +135,80 @@ describe("Server Module 实现完整性", () => {
|
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
137
|
});
|
|
138
|
+
|
|
139
|
+
// @windy-module system.agent begin
|
|
140
|
+
test("固定 materialization task 由同 capability Durable Handler 完整实现", async () => {
|
|
141
|
+
const moduleManifest = manifest("case", {
|
|
142
|
+
features: [
|
|
143
|
+
{
|
|
144
|
+
key: featureKey("case.materialize"),
|
|
145
|
+
label: "案件输入物化",
|
|
146
|
+
module: "case",
|
|
147
|
+
visible: "hidden",
|
|
148
|
+
enabled: true,
|
|
149
|
+
stage: "stable",
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
permissions: [
|
|
153
|
+
{
|
|
154
|
+
key: "case.materialize.execute",
|
|
155
|
+
label: "物化案件输入",
|
|
156
|
+
module: "case",
|
|
157
|
+
moduleLabel: "案件",
|
|
158
|
+
resource: "materialization",
|
|
159
|
+
resourceLabel: "输入物化",
|
|
160
|
+
action: "execute",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
tasks: [
|
|
164
|
+
{
|
|
165
|
+
key: "case.workflow-input-materialize",
|
|
166
|
+
label: "案件输入物化",
|
|
167
|
+
description: "物化受治理 Workflow 输入。",
|
|
168
|
+
featureKey: "case.materialize",
|
|
169
|
+
permissionKey: "case.materialize.execute",
|
|
170
|
+
auditAction: "case.materialize.execute",
|
|
171
|
+
intervalSeconds: 60,
|
|
172
|
+
maxAttempts: 3,
|
|
173
|
+
retryDelaySeconds: 5,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
auditActions: ["case.materialize.execute"],
|
|
177
|
+
});
|
|
178
|
+
const installation = {
|
|
179
|
+
manifest: moduleManifest,
|
|
180
|
+
setup(host: ModuleHost) {
|
|
181
|
+
host.registerDurableHandler(
|
|
182
|
+
defineModuleWorkflowInputMaterializationHandlerV1({
|
|
183
|
+
moduleName: "case",
|
|
184
|
+
featureKey: "case.materialize",
|
|
185
|
+
permissionKey: "case.materialize.execute",
|
|
186
|
+
auditAction: "case.materialize.execute",
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
const hosts = await initializeModuleHosts({
|
|
192
|
+
modules: [moduleManifest],
|
|
193
|
+
installations: [installation],
|
|
194
|
+
});
|
|
195
|
+
expect(() =>
|
|
196
|
+
validateHostedModuleImplementations([installation], hosts),
|
|
197
|
+
).not.toThrow();
|
|
198
|
+
expect(() =>
|
|
199
|
+
validateHostedModuleImplementations([installation], {
|
|
200
|
+
...hosts,
|
|
201
|
+
durableHandlers: hosts.durableHandlers.map((entry) => ({
|
|
202
|
+
...entry,
|
|
203
|
+
definition: {
|
|
204
|
+
...entry.definition,
|
|
205
|
+
auditAction: "case.materialize.drifted",
|
|
206
|
+
},
|
|
207
|
+
})),
|
|
208
|
+
}),
|
|
209
|
+
).toThrow("定时任务 case.workflow-input-materialize 缺少 Handler");
|
|
210
|
+
});
|
|
211
|
+
// @windy-module system.agent end
|
|
133
212
|
});
|
|
134
213
|
|
|
135
214
|
function completeRouteModule() {
|
|
@@ -4,6 +4,9 @@ import type {
|
|
|
4
4
|
InstallableServerModule,
|
|
5
5
|
ModuleGovernedResourceRegistration,
|
|
6
6
|
} from "@southwind-ai/server-sdk";
|
|
7
|
+
// @windy-module system.agent begin
|
|
8
|
+
import { moduleWorkflowInputMaterializationHandlerKey } from "@southwind-ai/server-sdk";
|
|
9
|
+
// @windy-module system.agent end
|
|
7
10
|
import type { InitializedModuleHosts } from "./initialize-contracts.js";
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -42,7 +45,10 @@ export function validateHostedModuleImplementations(
|
|
|
42
45
|
installation.manifest.tasks.map(({ key }) => key),
|
|
43
46
|
hosts.taskHandlers
|
|
44
47
|
.filter((entry) => entry.moduleName === moduleName)
|
|
45
|
-
.map(({ registration }) => registration.taskKey)
|
|
48
|
+
.map(({ registration }) => registration.taskKey)
|
|
49
|
+
// @windy-module system.agent begin
|
|
50
|
+
.concat(materializationDurableTaskKeys(installation.manifest, hosts)),
|
|
51
|
+
// @windy-module system.agent end
|
|
46
52
|
);
|
|
47
53
|
assertComplete(
|
|
48
54
|
"Agent Tool",
|
|
@@ -69,6 +75,27 @@ export function validateHostedModuleImplementations(
|
|
|
69
75
|
validateDurableHandlerInstallations(hosts);
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
// @windy-module system.agent begin
|
|
79
|
+
function materializationDurableTaskKeys(
|
|
80
|
+
manifest: ModuleManifest,
|
|
81
|
+
hosts: InitializedModuleHosts,
|
|
82
|
+
) {
|
|
83
|
+
const key = moduleWorkflowInputMaterializationHandlerKey(manifest.name);
|
|
84
|
+
const task = manifest.tasks.find((candidate) => candidate.key === key);
|
|
85
|
+
if (!task) return [];
|
|
86
|
+
return hosts.durableHandlers
|
|
87
|
+
.filter(
|
|
88
|
+
({ moduleName, definition }) =>
|
|
89
|
+
moduleName === manifest.name &&
|
|
90
|
+
definition.key === key &&
|
|
91
|
+
definition.featureKey === task.featureKey &&
|
|
92
|
+
definition.permissionKey === task.permissionKey &&
|
|
93
|
+
definition.auditAction === task.auditAction,
|
|
94
|
+
)
|
|
95
|
+
.map(({ definition }) => definition.key);
|
|
96
|
+
}
|
|
97
|
+
// @windy-module system.agent end
|
|
98
|
+
|
|
72
99
|
export function governedResourceRouteKeys(
|
|
73
100
|
registration: Pick<
|
|
74
101
|
ModuleGovernedResourceRegistration,
|
|
@@ -40,6 +40,9 @@ import {
|
|
|
40
40
|
import { unavailableModuleSecretPort } from "./module-secret-port.js";
|
|
41
41
|
import { unavailableModuleStoragePort } from "./storage-port.js";
|
|
42
42
|
import { unavailableModuleStorageWritePort } from "./storage-write-unavailable.js";
|
|
43
|
+
// @windy-module system.agent begin
|
|
44
|
+
import { createModuleWorkflowInputMaterializationPort } from "./workflow-input-materialization.js";
|
|
45
|
+
// @windy-module system.agent end
|
|
43
46
|
|
|
44
47
|
type ServerRuntime = ReturnType<typeof createServerRuntime>;
|
|
45
48
|
|
|
@@ -115,6 +118,17 @@ export interface ModuleRequestContextInput {
|
|
|
115
118
|
export function buildModuleRequestContext(
|
|
116
119
|
input: ModuleRequestContextInput,
|
|
117
120
|
): ModuleRequestContext {
|
|
121
|
+
const jobs = createModuleJobPort({
|
|
122
|
+
moduleName: input.moduleName,
|
|
123
|
+
guardContext: input.guardContext,
|
|
124
|
+
runtime: input.runtime,
|
|
125
|
+
jobs: input.jobs,
|
|
126
|
+
control: input.jobControl,
|
|
127
|
+
reauthorize: input.reauthorizeJobs,
|
|
128
|
+
});
|
|
129
|
+
// @windy-module system.agent begin
|
|
130
|
+
const materializationHandlerKey = `${input.moduleName}.workflow-input-materialize`;
|
|
131
|
+
// @windy-module system.agent end
|
|
118
132
|
return {
|
|
119
133
|
requestId: input.guardContext.requestId,
|
|
120
134
|
signal: input.signal ?? new AbortController().signal,
|
|
@@ -136,14 +150,24 @@ export function buildModuleRequestContext(
|
|
|
136
150
|
...(input.sourceInvalidation
|
|
137
151
|
? { sourceInvalidation: input.sourceInvalidation }
|
|
138
152
|
: {}),
|
|
139
|
-
jobs
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
153
|
+
jobs,
|
|
154
|
+
// @windy-module system.agent begin
|
|
155
|
+
...(input.jobs &&
|
|
156
|
+
input.manifest.tasks.some(({ key }) => key === materializationHandlerKey)
|
|
157
|
+
? {
|
|
158
|
+
workflowInputMaterialization:
|
|
159
|
+
createModuleWorkflowInputMaterializationPort({
|
|
160
|
+
moduleName: input.moduleName,
|
|
161
|
+
guardContext: input.guardContext,
|
|
162
|
+
jobs: input.jobs,
|
|
163
|
+
moduleJobs: jobs,
|
|
164
|
+
...(input.reauthorizeJobs
|
|
165
|
+
? { reauthorize: input.reauthorizeJobs }
|
|
166
|
+
: {}),
|
|
167
|
+
}),
|
|
168
|
+
}
|
|
169
|
+
: {}),
|
|
170
|
+
// @windy-module system.agent end
|
|
147
171
|
secrets: input.secrets ?? unavailableModuleSecretPort(),
|
|
148
172
|
storage: input.storage ?? unavailableModuleStoragePort(input.moduleName),
|
|
149
173
|
storageWriter:
|