@sanoma-ai/workflows 0.10.2 → 0.10.3
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 +43 -11
- package/dist/apply.js +1 -1
- package/dist/apply.js.map +1 -1
- package/dist/call.d.ts +5 -2
- package/dist/call.js +65 -20
- package/dist/call.js.map +1 -1
- package/dist/change.d.ts +5 -6
- package/dist/change.js +6 -9
- package/dist/change.js.map +1 -1
- package/dist/client.d.ts +18 -0
- package/dist/client.js +35 -3
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +42 -4
- package/dist/config.js +71 -10
- package/dist/config.js.map +1 -1
- package/dist/db.d.ts +2 -0
- package/dist/db.js +7 -0
- package/dist/db.js.map +1 -1
- package/dist/deadlines.d.ts +26 -13
- package/dist/deadlines.js +50 -11
- package/dist/deadlines.js.map +1 -1
- package/dist/define.d.ts +22 -0
- package/dist/define.js +2 -0
- package/dist/define.js.map +1 -1
- package/dist/deployment-key.d.ts +20 -1
- package/dist/deployment-key.js +28 -3
- package/dist/deployment-key.js.map +1 -1
- package/dist/describe.js +28 -1
- package/dist/describe.js.map +1 -1
- package/dist/description.d.ts +3 -0
- package/dist/description.js +2 -0
- package/dist/description.js.map +1 -1
- package/dist/host.d.ts +13 -4
- package/dist/host.js +14 -5
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +30 -1
- package/dist/ledger.js +31 -0
- package/dist/ledger.js.map +1 -1
- package/dist/login-code.d.ts +17 -0
- package/dist/login-code.js +32 -0
- package/dist/login-code.js.map +1 -0
- package/dist/op.d.ts +78 -10
- package/dist/op.js +20 -0
- package/dist/op.js.map +1 -1
- package/dist/outline.js +3 -3
- package/dist/outline.js.map +1 -1
- package/dist/resource.d.ts +6 -4
- package/dist/resource.js +8 -5
- package/dist/resource.js.map +1 -1
- package/dist/resources.d.ts +4 -2
- package/dist/resources.js +15 -6
- package/dist/resources.js.map +1 -1
- package/dist/run.d.ts +3 -0
- package/dist/run.js.map +1 -1
- package/dist/runtime-ops.d.ts +123 -0
- package/dist/runtime-ops.js +222 -0
- package/dist/runtime-ops.js.map +1 -0
- package/dist/seal.d.ts +27 -0
- package/dist/seal.js +76 -0
- package/dist/seal.js.map +1 -0
- package/dist/secret-store.d.ts +6 -4
- package/dist/secret-store.js +11 -6
- package/dist/secret-store.js.map +1 -1
- package/dist/secret.d.ts +7 -0
- package/dist/secret.js +13 -1
- package/dist/secret.js.map +1 -1
- package/dist/shared.d.ts +15 -4
- package/dist/shared.js +27 -7
- package/dist/shared.js.map +1 -1
- package/dist/ssh-key.d.ts +11 -0
- package/dist/ssh-key.js +44 -0
- package/dist/ssh-key.js.map +1 -0
- package/dist/tokens.d.ts +35 -0
- package/dist/tokens.js +71 -0
- package/dist/tokens.js.map +1 -0
- package/dist/wakeup-service.js +2 -7
- package/dist/wakeup-service.js.map +1 -1
- package/dist/wakeup.d.ts +18 -0
- package/dist/wakeup.js +15 -0
- package/dist/wakeup.js.map +1 -1
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +17 -9
- package/dist/worker.js.map +1 -1
- package/oxlint.json +1 -1
- package/package.json +1 -1
- package/src/apply.ts +1 -1
- package/src/call.ts +88 -18
- package/src/change.ts +9 -20
- package/src/client.ts +48 -7
- package/src/config.ts +111 -17
- package/src/db.ts +8 -1
- package/src/deadlines.ts +66 -20
- package/src/define.ts +25 -0
- package/src/deployment-key.ts +34 -8
- package/src/describe.ts +39 -2
- package/src/description.ts +2 -0
- package/src/host.ts +22 -6
- package/src/index.ts +18 -1
- package/src/ledger.ts +52 -1
- package/src/login-code.ts +52 -0
- package/src/op.ts +84 -11
- package/src/outline.ts +3 -3
- package/src/resource.ts +8 -5
- package/src/resources.ts +13 -4
- package/src/run.ts +3 -0
- package/src/runtime-ops.ts +234 -0
- package/src/seal.ts +95 -0
- package/src/secret-store.ts +12 -9
- package/src/secret.ts +16 -1
- package/src/shared.ts +31 -7
- package/src/ssh-key.ts +48 -0
- package/src/tokens.ts +100 -0
- package/src/wakeup-service.ts +2 -8
- package/src/wakeup.ts +21 -0
- package/src/worker.ts +29 -11
package/src/wakeup-service.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { connectMaking, createTable, type Db, openPool } from "./db.ts";
|
|
3
3
|
import { info, warn } from "./log.ts";
|
|
4
|
-
import { generateSecret } from "./secret.ts";
|
|
4
|
+
import { generateSecret, tokenHash as hashOf } from "./secret.ts";
|
|
5
5
|
import { errorMessage } from "./shared.ts";
|
|
6
6
|
|
|
7
7
|
/*
|
|
@@ -110,12 +110,6 @@ export const WAKEUP_TOKEN = "SANOMA_WAKEUP_TOKEN";
|
|
|
110
110
|
/** An instance token the service takes: `swi_`, then at least as many base64url characters as a minted one has. */
|
|
111
111
|
export const INSTANCE_TOKEN = /^swi_[\w-]{43,256}$/;
|
|
112
112
|
|
|
113
|
-
/**
|
|
114
|
-
* The token's sha256, which is all the tables keep: a token is found by its hash (a unique index),
|
|
115
|
-
* so the secret itself is never compared, and a guess's timing says nothing an attacker can steer.
|
|
116
|
-
*/
|
|
117
|
-
const hashOf = (token: string) => createHash("sha256").update(token).digest("hex");
|
|
118
|
-
|
|
119
113
|
/** A date-time as `Date.toISOString` writes one, or with an offset: four-digit years only. */
|
|
120
114
|
const ISO_DATE_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
121
115
|
|
package/src/wakeup.ts
CHANGED
|
@@ -11,6 +11,27 @@ import type { DeclaredResource } from "./resources.ts";
|
|
|
11
11
|
// replaced registration starts a worker that finds nothing to do and stops again. The contract is
|
|
12
12
|
// here; the worker's side is `deadlines.ts`.
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* A host's own wake route, where a wakeup that the host holds itself takes a registration: `PUT`
|
|
16
|
+
* with `WakeupRegistration` and the instance's `SANOMA_WAKEUP_TOKEN` as a bearer, answered 204.
|
|
17
|
+
* The Cloudflare Worker serves it; a hosted instance that declares no wakeup registers there
|
|
18
|
+
* (`EnvWakeup`).
|
|
19
|
+
*/
|
|
20
|
+
export const WAKE_PATH = "/__sanoma/wake";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The wakeup a hosted instance's environment names, when its data files declare none: its own
|
|
24
|
+
* host's URL (`SANOMA_HOST_URL`) and its token (`SANOMA_WAKEUP_TOKEN`), which the host gives the
|
|
25
|
+
* runtime. The worker registers each deadline at `{url}{WAKE_PATH}` itself, with no driver.
|
|
26
|
+
*/
|
|
27
|
+
export interface EnvWakeup {
|
|
28
|
+
url: string;
|
|
29
|
+
token: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Where a host gives the runtime its own URL: the Cloudflare Worker's plain-text binding. */
|
|
33
|
+
export const HOST_URL = "SANOMA_HOST_URL";
|
|
34
|
+
|
|
14
35
|
/** What a wakeup's state says of it. */
|
|
15
36
|
export interface WakeupOutputs {
|
|
16
37
|
healthy: boolean;
|
package/src/worker.ts
CHANGED
|
@@ -2,10 +2,19 @@ import { DBOS, type ScheduledWorkflowFn } from "@dbos-inc/dbos-sdk";
|
|
|
2
2
|
import { awaitApproval } from "./approvals.ts";
|
|
3
3
|
import { buildCtx, decideStart, isInfrastructureError, startHoldOf } from "./call.ts";
|
|
4
4
|
import { dbosStatusesOf } from "./client.ts";
|
|
5
|
-
import { declaredWakeup, type ResolvedConfig, resolveConfig, type SanomaConfig } from "./config.ts";
|
|
5
|
+
import { declaredWakeup, envWakeup, type ResolvedConfig, resolveConfig, type SanomaConfig } from "./config.ts";
|
|
6
6
|
import { configuredSecrets } from "./credentials.ts";
|
|
7
7
|
import { type Db, DBOS_DATABASE_OPTIONS, lazyPool, prepareDatabase, stampVersion } from "./db.ts";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
deadlineRegistry,
|
|
10
|
+
type Deadlines,
|
|
11
|
+
declaredRegister,
|
|
12
|
+
envRegister,
|
|
13
|
+
idle,
|
|
14
|
+
onOwnQueue,
|
|
15
|
+
type SelfStop,
|
|
16
|
+
stopWhenIdle,
|
|
17
|
+
} from "./deadlines.ts";
|
|
9
18
|
import type { WorkflowDefinition } from "./define.ts";
|
|
10
19
|
import { credentialsOf, unconfigured, unconfiguredSummary } from "./env.ts";
|
|
11
20
|
import { errorInfo, parseOrThrow, PolicyDeniedError } from "./errors.ts";
|
|
@@ -79,10 +88,10 @@ export interface WorkerOptions {
|
|
|
79
88
|
* and other short waits never stop it.
|
|
80
89
|
*/
|
|
81
90
|
idleHorizonMs?: number;
|
|
82
|
-
/** How often a worker
|
|
91
|
+
/** How often a worker with a wakeup (declared, or named by its environment: `envWakeup`) checks whether it may stop: every 15 s by default. */
|
|
83
92
|
stopCheckMs?: number;
|
|
84
93
|
/**
|
|
85
|
-
* Given, and with a wakeup declared, the worker stops itself when idle (`stopCheckMs`); without
|
|
94
|
+
* Given, and with a wakeup (declared, or named by the environment), the worker stops itself when idle (`stopCheckMs`); without
|
|
86
95
|
* it, nobody would start another, so it never does. Called once the worker has stopped itself, with the deadline it registered with the
|
|
87
96
|
* wakeup (null for none): a supervisor starts a fresh worker on the next write or wake, since a
|
|
88
97
|
* stopped one cannot launch again.
|
|
@@ -108,9 +117,12 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
|
|
|
108
117
|
setLogLevel(options.logLevel);
|
|
109
118
|
// Read once, when it matters: a worker stops itself, registering its next deadline there, only
|
|
110
119
|
// when its caller hears it stop (`onStopped`).
|
|
111
|
-
const
|
|
120
|
+
const declared = options.onStopped || config.wakeup === true ? await declaredWakeup(config) : undefined;
|
|
121
|
+
// Else the one a host Sanoma Cloud made names in the environment, which an instance running the
|
|
122
|
+
// wakeup service ignores: it must not stop.
|
|
123
|
+
const wakeup = declared ?? (options.onStopped && config.wakeup !== true ? envWakeup(process.env) : undefined);
|
|
112
124
|
// The wakeup service wakes the instances that stop: one that runs it must not stop with its worker.
|
|
113
|
-
if (config.wakeup === true &&
|
|
125
|
+
if (config.wakeup === true && declared) {
|
|
114
126
|
throw new Error(
|
|
115
127
|
"The config runs the wakeup service (`wakeup: true`) and declares a wakeup of its own: an instance that runs the service must not stop, so leave out one of them",
|
|
116
128
|
);
|
|
@@ -130,6 +142,7 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
|
|
|
130
142
|
...(resolved.scenarios && { scenarios: resolved.scenarios }),
|
|
131
143
|
...(options.source && { source: options.source }),
|
|
132
144
|
types: resolved.types,
|
|
145
|
+
declaredObjects: resolved.declaredObjects,
|
|
133
146
|
collections: resolved.collections,
|
|
134
147
|
steps: resolved.steps,
|
|
135
148
|
policy: resolved.policy,
|
|
@@ -232,14 +245,19 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
|
|
|
232
245
|
warn(`the policies do not hold, so every call and start is refused:\n ${resolved.policyProblems.join("\n ")}`);
|
|
233
246
|
}
|
|
234
247
|
if (wakeup && options.onStopped) {
|
|
235
|
-
const registerId = wakeup.type.wakeup!.register;
|
|
236
248
|
stopRule = stopWhenIdle({
|
|
237
249
|
// The row is one more of the runtime's tables: the secrets' pool, on the same database.
|
|
238
250
|
db: () => secrets.db(),
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
register:
|
|
252
|
+
"type" in wakeup
|
|
253
|
+
? declaredRegister({
|
|
254
|
+
// `resolveConfig` checked the operation is the config's (`checkWakeupOps`).
|
|
255
|
+
op: resolved.ops.get(wakeup.type.wakeup!.register)!,
|
|
256
|
+
fn: resolved.drivers.get(wakeup.type.wakeup!.register),
|
|
257
|
+
secrets: state.secrets,
|
|
258
|
+
wakeup: wakeup.binding ?? wakeup.id,
|
|
259
|
+
})
|
|
260
|
+
: envRegister(wakeup),
|
|
243
261
|
deadlines: state.deadlines,
|
|
244
262
|
idle: () => worker.idle(),
|
|
245
263
|
everyMs: options.stopCheckMs ?? 15_000,
|