sim 2.0.0-dev.19.1 → 2.0.0-dev.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/index.js +126 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,6 +57,11 @@ Each setting resolves independently, first match wins:
|
|
|
57
57
|
| --- | --- |
|
|
58
58
|
| 1 | Command-line flag (`--endpoint`, `--workspace`, `--output`) |
|
|
59
59
|
| 2 | Environment (`SIM_ENDPOINT`, `SIM_API_KEY`, `SIM_WORKSPACE`, `SIM_OUTPUT`) |
|
|
60
|
+
|
|
61
|
+
`SIM_TIMEOUT_SECONDS` bounds each request (default `3600`, `0` waits
|
|
62
|
+
indefinitely) and `SIM_DEBUG=1` traces requests to stderr. Node ignores
|
|
63
|
+
`HTTPS_PROXY` unless `NODE_USE_ENV_PROXY=1` is also set, on Node 22.21+ or
|
|
64
|
+
24.5+; the CLI warns when a proxy is configured but will not be used.
|
|
60
65
|
| 3 | `~/.sim/config` / `~/.sim/credentials` for the selected profile |
|
|
61
66
|
| 4 | Built-in default (`https://www.sim.ai`, `table`) |
|
|
62
67
|
|
package/dist/index.js
CHANGED
|
@@ -2539,6 +2539,53 @@ function readPackageVersion() {
|
|
|
2539
2539
|
var CLI_VERSION = readPackageVersion();
|
|
2540
2540
|
var USER_AGENT = `sim-cli/${CLI_VERSION} node/${process.versions.node} (${process.platform}; ${process.arch})`;
|
|
2541
2541
|
|
|
2542
|
+
// src/http/environment.ts
|
|
2543
|
+
var reported = new Set;
|
|
2544
|
+
function once(key, message) {
|
|
2545
|
+
if (reported.has(key))
|
|
2546
|
+
return;
|
|
2547
|
+
reported.add(key);
|
|
2548
|
+
process.stderr.write(`warning: ${message}
|
|
2549
|
+
`);
|
|
2550
|
+
}
|
|
2551
|
+
var PROXY_VARIABLES = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
|
|
2552
|
+
var PROXY_SUPPORT = { 22: 21, 24: 5 };
|
|
2553
|
+
var FIRST_SUPPORTED_MAJOR = 24;
|
|
2554
|
+
function runtimeCanProxy(version) {
|
|
2555
|
+
const [major, minor] = version.replace(/^v/, "").split(".").map(Number);
|
|
2556
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
2557
|
+
return false;
|
|
2558
|
+
const firstSupportedMinor = PROXY_SUPPORT[major];
|
|
2559
|
+
if (firstSupportedMinor !== undefined)
|
|
2560
|
+
return minor >= firstSupportedMinor;
|
|
2561
|
+
return major > FIRST_SUPPORTED_MAJOR;
|
|
2562
|
+
}
|
|
2563
|
+
function warnIfProxyIgnored(env2 = process.env, version = process.version) {
|
|
2564
|
+
const variable = PROXY_VARIABLES.find((name) => env2[name]);
|
|
2565
|
+
if (!variable)
|
|
2566
|
+
return;
|
|
2567
|
+
if (env2.NODE_USE_ENV_PROXY && runtimeCanProxy(version))
|
|
2568
|
+
return;
|
|
2569
|
+
once("proxy", runtimeCanProxy(version) ? `${variable} is set but Node only uses it when NODE_USE_ENV_PROXY=1. Re-run with NODE_USE_ENV_PROXY=1 to route through the proxy.` : `${variable} is set but Node ${version} cannot use it. Upgrade to Node 22.21 or 24.5 and set NODE_USE_ENV_PROXY=1 to route through the proxy.`);
|
|
2570
|
+
}
|
|
2571
|
+
var LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]", "::1", "0.0.0.0"]);
|
|
2572
|
+
function isLoopback(hostname) {
|
|
2573
|
+
return LOOPBACK.has(hostname) || hostname.endsWith(".localhost");
|
|
2574
|
+
}
|
|
2575
|
+
function warnIfKeyOverCleartext(endpoint, hasApiKey) {
|
|
2576
|
+
if (!hasApiKey)
|
|
2577
|
+
return;
|
|
2578
|
+
let url;
|
|
2579
|
+
try {
|
|
2580
|
+
url = new URL(endpoint);
|
|
2581
|
+
} catch {
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
if (url.protocol !== "http:" || isLoopback(url.hostname))
|
|
2585
|
+
return;
|
|
2586
|
+
once("cleartext", `sending your API key to ${url.host} over http. Anything on the path can read it — use https unless this network is trusted.`);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2542
2589
|
// src/http/client.ts
|
|
2543
2590
|
class SimApiError extends Error {
|
|
2544
2591
|
status;
|
|
@@ -2623,6 +2670,51 @@ function dropUnionBranchNoise(issues) {
|
|
|
2623
2670
|
const kept = issues.filter((issue) => !issues.some((other) => rejectsAKeyThatValidated(issue, other)));
|
|
2624
2671
|
return kept.length > 0 ? kept : issues;
|
|
2625
2672
|
}
|
|
2673
|
+
var DEFAULT_TIMEOUT_SECONDS = 3600;
|
|
2674
|
+
var MAX_TIMEOUT_MS = 2 ** 31 - 1;
|
|
2675
|
+
var RAISE_TIMEOUT_HINT = "Raise SIM_TIMEOUT_SECONDS, or set it to 0 to wait indefinitely.";
|
|
2676
|
+
function isRequestTimeout(error) {
|
|
2677
|
+
return error instanceof DOMException && error.name === "TimeoutError";
|
|
2678
|
+
}
|
|
2679
|
+
function resolveTimeoutMs(env2 = process.env) {
|
|
2680
|
+
const raw = env2.SIM_TIMEOUT_SECONDS;
|
|
2681
|
+
if (raw === undefined || raw.trim() === "")
|
|
2682
|
+
return DEFAULT_TIMEOUT_SECONDS * 1000;
|
|
2683
|
+
const seconds = Number(raw);
|
|
2684
|
+
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
2685
|
+
throw new SimApiError(`Invalid SIM_TIMEOUT_SECONDS "${raw}". Use a non-negative number of seconds, or 0 to disable.`, 0);
|
|
2686
|
+
}
|
|
2687
|
+
const ms = seconds === 0 ? 0 : Math.max(1, Math.round(seconds * 1000));
|
|
2688
|
+
if (ms > MAX_TIMEOUT_MS) {
|
|
2689
|
+
throw new SimApiError(`SIM_TIMEOUT_SECONDS "${raw}" is longer than Node can wait (${Math.floor(MAX_TIMEOUT_MS / 1000)}s). Use 0 to wait indefinitely.`, 0);
|
|
2690
|
+
}
|
|
2691
|
+
return ms;
|
|
2692
|
+
}
|
|
2693
|
+
function combineSignals(caller, timeout) {
|
|
2694
|
+
if (!caller)
|
|
2695
|
+
return timeout;
|
|
2696
|
+
if (!timeout)
|
|
2697
|
+
return caller;
|
|
2698
|
+
if (typeof AbortSignal.any === "function")
|
|
2699
|
+
return AbortSignal.any([caller, timeout]);
|
|
2700
|
+
const controller = new AbortController;
|
|
2701
|
+
for (const signal of [caller, timeout]) {
|
|
2702
|
+
if (signal.aborted) {
|
|
2703
|
+
controller.abort(signal.reason);
|
|
2704
|
+
break;
|
|
2705
|
+
}
|
|
2706
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
|
|
2707
|
+
}
|
|
2708
|
+
return controller.signal;
|
|
2709
|
+
}
|
|
2710
|
+
function debugEnabled(env2 = process.env) {
|
|
2711
|
+
const raw = env2.SIM_DEBUG;
|
|
2712
|
+
return raw !== undefined && raw !== "" && raw !== "0" && raw.toLowerCase() !== "false";
|
|
2713
|
+
}
|
|
2714
|
+
function traceRequest(method, url, status, startedAt) {
|
|
2715
|
+
process.stderr.write(`${source_default.dim(`[sim] ${method} ${url} → ${status} ${Math.round(performance.now() - startedAt)}ms`)}
|
|
2716
|
+
`);
|
|
2717
|
+
}
|
|
2626
2718
|
function formatApiErrorDetails(details) {
|
|
2627
2719
|
const issues = [];
|
|
2628
2720
|
const seen = new Set;
|
|
@@ -2706,10 +2798,18 @@ class SimClient {
|
|
|
2706
2798
|
const apiKey = this.resolveApiKey(options.auth);
|
|
2707
2799
|
const url = buildUrl(this.profile.endpoint, path, options.query);
|
|
2708
2800
|
const hasBody = options.body !== undefined;
|
|
2801
|
+
const method = options.method ?? "GET";
|
|
2802
|
+
warnIfProxyIgnored();
|
|
2803
|
+
warnIfKeyOverCleartext(this.profile.endpoint, Boolean(apiKey));
|
|
2804
|
+
const timeoutMs = resolveTimeoutMs();
|
|
2805
|
+
const timeout = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
2806
|
+
const signal = combineSignals(options.signal, timeout);
|
|
2807
|
+
const trace = debugEnabled();
|
|
2808
|
+
const startedAt = performance.now();
|
|
2709
2809
|
let response;
|
|
2710
2810
|
try {
|
|
2711
2811
|
response = await fetch(url, {
|
|
2712
|
-
method
|
|
2812
|
+
method,
|
|
2713
2813
|
headers: {
|
|
2714
2814
|
...apiKey ? { "x-api-key": apiKey } : {},
|
|
2715
2815
|
accept: "application/json",
|
|
@@ -2718,15 +2818,22 @@ class SimClient {
|
|
|
2718
2818
|
...options.headers
|
|
2719
2819
|
},
|
|
2720
2820
|
body: hasBody ? JSON.stringify(options.body) : undefined,
|
|
2721
|
-
signal
|
|
2821
|
+
signal,
|
|
2722
2822
|
redirect: "manual"
|
|
2723
2823
|
});
|
|
2724
2824
|
} catch (cause) {
|
|
2825
|
+
if (trace)
|
|
2826
|
+
traceRequest(method, url, "failed", startedAt);
|
|
2725
2827
|
if (options.signal?.aborted) {
|
|
2726
2828
|
throw new SimApiError("Request cancelled.", 0);
|
|
2727
2829
|
}
|
|
2830
|
+
if (timeout?.aborted) {
|
|
2831
|
+
throw new SimApiError(`${url} did not answer within ${timeoutMs / 1000}s. ${RAISE_TIMEOUT_HINT}`, 0);
|
|
2832
|
+
}
|
|
2728
2833
|
throw new SimApiError(`Could not reach ${this.profile.endpoint}: ${cause.message}`, 0);
|
|
2729
2834
|
}
|
|
2835
|
+
if (trace)
|
|
2836
|
+
traceRequest(method, url, response.status, startedAt);
|
|
2730
2837
|
if (REDIRECT_STATUSES.has(response.status))
|
|
2731
2838
|
throw this.toRedirectError(url, path, response);
|
|
2732
2839
|
if (!response.ok) {
|
|
@@ -2768,16 +2875,16 @@ function redirectEndpoint(endpoint, requestPath, target) {
|
|
|
2768
2875
|
return suggested === endpoint.replace(/\/+$/, "") ? null : suggested;
|
|
2769
2876
|
}
|
|
2770
2877
|
function pageProgress() {
|
|
2771
|
-
let
|
|
2878
|
+
let reported2 = false;
|
|
2772
2879
|
return {
|
|
2773
2880
|
advance: (fetched) => {
|
|
2774
2881
|
if (!process.stderr.isTTY)
|
|
2775
2882
|
return;
|
|
2776
|
-
|
|
2883
|
+
reported2 = true;
|
|
2777
2884
|
process.stderr.write(`\r${source_default.dim(`fetched ${fetched}…`)}\x1B[K`);
|
|
2778
2885
|
},
|
|
2779
2886
|
finish: () => {
|
|
2780
|
-
if (
|
|
2887
|
+
if (reported2)
|
|
2781
2888
|
process.stderr.write("\r\x1B[K");
|
|
2782
2889
|
}
|
|
2783
2890
|
};
|
|
@@ -11064,7 +11171,7 @@ function attachCredentialCommands(program2) {
|
|
|
11064
11171
|
}
|
|
11065
11172
|
|
|
11066
11173
|
// src/commands/protocol/files-get.ts
|
|
11067
|
-
import { once } from "node:events";
|
|
11174
|
+
import { once as once2 } from "node:events";
|
|
11068
11175
|
import { createWriteStream } from "node:fs";
|
|
11069
11176
|
import { link, lstat, mkdtemp, readlink, rename, rm } from "node:fs/promises";
|
|
11070
11177
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
|
|
@@ -11079,6 +11186,9 @@ function printProtocolResult(format, result) {
|
|
|
11079
11186
|
|
|
11080
11187
|
// src/commands/protocol/files-get.ts
|
|
11081
11188
|
function writeFailure(path, error) {
|
|
11189
|
+
if (isRequestTimeout(error)) {
|
|
11190
|
+
return new SimApiError(`Downloading ${path} timed out. ${RAISE_TIMEOUT_HINT}`, 0);
|
|
11191
|
+
}
|
|
11082
11192
|
const code = error.code;
|
|
11083
11193
|
if (code === "EEXIST") {
|
|
11084
11194
|
return new SimApiError(`${path} already exists. Pass --force to overwrite it, or choose another output path.`, 0);
|
|
@@ -11169,7 +11279,7 @@ async function streamToStdout(body, output = process.stdout) {
|
|
|
11169
11279
|
if (done)
|
|
11170
11280
|
return;
|
|
11171
11281
|
if (!output.write(value))
|
|
11172
|
-
await
|
|
11282
|
+
await once2(output, "drain");
|
|
11173
11283
|
}
|
|
11174
11284
|
} finally {
|
|
11175
11285
|
reader.releaseLock();
|
|
@@ -11613,17 +11723,17 @@ function jsonFlag(raw, flagName, kind) {
|
|
|
11613
11723
|
}
|
|
11614
11724
|
async function watchImport(client, workspaceId, job) {
|
|
11615
11725
|
let current = job;
|
|
11616
|
-
let
|
|
11726
|
+
let reported2 = -1;
|
|
11617
11727
|
while (!IMPORT_SETTLED.has(current.status)) {
|
|
11618
11728
|
await sleep2(IMPORT_POLL_MS);
|
|
11619
11729
|
const next = await client.request(`/api/v2/tables/imports/${encodeURIComponent(current.id)}`, { query: { workspaceId } });
|
|
11620
11730
|
current = next.data;
|
|
11621
|
-
if (process.stderr.isTTY && current.rowsProcessed !==
|
|
11622
|
-
|
|
11623
|
-
process.stderr.write(`\r${source_default.dim(`${current.status}… ${
|
|
11731
|
+
if (process.stderr.isTTY && current.rowsProcessed !== reported2) {
|
|
11732
|
+
reported2 = current.rowsProcessed;
|
|
11733
|
+
process.stderr.write(`\r${source_default.dim(`${current.status}… ${reported2} rows`)}\x1B[K`);
|
|
11624
11734
|
}
|
|
11625
11735
|
}
|
|
11626
|
-
if (process.stderr.isTTY &&
|
|
11736
|
+
if (process.stderr.isTTY && reported2 >= 0)
|
|
11627
11737
|
process.stderr.write("\r\x1B[K");
|
|
11628
11738
|
return current;
|
|
11629
11739
|
}
|
|
@@ -12248,6 +12358,10 @@ async function main() {
|
|
|
12248
12358
|
console.error(source_default.red(`Error: ${sanitize(error.message)}`));
|
|
12249
12359
|
process.exit(1);
|
|
12250
12360
|
}
|
|
12361
|
+
if (isRequestTimeout(error)) {
|
|
12362
|
+
console.error(source_default.red(`Error: the request timed out. ${RAISE_TIMEOUT_HINT}`));
|
|
12363
|
+
process.exit(1);
|
|
12364
|
+
}
|
|
12251
12365
|
if (error instanceof SimApiError) {
|
|
12252
12366
|
console.error(source_default.red(`Error: ${sanitize(error.message)}`));
|
|
12253
12367
|
if (error.code)
|