@radishbot/sdk 0.4.0 → 0.5.0
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 +35 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +28 -7
- package/package.json +1 -1
- package/src/cli.ts +26 -12
- package/src/index.ts +32 -4
- package/src/module_bindings/create_root_flow_reducer.ts +1 -0
- package/src/module_bindings/flow_table.ts +1 -0
- package/src/module_bindings/types.ts +1 -0
package/README.md
CHANGED
|
@@ -139,6 +139,34 @@ action.info("continuing from service B");
|
|
|
139
139
|
await action.finish();
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
## Run ID
|
|
143
|
+
|
|
144
|
+
Every `RL()` call auto-generates a **run ID** — a short random string that groups flows across processes. Pass it to subprocesses via env vars, CLI args, or message queues so all flows from one logical execution link together.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const root = await RL(key);
|
|
148
|
+
|
|
149
|
+
// Pass to subprocess
|
|
150
|
+
spawn("worker.ts", { env: { ...process.env, RADISH_RUN_ID: root.runId } });
|
|
151
|
+
|
|
152
|
+
// In the subprocess — same run ID groups them together
|
|
153
|
+
const worker = await RL(key, { runId: process.env.RADISH_RUN_ID });
|
|
154
|
+
worker.info("processing");
|
|
155
|
+
await worker.finishAndDisconnect();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
You can also generate a run ID upfront:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { generateRunId } from "@radishbot/sdk";
|
|
162
|
+
const runId = generateRunId();
|
|
163
|
+
// pass to multiple services
|
|
164
|
+
const a = await RL(key, { runId });
|
|
165
|
+
const b = await RL(key, { runId });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
In the dashboard, click a run ID badge to filter all flows from that run.
|
|
169
|
+
|
|
142
170
|
## Release Tracking
|
|
143
171
|
|
|
144
172
|
Tag every flow with a version or commit SHA. Errors are tracked per-release, and regressions (errors reappearing after being resolved) are detected automatically.
|
|
@@ -181,6 +209,7 @@ const root = await RL(key, {
|
|
|
181
209
|
defaultTimeout: 100, // seconds, default 100
|
|
182
210
|
release: "v1.0.0", // version or commit SHA
|
|
183
211
|
retention: "30d", // data retention period
|
|
212
|
+
runId: "abc123", // optional, auto-generated if omitted
|
|
184
213
|
});
|
|
185
214
|
```
|
|
186
215
|
|
|
@@ -204,12 +233,16 @@ Actions that exceed their timeout are automatically marked as timed out.
|
|
|
204
233
|
|
|
205
234
|
### `RL(secretKey, options?) → Promise<Flow>`
|
|
206
235
|
|
|
207
|
-
Connect and create a root action. Options: `host`, `dbName`, `defaultTimeout`, `release`, `retention`.
|
|
236
|
+
Connect and create a root action. Options: `host`, `dbName`, `defaultTimeout`, `release`, `retention`, `runId`.
|
|
208
237
|
|
|
209
238
|
### `generateKey() → string`
|
|
210
239
|
|
|
211
240
|
Generate a random secret key (prefix `rl_`).
|
|
212
241
|
|
|
242
|
+
### `generateRunId() → string`
|
|
243
|
+
|
|
244
|
+
Generate a random run ID. Useful when you want to create the ID before calling `RL()`.
|
|
245
|
+
|
|
213
246
|
### `restoreFlow(secretKey, handle, options?) → Promise<Flow>`
|
|
214
247
|
|
|
215
248
|
Restore an action from an exported handle string.
|
|
@@ -228,4 +261,5 @@ Restore an action from an exported handle string.
|
|
|
228
261
|
| `.debug(msg, data?)` | Log at debug level. |
|
|
229
262
|
| `.log(msg, data?, level?)` | Log at any level. |
|
|
230
263
|
| `.exportID()` | Export handle for cross-context restore. |
|
|
264
|
+
| `.runId` | The run ID (read-only). Pass to subprocesses. |
|
|
231
265
|
| `.getId()` | Get server-assigned action ID. |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type LogLevel = "info" | "warn" | "error" | "debug";
|
|
2
2
|
|
|
3
3
|
export declare class Flow {
|
|
4
|
+
readonly runId: string;
|
|
4
5
|
log(message: string, data?: unknown, level?: LogLevel): this;
|
|
5
6
|
info(message: string, data?: unknown): this;
|
|
6
7
|
warn(message: string, data?: unknown): this;
|
|
@@ -24,8 +25,10 @@ export interface RLOptions {
|
|
|
24
25
|
defaultTimeout?: number;
|
|
25
26
|
release?: string;
|
|
26
27
|
retention?: string;
|
|
28
|
+
runId?: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export declare function RL(secretKey: string, options?: RLOptions): Promise<Flow>;
|
|
30
32
|
export declare function restoreFlow(secretKey: string, exportedId: string, options?: RLOptions): Promise<Flow>;
|
|
31
33
|
export declare function generateKey(): string;
|
|
34
|
+
export declare function generateRunId(): string;
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
} from "spacetimedb";
|
|
49
49
|
var create_root_flow_reducer_default = {
|
|
50
50
|
keyHash: __t4.string(),
|
|
51
|
+
runId: __t4.string(),
|
|
51
52
|
timeoutSeconds: __t4.u64(),
|
|
52
53
|
exportToken: __t4.string(),
|
|
53
54
|
release: __t4.string()
|
|
@@ -165,6 +166,7 @@ import {
|
|
|
165
166
|
var flow_table_default = __t14.row({
|
|
166
167
|
id: __t14.u64().primaryKey(),
|
|
167
168
|
keyHash: __t14.string().name("key_hash"),
|
|
169
|
+
runId: __t14.string().name("run_id"),
|
|
168
170
|
parentFlowId: __t14.u64().name("parent_flow_id"),
|
|
169
171
|
name: __t14.string(),
|
|
170
172
|
path: __t14.string(),
|
|
@@ -454,13 +456,15 @@ class Flow {
|
|
|
454
456
|
_name;
|
|
455
457
|
_timeoutSeconds;
|
|
456
458
|
_release;
|
|
457
|
-
|
|
459
|
+
_runId;
|
|
460
|
+
constructor(sdk, keyHash, parentId, name, timeoutSeconds, release = "", runId = "") {
|
|
458
461
|
this._sdk = sdk;
|
|
459
462
|
this._keyHash = keyHash;
|
|
460
463
|
this._parentId = parentId;
|
|
461
464
|
this._name = name;
|
|
462
465
|
this._timeoutSeconds = !timeoutSeconds || timeoutSeconds === Infinity ? 0n : BigInt(timeoutSeconds);
|
|
463
466
|
this._release = release;
|
|
467
|
+
this._runId = runId;
|
|
464
468
|
this._exportToken = generateToken();
|
|
465
469
|
this._ready = new Promise((resolve) => {
|
|
466
470
|
this._resolveReady = resolve;
|
|
@@ -472,7 +476,13 @@ class Flow {
|
|
|
472
476
|
const keyHash = this._keyHash;
|
|
473
477
|
const timeoutSeconds = this._timeoutSeconds;
|
|
474
478
|
if (this._parentId === 0n) {
|
|
475
|
-
const id = await this._sdk.createFlowAndResolveId(() => conn.reducers.createRootFlow({
|
|
479
|
+
const id = await this._sdk.createFlowAndResolveId(() => conn.reducers.createRootFlow({
|
|
480
|
+
keyHash,
|
|
481
|
+
runId: this._runId,
|
|
482
|
+
timeoutSeconds,
|
|
483
|
+
exportToken,
|
|
484
|
+
release: this._release
|
|
485
|
+
}), exportToken);
|
|
476
486
|
this._id = id;
|
|
477
487
|
} else {
|
|
478
488
|
const parentFlowId = this._parentId;
|
|
@@ -521,7 +531,7 @@ class Flow {
|
|
|
521
531
|
return this.log(message, data, "debug");
|
|
522
532
|
}
|
|
523
533
|
action(name, timeoutSeconds = 100) {
|
|
524
|
-
const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release);
|
|
534
|
+
const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release, this._runId);
|
|
525
535
|
this._ready.then(() => {
|
|
526
536
|
child._parentId = this._id;
|
|
527
537
|
child._create();
|
|
@@ -574,12 +584,16 @@ class Flow {
|
|
|
574
584
|
errorMessage
|
|
575
585
|
});
|
|
576
586
|
}
|
|
587
|
+
get runId() {
|
|
588
|
+
return this._runId;
|
|
589
|
+
}
|
|
577
590
|
async exportID() {
|
|
578
591
|
await this._ready;
|
|
579
592
|
return JSON.stringify({
|
|
580
593
|
flowId: this._id.toString(),
|
|
581
594
|
exportToken: this._exportToken,
|
|
582
|
-
keyHash: this._keyHash
|
|
595
|
+
keyHash: this._keyHash,
|
|
596
|
+
runId: this._runId
|
|
583
597
|
});
|
|
584
598
|
}
|
|
585
599
|
async getId() {
|
|
@@ -639,7 +653,8 @@ async function RL(secretKey, options = {}) {
|
|
|
639
653
|
label = "",
|
|
640
654
|
defaultTimeout = 100,
|
|
641
655
|
release = "",
|
|
642
|
-
retention = "30d"
|
|
656
|
+
retention = "30d",
|
|
657
|
+
runId = generateRunId()
|
|
643
658
|
} = options;
|
|
644
659
|
const retentionDays = parseRetention(retention);
|
|
645
660
|
const keyHash = await hashKey(secretKey);
|
|
@@ -656,7 +671,7 @@ async function RL(secretKey, options = {}) {
|
|
|
656
671
|
try {
|
|
657
672
|
sdk.conn.reducers.checkTimeouts({ keyHash });
|
|
658
673
|
} catch {}
|
|
659
|
-
const root = new Flow(sdk, keyHash, 0n, "/", 0, release);
|
|
674
|
+
const root = new Flow(sdk, keyHash, 0n, "/", 0, release, runId);
|
|
660
675
|
root._create();
|
|
661
676
|
return root;
|
|
662
677
|
}
|
|
@@ -669,7 +684,7 @@ async function restoreFlow(secretKey, exportedId, options = {}) {
|
|
|
669
684
|
}
|
|
670
685
|
const sdk = new SdkConnection(host, dbName);
|
|
671
686
|
await sdk.connect();
|
|
672
|
-
const flow = new Flow(sdk, keyHash, 0n, "restored", 100);
|
|
687
|
+
const flow = new Flow(sdk, keyHash, 0n, "restored", 100, "", parsed.runId || "");
|
|
673
688
|
flow._id = BigInt(parsed.flowId);
|
|
674
689
|
flow._exportToken = parsed.exportToken;
|
|
675
690
|
flow._resolveReady();
|
|
@@ -680,8 +695,14 @@ function generateKey() {
|
|
|
680
695
|
crypto.getRandomValues(bytes);
|
|
681
696
|
return "rl_" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
682
697
|
}
|
|
698
|
+
function generateRunId() {
|
|
699
|
+
const bytes = new Uint8Array(8);
|
|
700
|
+
crypto.getRandomValues(bytes);
|
|
701
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
702
|
+
}
|
|
683
703
|
export {
|
|
684
704
|
restoreFlow,
|
|
705
|
+
generateRunId,
|
|
685
706
|
generateKey,
|
|
686
707
|
RL,
|
|
687
708
|
Flow
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -185,19 +185,27 @@ function cmdList(flows: any[]) {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
const hasRelease = roots.some((f) => f.release && f.release !== "");
|
|
188
|
+
const hasRunId = roots.some((f) => f.runId && f.runId !== "");
|
|
188
189
|
|
|
189
190
|
console.log(`${c.bold}Flows${c.reset} ${c.dim}(${roots.length} total)${c.reset}\n`);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
191
|
+
const cols = [
|
|
192
|
+
"ID".padEnd(8),
|
|
193
|
+
"STATUS".padEnd(10),
|
|
194
|
+
...(hasRunId ? ["RUN".padEnd(18)] : []),
|
|
195
|
+
...(hasRelease ? ["RELEASE".padEnd(10)] : []),
|
|
196
|
+
"DURATION".padEnd(10),
|
|
197
|
+
"CREATED",
|
|
198
|
+
];
|
|
199
|
+
const divs = [
|
|
200
|
+
"─".repeat(8),
|
|
201
|
+
"─".repeat(10),
|
|
202
|
+
...(hasRunId ? ["─".repeat(18)] : []),
|
|
203
|
+
...(hasRelease ? ["─".repeat(10)] : []),
|
|
204
|
+
"─".repeat(10),
|
|
205
|
+
"─".repeat(24),
|
|
206
|
+
];
|
|
207
|
+
console.log(`${c.dim} ${cols.join(" ")}${c.reset}`);
|
|
208
|
+
console.log(`${c.dim} ${divs.join(" ")}${c.reset}`);
|
|
201
209
|
|
|
202
210
|
for (const f of roots.slice(0, 30)) {
|
|
203
211
|
const id = f.id.toString().padEnd(8);
|
|
@@ -205,8 +213,11 @@ function cmdList(flows: any[]) {
|
|
|
205
213
|
const status = padRight(f.status, 10);
|
|
206
214
|
const dur = f.finishedAt !== 0n ? padRight(fmtDuration(f.createdAt, f.finishedAt), 10) : padRight("—", 10);
|
|
207
215
|
const time = fmtDateTime(f.createdAt);
|
|
216
|
+
const runIdCol = hasRunId ? padRight(f.runId ? f.runId.slice(0, 16) : "—", 18) : "";
|
|
208
217
|
const release = hasRelease ? padRight(f.release || "—", 10) + " " : "";
|
|
209
|
-
console.log(
|
|
218
|
+
console.log(
|
|
219
|
+
` ${c.dim}${id}${c.reset} ${sc}${status}${c.reset} ${hasRunId ? runIdCol + " " : ""}${release}${dur} ${c.dim}${time}${c.reset}`,
|
|
220
|
+
);
|
|
210
221
|
}
|
|
211
222
|
|
|
212
223
|
if (roots.length > 30) {
|
|
@@ -225,6 +236,9 @@ function cmdShow(flowId: bigint, allFlows: any[], allLogs: any[]) {
|
|
|
225
236
|
const sc = statusColor(flow.status);
|
|
226
237
|
const releaseTag = flow.release ? ` ${c.dim}(${flow.release})${c.reset}` : "";
|
|
227
238
|
console.log(`${c.bold}Flow #${flow.id}${c.reset} ${sc}${flow.status}${c.reset}${releaseTag}`);
|
|
239
|
+
if (flow.runId) {
|
|
240
|
+
console.log(`${c.dim}Run: ${flow.runId}${c.reset}`);
|
|
241
|
+
}
|
|
228
242
|
console.log(`${c.dim}Created: ${fmtDateTime(flow.createdAt)}${c.reset}`);
|
|
229
243
|
if (flow.finishedAt !== 0n) {
|
|
230
244
|
console.log(`${c.dim}Duration: ${fmtDuration(flow.createdAt, flow.finishedAt)}${c.reset}`);
|
package/src/index.ts
CHANGED
|
@@ -97,6 +97,7 @@ export class Flow {
|
|
|
97
97
|
private _name: string;
|
|
98
98
|
private _timeoutSeconds: bigint;
|
|
99
99
|
private _release: string;
|
|
100
|
+
private _runId: string;
|
|
100
101
|
|
|
101
102
|
/** @internal */
|
|
102
103
|
constructor(
|
|
@@ -106,6 +107,7 @@ export class Flow {
|
|
|
106
107
|
name: string,
|
|
107
108
|
timeoutSeconds: number,
|
|
108
109
|
release: string = "",
|
|
110
|
+
runId: string = "",
|
|
109
111
|
) {
|
|
110
112
|
this._sdk = sdk;
|
|
111
113
|
this._keyHash = keyHash;
|
|
@@ -113,6 +115,7 @@ export class Flow {
|
|
|
113
115
|
this._name = name;
|
|
114
116
|
this._timeoutSeconds = !timeoutSeconds || timeoutSeconds === Infinity ? 0n : BigInt(timeoutSeconds);
|
|
115
117
|
this._release = release;
|
|
118
|
+
this._runId = runId;
|
|
116
119
|
this._exportToken = generateToken();
|
|
117
120
|
this._ready = new Promise<void>((resolve) => {
|
|
118
121
|
this._resolveReady = resolve;
|
|
@@ -128,7 +131,14 @@ export class Flow {
|
|
|
128
131
|
|
|
129
132
|
if (this._parentId === 0n) {
|
|
130
133
|
const id = await this._sdk.createFlowAndResolveId(
|
|
131
|
-
() =>
|
|
134
|
+
() =>
|
|
135
|
+
conn.reducers.createRootFlow({
|
|
136
|
+
keyHash,
|
|
137
|
+
runId: this._runId,
|
|
138
|
+
timeoutSeconds,
|
|
139
|
+
exportToken,
|
|
140
|
+
release: this._release,
|
|
141
|
+
}),
|
|
132
142
|
exportToken,
|
|
133
143
|
);
|
|
134
144
|
this._id = id;
|
|
@@ -191,7 +201,7 @@ export class Flow {
|
|
|
191
201
|
|
|
192
202
|
/** Create a sub-action. Returns immediately — creation runs in background. */
|
|
193
203
|
action(name: string, timeoutSeconds = 100): Flow {
|
|
194
|
-
const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release);
|
|
204
|
+
const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release, this._runId);
|
|
195
205
|
this._ready.then(() => {
|
|
196
206
|
(child as any)._parentId = this._id!;
|
|
197
207
|
child._create();
|
|
@@ -252,6 +262,11 @@ export class Flow {
|
|
|
252
262
|
});
|
|
253
263
|
}
|
|
254
264
|
|
|
265
|
+
/** Get the run ID for this flow (pass to subprocesses via env, args, etc.) */
|
|
266
|
+
get runId(): string {
|
|
267
|
+
return this._runId;
|
|
268
|
+
}
|
|
269
|
+
|
|
255
270
|
/** Export this flow's handle for restoration in another context */
|
|
256
271
|
async exportID(): Promise<string> {
|
|
257
272
|
await this._ready;
|
|
@@ -259,6 +274,7 @@ export class Flow {
|
|
|
259
274
|
flowId: this._id!.toString(),
|
|
260
275
|
exportToken: this._exportToken,
|
|
261
276
|
keyHash: this._keyHash,
|
|
277
|
+
runId: this._runId,
|
|
262
278
|
});
|
|
263
279
|
}
|
|
264
280
|
|
|
@@ -323,6 +339,8 @@ export interface RLOptions {
|
|
|
323
339
|
defaultTimeout?: number;
|
|
324
340
|
release?: string;
|
|
325
341
|
retention?: string;
|
|
342
|
+
/** Run ID — groups flows across processes. Auto-generated if not provided. */
|
|
343
|
+
runId?: string;
|
|
326
344
|
}
|
|
327
345
|
|
|
328
346
|
function parseRetention(retention: string): number {
|
|
@@ -360,6 +378,7 @@ export async function RL(secretKey: string, options: RLOptions = {}): Promise<Fl
|
|
|
360
378
|
defaultTimeout = 100,
|
|
361
379
|
release = "",
|
|
362
380
|
retention = "30d",
|
|
381
|
+
runId = generateRunId(),
|
|
363
382
|
} = options;
|
|
364
383
|
|
|
365
384
|
const retentionDays = parseRetention(retention);
|
|
@@ -387,7 +406,7 @@ export async function RL(secretKey: string, options: RLOptions = {}): Promise<Fl
|
|
|
387
406
|
}
|
|
388
407
|
|
|
389
408
|
// Root flow — creation runs in background, logs queue until ready
|
|
390
|
-
const root = new Flow(sdk, keyHash, 0n, "/", 0, release);
|
|
409
|
+
const root = new Flow(sdk, keyHash, 0n, "/", 0, release, runId);
|
|
391
410
|
root._create(); // fire-and-forget — resolves _ready when server assigns ID
|
|
392
411
|
return root;
|
|
393
412
|
}
|
|
@@ -405,7 +424,7 @@ export async function restoreFlow(secretKey: string, exportedId: string, options
|
|
|
405
424
|
const sdk = new SdkConnection(host, dbName);
|
|
406
425
|
await sdk.connect();
|
|
407
426
|
|
|
408
|
-
const flow = new Flow(sdk, keyHash, 0n, "restored", 100);
|
|
427
|
+
const flow = new Flow(sdk, keyHash, 0n, "restored", 100, "", parsed.runId || "");
|
|
409
428
|
(flow as any)._id = BigInt(parsed.flowId);
|
|
410
429
|
(flow as any)._exportToken = parsed.exportToken;
|
|
411
430
|
(flow as any)._resolveReady();
|
|
@@ -423,3 +442,12 @@ export function generateKey(): string {
|
|
|
423
442
|
.join("")
|
|
424
443
|
);
|
|
425
444
|
}
|
|
445
|
+
|
|
446
|
+
/** Generate a random run ID (pass to subprocesses to link flows) */
|
|
447
|
+
export function generateRunId(): string {
|
|
448
|
+
const bytes = new Uint8Array(8);
|
|
449
|
+
crypto.getRandomValues(bytes);
|
|
450
|
+
return Array.from(bytes)
|
|
451
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
452
|
+
.join("");
|
|
453
|
+
}
|