@walkeros/cli 4.2.0 → 4.3.0-next-1781171238534
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 +32 -0
- package/dist/cli.js +193 -64
- package/dist/index.d.ts +86 -7
- package/dist/index.js +207 -69
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @walkeros/cli
|
|
2
2
|
|
|
3
|
+
## 4.3.0-next-1781171238534
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5cbcd23: `walkeros run` reads two new environment variables.
|
|
8
|
+
`WALKEROS_OBSERVE_LEVEL` sets the runtime's baseline telemetry level (`off`,
|
|
9
|
+
`standard`, or `trace`). `WALKEROS_CONFIG_FROZEN` (`1` or `true`) serves the
|
|
10
|
+
bundle as an immutable snapshot: secrets are still injected at boot, but
|
|
11
|
+
config hot-swap and heartbeat are disabled.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- b03bfce: `walkeros deploy` now waits long enough to cover a full server deploy
|
|
16
|
+
by default, so a slow but healthy deploy is no longer aborted early and
|
|
17
|
+
reported as a failure. Each run sends a fresh idempotency key, so retrying
|
|
18
|
+
after a failure starts a new deploy instead of replaying the previous result.
|
|
19
|
+
Failures print a stable, machine-readable error code (with a `Retry-After`
|
|
20
|
+
hint on rate limits), and `deploy create` no longer prints an empty token
|
|
21
|
+
placeholder.
|
|
22
|
+
- 5cbcd23: All four simulate functions (`simulateSource`, `simulateTransformer`,
|
|
23
|
+
`simulateCollector`, `simulateDestination`) accept a new `data` option to run
|
|
24
|
+
an existing bundle with updated configuration values, without rebundling. The
|
|
25
|
+
new `buildDataPayload`, `classifyStepProperties`, and `containsCodeMarkers`
|
|
26
|
+
exports build and inspect that payload. Destination simulation results now
|
|
27
|
+
include `mappingKey`, the entity-action key of the matched mapping rule.
|
|
28
|
+
- Updated dependencies [5cbcd23]
|
|
29
|
+
- @walkeros/core@4.3.0-next-1781171238534
|
|
30
|
+
- @walkeros/collector@4.3.0-next-1781171238534
|
|
31
|
+
- @walkeros/server-core@4.3.0-next-1781171238534
|
|
32
|
+
- @walkeros/server-destination-api@4.3.0-next-1781171238534
|
|
33
|
+
- @walkeros/transformer-validate@4.3.0-next-1781171238534
|
|
34
|
+
|
|
3
35
|
## 4.2.0
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
package/dist/cli.js
CHANGED
|
@@ -31,25 +31,62 @@ var init_client_context = __esm({
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// src/core/api-error.ts
|
|
34
|
-
function
|
|
35
|
-
if (error
|
|
36
|
-
|
|
37
|
-
const message = inner.message || fallbackMessage;
|
|
38
|
-
const code = inner.code;
|
|
39
|
-
const details = inner.details?.errors;
|
|
40
|
-
const options = { code, details };
|
|
41
|
-
if (code === "CLIENT_OUTDATED") {
|
|
42
|
-
options.minVersion = inner.minVersion;
|
|
43
|
-
options.clientVersion = inner.clientVersion;
|
|
44
|
-
options.client = inner.client;
|
|
45
|
-
options.upgrade = inner.upgrade;
|
|
46
|
-
options.docs = inner.docs;
|
|
47
|
-
}
|
|
48
|
-
throw new ApiError(message, options);
|
|
34
|
+
function extractApiErrorOptions(error, fallbackMessage) {
|
|
35
|
+
if (!error || typeof error !== "object" || !("error" in error) || typeof error.error !== "object") {
|
|
36
|
+
return null;
|
|
49
37
|
}
|
|
38
|
+
const inner = error.error;
|
|
39
|
+
const message = inner.message || fallbackMessage;
|
|
40
|
+
const code = inner.code;
|
|
41
|
+
const details = inner.details?.errors;
|
|
42
|
+
const options = { code, details };
|
|
43
|
+
if (code === "CLIENT_OUTDATED") {
|
|
44
|
+
options.minVersion = inner.minVersion;
|
|
45
|
+
options.clientVersion = inner.clientVersion;
|
|
46
|
+
options.client = inner.client;
|
|
47
|
+
options.upgrade = inner.upgrade;
|
|
48
|
+
options.docs = inner.docs;
|
|
49
|
+
}
|
|
50
|
+
return { message, options };
|
|
51
|
+
}
|
|
52
|
+
function throwApiError(error, fallbackMessage) {
|
|
53
|
+
const extracted = extractApiErrorOptions(error, fallbackMessage);
|
|
54
|
+
if (extracted) throw new ApiError(extracted.message, extracted.options);
|
|
50
55
|
throw new ApiError(fallbackMessage);
|
|
51
56
|
}
|
|
57
|
+
function parseRetryAfter(value) {
|
|
58
|
+
if (!value) return void 0;
|
|
59
|
+
const trimmed = value.trim();
|
|
60
|
+
if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10);
|
|
61
|
+
const when = Date.parse(trimmed);
|
|
62
|
+
if (Number.isNaN(when)) return void 0;
|
|
63
|
+
return Math.max(0, Math.round((when - Date.now()) / 1e3));
|
|
64
|
+
}
|
|
65
|
+
function throwApiResponseError(response, body, fallbackMessage) {
|
|
66
|
+
const status = response.status;
|
|
67
|
+
const retryAfterSeconds = parseRetryAfter(
|
|
68
|
+
response.headers.get("retry-after")
|
|
69
|
+
);
|
|
70
|
+
const retryable = status === 429 || status === 503 && retryAfterSeconds !== void 0;
|
|
71
|
+
const extracted = extractApiErrorOptions(body, fallbackMessage);
|
|
72
|
+
const message = extracted?.message ?? fallbackMessage;
|
|
73
|
+
const options = {
|
|
74
|
+
...extracted?.options ?? {},
|
|
75
|
+
status,
|
|
76
|
+
retryable,
|
|
77
|
+
retryAfterSeconds
|
|
78
|
+
};
|
|
79
|
+
throw new ApiError(message, options);
|
|
80
|
+
}
|
|
81
|
+
function machineReadableErrorLine(err) {
|
|
82
|
+
const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
|
|
83
|
+
const retryable = err instanceof ApiError ? err.retryable === true : false;
|
|
84
|
+
const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
|
|
87
|
+
}
|
|
52
88
|
function handleCliError(err) {
|
|
89
|
+
console.error(machineReadableErrorLine(err));
|
|
53
90
|
if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
|
|
54
91
|
console.error(`
|
|
55
92
|
${err.message}
|
|
@@ -59,17 +96,25 @@ ${err.message}
|
|
|
59
96
|
`);
|
|
60
97
|
process.exit(2);
|
|
61
98
|
}
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
if (err instanceof ApiError && err.retryable) {
|
|
100
|
+
const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
|
|
101
|
+
console.error(`${err.message}${hint}`);
|
|
102
|
+
process.exit(EXIT_RETRYABLE);
|
|
103
|
+
}
|
|
104
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
64
105
|
process.exit(1);
|
|
65
106
|
}
|
|
66
|
-
var ApiError;
|
|
107
|
+
var EXIT_RETRYABLE, ApiError;
|
|
67
108
|
var init_api_error = __esm({
|
|
68
109
|
"src/core/api-error.ts"() {
|
|
69
110
|
"use strict";
|
|
111
|
+
EXIT_RETRYABLE = 75;
|
|
70
112
|
ApiError = class extends Error {
|
|
71
113
|
code;
|
|
72
114
|
details;
|
|
115
|
+
status;
|
|
116
|
+
retryable;
|
|
117
|
+
retryAfterSeconds;
|
|
73
118
|
// Populated only for CLIENT_OUTDATED responses (HTTP 426).
|
|
74
119
|
minVersion;
|
|
75
120
|
clientVersion;
|
|
@@ -81,6 +126,9 @@ var init_api_error = __esm({
|
|
|
81
126
|
this.name = "ApiError";
|
|
82
127
|
this.code = options?.code;
|
|
83
128
|
this.details = options?.details;
|
|
129
|
+
this.status = options?.status;
|
|
130
|
+
this.retryable = options?.retryable;
|
|
131
|
+
this.retryAfterSeconds = options?.retryAfterSeconds;
|
|
84
132
|
this.minVersion = options?.minVersion;
|
|
85
133
|
this.clientVersion = options?.clientVersion;
|
|
86
134
|
this.client = options?.client;
|
|
@@ -4570,7 +4618,7 @@ ${destinationsEntries.join(",\n")}
|
|
|
4570
4618
|
stores${collectorStr}
|
|
4571
4619
|
}`;
|
|
4572
4620
|
const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
|
|
4573
|
-
return { storesDeclaration, codeConfigObject, dataPayload };
|
|
4621
|
+
return { storesDeclaration, codeConfigObject, dataPayloadObj, dataPayload };
|
|
4574
4622
|
}
|
|
4575
4623
|
function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
|
|
4576
4624
|
const codeSection = userCode ? `
|
|
@@ -6698,7 +6746,7 @@ async function wt(e4, t4, o4) {
|
|
|
6698
6746
|
}
|
|
6699
6747
|
}
|
|
6700
6748
|
function vt(e4, t4) {
|
|
6701
|
-
return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.
|
|
6749
|
+
return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.3.0-next-1781171238534" }, ...t4 };
|
|
6702
6750
|
}
|
|
6703
6751
|
function bt(e4, t4) {
|
|
6704
6752
|
if (!t4.name) throw new Error("Event name is required");
|
|
@@ -7753,7 +7801,7 @@ function toError(error) {
|
|
|
7753
7801
|
return error instanceof Error ? error : new Error(getErrorMessage(error));
|
|
7754
7802
|
}
|
|
7755
7803
|
function buildSimulationResult(args) {
|
|
7756
|
-
const { step, name, startTime, captured, usage, error } = args;
|
|
7804
|
+
const { step, name, startTime, captured, usage, mappingKey, error } = args;
|
|
7757
7805
|
const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
|
|
7758
7806
|
const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
|
|
7759
7807
|
return {
|
|
@@ -7762,6 +7810,7 @@ function buildSimulationResult(args) {
|
|
|
7762
7810
|
events,
|
|
7763
7811
|
calls,
|
|
7764
7812
|
duration: Date.now() - startTime,
|
|
7813
|
+
...mappingKey !== void 0 ? { mappingKey } : {},
|
|
7765
7814
|
...error !== void 0 ? { error: toError(error) } : {}
|
|
7766
7815
|
};
|
|
7767
7816
|
}
|
|
@@ -8355,7 +8404,9 @@ async function simulateSource(configOrPath, input, options) {
|
|
|
8355
8404
|
`Source package "${sourceConfig.package}" has no createTrigger in /dev export`
|
|
8356
8405
|
);
|
|
8357
8406
|
}
|
|
8358
|
-
const flowConfig = module.wireConfig(
|
|
8407
|
+
const flowConfig = module.wireConfig(
|
|
8408
|
+
options.data ?? module.__configData ?? void 0
|
|
8409
|
+
);
|
|
8359
8410
|
applyOverrides(flowConfig, prepared.overrides);
|
|
8360
8411
|
const captured = [];
|
|
8361
8412
|
flowConfig.hooks = {
|
|
@@ -8460,7 +8511,9 @@ async function simulateTransformer(configOrPath, event, options) {
|
|
|
8460
8511
|
networkCalls
|
|
8461
8512
|
},
|
|
8462
8513
|
async (module) => {
|
|
8463
|
-
const flowConfig = module.wireConfig(
|
|
8514
|
+
const flowConfig = module.wireConfig(
|
|
8515
|
+
options.data ?? module.__configData ?? void 0
|
|
8516
|
+
);
|
|
8464
8517
|
applyOverrides(flowConfig, prepared.overrides);
|
|
8465
8518
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
8466
8519
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -8626,7 +8679,9 @@ async function simulateCollector(configOrPath, event, options) {
|
|
|
8626
8679
|
networkCalls
|
|
8627
8680
|
},
|
|
8628
8681
|
async (module) => {
|
|
8629
|
-
const flowConfig = module.wireConfig(
|
|
8682
|
+
const flowConfig = module.wireConfig(
|
|
8683
|
+
options.data ?? module.__configData ?? void 0
|
|
8684
|
+
);
|
|
8630
8685
|
applyOverrides(flowConfig, prepared.overrides);
|
|
8631
8686
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
8632
8687
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -8729,7 +8784,9 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
8729
8784
|
networkCalls
|
|
8730
8785
|
},
|
|
8731
8786
|
async (module) => {
|
|
8732
|
-
const flowConfig = module.wireConfig(
|
|
8787
|
+
const flowConfig = module.wireConfig(
|
|
8788
|
+
options.data ?? module.__configData ?? void 0
|
|
8789
|
+
);
|
|
8733
8790
|
applyOverrides(flowConfig, prepared.overrides);
|
|
8734
8791
|
const destPkg = (prepared.flowSettings.destinations ?? {})[options.destinationId];
|
|
8735
8792
|
let trackedCalls = [];
|
|
@@ -8765,6 +8822,16 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
8765
8822
|
);
|
|
8766
8823
|
}
|
|
8767
8824
|
logger.info(`Simulating destination: ${options.destinationId}`);
|
|
8825
|
+
let mappingKey;
|
|
8826
|
+
const targetStepId = o("destination", options.destinationId);
|
|
8827
|
+
const captureMappingKey = (state) => {
|
|
8828
|
+
if (state.stepId === targetStepId && state.mappingKey) {
|
|
8829
|
+
mappingKey = state.mappingKey;
|
|
8830
|
+
}
|
|
8831
|
+
};
|
|
8832
|
+
if (collector.observers instanceof Set) {
|
|
8833
|
+
collector.observers.add(captureMappingKey);
|
|
8834
|
+
}
|
|
8768
8835
|
await collector.push(event, {
|
|
8769
8836
|
include: [options.destinationId]
|
|
8770
8837
|
});
|
|
@@ -8773,7 +8840,8 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
8773
8840
|
step: "destination",
|
|
8774
8841
|
name: options.destinationId,
|
|
8775
8842
|
startTime,
|
|
8776
|
-
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
|
|
8843
|
+
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0,
|
|
8844
|
+
mappingKey
|
|
8777
8845
|
});
|
|
8778
8846
|
},
|
|
8779
8847
|
(error) => buildSimulationResult({
|
|
@@ -9498,13 +9566,21 @@ init_cache();
|
|
|
9498
9566
|
async function runPipeline(options) {
|
|
9499
9567
|
const { bundlePath, port, logger, loggerConfig, api } = options;
|
|
9500
9568
|
let configVersion;
|
|
9569
|
+
const configFrozen = readConfigFrozen();
|
|
9501
9570
|
if (api) {
|
|
9502
9571
|
await injectSecrets(api, logger);
|
|
9503
9572
|
}
|
|
9504
9573
|
logger.info(`walkeros/flow v${VERSION}`);
|
|
9505
9574
|
logger.info(`Instance: ${getInstanceId()}`);
|
|
9575
|
+
if (configFrozen) {
|
|
9576
|
+
logger.info("Config frozen: hot-swap and heartbeat disabled");
|
|
9577
|
+
}
|
|
9506
9578
|
const healthServer = await createHealthServer(port, logger);
|
|
9507
|
-
const
|
|
9579
|
+
const observeLevel = readObserveLevel(logger);
|
|
9580
|
+
const telemetryObservers = buildTelemetryObservers(
|
|
9581
|
+
api?.flowId ?? "flow",
|
|
9582
|
+
observeLevel
|
|
9583
|
+
);
|
|
9508
9584
|
const runtimeConfig = { port };
|
|
9509
9585
|
let handle;
|
|
9510
9586
|
try {
|
|
@@ -9533,20 +9609,24 @@ async function runPipeline(options) {
|
|
|
9533
9609
|
const ingestToken = process.env.WALKEROS_INGEST_TOKEN;
|
|
9534
9610
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
9535
9611
|
if (observerBase && ingestToken && deploymentId) {
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9612
|
+
if (observeLevel === "trace") {
|
|
9613
|
+
logger.info("Trace poller: skipped (observe level is trace)");
|
|
9614
|
+
} else {
|
|
9615
|
+
tracePoller = createTracePoller(
|
|
9616
|
+
{
|
|
9617
|
+
url: `${observerBase}/trace/${deploymentId}`,
|
|
9618
|
+
token: ingestToken,
|
|
9619
|
+
intervalMs: 15e3
|
|
9620
|
+
},
|
|
9621
|
+
logger
|
|
9622
|
+
);
|
|
9623
|
+
tracePoller.start();
|
|
9624
|
+
logger.info("Trace poller: active (every 15s)");
|
|
9625
|
+
}
|
|
9546
9626
|
}
|
|
9547
9627
|
let currentBundleCleanup;
|
|
9548
9628
|
let currentConfigPath;
|
|
9549
|
-
if (api) {
|
|
9629
|
+
if (api && !configFrozen) {
|
|
9550
9630
|
heartbeat = createHeartbeat(
|
|
9551
9631
|
{
|
|
9552
9632
|
appUrl: api.appUrl,
|
|
@@ -9663,17 +9743,39 @@ async function runPipeline(options) {
|
|
|
9663
9743
|
await new Promise(() => {
|
|
9664
9744
|
});
|
|
9665
9745
|
}
|
|
9666
|
-
|
|
9746
|
+
var OBSERVE_LEVELS = [
|
|
9747
|
+
"off",
|
|
9748
|
+
"standard",
|
|
9749
|
+
"trace"
|
|
9750
|
+
];
|
|
9751
|
+
function readConfigFrozen() {
|
|
9752
|
+
const raw = process.env.WALKEROS_CONFIG_FROZEN;
|
|
9753
|
+
return raw === "1" || raw === "true";
|
|
9754
|
+
}
|
|
9755
|
+
function readObserveLevel(logger) {
|
|
9756
|
+
const raw = process.env.WALKEROS_OBSERVE_LEVEL;
|
|
9757
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
9758
|
+
const level = OBSERVE_LEVELS.find((candidate) => candidate === raw);
|
|
9759
|
+
if (!level) {
|
|
9760
|
+
logger.warn(
|
|
9761
|
+
`Ignoring invalid WALKEROS_OBSERVE_LEVEL "${raw}" (expected off, standard, or trace)`
|
|
9762
|
+
);
|
|
9763
|
+
return void 0;
|
|
9764
|
+
}
|
|
9765
|
+
return level;
|
|
9766
|
+
}
|
|
9767
|
+
function buildTelemetryObservers(flowId, observeLevel) {
|
|
9667
9768
|
const base = process.env.WALKEROS_OBSERVER_URL;
|
|
9668
9769
|
const token = process.env.WALKEROS_INGEST_TOKEN;
|
|
9669
9770
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
9670
9771
|
if (!base || !token || !deploymentId) return void 0;
|
|
9671
9772
|
const url = `${base}/ingest/${deploymentId}`;
|
|
9672
9773
|
const emit = ht({ url, token });
|
|
9774
|
+
const observe = observeLevel !== void 0 ? { level: observeLevel } : void 0;
|
|
9673
9775
|
return [
|
|
9674
9776
|
ft(
|
|
9675
9777
|
emit,
|
|
9676
|
-
() => pt({ flowId, traceUntil: mt() })
|
|
9778
|
+
() => pt({ flowId, observe, traceUntil: mt() })
|
|
9677
9779
|
)
|
|
9678
9780
|
];
|
|
9679
9781
|
}
|
|
@@ -11940,7 +12042,7 @@ function validateMapping(input) {
|
|
|
11940
12042
|
// src/commands/validate/validators/entry.ts
|
|
11941
12043
|
init_dist();
|
|
11942
12044
|
import Ajv from "ajv";
|
|
11943
|
-
var CLIENT_HEADER = "walkeros-cli/4.
|
|
12045
|
+
var CLIENT_HEADER = "walkeros-cli/4.3.0-next-1781171238534";
|
|
11944
12046
|
var SECTIONS = ["destinations", "sources", "transformers"];
|
|
11945
12047
|
function resolveEntry(path19, flowConfig) {
|
|
11946
12048
|
const flows = flowConfig.flows;
|
|
@@ -12575,6 +12677,7 @@ init_sse();
|
|
|
12575
12677
|
init_cli_logger();
|
|
12576
12678
|
init_output();
|
|
12577
12679
|
init_flows();
|
|
12680
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
12578
12681
|
async function resolveSettingsId(options) {
|
|
12579
12682
|
const flow = await getFlow({
|
|
12580
12683
|
flowId: options.flowId,
|
|
@@ -12600,8 +12703,9 @@ async function getAvailableFlowNames(options) {
|
|
|
12600
12703
|
const settings = flow.settings;
|
|
12601
12704
|
return settings?.map((c2) => c2.name) ?? [];
|
|
12602
12705
|
}
|
|
12706
|
+
var DEFAULT_DEPLOY_WAIT_MS = 12 * 60 * 1e3;
|
|
12603
12707
|
async function streamDeploymentStatus(projectId, deploymentId, options) {
|
|
12604
|
-
const timeoutMs = options.timeout ??
|
|
12708
|
+
const timeoutMs = options.timeout ?? DEFAULT_DEPLOY_WAIT_MS;
|
|
12605
12709
|
const response = await apiFetch(
|
|
12606
12710
|
`/api/projects/${projectId}/deployments/${deploymentId}/stream`,
|
|
12607
12711
|
{
|
|
@@ -12660,7 +12764,10 @@ async function deploy(options) {
|
|
|
12660
12764
|
}
|
|
12661
12765
|
const { data, error } = await client.POST(
|
|
12662
12766
|
"/api/projects/{projectId}/flows/{flowId}/deploy",
|
|
12663
|
-
{
|
|
12767
|
+
{
|
|
12768
|
+
params: { path: { projectId, flowId: options.flowId } },
|
|
12769
|
+
headers: { "Idempotency-Key": randomUUID2() }
|
|
12770
|
+
}
|
|
12664
12771
|
);
|
|
12665
12772
|
if (error) {
|
|
12666
12773
|
try {
|
|
@@ -12690,13 +12797,38 @@ Available: ${names.join(", ")}`,
|
|
|
12690
12797
|
}
|
|
12691
12798
|
async function deploySettings(options) {
|
|
12692
12799
|
const { flowId, projectId, settingsId } = options;
|
|
12693
|
-
const
|
|
12800
|
+
const triggerDeploy = () => apiFetch(
|
|
12694
12801
|
`/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
|
|
12695
|
-
{ method: "POST" }
|
|
12802
|
+
{ method: "POST", headers: { "Idempotency-Key": randomUUID2() } }
|
|
12696
12803
|
);
|
|
12804
|
+
let response = await triggerDeploy();
|
|
12805
|
+
if (!response.ok && options.wait) {
|
|
12806
|
+
let retryBody = {};
|
|
12807
|
+
try {
|
|
12808
|
+
retryBody = await response.clone().json();
|
|
12809
|
+
} catch {
|
|
12810
|
+
retryBody = {};
|
|
12811
|
+
}
|
|
12812
|
+
try {
|
|
12813
|
+
throwApiResponseError(
|
|
12814
|
+
response,
|
|
12815
|
+
retryBody,
|
|
12816
|
+
`Deploy failed (${response.status})`
|
|
12817
|
+
);
|
|
12818
|
+
} catch (e4) {
|
|
12819
|
+
if (e4 instanceof ApiError && e4.retryable) {
|
|
12820
|
+
const waitSeconds = Math.min(e4.retryAfterSeconds ?? 5, 60);
|
|
12821
|
+
options.onStatus?.("rate_limited", `retrying in ${waitSeconds}s`);
|
|
12822
|
+
await new Promise((resolve4) => setTimeout(resolve4, waitSeconds * 1e3));
|
|
12823
|
+
response = await triggerDeploy();
|
|
12824
|
+
} else {
|
|
12825
|
+
throw e4;
|
|
12826
|
+
}
|
|
12827
|
+
}
|
|
12828
|
+
}
|
|
12697
12829
|
if (!response.ok) {
|
|
12698
12830
|
const body = await response.json().catch(() => ({}));
|
|
12699
|
-
|
|
12831
|
+
throwApiResponseError(response, body, `Deploy failed (${response.status})`);
|
|
12700
12832
|
}
|
|
12701
12833
|
const data = await response.json();
|
|
12702
12834
|
if (!options.wait) return data;
|
|
@@ -12708,16 +12840,20 @@ async function deploySettings(options) {
|
|
|
12708
12840
|
return { ...data, ...result };
|
|
12709
12841
|
}
|
|
12710
12842
|
var statusLabels = {
|
|
12711
|
-
|
|
12712
|
-
"
|
|
12713
|
-
"bundling:publishing": "Publishing to web...",
|
|
12714
|
-
deploying: "Deploying container...",
|
|
12843
|
+
"deploying:building": "Building bundle...",
|
|
12844
|
+
"deploying:publishing": "Publishing to web...",
|
|
12715
12845
|
"deploying:provisioning": "Provisioning container...",
|
|
12716
12846
|
"deploying:starting": "Starting container...",
|
|
12847
|
+
deploying: "Deploying...",
|
|
12717
12848
|
active: "Container is live",
|
|
12718
12849
|
published: "Published",
|
|
12850
|
+
stopped: "Stopped",
|
|
12719
12851
|
failed: "Deployment failed"
|
|
12720
12852
|
};
|
|
12853
|
+
function renderStatusLabel(status, substatus) {
|
|
12854
|
+
const key = substatus ? `${status}:${substatus}` : status;
|
|
12855
|
+
return statusLabels[key] || statusLabels[status] || `Status: ${status}`;
|
|
12856
|
+
}
|
|
12721
12857
|
async function deployCommand(flowId, options) {
|
|
12722
12858
|
const log = createCLILogger(options);
|
|
12723
12859
|
const timeoutMs = options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0;
|
|
@@ -12729,10 +12865,7 @@ async function deployCommand(flowId, options) {
|
|
|
12729
12865
|
wait: options.wait !== false,
|
|
12730
12866
|
timeout: timeoutMs,
|
|
12731
12867
|
onStatus: options.json ? void 0 : (status, substatus) => {
|
|
12732
|
-
|
|
12733
|
-
log.info(
|
|
12734
|
-
statusLabels[key] || statusLabels[status] || `Status: ${status}`
|
|
12735
|
-
);
|
|
12868
|
+
log.info(renderStatusLabel(status, substatus));
|
|
12736
12869
|
}
|
|
12737
12870
|
});
|
|
12738
12871
|
if (options.json) {
|
|
@@ -12747,7 +12880,7 @@ async function deployCommand(flowId, options) {
|
|
|
12747
12880
|
} else if (r5.status === "failed") {
|
|
12748
12881
|
log.error(`Failed: ${r5.errorMessage || "Unknown error"}`);
|
|
12749
12882
|
process.exit(1);
|
|
12750
|
-
} else if (r5.status === "
|
|
12883
|
+
} else if (r5.status === "deploying") {
|
|
12751
12884
|
log.info(`Deployment started: ${r5.deploymentId} (${r5.type})`);
|
|
12752
12885
|
} else {
|
|
12753
12886
|
log.info(`Status: ${r5.status}`);
|
|
@@ -12902,20 +13035,16 @@ async function createDeployCommand(config, options) {
|
|
|
12902
13035
|
log.info(`Deployment created: ${result.id}`);
|
|
12903
13036
|
log.info(` Slug: ${result.slug}`);
|
|
12904
13037
|
log.info(` Type: ${result.type}`);
|
|
12905
|
-
if (result.deployToken) {
|
|
12906
|
-
log.info(` Token: ${result.deployToken}`);
|
|
12907
|
-
log.warn(" Save this token \u2014 it will not be shown again.");
|
|
12908
|
-
}
|
|
12909
13038
|
log.info("");
|
|
12910
13039
|
log.info("Run locally:");
|
|
12911
13040
|
log.info(
|
|
12912
13041
|
` walkeros run ${isRemoteFlow ? "flow.json" : config} --deploy ${result.id}`
|
|
12913
13042
|
);
|
|
12914
13043
|
log.info("");
|
|
12915
|
-
log.info("
|
|
12916
|
-
log.info(
|
|
12917
|
-
|
|
12918
|
-
);
|
|
13044
|
+
log.info("Create a deploy token for this flow in the app");
|
|
13045
|
+
log.info("(Settings, Self-hosted deploy token) and set it as");
|
|
13046
|
+
log.info("WALKEROS_DEPLOY_TOKEN, then run with Docker:");
|
|
13047
|
+
log.info(" docker run -e WALKEROS_DEPLOY_TOKEN \\");
|
|
12919
13048
|
log.info(" -e WALKEROS_APP_URL=https://app.walkeros.io \\");
|
|
12920
13049
|
log.info(" walkeros/flow:latest");
|
|
12921
13050
|
} catch (err) {
|
|
@@ -13231,7 +13360,7 @@ deployCmd.command("create [config]").description(
|
|
|
13231
13360
|
});
|
|
13232
13361
|
deployCmd.command("start <flowId>").description("Deploy a flow to walkerOS cloud (auto-detects web or server)").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("-f, --flow <name>", "flow name for multi-config flows").option("--no-wait", "do not wait for deployment to complete").option(
|
|
13233
13362
|
"--timeout <seconds>",
|
|
13234
|
-
"timeout for deployment polling (default:
|
|
13363
|
+
"timeout in seconds for deployment polling (default: 720, the 12-minute server deploy budget)"
|
|
13235
13364
|
).option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (flowId, options) => {
|
|
13236
13365
|
await deployCommand(flowId, options);
|
|
13237
13366
|
});
|