sim 2.1.2 → 2.1.3-preview.51.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 (2) hide show
  1. package/dist/index.js +1301 -788
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2315,8 +2315,26 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "n
2315
2315
  import { dirname } from "node:path";
2316
2316
 
2317
2317
  // src/config/ini.ts
2318
+ class ProfileConfigError extends Error {
2319
+ constructor(message) {
2320
+ super(message);
2321
+ this.name = "ProfileConfigError";
2322
+ }
2323
+ }
2318
2324
  var SECTION_PATTERN = /^\s*\[([^\]]*)\]\s*$/;
2319
2325
  var KV_PATTERN = /^\s*([A-Za-z0-9_.-]+)\s*=\s*(.*?)\s*$/;
2326
+ var FORBIDDEN_CLASS = "\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029";
2327
+ var FORBIDDEN_IN_VALUE = new RegExp(`[${FORBIDDEN_CLASS}]`);
2328
+ var FORBIDDEN_IN_NAME = new RegExp(`[${FORBIDDEN_CLASS}[\\]]`);
2329
+ var WRITABLE_KEY = /^[A-Za-z0-9_.-]+$/;
2330
+ function assertWritable(text, what, forbidden) {
2331
+ if (forbidden.test(text)) {
2332
+ throw new ProfileConfigError(`Refusing to write ${what}: line breaks and control characters cannot be stored in the ~/.sim files, because the format has no way to escape them.`);
2333
+ }
2334
+ if (text !== text.trim()) {
2335
+ throw new ProfileConfigError(`Refusing to write ${what}: leading or trailing whitespace is not preserved by the ~/.sim files, so it would not read back as written.`);
2336
+ }
2337
+ }
2320
2338
  function parseIni(text) {
2321
2339
  const doc = { preamble: [], sections: [] };
2322
2340
  let current = null;
@@ -2327,7 +2345,7 @@ function parseIni(text) {
2327
2345
  for (const line of lines) {
2328
2346
  const sectionMatch = SECTION_PATTERN.exec(line);
2329
2347
  if (sectionMatch) {
2330
- current = { name: sectionMatch[1].trim(), entries: [] };
2348
+ current = { name: sectionMatch[1].trim(), header: line, entries: [] };
2331
2349
  doc.sections.push(current);
2332
2350
  continue;
2333
2351
  }
@@ -2351,7 +2369,7 @@ function serializeIni(doc) {
2351
2369
  lines.pop();
2352
2370
  if (lines.length > 0)
2353
2371
  lines.push("");
2354
- lines.push(`[${section.name}]`);
2372
+ lines.push(section.header ?? `[${section.name}]`);
2355
2373
  for (const entry of section.entries) {
2356
2374
  lines.push(entry.kind === "kv" ? `${entry.key} = ${entry.value}` : entry.text);
2357
2375
  }
@@ -2379,9 +2397,23 @@ function listSections(doc) {
2379
2397
  return doc.sections.map((s) => s.name);
2380
2398
  }
2381
2399
  function setSectionValues(doc, name, values) {
2400
+ assertWritable(name, `a section named "${name}"`, FORBIDDEN_IN_NAME);
2401
+ for (const [key, value] of Object.entries(values)) {
2402
+ if (!WRITABLE_KEY.test(key)) {
2403
+ throw new ProfileConfigError(`Refusing to write an unreadable setting name "${key}".`);
2404
+ }
2405
+ if (value === null)
2406
+ continue;
2407
+ if (value.trim() === "") {
2408
+ throw new ProfileConfigError(`Refusing to write a blank value for "${key}".`);
2409
+ }
2410
+ assertWritable(value, `a value for "${key}"`, FORBIDDEN_IN_VALUE);
2411
+ }
2382
2412
  const matching = doc.sections.filter((s) => s.name === name);
2383
2413
  let section = matching[0];
2384
2414
  if (!section) {
2415
+ if (Object.values(values).every((value) => value === null))
2416
+ return;
2385
2417
  section = { name, entries: [] };
2386
2418
  doc.sections.push(section);
2387
2419
  matching.push(section);
@@ -2413,11 +2445,21 @@ function removeSection(doc, name) {
2413
2445
  var DEFAULT_PROFILE = "default";
2414
2446
  var DEFAULT_ENDPOINT = "https://www.sim.ai";
2415
2447
  var OUTPUT_FORMATS = ["table", "json", "yaml", "text"];
2448
+ var FORBIDDEN_IN_VALUE_GLOBAL = new RegExp(FORBIDDEN_IN_VALUE.source, "g");
2449
+ function redact(value) {
2450
+ return value.replace(FORBIDDEN_IN_VALUE_GLOBAL, " ");
2451
+ }
2416
2452
 
2417
- class ProfileConfigError extends Error {
2453
+ class ProfileOverrideError extends ProfileConfigError {
2418
2454
  constructor(message) {
2419
2455
  super(message);
2420
- this.name = "ProfileConfigError";
2456
+ this.name = "ProfileOverrideError";
2457
+ }
2458
+ }
2459
+ var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
2460
+ function validateProfileName(name) {
2461
+ if (!PROFILE_NAME_PATTERN.test(name)) {
2462
+ throw new ProfileConfigError(`Invalid profile name "${redact(name)}". Use letters, numbers, dots, underscores, or hyphens, starting with a letter or number.`);
2421
2463
  }
2422
2464
  }
2423
2465
  function configSectionName(profile) {
@@ -2446,24 +2488,24 @@ function resolveAuthenticationProfileName(profile) {
2446
2488
  return profile;
2447
2489
  const authProfile = config.auth_profile.trim();
2448
2490
  if (!authProfile) {
2449
- throw new ProfileConfigError(`Profile "${profile}" has an empty auth_profile.`);
2491
+ throw new ProfileConfigError(`Profile "${redact(profile)}" has an empty auth_profile.`);
2450
2492
  }
2451
2493
  if (authProfile === profile) {
2452
- throw new ProfileConfigError(`Profile "${profile}" cannot use itself as auth_profile. Remove the auth_profile setting instead.`);
2494
+ throw new ProfileConfigError(`Profile "${redact(profile)}" cannot use itself as auth_profile. Remove the auth_profile setting instead.`);
2453
2495
  }
2454
2496
  if (Object.hasOwn(config, "endpoint")) {
2455
- throw new ProfileConfigError(`Profile "${profile}" cannot set both auth_profile and endpoint. Set the endpoint on authentication profile "${authProfile}".`);
2497
+ throw new ProfileConfigError(`Profile "${redact(profile)}" cannot set both auth_profile and endpoint. Set the endpoint on authentication profile "${redact(authProfile)}".`);
2456
2498
  }
2457
2499
  if (readCredentialsProfile(profile).api_key) {
2458
- throw new ProfileConfigError(`Profile "${profile}" cannot set both auth_profile and its own API key. Remove one of them.`);
2500
+ throw new ProfileConfigError(`Profile "${redact(profile)}" cannot set both auth_profile and its own API key. Remove one of them.`);
2459
2501
  }
2460
2502
  const authConfig = readConfigProfile(authProfile);
2461
2503
  const credentials = readCredentialsProfile(authProfile);
2462
2504
  if (Object.keys(authConfig).length === 0 && Object.keys(credentials).length === 0) {
2463
- throw new ProfileConfigError(`Profile "${profile}" references missing auth_profile "${authProfile}".`);
2505
+ throw new ProfileConfigError(`Profile "${redact(profile)}" references missing auth_profile "${redact(authProfile)}".`);
2464
2506
  }
2465
2507
  if (Object.hasOwn(authConfig, "auth_profile")) {
2466
- throw new ProfileConfigError(`Profile "${profile}" references auth_profile "${authProfile}", which also has auth_profile set. Authentication profile references cannot be chained.`);
2508
+ throw new ProfileConfigError(`Profile "${redact(profile)}" references auth_profile "${redact(authProfile)}", which also has auth_profile set. Authentication profile references cannot be chained.`);
2467
2509
  }
2468
2510
  return authProfile;
2469
2511
  }
@@ -2473,7 +2515,7 @@ function listProfiles() {
2473
2515
  if (section === DEFAULT_PROFILE)
2474
2516
  names.add(DEFAULT_PROFILE);
2475
2517
  else if (section.startsWith("profile "))
2476
- names.add(section.slice("profile ".length).trim());
2518
+ names.add(section.slice("profile ".length));
2477
2519
  }
2478
2520
  for (const section of listSections(readIni(credentialsPath()))) {
2479
2521
  names.add(section);
@@ -2511,10 +2553,10 @@ function requireKnownProfile(name) {
2511
2553
  if (known.includes(name))
2512
2554
  return;
2513
2555
  if (known.length === 0) {
2514
- throw new ProfileConfigError(`Unknown profile "${name}". No profiles are configured yet. Run: sim login --profile ${name}`);
2556
+ throw new ProfileConfigError(`Unknown profile "${redact(name)}". No profiles are configured yet. Run: sim login --profile ${redact(name)}`);
2515
2557
  }
2516
2558
  const suggestion = nearestProfile(name, known);
2517
- throw new ProfileConfigError(`Unknown profile "${name}".${suggestion ? ` Did you mean "${suggestion}"?` : ""} Configured profiles: ${known.join(", ")}.`);
2559
+ throw new ProfileConfigError(`Unknown profile "${redact(name)}".${suggestion ? ` Did you mean "${redact(suggestion)}"?` : ""} Configured profiles: ${known.map(redact).join(", ")}.`);
2518
2560
  }
2519
2561
  function listAuthenticationDependents(authProfile) {
2520
2562
  return listProfiles().filter((profile) => profile !== authProfile && readConfigProfile(profile).auth_profile?.trim() === authProfile);
@@ -2541,18 +2583,31 @@ function deleteProfile(profile) {
2541
2583
  return { config, credentials };
2542
2584
  }
2543
2585
  function normalizeEndpoint(endpoint, source) {
2544
- const trimmed = endpoint.replace(/\/+$/, "");
2586
+ const trimmed = endpoint.trim().replace(/\/+$/, "");
2587
+ if (FORBIDDEN_IN_VALUE.test(trimmed)) {
2588
+ throw new ProfileConfigError(`Invalid endpoint "${redact(endpoint)}" from ${source}. An endpoint cannot contain line breaks or control characters.`);
2589
+ }
2545
2590
  let parsed;
2546
2591
  try {
2547
2592
  parsed = new URL(trimmed);
2548
2593
  } catch {
2549
- throw new ProfileConfigError(`Invalid endpoint "${endpoint}" from ${source}. Use an absolute URL, e.g. ${DEFAULT_ENDPOINT} or http://localhost:3000`);
2594
+ throw new ProfileConfigError(`Invalid endpoint "${redact(endpoint)}" from ${source}. Use an absolute URL, e.g. ${DEFAULT_ENDPOINT} or http://localhost:3000`);
2550
2595
  }
2551
2596
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2552
2597
  throw new ProfileConfigError(`Unsupported endpoint scheme "${parsed.protocol.replace(/:$/, "")}" from ${source}. Use http or https, e.g. ${DEFAULT_ENDPOINT}`);
2553
2598
  }
2554
2599
  return trimmed;
2555
2600
  }
2601
+ function normalizeWorkspaceId(workspaceId, source) {
2602
+ const trimmed = workspaceId.trim();
2603
+ if (!trimmed) {
2604
+ throw new ProfileConfigError(`Empty workspace id from ${source}.`);
2605
+ }
2606
+ if (FORBIDDEN_IN_VALUE.test(trimmed)) {
2607
+ throw new ProfileConfigError(`Invalid workspace id "${redact(trimmed)}" from ${source}. A workspace id cannot contain line breaks or control characters.`);
2608
+ }
2609
+ return trimmed;
2610
+ }
2556
2611
  function resolve(candidates, fallback, fallbackSource) {
2557
2612
  for (const [source, value] of candidates) {
2558
2613
  if (value !== null && value !== undefined && value !== "")
@@ -2560,11 +2615,28 @@ function resolve(candidates, fallback, fallbackSource) {
2560
2615
  }
2561
2616
  return { value: fallback, source: fallbackSource };
2562
2617
  }
2618
+ var OVERRIDE_FLAGS = {
2619
+ profile: "--profile",
2620
+ endpoint: "--endpoint",
2621
+ workspaceId: "--workspace"
2622
+ };
2623
+ function refuseBlankOverrides(overrides) {
2624
+ for (const [key, flag] of Object.entries(OVERRIDE_FLAGS)) {
2625
+ const value = overrides[key];
2626
+ if (value !== undefined && value.trim() === "") {
2627
+ throw new ProfileOverrideError(`${flag} requires a value. Omit the flag to fall back to what is configured.`);
2628
+ }
2629
+ }
2630
+ }
2563
2631
  function resolveProfile(overrides = {}) {
2632
+ refuseBlankOverrides(overrides);
2564
2633
  const named = overrides.profile || process.env.SIM_PROFILE;
2565
2634
  const name = named || DEFAULT_PROFILE;
2566
2635
  if (named && !overrides.allowUnknownProfile)
2567
2636
  requireKnownProfile(named);
2637
+ if (named && overrides.allowUnknownProfile && !listProfiles().includes(named)) {
2638
+ validateProfileName(named);
2639
+ }
2568
2640
  const config = readConfigProfile(name);
2569
2641
  const authProfile = resolveAuthenticationProfileName(name);
2570
2642
  const authConfig = authProfile === name ? config : readConfigProfile(authProfile);
@@ -2590,7 +2662,7 @@ function resolveProfile(overrides = {}) {
2590
2662
  ["config", config.output]
2591
2663
  ], "table", "default");
2592
2664
  if (!OUTPUT_FORMATS.includes(output.value)) {
2593
- throw new ProfileConfigError(`Unknown output format "${output.value}" from ${output.source}. Use one of: ${OUTPUT_FORMATS.join(", ")}`);
2665
+ throw new ProfileConfigError(`Unknown output format "${redact(String(output.value))}" from ${output.source}. Use one of: ${OUTPUT_FORMATS.join(", ")}`);
2594
2666
  }
2595
2667
  return {
2596
2668
  name,
@@ -2794,6 +2866,10 @@ function traceRequest(method, url, status, startedAt) {
2794
2866
  process.stderr.write(`${source_default.dim(`[sim] ${method} ${url} → ${status} ${Math.round(performance.now() - startedAt)}ms`)}
2795
2867
  `);
2796
2868
  }
2869
+ function withoutLeadingLabel(message, label) {
2870
+ const prefix = `${label}: `;
2871
+ return message.startsWith(prefix) ? message.slice(prefix.length) : message;
2872
+ }
2797
2873
  function formatApiErrorDetails(details) {
2798
2874
  const issues = [];
2799
2875
  const seen = new Set;
@@ -2831,7 +2907,10 @@ function formatApiErrorDetails(details) {
2831
2907
  const visible = kept.slice(0, 8);
2832
2908
  const lines = [
2833
2909
  " details:",
2834
- ...visible.map((issue) => ` ${issue.path.length > 0 ? issue.path.join(".") : "request"}: ${issue.message}`)
2910
+ ...visible.map((issue) => {
2911
+ const label = issue.path.length > 0 ? issue.path.join(".") : "request";
2912
+ return ` ${label}: ${withoutLeadingLabel(issue.message, label)}`;
2913
+ })
2835
2914
  ];
2836
2915
  if (kept.length > visible.length)
2837
2916
  lines.push(` … ${kept.length - visible.length} more issues`);
@@ -2969,10 +3048,13 @@ function pageProgress() {
2969
3048
  };
2970
3049
  }
2971
3050
  async function requestAllPages(client, path, options) {
3051
+ return (await requestPages(client, path, options)).items;
3052
+ }
3053
+ async function requestPages(client, path, options) {
2972
3054
  const { query, pageSize, limit: requestedLimit, ...requestOptions } = options;
2973
3055
  const limit = requestedLimit ?? Number.POSITIVE_INFINITY;
2974
3056
  if (limit <= 0)
2975
- return [];
3057
+ return { items: [], truncated: false };
2976
3058
  const items = [];
2977
3059
  const progress = pageProgress();
2978
3060
  let cursor = null;
@@ -2994,7 +3076,7 @@ async function requestAllPages(client, path, options) {
2994
3076
  } finally {
2995
3077
  progress.finish();
2996
3078
  }
2997
- return items.slice(0, limit);
3079
+ return { items: items.slice(0, limit), truncated: cursor !== null || items.length > limit };
2998
3080
  }
2999
3081
  function resolvePath(template, params = {}) {
3000
3082
  return template.replace(/\[([^\]]+)\]/g, (_match, key) => {
@@ -6518,7 +6600,8 @@ var V2_OPERATIONS = {
6518
6600
  version: "Numeric deployment version."
6519
6601
  },
6520
6602
  responseMode: "json",
6521
- summary: "Activate Workflow Version"
6603
+ summary: "Activate Workflow Version",
6604
+ personalKeyOnly: true
6522
6605
  },
6523
6606
  addTableColumn: {
6524
6607
  method: "POST",
@@ -6565,6 +6648,7 @@ var V2_OPERATIONS = {
6565
6648
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6566
6649
  responseMode: "json",
6567
6650
  summary: "Index Workspace Files",
6651
+ personalKeyOnly: true,
6568
6652
  body: {
6569
6653
  workspaceId: {
6570
6654
  kind: "string",
@@ -6585,6 +6669,7 @@ var V2_OPERATIONS = {
6585
6669
  pathParamDocs: { workflowId: "Unique workflow identifier." },
6586
6670
  responseMode: "json",
6587
6671
  summary: "Apply Workflow Operations",
6672
+ personalKeyOnly: true,
6588
6673
  query: {
6589
6674
  dryRun: {
6590
6675
  kind: "boolean",
@@ -6684,6 +6769,7 @@ var V2_OPERATIONS = {
6684
6769
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6685
6770
  responseMode: "json",
6686
6771
  summary: "Bulk Save Tag Definitions",
6772
+ personalKeyOnly: true,
6687
6773
  body: {
6688
6774
  workspaceId: {
6689
6775
  kind: "string",
@@ -6707,6 +6793,7 @@ var V2_OPERATIONS = {
6707
6793
  },
6708
6794
  responseMode: "json",
6709
6795
  summary: "Bulk Update Chunks",
6796
+ personalKeyOnly: true,
6710
6797
  body: {
6711
6798
  workspaceId: {
6712
6799
  kind: "string",
@@ -6722,7 +6809,7 @@ var V2_OPERATIONS = {
6722
6809
  chunkIds: {
6723
6810
  kind: "array",
6724
6811
  required: true,
6725
- describe: "Chunks to operate on, by identifier. Ids outside the document are ignored."
6812
+ describe: "Chunks to operate on, by identifier. An id naming no chunk in the document is reported in errors and does not fail the request."
6726
6813
  }
6727
6814
  }
6728
6815
  },
@@ -6733,6 +6820,7 @@ var V2_OPERATIONS = {
6733
6820
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6734
6821
  responseMode: "json",
6735
6822
  summary: "Bulk Enable or Disable Documents",
6823
+ personalKeyOnly: true,
6736
6824
  body: {
6737
6825
  workspaceId: {
6738
6826
  kind: "string",
@@ -6957,6 +7045,7 @@ var V2_OPERATIONS = {
6957
7045
  pathParams: [],
6958
7046
  responseMode: "json",
6959
7047
  summary: "Create Credential Connection",
7048
+ personalKeyOnly: true,
6960
7049
  body: {
6961
7050
  workspaceId: {
6962
7051
  kind: "string",
@@ -7132,6 +7221,7 @@ var V2_OPERATIONS = {
7132
7221
  },
7133
7222
  responseMode: "json",
7134
7223
  summary: "Create Chunk",
7224
+ personalKeyOnly: true,
7135
7225
  body: {
7136
7226
  workspaceId: {
7137
7227
  kind: "string",
@@ -7157,6 +7247,7 @@ var V2_OPERATIONS = {
7157
7247
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7158
7248
  responseMode: "json",
7159
7249
  summary: "Create Knowledge Connector",
7250
+ personalKeyOnly: true,
7160
7251
  body: {
7161
7252
  workspaceId: {
7162
7253
  kind: "string",
@@ -7272,6 +7363,7 @@ var V2_OPERATIONS = {
7272
7363
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7273
7364
  responseMode: "json",
7274
7365
  summary: "Create Tag",
7366
+ personalKeyOnly: true,
7275
7367
  body: {
7276
7368
  workspaceId: {
7277
7369
  kind: "string",
@@ -7379,6 +7471,7 @@ var V2_OPERATIONS = {
7379
7471
  pathParams: [],
7380
7472
  responseMode: "json",
7381
7473
  summary: "Create Service-Account Credential",
7474
+ personalKeyOnly: true,
7382
7475
  body: {
7383
7476
  workspaceId: {
7384
7477
  kind: "string",
@@ -7417,6 +7510,7 @@ var V2_OPERATIONS = {
7417
7510
  pathParams: [],
7418
7511
  responseMode: "json",
7419
7512
  summary: "Create Skill",
7513
+ personalKeyOnly: true,
7420
7514
  body: {
7421
7515
  workspaceId: {
7422
7516
  kind: "string",
@@ -7632,6 +7726,7 @@ var V2_OPERATIONS = {
7632
7726
  pathParams: [],
7633
7727
  responseMode: "json",
7634
7728
  summary: "Create Workflow MCP Server",
7729
+ personalKeyOnly: true,
7635
7730
  body: {
7636
7731
  workspaceId: {
7637
7732
  kind: "string",
@@ -7662,6 +7757,7 @@ var V2_OPERATIONS = {
7662
7757
  pathParamDocs: { credentialId: "Credential to disconnect." },
7663
7758
  responseMode: "json",
7664
7759
  summary: "Disconnect Credential",
7760
+ personalKeyOnly: true,
7665
7761
  query: {
7666
7762
  workspaceId: {
7667
7763
  kind: "string",
@@ -7752,6 +7848,7 @@ var V2_OPERATIONS = {
7752
7848
  },
7753
7849
  responseMode: "json",
7754
7850
  summary: "Delete Chunk",
7851
+ personalKeyOnly: true,
7755
7852
  query: {
7756
7853
  workspaceId: {
7757
7854
  kind: "string",
@@ -7770,6 +7867,7 @@ var V2_OPERATIONS = {
7770
7867
  },
7771
7868
  responseMode: "json",
7772
7869
  summary: "Delete Knowledge Connector",
7870
+ personalKeyOnly: true,
7773
7871
  query: {
7774
7872
  workspaceId: {
7775
7873
  kind: "string",
@@ -7840,6 +7938,7 @@ var V2_OPERATIONS = {
7840
7938
  },
7841
7939
  responseMode: "json",
7842
7940
  summary: "Delete Tag",
7941
+ personalKeyOnly: true,
7843
7942
  query: {
7844
7943
  workspaceId: {
7845
7944
  kind: "string",
@@ -7855,6 +7954,7 @@ var V2_OPERATIONS = {
7855
7954
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7856
7955
  responseMode: "json",
7857
7956
  summary: "Delete Tag Definitions",
7957
+ personalKeyOnly: true,
7858
7958
  query: {
7859
7959
  workspaceId: {
7860
7960
  kind: "string",
@@ -7886,9 +7986,10 @@ var V2_OPERATIONS = {
7886
7986
  method: "DELETE",
7887
7987
  path: "/api/v2/secrets/[name]",
7888
7988
  pathParams: ["name"],
7889
- pathParamDocs: { name: "Secret to create, replace, or delete." },
7989
+ pathParamDocs: { name: "Secret to delete." },
7890
7990
  responseMode: "json",
7891
7991
  summary: "Delete Secret",
7992
+ personalKeyOnly: true,
7892
7993
  query: {
7893
7994
  workspaceId: {
7894
7995
  kind: "string",
@@ -7912,6 +8013,7 @@ var V2_OPERATIONS = {
7912
8013
  },
7913
8014
  responseMode: "json",
7914
8015
  summary: "Delete Skill",
8016
+ personalKeyOnly: true,
7915
8017
  query: {
7916
8018
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." }
7917
8019
  }
@@ -8022,7 +8124,8 @@ var V2_OPERATIONS = {
8022
8124
  pathParams: ["workflowId"],
8023
8125
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8024
8126
  responseMode: "json",
8025
- summary: "Delete Workflow Chat Deployment"
8127
+ summary: "Delete Workflow Chat Deployment",
8128
+ personalKeyOnly: true
8026
8129
  },
8027
8130
  deleteWorkflowFolder: {
8028
8131
  method: "DELETE",
@@ -8072,7 +8175,8 @@ var V2_OPERATIONS = {
8072
8175
  pathParams: ["serverId"],
8073
8176
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8074
8177
  responseMode: "json",
8075
- summary: "Delete Workflow MCP Server"
8178
+ summary: "Delete Workflow MCP Server",
8179
+ personalKeyOnly: true
8076
8180
  },
8077
8181
  deployWorkflow: {
8078
8182
  method: "POST",
@@ -8081,6 +8185,7 @@ var V2_OPERATIONS = {
8081
8185
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8082
8186
  responseMode: "json",
8083
8187
  summary: "Deploy Workflow",
8188
+ personalKeyOnly: true,
8084
8189
  body: {
8085
8190
  name: { kind: "string", describe: "Optional label for the deployment version." },
8086
8191
  description: {
@@ -8096,6 +8201,7 @@ var V2_OPERATIONS = {
8096
8201
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8097
8202
  responseMode: "json",
8098
8203
  summary: "Publish Workflow As MCP Tool",
8204
+ personalKeyOnly: true,
8099
8205
  body: {
8100
8206
  workflowId: {
8101
8207
  kind: "string",
@@ -8236,6 +8342,7 @@ var V2_OPERATIONS = {
8236
8342
  pathParamDocs: { auditLogId: "Audit-log entry identifier." },
8237
8343
  responseMode: "json",
8238
8344
  summary: "Get Audit Log",
8345
+ personalKeyOnly: true,
8239
8346
  query: {
8240
8347
  organizationId: {
8241
8348
  kind: "string",
@@ -8301,7 +8408,7 @@ var V2_OPERATIONS = {
8301
8408
  kind: "enum",
8302
8409
  values: ["active", "archived"],
8303
8410
  default: "active",
8304
- describe: "Which lifecycle set to read from: `active` (default) resolves live files only and returns `404` for a file a `DELETE` soft-deleted; `archived` also resolves soft-deleted files, so metadata stays readable before `POST /files/{fileId}/restore`. Authorization is identical for both."
8411
+ describe: "Which lifecycle set to read from: `active` (default) resolves live files only and returns `404` for a file a delete soft-deleted; `archived` also resolves soft-deleted files, so metadata stays readable before the file is restored. Authorization is identical for both."
8305
8412
  }
8306
8413
  }
8307
8414
  },
@@ -8364,6 +8471,7 @@ var V2_OPERATIONS = {
8364
8471
  },
8365
8472
  responseMode: "json",
8366
8473
  summary: "Get Chunk",
8474
+ personalKeyOnly: true,
8367
8475
  query: {
8368
8476
  workspaceId: {
8369
8477
  kind: "string",
@@ -8382,6 +8490,7 @@ var V2_OPERATIONS = {
8382
8490
  },
8383
8491
  responseMode: "json",
8384
8492
  summary: "Get Knowledge Connector",
8493
+ personalKeyOnly: true,
8385
8494
  query: {
8386
8495
  workspaceId: {
8387
8496
  kind: "string",
@@ -8489,6 +8598,7 @@ var V2_OPERATIONS = {
8489
8598
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
8490
8599
  responseMode: "json",
8491
8600
  summary: "Get Next Tag Slot",
8601
+ personalKeyOnly: true,
8492
8602
  query: {
8493
8603
  workspaceId: {
8494
8604
  kind: "string",
@@ -8656,7 +8766,8 @@ var V2_OPERATIONS = {
8656
8766
  pathParams: ["workflowId"],
8657
8767
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8658
8768
  responseMode: "json",
8659
- summary: "Get Workflow Chat Deployment"
8769
+ summary: "Get Workflow Chat Deployment",
8770
+ personalKeyOnly: true
8660
8771
  },
8661
8772
  getWorkflowDeployment: {
8662
8773
  method: "GET",
@@ -8672,7 +8783,8 @@ var V2_OPERATIONS = {
8672
8783
  pathParams: ["serverId"],
8673
8784
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8674
8785
  responseMode: "json",
8675
- summary: "Get Workflow MCP Server"
8786
+ summary: "Get Workflow MCP Server",
8787
+ personalKeyOnly: true
8676
8788
  },
8677
8789
  getWorkflowRun: {
8678
8790
  method: "GET",
@@ -8739,6 +8851,7 @@ var V2_OPERATIONS = {
8739
8851
  },
8740
8852
  responseMode: "json",
8741
8853
  summary: "Grant Skill Editor",
8854
+ personalKeyOnly: true,
8742
8855
  body: {
8743
8856
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
8744
8857
  email: {
@@ -8779,6 +8892,7 @@ var V2_OPERATIONS = {
8779
8892
  pathParams: [],
8780
8893
  responseMode: "json",
8781
8894
  summary: "List Audit Logs",
8895
+ personalKeyOnly: true,
8782
8896
  query: {
8783
8897
  action: { kind: "string", describe: "Filter by exact action name." },
8784
8898
  resourceType: {
@@ -9116,7 +9230,7 @@ var V2_OPERATIONS = {
9116
9230
  kind: "enum",
9117
9231
  values: ["active", "archived"],
9118
9232
  default: "active",
9119
- describe: "Which lifecycle set to list: `active` (default) returns live folders only; `archived` returns folders a recursive `DELETE` soft-deleted, which is how a caller finds a path to hand to `POST /api/v2/files/folders/restore`. Authorization is identical for both."
9233
+ describe: "Which lifecycle set to list: `active` (default) returns live folders only; `archived` returns folders a recursive delete soft-deleted, which is how a caller finds a path to hand to the folder restore. Authorization is identical for both."
9120
9234
  }
9121
9235
  }
9122
9236
  },
@@ -9158,7 +9272,7 @@ var V2_OPERATIONS = {
9158
9272
  kind: "enum",
9159
9273
  values: ["active", "archived"],
9160
9274
  default: "active",
9161
- describe: "Which lifecycle set to list: `active` (default) for live files, `archived` for files a `DELETE` soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too."
9275
+ describe: "Which lifecycle set to list: `active` (default) for live files, `archived` for files a delete soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too."
9162
9276
  },
9163
9277
  search: {
9164
9278
  kind: "string",
@@ -9246,6 +9360,7 @@ var V2_OPERATIONS = {
9246
9360
  },
9247
9361
  responseMode: "json",
9248
9362
  summary: "List Chunks",
9363
+ personalKeyOnly: true,
9249
9364
  query: {
9250
9365
  workspaceId: {
9251
9366
  kind: "string",
@@ -9295,6 +9410,7 @@ var V2_OPERATIONS = {
9295
9410
  },
9296
9411
  responseMode: "json",
9297
9412
  summary: "List Knowledge Connector Documents",
9413
+ personalKeyOnly: true,
9298
9414
  query: {
9299
9415
  workspaceId: {
9300
9416
  kind: "string",
@@ -9323,6 +9439,7 @@ var V2_OPERATIONS = {
9323
9439
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9324
9440
  responseMode: "json",
9325
9441
  summary: "List Knowledge Connectors",
9442
+ personalKeyOnly: true,
9326
9443
  query: {
9327
9444
  workspaceId: {
9328
9445
  kind: "string",
@@ -9466,6 +9583,7 @@ var V2_OPERATIONS = {
9466
9583
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9467
9584
  responseMode: "json",
9468
9585
  summary: "List Tag Usage",
9586
+ personalKeyOnly: true,
9469
9587
  query: {
9470
9588
  workspaceId: {
9471
9589
  kind: "string",
@@ -9557,14 +9675,14 @@ var V2_OPERATIONS = {
9557
9675
  },
9558
9676
  includeJobRuns: {
9559
9677
  kind: "boolean",
9560
- describe: 'Whether Chat and Sim-agent job runs join the sequence alongside workflow runs. Job runs report `kind: "job"`, carry no `workflow` summary, and never carry a cost ledger. They are dropped entirely — not partially matched — whenever a filter they cannot answer is set (`workflowIds`, `workflowName`, `folderPaths`, `model`, or `status`), so a filter never means two different things across the union. Accepted only under `sortBy=startedAt`: job runs record cost as a document and no comparable status, so they cannot participate in the other orderings.'
9678
+ describe: 'Whether Chat and Sim-agent job runs join the sequence alongside workflow runs. Job runs report `kind: "job"`, carry no `workflow` summary, and never carry a cost ledger. They are dropped entirely — not partially matched — whenever a filter they cannot answer is set: by workflow, workflow name, folder, model, or status. A filter therefore never means two different things across the union. Accepted only when sorting by `startedAt`: job runs record cost as a document and no comparable status, so they cannot participate in the other orderings.'
9561
9679
  },
9562
9680
  runId: { kind: "string", describe: "Exact run identifier to match." },
9563
9681
  sortBy: {
9564
9682
  kind: "enum",
9565
9683
  values: ["startedAt", "durationMs", "cost", "status"],
9566
9684
  default: "startedAt",
9567
- describe: "Field used to sort the result. `durationMs` and `cost` are null until a run settles; those runs order as though the value were below every recorded one, so they trail an ascending page and lead a descending one. Only `startedAt` can order Chat and Sim-agent job runs, so any other value is rejected together with `includeJobRuns=true`."
9685
+ describe: "Field used to sort the result. `durationMs` and `cost` are null until a run settles; those runs order as though the value were below every recorded one, so they trail an ascending page and lead a descending one. Only `startedAt` can order Chat and Sim-agent job runs, so any other value is rejected when job runs are included."
9568
9686
  },
9569
9687
  sortOrder: {
9570
9688
  kind: "enum",
@@ -9624,6 +9742,7 @@ var V2_OPERATIONS = {
9624
9742
  pathParamDocs: { mcpServerId: "Unique MCP server identifier." },
9625
9743
  responseMode: "json",
9626
9744
  summary: "List MCP Server Tools",
9745
+ personalKeyOnly: true,
9627
9746
  query: {
9628
9747
  workspaceId: {
9629
9748
  kind: "string",
@@ -9642,6 +9761,7 @@ var V2_OPERATIONS = {
9642
9761
  pathParams: [],
9643
9762
  responseMode: "json",
9644
9763
  summary: "List Secrets",
9764
+ personalKeyOnly: true,
9645
9765
  query: {
9646
9766
  workspaceId: {
9647
9767
  kind: "string",
@@ -9834,7 +9954,7 @@ var V2_OPERATIONS = {
9834
9954
  kind: "enum",
9835
9955
  values: ["active", "archived"],
9836
9956
  default: "active",
9837
- describe: "Which lifecycle set to list: `active` (default) for live tables, `archived` for tables a `DELETE` archived and `POST /tables/{tableId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too."
9957
+ describe: "Which lifecycle set to list: `active` (default) for live tables, `archived` for tables a delete archived and a table restore can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too."
9838
9958
  },
9839
9959
  folderPath: {
9840
9960
  kind: "string",
@@ -9977,6 +10097,7 @@ var V2_OPERATIONS = {
9977
10097
  pathParams: [],
9978
10098
  responseMode: "json",
9979
10099
  summary: "List Workflow MCP Servers",
10100
+ personalKeyOnly: true,
9980
10101
  query: {
9981
10102
  workspaceId: {
9982
10103
  kind: "string",
@@ -10012,7 +10133,8 @@ var V2_OPERATIONS = {
10012
10133
  pathParams: ["serverId"],
10013
10134
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
10014
10135
  responseMode: "json",
10015
- summary: "List Workflow MCP Tools"
10136
+ summary: "List Workflow MCP Tools",
10137
+ personalKeyOnly: true
10016
10138
  },
10017
10139
  listWorkflowRuns: {
10018
10140
  method: "GET",
@@ -10069,7 +10191,7 @@ var V2_OPERATIONS = {
10069
10191
  kind: "enum",
10070
10192
  values: ["active", "archived"],
10071
10193
  default: "active",
10072
- describe: "Which lifecycle set to list: `active` (default) for live workflows, `archived` for workflows a `DELETE` archived. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too."
10194
+ describe: "Which lifecycle set to list: `active` (default) for live workflows, `archived` for workflows a `DELETE` archived. The folder filter resolves against active folders only, so pairing it with `archived` returns an empty page when the containing folder was archived too."
10073
10195
  },
10074
10196
  folderPath: {
10075
10197
  kind: "string",
@@ -10372,6 +10494,7 @@ var V2_OPERATIONS = {
10372
10494
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10373
10495
  responseMode: "json",
10374
10496
  summary: "Create or Replace Workflow Chat Deployment",
10497
+ personalKeyOnly: true,
10375
10498
  body: {
10376
10499
  identifier: {
10377
10500
  kind: "string",
@@ -10424,6 +10547,7 @@ var V2_OPERATIONS = {
10424
10547
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10425
10548
  responseMode: "json",
10426
10549
  summary: "Replace Workflow State",
10550
+ personalKeyOnly: true,
10427
10551
  query: {
10428
10552
  dryRun: {
10429
10553
  kind: "boolean",
@@ -10477,7 +10601,7 @@ var V2_OPERATIONS = {
10477
10601
  path: {
10478
10602
  kind: "string",
10479
10603
  required: true,
10480
- describe: "Path of the archived folder to restore, as reported by `GET /api/v2/files/folders?scope=archived`."
10604
+ describe: "Path of the archived folder to restore, as reported by an archived-scope folder list."
10481
10605
  }
10482
10606
  }
10483
10607
  },
@@ -10522,7 +10646,7 @@ var V2_OPERATIONS = {
10522
10646
  path: {
10523
10647
  kind: "string",
10524
10648
  required: true,
10525
- describe: "Path the folder held when `DELETE /api/v2/tables/folders` archived it."
10649
+ describe: "Path the folder held when a folder delete archived it."
10526
10650
  }
10527
10651
  }
10528
10652
  },
@@ -10562,7 +10686,8 @@ var V2_OPERATIONS = {
10562
10686
  version: "Numeric deployment version, or `active` for the currently live version."
10563
10687
  },
10564
10688
  responseMode: "json",
10565
- summary: "Revert Workflow To Version"
10689
+ summary: "Revert Workflow To Version",
10690
+ personalKeyOnly: true
10566
10691
  },
10567
10692
  revokeSkillEditor: {
10568
10693
  method: "DELETE",
@@ -10573,6 +10698,7 @@ var V2_OPERATIONS = {
10573
10698
  },
10574
10699
  responseMode: "json",
10575
10700
  summary: "Revoke Skill Editor",
10701
+ personalKeyOnly: true,
10576
10702
  query: {
10577
10703
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
10578
10704
  email: {
@@ -10589,6 +10715,7 @@ var V2_OPERATIONS = {
10589
10715
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10590
10716
  responseMode: "json",
10591
10717
  summary: "Rollback Workflow",
10718
+ personalKeyOnly: true,
10592
10719
  body: {
10593
10720
  version: {
10594
10721
  kind: "integer",
@@ -10683,9 +10810,10 @@ var V2_OPERATIONS = {
10683
10810
  method: "PUT",
10684
10811
  path: "/api/v2/secrets/[name]",
10685
10812
  pathParams: ["name"],
10686
- pathParamDocs: { name: "Secret to create, replace, or delete." },
10813
+ pathParamDocs: { name: "Secret to create or replace." },
10687
10814
  responseMode: "json",
10688
10815
  summary: "Set Secret",
10816
+ personalKeyOnly: true,
10689
10817
  body: {
10690
10818
  workspaceId: {
10691
10819
  kind: "string",
@@ -10700,8 +10828,7 @@ var V2_OPERATIONS = {
10700
10828
  },
10701
10829
  value: {
10702
10830
  kind: "string",
10703
- required: true,
10704
- describe: "Write-only secret value. It is never returned."
10831
+ describe: "Write-only secret value. It is never returned. Omit it on a workspace secret to change description or unredacted alone, leaving the stored value untouched; the secret must already exist. Always required for a personal secret, which carries no other writable field."
10705
10832
  },
10706
10833
  description: {
10707
10834
  kind: "string",
@@ -10723,6 +10850,7 @@ var V2_OPERATIONS = {
10723
10850
  },
10724
10851
  responseMode: "json",
10725
10852
  summary: "Sync Knowledge Connector",
10853
+ personalKeyOnly: true,
10726
10854
  body: {
10727
10855
  workspaceId: {
10728
10856
  kind: "string",
@@ -10760,7 +10888,8 @@ var V2_OPERATIONS = {
10760
10888
  pathParams: ["workflowId"],
10761
10889
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10762
10890
  responseMode: "json",
10763
- summary: "Undeploy Workflow"
10891
+ summary: "Undeploy Workflow",
10892
+ personalKeyOnly: true
10764
10893
  },
10765
10894
  undeployWorkflowMcpTool: {
10766
10895
  method: "DELETE",
@@ -10771,7 +10900,8 @@ var V2_OPERATIONS = {
10771
10900
  workflowId: "Workflow published as a tool on this server."
10772
10901
  },
10773
10902
  responseMode: "json",
10774
- summary: "Unpublish Workflow MCP Tool"
10903
+ summary: "Unpublish Workflow MCP Tool",
10904
+ personalKeyOnly: true
10775
10905
  },
10776
10906
  unzipFile: {
10777
10907
  method: "POST",
@@ -10791,6 +10921,7 @@ var V2_OPERATIONS = {
10791
10921
  pathParamDocs: { credentialId: "Credential to update." },
10792
10922
  responseMode: "json",
10793
10923
  summary: "Update Credential",
10924
+ personalKeyOnly: true,
10794
10925
  query: {
10795
10926
  workspaceId: {
10796
10927
  kind: "string",
@@ -10892,6 +11023,7 @@ var V2_OPERATIONS = {
10892
11023
  },
10893
11024
  responseMode: "json",
10894
11025
  summary: "Update Chunk",
11026
+ personalKeyOnly: true,
10895
11027
  body: {
10896
11028
  workspaceId: {
10897
11029
  kind: "string",
@@ -10918,6 +11050,7 @@ var V2_OPERATIONS = {
10918
11050
  },
10919
11051
  responseMode: "json",
10920
11052
  summary: "Update Knowledge Connector",
11053
+ personalKeyOnly: true,
10921
11054
  body: {
10922
11055
  workspaceId: {
10923
11056
  kind: "string",
@@ -10949,6 +11082,7 @@ var V2_OPERATIONS = {
10949
11082
  },
10950
11083
  responseMode: "json",
10951
11084
  summary: "Update Knowledge Connector Documents",
11085
+ personalKeyOnly: true,
10952
11086
  body: {
10953
11087
  workspaceId: {
10954
11088
  kind: "string",
@@ -10978,6 +11112,7 @@ var V2_OPERATIONS = {
10978
11112
  },
10979
11113
  responseMode: "json",
10980
11114
  summary: "Update Document",
11115
+ personalKeyOnly: true,
10981
11116
  body: {
10982
11117
  workspaceId: {
10983
11118
  kind: "string",
@@ -11022,6 +11157,7 @@ var V2_OPERATIONS = {
11022
11157
  },
11023
11158
  responseMode: "json",
11024
11159
  summary: "Update Tag",
11160
+ personalKeyOnly: true,
11025
11161
  body: {
11026
11162
  workspaceId: {
11027
11163
  kind: "string",
@@ -11126,6 +11262,7 @@ var V2_OPERATIONS = {
11126
11262
  },
11127
11263
  responseMode: "json",
11128
11264
  summary: "Update Skill",
11265
+ personalKeyOnly: true,
11129
11266
  body: {
11130
11267
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
11131
11268
  name: { kind: "string", describe: "New kebab-case skill name." },
@@ -11263,6 +11400,7 @@ var V2_OPERATIONS = {
11263
11400
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
11264
11401
  responseMode: "json",
11265
11402
  summary: "Update Workflow MCP Server",
11403
+ personalKeyOnly: true,
11266
11404
  body: {
11267
11405
  name: { kind: "string", describe: "Server display name, shown to connecting MCP clients." },
11268
11406
  description: { kind: "string", describe: "New server description, or null to clear it." },
@@ -11279,6 +11417,7 @@ var V2_OPERATIONS = {
11279
11417
  pathParamDocs: { workflowId: "Unique workflow identifier." },
11280
11418
  responseMode: "json",
11281
11419
  summary: "Update Workflow Public API Access",
11420
+ personalKeyOnly: true,
11282
11421
  body: {
11283
11422
  isPublicApi: {
11284
11423
  kind: "boolean",
@@ -11327,6 +11466,7 @@ var V2_OPERATIONS = {
11327
11466
  pathParamDocs: { fileId: "File identifier." },
11328
11467
  responseMode: "json",
11329
11468
  summary: "Enable or Disable File Share",
11469
+ personalKeyOnly: true,
11330
11470
  body: {
11331
11471
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the file." },
11332
11472
  isActive: {
@@ -11361,7 +11501,7 @@ var V2_OPERATIONS = {
11361
11501
  data: {
11362
11502
  kind: "object",
11363
11503
  required: true,
11364
- describe: "Complete set of row cells keyed by column name. On the update branch this REPLACES the matched row: any column not present here is cleared, unlike the merging `PATCH /api/v2/tables/{tableId}/rows/{rowId}`."
11504
+ describe: "Complete set of row cells keyed by column name. On the update branch this REPLACES the matched row: any column not present here is cleared, unlike a single-row update, which merges."
11365
11505
  },
11366
11506
  conflictTarget: { kind: "string", describe: "Unique column used to detect a conflict." }
11367
11507
  }
@@ -11369,7 +11509,6 @@ var V2_OPERATIONS = {
11369
11509
  };
11370
11510
 
11371
11511
  // src/commands/auth.ts
11372
- var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
11373
11512
  var MAX_INTERACTIVE_WORKSPACES = 1000;
11374
11513
  function openBrowser(url) {
11375
11514
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : [process.platform === "darwin" ? "open" : "xdg-open", [url]];
@@ -11396,11 +11535,11 @@ function presentAuthentication(source) {
11396
11535
  }
11397
11536
  async function confirmProfileOverwrite(profileName) {
11398
11537
  if (!process.stdin.isTTY) {
11399
- throw new SimApiError(`Profile "${profileName}" already exists. Re-run with --yes to overwrite it.`, 0);
11538
+ throw new SimApiError(`Profile "${redact(profileName)}" already exists. Re-run with --yes to overwrite it.`, 0);
11400
11539
  }
11401
11540
  const prompt = createInterface({ input: process.stdin, output: process.stderr });
11402
11541
  try {
11403
- const answer = await prompt.question(`Profile "${profileName}" already exists. Replace its API key and login defaults? (y/N) `);
11542
+ const answer = await prompt.question(`Profile "${redact(profileName)}" already exists. Replace its API key and login defaults? (y/N) `);
11404
11543
  return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
11405
11544
  } finally {
11406
11545
  prompt.close();
@@ -11410,21 +11549,24 @@ function selectedProfileName(command) {
11410
11549
  return globalsOf(command).profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
11411
11550
  }
11412
11551
  function validateNewProfileName(profileName) {
11413
- if (!PROFILE_NAME_PATTERN.test(profileName)) {
11414
- throw new SimApiError(`Invalid profile name "${profileName}". Use letters, numbers, dots, underscores, or hyphens, starting with a letter or number.`, 0);
11415
- }
11552
+ validateProfileName(profileName);
11416
11553
  if (listProfiles().includes(profileName)) {
11417
- throw new SimApiError(`Profile "${profileName}" already exists. Remove it first with: sim logout --all --profile ${profileName}`, 0);
11554
+ throw new SimApiError(`Profile "${redact(profileName)}" already exists. Remove it first with: sim logout --all --profile ${redact(profileName)}`, 0);
11555
+ }
11556
+ }
11557
+ function requireStorableKey(apiKey) {
11558
+ if (typeof apiKey !== "string" || !apiKey || apiKey !== apiKey.trim() || FORBIDDEN_IN_VALUE.test(apiKey)) {
11559
+ throw new SimApiError("The server returned a malformed API key. Nothing was stored; check the endpoint.", 0);
11418
11560
  }
11419
11561
  }
11420
11562
  function requireStoredAuthentication(profile) {
11421
11563
  const authProfile = resolveAuthenticationProfileName(profile.name);
11422
11564
  const storedKey = readCredentialsProfile(authProfile).api_key;
11423
11565
  if (profile.sources.apiKey !== "credentials" || !storedKey) {
11424
- throw new SimApiError(`Cannot create a shared profile from "${profile.name}": the active API key is not stored. Run: sim login --profile ${authProfile}`, 0);
11566
+ throw new SimApiError(`Cannot create a shared profile from "${redact(profile.name)}": the active API key is not stored. Run: sim login --profile ${redact(authProfile)}`, 0);
11425
11567
  }
11426
11568
  if (profile.sources.endpoint === "flag" || profile.sources.endpoint === "env") {
11427
- throw new SimApiError(`Cannot create a shared profile from "${profile.name}": the active endpoint comes from ${profile.sources.endpoint}. Save it with: sim configure --profile ${authProfile} --set-endpoint ${profile.endpoint}`, 0);
11569
+ throw new SimApiError(`Cannot create a shared profile from "${redact(profile.name)}": the active endpoint comes from ${profile.sources.endpoint}. Save it with: sim configure --profile ${redact(authProfile)} --set-endpoint ${profile.endpoint}`, 0);
11428
11570
  }
11429
11571
  return authProfile;
11430
11572
  }
@@ -11476,12 +11618,12 @@ function addProfileCommand() {
11476
11618
  const workspace = workspaceId ? await getWorkspaceById(client, workspaceId) : await chooseWorkspace(client);
11477
11619
  writeConfigProfile(profileName, {
11478
11620
  auth_profile: authProfile,
11479
- workspace: workspace.id
11621
+ workspace: normalizeWorkspaceId(workspace.id, "the workspace response")
11480
11622
  });
11481
- console.log(source_default.green(`✓ Added profile "${profileName}" in ${configPath()}`));
11623
+ console.log(source_default.green(`✓ Added profile "${safeOneLine(profileName)}" in ${configPath()}`));
11482
11624
  console.log(` Workspace: ${safeOneLine(workspace.name)} (${workspace.id})`);
11483
- console.log(` Authentication: ${authProfile}`);
11484
- console.log(source_default.dim(` Try: sim --profile ${profileName} whoami`));
11625
+ console.log(` Authentication: ${safeOneLine(authProfile)}`);
11626
+ console.log(source_default.dim(` Try: sim --profile ${safeOneLine(profileName)} whoami`));
11485
11627
  });
11486
11628
  }
11487
11629
  function loginCommand() {
@@ -11489,7 +11631,7 @@ function loginCommand() {
11489
11631
  const profile = profileFrom(command, { allowUnknownProfile: true });
11490
11632
  const authProfile = resolveAuthenticationProfileName(profile.name);
11491
11633
  if (authProfile !== profile.name) {
11492
- throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Run: sim login --profile ${authProfile}`, 0);
11634
+ throw new SimApiError(`Profile "${redact(profile.name)}" shares authentication with "${redact(authProfile)}". Run: sim login --profile ${redact(authProfile)}`, 0);
11493
11635
  }
11494
11636
  if (options.scope !== "platform" && options.scope !== "copilot") {
11495
11637
  throw new SimApiError(`Unknown scope "${options.scope}". Use platform or copilot.`, 0);
@@ -11504,7 +11646,7 @@ function loginCommand() {
11504
11646
  }
11505
11647
  const auth = createAuthRequest();
11506
11648
  const url = buildApprovalUrl(profile.endpoint, auth, scope, profile.workspaceId ?? undefined);
11507
- console.log(`Signing in to ${source_default.bold(profile.endpoint)} as profile ${source_default.bold(profile.name)}`);
11649
+ console.log(`Signing in to ${source_default.bold(profile.endpoint)} as profile ${source_default.bold(safeOneLine(profile.name))}`);
11508
11650
  console.log(`
11509
11651
  Pairing code: ${source_default.bold(auth.pairing)}`);
11510
11652
  console.log(source_default.dim(`Confirm this code matches what the browser shows before approving.
@@ -11518,12 +11660,13 @@ Waiting for approval…`));
11518
11660
  if (key.scope !== scope) {
11519
11661
  throw new SimApiError(`Server issued a ${key.scope} key but this profile needs a ${scope} key. Update the Sim deployment, or run: sim login --scope ${key.scope}`, 0);
11520
11662
  }
11521
- writeCredentialsProfile(profile.name, key.apiKey);
11522
11663
  const settings = {
11523
11664
  endpoint: profile.endpoint,
11524
- workspace: key.workspaceId ?? null
11665
+ workspace: key.workspaceId == null ? null : normalizeWorkspaceId(key.workspaceId, "the login response")
11525
11666
  };
11667
+ requireStorableKey(key.apiKey);
11526
11668
  writeConfigProfile(profile.name, settings);
11669
+ writeCredentialsProfile(profile.name, key.apiKey);
11527
11670
  console.log(source_default.green(`
11528
11671
  ✓ Logged in. Key stored in ${credentialsPath()}`));
11529
11672
  if (key.workspaceBound && key.workspaceId) {
@@ -11541,27 +11684,27 @@ function logoutCommand() {
11541
11684
  const profileName = selectedProfileName(command);
11542
11685
  const dependents = listAuthenticationDependents(profileName);
11543
11686
  if (dependents.length > 0) {
11544
- throw new SimApiError(`Cannot remove authentication profile "${profileName}" because it is used by: ${dependents.join(", ")}. Remove those profiles first.`, 0);
11687
+ throw new SimApiError(`Cannot remove authentication profile "${redact(profileName)}" because it is used by: ${dependents.map(redact).join(", ")}. Remove those profiles first.`, 0);
11545
11688
  }
11546
11689
  const removed = deleteProfile(profileName);
11547
11690
  if (!removed.config && !removed.credentials) {
11548
- console.log(source_default.dim(`Nothing stored for profile "${profileName}".`));
11691
+ console.log(source_default.dim(`Nothing stored for profile "${safeOneLine(profileName)}".`));
11549
11692
  return;
11550
11693
  }
11551
- console.log(source_default.green(`✓ Removed profile "${profileName}".`));
11694
+ console.log(source_default.green(`✓ Removed profile "${safeOneLine(profileName)}".`));
11552
11695
  return;
11553
11696
  }
11554
11697
  const profile = profileFrom(command);
11555
11698
  const authProfile = resolveAuthenticationProfileName(profile.name);
11556
11699
  if (authProfile !== profile.name) {
11557
- throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Log out of the authentication profile instead: sim logout --profile ${authProfile}`, 0);
11700
+ throw new SimApiError(`Profile "${redact(profile.name)}" shares authentication with "${redact(authProfile)}". Log out of the authentication profile instead: sim logout --profile ${redact(authProfile)}`, 0);
11558
11701
  }
11559
11702
  if (!readCredentialsProfile(profile.name).api_key) {
11560
- console.log(source_default.dim(`No stored key for profile "${profile.name}".`));
11703
+ console.log(source_default.dim(`No stored key for profile "${safeOneLine(profile.name)}".`));
11561
11704
  return;
11562
11705
  }
11563
11706
  writeCredentialsProfile(profile.name, null);
11564
- console.log(source_default.green(`✓ Removed the stored key for profile "${profile.name}".`));
11707
+ console.log(source_default.green(`✓ Removed the stored key for profile "${safeOneLine(profile.name)}".`));
11565
11708
  console.log(source_default.dim(" The key itself is still active — revoke it in Settings → API keys."));
11566
11709
  });
11567
11710
  }
@@ -11591,7 +11734,7 @@ async function verifyProfile(client, profile) {
11591
11734
  status: "unauthenticated",
11592
11735
  workspace: null,
11593
11736
  keyType: null,
11594
- detail: `no API key — run: sim login --profile ${profile.name}`
11737
+ detail: `no API key — run: sim login --profile ${safeOneLine(profile.name)}`
11595
11738
  };
11596
11739
  }
11597
11740
  const keyType = await readKeyType(client);
@@ -11600,7 +11743,7 @@ async function verifyProfile(client, profile) {
11600
11743
  status: "no-workspace",
11601
11744
  workspace: null,
11602
11745
  keyType,
11603
- detail: `no workspace to check against — run: sim configure --profile ${profile.name} --set-workspace <id>`
11746
+ detail: `no workspace to check against — run: sim configure --profile ${safeOneLine(profile.name)} --set-workspace <id>`
11604
11747
  };
11605
11748
  }
11606
11749
  const operation = V2_OPERATIONS.getWorkspace;
@@ -11687,38 +11830,83 @@ function whoamiCommand() {
11687
11830
  process.exitCode = exitCode;
11688
11831
  });
11689
11832
  }
11833
+ var PROFILE_COLUMNS = [
11834
+ { header: "", value: (row) => row.active ? source_default.green("*") : " " },
11835
+ { header: "profile", value: (row) => safeOneLine(row.name) },
11836
+ { header: "key", value: (row) => row.error ? text(null) : row.hasKey ? "yes" : "no" },
11837
+ { header: "auth", value: (row) => row.authProfile ? safeOneLine(row.authProfile) : text(null) },
11838
+ { header: "error", value: (row) => row.error ? source_default.red(safeOneLine(row.error)) : text(null) }
11839
+ ];
11840
+ function buildProfileRow(name, active) {
11841
+ try {
11842
+ const authProfile = resolveAuthenticationProfileName(name);
11843
+ return {
11844
+ name,
11845
+ active,
11846
+ hasKey: Boolean(readCredentialsProfile(authProfile).api_key),
11847
+ authProfile,
11848
+ error: null
11849
+ };
11850
+ } catch (error) {
11851
+ if (!(error instanceof ProfileConfigError))
11852
+ throw error;
11853
+ return { name, active, hasKey: false, authProfile: null, error: error.message };
11854
+ }
11855
+ }
11856
+ function profileListingContext(command) {
11857
+ try {
11858
+ const profile = profileFrom(command);
11859
+ return { activeName: profile.name, output: profile.output };
11860
+ } catch (error) {
11861
+ if (!(error instanceof ProfileConfigError))
11862
+ throw error;
11863
+ if (error instanceof ProfileOverrideError)
11864
+ throw error;
11865
+ const globals = globalsOf(command);
11866
+ const named = globals.profile || process.env.SIM_PROFILE;
11867
+ if (named && named !== DEFAULT_PROFILE && !listProfiles().includes(named))
11868
+ throw error;
11869
+ const requested = globals.output ?? process.env.SIM_OUTPUT;
11870
+ if (requested && !OUTPUT_FORMATS.includes(requested))
11871
+ throw error;
11872
+ return {
11873
+ activeName: named || DEFAULT_PROFILE,
11874
+ output: requested ? requested : "table"
11875
+ };
11876
+ }
11877
+ }
11690
11878
  function profilesCommand() {
11691
11879
  const command = new Command("profiles").alias("profile").description("List profiles or add a workspace profile that shares a stored login");
11692
11880
  const printProfiles = (_options, actionCommand) => {
11693
- const profiles = listProfiles();
11694
- if (profiles.length === 0) {
11695
- console.log(source_default.dim("No profiles yet. Run: sim login"));
11881
+ const { activeName, output } = profileListingContext(actionCommand);
11882
+ const rows = listProfiles().map((name) => buildProfileRow(name, name === activeName));
11883
+ if (rows.length === 0) {
11884
+ if (output === "table")
11885
+ console.log(source_default.dim("No profiles yet. Run: sim login"));
11886
+ else
11887
+ printList(output, rows, PROFILE_COLUMNS);
11696
11888
  return;
11697
11889
  }
11698
- const active = selectedProfileName(actionCommand);
11699
- for (const name of profiles) {
11700
- const marker = name === active ? source_default.green("*") : " ";
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
- }
11710
- const hasKey = Boolean(readCredentialsProfile(authProfile).api_key);
11711
- const authentication = authProfile === name ? "" : source_default.dim(` (auth: ${authProfile})`);
11712
- console.log(`${marker} ${name}${hasKey ? "" : source_default.dim(" (no key)")}${authentication}`);
11713
- }
11890
+ printList(output, rows, PROFILE_COLUMNS);
11714
11891
  };
11715
- command.action(printProfiles);
11716
- command.addCommand(new Command("list").description("List configured profiles").action(printProfiles));
11892
+ command.addCommand(new Command("list").allowExcessArguments(false).description("List configured profiles").action(printProfiles), { isDefault: true });
11717
11893
  command.addCommand(addProfileCommand());
11894
+ const known = new Set(command.commands.flatMap((child) => [child.name(), ...child.aliases()]));
11895
+ command.hook("preSubcommand", (group) => {
11896
+ const first = group.args[0];
11897
+ if (first !== undefined && !first.startsWith("-") && !known.has(first)) {
11898
+ group.unknownCommand();
11899
+ }
11900
+ });
11718
11901
  return command;
11719
11902
  }
11720
11903
 
11721
11904
  // src/commands/configure.ts
11905
+ var GLOBAL_FLAG_TWINS = [
11906
+ { option: "endpoint", flag: "--endpoint", setFlag: "--set-endpoint" },
11907
+ { option: "workspace", flag: "-w, --workspace", setFlag: "--set-workspace" },
11908
+ { option: "output", flag: "--output", setFlag: "--set-output" }
11909
+ ];
11722
11910
  function requireValue(value, flag, key) {
11723
11911
  if (value !== undefined && value.trim() === "") {
11724
11912
  throw new SimApiError(`${flag} requires a value. To remove it, run: sim configure --unset ${key}`, 0);
@@ -11726,6 +11914,13 @@ function requireValue(value, flag, key) {
11726
11914
  }
11727
11915
  function configureCommand() {
11728
11916
  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) => {
11917
+ const globals = globalsOf(command);
11918
+ for (const { option, flag, setFlag } of GLOBAL_FLAG_TWINS) {
11919
+ const value = globals[option];
11920
+ if (value === undefined)
11921
+ continue;
11922
+ throw new SimApiError(`${flag} applies to a single command and is not stored. To save it, run: sim configure ${setFlag} ${redact(value)}`, 0);
11923
+ }
11729
11924
  const profile = profileFrom(command, { allowUnknownProfile: true });
11730
11925
  const authProfile = resolveAuthenticationProfileName(profile.name);
11731
11926
  const updates = {};
@@ -11738,8 +11933,9 @@ function configureCommand() {
11738
11933
  }
11739
11934
  updates.endpoint = normalizeEndpoint(options.setEndpoint, "--set-endpoint");
11740
11935
  }
11741
- if (options.setWorkspace)
11742
- updates.workspace = options.setWorkspace;
11936
+ if (options.setWorkspace) {
11937
+ updates.workspace = normalizeWorkspaceId(options.setWorkspace, "--set-workspace");
11938
+ }
11743
11939
  if (options.setOutput) {
11744
11940
  if (!OUTPUT_FORMATS.includes(options.setOutput)) {
11745
11941
  throw new SimApiError(`Unknown output format "${options.setOutput}". Use one of: ${OUTPUT_FORMATS.join(", ")}`, 0);
@@ -11748,7 +11944,7 @@ function configureCommand() {
11748
11944
  }
11749
11945
  for (const key of options.unset ?? []) {
11750
11946
  if (!["endpoint", "workspace", "output"].includes(key)) {
11751
- throw new SimApiError(`Cannot unset "${key}". Use endpoint, workspace, or output.`, 0);
11947
+ throw new SimApiError(`Cannot unset "${redact(key)}". Use endpoint, workspace, or output.`, 0);
11752
11948
  }
11753
11949
  if (key === "endpoint" && authProfile !== profile.name) {
11754
11950
  throw new SimApiError(`Profile "${profile.name}" shares its endpoint with authentication profile "${authProfile}". Run: sim configure --profile ${authProfile} --unset endpoint`, 0);
@@ -11766,14 +11962,16 @@ function configureCommand() {
11766
11962
  }
11767
11963
  return;
11768
11964
  }
11965
+ const removalOnly = Object.values(updates).every((value) => value === null);
11966
+ if (removalOnly && Object.keys(readConfigProfile(profile.name)).length === 0) {
11967
+ console.log(source_default.dim(`No settings stored for profile "${profile.name}".`));
11968
+ return;
11969
+ }
11769
11970
  writeConfigProfile(profile.name, updates);
11770
11971
  console.log(source_default.green(`✓ Updated profile "${profile.name}" in ${configPath()}`));
11771
11972
  });
11772
11973
  }
11773
11974
 
11774
- // src/runtime/request.ts
11775
- import { existsSync as existsSync2, readFileSync as readFileSync3, readSync } from "node:fs";
11776
-
11777
11975
  // src/contract/commands.ts
11778
11976
  var TABLE_NAME_HELP = "Identifier: letters, numbers, and underscores; cannot start with a number";
11779
11977
  var TABLE_FILTER_HELP = 'Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull';
@@ -11782,7 +11980,7 @@ var TABLE_SORT_HELP = 'Ordered sort keys: [{"field":"createdAt","direction":"des
11782
11980
  var KNOWLEDGE_TAG_DEFINITIONS_HELP = 'Tag definitions: [{"tagSlot":"tag1","displayName":"category","fieldType":"text"}]';
11783
11981
  var CUSTOM_TOOL_SCHEMA_HELP = 'OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}}';
11784
11982
  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>"}';
11983
+ var WORKFLOW_OPERATIONS_HELP = 'Edits to apply, in a single batch, keyed by operation_type: [{"operation_type":"add","block_id":"my-fn","params":{"type":"function","name":"My Fn","inputs":{"code":"return {ok:true}"}}},{"operation_type":"edit","block_id":"<uuid>","params":{"name":"Renamed","connections":{"success":"my-fn"}}},{"operation_type":"delete","block_id":"<uuid>"}]. Also extract_from_subflow, whose params carry {"subflowId":"<loop-id>"}, and insert_into_subflow, which creates a block and so takes an add’s params plus that subflowId';
11786
11984
  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
11985
  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
11986
  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';
@@ -11858,7 +12056,7 @@ var CLI_CONTRACT = {
11858
12056
  listBillingLogs: {
11859
12057
  command: "billing logs",
11860
12058
  allWorkspaces: true,
11861
- describe: "List credit usage events",
12059
+ describe: "List credit usage events (a personal API key reports only your own events; a workspace API key reports every member's in aggregate, unattributed)",
11862
12060
  flags: {
11863
12061
  source: { describe: "Filter by usage source; sim-chat combines Copilot and workspace chat" },
11864
12062
  period: { describe: "Billing period" },
@@ -12014,14 +12212,14 @@ var CLI_CONTRACT = {
12014
12212
  }
12015
12213
  },
12016
12214
  columns: [
12017
- { header: "started", path: "startedAt", format: "timestamp" },
12018
- { header: "status" },
12215
+ { header: "started", path: "startedAt", format: "timestamp", minWidth: 19 },
12216
+ { header: "status", minWidth: 9 },
12019
12217
  { header: "level" },
12020
- { header: "trigger" },
12021
- { header: "workflow", path: "workflow.name" },
12218
+ { header: "trigger", minWidth: 12 },
12219
+ { header: "workflow", path: "workflow.name", minWidth: 24 },
12022
12220
  { header: "duration", path: "totalDurationMs", format: "duration" },
12023
- { header: "cost", path: "cost.total", format: "cost" },
12024
- { header: "run", path: "runId" }
12221
+ { header: "cost", path: "cost.total", format: "cost", minWidth: 8 },
12222
+ { header: "run", path: "runId", minWidth: 36 }
12025
12223
  ]
12026
12224
  },
12027
12225
  getLog: {
@@ -12051,7 +12249,7 @@ var CLI_CONTRACT = {
12051
12249
  },
12052
12250
  getLogStats: {
12053
12251
  command: "logs stats",
12054
- describe: "Summarize run counts, failures, and cost over a window",
12252
+ describe: "Summarize run counts, failures and latency over a window",
12055
12253
  flags: LOG_LIST_FILTER_FLAGS,
12056
12254
  fields: [
12057
12255
  { header: "runs", path: "totalRuns" },
@@ -12379,7 +12577,7 @@ var CLI_CONTRACT = {
12379
12577
  listCustomTools: {
12380
12578
  columns: [
12381
12579
  { header: "id" },
12382
- { header: "name", path: "title" },
12580
+ { header: "title", path: "title" },
12383
12581
  { header: "description", path: "schema.function.description" },
12384
12582
  { header: "updated", path: "updatedAt", format: "timestamp" }
12385
12583
  ]
@@ -12611,6 +12809,7 @@ var CLI_CONTRACT = {
12611
12809
  ]
12612
12810
  },
12613
12811
  listFileFolders: {
12812
+ describe: "List folders",
12614
12813
  aliases: ["ls"],
12615
12814
  flags: {
12616
12815
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12618,6 +12817,7 @@ var CLI_CONTRACT = {
12618
12817
  columns: FOLDER_LIST_COLUMNS
12619
12818
  },
12620
12819
  listKnowledgeFolders: {
12820
+ describe: "List knowledge folders",
12621
12821
  aliases: ["ls"],
12622
12822
  flags: {
12623
12823
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12625,6 +12825,7 @@ var CLI_CONTRACT = {
12625
12825
  columns: FOLDER_LIST_COLUMNS
12626
12826
  },
12627
12827
  listTableFolders: {
12828
+ describe: "List table folders",
12628
12829
  aliases: ["ls"],
12629
12830
  flags: {
12630
12831
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12632,6 +12833,7 @@ var CLI_CONTRACT = {
12632
12833
  columns: FOLDER_LIST_COLUMNS
12633
12834
  },
12634
12835
  listWorkflowFolders: {
12836
+ describe: "List workflow folders",
12635
12837
  aliases: ["ls"],
12636
12838
  flags: {
12637
12839
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12769,6 +12971,23 @@ var CLI_CONTRACT = {
12769
12971
  }
12770
12972
  }
12771
12973
  },
12974
+ listTableDispatches: {
12975
+ columns: [
12976
+ { header: "id" },
12977
+ { header: "status" },
12978
+ { header: "mode" },
12979
+ { header: "max rows", path: "limit.max" },
12980
+ { header: "processed", path: "processedCount" },
12981
+ { header: "manual", path: "isManualRun", format: "bool" },
12982
+ { header: "requested", path: "requestedAt", format: "timestamp" },
12983
+ { header: "completed", path: "completedAt", format: "timestamp" },
12984
+ { header: "canceled", path: "canceledAt", format: "timestamp" },
12985
+ { header: "groups", path: "scope.groupIds", format: "count" },
12986
+ { header: "rows", path: "scope.rowIds", format: "count" },
12987
+ { header: "filtered", path: "scope.filtered", format: "bool" },
12988
+ { header: "excluded", path: "scope.excludeRowIds", format: "count" }
12989
+ ]
12990
+ },
12772
12991
  runRowEnrichment: {
12773
12992
  command: "tables rows enrich",
12774
12993
  describe: "Run one row’s enrichment group"
@@ -12777,8 +12996,16 @@ var CLI_CONTRACT = {
12777
12996
  createTableImportPartUrls: { hidden: true },
12778
12997
  completeTableImport: { hidden: true },
12779
12998
  getTableImport: { flags: TRANSFER_TOKEN_OMITTED },
12780
- cancelTableImport: { command: "tables imports cancel", flags: TRANSFER_TOKEN_OMITTED },
12781
- cancelTableExport: { command: "tables exports cancel" },
12999
+ cancelTableImport: {
13000
+ command: "tables imports cancel",
13001
+ flags: TRANSFER_TOKEN_OMITTED,
13002
+ describe: "Stop a running import",
13003
+ confirm: "This stops the import between row batches, so whatever it already wrote stays and nothing resumes it. A replace import empties the table before its first batch, so cancelling one leaves only part of the new file; an append adds its rows again if you import the file a second time."
13004
+ },
13005
+ cancelTableExport: {
13006
+ command: "tables exports cancel",
13007
+ describe: "Stop a running export"
13008
+ },
12782
13009
  tableExportDownload: {
12783
13010
  command: "tables exports download",
12784
13011
  describe: "Get the download URL for a finished export"
@@ -12800,7 +13027,7 @@ var CLI_CONTRACT = {
12800
13027
  selectedOutputs: {
12801
13028
  name: "select-output",
12802
13029
  list: true,
12803
- describe: "Return blockName.field values (e.g. agent_1.content); missing fields are omitted"
13030
+ describe: "Return blockName.field values from the streamed result (e.g. agent_1.content), requires --follow; missing fields are omitted"
12804
13031
  },
12805
13032
  stream: { omit: true },
12806
13033
  includeThinking: { omit: true },
@@ -12824,7 +13051,7 @@ var CLI_CONTRACT = {
12824
13051
  selectedOutputs: {
12825
13052
  name: "select-output",
12826
13053
  list: true,
12827
- describe: "Include blockName.field values in JSON or YAML output (e.g. agent_1.content)"
13054
+ describe: "Include blockId or blockId.path values in JSON or YAML output; block names are not resolved on a finished run"
12828
13055
  }
12829
13056
  },
12830
13057
  fields: [
@@ -12931,11 +13158,20 @@ function camel(flag) {
12931
13158
  }
12932
13159
 
12933
13160
  // src/runtime/request.ts
13161
+ import { existsSync as existsSync2, readFileSync as readFileSync3, readSync } from "node:fs";
12934
13162
  var PROFILE_INJECTED_FIELD = "workspaceId";
12935
13163
  function isProfileWorkspacePath(commandSpec, param) {
12936
13164
  return commandSpec.profileWorkspacePath === true && param === PROFILE_INJECTED_FIELD;
12937
13165
  }
13166
+ function cursorSlot(operationSpec) {
13167
+ if (operationSpec.query && "cursor" in operationSpec.query)
13168
+ return "query";
13169
+ if (operationSpec.body && "cursor" in operationSpec.body)
13170
+ return "body";
13171
+ return null;
13172
+ }
12938
13173
  var JSON_KINDS = new Set(["object", "array", "unknown"]);
13174
+ var NUMERIC_KINDS = new Set(["number", "integer"]);
12939
13175
  function flagSpecFor(operation, field) {
12940
13176
  return CLI_CONTRACT[operation]?.flags?.[field] ?? {};
12941
13177
  }
@@ -13001,6 +13237,9 @@ function readStdin() {
13001
13237
  }
13002
13238
  return Buffer.concat(chunks).toString("utf8");
13003
13239
  }
13240
+ function literalAtHint(error, path) {
13241
+ return error?.code === "ENOENT" ? `. To pass the literal value @${path}, write @@${path}` : "";
13242
+ }
13004
13243
  function readArgumentSource(raw, flagName) {
13005
13244
  if (raw.startsWith("@@"))
13006
13245
  return { text: raw.slice(1), from: "" };
@@ -13020,7 +13259,7 @@ function readArgumentSource(raw, flagName) {
13020
13259
  try {
13021
13260
  return { text: readFileSync3(path, "utf8"), from: ` (read from ${path})` };
13022
13261
  } catch (error) {
13023
- throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}`, 0);
13262
+ throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}${literalAtHint(error, path)}`, 0);
13024
13263
  }
13025
13264
  }
13026
13265
  function readListValues(raw, flagName) {
@@ -13073,6 +13312,7 @@ function encodeFolderPath(value) {
13073
13312
  }
13074
13313
  }).join("/");
13075
13314
  }
13315
+ var FRACTIONAL_DIGITS = /\.\d*[1-9]/;
13076
13316
  function pathHint(raw) {
13077
13317
  if (raw.startsWith("@") || /^\s*[[{"\-\d]|^\s*(true|false|null)/.test(raw))
13078
13318
  return "";
@@ -13097,10 +13337,16 @@ function coerce(raw, field, flag, flagName) {
13097
13337
  throw new SimApiError(`--${flagName} must be valid JSON${source.from}: ${error.message}${pathHint(raw)}`, 0);
13098
13338
  }
13099
13339
  }
13100
- if (field.kind === "number" || field.kind === "integer") {
13340
+ if (NUMERIC_KINDS.has(field.kind)) {
13101
13341
  const value = Number(raw);
13102
13342
  if (Number.isNaN(value))
13103
13343
  throw new SimApiError(`--${flagName} must be a number`, 0);
13344
+ if (field.kind === "integer" && (!Number.isInteger(value) || FRACTIONAL_DIGITS.test(String(raw)))) {
13345
+ throw new SimApiError(`--${flagName} must be a whole number`, 0);
13346
+ }
13347
+ if (field.kind === "integer" && !Number.isSafeInteger(value)) {
13348
+ throw new SimApiError(`--${flagName} is outside the whole-number range the API accepts (±${Number.MAX_SAFE_INTEGER})`, 0);
13349
+ }
13104
13350
  return value;
13105
13351
  }
13106
13352
  if (field.kind === "boolean" || flag.boolean)
@@ -13113,6 +13359,7 @@ function coerce(raw, field, flag, flagName) {
13113
13359
  return encodeFolderPath(raw);
13114
13360
  return raw;
13115
13361
  }
13362
+ var NO_WORKSPACE_FALLBACK = "No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>";
13116
13363
  function asQueryValue(value) {
13117
13364
  if (value === null || value === undefined)
13118
13365
  return;
@@ -13133,7 +13380,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13133
13380
  const value = profileWorkspacePath ? workspaceId : pathFlag ? flags[camel(flagName)] : positional[positionalIndex++];
13134
13381
  if (value === undefined || value === null) {
13135
13382
  if (profileWorkspacePath) {
13136
- throw new SimApiError("No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>", 0);
13383
+ throw new SimApiError(NO_WORKSPACE_FALLBACK, 0);
13137
13384
  }
13138
13385
  throw new SimApiError(pathFlag ? `--${flagName} is required` : `Missing <${argumentName}>`, 0);
13139
13386
  }
@@ -13145,6 +13392,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13145
13392
  const query = {};
13146
13393
  const body = {};
13147
13394
  const headers = {};
13395
+ const paginatedLimit = cursorSlot(spec) !== null;
13148
13396
  for (const slot of ["query", "body", "headers"]) {
13149
13397
  for (const [field, descriptor] of Object.entries(spec[slot] ?? {})) {
13150
13398
  const flag = flagSpecFor(operation, field);
@@ -13154,10 +13402,16 @@ function buildRequest(operation, positional, flags, workspaceId) {
13154
13402
  const omitProfileWorkspace = commandSpec.allWorkspaces && flags.allWorkspaces === true;
13155
13403
  const provided = field === PROFILE_INJECTED_FIELD ? omitProfileWorkspace ? undefined : workspaceId : flags[camel(flagName)];
13156
13404
  const raw = provided ?? flag.requestDefault;
13405
+ if ((slot === "query" || NUMERIC_KINDS.has(descriptor.kind)) && typeof raw === "string" && raw.trim() === "" && !(field === "limit" && paginatedLimit)) {
13406
+ throw new SimApiError(`--${flagName} cannot be empty`, 0);
13407
+ }
13157
13408
  const value = coerce(raw ?? undefined, descriptor, flag, flagName);
13409
+ if (field === "limit" && !paginatedLimit && NUMERIC_KINDS.has(descriptor.kind) && typeof value === "number" && value < 1) {
13410
+ throw new SimApiError(`--${flagName} must be 1 or more`, 0);
13411
+ }
13158
13412
  if (value === undefined) {
13159
13413
  if (descriptor.required) {
13160
- throw new SimApiError(field === PROFILE_INJECTED_FIELD ? "No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>" : `--${flagName} is required`, 0);
13414
+ throw new SimApiError(field === PROFILE_INJECTED_FIELD ? NO_WORKSPACE_FALLBACK : `--${flagName} is required`, 0);
13161
13415
  }
13162
13416
  continue;
13163
13417
  }
@@ -13204,6 +13458,126 @@ function buildRequest(operation, positional, flags, workspaceId) {
13204
13458
  };
13205
13459
  }
13206
13460
 
13461
+ // src/runtime/options.ts
13462
+ var DEFAULT_LIMIT = 100;
13463
+ function describeField(flag, descriptor, name, field) {
13464
+ return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
13465
+ }
13466
+ function literalNullHint(documented, name) {
13467
+ return /\bnull\b/i.test(documented) ? ` (--${name} null sends the word, not JSON null)` : "";
13468
+ }
13469
+ var WIRE_VOCABULARY_SENTENCE = /\s*The listed spellings[^.]*\.\s*/g;
13470
+ function withoutWireVocabulary(documented) {
13471
+ return documented.replace(WIRE_VOCABULARY_SENTENCE, " ").trim();
13472
+ }
13473
+ var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
13474
+ function addFieldOption(command, operation, field, descriptor, slot, paginates) {
13475
+ if (field === PROFILE_INJECTED_FIELD || field === "cursor")
13476
+ return;
13477
+ const flag = flagSpecFor(operation, field);
13478
+ if (flag.omit)
13479
+ return;
13480
+ const name = flagNameFor(operation, field);
13481
+ const short = flag.short ? `-${flag.short}, ` : "";
13482
+ if (paginates && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
13483
+ command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
13484
+ return;
13485
+ }
13486
+ const documented = `${describeField(flag, descriptor, name, field)}${field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
13487
+ if (descriptor.kind === "boolean" || flag.boolean) {
13488
+ const booleanDoc = withoutWireVocabulary(documented);
13489
+ if (descriptor.required) {
13490
+ command.addOption(new Option(`${short}--${name} <true|false>`, `${booleanDoc} (required)`).choices(["true", "false"]).makeOptionMandatory());
13491
+ return;
13492
+ }
13493
+ command.option(`${short}--${name}`, booleanDoc);
13494
+ if (!flag.boolean || flag.negatable) {
13495
+ command.option(`--no-${name}`, `Send --${name} as false`);
13496
+ }
13497
+ return;
13498
+ }
13499
+ const takesList = flag.list === true;
13500
+ const wantsJson = takesJson(descriptor, flag);
13501
+ const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
13502
+ const choices = flag.choices ?? descriptor.values;
13503
+ const literalNull = slot === "body" && !takesList && !wantsJson;
13504
+ const describe = `${documented}${takesList ? " (space-separated, or @path / @- with one value per line; @@value for a literal leading @)" : wantsJson ? " (JSON, or @path / @- to read a file or stdin)" : ""}${descriptor.required ? " (required)" : ""}${literalNull ? literalNullHint(documented, name) : ""}`;
13505
+ const renamedFrom = flag.renamedFrom ?? [];
13506
+ const option = new Option(`${short}--${name} ${placeholder}`, describe);
13507
+ if (flag.hidden)
13508
+ option.hideHelp();
13509
+ if (choices && !takesList)
13510
+ option.choices([...choices]);
13511
+ if (descriptor.default !== undefined && field !== "limit") {
13512
+ option.default(undefined, String(descriptor.default));
13513
+ }
13514
+ if (descriptor.required && renamedFrom.length === 0)
13515
+ option.makeOptionMandatory();
13516
+ command.addOption(option);
13517
+ for (const previous of renamedFrom) {
13518
+ const retired = new Option(`--${previous} ${placeholder}`).hideHelp();
13519
+ if (choices && !takesList)
13520
+ retired.choices([...choices]);
13521
+ command.addOption(retired);
13522
+ }
13523
+ }
13524
+ function addOperationOptions(command, operation, commandSpec, operationSpec) {
13525
+ for (const param of operationSpec.pathParams) {
13526
+ const flag = commandSpec.pathFlags?.[param];
13527
+ if (!flag)
13528
+ continue;
13529
+ const name = pathFlagNameFor(commandSpec, param);
13530
+ const short = flag.short ? `-${flag.short}, ` : "";
13531
+ command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
13532
+ }
13533
+ const paginates = cursorSlot(operationSpec) !== null;
13534
+ for (const slot of ["query", "body", "headers"]) {
13535
+ for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
13536
+ if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
13537
+ continue;
13538
+ if (commandSpec.positionals?.includes(field))
13539
+ continue;
13540
+ addFieldOption(command, operation, field, descriptor, slot, paginates);
13541
+ }
13542
+ }
13543
+ if (commandSpec.allWorkspaces) {
13544
+ command.option("--all-workspaces", "Do not filter to the configured workspace (personal API key required for account-wide access)");
13545
+ }
13546
+ if (commandSpec.expandedTrace) {
13547
+ command.option("--trace", "Show expanded trace spans with inputs, outputs, errors, timing, and cost");
13548
+ }
13549
+ if (operationSpec.opaqueBody) {
13550
+ if (commandSpec.bodyVariants) {
13551
+ for (const variant of commandSpec.bodyVariants) {
13552
+ command.option(`--${variant.name} <json|@file>`, `${variant.describe} (JSON, or @path / @-; choose exactly one body flag)`);
13553
+ }
13554
+ } else {
13555
+ command.requiredOption("--body <json|@file>", "Request body as JSON (or @path / @- to read a file or stdin) (required)");
13556
+ }
13557
+ }
13558
+ if (commandSpec.confirm) {
13559
+ const exemptedByDryRun = operationSpec.query?.dryRun !== undefined || operationSpec.body?.dryRun !== undefined;
13560
+ command.option("-y, --yes", exemptedByDryRun ? "Confirm this operation (required unless --dry-run)" : "Confirm this operation (required)");
13561
+ }
13562
+ }
13563
+
13564
+ // src/runtime/renamed.ts
13565
+ var warned = new Set;
13566
+ function warn(kind, from, to) {
13567
+ const key = `${kind}:${from}`;
13568
+ if (warned.has(key))
13569
+ return;
13570
+ warned.add(key);
13571
+ process.stderr.write(`warning: ${kind} "${from}" has been renamed to "${to}". The old name still works.
13572
+ `);
13573
+ }
13574
+ function warnRenamedCommand(from, to) {
13575
+ warn("command", `sim ${from}`, `sim ${to}`);
13576
+ }
13577
+ function warnRenamedFlag(from, to) {
13578
+ warn("flag", `--${from}`, `--${to}`);
13579
+ }
13580
+
13207
13581
  // src/output/trace.ts
13208
13582
  function traceSpan(value) {
13209
13583
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -13333,7 +13707,8 @@ function at(row, path) {
13333
13707
  function decodeFolderPath(value) {
13334
13708
  return value.split("/").map((segment) => {
13335
13709
  try {
13336
- return decodeURIComponent(segment);
13710
+ const decoded = decodeURIComponent(segment);
13711
+ return decoded.includes("/") ? segment : decoded;
13337
13712
  } catch {
13338
13713
  return segment;
13339
13714
  }
@@ -13464,8 +13839,10 @@ function unwrapResource(data) {
13464
13839
  const [, value] = entries[0];
13465
13840
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
13466
13841
  }
13467
- function renderPage(format, rows, spec, envelope) {
13842
+ function renderPage(format, rows, spec, envelope, options = {}) {
13468
13843
  writePageNote(spec, envelope);
13844
+ writeEnvelopeTruncation(envelope);
13845
+ writeCursorTruncation(rows.length, options.truncated === true);
13469
13846
  printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
13470
13847
  }
13471
13848
  function writePageNote(spec, envelope) {
@@ -13477,7 +13854,48 @@ function writePageNote(spec, envelope) {
13477
13854
  process.stderr.write(source_default.dim(`${spec.pageNote.label}: ${String(value)}
13478
13855
  `));
13479
13856
  }
13480
- function renderResult(operation, format, raw, spec, options = {}) {
13857
+ var TRUNCATION_FLAG = /^truncated$|^[A-Za-z0-9]+Truncated$/;
13858
+ var NEGATED_TRUNCATION_FLAG = /^(?:not|un|non|never)Truncated$|(?:Not|Un|Non|Never)Truncated$/;
13859
+ function truncationFlags(container) {
13860
+ if (!container || typeof container !== "object" || Array.isArray(container))
13861
+ return [];
13862
+ return Object.entries(container).filter(([key, value]) => value === true && TRUNCATION_FLAG.test(key) && !NEGATED_TRUNCATION_FLAG.test(key)).map(([key]) => key);
13863
+ }
13864
+ function responseTruncationFlags(envelope) {
13865
+ return [...truncationFlags(envelope), ...truncationFlags(at(envelope, "data"))];
13866
+ }
13867
+ function foldPageEnvelope(current, page) {
13868
+ if (current === undefined)
13869
+ return page;
13870
+ const raised = truncationFlags(page);
13871
+ if (raised.length === 0 || !current || typeof current !== "object")
13872
+ return current;
13873
+ return {
13874
+ ...current,
13875
+ ...Object.fromEntries(raised.map((flag) => [flag, true]))
13876
+ };
13877
+ }
13878
+ function spellOut(flag) {
13879
+ return flag.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().trim();
13880
+ }
13881
+ function clippedSubject(flag) {
13882
+ const subject = flag.replace(/^truncated$|Truncated$/, "").replace(/^is(?=[A-Z]|$)/, "");
13883
+ return subject ? `the ${spellOut(subject)} it returned` : "this result";
13884
+ }
13885
+ function writeEnvelopeTruncation(envelope) {
13886
+ for (const flag of responseTruncationFlags(envelope)) {
13887
+ process.stderr.write(source_default.dim(`${spellOut(flag)}: the server clipped ${clippedSubject(flag)}, so the answer is incomplete
13888
+ `));
13889
+ }
13890
+ }
13891
+ function writeCursorTruncation(count, truncated) {
13892
+ if (!truncated)
13893
+ return;
13894
+ process.stderr.write(source_default.dim(`showing the first ${count}; more results exist — re-run with --limit 0 for all
13895
+ `));
13896
+ }
13897
+ function renderResult(operation, format, raw, spec, options = {}, envelope) {
13898
+ writeEnvelopeTruncation(envelope);
13481
13899
  if (spec.document) {
13482
13900
  printDocument(format, raw);
13483
13901
  return;
@@ -13509,26 +13927,598 @@ function renderResult(operation, format, raw, spec, options = {}) {
13509
13927
  }
13510
13928
  }
13511
13929
 
13512
- // src/commands/credentials.ts
13513
- var CONNECTION_RESULT = {
13514
- fields: [
13515
- { header: "connection link", path: "authorizationUrl" },
13516
- { header: "expires", path: "expiresAt", format: "timestamp" }
13517
- ]
13518
- };
13519
- var SERVICE_ACCOUNT_RESULT = {
13520
- fields: [
13521
- { header: "id" },
13522
- { header: "name", path: "displayName" },
13523
- { header: "provider", path: "providerId" },
13524
- { header: "role" },
13525
- { header: "created", path: "createdAt", format: "timestamp" }
13526
- ]
13930
+ // src/runtime/execute.ts
13931
+ var RUN_OUTCOME_OPERATIONS = new Set(["executeWorkflow"]);
13932
+ var FAILED_RUN_STATUS_MESSAGES = {
13933
+ failed: "The workflow run failed.",
13934
+ cancelled: "The workflow run was cancelled."
13527
13935
  };
13528
- function serviceAccountProvider(providers, providerId) {
13529
- const provider = providers.find((candidate) => candidate.type === "service_account" && candidate.providerId === providerId);
13530
- if (!provider) {
13531
- throw new SimApiError(`Unknown service-account provider "${providerId}".`, 0);
13936
+ function runFailureMessage(operation, payload) {
13937
+ if (!RUN_OUTCOME_OPERATIONS.has(operation))
13938
+ return null;
13939
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
13940
+ return null;
13941
+ const { status, error } = payload;
13942
+ if (typeof status !== "string")
13943
+ return null;
13944
+ const fallback = FAILED_RUN_STATUS_MESSAGES[status];
13945
+ if (!fallback)
13946
+ return null;
13947
+ const reported2 = error?.message;
13948
+ return safeOneLine(typeof reported2 === "string" && reported2 ? reported2 : fallback);
13949
+ }
13950
+ var BULK_OUTCOME_CHECKS = {
13951
+ bulkDeleteFiles: (payload, body) => {
13952
+ if (countOf(payload.deletedItems?.files) > 0)
13953
+ return null;
13954
+ const requested = lengthOf(body?.fileIds);
13955
+ if (requested === 0)
13956
+ return null;
13957
+ return `Deleted nothing: none of the ${requested} requested ${requested === 1 ? "file was" : "files were"} deleted.`;
13958
+ },
13959
+ addWorkspaceFilesToKnowledgeBase: (payload) => {
13960
+ if (lengthOf(payload.added) > 0)
13961
+ return null;
13962
+ const failed = lengthOf(payload.failed);
13963
+ if (failed === 0)
13964
+ return null;
13965
+ return `Indexed nothing: none of the ${failed} requested ${failed === 1 ? "file was" : "files were"} added.`;
13966
+ },
13967
+ bulkDeleteTables: (payload) => {
13968
+ const items = payload.deletedItems;
13969
+ const deleted = countOf(items?.tables) + countOf(items?.folders);
13970
+ if (deleted > 0)
13971
+ return null;
13972
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
13973
+ if (missed === 0)
13974
+ return null;
13975
+ return `Deleted nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be deleted.`;
13976
+ },
13977
+ bulkUpdateKnowledgeChunks: (payload, body) => {
13978
+ if (countOf(payload.processed) > 0)
13979
+ return null;
13980
+ const requested = lengthOf(body?.chunkIds);
13981
+ if (requested === 0)
13982
+ return null;
13983
+ const reported2 = payload.errors?.[0];
13984
+ return typeof reported2 === "string" && reported2 ? safeOneLine(reported2) : `Updated nothing: none of the ${requested} requested ${requested === 1 ? "chunk" : "chunks"} matched.`;
13985
+ },
13986
+ moveTables: (payload) => {
13987
+ if (lengthOf(payload.moved) > 0)
13988
+ return null;
13989
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
13990
+ if (missed === 0)
13991
+ return null;
13992
+ return `Moved nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be moved.`;
13993
+ },
13994
+ moveWorkflows: (payload) => {
13995
+ if (lengthOf(payload.moved) > 0)
13996
+ return null;
13997
+ const failed = lengthOf(payload.failed);
13998
+ if (failed === 0)
13999
+ return null;
14000
+ return `Moved nothing: ${failed} of ${failed} ${failed === 1 ? "workflow" : "workflows"} could not be moved.`;
14001
+ }
14002
+ };
14003
+ function countOf(value) {
14004
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
14005
+ }
14006
+ function lengthOf(value) {
14007
+ return Array.isArray(value) ? value.length : 0;
14008
+ }
14009
+ function bulkFailureMessage(operation, payload, body) {
14010
+ const check = BULK_OUTCOME_CHECKS[operation];
14011
+ if (!check)
14012
+ return null;
14013
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
14014
+ return null;
14015
+ return check(payload, body);
14016
+ }
14017
+ var EXCLUSIVE_CAP_FIELDS = {
14018
+ deleteTableRows: { cap: "limit", ids: "rowIds" }
14019
+ };
14020
+ function readPagedLimit(raw) {
14021
+ const text2 = String(raw ?? DEFAULT_LIMIT).trim();
14022
+ const value = text2 === "" ? Number.NaN : Number(text2);
14023
+ if (!Number.isInteger(value) || value < 0) {
14024
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14025
+ }
14026
+ return value;
14027
+ }
14028
+ function assertCapIsUsable(operation, flags) {
14029
+ const exclusive = EXCLUSIVE_CAP_FIELDS[operation];
14030
+ if (!exclusive)
14031
+ return;
14032
+ const cap = flagNameFor(operation, exclusive.cap);
14033
+ const ids = flagNameFor(operation, exclusive.ids);
14034
+ if (flags[camel(cap)] === undefined || flags[camel(ids)] === undefined)
14035
+ return;
14036
+ throw new SimApiError(`--${cap} caps a --filter match and does nothing to an explicit --${ids} list; pass one, not both`, 0);
14037
+ }
14038
+ var REQUIRED_SELECTORS = {
14039
+ deleteTableRows: { fields: ["filter", "rowIds"], noun: "rows to delete" }
14040
+ };
14041
+ function assertSelectorIsUsable(operation, flags) {
14042
+ const selector = REQUIRED_SELECTORS[operation];
14043
+ if (!selector)
14044
+ return;
14045
+ const [first, second] = selector.fields.map((field) => flagNameFor(operation, field));
14046
+ const given = [first, second].filter((name) => flags[camel(name)] !== undefined);
14047
+ if (given.length === 1)
14048
+ return;
14049
+ throw new SimApiError(given.length === 0 ? `--${first} or --${second} is required to choose the ${selector.noun}` : `--${first} and --${second} choose the ${selector.noun} two different ways; pass one, not both`, 0);
14050
+ }
14051
+ function foldRenamedFlags(operation, commandSpec, flags) {
14052
+ for (const [field, flag] of Object.entries(commandSpec.flags ?? {})) {
14053
+ if (!flag.renamedFrom?.length)
14054
+ continue;
14055
+ const current = flagNameFor(operation, field);
14056
+ for (const previous of flag.renamedFrom) {
14057
+ const supplied = flags[camel(previous)];
14058
+ if (supplied === undefined)
14059
+ continue;
14060
+ if (flags[camel(current)] !== undefined) {
14061
+ throw new SimApiError(`--${previous} is the former name of --${current}; pass one, not both`, 0);
14062
+ }
14063
+ warnRenamedFlag(previous, current);
14064
+ flags[camel(current)] = supplied;
14065
+ }
14066
+ }
14067
+ }
14068
+ async function executeOperation(operation, commandSpec, operationSpec, invocation) {
14069
+ const host = invocation[invocation.length - 1];
14070
+ const inheritedFlags = host.optsWithGlobals();
14071
+ const flags = {
14072
+ ...inheritedFlags.workspace === undefined ? {} : { workspace: inheritedFlags.workspace },
14073
+ ...inheritedFlags.allWorkspaces === undefined ? {} : { allWorkspaces: inheritedFlags.allWorkspaces },
14074
+ ...invocation[invocation.length - 2]
14075
+ };
14076
+ const pathPositionalCount = operationSpec.pathParams.filter((param) => !commandSpec.pathFlags?.[param] && !isProfileWorkspacePath(commandSpec, param)).length;
14077
+ const positional = invocation.slice(0, pathPositionalCount);
14078
+ const requestFlags = { ...flags };
14079
+ for (const [index, field] of (commandSpec.positionals ?? []).entries()) {
14080
+ requestFlags[camel(flagNameFor(operation, field))] = invocation[pathPositionalCount + index];
14081
+ }
14082
+ foldRenamedFlags(operation, commandSpec, requestFlags);
14083
+ assertCapIsUsable(operation, requestFlags);
14084
+ assertSelectorIsUsable(operation, requestFlags);
14085
+ if (commandSpec.confirm && !requestFlags.yes && requestFlags.dryRun !== true) {
14086
+ throw new SimApiError(`${commandSpec.confirm} Re-run with --yes to confirm.`, 0);
14087
+ }
14088
+ if (commandSpec.allWorkspaces && requestFlags.allWorkspaces && requestFlags.workspace) {
14089
+ throw new SimApiError("--all-workspaces cannot be combined with --workspace", 0);
14090
+ }
14091
+ const { client, profile } = clientFrom(host);
14092
+ const hasWorkspaceField = Boolean(operationSpec.query && PROFILE_INJECTED_FIELD in operationSpec.query || operationSpec.body && PROFILE_INJECTED_FIELD in operationSpec.body);
14093
+ const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
14094
+ const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
14095
+ const paging = cursorSlot(operationSpec);
14096
+ const pagedLimit = paging ? readPagedLimit(requestFlags.limit) : 0;
14097
+ const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14098
+ if (paging) {
14099
+ const limit = pagedLimit === 0 ? Number.POSITIVE_INFINITY : pagedLimit;
14100
+ const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
14101
+ const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
14102
+ const rows = [];
14103
+ const progress = pageProgress();
14104
+ let cursor = null;
14105
+ let envelope;
14106
+ try {
14107
+ do {
14108
+ const page = await client.request(request.path, {
14109
+ method: operationSpec.method,
14110
+ headers: request.headers,
14111
+ query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
14112
+ body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
14113
+ });
14114
+ envelope = foldPageEnvelope(envelope, page);
14115
+ rows.push(...page.data);
14116
+ cursor = page.nextCursor;
14117
+ if (cursor && rows.length < limit)
14118
+ progress.advance(rows.length);
14119
+ } while (cursor && rows.length < limit);
14120
+ } finally {
14121
+ progress.finish();
14122
+ }
14123
+ renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope, { truncated: Boolean(cursor) });
14124
+ return;
14125
+ }
14126
+ const result = await client.request(request.path, {
14127
+ method: operationSpec.method,
14128
+ headers: request.headers,
14129
+ query: request.query,
14130
+ body: request.body
14131
+ });
14132
+ const payload = result?.data ?? result;
14133
+ renderResult(operation, profile.output, payload, commandSpec, { expandedTrace: requestFlags.trace === true }, result);
14134
+ const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14135
+ if (failure)
14136
+ throw new SimApiError(failure, 0);
14137
+ }
14138
+
14139
+ // src/runtime/naming.ts
14140
+ var WIRE_IDENTIFIER = /^[a-z]+[A-Z]/;
14141
+ function spellingFor(operation, commandSpec, operationSpec, field) {
14142
+ if (field === PROFILE_INJECTED_FIELD)
14143
+ return "--workspace";
14144
+ if (field === "cursor")
14145
+ return null;
14146
+ if (operationSpec.pathParams.includes(field)) {
14147
+ return commandSpec.pathFlags?.[field] ? `--${pathFlagNameFor(commandSpec, field)}` : `<${commandSpec.pathArgumentNames?.[field] ?? field}>`;
14148
+ }
14149
+ if (commandSpec.positionals?.includes(field))
14150
+ return `<${flagNameFor(operation, field)}>`;
14151
+ if (flagSpecFor(operation, field).omit)
14152
+ return null;
14153
+ if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
14154
+ return null;
14155
+ const declared = operationSpec.query && field in operationSpec.query || operationSpec.body && field in operationSpec.body || operationSpec.headers && field in operationSpec.headers;
14156
+ if (!declared)
14157
+ return null;
14158
+ return `--${flagNameFor(operation, field)}`;
14159
+ }
14160
+ function typeableFields(operation, commandSpec, operationSpec) {
14161
+ const spellings = new Map;
14162
+ const fields = [
14163
+ ...operationSpec.pathParams,
14164
+ ...Object.keys(operationSpec.query ?? {}),
14165
+ ...Object.keys(operationSpec.body ?? {}),
14166
+ ...Object.keys(operationSpec.headers ?? {})
14167
+ ];
14168
+ for (const field of fields) {
14169
+ if (spellings.has(field))
14170
+ continue;
14171
+ const spelling = spellingFor(operation, commandSpec, operationSpec, field);
14172
+ if (spelling)
14173
+ spellings.set(field, spelling);
14174
+ }
14175
+ return spellings;
14176
+ }
14177
+ function retypeMessage(message, spellings) {
14178
+ let retyped = message;
14179
+ for (const [field, spelling] of spellings) {
14180
+ if (!WIRE_IDENTIFIER.test(field))
14181
+ continue;
14182
+ retyped = retyped.replaceAll(new RegExp(`\\b${field}\\b`, "g"), spelling);
14183
+ }
14184
+ if (retyped === message)
14185
+ return message;
14186
+ for (const field of spellings.keys()) {
14187
+ if (WIRE_IDENTIFIER.test(field))
14188
+ continue;
14189
+ if (new RegExp(`\\b${field}\\b`).test(message))
14190
+ return message;
14191
+ }
14192
+ return retyped;
14193
+ }
14194
+ function retypeDetails(details, spellings) {
14195
+ if (Array.isArray(details))
14196
+ return details.map((issue2) => retypeDetails(issue2, spellings));
14197
+ if (!details || typeof details !== "object")
14198
+ return details;
14199
+ const issue = details;
14200
+ const retyped = { ...issue };
14201
+ if (Array.isArray(issue.path) && issue.path.length > 0) {
14202
+ const [head, ...rest] = issue.path.map(String);
14203
+ const spelling = spellings.get(head);
14204
+ if (spelling)
14205
+ retyped.path = [spelling, ...rest];
14206
+ }
14207
+ if (typeof issue.message === "string") {
14208
+ retyped.message = retypeMessage(issue.message, spellings);
14209
+ }
14210
+ if (Array.isArray(issue.errors)) {
14211
+ retyped.errors = retypeDetails(issue.errors, spellings);
14212
+ }
14213
+ return retyped;
14214
+ }
14215
+ function retypeApiError(error, operation, commandSpec, operationSpec) {
14216
+ if (!(error instanceof SimApiError) || error.status === 0)
14217
+ return error;
14218
+ const spellings = typeableFields(operation, commandSpec, operationSpec);
14219
+ if (spellings.size === 0)
14220
+ return error;
14221
+ return new SimApiError(retypeMessage(error.message, spellings), error.status, error.code, error.details === undefined ? undefined : retypeDetails(error.details, spellings));
14222
+ }
14223
+
14224
+ // src/runtime/build.ts
14225
+ var GROUP_ALIASES = {
14226
+ "audit-logs": "audit-log",
14227
+ credentials: "credential",
14228
+ "custom-tools": "custom-tool",
14229
+ files: "file",
14230
+ knowledge: "kb",
14231
+ logs: "log",
14232
+ "mcp-servers": "mcp-server",
14233
+ secrets: "secret",
14234
+ skills: "skill",
14235
+ tables: "table",
14236
+ workflows: "workflow",
14237
+ workspaces: "workspace"
14238
+ };
14239
+ function describeOperation(operationSpec, described) {
14240
+ return operationSpec.personalKeyOnly ? `${described} (personal API key required)` : described;
14241
+ }
14242
+ function argumentSyntax(command) {
14243
+ return command.registeredArguments.map((argument) => {
14244
+ const name = `${argument.name()}${argument.variadic ? "..." : ""}`;
14245
+ return argument.required ? `<${name}>` : `[${name}]`;
14246
+ }).join(" ");
14247
+ }
14248
+ function commandPath(command) {
14249
+ const names = [];
14250
+ let current = command;
14251
+ while (current) {
14252
+ names.unshift(current.name());
14253
+ current = current.parent;
14254
+ }
14255
+ return names.join(" ");
14256
+ }
14257
+ function addMissingArgumentExample(command) {
14258
+ const outputError = command.configureOutput().outputError;
14259
+ if (!outputError)
14260
+ throw new Error("Commander output formatter is not configured");
14261
+ command.configureOutput({
14262
+ outputError: (message, write) => {
14263
+ outputError(message, write);
14264
+ if (!message.startsWith("error: missing required argument "))
14265
+ return;
14266
+ const syntax = argumentSyntax(command);
14267
+ const example = syntax ? `${commandPath(command)} ${syntax}` : commandPath(command);
14268
+ write(`Example: ${example}
14269
+ `);
14270
+ }
14271
+ });
14272
+ return command;
14273
+ }
14274
+ function assertNoReservedFlags(command, operation) {
14275
+ for (const option of command.options) {
14276
+ for (const flag of [option.long, option.short]) {
14277
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
14278
+ throw new Error(`${operation} declares ${flag}, which the root program already owns; give the flag another name`);
14279
+ }
14280
+ }
14281
+ }
14282
+ }
14283
+ var RESERVED_FLAG_EXEMPTIONS = new Set(["profiles add"]);
14284
+ function assertNoReservedProgramFlags(program2) {
14285
+ const walk = (command, prefix) => {
14286
+ const path = [...prefix, command.name()];
14287
+ const name = path.join(" ");
14288
+ if (!RESERVED_FLAG_EXEMPTIONS.has(name)) {
14289
+ for (const option of command.options) {
14290
+ for (const flag of [option.long, option.short]) {
14291
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
14292
+ throw new Error(`"sim ${name}" declares ${flag}, which the root program already owns; give the flag another name`);
14293
+ }
14294
+ }
14295
+ }
14296
+ }
14297
+ for (const child of command.commands)
14298
+ walk(child, path);
14299
+ };
14300
+ for (const child of program2.commands)
14301
+ walk(child, []);
14302
+ }
14303
+ function refuseHelpAfterUnknownCommand(program2) {
14304
+ const walk = (command) => {
14305
+ const internals = command;
14306
+ const dispatchesOnly = command.commands.length > 0 && !internals._actionHandler && command.registeredArguments.length === 0;
14307
+ if (dispatchesOnly) {
14308
+ const known = new Set(["help"]);
14309
+ for (const child of command.commands) {
14310
+ known.add(child.name());
14311
+ for (const alias of child.aliases())
14312
+ known.add(alias);
14313
+ }
14314
+ command.on("beforeHelp", () => {
14315
+ const first = command.args[0];
14316
+ if (first === undefined || first.startsWith("-") || known.has(first))
14317
+ return;
14318
+ internals.unknownCommand();
14319
+ });
14320
+ }
14321
+ for (const child of command.commands)
14322
+ walk(child);
14323
+ };
14324
+ walk(program2);
14325
+ }
14326
+ function configureOperation(command, operation, spec) {
14327
+ const operationSpec = V2_OPERATIONS[operation];
14328
+ command.allowExcessArguments(false);
14329
+ for (const alias of spec.aliases ?? [])
14330
+ command.alias(alias);
14331
+ for (const param of Object.keys(spec.pathFlags ?? {})) {
14332
+ if (!operationSpec.pathParams.includes(param)) {
14333
+ throw new Error(`${operation}.${param} is not a path parameter`);
14334
+ }
14335
+ }
14336
+ for (const param of Object.keys(spec.pathArgumentNames ?? {})) {
14337
+ if (!operationSpec.pathParams.includes(param)) {
14338
+ throw new Error(`${operation}.${param} is not a path parameter`);
14339
+ }
14340
+ if (spec.pathFlags?.[param]) {
14341
+ throw new Error(`${operation}.${param} cannot be both a path argument and a path flag`);
14342
+ }
14343
+ }
14344
+ if (spec.profileWorkspacePath) {
14345
+ if (!operationSpec.pathParams.includes(PROFILE_INJECTED_FIELD)) {
14346
+ throw new Error(`${operation}.profileWorkspacePath requires a workspaceId path parameter`);
14347
+ }
14348
+ if (spec.pathFlags?.[PROFILE_INJECTED_FIELD]) {
14349
+ throw new Error(`${operation}.workspaceId cannot be both profile-injected and a path flag`);
14350
+ }
14351
+ }
14352
+ for (const param of operationSpec.pathParams) {
14353
+ if (spec.pathFlags?.[param] || isProfileWorkspacePath(spec, param))
14354
+ continue;
14355
+ command.argument(`<${spec.pathArgumentNames?.[param] ?? param}>`, operationSpec.pathParamDocs?.[param]);
14356
+ }
14357
+ if (spec.allWorkspaces) {
14358
+ const workspace = operationSpec.query?.workspaceId ?? operationSpec.body?.workspaceId;
14359
+ if (!workspace || workspace.required) {
14360
+ throw new Error(`${operation}.allWorkspaces requires an optional workspaceId field`);
14361
+ }
14362
+ }
14363
+ for (const field of spec.positionals ?? []) {
14364
+ const descriptor = operationSpec.query?.[field] ?? operationSpec.body?.[field];
14365
+ if (!descriptor)
14366
+ throw new Error(`${operation}.${field} is not a request field`);
14367
+ if (spec.requestFields && !spec.requestFields.includes(field)) {
14368
+ throw new Error(`${operation}.${field} is positional but not exposed`);
14369
+ }
14370
+ command.argument(`<${flagNameFor(operation, field)}>`, flagSpecFor(operation, field).describe ?? descriptor.describe);
14371
+ }
14372
+ if (spec.requestFields) {
14373
+ for (const field of spec.requestFields) {
14374
+ if (!operationSpec.query?.[field] && !operationSpec.body?.[field] && !operationSpec.headers?.[field]) {
14375
+ throw new Error(`${operation}.${field} is not a request field`);
14376
+ }
14377
+ }
14378
+ for (const slot of ["query", "body", "headers"]) {
14379
+ for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
14380
+ if (descriptor.required && field !== PROFILE_INJECTED_FIELD && !spec.requestFields.includes(field)) {
14381
+ throw new Error(`${operation}.${field} is required but not exposed`);
14382
+ }
14383
+ }
14384
+ }
14385
+ }
14386
+ command.description(describeOperation(operationSpec, spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`));
14387
+ addOperationOptions(command, operation, spec, operationSpec);
14388
+ assertNoReservedFlags(command, operation);
14389
+ command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation).catch((error) => {
14390
+ throw retypeApiError(error, operation, spec, operationSpec);
14391
+ }));
14392
+ return command;
14393
+ }
14394
+ function buildLeaf(operation, spec, leafName) {
14395
+ return addMissingArgumentExample(configureOperation(new Command(leafName), operation, spec));
14396
+ }
14397
+ function addRenamedCommand(groups, operation, spec, from, to) {
14398
+ const segments = from.split(" ");
14399
+ const [groupName, ...rest] = segments;
14400
+ if (rest.length === 0)
14401
+ throw new Error(`${operation}.renamedFrom "${from}" must include a verb`);
14402
+ let parent = groupFor(groups, groupName);
14403
+ for (const segment of rest.slice(0, -1)) {
14404
+ parent = nestedGroup(parent, segment, { hidden: true });
14405
+ }
14406
+ const leaf = buildLeaf(operation, spec, rest[rest.length - 1]);
14407
+ leaf.hook("preAction", () => warnRenamedCommand(from, to));
14408
+ addSubcommand(parent, leaf, { hidden: true });
14409
+ }
14410
+ function addSubcommand(parent, child, options = {}) {
14411
+ const wasLeaf = parent.commands.length === 0 && parent.registeredArguments.length > 0;
14412
+ const usage = wasLeaf ? parent.usage() : null;
14413
+ parent.addCommand(child, { hidden: options.hidden });
14414
+ if (usage !== null)
14415
+ parent.usage(usage);
14416
+ }
14417
+ function groupFor(groups, name) {
14418
+ const existing = groups.get(name);
14419
+ if (existing)
14420
+ return existing;
14421
+ const group = new Command(name).description(`Manage ${name.replaceAll("-", " ")}`);
14422
+ const alias = GROUP_ALIASES[name];
14423
+ if (alias)
14424
+ group.alias(alias);
14425
+ groups.set(name, group);
14426
+ return group;
14427
+ }
14428
+ function resourceLabel(name) {
14429
+ const label = name.endsWith("s") ? name.slice(0, -1) : name;
14430
+ return label.replaceAll("-", " ");
14431
+ }
14432
+ function nestedGroup(parent, name, options = {}) {
14433
+ const existing = parent.commands.find((candidate) => candidate.name() === name);
14434
+ if (existing)
14435
+ return existing;
14436
+ const created = new Command(name).description(`Manage ${resourceLabel(parent.name())} ${name.replaceAll("-", " ")}`);
14437
+ addSubcommand(parent, created, { hidden: options.hidden });
14438
+ return created;
14439
+ }
14440
+ function addLeafCommand(groups, operation, spec, segments) {
14441
+ const [groupName, ...rest] = segments;
14442
+ if (rest.length === 0)
14443
+ throw new Error(`${operation} leaf command must include a verb`);
14444
+ const group = groupFor(groups, groupName);
14445
+ if (rest.length > 1) {
14446
+ let parent = group;
14447
+ for (const segment of rest.slice(0, -1)) {
14448
+ parent = nestedGroup(parent, segment);
14449
+ }
14450
+ parent.addCommand(buildLeaf(operation, spec, rest[rest.length - 1]));
14451
+ return;
14452
+ }
14453
+ group.addCommand(buildLeaf(operation, spec, rest[0]));
14454
+ }
14455
+ function variantCommandSpec(spec, variant) {
14456
+ return {
14457
+ ...spec,
14458
+ command: variant.command,
14459
+ groupDefault: false,
14460
+ aliases: [],
14461
+ positionals: variant.positionals,
14462
+ requestFields: variant.requestFields,
14463
+ variants: [],
14464
+ describe: variant.describe ?? spec.describe
14465
+ };
14466
+ }
14467
+ function buildGeneratedCommands() {
14468
+ const groups = new Map;
14469
+ const renamed = [];
14470
+ for (const operation of Object.keys(V2_OPERATIONS)) {
14471
+ const spec = CLI_CONTRACT[operation] ?? {};
14472
+ const operationSpec = V2_OPERATIONS[operation];
14473
+ if (spec.hidden || operationSpec.responseMode !== "json")
14474
+ continue;
14475
+ const segments = spec.command ? spec.command.split(" ") : deriveCommandPath(operation);
14476
+ if (spec.groupDefault) {
14477
+ const [groupName, ...rest] = segments;
14478
+ const group = groupFor(groups, groupName);
14479
+ if (rest.length > 0)
14480
+ throw new Error(`${operation} groupDefault must name a command group`);
14481
+ const pathPositionals = operationSpec.pathParams.filter((param) => !spec.pathFlags?.[param] && !isProfileWorkspacePath(spec, param));
14482
+ if (pathPositionals.length > 0 || spec.positionals?.length) {
14483
+ throw new Error(`${operation} groupDefault cannot require positional arguments`);
14484
+ }
14485
+ configureOperation(group, operation, spec);
14486
+ } else {
14487
+ addLeafCommand(groups, operation, spec, segments);
14488
+ }
14489
+ for (const variant of spec.variants ?? []) {
14490
+ addLeafCommand(groups, operation, variantCommandSpec(spec, variant), variant.command.split(" "));
14491
+ }
14492
+ for (const from of spec.renamedFrom ?? []) {
14493
+ renamed.push({ operation, spec, from, to: segments.join(" ") });
14494
+ }
14495
+ }
14496
+ for (const { operation, spec, from, to } of renamed) {
14497
+ addRenamedCommand(groups, operation, spec, from, to);
14498
+ }
14499
+ return [...groups.values()].sort((a, b) => a.name().localeCompare(b.name()));
14500
+ }
14501
+
14502
+ // src/commands/credentials.ts
14503
+ var CONNECTION_RESULT = {
14504
+ fields: [
14505
+ { header: "connection link", path: "authorizationUrl" },
14506
+ { header: "expires", path: "expiresAt", format: "timestamp" }
14507
+ ]
14508
+ };
14509
+ var SERVICE_ACCOUNT_RESULT = {
14510
+ fields: [
14511
+ { header: "id" },
14512
+ { header: "name", path: "displayName" },
14513
+ { header: "provider", path: "providerId" },
14514
+ { header: "role" },
14515
+ { header: "created", path: "createdAt", format: "timestamp" }
14516
+ ]
14517
+ };
14518
+ function serviceAccountProvider(providers, providerId) {
14519
+ const provider = providers.find((candidate) => candidate.type === "service_account" && candidate.providerId === providerId);
14520
+ if (!provider) {
14521
+ throw new SimApiError(`Unknown service-account provider "${providerId}".`, 0);
13532
14522
  }
13533
14523
  if (!provider.available) {
13534
14524
  throw new SimApiError(`Service-account provider "${providerId}" is not available.`, 0);
@@ -13620,9 +14610,9 @@ function attachCredentialCommands(program2) {
13620
14610
  if (!credentials)
13621
14611
  throw new Error("The generated credentials command group is missing");
13622
14612
  acceptNameOnUpdate(credentials);
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));
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 }));
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 }));
14613
+ credentials.command("create").argument("<providerId>", "Service-account provider to create a credential for").description(describeOperation(V2_OPERATIONS.createServiceAccountCredential, "Create a service-account credential using its discovered provider schema")).requiredOption("--name <displayName>", "Name shown for the credential in Sim (required)").requiredOption("--credentials <json|@file>", "Provider credentials as JSON (or @path / @- to read a file or stdin) (required)").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));
14614
+ credentials.command("connect").argument("<providerId>", "OAuth provider to connect").description(describeOperation(V2_OPERATIONS.createCredentialConnection, "Create a short-lived link for connecting an OAuth provider")).requiredOption("--name <displayName>", "Name shown for the new credential in Sim (required)").action(async (providerId, options, command) => createConnectionLink(command, { providerId, displayName: options.name }));
14615
+ credentials.command("reconnect").argument("<credentialId>", "Existing OAuth credential to re-authorize").description(describeOperation(V2_OPERATIONS.createCredentialConnection, "Create a short-lived link for reconnecting an OAuth credential")).action((credentialId, _options, command) => createConnectionLink(command, { credentialId }));
13626
14616
  }
13627
14617
 
13628
14618
  // src/commands/protocol/result.ts
@@ -13722,6 +14712,12 @@ Examples:
13722
14712
  $ sim chat -c 3f2a… "Which of those run on a schedule?"
13723
14713
  $ sim --output json chat "Summarize yesterday's failed runs" | jq -r '.content'
13724
14714
  `).action(async (message, options, command) => {
14715
+ if (message.trim() === "") {
14716
+ throw new SimApiError("<message> cannot be empty", 0);
14717
+ }
14718
+ if (options.conversation !== undefined && options.conversation.trim() === "") {
14719
+ throw new SimApiError("-c/--conversation cannot be empty — pass the conversation id printed on stderr after each turn", 0);
14720
+ }
13725
14721
  const { client, profile } = clientFrom(command);
13726
14722
  const workspaceId = client.requireWorkspace();
13727
14723
  const response = await client.requestRaw(V2_OPERATIONS.chat.path, {
@@ -13775,7 +14771,7 @@ Examples:
13775
14771
 
13776
14772
  // src/commands/protocol/files-get.ts
13777
14773
  import { once as once2 } from "node:events";
13778
- import { createWriteStream } from "node:fs";
14774
+ import { createWriteStream, rmSync } from "node:fs";
13779
14775
  import { link, lstat, mkdtemp, readlink, rename, rm } from "node:fs/promises";
13780
14776
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
13781
14777
  import { Readable } from "node:stream";
@@ -13831,37 +14827,66 @@ async function streamToFile(body, file, reportedPath = file.path) {
13831
14827
  throw writeFailure(reportedPath, error);
13832
14828
  }
13833
14829
  }
14830
+ var STAGE_SIGNALS = ["SIGINT", "SIGTERM"];
14831
+ function reRaise(signal) {
14832
+ process.kill(process.pid, signal);
14833
+ }
14834
+ function removeStagingOnSignal(stagingDirectory, terminate = reRaise) {
14835
+ const installed = STAGE_SIGNALS.map((signal) => {
14836
+ const onSignal = () => {
14837
+ process.off(signal, onSignal);
14838
+ const directory = stagingDirectory();
14839
+ if (directory) {
14840
+ try {
14841
+ rmSync(directory, { recursive: true, force: true });
14842
+ } catch {}
14843
+ }
14844
+ terminate(signal);
14845
+ };
14846
+ process.on(signal, onSignal);
14847
+ return [signal, onSignal];
14848
+ });
14849
+ return () => {
14850
+ for (const [signal, onSignal] of installed)
14851
+ process.off(signal, onSignal);
14852
+ };
14853
+ }
13834
14854
  async function saveStagedFile(body, target, force) {
13835
14855
  let temporaryDirectory = null;
13836
14856
  let failure = null;
14857
+ const disposeSignalCleanup = removeStagingOnSignal(() => temporaryDirectory);
13837
14858
  try {
13838
- const publicationTarget = force ? await forcedPublicationTarget(target) : target;
13839
- temporaryDirectory = await mkdtemp(join2(dirname2(publicationTarget), ".sim-download-"));
13840
- const temporaryPath = join2(temporaryDirectory, "payload");
13841
- await streamToFile(body, createWriteStream(temporaryPath, { flags: "wx" }), target);
13842
- if (force) {
13843
- await rename(temporaryPath, publicationTarget);
13844
- } else {
13845
- try {
13846
- await link(temporaryPath, publicationTarget);
13847
- } catch (error) {
13848
- throw unsupportedAtomicPublish(target, error) ?? error;
14859
+ try {
14860
+ const publicationTarget = force ? await forcedPublicationTarget(target) : target;
14861
+ temporaryDirectory = await mkdtemp(join2(dirname2(publicationTarget), ".sim-download-"));
14862
+ const temporaryPath = join2(temporaryDirectory, "payload");
14863
+ await streamToFile(body, createWriteStream(temporaryPath, { flags: "wx" }), target);
14864
+ if (force) {
14865
+ await rename(temporaryPath, publicationTarget);
14866
+ } else {
14867
+ try {
14868
+ await link(temporaryPath, publicationTarget);
14869
+ } catch (error) {
14870
+ throw unsupportedAtomicPublish(target, error) ?? error;
14871
+ }
13849
14872
  }
14873
+ } catch (error) {
14874
+ failure = normalizedWriteFailure(target, error);
13850
14875
  }
13851
- } catch (error) {
13852
- failure = normalizedWriteFailure(target, error);
13853
- }
13854
- if (temporaryDirectory) {
13855
- try {
13856
- await rm(temporaryDirectory, { recursive: true, force: true });
13857
- } catch (cleanupError) {
13858
- if (failure)
13859
- throw combinedCleanupFailure(failure, temporaryDirectory, cleanupError);
13860
- throw new SimApiError(`Saved ${target}, but could not remove temporary directory ${temporaryDirectory}: ${cleanupError.message}`, 0);
14876
+ if (temporaryDirectory) {
14877
+ try {
14878
+ await rm(temporaryDirectory, { recursive: true, force: true });
14879
+ } catch (cleanupError) {
14880
+ if (failure)
14881
+ throw combinedCleanupFailure(failure, temporaryDirectory, cleanupError);
14882
+ throw new SimApiError(`Saved ${target}, but could not remove temporary directory ${temporaryDirectory}: ${cleanupError.message}`, 0);
14883
+ }
13861
14884
  }
14885
+ if (failure)
14886
+ throw failure;
14887
+ } finally {
14888
+ disposeSignalCleanup();
13862
14889
  }
13863
- if (failure)
13864
- throw failure;
13865
14890
  }
13866
14891
  async function saveToFile(body, target, force) {
13867
14892
  return saveStagedFile(body, target, force);
@@ -14101,8 +15126,15 @@ function uploadMetadata(options) {
14101
15126
  }
14102
15127
  return metadata;
14103
15128
  }
15129
+ var UPLOAD_RECIPES = [
15130
+ "default",
15131
+ "plain",
15132
+ "markdown",
15133
+ "code"
15134
+ ];
15135
+ var LANGUAGE_TAG_HELP = "Document language tag: hyphen-separated letter and digit subtags, for example en or en-US";
14104
15136
  function attachKnowledgeDocumentUpload(documents) {
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) => {
15137
+ 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").addOption(new Option("--recipe <name>", "Document processing recipe").choices(UPLOAD_RECIPES)).option("--lang <code>", LANGUAGE_TAG_HELP).action(async (knowledgeBaseId, path, options, command) => {
14106
15138
  const { client, profile } = clientFrom(command);
14107
15139
  const workspaceId = client.requireWorkspace();
14108
15140
  const { name, size } = await localFile(path, options.name);
@@ -14172,6 +15204,7 @@ function renderCell2(value, format) {
14172
15204
  }
14173
15205
  var COLUMNS = (CLI_CONTRACT.listLogs?.columns ?? []).map((spec) => ({
14174
15206
  header: spec.header,
15207
+ floor: Math.min(MAX_CELL_WIDTH2, spec.minWidth ?? 0),
14175
15208
  value: (row) => renderCell2(at2(row, spec.path ?? spec.header), spec.format)
14176
15209
  }));
14177
15210
  function oneLine2(value) {
@@ -14188,15 +15221,15 @@ function clamp2(value, width) {
14188
15221
  function createTableWriter() {
14189
15222
  let widths = null;
14190
15223
  return (rows) => {
14191
- const lines = rows.map((row) => COLUMNS.map((column) => oneLine2(column.value(row))));
15224
+ const lines = rows.map((row) => COLUMNS.map((column) => clamp2(oneLine2(column.value(row)), MAX_CELL_WIDTH2)));
14192
15225
  if (!widths) {
14193
- widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
15226
+ widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(column.floor, visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
14194
15227
  const header = widths;
14195
15228
  console.log(source_default.dim(COLUMNS.map((column, index) => pad2(column.header.toUpperCase(), header[index])).join(" ").trimEnd()));
14196
15229
  }
14197
15230
  const locked = widths;
14198
15231
  for (const line of lines) {
14199
- console.log(line.map((cell, index) => pad2(clamp2(cell, locked[index]), locked[index])).join(" ").trimEnd());
15232
+ console.log(line.map((cell, index) => pad2(cell, locked[index])).join(" ").trimEnd());
14200
15233
  }
14201
15234
  };
14202
15235
  }
@@ -14320,7 +15353,7 @@ function isTransient(error) {
14320
15353
  function nonNegativeInteger(raw, flag) {
14321
15354
  const value = Number(raw);
14322
15355
  if (!Number.isSafeInteger(value) || value < 0) {
14323
- throw new SimApiError(`${flag} must be a non-negative integer`, 0);
15356
+ throw new SimApiError(`${flag} must be a whole number of 0 or more`, 0);
14324
15357
  }
14325
15358
  return value;
14326
15359
  }
@@ -14339,175 +15372,74 @@ function attachLogsFollow(logs) {
14339
15372
  ...V2_OPERATIONS.listLogs.query.level.values
14340
15373
  ])).addOption(new Option("--details <level>", "Response detail level; full names each run’s workflow").choices([...V2_OPERATIONS.listLogs.query.details.values]).default("full")).option("-n, --lines <count>", "Recent runs to print before watching", String(DEFAULT_BACKLOG)).option("--interval <seconds>", "Seconds between polls", String(DEFAULT_INTERVAL_SECONDS)).addHelpText("after", `
14341
15374
  Each run prints once, when it is first seen, so its status is the status it had
14342
- at that moment. With --output json every run is a JSON object on its own line
14343
- (JSONL) rather than a member of an array, because a follow never ends and so can
14344
- never close one; --output yaml emits a --- separated document stream. Progress
14345
- and retries go to stderr, leaving stdout a clean stream of rows. Ctrl-C stops the
14346
- follow.
14347
-
14348
- Examples:
14349
- $ sim logs follow --level error
14350
- $ sim logs follow --workflow wf_123 -n 0
14351
- $ sim --output json logs follow | jq -r '.runId'
14352
- `).action(async (options, command) => {
14353
- const lines = nonNegativeInteger(options.lines, "--lines");
14354
- const delay = intervalMs(options.interval);
14355
- const { client, profile } = clientFrom(command);
14356
- const path = V2_OPERATIONS.listLogs.path;
14357
- const query = {
14358
- workspaceId: client.requireWorkspace(),
14359
- workflowIds: options.workflow?.length ? options.workflow.join(",") : undefined,
14360
- folderPaths: options.folder?.length ? options.folder.map(encodeFolderPath).join(",") : undefined,
14361
- triggers: options.trigger?.length ? options.trigger.join(",") : undefined,
14362
- level: options.level,
14363
- details: options.details,
14364
- sortBy: "startedAt",
14365
- sortOrder: "desc"
14366
- };
14367
- const write = createWriter(profile.output);
14368
- const status = followStatus();
14369
- const interrupt = watchForInterrupt();
14370
- const state = { seen: new Map, floor: null };
14371
- try {
14372
- const seed = await collectUnprinted(client, path, query, state, Math.max(lines, 1), 1);
14373
- remember(state, seed.rows);
14374
- state.floor = seed.rows.at(-1)?.startedAt ?? null;
14375
- if (seed.truncated && seed.rows.length < lines) {
14376
- status.warn(`asked for ${lines} earlier runs but a page holds ${seed.rows.length}; following from there — see sim logs list for more`);
14377
- }
14378
- write(lines > 0 ? seed.rows.slice(0, lines).reverse() : []);
14379
- let failures = 0;
14380
- while (!interrupt.interrupted()) {
14381
- await waitFor(failures === 0 ? delay : Math.min(delay * 2 ** failures, MAX_BACKOFF_MS), interrupt.interrupted);
14382
- if (interrupt.interrupted())
14383
- break;
14384
- let fresh;
14385
- try {
14386
- fresh = await collectUnprinted(client, path, query, state, POLL_PAGE_SIZE, MAX_PAGES_PER_POLL);
14387
- } catch (error) {
14388
- if (!isTransient(error))
14389
- throw error;
14390
- failures += 1;
14391
- const next = Math.min(delay * 2 ** failures, MAX_BACKOFF_MS);
14392
- status.note(`poll failed (${error.message}); retrying in ${inSeconds(next)}s…`);
14393
- continue;
14394
- }
14395
- failures = 0;
14396
- status.clear();
14397
- if (fresh.truncated) {
14398
- status.warn(`more than ${MAX_PAGES_PER_POLL * POLL_PAGE_SIZE} runs arrived at once; older ones were skipped — see sim logs list`);
14399
- }
14400
- if (fresh.rows.length === 0)
14401
- continue;
14402
- remember(state, fresh.rows);
14403
- write(fresh.rows.reverse());
14404
- }
14405
- } finally {
14406
- status.clear();
14407
- interrupt.dispose();
14408
- }
14409
- });
14410
- }
14411
-
14412
- // src/runtime/options.ts
14413
- var DEFAULT_LIMIT = 100;
14414
- function describeField(flag, descriptor, name, field) {
14415
- return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
14416
- }
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) {
14425
- if (field === PROFILE_INJECTED_FIELD || field === "cursor")
14426
- return;
14427
- const flag = flagSpecFor(operation, field);
14428
- if (flag.omit)
14429
- return;
14430
- const name = flagNameFor(operation, field);
14431
- const short = flag.short ? `-${flag.short}, ` : "";
14432
- if (field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
14433
- command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
14434
- return;
14435
- }
14436
- const documented = describeField(flag, descriptor, name, field);
14437
- if (descriptor.kind === "boolean" || flag.boolean) {
14438
- const booleanDoc = withoutWireVocabulary(documented);
14439
- if (descriptor.required) {
14440
- command.addOption(new Option(`${short}--${name} <true|false>`, `${booleanDoc} (required)`).choices(["true", "false"]).makeOptionMandatory());
14441
- return;
14442
- }
14443
- command.option(`${short}--${name}`, booleanDoc);
14444
- if (!flag.boolean || flag.negatable) {
14445
- command.option(`--no-${name}`, `Send --${name} as false`);
14446
- }
14447
- return;
14448
- }
14449
- const takesList = flag.list === true;
14450
- const wantsJson = takesJson(descriptor, flag);
14451
- const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
14452
- const choices = flag.choices ?? descriptor.values;
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) : ""}`;
14455
- const renamedFrom = flag.renamedFrom ?? [];
14456
- const option = new Option(`${short}--${name} ${placeholder}`, describe);
14457
- if (flag.hidden)
14458
- option.hideHelp();
14459
- if (choices && !takesList)
14460
- option.choices([...choices]);
14461
- if (descriptor.default !== undefined && field !== "limit") {
14462
- option.default(undefined, String(descriptor.default));
14463
- }
14464
- if (descriptor.required && renamedFrom.length === 0)
14465
- option.makeOptionMandatory();
14466
- command.addOption(option);
14467
- for (const previous of renamedFrom) {
14468
- const retired = new Option(`--${previous} ${placeholder}`).hideHelp();
14469
- if (choices && !takesList)
14470
- retired.choices([...choices]);
14471
- command.addOption(retired);
14472
- }
14473
- }
14474
- function addOperationOptions(command, operation, commandSpec, operationSpec) {
14475
- for (const param of operationSpec.pathParams) {
14476
- const flag = commandSpec.pathFlags?.[param];
14477
- if (!flag)
14478
- continue;
14479
- const name = pathFlagNameFor(commandSpec, param);
14480
- const short = flag.short ? `-${flag.short}, ` : "";
14481
- command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
14482
- }
14483
- for (const slot of ["query", "body", "headers"]) {
14484
- for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
14485
- if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
14486
- continue;
14487
- if (commandSpec.positionals?.includes(field))
14488
- continue;
14489
- addFieldOption(command, operation, field, descriptor, slot);
14490
- }
14491
- }
14492
- if (commandSpec.allWorkspaces) {
14493
- command.option("--all-workspaces", "Do not filter to the configured workspace (personal API key required for account-wide access)");
14494
- }
14495
- if (commandSpec.expandedTrace) {
14496
- command.option("--trace", "Show expanded trace spans with inputs, outputs, errors, timing, and cost");
14497
- }
14498
- if (operationSpec.opaqueBody) {
14499
- if (commandSpec.bodyVariants) {
14500
- for (const variant of commandSpec.bodyVariants) {
14501
- command.option(`--${variant.name} <json|@file>`, `${variant.describe} (JSON, or @path / @-; choose exactly one body flag)`);
15375
+ at that moment. With --output json every run is a JSON object on its own line
15376
+ (JSONL) rather than a member of an array, because a follow never ends and so can
15377
+ never close one; --output yaml emits a --- separated document stream. Progress
15378
+ and retries go to stderr, leaving stdout a clean stream of rows. Ctrl-C stops the
15379
+ follow.
15380
+
15381
+ Examples:
15382
+ $ sim logs follow --level error
15383
+ $ sim logs follow --workflow 00000000-0000-4000-8000-000000000000 -n 0
15384
+ $ sim --output json logs follow | jq -r '.runId'
15385
+ `).action(async (options, command) => {
15386
+ const lines = nonNegativeInteger(options.lines, "--lines");
15387
+ const delay = intervalMs(options.interval);
15388
+ const { client, profile } = clientFrom(command);
15389
+ const path = V2_OPERATIONS.listLogs.path;
15390
+ const query = {
15391
+ workspaceId: client.requireWorkspace(),
15392
+ workflowIds: options.workflow?.length ? options.workflow.join(",") : undefined,
15393
+ folderPaths: options.folder?.length ? options.folder.map(encodeFolderPath).join(",") : undefined,
15394
+ triggers: options.trigger?.length ? options.trigger.join(",") : undefined,
15395
+ level: options.level,
15396
+ details: options.details,
15397
+ sortBy: "startedAt",
15398
+ sortOrder: "desc"
15399
+ };
15400
+ const write = createWriter(profile.output);
15401
+ const status = followStatus();
15402
+ const interrupt = watchForInterrupt();
15403
+ const state = { seen: new Map, floor: null };
15404
+ try {
15405
+ const seed = await collectUnprinted(client, path, query, state, Math.max(lines, 1), 1);
15406
+ remember(state, seed.rows);
15407
+ state.floor = seed.rows.at(-1)?.startedAt ?? null;
15408
+ if (seed.truncated && seed.rows.length < lines) {
15409
+ status.warn(`asked for ${lines} earlier runs but a page holds ${seed.rows.length}; following from there — see sim logs list for more`);
14502
15410
  }
14503
- } else {
14504
- command.requiredOption("--body <json|@file>", "Request body as JSON (or @path / @- to read a file or stdin) (required)");
15411
+ write(lines > 0 ? seed.rows.slice(0, lines).reverse() : []);
15412
+ let failures = 0;
15413
+ while (!interrupt.interrupted()) {
15414
+ await waitFor(failures === 0 ? delay : Math.min(delay * 2 ** failures, MAX_BACKOFF_MS), interrupt.interrupted);
15415
+ if (interrupt.interrupted())
15416
+ break;
15417
+ let fresh;
15418
+ try {
15419
+ fresh = await collectUnprinted(client, path, query, state, POLL_PAGE_SIZE, MAX_PAGES_PER_POLL);
15420
+ } catch (error) {
15421
+ if (!isTransient(error))
15422
+ throw error;
15423
+ failures += 1;
15424
+ const next = Math.min(delay * 2 ** failures, MAX_BACKOFF_MS);
15425
+ status.note(`poll failed (${error.message}); retrying in ${inSeconds(next)}s…`);
15426
+ continue;
15427
+ }
15428
+ failures = 0;
15429
+ status.clear();
15430
+ if (fresh.truncated) {
15431
+ status.warn(`more than ${MAX_PAGES_PER_POLL * POLL_PAGE_SIZE} runs arrived at once; older ones were skipped — see sim logs list`);
15432
+ }
15433
+ if (fresh.rows.length === 0)
15434
+ continue;
15435
+ remember(state, fresh.rows);
15436
+ write(fresh.rows.reverse());
15437
+ }
15438
+ } finally {
15439
+ status.clear();
15440
+ interrupt.dispose();
14505
15441
  }
14506
- }
14507
- if (commandSpec.confirm) {
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)");
14510
- }
15442
+ });
14511
15443
  }
14512
15444
 
14513
15445
  // src/commands/protocol/resource-directory.ts
@@ -14530,9 +15462,9 @@ async function listResources(client, config, workspaceId, folderPath, search, li
14530
15462
  const paginated = "cursor" in V2_OPERATIONS[config.resources].query;
14531
15463
  if (!paginated) {
14532
15464
  const page = await client.request(path, { query });
14533
- return page.data.slice(0, limit);
15465
+ return { items: page.data.slice(0, limit), truncated: page.data.length > limit };
14534
15466
  }
14535
- return requestAllPages(client, path, {
15467
+ return requestPages(client, path, {
14536
15468
  query,
14537
15469
  pageSize: DEFAULT_LIMIT,
14538
15470
  limit
@@ -14567,7 +15499,7 @@ function attachResourceDirectoryCommands(group, config) {
14567
15499
  group.command("ls").argument("[path]", "Folder path to list; defaults to the root folder").allowExcessArguments(false).description(`List ${config.kind} resources and child folders together`).option("--search <text>", "Filter folders and resources by name").addOption(new Option("--limit <n>", "Maximum combined items to return (0 for everything)").default(String(DEFAULT_LIMIT))).action(async (path, options, command) => {
14568
15500
  const rawLimit = Number(options.limit);
14569
15501
  if (!Number.isSafeInteger(rawLimit) || rawLimit < 0) {
14570
- throw new SimApiError("--limit must be a non-negative integer", 0);
15502
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14571
15503
  }
14572
15504
  const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
14573
15505
  const folderPath = encodeFolderPath(path ?? "/");
@@ -14577,8 +15509,10 @@ function attachResourceDirectoryCommands(group, config) {
14577
15509
  listFolders(client, config.folders, workspaceId, folderPath, options.search),
14578
15510
  listResources(client, config, workspaceId, folderPath, options.search, limit)
14579
15511
  ]);
14580
- const entries = entriesFor(config, folders, resources);
14581
- printList(profile.output, entries.slice(0, limit), COLUMNS2);
15512
+ const entries = entriesFor(config, folders, resources.items);
15513
+ const shown = entries.slice(0, limit);
15514
+ writeCursorTruncation(shown.length, resources.truncated || entries.length > limit);
15515
+ printList(profile.output, shown, COLUMNS2);
14582
15516
  });
14583
15517
  group.command("mkdir").argument("<path>", "Folder path to create; the leading / is optional").allowExcessArguments(false).description(`Create a ${config.kind} directory at a path`).action(async (path, _options, command) => {
14584
15518
  const { client, profile } = clientFrom(command);
@@ -14663,13 +15597,16 @@ function validateTargetOptions(options) {
14663
15597
  return intoExisting;
14664
15598
  }
14665
15599
  function attachTableImport(tables) {
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) => {
15600
+ 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("-y, --yes", "Confirm this destructive operation (required with --mode replace)").option("--no-wait", "Return once the import is queued instead of watching it").action(async (path, options, command) => {
14667
15601
  const { client, profile } = clientFrom(command);
14668
15602
  const workspaceId = client.requireWorkspace();
14669
15603
  if (Boolean(path) === Boolean(options.fileId)) {
14670
15604
  throw new SimApiError("Pass exactly one of <path> or --file-id <id>", 0);
14671
15605
  }
14672
15606
  const intoExisting = validateTargetOptions(options);
15607
+ if (intoExisting && options.mode === "replace" && options.yes !== true) {
15608
+ throw new SimApiError("This deletes every row in the table before loading the CSV and cannot be undone. Re-run with --yes to confirm.", 0);
15609
+ }
14673
15610
  const local = path ? await localFile(path) : null;
14674
15611
  const source = local ? {
14675
15612
  type: "upload",
@@ -14724,202 +15661,18 @@ function attachTableImport(tables) {
14724
15661
  });
14725
15662
  return;
14726
15663
  }
14727
- const finished = await watchImport(client, workspaceId, job);
14728
- if (finished.status !== "completed") {
14729
- throw new SimApiError(`Import ${finished.status}${finished.error ? `: ${finished.error}` : ""}`, 0);
14730
- }
14731
- printProtocolResult(profile.output, {
14732
- id: finished.id,
14733
- status: finished.status,
14734
- tableId: finished.tableId,
14735
- rowsProcessed: finished.rowsProcessed,
14736
- ...rejectionFields(finished)
14737
- });
14738
- });
14739
- }
14740
-
14741
- // src/runtime/renamed.ts
14742
- var warned = new Set;
14743
- function warn(kind, from, to) {
14744
- const key = `${kind}:${from}`;
14745
- if (warned.has(key))
14746
- return;
14747
- warned.add(key);
14748
- process.stderr.write(`warning: ${kind} "${from}" has been renamed to "${to}". The old name still works.
14749
- `);
14750
- }
14751
- function warnRenamedCommand(from, to) {
14752
- warn("command", `sim ${from}`, `sim ${to}`);
14753
- }
14754
- function warnRenamedFlag(from, to) {
14755
- warn("flag", `--${from}`, `--${to}`);
14756
- }
14757
-
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
- }
14828
- function cursorSlot(operationSpec) {
14829
- if (operationSpec.query && "cursor" in operationSpec.query)
14830
- return "query";
14831
- if (operationSpec.body && "cursor" in operationSpec.body)
14832
- return "body";
14833
- return null;
14834
- }
14835
- function foldRenamedFlags(operation, commandSpec, flags) {
14836
- for (const [field, flag] of Object.entries(commandSpec.flags ?? {})) {
14837
- if (!flag.renamedFrom?.length)
14838
- continue;
14839
- const current = flagNameFor(operation, field);
14840
- for (const previous of flag.renamedFrom) {
14841
- const supplied = flags[camel(previous)];
14842
- if (supplied === undefined)
14843
- continue;
14844
- if (flags[camel(current)] !== undefined) {
14845
- throw new SimApiError(`--${previous} is the former name of --${current}; pass one, not both`, 0);
14846
- }
14847
- warnRenamedFlag(previous, current);
14848
- flags[camel(current)] = supplied;
14849
- }
14850
- }
14851
- }
14852
- async function executeOperation(operation, commandSpec, operationSpec, invocation) {
14853
- const host = invocation[invocation.length - 1];
14854
- const inheritedFlags = host.optsWithGlobals();
14855
- const flags = {
14856
- ...inheritedFlags.workspace === undefined ? {} : { workspace: inheritedFlags.workspace },
14857
- ...inheritedFlags.allWorkspaces === undefined ? {} : { allWorkspaces: inheritedFlags.allWorkspaces },
14858
- ...invocation[invocation.length - 2]
14859
- };
14860
- const pathPositionalCount = operationSpec.pathParams.filter((param) => !commandSpec.pathFlags?.[param] && !isProfileWorkspacePath(commandSpec, param)).length;
14861
- const positional = invocation.slice(0, pathPositionalCount);
14862
- const requestFlags = { ...flags };
14863
- for (const [index, field] of (commandSpec.positionals ?? []).entries()) {
14864
- requestFlags[camel(flagNameFor(operation, field))] = invocation[pathPositionalCount + index];
14865
- }
14866
- foldRenamedFlags(operation, commandSpec, requestFlags);
14867
- if (commandSpec.confirm && !requestFlags.yes && requestFlags.dryRun !== true) {
14868
- throw new SimApiError(`${commandSpec.confirm} Re-run with --yes to confirm.`, 0);
14869
- }
14870
- if (commandSpec.allWorkspaces && requestFlags.allWorkspaces && requestFlags.workspace) {
14871
- throw new SimApiError("--all-workspaces cannot be combined with --workspace", 0);
14872
- }
14873
- const { client, profile } = clientFrom(host);
14874
- const hasWorkspaceField = Boolean(operationSpec.query && PROFILE_INJECTED_FIELD in operationSpec.query || operationSpec.body && PROFILE_INJECTED_FIELD in operationSpec.body);
14875
- const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
14876
- const request = buildRequest(operation, positional, requestFlags, hasWorkspaceField && !omitsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14877
- const paging = cursorSlot(operationSpec);
14878
- if (paging) {
14879
- const rawLimit = Number.parseInt(String(requestFlags.limit ?? DEFAULT_LIMIT), 10);
14880
- if (Number.isNaN(rawLimit) || rawLimit < 0) {
14881
- throw new SimApiError("--limit must be a non-negative number", 0);
14882
- }
14883
- const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
14884
- const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
14885
- const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
14886
- const rows = [];
14887
- const progress = pageProgress();
14888
- let cursor = null;
14889
- let envelope;
14890
- try {
14891
- do {
14892
- const page = await client.request(request.path, {
14893
- method: operationSpec.method,
14894
- headers: request.headers,
14895
- query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
14896
- body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
14897
- });
14898
- envelope ??= page;
14899
- rows.push(...page.data);
14900
- cursor = page.nextCursor;
14901
- if (cursor && rows.length < limit)
14902
- progress.advance(rows.length);
14903
- } while (cursor && rows.length < limit);
14904
- } finally {
14905
- progress.finish();
14906
- }
14907
- renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope);
14908
- return;
14909
- }
14910
- const result = await client.request(request.path, {
14911
- method: operationSpec.method,
14912
- headers: request.headers,
14913
- query: request.query,
14914
- body: request.body
14915
- });
14916
- const payload = result?.data ?? result;
14917
- renderResult(operation, profile.output, payload, commandSpec, {
14918
- expandedTrace: requestFlags.trace === true
15664
+ const finished = await watchImport(client, workspaceId, job);
15665
+ if (finished.status !== "completed") {
15666
+ throw new SimApiError(`Import ${finished.status}${finished.error ? `: ${finished.error}` : ""}`, 0);
15667
+ }
15668
+ printProtocolResult(profile.output, {
15669
+ id: finished.id,
15670
+ status: finished.status,
15671
+ tableId: finished.tableId,
15672
+ rowsProcessed: finished.rowsProcessed,
15673
+ ...rejectionFields(finished)
15674
+ });
14919
15675
  });
14920
- const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14921
- if (failure)
14922
- throw new SimApiError(failure, 0);
14923
15676
  }
14924
15677
 
14925
15678
  // src/commands/protocol/workflow-run-follow.ts
@@ -15141,6 +15894,9 @@ function followOrDelegate(previous) {
15141
15894
  command.setOptionValue("run", selection);
15142
15895
  const flags = command.optsWithGlobals();
15143
15896
  if (flags.follow !== true) {
15897
+ if (Array.isArray(flags.selectOutput) && flags.selectOutput.length > 0) {
15898
+ throw new SimApiError(flags.async === true ? "--select-output shapes a streamed result, and --async returns as soon as the run is queued, so there is no stream to shape. Drop one of them, or read the finished run with: sim workflows runs get <runId> --workflow <workflowId> --select-output <blockId>[.path] — that resource matches block ids, not the block names --select-output takes here." : "--select-output shapes a streamed result; add --follow. To narrow a run that has already finished: sim workflows runs get <runId> --workflow <workflowId> --select-output <blockId>[.path] — that resource matches block ids, not the block names --select-output takes here.", 0);
15899
+ }
15144
15900
  if (flags.includeThinking === true || flags.includeToolCalls === true) {
15145
15901
  throw new SimApiError("--include-thinking and --include-tool-calls describe a stream; add --follow", 0);
15146
15902
  }
@@ -15455,6 +16211,8 @@ function validateWorkspaceOnlyFlag(flag, value, scope) {
15455
16211
  async function readSecretValue(options) {
15456
16212
  if (options.value !== undefined)
15457
16213
  return validateSecretValue(readValueArgument(options.value));
16214
+ if (options.description !== undefined || options.unredacted !== undefined)
16215
+ return;
15458
16216
  try {
15459
16217
  return validateSecretValue(await promptSecret());
15460
16218
  } catch (error) {
@@ -15464,7 +16222,10 @@ async function readSecretValue(options) {
15464
16222
  return process.exit(CANCELLED_EXIT_CODE);
15465
16223
  }
15466
16224
  }
15467
- async function setSecret(name, options, command) {
16225
+ async function setSecret(name, options, command, redactionSpellings) {
16226
+ if (redactionSpellings.size > 1) {
16227
+ throw new SimApiError("Pass either --unredacted or --no-unredacted, not both: they are one setting, and commander keeps only whichever came last.", 0);
16228
+ }
15468
16229
  const description = validateWorkspaceOnlyFlag("description", options.description, options.scope);
15469
16230
  const unredacted = validateWorkspaceOnlyFlag("unredacted", options.unredacted, options.scope);
15470
16231
  const value = await readSecretValue(options);
@@ -15475,7 +16236,7 @@ async function setSecret(name, options, command) {
15475
16236
  body: {
15476
16237
  workspaceId: client.requireWorkspace(),
15477
16238
  scope: options.scope,
15478
- value,
16239
+ ...value === undefined ? {} : { value },
15479
16240
  description,
15480
16241
  ...unredacted === undefined ? {} : { unredacted }
15481
16242
  }
@@ -15486,257 +16247,8 @@ function attachSecretCommands(program2) {
15486
16247
  const secrets = program2.commands.find((command) => command.name() === "secrets");
15487
16248
  if (!secrets)
15488
16249
  throw new Error("The generated secrets command group is missing");
15489
- 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));
15490
- }
15491
-
15492
- // src/runtime/build.ts
15493
- var GROUP_ALIASES = {
15494
- "audit-logs": "audit-log",
15495
- credentials: "credential",
15496
- "custom-tools": "custom-tool",
15497
- files: "file",
15498
- knowledge: "kb",
15499
- logs: "log",
15500
- "mcp-servers": "mcp-server",
15501
- secrets: "secret",
15502
- skills: "skill",
15503
- tables: "table",
15504
- workflows: "workflow",
15505
- workspaces: "workspace"
15506
- };
15507
- function argumentSyntax(command) {
15508
- return command.registeredArguments.map((argument) => {
15509
- const name = `${argument.name()}${argument.variadic ? "..." : ""}`;
15510
- return argument.required ? `<${name}>` : `[${name}]`;
15511
- }).join(" ");
15512
- }
15513
- function commandPath(command) {
15514
- const names = [];
15515
- let current = command;
15516
- while (current) {
15517
- names.unshift(current.name());
15518
- current = current.parent;
15519
- }
15520
- return names.join(" ");
15521
- }
15522
- function addMissingArgumentExample(command) {
15523
- const outputError = command.configureOutput().outputError;
15524
- if (!outputError)
15525
- throw new Error("Commander output formatter is not configured");
15526
- command.configureOutput({
15527
- outputError: (message, write) => {
15528
- outputError(message, write);
15529
- if (!message.startsWith("error: missing required argument "))
15530
- return;
15531
- const syntax = argumentSyntax(command);
15532
- const example = syntax ? `${commandPath(command)} ${syntax}` : commandPath(command);
15533
- write(`Example: ${example}
15534
- `);
15535
- }
15536
- });
15537
- return command;
15538
- }
15539
- function assertNoReservedFlags(command, operation) {
15540
- for (const option of command.options) {
15541
- for (const flag of [option.long, option.short]) {
15542
- if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
15543
- throw new Error(`${operation} declares ${flag}, which the root program already owns; give the flag another name`);
15544
- }
15545
- }
15546
- }
15547
- }
15548
- var RESERVED_FLAG_EXEMPTIONS = new Set(["profiles add"]);
15549
- function assertNoReservedProgramFlags(program2) {
15550
- const walk = (command, prefix) => {
15551
- const path = [...prefix, command.name()];
15552
- const name = path.join(" ");
15553
- if (!RESERVED_FLAG_EXEMPTIONS.has(name)) {
15554
- for (const option of command.options) {
15555
- for (const flag of [option.long, option.short]) {
15556
- if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
15557
- throw new Error(`"sim ${name}" declares ${flag}, which the root program already owns; give the flag another name`);
15558
- }
15559
- }
15560
- }
15561
- }
15562
- for (const child of command.commands)
15563
- walk(child, path);
15564
- };
15565
- for (const child of program2.commands)
15566
- walk(child, []);
15567
- }
15568
- function configureOperation(command, operation, spec) {
15569
- const operationSpec = V2_OPERATIONS[operation];
15570
- command.allowExcessArguments(false);
15571
- for (const alias of spec.aliases ?? [])
15572
- command.alias(alias);
15573
- for (const param of Object.keys(spec.pathFlags ?? {})) {
15574
- if (!operationSpec.pathParams.includes(param)) {
15575
- throw new Error(`${operation}.${param} is not a path parameter`);
15576
- }
15577
- }
15578
- for (const param of Object.keys(spec.pathArgumentNames ?? {})) {
15579
- if (!operationSpec.pathParams.includes(param)) {
15580
- throw new Error(`${operation}.${param} is not a path parameter`);
15581
- }
15582
- if (spec.pathFlags?.[param]) {
15583
- throw new Error(`${operation}.${param} cannot be both a path argument and a path flag`);
15584
- }
15585
- }
15586
- if (spec.profileWorkspacePath) {
15587
- if (!operationSpec.pathParams.includes(PROFILE_INJECTED_FIELD)) {
15588
- throw new Error(`${operation}.profileWorkspacePath requires a workspaceId path parameter`);
15589
- }
15590
- if (spec.pathFlags?.[PROFILE_INJECTED_FIELD]) {
15591
- throw new Error(`${operation}.workspaceId cannot be both profile-injected and a path flag`);
15592
- }
15593
- }
15594
- for (const param of operationSpec.pathParams) {
15595
- if (spec.pathFlags?.[param] || isProfileWorkspacePath(spec, param))
15596
- continue;
15597
- command.argument(`<${spec.pathArgumentNames?.[param] ?? param}>`, operationSpec.pathParamDocs?.[param]);
15598
- }
15599
- if (spec.allWorkspaces) {
15600
- const workspace = operationSpec.query?.workspaceId ?? operationSpec.body?.workspaceId;
15601
- if (!workspace || workspace.required) {
15602
- throw new Error(`${operation}.allWorkspaces requires an optional workspaceId field`);
15603
- }
15604
- }
15605
- for (const field of spec.positionals ?? []) {
15606
- const descriptor = operationSpec.query?.[field] ?? operationSpec.body?.[field];
15607
- if (!descriptor)
15608
- throw new Error(`${operation}.${field} is not a request field`);
15609
- if (spec.requestFields && !spec.requestFields.includes(field)) {
15610
- throw new Error(`${operation}.${field} is positional but not exposed`);
15611
- }
15612
- command.argument(`<${flagNameFor(operation, field)}>`, flagSpecFor(operation, field).describe ?? descriptor.describe);
15613
- }
15614
- if (spec.requestFields) {
15615
- for (const field of spec.requestFields) {
15616
- if (!operationSpec.query?.[field] && !operationSpec.body?.[field] && !operationSpec.headers?.[field]) {
15617
- throw new Error(`${operation}.${field} is not a request field`);
15618
- }
15619
- }
15620
- for (const slot of ["query", "body", "headers"]) {
15621
- for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
15622
- if (descriptor.required && field !== PROFILE_INJECTED_FIELD && !spec.requestFields.includes(field)) {
15623
- throw new Error(`${operation}.${field} is required but not exposed`);
15624
- }
15625
- }
15626
- }
15627
- }
15628
- command.description(spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`);
15629
- addOperationOptions(command, operation, spec, operationSpec);
15630
- assertNoReservedFlags(command, operation);
15631
- command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation));
15632
- return command;
15633
- }
15634
- function buildLeaf(operation, spec, leafName) {
15635
- return addMissingArgumentExample(configureOperation(new Command(leafName), operation, spec));
15636
- }
15637
- function addRenamedCommand(groups, operation, spec, from, to) {
15638
- const segments = from.split(" ");
15639
- const [groupName, ...rest] = segments;
15640
- if (rest.length === 0)
15641
- throw new Error(`${operation}.renamedFrom "${from}" must include a verb`);
15642
- let parent = groupFor(groups, groupName);
15643
- for (const segment of rest.slice(0, -1)) {
15644
- parent = nestedGroup(parent, segment, { hidden: true });
15645
- }
15646
- const leaf = buildLeaf(operation, spec, rest[rest.length - 1]);
15647
- leaf.hook("preAction", () => warnRenamedCommand(from, to));
15648
- addSubcommand(parent, leaf, { hidden: true });
15649
- }
15650
- function addSubcommand(parent, child, options = {}) {
15651
- const wasLeaf = parent.commands.length === 0 && parent.registeredArguments.length > 0;
15652
- const usage = wasLeaf ? parent.usage() : null;
15653
- parent.addCommand(child, { hidden: options.hidden });
15654
- if (usage !== null)
15655
- parent.usage(usage);
15656
- }
15657
- function groupFor(groups, name) {
15658
- const existing = groups.get(name);
15659
- if (existing)
15660
- return existing;
15661
- const group2 = new Command(name).description(`Manage ${name.replaceAll("-", " ")}`);
15662
- const alias = GROUP_ALIASES[name];
15663
- if (alias)
15664
- group2.alias(alias);
15665
- groups.set(name, group2);
15666
- return group2;
15667
- }
15668
- function resourceLabel(name) {
15669
- const label = name.endsWith("s") ? name.slice(0, -1) : name;
15670
- return label.replaceAll("-", " ");
15671
- }
15672
- function nestedGroup(parent, name, options = {}) {
15673
- const existing = parent.commands.find((candidate) => candidate.name() === name);
15674
- if (existing)
15675
- return existing;
15676
- const created = new Command(name).description(`Manage ${resourceLabel(parent.name())} ${name.replaceAll("-", " ")}`);
15677
- addSubcommand(parent, created, { hidden: options.hidden });
15678
- return created;
15679
- }
15680
- function addLeafCommand(groups, operation, spec, segments) {
15681
- const [groupName, ...rest] = segments;
15682
- if (rest.length === 0)
15683
- throw new Error(`${operation} leaf command must include a verb`);
15684
- const group2 = groupFor(groups, groupName);
15685
- if (rest.length > 1) {
15686
- let parent = group2;
15687
- for (const segment of rest.slice(0, -1)) {
15688
- parent = nestedGroup(parent, segment);
15689
- }
15690
- parent.addCommand(buildLeaf(operation, spec, rest[rest.length - 1]));
15691
- return;
15692
- }
15693
- group2.addCommand(buildLeaf(operation, spec, rest[0]));
15694
- }
15695
- function variantCommandSpec(spec, variant) {
15696
- return {
15697
- ...spec,
15698
- command: variant.command,
15699
- groupDefault: false,
15700
- aliases: [],
15701
- positionals: variant.positionals,
15702
- requestFields: variant.requestFields,
15703
- variants: [],
15704
- describe: variant.describe ?? spec.describe
15705
- };
15706
- }
15707
- function buildGeneratedCommands() {
15708
- const groups = new Map;
15709
- const renamed = [];
15710
- for (const operation of Object.keys(V2_OPERATIONS)) {
15711
- const spec = CLI_CONTRACT[operation] ?? {};
15712
- const operationSpec = V2_OPERATIONS[operation];
15713
- if (spec.hidden || operationSpec.responseMode !== "json")
15714
- continue;
15715
- const segments = spec.command ? spec.command.split(" ") : deriveCommandPath(operation);
15716
- if (spec.groupDefault) {
15717
- const [groupName, ...rest] = segments;
15718
- const group2 = groupFor(groups, groupName);
15719
- if (rest.length > 0)
15720
- throw new Error(`${operation} groupDefault must name a command group`);
15721
- const pathPositionals = operationSpec.pathParams.filter((param) => !spec.pathFlags?.[param] && !isProfileWorkspacePath(spec, param));
15722
- if (pathPositionals.length > 0 || spec.positionals?.length) {
15723
- throw new Error(`${operation} groupDefault cannot require positional arguments`);
15724
- }
15725
- configureOperation(group2, operation, spec);
15726
- } else {
15727
- addLeafCommand(groups, operation, spec, segments);
15728
- }
15729
- for (const variant of spec.variants ?? []) {
15730
- addLeafCommand(groups, operation, variantCommandSpec(spec, variant), variant.command.split(" "));
15731
- }
15732
- for (const from of spec.renamedFrom ?? []) {
15733
- renamed.push({ operation, spec, from, to: segments.join(" ") });
15734
- }
15735
- }
15736
- for (const { operation, spec, from, to } of renamed) {
15737
- addRenamedCommand(groups, operation, spec, from, to);
15738
- }
15739
- return [...groups.values()].sort((a, b) => a.name().localeCompare(b.name()));
16250
+ const redactionSpellings = new Set;
16251
+ secrets.command("set").argument("<name>", "Secret name, as referenced in workflows").description(describeOperation(V2_OPERATIONS.setSecret, "Create or replace a named secret")).addOption(new Option("--scope <scope>", "Secret ownership scope (required)").choices([...SECRET_SCOPES]).makeOptionMandatory()).option("--value <value|@file>", "Secret value. Passing it inline exposes it to shell history and process listings; @path reads it from a file and @- from stdin, verbatim — a trailing newline is part of the value, so write the file with printf rather than echo. Prefix a literal leading @ with a second one").option("--description <description>", "What the secret is for, shown to teammates; workspace scope only. Omit to leave an existing description unchanged").option("--unredacted", `${V2_OPERATIONS.setSecret.body.unredacted.describe} Pass --no-unredacted to restore redaction`).option("--no-unredacted", "Send --unredacted as false").on("option:unredacted", () => redactionSpellings.add("--unredacted")).on("option:no-unredacted", () => redactionSpellings.add("--no-unredacted")).action((name, options, command) => setSecret(name, options, command, redactionSpellings));
15740
16252
  }
15741
16253
 
15742
16254
  // src/program.ts
@@ -15816,6 +16328,7 @@ function buildProgram(options = {}) {
15816
16328
  attachProtocolCommands(program2);
15817
16329
  attachSecretCommands(program2);
15818
16330
  program2.addHelpText("after", HELP_EPILOGUE);
16331
+ refuseHelpAfterUnknownCommand(program2);
15819
16332
  assertNoReservedProgramFlags(program2);
15820
16333
  return program2;
15821
16334
  }