sim 2.1.6-preview.83.1 → 2.1.6-preview.89.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 +18 -1
- package/dist/index.js +447 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -256,9 +256,26 @@ The main environment variables are:
|
|
|
256
256
|
| `SIM_API_KEY` | API key, usually for CI |
|
|
257
257
|
| `SIM_WORKSPACE` | Workspace to target |
|
|
258
258
|
| `SIM_OUTPUT` | `table`, `json`, `yaml`, or `text` |
|
|
259
|
-
| `SIM_CONFIG_DIR` |
|
|
259
|
+
| `SIM_CONFIG_DIR` | Base directory for CLI config, credentials, and the update cache |
|
|
260
260
|
| `SIM_TIMEOUT_SECONDS` | Per-request timeout; `0` waits indefinitely |
|
|
261
261
|
| `SIM_DEBUG` | Print request diagnostics to stderr |
|
|
262
|
+
| `SIM_NO_UPDATE_CHECK` | Turn off the update notice |
|
|
263
|
+
|
|
264
|
+
On eligible interactive invocations, `sim` uses a daily cache before asking
|
|
265
|
+
`registry.npmjs.org` what is published under the `latest` tag and prints one
|
|
266
|
+
line on stderr when a newer version exists. Prerelease installs are skipped
|
|
267
|
+
entirely. The cache lives in `~/.sim` by default and follows `SIM_CONFIG_DIR`;
|
|
268
|
+
without a writable cache, each eligible invocation checks again. Concurrent
|
|
269
|
+
invocations can also perform duplicate checks. The registry request has a
|
|
270
|
+
one-second deadline; the short-lived request process is terminated on expiry.
|
|
271
|
+
Apart from the configured registry URL, it sends only its own version and never
|
|
272
|
+
your Sim API key. If `npm_config_registry` points at a private mirror, its query
|
|
273
|
+
string is preserved, including any query-string credentials. Registry URLs
|
|
274
|
+
containing username/password userinfo are rejected. Set
|
|
275
|
+
`SIM_NO_UPDATE_CHECK=1` to turn it off. Empty or whitespace-only registry values
|
|
276
|
+
use the public default; non-empty malformed or non-HTTP(S) values fail closed.
|
|
277
|
+
The full list of cases where it stays quiet is in the
|
|
278
|
+
[configuration guide](https://docs.sim.ai/cli/configuration).
|
|
262
279
|
|
|
263
280
|
## Documentation
|
|
264
281
|
|
package/dist/index.js
CHANGED
|
@@ -2310,6 +2310,9 @@ function configPath() {
|
|
|
2310
2310
|
function credentialsPath() {
|
|
2311
2311
|
return process.env.SIM_CREDENTIALS_FILE || join(configDir(), "credentials");
|
|
2312
2312
|
}
|
|
2313
|
+
function updateCachePath() {
|
|
2314
|
+
return join(configDir(), "update-check.json");
|
|
2315
|
+
}
|
|
2313
2316
|
// src/config/profile.ts
|
|
2314
2317
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2315
2318
|
import { dirname } from "node:path";
|
|
@@ -8263,6 +8266,22 @@ var V2_OPERATIONS = {
|
|
|
8263
8266
|
}
|
|
8264
8267
|
}
|
|
8265
8268
|
},
|
|
8269
|
+
editFileContent: {
|
|
8270
|
+
method: "PATCH",
|
|
8271
|
+
path: "/api/v2/files/[fileId]/content",
|
|
8272
|
+
pathParams: ["fileId"],
|
|
8273
|
+
pathParamDocs: { fileId: "File identifier." },
|
|
8274
|
+
responseMode: "json",
|
|
8275
|
+
summary: "Edit File Content",
|
|
8276
|
+
body: {
|
|
8277
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the file." },
|
|
8278
|
+
edit: {
|
|
8279
|
+
kind: "unknown",
|
|
8280
|
+
required: true,
|
|
8281
|
+
describe: "One exact or anchor-based edit: search_replace, replace_between, insert_after, or delete_between."
|
|
8282
|
+
}
|
|
8283
|
+
}
|
|
8284
|
+
},
|
|
8266
8285
|
executeTool: {
|
|
8267
8286
|
method: "POST",
|
|
8268
8287
|
path: "/api/v2/tools/[toolId]/execute",
|
|
@@ -9263,6 +9282,28 @@ var V2_OPERATIONS = {
|
|
|
9263
9282
|
values: ["active", "archived"],
|
|
9264
9283
|
default: "active",
|
|
9265
9284
|
describe: "Which lifecycle set to list: `active` (default) returns live folders only; `archived` returns folders a recursive delete soft-deleted, which is how a caller finds a path to hand to the folder restore. Authorization is identical for both."
|
|
9285
|
+
},
|
|
9286
|
+
recursive: {
|
|
9287
|
+
kind: "enum",
|
|
9288
|
+
values: [
|
|
9289
|
+
"true",
|
|
9290
|
+
"1",
|
|
9291
|
+
"yes",
|
|
9292
|
+
"on",
|
|
9293
|
+
"y",
|
|
9294
|
+
"enabled",
|
|
9295
|
+
"false",
|
|
9296
|
+
"0",
|
|
9297
|
+
"no",
|
|
9298
|
+
"off",
|
|
9299
|
+
"n",
|
|
9300
|
+
"disabled"
|
|
9301
|
+
],
|
|
9302
|
+
describe: "Whether parentPath includes every descendant instead of direct children only."
|
|
9303
|
+
},
|
|
9304
|
+
depth: {
|
|
9305
|
+
kind: "integer",
|
|
9306
|
+
describe: "Deepest level below parentPath to include when recursive is true."
|
|
9266
9307
|
}
|
|
9267
9308
|
}
|
|
9268
9309
|
},
|
|
@@ -10440,6 +10481,14 @@ var V2_OPERATIONS = {
|
|
|
10440
10481
|
maxBytes: {
|
|
10441
10482
|
kind: "integer",
|
|
10442
10483
|
describe: "Optional ceiling on the source bytes fed to the parser, lowering but never raising the server limit."
|
|
10484
|
+
},
|
|
10485
|
+
offset: {
|
|
10486
|
+
kind: "integer",
|
|
10487
|
+
describe: "First line to return, 1-based. Absent starts at the first line."
|
|
10488
|
+
},
|
|
10489
|
+
limit: {
|
|
10490
|
+
kind: "integer",
|
|
10491
|
+
describe: "How many lines to return from `offset`. Absent reads to the end."
|
|
10443
10492
|
}
|
|
10444
10493
|
}
|
|
10445
10494
|
},
|
|
@@ -10770,6 +10819,50 @@ var V2_OPERATIONS = {
|
|
|
10770
10819
|
workspaceId: { kind: "string", required: true, describe: "Unique workspace identifier." }
|
|
10771
10820
|
}
|
|
10772
10821
|
},
|
|
10822
|
+
searchFileContent: {
|
|
10823
|
+
method: "GET",
|
|
10824
|
+
path: "/api/v2/files/search",
|
|
10825
|
+
pathParams: [],
|
|
10826
|
+
responseMode: "json",
|
|
10827
|
+
summary: "Search File Content",
|
|
10828
|
+
query: {
|
|
10829
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace to search." },
|
|
10830
|
+
query: {
|
|
10831
|
+
kind: "string",
|
|
10832
|
+
required: true,
|
|
10833
|
+
describe: "Regular expression, or exact text when `mode` is `exact`."
|
|
10834
|
+
},
|
|
10835
|
+
mode: {
|
|
10836
|
+
kind: "enum",
|
|
10837
|
+
values: ["exact", "regex"],
|
|
10838
|
+
default: "regex",
|
|
10839
|
+
describe: "How `query` is read."
|
|
10840
|
+
},
|
|
10841
|
+
maxResults: { kind: "integer", default: 50, describe: "Maximum matching lines to return." },
|
|
10842
|
+
folderPaths: {
|
|
10843
|
+
kind: "string",
|
|
10844
|
+
describe: "Folders the search is confined to, comma-separated. Absent searches the whole workspace. The scope also narrows `indexStatus`, so `complete` describes the folders searched rather than the workspace."
|
|
10845
|
+
},
|
|
10846
|
+
includeSubfolders: {
|
|
10847
|
+
kind: "enum",
|
|
10848
|
+
values: [
|
|
10849
|
+
"true",
|
|
10850
|
+
"1",
|
|
10851
|
+
"yes",
|
|
10852
|
+
"on",
|
|
10853
|
+
"y",
|
|
10854
|
+
"enabled",
|
|
10855
|
+
"false",
|
|
10856
|
+
"0",
|
|
10857
|
+
"no",
|
|
10858
|
+
"off",
|
|
10859
|
+
"n",
|
|
10860
|
+
"disabled"
|
|
10861
|
+
],
|
|
10862
|
+
describe: "Whether the scope descends into nested folders. Absent means yes. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected."
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
},
|
|
10773
10866
|
searchKnowledge: {
|
|
10774
10867
|
method: "POST",
|
|
10775
10868
|
path: "/api/v2/knowledge/search",
|
|
@@ -12019,6 +12112,7 @@ var TABLE_SORT_HELP = 'Ordered sort keys: [{"field":"createdAt","direction":"des
|
|
|
12019
12112
|
var KNOWLEDGE_TAG_DEFINITIONS_HELP = 'Tag definitions: [{"tagSlot":"tag1","displayName":"category","fieldType":"text"}]';
|
|
12020
12113
|
var CUSTOM_TOOL_SCHEMA_HELP = 'OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}}';
|
|
12021
12114
|
var DISPATCH_ROW_LIMIT_HELP = "Stop after this many eligible rows have run (1-1,000,000). Omit for an unbounded run";
|
|
12115
|
+
var FILE_EDIT_HELP = 'One edit object: {"mode":"search_replace","search":"old","content":"new","replaceAll":false}, {"mode":"replace_between","beforeAnchor":"start line","afterAnchor":"end line","content":"new"}, {"mode":"insert_after","anchor":"line","content":"new"}, or {"mode":"delete_between","startAnchor":"first line deleted","endAnchor":"ending line kept"}. Anchored modes also accept occurrence starting at 1';
|
|
12022
12116
|
var WORKFLOW_OPERATIONS_HELP = 'Edits to apply, in a single batch, keyed by operation_type: [{"operation_type":"add","block_id":"my-fn","params":{"type":"function","name":"My Fn","inputs":{"code":"return {ok:true}"}}},{"operation_type":"edit","block_id":"<uuid>","params":{"name":"Renamed","connections":{"success":"my-fn"}}},{"operation_type":"delete","block_id":"<uuid>"}]. Also extract_from_subflow, whose params carry {"subflowId":"<loop-id>"}, and insert_into_subflow, which creates a block and so takes an add’s params plus that subflowId';
|
|
12023
12117
|
var WORKFLOW_SET_BLOCK_ENABLED_HELP = 'Blocks to enable or disable, applied after --operations: [{"block_id":"<uuid>","enabled":false}]. Disabling a loop or parallel cascades to its unlocked descendants; enabling a block whose container is disabled is declined';
|
|
12024
12118
|
var WORKFLOW_VARIABLE_OPERATIONS_HELP = 'Variable changes to apply in order, keyed by operation: [{"operation":"add","name":"my_var","type":"string","value":"hello"},{"operation":"edit","name":"my_var","value":"updated"},{"operation":"delete","name":"my_var"}]';
|
|
@@ -12399,6 +12493,25 @@ var CLI_CONTRACT = {
|
|
|
12399
12493
|
flags: { folderPaths: FOLDER_PATHS_FLAG },
|
|
12400
12494
|
confirm: "This deletes every listed table and all of their rows."
|
|
12401
12495
|
},
|
|
12496
|
+
searchFileContent: {
|
|
12497
|
+
flags: {
|
|
12498
|
+
folderPaths: {
|
|
12499
|
+
...FOLDER_PATHS_FLAG,
|
|
12500
|
+
describe: "Folders to search, by path as shown in the app; omit to search the whole workspace"
|
|
12501
|
+
},
|
|
12502
|
+
includeSubfolders: {
|
|
12503
|
+
boolean: true,
|
|
12504
|
+
negatable: true,
|
|
12505
|
+
describe: "Whether each folder scope includes nested folders; on by default"
|
|
12506
|
+
}
|
|
12507
|
+
},
|
|
12508
|
+
itemsPath: "results",
|
|
12509
|
+
columns: [
|
|
12510
|
+
{ header: "file", path: "fileId" },
|
|
12511
|
+
{ header: "line", path: "lineNumber" },
|
|
12512
|
+
{ header: "text" }
|
|
12513
|
+
]
|
|
12514
|
+
},
|
|
12402
12515
|
searchKnowledge: {
|
|
12403
12516
|
flags: {
|
|
12404
12517
|
knowledgeBaseIds: { name: "kb", list: true, describe: "Knowledge base ID (repeatable)" },
|
|
@@ -12818,6 +12931,13 @@ var CLI_CONTRACT = {
|
|
|
12818
12931
|
folderPaths: FOLDER_PATHS_FLAG
|
|
12819
12932
|
}
|
|
12820
12933
|
},
|
|
12934
|
+
editFileContent: {
|
|
12935
|
+
command: "files edit",
|
|
12936
|
+
describe: "Apply one exact or anchor-based edit to a text file",
|
|
12937
|
+
flags: {
|
|
12938
|
+
edit: { describe: FILE_EDIT_HELP }
|
|
12939
|
+
}
|
|
12940
|
+
},
|
|
12821
12941
|
updateFileContent: {
|
|
12822
12942
|
command: "files set-content",
|
|
12823
12943
|
describe: "Replace a file’s contents",
|
|
@@ -13531,7 +13651,7 @@ function withoutWireVocabulary(documented) {
|
|
|
13531
13651
|
return documented.replace(WIRE_VOCABULARY_SENTENCE, " ").trim();
|
|
13532
13652
|
}
|
|
13533
13653
|
var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
|
|
13534
|
-
function addFieldOption(command, operation, field, descriptor, slot, paginates) {
|
|
13654
|
+
function addFieldOption(command, operation, field, descriptor, slot, paginates, capsAFilter) {
|
|
13535
13655
|
if (field === PROFILE_INJECTED_FIELD || field === "cursor")
|
|
13536
13656
|
return;
|
|
13537
13657
|
const flag = flagSpecFor(operation, field);
|
|
@@ -13543,7 +13663,7 @@ function addFieldOption(command, operation, field, descriptor, slot, paginates)
|
|
|
13543
13663
|
command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
|
|
13544
13664
|
return;
|
|
13545
13665
|
}
|
|
13546
|
-
const documented = `${describeField(flag, descriptor, name, field)}${field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
|
|
13666
|
+
const documented = `${describeField(flag, descriptor, name, field)}${capsAFilter && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
|
|
13547
13667
|
if (descriptor.kind === "boolean" || flag.boolean) {
|
|
13548
13668
|
const booleanDoc = withoutWireVocabulary(documented);
|
|
13549
13669
|
if (descriptor.required) {
|
|
@@ -13591,13 +13711,14 @@ function addOperationOptions(command, operation, commandSpec, operationSpec) {
|
|
|
13591
13711
|
command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
|
|
13592
13712
|
}
|
|
13593
13713
|
const paginates = cursorSlot(operationSpec) !== null;
|
|
13714
|
+
const capsAFilter = ["query", "body", "headers"].some((slot) => operationSpec[slot] !== undefined && ("filter" in operationSpec[slot]));
|
|
13594
13715
|
for (const slot of ["query", "body", "headers"]) {
|
|
13595
13716
|
for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
|
|
13596
13717
|
if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
|
|
13597
13718
|
continue;
|
|
13598
13719
|
if (commandSpec.positionals?.includes(field))
|
|
13599
13720
|
continue;
|
|
13600
|
-
addFieldOption(command, operation, field, descriptor, slot, paginates);
|
|
13721
|
+
addFieldOption(command, operation, field, descriptor, slot, paginates, capsAFilter);
|
|
13601
13722
|
}
|
|
13602
13723
|
}
|
|
13603
13724
|
if (commandSpec.allWorkspaces) {
|
|
@@ -16336,6 +16457,328 @@ function attachSecretCommands(program2) {
|
|
|
16336
16457
|
secrets.command("set").argument("<name>", "Secret name, as referenced in workflows").description(describeOperation(V2_OPERATIONS.setSecret, "Create or replace a named secret")).addOption(new Option("--scope <scope>", "Secret ownership scope (required)").choices([...SECRET_SCOPES]).makeOptionMandatory()).option("--value <value|@file>", "Secret value. Passing it inline exposes it to shell history and process listings; @path reads it from a file and @- from stdin, verbatim — a trailing newline is part of the value, so write the file with printf rather than echo. Prefix a literal leading @ with a second one").option("--description <description>", "What the secret is for, shown to teammates; workspace scope only. Omit to leave an existing description unchanged").option("--unredacted", `${V2_OPERATIONS.setSecret.body.unredacted.describe} Pass --no-unredacted to restore redaction`).option("--no-unredacted", "Send --unredacted as false").on("option:unredacted", () => redactionSpellings.add("--unredacted")).on("option:no-unredacted", () => redactionSpellings.add("--no-unredacted")).action((name, options, command) => setSecret(name, options, command, redactionSpellings));
|
|
16337
16458
|
}
|
|
16338
16459
|
|
|
16460
|
+
// src/update/check.ts
|
|
16461
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
16462
|
+
import {
|
|
16463
|
+
closeSync,
|
|
16464
|
+
constants as constants2,
|
|
16465
|
+
fstatSync,
|
|
16466
|
+
lstatSync,
|
|
16467
|
+
mkdirSync as mkdirSync2,
|
|
16468
|
+
openSync,
|
|
16469
|
+
readSync as readSync2,
|
|
16470
|
+
renameSync,
|
|
16471
|
+
unlinkSync,
|
|
16472
|
+
writeFileSync as writeFileSync2
|
|
16473
|
+
} from "node:fs";
|
|
16474
|
+
import { dirname as dirname3 } from "node:path";
|
|
16475
|
+
import { fileURLToPath } from "node:url";
|
|
16476
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
16477
|
+
var REGISTRY_TIMEOUT_MS = 1000;
|
|
16478
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
16479
|
+
var PACKAGE_NAME = "sim";
|
|
16480
|
+
var DIST_TAGS_PATH = `-/package/${PACKAGE_NAME}/dist-tags`;
|
|
16481
|
+
var MAX_RESPONSE_BYTES = 64 * 1024;
|
|
16482
|
+
var MAX_CACHE_BYTES = 4 * 1024;
|
|
16483
|
+
var STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
16484
|
+
function parseStableVersion(version) {
|
|
16485
|
+
const match = STABLE_VERSION_PATTERN.exec(version);
|
|
16486
|
+
if (!match)
|
|
16487
|
+
return null;
|
|
16488
|
+
const parsed = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
16489
|
+
return parsed.every(Number.isSafeInteger) ? parsed : null;
|
|
16490
|
+
}
|
|
16491
|
+
function isNewerVersion(candidate, current) {
|
|
16492
|
+
if (candidate[0] !== current[0])
|
|
16493
|
+
return candidate[0] > current[0];
|
|
16494
|
+
if (candidate[1] !== current[1])
|
|
16495
|
+
return candidate[1] > current[1];
|
|
16496
|
+
return candidate[2] > current[2];
|
|
16497
|
+
}
|
|
16498
|
+
var CI_VARIABLES = [
|
|
16499
|
+
"CI",
|
|
16500
|
+
"GITHUB_ACTIONS",
|
|
16501
|
+
"JENKINS_URL",
|
|
16502
|
+
"TEAMCITY_VERSION",
|
|
16503
|
+
"BUILDKITE"
|
|
16504
|
+
];
|
|
16505
|
+
var CACHE_VERSION = 1;
|
|
16506
|
+
var cacheWriteSequence = 0;
|
|
16507
|
+
function isEnabled(value) {
|
|
16508
|
+
if (value === undefined)
|
|
16509
|
+
return false;
|
|
16510
|
+
const normalized = value.trim().toLowerCase();
|
|
16511
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
16512
|
+
}
|
|
16513
|
+
function isProjectLocalInstall(modulePath, cwd) {
|
|
16514
|
+
const normalizedModulePath = normalizeModulePath(modulePath);
|
|
16515
|
+
const nodeModulesIndex = normalizedModulePath.indexOf("/node_modules/");
|
|
16516
|
+
if (nodeModulesIndex < 0)
|
|
16517
|
+
return false;
|
|
16518
|
+
const installRoot = normalizedModulePath.slice(0, nodeModulesIndex);
|
|
16519
|
+
const workingDirectory = normalizeModulePath(cwd).replace(/\/+$/, "");
|
|
16520
|
+
return workingDirectory === installRoot || workingDirectory.startsWith(`${installRoot}/`);
|
|
16521
|
+
}
|
|
16522
|
+
function isUnadvisableInstall(modulePath, env2, cwd) {
|
|
16523
|
+
const normalized = normalizeModulePath(modulePath);
|
|
16524
|
+
return env2.npm_command === "exec" || normalized.includes("/_npx/") || normalized.includes("/packages/sim-cli/") || isProjectLocalInstall(modulePath, cwd);
|
|
16525
|
+
}
|
|
16526
|
+
function normalizeModulePath(modulePath) {
|
|
16527
|
+
return modulePath.replace(/\\/g, "/").toLowerCase();
|
|
16528
|
+
}
|
|
16529
|
+
function registryUrl(env2) {
|
|
16530
|
+
const fallback = new URL(DIST_TAGS_PATH, DEFAULT_REGISTRY);
|
|
16531
|
+
const configured = env2.npm_config_registry?.trim();
|
|
16532
|
+
if (!configured)
|
|
16533
|
+
return fallback;
|
|
16534
|
+
try {
|
|
16535
|
+
const base = new URL(configured);
|
|
16536
|
+
if (base.protocol !== "http:" && base.protocol !== "https:")
|
|
16537
|
+
return null;
|
|
16538
|
+
if (base.username || base.password)
|
|
16539
|
+
return null;
|
|
16540
|
+
base.pathname = `${base.pathname.replace(/\/$/, "")}/${DIST_TAGS_PATH}`;
|
|
16541
|
+
return base;
|
|
16542
|
+
} catch {
|
|
16543
|
+
return null;
|
|
16544
|
+
}
|
|
16545
|
+
}
|
|
16546
|
+
var REGISTRY_REQUEST_SCRIPT = `
|
|
16547
|
+
let input = ''
|
|
16548
|
+
process.stdin.setEncoding('utf8')
|
|
16549
|
+
for await (const chunk of process.stdin) input += chunk
|
|
16550
|
+
|
|
16551
|
+
try {
|
|
16552
|
+
const { url, headers, maxResponseBytes, timeoutMs } = JSON.parse(input)
|
|
16553
|
+
const deadline = setTimeout(() => process.exit(1), timeoutMs)
|
|
16554
|
+
const response = await fetch(url, { headers, redirect: 'error' })
|
|
16555
|
+
const declared = Number(response.headers.get('content-length'))
|
|
16556
|
+
|
|
16557
|
+
if (!response.ok || !response.body || (Number.isFinite(declared) && declared > maxResponseBytes)) {
|
|
16558
|
+
process.exit(1)
|
|
16559
|
+
}
|
|
16560
|
+
|
|
16561
|
+
const reader = response.body.getReader()
|
|
16562
|
+
const chunks = []
|
|
16563
|
+
let seen = 0
|
|
16564
|
+
|
|
16565
|
+
while (true) {
|
|
16566
|
+
const { done, value } = await reader.read()
|
|
16567
|
+
if (done) break
|
|
16568
|
+
seen += value.byteLength
|
|
16569
|
+
if (seen > maxResponseBytes) {
|
|
16570
|
+
process.exit(1)
|
|
16571
|
+
}
|
|
16572
|
+
chunks.push(Buffer.from(value))
|
|
16573
|
+
}
|
|
16574
|
+
|
|
16575
|
+
clearTimeout(deadline)
|
|
16576
|
+
process.stdout.write(Buffer.concat(chunks), () => process.exit(0))
|
|
16577
|
+
} catch {
|
|
16578
|
+
process.exit(1)
|
|
16579
|
+
}
|
|
16580
|
+
`;
|
|
16581
|
+
function registryProcessEnv() {
|
|
16582
|
+
const env2 = { ...process.env };
|
|
16583
|
+
for (const key of Object.keys(env2)) {
|
|
16584
|
+
const normalized = key.toLowerCase();
|
|
16585
|
+
if (normalized === "npm_config_registry" || normalized === "sim_api_key")
|
|
16586
|
+
delete env2[key];
|
|
16587
|
+
}
|
|
16588
|
+
return env2;
|
|
16589
|
+
}
|
|
16590
|
+
function requestRegistry(url, { headers, maxResponseBytes, timeoutMs }) {
|
|
16591
|
+
return new Promise((resolve3, reject) => {
|
|
16592
|
+
const proxyArguments = process.execArgv.filter((argument) => argument === "--use-env-proxy" || argument === "--no-use-env-proxy");
|
|
16593
|
+
const child = spawn2(process.execPath, [...proxyArguments, "--input-type=module", "--eval", REGISTRY_REQUEST_SCRIPT], {
|
|
16594
|
+
env: registryProcessEnv(),
|
|
16595
|
+
killSignal: "SIGKILL",
|
|
16596
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
16597
|
+
timeout: timeoutMs,
|
|
16598
|
+
windowsHide: true
|
|
16599
|
+
});
|
|
16600
|
+
const chunks = [];
|
|
16601
|
+
let failed = false;
|
|
16602
|
+
let seen = 0;
|
|
16603
|
+
child.stdout.on("data", (chunk) => {
|
|
16604
|
+
seen += chunk.byteLength;
|
|
16605
|
+
if (seen > maxResponseBytes) {
|
|
16606
|
+
failed = true;
|
|
16607
|
+
child.kill("SIGKILL");
|
|
16608
|
+
return;
|
|
16609
|
+
}
|
|
16610
|
+
chunks.push(chunk);
|
|
16611
|
+
});
|
|
16612
|
+
child.stdout.on("error", () => {
|
|
16613
|
+
failed = true;
|
|
16614
|
+
child.kill("SIGKILL");
|
|
16615
|
+
});
|
|
16616
|
+
child.stdin.on("error", () => {});
|
|
16617
|
+
child.once("error", reject);
|
|
16618
|
+
child.once("close", (code) => {
|
|
16619
|
+
resolve3(code === 0 && !failed ? Buffer.concat(chunks).toString("utf8") : null);
|
|
16620
|
+
});
|
|
16621
|
+
child.stdin.end(JSON.stringify({ headers, maxResponseBytes, timeoutMs, url: url.href }));
|
|
16622
|
+
});
|
|
16623
|
+
}
|
|
16624
|
+
async function fetchDistTags(env2, request) {
|
|
16625
|
+
try {
|
|
16626
|
+
const url = registryUrl(env2);
|
|
16627
|
+
if (!url)
|
|
16628
|
+
return null;
|
|
16629
|
+
const text2 = await request(url, {
|
|
16630
|
+
headers: { accept: "application/json", "user-agent": `${PACKAGE_NAME}-cli/${CLI_VERSION}` },
|
|
16631
|
+
maxResponseBytes: MAX_RESPONSE_BYTES,
|
|
16632
|
+
timeoutMs: REGISTRY_TIMEOUT_MS
|
|
16633
|
+
});
|
|
16634
|
+
if (text2 === null || Buffer.byteLength(text2) > MAX_RESPONSE_BYTES)
|
|
16635
|
+
return null;
|
|
16636
|
+
const body = JSON.parse(text2);
|
|
16637
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
16638
|
+
return null;
|
|
16639
|
+
const tags = {};
|
|
16640
|
+
for (const [tag, version] of Object.entries(body)) {
|
|
16641
|
+
if (typeof version === "string")
|
|
16642
|
+
tags[tag] = version;
|
|
16643
|
+
}
|
|
16644
|
+
return tags;
|
|
16645
|
+
} catch {
|
|
16646
|
+
return null;
|
|
16647
|
+
}
|
|
16648
|
+
}
|
|
16649
|
+
function readCache(path) {
|
|
16650
|
+
let descriptor = null;
|
|
16651
|
+
try {
|
|
16652
|
+
if (!lstatSync(path).isFile())
|
|
16653
|
+
return null;
|
|
16654
|
+
descriptor = openSync(path, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOFOLLOW);
|
|
16655
|
+
const descriptorStats = fstatSync(descriptor);
|
|
16656
|
+
if (!descriptorStats.isFile() || descriptorStats.size > MAX_CACHE_BYTES) {
|
|
16657
|
+
return null;
|
|
16658
|
+
}
|
|
16659
|
+
const buffer = Buffer.allocUnsafe(MAX_CACHE_BYTES + 1);
|
|
16660
|
+
let bytesRead = 0;
|
|
16661
|
+
while (bytesRead < buffer.byteLength) {
|
|
16662
|
+
const count = readSync2(descriptor, buffer, bytesRead, buffer.byteLength - bytesRead, bytesRead);
|
|
16663
|
+
if (count === 0)
|
|
16664
|
+
break;
|
|
16665
|
+
bytesRead += count;
|
|
16666
|
+
}
|
|
16667
|
+
if (bytesRead > MAX_CACHE_BYTES)
|
|
16668
|
+
return null;
|
|
16669
|
+
const parsed = JSON.parse(buffer.subarray(0, bytesRead).toString("utf8"));
|
|
16670
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
16671
|
+
return null;
|
|
16672
|
+
const entry = parsed;
|
|
16673
|
+
if (entry.version !== CACHE_VERSION)
|
|
16674
|
+
return null;
|
|
16675
|
+
if (typeof entry.checkedAt !== "string" || Number.isNaN(Date.parse(entry.checkedAt)))
|
|
16676
|
+
return null;
|
|
16677
|
+
return {
|
|
16678
|
+
version: CACHE_VERSION,
|
|
16679
|
+
checkedAt: entry.checkedAt
|
|
16680
|
+
};
|
|
16681
|
+
} catch {
|
|
16682
|
+
return null;
|
|
16683
|
+
} finally {
|
|
16684
|
+
if (descriptor !== null) {
|
|
16685
|
+
try {
|
|
16686
|
+
closeSync(descriptor);
|
|
16687
|
+
} catch {}
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
}
|
|
16691
|
+
function writeCache(path, entry) {
|
|
16692
|
+
let descriptor = null;
|
|
16693
|
+
let temporaryCreated = false;
|
|
16694
|
+
const temporaryPath = `${path}.${process.pid}.${Date.now()}.${cacheWriteSequence++}.tmp`;
|
|
16695
|
+
try {
|
|
16696
|
+
mkdirSync2(dirname3(path), { recursive: true, mode: 448 });
|
|
16697
|
+
descriptor = openSync(temporaryPath, "wx", 420);
|
|
16698
|
+
temporaryCreated = true;
|
|
16699
|
+
writeFileSync2(descriptor, `${JSON.stringify(entry, null, 2)}
|
|
16700
|
+
`);
|
|
16701
|
+
closeSync(descriptor);
|
|
16702
|
+
descriptor = null;
|
|
16703
|
+
renameSync(temporaryPath, path);
|
|
16704
|
+
temporaryCreated = false;
|
|
16705
|
+
} catch {} finally {
|
|
16706
|
+
if (descriptor !== null) {
|
|
16707
|
+
try {
|
|
16708
|
+
closeSync(descriptor);
|
|
16709
|
+
} catch {}
|
|
16710
|
+
}
|
|
16711
|
+
if (temporaryCreated) {
|
|
16712
|
+
try {
|
|
16713
|
+
unlinkSync(temporaryPath);
|
|
16714
|
+
} catch {}
|
|
16715
|
+
}
|
|
16716
|
+
}
|
|
16717
|
+
}
|
|
16718
|
+
function isFresh(entry, now) {
|
|
16719
|
+
const age = now.getTime() - Date.parse(entry.checkedAt);
|
|
16720
|
+
return age >= 0 && age < CHECK_INTERVAL_MS;
|
|
16721
|
+
}
|
|
16722
|
+
function upgradeCommand(modulePath = fileURLToPath(import.meta.url), env2 = process.env) {
|
|
16723
|
+
const target = `${PACKAGE_NAME}@latest`;
|
|
16724
|
+
const normalized = normalizeModulePath(modulePath);
|
|
16725
|
+
if (normalized.includes(".bun/install/global"))
|
|
16726
|
+
return `bun add -g ${target}`;
|
|
16727
|
+
if (normalized.includes("/pnpm/") || normalized.includes("/.pnpm/")) {
|
|
16728
|
+
return `pnpm add -g ${target}`;
|
|
16729
|
+
}
|
|
16730
|
+
if (normalized.includes("/.yarn/") || normalized.includes("/yarn/")) {
|
|
16731
|
+
return `yarn global add ${target}`;
|
|
16732
|
+
}
|
|
16733
|
+
const agent = env2.npm_config_user_agent ?? "";
|
|
16734
|
+
if (agent.startsWith("pnpm/"))
|
|
16735
|
+
return `pnpm add -g ${target}`;
|
|
16736
|
+
if (agent.startsWith("yarn/"))
|
|
16737
|
+
return `yarn global add ${target}`;
|
|
16738
|
+
if (agent.startsWith("bun/"))
|
|
16739
|
+
return `bun add -g ${target}`;
|
|
16740
|
+
return `npm install -g ${target}`;
|
|
16741
|
+
}
|
|
16742
|
+
async function announceUpdateIfAvailable(options = {}) {
|
|
16743
|
+
try {
|
|
16744
|
+
const env2 = options.env ?? process.env;
|
|
16745
|
+
const isTty = options.isTty ?? process.stderr.isTTY === true;
|
|
16746
|
+
const modulePath = options.modulePath ?? fileURLToPath(import.meta.url);
|
|
16747
|
+
const cwd = options.cwd ?? process.cwd();
|
|
16748
|
+
const now = options.now ?? new Date;
|
|
16749
|
+
if (isEnabled(env2.SIM_NO_UPDATE_CHECK))
|
|
16750
|
+
return;
|
|
16751
|
+
if (!isTty)
|
|
16752
|
+
return;
|
|
16753
|
+
if (CI_VARIABLES.some((variable) => isEnabled(env2[variable])))
|
|
16754
|
+
return;
|
|
16755
|
+
if (isUnadvisableInstall(modulePath, env2, cwd))
|
|
16756
|
+
return;
|
|
16757
|
+
const currentVersion = options.currentVersion ?? CLI_VERSION;
|
|
16758
|
+
const current = parseStableVersion(currentVersion);
|
|
16759
|
+
if (!current)
|
|
16760
|
+
return;
|
|
16761
|
+
const cachePath = updateCachePath();
|
|
16762
|
+
const cached = readCache(cachePath);
|
|
16763
|
+
if (cached && isFresh(cached, now))
|
|
16764
|
+
return;
|
|
16765
|
+
const tags = await fetchDistTags(env2, options.registryRequest ?? requestRegistry);
|
|
16766
|
+
const latest = tags?.latest ?? null;
|
|
16767
|
+
const available = latest ? parseStableVersion(latest) : null;
|
|
16768
|
+
writeCache(cachePath, {
|
|
16769
|
+
version: CACHE_VERSION,
|
|
16770
|
+
checkedAt: now.toISOString()
|
|
16771
|
+
});
|
|
16772
|
+
if (!latest || !available)
|
|
16773
|
+
return;
|
|
16774
|
+
if (!isNewerVersion(available, current))
|
|
16775
|
+
return;
|
|
16776
|
+
const write = options.write ?? ((message) => void process.stderr.write(message));
|
|
16777
|
+
write(`Update available: sim ${currentVersion} → ${latest}. Run: ${upgradeCommand(modulePath, env2)}
|
|
16778
|
+
`);
|
|
16779
|
+
} catch {}
|
|
16780
|
+
}
|
|
16781
|
+
|
|
16339
16782
|
// src/program.ts
|
|
16340
16783
|
var PROGRAM_DESCRIPTION = "Talk to the Sim API from your terminal";
|
|
16341
16784
|
var HELP_EPILOGUE = `
|
|
@@ -16415,6 +16858,7 @@ function buildProgram(options = {}) {
|
|
|
16415
16858
|
attachProtocolCommands(program2);
|
|
16416
16859
|
attachSecretCommands(program2);
|
|
16417
16860
|
program2.addHelpText("after", HELP_EPILOGUE);
|
|
16861
|
+
program2.hook("preAction", () => announceUpdateIfAvailable());
|
|
16418
16862
|
refuseHelpAfterUnknownCommand(program2);
|
|
16419
16863
|
assertNoReservedProgramFlags(program2);
|
|
16420
16864
|
return program2;
|