sim 2.1.1-preview.41.1 → 2.1.1-preview.43.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.
Files changed (3) hide show
  1. package/README.md +91 -22
  2. package/dist/index.js +752 -140
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -538,7 +538,7 @@ var require_option = __commonJS((exports) => {
538
538
  // ../../node_modules/commander/lib/suggestSimilar.js
539
539
  var require_suggestSimilar = __commonJS((exports) => {
540
540
  var maxDistance = 3;
541
- function editDistance(a, b) {
541
+ function editDistance2(a, b) {
542
542
  if (Math.abs(a.length - b.length) > maxDistance)
543
543
  return Math.max(a.length, b.length);
544
544
  const d = [];
@@ -579,7 +579,7 @@ var require_suggestSimilar = __commonJS((exports) => {
579
579
  candidates.forEach((candidate) => {
580
580
  if (candidate.length <= 1)
581
581
  return;
582
- const distance = editDistance(word, candidate);
582
+ const distance = editDistance2(word, candidate);
583
583
  const length = Math.max(word.length, candidate.length);
584
584
  const similarity = (length - distance) / length;
585
585
  if (similarity > minSimilarity) {
@@ -2320,8 +2320,11 @@ var KV_PATTERN = /^\s*([A-Za-z0-9_.-]+)\s*=\s*(.*?)\s*$/;
2320
2320
  function parseIni(text) {
2321
2321
  const doc = { preamble: [], sections: [] };
2322
2322
  let current = null;
2323
- for (const line of text.split(`
2324
- `)) {
2323
+ const lines = text.split(`
2324
+ `);
2325
+ if (lines.length > 1 && lines[lines.length - 1] === "")
2326
+ lines.pop();
2327
+ for (const line of lines) {
2325
2328
  const sectionMatch = SECTION_PATTERN.exec(line);
2326
2329
  if (sectionMatch) {
2327
2330
  current = { name: sectionMatch[1].trim(), entries: [] };
@@ -2360,13 +2363,15 @@ function serializeIni(doc) {
2360
2363
  ` : "";
2361
2364
  }
2362
2365
  function getSection(doc, name) {
2363
- const section = doc.sections.find((s) => s.name === name);
2364
- if (!section)
2366
+ const sections = doc.sections.filter((s) => s.name === name);
2367
+ if (sections.length === 0)
2365
2368
  return null;
2366
2369
  const values = {};
2367
- for (const entry of section.entries) {
2368
- if (entry.kind === "kv")
2369
- values[entry.key] = entry.value;
2370
+ for (const section of sections) {
2371
+ for (const entry of section.entries) {
2372
+ if (entry.kind === "kv" && !Object.hasOwn(values, entry.key))
2373
+ values[entry.key] = entry.value;
2374
+ }
2370
2375
  }
2371
2376
  return values;
2372
2377
  }
@@ -2374,18 +2379,21 @@ function listSections(doc) {
2374
2379
  return doc.sections.map((s) => s.name);
2375
2380
  }
2376
2381
  function setSectionValues(doc, name, values) {
2377
- let section = doc.sections.find((s) => s.name === name);
2382
+ const matching = doc.sections.filter((s) => s.name === name);
2383
+ let section = matching[0];
2378
2384
  if (!section) {
2379
2385
  section = { name, entries: [] };
2380
2386
  doc.sections.push(section);
2387
+ matching.push(section);
2381
2388
  }
2382
2389
  for (const [key, value] of Object.entries(values)) {
2383
- const index = section.entries.findIndex((e) => e.kind === "kv" && e.key === key);
2384
2390
  if (value === null) {
2385
- if (index !== -1)
2386
- section.entries.splice(index, 1);
2391
+ for (const target of matching) {
2392
+ target.entries = target.entries.filter((e) => !(e.kind === "kv" && e.key === key));
2393
+ }
2387
2394
  continue;
2388
2395
  }
2396
+ const index = section.entries.findIndex((e) => e.kind === "kv" && e.key === key);
2389
2397
  if (index === -1) {
2390
2398
  section.entries.push({ kind: "kv", key, value });
2391
2399
  } else {
@@ -2394,10 +2402,10 @@ function setSectionValues(doc, name, values) {
2394
2402
  }
2395
2403
  }
2396
2404
  function removeSection(doc, name) {
2397
- const index = doc.sections.findIndex((s) => s.name === name);
2398
- if (index === -1)
2405
+ const remaining = doc.sections.filter((s) => s.name !== name);
2406
+ if (remaining.length === doc.sections.length)
2399
2407
  return false;
2400
- doc.sections.splice(index, 1);
2408
+ doc.sections = remaining;
2401
2409
  return true;
2402
2410
  }
2403
2411
 
@@ -2472,6 +2480,42 @@ function listProfiles() {
2472
2480
  }
2473
2481
  return [...names].sort();
2474
2482
  }
2483
+ function editDistance(a, b) {
2484
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
2485
+ for (let i = 1;i <= a.length; i++) {
2486
+ const current = [i];
2487
+ for (let j = 1;j <= b.length; j++) {
2488
+ current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
2489
+ }
2490
+ previous = current;
2491
+ }
2492
+ return previous[b.length];
2493
+ }
2494
+ function nearestProfile(name, known) {
2495
+ const lowered = name.toLowerCase();
2496
+ let best = null;
2497
+ let bestDistance = Number.POSITIVE_INFINITY;
2498
+ for (const candidate of known) {
2499
+ const distance = editDistance(lowered, candidate.toLowerCase());
2500
+ if (distance < bestDistance) {
2501
+ best = candidate;
2502
+ bestDistance = distance;
2503
+ }
2504
+ }
2505
+ return bestDistance <= 2 ? best : null;
2506
+ }
2507
+ function requireKnownProfile(name) {
2508
+ if (name === DEFAULT_PROFILE)
2509
+ return;
2510
+ const known = listProfiles();
2511
+ if (known.includes(name))
2512
+ return;
2513
+ if (known.length === 0) {
2514
+ throw new ProfileConfigError(`Unknown profile "${name}". No profiles are configured yet. Run: sim login --profile ${name}`);
2515
+ }
2516
+ const suggestion = nearestProfile(name, known);
2517
+ throw new ProfileConfigError(`Unknown profile "${name}".${suggestion ? ` Did you mean "${suggestion}"?` : ""} Configured profiles: ${known.join(", ")}.`);
2518
+ }
2475
2519
  function listAuthenticationDependents(authProfile) {
2476
2520
  return listProfiles().filter((profile) => profile !== authProfile && readConfigProfile(profile).auth_profile?.trim() === authProfile);
2477
2521
  }
@@ -2517,7 +2561,10 @@ function resolve(candidates, fallback, fallbackSource) {
2517
2561
  return { value: fallback, source: fallbackSource };
2518
2562
  }
2519
2563
  function resolveProfile(overrides = {}) {
2520
- const name = overrides.profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
2564
+ const named = overrides.profile || process.env.SIM_PROFILE;
2565
+ const name = named || DEFAULT_PROFILE;
2566
+ if (named && !overrides.allowUnknownProfile)
2567
+ requireKnownProfile(named);
2521
2568
  const config = readConfigProfile(name);
2522
2569
  const authProfile = resolveAuthenticationProfileName(name);
2523
2570
  const authConfig = authProfile === name ? config : readConfigProfile(authProfile);
@@ -6284,6 +6331,7 @@ var POLL_TIMEOUT_MS = 15 * 60 * 1000;
6284
6331
  var APPROVAL_PATH = "/cli/auth";
6285
6332
  var POLL_PATH = "/api/cli/auth/poll";
6286
6333
  var RETRYABLE_POLL_STATUSES = new Set([409, 429, 500, 502, 503, 504]);
6334
+ var TRANSPORT_FAILURES_BEFORE_WARNING = 3;
6287
6335
  function token() {
6288
6336
  return randomBytes(32).toString("base64url");
6289
6337
  }
@@ -6328,6 +6376,8 @@ function toRedirectError(endpoint, response) {
6328
6376
  }
6329
6377
  async function pollForKey(endpoint, auth, signal) {
6330
6378
  const deadline = Date.now() + POLL_TIMEOUT_MS;
6379
+ let consecutiveTransportFailures = 0;
6380
+ let warnedAboutTransport = false;
6331
6381
  while (Date.now() < deadline) {
6332
6382
  if (signal?.aborted)
6333
6383
  throw new SimApiError("Login cancelled.", 0);
@@ -6344,10 +6394,17 @@ async function pollForKey(endpoint, auth, signal) {
6344
6394
  signal,
6345
6395
  redirect: "manual"
6346
6396
  });
6347
- } catch {
6397
+ } catch (cause) {
6348
6398
  response = null;
6399
+ consecutiveTransportFailures++;
6400
+ if (!warnedAboutTransport && consecutiveTransportFailures >= TRANSPORT_FAILURES_BEFORE_WARNING) {
6401
+ warnedAboutTransport = true;
6402
+ process.stderr.write(`Still waiting: ${endpoint} is not answering the login poll (${cause.message}). Check the endpoint; retrying until you approve or the login times out.
6403
+ `);
6404
+ }
6349
6405
  }
6350
6406
  if (response) {
6407
+ consecutiveTransportFailures = 0;
6351
6408
  if (REDIRECT_STATUSES.has(response.status))
6352
6409
  throw toRedirectError(endpoint, response);
6353
6410
  const raw = await response.text();
@@ -6418,6 +6475,13 @@ var V2_OPERATIONS = {
6418
6475
  required: true,
6419
6476
  describe: "Workspace that owns the upload session."
6420
6477
  }
6478
+ },
6479
+ headers: {
6480
+ "upload-token": {
6481
+ kind: "string",
6482
+ required: true,
6483
+ describe: "Signed upload control token returned when the upload session was created."
6484
+ }
6421
6485
  }
6422
6486
  },
6423
6487
  abortKnowledgeDocumentUpload: {
@@ -6436,6 +6500,13 @@ var V2_OPERATIONS = {
6436
6500
  required: true,
6437
6501
  describe: "Workspace that owns the knowledge base."
6438
6502
  }
6503
+ },
6504
+ headers: {
6505
+ "upload-token": {
6506
+ kind: "string",
6507
+ required: true,
6508
+ describe: "Signed upload control token returned when the upload session was created."
6509
+ }
6439
6510
  }
6440
6511
  },
6441
6512
  activateWorkflowVersion: {
@@ -6751,6 +6822,12 @@ var V2_OPERATIONS = {
6751
6822
  required: true,
6752
6823
  describe: "Workspace that owns the transfer resource."
6753
6824
  }
6825
+ },
6826
+ headers: {
6827
+ "upload-token": {
6828
+ kind: "string",
6829
+ describe: "Signed upload control token returned when an upload-backed import was created."
6830
+ }
6754
6831
  }
6755
6832
  },
6756
6833
  cancelTableRuns: {
@@ -6818,6 +6895,13 @@ var V2_OPERATIONS = {
6818
6895
  required: true,
6819
6896
  describe: "Workspace that owns the upload session."
6820
6897
  }
6898
+ },
6899
+ headers: {
6900
+ "upload-token": {
6901
+ kind: "string",
6902
+ required: true,
6903
+ describe: "Signed upload control token returned when the upload session was created."
6904
+ }
6821
6905
  }
6822
6906
  },
6823
6907
  completeKnowledgeDocumentUpload: {
@@ -6836,6 +6920,13 @@ var V2_OPERATIONS = {
6836
6920
  required: true,
6837
6921
  describe: "Workspace that owns the knowledge base."
6838
6922
  }
6923
+ },
6924
+ headers: {
6925
+ "upload-token": {
6926
+ kind: "string",
6927
+ required: true,
6928
+ describe: "Signed upload control token returned when the upload session was created."
6929
+ }
6839
6930
  }
6840
6931
  },
6841
6932
  completeTableImport: {
@@ -6851,6 +6942,13 @@ var V2_OPERATIONS = {
6851
6942
  required: true,
6852
6943
  describe: "Workspace that owns the transfer resource."
6853
6944
  }
6945
+ },
6946
+ headers: {
6947
+ "upload-token": {
6948
+ kind: "string",
6949
+ required: true,
6950
+ describe: "Signed upload control token returned when the upload session was created."
6951
+ }
6854
6952
  }
6855
6953
  },
6856
6954
  createCredentialConnection: {
@@ -6991,6 +7089,13 @@ var V2_OPERATIONS = {
6991
7089
  required: true,
6992
7090
  describe: "Multipart part numbers for which signed URLs should be created."
6993
7091
  }
7092
+ },
7093
+ headers: {
7094
+ "upload-token": {
7095
+ kind: "string",
7096
+ required: true,
7097
+ describe: "Signed upload control token returned when the upload session was created."
7098
+ }
6994
7099
  }
6995
7100
  },
6996
7101
  createKnowledgeBase: {
@@ -7136,6 +7241,13 @@ var V2_OPERATIONS = {
7136
7241
  required: true,
7137
7242
  describe: "Multipart part numbers for which signed URLs should be created."
7138
7243
  }
7244
+ },
7245
+ headers: {
7246
+ "upload-token": {
7247
+ kind: "string",
7248
+ required: true,
7249
+ describe: "Signed upload control token returned when the upload session was created."
7250
+ }
7139
7251
  }
7140
7252
  },
7141
7253
  createKnowledgeFolder: {
@@ -7441,6 +7553,13 @@ var V2_OPERATIONS = {
7441
7553
  required: true,
7442
7554
  describe: "Multipart part numbers for which signed URLs should be created."
7443
7555
  }
7556
+ },
7557
+ headers: {
7558
+ "upload-token": {
7559
+ kind: "string",
7560
+ required: true,
7561
+ describe: "Signed upload control token returned when the upload session was created."
7562
+ }
7444
7563
  }
7445
7564
  },
7446
7565
  createTableRows: {
@@ -7540,7 +7659,7 @@ var V2_OPERATIONS = {
7540
7659
  method: "DELETE",
7541
7660
  path: "/api/v2/credentials/[credentialId]",
7542
7661
  pathParams: ["credentialId"],
7543
- pathParamDocs: { credentialId: "Credential to update or disconnect." },
7662
+ pathParamDocs: { credentialId: "Credential to disconnect." },
7544
7663
  responseMode: "json",
7545
7664
  summary: "Disconnect Credential",
7546
7665
  query: {
@@ -8070,7 +8189,7 @@ var V2_OPERATIONS = {
8070
8189
  },
8071
8190
  selectedOutputs: {
8072
8191
  kind: "array",
8073
- describe: "Block output references to include in a streamed response. Rejected when `async` is true."
8192
+ describe: "Block output references to include in a streamed response, as `blockId`, `blockId.path`, or `BlockName.path` (resolved against the live workflow). Requires `stream: true` — it shapes the streamed envelope only, so it is rejected on a sync request and when `async` is true. To narrow a finished run, pass `selectedOutputs` to the run resource instead."
8074
8193
  },
8075
8194
  includeThinking: {
8076
8195
  kind: "boolean",
@@ -8090,6 +8209,16 @@ var V2_OPERATIONS = {
8090
8209
  kind: "integer",
8091
8210
  describe: "Maximum total bytes of file content to inline as base64, lowering but never raising the server limit of 16 MiB. Rejected when `async` is true."
8092
8211
  }
8212
+ },
8213
+ headers: {
8214
+ "x-run-id": {
8215
+ kind: "string",
8216
+ describe: 'Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: "RUN_ID_CONFLICT"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.'
8217
+ },
8218
+ "x-sim-via": {
8219
+ kind: "string",
8220
+ describe: 'Comma-separated workflow identifiers naming the workflow-to-workflow call chain that led to this request. Each hop appends its own workflow id, and Sim sets it automatically; supply it yourself only when relaying an existing chain. A chain at the maximum depth is rejected with `409` and `error.details.code: "CALL_CHAIN_DEPTH_EXCEEDED"`.'
8221
+ }
8093
8222
  }
8094
8223
  },
8095
8224
  exportWorkflow: {
@@ -8110,8 +8239,7 @@ var V2_OPERATIONS = {
8110
8239
  query: {
8111
8240
  organizationId: {
8112
8241
  kind: "string",
8113
- required: true,
8114
- describe: "Organization whose audit-log entry should be returned."
8242
+ describe: "Organization whose audit-log entry should be returned. Defaults to the caller's own organization when omitted. A caller that belongs to no organization, or that names one it is not a member of, is refused with a 403."
8115
8243
  }
8116
8244
  }
8117
8245
  },
@@ -8201,6 +8329,13 @@ var V2_OPERATIONS = {
8201
8329
  required: true,
8202
8330
  describe: "Workspace that owns the upload session."
8203
8331
  }
8332
+ },
8333
+ headers: {
8334
+ "upload-token": {
8335
+ kind: "string",
8336
+ required: true,
8337
+ describe: "Signed upload control token returned when the upload session was created."
8338
+ }
8204
8339
  }
8205
8340
  },
8206
8341
  getKnowledgeBase: {
@@ -8456,6 +8591,12 @@ var V2_OPERATIONS = {
8456
8591
  required: true,
8457
8592
  describe: "Workspace that owns the transfer resource."
8458
8593
  }
8594
+ },
8595
+ headers: {
8596
+ "upload-token": {
8597
+ kind: "string",
8598
+ describe: "Signed upload control token returned when an upload-backed import was created."
8599
+ }
8459
8600
  }
8460
8601
  },
8461
8602
  getTableRow: {
@@ -8550,7 +8691,7 @@ var V2_OPERATIONS = {
8550
8691
  },
8551
8692
  selectedOutputs: {
8552
8693
  kind: "string",
8553
- describe: "Comma-separated block output references to include, as `blockId` or `blockId.path`. Block *names* are not resolved here — unlike the execute request, this resource reads a recorded run and matches ids only, so a name selects nothing and yields an empty `blockOutputs`."
8694
+ describe: "Comma-separated block output references to include, as `blockId` or `blockId.path`. Block *names* are not resolved here — unlike the execute request, this resource reads a recorded run and matches ids only, so a selector that is not headed by a block id answers `400` instead of an empty `blockOutputs`."
8554
8695
  },
8555
8696
  includeFileBase64: {
8556
8697
  kind: "boolean",
@@ -8669,8 +8810,7 @@ var V2_OPERATIONS = {
8669
8810
  },
8670
8811
  organizationId: {
8671
8812
  kind: "string",
8672
- required: true,
8673
- describe: "Organization whose audit trail should be queried."
8813
+ describe: "Organization whose audit trail should be queried. Defaults to the caller's own organization when omitted. A caller that belongs to no organization, or that names one it is not a member of, is refused with a 403."
8674
8814
  },
8675
8815
  actorEmail: { kind: "string", describe: "Filter by actor email address." }
8676
8816
  }
@@ -8699,7 +8839,7 @@ var V2_OPERATIONS = {
8699
8839
  },
8700
8840
  workspaceId: {
8701
8841
  kind: "string",
8702
- describe: "Restrict results to one workspace whose payer the caller can inspect."
8842
+ describe: "Narrow the ledger to usage events attributed to one workspace. It does not change whose events are reported — a personal API key always reports the usage of the person holding it, and a workspace API key always reports its own workspace's complete ledger across every member. The response `scope` field says which of the two you received. A workspace API key is pinned to its own workspace: any other id answers `404 Workspace not found`, which is also what an id that does not exist answers."
8703
8843
  },
8704
8844
  period: {
8705
8845
  kind: "enum",
@@ -10648,7 +10788,7 @@ var V2_OPERATIONS = {
10648
10788
  method: "PATCH",
10649
10789
  path: "/api/v2/credentials/[credentialId]",
10650
10790
  pathParams: ["credentialId"],
10651
- pathParamDocs: { credentialId: "Credential to update or disconnect." },
10791
+ pathParamDocs: { credentialId: "Credential to update." },
10652
10792
  responseMode: "json",
10653
10793
  summary: "Update Credential",
10654
10794
  query: {
@@ -11346,7 +11486,7 @@ function addProfileCommand() {
11346
11486
  }
11347
11487
  function loginCommand() {
11348
11488
  return new Command("login").description("Authorize this terminal and store an API key for the profile").option("--scope <scope>", "Key space to mint from: platform or copilot", "platform").option("--no-browser", "Print the URL instead of opening a browser").option("-y, --yes", "Overwrite an existing profile without prompting").action(async (options, command) => {
11349
- const profile = profileFrom(command);
11489
+ const profile = profileFrom(command, { allowUnknownProfile: true });
11350
11490
  const authProfile = resolveAuthenticationProfileName(profile.name);
11351
11491
  if (authProfile !== profile.name) {
11352
11492
  throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Run: sim login --profile ${authProfile}`, 0);
@@ -11425,6 +11565,17 @@ function logoutCommand() {
11425
11565
  console.log(source_default.dim(" The key itself is still active — revoke it in Settings → API keys."));
11426
11566
  });
11427
11567
  }
11568
+ async function readKeyType(client) {
11569
+ const operation = V2_OPERATIONS.getMeta;
11570
+ try {
11571
+ const response = await client.request(operation.path, {
11572
+ method: operation.method
11573
+ });
11574
+ return response.data.keyType;
11575
+ } catch {
11576
+ return null;
11577
+ }
11578
+ }
11428
11579
  var CREDENTIAL_VERDICT_STATUSES = new Set([401, 403, 404]);
11429
11580
  var WHOAMI_EXIT_CODES = {
11430
11581
  verified: 0,
@@ -11439,13 +11590,16 @@ async function verifyProfile(client, profile) {
11439
11590
  return {
11440
11591
  status: "unauthenticated",
11441
11592
  workspace: null,
11593
+ keyType: null,
11442
11594
  detail: `no API key — run: sim login --profile ${profile.name}`
11443
11595
  };
11444
11596
  }
11597
+ const keyType = await readKeyType(client);
11445
11598
  if (!profile.workspaceId) {
11446
11599
  return {
11447
11600
  status: "no-workspace",
11448
11601
  workspace: null,
11602
+ keyType,
11449
11603
  detail: `no workspace to check against — run: sim configure --profile ${profile.name} --set-workspace <id>`
11450
11604
  };
11451
11605
  }
@@ -11453,13 +11607,14 @@ async function verifyProfile(client, profile) {
11453
11607
  try {
11454
11608
  const response = await client.request(resolvePath(operation.path, { workspaceId: profile.workspaceId }), { method: operation.method });
11455
11609
  const { id, name, memberCount } = response.data;
11456
- return { status: "verified", workspace: { id, name, memberCount }, detail: null };
11610
+ return { status: "verified", workspace: { id, name, memberCount }, keyType, detail: null };
11457
11611
  } catch (error) {
11458
11612
  if (!(error instanceof SimApiError))
11459
11613
  throw error;
11460
11614
  return {
11461
11615
  status: CREDENTIAL_VERDICT_STATUSES.has(error.status) ? "rejected" : "unreachable",
11462
11616
  workspace: null,
11617
+ keyType,
11463
11618
  detail: error.message
11464
11619
  };
11465
11620
  }
@@ -11487,7 +11642,12 @@ function whoamiCommand() {
11487
11642
  const { client, profile } = clientFrom(command);
11488
11643
  const { sources } = profile;
11489
11644
  const authentication = presentAuthentication(sources.apiKey);
11490
- const verification = options.verify ? await verifyProfile(client, profile) : { status: "disabled", workspace: null, detail: "not checked (--no-verify)" };
11645
+ const verification = options.verify ? await verifyProfile(client, profile) : {
11646
+ status: "disabled",
11647
+ workspace: null,
11648
+ keyType: null,
11649
+ detail: "not checked (--no-verify)"
11650
+ };
11491
11651
  const annotate = (value, source) => source === "unset" ? source_default.dim("not set") : `${value} ${source_default.dim(`(${source})`)}`;
11492
11652
  printRecord(profile.output, [
11493
11653
  ["Profile", profile.name],
@@ -11496,6 +11656,10 @@ function whoamiCommand() {
11496
11656
  "API key",
11497
11657
  authentication.authenticated ? annotate("configured", authentication.source) : source_default.yellow("not logged in")
11498
11658
  ],
11659
+ [
11660
+ "Key type",
11661
+ verification.keyType ?? source_default.dim(options.verify ? "unknown" : "not checked (--no-verify)")
11662
+ ],
11499
11663
  ["Workspace", annotate(profile.workspaceId ?? "", sources.workspaceId)],
11500
11664
  ["Output", annotate(profile.output, sources.output)],
11501
11665
  ["Verified", presentVerification(verification)]
@@ -11514,6 +11678,7 @@ function whoamiCommand() {
11514
11678
  verification: {
11515
11679
  status: verification.status,
11516
11680
  workspace: verification.workspace,
11681
+ keyType: verification.keyType,
11517
11682
  detail: verification.detail
11518
11683
  }
11519
11684
  });
@@ -11533,7 +11698,15 @@ function profilesCommand() {
11533
11698
  const active = selectedProfileName(actionCommand);
11534
11699
  for (const name of profiles) {
11535
11700
  const marker = name === active ? source_default.green("*") : " ";
11536
- const authProfile = resolveAuthenticationProfileName(name);
11701
+ let authProfile;
11702
+ try {
11703
+ authProfile = resolveAuthenticationProfileName(name);
11704
+ } catch (error) {
11705
+ if (!(error instanceof ProfileConfigError))
11706
+ throw error;
11707
+ console.log(`${marker} ${name}${source_default.red(` (${safeOneLine(error.message)})`)}`);
11708
+ continue;
11709
+ }
11537
11710
  const hasKey = Boolean(readCredentialsProfile(authProfile).api_key);
11538
11711
  const authentication = authProfile === name ? "" : source_default.dim(` (auth: ${authProfile})`);
11539
11712
  console.log(`${marker} ${name}${hasKey ? "" : source_default.dim(" (no key)")}${authentication}`);
@@ -11546,11 +11719,19 @@ function profilesCommand() {
11546
11719
  }
11547
11720
 
11548
11721
  // src/commands/configure.ts
11722
+ function requireValue(value, flag, key) {
11723
+ if (value !== undefined && value.trim() === "") {
11724
+ throw new SimApiError(`${flag} requires a value. To remove it, run: sim configure --unset ${key}`, 0);
11725
+ }
11726
+ }
11549
11727
  function configureCommand() {
11550
11728
  return new Command("configure").description("Set a profile's endpoint, default workspace, or output format").option("--set-endpoint <url>", "Sim deployment to talk to").option("--set-workspace <id>", "Default workspace for workspace-scoped commands").option("--set-output <format>", `Default output format (${OUTPUT_FORMATS.join(" | ")})`).option("--unset <key...>", "Remove settings (endpoint, workspace, output)").action((options, command) => {
11551
- const profile = profileFrom(command);
11729
+ const profile = profileFrom(command, { allowUnknownProfile: true });
11552
11730
  const authProfile = resolveAuthenticationProfileName(profile.name);
11553
11731
  const updates = {};
11732
+ requireValue(options.setEndpoint, "--set-endpoint", "endpoint");
11733
+ requireValue(options.setWorkspace, "--set-workspace", "workspace");
11734
+ requireValue(options.setOutput, "--set-output", "output");
11554
11735
  if (options.setEndpoint) {
11555
11736
  if (authProfile !== profile.name) {
11556
11737
  throw new SimApiError(`Profile "${profile.name}" shares its endpoint with authentication profile "${authProfile}". Run: sim configure --profile ${authProfile} --set-endpoint ${options.setEndpoint}`, 0);
@@ -11600,6 +11781,11 @@ var TABLE_READ_FILTER_HELP = 'Condition: {"field":"status","op":"eq","value":"ac
11600
11781
  var TABLE_SORT_HELP = 'Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc)';
11601
11782
  var KNOWLEDGE_TAG_DEFINITIONS_HELP = 'Tag definitions: [{"tagSlot":"tag1","displayName":"category","fieldType":"text"}]';
11602
11783
  var CUSTOM_TOOL_SCHEMA_HELP = 'OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}}';
11784
+ var DISPATCH_ROW_LIMIT_HELP = "Stop after this many eligible rows have run (1-1,000,000). Omit for an unbounded run";
11785
+ 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 insert_into_subflow and extract_from_subflow, whose params carry {"subflowId":"<loop-id>"}';
11786
+ 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';
11787
+ 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"}]';
11788
+ var MCP_PARAMETER_DESCRIPTIONS_HELP = 'Per-field description overrides applied to the schema generated from the deployed workflow inputs, as [{"name":"email","description":"Customer email address"}]. A name matching no input field is ignored';
11603
11789
  var FOLDER_PATH_INPUT = {
11604
11790
  describe: "Folder path as shown in the app; the leading / is optional",
11605
11791
  folderPath: true
@@ -11613,6 +11799,7 @@ var FOLDER_DELETE_FLAGS = {
11613
11799
  recursive: { boolean: true, describe: "Delete the folder and its descendants" }
11614
11800
  };
11615
11801
  var KNOWLEDGE_BASE_PATH_ARGUMENT = { knowledgeBaseId: "knowledgeBaseId" };
11802
+ var TRANSFER_TOKEN_OMITTED = { "upload-token": { omit: true } };
11616
11803
  var WORKFLOW_RUN_SCOPE = {
11617
11804
  workflowId: {
11618
11805
  name: "workflow",
@@ -11653,7 +11840,7 @@ var CLI_CONTRACT = {
11653
11840
  getBillingStatus: {
11654
11841
  command: "billing status",
11655
11842
  allWorkspaces: true,
11656
- describe: "Show billing status and current-period credit usage",
11843
+ describe: "Show billing status and current-period credit usage (credits and storage require a personal API key)",
11657
11844
  fields: [
11658
11845
  { header: "plan" },
11659
11846
  { header: "status" },
@@ -11662,7 +11849,10 @@ var CLI_CONTRACT = {
11662
11849
  { header: "period end", path: "period.end", format: "timestamp" },
11663
11850
  { header: "used credits", path: "credits.used" },
11664
11851
  { header: "limit credits", path: "credits.limit" },
11665
- { header: "remaining credits", path: "credits.remaining" }
11852
+ { header: "remaining credits", path: "credits.remaining" },
11853
+ { header: "used storage", path: "storage.usedBytes", format: "bytes" },
11854
+ { header: "limit storage", path: "storage.limitBytes", format: "bytes" },
11855
+ { header: "storage used %", path: "storage.percentUsed" }
11666
11856
  ]
11667
11857
  },
11668
11858
  listBillingLogs: {
@@ -11675,6 +11865,7 @@ var CLI_CONTRACT = {
11675
11865
  startDate: { describe: "Custom period start (ISO 8601)" },
11676
11866
  endDate: { describe: "Custom period end (ISO 8601)" }
11677
11867
  },
11868
+ pageNote: { path: "scope", label: "scope" },
11678
11869
  columns: [
11679
11870
  { header: "at", path: "createdAt", format: "timestamp" },
11680
11871
  { header: "workspace", path: "workspaceId" },
@@ -11719,7 +11910,7 @@ var CLI_CONTRACT = {
11719
11910
  flags: {
11720
11911
  chunkIds: { name: "chunk", list: true }
11721
11912
  },
11722
- confirm: "This can delete every named chunk and its embedding, and cannot be undone."
11913
+ confirm: "This applies --operation to every named chunk; with --operation delete it deletes them and their embeddings, which cannot be undone."
11723
11914
  },
11724
11915
  createKnowledgeConnector: {
11725
11916
  pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT
@@ -11751,7 +11942,8 @@ var CLI_CONTRACT = {
11751
11942
  },
11752
11943
  undeployWorkflow: {
11753
11944
  command: "workflows undeploy",
11754
- describe: "Take a workflow out of deployment"
11945
+ describe: "Take a workflow out of deployment",
11946
+ confirm: "This takes the workflow offline for every API and chat consumer, and agents calling its MCP tools lose access until it is deployed again."
11755
11947
  },
11756
11948
  getWorkflowDeployment: {
11757
11949
  command: "workflows deployment status",
@@ -11759,13 +11951,17 @@ var CLI_CONTRACT = {
11759
11951
  describe: "Show a workflow’s current deployment"
11760
11952
  },
11761
11953
  setSecret: { hidden: true },
11762
- deleteTable: { confirm: "This deletes the table and all of its rows." },
11954
+ deleteTable: {
11955
+ confirm: "This archives the table and all of its rows; restore with `tables restore`."
11956
+ },
11763
11957
  deleteTableRow: { confirm: "This deletes the row." },
11764
11958
  deleteTableColumn: {
11765
11959
  confirm: "This deletes the column and its values in every row.",
11766
11960
  fields: [{ header: "remaining columns", path: "columns", format: "count" }]
11767
11961
  },
11768
- deleteKnowledgeBase: { confirm: "This deletes the knowledge base and every document in it." },
11962
+ deleteKnowledgeBase: {
11963
+ confirm: "This archives the knowledge base and every document in it; restore with `knowledge restore`."
11964
+ },
11769
11965
  deleteKnowledgeDocument: {
11770
11966
  pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
11771
11967
  confirm: "This deletes the document and its embeddings."
@@ -11789,7 +11985,9 @@ var CLI_CONTRACT = {
11789
11985
  deleteSecret: {
11790
11986
  confirm: "This deletes the secret; anything using it may stop working."
11791
11987
  },
11792
- deleteWorkflow: { confirm: "This deletes the workflow and its run history." },
11988
+ deleteWorkflow: {
11989
+ confirm: "This archives the workflow and its run history; restore with `workflows restore`."
11990
+ },
11793
11991
  deleteTableView: { confirm: "This deletes the saved view and its filters." },
11794
11992
  deleteWorkflowGroup: {
11795
11993
  confirm: "This deletes the group, every column it fed, and the values in them.",
@@ -11854,7 +12052,17 @@ var CLI_CONTRACT = {
11854
12052
  getLogStats: {
11855
12053
  command: "logs stats",
11856
12054
  describe: "Summarize run counts, failures, and cost over a window",
11857
- flags: LOG_LIST_FILTER_FLAGS
12055
+ flags: LOG_LIST_FILTER_FLAGS,
12056
+ fields: [
12057
+ { header: "runs", path: "totalRuns" },
12058
+ { header: "errors", path: "totalErrors" },
12059
+ { header: "avg latency", path: "avgLatency", format: "duration" },
12060
+ { header: "window start", path: "timeBounds.start", format: "timestamp" },
12061
+ { header: "window end", path: "timeBounds.end", format: "timestamp" },
12062
+ { header: "bucket width", path: "segmentMs", format: "duration" },
12063
+ { header: "workflows", format: "count" },
12064
+ { header: "workflows truncated", path: "workflowsTruncated", format: "bool" }
12065
+ ]
11858
12066
  },
11859
12067
  readFileText: {
11860
12068
  command: "files read",
@@ -11866,6 +12074,29 @@ var CLI_CONTRACT = {
11866
12074
  deleteWorkflowMcpServer: {
11867
12075
  confirm: "This deletes the MCP server, and any agent calling its tools loses access."
11868
12076
  },
12077
+ deployWorkflowMcpTool: {
12078
+ flags: {
12079
+ parameterDescriptions: { json: true, describe: MCP_PARAMETER_DESCRIPTIONS_HELP }
12080
+ }
12081
+ },
12082
+ listWorkflowMcpServers: {
12083
+ columns: [
12084
+ { header: "id" },
12085
+ { header: "name" },
12086
+ { header: "tools", path: "toolCount" },
12087
+ { header: "public", path: "isPublic", format: "bool" },
12088
+ { header: "url", path: "mcpServerUrl" },
12089
+ { header: "updated", path: "updatedAt", format: "timestamp" }
12090
+ ]
12091
+ },
12092
+ listWorkflowMcpTools: {
12093
+ columns: [
12094
+ { header: "tool", path: "toolName" },
12095
+ { header: "workflow", path: "workflowId" },
12096
+ { header: "description", path: "toolDescription" },
12097
+ { header: "updated", path: "updatedAt", format: "timestamp" }
12098
+ ]
12099
+ },
11869
12100
  undeployWorkflowMcpTool: {
11870
12101
  confirm: "This withdraws the tool, and any agent calling it loses access."
11871
12102
  },
@@ -11879,25 +12110,43 @@ var CLI_CONTRACT = {
11879
12110
  },
11880
12111
  applyWorkflowOperations: {
11881
12112
  command: "workflows operations apply",
11882
- confirm: "This edits the draft graph, and a delete operation removes blocks and their edges."
12113
+ confirm: "This edits the draft graph, and a delete operation removes blocks and their edges.",
12114
+ flags: {
12115
+ operations: { json: true, describe: WORKFLOW_OPERATIONS_HELP },
12116
+ setBlockEnabled: { json: true, describe: WORKFLOW_SET_BLOCK_ENABLED_HELP }
12117
+ }
11883
12118
  },
11884
12119
  applyWorkflowVariables: {
11885
- confirm: "This replaces the workflow’s variables and cannot be undone."
12120
+ confirm: "This replaces the workflow’s variables and cannot be undone.",
12121
+ flags: {
12122
+ operations: { json: true, describe: WORKFLOW_VARIABLE_OPERATIONS_HELP }
12123
+ }
11886
12124
  },
11887
12125
  revertWorkflowVersion: {
11888
12126
  confirm: "This overwrites the draft graph with the selected version and cannot be undone."
11889
12127
  },
12128
+ rollbackWorkflow: {
12129
+ confirm: "This changes which deployed version runs in production for every API and chat consumer."
12130
+ },
11890
12131
  moveWorkflows: {
11891
12132
  command: "workflows move",
11892
12133
  flags: {
11893
12134
  workflowIds: { name: "workflow", list: true },
11894
- folderPath: FOLDER_PATH_FLAG
12135
+ folderPath: {
12136
+ ...FOLDER_PATH_INPUT,
12137
+ name: "to",
12138
+ renamedFrom: ["folder"],
12139
+ describe: "Destination folder path; / moves the workflows to the workspace root"
12140
+ }
11895
12141
  }
11896
12142
  },
11897
12143
  moveTables: {
11898
12144
  command: "tables move",
11899
12145
  flags: {
11900
- folderPaths: FOLDER_PATHS_FLAG,
12146
+ folderPaths: {
12147
+ ...FOLDER_PATHS_FLAG,
12148
+ describe: "Table folders to move, by path as shown in the app; the leading / is optional"
12149
+ },
11901
12150
  targetFolderPath: TARGET_FOLDER_PATH_FLAG
11902
12151
  }
11903
12152
  },
@@ -12016,7 +12265,10 @@ var CLI_CONTRACT = {
12016
12265
  ]
12017
12266
  },
12018
12267
  listFiles: {
12019
- flags: { folderPath: FOLDER_PATH_FLAG },
12268
+ flags: {
12269
+ folderPath: FOLDER_PATH_FLAG,
12270
+ recursive: { boolean: true, negatable: true }
12271
+ },
12020
12272
  columns: [
12021
12273
  { header: "id" },
12022
12274
  { header: "name" },
@@ -12040,9 +12292,19 @@ var CLI_CONTRACT = {
12040
12292
  ]
12041
12293
  },
12042
12294
  getKnowledgeDocument: { pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT },
12043
- updateKnowledgeDocument: { pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT },
12295
+ updateKnowledgeDocument: {
12296
+ pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
12297
+ flags: { retryProcessing: { boolean: true } }
12298
+ },
12044
12299
  listKnowledgeTags: { pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT },
12045
- createKnowledgeTag: { pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT },
12300
+ createKnowledgeTag: {
12301
+ pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
12302
+ flags: {
12303
+ fieldType: {
12304
+ describe: "Value type stored in the slot; it decides which slots are usable and which filter operators apply. Defaults to text, so a number, date, or boolean slot must name its type here. Slot capacity per type: text 7, number 5, date 2, boolean 3"
12305
+ }
12306
+ }
12307
+ },
12046
12308
  updateKnowledgeTag: { pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT },
12047
12309
  deleteKnowledgeTag: {
12048
12310
  pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
@@ -12147,7 +12409,8 @@ var CLI_CONTRACT = {
12147
12409
  { header: "scope" },
12148
12410
  { header: "role" },
12149
12411
  { header: "updated", path: "updatedAt", format: "timestamp" },
12150
- { header: "description" }
12412
+ { header: "description" },
12413
+ { header: "unredacted", format: "bool" }
12151
12414
  ]
12152
12415
  },
12153
12416
  getWorkspace: {
@@ -12155,7 +12418,8 @@ var CLI_CONTRACT = {
12155
12418
  fields: [
12156
12419
  { header: "id" },
12157
12420
  { header: "name" },
12158
- { header: "mode" },
12421
+ { header: "color" },
12422
+ { header: "logo", path: "logoUrl" },
12159
12423
  { header: "members", path: "memberCount" },
12160
12424
  { header: "created", path: "createdAt", format: "timestamp" },
12161
12425
  { header: "updated", path: "updatedAt", format: "timestamp" }
@@ -12178,7 +12442,7 @@ var CLI_CONTRACT = {
12178
12442
  flags: {
12179
12443
  organizationId: {
12180
12444
  name: "organization",
12181
- describe: "Organization ID (personal API key required)"
12445
+ describe: "Organization ID; defaults to your only organization, and is required when your account belongs to more than one (personal API key required)"
12182
12446
  }
12183
12447
  },
12184
12448
  columns: [
@@ -12186,14 +12450,15 @@ var CLI_CONTRACT = {
12186
12450
  { header: "workspace", path: "workspaceId" },
12187
12451
  { header: "actor", path: "actorEmail" },
12188
12452
  { header: "action" },
12189
- { header: "resource", path: "resourceName" }
12453
+ { header: "resource", path: "resourceName" },
12454
+ { header: "id" }
12190
12455
  ]
12191
12456
  },
12192
12457
  getAuditLog: {
12193
12458
  flags: {
12194
12459
  organizationId: {
12195
12460
  name: "organization",
12196
- describe: "Organization ID (personal API key required)"
12461
+ describe: "Organization ID; defaults to your only organization, and is required when your account belongs to more than one (personal API key required)"
12197
12462
  }
12198
12463
  }
12199
12464
  },
@@ -12293,6 +12558,11 @@ var CLI_CONTRACT = {
12293
12558
  command: "knowledge tags cleanup",
12294
12559
  pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
12295
12560
  describe: "Remove tag definitions no document still uses",
12561
+ flags: {
12562
+ unused: {
12563
+ describe: "Whether to remove only the tag definitions no document in the knowledge base still carries a value for. Defaults to true. Pass --no-unused to delete every definition on the knowledge base, which also clears its slot on every document and chunk and is not recoverable"
12564
+ }
12565
+ },
12296
12566
  confirm: "This deletes every tag definition no document still uses. Their slots become free for a different field."
12297
12567
  },
12298
12568
  unzipFile: {
@@ -12454,7 +12724,8 @@ var CLI_CONTRACT = {
12454
12724
  flags: {
12455
12725
  excludeRowIds: { list: true },
12456
12726
  filter: { json: true, describe: TABLE_FILTER_HELP }
12457
- }
12727
+ },
12728
+ confirm: "This cancels running column jobs — the whole table with --scope all, one row with --scope row."
12458
12729
  },
12459
12730
  searchTableRows: {
12460
12731
  command: "tables rows search",
@@ -12489,7 +12760,13 @@ var CLI_CONTRACT = {
12489
12760
  groupIds: { list: true },
12490
12761
  rowIds: { list: true },
12491
12762
  excludeRowIds: { list: true },
12492
- filter: { json: true, describe: TABLE_FILTER_HELP }
12763
+ filter: { json: true, describe: TABLE_FILTER_HELP },
12764
+ limit: {
12765
+ name: "max-rows",
12766
+ renamedFrom: ["limit"],
12767
+ rowCap: true,
12768
+ describe: DISPATCH_ROW_LIMIT_HELP
12769
+ }
12493
12770
  }
12494
12771
  },
12495
12772
  runRowEnrichment: {
@@ -12499,7 +12776,8 @@ var CLI_CONTRACT = {
12499
12776
  createTableImport: { hidden: true },
12500
12777
  createTableImportPartUrls: { hidden: true },
12501
12778
  completeTableImport: { hidden: true },
12502
- cancelTableImport: { command: "tables imports cancel" },
12779
+ getTableImport: { flags: TRANSFER_TOKEN_OMITTED },
12780
+ cancelTableImport: { command: "tables imports cancel", flags: TRANSFER_TOKEN_OMITTED },
12503
12781
  cancelTableExport: { command: "tables exports cancel" },
12504
12782
  tableExportDownload: {
12505
12783
  command: "tables exports download",
@@ -12526,7 +12804,12 @@ var CLI_CONTRACT = {
12526
12804
  },
12527
12805
  stream: { omit: true },
12528
12806
  includeThinking: { omit: true },
12529
- includeToolCalls: { omit: true }
12807
+ includeToolCalls: { omit: true },
12808
+ "x-run-id": {
12809
+ name: "run-id",
12810
+ describe: "One-shot identifier for this run; NOT an idempotency key — reusing a claimed value fails with RUN_ID_CONFLICT instead of replaying the first result, and a fresh value starts another run"
12811
+ },
12812
+ "x-sim-via": { omit: true }
12530
12813
  }
12531
12814
  },
12532
12815
  getWorkflowRun: {
@@ -12614,7 +12897,8 @@ var CLI_CONTRACT = {
12614
12897
  createFileUpload: { hidden: true },
12615
12898
  createFileUploadPartUrls: { hidden: true },
12616
12899
  completeFileUpload: { hidden: true },
12617
- abortFileUpload: { hidden: true }
12900
+ abortFileUpload: { hidden: true },
12901
+ getFileUpload: { hidden: true }
12618
12902
  };
12619
12903
 
12620
12904
  // src/runtime/derive.ts
@@ -12655,15 +12939,44 @@ var JSON_KINDS = new Set(["object", "array", "unknown"]);
12655
12939
  function flagSpecFor(operation, field) {
12656
12940
  return CLI_CONTRACT[operation]?.flags?.[field] ?? {};
12657
12941
  }
12942
+ var RESERVED_PROGRAM_FLAGS = new Set([
12943
+ "--version",
12944
+ "-V",
12945
+ "--help",
12946
+ "-h",
12947
+ "--profile",
12948
+ "-P",
12949
+ "--endpoint",
12950
+ "--workspace",
12951
+ "-w",
12952
+ "--output"
12953
+ ]);
12954
+ var RESERVED_FLAG_REPLACEMENTS = {
12955
+ version: "to-version"
12956
+ };
12658
12957
  function flagNameFor(operation, field) {
12659
- return flagSpecFor(operation, field).name ?? kebab(field);
12958
+ const declared = flagSpecFor(operation, field).name;
12959
+ if (declared)
12960
+ return declared;
12961
+ const derived = kebab(field);
12962
+ return RESERVED_FLAG_REPLACEMENTS[derived] ?? derived;
12660
12963
  }
12661
12964
  function pathFlagNameFor(commandSpec, param) {
12662
12965
  return commandSpec.pathFlags?.[param]?.name ?? kebab(param);
12663
12966
  }
12664
12967
  function takesJson(field, flag) {
12968
+ if (flag.rowCap)
12969
+ return false;
12665
12970
  return flag.json === true || JSON_KINDS.has(field.kind);
12666
12971
  }
12972
+ var MAX_ROW_CAP = 1e6;
12973
+ function coerceRowCap(raw, flagName) {
12974
+ const value = Number(raw);
12975
+ if (!Number.isInteger(value) || value < 1 || value > MAX_ROW_CAP) {
12976
+ throw new SimApiError(`--${flagName} must be a whole number between 1 and ${MAX_ROW_CAP.toLocaleString("en-US")}`, 0);
12977
+ }
12978
+ return { type: "rows", max: value };
12979
+ }
12667
12980
  function readStdin() {
12668
12981
  const idle = new Int32Array(new SharedArrayBuffer(4));
12669
12982
  const buffer = Buffer.alloc(64 * 1024);
@@ -12689,6 +13002,8 @@ function readStdin() {
12689
13002
  return Buffer.concat(chunks).toString("utf8");
12690
13003
  }
12691
13004
  function readArgumentSource(raw, flagName) {
13005
+ if (raw.startsWith("@@"))
13006
+ return { text: raw.slice(1), from: "" };
12692
13007
  if (!raw.startsWith("@"))
12693
13008
  return { text: raw, from: "" };
12694
13009
  const path = raw.slice(1);
@@ -12770,6 +13085,8 @@ function coerce(raw, field, flag, flagName) {
12770
13085
  const values = readListValues(raw, flagName).map((value) => flag.folderPath ? encodeFolderPath(value) : value);
12771
13086
  return field.kind === "string" ? values.join(",") : values;
12772
13087
  }
13088
+ if (flag.rowCap)
13089
+ return coerceRowCap(raw, flagName);
12773
13090
  if (takesJson(field, flag)) {
12774
13091
  if (typeof raw !== "string")
12775
13092
  return raw;
@@ -12827,7 +13144,8 @@ function buildRequest(operation, positional, flags, workspaceId) {
12827
13144
  }
12828
13145
  const query = {};
12829
13146
  const body = {};
12830
- for (const slot of ["query", "body"]) {
13147
+ const headers = {};
13148
+ for (const slot of ["query", "body", "headers"]) {
12831
13149
  for (const [field, descriptor] of Object.entries(spec[slot] ?? {})) {
12832
13150
  const flag = flagSpecFor(operation, field);
12833
13151
  if (flag.omit)
@@ -12845,10 +13163,13 @@ function buildRequest(operation, positional, flags, workspaceId) {
12845
13163
  }
12846
13164
  if (slot === "query")
12847
13165
  query[field] = asQueryValue(value);
13166
+ else if (slot === "headers")
13167
+ headers[field] = String(value);
12848
13168
  else
12849
13169
  body[field] = value;
12850
13170
  }
12851
13171
  }
13172
+ const headerSlot = Object.keys(headers).length > 0 ? { headers } : {};
12852
13173
  if (spec.opaqueBody) {
12853
13174
  if (commandSpec.bodyVariants) {
12854
13175
  const provided = commandSpec.bodyVariants.filter((variant2) => flags[camel(variant2.name)] !== undefined);
@@ -12864,7 +13185,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
12864
13185
  if (variant.kind === "object" && (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) || variant.kind === "array" && !Array.isArray(parsed2)) {
12865
13186
  throw new SimApiError(`--${variant.name} must be a JSON ${variant.kind}`, 0);
12866
13187
  }
12867
- return { path, query, body: { ...body, [variant.property]: parsed2 } };
13188
+ return { path, query, body: { ...body, [variant.property]: parsed2 }, ...headerSlot };
12868
13189
  }
12869
13190
  const raw = flags.body;
12870
13191
  if (typeof raw !== "string")
@@ -12873,12 +13194,13 @@ function buildRequest(operation, positional, flags, workspaceId) {
12873
13194
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
12874
13195
  throw new SimApiError("--body must be a JSON object", 0);
12875
13196
  }
12876
- return { path, query, body: { ...body, ...parsed } };
13197
+ return { path, query, body: { ...body, ...parsed }, ...headerSlot };
12877
13198
  }
12878
13199
  return {
12879
13200
  path,
12880
13201
  query,
12881
- body: spec.body ? body : undefined
13202
+ body: spec.body ? body : undefined,
13203
+ ...headerSlot
12882
13204
  };
12883
13205
  }
12884
13206
 
@@ -13142,9 +13464,19 @@ function unwrapResource(data) {
13142
13464
  const [, value] = entries[0];
13143
13465
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
13144
13466
  }
13145
- function renderPage(format, rows, spec) {
13467
+ function renderPage(format, rows, spec, envelope) {
13468
+ writePageNote(spec, envelope);
13146
13469
  printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
13147
13470
  }
13471
+ function writePageNote(spec, envelope) {
13472
+ if (!spec.pageNote)
13473
+ return;
13474
+ const value = at(envelope, spec.pageNote.path);
13475
+ if (value === undefined || value === null)
13476
+ return;
13477
+ process.stderr.write(source_default.dim(`${spec.pageNote.label}: ${String(value)}
13478
+ `));
13479
+ }
13148
13480
  function renderResult(operation, format, raw, spec, options = {}) {
13149
13481
  if (spec.document) {
13150
13482
  printDocument(format, raw);
@@ -13269,10 +13601,25 @@ async function createConnectionLink(command, body) {
13269
13601
  });
13270
13602
  renderResult("createCredentialConnection", profile.output, response.data, CONNECTION_RESULT);
13271
13603
  }
13604
+ function acceptNameOnUpdate(credentials) {
13605
+ const update = credentials.commands.find((command) => command.name() === "update");
13606
+ if (!update)
13607
+ throw new Error("The generated credentials update command is missing");
13608
+ update.addOption(new Option("--name <displayName>", "Alias for --display-name")).hook("preAction", (_parent, action) => {
13609
+ const options = action.opts();
13610
+ if (options.name === undefined)
13611
+ return;
13612
+ if (options.displayName !== undefined) {
13613
+ throw new SimApiError("--name and --display-name are the same field; pass one, not both", 0);
13614
+ }
13615
+ options.displayName = options.name;
13616
+ });
13617
+ }
13272
13618
  function attachCredentialCommands(program2) {
13273
13619
  const credentials = program2.commands.find((command) => command.name() === "credentials");
13274
13620
  if (!credentials)
13275
13621
  throw new Error("The generated credentials command group is missing");
13622
+ acceptNameOnUpdate(credentials);
13276
13623
  credentials.command("create").argument("<providerId>", "Service-account provider to create a credential for").description("Create a service-account credential using its discovered provider schema").requiredOption("--name <displayName>", "Name shown for the credential in Sim").requiredOption("--credentials <json|@file>", "Provider credentials as JSON (or @path / @- to read a file or stdin)").option("--description <description>", "Optional credential description").option("--id <credentialId>", "Client-generated credential ID when provider discovery requires it").action((providerId, options, command) => createServiceAccount(command, providerId, options));
13277
13624
  credentials.command("connect").argument("<providerId>", "OAuth provider to connect").description("Create a short-lived link for connecting an OAuth provider").requiredOption("--name <displayName>", "Name shown for the new credential in Sim").action(async (providerId, options, command) => createConnectionLink(command, { providerId, displayName: options.name }));
13278
13625
  credentials.command("reconnect").argument("<credentialId>", "Existing OAuth credential to re-authorize").description("Create a short-lived link for reconnecting an OAuth credential").action((credentialId, _options, command) => createConnectionLink(command, { credentialId }));
@@ -13306,23 +13653,24 @@ async function readChatStream(response, onChunk) {
13306
13653
  const processLine = (line) => {
13307
13654
  const event = parseChatStreamLine(line);
13308
13655
  if (!event || event.type === "heartbeat")
13309
- return;
13656
+ return false;
13310
13657
  if (event.type === "chunk") {
13311
13658
  if (event.content)
13312
13659
  onChunk(sanitize(event.content));
13313
- return;
13660
+ return false;
13314
13661
  }
13315
13662
  if (event.type === "error") {
13316
13663
  throw new SimApiError(event.error || "Chat request failed", 0);
13317
13664
  }
13318
13665
  if (event.type === "final") {
13319
13666
  finalResult = event.data;
13320
- return;
13667
+ return true;
13321
13668
  }
13322
13669
  throw new SimApiError("Chat stream returned an unknown event", 0);
13323
13670
  };
13324
13671
  try {
13325
- while (true) {
13672
+ let ended = false;
13673
+ while (!ended) {
13326
13674
  const { done, value } = await reader.read();
13327
13675
  if (done)
13328
13676
  break;
@@ -13331,21 +13679,39 @@ async function readChatStream(response, onChunk) {
13331
13679
  `);
13332
13680
  buffer = lines.pop() ?? "";
13333
13681
  for (const line of lines) {
13334
- processLine(line);
13682
+ if (processLine(line)) {
13683
+ ended = true;
13684
+ break;
13685
+ }
13335
13686
  }
13336
13687
  }
13337
- buffer += decoder.decode();
13338
- processLine(buffer);
13688
+ if (!ended) {
13689
+ buffer += decoder.decode();
13690
+ processLine(buffer);
13691
+ }
13339
13692
  if (!finalResult) {
13340
13693
  throw new SimApiError("Chat stream ended without a final result", 0);
13341
13694
  }
13342
13695
  return finalResult;
13343
13696
  } finally {
13344
- reader.releaseLock();
13697
+ reader.cancel().catch(() => {
13698
+ return;
13699
+ });
13345
13700
  }
13346
13701
  }
13702
+ function ignoreBrokenPipe(stream) {
13703
+ const onError = (error) => {
13704
+ if (error.code !== "EPIPE")
13705
+ throw error;
13706
+ process.exit(0);
13707
+ };
13708
+ stream.on("error", onError);
13709
+ return () => {
13710
+ stream.off("error", onError);
13711
+ };
13712
+ }
13347
13713
  function attachChat(program2) {
13348
- program2.command("chat").description("Ask Sim and print the reply").argument("<message>", "What to ask Sim").option("-c, --conversation <id>", "Continue the conversation with this ID").addHelpText("after", `
13714
+ program2.command("chat").description("Ask Sim and print the reply").argument("<message>", "What to ask Sim").allowExcessArguments(false).option("-c, --conversation <id>", "Continue the conversation with this ID").addHelpText("after", `
13349
13715
  Each turn prints the reply on stdout and the conversation ID on stderr; pass
13350
13716
  that ID back with -c to continue the same conversation. With --output json or
13351
13717
  yaml the reply is not streamed — the finished result is printed as one
@@ -13369,28 +13735,41 @@ Examples:
13369
13735
  });
13370
13736
  const streaming = profile.output === "table" || profile.output === "text";
13371
13737
  let streamed = "";
13372
- const result = await readChatStream(response, (content2) => {
13373
- if (!streaming)
13374
- return;
13375
- streamed += content2;
13376
- process.stdout.write(content2);
13377
- });
13378
- if (!streaming) {
13379
- printProtocolResult(profile.output, result);
13380
- return;
13381
- }
13382
- const content = sanitize(result.content ?? "");
13383
- if (content.startsWith(streamed) && content.length > streamed.length) {
13384
- process.stdout.write(content.slice(streamed.length));
13385
- streamed = content;
13386
- }
13387
- if (streamed.length > 0 && !streamed.endsWith(`
13738
+ const endStreamedLine = () => {
13739
+ if (streamed.length > 0 && !streamed.endsWith(`
13388
13740
  `)) {
13389
- process.stdout.write(`
13741
+ process.stdout.write(`
13390
13742
  `);
13391
- }
13392
- process.stderr.write(`${source_default.dim(`conversation: ${result.conversationId}`)}
13743
+ streamed += `
13744
+ `;
13745
+ }
13746
+ };
13747
+ const restorePipeHandling = streaming ? ignoreBrokenPipe(process.stdout) : undefined;
13748
+ try {
13749
+ const result = await readChatStream(response, (content2) => {
13750
+ if (!streaming)
13751
+ return;
13752
+ streamed += content2;
13753
+ process.stdout.write(content2);
13754
+ });
13755
+ if (!streaming) {
13756
+ printProtocolResult(profile.output, result);
13757
+ return;
13758
+ }
13759
+ const content = sanitize(result.content ?? "");
13760
+ if (content.startsWith(streamed) && content.length > streamed.length) {
13761
+ process.stdout.write(content.slice(streamed.length));
13762
+ streamed = content;
13763
+ }
13764
+ endStreamedLine();
13765
+ process.stderr.write(`${source_default.dim(`conversation: ${result.conversationId}`)}
13393
13766
  `);
13767
+ } catch (error) {
13768
+ endStreamedLine();
13769
+ throw error;
13770
+ } finally {
13771
+ restorePipeHandling?.();
13772
+ }
13394
13773
  });
13395
13774
  }
13396
13775
 
@@ -13518,7 +13897,7 @@ function isTerminalSafeContentType(contentType) {
13518
13897
  ].includes(mediaType);
13519
13898
  }
13520
13899
  function attachFileGet(files) {
13521
- files.command("get").argument("<fileId>", "File whose content to read").description("Get a file’s content").option("-o, --output-file <path>", "Write content to a file instead of stdout").option("--force", "Overwrite --output-file if it already exists").action(async (fileId, options, command) => {
13900
+ files.command("get").argument("<fileId>", "File whose content to read").allowExcessArguments(false).description("Get a file’s content").option("-o, --output-file <path>", "Write content to a file instead of stdout").option("--force", "Overwrite --output-file if it already exists").action(async (fileId, options, command) => {
13522
13901
  const writesToStdout = options.outputFile === undefined || options.outputFile === "-";
13523
13902
  if (writesToStdout && options.force) {
13524
13903
  throw new SimApiError("--force requires --output-file <path>", 0);
@@ -13553,7 +13932,8 @@ function attachFileGet(files) {
13553
13932
  }
13554
13933
 
13555
13934
  // src/transfer/local-file.ts
13556
- import { stat } from "node:fs/promises";
13935
+ import { constants } from "node:fs";
13936
+ import { access, stat } from "node:fs/promises";
13557
13937
  import { basename } from "node:path";
13558
13938
  var CONTENT_TYPES = {
13559
13939
  css: "text/css",
@@ -13593,6 +13973,7 @@ async function localFile(path, override) {
13593
13973
  const stats = await stat(path);
13594
13974
  if (!stats.isFile())
13595
13975
  throw new SimApiError(`${path} is not a regular file`, 0);
13976
+ await access(path, constants.R_OK);
13596
13977
  size = stats.size;
13597
13978
  } catch (error) {
13598
13979
  if (error instanceof SimApiError)
@@ -13675,7 +14056,7 @@ async function finishUploadSession(client, workspaceId, session, path) {
13675
14056
 
13676
14057
  // src/commands/protocol/files-upload.ts
13677
14058
  function attachFileUpload(files) {
13678
- files.command("upload").argument("<path>", "Local file to upload").description("Upload a file to the workspace").option("--folder <path>", "Folder path as shown in the app; defaults to the root folder").option("--name <name>", "Store it under a different name").action(async (path, options, command) => {
14059
+ files.command("upload").argument("<path>", "Local file to upload").allowExcessArguments(false).description("Upload a file to the workspace").option("--folder <path>", "Folder path as shown in the app; defaults to the root folder").option("--name <name>", "Store it under a different name").action(async (path, options, command) => {
13679
14060
  const { client, profile } = clientFrom(command);
13680
14061
  const workspaceId = client.requireWorkspace();
13681
14062
  const { name, size } = await localFile(path, options.name);
@@ -13721,7 +14102,7 @@ function uploadMetadata(options) {
13721
14102
  return metadata;
13722
14103
  }
13723
14104
  function attachKnowledgeDocumentUpload(documents) {
13724
- documents.command("upload").argument("<knowledgeBaseId>", "Knowledge base to upload into").argument("<path>", "Local file to upload").description("Upload a document to a knowledge base").option("--name <name>", "Store it under a different name").option("--tag <value...>", "Document tags, in tag1 through tag7 order").option("--recipe <name>", "Document processing recipe").option("--lang <code>", "Document language code").action(async (knowledgeBaseId, path, options, command) => {
14105
+ documents.command("upload").argument("<knowledgeBaseId>", "Knowledge base to upload into").argument("<path>", "Local file to upload").allowExcessArguments(false).description("Upload a document to a knowledge base").option("--name <name>", "Store it under a different name").option("--tag <value...>", "Document tags, in tag1 through tag7 order").option("--recipe <name>", "Document processing recipe").option("--lang <code>", "Document language code").action(async (knowledgeBaseId, path, options, command) => {
13725
14106
  const { client, profile } = clientFrom(command);
13726
14107
  const workspaceId = client.requireWorkspace();
13727
14108
  const { name, size } = await localFile(path, options.name);
@@ -13980,7 +14361,8 @@ Examples:
13980
14361
  triggers: options.trigger?.length ? options.trigger.join(",") : undefined,
13981
14362
  level: options.level,
13982
14363
  details: options.details,
13983
- order: "desc"
14364
+ sortBy: "startedAt",
14365
+ sortOrder: "desc"
13984
14366
  };
13985
14367
  const write = createWriter(profile.output);
13986
14368
  const status = followStatus();
@@ -14032,7 +14414,14 @@ var DEFAULT_LIMIT = 100;
14032
14414
  function describeField(flag, descriptor, name, field) {
14033
14415
  return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
14034
14416
  }
14035
- function addFieldOption(command, operation, field, descriptor) {
14417
+ function literalNullHint(documented, name) {
14418
+ return /\bnull\b/i.test(documented) ? ` (--${name} null sends the word, not JSON null)` : "";
14419
+ }
14420
+ var WIRE_VOCABULARY_SENTENCE = /\s*The listed spellings[^.]*\.\s*/g;
14421
+ function withoutWireVocabulary(documented) {
14422
+ return documented.replace(WIRE_VOCABULARY_SENTENCE, " ").trim();
14423
+ }
14424
+ function addFieldOption(command, operation, field, descriptor, slot) {
14036
14425
  if (field === PROFILE_INJECTED_FIELD || field === "cursor")
14037
14426
  return;
14038
14427
  const flag = flagSpecFor(operation, field);
@@ -14046,20 +14435,23 @@ function addFieldOption(command, operation, field, descriptor) {
14046
14435
  }
14047
14436
  const documented = describeField(flag, descriptor, name, field);
14048
14437
  if (descriptor.kind === "boolean" || flag.boolean) {
14438
+ const booleanDoc = withoutWireVocabulary(documented);
14049
14439
  if (descriptor.required) {
14050
- command.addOption(new Option(`${short}--${name} <true|false>`, `${documented} (required)`).choices(["true", "false"]).makeOptionMandatory());
14440
+ command.addOption(new Option(`${short}--${name} <true|false>`, `${booleanDoc} (required)`).choices(["true", "false"]).makeOptionMandatory());
14051
14441
  return;
14052
14442
  }
14053
- command.option(`${short}--${name}`, documented);
14054
- if (!flag.boolean)
14443
+ command.option(`${short}--${name}`, booleanDoc);
14444
+ if (!flag.boolean || flag.negatable) {
14055
14445
  command.option(`--no-${name}`, `Send --${name} as false`);
14446
+ }
14056
14447
  return;
14057
14448
  }
14058
14449
  const takesList = flag.list === true;
14059
14450
  const wantsJson = takesJson(descriptor, flag);
14060
- const placeholder = takesList ? "<value...>" : wantsJson ? "<json|@file>" : "<value>";
14451
+ const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
14061
14452
  const choices = flag.choices ?? descriptor.values;
14062
- const describe = `${documented}${takesList ? " (space-separated, or @path / @- with one value per line)" : wantsJson ? " (JSON, or @path / @- to read a file or stdin)" : ""}${descriptor.required ? " (required)" : ""}`;
14453
+ const literalNull = slot === "body" && !takesList && !wantsJson;
14454
+ const describe = `${documented}${takesList ? " (space-separated, or @path / @- with one value per line)" : wantsJson ? " (JSON, or @path / @- to read a file or stdin)" : ""}${descriptor.required ? " (required)" : ""}${literalNull ? literalNullHint(documented, name) : ""}`;
14063
14455
  const renamedFrom = flag.renamedFrom ?? [];
14064
14456
  const option = new Option(`${short}--${name} ${placeholder}`, describe);
14065
14457
  if (flag.hidden)
@@ -14088,13 +14480,13 @@ function addOperationOptions(command, operation, commandSpec, operationSpec) {
14088
14480
  const short = flag.short ? `-${flag.short}, ` : "";
14089
14481
  command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
14090
14482
  }
14091
- for (const slot of ["query", "body"]) {
14483
+ for (const slot of ["query", "body", "headers"]) {
14092
14484
  for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
14093
14485
  if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
14094
14486
  continue;
14095
14487
  if (commandSpec.positionals?.includes(field))
14096
14488
  continue;
14097
- addFieldOption(command, operation, field, descriptor);
14489
+ addFieldOption(command, operation, field, descriptor, slot);
14098
14490
  }
14099
14491
  }
14100
14492
  if (commandSpec.allWorkspaces) {
@@ -14113,7 +14505,8 @@ function addOperationOptions(command, operation, commandSpec, operationSpec) {
14113
14505
  }
14114
14506
  }
14115
14507
  if (commandSpec.confirm) {
14116
- command.option("-y, --yes", "Confirm this destructive operation (required)");
14508
+ const exemptedByDryRun = operationSpec.query?.dryRun !== undefined || operationSpec.body?.dryRun !== undefined;
14509
+ command.option("-y, --yes", exemptedByDryRun ? "Confirm this destructive operation (required unless --dry-run)" : "Confirm this destructive operation (required)");
14117
14510
  }
14118
14511
  }
14119
14512
 
@@ -14212,19 +14605,43 @@ function tableNameFrom(fileName) {
14212
14605
  function jsonFlag(raw, flagName, kind) {
14213
14606
  return coerce(raw, { kind }, { json: true }, flagName);
14214
14607
  }
14608
+ function rejectionFields(job) {
14609
+ const rows = job.rowsRejected ?? 0;
14610
+ const cells = job.cellsRejected ?? 0;
14611
+ if (rows === 0 && cells === 0)
14612
+ return {};
14613
+ const samples = (job.rejectedSamples ?? []).map((sample) => sample.line === null || sample.line === undefined ? `${sample.message} (${sample.code})` : `line ${sample.line}: ${sample.message} (${sample.code})`);
14614
+ return {
14615
+ rowsRejected: rows,
14616
+ cellsRejected: cells,
14617
+ ...samples.length > 0 ? { rejectedSamples: samples } : {}
14618
+ };
14619
+ }
14620
+ function progressLine(job) {
14621
+ const rows = job.rowsRejected ?? 0;
14622
+ const cells = job.cellsRejected ?? 0;
14623
+ const parts = [];
14624
+ if (rows > 0)
14625
+ parts.push(`${rows} rows rejected`);
14626
+ if (cells > 0)
14627
+ parts.push(`${cells} cells rejected`);
14628
+ const suffix = parts.length > 0 ? `, ${parts.join(", ")}` : "";
14629
+ return `${job.status}… ${job.rowsProcessed} rows${suffix}`;
14630
+ }
14215
14631
  async function watchImport(client, workspaceId, job) {
14216
14632
  let current = job;
14217
- let reported2 = -1;
14633
+ let reported2 = null;
14218
14634
  while (!IMPORT_SETTLED.has(current.status)) {
14219
14635
  await sleep2(IMPORT_POLL_MS);
14220
14636
  const next = await client.request(`/api/v2/tables/imports/${encodeURIComponent(current.id)}`, { query: { workspaceId } });
14221
14637
  current = next.data;
14222
- if (process.stderr.isTTY && current.rowsProcessed !== reported2) {
14223
- reported2 = current.rowsProcessed;
14224
- process.stderr.write(`\r${source_default.dim(`${current.status}… ${reported2} rows`)}\x1B[K`);
14638
+ const line = progressLine(current);
14639
+ if (process.stderr.isTTY && line !== reported2) {
14640
+ reported2 = line;
14641
+ process.stderr.write(`\r${source_default.dim(line)}\x1B[K`);
14225
14642
  }
14226
14643
  }
14227
- if (process.stderr.isTTY && reported2 >= 0)
14644
+ if (process.stderr.isTTY && reported2 !== null)
14228
14645
  process.stderr.write("\r\x1B[K");
14229
14646
  return current;
14230
14647
  }
@@ -14246,7 +14663,7 @@ function validateTargetOptions(options) {
14246
14663
  return intoExisting;
14247
14664
  }
14248
14665
  function attachTableImport(tables) {
14249
- tables.command("import").argument("[path]", "Local CSV file to import; omit when using --file-id").description("Import a CSV, into a new table by default").option("--name <name>", "Identifier for the new table: letters, numbers, and underscores; defaults to the sanitized file name").option("--table-id <id>", "Import into this existing table instead of creating one").addOption(new Option("--mode <append|replace>", "How to write into --table-id (default: append)").choices(["append", "replace"])).option("--folder <path>", "Folder path for the new table, as shown in the app").option("--file-id <id>", "Import a file already in the workspace instead of a local path").option("--mapping <json|@file>", "Column mapping (--table-id only)").option("--create-columns <json|@file>", "Columns to create (--table-id only)").option("--timezone <iana>", "Timezone for date parsing, e.g. America/New_York").option("--no-wait", "Return once the import is queued instead of watching it").action(async (path, options, command) => {
14666
+ tables.command("import").argument("[path]", "Local CSV file to import; omit when using --file-id").allowExcessArguments(false).description("Import a CSV, into a new table by default").option("--name <name>", "Identifier for the new table: letters, numbers, and underscores; defaults to the sanitized file name").option("--table-id <id>", "Import into this existing table instead of creating one").addOption(new Option("--mode <append|replace>", "How to write into --table-id (default: append)").choices(["append", "replace"])).option("--folder <path>", "Folder path for the new table, as shown in the app").option("--file-id <id>", "Import a file already in the workspace instead of a local path").option("--mapping <json|@file>", "Column mapping (--table-id only)").option("--create-columns <json|@file>", "Columns to create (--table-id only)").option("--timezone <iana>", "Timezone for date parsing, e.g. America/New_York").option("--no-wait", "Return once the import is queued instead of watching it").action(async (path, options, command) => {
14250
14667
  const { client, profile } = clientFrom(command);
14251
14668
  const workspaceId = client.requireWorkspace();
14252
14669
  if (Boolean(path) === Boolean(options.fileId)) {
@@ -14302,7 +14719,8 @@ function attachTableImport(tables) {
14302
14719
  id: job.id,
14303
14720
  status: job.status,
14304
14721
  tableId: job.tableId,
14305
- rowsProcessed: job.rowsProcessed
14722
+ rowsProcessed: job.rowsProcessed,
14723
+ ...rejectionFields(job)
14306
14724
  });
14307
14725
  return;
14308
14726
  }
@@ -14314,7 +14732,8 @@ function attachTableImport(tables) {
14314
14732
  id: finished.id,
14315
14733
  status: finished.status,
14316
14734
  tableId: finished.tableId,
14317
- rowsProcessed: finished.rowsProcessed
14735
+ rowsProcessed: finished.rowsProcessed,
14736
+ ...rejectionFields(finished)
14318
14737
  });
14319
14738
  });
14320
14739
  }
@@ -14337,6 +14756,75 @@ function warnRenamedFlag(from, to) {
14337
14756
  }
14338
14757
 
14339
14758
  // src/runtime/execute.ts
14759
+ var RUN_OUTCOME_OPERATIONS = new Set(["executeWorkflow"]);
14760
+ var FAILED_RUN_STATUS_MESSAGES = {
14761
+ failed: "The workflow run failed.",
14762
+ cancelled: "The workflow run was cancelled."
14763
+ };
14764
+ function runFailureMessage(operation, payload) {
14765
+ if (!RUN_OUTCOME_OPERATIONS.has(operation))
14766
+ return null;
14767
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
14768
+ return null;
14769
+ const { status, error } = payload;
14770
+ if (typeof status !== "string")
14771
+ return null;
14772
+ const fallback = FAILED_RUN_STATUS_MESSAGES[status];
14773
+ if (!fallback)
14774
+ return null;
14775
+ const reported2 = error?.message;
14776
+ return safeOneLine(typeof reported2 === "string" && reported2 ? reported2 : fallback);
14777
+ }
14778
+ var BULK_OUTCOME_CHECKS = {
14779
+ bulkDeleteFiles: (payload, body) => {
14780
+ if (countOf(payload.deletedItems?.files) > 0)
14781
+ return null;
14782
+ const requested = lengthOf(body?.fileIds);
14783
+ if (requested === 0)
14784
+ return null;
14785
+ return `Deleted nothing: none of the ${requested} requested ${requested === 1 ? "file was" : "files were"} deleted.`;
14786
+ },
14787
+ bulkDeleteTables: (payload) => {
14788
+ const items = payload.deletedItems;
14789
+ const deleted = countOf(items?.tables) + countOf(items?.folders);
14790
+ if (deleted > 0)
14791
+ return null;
14792
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
14793
+ if (missed === 0)
14794
+ return null;
14795
+ return `Deleted nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be deleted.`;
14796
+ },
14797
+ moveTables: (payload) => {
14798
+ if (lengthOf(payload.moved) > 0)
14799
+ return null;
14800
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
14801
+ if (missed === 0)
14802
+ return null;
14803
+ return `Moved nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be moved.`;
14804
+ },
14805
+ moveWorkflows: (payload) => {
14806
+ if (lengthOf(payload.moved) > 0)
14807
+ return null;
14808
+ const failed = lengthOf(payload.failed);
14809
+ if (failed === 0)
14810
+ return null;
14811
+ return `Moved nothing: ${failed} of ${failed} ${failed === 1 ? "workflow" : "workflows"} could not be moved.`;
14812
+ }
14813
+ };
14814
+ function countOf(value) {
14815
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
14816
+ }
14817
+ function lengthOf(value) {
14818
+ return Array.isArray(value) ? value.length : 0;
14819
+ }
14820
+ function bulkFailureMessage(operation, payload, body) {
14821
+ const check = BULK_OUTCOME_CHECKS[operation];
14822
+ if (!check)
14823
+ return null;
14824
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
14825
+ return null;
14826
+ return check(payload, body);
14827
+ }
14340
14828
  function cursorSlot(operationSpec) {
14341
14829
  if (operationSpec.query && "cursor" in operationSpec.query)
14342
14830
  return "query";
@@ -14398,13 +14886,16 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14398
14886
  const rows = [];
14399
14887
  const progress = pageProgress();
14400
14888
  let cursor = null;
14889
+ let envelope;
14401
14890
  try {
14402
14891
  do {
14403
14892
  const page = await client.request(request.path, {
14404
14893
  method: operationSpec.method,
14894
+ headers: request.headers,
14405
14895
  query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
14406
14896
  body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
14407
14897
  });
14898
+ envelope ??= page;
14408
14899
  rows.push(...page.data);
14409
14900
  cursor = page.nextCursor;
14410
14901
  if (cursor && rows.length < limit)
@@ -14413,17 +14904,22 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14413
14904
  } finally {
14414
14905
  progress.finish();
14415
14906
  }
14416
- renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec);
14907
+ renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope);
14417
14908
  return;
14418
14909
  }
14419
14910
  const result = await client.request(request.path, {
14420
14911
  method: operationSpec.method,
14912
+ headers: request.headers,
14421
14913
  query: request.query,
14422
14914
  body: request.body
14423
14915
  });
14424
- renderResult(operation, profile.output, result?.data ?? result, commandSpec, {
14916
+ const payload = result?.data ?? result;
14917
+ renderResult(operation, profile.output, payload, commandSpec, {
14425
14918
  expandedTrace: requestFlags.trace === true
14426
14919
  });
14920
+ const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14921
+ if (failure)
14922
+ throw new SimApiError(failure, 0);
14427
14923
  }
14428
14924
 
14429
14925
  // src/commands/protocol/workflow-run-follow.ts
@@ -14746,7 +15242,7 @@ function runSpec() {
14746
15242
  return CLI_CONTRACT.getWorkflowRun ?? {};
14747
15243
  }
14748
15244
  function attachWorkflowRunWait(runs) {
14749
- runs.command("wait").argument("<runId>", V2_OPERATIONS.getWorkflowRun.pathParamDocs?.runId).description("Wait for a run to reach a terminal state, then show it").addOption(new Option("--workflow <workflowId>", "Workflow ID (required)").makeOptionMandatory()).addOption(new Option(WAIT_TIMEOUT_FLAG, `Give up after this many seconds, or 0 to wait indefinitely (default: ${DEFAULT_WAIT_TIMEOUT_SECONDS}). Bounds the whole wait; SIM_TIMEOUT_SECONDS bounds one request`)).action(async (runId, options, command) => {
15245
+ runs.command("wait").argument("<runId>", V2_OPERATIONS.getWorkflowRun.pathParamDocs?.runId).allowExcessArguments(false).description("Wait for a run to reach a terminal state, then show it").addOption(new Option("--workflow <workflowId>", "Workflow ID (required)").makeOptionMandatory()).addOption(new Option(WAIT_TIMEOUT_FLAG, `Give up after this many seconds, or 0 to wait indefinitely (default: ${DEFAULT_WAIT_TIMEOUT_SECONDS}). Bounds the whole wait; SIM_TIMEOUT_SECONDS bounds one request`)).action(async (runId, options, command) => {
14750
15246
  const timeoutSeconds = options.waitTimeout === undefined ? DEFAULT_WAIT_TIMEOUT_SECONDS : parseWaitTimeout(options.waitTimeout);
14751
15247
  const { client, profile } = clientFrom(command);
14752
15248
  const operation = V2_OPERATIONS.getWorkflowRun;
@@ -14838,6 +15334,21 @@ function attachProtocolCommands(program2) {
14838
15334
  // src/terminal/secret-input.ts
14839
15335
  import { emitKeypressEvents } from "node:readline";
14840
15336
  var MAX_SECRET_LENGTH = 65536;
15337
+
15338
+ class SecretInputCancelledError extends SimApiError {
15339
+ constructor() {
15340
+ super("Secret input cancelled.", 0);
15341
+ this.name = "SecretInputCancelledError";
15342
+ }
15343
+ }
15344
+ function metaCharacter(key) {
15345
+ if (!key.meta || !key.sequence)
15346
+ return "";
15347
+ const character = key.sequence.replace(/^\u001b+/, "");
15348
+ if (character.length !== 1 || character < " " || character === "")
15349
+ return "";
15350
+ return character;
15351
+ }
14841
15352
  function promptSecret(input = process.stdin, output = process.stderr) {
14842
15353
  if (!input.isTTY) {
14843
15354
  throw new SimApiError("Interactive secret input requires a terminal. Pass --value instead.", 0);
@@ -14871,9 +15382,10 @@ function promptSecret(input = process.stdin, output = process.stderr) {
14871
15382
  }
14872
15383
  };
14873
15384
  const fail = (message) => finish(() => reject(new SimApiError(message, 0)));
15385
+ const cancel = () => finish(() => reject(new SecretInputCancelledError));
14874
15386
  function onKeypress(text2, key) {
14875
15387
  if (key.ctrl && (key.name === "c" || key.name === "d")) {
14876
- fail("Secret input cancelled.");
15388
+ cancel();
14877
15389
  return;
14878
15390
  }
14879
15391
  if (key.name === "return" || key.name === "enter") {
@@ -14892,14 +15404,15 @@ function promptSecret(input = process.stdin, output = process.stderr) {
14892
15404
  }
14893
15405
  return;
14894
15406
  }
14895
- if (!text2 || key.ctrl || key.meta || key.name === "escape")
15407
+ const entered = text2 || metaCharacter(key);
15408
+ if (!entered || key.ctrl || key.name === "escape")
14896
15409
  return;
14897
- if (value.length + text2.length > MAX_SECRET_LENGTH) {
15410
+ if (value.length + entered.length > MAX_SECRET_LENGTH) {
14898
15411
  fail(`Secret value cannot exceed ${MAX_SECRET_LENGTH} characters.`);
14899
15412
  return;
14900
15413
  }
14901
- value += text2;
14902
- output.write("*".repeat(Array.from(text2).length));
15414
+ value += entered;
15415
+ output.write("*".repeat(Array.from(entered).length));
14903
15416
  }
14904
15417
  input.on("keypress", onKeypress);
14905
15418
  });
@@ -14907,6 +15420,7 @@ function promptSecret(input = process.stdin, output = process.stderr) {
14907
15420
 
14908
15421
  // src/commands/secrets.ts
14909
15422
  var MAX_SECRET_LENGTH2 = 65536;
15423
+ var CANCELLED_EXIT_CODE = 130;
14910
15424
  var SECRET_SCOPES = ["workspace", "personal"];
14911
15425
  var SECRET_RESULT = {
14912
15426
  fields: [
@@ -14917,6 +15431,13 @@ var SECRET_RESULT = {
14917
15431
  { header: "description" }
14918
15432
  ]
14919
15433
  };
15434
+ function readValueArgument(raw) {
15435
+ if (raw.startsWith("@@"))
15436
+ return raw.slice(1);
15437
+ if (!raw.startsWith("@"))
15438
+ return raw;
15439
+ return readArgumentSource(raw, "value").text;
15440
+ }
14920
15441
  function validateSecretValue(value) {
14921
15442
  if (value.length === 0)
14922
15443
  throw new SimApiError("Secret value cannot be empty.", 0);
@@ -14925,17 +15446,30 @@ function validateSecretValue(value) {
14925
15446
  }
14926
15447
  return value;
14927
15448
  }
14928
- function validateDescriptionScope(description, scope) {
14929
- if (description === undefined)
15449
+ function validateWorkspaceOnlyFlag(flag, value, scope) {
15450
+ if (value === undefined)
14930
15451
  return;
14931
15452
  if (scope === "personal") {
14932
- throw new SimApiError("--description is only supported for a workspace secret.", 0);
15453
+ throw new SimApiError(`--${flag} is only supported for a workspace secret.`, 0);
15454
+ }
15455
+ return value;
15456
+ }
15457
+ async function readSecretValue(options) {
15458
+ if (options.value !== undefined)
15459
+ return validateSecretValue(readValueArgument(options.value));
15460
+ try {
15461
+ return validateSecretValue(await promptSecret());
15462
+ } catch (error) {
15463
+ if (!(error instanceof SecretInputCancelledError))
15464
+ throw error;
15465
+ console.error(source_default.red(`Error: ${error.message}`));
15466
+ return process.exit(CANCELLED_EXIT_CODE);
14933
15467
  }
14934
- return description;
14935
15468
  }
14936
15469
  async function setSecret(name, options, command) {
14937
- const description = validateDescriptionScope(options.description, options.scope);
14938
- const value = validateSecretValue(options.value ?? await promptSecret());
15470
+ const description = validateWorkspaceOnlyFlag("description", options.description, options.scope);
15471
+ const unredacted = validateWorkspaceOnlyFlag("unredacted", options.unredacted, options.scope);
15472
+ const value = await readSecretValue(options);
14939
15473
  const { client, profile } = clientFrom(command);
14940
15474
  const operation = V2_OPERATIONS.setSecret;
14941
15475
  const response = await client.request(resolvePath(operation.path, { name }), {
@@ -14944,7 +15478,8 @@ async function setSecret(name, options, command) {
14944
15478
  workspaceId: client.requireWorkspace(),
14945
15479
  scope: options.scope,
14946
15480
  value,
14947
- description
15481
+ description,
15482
+ ...unredacted === undefined ? {} : { unredacted }
14948
15483
  }
14949
15484
  });
14950
15485
  renderResult("setSecret", profile.output, response.data, SECRET_RESULT);
@@ -14953,7 +15488,7 @@ function attachSecretCommands(program2) {
14953
15488
  const secrets = program2.commands.find((command) => command.name() === "secrets");
14954
15489
  if (!secrets)
14955
15490
  throw new Error("The generated secrets command group is missing");
14956
- 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));
15491
+ 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|@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").action((name, options, command) => setSecret(name, options, command));
14957
15492
  }
14958
15493
 
14959
15494
  // src/runtime/build.ts
@@ -15003,6 +15538,35 @@ function addMissingArgumentExample(command) {
15003
15538
  });
15004
15539
  return command;
15005
15540
  }
15541
+ function assertNoReservedFlags(command, operation) {
15542
+ for (const option of command.options) {
15543
+ for (const flag of [option.long, option.short]) {
15544
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
15545
+ throw new Error(`${operation} declares ${flag}, which the root program already owns; give the flag another name`);
15546
+ }
15547
+ }
15548
+ }
15549
+ }
15550
+ var RESERVED_FLAG_EXEMPTIONS = new Set(["profiles add"]);
15551
+ function assertNoReservedProgramFlags(program2) {
15552
+ const walk = (command, prefix) => {
15553
+ const path = [...prefix, command.name()];
15554
+ const name = path.join(" ");
15555
+ if (!RESERVED_FLAG_EXEMPTIONS.has(name)) {
15556
+ for (const option of command.options) {
15557
+ for (const flag of [option.long, option.short]) {
15558
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
15559
+ throw new Error(`"sim ${name}" declares ${flag}, which the root program already owns; give the flag another name`);
15560
+ }
15561
+ }
15562
+ }
15563
+ }
15564
+ for (const child of command.commands)
15565
+ walk(child, path);
15566
+ };
15567
+ for (const child of program2.commands)
15568
+ walk(child, []);
15569
+ }
15006
15570
  function configureOperation(command, operation, spec) {
15007
15571
  const operationSpec = V2_OPERATIONS[operation];
15008
15572
  command.allowExcessArguments(false);
@@ -15051,11 +15615,11 @@ function configureOperation(command, operation, spec) {
15051
15615
  }
15052
15616
  if (spec.requestFields) {
15053
15617
  for (const field of spec.requestFields) {
15054
- if (!operationSpec.query?.[field] && !operationSpec.body?.[field]) {
15618
+ if (!operationSpec.query?.[field] && !operationSpec.body?.[field] && !operationSpec.headers?.[field]) {
15055
15619
  throw new Error(`${operation}.${field} is not a request field`);
15056
15620
  }
15057
15621
  }
15058
- for (const slot of ["query", "body"]) {
15622
+ for (const slot of ["query", "body", "headers"]) {
15059
15623
  for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
15060
15624
  if (descriptor.required && field !== PROFILE_INJECTED_FIELD && !spec.requestFields.includes(field)) {
15061
15625
  throw new Error(`${operation}.${field} is required but not exposed`);
@@ -15065,6 +15629,7 @@ function configureOperation(command, operation, spec) {
15065
15629
  }
15066
15630
  command.description(spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`);
15067
15631
  addOperationOptions(command, operation, spec, operationSpec);
15632
+ assertNoReservedFlags(command, operation);
15068
15633
  command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation));
15069
15634
  return command;
15070
15635
  }
@@ -15082,7 +15647,14 @@ function addRenamedCommand(groups, operation, spec, from, to) {
15082
15647
  }
15083
15648
  const leaf = buildLeaf(operation, spec, rest[rest.length - 1]);
15084
15649
  leaf.hook("preAction", () => warnRenamedCommand(from, to));
15085
- parent.addCommand(leaf, { hidden: true });
15650
+ addSubcommand(parent, leaf, { hidden: true });
15651
+ }
15652
+ function addSubcommand(parent, child, options = {}) {
15653
+ const wasLeaf = parent.commands.length === 0 && parent.registeredArguments.length > 0;
15654
+ const usage = wasLeaf ? parent.usage() : null;
15655
+ parent.addCommand(child, { hidden: options.hidden });
15656
+ if (usage !== null)
15657
+ parent.usage(usage);
15086
15658
  }
15087
15659
  function groupFor(groups, name) {
15088
15660
  const existing = groups.get(name);
@@ -15104,7 +15676,7 @@ function nestedGroup(parent, name, options = {}) {
15104
15676
  if (existing)
15105
15677
  return existing;
15106
15678
  const created = new Command(name).description(`Manage ${resourceLabel(parent.name())} ${name.replaceAll("-", " ")}`);
15107
- parent.addCommand(created, { hidden: options.hidden });
15679
+ addSubcommand(parent, created, { hidden: options.hidden });
15108
15680
  return created;
15109
15681
  }
15110
15682
  function addLeafCommand(groups, operation, spec, segments) {
@@ -15176,24 +15748,63 @@ Profiles work like the AWS CLI: settings live in ~/.sim/config, keys in
15176
15748
  ~/.sim/credentials (0600), or under SIM_CONFIG_DIR when it is set. Select one
15177
15749
  with -P, --profile, or SIM_PROFILE.
15178
15750
 
15751
+ Workflow, knowledge-base and workspace IDs are bare UUIDs. Table IDs carry a
15752
+ tbl_ prefix and file IDs a wf_ one, so wf_ never names a workflow.
15753
+
15179
15754
  Examples:
15180
- $ sim login Authorize the default profile
15181
- $ sim profile add acme --workspace ws_123 Reuse that login for a workspace
15755
+ $ sim login Authorize the default profile
15182
15756
  $ sim login --profile dev --endpoint http://localhost:3000
15757
+ $ sim profile add acme --workspace 7e2d9c14-6b83-4a55-8f01-c4d3e9a76b28
15183
15758
  $ sim workflows list
15184
15759
  $ sim logs list --level error --limit 20
15185
- $ sim --output json tables get tbl_123 Override output for one command
15186
- $ sim configure --set-output json Save a profile output default
15187
- $ sim knowledge search --query "refund policy" --kb kb_123
15188
- $ sim workflows export wf_123 > wf.json JSON flags read files with @
15760
+ $ sim configure --set-output json Save a profile output default
15761
+ $ sim --output json tables get tbl_9f3c1a05d4b7426e8c2f0917ab35de64
15762
+ $ sim knowledge search --query "refund policy" --kb 4c1b7f60-2d55-4a3e-9c18-70b6ea2f9d31
15763
+ $ sim workflows export 3a9e21d8-5f47-4c0b-b2ea-91d7c6034ef8 > wf.json
15189
15764
  $ sim workflows import --workflow @wf.json
15190
15765
  $ sim whoami --profile dev
15191
15766
  `;
15767
+ var ROOT_VALUE_FLAGS = new Set([
15768
+ "-P",
15769
+ "--profile",
15770
+ "--endpoint",
15771
+ "-w",
15772
+ "--workspace",
15773
+ "--output"
15774
+ ]);
15775
+ function namesASubcommandFirst(rawArgs) {
15776
+ const tokens = rawArgs.slice(2);
15777
+ for (let index = 0;index < tokens.length; index += 1) {
15778
+ const token2 = tokens[index];
15779
+ if (token2 === "--version" || token2 === "-V")
15780
+ return false;
15781
+ if (ROOT_VALUE_FLAGS.has(token2)) {
15782
+ index += 1;
15783
+ continue;
15784
+ }
15785
+ if (token2.startsWith("-"))
15786
+ continue;
15787
+ return true;
15788
+ }
15789
+ return false;
15790
+ }
15791
+ function addVersionOption(program2) {
15792
+ program2.on("option:version", (value) => {
15793
+ if (value === null || value === undefined) {
15794
+ const { rawArgs } = program2;
15795
+ if (!namesASubcommandFirst(rawArgs ?? []))
15796
+ return;
15797
+ program2.error("error: --version reports the Sim CLI version and is not a command flag. A command that acts on a deployment version reads it from --to-version.");
15798
+ }
15799
+ program2.error("error: --version reports the Sim CLI version and takes no value. A command that acts on a deployment version reads it from --to-version.");
15800
+ });
15801
+ program2.version(CLI_VERSION, "-V, --version [none]", "output the version number (takes no value)");
15802
+ }
15192
15803
  function buildProgram(options = {}) {
15193
15804
  const program2 = new Command;
15194
15805
  program2.name("sim").description(PROGRAM_DESCRIPTION);
15195
15806
  if (options.version !== false)
15196
- program2.version(CLI_VERSION);
15807
+ addVersionOption(program2);
15197
15808
  program2.option("-P, --profile <name>", "Profile to use (env: SIM_PROFILE)").option("--endpoint <url>", "Sim deployment to talk to (env: SIM_ENDPOINT)").option("-w, --workspace <id>", "Workspace to target (env: SIM_WORKSPACE)").addOption(new Option("--output <format>", "Output format for this command").choices([...OUTPUT_FORMATS]));
15198
15809
  program2.addCommand(loginCommand());
15199
15810
  program2.addCommand(logoutCommand());
@@ -15207,6 +15818,7 @@ function buildProgram(options = {}) {
15207
15818
  attachProtocolCommands(program2);
15208
15819
  attachSecretCommands(program2);
15209
15820
  program2.addHelpText("after", HELP_EPILOGUE);
15821
+ assertNoReservedProgramFlags(program2);
15210
15822
  return program2;
15211
15823
  }
15212
15824