sim 2.0.0-preview.16.1 → 2.0.0-preview.18.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 +146 -16
- 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
|
};
|
|
@@ -8939,6 +9046,10 @@ var V2_OPERATIONS = {
|
|
|
8939
9046
|
kind: "string",
|
|
8940
9047
|
required: true,
|
|
8941
9048
|
describe: "Write-only secret value. It is never returned."
|
|
9049
|
+
},
|
|
9050
|
+
description: {
|
|
9051
|
+
kind: "string",
|
|
9052
|
+
describe: "What the secret is for, shown to teammates. Workspace scope only — sending it for a personal secret is rejected. Omit it to leave an existing description untouched; send null or an empty string to clear one."
|
|
8942
9053
|
}
|
|
8943
9054
|
}
|
|
8944
9055
|
},
|
|
@@ -9996,7 +10107,8 @@ var CLI_CONTRACT = {
|
|
|
9996
10107
|
{ header: "name" },
|
|
9997
10108
|
{ header: "scope" },
|
|
9998
10109
|
{ header: "role" },
|
|
9999
|
-
{ header: "updated", path: "updatedAt", format: "timestamp" }
|
|
10110
|
+
{ header: "updated", path: "updatedAt", format: "timestamp" },
|
|
10111
|
+
{ header: "description" }
|
|
10000
10112
|
]
|
|
10001
10113
|
},
|
|
10002
10114
|
getWorkspace: {
|
|
@@ -11059,7 +11171,7 @@ function attachCredentialCommands(program2) {
|
|
|
11059
11171
|
}
|
|
11060
11172
|
|
|
11061
11173
|
// src/commands/protocol/files-get.ts
|
|
11062
|
-
import { once } from "node:events";
|
|
11174
|
+
import { once as once2 } from "node:events";
|
|
11063
11175
|
import { createWriteStream } from "node:fs";
|
|
11064
11176
|
import { link, lstat, mkdtemp, readlink, rename, rm } from "node:fs/promises";
|
|
11065
11177
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
|
|
@@ -11074,6 +11186,9 @@ function printProtocolResult(format, result) {
|
|
|
11074
11186
|
|
|
11075
11187
|
// src/commands/protocol/files-get.ts
|
|
11076
11188
|
function writeFailure(path, error) {
|
|
11189
|
+
if (isRequestTimeout(error)) {
|
|
11190
|
+
return new SimApiError(`Downloading ${path} timed out. ${RAISE_TIMEOUT_HINT}`, 0);
|
|
11191
|
+
}
|
|
11077
11192
|
const code = error.code;
|
|
11078
11193
|
if (code === "EEXIST") {
|
|
11079
11194
|
return new SimApiError(`${path} already exists. Pass --force to overwrite it, or choose another output path.`, 0);
|
|
@@ -11164,7 +11279,7 @@ async function streamToStdout(body, output = process.stdout) {
|
|
|
11164
11279
|
if (done)
|
|
11165
11280
|
return;
|
|
11166
11281
|
if (!output.write(value))
|
|
11167
|
-
await
|
|
11282
|
+
await once2(output, "drain");
|
|
11168
11283
|
}
|
|
11169
11284
|
} finally {
|
|
11170
11285
|
reader.releaseLock();
|
|
@@ -11608,17 +11723,17 @@ function jsonFlag(raw, flagName, kind) {
|
|
|
11608
11723
|
}
|
|
11609
11724
|
async function watchImport(client, workspaceId, job) {
|
|
11610
11725
|
let current = job;
|
|
11611
|
-
let
|
|
11726
|
+
let reported2 = -1;
|
|
11612
11727
|
while (!IMPORT_SETTLED.has(current.status)) {
|
|
11613
11728
|
await sleep2(IMPORT_POLL_MS);
|
|
11614
11729
|
const next = await client.request(`/api/v2/tables/imports/${encodeURIComponent(current.id)}`, { query: { workspaceId } });
|
|
11615
11730
|
current = next.data;
|
|
11616
|
-
if (process.stderr.isTTY && current.rowsProcessed !==
|
|
11617
|
-
|
|
11618
|
-
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`);
|
|
11619
11734
|
}
|
|
11620
11735
|
}
|
|
11621
|
-
if (process.stderr.isTTY &&
|
|
11736
|
+
if (process.stderr.isTTY && reported2 >= 0)
|
|
11622
11737
|
process.stderr.write("\r\x1B[K");
|
|
11623
11738
|
return current;
|
|
11624
11739
|
}
|
|
@@ -11834,7 +11949,8 @@ var SECRET_RESULT = {
|
|
|
11834
11949
|
{ header: "name" },
|
|
11835
11950
|
{ header: "scope" },
|
|
11836
11951
|
{ header: "role" },
|
|
11837
|
-
{ header: "updated", path: "updatedAt", format: "timestamp" }
|
|
11952
|
+
{ header: "updated", path: "updatedAt", format: "timestamp" },
|
|
11953
|
+
{ header: "description" }
|
|
11838
11954
|
]
|
|
11839
11955
|
};
|
|
11840
11956
|
function validateSecretValue(value) {
|
|
@@ -11845,7 +11961,16 @@ function validateSecretValue(value) {
|
|
|
11845
11961
|
}
|
|
11846
11962
|
return value;
|
|
11847
11963
|
}
|
|
11964
|
+
function validateDescriptionScope(description, scope) {
|
|
11965
|
+
if (description === undefined)
|
|
11966
|
+
return;
|
|
11967
|
+
if (scope === "personal") {
|
|
11968
|
+
throw new SimApiError("--description is only supported for a workspace secret.", 0);
|
|
11969
|
+
}
|
|
11970
|
+
return description;
|
|
11971
|
+
}
|
|
11848
11972
|
async function setSecret(name, options, command) {
|
|
11973
|
+
const description = validateDescriptionScope(options.description, options.scope);
|
|
11849
11974
|
const value = validateSecretValue(options.value ?? await promptSecret());
|
|
11850
11975
|
const { client, profile } = clientFrom(command);
|
|
11851
11976
|
const operation = V2_OPERATIONS.setSecret;
|
|
@@ -11854,7 +11979,8 @@ async function setSecret(name, options, command) {
|
|
|
11854
11979
|
body: {
|
|
11855
11980
|
workspaceId: client.requireWorkspace(),
|
|
11856
11981
|
scope: options.scope,
|
|
11857
|
-
value
|
|
11982
|
+
value,
|
|
11983
|
+
description
|
|
11858
11984
|
}
|
|
11859
11985
|
});
|
|
11860
11986
|
renderResult("setSecret", profile.output, response.data, SECRET_RESULT);
|
|
@@ -11863,7 +11989,7 @@ function attachSecretCommands(program2) {
|
|
|
11863
11989
|
const secrets = program2.commands.find((command) => command.name() === "secrets");
|
|
11864
11990
|
if (!secrets)
|
|
11865
11991
|
throw new Error("The generated secrets command group is missing");
|
|
11866
|
-
secrets.command("set").argument("<name>", "Secret name, as referenced in workflows").description("Create or replace a named secret").addOption(new Option("--scope <scope>", "Secret ownership scope").choices([...SECRET_SCOPES]).makeOptionMandatory()).option("--value <value>", "Secret value; visible to shell history when supplied directly").action((name, options, command) => setSecret(name, options, command));
|
|
11992
|
+
secrets.command("set").argument("<name>", "Secret name, as referenced in workflows").description("Create or replace a named secret").addOption(new Option("--scope <scope>", "Secret ownership scope").choices([...SECRET_SCOPES]).makeOptionMandatory()).option("--value <value>", "Secret value; visible to shell history when supplied directly").option("--description <description>", "What the secret is for, shown to teammates; workspace scope only. Omit to leave an existing description unchanged").action((name, options, command) => setSecret(name, options, command));
|
|
11867
11993
|
}
|
|
11868
11994
|
|
|
11869
11995
|
// src/runtime/renamed.ts
|
|
@@ -12232,6 +12358,10 @@ async function main() {
|
|
|
12232
12358
|
console.error(source_default.red(`Error: ${sanitize(error.message)}`));
|
|
12233
12359
|
process.exit(1);
|
|
12234
12360
|
}
|
|
12361
|
+
if (isRequestTimeout(error)) {
|
|
12362
|
+
console.error(source_default.red(`Error: the request timed out. ${RAISE_TIMEOUT_HINT}`));
|
|
12363
|
+
process.exit(1);
|
|
12364
|
+
}
|
|
12235
12365
|
if (error instanceof SimApiError) {
|
|
12236
12366
|
console.error(source_default.red(`Error: ${sanitize(error.message)}`));
|
|
12237
12367
|
if (error.code)
|