@relayflows/sdk 2.0.53 → 2.0.54

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.
@@ -5,8 +5,10 @@ export declare function postCloudSignal(runId: string, name: string, payload: un
5
5
  duplicate: boolean;
6
6
  }>;
7
7
  /**
8
- * Wait on Cloud's held GET. A normally held 204 re-arms immediately. A fast
9
- * 204 backs off so a proxy or broken server cannot turn this into a hot loop.
8
+ * Wait on Cloud's held GET. The wire behaviour (validation, held GET, fast-204
9
+ * backoff, error classification) is `@relayflows/surface/signals`' single
10
+ * implementation, so the SDK and hosted flows cannot drift. The SDK keeps its
11
+ * own credential resolution and throws `CloudFlowError`.
10
12
  */
11
13
  export declare function waitForCloudSignal(runId: string, name: string, options?: CloudConnectionOptions): Promise<unknown>;
12
14
  //# sourceMappingURL=cloud-signal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-signal.d.ts","sourceRoot":"","sources":["../src/cloud-signal.ts"],"names":[],"mappings":"AACA,OAAO,EACW,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAevE,6EAA6E;AAC7E,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,sBAA2B,GAClF,OAAO,CAAC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAiBjD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAChE,OAAO,CAAC,OAAO,CAAC,CAgElB"}
1
+ {"version":3,"file":"cloud-signal.d.ts","sourceRoot":"","sources":["../src/cloud-signal.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAUvD,6EAA6E;AAC7E,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,sBAA2B,GAClF,OAAO,CAAC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAiBjD;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAChE,OAAO,CAAC,OAAO,CAAC,CAmBlB"}
@@ -1,15 +1,10 @@
1
1
  import { setTimeout as delay } from 'node:timers/promises';
2
- import { CloudFlowError, cloudFetch, cloudRunId, isCloudRecord, resolveCloudConnection, transportError } from './cloud-http.js';
3
- const SIGNAL_NAME = /^[A-Za-z0-9_-]{1,80}$/u;
2
+ import { HOSTED_SIGNAL_READ_TOKEN_ENV, holdSignalWait, signalPath as sharedSignalPath } from '@relayflows/surface/signals';
3
+ import { CloudFlowError, cloudFetch, isCloudRecord, resolveCloudConnection } from './cloud-http.js';
4
4
  const MAX_BYTES = 4 * 1024;
5
- const HELD_GET_MS = 25_000;
6
- const HOSTED_SIGNAL_READ_TOKEN_ENV = 'RELAYFLOW_V2_SIGNAL_READ_TOKEN';
5
+ const cloudSignalError = (code, message, status) => new CloudFlowError(code, message, status);
7
6
  function signalPath(runId, name) {
8
- cloudRunId(runId);
9
- if (!SIGNAL_NAME.test(name)) {
10
- throw new CloudFlowError('invalid_input', 'Signal name must contain only letters, digits, underscores, or hyphens (1–80 characters).');
11
- }
12
- return `/api/v1/workflows/runs/${runId}/signals/${name}`;
7
+ return sharedSignalPath(runId, name, cloudSignalError);
13
8
  }
14
9
  /** Store a run-scoped completion. Retrying the same JSON payload is safe. */
15
10
  export async function postCloudSignal(runId, name, payload, options = {}) {
@@ -37,81 +32,31 @@ export async function postCloudSignal(runId, name, payload, options = {}) {
37
32
  return { accepted: true, duplicate: result.duplicate };
38
33
  }
39
34
  /**
40
- * Wait on Cloud's held GET. A normally held 204 re-arms immediately. A fast
41
- * 204 backs off so a proxy or broken server cannot turn this into a hot loop.
35
+ * Wait on Cloud's held GET. The wire behaviour (validation, held GET, fast-204
36
+ * backoff, error classification) is `@relayflows/surface/signals`' single
37
+ * implementation, so the SDK and hosted flows cannot drift. The SDK keeps its
38
+ * own credential resolution and throws `CloudFlowError`.
42
39
  */
43
40
  export async function waitForCloudSignal(runId, name, options = {}) {
44
- const path = signalPath(runId, name);
45
- const requestTimeout = options.requestTimeoutMs ?? 30_000;
46
- if (!Number.isSafeInteger(requestTimeout) || requestTimeout <= HELD_GET_MS) {
47
- throw new CloudFlowError('invalid_input', 'Signal requestTimeoutMs must exceed 25 seconds.');
48
- }
49
- let fast204BackoffMs = 250;
50
- for (;;) {
51
- options.signal?.throwIfAborted();
52
- let connection;
53
- try {
54
- connection = await resolveCloudConnection(options);
55
- }
56
- catch (error) {
57
- // The hosted run's read-only credential is only a fallback for waits.
58
- // Explicit Cloud credentials and stored logins retain precedence, and
59
- // postCloudSignal never sees this token.
60
- const hostedToken = process.env[HOSTED_SIGNAL_READ_TOKEN_ENV];
61
- if (!(error instanceof CloudFlowError) || error.code !== 'configuration'
62
- || error.reason !== 'auth_missing' || options.token !== undefined
63
- || process.env['FLOWS_CLOUD_TOKEN'] !== undefined || hostedToken === undefined)
64
- throw error;
65
- connection = await resolveCloudConnection({ ...options, token: hostedToken });
66
- }
67
- const { baseUrl, token } = connection;
68
- const deadline = AbortSignal.timeout(requestTimeout);
69
- const requestStartedAt = performance.now();
70
- let response;
71
- try {
72
- response = await fetch(`${baseUrl}${path}?waitMs=${HELD_GET_MS}`, {
73
- method: 'GET',
74
- headers: { authorization: `Bearer ${token}` },
75
- signal: options.signal ? AbortSignal.any([options.signal, deadline]) : deadline,
76
- redirect: 'error',
77
- });
78
- }
79
- catch (error) {
80
- options.signal?.throwIfAborted();
81
- throw transportError(deadline.aborted ? deadline.reason : error);
82
- }
83
- if (response.status === 204) {
84
- if (performance.now() - requestStartedAt < 1_000) {
85
- try {
86
- await delay(fast204BackoffMs, undefined, { signal: options.signal });
87
- }
88
- catch (error) {
89
- options.signal?.throwIfAborted();
90
- throw error;
91
- }
92
- fast204BackoffMs = Math.min(fast204BackoffMs * 2, 5_000);
41
+ return holdSignalWait(runId, name, { signal: options.signal, requestTimeoutMs: options.requestTimeoutMs }, {
42
+ error: cloudSignalError,
43
+ sleep: (ms, signal) => delay(ms, undefined, { signal }),
44
+ connection: async () => {
45
+ try {
46
+ return await resolveCloudConnection(options);
93
47
  }
94
- else {
95
- fast204BackoffMs = 250;
48
+ catch (error) {
49
+ // The hosted run's read-only credential is only a fallback for waits.
50
+ // Explicit Cloud credentials and stored logins retain precedence, and
51
+ // postCloudSignal never sees this token.
52
+ const hostedToken = process.env[HOSTED_SIGNAL_READ_TOKEN_ENV];
53
+ if (!(error instanceof CloudFlowError) || error.code !== 'configuration'
54
+ || error.reason !== 'auth_missing' || options.token !== undefined
55
+ || process.env['FLOWS_CLOUD_TOKEN'] !== undefined || hostedToken === undefined)
56
+ throw error;
57
+ return resolveCloudConnection({ ...options, token: hostedToken });
96
58
  }
97
- continue;
98
- }
99
- if (!response.ok)
100
- throw new CloudFlowError('http_error', `Cloud signal wait failed with HTTP ${response.status}.`, response.status);
101
- let body;
102
- try {
103
- body = await response.json();
104
- }
105
- catch (error) {
106
- options.signal?.throwIfAborted();
107
- if (!(error instanceof SyntaxError))
108
- throw transportError(deadline.aborted ? deadline.reason : error);
109
- throw new CloudFlowError('invalid_response', 'Cloud returned an invalid signal.');
110
- }
111
- if (!isCloudRecord(body) || !Object.hasOwn(body, 'payload') || typeof body.acceptedAt !== 'number') {
112
- throw new CloudFlowError('invalid_response', 'Cloud returned an invalid signal.');
113
- }
114
- return body.payload;
115
- }
59
+ },
60
+ });
116
61
  }
117
62
  //# sourceMappingURL=cloud-signal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-signal.js","sourceRoot":"","sources":["../src/cloud-signal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloud-signal.js","sourceRoot":"","sources":["../src/cloud-signal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayflows/sdk",
3
- "version": "2.0.53",
3
+ "version": "2.0.54",
4
4
  "description": "TypeScript-first authoring SDK for Relayflows. Compiles specs; speaks the journal protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "@modelcontextprotocol/sdk": "^1.30.0",
51
51
  "@relayfile/adapter-core": "0.6.2",
52
52
  "@relayfile/relay-helpers": "0.4.12",
53
- "@relayflows/surface": "2.0.53",
53
+ "@relayflows/surface": "2.0.54",
54
54
  "@types/js-yaml": "^4.0.9",
55
55
  "acorn": "^8.18.0",
56
56
  "ai-hist": "0.4.1",
@@ -1,18 +1,15 @@
1
1
  import { setTimeout as delay } from 'node:timers/promises';
2
- import { CloudFlowError, cloudFetch, cloudRunId, isCloudRecord, resolveCloudConnection,
3
- transportError, type CloudConnectionOptions } from './cloud-http.js';
2
+ import { HOSTED_SIGNAL_READ_TOKEN_ENV, holdSignalWait, signalPath as sharedSignalPath,
3
+ type SignalErrorFactory } from '@relayflows/surface/signals';
4
+ import { CloudFlowError, cloudFetch, isCloudRecord, resolveCloudConnection,
5
+ type CloudConnectionOptions } from './cloud-http.js';
4
6
 
5
- const SIGNAL_NAME = /^[A-Za-z0-9_-]{1,80}$/u;
6
7
  const MAX_BYTES = 4 * 1024;
7
- const HELD_GET_MS = 25_000;
8
- const HOSTED_SIGNAL_READ_TOKEN_ENV = 'RELAYFLOW_V2_SIGNAL_READ_TOKEN';
8
+
9
+ const cloudSignalError: SignalErrorFactory = (code, message, status) => new CloudFlowError(code, message, status);
9
10
 
10
11
  function signalPath(runId: string, name: string): string {
11
- cloudRunId(runId);
12
- if (!SIGNAL_NAME.test(name)) {
13
- throw new CloudFlowError('invalid_input', 'Signal name must contain only letters, digits, underscores, or hyphens (1–80 characters).');
14
- }
15
- return `/api/v1/workflows/runs/${runId}/signals/${name}`;
12
+ return sharedSignalPath(runId, name, cloudSignalError);
16
13
  }
17
14
 
18
15
  /** Store a run-scoped completion. Retrying the same JSON payload is safe. */
@@ -38,73 +35,30 @@ export async function postCloudSignal(
38
35
  }
39
36
 
40
37
  /**
41
- * Wait on Cloud's held GET. A normally held 204 re-arms immediately. A fast
42
- * 204 backs off so a proxy or broken server cannot turn this into a hot loop.
38
+ * Wait on Cloud's held GET. The wire behaviour (validation, held GET, fast-204
39
+ * backoff, error classification) is `@relayflows/surface/signals`' single
40
+ * implementation, so the SDK and hosted flows cannot drift. The SDK keeps its
41
+ * own credential resolution and throws `CloudFlowError`.
43
42
  */
44
43
  export async function waitForCloudSignal(
45
44
  runId: string, name: string, options: CloudConnectionOptions = {},
46
45
  ): Promise<unknown> {
47
- const path = signalPath(runId, name);
48
- const requestTimeout = options.requestTimeoutMs ?? 30_000;
49
- if (!Number.isSafeInteger(requestTimeout) || requestTimeout <= HELD_GET_MS) {
50
- throw new CloudFlowError('invalid_input', 'Signal requestTimeoutMs must exceed 25 seconds.');
51
- }
52
- let fast204BackoffMs = 250;
53
- for (;;) {
54
- options.signal?.throwIfAborted();
55
- let connection: { baseUrl: string; token: string };
56
- try {
57
- connection = await resolveCloudConnection(options);
58
- } catch (error) {
59
- // The hosted run's read-only credential is only a fallback for waits.
60
- // Explicit Cloud credentials and stored logins retain precedence, and
61
- // postCloudSignal never sees this token.
62
- const hostedToken = process.env[HOSTED_SIGNAL_READ_TOKEN_ENV];
63
- if (!(error instanceof CloudFlowError) || error.code !== 'configuration'
64
- || error.reason !== 'auth_missing' || options.token !== undefined
65
- || process.env['FLOWS_CLOUD_TOKEN'] !== undefined || hostedToken === undefined) throw error;
66
- connection = await resolveCloudConnection({ ...options, token: hostedToken });
67
- }
68
- const { baseUrl, token } = connection;
69
- const deadline = AbortSignal.timeout(requestTimeout);
70
- const requestStartedAt = performance.now();
71
- let response: Response;
72
- try {
73
- response = await fetch(`${baseUrl}${path}?waitMs=${HELD_GET_MS}`, {
74
- method: 'GET',
75
- headers: { authorization: `Bearer ${token}` },
76
- signal: options.signal ? AbortSignal.any([options.signal, deadline]) : deadline,
77
- redirect: 'error',
78
- });
79
- } catch (error) {
80
- options.signal?.throwIfAborted();
81
- throw transportError(deadline.aborted ? deadline.reason : error);
82
- }
83
- if (response.status === 204) {
84
- if (performance.now() - requestStartedAt < 1_000) {
85
- try {
86
- await delay(fast204BackoffMs, undefined, { signal: options.signal });
87
- } catch (error) {
88
- options.signal?.throwIfAborted();
89
- throw error;
90
- }
91
- fast204BackoffMs = Math.min(fast204BackoffMs * 2, 5_000);
92
- } else {
93
- fast204BackoffMs = 250;
46
+ return holdSignalWait(runId, name, { signal: options.signal, requestTimeoutMs: options.requestTimeoutMs }, {
47
+ error: cloudSignalError,
48
+ sleep: (ms, signal) => delay(ms, undefined, { signal }),
49
+ connection: async () => {
50
+ try {
51
+ return await resolveCloudConnection(options);
52
+ } catch (error) {
53
+ // The hosted run's read-only credential is only a fallback for waits.
54
+ // Explicit Cloud credentials and stored logins retain precedence, and
55
+ // postCloudSignal never sees this token.
56
+ const hostedToken = process.env[HOSTED_SIGNAL_READ_TOKEN_ENV];
57
+ if (!(error instanceof CloudFlowError) || error.code !== 'configuration'
58
+ || error.reason !== 'auth_missing' || options.token !== undefined
59
+ || process.env['FLOWS_CLOUD_TOKEN'] !== undefined || hostedToken === undefined) throw error;
60
+ return resolveCloudConnection({ ...options, token: hostedToken });
94
61
  }
95
- continue;
96
- }
97
- if (!response.ok) throw new CloudFlowError('http_error', `Cloud signal wait failed with HTTP ${response.status}.`, response.status);
98
- let body: unknown;
99
- try { body = await response.json(); }
100
- catch (error) {
101
- options.signal?.throwIfAborted();
102
- if (!(error instanceof SyntaxError)) throw transportError(deadline.aborted ? deadline.reason : error);
103
- throw new CloudFlowError('invalid_response', 'Cloud returned an invalid signal.');
104
- }
105
- if (!isCloudRecord(body) || !Object.hasOwn(body, 'payload') || typeof body.acceptedAt !== 'number') {
106
- throw new CloudFlowError('invalid_response', 'Cloud returned an invalid signal.');
107
- }
108
- return body.payload;
109
- }
62
+ },
63
+ });
110
64
  }