sim 2.1.6-preview.84.1 → 2.1.6-preview.90.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 +495 -5
- 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";
|
|
@@ -7465,6 +7468,47 @@ var V2_OPERATIONS = {
|
|
|
7465
7468
|
}
|
|
7466
7469
|
}
|
|
7467
7470
|
},
|
|
7471
|
+
createSandbox: {
|
|
7472
|
+
method: "POST",
|
|
7473
|
+
path: "/api/v2/sandboxes",
|
|
7474
|
+
pathParams: [],
|
|
7475
|
+
responseMode: "json",
|
|
7476
|
+
summary: "Create Sandbox",
|
|
7477
|
+
personalKeyOnly: true,
|
|
7478
|
+
body: {
|
|
7479
|
+
workspaceId: {
|
|
7480
|
+
kind: "string",
|
|
7481
|
+
required: true,
|
|
7482
|
+
describe: "Workspace in which to create the sandbox."
|
|
7483
|
+
},
|
|
7484
|
+
name: {
|
|
7485
|
+
kind: "string",
|
|
7486
|
+
required: true,
|
|
7487
|
+
describe: "Display name, unique within the workspace; 1 to 64 characters."
|
|
7488
|
+
},
|
|
7489
|
+
language: {
|
|
7490
|
+
kind: "enum",
|
|
7491
|
+
required: true,
|
|
7492
|
+
values: ["javascript", "python"],
|
|
7493
|
+
describe: "Dependency ecosystem: `javascript` installs from npm, `python` from PyPI."
|
|
7494
|
+
},
|
|
7495
|
+
dependencies: {
|
|
7496
|
+
kind: "array",
|
|
7497
|
+
default: [],
|
|
7498
|
+
describe: "Package specifiers installed into the sandbox, one per entry."
|
|
7499
|
+
},
|
|
7500
|
+
cliTools: {
|
|
7501
|
+
kind: "array",
|
|
7502
|
+
default: [],
|
|
7503
|
+
describe: "Pinned managed CLI ids installed into the sandbox, at most 10, no duplicates."
|
|
7504
|
+
},
|
|
7505
|
+
systemPackages: {
|
|
7506
|
+
kind: "array",
|
|
7507
|
+
default: [],
|
|
7508
|
+
describe: "Debian packages installed into the sandbox, one per entry."
|
|
7509
|
+
}
|
|
7510
|
+
}
|
|
7511
|
+
},
|
|
7468
7512
|
createServiceAccountCredential: {
|
|
7469
7513
|
method: "POST",
|
|
7470
7514
|
path: "/api/v2/credentials",
|
|
@@ -7982,6 +8026,18 @@ var V2_OPERATIONS = {
|
|
|
7982
8026
|
}
|
|
7983
8027
|
}
|
|
7984
8028
|
},
|
|
8029
|
+
deleteSandbox: {
|
|
8030
|
+
method: "DELETE",
|
|
8031
|
+
path: "/api/v2/sandboxes/[sandboxId]",
|
|
8032
|
+
pathParams: ["sandboxId"],
|
|
8033
|
+
pathParamDocs: { sandboxId: "Unique sandbox identifier." },
|
|
8034
|
+
responseMode: "json",
|
|
8035
|
+
summary: "Delete Sandbox",
|
|
8036
|
+
personalKeyOnly: true,
|
|
8037
|
+
query: {
|
|
8038
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the sandbox." }
|
|
8039
|
+
}
|
|
8040
|
+
},
|
|
7985
8041
|
deleteSecret: {
|
|
7986
8042
|
method: "DELETE",
|
|
7987
8043
|
path: "/api/v2/secrets/[name]",
|
|
@@ -8675,6 +8731,17 @@ var V2_OPERATIONS = {
|
|
|
8675
8731
|
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the table." }
|
|
8676
8732
|
}
|
|
8677
8733
|
},
|
|
8734
|
+
getSandbox: {
|
|
8735
|
+
method: "GET",
|
|
8736
|
+
path: "/api/v2/sandboxes/[sandboxId]",
|
|
8737
|
+
pathParams: ["sandboxId"],
|
|
8738
|
+
pathParamDocs: { sandboxId: "Unique sandbox identifier." },
|
|
8739
|
+
responseMode: "json",
|
|
8740
|
+
summary: "Get Sandbox",
|
|
8741
|
+
query: {
|
|
8742
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the sandbox." }
|
|
8743
|
+
}
|
|
8744
|
+
},
|
|
8678
8745
|
getSkill: {
|
|
8679
8746
|
method: "GET",
|
|
8680
8747
|
path: "/api/v2/skills/[skillId]",
|
|
@@ -9825,6 +9892,41 @@ var V2_OPERATIONS = {
|
|
|
9825
9892
|
}
|
|
9826
9893
|
}
|
|
9827
9894
|
},
|
|
9895
|
+
listSandboxes: {
|
|
9896
|
+
method: "GET",
|
|
9897
|
+
path: "/api/v2/sandboxes",
|
|
9898
|
+
pathParams: [],
|
|
9899
|
+
responseMode: "json",
|
|
9900
|
+
summary: "List Sandboxes",
|
|
9901
|
+
query: {
|
|
9902
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the sandbox." },
|
|
9903
|
+
search: {
|
|
9904
|
+
kind: "string",
|
|
9905
|
+
describe: "Case-insensitive substring match against the sandbox name."
|
|
9906
|
+
},
|
|
9907
|
+
sortBy: {
|
|
9908
|
+
kind: "enum",
|
|
9909
|
+
values: ["name", "createdAt", "updatedAt"],
|
|
9910
|
+
default: "name",
|
|
9911
|
+
describe: "Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order."
|
|
9912
|
+
},
|
|
9913
|
+
sortOrder: {
|
|
9914
|
+
kind: "enum",
|
|
9915
|
+
values: ["asc", "desc"],
|
|
9916
|
+
default: "asc",
|
|
9917
|
+
describe: "Sort direction."
|
|
9918
|
+
},
|
|
9919
|
+
limit: {
|
|
9920
|
+
kind: "integer",
|
|
9921
|
+
default: 50,
|
|
9922
|
+
describe: "Maximum sandboxes to return per page. Must be a whole number from 1 to 100. Defaults to 50."
|
|
9923
|
+
},
|
|
9924
|
+
cursor: {
|
|
9925
|
+
kind: "string",
|
|
9926
|
+
describe: "Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor."
|
|
9927
|
+
}
|
|
9928
|
+
}
|
|
9929
|
+
},
|
|
9828
9930
|
listSecrets: {
|
|
9829
9931
|
method: "GET",
|
|
9830
9932
|
path: "/api/v2/secrets",
|
|
@@ -11374,6 +11476,39 @@ var V2_OPERATIONS = {
|
|
|
11374
11476
|
limit: { kind: "integer", describe: "Maximum matching rows to update." }
|
|
11375
11477
|
}
|
|
11376
11478
|
},
|
|
11479
|
+
updateSandbox: {
|
|
11480
|
+
method: "PATCH",
|
|
11481
|
+
path: "/api/v2/sandboxes/[sandboxId]",
|
|
11482
|
+
pathParams: ["sandboxId"],
|
|
11483
|
+
pathParamDocs: { sandboxId: "Unique sandbox identifier." },
|
|
11484
|
+
responseMode: "json",
|
|
11485
|
+
summary: "Update Sandbox",
|
|
11486
|
+
personalKeyOnly: true,
|
|
11487
|
+
body: {
|
|
11488
|
+
workspaceId: { kind: "string", required: true, describe: "Workspace that owns the sandbox." },
|
|
11489
|
+
name: {
|
|
11490
|
+
kind: "string",
|
|
11491
|
+
describe: "New display name, unique within the workspace; 1 to 64 characters."
|
|
11492
|
+
},
|
|
11493
|
+
language: {
|
|
11494
|
+
kind: "enum",
|
|
11495
|
+
values: ["javascript", "python"],
|
|
11496
|
+
describe: "Replacement dependency ecosystem. The whole spec is revalidated against it, so a Python dependency list does not survive a switch to JavaScript."
|
|
11497
|
+
},
|
|
11498
|
+
dependencies: {
|
|
11499
|
+
kind: "array",
|
|
11500
|
+
describe: "Replacement package list; replaces the whole list."
|
|
11501
|
+
},
|
|
11502
|
+
cliTools: {
|
|
11503
|
+
kind: "array",
|
|
11504
|
+
describe: "Replacement managed CLI list; replaces the whole list."
|
|
11505
|
+
},
|
|
11506
|
+
systemPackages: {
|
|
11507
|
+
kind: "array",
|
|
11508
|
+
describe: "Replacement Debian package list; replaces the whole list."
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
},
|
|
11377
11512
|
updateSkill: {
|
|
11378
11513
|
method: "PATCH",
|
|
11379
11514
|
path: "/api/v2/skills/[skillId]",
|
|
@@ -12307,6 +12442,9 @@ var CLI_CONTRACT = {
|
|
|
12307
12442
|
confirm: "This revokes the explicit skill editor grant for the selected email."
|
|
12308
12443
|
},
|
|
12309
12444
|
deleteCustomTool: { confirm: "This deletes the custom tool." },
|
|
12445
|
+
deleteSandbox: {
|
|
12446
|
+
confirm: "This deletes the sandbox; Function blocks that select it fail until re-pointed."
|
|
12447
|
+
},
|
|
12310
12448
|
deleteMcpServer: {
|
|
12311
12449
|
confirm: "This removes the MCP server and the tools it provides."
|
|
12312
12450
|
},
|
|
@@ -12593,6 +12731,20 @@ var CLI_CONTRACT = {
|
|
|
12593
12731
|
importWorkflow: { flags: { folderPath: FOLDER_PATH_FLAG } },
|
|
12594
12732
|
createCustomTool: { flags: { schema: { json: true, describe: CUSTOM_TOOL_SCHEMA_HELP } } },
|
|
12595
12733
|
updateCustomTool: { flags: { schema: { json: true, describe: CUSTOM_TOOL_SCHEMA_HELP } } },
|
|
12734
|
+
createSandbox: {
|
|
12735
|
+
flags: {
|
|
12736
|
+
dependencies: { list: true, manifest: true },
|
|
12737
|
+
cliTools: { list: true },
|
|
12738
|
+
systemPackages: { list: true, manifest: true }
|
|
12739
|
+
}
|
|
12740
|
+
},
|
|
12741
|
+
updateSandbox: {
|
|
12742
|
+
flags: {
|
|
12743
|
+
dependencies: { list: true, manifest: true },
|
|
12744
|
+
cliTools: { list: true },
|
|
12745
|
+
systemPackages: { list: true, manifest: true }
|
|
12746
|
+
}
|
|
12747
|
+
},
|
|
12596
12748
|
listTables: {
|
|
12597
12749
|
flags: { folderPath: FOLDER_PATH_FLAG },
|
|
12598
12750
|
columns: [
|
|
@@ -12734,6 +12886,15 @@ var CLI_CONTRACT = {
|
|
|
12734
12886
|
{ header: "updated", path: "updatedAt", format: "timestamp" }
|
|
12735
12887
|
]
|
|
12736
12888
|
},
|
|
12889
|
+
listSandboxes: {
|
|
12890
|
+
columns: [
|
|
12891
|
+
{ header: "id" },
|
|
12892
|
+
{ header: "name" },
|
|
12893
|
+
{ header: "language" },
|
|
12894
|
+
{ header: "status", path: "buildStatus" },
|
|
12895
|
+
{ header: "updated", path: "updatedAt", format: "timestamp" }
|
|
12896
|
+
]
|
|
12897
|
+
},
|
|
12737
12898
|
listCredentials: {
|
|
12738
12899
|
columns: [
|
|
12739
12900
|
{ header: "id" },
|
|
@@ -13439,7 +13600,11 @@ function readArgumentSource(raw, flagName) {
|
|
|
13439
13600
|
throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}${literalAtHint(error, path)}`, 0);
|
|
13440
13601
|
}
|
|
13441
13602
|
}
|
|
13442
|
-
function
|
|
13603
|
+
function isManifestNoise(line) {
|
|
13604
|
+
const trimmed = line.trim();
|
|
13605
|
+
return trimmed === "" || trimmed.startsWith("#");
|
|
13606
|
+
}
|
|
13607
|
+
function readListValues(raw, flagName, manifest = false) {
|
|
13443
13608
|
const arguments_ = Array.isArray(raw) ? raw : [raw];
|
|
13444
13609
|
const values = arguments_.flatMap((argument) => {
|
|
13445
13610
|
if (typeof argument !== "string") {
|
|
@@ -13451,10 +13616,11 @@ function readListValues(raw, flagName) {
|
|
|
13451
13616
|
const lines = source.text.split(/\r?\n/);
|
|
13452
13617
|
if (lines.at(-1) === "")
|
|
13453
13618
|
lines.pop();
|
|
13454
|
-
|
|
13619
|
+
const kept = manifest ? lines.filter((line) => !isManifestNoise(line)) : lines;
|
|
13620
|
+
if (kept.length === 0) {
|
|
13455
13621
|
throw new SimApiError(`--${flagName}${source.from} contains no values`, 0);
|
|
13456
13622
|
}
|
|
13457
|
-
return
|
|
13623
|
+
return kept.map((line, index) => {
|
|
13458
13624
|
const value = line.trim();
|
|
13459
13625
|
if (!value) {
|
|
13460
13626
|
throw new SimApiError(`--${flagName}${source.from} has an empty value on line ${index + 1}`, 0);
|
|
@@ -13499,7 +13665,7 @@ function coerce(raw, field, flag, flagName) {
|
|
|
13499
13665
|
if (raw === undefined)
|
|
13500
13666
|
return;
|
|
13501
13667
|
if (flag.list) {
|
|
13502
|
-
const values = readListValues(raw, flagName).map((value) => flag.folderPath ? encodeFolderPath(value) : value);
|
|
13668
|
+
const values = readListValues(raw, flagName, flag.manifest === true).map((value) => flag.folderPath ? encodeFolderPath(value) : value);
|
|
13503
13669
|
return field.kind === "string" ? values.join(",") : values;
|
|
13504
13670
|
}
|
|
13505
13671
|
if (flag.rowCap)
|
|
@@ -13678,7 +13844,7 @@ function addFieldOption(command, operation, field, descriptor, slot, paginates,
|
|
|
13678
13844
|
const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
|
|
13679
13845
|
const choices = flag.choices ?? descriptor.values;
|
|
13680
13846
|
const literalNull = slot === "body" && !takesList && !wantsJson;
|
|
13681
|
-
const describe = `${documented}${takesList ? " (space-separated, or @path / @- with one value per line; @@value for a literal leading @)" : wantsJson ? " (JSON, or @path / @- to read a file or stdin)" : ""}${descriptor.required ? " (required)" : ""}${literalNull ? literalNullHint(documented, name) : ""}`;
|
|
13847
|
+
const describe = `${documented}${takesList ? flag.manifest ? " (space-separated, or @path / @- with one value per line; in a file, blank lines and # comments are ignored, while inline values are sent as typed and may not be empty; @@value for a literal leading @)" : " (space-separated, or @path / @- with one value per line; @@value for a literal leading @)" : wantsJson ? " (JSON, or @path / @- to read a file or stdin)" : ""}${descriptor.required ? " (required)" : ""}${literalNull ? literalNullHint(documented, name) : ""}`;
|
|
13682
13848
|
const renamedFrom = flag.renamedFrom ?? [];
|
|
13683
13849
|
const option = new Option(`${short}--${name} ${placeholder}`, describe);
|
|
13684
13850
|
if (flag.hidden)
|
|
@@ -14421,6 +14587,7 @@ var GROUP_ALIASES = {
|
|
|
14421
14587
|
knowledge: "kb",
|
|
14422
14588
|
logs: "log",
|
|
14423
14589
|
"mcp-servers": "mcp-server",
|
|
14590
|
+
sandboxes: "sandbox",
|
|
14424
14591
|
secrets: "secret",
|
|
14425
14592
|
skills: "skill",
|
|
14426
14593
|
tables: "table",
|
|
@@ -16454,6 +16621,328 @@ function attachSecretCommands(program2) {
|
|
|
16454
16621
|
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));
|
|
16455
16622
|
}
|
|
16456
16623
|
|
|
16624
|
+
// src/update/check.ts
|
|
16625
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
16626
|
+
import {
|
|
16627
|
+
closeSync,
|
|
16628
|
+
constants as constants2,
|
|
16629
|
+
fstatSync,
|
|
16630
|
+
lstatSync,
|
|
16631
|
+
mkdirSync as mkdirSync2,
|
|
16632
|
+
openSync,
|
|
16633
|
+
readSync as readSync2,
|
|
16634
|
+
renameSync,
|
|
16635
|
+
unlinkSync,
|
|
16636
|
+
writeFileSync as writeFileSync2
|
|
16637
|
+
} from "node:fs";
|
|
16638
|
+
import { dirname as dirname3 } from "node:path";
|
|
16639
|
+
import { fileURLToPath } from "node:url";
|
|
16640
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
16641
|
+
var REGISTRY_TIMEOUT_MS = 1000;
|
|
16642
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
16643
|
+
var PACKAGE_NAME = "sim";
|
|
16644
|
+
var DIST_TAGS_PATH = `-/package/${PACKAGE_NAME}/dist-tags`;
|
|
16645
|
+
var MAX_RESPONSE_BYTES = 64 * 1024;
|
|
16646
|
+
var MAX_CACHE_BYTES = 4 * 1024;
|
|
16647
|
+
var STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
16648
|
+
function parseStableVersion(version) {
|
|
16649
|
+
const match = STABLE_VERSION_PATTERN.exec(version);
|
|
16650
|
+
if (!match)
|
|
16651
|
+
return null;
|
|
16652
|
+
const parsed = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
16653
|
+
return parsed.every(Number.isSafeInteger) ? parsed : null;
|
|
16654
|
+
}
|
|
16655
|
+
function isNewerVersion(candidate, current) {
|
|
16656
|
+
if (candidate[0] !== current[0])
|
|
16657
|
+
return candidate[0] > current[0];
|
|
16658
|
+
if (candidate[1] !== current[1])
|
|
16659
|
+
return candidate[1] > current[1];
|
|
16660
|
+
return candidate[2] > current[2];
|
|
16661
|
+
}
|
|
16662
|
+
var CI_VARIABLES = [
|
|
16663
|
+
"CI",
|
|
16664
|
+
"GITHUB_ACTIONS",
|
|
16665
|
+
"JENKINS_URL",
|
|
16666
|
+
"TEAMCITY_VERSION",
|
|
16667
|
+
"BUILDKITE"
|
|
16668
|
+
];
|
|
16669
|
+
var CACHE_VERSION = 1;
|
|
16670
|
+
var cacheWriteSequence = 0;
|
|
16671
|
+
function isEnabled(value) {
|
|
16672
|
+
if (value === undefined)
|
|
16673
|
+
return false;
|
|
16674
|
+
const normalized = value.trim().toLowerCase();
|
|
16675
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
16676
|
+
}
|
|
16677
|
+
function isProjectLocalInstall(modulePath, cwd) {
|
|
16678
|
+
const normalizedModulePath = normalizeModulePath(modulePath);
|
|
16679
|
+
const nodeModulesIndex = normalizedModulePath.indexOf("/node_modules/");
|
|
16680
|
+
if (nodeModulesIndex < 0)
|
|
16681
|
+
return false;
|
|
16682
|
+
const installRoot = normalizedModulePath.slice(0, nodeModulesIndex);
|
|
16683
|
+
const workingDirectory = normalizeModulePath(cwd).replace(/\/+$/, "");
|
|
16684
|
+
return workingDirectory === installRoot || workingDirectory.startsWith(`${installRoot}/`);
|
|
16685
|
+
}
|
|
16686
|
+
function isUnadvisableInstall(modulePath, env2, cwd) {
|
|
16687
|
+
const normalized = normalizeModulePath(modulePath);
|
|
16688
|
+
return env2.npm_command === "exec" || normalized.includes("/_npx/") || normalized.includes("/packages/sim-cli/") || isProjectLocalInstall(modulePath, cwd);
|
|
16689
|
+
}
|
|
16690
|
+
function normalizeModulePath(modulePath) {
|
|
16691
|
+
return modulePath.replace(/\\/g, "/").toLowerCase();
|
|
16692
|
+
}
|
|
16693
|
+
function registryUrl(env2) {
|
|
16694
|
+
const fallback = new URL(DIST_TAGS_PATH, DEFAULT_REGISTRY);
|
|
16695
|
+
const configured = env2.npm_config_registry?.trim();
|
|
16696
|
+
if (!configured)
|
|
16697
|
+
return fallback;
|
|
16698
|
+
try {
|
|
16699
|
+
const base = new URL(configured);
|
|
16700
|
+
if (base.protocol !== "http:" && base.protocol !== "https:")
|
|
16701
|
+
return null;
|
|
16702
|
+
if (base.username || base.password)
|
|
16703
|
+
return null;
|
|
16704
|
+
base.pathname = `${base.pathname.replace(/\/$/, "")}/${DIST_TAGS_PATH}`;
|
|
16705
|
+
return base;
|
|
16706
|
+
} catch {
|
|
16707
|
+
return null;
|
|
16708
|
+
}
|
|
16709
|
+
}
|
|
16710
|
+
var REGISTRY_REQUEST_SCRIPT = `
|
|
16711
|
+
let input = ''
|
|
16712
|
+
process.stdin.setEncoding('utf8')
|
|
16713
|
+
for await (const chunk of process.stdin) input += chunk
|
|
16714
|
+
|
|
16715
|
+
try {
|
|
16716
|
+
const { url, headers, maxResponseBytes, timeoutMs } = JSON.parse(input)
|
|
16717
|
+
const deadline = setTimeout(() => process.exit(1), timeoutMs)
|
|
16718
|
+
const response = await fetch(url, { headers, redirect: 'error' })
|
|
16719
|
+
const declared = Number(response.headers.get('content-length'))
|
|
16720
|
+
|
|
16721
|
+
if (!response.ok || !response.body || (Number.isFinite(declared) && declared > maxResponseBytes)) {
|
|
16722
|
+
process.exit(1)
|
|
16723
|
+
}
|
|
16724
|
+
|
|
16725
|
+
const reader = response.body.getReader()
|
|
16726
|
+
const chunks = []
|
|
16727
|
+
let seen = 0
|
|
16728
|
+
|
|
16729
|
+
while (true) {
|
|
16730
|
+
const { done, value } = await reader.read()
|
|
16731
|
+
if (done) break
|
|
16732
|
+
seen += value.byteLength
|
|
16733
|
+
if (seen > maxResponseBytes) {
|
|
16734
|
+
process.exit(1)
|
|
16735
|
+
}
|
|
16736
|
+
chunks.push(Buffer.from(value))
|
|
16737
|
+
}
|
|
16738
|
+
|
|
16739
|
+
clearTimeout(deadline)
|
|
16740
|
+
process.stdout.write(Buffer.concat(chunks), () => process.exit(0))
|
|
16741
|
+
} catch {
|
|
16742
|
+
process.exit(1)
|
|
16743
|
+
}
|
|
16744
|
+
`;
|
|
16745
|
+
function registryProcessEnv() {
|
|
16746
|
+
const env2 = { ...process.env };
|
|
16747
|
+
for (const key of Object.keys(env2)) {
|
|
16748
|
+
const normalized = key.toLowerCase();
|
|
16749
|
+
if (normalized === "npm_config_registry" || normalized === "sim_api_key")
|
|
16750
|
+
delete env2[key];
|
|
16751
|
+
}
|
|
16752
|
+
return env2;
|
|
16753
|
+
}
|
|
16754
|
+
function requestRegistry(url, { headers, maxResponseBytes, timeoutMs }) {
|
|
16755
|
+
return new Promise((resolve3, reject) => {
|
|
16756
|
+
const proxyArguments = process.execArgv.filter((argument) => argument === "--use-env-proxy" || argument === "--no-use-env-proxy");
|
|
16757
|
+
const child = spawn2(process.execPath, [...proxyArguments, "--input-type=module", "--eval", REGISTRY_REQUEST_SCRIPT], {
|
|
16758
|
+
env: registryProcessEnv(),
|
|
16759
|
+
killSignal: "SIGKILL",
|
|
16760
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
16761
|
+
timeout: timeoutMs,
|
|
16762
|
+
windowsHide: true
|
|
16763
|
+
});
|
|
16764
|
+
const chunks = [];
|
|
16765
|
+
let failed = false;
|
|
16766
|
+
let seen = 0;
|
|
16767
|
+
child.stdout.on("data", (chunk) => {
|
|
16768
|
+
seen += chunk.byteLength;
|
|
16769
|
+
if (seen > maxResponseBytes) {
|
|
16770
|
+
failed = true;
|
|
16771
|
+
child.kill("SIGKILL");
|
|
16772
|
+
return;
|
|
16773
|
+
}
|
|
16774
|
+
chunks.push(chunk);
|
|
16775
|
+
});
|
|
16776
|
+
child.stdout.on("error", () => {
|
|
16777
|
+
failed = true;
|
|
16778
|
+
child.kill("SIGKILL");
|
|
16779
|
+
});
|
|
16780
|
+
child.stdin.on("error", () => {});
|
|
16781
|
+
child.once("error", reject);
|
|
16782
|
+
child.once("close", (code) => {
|
|
16783
|
+
resolve3(code === 0 && !failed ? Buffer.concat(chunks).toString("utf8") : null);
|
|
16784
|
+
});
|
|
16785
|
+
child.stdin.end(JSON.stringify({ headers, maxResponseBytes, timeoutMs, url: url.href }));
|
|
16786
|
+
});
|
|
16787
|
+
}
|
|
16788
|
+
async function fetchDistTags(env2, request) {
|
|
16789
|
+
try {
|
|
16790
|
+
const url = registryUrl(env2);
|
|
16791
|
+
if (!url)
|
|
16792
|
+
return null;
|
|
16793
|
+
const text2 = await request(url, {
|
|
16794
|
+
headers: { accept: "application/json", "user-agent": `${PACKAGE_NAME}-cli/${CLI_VERSION}` },
|
|
16795
|
+
maxResponseBytes: MAX_RESPONSE_BYTES,
|
|
16796
|
+
timeoutMs: REGISTRY_TIMEOUT_MS
|
|
16797
|
+
});
|
|
16798
|
+
if (text2 === null || Buffer.byteLength(text2) > MAX_RESPONSE_BYTES)
|
|
16799
|
+
return null;
|
|
16800
|
+
const body = JSON.parse(text2);
|
|
16801
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
16802
|
+
return null;
|
|
16803
|
+
const tags = {};
|
|
16804
|
+
for (const [tag, version] of Object.entries(body)) {
|
|
16805
|
+
if (typeof version === "string")
|
|
16806
|
+
tags[tag] = version;
|
|
16807
|
+
}
|
|
16808
|
+
return tags;
|
|
16809
|
+
} catch {
|
|
16810
|
+
return null;
|
|
16811
|
+
}
|
|
16812
|
+
}
|
|
16813
|
+
function readCache(path) {
|
|
16814
|
+
let descriptor = null;
|
|
16815
|
+
try {
|
|
16816
|
+
if (!lstatSync(path).isFile())
|
|
16817
|
+
return null;
|
|
16818
|
+
descriptor = openSync(path, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOFOLLOW);
|
|
16819
|
+
const descriptorStats = fstatSync(descriptor);
|
|
16820
|
+
if (!descriptorStats.isFile() || descriptorStats.size > MAX_CACHE_BYTES) {
|
|
16821
|
+
return null;
|
|
16822
|
+
}
|
|
16823
|
+
const buffer = Buffer.allocUnsafe(MAX_CACHE_BYTES + 1);
|
|
16824
|
+
let bytesRead = 0;
|
|
16825
|
+
while (bytesRead < buffer.byteLength) {
|
|
16826
|
+
const count = readSync2(descriptor, buffer, bytesRead, buffer.byteLength - bytesRead, bytesRead);
|
|
16827
|
+
if (count === 0)
|
|
16828
|
+
break;
|
|
16829
|
+
bytesRead += count;
|
|
16830
|
+
}
|
|
16831
|
+
if (bytesRead > MAX_CACHE_BYTES)
|
|
16832
|
+
return null;
|
|
16833
|
+
const parsed = JSON.parse(buffer.subarray(0, bytesRead).toString("utf8"));
|
|
16834
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
16835
|
+
return null;
|
|
16836
|
+
const entry = parsed;
|
|
16837
|
+
if (entry.version !== CACHE_VERSION)
|
|
16838
|
+
return null;
|
|
16839
|
+
if (typeof entry.checkedAt !== "string" || Number.isNaN(Date.parse(entry.checkedAt)))
|
|
16840
|
+
return null;
|
|
16841
|
+
return {
|
|
16842
|
+
version: CACHE_VERSION,
|
|
16843
|
+
checkedAt: entry.checkedAt
|
|
16844
|
+
};
|
|
16845
|
+
} catch {
|
|
16846
|
+
return null;
|
|
16847
|
+
} finally {
|
|
16848
|
+
if (descriptor !== null) {
|
|
16849
|
+
try {
|
|
16850
|
+
closeSync(descriptor);
|
|
16851
|
+
} catch {}
|
|
16852
|
+
}
|
|
16853
|
+
}
|
|
16854
|
+
}
|
|
16855
|
+
function writeCache(path, entry) {
|
|
16856
|
+
let descriptor = null;
|
|
16857
|
+
let temporaryCreated = false;
|
|
16858
|
+
const temporaryPath = `${path}.${process.pid}.${Date.now()}.${cacheWriteSequence++}.tmp`;
|
|
16859
|
+
try {
|
|
16860
|
+
mkdirSync2(dirname3(path), { recursive: true, mode: 448 });
|
|
16861
|
+
descriptor = openSync(temporaryPath, "wx", 420);
|
|
16862
|
+
temporaryCreated = true;
|
|
16863
|
+
writeFileSync2(descriptor, `${JSON.stringify(entry, null, 2)}
|
|
16864
|
+
`);
|
|
16865
|
+
closeSync(descriptor);
|
|
16866
|
+
descriptor = null;
|
|
16867
|
+
renameSync(temporaryPath, path);
|
|
16868
|
+
temporaryCreated = false;
|
|
16869
|
+
} catch {} finally {
|
|
16870
|
+
if (descriptor !== null) {
|
|
16871
|
+
try {
|
|
16872
|
+
closeSync(descriptor);
|
|
16873
|
+
} catch {}
|
|
16874
|
+
}
|
|
16875
|
+
if (temporaryCreated) {
|
|
16876
|
+
try {
|
|
16877
|
+
unlinkSync(temporaryPath);
|
|
16878
|
+
} catch {}
|
|
16879
|
+
}
|
|
16880
|
+
}
|
|
16881
|
+
}
|
|
16882
|
+
function isFresh(entry, now) {
|
|
16883
|
+
const age = now.getTime() - Date.parse(entry.checkedAt);
|
|
16884
|
+
return age >= 0 && age < CHECK_INTERVAL_MS;
|
|
16885
|
+
}
|
|
16886
|
+
function upgradeCommand(modulePath = fileURLToPath(import.meta.url), env2 = process.env) {
|
|
16887
|
+
const target = `${PACKAGE_NAME}@latest`;
|
|
16888
|
+
const normalized = normalizeModulePath(modulePath);
|
|
16889
|
+
if (normalized.includes(".bun/install/global"))
|
|
16890
|
+
return `bun add -g ${target}`;
|
|
16891
|
+
if (normalized.includes("/pnpm/") || normalized.includes("/.pnpm/")) {
|
|
16892
|
+
return `pnpm add -g ${target}`;
|
|
16893
|
+
}
|
|
16894
|
+
if (normalized.includes("/.yarn/") || normalized.includes("/yarn/")) {
|
|
16895
|
+
return `yarn global add ${target}`;
|
|
16896
|
+
}
|
|
16897
|
+
const agent = env2.npm_config_user_agent ?? "";
|
|
16898
|
+
if (agent.startsWith("pnpm/"))
|
|
16899
|
+
return `pnpm add -g ${target}`;
|
|
16900
|
+
if (agent.startsWith("yarn/"))
|
|
16901
|
+
return `yarn global add ${target}`;
|
|
16902
|
+
if (agent.startsWith("bun/"))
|
|
16903
|
+
return `bun add -g ${target}`;
|
|
16904
|
+
return `npm install -g ${target}`;
|
|
16905
|
+
}
|
|
16906
|
+
async function announceUpdateIfAvailable(options = {}) {
|
|
16907
|
+
try {
|
|
16908
|
+
const env2 = options.env ?? process.env;
|
|
16909
|
+
const isTty = options.isTty ?? process.stderr.isTTY === true;
|
|
16910
|
+
const modulePath = options.modulePath ?? fileURLToPath(import.meta.url);
|
|
16911
|
+
const cwd = options.cwd ?? process.cwd();
|
|
16912
|
+
const now = options.now ?? new Date;
|
|
16913
|
+
if (isEnabled(env2.SIM_NO_UPDATE_CHECK))
|
|
16914
|
+
return;
|
|
16915
|
+
if (!isTty)
|
|
16916
|
+
return;
|
|
16917
|
+
if (CI_VARIABLES.some((variable) => isEnabled(env2[variable])))
|
|
16918
|
+
return;
|
|
16919
|
+
if (isUnadvisableInstall(modulePath, env2, cwd))
|
|
16920
|
+
return;
|
|
16921
|
+
const currentVersion = options.currentVersion ?? CLI_VERSION;
|
|
16922
|
+
const current = parseStableVersion(currentVersion);
|
|
16923
|
+
if (!current)
|
|
16924
|
+
return;
|
|
16925
|
+
const cachePath = updateCachePath();
|
|
16926
|
+
const cached = readCache(cachePath);
|
|
16927
|
+
if (cached && isFresh(cached, now))
|
|
16928
|
+
return;
|
|
16929
|
+
const tags = await fetchDistTags(env2, options.registryRequest ?? requestRegistry);
|
|
16930
|
+
const latest = tags?.latest ?? null;
|
|
16931
|
+
const available = latest ? parseStableVersion(latest) : null;
|
|
16932
|
+
writeCache(cachePath, {
|
|
16933
|
+
version: CACHE_VERSION,
|
|
16934
|
+
checkedAt: now.toISOString()
|
|
16935
|
+
});
|
|
16936
|
+
if (!latest || !available)
|
|
16937
|
+
return;
|
|
16938
|
+
if (!isNewerVersion(available, current))
|
|
16939
|
+
return;
|
|
16940
|
+
const write = options.write ?? ((message) => void process.stderr.write(message));
|
|
16941
|
+
write(`Update available: sim ${currentVersion} → ${latest}. Run: ${upgradeCommand(modulePath, env2)}
|
|
16942
|
+
`);
|
|
16943
|
+
} catch {}
|
|
16944
|
+
}
|
|
16945
|
+
|
|
16457
16946
|
// src/program.ts
|
|
16458
16947
|
var PROGRAM_DESCRIPTION = "Talk to the Sim API from your terminal";
|
|
16459
16948
|
var HELP_EPILOGUE = `
|
|
@@ -16533,6 +17022,7 @@ function buildProgram(options = {}) {
|
|
|
16533
17022
|
attachProtocolCommands(program2);
|
|
16534
17023
|
attachSecretCommands(program2);
|
|
16535
17024
|
program2.addHelpText("after", HELP_EPILOGUE);
|
|
17025
|
+
program2.hook("preAction", () => announceUpdateIfAvailable());
|
|
16536
17026
|
refuseHelpAfterUnknownCommand(program2);
|
|
16537
17027
|
assertNoReservedProgramFlags(program2);
|
|
16538
17028
|
return program2;
|