@sylphx/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/dist/index.d.cts +583 -144
- package/dist/index.d.ts +583 -144
- package/dist/index.js +697 -267
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +691 -269
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.js +43 -24
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/index.mjs +43 -24
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.cts +61 -33
- package/dist/react/index.d.ts +61 -33
- package/dist/react/index.js +907 -749
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +907 -749
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.cts +355 -18
- package/dist/server/index.d.ts +355 -18
- package/dist/server/index.js +529 -11
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +525 -11
- package/dist/server/index.mjs.map +1 -1
- package/dist/web-analytics.js.map +1 -1
- package/dist/web-analytics.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/web-analytics.d.cts +0 -90
- package/dist/web-analytics.d.ts +0 -90
package/dist/index.js
CHANGED
|
@@ -35,17 +35,24 @@ __export(index_exports, {
|
|
|
35
35
|
AuthorizationError: () => AuthorizationError,
|
|
36
36
|
CircuitBreakerOpenError: () => CircuitBreakerOpenError,
|
|
37
37
|
ERROR_CODE_STATUS: () => ERROR_CODE_STATUS,
|
|
38
|
+
InvalidConnectionUrlError: () => InvalidConnectionUrlError,
|
|
38
39
|
NetworkError: () => NetworkError,
|
|
39
40
|
NotFoundError: () => NotFoundError,
|
|
40
41
|
RETRYABLE_CODES: () => RETRYABLE_CODES,
|
|
41
42
|
RateLimitError: () => RateLimitError,
|
|
43
|
+
RunHandle: () => RunHandle,
|
|
44
|
+
RunsClient: () => RunsClient,
|
|
42
45
|
SandboxClient: () => SandboxClient,
|
|
46
|
+
SandboxFiles: () => SandboxFiles,
|
|
47
|
+
SandboxProcesses: () => SandboxProcesses,
|
|
48
|
+
SandboxWatch: () => SandboxWatch,
|
|
43
49
|
StepCompleteSignal: () => StepCompleteSignal,
|
|
44
50
|
StepSleepSignal: () => StepSleepSignal,
|
|
45
51
|
SylphxError: () => SylphxError,
|
|
46
52
|
TimeoutError: () => TimeoutError,
|
|
53
|
+
TriggersClient: () => TriggersClient,
|
|
47
54
|
ValidationError: () => ValidationError,
|
|
48
|
-
WorkerHandle: () =>
|
|
55
|
+
WorkerHandle: () => RunHandle,
|
|
49
56
|
WorkersClient: () => WorkersClient,
|
|
50
57
|
acceptAllConsents: () => acceptAllConsents,
|
|
51
58
|
acceptOrganizationInvitation: () => acceptOrganizationInvitation,
|
|
@@ -64,6 +71,7 @@ __export(index_exports, {
|
|
|
64
71
|
checkFlag: () => checkFlag,
|
|
65
72
|
complete: () => complete,
|
|
66
73
|
createCheckout: () => createCheckout,
|
|
74
|
+
createClient: () => createClient,
|
|
67
75
|
createConfig: () => createConfig,
|
|
68
76
|
createCron: () => createCron,
|
|
69
77
|
createDynamicRestClient: () => createDynamicRestClient,
|
|
@@ -72,6 +80,7 @@ __export(index_exports, {
|
|
|
72
80
|
createPortalSession: () => createPortalSession,
|
|
73
81
|
createRestClient: () => createRestClient,
|
|
74
82
|
createRole: () => createRole,
|
|
83
|
+
createServerClient: () => createServerClient,
|
|
75
84
|
createServiceWorkerScript: () => createServiceWorkerScript,
|
|
76
85
|
createStepContext: () => createStepContext,
|
|
77
86
|
createTasksHandler: () => createTasksHandler,
|
|
@@ -201,7 +210,6 @@ __export(index_exports, {
|
|
|
201
210
|
listTasks: () => listTasks,
|
|
202
211
|
listUsers: () => listUsers,
|
|
203
212
|
page: () => page,
|
|
204
|
-
parseKey: () => parseKey,
|
|
205
213
|
pauseCron: () => pauseCron,
|
|
206
214
|
realtimeEmit: () => realtimeEmit,
|
|
207
215
|
recordStreakActivity: () => recordStreakActivity,
|
|
@@ -262,10 +270,104 @@ __export(index_exports, {
|
|
|
262
270
|
});
|
|
263
271
|
module.exports = __toCommonJS(index_exports);
|
|
264
272
|
|
|
273
|
+
// src/connection-url.ts
|
|
274
|
+
var SYLPHX_PROTOCOL = "sylphx:";
|
|
275
|
+
var DEFAULT_VERSION = "v1";
|
|
276
|
+
var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}$/;
|
|
277
|
+
var VERSION_REGEX = /^v[0-9]+$/;
|
|
278
|
+
var SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
279
|
+
var InvalidConnectionUrlError = class _InvalidConnectionUrlError extends Error {
|
|
280
|
+
code = "INVALID_CONNECTION_URL";
|
|
281
|
+
constructor(message) {
|
|
282
|
+
super(message);
|
|
283
|
+
this.name = "InvalidConnectionUrlError";
|
|
284
|
+
Object.setPrototypeOf(this, _InvalidConnectionUrlError.prototype);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function fail(reason) {
|
|
288
|
+
throw new InvalidConnectionUrlError(`Invalid Sylphx connection URL: ${reason}`);
|
|
289
|
+
}
|
|
290
|
+
function parseCredential(raw) {
|
|
291
|
+
const match = CREDENTIAL_REGEX.exec(raw);
|
|
292
|
+
if (!match) {
|
|
293
|
+
fail(`credential must match (pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}, got "${raw}"`);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
credentialType: match[1],
|
|
297
|
+
env: match[2]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function validateSlug(candidate) {
|
|
301
|
+
if (!candidate || candidate.length > 63 || !SLUG_REGEX.test(candidate)) {
|
|
302
|
+
fail(`slug "${candidate}" is not a valid DNS label (lowercase alnum + hyphens, 1-63 chars)`);
|
|
303
|
+
}
|
|
304
|
+
return candidate;
|
|
305
|
+
}
|
|
306
|
+
function parseConnectionUrl(url) {
|
|
307
|
+
if (typeof url !== "string" || url.length === 0) {
|
|
308
|
+
fail("url must be a non-empty string");
|
|
309
|
+
}
|
|
310
|
+
let parsed;
|
|
311
|
+
try {
|
|
312
|
+
parsed = new URL(url);
|
|
313
|
+
} catch {
|
|
314
|
+
fail(`not a valid URL: "${url}"`);
|
|
315
|
+
}
|
|
316
|
+
if (parsed.protocol !== SYLPHX_PROTOCOL) {
|
|
317
|
+
fail(`protocol must be "sylphx:", got "${parsed.protocol}"`);
|
|
318
|
+
}
|
|
319
|
+
const credential = decodeURIComponent(parsed.username);
|
|
320
|
+
if (!credential) {
|
|
321
|
+
fail("missing credential (expected `sylphx://<credential>@<host>`)");
|
|
322
|
+
}
|
|
323
|
+
if (parsed.password) {
|
|
324
|
+
fail("connection URL must not contain a password component");
|
|
325
|
+
}
|
|
326
|
+
const { credentialType, env } = parseCredential(credential);
|
|
327
|
+
const host = parsed.host;
|
|
328
|
+
if (!host) {
|
|
329
|
+
fail("missing host");
|
|
330
|
+
}
|
|
331
|
+
const hostname = parsed.hostname;
|
|
332
|
+
const firstDot = hostname.indexOf(".");
|
|
333
|
+
if (firstDot <= 0) {
|
|
334
|
+
fail(`host "${hostname}" must contain at least one dot (slug.domain)`);
|
|
335
|
+
}
|
|
336
|
+
const slugCandidate = hostname.slice(0, firstDot);
|
|
337
|
+
const domainSuffix = hostname.slice(firstDot + 1);
|
|
338
|
+
if (!domainSuffix) {
|
|
339
|
+
fail(`host "${hostname}" has empty domain suffix`);
|
|
340
|
+
}
|
|
341
|
+
const slug = validateSlug(slugCandidate);
|
|
342
|
+
const rawPath = parsed.pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
343
|
+
let version = DEFAULT_VERSION;
|
|
344
|
+
if (rawPath !== "") {
|
|
345
|
+
if (!VERSION_REGEX.test(rawPath)) {
|
|
346
|
+
fail(`path "${parsed.pathname}" must be empty or match /v{N}`);
|
|
347
|
+
}
|
|
348
|
+
version = rawPath;
|
|
349
|
+
}
|
|
350
|
+
if (parsed.search) {
|
|
351
|
+
fail("connection URL must not contain a query string");
|
|
352
|
+
}
|
|
353
|
+
if (parsed.hash) {
|
|
354
|
+
fail("connection URL must not contain a fragment");
|
|
355
|
+
}
|
|
356
|
+
const apiBaseUrl = `https://${host}/${version}`;
|
|
357
|
+
return {
|
|
358
|
+
credential,
|
|
359
|
+
credentialType,
|
|
360
|
+
env,
|
|
361
|
+
slug,
|
|
362
|
+
host,
|
|
363
|
+
apiBaseUrl
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
265
367
|
// src/constants.ts
|
|
266
368
|
var SDK_API_PATH = `/v1`;
|
|
267
369
|
var DEFAULT_SDK_API_HOST = "api.sylphx.com";
|
|
268
|
-
var SDK_VERSION = "0.
|
|
370
|
+
var SDK_VERSION = "0.5.0";
|
|
269
371
|
var SDK_PLATFORM = typeof window !== "undefined" ? "browser" : typeof process !== "undefined" && process.versions?.node ? "node" : "unknown";
|
|
270
372
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
271
373
|
var SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60;
|
|
@@ -600,195 +702,153 @@ function exponentialBackoff(attempt, baseDelay = BASE_RETRY_DELAY_MS, maxDelay =
|
|
|
600
702
|
return Math.round(cappedDelay + jitter);
|
|
601
703
|
}
|
|
602
704
|
|
|
603
|
-
// src/
|
|
604
|
-
var
|
|
605
|
-
var
|
|
606
|
-
var
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
};
|
|
612
|
-
function detectKeyIssues(key) {
|
|
613
|
-
const issues = [];
|
|
614
|
-
if (key !== key.trim()) issues.push("whitespace");
|
|
615
|
-
if (key.includes("\n")) issues.push("newline");
|
|
616
|
-
if (key.includes("\r")) issues.push("carriage-return");
|
|
617
|
-
if (key.includes(" ")) issues.push("space");
|
|
618
|
-
if (key !== key.toLowerCase()) issues.push("uppercase-chars");
|
|
619
|
-
return issues;
|
|
620
|
-
}
|
|
621
|
-
function createSanitizationWarning(keyType, issues, envVarName) {
|
|
622
|
-
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
623
|
-
return `[Sylphx] ${keyTypeName} contains ${issues.join(", ")}. This is commonly caused by Vercel CLI's 'env pull' command.
|
|
624
|
-
|
|
625
|
-
To fix permanently:
|
|
626
|
-
1. Go to Vercel Dashboard \u2192 Your Project \u2192 Settings \u2192 Environment Variables
|
|
627
|
-
2. Edit ${envVarName}
|
|
628
|
-
3. Remove any trailing whitespace or newline characters
|
|
629
|
-
4. Redeploy your application
|
|
630
|
-
|
|
631
|
-
The SDK will automatically sanitize the key, but fixing the source is recommended.`;
|
|
632
|
-
}
|
|
633
|
-
function createInvalidKeyError(keyType, key, envVarName) {
|
|
634
|
-
const prefix = keyType === "appId" ? "app" : "sk";
|
|
635
|
-
const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
|
|
636
|
-
const formatHint = `${prefix}_(dev|stg|prod)_[identifier]`;
|
|
637
|
-
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
638
|
-
return `[Sylphx] Invalid ${keyTypeName} format.
|
|
639
|
-
|
|
640
|
-
Expected format: ${formatHint}
|
|
641
|
-
Received: "${maskedKey}"
|
|
642
|
-
|
|
643
|
-
Please check your ${envVarName} environment variable.
|
|
644
|
-
You can find your keys in the Sylphx Console \u2192 API Keys.
|
|
645
|
-
|
|
646
|
-
Common issues:
|
|
647
|
-
\u2022 Key has uppercase characters (must be lowercase)
|
|
648
|
-
\u2022 Key has wrong prefix (App ID: app_, Secret Key: sk_)
|
|
649
|
-
\u2022 Key has invalid environment (must be dev, stg, or prod)
|
|
650
|
-
\u2022 Key was copied with extra whitespace`;
|
|
651
|
-
}
|
|
652
|
-
function extractEnvironment(key) {
|
|
653
|
-
const match = key.match(/^(?:app|sk)_(dev|stg|prod)_/);
|
|
654
|
-
if (!match) return void 0;
|
|
655
|
-
return ENV_PREFIX_MAP[match[1]];
|
|
656
|
-
}
|
|
657
|
-
function validateKeyForType(key, keyType, pattern, envVarName) {
|
|
658
|
-
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
659
|
-
if (!key) {
|
|
660
|
-
return {
|
|
661
|
-
valid: false,
|
|
662
|
-
sanitizedKey: "",
|
|
663
|
-
error: `[Sylphx] ${keyTypeName} is required. Set ${envVarName} in your environment variables.`,
|
|
664
|
-
issues: ["missing"]
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
const issues = detectKeyIssues(key);
|
|
668
|
-
if (pattern.test(key)) {
|
|
669
|
-
return {
|
|
670
|
-
valid: true,
|
|
671
|
-
sanitizedKey: key,
|
|
672
|
-
keyType,
|
|
673
|
-
environment: extractEnvironment(key),
|
|
674
|
-
issues: []
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
const sanitized = key.trim().toLowerCase();
|
|
678
|
-
if (pattern.test(sanitized)) {
|
|
679
|
-
return {
|
|
680
|
-
valid: true,
|
|
681
|
-
sanitizedKey: sanitized,
|
|
682
|
-
keyType,
|
|
683
|
-
environment: extractEnvironment(sanitized),
|
|
684
|
-
warning: createSanitizationWarning(keyType, issues, envVarName),
|
|
685
|
-
issues
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
return {
|
|
689
|
-
valid: false,
|
|
690
|
-
sanitizedKey: "",
|
|
691
|
-
error: createInvalidKeyError(keyType, key, envVarName),
|
|
692
|
-
issues: [...issues, "invalid-format"]
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
function validatePublicKey(key) {
|
|
696
|
-
return validateKeyForType(key, "publicKey", PUBLIC_KEY_PATTERN, "NEXT_PUBLIC_SYLPHX_KEY");
|
|
697
|
-
}
|
|
698
|
-
function validateAppId(key) {
|
|
699
|
-
return validateKeyForType(key, "appId", APP_ID_PATTERN, "NEXT_PUBLIC_SYLPHX_APP_ID");
|
|
700
|
-
}
|
|
701
|
-
function validateSecretKey(key) {
|
|
702
|
-
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "SYLPHX_SECRET_KEY");
|
|
703
|
-
}
|
|
704
|
-
function validateAndSanitizeSecretKey(key) {
|
|
705
|
-
const result = validateSecretKey(key);
|
|
706
|
-
if (!result.valid) {
|
|
707
|
-
throw new Error(result.error);
|
|
708
|
-
}
|
|
709
|
-
if (result.warning) {
|
|
710
|
-
console.warn(result.warning);
|
|
711
|
-
}
|
|
712
|
-
return result.sanitizedKey;
|
|
713
|
-
}
|
|
714
|
-
function detectKeyType(key) {
|
|
715
|
-
const sanitized = key.trim().toLowerCase();
|
|
716
|
-
if (sanitized.startsWith("pk_")) return "publicKey";
|
|
717
|
-
if (sanitized.startsWith("app_")) return "appId";
|
|
718
|
-
if (sanitized.startsWith("sk_")) return "secret";
|
|
719
|
-
return null;
|
|
720
|
-
}
|
|
721
|
-
function validateKey(key) {
|
|
722
|
-
const keyType = key ? detectKeyType(key) : null;
|
|
723
|
-
if (keyType === "publicKey") {
|
|
724
|
-
return validatePublicKey(key);
|
|
725
|
-
}
|
|
726
|
-
if (keyType === "appId") {
|
|
727
|
-
return validateAppId(key);
|
|
705
|
+
// src/config.ts
|
|
706
|
+
var LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/;
|
|
707
|
+
var LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/;
|
|
708
|
+
var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{hex}@your-slug.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
|
|
709
|
+
function rejectLegacyKeyFormat(input) {
|
|
710
|
+
const trimmed = input.trim().toLowerCase();
|
|
711
|
+
if (LEGACY_APP_KEY_PATTERN.test(trimmed)) {
|
|
712
|
+
throw new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: "BAD_REQUEST" });
|
|
728
713
|
}
|
|
729
|
-
if (
|
|
730
|
-
|
|
714
|
+
if (LEGACY_EMBEDDED_REF_PATTERN.test(trimmed)) {
|
|
715
|
+
throw new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: "BAD_REQUEST" });
|
|
731
716
|
}
|
|
732
|
-
return {
|
|
733
|
-
valid: false,
|
|
734
|
-
sanitizedKey: "",
|
|
735
|
-
error: key ? `Invalid key format. Keys must start with 'pk_' (publishable), 'app_' (legacy), or 'sk_' (secret), followed by environment (dev/stg/prod). Got: ${key.slice(0, 20)}...` : "API key is required but was not provided.",
|
|
736
|
-
issues: key ? ["invalid_format"] : ["missing"]
|
|
737
|
-
};
|
|
738
717
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
718
|
+
function freezeConfig(opts) {
|
|
719
|
+
return Object.freeze({
|
|
720
|
+
credential: opts.credential,
|
|
721
|
+
credentialType: opts.credentialType,
|
|
722
|
+
env: opts.env,
|
|
723
|
+
slug: opts.slug,
|
|
724
|
+
baseUrl: opts.baseUrl,
|
|
725
|
+
accessToken: opts.accessToken,
|
|
726
|
+
// Backward-compat aliases
|
|
727
|
+
secretKey: opts.credentialType === "sk" ? opts.credential : void 0,
|
|
728
|
+
publicKey: opts.credentialType === "pk" ? opts.credential : void 0,
|
|
729
|
+
ref: opts.slug
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
function createClient(input) {
|
|
733
|
+
if (typeof input === "string") {
|
|
734
|
+
return createConfigFromUrl(input);
|
|
735
|
+
}
|
|
736
|
+
return createConfigFromComponents(input);
|
|
737
|
+
}
|
|
738
|
+
function createServerClient(input) {
|
|
739
|
+
const config = createClient(input);
|
|
740
|
+
if (config.credentialType !== "sk") {
|
|
744
741
|
throw new SylphxError(
|
|
745
|
-
"[Sylphx]
|
|
742
|
+
"[Sylphx] createServerClient() requires a secret key (sk_*). Use a SYLPHX_SECRET_URL with an sk_ credential, or pass { secretKey } in the components object.",
|
|
746
743
|
{ code: "BAD_REQUEST" }
|
|
747
744
|
);
|
|
748
745
|
}
|
|
749
|
-
|
|
750
|
-
|
|
746
|
+
return config;
|
|
747
|
+
}
|
|
748
|
+
function createConfigFromUrl(url) {
|
|
749
|
+
if (!url || typeof url !== "string") {
|
|
751
750
|
throw new SylphxError(
|
|
752
|
-
|
|
751
|
+
"[Sylphx] Connection URL is required. Set SYLPHX_URL or NEXT_PUBLIC_SYLPHX_URL environment variable.\n\nFormat: sylphx://pk_prod_{hex}@your-slug.sylphx.com",
|
|
753
752
|
{ code: "BAD_REQUEST" }
|
|
754
753
|
);
|
|
755
754
|
}
|
|
756
|
-
const
|
|
757
|
-
|
|
755
|
+
const trimmed = url.trim();
|
|
756
|
+
rejectLegacyKeyFormat(trimmed);
|
|
757
|
+
if (!trimmed.startsWith("sylphx://")) {
|
|
758
|
+
if (CREDENTIAL_REGEX.test(trimmed)) {
|
|
759
|
+
throw new SylphxError(
|
|
760
|
+
"[Sylphx] Received a bare credential instead of a connection URL.\n\nWrap it in a connection URL: sylphx://<credential>@<slug>.sylphx.com\nOr use createClient({ slug, publicKey }) for explicit components.",
|
|
761
|
+
{ code: "BAD_REQUEST" }
|
|
762
|
+
);
|
|
763
|
+
}
|
|
758
764
|
throw new SylphxError(
|
|
759
|
-
`[Sylphx] Invalid
|
|
765
|
+
`[Sylphx] Invalid connection URL \u2014 must start with "sylphx://". Got: "${trimmed.slice(0, 30)}..."`,
|
|
760
766
|
{ code: "BAD_REQUEST" }
|
|
761
767
|
);
|
|
762
768
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
+
let parsed;
|
|
770
|
+
try {
|
|
771
|
+
parsed = parseConnectionUrl(trimmed);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
if (err instanceof InvalidConnectionUrlError) {
|
|
774
|
+
throw new SylphxError(err.message, { code: "BAD_REQUEST", cause: err });
|
|
775
|
+
}
|
|
776
|
+
throw err;
|
|
769
777
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
778
|
+
return freezeConfig({
|
|
779
|
+
credential: parsed.credential,
|
|
780
|
+
credentialType: parsed.credentialType,
|
|
781
|
+
env: parsed.env,
|
|
782
|
+
slug: parsed.slug,
|
|
783
|
+
baseUrl: parsed.apiBaseUrl
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
function createConfigFromComponents(input) {
|
|
787
|
+
const credential = input.secretKey || input.publicKey;
|
|
788
|
+
if (!credential) {
|
|
789
|
+
throw new SylphxError("[Sylphx] Either publicKey or secretKey must be provided.", {
|
|
790
|
+
code: "BAD_REQUEST"
|
|
791
|
+
});
|
|
775
792
|
}
|
|
776
|
-
const
|
|
777
|
-
if (
|
|
778
|
-
throw new SylphxError(
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
);
|
|
793
|
+
const resolvedSlug = input.slug || input.ref;
|
|
794
|
+
if (!resolvedSlug) {
|
|
795
|
+
throw new SylphxError("[Sylphx] slug is required when using explicit components.", {
|
|
796
|
+
code: "BAD_REQUEST"
|
|
797
|
+
});
|
|
782
798
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
799
|
+
const trimmedCred = credential.trim().toLowerCase();
|
|
800
|
+
if (CREDENTIAL_REGEX.test(trimmedCred)) {
|
|
801
|
+
const match = CREDENTIAL_REGEX.exec(trimmedCred);
|
|
802
|
+
const credentialType = match[1];
|
|
803
|
+
const env = match[2];
|
|
804
|
+
const slug = resolvedSlug.trim().toLowerCase();
|
|
805
|
+
const domain = input.domain?.trim() || "sylphx.com";
|
|
806
|
+
const baseUrl = `https://${slug}.${domain}/v1`;
|
|
807
|
+
return freezeConfig({
|
|
808
|
+
credential: trimmedCred,
|
|
809
|
+
credentialType,
|
|
810
|
+
env,
|
|
811
|
+
slug,
|
|
812
|
+
baseUrl,
|
|
813
|
+
accessToken: input.accessToken
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
const parts = trimmedCred.split("_");
|
|
817
|
+
const prefix = parts[0];
|
|
818
|
+
if ((prefix === "pk" || prefix === "sk") && parts.length >= 3) {
|
|
819
|
+
const envSegment = parts[1];
|
|
820
|
+
const validEnvs = ["dev", "stg", "prod", "prev"];
|
|
821
|
+
const env = validEnvs.includes(envSegment) ? envSegment : "prod";
|
|
822
|
+
const slug = resolvedSlug.trim().toLowerCase();
|
|
823
|
+
let baseUrl;
|
|
824
|
+
if (input.platformUrl) {
|
|
825
|
+
const platform = input.platformUrl.trim().replace(/\/$/, "");
|
|
826
|
+
baseUrl = platform.includes("/v1") ? platform : `${platform}/v1`;
|
|
827
|
+
} else {
|
|
828
|
+
const domain = input.domain?.trim() || "api.sylphx.com";
|
|
829
|
+
baseUrl = `https://${slug}.${domain}/v1`;
|
|
830
|
+
}
|
|
831
|
+
return freezeConfig({
|
|
832
|
+
credential: trimmedCred,
|
|
833
|
+
credentialType: prefix,
|
|
834
|
+
env,
|
|
835
|
+
slug,
|
|
836
|
+
baseUrl,
|
|
837
|
+
accessToken: input.accessToken
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
throw new SylphxError(
|
|
841
|
+
`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}. Got: "${trimmedCred.slice(0, 30)}..."`,
|
|
842
|
+
{ code: "BAD_REQUEST" }
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
function withToken(config, accessToken) {
|
|
846
|
+
return Object.freeze({
|
|
847
|
+
...config,
|
|
848
|
+
accessToken
|
|
849
|
+
});
|
|
791
850
|
}
|
|
851
|
+
var createConfig = createClient;
|
|
792
852
|
function httpStatusToErrorCode(status) {
|
|
793
853
|
switch (status) {
|
|
794
854
|
case 400:
|
|
@@ -821,79 +881,12 @@ function httpStatusToErrorCode(status) {
|
|
|
821
881
|
return status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST";
|
|
822
882
|
}
|
|
823
883
|
}
|
|
824
|
-
var REF_PATTERN = /^[a-z0-9]{12}$/;
|
|
825
|
-
function createConfig(input) {
|
|
826
|
-
const keyForParsing = input.secretKey || input.publicKey;
|
|
827
|
-
if (!keyForParsing) {
|
|
828
|
-
if (input.ref) {
|
|
829
|
-
const trimmedRef = input.ref.trim();
|
|
830
|
-
if (!REF_PATTERN.test(trimmedRef)) {
|
|
831
|
-
throw new SylphxError(
|
|
832
|
-
`[Sylphx] Invalid project ref format: "${input.ref}". Expected a 12-character lowercase alphanumeric string (e.g. "abc123def456"). Get your ref from Platform Console \u2192 Projects \u2192 Your Project \u2192 Overview.`,
|
|
833
|
-
{ code: "BAD_REQUEST" }
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
const baseUrl2 = `https://${trimmedRef}.${DEFAULT_SDK_API_HOST}${SDK_API_PATH}`;
|
|
837
|
-
console.warn(
|
|
838
|
-
"[Sylphx] Providing only ref without a key is deprecated. Provide secretKey or publicKey \u2014 the ref is now embedded in keys (ADR-021)."
|
|
839
|
-
);
|
|
840
|
-
return Object.freeze({ ref: trimmedRef, baseUrl: baseUrl2, accessToken: input.accessToken });
|
|
841
|
-
}
|
|
842
|
-
throw new SylphxError(
|
|
843
|
-
"[Sylphx] Either publicKey or secretKey must be provided to createConfig().",
|
|
844
|
-
{ code: "BAD_REQUEST" }
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
const parsed = parseKey(keyForParsing);
|
|
848
|
-
const ref = parsed.ref;
|
|
849
|
-
const baseUrl = parsed.baseUrl;
|
|
850
|
-
let secretKey;
|
|
851
|
-
if (input.secretKey) {
|
|
852
|
-
const result = validateKey(input.secretKey);
|
|
853
|
-
if (!result.valid) {
|
|
854
|
-
throw new SylphxError(result.error || "Invalid secret key", {
|
|
855
|
-
code: "BAD_REQUEST",
|
|
856
|
-
data: { issues: result.issues }
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
if (result.warning) console.warn(`[Sylphx] ${result.warning}`);
|
|
860
|
-
secretKey = result.sanitizedKey;
|
|
861
|
-
}
|
|
862
|
-
let publicKey;
|
|
863
|
-
if (input.publicKey) {
|
|
864
|
-
const result = validateKey(input.publicKey);
|
|
865
|
-
if (!result.valid) {
|
|
866
|
-
throw new SylphxError(result.error || "Invalid public key", {
|
|
867
|
-
code: "BAD_REQUEST",
|
|
868
|
-
data: { issues: result.issues }
|
|
869
|
-
});
|
|
870
|
-
}
|
|
871
|
-
if (result.warning) console.warn(`[Sylphx] ${result.warning}`);
|
|
872
|
-
publicKey = result.sanitizedKey;
|
|
873
|
-
}
|
|
874
|
-
return Object.freeze({
|
|
875
|
-
secretKey,
|
|
876
|
-
publicKey,
|
|
877
|
-
ref,
|
|
878
|
-
baseUrl,
|
|
879
|
-
accessToken: input.accessToken
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
function withToken(config, accessToken) {
|
|
883
|
-
return Object.freeze({
|
|
884
|
-
...config,
|
|
885
|
-
accessToken
|
|
886
|
-
// Preserve baseUrl and ref from original config
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
884
|
function buildHeaders(config) {
|
|
890
885
|
const headers = {
|
|
891
886
|
"Content-Type": "application/json"
|
|
892
887
|
};
|
|
893
|
-
if (config.
|
|
894
|
-
headers["x-app-secret"] = config.
|
|
895
|
-
} else if (config.publicKey) {
|
|
896
|
-
headers["x-app-secret"] = config.publicKey;
|
|
888
|
+
if (config.credential) {
|
|
889
|
+
headers["x-app-secret"] = config.credential;
|
|
897
890
|
}
|
|
898
891
|
if (config.accessToken) {
|
|
899
892
|
headers.Authorization = `Bearer ${config.accessToken}`;
|
|
@@ -912,7 +905,8 @@ async function callApi(config, path, options = {}) {
|
|
|
912
905
|
query,
|
|
913
906
|
timeout = DEFAULT_TIMEOUT_MS,
|
|
914
907
|
signal,
|
|
915
|
-
idempotencyKey
|
|
908
|
+
idempotencyKey,
|
|
909
|
+
headers: extraHeaders
|
|
916
910
|
} = options;
|
|
917
911
|
let url = buildApiUrl(config, path);
|
|
918
912
|
if (query) {
|
|
@@ -934,6 +928,11 @@ async function callApi(config, path, options = {}) {
|
|
|
934
928
|
if (idempotencyKey) {
|
|
935
929
|
headers["Idempotency-Key"] = idempotencyKey;
|
|
936
930
|
}
|
|
931
|
+
if (extraHeaders) {
|
|
932
|
+
for (const [k, v] of Object.entries(extraHeaders)) {
|
|
933
|
+
headers[k] = v;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
937
936
|
const fetchOptions = {
|
|
938
937
|
method,
|
|
939
938
|
headers,
|
|
@@ -1010,7 +1009,6 @@ async function callApi(config, path, options = {}) {
|
|
|
1010
1009
|
code: "PARSE_ERROR",
|
|
1011
1010
|
cause: error instanceof Error ? error : void 0,
|
|
1012
1011
|
data: { body: text.slice(0, 200) }
|
|
1013
|
-
// Include snippet for debugging
|
|
1014
1012
|
});
|
|
1015
1013
|
}
|
|
1016
1014
|
}
|
|
@@ -1120,6 +1118,111 @@ function installGlobalDebugHelpers() {
|
|
|
1120
1118
|
|
|
1121
1119
|
// src/rest-client.ts
|
|
1122
1120
|
var import_openapi_fetch = __toESM(require("openapi-fetch"), 1);
|
|
1121
|
+
|
|
1122
|
+
// src/key-validation.ts
|
|
1123
|
+
var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod)_[a-z0-9_-]+$/;
|
|
1124
|
+
var ENV_PREFIX_MAP = {
|
|
1125
|
+
dev: "development",
|
|
1126
|
+
stg: "staging",
|
|
1127
|
+
prod: "production"
|
|
1128
|
+
};
|
|
1129
|
+
function detectKeyIssues(key) {
|
|
1130
|
+
const issues = [];
|
|
1131
|
+
if (key !== key.trim()) issues.push("whitespace");
|
|
1132
|
+
if (key.includes("\n")) issues.push("newline");
|
|
1133
|
+
if (key.includes("\r")) issues.push("carriage-return");
|
|
1134
|
+
if (key.includes(" ")) issues.push("space");
|
|
1135
|
+
if (key !== key.toLowerCase()) issues.push("uppercase-chars");
|
|
1136
|
+
return issues;
|
|
1137
|
+
}
|
|
1138
|
+
function createSanitizationWarning(keyType, issues, envVarName) {
|
|
1139
|
+
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
1140
|
+
return `[Sylphx] ${keyTypeName} contains ${issues.join(", ")}. This is commonly caused by Vercel CLI's 'env pull' command.
|
|
1141
|
+
|
|
1142
|
+
To fix permanently:
|
|
1143
|
+
1. Go to Vercel Dashboard \u2192 Your Project \u2192 Settings \u2192 Environment Variables
|
|
1144
|
+
2. Edit ${envVarName}
|
|
1145
|
+
3. Remove any trailing whitespace or newline characters
|
|
1146
|
+
4. Redeploy your application
|
|
1147
|
+
|
|
1148
|
+
The SDK will automatically sanitize the key, but fixing the source is recommended.`;
|
|
1149
|
+
}
|
|
1150
|
+
function createInvalidKeyError(keyType, key, envVarName) {
|
|
1151
|
+
const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
|
|
1152
|
+
const formatHint = keyType === "appId" ? "pk_(dev|stg|prod)_{ref}_{hex} or app_(dev|stg|prod)_[id]" : "sk_(dev|stg|prod)_{ref}_{hex}";
|
|
1153
|
+
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
1154
|
+
return `[Sylphx] Invalid ${keyTypeName} format.
|
|
1155
|
+
|
|
1156
|
+
Expected format: ${formatHint}
|
|
1157
|
+
Received: "${maskedKey}"
|
|
1158
|
+
|
|
1159
|
+
Please check your ${envVarName} environment variable.
|
|
1160
|
+
You can find your keys in the Sylphx Console \u2192 API Keys.
|
|
1161
|
+
|
|
1162
|
+
Common issues:
|
|
1163
|
+
\u2022 Key has uppercase characters (must be lowercase)
|
|
1164
|
+
\u2022 Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)
|
|
1165
|
+
\u2022 Key has invalid environment (must be dev, stg, or prod)
|
|
1166
|
+
\u2022 Key was copied with extra whitespace`;
|
|
1167
|
+
}
|
|
1168
|
+
function extractEnvironment(key) {
|
|
1169
|
+
const match = key.match(/^(?:app|pk|sk)_(dev|stg|prod|prev)_/);
|
|
1170
|
+
if (!match) return void 0;
|
|
1171
|
+
return ENV_PREFIX_MAP[match[1]];
|
|
1172
|
+
}
|
|
1173
|
+
function validateKeyForType(key, keyType, pattern, envVarName) {
|
|
1174
|
+
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
1175
|
+
if (!key) {
|
|
1176
|
+
return {
|
|
1177
|
+
valid: false,
|
|
1178
|
+
sanitizedKey: "",
|
|
1179
|
+
error: `[Sylphx] ${keyTypeName} is required. Set ${envVarName} in your environment variables.`,
|
|
1180
|
+
issues: ["missing"]
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
const issues = detectKeyIssues(key);
|
|
1184
|
+
if (pattern.test(key)) {
|
|
1185
|
+
return {
|
|
1186
|
+
valid: true,
|
|
1187
|
+
sanitizedKey: key,
|
|
1188
|
+
keyType,
|
|
1189
|
+
environment: extractEnvironment(key),
|
|
1190
|
+
issues: []
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
const sanitized = key.trim().toLowerCase();
|
|
1194
|
+
if (pattern.test(sanitized)) {
|
|
1195
|
+
return {
|
|
1196
|
+
valid: true,
|
|
1197
|
+
sanitizedKey: sanitized,
|
|
1198
|
+
keyType,
|
|
1199
|
+
environment: extractEnvironment(sanitized),
|
|
1200
|
+
warning: createSanitizationWarning(keyType, issues, envVarName),
|
|
1201
|
+
issues
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
valid: false,
|
|
1206
|
+
sanitizedKey: "",
|
|
1207
|
+
error: createInvalidKeyError(keyType, key, envVarName),
|
|
1208
|
+
issues: [...issues, "invalid-format"]
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
function validateSecretKey(key) {
|
|
1212
|
+
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "SYLPHX_SECRET_KEY");
|
|
1213
|
+
}
|
|
1214
|
+
function validateAndSanitizeSecretKey(key) {
|
|
1215
|
+
const result = validateSecretKey(key);
|
|
1216
|
+
if (!result.valid) {
|
|
1217
|
+
throw new Error(result.error);
|
|
1218
|
+
}
|
|
1219
|
+
if (result.warning) {
|
|
1220
|
+
console.warn(result.warning);
|
|
1221
|
+
}
|
|
1222
|
+
return result.sanitizedKey;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/rest-client.ts
|
|
1123
1226
|
function createAuthMiddleware(config) {
|
|
1124
1227
|
return {
|
|
1125
1228
|
async onRequest({ request }) {
|
|
@@ -2339,6 +2442,73 @@ async function updatePushPreferences(config, preferences) {
|
|
|
2339
2442
|
});
|
|
2340
2443
|
}
|
|
2341
2444
|
|
|
2445
|
+
// src/lib/triggers/index.ts
|
|
2446
|
+
var TriggersClient = class {
|
|
2447
|
+
/** Create a new trigger (cron or event source, task/run/http target) */
|
|
2448
|
+
static async create(config, options) {
|
|
2449
|
+
return callApi(config, "/triggers", {
|
|
2450
|
+
method: "POST",
|
|
2451
|
+
body: options
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
/** List all triggers for the project */
|
|
2455
|
+
static async list(config) {
|
|
2456
|
+
return callApi(config, "/triggers");
|
|
2457
|
+
}
|
|
2458
|
+
/** Get a trigger by ID */
|
|
2459
|
+
static async get(config, triggerId) {
|
|
2460
|
+
return callApi(config, `/triggers/${triggerId}`);
|
|
2461
|
+
}
|
|
2462
|
+
/** Update a trigger */
|
|
2463
|
+
static async update(config, triggerId, options) {
|
|
2464
|
+
return callApi(config, `/triggers/${triggerId}`, {
|
|
2465
|
+
method: "PATCH",
|
|
2466
|
+
body: options
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
/** Delete a trigger */
|
|
2470
|
+
static async delete(config, triggerId) {
|
|
2471
|
+
return callApi(config, `/triggers/${triggerId}`, { method: "DELETE" });
|
|
2472
|
+
}
|
|
2473
|
+
/** Pause a trigger */
|
|
2474
|
+
static async pause(config, triggerId) {
|
|
2475
|
+
return callApi(config, `/triggers/${triggerId}/pause`, { method: "POST" });
|
|
2476
|
+
}
|
|
2477
|
+
/** Resume a paused trigger */
|
|
2478
|
+
static async resume(config, triggerId) {
|
|
2479
|
+
return callApi(config, `/triggers/${triggerId}/resume`, { method: "POST" });
|
|
2480
|
+
}
|
|
2481
|
+
/** Fire a trigger immediately (one-shot, regardless of schedule) */
|
|
2482
|
+
static async fire(config, triggerId) {
|
|
2483
|
+
return callApi(config, `/triggers/${triggerId}/fire`, {
|
|
2484
|
+
method: "POST"
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* Publish an event — dispatches all active event triggers matching the event name.
|
|
2489
|
+
*
|
|
2490
|
+
* @example
|
|
2491
|
+
* ```typescript
|
|
2492
|
+
* await TriggersClient.publishEvent(config, 'user.signup', { userId: '123', plan: 'pro' })
|
|
2493
|
+
* ```
|
|
2494
|
+
*/
|
|
2495
|
+
/**
|
|
2496
|
+
* Publish an event — dispatches all active event triggers matching the event name.
|
|
2497
|
+
* Endpoint: POST /triggers/events
|
|
2498
|
+
*
|
|
2499
|
+
* @example
|
|
2500
|
+
* ```typescript
|
|
2501
|
+
* await TriggersClient.publishEvent(config, 'user.signup', { userId: '123', plan: 'pro' })
|
|
2502
|
+
* ```
|
|
2503
|
+
*/
|
|
2504
|
+
static async publishEvent(config, eventName, payload) {
|
|
2505
|
+
return callApi(config, "/triggers/events", {
|
|
2506
|
+
method: "POST",
|
|
2507
|
+
body: { eventName, payload: payload ?? {} }
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2342
2512
|
// src/lib/tasks/handler.ts
|
|
2343
2513
|
var import_node_crypto = require("crypto");
|
|
2344
2514
|
var StepCompleteSignal = class {
|
|
@@ -2355,6 +2525,14 @@ var StepSleepSignal = class {
|
|
|
2355
2525
|
}
|
|
2356
2526
|
_isStepSleepSignal = true;
|
|
2357
2527
|
};
|
|
2528
|
+
var StepWaitEventSignal = class {
|
|
2529
|
+
constructor(stepName, eventName, options = {}) {
|
|
2530
|
+
this.stepName = stepName;
|
|
2531
|
+
this.eventName = eventName;
|
|
2532
|
+
this.options = options;
|
|
2533
|
+
}
|
|
2534
|
+
_isStepWaitEventSignal = true;
|
|
2535
|
+
};
|
|
2358
2536
|
function createStepContext(completedSteps, resolvedWaits) {
|
|
2359
2537
|
return {
|
|
2360
2538
|
/**
|
|
@@ -2381,6 +2559,32 @@ function createStepContext(completedSteps, resolvedWaits) {
|
|
|
2381
2559
|
return;
|
|
2382
2560
|
}
|
|
2383
2561
|
throw new StepSleepSignal(name, duration);
|
|
2562
|
+
},
|
|
2563
|
+
/**
|
|
2564
|
+
* Pause execution until a named event is published via TriggersClient.publishEvent().
|
|
2565
|
+
*
|
|
2566
|
+
* - If event already arrived (platform re-dispatched with result): return event payload.
|
|
2567
|
+
* - If not yet arrived: throw StepWaitEventSignal to pause execution.
|
|
2568
|
+
*
|
|
2569
|
+
* @param name Step identifier (unique within handler).
|
|
2570
|
+
* @param eventName The event name to listen for (e.g. 'user.approved').
|
|
2571
|
+
* @param options Optional timeout ('24h', '7d') and payload filter.
|
|
2572
|
+
*
|
|
2573
|
+
* @example Human-in-the-loop approval
|
|
2574
|
+
* ```typescript
|
|
2575
|
+
* const approval = await step.waitForEvent('wait-approval', 'order.approved', {
|
|
2576
|
+
* timeout: '48h',
|
|
2577
|
+
* filter: { orderId: payload.orderId },
|
|
2578
|
+
* })
|
|
2579
|
+
* if (!approval) throw new Error('Approval timed out')
|
|
2580
|
+
* await sendConfirmation(approval.approvedBy)
|
|
2581
|
+
* ```
|
|
2582
|
+
*/
|
|
2583
|
+
async waitForEvent(name, eventName, options = {}) {
|
|
2584
|
+
if (resolvedWaits.has(name)) {
|
|
2585
|
+
return resolvedWaits.get(name) ?? null;
|
|
2586
|
+
}
|
|
2587
|
+
throw new StepWaitEventSignal(name, eventName, options);
|
|
2384
2588
|
}
|
|
2385
2589
|
};
|
|
2386
2590
|
}
|
|
@@ -2460,9 +2664,9 @@ function createTasksHandler(taskDefs, options = {}) {
|
|
|
2460
2664
|
for (const step of context?.steps ?? []) {
|
|
2461
2665
|
completedSteps.set(step.name, step.result);
|
|
2462
2666
|
}
|
|
2463
|
-
const resolvedWaits = /* @__PURE__ */ new
|
|
2667
|
+
const resolvedWaits = /* @__PURE__ */ new Map();
|
|
2464
2668
|
for (const wait of context?.waits ?? []) {
|
|
2465
|
-
resolvedWaits.
|
|
2669
|
+
resolvedWaits.set(wait.name, wait.result ?? void 0);
|
|
2466
2670
|
}
|
|
2467
2671
|
const stepCtx = createStepContext(completedSteps, resolvedWaits);
|
|
2468
2672
|
try {
|
|
@@ -2485,6 +2689,16 @@ function createTasksHandler(taskDefs, options = {}) {
|
|
|
2485
2689
|
duration: signal.duration
|
|
2486
2690
|
});
|
|
2487
2691
|
}
|
|
2692
|
+
if (err instanceof StepWaitEventSignal || err?._isStepWaitEventSignal) {
|
|
2693
|
+
const signal = err;
|
|
2694
|
+
return Response.json({
|
|
2695
|
+
status: "step_wait_event",
|
|
2696
|
+
stepName: signal.stepName,
|
|
2697
|
+
eventName: signal.eventName,
|
|
2698
|
+
timeout: signal.options.timeout ?? null,
|
|
2699
|
+
filter: signal.options.filter ?? null
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2488
2702
|
const message = err instanceof Error ? err.message : String(err);
|
|
2489
2703
|
console.error(`[sylphx/tasks] Task "${taskName}" threw an error:`, err);
|
|
2490
2704
|
return Response.json(
|
|
@@ -3494,6 +3708,143 @@ var SandboxFiles = class {
|
|
|
3494
3708
|
return data.files;
|
|
3495
3709
|
}
|
|
3496
3710
|
};
|
|
3711
|
+
var SandboxProcesses = class {
|
|
3712
|
+
constructor(endpoint, token) {
|
|
3713
|
+
this.endpoint = endpoint;
|
|
3714
|
+
this.token = token;
|
|
3715
|
+
}
|
|
3716
|
+
authHeader() {
|
|
3717
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
3718
|
+
}
|
|
3719
|
+
/** Spawn a new tracked process. Returns processId + pid immediately. */
|
|
3720
|
+
async start(opts) {
|
|
3721
|
+
const res = await fetch(`${this.endpoint}/process/start`, {
|
|
3722
|
+
method: "POST",
|
|
3723
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3724
|
+
body: JSON.stringify(opts)
|
|
3725
|
+
});
|
|
3726
|
+
if (!res.ok) throw new Error(`process.start failed: ${await res.text()}`);
|
|
3727
|
+
return await res.json();
|
|
3728
|
+
}
|
|
3729
|
+
/** List all tracked processes. */
|
|
3730
|
+
async list() {
|
|
3731
|
+
const res = await fetch(`${this.endpoint}/process/list`, {
|
|
3732
|
+
headers: this.authHeader()
|
|
3733
|
+
});
|
|
3734
|
+
if (!res.ok) throw new Error(`process.list failed: ${await res.text()}`);
|
|
3735
|
+
return await res.json();
|
|
3736
|
+
}
|
|
3737
|
+
/** Get full process info including buffered output. */
|
|
3738
|
+
async get(processId) {
|
|
3739
|
+
const res = await fetch(`${this.endpoint}/process/${processId}`, {
|
|
3740
|
+
headers: this.authHeader()
|
|
3741
|
+
});
|
|
3742
|
+
if (!res.ok) throw new Error(`process.get failed: ${await res.text()}`);
|
|
3743
|
+
return await res.json();
|
|
3744
|
+
}
|
|
3745
|
+
/** Send a signal to a process. */
|
|
3746
|
+
async kill(processId, signal = "SIGTERM") {
|
|
3747
|
+
const res = await fetch(`${this.endpoint}/process/${processId}/kill`, {
|
|
3748
|
+
method: "POST",
|
|
3749
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3750
|
+
body: JSON.stringify({ signal })
|
|
3751
|
+
});
|
|
3752
|
+
if (!res.ok) throw new Error(`process.kill failed: ${await res.text()}`);
|
|
3753
|
+
}
|
|
3754
|
+
/** Write to process stdin. */
|
|
3755
|
+
async writeStdin(processId, data) {
|
|
3756
|
+
const res = await fetch(`${this.endpoint}/process/${processId}/input`, {
|
|
3757
|
+
method: "POST",
|
|
3758
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3759
|
+
body: JSON.stringify({ data })
|
|
3760
|
+
});
|
|
3761
|
+
if (!res.ok) throw new Error(`process.writeStdin failed: ${await res.text()}`);
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Wait for a process to complete and return its final info.
|
|
3765
|
+
* Polls every 500ms until status is no longer 'running'.
|
|
3766
|
+
*
|
|
3767
|
+
* For real-time output, use stream() instead.
|
|
3768
|
+
*/
|
|
3769
|
+
async wait(processId, timeoutMs = 3e5) {
|
|
3770
|
+
const deadline = Date.now() + timeoutMs;
|
|
3771
|
+
while (Date.now() < deadline) {
|
|
3772
|
+
const info = await this.get(processId);
|
|
3773
|
+
if (info.status !== "running") return info;
|
|
3774
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
3775
|
+
}
|
|
3776
|
+
throw new Error(`Timed out waiting for process ${processId} to complete (${timeoutMs}ms)`);
|
|
3777
|
+
}
|
|
3778
|
+
/** Stream process output as async iterable SSE events. */
|
|
3779
|
+
async *stream(processId) {
|
|
3780
|
+
const res = await fetch(
|
|
3781
|
+
`${this.endpoint}/process/${processId}/stream`,
|
|
3782
|
+
{ headers: this.authHeader() }
|
|
3783
|
+
);
|
|
3784
|
+
if (!res.ok) throw new Error(`process.stream failed: ${await res.text()}`);
|
|
3785
|
+
if (!res.body) throw new Error("process.stream: no response body");
|
|
3786
|
+
const decoder = new TextDecoder();
|
|
3787
|
+
const reader = res.body.getReader();
|
|
3788
|
+
let buffer = "";
|
|
3789
|
+
try {
|
|
3790
|
+
while (true) {
|
|
3791
|
+
const { done, value } = await reader.read();
|
|
3792
|
+
if (done) break;
|
|
3793
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3794
|
+
const lines = buffer.split("\n");
|
|
3795
|
+
buffer = lines.pop() ?? "";
|
|
3796
|
+
for (const line of lines) {
|
|
3797
|
+
if (line.startsWith("data: ")) {
|
|
3798
|
+
try {
|
|
3799
|
+
const event = JSON.parse(line.slice(6));
|
|
3800
|
+
yield event;
|
|
3801
|
+
if (event.type === "exit") return;
|
|
3802
|
+
} catch {
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
} finally {
|
|
3808
|
+
reader.releaseLock();
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
var SandboxWatch = class {
|
|
3813
|
+
constructor(endpoint, token) {
|
|
3814
|
+
this.endpoint = endpoint;
|
|
3815
|
+
this.token = token;
|
|
3816
|
+
}
|
|
3817
|
+
authHeader() {
|
|
3818
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
3819
|
+
}
|
|
3820
|
+
/** Start watching a path. Events delivered via sandbox.events() SSE stream. */
|
|
3821
|
+
async add(opts) {
|
|
3822
|
+
const res = await fetch(`${this.endpoint}/watch`, {
|
|
3823
|
+
method: "POST",
|
|
3824
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3825
|
+
body: JSON.stringify(opts)
|
|
3826
|
+
});
|
|
3827
|
+
if (!res.ok) throw new Error(`watch.add failed: ${await res.text()}`);
|
|
3828
|
+
return await res.json();
|
|
3829
|
+
}
|
|
3830
|
+
/** List active watches. */
|
|
3831
|
+
async list() {
|
|
3832
|
+
const res = await fetch(`${this.endpoint}/watch`, {
|
|
3833
|
+
headers: this.authHeader()
|
|
3834
|
+
});
|
|
3835
|
+
if (!res.ok) throw new Error(`watch.list failed: ${await res.text()}`);
|
|
3836
|
+
const data = await res.json();
|
|
3837
|
+
return data.watches;
|
|
3838
|
+
}
|
|
3839
|
+
/** Stop watching a path. */
|
|
3840
|
+
async remove(path) {
|
|
3841
|
+
const res = await fetch(`${this.endpoint}/watch?path=${encodeURIComponent(path)}`, {
|
|
3842
|
+
method: "DELETE",
|
|
3843
|
+
headers: this.authHeader()
|
|
3844
|
+
});
|
|
3845
|
+
if (!res.ok) throw new Error(`watch.remove failed: ${await res.text()}`);
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3497
3848
|
var SandboxClient = class _SandboxClient {
|
|
3498
3849
|
id;
|
|
3499
3850
|
config;
|
|
@@ -3503,12 +3854,18 @@ var SandboxClient = class _SandboxClient {
|
|
|
3503
3854
|
token;
|
|
3504
3855
|
/** File operations (direct to exec-server) */
|
|
3505
3856
|
files;
|
|
3857
|
+
/** Concurrent process management (direct to exec-server) */
|
|
3858
|
+
processes;
|
|
3859
|
+
/** Filesystem watch management (direct to exec-server) */
|
|
3860
|
+
watch;
|
|
3506
3861
|
constructor(id, config, endpoint, token) {
|
|
3507
3862
|
this.id = id;
|
|
3508
3863
|
this.config = config;
|
|
3509
3864
|
this.endpoint = endpoint;
|
|
3510
3865
|
this.token = token;
|
|
3511
3866
|
this.files = endpoint && token ? new SandboxFiles(endpoint, token) : null;
|
|
3867
|
+
this.processes = endpoint && token ? new SandboxProcesses(endpoint, token) : null;
|
|
3868
|
+
this.watch = endpoint && token ? new SandboxWatch(endpoint, token) : null;
|
|
3512
3869
|
}
|
|
3513
3870
|
// ---------------------------------------------------------------------------
|
|
3514
3871
|
// Factory
|
|
@@ -3528,7 +3885,8 @@ var SandboxClient = class _SandboxClient {
|
|
|
3528
3885
|
idleTimeoutMs: options?.idleTimeoutMs ?? 3e5,
|
|
3529
3886
|
resources: options?.resources,
|
|
3530
3887
|
env: options?.env,
|
|
3531
|
-
storage: options?.storageGi !== void 0 ? { enabled: true, sizeGi: options.storageGi } : void 0
|
|
3888
|
+
storage: options?.storageGi !== void 0 ? { enabled: true, sizeGi: options.storageGi } : void 0,
|
|
3889
|
+
volumeMounts: options?.volumeMounts
|
|
3532
3890
|
}
|
|
3533
3891
|
});
|
|
3534
3892
|
return new _SandboxClient(record.id, config, record.endpoint, record.token);
|
|
@@ -3556,10 +3914,21 @@ var SandboxClient = class _SandboxClient {
|
|
|
3556
3914
|
// Exec — SSE streaming (primary)
|
|
3557
3915
|
// ---------------------------------------------------------------------------
|
|
3558
3916
|
/**
|
|
3559
|
-
* Execute a command and stream output as async iterable events.
|
|
3917
|
+
* Execute a command and stream output as async iterable SSE events.
|
|
3918
|
+
*
|
|
3919
|
+
* **Stateless mode**: each exec() call runs in an isolated bash invocation.
|
|
3920
|
+
* Shell state (CWD changes, exported env vars, functions) is NOT preserved
|
|
3921
|
+
* between calls.
|
|
3560
3922
|
*
|
|
3561
|
-
*
|
|
3562
|
-
*
|
|
3923
|
+
* For state-preserving execution (CWD, env), use `run()` which runs in the
|
|
3924
|
+
* persistent active shell and returns the result once complete.
|
|
3925
|
+
*
|
|
3926
|
+
* For streaming + state-preserving (advanced), combine `sandbox.events()` with `run()`:
|
|
3927
|
+
* ```typescript
|
|
3928
|
+
* const eventStream = sandbox.events({ type: 'stdout' })
|
|
3929
|
+
* sandbox.run(['npm', 'install']) // don't await yet
|
|
3930
|
+
* for await (const ev of eventStream) { ... }
|
|
3931
|
+
* ```
|
|
3563
3932
|
*
|
|
3564
3933
|
* @example
|
|
3565
3934
|
* ```typescript
|
|
@@ -3571,13 +3940,13 @@ var SandboxClient = class _SandboxClient {
|
|
|
3571
3940
|
*/
|
|
3572
3941
|
async *exec(command, options) {
|
|
3573
3942
|
this.assertDirect();
|
|
3574
|
-
const res = await fetch(`${this.endpoint}/exec
|
|
3943
|
+
const res = await fetch(`${this.endpoint}/exec`, {
|
|
3575
3944
|
method: "POST",
|
|
3576
3945
|
headers: {
|
|
3577
3946
|
Authorization: `Bearer ${this.token}`,
|
|
3578
3947
|
"Content-Type": "application/json"
|
|
3579
3948
|
},
|
|
3580
|
-
body: JSON.stringify({ command, ...options })
|
|
3949
|
+
body: JSON.stringify({ command, ...options, stateless: true, stream: true })
|
|
3581
3950
|
});
|
|
3582
3951
|
if (!res.ok) {
|
|
3583
3952
|
throw new Error(`exec failed (${res.status}): ${await res.text()}`);
|
|
@@ -3630,6 +3999,58 @@ var SandboxClient = class _SandboxClient {
|
|
|
3630
3999
|
return { stdout, stderr, exitCode, durationMs };
|
|
3631
4000
|
}
|
|
3632
4001
|
// ---------------------------------------------------------------------------
|
|
4002
|
+
// Events — Unified SSE stream
|
|
4003
|
+
// ---------------------------------------------------------------------------
|
|
4004
|
+
/**
|
|
4005
|
+
* Subscribe to the unified event stream (SSE).
|
|
4006
|
+
*
|
|
4007
|
+
* Receives all sandbox events: stdout, stderr, exit, port, file, shell, resource.
|
|
4008
|
+
* Filter by type/pid/shellId using query params.
|
|
4009
|
+
*
|
|
4010
|
+
* @example
|
|
4011
|
+
* ```typescript
|
|
4012
|
+
* for await (const event of sandbox.events({ type: 'file' })) {
|
|
4013
|
+
* console.log('File changed:', event.path, event.event)
|
|
4014
|
+
* }
|
|
4015
|
+
* ```
|
|
4016
|
+
*/
|
|
4017
|
+
async *events(filter) {
|
|
4018
|
+
this.assertDirect();
|
|
4019
|
+
const params = new URLSearchParams();
|
|
4020
|
+
if (filter?.type) params.set("type", filter.type);
|
|
4021
|
+
if (filter?.pid !== void 0) params.set("pid", String(filter.pid));
|
|
4022
|
+
if (filter?.shellId) params.set("shellId", filter.shellId);
|
|
4023
|
+
const qs = params.toString();
|
|
4024
|
+
const url = `${this.endpoint}/events${qs ? `?${qs}` : ""}`;
|
|
4025
|
+
const res = await fetch(url, {
|
|
4026
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
4027
|
+
});
|
|
4028
|
+
if (!res.ok) throw new Error(`events failed (${res.status}): ${await res.text()}`);
|
|
4029
|
+
if (!res.body) throw new Error("events: no response body");
|
|
4030
|
+
const decoder = new TextDecoder();
|
|
4031
|
+
const reader = res.body.getReader();
|
|
4032
|
+
let buffer = "";
|
|
4033
|
+
try {
|
|
4034
|
+
while (true) {
|
|
4035
|
+
const { done, value } = await reader.read();
|
|
4036
|
+
if (done) break;
|
|
4037
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4038
|
+
const lines = buffer.split("\n");
|
|
4039
|
+
buffer = lines.pop() ?? "";
|
|
4040
|
+
for (const line of lines) {
|
|
4041
|
+
if (line.startsWith("data: ")) {
|
|
4042
|
+
try {
|
|
4043
|
+
yield JSON.parse(line.slice(6));
|
|
4044
|
+
} catch {
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
} finally {
|
|
4050
|
+
reader.releaseLock();
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
// ---------------------------------------------------------------------------
|
|
3633
4054
|
// PTY — Interactive terminal (WebSocket)
|
|
3634
4055
|
// ---------------------------------------------------------------------------
|
|
3635
4056
|
/**
|
|
@@ -3675,7 +4096,7 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3675
4096
|
]);
|
|
3676
4097
|
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
3677
4098
|
var DEFAULT_WAIT_TIMEOUT_MS = 72e5;
|
|
3678
|
-
var
|
|
4099
|
+
var RunHandle = class {
|
|
3679
4100
|
id;
|
|
3680
4101
|
config;
|
|
3681
4102
|
constructor(id, config) {
|
|
@@ -3699,7 +4120,7 @@ var WorkerHandle = class {
|
|
|
3699
4120
|
*
|
|
3700
4121
|
* @param options.pollIntervalMs - How often to poll in ms (default: 3000)
|
|
3701
4122
|
* @param options.timeoutMs - Max time to wait before throwing (default: 7_200_000 = 2h)
|
|
3702
|
-
* @returns
|
|
4123
|
+
* @returns RunResult with exit code, status, stdout/stderr
|
|
3703
4124
|
* @throws Error if waitTimeout is exceeded
|
|
3704
4125
|
*
|
|
3705
4126
|
* @example
|
|
@@ -3767,7 +4188,7 @@ var WorkerHandle = class {
|
|
|
3767
4188
|
await callApi(this.config, `/workers/${this.id}`, { method: "DELETE" });
|
|
3768
4189
|
}
|
|
3769
4190
|
};
|
|
3770
|
-
var
|
|
4191
|
+
var RunsClient = {
|
|
3771
4192
|
// --------------------------------------------------------------------------
|
|
3772
4193
|
// Run
|
|
3773
4194
|
// --------------------------------------------------------------------------
|
|
@@ -3779,7 +4200,7 @@ var WorkersClient = {
|
|
|
3779
4200
|
*
|
|
3780
4201
|
* @example
|
|
3781
4202
|
* ```typescript
|
|
3782
|
-
* const
|
|
4203
|
+
* const run = await RunsClient.create(config, {
|
|
3783
4204
|
* image: 'registry.sylphx.com/sylphx/trainer:abc123',
|
|
3784
4205
|
* command: ['python', 'train.py', '--fold', '3'],
|
|
3785
4206
|
* resources: { requests: { cpu: '4', memory: '16Gi' } },
|
|
@@ -3789,7 +4210,7 @@ var WorkersClient = {
|
|
|
3789
4210
|
* ```
|
|
3790
4211
|
*/
|
|
3791
4212
|
async run(config, options) {
|
|
3792
|
-
const run = await callApi(config, "/
|
|
4213
|
+
const run = await callApi(config, "/runs", {
|
|
3793
4214
|
method: "POST",
|
|
3794
4215
|
body: {
|
|
3795
4216
|
image: options.image,
|
|
@@ -3800,25 +4221,25 @@ var WorkersClient = {
|
|
|
3800
4221
|
volumeMounts: options.volumeMounts
|
|
3801
4222
|
}
|
|
3802
4223
|
});
|
|
3803
|
-
return new
|
|
4224
|
+
return new RunHandle(run.id, config);
|
|
3804
4225
|
},
|
|
3805
4226
|
// --------------------------------------------------------------------------
|
|
3806
4227
|
// Get
|
|
3807
4228
|
// --------------------------------------------------------------------------
|
|
3808
4229
|
/**
|
|
3809
|
-
* Get a
|
|
4230
|
+
* Get a RunHandle for an existing run by ID.
|
|
3810
4231
|
*
|
|
3811
4232
|
* Useful for resuming monitoring across requests.
|
|
3812
4233
|
*
|
|
3813
4234
|
* @example
|
|
3814
4235
|
* ```typescript
|
|
3815
4236
|
* // Store the worker ID, retrieve later
|
|
3816
|
-
* const handle =
|
|
4237
|
+
* const handle = RunsClient.fromId(config, storedWorkerId)
|
|
3817
4238
|
* const result = await handle.wait()
|
|
3818
4239
|
* ```
|
|
3819
4240
|
*/
|
|
3820
4241
|
fromId(config, workerId) {
|
|
3821
|
-
return new
|
|
4242
|
+
return new RunHandle(workerId, config);
|
|
3822
4243
|
},
|
|
3823
4244
|
// --------------------------------------------------------------------------
|
|
3824
4245
|
// List
|
|
@@ -3828,12 +4249,12 @@ var WorkersClient = {
|
|
|
3828
4249
|
*
|
|
3829
4250
|
* @example
|
|
3830
4251
|
* ```typescript
|
|
3831
|
-
* const { workers } = await
|
|
4252
|
+
* const { workers } = await RunsClient.list(config, { status: 'running' })
|
|
3832
4253
|
* console.log(`${workers.length} workers currently running`)
|
|
3833
4254
|
* ```
|
|
3834
4255
|
*/
|
|
3835
4256
|
async list(config, options) {
|
|
3836
|
-
return callApi(config, "/
|
|
4257
|
+
return callApi(config, "/runs", {
|
|
3837
4258
|
method: "GET",
|
|
3838
4259
|
query: options?.status ? { status: options.status } : void 0
|
|
3839
4260
|
});
|
|
@@ -3844,11 +4265,11 @@ var WorkersClient = {
|
|
|
3844
4265
|
/**
|
|
3845
4266
|
* Spawn a worker and wait for it to complete in one call.
|
|
3846
4267
|
*
|
|
3847
|
-
* Equivalent to `(await
|
|
4268
|
+
* Equivalent to `(await RunsClient.create(config, options)).wait(waitOptions)`.
|
|
3848
4269
|
*
|
|
3849
4270
|
* @example
|
|
3850
4271
|
* ```typescript
|
|
3851
|
-
* const result = await
|
|
4272
|
+
* const result = await RunsClient.runAndWait(config, {
|
|
3852
4273
|
* image: 'registry.sylphx.com/sylphx/process:abc',
|
|
3853
4274
|
* command: ['node', 'dist/process.js'],
|
|
3854
4275
|
* })
|
|
@@ -3856,13 +4277,14 @@ var WorkersClient = {
|
|
|
3856
4277
|
* ```
|
|
3857
4278
|
*/
|
|
3858
4279
|
async runAndWait(config, options, waitOptions) {
|
|
3859
|
-
const handle = await
|
|
4280
|
+
const handle = await RunsClient.run(config, options);
|
|
3860
4281
|
return handle.wait(waitOptions);
|
|
3861
4282
|
}
|
|
3862
4283
|
};
|
|
3863
4284
|
function sleep2(ms) {
|
|
3864
4285
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3865
4286
|
}
|
|
4287
|
+
var WorkersClient = RunsClient;
|
|
3866
4288
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3867
4289
|
0 && (module.exports = {
|
|
3868
4290
|
ACHIEVEMENT_TIER_CONFIG,
|
|
@@ -3870,15 +4292,22 @@ function sleep2(ms) {
|
|
|
3870
4292
|
AuthorizationError,
|
|
3871
4293
|
CircuitBreakerOpenError,
|
|
3872
4294
|
ERROR_CODE_STATUS,
|
|
4295
|
+
InvalidConnectionUrlError,
|
|
3873
4296
|
NetworkError,
|
|
3874
4297
|
NotFoundError,
|
|
3875
4298
|
RETRYABLE_CODES,
|
|
3876
4299
|
RateLimitError,
|
|
4300
|
+
RunHandle,
|
|
4301
|
+
RunsClient,
|
|
3877
4302
|
SandboxClient,
|
|
4303
|
+
SandboxFiles,
|
|
4304
|
+
SandboxProcesses,
|
|
4305
|
+
SandboxWatch,
|
|
3878
4306
|
StepCompleteSignal,
|
|
3879
4307
|
StepSleepSignal,
|
|
3880
4308
|
SylphxError,
|
|
3881
4309
|
TimeoutError,
|
|
4310
|
+
TriggersClient,
|
|
3882
4311
|
ValidationError,
|
|
3883
4312
|
WorkerHandle,
|
|
3884
4313
|
WorkersClient,
|
|
@@ -3899,6 +4328,7 @@ function sleep2(ms) {
|
|
|
3899
4328
|
checkFlag,
|
|
3900
4329
|
complete,
|
|
3901
4330
|
createCheckout,
|
|
4331
|
+
createClient,
|
|
3902
4332
|
createConfig,
|
|
3903
4333
|
createCron,
|
|
3904
4334
|
createDynamicRestClient,
|
|
@@ -3907,6 +4337,7 @@ function sleep2(ms) {
|
|
|
3907
4337
|
createPortalSession,
|
|
3908
4338
|
createRestClient,
|
|
3909
4339
|
createRole,
|
|
4340
|
+
createServerClient,
|
|
3910
4341
|
createServiceWorkerScript,
|
|
3911
4342
|
createStepContext,
|
|
3912
4343
|
createTasksHandler,
|
|
@@ -4036,7 +4467,6 @@ function sleep2(ms) {
|
|
|
4036
4467
|
listTasks,
|
|
4037
4468
|
listUsers,
|
|
4038
4469
|
page,
|
|
4039
|
-
parseKey,
|
|
4040
4470
|
pauseCron,
|
|
4041
4471
|
realtimeEmit,
|
|
4042
4472
|
recordStreakActivity,
|