@walkeros/cli 4.2.0 → 4.2.1-next-1781526381392
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/CHANGELOG.md +34 -0
- package/dist/cli.js +315 -79
- package/dist/index.d.ts +855 -119
- package/dist/index.js +330 -85
- package/dist/index.js.map +1 -1
- package/openapi/spec.json +2599 -1580
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -2755,6 +2755,9 @@ ${firstError.text}`
|
|
|
2755
2755
|
` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
|
|
2756
2756
|
);
|
|
2757
2757
|
}
|
|
2758
|
+
function buildDataPayload(flowSettings) {
|
|
2759
|
+
return buildSplitConfigObject(flowSettings, detectNamedImports(flowSettings)).dataPayloadObj;
|
|
2760
|
+
}
|
|
2758
2761
|
function buildSplitConfigObject(flowSettings, namedImports) {
|
|
2759
2762
|
const sources = flowSettings.sources || {};
|
|
2760
2763
|
const destinations = flowSettings.destinations || {};
|
|
@@ -2777,7 +2780,7 @@ function buildSplitConfigObject(flowSettings, namedImports) {
|
|
|
2777
2780
|
}
|
|
2778
2781
|
return props;
|
|
2779
2782
|
}
|
|
2780
|
-
function buildSplitStepEntry(section,
|
|
2783
|
+
function buildSplitStepEntry(section, stepId2, step) {
|
|
2781
2784
|
const codeVar = resolveCodeVar(step);
|
|
2782
2785
|
const stepProps = getStepProps(step);
|
|
2783
2786
|
const { codeProps, dataProps } = classifyStepProperties(stepProps);
|
|
@@ -2788,13 +2791,13 @@ function buildSplitConfigObject(flowSettings, namedImports) {
|
|
|
2788
2791
|
codeEntries.push(`${key}: ${processConfigValue(value)}`);
|
|
2789
2792
|
}
|
|
2790
2793
|
for (const key of Object.keys(dataProps)) {
|
|
2791
|
-
codeEntries.push(`${key}: __data.${section}.${
|
|
2794
|
+
codeEntries.push(`${key}: __data.${section}.${stepId2}.${key}`);
|
|
2792
2795
|
}
|
|
2793
2796
|
if (Object.keys(dataProps).length > 0) {
|
|
2794
2797
|
if (!dataPayloadObj[section]) dataPayloadObj[section] = {};
|
|
2795
|
-
dataPayloadObj[section][
|
|
2798
|
+
dataPayloadObj[section][stepId2] = dataProps;
|
|
2796
2799
|
}
|
|
2797
|
-
return ` ${
|
|
2800
|
+
return ` ${stepId2}: {
|
|
2798
2801
|
${codeEntries.join(",\n ")}
|
|
2799
2802
|
}`;
|
|
2800
2803
|
}
|
|
@@ -2904,7 +2907,7 @@ ${destinationsEntries.join(",\n")}
|
|
|
2904
2907
|
stores${collectorStr}
|
|
2905
2908
|
}`;
|
|
2906
2909
|
const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
|
|
2907
|
-
return { storesDeclaration, codeConfigObject, dataPayload };
|
|
2910
|
+
return { storesDeclaration, codeConfigObject, dataPayloadObj, dataPayload };
|
|
2908
2911
|
}
|
|
2909
2912
|
function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
|
|
2910
2913
|
const codeSection = userCode ? `
|
|
@@ -3848,7 +3851,8 @@ import {
|
|
|
3848
3851
|
createIngest,
|
|
3849
3852
|
getPlatform as getPlatform3,
|
|
3850
3853
|
getNextSteps,
|
|
3851
|
-
buildCacheContext
|
|
3854
|
+
buildCacheContext,
|
|
3855
|
+
stepId
|
|
3852
3856
|
} from "@walkeros/core";
|
|
3853
3857
|
import {
|
|
3854
3858
|
enrichEvent,
|
|
@@ -4052,13 +4056,13 @@ function installTimerInterception(options = {}) {
|
|
|
4052
4056
|
setInterval: Reflect.get(target, "setInterval"),
|
|
4053
4057
|
clearInterval: Reflect.get(target, "clearInterval")
|
|
4054
4058
|
});
|
|
4055
|
-
const trackedSetTimeout = (callback,
|
|
4059
|
+
const trackedSetTimeout = (callback, delay2, ...args) => {
|
|
4056
4060
|
if (typeof callback !== "function") return 0;
|
|
4057
4061
|
const id = nextId++;
|
|
4058
4062
|
pending.set(id, {
|
|
4059
4063
|
id,
|
|
4060
4064
|
callback,
|
|
4061
|
-
delay:
|
|
4065
|
+
delay: delay2 ?? 0,
|
|
4062
4066
|
type: "timeout",
|
|
4063
4067
|
args,
|
|
4064
4068
|
cleared: false
|
|
@@ -4071,13 +4075,13 @@ function installTimerInterception(options = {}) {
|
|
|
4071
4075
|
const entry = pending.get(numId);
|
|
4072
4076
|
if (entry) entry.cleared = true;
|
|
4073
4077
|
};
|
|
4074
|
-
const trackedSetInterval = (callback,
|
|
4078
|
+
const trackedSetInterval = (callback, delay2, ...args) => {
|
|
4075
4079
|
if (typeof callback !== "function") return 0;
|
|
4076
4080
|
const id = nextId++;
|
|
4077
4081
|
pending.set(id, {
|
|
4078
4082
|
id,
|
|
4079
4083
|
callback,
|
|
4080
|
-
delay:
|
|
4084
|
+
delay: delay2 ?? 0,
|
|
4081
4085
|
type: "interval",
|
|
4082
4086
|
args,
|
|
4083
4087
|
cleared: false
|
|
@@ -4354,7 +4358,7 @@ function toError(error) {
|
|
|
4354
4358
|
return error instanceof Error ? error : new Error(getErrorMessage(error));
|
|
4355
4359
|
}
|
|
4356
4360
|
function buildSimulationResult(args) {
|
|
4357
|
-
const { step, name, startTime, captured, usage, error } = args;
|
|
4361
|
+
const { step, name, startTime, captured, usage, mappingKey, error } = args;
|
|
4358
4362
|
const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
|
|
4359
4363
|
const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
|
|
4360
4364
|
return {
|
|
@@ -4363,6 +4367,7 @@ function buildSimulationResult(args) {
|
|
|
4363
4367
|
events,
|
|
4364
4368
|
calls,
|
|
4365
4369
|
duration: Date.now() - startTime,
|
|
4370
|
+
...mappingKey !== void 0 ? { mappingKey } : {},
|
|
4366
4371
|
...error !== void 0 ? { error: toError(error) } : {}
|
|
4367
4372
|
};
|
|
4368
4373
|
}
|
|
@@ -4956,7 +4961,9 @@ async function simulateSource(configOrPath, input, options) {
|
|
|
4956
4961
|
`Source package "${sourceConfig.package}" has no createTrigger in /dev export`
|
|
4957
4962
|
);
|
|
4958
4963
|
}
|
|
4959
|
-
const flowConfig = module.wireConfig(
|
|
4964
|
+
const flowConfig = module.wireConfig(
|
|
4965
|
+
options.data ?? module.__configData ?? void 0
|
|
4966
|
+
);
|
|
4960
4967
|
applyOverrides(flowConfig, prepared.overrides);
|
|
4961
4968
|
const captured = [];
|
|
4962
4969
|
flowConfig.hooks = {
|
|
@@ -5061,7 +5068,9 @@ async function simulateTransformer(configOrPath, event, options) {
|
|
|
5061
5068
|
networkCalls
|
|
5062
5069
|
},
|
|
5063
5070
|
async (module) => {
|
|
5064
|
-
const flowConfig = module.wireConfig(
|
|
5071
|
+
const flowConfig = module.wireConfig(
|
|
5072
|
+
options.data ?? module.__configData ?? void 0
|
|
5073
|
+
);
|
|
5065
5074
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5066
5075
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
5067
5076
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -5227,7 +5236,9 @@ async function simulateCollector(configOrPath, event, options) {
|
|
|
5227
5236
|
networkCalls
|
|
5228
5237
|
},
|
|
5229
5238
|
async (module) => {
|
|
5230
|
-
const flowConfig = module.wireConfig(
|
|
5239
|
+
const flowConfig = module.wireConfig(
|
|
5240
|
+
options.data ?? module.__configData ?? void 0
|
|
5241
|
+
);
|
|
5231
5242
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5232
5243
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
5233
5244
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -5330,7 +5341,9 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5330
5341
|
networkCalls
|
|
5331
5342
|
},
|
|
5332
5343
|
async (module) => {
|
|
5333
|
-
const flowConfig = module.wireConfig(
|
|
5344
|
+
const flowConfig = module.wireConfig(
|
|
5345
|
+
options.data ?? module.__configData ?? void 0
|
|
5346
|
+
);
|
|
5334
5347
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5335
5348
|
const destPkg = (prepared.flowSettings.destinations ?? {})[options.destinationId];
|
|
5336
5349
|
let trackedCalls = [];
|
|
@@ -5366,6 +5379,16 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5366
5379
|
);
|
|
5367
5380
|
}
|
|
5368
5381
|
logger.info(`Simulating destination: ${options.destinationId}`);
|
|
5382
|
+
let mappingKey;
|
|
5383
|
+
const targetStepId = stepId("destination", options.destinationId);
|
|
5384
|
+
const captureMappingKey = (state) => {
|
|
5385
|
+
if (state.stepId === targetStepId && state.mappingKey) {
|
|
5386
|
+
mappingKey = state.mappingKey;
|
|
5387
|
+
}
|
|
5388
|
+
};
|
|
5389
|
+
if (collector.observers instanceof Set) {
|
|
5390
|
+
collector.observers.add(captureMappingKey);
|
|
5391
|
+
}
|
|
5369
5392
|
await collector.push(event, {
|
|
5370
5393
|
include: [options.destinationId]
|
|
5371
5394
|
});
|
|
@@ -5374,7 +5397,8 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5374
5397
|
step: "destination",
|
|
5375
5398
|
name: options.destinationId,
|
|
5376
5399
|
startTime,
|
|
5377
|
-
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
|
|
5400
|
+
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0,
|
|
5401
|
+
mappingKey
|
|
5378
5402
|
});
|
|
5379
5403
|
},
|
|
5380
5404
|
(error) => buildSimulationResult({
|
|
@@ -5419,8 +5443,117 @@ import {
|
|
|
5419
5443
|
import { dirname as dirname3, join as join4 } from "path";
|
|
5420
5444
|
import { Readable } from "stream";
|
|
5421
5445
|
import { x as tarExtract } from "tar";
|
|
5446
|
+
|
|
5447
|
+
// src/runtime/fetch-retry.ts
|
|
5448
|
+
var DEFAULT_PER_ATTEMPT_TIMEOUT_MS = 3e4;
|
|
5449
|
+
var DEFAULT_MAX_TOTAL_MS = 6e4;
|
|
5450
|
+
var DEFAULT_ATTEMPTS = 3;
|
|
5451
|
+
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
5452
|
+
var BASE_BACKOFF_MS = [2e3, 5e3];
|
|
5453
|
+
var JITTER = 0.2;
|
|
5454
|
+
var RETRYABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
5455
|
+
"ECONNRESET",
|
|
5456
|
+
"ECONNREFUSED",
|
|
5457
|
+
"ETIMEDOUT",
|
|
5458
|
+
"EAI_AGAIN",
|
|
5459
|
+
"ENOTFOUND"
|
|
5460
|
+
]);
|
|
5461
|
+
function readErrorCode(value) {
|
|
5462
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5463
|
+
const code = Reflect.get(value, "code");
|
|
5464
|
+
return typeof code === "string" ? code : void 0;
|
|
5465
|
+
}
|
|
5466
|
+
function readErrorName(value) {
|
|
5467
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5468
|
+
const name = Reflect.get(value, "name");
|
|
5469
|
+
return typeof name === "string" ? name : void 0;
|
|
5470
|
+
}
|
|
5471
|
+
function isTransientThrow(error) {
|
|
5472
|
+
const name = readErrorName(error);
|
|
5473
|
+
if (name === "TimeoutError" || name === "AbortError") return true;
|
|
5474
|
+
const directCode = readErrorCode(error);
|
|
5475
|
+
if (directCode && RETRYABLE_NETWORK_CODES.has(directCode)) return true;
|
|
5476
|
+
if (typeof error === "object" && error !== null) {
|
|
5477
|
+
const causeCode = readErrorCode(Reflect.get(error, "cause"));
|
|
5478
|
+
if (causeCode && RETRYABLE_NETWORK_CODES.has(causeCode)) return true;
|
|
5479
|
+
}
|
|
5480
|
+
return error instanceof TypeError;
|
|
5481
|
+
}
|
|
5482
|
+
function isTransientStatus(status) {
|
|
5483
|
+
return status >= 500 || status === 429;
|
|
5484
|
+
}
|
|
5485
|
+
function readErrorMessage(value) {
|
|
5486
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5487
|
+
const message = Reflect.get(value, "message");
|
|
5488
|
+
return typeof message === "string" ? message : void 0;
|
|
5489
|
+
}
|
|
5490
|
+
function describeReason(reason) {
|
|
5491
|
+
if (reason.kind === "status") return `HTTP ${reason.status}`;
|
|
5492
|
+
const name = readErrorName(reason.error);
|
|
5493
|
+
const message = reason.error instanceof Error ? reason.error.message : String(reason.error);
|
|
5494
|
+
let detail = name ? `${name}: ${message}` : message;
|
|
5495
|
+
if (typeof reason.error === "object" && reason.error !== null) {
|
|
5496
|
+
const cause = Reflect.get(reason.error, "cause");
|
|
5497
|
+
const causeCode = readErrorCode(cause);
|
|
5498
|
+
const causeMessage = readErrorMessage(cause);
|
|
5499
|
+
const causeDetail = causeCode ?? causeMessage;
|
|
5500
|
+
if (causeDetail) detail = `${detail} (${causeDetail})`;
|
|
5501
|
+
}
|
|
5502
|
+
return detail;
|
|
5503
|
+
}
|
|
5504
|
+
function backoffForAttempt(index) {
|
|
5505
|
+
const base = BASE_BACKOFF_MS[Math.min(index, BASE_BACKOFF_MS.length - 1)] ?? 0;
|
|
5506
|
+
const spread = base * JITTER;
|
|
5507
|
+
return base + (Math.random() * 2 - 1) * spread;
|
|
5508
|
+
}
|
|
5509
|
+
function delay(ms) {
|
|
5510
|
+
return new Promise((resolve5) => {
|
|
5511
|
+
setTimeout(resolve5, ms);
|
|
5512
|
+
});
|
|
5513
|
+
}
|
|
5514
|
+
async function fetchWithRetry(url, options = {}) {
|
|
5515
|
+
const attempts = options.attempts ?? DEFAULT_ATTEMPTS;
|
|
5516
|
+
const perAttemptTimeoutMs = options.perAttemptTimeoutMs ?? DEFAULT_PER_ATTEMPT_TIMEOUT_MS;
|
|
5517
|
+
const maxTotalMs = options.maxTotalMs ?? DEFAULT_MAX_TOTAL_MS;
|
|
5518
|
+
const init = options.init;
|
|
5519
|
+
const start = Date.now();
|
|
5520
|
+
let lastReason;
|
|
5521
|
+
let made = 0;
|
|
5522
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
5523
|
+
const remaining = maxTotalMs - (Date.now() - start);
|
|
5524
|
+
if (remaining <= MIN_ATTEMPT_BUDGET_MS) break;
|
|
5525
|
+
const attemptTimeoutMs = Math.min(perAttemptTimeoutMs, remaining);
|
|
5526
|
+
made = attempt + 1;
|
|
5527
|
+
let reason;
|
|
5528
|
+
try {
|
|
5529
|
+
const response = await fetch(url, {
|
|
5530
|
+
...init,
|
|
5531
|
+
signal: AbortSignal.timeout(attemptTimeoutMs)
|
|
5532
|
+
});
|
|
5533
|
+
if (!isTransientStatus(response.status)) return response;
|
|
5534
|
+
await response.body?.cancel();
|
|
5535
|
+
reason = { kind: "status", status: response.status };
|
|
5536
|
+
} catch (error) {
|
|
5537
|
+
if (!isTransientThrow(error)) throw error;
|
|
5538
|
+
reason = { kind: "throw", error };
|
|
5539
|
+
}
|
|
5540
|
+
lastReason = reason;
|
|
5541
|
+
const isLastAttempt = attempt === attempts - 1;
|
|
5542
|
+
const budgetSpent = Date.now() - start >= maxTotalMs;
|
|
5543
|
+
if (isLastAttempt || budgetSpent) break;
|
|
5544
|
+
const sleepMs = Math.min(
|
|
5545
|
+
backoffForAttempt(attempt),
|
|
5546
|
+
maxTotalMs - (Date.now() - start)
|
|
5547
|
+
);
|
|
5548
|
+
if (sleepMs <= 0) break;
|
|
5549
|
+
await delay(sleepMs);
|
|
5550
|
+
}
|
|
5551
|
+
const cause = lastReason ? describeReason(lastReason) : "no attempts made";
|
|
5552
|
+
throw new Error(`Fetch failed after ${made} attempts: ${cause}`);
|
|
5553
|
+
}
|
|
5554
|
+
|
|
5555
|
+
// src/runtime/resolve-bundle.ts
|
|
5422
5556
|
var ARCHIVE_ENTRY = "flow.mjs";
|
|
5423
|
-
var FETCH_TIMEOUT_MS = 3e4;
|
|
5424
5557
|
function getDefaultWritePath() {
|
|
5425
5558
|
if (existsSync2("/app/flow")) return "/app/flow/flow.mjs";
|
|
5426
5559
|
return "/tmp/walkeros-flow.mjs";
|
|
@@ -5445,9 +5578,7 @@ function writeBundleToDisk(writePath, content) {
|
|
|
5445
5578
|
writeFileSync2(writePath, content, "utf-8");
|
|
5446
5579
|
}
|
|
5447
5580
|
async function fetchOk(url) {
|
|
5448
|
-
const response = await
|
|
5449
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
5450
|
-
});
|
|
5581
|
+
const response = await fetchWithRetry(url);
|
|
5451
5582
|
if (!response.ok) {
|
|
5452
5583
|
throw new Error(
|
|
5453
5584
|
`Failed to fetch bundle from ${url}: ${response.status} ${response.statusText}`
|
|
@@ -5568,10 +5699,7 @@ async function fetchConfig(options) {
|
|
|
5568
5699
|
options.token,
|
|
5569
5700
|
options.lastEtag ? { "If-None-Match": options.lastEtag } : void 0
|
|
5570
5701
|
);
|
|
5571
|
-
const response = await
|
|
5572
|
-
headers,
|
|
5573
|
-
signal: AbortSignal.timeout(3e4)
|
|
5574
|
-
});
|
|
5702
|
+
const response = await fetchWithRetry(url, { init: { headers } });
|
|
5575
5703
|
if (response.status === 304) {
|
|
5576
5704
|
return { changed: false };
|
|
5577
5705
|
}
|
|
@@ -6113,8 +6241,11 @@ var SecretsHttpError = class extends Error {
|
|
|
6113
6241
|
async function fetchSecrets(options) {
|
|
6114
6242
|
const { appUrl, token, projectId, flowId } = options;
|
|
6115
6243
|
const url = `${appUrl}/api/projects/${encodeURIComponent(projectId)}/flows/${encodeURIComponent(flowId)}/secrets/values`;
|
|
6116
|
-
const res = await
|
|
6117
|
-
|
|
6244
|
+
const res = await fetchWithRetry(url, {
|
|
6245
|
+
maxTotalMs: 2e4,
|
|
6246
|
+
init: {
|
|
6247
|
+
headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
|
|
6248
|
+
}
|
|
6118
6249
|
});
|
|
6119
6250
|
await throwIfRunnerAuthFailure(res);
|
|
6120
6251
|
if (!res.ok) {
|
|
@@ -6129,13 +6260,21 @@ init_cache();
|
|
|
6129
6260
|
async function runPipeline(options) {
|
|
6130
6261
|
const { bundlePath, port, logger, loggerConfig, api } = options;
|
|
6131
6262
|
let configVersion;
|
|
6263
|
+
const configFrozen = readConfigFrozen();
|
|
6132
6264
|
if (api) {
|
|
6133
6265
|
await injectSecrets(api, logger);
|
|
6134
6266
|
}
|
|
6135
6267
|
logger.info(`walkeros/flow v${VERSION}`);
|
|
6136
6268
|
logger.info(`Instance: ${getInstanceId()}`);
|
|
6269
|
+
if (configFrozen) {
|
|
6270
|
+
logger.info("Config frozen: hot-swap and heartbeat disabled");
|
|
6271
|
+
}
|
|
6137
6272
|
const healthServer = await createHealthServer(port, logger);
|
|
6138
|
-
const
|
|
6273
|
+
const observeLevel = readObserveLevel(logger);
|
|
6274
|
+
const telemetryObservers = buildTelemetryObservers(
|
|
6275
|
+
api?.flowId ?? "flow",
|
|
6276
|
+
observeLevel
|
|
6277
|
+
);
|
|
6139
6278
|
const runtimeConfig = { port };
|
|
6140
6279
|
let handle;
|
|
6141
6280
|
try {
|
|
@@ -6164,20 +6303,24 @@ async function runPipeline(options) {
|
|
|
6164
6303
|
const ingestToken = process.env.WALKEROS_INGEST_TOKEN;
|
|
6165
6304
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
6166
6305
|
if (observerBase && ingestToken && deploymentId) {
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6306
|
+
if (observeLevel === "trace") {
|
|
6307
|
+
logger.info("Trace poller: skipped (observe level is trace)");
|
|
6308
|
+
} else {
|
|
6309
|
+
tracePoller = createTracePoller(
|
|
6310
|
+
{
|
|
6311
|
+
url: `${observerBase}/trace/${deploymentId}`,
|
|
6312
|
+
token: ingestToken,
|
|
6313
|
+
intervalMs: 15e3
|
|
6314
|
+
},
|
|
6315
|
+
logger
|
|
6316
|
+
);
|
|
6317
|
+
tracePoller.start();
|
|
6318
|
+
logger.info("Trace poller: active (every 15s)");
|
|
6319
|
+
}
|
|
6177
6320
|
}
|
|
6178
6321
|
let currentBundleCleanup;
|
|
6179
6322
|
let currentConfigPath;
|
|
6180
|
-
if (api) {
|
|
6323
|
+
if (api && !configFrozen) {
|
|
6181
6324
|
heartbeat = createHeartbeat(
|
|
6182
6325
|
{
|
|
6183
6326
|
appUrl: api.appUrl,
|
|
@@ -6294,17 +6437,39 @@ async function runPipeline(options) {
|
|
|
6294
6437
|
await new Promise(() => {
|
|
6295
6438
|
});
|
|
6296
6439
|
}
|
|
6297
|
-
|
|
6440
|
+
var OBSERVE_LEVELS = [
|
|
6441
|
+
"off",
|
|
6442
|
+
"standard",
|
|
6443
|
+
"trace"
|
|
6444
|
+
];
|
|
6445
|
+
function readConfigFrozen() {
|
|
6446
|
+
const raw = process.env.WALKEROS_CONFIG_FROZEN;
|
|
6447
|
+
return raw === "1" || raw === "true";
|
|
6448
|
+
}
|
|
6449
|
+
function readObserveLevel(logger) {
|
|
6450
|
+
const raw = process.env.WALKEROS_OBSERVE_LEVEL;
|
|
6451
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
6452
|
+
const level = OBSERVE_LEVELS.find((candidate) => candidate === raw);
|
|
6453
|
+
if (!level) {
|
|
6454
|
+
logger.warn(
|
|
6455
|
+
`Ignoring invalid WALKEROS_OBSERVE_LEVEL "${raw}" (expected off, standard, or trace)`
|
|
6456
|
+
);
|
|
6457
|
+
return void 0;
|
|
6458
|
+
}
|
|
6459
|
+
return level;
|
|
6460
|
+
}
|
|
6461
|
+
function buildTelemetryObservers(flowId, observeLevel) {
|
|
6298
6462
|
const base = process.env.WALKEROS_OBSERVER_URL;
|
|
6299
6463
|
const token = process.env.WALKEROS_INGEST_TOKEN;
|
|
6300
6464
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
6301
6465
|
if (!base || !token || !deploymentId) return void 0;
|
|
6302
6466
|
const url = `${base}/ingest/${deploymentId}`;
|
|
6303
6467
|
const emit = createBatchedPoster({ url, token });
|
|
6468
|
+
const observe = observeLevel !== void 0 ? { level: observeLevel } : void 0;
|
|
6304
6469
|
return [
|
|
6305
6470
|
createTelemetryObserver(
|
|
6306
6471
|
emit,
|
|
6307
|
-
() => resolveTelemetryOptions({ flowId, traceUntil: getTraceUntil() })
|
|
6472
|
+
() => resolveTelemetryOptions({ flowId, observe, traceUntil: getTraceUntil() })
|
|
6308
6473
|
)
|
|
6309
6474
|
];
|
|
6310
6475
|
}
|
|
@@ -7235,7 +7400,7 @@ function validateMapping(input) {
|
|
|
7235
7400
|
// src/commands/validate/validators/entry.ts
|
|
7236
7401
|
import Ajv from "ajv";
|
|
7237
7402
|
import { fetchPackageSchema } from "@walkeros/core";
|
|
7238
|
-
var CLIENT_HEADER = "walkeros-cli/4.2.
|
|
7403
|
+
var CLIENT_HEADER = "walkeros-cli/4.2.1-next-1781526381392";
|
|
7239
7404
|
var SECTIONS = ["destinations", "sources", "transformers"];
|
|
7240
7405
|
function resolveEntry(path20, flowConfig) {
|
|
7241
7406
|
const flows = flowConfig.flows;
|
|
@@ -7800,8 +7965,8 @@ import createClient from "openapi-fetch";
|
|
|
7800
7965
|
init_config_file();
|
|
7801
7966
|
import { createHash } from "crypto";
|
|
7802
7967
|
import semver4 from "semver";
|
|
7803
|
-
var bakedContractVersion = true ? "
|
|
7804
|
-
var bakedContractHash = true ? "
|
|
7968
|
+
var bakedContractVersion = true ? "2.0.0" : PLACEHOLDER;
|
|
7969
|
+
var bakedContractHash = true ? "b9ac141c5d759debbb52b838669daabec64659c3b79038dee69e5488b3f31dea" : "";
|
|
7805
7970
|
function isRecord2(value) {
|
|
7806
7971
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7807
7972
|
}
|
|
@@ -7932,9 +8097,13 @@ function createApiClient() {
|
|
|
7932
8097
|
}
|
|
7933
8098
|
|
|
7934
8099
|
// src/core/api-error.ts
|
|
8100
|
+
var EXIT_RETRYABLE = 75;
|
|
7935
8101
|
var ApiError = class extends Error {
|
|
7936
8102
|
code;
|
|
7937
8103
|
details;
|
|
8104
|
+
status;
|
|
8105
|
+
retryable;
|
|
8106
|
+
retryAfterSeconds;
|
|
7938
8107
|
// Populated only for CLIENT_OUTDATED responses (HTTP 426).
|
|
7939
8108
|
minVersion;
|
|
7940
8109
|
clientVersion;
|
|
@@ -7946,6 +8115,9 @@ var ApiError = class extends Error {
|
|
|
7946
8115
|
this.name = "ApiError";
|
|
7947
8116
|
this.code = options?.code;
|
|
7948
8117
|
this.details = options?.details;
|
|
8118
|
+
this.status = options?.status;
|
|
8119
|
+
this.retryable = options?.retryable;
|
|
8120
|
+
this.retryAfterSeconds = options?.retryAfterSeconds;
|
|
7949
8121
|
this.minVersion = options?.minVersion;
|
|
7950
8122
|
this.clientVersion = options?.clientVersion;
|
|
7951
8123
|
this.client = options?.client;
|
|
@@ -7953,25 +8125,62 @@ var ApiError = class extends Error {
|
|
|
7953
8125
|
this.docs = options?.docs;
|
|
7954
8126
|
}
|
|
7955
8127
|
};
|
|
7956
|
-
function
|
|
7957
|
-
if (error
|
|
7958
|
-
|
|
7959
|
-
const message = inner.message || fallbackMessage;
|
|
7960
|
-
const code = inner.code;
|
|
7961
|
-
const details = inner.details?.errors;
|
|
7962
|
-
const options = { code, details };
|
|
7963
|
-
if (code === "CLIENT_OUTDATED") {
|
|
7964
|
-
options.minVersion = inner.minVersion;
|
|
7965
|
-
options.clientVersion = inner.clientVersion;
|
|
7966
|
-
options.client = inner.client;
|
|
7967
|
-
options.upgrade = inner.upgrade;
|
|
7968
|
-
options.docs = inner.docs;
|
|
7969
|
-
}
|
|
7970
|
-
throw new ApiError(message, options);
|
|
8128
|
+
function extractApiErrorOptions(error, fallbackMessage) {
|
|
8129
|
+
if (!error || typeof error !== "object" || !("error" in error) || typeof error.error !== "object") {
|
|
8130
|
+
return null;
|
|
7971
8131
|
}
|
|
8132
|
+
const inner = error.error;
|
|
8133
|
+
const message = inner.message || fallbackMessage;
|
|
8134
|
+
const code = inner.code;
|
|
8135
|
+
const details = inner.details?.errors;
|
|
8136
|
+
const options = { code, details };
|
|
8137
|
+
if (code === "CLIENT_OUTDATED") {
|
|
8138
|
+
options.minVersion = inner.minVersion;
|
|
8139
|
+
options.clientVersion = inner.clientVersion;
|
|
8140
|
+
options.client = inner.client;
|
|
8141
|
+
options.upgrade = inner.upgrade;
|
|
8142
|
+
options.docs = inner.docs;
|
|
8143
|
+
}
|
|
8144
|
+
return { message, options };
|
|
8145
|
+
}
|
|
8146
|
+
function throwApiError(error, fallbackMessage) {
|
|
8147
|
+
const extracted = extractApiErrorOptions(error, fallbackMessage);
|
|
8148
|
+
if (extracted) throw new ApiError(extracted.message, extracted.options);
|
|
7972
8149
|
throw new ApiError(fallbackMessage);
|
|
7973
8150
|
}
|
|
8151
|
+
function parseRetryAfter(value) {
|
|
8152
|
+
if (!value) return void 0;
|
|
8153
|
+
const trimmed = value.trim();
|
|
8154
|
+
if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10);
|
|
8155
|
+
const when = Date.parse(trimmed);
|
|
8156
|
+
if (Number.isNaN(when)) return void 0;
|
|
8157
|
+
return Math.max(0, Math.round((when - Date.now()) / 1e3));
|
|
8158
|
+
}
|
|
8159
|
+
function throwApiResponseError(response, body, fallbackMessage) {
|
|
8160
|
+
const status = response.status;
|
|
8161
|
+
const retryAfterSeconds = parseRetryAfter(
|
|
8162
|
+
response.headers.get("retry-after")
|
|
8163
|
+
);
|
|
8164
|
+
const retryable = status === 429 || status === 503 && retryAfterSeconds !== void 0;
|
|
8165
|
+
const extracted = extractApiErrorOptions(body, fallbackMessage);
|
|
8166
|
+
const message = extracted?.message ?? fallbackMessage;
|
|
8167
|
+
const options = {
|
|
8168
|
+
...extracted?.options ?? {},
|
|
8169
|
+
status,
|
|
8170
|
+
retryable,
|
|
8171
|
+
retryAfterSeconds
|
|
8172
|
+
};
|
|
8173
|
+
throw new ApiError(message, options);
|
|
8174
|
+
}
|
|
8175
|
+
function machineReadableErrorLine(err) {
|
|
8176
|
+
const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
|
|
8177
|
+
const retryable = err instanceof ApiError ? err.retryable === true : false;
|
|
8178
|
+
const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
|
|
8179
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8180
|
+
return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
|
|
8181
|
+
}
|
|
7974
8182
|
function handleCliError(err) {
|
|
8183
|
+
console.error(machineReadableErrorLine(err));
|
|
7975
8184
|
if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
|
|
7976
8185
|
console.error(`
|
|
7977
8186
|
${err.message}
|
|
@@ -7981,8 +8190,12 @@ ${err.message}
|
|
|
7981
8190
|
`);
|
|
7982
8191
|
process.exit(2);
|
|
7983
8192
|
}
|
|
7984
|
-
|
|
7985
|
-
|
|
8193
|
+
if (err instanceof ApiError && err.retryable) {
|
|
8194
|
+
const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
|
|
8195
|
+
console.error(`${err.message}${hint}`);
|
|
8196
|
+
process.exit(EXIT_RETRYABLE);
|
|
8197
|
+
}
|
|
8198
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
7986
8199
|
process.exit(1);
|
|
7987
8200
|
}
|
|
7988
8201
|
|
|
@@ -8278,6 +8491,7 @@ async function readFlowStdin() {
|
|
|
8278
8491
|
}
|
|
8279
8492
|
|
|
8280
8493
|
// src/commands/deploy/index.ts
|
|
8494
|
+
import { randomUUID } from "crypto";
|
|
8281
8495
|
init_auth();
|
|
8282
8496
|
init_http();
|
|
8283
8497
|
init_sse();
|
|
@@ -8308,8 +8522,9 @@ async function getAvailableFlowNames(options) {
|
|
|
8308
8522
|
const settings = flow.settings;
|
|
8309
8523
|
return settings?.map((c) => c.name) ?? [];
|
|
8310
8524
|
}
|
|
8525
|
+
var DEFAULT_DEPLOY_WAIT_MS = 12 * 60 * 1e3;
|
|
8311
8526
|
async function streamDeploymentStatus(projectId, deploymentId, options) {
|
|
8312
|
-
const timeoutMs = options.timeout ??
|
|
8527
|
+
const timeoutMs = options.timeout ?? DEFAULT_DEPLOY_WAIT_MS;
|
|
8313
8528
|
const response = await apiFetch(
|
|
8314
8529
|
`/api/projects/${projectId}/deployments/${deploymentId}/stream`,
|
|
8315
8530
|
{
|
|
@@ -8368,7 +8583,10 @@ async function deploy(options) {
|
|
|
8368
8583
|
}
|
|
8369
8584
|
const { data, error } = await client.POST(
|
|
8370
8585
|
"/api/projects/{projectId}/flows/{flowId}/deploy",
|
|
8371
|
-
{
|
|
8586
|
+
{
|
|
8587
|
+
params: { path: { projectId, flowId: options.flowId } },
|
|
8588
|
+
headers: { "Idempotency-Key": randomUUID() }
|
|
8589
|
+
}
|
|
8372
8590
|
);
|
|
8373
8591
|
if (error) {
|
|
8374
8592
|
try {
|
|
@@ -8398,13 +8616,38 @@ Available: ${names.join(", ")}`,
|
|
|
8398
8616
|
}
|
|
8399
8617
|
async function deploySettings(options) {
|
|
8400
8618
|
const { flowId, projectId, settingsId } = options;
|
|
8401
|
-
const
|
|
8619
|
+
const triggerDeploy = () => apiFetch(
|
|
8402
8620
|
`/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
|
|
8403
|
-
{ method: "POST" }
|
|
8621
|
+
{ method: "POST", headers: { "Idempotency-Key": randomUUID() } }
|
|
8404
8622
|
);
|
|
8623
|
+
let response = await triggerDeploy();
|
|
8624
|
+
if (!response.ok && options.wait) {
|
|
8625
|
+
let retryBody = {};
|
|
8626
|
+
try {
|
|
8627
|
+
retryBody = await response.clone().json();
|
|
8628
|
+
} catch {
|
|
8629
|
+
retryBody = {};
|
|
8630
|
+
}
|
|
8631
|
+
try {
|
|
8632
|
+
throwApiResponseError(
|
|
8633
|
+
response,
|
|
8634
|
+
retryBody,
|
|
8635
|
+
`Deploy failed (${response.status})`
|
|
8636
|
+
);
|
|
8637
|
+
} catch (e) {
|
|
8638
|
+
if (e instanceof ApiError && e.retryable) {
|
|
8639
|
+
const waitSeconds = Math.min(e.retryAfterSeconds ?? 5, 60);
|
|
8640
|
+
options.onStatus?.("rate_limited", `retrying in ${waitSeconds}s`);
|
|
8641
|
+
await new Promise((resolve5) => setTimeout(resolve5, waitSeconds * 1e3));
|
|
8642
|
+
response = await triggerDeploy();
|
|
8643
|
+
} else {
|
|
8644
|
+
throw e;
|
|
8645
|
+
}
|
|
8646
|
+
}
|
|
8647
|
+
}
|
|
8405
8648
|
if (!response.ok) {
|
|
8406
8649
|
const body = await response.json().catch(() => ({}));
|
|
8407
|
-
|
|
8650
|
+
throwApiResponseError(response, body, `Deploy failed (${response.status})`);
|
|
8408
8651
|
}
|
|
8409
8652
|
const data = await response.json();
|
|
8410
8653
|
if (!options.wait) return data;
|
|
@@ -8428,7 +8671,7 @@ async function getDeployment(options) {
|
|
|
8428
8671
|
);
|
|
8429
8672
|
if (!response.ok) {
|
|
8430
8673
|
const body = await response.json().catch(() => ({}));
|
|
8431
|
-
|
|
8674
|
+
throwApiResponseError(response, body, "Failed to get deployment");
|
|
8432
8675
|
}
|
|
8433
8676
|
return response.json();
|
|
8434
8677
|
}
|
|
@@ -8441,16 +8684,20 @@ async function getDeployment(options) {
|
|
|
8441
8684
|
return data;
|
|
8442
8685
|
}
|
|
8443
8686
|
var statusLabels = {
|
|
8444
|
-
|
|
8445
|
-
"
|
|
8446
|
-
"bundling:publishing": "Publishing to web...",
|
|
8447
|
-
deploying: "Deploying container...",
|
|
8687
|
+
"deploying:building": "Building bundle...",
|
|
8688
|
+
"deploying:publishing": "Publishing to web...",
|
|
8448
8689
|
"deploying:provisioning": "Provisioning container...",
|
|
8449
8690
|
"deploying:starting": "Starting container...",
|
|
8691
|
+
deploying: "Deploying...",
|
|
8450
8692
|
active: "Container is live",
|
|
8451
8693
|
published: "Published",
|
|
8694
|
+
stopped: "Stopped",
|
|
8452
8695
|
failed: "Deployment failed"
|
|
8453
8696
|
};
|
|
8697
|
+
function renderStatusLabel(status, substatus) {
|
|
8698
|
+
const key = substatus ? `${status}:${substatus}` : status;
|
|
8699
|
+
return statusLabels[key] || statusLabels[status] || `Status: ${status}`;
|
|
8700
|
+
}
|
|
8454
8701
|
async function deployCommand(flowId, options) {
|
|
8455
8702
|
const log = createCLILogger(options);
|
|
8456
8703
|
const timeoutMs = options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0;
|
|
@@ -8462,10 +8709,7 @@ async function deployCommand(flowId, options) {
|
|
|
8462
8709
|
wait: options.wait !== false,
|
|
8463
8710
|
timeout: timeoutMs,
|
|
8464
8711
|
onStatus: options.json ? void 0 : (status, substatus) => {
|
|
8465
|
-
|
|
8466
|
-
log.info(
|
|
8467
|
-
statusLabels[key] || statusLabels[status] || `Status: ${status}`
|
|
8468
|
-
);
|
|
8712
|
+
log.info(renderStatusLabel(status, substatus));
|
|
8469
8713
|
}
|
|
8470
8714
|
});
|
|
8471
8715
|
if (options.json) {
|
|
@@ -8480,7 +8724,7 @@ async function deployCommand(flowId, options) {
|
|
|
8480
8724
|
} else if (r.status === "failed") {
|
|
8481
8725
|
log.error(`Failed: ${r.errorMessage || "Unknown error"}`);
|
|
8482
8726
|
process.exit(1);
|
|
8483
|
-
} else if (r.status === "
|
|
8727
|
+
} else if (r.status === "deploying") {
|
|
8484
8728
|
log.info(`Deployment started: ${r.deploymentId} (${r.type})`);
|
|
8485
8729
|
} else {
|
|
8486
8730
|
log.info(`Status: ${r.status}`);
|
|
@@ -8708,20 +8952,16 @@ async function createDeployCommand(config, options) {
|
|
|
8708
8952
|
log.info(`Deployment created: ${result.id}`);
|
|
8709
8953
|
log.info(` Slug: ${result.slug}`);
|
|
8710
8954
|
log.info(` Type: ${result.type}`);
|
|
8711
|
-
if (result.deployToken) {
|
|
8712
|
-
log.info(` Token: ${result.deployToken}`);
|
|
8713
|
-
log.warn(" Save this token \u2014 it will not be shown again.");
|
|
8714
|
-
}
|
|
8715
8955
|
log.info("");
|
|
8716
8956
|
log.info("Run locally:");
|
|
8717
8957
|
log.info(
|
|
8718
8958
|
` walkeros run ${isRemoteFlow ? "flow.json" : config} --deploy ${result.id}`
|
|
8719
8959
|
);
|
|
8720
8960
|
log.info("");
|
|
8721
|
-
log.info("
|
|
8722
|
-
log.info(
|
|
8723
|
-
|
|
8724
|
-
);
|
|
8961
|
+
log.info("Create a deploy token for this flow in the app");
|
|
8962
|
+
log.info("(Settings, Self-hosted deploy token) and set it as");
|
|
8963
|
+
log.info("WALKEROS_DEPLOY_TOKEN, then run with Docker:");
|
|
8964
|
+
log.info(" docker run -e WALKEROS_DEPLOY_TOKEN \\");
|
|
8725
8965
|
log.info(" -e WALKEROS_APP_URL=https://app.walkeros.io \\");
|
|
8726
8966
|
log.info(" walkeros/flow:latest");
|
|
8727
8967
|
} catch (err) {
|
|
@@ -8807,14 +9047,14 @@ init_config_file();
|
|
|
8807
9047
|
|
|
8808
9048
|
// src/telemetry/install-id.ts
|
|
8809
9049
|
init_config_file();
|
|
8810
|
-
import { randomUUID } from "crypto";
|
|
9050
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8811
9051
|
function getInstallationId() {
|
|
8812
9052
|
return readConfig()?.installationId;
|
|
8813
9053
|
}
|
|
8814
9054
|
function createInstallationId() {
|
|
8815
9055
|
const existing = readConfig()?.installationId;
|
|
8816
9056
|
if (existing) return existing;
|
|
8817
|
-
const id =
|
|
9057
|
+
const id = randomUUID2();
|
|
8818
9058
|
writeTelemetryOnlyConfig({ installationId: id });
|
|
8819
9059
|
return id;
|
|
8820
9060
|
}
|
|
@@ -8875,6 +9115,8 @@ function telemetryDisableCommand() {
|
|
|
8875
9115
|
|
|
8876
9116
|
// src/index.ts
|
|
8877
9117
|
init_bundle();
|
|
9118
|
+
init_bundler();
|
|
9119
|
+
init_config_classifier();
|
|
8878
9120
|
|
|
8879
9121
|
// src/commands/bundle/validate-structure.ts
|
|
8880
9122
|
init_structural_validators();
|
|
@@ -9560,12 +9802,15 @@ export {
|
|
|
9560
9802
|
apiFetch,
|
|
9561
9803
|
bakedContractHash,
|
|
9562
9804
|
bakedContractVersion,
|
|
9805
|
+
buildDataPayload,
|
|
9563
9806
|
bundle,
|
|
9564
9807
|
bundleCommand,
|
|
9565
9808
|
canonicalContractHash,
|
|
9809
|
+
classifyStepProperties,
|
|
9566
9810
|
clientContextHeaders,
|
|
9567
9811
|
compareContract,
|
|
9568
9812
|
compareOutput,
|
|
9813
|
+
containsCodeMarkers,
|
|
9569
9814
|
createApiClient,
|
|
9570
9815
|
createDeployCommand,
|
|
9571
9816
|
createDeployment,
|