@stigmer/runner 3.15.3-dev.20260916211208 → 3.16.1-dev.20260917224426
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 +1 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/generate-session-subject.d.ts +25 -4
- package/dist/activities/generate-session-subject.js +17 -7
- package/dist/activities/generate-session-subject.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +17 -6
- package/dist/client/stigmer-client.js +47 -14
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/interceptors/run-credential-activity.d.ts +28 -0
- package/dist/interceptors/run-credential-activity.js +56 -0
- package/dist/interceptors/run-credential-activity.js.map +1 -0
- package/dist/runner-manager.js +9 -3
- package/dist/runner-manager.js.map +1 -1
- package/dist/shared/activity-input.d.ts +9 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.js.map +1 -1
- package/dist/shared/channel-attachment.d.ts +8 -5
- package/dist/shared/channel-attachment.js +10 -6
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/shared/filereview/progress.d.ts +13 -0
- package/dist/shared/filereview/progress.js +13 -0
- package/dist/shared/filereview/progress.js.map +1 -1
- package/dist/shared/memory-attachment.d.ts +16 -9
- package/dist/shared/memory-attachment.js +18 -10
- package/dist/shared/memory-attachment.js.map +1 -1
- package/dist/shared/run-credential-store.d.ts +32 -0
- package/dist/shared/run-credential-store.js +39 -0
- package/dist/shared/run-credential-store.js.map +1 -0
- package/dist/shared/run-credential.d.ts +61 -0
- package/dist/shared/run-credential.js +68 -0
- package/dist/shared/run-credential.js.map +1 -0
- package/dist/shared/synthesized-attachment.d.ts +30 -3
- package/dist/shared/synthesized-attachment.js +26 -0
- package/dist/shared/synthesized-attachment.js.map +1 -1
- package/dist/worker.js +10 -3
- package/dist/worker.js.map +1 -1
- package/dist/workflow-source.d.ts +10 -0
- package/dist/workflow-source.js +12 -0
- package/dist/workflow-source.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +15 -0
- package/dist/workflows/execute-from-execution.js +11 -0
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/dist/workflows/index.d.ts +5 -3
- package/dist/workflows/index.js +5 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/interceptors/run-credential.d.ts +34 -0
- package/dist/workflows/interceptors/run-credential.js +71 -0
- package/dist/workflows/interceptors/run-credential.js.map +1 -0
- package/package.json +4 -4
- package/src/__tests__/run-credential-e2e.test.ts +233 -0
- package/src/__tests__/run-credential-registration.test.ts +80 -0
- package/src/activities/__tests__/generate-session-subject-wire.test.ts +119 -0
- package/src/activities/generate-session-subject.ts +29 -4
- package/src/client/__tests__/stigmer-client.test.ts +92 -0
- package/src/client/stigmer-client.ts +48 -14
- package/src/config.ts +4 -1
- package/src/interceptors/__tests__/run-credential-activity.test.ts +133 -0
- package/src/interceptors/run-credential-activity.ts +67 -0
- package/src/runner-manager.ts +13 -3
- package/src/shared/__tests__/channel-attachment.test.ts +12 -0
- package/src/shared/__tests__/memory-attachment.test.ts +17 -0
- package/src/shared/__tests__/run-credential.test.ts +111 -0
- package/src/shared/activity-input.ts +21 -0
- package/src/shared/channel-attachment.ts +14 -6
- package/src/shared/filereview/progress.ts +13 -0
- package/src/shared/memory-attachment.ts +22 -10
- package/src/shared/run-credential-store.ts +45 -0
- package/src/shared/run-credential.ts +70 -0
- package/src/shared/synthesized-attachment.ts +38 -3
- package/src/worker.ts +15 -3
- package/src/workflow-source.ts +15 -0
- package/src/workflows/__tests__/run-credential-interceptor.test.ts +178 -0
- package/src/workflows/execute-from-execution.ts +17 -0
- package/src/workflows/index.ts +5 -3
- package/src/workflows/interceptors/run-credential.ts +101 -0
|
@@ -26,9 +26,14 @@ export interface SynthesizedAttachmentOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
bridgeEndpoint: string | null;
|
|
28
28
|
/**
|
|
29
|
-
* The
|
|
30
|
-
*
|
|
31
|
-
* token
|
|
29
|
+
* The credential of the RUN this attachment serves — the run's own
|
|
30
|
+
* credential the server minted at dispatch (the OSS shape), or the
|
|
31
|
+
* session-scoped sandbox token a cloud runner holds. Both connection
|
|
32
|
+
* shapes present it: as the Bearer on the bridge's per-request path,
|
|
33
|
+
* and as the stdio child's startup credential (`stdioCredentialEnv`),
|
|
34
|
+
* so what the child writes on the person's behalf is stamped as that
|
|
35
|
+
* person and not refused as nobody's on a server that signs people in.
|
|
36
|
+
* Null presents nothing (a run whose dispatch carried no credential).
|
|
32
37
|
*/
|
|
33
38
|
credential: string | null;
|
|
34
39
|
/**
|
|
@@ -37,6 +42,28 @@ export interface SynthesizedAttachmentOptions {
|
|
|
37
42
|
*/
|
|
38
43
|
backendEndpoint: string;
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The mcp-server's startup-credential variable — the ONE name the stdio
|
|
47
|
+
* child reads its bearer from (mcp-server/src/config.ts `STIGMER_API_KEY`;
|
|
48
|
+
* the value is any bearer the server honours, an `stk_` key for an IDE
|
|
49
|
+
* client, the run's credential here). Cross-repo string, pinned on both
|
|
50
|
+
* sides (the TOOL_CALL_LIMIT precedent).
|
|
51
|
+
*/
|
|
52
|
+
export declare const STDIO_CREDENTIAL_ENV = "STIGMER_API_KEY";
|
|
53
|
+
/**
|
|
54
|
+
* The stdio child's startup-credential entry: `{ STIGMER_API_KEY }` when the
|
|
55
|
+
* attachment holds a credential, nothing when it holds none — so a
|
|
56
|
+
* credential-less run's child environment is byte-for-byte today's, and
|
|
57
|
+
* the two stdio shapes cannot disagree on the carrier.
|
|
58
|
+
*
|
|
59
|
+
* The exposure, stated once: the credential sits in a first-party child's
|
|
60
|
+
* process environment on the runner host, readable by the same OS user.
|
|
61
|
+
* That is the runner's own trust boundary (the runner process holds the
|
|
62
|
+
* same credential), and the child is `stigmer mcp-server`, never a
|
|
63
|
+
* third-party server — those receive only their declared env (oss#256,
|
|
64
|
+
* mcp-manager.ts). The alternative was the operator's API key.
|
|
65
|
+
*/
|
|
66
|
+
export declare function stdioCredentialEnv(credential: string | null): Record<string, string>;
|
|
40
67
|
/**
|
|
41
68
|
* Inject a synthesized attachment into a resolved server list — AFTER
|
|
42
69
|
* resolve + backfill (see the module header). A user server shadowing
|
|
@@ -13,6 +13,32 @@
|
|
|
13
13
|
* tightener structurally unable to touch it. Callers must still inject
|
|
14
14
|
* AFTER resolve + backfill; every harness call site does.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The mcp-server's startup-credential variable — the ONE name the stdio
|
|
18
|
+
* child reads its bearer from (mcp-server/src/config.ts `STIGMER_API_KEY`;
|
|
19
|
+
* the value is any bearer the server honours, an `stk_` key for an IDE
|
|
20
|
+
* client, the run's credential here). Cross-repo string, pinned on both
|
|
21
|
+
* sides (the TOOL_CALL_LIMIT precedent).
|
|
22
|
+
*/
|
|
23
|
+
export const STDIO_CREDENTIAL_ENV = "STIGMER_API_KEY";
|
|
24
|
+
/**
|
|
25
|
+
* The stdio child's startup-credential entry: `{ STIGMER_API_KEY }` when the
|
|
26
|
+
* attachment holds a credential, nothing when it holds none — so a
|
|
27
|
+
* credential-less run's child environment is byte-for-byte today's, and
|
|
28
|
+
* the two stdio shapes cannot disagree on the carrier.
|
|
29
|
+
*
|
|
30
|
+
* The exposure, stated once: the credential sits in a first-party child's
|
|
31
|
+
* process environment on the runner host, readable by the same OS user.
|
|
32
|
+
* That is the runner's own trust boundary (the runner process holds the
|
|
33
|
+
* same credential), and the child is `stigmer mcp-server`, never a
|
|
34
|
+
* third-party server — those receive only their declared env (oss#256,
|
|
35
|
+
* mcp-manager.ts). The alternative was the operator's API key.
|
|
36
|
+
*/
|
|
37
|
+
export function stdioCredentialEnv(credential) {
|
|
38
|
+
return credential !== null && credential !== ""
|
|
39
|
+
? { [STDIO_CREDENTIAL_ENV]: credential }
|
|
40
|
+
: {};
|
|
41
|
+
}
|
|
16
42
|
/**
|
|
17
43
|
* Inject a synthesized attachment into a resolved server list — AFTER
|
|
18
44
|
* resolve + backfill (see the module header). A user server shadowing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesized-attachment.js","sourceRoot":"","sources":["../../src/shared/synthesized-attachment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"synthesized-attachment.js","sourceRoot":"","sources":["../../src/shared/synthesized-attachment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiCH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAyB;IAEzB,OAAO,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,EAAE;QAC7C,CAAC,CAAC,EAAE,CAAC,oBAAoB,CAAC,EAAE,UAAU,EAAE;QACxC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,eAAoC,EACpC,UAA6B,EAC7B,KAAa;IAEb,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC;IACzE,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,oBAAoB,UAAU,CAAC,IAAI,yBAAyB,KAAK,GAAG;YACpE,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,CAAC;QAC5D,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/worker.js
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { createRequire } from "node:module";
|
|
17
17
|
import { NativeConnection, Worker } from "@temporalio/worker";
|
|
18
|
-
import { resolveWorkflowSource, OTEL_WORKFLOW_INTERCEPTOR_MODULE } from "./workflow-source.js";
|
|
18
|
+
import { resolveWorkflowSource, runCredentialWorkflowInterceptorModule, OTEL_WORKFLOW_INTERCEPTOR_MODULE, } from "./workflow-source.js";
|
|
19
|
+
import { runCredentialActivityInterceptor } from "./interceptors/run-credential-activity.js";
|
|
19
20
|
export async function startWorker(opts) {
|
|
20
21
|
const { config, activities, payloadCodecs } = opts;
|
|
21
22
|
const connection = await NativeConnection.connect({
|
|
@@ -23,9 +24,15 @@ export async function startWorker(opts) {
|
|
|
23
24
|
});
|
|
24
25
|
const { createWorkflowMetricsSinks } = await import("./interceptors/workflow-metrics-sink.js");
|
|
25
26
|
const workflowSource = resolveWorkflowSource();
|
|
26
|
-
|
|
27
|
+
// The run credential's two ends (shared/run-credential.ts): the workflow
|
|
28
|
+
// side lifts a run's credential onto every activity it schedules, the
|
|
29
|
+
// activity side enters it for the client to present. Always on; inert
|
|
30
|
+
// without a credential. Workflow-side modules must live INSIDE the
|
|
31
|
+
// workflow bundle — a pre-built bundle has it baked in
|
|
32
|
+
// (scripts/bundle-slim.mjs); only the runtime path registers it here.
|
|
33
|
+
const activityInterceptors = [runCredentialActivityInterceptor];
|
|
27
34
|
let sinks = { ...createWorkflowMetricsSinks() };
|
|
28
|
-
const workflowInterceptorModules = [];
|
|
35
|
+
const workflowInterceptorModules = workflowSource.kind === "runtime" ? [runCredentialWorkflowInterceptorModule()] : [];
|
|
29
36
|
if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
30
37
|
const { OpenTelemetryActivityInboundInterceptor, makeWorkflowExporter, } = await import("@temporalio/interceptors-opentelemetry");
|
|
31
38
|
const { OTLPTraceExporter } = await import("@opentelemetry/exporter-trace-otlp-grpc");
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAwD,MAAM,oBAAoB,CAAC;AAGpH,OAAO,
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,4 +35,14 @@ export type WorkflowSource = {
|
|
|
35
35
|
* with the workflow bundle.
|
|
36
36
|
*/
|
|
37
37
|
export declare const OTEL_WORKFLOW_INTERCEPTOR_MODULE = "@temporalio/interceptors-opentelemetry/lib/workflow-interceptors";
|
|
38
|
+
/**
|
|
39
|
+
* Path of the runner's own workflow interceptor — the one that carries a
|
|
40
|
+
* run's credential from the workflow input to every activity it schedules
|
|
41
|
+
* (`workflows/interceptors/run-credential.ts`). Always registered, unlike
|
|
42
|
+
* the OTel module: it is inert without a credential and load-bearing with
|
|
43
|
+
* one. Resolved beside this module so the tsc dist and the slim artifact
|
|
44
|
+
* both find their own compiled copy; the build script names the source
|
|
45
|
+
* file itself.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runCredentialWorkflowInterceptorModule(): string;
|
|
38
48
|
export declare function resolveWorkflowSource(): WorkflowSource;
|
package/dist/workflow-source.js
CHANGED
|
@@ -28,6 +28,18 @@ import { fileURLToPath } from "node:url";
|
|
|
28
28
|
* with the workflow bundle.
|
|
29
29
|
*/
|
|
30
30
|
export const OTEL_WORKFLOW_INTERCEPTOR_MODULE = "@temporalio/interceptors-opentelemetry/lib/workflow-interceptors";
|
|
31
|
+
/**
|
|
32
|
+
* Path of the runner's own workflow interceptor — the one that carries a
|
|
33
|
+
* run's credential from the workflow input to every activity it schedules
|
|
34
|
+
* (`workflows/interceptors/run-credential.ts`). Always registered, unlike
|
|
35
|
+
* the OTel module: it is inert without a credential and load-bearing with
|
|
36
|
+
* one. Resolved beside this module so the tsc dist and the slim artifact
|
|
37
|
+
* both find their own compiled copy; the build script names the source
|
|
38
|
+
* file itself.
|
|
39
|
+
*/
|
|
40
|
+
export function runCredentialWorkflowInterceptorModule() {
|
|
41
|
+
return fileURLToPath(new URL("./workflows/interceptors/run-credential.js", import.meta.url));
|
|
42
|
+
}
|
|
31
43
|
export function resolveWorkflowSource() {
|
|
32
44
|
const explicit = process.env.STIGMER_WORKFLOW_BUNDLE;
|
|
33
45
|
if (explicit) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-source.js","sourceRoot":"","sources":["../src/workflow-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAczC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,kEAAkE,CAAC;AAErE,MAAM,UAAU,qBAAqB;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACrD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,0DAA0D,QAAQ,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;IAChE,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS;QACf,aAAa,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC/E,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"workflow-source.js","sourceRoot":"","sources":["../src/workflow-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAczC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,kEAAkE,CAAC;AAErE;;;;;;;;GAQG;AACH,MAAM,UAAU,sCAAsC;IACpD,OAAO,aAAa,CAClB,IAAI,GAAG,CAAC,4CAA4C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CACvE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACrD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,0DAA0D,QAAQ,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;IAChE,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS;QACf,aAAa,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC/E,CAAC;AACJ,CAAC"}
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
* SANDBOX RULES: This file runs inside the Temporal deterministic V8
|
|
21
21
|
* isolate. No Node.js built-ins, no non-deterministic operations.
|
|
22
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* The Temporal workflow type this function is exported as in `index.ts` — the
|
|
25
|
+
* server's `CHILD_WORKFLOW_TYPE`, byte-pinned on both sides. Named here so
|
|
26
|
+
* the run-credential interceptor can gate on it without a second literal; a
|
|
27
|
+
* test pins it to the barrel's export alias (an ES2022 export alias must be a
|
|
28
|
+
* literal, so the barrel cannot import this).
|
|
29
|
+
*/
|
|
30
|
+
export declare const EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE = "stigmer/workflow/execute-from-execution";
|
|
23
31
|
export interface ExecuteFromExecutionInput {
|
|
24
32
|
execution_id: string;
|
|
25
33
|
workflow_instance_id: string;
|
|
@@ -28,5 +36,12 @@ export interface ExecuteFromExecutionInput {
|
|
|
28
36
|
callback_token?: Uint8Array | null;
|
|
29
37
|
invoker_identity_account_id?: string;
|
|
30
38
|
recovery_mode?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The run credential the server minted for this execution at dispatch —
|
|
41
|
+
* absent from an older server's dispatch. This workflow never reads it: the
|
|
42
|
+
* interceptor in `interceptors/run-credential.ts` lifts it onto every
|
|
43
|
+
* activity this workflow schedules, as a header (`shared/run-credential.ts`).
|
|
44
|
+
*/
|
|
45
|
+
execution_context_token?: string;
|
|
31
46
|
}
|
|
32
47
|
export declare function executeFromExecution(input: ExecuteFromExecutionInput): Promise<void>;
|
|
@@ -33,6 +33,17 @@ const hydrateProxy = proxyActivities({
|
|
|
33
33
|
},
|
|
34
34
|
});
|
|
35
35
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
// Workflow Input Type (Temporal wire contract with Java/Go orchestrators)
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
/**
|
|
39
|
+
* The Temporal workflow type this function is exported as in `index.ts` — the
|
|
40
|
+
* server's `CHILD_WORKFLOW_TYPE`, byte-pinned on both sides. Named here so
|
|
41
|
+
* the run-credential interceptor can gate on it without a second literal; a
|
|
42
|
+
* test pins it to the barrel's export alias (an ES2022 export alias must be a
|
|
43
|
+
* literal, so the barrel cannot import this).
|
|
44
|
+
*/
|
|
45
|
+
export const EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE = "stigmer/workflow/execute-from-execution";
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
47
|
// Workflow Function
|
|
37
48
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
49
|
export async function executeFromExecution(input) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute-from-execution.js","sourceRoot":"","sources":["../../src/workflows/execute-from-execution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAUjE,MAAM,YAAY,GAAG,eAAe,CAAoB;IACtD,mBAAmB,EAAE,IAAI;IACzB,KAAK,EAAE;QACL,eAAe,EAAE,CAAC;QAClB,eAAe,EAAE,IAAI;QACrB,kBAAkB,EAAE,CAAC;QACrB,eAAe,EAAE,KAAK;KACvB;CACF,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"execute-from-execution.js","sourceRoot":"","sources":["../../src/workflows/execute-from-execution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAUjE,MAAM,YAAY,GAAG,eAAe,CAAoB;IACtD,mBAAmB,EAAE,IAAI;IACzB,KAAK,EAAE;QACL,eAAe,EAAE,CAAC;QAClB,eAAe,EAAE,IAAI;QACrB,kBAAkB,EAAE,CAAC;QACrB,eAAe,EAAE,KAAK;KACvB;CACF,CAAC,CAAC;AAEH,gFAAgF;AAChF,0EAA0E;AAC1E,gFAAgF;AAEhF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAC/C,yCAAyC,CAAC;AAmB5C,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAAgC;IAEhC,MAAM,EAAE,UAAU,EAAE,GAAG,wBAAwB,EAAE,CAAC;IAElD,GAAG,CAAC,IAAI,CAAC,4CAA4C,EAAE;QACrD,WAAW,EAAE,KAAK,CAAC,YAAY;QAC/B,UAAU,EAAE,KAAK,CAAC,WAAW;QAC7B,kBAAkB,EAAE,KAAK,CAAC,oBAAoB;QAC9C,KAAK,EAAE,KAAK,CAAC,MAAM;KACpB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,wBAAwB,CAAC;QAC/D,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,qCAAqC,EAAE;QAC9C,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI;QAC9C,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM;KAC/C,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,sEAAsE;QACtE,qEAAqE;QACrE,mEAAmE;QACnE,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,wEAAwE;QACxE,iEAAiE;QACjE,oEAAoE;QACpE,iEAAiE;QACjE,MAAM,iBAAiB,CAAC,YAAY,EAAE;YACpC,UAAU;YACV,YAAY,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK;SAC3C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,8CAA8C,EAAE;gBACvD,WAAW,EAAE,KAAK,CAAC,YAAY;aAChC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
* us map TypeScript function names to the slash-delimited Temporal
|
|
14
14
|
* workflow type names that the backend expects.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Workflow interceptors are registered at bundle time — not imported
|
|
17
|
+
* here: the runner's own run-credential interceptor
|
|
18
|
+
* (./interceptors/run-credential.ts, always) and OTel's (when the host
|
|
19
|
+
* configures the sink). Pre-built bundles (scripts/bundle-slim.mjs) bake
|
|
20
|
+
* them in; the runtime-bundling fallback passes them to the bundler (see
|
|
19
21
|
* src/workflow-source.ts).
|
|
20
22
|
*/
|
|
21
23
|
export { connectMcpServer as "stigmer/mcp-server/connect", discoverMcpServerLegacy as "stigmer/mcp-server/discover", } from "./connect-mcp-server.js";
|
package/dist/workflows/index.js
CHANGED
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
* us map TypeScript function names to the slash-delimited Temporal
|
|
14
14
|
* workflow type names that the backend expects.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Workflow interceptors are registered at bundle time — not imported
|
|
17
|
+
* here: the runner's own run-credential interceptor
|
|
18
|
+
* (./interceptors/run-credential.ts, always) and OTel's (when the host
|
|
19
|
+
* configures the sink). Pre-built bundles (scripts/bundle-slim.mjs) bake
|
|
20
|
+
* them in; the runtime-bundling fallback passes them to the bundler (see
|
|
19
21
|
* src/workflow-source.ts).
|
|
20
22
|
*/
|
|
21
23
|
export { connectMcpServer as "stigmer/mcp-server/connect", discoverMcpServerLegacy as "stigmer/mcp-server/discover", } from "./connect-mcp-server.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflows/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflows/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,gBAAgB,IAAI,4BAA4B,EAChD,uBAAuB,IAAI,6BAA6B,GACzD,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,yBAAyB,IAAI,0BAA0B,GACxD,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,oBAAoB,IAAI,yCAAyC,GAClE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-side interceptors that carry a run's credential from the workflow
|
|
3
|
+
* input to every activity the workflow schedules — as a Temporal activity
|
|
4
|
+
* header, the SDK's channel for context that crosses the isolate boundary
|
|
5
|
+
* without being anybody's argument (`shared/run-credential.ts` has the
|
|
6
|
+
* whole story; `interceptors/run-credential-activity.ts` is the other end).
|
|
7
|
+
*
|
|
8
|
+
* Gated by WORKFLOW TYPE, not by input shape. The connect workflow's input
|
|
9
|
+
* carries the same key for a different token — a clocked decrypt-lane token
|
|
10
|
+
* bound to a synthetic id that no verifier admits as a run — and stamping it
|
|
11
|
+
* onto that lane's activities would present it where it does not belong.
|
|
12
|
+
* The gate is the positive list below: the workflow types whose input
|
|
13
|
+
* carries a RUN credential. One entry today; a test pins each entry to an
|
|
14
|
+
* export of `workflows/index.ts`, so the list cannot name a type that does
|
|
15
|
+
* not exist.
|
|
16
|
+
*
|
|
17
|
+
* Per-run state lives on this factory's closure: the SDK instantiates the
|
|
18
|
+
* factory once per workflow run, which is the documented contract. The
|
|
19
|
+
* header is a pure function of the input, so replay recomputes exactly what
|
|
20
|
+
* was recorded, and a credential-less input stamps nothing — an in-flight
|
|
21
|
+
* run from before the credential existed replays byte for byte.
|
|
22
|
+
*
|
|
23
|
+
* TEMPORAL SANDBOX: this file is bundled into the workflow isolate. No Node
|
|
24
|
+
* built-ins. It is baked into the pre-built bundle by
|
|
25
|
+
* `scripts/bundle-slim.mjs` and passed to the runtime bundler by both worker
|
|
26
|
+
* roots (`src/workflow-source.ts` names it beside the OTel module).
|
|
27
|
+
*/
|
|
28
|
+
import type { WorkflowInterceptorsFactory } from "@temporalio/workflow";
|
|
29
|
+
/**
|
|
30
|
+
* The workflow types whose input carries a run credential. Byte-pinned wire
|
|
31
|
+
* names; each must be an export alias of `workflows/index.ts`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const RUN_WORKFLOW_TYPES: readonly string[];
|
|
34
|
+
export declare const interceptors: WorkflowInterceptorsFactory;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-side interceptors that carry a run's credential from the workflow
|
|
3
|
+
* input to every activity the workflow schedules — as a Temporal activity
|
|
4
|
+
* header, the SDK's channel for context that crosses the isolate boundary
|
|
5
|
+
* without being anybody's argument (`shared/run-credential.ts` has the
|
|
6
|
+
* whole story; `interceptors/run-credential-activity.ts` is the other end).
|
|
7
|
+
*
|
|
8
|
+
* Gated by WORKFLOW TYPE, not by input shape. The connect workflow's input
|
|
9
|
+
* carries the same key for a different token — a clocked decrypt-lane token
|
|
10
|
+
* bound to a synthetic id that no verifier admits as a run — and stamping it
|
|
11
|
+
* onto that lane's activities would present it where it does not belong.
|
|
12
|
+
* The gate is the positive list below: the workflow types whose input
|
|
13
|
+
* carries a RUN credential. One entry today; a test pins each entry to an
|
|
14
|
+
* export of `workflows/index.ts`, so the list cannot name a type that does
|
|
15
|
+
* not exist.
|
|
16
|
+
*
|
|
17
|
+
* Per-run state lives on this factory's closure: the SDK instantiates the
|
|
18
|
+
* factory once per workflow run, which is the documented contract. The
|
|
19
|
+
* header is a pure function of the input, so replay recomputes exactly what
|
|
20
|
+
* was recorded, and a credential-less input stamps nothing — an in-flight
|
|
21
|
+
* run from before the credential existed replays byte for byte.
|
|
22
|
+
*
|
|
23
|
+
* TEMPORAL SANDBOX: this file is bundled into the workflow isolate. No Node
|
|
24
|
+
* built-ins. It is baked into the pre-built bundle by
|
|
25
|
+
* `scripts/bundle-slim.mjs` and passed to the runtime bundler by both worker
|
|
26
|
+
* roots (`src/workflow-source.ts` names it beside the OTel module).
|
|
27
|
+
*/
|
|
28
|
+
import { workflowInfo } from "@temporalio/workflow";
|
|
29
|
+
import { defaultPayloadConverter } from "@temporalio/common";
|
|
30
|
+
import { RUN_CREDENTIAL_HEADER, readRunCredentialFromInput, } from "../../shared/run-credential.js";
|
|
31
|
+
import { EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE } from "../execute-from-execution.js";
|
|
32
|
+
/**
|
|
33
|
+
* The workflow types whose input carries a run credential. Byte-pinned wire
|
|
34
|
+
* names; each must be an export alias of `workflows/index.ts`.
|
|
35
|
+
*/
|
|
36
|
+
export const RUN_WORKFLOW_TYPES = [
|
|
37
|
+
EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE,
|
|
38
|
+
];
|
|
39
|
+
export const interceptors = () => {
|
|
40
|
+
let credential;
|
|
41
|
+
const inbound = {
|
|
42
|
+
async execute(input, next) {
|
|
43
|
+
if (RUN_WORKFLOW_TYPES.includes(workflowInfo().workflowType)) {
|
|
44
|
+
credential = readRunCredentialFromInput(input.args[0]);
|
|
45
|
+
}
|
|
46
|
+
return next(input);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const outbound = {
|
|
50
|
+
async scheduleActivity(input, next) {
|
|
51
|
+
return next(stamped(input));
|
|
52
|
+
},
|
|
53
|
+
async scheduleLocalActivity(input, next) {
|
|
54
|
+
return next(stamped(input));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
function stamped(input) {
|
|
58
|
+
if (credential === undefined) {
|
|
59
|
+
return input;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
...input,
|
|
63
|
+
headers: {
|
|
64
|
+
...input.headers,
|
|
65
|
+
[RUN_CREDENTIAL_HEADER]: defaultPayloadConverter.toPayload(credential),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { inbound: [inbound], outbound: [outbound] };
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=run-credential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-credential.js","sourceRoot":"","sources":["../../../src/workflows/interceptors/run-credential.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAUpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oCAAoC,EAAE,MAAM,8BAA8B,CAAC;AAEpF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,oCAAoC;CACrC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAgC,GAAG,EAAE;IAC5D,IAAI,UAA8B,CAAC;IAEnC,MAAM,OAAO,GAAoC;QAC/C,KAAK,CAAC,OAAO,CACX,KAA2B,EAC3B,IAAsD;YAEtD,IAAI,kBAAkB,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC7D,UAAU,GAAG,0BAA0B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;KACF,CAAC;IAEF,MAAM,QAAQ,GAAqC;QACjD,KAAK,CAAC,gBAAgB,CACpB,KAAoB,EACpB,IAAgE;YAEhE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,KAAK,CAAC,qBAAqB,CACzB,KAAyB,EACzB,IAAqE;YAErE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;IAEF,SAAS,OAAO,CACd,KAAQ;QAER,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO;YACL,GAAG,KAAK;YACR,OAAO,EAAE;gBACP,GAAG,KAAK,CAAC,OAAO;gBAChB,CAAC,qBAAqB,CAAC,EAAE,uBAAuB,CAAC,SAAS,CAAC,UAAU,CAAC;aACvE;SACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;AACtD,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/runner",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.1-dev.20260917224426",
|
|
4
4
|
"description": "Embeddable Temporal worker for the Stigmer AI agent platform — handles agent execution, workflow orchestration, and MCP server management",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
105
105
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
106
106
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
107
|
-
"@stigmer/protos": "3.
|
|
108
|
-
"@stigmer/temporal-codecs": "3.
|
|
109
|
-
"@stigmer/zip-structure": "3.
|
|
107
|
+
"@stigmer/protos": "3.16.1-dev.20260917224426",
|
|
108
|
+
"@stigmer/temporal-codecs": "3.16.1-dev.20260917224426",
|
|
109
|
+
"@stigmer/zip-structure": "3.16.1-dev.20260917224426",
|
|
110
110
|
"@temporalio/activity": "1.16.2",
|
|
111
111
|
"@temporalio/common": "1.16.2",
|
|
112
112
|
"@temporalio/interceptors-opentelemetry": "1.16.2",
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run credential end to end through a real Temporal
|
|
3
|
+
* (`TestWorkflowEnvironment`, the golden E2E's harness): a credential on the
|
|
4
|
+
* `execute-from-execution` workflow input reaches EVERY activity the run
|
|
5
|
+
* schedules — the remote hydrate and the engine's local activities alike —
|
|
6
|
+
* as the activity's current run credential, and a credential-less input
|
|
7
|
+
* reaches none of them. This is the property the whole carrier exists for
|
|
8
|
+
* (`shared/run-credential.ts`): no activity of a member's run ever runs as
|
|
9
|
+
* the operator, and no activity of a credential-less run changes.
|
|
10
|
+
*
|
|
11
|
+
* Both ends are registered exactly as the worker roots register them: the
|
|
12
|
+
* activity interceptor on the worker, the workflow interceptor module in the
|
|
13
|
+
* bundle. Skipped, like the golden E2E, when a local Temporal cannot boot.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join, dirname } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
import { loadWorkflowFromYaml } from "../workflow-engine/loader.js";
|
|
22
|
+
import { evaluateExpressionBatch } from "../workflow-engine/expression.js";
|
|
23
|
+
import type { ExecuteFromExecutionInput } from "../workflows/execute-from-execution.js";
|
|
24
|
+
import { EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE } from "../workflows/execute-from-execution.js";
|
|
25
|
+
import { runCredentialActivityInterceptor } from "../interceptors/run-credential-activity.js";
|
|
26
|
+
import { currentRunCredential } from "../shared/run-credential-store.js";
|
|
27
|
+
|
|
28
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const WORKFLOWS_PATH = join(__dirname, "../workflows/index.ts");
|
|
30
|
+
const RUN_CREDENTIAL_WORKFLOW_INTERCEPTOR = join(
|
|
31
|
+
__dirname,
|
|
32
|
+
"../workflows/interceptors/run-credential.ts",
|
|
33
|
+
);
|
|
34
|
+
const GOLDEN_MODEL = readFileSync(
|
|
35
|
+
join(__dirname, "../../test/golden/01-operation-basic.yaml"),
|
|
36
|
+
"utf-8",
|
|
37
|
+
);
|
|
38
|
+
const TASK_QUEUE = "run-credential-e2e-test";
|
|
39
|
+
|
|
40
|
+
type TestWorkflowEnvironment =
|
|
41
|
+
import("@temporalio/testing").TestWorkflowEnvironment;
|
|
42
|
+
type Worker = import("@temporalio/worker").Worker;
|
|
43
|
+
|
|
44
|
+
let env: TestWorkflowEnvironment | null = null;
|
|
45
|
+
let worker: Worker | null = null;
|
|
46
|
+
let workerRunPromise: Promise<void> | null = null;
|
|
47
|
+
let envReady = false;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* What each activity saw as its run credential, in the order they ran, with
|
|
51
|
+
* the execution it was working on (the hydrate's input object; the engine's
|
|
52
|
+
* positional first argument).
|
|
53
|
+
*/
|
|
54
|
+
const observed: Array<{
|
|
55
|
+
activity: string;
|
|
56
|
+
executionId: string;
|
|
57
|
+
credential: string | undefined;
|
|
58
|
+
}> = [];
|
|
59
|
+
|
|
60
|
+
function observing<T extends (...args: never[]) => unknown>(
|
|
61
|
+
activity: string,
|
|
62
|
+
body: T,
|
|
63
|
+
): T {
|
|
64
|
+
return ((...args: never[]) => {
|
|
65
|
+
const first: unknown = args[0];
|
|
66
|
+
const executionId =
|
|
67
|
+
typeof first === "string"
|
|
68
|
+
? first
|
|
69
|
+
: typeof first === "object" && first !== null && "execution_id" in first
|
|
70
|
+
? String((first as { execution_id: unknown }).execution_id)
|
|
71
|
+
: "";
|
|
72
|
+
observed.push({
|
|
73
|
+
activity,
|
|
74
|
+
executionId,
|
|
75
|
+
credential: currentRunCredential(),
|
|
76
|
+
});
|
|
77
|
+
return body(...args);
|
|
78
|
+
}) as T;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createObservingActivities() {
|
|
82
|
+
return {
|
|
83
|
+
HydrateWorkflowExecution: observing(
|
|
84
|
+
"HydrateWorkflowExecution",
|
|
85
|
+
async (input: ExecuteFromExecutionInput) => ({
|
|
86
|
+
model: loadWorkflowFromYaml(GOLDEN_MODEL),
|
|
87
|
+
workflow_input: null,
|
|
88
|
+
env: {},
|
|
89
|
+
metadata: { execution_id: input.execution_id },
|
|
90
|
+
}),
|
|
91
|
+
),
|
|
92
|
+
ResetEventSequence: observing(
|
|
93
|
+
"ResetEventSequence",
|
|
94
|
+
async (): Promise<number> => 0,
|
|
95
|
+
),
|
|
96
|
+
LoadRecoveryContext: observing(
|
|
97
|
+
"LoadRecoveryContext",
|
|
98
|
+
async (): Promise<unknown[]> => [],
|
|
99
|
+
),
|
|
100
|
+
EmitWorkflowEvents: observing(
|
|
101
|
+
"EmitWorkflowEvents",
|
|
102
|
+
async (): Promise<void> => {},
|
|
103
|
+
),
|
|
104
|
+
PromoteTaskOutput: observing(
|
|
105
|
+
"PromoteTaskOutput",
|
|
106
|
+
async (): Promise<void> => {},
|
|
107
|
+
),
|
|
108
|
+
EvaluateExpressions: observing(
|
|
109
|
+
"EvaluateExpressions",
|
|
110
|
+
async (
|
|
111
|
+
expressions: Record<string, string>,
|
|
112
|
+
input: unknown,
|
|
113
|
+
stateVars: Record<string, unknown>,
|
|
114
|
+
) => evaluateExpressionBatch(expressions, input, stateVars),
|
|
115
|
+
),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function runOnce(credential: string | undefined): Promise<string> {
|
|
120
|
+
if (!env) throw new Error("TestWorkflowEnvironment not initialized");
|
|
121
|
+
const executionId = `wex_e2e_${Math.random().toString(36).slice(2)}`;
|
|
122
|
+
const input: ExecuteFromExecutionInput = {
|
|
123
|
+
execution_id: executionId,
|
|
124
|
+
workflow_instance_id: "wfi_1",
|
|
125
|
+
workflow_id: "wf_1",
|
|
126
|
+
org_id: "org_1",
|
|
127
|
+
...(credential !== undefined
|
|
128
|
+
? { execution_context_token: credential }
|
|
129
|
+
: {}),
|
|
130
|
+
};
|
|
131
|
+
await env.client.workflow.execute(EXECUTE_FROM_EXECUTION_WORKFLOW_TYPE, {
|
|
132
|
+
taskQueue: TASK_QUEUE,
|
|
133
|
+
workflowId: `run-credential-e2e-${executionId}`,
|
|
134
|
+
args: [input],
|
|
135
|
+
workflowExecutionTimeout: "30s",
|
|
136
|
+
});
|
|
137
|
+
return executionId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
describe("run credential E2E — Temporal TestWorkflowEnvironment", () => {
|
|
141
|
+
beforeAll(async () => {
|
|
142
|
+
try {
|
|
143
|
+
const { TestWorkflowEnvironment: TWE } =
|
|
144
|
+
await import("@temporalio/testing");
|
|
145
|
+
const { Worker: W } = await import("@temporalio/worker");
|
|
146
|
+
|
|
147
|
+
env = await TWE.createLocal();
|
|
148
|
+
worker = await W.create({
|
|
149
|
+
connection: env.nativeConnection,
|
|
150
|
+
taskQueue: TASK_QUEUE,
|
|
151
|
+
workflowsPath: WORKFLOWS_PATH,
|
|
152
|
+
activities: createObservingActivities(),
|
|
153
|
+
interceptors: {
|
|
154
|
+
activity: [runCredentialActivityInterceptor],
|
|
155
|
+
workflowModules: [RUN_CREDENTIAL_WORKFLOW_INTERCEPTOR],
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
workerRunPromise = worker.run();
|
|
159
|
+
await runOnce(undefined);
|
|
160
|
+
observed.length = 0;
|
|
161
|
+
envReady = true;
|
|
162
|
+
} catch (err: unknown) {
|
|
163
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
164
|
+
console.warn(
|
|
165
|
+
`Temporal E2E smoke test failed (tests will be skipped): ${msg}`,
|
|
166
|
+
);
|
|
167
|
+
envReady = false;
|
|
168
|
+
}
|
|
169
|
+
}, 90_000);
|
|
170
|
+
|
|
171
|
+
afterAll(async () => {
|
|
172
|
+
if (worker) {
|
|
173
|
+
worker.shutdown();
|
|
174
|
+
await workerRunPromise;
|
|
175
|
+
}
|
|
176
|
+
await env?.teardown();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("every activity of a run whose input carries a credential runs with that credential current", async (testCtx) => {
|
|
180
|
+
if (!envReady) return testCtx.skip();
|
|
181
|
+
observed.length = 0;
|
|
182
|
+
|
|
183
|
+
await runOnce("run-cred-e2e");
|
|
184
|
+
|
|
185
|
+
const activities = new Set(observed.map((o) => o.activity));
|
|
186
|
+
expect(activities).toContain("HydrateWorkflowExecution");
|
|
187
|
+
expect(activities).toContain("ResetEventSequence");
|
|
188
|
+
expect(activities).toContain("EmitWorkflowEvents");
|
|
189
|
+
expect(observed.length).toBeGreaterThan(3);
|
|
190
|
+
expect(
|
|
191
|
+
observed.every((o) => o.credential === "run-cred-e2e"),
|
|
192
|
+
JSON.stringify(observed),
|
|
193
|
+
).toBe(true);
|
|
194
|
+
}, 60_000);
|
|
195
|
+
|
|
196
|
+
it("no activity of a credential-less run sees a credential — an older server's dispatch is unchanged", async (testCtx) => {
|
|
197
|
+
if (!envReady) return testCtx.skip();
|
|
198
|
+
observed.length = 0;
|
|
199
|
+
|
|
200
|
+
await runOnce(undefined);
|
|
201
|
+
|
|
202
|
+
expect(observed.length).toBeGreaterThan(3);
|
|
203
|
+
expect(
|
|
204
|
+
observed.every((o) => o.credential === undefined),
|
|
205
|
+
JSON.stringify(observed),
|
|
206
|
+
).toBe(true);
|
|
207
|
+
}, 60_000);
|
|
208
|
+
|
|
209
|
+
it("two runs in flight at once each see their own credential", async (testCtx) => {
|
|
210
|
+
if (!envReady) return testCtx.skip();
|
|
211
|
+
observed.length = 0;
|
|
212
|
+
|
|
213
|
+
const [one, two] = await Promise.all([
|
|
214
|
+
runOnce("run-cred-one"),
|
|
215
|
+
runOnce("run-cred-two"),
|
|
216
|
+
]);
|
|
217
|
+
const expected = new Map([
|
|
218
|
+
[one, "run-cred-one"],
|
|
219
|
+
[two, "run-cred-two"],
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
const identified = observed.filter((o) => o.executionId !== "");
|
|
223
|
+
expect(identified.length).toBeGreaterThan(6);
|
|
224
|
+
expect(new Set(identified.map((o) => o.executionId))).toEqual(
|
|
225
|
+
new Set([one, two]),
|
|
226
|
+
);
|
|
227
|
+
for (const o of identified) {
|
|
228
|
+
expect(o.credential, `${o.activity} on ${o.executionId}`).toBe(
|
|
229
|
+
expected.get(o.executionId),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}, 60_000);
|
|
233
|
+
});
|