sim 2.1.2 → 2.1.3-preview.50.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 +1226 -778
  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,
@@ -6518,7 +6590,8 @@ var V2_OPERATIONS = {
6518
6590
  version: "Numeric deployment version."
6519
6591
  },
6520
6592
  responseMode: "json",
6521
- summary: "Activate Workflow Version"
6593
+ summary: "Activate Workflow Version",
6594
+ personalKeyOnly: true
6522
6595
  },
6523
6596
  addTableColumn: {
6524
6597
  method: "POST",
@@ -6565,6 +6638,7 @@ var V2_OPERATIONS = {
6565
6638
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6566
6639
  responseMode: "json",
6567
6640
  summary: "Index Workspace Files",
6641
+ personalKeyOnly: true,
6568
6642
  body: {
6569
6643
  workspaceId: {
6570
6644
  kind: "string",
@@ -6585,6 +6659,7 @@ var V2_OPERATIONS = {
6585
6659
  pathParamDocs: { workflowId: "Unique workflow identifier." },
6586
6660
  responseMode: "json",
6587
6661
  summary: "Apply Workflow Operations",
6662
+ personalKeyOnly: true,
6588
6663
  query: {
6589
6664
  dryRun: {
6590
6665
  kind: "boolean",
@@ -6684,6 +6759,7 @@ var V2_OPERATIONS = {
6684
6759
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6685
6760
  responseMode: "json",
6686
6761
  summary: "Bulk Save Tag Definitions",
6762
+ personalKeyOnly: true,
6687
6763
  body: {
6688
6764
  workspaceId: {
6689
6765
  kind: "string",
@@ -6707,6 +6783,7 @@ var V2_OPERATIONS = {
6707
6783
  },
6708
6784
  responseMode: "json",
6709
6785
  summary: "Bulk Update Chunks",
6786
+ personalKeyOnly: true,
6710
6787
  body: {
6711
6788
  workspaceId: {
6712
6789
  kind: "string",
@@ -6722,7 +6799,7 @@ var V2_OPERATIONS = {
6722
6799
  chunkIds: {
6723
6800
  kind: "array",
6724
6801
  required: true,
6725
- describe: "Chunks to operate on, by identifier. Ids outside the document are ignored."
6802
+ 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
6803
  }
6727
6804
  }
6728
6805
  },
@@ -6733,6 +6810,7 @@ var V2_OPERATIONS = {
6733
6810
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6734
6811
  responseMode: "json",
6735
6812
  summary: "Bulk Enable or Disable Documents",
6813
+ personalKeyOnly: true,
6736
6814
  body: {
6737
6815
  workspaceId: {
6738
6816
  kind: "string",
@@ -6957,6 +7035,7 @@ var V2_OPERATIONS = {
6957
7035
  pathParams: [],
6958
7036
  responseMode: "json",
6959
7037
  summary: "Create Credential Connection",
7038
+ personalKeyOnly: true,
6960
7039
  body: {
6961
7040
  workspaceId: {
6962
7041
  kind: "string",
@@ -7132,6 +7211,7 @@ var V2_OPERATIONS = {
7132
7211
  },
7133
7212
  responseMode: "json",
7134
7213
  summary: "Create Chunk",
7214
+ personalKeyOnly: true,
7135
7215
  body: {
7136
7216
  workspaceId: {
7137
7217
  kind: "string",
@@ -7157,6 +7237,7 @@ var V2_OPERATIONS = {
7157
7237
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7158
7238
  responseMode: "json",
7159
7239
  summary: "Create Knowledge Connector",
7240
+ personalKeyOnly: true,
7160
7241
  body: {
7161
7242
  workspaceId: {
7162
7243
  kind: "string",
@@ -7272,6 +7353,7 @@ var V2_OPERATIONS = {
7272
7353
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7273
7354
  responseMode: "json",
7274
7355
  summary: "Create Tag",
7356
+ personalKeyOnly: true,
7275
7357
  body: {
7276
7358
  workspaceId: {
7277
7359
  kind: "string",
@@ -7379,6 +7461,7 @@ var V2_OPERATIONS = {
7379
7461
  pathParams: [],
7380
7462
  responseMode: "json",
7381
7463
  summary: "Create Service-Account Credential",
7464
+ personalKeyOnly: true,
7382
7465
  body: {
7383
7466
  workspaceId: {
7384
7467
  kind: "string",
@@ -7417,6 +7500,7 @@ var V2_OPERATIONS = {
7417
7500
  pathParams: [],
7418
7501
  responseMode: "json",
7419
7502
  summary: "Create Skill",
7503
+ personalKeyOnly: true,
7420
7504
  body: {
7421
7505
  workspaceId: {
7422
7506
  kind: "string",
@@ -7632,6 +7716,7 @@ var V2_OPERATIONS = {
7632
7716
  pathParams: [],
7633
7717
  responseMode: "json",
7634
7718
  summary: "Create Workflow MCP Server",
7719
+ personalKeyOnly: true,
7635
7720
  body: {
7636
7721
  workspaceId: {
7637
7722
  kind: "string",
@@ -7662,6 +7747,7 @@ var V2_OPERATIONS = {
7662
7747
  pathParamDocs: { credentialId: "Credential to disconnect." },
7663
7748
  responseMode: "json",
7664
7749
  summary: "Disconnect Credential",
7750
+ personalKeyOnly: true,
7665
7751
  query: {
7666
7752
  workspaceId: {
7667
7753
  kind: "string",
@@ -7752,6 +7838,7 @@ var V2_OPERATIONS = {
7752
7838
  },
7753
7839
  responseMode: "json",
7754
7840
  summary: "Delete Chunk",
7841
+ personalKeyOnly: true,
7755
7842
  query: {
7756
7843
  workspaceId: {
7757
7844
  kind: "string",
@@ -7770,6 +7857,7 @@ var V2_OPERATIONS = {
7770
7857
  },
7771
7858
  responseMode: "json",
7772
7859
  summary: "Delete Knowledge Connector",
7860
+ personalKeyOnly: true,
7773
7861
  query: {
7774
7862
  workspaceId: {
7775
7863
  kind: "string",
@@ -7840,6 +7928,7 @@ var V2_OPERATIONS = {
7840
7928
  },
7841
7929
  responseMode: "json",
7842
7930
  summary: "Delete Tag",
7931
+ personalKeyOnly: true,
7843
7932
  query: {
7844
7933
  workspaceId: {
7845
7934
  kind: "string",
@@ -7855,6 +7944,7 @@ var V2_OPERATIONS = {
7855
7944
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7856
7945
  responseMode: "json",
7857
7946
  summary: "Delete Tag Definitions",
7947
+ personalKeyOnly: true,
7858
7948
  query: {
7859
7949
  workspaceId: {
7860
7950
  kind: "string",
@@ -7886,9 +7976,10 @@ var V2_OPERATIONS = {
7886
7976
  method: "DELETE",
7887
7977
  path: "/api/v2/secrets/[name]",
7888
7978
  pathParams: ["name"],
7889
- pathParamDocs: { name: "Secret to create, replace, or delete." },
7979
+ pathParamDocs: { name: "Secret to delete." },
7890
7980
  responseMode: "json",
7891
7981
  summary: "Delete Secret",
7982
+ personalKeyOnly: true,
7892
7983
  query: {
7893
7984
  workspaceId: {
7894
7985
  kind: "string",
@@ -7912,6 +8003,7 @@ var V2_OPERATIONS = {
7912
8003
  },
7913
8004
  responseMode: "json",
7914
8005
  summary: "Delete Skill",
8006
+ personalKeyOnly: true,
7915
8007
  query: {
7916
8008
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." }
7917
8009
  }
@@ -8022,7 +8114,8 @@ var V2_OPERATIONS = {
8022
8114
  pathParams: ["workflowId"],
8023
8115
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8024
8116
  responseMode: "json",
8025
- summary: "Delete Workflow Chat Deployment"
8117
+ summary: "Delete Workflow Chat Deployment",
8118
+ personalKeyOnly: true
8026
8119
  },
8027
8120
  deleteWorkflowFolder: {
8028
8121
  method: "DELETE",
@@ -8072,7 +8165,8 @@ var V2_OPERATIONS = {
8072
8165
  pathParams: ["serverId"],
8073
8166
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8074
8167
  responseMode: "json",
8075
- summary: "Delete Workflow MCP Server"
8168
+ summary: "Delete Workflow MCP Server",
8169
+ personalKeyOnly: true
8076
8170
  },
8077
8171
  deployWorkflow: {
8078
8172
  method: "POST",
@@ -8081,6 +8175,7 @@ var V2_OPERATIONS = {
8081
8175
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8082
8176
  responseMode: "json",
8083
8177
  summary: "Deploy Workflow",
8178
+ personalKeyOnly: true,
8084
8179
  body: {
8085
8180
  name: { kind: "string", describe: "Optional label for the deployment version." },
8086
8181
  description: {
@@ -8096,6 +8191,7 @@ var V2_OPERATIONS = {
8096
8191
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8097
8192
  responseMode: "json",
8098
8193
  summary: "Publish Workflow As MCP Tool",
8194
+ personalKeyOnly: true,
8099
8195
  body: {
8100
8196
  workflowId: {
8101
8197
  kind: "string",
@@ -8236,6 +8332,7 @@ var V2_OPERATIONS = {
8236
8332
  pathParamDocs: { auditLogId: "Audit-log entry identifier." },
8237
8333
  responseMode: "json",
8238
8334
  summary: "Get Audit Log",
8335
+ personalKeyOnly: true,
8239
8336
  query: {
8240
8337
  organizationId: {
8241
8338
  kind: "string",
@@ -8301,7 +8398,7 @@ var V2_OPERATIONS = {
8301
8398
  kind: "enum",
8302
8399
  values: ["active", "archived"],
8303
8400
  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."
8401
+ 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
8402
  }
8306
8403
  }
8307
8404
  },
@@ -8364,6 +8461,7 @@ var V2_OPERATIONS = {
8364
8461
  },
8365
8462
  responseMode: "json",
8366
8463
  summary: "Get Chunk",
8464
+ personalKeyOnly: true,
8367
8465
  query: {
8368
8466
  workspaceId: {
8369
8467
  kind: "string",
@@ -8382,6 +8480,7 @@ var V2_OPERATIONS = {
8382
8480
  },
8383
8481
  responseMode: "json",
8384
8482
  summary: "Get Knowledge Connector",
8483
+ personalKeyOnly: true,
8385
8484
  query: {
8386
8485
  workspaceId: {
8387
8486
  kind: "string",
@@ -8489,6 +8588,7 @@ var V2_OPERATIONS = {
8489
8588
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
8490
8589
  responseMode: "json",
8491
8590
  summary: "Get Next Tag Slot",
8591
+ personalKeyOnly: true,
8492
8592
  query: {
8493
8593
  workspaceId: {
8494
8594
  kind: "string",
@@ -8656,7 +8756,8 @@ var V2_OPERATIONS = {
8656
8756
  pathParams: ["workflowId"],
8657
8757
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8658
8758
  responseMode: "json",
8659
- summary: "Get Workflow Chat Deployment"
8759
+ summary: "Get Workflow Chat Deployment",
8760
+ personalKeyOnly: true
8660
8761
  },
8661
8762
  getWorkflowDeployment: {
8662
8763
  method: "GET",
@@ -8672,7 +8773,8 @@ var V2_OPERATIONS = {
8672
8773
  pathParams: ["serverId"],
8673
8774
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8674
8775
  responseMode: "json",
8675
- summary: "Get Workflow MCP Server"
8776
+ summary: "Get Workflow MCP Server",
8777
+ personalKeyOnly: true
8676
8778
  },
8677
8779
  getWorkflowRun: {
8678
8780
  method: "GET",
@@ -8739,6 +8841,7 @@ var V2_OPERATIONS = {
8739
8841
  },
8740
8842
  responseMode: "json",
8741
8843
  summary: "Grant Skill Editor",
8844
+ personalKeyOnly: true,
8742
8845
  body: {
8743
8846
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
8744
8847
  email: {
@@ -8779,6 +8882,7 @@ var V2_OPERATIONS = {
8779
8882
  pathParams: [],
8780
8883
  responseMode: "json",
8781
8884
  summary: "List Audit Logs",
8885
+ personalKeyOnly: true,
8782
8886
  query: {
8783
8887
  action: { kind: "string", describe: "Filter by exact action name." },
8784
8888
  resourceType: {
@@ -9116,7 +9220,7 @@ var V2_OPERATIONS = {
9116
9220
  kind: "enum",
9117
9221
  values: ["active", "archived"],
9118
9222
  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."
9223
+ 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
9224
  }
9121
9225
  }
9122
9226
  },
@@ -9158,7 +9262,7 @@ var V2_OPERATIONS = {
9158
9262
  kind: "enum",
9159
9263
  values: ["active", "archived"],
9160
9264
  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."
9265
+ 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
9266
  },
9163
9267
  search: {
9164
9268
  kind: "string",
@@ -9246,6 +9350,7 @@ var V2_OPERATIONS = {
9246
9350
  },
9247
9351
  responseMode: "json",
9248
9352
  summary: "List Chunks",
9353
+ personalKeyOnly: true,
9249
9354
  query: {
9250
9355
  workspaceId: {
9251
9356
  kind: "string",
@@ -9295,6 +9400,7 @@ var V2_OPERATIONS = {
9295
9400
  },
9296
9401
  responseMode: "json",
9297
9402
  summary: "List Knowledge Connector Documents",
9403
+ personalKeyOnly: true,
9298
9404
  query: {
9299
9405
  workspaceId: {
9300
9406
  kind: "string",
@@ -9323,6 +9429,7 @@ var V2_OPERATIONS = {
9323
9429
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9324
9430
  responseMode: "json",
9325
9431
  summary: "List Knowledge Connectors",
9432
+ personalKeyOnly: true,
9326
9433
  query: {
9327
9434
  workspaceId: {
9328
9435
  kind: "string",
@@ -9466,6 +9573,7 @@ var V2_OPERATIONS = {
9466
9573
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9467
9574
  responseMode: "json",
9468
9575
  summary: "List Tag Usage",
9576
+ personalKeyOnly: true,
9469
9577
  query: {
9470
9578
  workspaceId: {
9471
9579
  kind: "string",
@@ -9557,14 +9665,14 @@ var V2_OPERATIONS = {
9557
9665
  },
9558
9666
  includeJobRuns: {
9559
9667
  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.'
9668
+ 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
9669
  },
9562
9670
  runId: { kind: "string", describe: "Exact run identifier to match." },
9563
9671
  sortBy: {
9564
9672
  kind: "enum",
9565
9673
  values: ["startedAt", "durationMs", "cost", "status"],
9566
9674
  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`."
9675
+ 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
9676
  },
9569
9677
  sortOrder: {
9570
9678
  kind: "enum",
@@ -9624,6 +9732,7 @@ var V2_OPERATIONS = {
9624
9732
  pathParamDocs: { mcpServerId: "Unique MCP server identifier." },
9625
9733
  responseMode: "json",
9626
9734
  summary: "List MCP Server Tools",
9735
+ personalKeyOnly: true,
9627
9736
  query: {
9628
9737
  workspaceId: {
9629
9738
  kind: "string",
@@ -9642,6 +9751,7 @@ var V2_OPERATIONS = {
9642
9751
  pathParams: [],
9643
9752
  responseMode: "json",
9644
9753
  summary: "List Secrets",
9754
+ personalKeyOnly: true,
9645
9755
  query: {
9646
9756
  workspaceId: {
9647
9757
  kind: "string",
@@ -9834,7 +9944,7 @@ var V2_OPERATIONS = {
9834
9944
  kind: "enum",
9835
9945
  values: ["active", "archived"],
9836
9946
  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."
9947
+ 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
9948
  },
9839
9949
  folderPath: {
9840
9950
  kind: "string",
@@ -9977,6 +10087,7 @@ var V2_OPERATIONS = {
9977
10087
  pathParams: [],
9978
10088
  responseMode: "json",
9979
10089
  summary: "List Workflow MCP Servers",
10090
+ personalKeyOnly: true,
9980
10091
  query: {
9981
10092
  workspaceId: {
9982
10093
  kind: "string",
@@ -10012,7 +10123,8 @@ var V2_OPERATIONS = {
10012
10123
  pathParams: ["serverId"],
10013
10124
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
10014
10125
  responseMode: "json",
10015
- summary: "List Workflow MCP Tools"
10126
+ summary: "List Workflow MCP Tools",
10127
+ personalKeyOnly: true
10016
10128
  },
10017
10129
  listWorkflowRuns: {
10018
10130
  method: "GET",
@@ -10069,7 +10181,7 @@ var V2_OPERATIONS = {
10069
10181
  kind: "enum",
10070
10182
  values: ["active", "archived"],
10071
10183
  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."
10184
+ 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
10185
  },
10074
10186
  folderPath: {
10075
10187
  kind: "string",
@@ -10372,6 +10484,7 @@ var V2_OPERATIONS = {
10372
10484
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10373
10485
  responseMode: "json",
10374
10486
  summary: "Create or Replace Workflow Chat Deployment",
10487
+ personalKeyOnly: true,
10375
10488
  body: {
10376
10489
  identifier: {
10377
10490
  kind: "string",
@@ -10424,6 +10537,7 @@ var V2_OPERATIONS = {
10424
10537
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10425
10538
  responseMode: "json",
10426
10539
  summary: "Replace Workflow State",
10540
+ personalKeyOnly: true,
10427
10541
  query: {
10428
10542
  dryRun: {
10429
10543
  kind: "boolean",
@@ -10477,7 +10591,7 @@ var V2_OPERATIONS = {
10477
10591
  path: {
10478
10592
  kind: "string",
10479
10593
  required: true,
10480
- describe: "Path of the archived folder to restore, as reported by `GET /api/v2/files/folders?scope=archived`."
10594
+ describe: "Path of the archived folder to restore, as reported by an archived-scope folder list."
10481
10595
  }
10482
10596
  }
10483
10597
  },
@@ -10522,7 +10636,7 @@ var V2_OPERATIONS = {
10522
10636
  path: {
10523
10637
  kind: "string",
10524
10638
  required: true,
10525
- describe: "Path the folder held when `DELETE /api/v2/tables/folders` archived it."
10639
+ describe: "Path the folder held when a folder delete archived it."
10526
10640
  }
10527
10641
  }
10528
10642
  },
@@ -10562,7 +10676,8 @@ var V2_OPERATIONS = {
10562
10676
  version: "Numeric deployment version, or `active` for the currently live version."
10563
10677
  },
10564
10678
  responseMode: "json",
10565
- summary: "Revert Workflow To Version"
10679
+ summary: "Revert Workflow To Version",
10680
+ personalKeyOnly: true
10566
10681
  },
10567
10682
  revokeSkillEditor: {
10568
10683
  method: "DELETE",
@@ -10573,6 +10688,7 @@ var V2_OPERATIONS = {
10573
10688
  },
10574
10689
  responseMode: "json",
10575
10690
  summary: "Revoke Skill Editor",
10691
+ personalKeyOnly: true,
10576
10692
  query: {
10577
10693
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
10578
10694
  email: {
@@ -10589,6 +10705,7 @@ var V2_OPERATIONS = {
10589
10705
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10590
10706
  responseMode: "json",
10591
10707
  summary: "Rollback Workflow",
10708
+ personalKeyOnly: true,
10592
10709
  body: {
10593
10710
  version: {
10594
10711
  kind: "integer",
@@ -10683,9 +10800,10 @@ var V2_OPERATIONS = {
10683
10800
  method: "PUT",
10684
10801
  path: "/api/v2/secrets/[name]",
10685
10802
  pathParams: ["name"],
10686
- pathParamDocs: { name: "Secret to create, replace, or delete." },
10803
+ pathParamDocs: { name: "Secret to create or replace." },
10687
10804
  responseMode: "json",
10688
10805
  summary: "Set Secret",
10806
+ personalKeyOnly: true,
10689
10807
  body: {
10690
10808
  workspaceId: {
10691
10809
  kind: "string",
@@ -10700,8 +10818,7 @@ var V2_OPERATIONS = {
10700
10818
  },
10701
10819
  value: {
10702
10820
  kind: "string",
10703
- required: true,
10704
- describe: "Write-only secret value. It is never returned."
10821
+ 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
10822
  },
10706
10823
  description: {
10707
10824
  kind: "string",
@@ -10723,6 +10840,7 @@ var V2_OPERATIONS = {
10723
10840
  },
10724
10841
  responseMode: "json",
10725
10842
  summary: "Sync Knowledge Connector",
10843
+ personalKeyOnly: true,
10726
10844
  body: {
10727
10845
  workspaceId: {
10728
10846
  kind: "string",
@@ -10760,7 +10878,8 @@ var V2_OPERATIONS = {
10760
10878
  pathParams: ["workflowId"],
10761
10879
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10762
10880
  responseMode: "json",
10763
- summary: "Undeploy Workflow"
10881
+ summary: "Undeploy Workflow",
10882
+ personalKeyOnly: true
10764
10883
  },
10765
10884
  undeployWorkflowMcpTool: {
10766
10885
  method: "DELETE",
@@ -10771,7 +10890,8 @@ var V2_OPERATIONS = {
10771
10890
  workflowId: "Workflow published as a tool on this server."
10772
10891
  },
10773
10892
  responseMode: "json",
10774
- summary: "Unpublish Workflow MCP Tool"
10893
+ summary: "Unpublish Workflow MCP Tool",
10894
+ personalKeyOnly: true
10775
10895
  },
10776
10896
  unzipFile: {
10777
10897
  method: "POST",
@@ -10791,6 +10911,7 @@ var V2_OPERATIONS = {
10791
10911
  pathParamDocs: { credentialId: "Credential to update." },
10792
10912
  responseMode: "json",
10793
10913
  summary: "Update Credential",
10914
+ personalKeyOnly: true,
10794
10915
  query: {
10795
10916
  workspaceId: {
10796
10917
  kind: "string",
@@ -10892,6 +11013,7 @@ var V2_OPERATIONS = {
10892
11013
  },
10893
11014
  responseMode: "json",
10894
11015
  summary: "Update Chunk",
11016
+ personalKeyOnly: true,
10895
11017
  body: {
10896
11018
  workspaceId: {
10897
11019
  kind: "string",
@@ -10918,6 +11040,7 @@ var V2_OPERATIONS = {
10918
11040
  },
10919
11041
  responseMode: "json",
10920
11042
  summary: "Update Knowledge Connector",
11043
+ personalKeyOnly: true,
10921
11044
  body: {
10922
11045
  workspaceId: {
10923
11046
  kind: "string",
@@ -10949,6 +11072,7 @@ var V2_OPERATIONS = {
10949
11072
  },
10950
11073
  responseMode: "json",
10951
11074
  summary: "Update Knowledge Connector Documents",
11075
+ personalKeyOnly: true,
10952
11076
  body: {
10953
11077
  workspaceId: {
10954
11078
  kind: "string",
@@ -10978,6 +11102,7 @@ var V2_OPERATIONS = {
10978
11102
  },
10979
11103
  responseMode: "json",
10980
11104
  summary: "Update Document",
11105
+ personalKeyOnly: true,
10981
11106
  body: {
10982
11107
  workspaceId: {
10983
11108
  kind: "string",
@@ -11022,6 +11147,7 @@ var V2_OPERATIONS = {
11022
11147
  },
11023
11148
  responseMode: "json",
11024
11149
  summary: "Update Tag",
11150
+ personalKeyOnly: true,
11025
11151
  body: {
11026
11152
  workspaceId: {
11027
11153
  kind: "string",
@@ -11126,6 +11252,7 @@ var V2_OPERATIONS = {
11126
11252
  },
11127
11253
  responseMode: "json",
11128
11254
  summary: "Update Skill",
11255
+ personalKeyOnly: true,
11129
11256
  body: {
11130
11257
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
11131
11258
  name: { kind: "string", describe: "New kebab-case skill name." },
@@ -11263,6 +11390,7 @@ var V2_OPERATIONS = {
11263
11390
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
11264
11391
  responseMode: "json",
11265
11392
  summary: "Update Workflow MCP Server",
11393
+ personalKeyOnly: true,
11266
11394
  body: {
11267
11395
  name: { kind: "string", describe: "Server display name, shown to connecting MCP clients." },
11268
11396
  description: { kind: "string", describe: "New server description, or null to clear it." },
@@ -11279,6 +11407,7 @@ var V2_OPERATIONS = {
11279
11407
  pathParamDocs: { workflowId: "Unique workflow identifier." },
11280
11408
  responseMode: "json",
11281
11409
  summary: "Update Workflow Public API Access",
11410
+ personalKeyOnly: true,
11282
11411
  body: {
11283
11412
  isPublicApi: {
11284
11413
  kind: "boolean",
@@ -11327,6 +11456,7 @@ var V2_OPERATIONS = {
11327
11456
  pathParamDocs: { fileId: "File identifier." },
11328
11457
  responseMode: "json",
11329
11458
  summary: "Enable or Disable File Share",
11459
+ personalKeyOnly: true,
11330
11460
  body: {
11331
11461
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the file." },
11332
11462
  isActive: {
@@ -11361,7 +11491,7 @@ var V2_OPERATIONS = {
11361
11491
  data: {
11362
11492
  kind: "object",
11363
11493
  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}`."
11494
+ 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
11495
  },
11366
11496
  conflictTarget: { kind: "string", describe: "Unique column used to detect a conflict." }
11367
11497
  }
@@ -11369,7 +11499,6 @@ var V2_OPERATIONS = {
11369
11499
  };
11370
11500
 
11371
11501
  // src/commands/auth.ts
11372
- var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
11373
11502
  var MAX_INTERACTIVE_WORKSPACES = 1000;
11374
11503
  function openBrowser(url) {
11375
11504
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : [process.platform === "darwin" ? "open" : "xdg-open", [url]];
@@ -11396,11 +11525,11 @@ function presentAuthentication(source) {
11396
11525
  }
11397
11526
  async function confirmProfileOverwrite(profileName) {
11398
11527
  if (!process.stdin.isTTY) {
11399
- throw new SimApiError(`Profile "${profileName}" already exists. Re-run with --yes to overwrite it.`, 0);
11528
+ throw new SimApiError(`Profile "${redact(profileName)}" already exists. Re-run with --yes to overwrite it.`, 0);
11400
11529
  }
11401
11530
  const prompt = createInterface({ input: process.stdin, output: process.stderr });
11402
11531
  try {
11403
- const answer = await prompt.question(`Profile "${profileName}" already exists. Replace its API key and login defaults? (y/N) `);
11532
+ const answer = await prompt.question(`Profile "${redact(profileName)}" already exists. Replace its API key and login defaults? (y/N) `);
11404
11533
  return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
11405
11534
  } finally {
11406
11535
  prompt.close();
@@ -11410,21 +11539,24 @@ function selectedProfileName(command) {
11410
11539
  return globalsOf(command).profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
11411
11540
  }
11412
11541
  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
- }
11542
+ validateProfileName(profileName);
11416
11543
  if (listProfiles().includes(profileName)) {
11417
- throw new SimApiError(`Profile "${profileName}" already exists. Remove it first with: sim logout --all --profile ${profileName}`, 0);
11544
+ throw new SimApiError(`Profile "${redact(profileName)}" already exists. Remove it first with: sim logout --all --profile ${redact(profileName)}`, 0);
11545
+ }
11546
+ }
11547
+ function requireStorableKey(apiKey) {
11548
+ if (typeof apiKey !== "string" || !apiKey || apiKey !== apiKey.trim() || FORBIDDEN_IN_VALUE.test(apiKey)) {
11549
+ throw new SimApiError("The server returned a malformed API key. Nothing was stored; check the endpoint.", 0);
11418
11550
  }
11419
11551
  }
11420
11552
  function requireStoredAuthentication(profile) {
11421
11553
  const authProfile = resolveAuthenticationProfileName(profile.name);
11422
11554
  const storedKey = readCredentialsProfile(authProfile).api_key;
11423
11555
  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);
11556
+ 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
11557
  }
11426
11558
  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);
11559
+ 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
11560
  }
11429
11561
  return authProfile;
11430
11562
  }
@@ -11476,12 +11608,12 @@ function addProfileCommand() {
11476
11608
  const workspace = workspaceId ? await getWorkspaceById(client, workspaceId) : await chooseWorkspace(client);
11477
11609
  writeConfigProfile(profileName, {
11478
11610
  auth_profile: authProfile,
11479
- workspace: workspace.id
11611
+ workspace: normalizeWorkspaceId(workspace.id, "the workspace response")
11480
11612
  });
11481
- console.log(source_default.green(`✓ Added profile "${profileName}" in ${configPath()}`));
11613
+ console.log(source_default.green(`✓ Added profile "${safeOneLine(profileName)}" in ${configPath()}`));
11482
11614
  console.log(` Workspace: ${safeOneLine(workspace.name)} (${workspace.id})`);
11483
- console.log(` Authentication: ${authProfile}`);
11484
- console.log(source_default.dim(` Try: sim --profile ${profileName} whoami`));
11615
+ console.log(` Authentication: ${safeOneLine(authProfile)}`);
11616
+ console.log(source_default.dim(` Try: sim --profile ${safeOneLine(profileName)} whoami`));
11485
11617
  });
11486
11618
  }
11487
11619
  function loginCommand() {
@@ -11489,7 +11621,7 @@ function loginCommand() {
11489
11621
  const profile = profileFrom(command, { allowUnknownProfile: true });
11490
11622
  const authProfile = resolveAuthenticationProfileName(profile.name);
11491
11623
  if (authProfile !== profile.name) {
11492
- throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Run: sim login --profile ${authProfile}`, 0);
11624
+ throw new SimApiError(`Profile "${redact(profile.name)}" shares authentication with "${redact(authProfile)}". Run: sim login --profile ${redact(authProfile)}`, 0);
11493
11625
  }
11494
11626
  if (options.scope !== "platform" && options.scope !== "copilot") {
11495
11627
  throw new SimApiError(`Unknown scope "${options.scope}". Use platform or copilot.`, 0);
@@ -11504,7 +11636,7 @@ function loginCommand() {
11504
11636
  }
11505
11637
  const auth = createAuthRequest();
11506
11638
  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)}`);
11639
+ console.log(`Signing in to ${source_default.bold(profile.endpoint)} as profile ${source_default.bold(safeOneLine(profile.name))}`);
11508
11640
  console.log(`
11509
11641
  Pairing code: ${source_default.bold(auth.pairing)}`);
11510
11642
  console.log(source_default.dim(`Confirm this code matches what the browser shows before approving.
@@ -11518,12 +11650,13 @@ Waiting for approval…`));
11518
11650
  if (key.scope !== scope) {
11519
11651
  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
11652
  }
11521
- writeCredentialsProfile(profile.name, key.apiKey);
11522
11653
  const settings = {
11523
11654
  endpoint: profile.endpoint,
11524
- workspace: key.workspaceId ?? null
11655
+ workspace: key.workspaceId == null ? null : normalizeWorkspaceId(key.workspaceId, "the login response")
11525
11656
  };
11657
+ requireStorableKey(key.apiKey);
11526
11658
  writeConfigProfile(profile.name, settings);
11659
+ writeCredentialsProfile(profile.name, key.apiKey);
11527
11660
  console.log(source_default.green(`
11528
11661
  ✓ Logged in. Key stored in ${credentialsPath()}`));
11529
11662
  if (key.workspaceBound && key.workspaceId) {
@@ -11541,27 +11674,27 @@ function logoutCommand() {
11541
11674
  const profileName = selectedProfileName(command);
11542
11675
  const dependents = listAuthenticationDependents(profileName);
11543
11676
  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);
11677
+ throw new SimApiError(`Cannot remove authentication profile "${redact(profileName)}" because it is used by: ${dependents.map(redact).join(", ")}. Remove those profiles first.`, 0);
11545
11678
  }
11546
11679
  const removed = deleteProfile(profileName);
11547
11680
  if (!removed.config && !removed.credentials) {
11548
- console.log(source_default.dim(`Nothing stored for profile "${profileName}".`));
11681
+ console.log(source_default.dim(`Nothing stored for profile "${safeOneLine(profileName)}".`));
11549
11682
  return;
11550
11683
  }
11551
- console.log(source_default.green(`✓ Removed profile "${profileName}".`));
11684
+ console.log(source_default.green(`✓ Removed profile "${safeOneLine(profileName)}".`));
11552
11685
  return;
11553
11686
  }
11554
11687
  const profile = profileFrom(command);
11555
11688
  const authProfile = resolveAuthenticationProfileName(profile.name);
11556
11689
  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);
11690
+ 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
11691
  }
11559
11692
  if (!readCredentialsProfile(profile.name).api_key) {
11560
- console.log(source_default.dim(`No stored key for profile "${profile.name}".`));
11693
+ console.log(source_default.dim(`No stored key for profile "${safeOneLine(profile.name)}".`));
11561
11694
  return;
11562
11695
  }
11563
11696
  writeCredentialsProfile(profile.name, null);
11564
- console.log(source_default.green(`✓ Removed the stored key for profile "${profile.name}".`));
11697
+ console.log(source_default.green(`✓ Removed the stored key for profile "${safeOneLine(profile.name)}".`));
11565
11698
  console.log(source_default.dim(" The key itself is still active — revoke it in Settings → API keys."));
11566
11699
  });
11567
11700
  }
@@ -11591,7 +11724,7 @@ async function verifyProfile(client, profile) {
11591
11724
  status: "unauthenticated",
11592
11725
  workspace: null,
11593
11726
  keyType: null,
11594
- detail: `no API key — run: sim login --profile ${profile.name}`
11727
+ detail: `no API key — run: sim login --profile ${safeOneLine(profile.name)}`
11595
11728
  };
11596
11729
  }
11597
11730
  const keyType = await readKeyType(client);
@@ -11600,7 +11733,7 @@ async function verifyProfile(client, profile) {
11600
11733
  status: "no-workspace",
11601
11734
  workspace: null,
11602
11735
  keyType,
11603
- detail: `no workspace to check against — run: sim configure --profile ${profile.name} --set-workspace <id>`
11736
+ detail: `no workspace to check against — run: sim configure --profile ${safeOneLine(profile.name)} --set-workspace <id>`
11604
11737
  };
11605
11738
  }
11606
11739
  const operation = V2_OPERATIONS.getWorkspace;
@@ -11687,38 +11820,83 @@ function whoamiCommand() {
11687
11820
  process.exitCode = exitCode;
11688
11821
  });
11689
11822
  }
11823
+ var PROFILE_COLUMNS = [
11824
+ { header: "", value: (row) => row.active ? source_default.green("*") : " " },
11825
+ { header: "profile", value: (row) => safeOneLine(row.name) },
11826
+ { header: "key", value: (row) => row.error ? text(null) : row.hasKey ? "yes" : "no" },
11827
+ { header: "auth", value: (row) => row.authProfile ? safeOneLine(row.authProfile) : text(null) },
11828
+ { header: "error", value: (row) => row.error ? source_default.red(safeOneLine(row.error)) : text(null) }
11829
+ ];
11830
+ function buildProfileRow(name, active) {
11831
+ try {
11832
+ const authProfile = resolveAuthenticationProfileName(name);
11833
+ return {
11834
+ name,
11835
+ active,
11836
+ hasKey: Boolean(readCredentialsProfile(authProfile).api_key),
11837
+ authProfile,
11838
+ error: null
11839
+ };
11840
+ } catch (error) {
11841
+ if (!(error instanceof ProfileConfigError))
11842
+ throw error;
11843
+ return { name, active, hasKey: false, authProfile: null, error: error.message };
11844
+ }
11845
+ }
11846
+ function profileListingContext(command) {
11847
+ try {
11848
+ const profile = profileFrom(command);
11849
+ return { activeName: profile.name, output: profile.output };
11850
+ } catch (error) {
11851
+ if (!(error instanceof ProfileConfigError))
11852
+ throw error;
11853
+ if (error instanceof ProfileOverrideError)
11854
+ throw error;
11855
+ const globals = globalsOf(command);
11856
+ const named = globals.profile || process.env.SIM_PROFILE;
11857
+ if (named && named !== DEFAULT_PROFILE && !listProfiles().includes(named))
11858
+ throw error;
11859
+ const requested = globals.output ?? process.env.SIM_OUTPUT;
11860
+ if (requested && !OUTPUT_FORMATS.includes(requested))
11861
+ throw error;
11862
+ return {
11863
+ activeName: named || DEFAULT_PROFILE,
11864
+ output: requested ? requested : "table"
11865
+ };
11866
+ }
11867
+ }
11690
11868
  function profilesCommand() {
11691
11869
  const command = new Command("profiles").alias("profile").description("List profiles or add a workspace profile that shares a stored login");
11692
11870
  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"));
11871
+ const { activeName, output } = profileListingContext(actionCommand);
11872
+ const rows = listProfiles().map((name) => buildProfileRow(name, name === activeName));
11873
+ if (rows.length === 0) {
11874
+ if (output === "table")
11875
+ console.log(source_default.dim("No profiles yet. Run: sim login"));
11876
+ else
11877
+ printList(output, rows, PROFILE_COLUMNS);
11696
11878
  return;
11697
11879
  }
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
- }
11880
+ printList(output, rows, PROFILE_COLUMNS);
11714
11881
  };
11715
- command.action(printProfiles);
11716
- command.addCommand(new Command("list").description("List configured profiles").action(printProfiles));
11882
+ command.addCommand(new Command("list").allowExcessArguments(false).description("List configured profiles").action(printProfiles), { isDefault: true });
11717
11883
  command.addCommand(addProfileCommand());
11884
+ const known = new Set(command.commands.flatMap((child) => [child.name(), ...child.aliases()]));
11885
+ command.hook("preSubcommand", (group) => {
11886
+ const first = group.args[0];
11887
+ if (first !== undefined && !first.startsWith("-") && !known.has(first)) {
11888
+ group.unknownCommand();
11889
+ }
11890
+ });
11718
11891
  return command;
11719
11892
  }
11720
11893
 
11721
11894
  // src/commands/configure.ts
11895
+ var GLOBAL_FLAG_TWINS = [
11896
+ { option: "endpoint", flag: "--endpoint", setFlag: "--set-endpoint" },
11897
+ { option: "workspace", flag: "-w, --workspace", setFlag: "--set-workspace" },
11898
+ { option: "output", flag: "--output", setFlag: "--set-output" }
11899
+ ];
11722
11900
  function requireValue(value, flag, key) {
11723
11901
  if (value !== undefined && value.trim() === "") {
11724
11902
  throw new SimApiError(`${flag} requires a value. To remove it, run: sim configure --unset ${key}`, 0);
@@ -11726,6 +11904,13 @@ function requireValue(value, flag, key) {
11726
11904
  }
11727
11905
  function configureCommand() {
11728
11906
  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) => {
11907
+ const globals = globalsOf(command);
11908
+ for (const { option, flag, setFlag } of GLOBAL_FLAG_TWINS) {
11909
+ const value = globals[option];
11910
+ if (value === undefined)
11911
+ continue;
11912
+ throw new SimApiError(`${flag} applies to a single command and is not stored. To save it, run: sim configure ${setFlag} ${value}`, 0);
11913
+ }
11729
11914
  const profile = profileFrom(command, { allowUnknownProfile: true });
11730
11915
  const authProfile = resolveAuthenticationProfileName(profile.name);
11731
11916
  const updates = {};
@@ -11738,8 +11923,9 @@ function configureCommand() {
11738
11923
  }
11739
11924
  updates.endpoint = normalizeEndpoint(options.setEndpoint, "--set-endpoint");
11740
11925
  }
11741
- if (options.setWorkspace)
11742
- updates.workspace = options.setWorkspace;
11926
+ if (options.setWorkspace) {
11927
+ updates.workspace = normalizeWorkspaceId(options.setWorkspace, "--set-workspace");
11928
+ }
11743
11929
  if (options.setOutput) {
11744
11930
  if (!OUTPUT_FORMATS.includes(options.setOutput)) {
11745
11931
  throw new SimApiError(`Unknown output format "${options.setOutput}". Use one of: ${OUTPUT_FORMATS.join(", ")}`, 0);
@@ -11748,7 +11934,7 @@ function configureCommand() {
11748
11934
  }
11749
11935
  for (const key of options.unset ?? []) {
11750
11936
  if (!["endpoint", "workspace", "output"].includes(key)) {
11751
- throw new SimApiError(`Cannot unset "${key}". Use endpoint, workspace, or output.`, 0);
11937
+ throw new SimApiError(`Cannot unset "${redact(key)}". Use endpoint, workspace, or output.`, 0);
11752
11938
  }
11753
11939
  if (key === "endpoint" && authProfile !== profile.name) {
11754
11940
  throw new SimApiError(`Profile "${profile.name}" shares its endpoint with authentication profile "${authProfile}". Run: sim configure --profile ${authProfile} --unset endpoint`, 0);
@@ -11766,14 +11952,16 @@ function configureCommand() {
11766
11952
  }
11767
11953
  return;
11768
11954
  }
11955
+ const removalOnly = Object.values(updates).every((value) => value === null);
11956
+ if (removalOnly && Object.keys(readConfigProfile(profile.name)).length === 0) {
11957
+ console.log(source_default.dim(`No settings stored for profile "${profile.name}".`));
11958
+ return;
11959
+ }
11769
11960
  writeConfigProfile(profile.name, updates);
11770
11961
  console.log(source_default.green(`✓ Updated profile "${profile.name}" in ${configPath()}`));
11771
11962
  });
11772
11963
  }
11773
11964
 
11774
- // src/runtime/request.ts
11775
- import { existsSync as existsSync2, readFileSync as readFileSync3, readSync } from "node:fs";
11776
-
11777
11965
  // src/contract/commands.ts
11778
11966
  var TABLE_NAME_HELP = "Identifier: letters, numbers, and underscores; cannot start with a number";
11779
11967
  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';
@@ -11858,7 +12046,7 @@ var CLI_CONTRACT = {
11858
12046
  listBillingLogs: {
11859
12047
  command: "billing logs",
11860
12048
  allWorkspaces: true,
11861
- describe: "List credit usage events",
12049
+ describe: "List credit usage events (a personal API key reports only your own events; a workspace API key reports every member's)",
11862
12050
  flags: {
11863
12051
  source: { describe: "Filter by usage source; sim-chat combines Copilot and workspace chat" },
11864
12052
  period: { describe: "Billing period" },
@@ -12014,14 +12202,14 @@ var CLI_CONTRACT = {
12014
12202
  }
12015
12203
  },
12016
12204
  columns: [
12017
- { header: "started", path: "startedAt", format: "timestamp" },
12018
- { header: "status" },
12205
+ { header: "started", path: "startedAt", format: "timestamp", minWidth: 19 },
12206
+ { header: "status", minWidth: 9 },
12019
12207
  { header: "level" },
12020
- { header: "trigger" },
12021
- { header: "workflow", path: "workflow.name" },
12208
+ { header: "trigger", minWidth: 12 },
12209
+ { header: "workflow", path: "workflow.name", minWidth: 24 },
12022
12210
  { header: "duration", path: "totalDurationMs", format: "duration" },
12023
- { header: "cost", path: "cost.total", format: "cost" },
12024
- { header: "run", path: "runId" }
12211
+ { header: "cost", path: "cost.total", format: "cost", minWidth: 8 },
12212
+ { header: "run", path: "runId", minWidth: 36 }
12025
12213
  ]
12026
12214
  },
12027
12215
  getLog: {
@@ -12769,6 +12957,23 @@ var CLI_CONTRACT = {
12769
12957
  }
12770
12958
  }
12771
12959
  },
12960
+ listTableDispatches: {
12961
+ columns: [
12962
+ { header: "id" },
12963
+ { header: "status" },
12964
+ { header: "mode" },
12965
+ { header: "max rows", path: "limit.max" },
12966
+ { header: "processed", path: "processedCount" },
12967
+ { header: "manual", path: "isManualRun", format: "bool" },
12968
+ { header: "requested", path: "requestedAt", format: "timestamp" },
12969
+ { header: "completed", path: "completedAt", format: "timestamp" },
12970
+ { header: "canceled", path: "canceledAt", format: "timestamp" },
12971
+ { header: "groups", path: "scope.groupIds", format: "count" },
12972
+ { header: "rows", path: "scope.rowIds", format: "count" },
12973
+ { header: "filtered", path: "scope.filtered", format: "bool" },
12974
+ { header: "excluded", path: "scope.excludeRowIds", format: "count" }
12975
+ ]
12976
+ },
12772
12977
  runRowEnrichment: {
12773
12978
  command: "tables rows enrich",
12774
12979
  describe: "Run one row’s enrichment group"
@@ -12777,8 +12982,16 @@ var CLI_CONTRACT = {
12777
12982
  createTableImportPartUrls: { hidden: true },
12778
12983
  completeTableImport: { hidden: true },
12779
12984
  getTableImport: { flags: TRANSFER_TOKEN_OMITTED },
12780
- cancelTableImport: { command: "tables imports cancel", flags: TRANSFER_TOKEN_OMITTED },
12781
- cancelTableExport: { command: "tables exports cancel" },
12985
+ cancelTableImport: {
12986
+ command: "tables imports cancel",
12987
+ flags: TRANSFER_TOKEN_OMITTED,
12988
+ describe: "Stop a running import",
12989
+ 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."
12990
+ },
12991
+ cancelTableExport: {
12992
+ command: "tables exports cancel",
12993
+ describe: "Stop a running export"
12994
+ },
12782
12995
  tableExportDownload: {
12783
12996
  command: "tables exports download",
12784
12997
  describe: "Get the download URL for a finished export"
@@ -12800,7 +13013,7 @@ var CLI_CONTRACT = {
12800
13013
  selectedOutputs: {
12801
13014
  name: "select-output",
12802
13015
  list: true,
12803
- describe: "Return blockName.field values (e.g. agent_1.content); missing fields are omitted"
13016
+ describe: "Return blockName.field values from the streamed result (e.g. agent_1.content), requires --follow; missing fields are omitted"
12804
13017
  },
12805
13018
  stream: { omit: true },
12806
13019
  includeThinking: { omit: true },
@@ -12824,7 +13037,7 @@ var CLI_CONTRACT = {
12824
13037
  selectedOutputs: {
12825
13038
  name: "select-output",
12826
13039
  list: true,
12827
- describe: "Include blockName.field values in JSON or YAML output (e.g. agent_1.content)"
13040
+ describe: "Include blockId or blockId.path values in JSON or YAML output; block names are not resolved on a finished run"
12828
13041
  }
12829
13042
  },
12830
13043
  fields: [
@@ -12931,11 +13144,20 @@ function camel(flag) {
12931
13144
  }
12932
13145
 
12933
13146
  // src/runtime/request.ts
13147
+ import { existsSync as existsSync2, readFileSync as readFileSync3, readSync } from "node:fs";
12934
13148
  var PROFILE_INJECTED_FIELD = "workspaceId";
12935
13149
  function isProfileWorkspacePath(commandSpec, param) {
12936
13150
  return commandSpec.profileWorkspacePath === true && param === PROFILE_INJECTED_FIELD;
12937
13151
  }
13152
+ function cursorSlot(operationSpec) {
13153
+ if (operationSpec.query && "cursor" in operationSpec.query)
13154
+ return "query";
13155
+ if (operationSpec.body && "cursor" in operationSpec.body)
13156
+ return "body";
13157
+ return null;
13158
+ }
12938
13159
  var JSON_KINDS = new Set(["object", "array", "unknown"]);
13160
+ var NUMERIC_KINDS = new Set(["number", "integer"]);
12939
13161
  function flagSpecFor(operation, field) {
12940
13162
  return CLI_CONTRACT[operation]?.flags?.[field] ?? {};
12941
13163
  }
@@ -13001,6 +13223,9 @@ function readStdin() {
13001
13223
  }
13002
13224
  return Buffer.concat(chunks).toString("utf8");
13003
13225
  }
13226
+ function literalAtHint(error, path) {
13227
+ return error?.code === "ENOENT" ? `. To pass the literal value @${path}, write @@${path}` : "";
13228
+ }
13004
13229
  function readArgumentSource(raw, flagName) {
13005
13230
  if (raw.startsWith("@@"))
13006
13231
  return { text: raw.slice(1), from: "" };
@@ -13020,7 +13245,7 @@ function readArgumentSource(raw, flagName) {
13020
13245
  try {
13021
13246
  return { text: readFileSync3(path, "utf8"), from: ` (read from ${path})` };
13022
13247
  } catch (error) {
13023
- throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}`, 0);
13248
+ throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}${literalAtHint(error, path)}`, 0);
13024
13249
  }
13025
13250
  }
13026
13251
  function readListValues(raw, flagName) {
@@ -13097,7 +13322,7 @@ function coerce(raw, field, flag, flagName) {
13097
13322
  throw new SimApiError(`--${flagName} must be valid JSON${source.from}: ${error.message}${pathHint(raw)}`, 0);
13098
13323
  }
13099
13324
  }
13100
- if (field.kind === "number" || field.kind === "integer") {
13325
+ if (NUMERIC_KINDS.has(field.kind)) {
13101
13326
  const value = Number(raw);
13102
13327
  if (Number.isNaN(value))
13103
13328
  throw new SimApiError(`--${flagName} must be a number`, 0);
@@ -13113,6 +13338,7 @@ function coerce(raw, field, flag, flagName) {
13113
13338
  return encodeFolderPath(raw);
13114
13339
  return raw;
13115
13340
  }
13341
+ var NO_WORKSPACE_FALLBACK = "No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>";
13116
13342
  function asQueryValue(value) {
13117
13343
  if (value === null || value === undefined)
13118
13344
  return;
@@ -13133,7 +13359,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13133
13359
  const value = profileWorkspacePath ? workspaceId : pathFlag ? flags[camel(flagName)] : positional[positionalIndex++];
13134
13360
  if (value === undefined || value === null) {
13135
13361
  if (profileWorkspacePath) {
13136
- throw new SimApiError("No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>", 0);
13362
+ throw new SimApiError(NO_WORKSPACE_FALLBACK, 0);
13137
13363
  }
13138
13364
  throw new SimApiError(pathFlag ? `--${flagName} is required` : `Missing <${argumentName}>`, 0);
13139
13365
  }
@@ -13145,6 +13371,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13145
13371
  const query = {};
13146
13372
  const body = {};
13147
13373
  const headers = {};
13374
+ const paginatedLimit = cursorSlot(spec) !== null;
13148
13375
  for (const slot of ["query", "body", "headers"]) {
13149
13376
  for (const [field, descriptor] of Object.entries(spec[slot] ?? {})) {
13150
13377
  const flag = flagSpecFor(operation, field);
@@ -13154,10 +13381,13 @@ function buildRequest(operation, positional, flags, workspaceId) {
13154
13381
  const omitProfileWorkspace = commandSpec.allWorkspaces && flags.allWorkspaces === true;
13155
13382
  const provided = field === PROFILE_INJECTED_FIELD ? omitProfileWorkspace ? undefined : workspaceId : flags[camel(flagName)];
13156
13383
  const raw = provided ?? flag.requestDefault;
13384
+ if ((slot === "query" || NUMERIC_KINDS.has(descriptor.kind)) && typeof raw === "string" && raw.trim() === "" && !(field === "limit" && paginatedLimit)) {
13385
+ throw new SimApiError(`--${flagName} cannot be empty`, 0);
13386
+ }
13157
13387
  const value = coerce(raw ?? undefined, descriptor, flag, flagName);
13158
13388
  if (value === undefined) {
13159
13389
  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);
13390
+ throw new SimApiError(field === PROFILE_INJECTED_FIELD ? NO_WORKSPACE_FALLBACK : `--${flagName} is required`, 0);
13161
13391
  }
13162
13392
  continue;
13163
13393
  }
@@ -13204,6 +13434,126 @@ function buildRequest(operation, positional, flags, workspaceId) {
13204
13434
  };
13205
13435
  }
13206
13436
 
13437
+ // src/runtime/options.ts
13438
+ var DEFAULT_LIMIT = 100;
13439
+ function describeField(flag, descriptor, name, field) {
13440
+ return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
13441
+ }
13442
+ function literalNullHint(documented, name) {
13443
+ return /\bnull\b/i.test(documented) ? ` (--${name} null sends the word, not JSON null)` : "";
13444
+ }
13445
+ var WIRE_VOCABULARY_SENTENCE = /\s*The listed spellings[^.]*\.\s*/g;
13446
+ function withoutWireVocabulary(documented) {
13447
+ return documented.replace(WIRE_VOCABULARY_SENTENCE, " ").trim();
13448
+ }
13449
+ var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
13450
+ function addFieldOption(command, operation, field, descriptor, slot, paginates) {
13451
+ if (field === PROFILE_INJECTED_FIELD || field === "cursor")
13452
+ return;
13453
+ const flag = flagSpecFor(operation, field);
13454
+ if (flag.omit)
13455
+ return;
13456
+ const name = flagNameFor(operation, field);
13457
+ const short = flag.short ? `-${flag.short}, ` : "";
13458
+ if (paginates && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
13459
+ command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
13460
+ return;
13461
+ }
13462
+ const documented = `${describeField(flag, descriptor, name, field)}${field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
13463
+ if (descriptor.kind === "boolean" || flag.boolean) {
13464
+ const booleanDoc = withoutWireVocabulary(documented);
13465
+ if (descriptor.required) {
13466
+ command.addOption(new Option(`${short}--${name} <true|false>`, `${booleanDoc} (required)`).choices(["true", "false"]).makeOptionMandatory());
13467
+ return;
13468
+ }
13469
+ command.option(`${short}--${name}`, booleanDoc);
13470
+ if (!flag.boolean || flag.negatable) {
13471
+ command.option(`--no-${name}`, `Send --${name} as false`);
13472
+ }
13473
+ return;
13474
+ }
13475
+ const takesList = flag.list === true;
13476
+ const wantsJson = takesJson(descriptor, flag);
13477
+ const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
13478
+ const choices = flag.choices ?? descriptor.values;
13479
+ const literalNull = slot === "body" && !takesList && !wantsJson;
13480
+ 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) : ""}`;
13481
+ const renamedFrom = flag.renamedFrom ?? [];
13482
+ const option = new Option(`${short}--${name} ${placeholder}`, describe);
13483
+ if (flag.hidden)
13484
+ option.hideHelp();
13485
+ if (choices && !takesList)
13486
+ option.choices([...choices]);
13487
+ if (descriptor.default !== undefined && field !== "limit") {
13488
+ option.default(undefined, String(descriptor.default));
13489
+ }
13490
+ if (descriptor.required && renamedFrom.length === 0)
13491
+ option.makeOptionMandatory();
13492
+ command.addOption(option);
13493
+ for (const previous of renamedFrom) {
13494
+ const retired = new Option(`--${previous} ${placeholder}`).hideHelp();
13495
+ if (choices && !takesList)
13496
+ retired.choices([...choices]);
13497
+ command.addOption(retired);
13498
+ }
13499
+ }
13500
+ function addOperationOptions(command, operation, commandSpec, operationSpec) {
13501
+ for (const param of operationSpec.pathParams) {
13502
+ const flag = commandSpec.pathFlags?.[param];
13503
+ if (!flag)
13504
+ continue;
13505
+ const name = pathFlagNameFor(commandSpec, param);
13506
+ const short = flag.short ? `-${flag.short}, ` : "";
13507
+ command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
13508
+ }
13509
+ const paginates = cursorSlot(operationSpec) !== null;
13510
+ for (const slot of ["query", "body", "headers"]) {
13511
+ for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
13512
+ if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
13513
+ continue;
13514
+ if (commandSpec.positionals?.includes(field))
13515
+ continue;
13516
+ addFieldOption(command, operation, field, descriptor, slot, paginates);
13517
+ }
13518
+ }
13519
+ if (commandSpec.allWorkspaces) {
13520
+ command.option("--all-workspaces", "Do not filter to the configured workspace (personal API key required for account-wide access)");
13521
+ }
13522
+ if (commandSpec.expandedTrace) {
13523
+ command.option("--trace", "Show expanded trace spans with inputs, outputs, errors, timing, and cost");
13524
+ }
13525
+ if (operationSpec.opaqueBody) {
13526
+ if (commandSpec.bodyVariants) {
13527
+ for (const variant of commandSpec.bodyVariants) {
13528
+ command.option(`--${variant.name} <json|@file>`, `${variant.describe} (JSON, or @path / @-; choose exactly one body flag)`);
13529
+ }
13530
+ } else {
13531
+ command.requiredOption("--body <json|@file>", "Request body as JSON (or @path / @- to read a file or stdin) (required)");
13532
+ }
13533
+ }
13534
+ if (commandSpec.confirm) {
13535
+ const exemptedByDryRun = operationSpec.query?.dryRun !== undefined || operationSpec.body?.dryRun !== undefined;
13536
+ command.option("-y, --yes", exemptedByDryRun ? "Confirm this destructive operation (required unless --dry-run)" : "Confirm this destructive operation (required)");
13537
+ }
13538
+ }
13539
+
13540
+ // src/runtime/renamed.ts
13541
+ var warned = new Set;
13542
+ function warn(kind, from, to) {
13543
+ const key = `${kind}:${from}`;
13544
+ if (warned.has(key))
13545
+ return;
13546
+ warned.add(key);
13547
+ process.stderr.write(`warning: ${kind} "${from}" has been renamed to "${to}". The old name still works.
13548
+ `);
13549
+ }
13550
+ function warnRenamedCommand(from, to) {
13551
+ warn("command", `sim ${from}`, `sim ${to}`);
13552
+ }
13553
+ function warnRenamedFlag(from, to) {
13554
+ warn("flag", `--${from}`, `--${to}`);
13555
+ }
13556
+
13207
13557
  // src/output/trace.ts
13208
13558
  function traceSpan(value) {
13209
13559
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -13333,7 +13683,8 @@ function at(row, path) {
13333
13683
  function decodeFolderPath(value) {
13334
13684
  return value.split("/").map((segment) => {
13335
13685
  try {
13336
- return decodeURIComponent(segment);
13686
+ const decoded = decodeURIComponent(segment);
13687
+ return decoded.includes("/") ? segment : decoded;
13337
13688
  } catch {
13338
13689
  return segment;
13339
13690
  }
@@ -13464,8 +13815,10 @@ function unwrapResource(data) {
13464
13815
  const [, value] = entries[0];
13465
13816
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
13466
13817
  }
13467
- function renderPage(format, rows, spec, envelope) {
13818
+ function renderPage(format, rows, spec, envelope, options = {}) {
13468
13819
  writePageNote(spec, envelope);
13820
+ writeEnvelopeTruncation(envelope);
13821
+ writeCursorTruncation(rows.length, options.truncated === true);
13469
13822
  printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
13470
13823
  }
13471
13824
  function writePageNote(spec, envelope) {
@@ -13477,7 +13830,48 @@ function writePageNote(spec, envelope) {
13477
13830
  process.stderr.write(source_default.dim(`${spec.pageNote.label}: ${String(value)}
13478
13831
  `));
13479
13832
  }
13480
- function renderResult(operation, format, raw, spec, options = {}) {
13833
+ var TRUNCATION_FLAG = /^truncated$|^[A-Za-z0-9]+Truncated$/;
13834
+ var NEGATED_TRUNCATION_FLAG = /^(?:not|un|non|never)Truncated$|(?:Not|Un|Non|Never)Truncated$/;
13835
+ function truncationFlags(container) {
13836
+ if (!container || typeof container !== "object" || Array.isArray(container))
13837
+ return [];
13838
+ return Object.entries(container).filter(([key, value]) => value === true && TRUNCATION_FLAG.test(key) && !NEGATED_TRUNCATION_FLAG.test(key)).map(([key]) => key);
13839
+ }
13840
+ function responseTruncationFlags(envelope) {
13841
+ return [...truncationFlags(envelope), ...truncationFlags(at(envelope, "data"))];
13842
+ }
13843
+ function foldPageEnvelope(current, page) {
13844
+ if (current === undefined)
13845
+ return page;
13846
+ const raised = truncationFlags(page);
13847
+ if (raised.length === 0 || !current || typeof current !== "object")
13848
+ return current;
13849
+ return {
13850
+ ...current,
13851
+ ...Object.fromEntries(raised.map((flag) => [flag, true]))
13852
+ };
13853
+ }
13854
+ function spellOut(flag) {
13855
+ return flag.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().trim();
13856
+ }
13857
+ function clippedSubject(flag) {
13858
+ const subject = flag.replace(/^truncated$|Truncated$/, "").replace(/^is(?=[A-Z]|$)/, "");
13859
+ return subject ? `the ${spellOut(subject)} it returned` : "this result";
13860
+ }
13861
+ function writeEnvelopeTruncation(envelope) {
13862
+ for (const flag of responseTruncationFlags(envelope)) {
13863
+ process.stderr.write(source_default.dim(`${spellOut(flag)}: the server clipped ${clippedSubject(flag)}, so the answer is incomplete
13864
+ `));
13865
+ }
13866
+ }
13867
+ function writeCursorTruncation(count, truncated) {
13868
+ if (!truncated)
13869
+ return;
13870
+ process.stderr.write(source_default.dim(`showing the first ${count}; more results exist — re-run with --limit 0 for all
13871
+ `));
13872
+ }
13873
+ function renderResult(operation, format, raw, spec, options = {}, envelope) {
13874
+ writeEnvelopeTruncation(envelope);
13481
13875
  if (spec.document) {
13482
13876
  printDocument(format, raw);
13483
13877
  return;
@@ -13509,31 +13903,577 @@ function renderResult(operation, format, raw, spec, options = {}) {
13509
13903
  }
13510
13904
  }
13511
13905
 
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
- ]
13906
+ // src/runtime/execute.ts
13907
+ var RUN_OUTCOME_OPERATIONS = new Set(["executeWorkflow"]);
13908
+ var FAILED_RUN_STATUS_MESSAGES = {
13909
+ failed: "The workflow run failed.",
13910
+ cancelled: "The workflow run was cancelled."
13527
13911
  };
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);
13532
- }
13533
- if (!provider.available) {
13534
- throw new SimApiError(`Service-account provider "${providerId}" is not available.`, 0);
13535
- }
13536
- return provider;
13912
+ function runFailureMessage(operation, payload) {
13913
+ if (!RUN_OUTCOME_OPERATIONS.has(operation))
13914
+ return null;
13915
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
13916
+ return null;
13917
+ const { status, error } = payload;
13918
+ if (typeof status !== "string")
13919
+ return null;
13920
+ const fallback = FAILED_RUN_STATUS_MESSAGES[status];
13921
+ if (!fallback)
13922
+ return null;
13923
+ const reported2 = error?.message;
13924
+ return safeOneLine(typeof reported2 === "string" && reported2 ? reported2 : fallback);
13925
+ }
13926
+ var BULK_OUTCOME_CHECKS = {
13927
+ bulkDeleteFiles: (payload, body) => {
13928
+ if (countOf(payload.deletedItems?.files) > 0)
13929
+ return null;
13930
+ const requested = lengthOf(body?.fileIds);
13931
+ if (requested === 0)
13932
+ return null;
13933
+ return `Deleted nothing: none of the ${requested} requested ${requested === 1 ? "file was" : "files were"} deleted.`;
13934
+ },
13935
+ addWorkspaceFilesToKnowledgeBase: (payload) => {
13936
+ if (lengthOf(payload.added) > 0)
13937
+ return null;
13938
+ const failed = lengthOf(payload.failed);
13939
+ if (failed === 0)
13940
+ return null;
13941
+ return `Indexed nothing: none of the ${failed} requested ${failed === 1 ? "file was" : "files were"} added.`;
13942
+ },
13943
+ bulkDeleteTables: (payload) => {
13944
+ const items = payload.deletedItems;
13945
+ const deleted = countOf(items?.tables) + countOf(items?.folders);
13946
+ if (deleted > 0)
13947
+ return null;
13948
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
13949
+ if (missed === 0)
13950
+ return null;
13951
+ return `Deleted nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be deleted.`;
13952
+ },
13953
+ bulkUpdateKnowledgeChunks: (payload, body) => {
13954
+ if (countOf(payload.processed) > 0)
13955
+ return null;
13956
+ const requested = lengthOf(body?.chunkIds);
13957
+ if (requested === 0)
13958
+ return null;
13959
+ const reported2 = payload.errors?.[0];
13960
+ return typeof reported2 === "string" && reported2 ? safeOneLine(reported2) : `Updated nothing: none of the ${requested} requested ${requested === 1 ? "chunk" : "chunks"} matched.`;
13961
+ },
13962
+ moveTables: (payload) => {
13963
+ if (lengthOf(payload.moved) > 0)
13964
+ return null;
13965
+ const missed = lengthOf(payload.notFound) + lengthOf(payload.failed);
13966
+ if (missed === 0)
13967
+ return null;
13968
+ return `Moved nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be moved.`;
13969
+ },
13970
+ moveWorkflows: (payload) => {
13971
+ if (lengthOf(payload.moved) > 0)
13972
+ return null;
13973
+ const failed = lengthOf(payload.failed);
13974
+ if (failed === 0)
13975
+ return null;
13976
+ return `Moved nothing: ${failed} of ${failed} ${failed === 1 ? "workflow" : "workflows"} could not be moved.`;
13977
+ }
13978
+ };
13979
+ function countOf(value) {
13980
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
13981
+ }
13982
+ function lengthOf(value) {
13983
+ return Array.isArray(value) ? value.length : 0;
13984
+ }
13985
+ function bulkFailureMessage(operation, payload, body) {
13986
+ const check = BULK_OUTCOME_CHECKS[operation];
13987
+ if (!check)
13988
+ return null;
13989
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
13990
+ return null;
13991
+ return check(payload, body);
13992
+ }
13993
+ var EXCLUSIVE_CAP_FIELDS = {
13994
+ deleteTableRows: { cap: "limit", ids: "rowIds" }
13995
+ };
13996
+ function assertCapIsUsable(operation, flags) {
13997
+ const exclusive = EXCLUSIVE_CAP_FIELDS[operation];
13998
+ if (!exclusive)
13999
+ return;
14000
+ const cap = flagNameFor(operation, exclusive.cap);
14001
+ const ids = flagNameFor(operation, exclusive.ids);
14002
+ if (flags[camel(cap)] === undefined || flags[camel(ids)] === undefined)
14003
+ return;
14004
+ throw new SimApiError(`--${cap} caps a --filter match and does nothing to an explicit --${ids} list; pass one, not both`, 0);
14005
+ }
14006
+ function foldRenamedFlags(operation, commandSpec, flags) {
14007
+ for (const [field, flag] of Object.entries(commandSpec.flags ?? {})) {
14008
+ if (!flag.renamedFrom?.length)
14009
+ continue;
14010
+ const current = flagNameFor(operation, field);
14011
+ for (const previous of flag.renamedFrom) {
14012
+ const supplied = flags[camel(previous)];
14013
+ if (supplied === undefined)
14014
+ continue;
14015
+ if (flags[camel(current)] !== undefined) {
14016
+ throw new SimApiError(`--${previous} is the former name of --${current}; pass one, not both`, 0);
14017
+ }
14018
+ warnRenamedFlag(previous, current);
14019
+ flags[camel(current)] = supplied;
14020
+ }
14021
+ }
14022
+ }
14023
+ async function executeOperation(operation, commandSpec, operationSpec, invocation) {
14024
+ const host = invocation[invocation.length - 1];
14025
+ const inheritedFlags = host.optsWithGlobals();
14026
+ const flags = {
14027
+ ...inheritedFlags.workspace === undefined ? {} : { workspace: inheritedFlags.workspace },
14028
+ ...inheritedFlags.allWorkspaces === undefined ? {} : { allWorkspaces: inheritedFlags.allWorkspaces },
14029
+ ...invocation[invocation.length - 2]
14030
+ };
14031
+ const pathPositionalCount = operationSpec.pathParams.filter((param) => !commandSpec.pathFlags?.[param] && !isProfileWorkspacePath(commandSpec, param)).length;
14032
+ const positional = invocation.slice(0, pathPositionalCount);
14033
+ const requestFlags = { ...flags };
14034
+ for (const [index, field] of (commandSpec.positionals ?? []).entries()) {
14035
+ requestFlags[camel(flagNameFor(operation, field))] = invocation[pathPositionalCount + index];
14036
+ }
14037
+ foldRenamedFlags(operation, commandSpec, requestFlags);
14038
+ assertCapIsUsable(operation, requestFlags);
14039
+ if (commandSpec.confirm && !requestFlags.yes && requestFlags.dryRun !== true) {
14040
+ throw new SimApiError(`${commandSpec.confirm} Re-run with --yes to confirm.`, 0);
14041
+ }
14042
+ if (commandSpec.allWorkspaces && requestFlags.allWorkspaces && requestFlags.workspace) {
14043
+ throw new SimApiError("--all-workspaces cannot be combined with --workspace", 0);
14044
+ }
14045
+ const { client, profile } = clientFrom(host);
14046
+ const hasWorkspaceField = Boolean(operationSpec.query && PROFILE_INJECTED_FIELD in operationSpec.query || operationSpec.body && PROFILE_INJECTED_FIELD in operationSpec.body);
14047
+ const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
14048
+ const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
14049
+ const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14050
+ const paging = cursorSlot(operationSpec);
14051
+ if (paging) {
14052
+ const limitText = String(requestFlags.limit ?? DEFAULT_LIMIT).trim();
14053
+ const rawLimit = limitText === "" ? Number.NaN : Number(limitText);
14054
+ if (!Number.isInteger(rawLimit) || rawLimit < 0) {
14055
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14056
+ }
14057
+ const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
14058
+ const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
14059
+ const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
14060
+ const rows = [];
14061
+ const progress = pageProgress();
14062
+ let cursor = null;
14063
+ let envelope;
14064
+ try {
14065
+ do {
14066
+ const page = await client.request(request.path, {
14067
+ method: operationSpec.method,
14068
+ headers: request.headers,
14069
+ query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
14070
+ body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
14071
+ });
14072
+ envelope = foldPageEnvelope(envelope, page);
14073
+ rows.push(...page.data);
14074
+ cursor = page.nextCursor;
14075
+ if (cursor && rows.length < limit)
14076
+ progress.advance(rows.length);
14077
+ } while (cursor && rows.length < limit);
14078
+ } finally {
14079
+ progress.finish();
14080
+ }
14081
+ renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope, { truncated: Boolean(cursor) });
14082
+ return;
14083
+ }
14084
+ const result = await client.request(request.path, {
14085
+ method: operationSpec.method,
14086
+ headers: request.headers,
14087
+ query: request.query,
14088
+ body: request.body
14089
+ });
14090
+ const payload = result?.data ?? result;
14091
+ renderResult(operation, profile.output, payload, commandSpec, { expandedTrace: requestFlags.trace === true }, result);
14092
+ const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14093
+ if (failure)
14094
+ throw new SimApiError(failure, 0);
14095
+ }
14096
+
14097
+ // src/runtime/naming.ts
14098
+ var WIRE_IDENTIFIER = /^[a-z]+[A-Z]/;
14099
+ function spellingFor(operation, commandSpec, operationSpec, field) {
14100
+ if (field === PROFILE_INJECTED_FIELD)
14101
+ return "--workspace";
14102
+ if (field === "cursor")
14103
+ return null;
14104
+ if (operationSpec.pathParams.includes(field)) {
14105
+ return commandSpec.pathFlags?.[field] ? `--${pathFlagNameFor(commandSpec, field)}` : `<${commandSpec.pathArgumentNames?.[field] ?? field}>`;
14106
+ }
14107
+ if (commandSpec.positionals?.includes(field))
14108
+ return `<${flagNameFor(operation, field)}>`;
14109
+ if (flagSpecFor(operation, field).omit)
14110
+ return null;
14111
+ if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
14112
+ return null;
14113
+ const declared = operationSpec.query && field in operationSpec.query || operationSpec.body && field in operationSpec.body || operationSpec.headers && field in operationSpec.headers;
14114
+ if (!declared)
14115
+ return null;
14116
+ return `--${flagNameFor(operation, field)}`;
14117
+ }
14118
+ function typeableFields(operation, commandSpec, operationSpec) {
14119
+ const spellings = new Map;
14120
+ const fields = [
14121
+ ...operationSpec.pathParams,
14122
+ ...Object.keys(operationSpec.query ?? {}),
14123
+ ...Object.keys(operationSpec.body ?? {}),
14124
+ ...Object.keys(operationSpec.headers ?? {})
14125
+ ];
14126
+ for (const field of fields) {
14127
+ if (spellings.has(field))
14128
+ continue;
14129
+ const spelling = spellingFor(operation, commandSpec, operationSpec, field);
14130
+ if (spelling)
14131
+ spellings.set(field, spelling);
14132
+ }
14133
+ return spellings;
14134
+ }
14135
+ function retypeMessage(message, spellings) {
14136
+ let retyped = message;
14137
+ for (const [field, spelling] of spellings) {
14138
+ if (!WIRE_IDENTIFIER.test(field))
14139
+ continue;
14140
+ retyped = retyped.replaceAll(new RegExp(`\\b${field}\\b`, "g"), spelling);
14141
+ }
14142
+ return retyped;
14143
+ }
14144
+ function retypeDetails(details, spellings) {
14145
+ if (Array.isArray(details))
14146
+ return details.map((issue2) => retypeDetails(issue2, spellings));
14147
+ if (!details || typeof details !== "object")
14148
+ return details;
14149
+ const issue = details;
14150
+ const retyped = { ...issue };
14151
+ if (Array.isArray(issue.path) && issue.path.length > 0) {
14152
+ const [head, ...rest] = issue.path.map(String);
14153
+ const spelling = spellings.get(head);
14154
+ if (spelling)
14155
+ retyped.path = [spelling, ...rest];
14156
+ }
14157
+ if (typeof issue.message === "string") {
14158
+ retyped.message = retypeMessage(issue.message, spellings);
14159
+ }
14160
+ if (Array.isArray(issue.errors)) {
14161
+ retyped.errors = retypeDetails(issue.errors, spellings);
14162
+ }
14163
+ return retyped;
14164
+ }
14165
+ function retypeApiError(error, operation, commandSpec, operationSpec) {
14166
+ if (!(error instanceof SimApiError) || error.status === 0)
14167
+ return error;
14168
+ const spellings = typeableFields(operation, commandSpec, operationSpec);
14169
+ if (spellings.size === 0)
14170
+ return error;
14171
+ return new SimApiError(retypeMessage(error.message, spellings), error.status, error.code, error.details === undefined ? undefined : retypeDetails(error.details, spellings));
14172
+ }
14173
+
14174
+ // src/runtime/build.ts
14175
+ var GROUP_ALIASES = {
14176
+ "audit-logs": "audit-log",
14177
+ credentials: "credential",
14178
+ "custom-tools": "custom-tool",
14179
+ files: "file",
14180
+ knowledge: "kb",
14181
+ logs: "log",
14182
+ "mcp-servers": "mcp-server",
14183
+ secrets: "secret",
14184
+ skills: "skill",
14185
+ tables: "table",
14186
+ workflows: "workflow",
14187
+ workspaces: "workspace"
14188
+ };
14189
+ function describeOperation(operationSpec, described) {
14190
+ return operationSpec.personalKeyOnly ? `${described} (personal API key required)` : described;
14191
+ }
14192
+ function argumentSyntax(command) {
14193
+ return command.registeredArguments.map((argument) => {
14194
+ const name = `${argument.name()}${argument.variadic ? "..." : ""}`;
14195
+ return argument.required ? `<${name}>` : `[${name}]`;
14196
+ }).join(" ");
14197
+ }
14198
+ function commandPath(command) {
14199
+ const names = [];
14200
+ let current = command;
14201
+ while (current) {
14202
+ names.unshift(current.name());
14203
+ current = current.parent;
14204
+ }
14205
+ return names.join(" ");
14206
+ }
14207
+ function addMissingArgumentExample(command) {
14208
+ const outputError = command.configureOutput().outputError;
14209
+ if (!outputError)
14210
+ throw new Error("Commander output formatter is not configured");
14211
+ command.configureOutput({
14212
+ outputError: (message, write) => {
14213
+ outputError(message, write);
14214
+ if (!message.startsWith("error: missing required argument "))
14215
+ return;
14216
+ const syntax = argumentSyntax(command);
14217
+ const example = syntax ? `${commandPath(command)} ${syntax}` : commandPath(command);
14218
+ write(`Example: ${example}
14219
+ `);
14220
+ }
14221
+ });
14222
+ return command;
14223
+ }
14224
+ function assertNoReservedFlags(command, operation) {
14225
+ for (const option of command.options) {
14226
+ for (const flag of [option.long, option.short]) {
14227
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
14228
+ throw new Error(`${operation} declares ${flag}, which the root program already owns; give the flag another name`);
14229
+ }
14230
+ }
14231
+ }
14232
+ }
14233
+ var RESERVED_FLAG_EXEMPTIONS = new Set(["profiles add"]);
14234
+ function assertNoReservedProgramFlags(program2) {
14235
+ const walk = (command, prefix) => {
14236
+ const path = [...prefix, command.name()];
14237
+ const name = path.join(" ");
14238
+ if (!RESERVED_FLAG_EXEMPTIONS.has(name)) {
14239
+ for (const option of command.options) {
14240
+ for (const flag of [option.long, option.short]) {
14241
+ if (flag && RESERVED_PROGRAM_FLAGS.has(flag)) {
14242
+ throw new Error(`"sim ${name}" declares ${flag}, which the root program already owns; give the flag another name`);
14243
+ }
14244
+ }
14245
+ }
14246
+ }
14247
+ for (const child of command.commands)
14248
+ walk(child, path);
14249
+ };
14250
+ for (const child of program2.commands)
14251
+ walk(child, []);
14252
+ }
14253
+ function refuseHelpAfterUnknownCommand(program2) {
14254
+ const walk = (command) => {
14255
+ const internals = command;
14256
+ const dispatchesOnly = command.commands.length > 0 && !internals._actionHandler && command.registeredArguments.length === 0;
14257
+ if (dispatchesOnly) {
14258
+ const known = new Set(["help"]);
14259
+ for (const child of command.commands) {
14260
+ known.add(child.name());
14261
+ for (const alias of child.aliases())
14262
+ known.add(alias);
14263
+ }
14264
+ command.on("beforeHelp", () => {
14265
+ const first = command.args[0];
14266
+ if (first === undefined || first.startsWith("-") || known.has(first))
14267
+ return;
14268
+ internals.unknownCommand();
14269
+ });
14270
+ }
14271
+ for (const child of command.commands)
14272
+ walk(child);
14273
+ };
14274
+ walk(program2);
14275
+ }
14276
+ function configureOperation(command, operation, spec) {
14277
+ const operationSpec = V2_OPERATIONS[operation];
14278
+ command.allowExcessArguments(false);
14279
+ for (const alias of spec.aliases ?? [])
14280
+ command.alias(alias);
14281
+ for (const param of Object.keys(spec.pathFlags ?? {})) {
14282
+ if (!operationSpec.pathParams.includes(param)) {
14283
+ throw new Error(`${operation}.${param} is not a path parameter`);
14284
+ }
14285
+ }
14286
+ for (const param of Object.keys(spec.pathArgumentNames ?? {})) {
14287
+ if (!operationSpec.pathParams.includes(param)) {
14288
+ throw new Error(`${operation}.${param} is not a path parameter`);
14289
+ }
14290
+ if (spec.pathFlags?.[param]) {
14291
+ throw new Error(`${operation}.${param} cannot be both a path argument and a path flag`);
14292
+ }
14293
+ }
14294
+ if (spec.profileWorkspacePath) {
14295
+ if (!operationSpec.pathParams.includes(PROFILE_INJECTED_FIELD)) {
14296
+ throw new Error(`${operation}.profileWorkspacePath requires a workspaceId path parameter`);
14297
+ }
14298
+ if (spec.pathFlags?.[PROFILE_INJECTED_FIELD]) {
14299
+ throw new Error(`${operation}.workspaceId cannot be both profile-injected and a path flag`);
14300
+ }
14301
+ }
14302
+ for (const param of operationSpec.pathParams) {
14303
+ if (spec.pathFlags?.[param] || isProfileWorkspacePath(spec, param))
14304
+ continue;
14305
+ command.argument(`<${spec.pathArgumentNames?.[param] ?? param}>`, operationSpec.pathParamDocs?.[param]);
14306
+ }
14307
+ if (spec.allWorkspaces) {
14308
+ const workspace = operationSpec.query?.workspaceId ?? operationSpec.body?.workspaceId;
14309
+ if (!workspace || workspace.required) {
14310
+ throw new Error(`${operation}.allWorkspaces requires an optional workspaceId field`);
14311
+ }
14312
+ }
14313
+ for (const field of spec.positionals ?? []) {
14314
+ const descriptor = operationSpec.query?.[field] ?? operationSpec.body?.[field];
14315
+ if (!descriptor)
14316
+ throw new Error(`${operation}.${field} is not a request field`);
14317
+ if (spec.requestFields && !spec.requestFields.includes(field)) {
14318
+ throw new Error(`${operation}.${field} is positional but not exposed`);
14319
+ }
14320
+ command.argument(`<${flagNameFor(operation, field)}>`, flagSpecFor(operation, field).describe ?? descriptor.describe);
14321
+ }
14322
+ if (spec.requestFields) {
14323
+ for (const field of spec.requestFields) {
14324
+ if (!operationSpec.query?.[field] && !operationSpec.body?.[field] && !operationSpec.headers?.[field]) {
14325
+ throw new Error(`${operation}.${field} is not a request field`);
14326
+ }
14327
+ }
14328
+ for (const slot of ["query", "body", "headers"]) {
14329
+ for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
14330
+ if (descriptor.required && field !== PROFILE_INJECTED_FIELD && !spec.requestFields.includes(field)) {
14331
+ throw new Error(`${operation}.${field} is required but not exposed`);
14332
+ }
14333
+ }
14334
+ }
14335
+ }
14336
+ command.description(describeOperation(operationSpec, spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`));
14337
+ addOperationOptions(command, operation, spec, operationSpec);
14338
+ assertNoReservedFlags(command, operation);
14339
+ command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation).catch((error) => {
14340
+ throw retypeApiError(error, operation, spec, operationSpec);
14341
+ }));
14342
+ return command;
14343
+ }
14344
+ function buildLeaf(operation, spec, leafName) {
14345
+ return addMissingArgumentExample(configureOperation(new Command(leafName), operation, spec));
14346
+ }
14347
+ function addRenamedCommand(groups, operation, spec, from, to) {
14348
+ const segments = from.split(" ");
14349
+ const [groupName, ...rest] = segments;
14350
+ if (rest.length === 0)
14351
+ throw new Error(`${operation}.renamedFrom "${from}" must include a verb`);
14352
+ let parent = groupFor(groups, groupName);
14353
+ for (const segment of rest.slice(0, -1)) {
14354
+ parent = nestedGroup(parent, segment, { hidden: true });
14355
+ }
14356
+ const leaf = buildLeaf(operation, spec, rest[rest.length - 1]);
14357
+ leaf.hook("preAction", () => warnRenamedCommand(from, to));
14358
+ addSubcommand(parent, leaf, { hidden: true });
14359
+ }
14360
+ function addSubcommand(parent, child, options = {}) {
14361
+ const wasLeaf = parent.commands.length === 0 && parent.registeredArguments.length > 0;
14362
+ const usage = wasLeaf ? parent.usage() : null;
14363
+ parent.addCommand(child, { hidden: options.hidden });
14364
+ if (usage !== null)
14365
+ parent.usage(usage);
14366
+ }
14367
+ function groupFor(groups, name) {
14368
+ const existing = groups.get(name);
14369
+ if (existing)
14370
+ return existing;
14371
+ const group = new Command(name).description(`Manage ${name.replaceAll("-", " ")}`);
14372
+ const alias = GROUP_ALIASES[name];
14373
+ if (alias)
14374
+ group.alias(alias);
14375
+ groups.set(name, group);
14376
+ return group;
14377
+ }
14378
+ function resourceLabel(name) {
14379
+ const label = name.endsWith("s") ? name.slice(0, -1) : name;
14380
+ return label.replaceAll("-", " ");
14381
+ }
14382
+ function nestedGroup(parent, name, options = {}) {
14383
+ const existing = parent.commands.find((candidate) => candidate.name() === name);
14384
+ if (existing)
14385
+ return existing;
14386
+ const created = new Command(name).description(`Manage ${resourceLabel(parent.name())} ${name.replaceAll("-", " ")}`);
14387
+ addSubcommand(parent, created, { hidden: options.hidden });
14388
+ return created;
14389
+ }
14390
+ function addLeafCommand(groups, operation, spec, segments) {
14391
+ const [groupName, ...rest] = segments;
14392
+ if (rest.length === 0)
14393
+ throw new Error(`${operation} leaf command must include a verb`);
14394
+ const group = groupFor(groups, groupName);
14395
+ if (rest.length > 1) {
14396
+ let parent = group;
14397
+ for (const segment of rest.slice(0, -1)) {
14398
+ parent = nestedGroup(parent, segment);
14399
+ }
14400
+ parent.addCommand(buildLeaf(operation, spec, rest[rest.length - 1]));
14401
+ return;
14402
+ }
14403
+ group.addCommand(buildLeaf(operation, spec, rest[0]));
14404
+ }
14405
+ function variantCommandSpec(spec, variant) {
14406
+ return {
14407
+ ...spec,
14408
+ command: variant.command,
14409
+ groupDefault: false,
14410
+ aliases: [],
14411
+ positionals: variant.positionals,
14412
+ requestFields: variant.requestFields,
14413
+ variants: [],
14414
+ describe: variant.describe ?? spec.describe
14415
+ };
14416
+ }
14417
+ function buildGeneratedCommands() {
14418
+ const groups = new Map;
14419
+ const renamed = [];
14420
+ for (const operation of Object.keys(V2_OPERATIONS)) {
14421
+ const spec = CLI_CONTRACT[operation] ?? {};
14422
+ const operationSpec = V2_OPERATIONS[operation];
14423
+ if (spec.hidden || operationSpec.responseMode !== "json")
14424
+ continue;
14425
+ const segments = spec.command ? spec.command.split(" ") : deriveCommandPath(operation);
14426
+ if (spec.groupDefault) {
14427
+ const [groupName, ...rest] = segments;
14428
+ const group = groupFor(groups, groupName);
14429
+ if (rest.length > 0)
14430
+ throw new Error(`${operation} groupDefault must name a command group`);
14431
+ const pathPositionals = operationSpec.pathParams.filter((param) => !spec.pathFlags?.[param] && !isProfileWorkspacePath(spec, param));
14432
+ if (pathPositionals.length > 0 || spec.positionals?.length) {
14433
+ throw new Error(`${operation} groupDefault cannot require positional arguments`);
14434
+ }
14435
+ configureOperation(group, operation, spec);
14436
+ } else {
14437
+ addLeafCommand(groups, operation, spec, segments);
14438
+ }
14439
+ for (const variant of spec.variants ?? []) {
14440
+ addLeafCommand(groups, operation, variantCommandSpec(spec, variant), variant.command.split(" "));
14441
+ }
14442
+ for (const from of spec.renamedFrom ?? []) {
14443
+ renamed.push({ operation, spec, from, to: segments.join(" ") });
14444
+ }
14445
+ }
14446
+ for (const { operation, spec, from, to } of renamed) {
14447
+ addRenamedCommand(groups, operation, spec, from, to);
14448
+ }
14449
+ return [...groups.values()].sort((a, b) => a.name().localeCompare(b.name()));
14450
+ }
14451
+
14452
+ // src/commands/credentials.ts
14453
+ var CONNECTION_RESULT = {
14454
+ fields: [
14455
+ { header: "connection link", path: "authorizationUrl" },
14456
+ { header: "expires", path: "expiresAt", format: "timestamp" }
14457
+ ]
14458
+ };
14459
+ var SERVICE_ACCOUNT_RESULT = {
14460
+ fields: [
14461
+ { header: "id" },
14462
+ { header: "name", path: "displayName" },
14463
+ { header: "provider", path: "providerId" },
14464
+ { header: "role" },
14465
+ { header: "created", path: "createdAt", format: "timestamp" }
14466
+ ]
14467
+ };
14468
+ function serviceAccountProvider(providers, providerId) {
14469
+ const provider = providers.find((candidate) => candidate.type === "service_account" && candidate.providerId === providerId);
14470
+ if (!provider) {
14471
+ throw new SimApiError(`Unknown service-account provider "${providerId}".`, 0);
14472
+ }
14473
+ if (!provider.available) {
14474
+ throw new SimApiError(`Service-account provider "${providerId}" is not available.`, 0);
14475
+ }
14476
+ return provider;
13537
14477
  }
13538
14478
  function credentialValues(provider, raw) {
13539
14479
  const parsed = coerce(raw, { kind: "object" }, { json: true }, "credentials");
@@ -13620,9 +14560,9 @@ function attachCredentialCommands(program2) {
13620
14560
  if (!credentials)
13621
14561
  throw new Error("The generated credentials command group is missing");
13622
14562
  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 }));
14563
+ 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));
14564
+ 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 }));
14565
+ 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
14566
  }
13627
14567
 
13628
14568
  // src/commands/protocol/result.ts
@@ -13775,7 +14715,7 @@ Examples:
13775
14715
 
13776
14716
  // src/commands/protocol/files-get.ts
13777
14717
  import { once as once2 } from "node:events";
13778
- import { createWriteStream } from "node:fs";
14718
+ import { createWriteStream, rmSync } from "node:fs";
13779
14719
  import { link, lstat, mkdtemp, readlink, rename, rm } from "node:fs/promises";
13780
14720
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
13781
14721
  import { Readable } from "node:stream";
@@ -13831,37 +14771,66 @@ async function streamToFile(body, file, reportedPath = file.path) {
13831
14771
  throw writeFailure(reportedPath, error);
13832
14772
  }
13833
14773
  }
14774
+ var STAGE_SIGNALS = ["SIGINT", "SIGTERM"];
14775
+ function reRaise(signal) {
14776
+ process.kill(process.pid, signal);
14777
+ }
14778
+ function removeStagingOnSignal(stagingDirectory, terminate = reRaise) {
14779
+ const installed = STAGE_SIGNALS.map((signal) => {
14780
+ const onSignal = () => {
14781
+ process.off(signal, onSignal);
14782
+ const directory = stagingDirectory();
14783
+ if (directory) {
14784
+ try {
14785
+ rmSync(directory, { recursive: true, force: true });
14786
+ } catch {}
14787
+ }
14788
+ terminate(signal);
14789
+ };
14790
+ process.on(signal, onSignal);
14791
+ return [signal, onSignal];
14792
+ });
14793
+ return () => {
14794
+ for (const [signal, onSignal] of installed)
14795
+ process.off(signal, onSignal);
14796
+ };
14797
+ }
13834
14798
  async function saveStagedFile(body, target, force) {
13835
14799
  let temporaryDirectory = null;
13836
14800
  let failure = null;
14801
+ const disposeSignalCleanup = removeStagingOnSignal(() => temporaryDirectory);
13837
14802
  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;
14803
+ try {
14804
+ const publicationTarget = force ? await forcedPublicationTarget(target) : target;
14805
+ temporaryDirectory = await mkdtemp(join2(dirname2(publicationTarget), ".sim-download-"));
14806
+ const temporaryPath = join2(temporaryDirectory, "payload");
14807
+ await streamToFile(body, createWriteStream(temporaryPath, { flags: "wx" }), target);
14808
+ if (force) {
14809
+ await rename(temporaryPath, publicationTarget);
14810
+ } else {
14811
+ try {
14812
+ await link(temporaryPath, publicationTarget);
14813
+ } catch (error) {
14814
+ throw unsupportedAtomicPublish(target, error) ?? error;
14815
+ }
13849
14816
  }
14817
+ } catch (error) {
14818
+ failure = normalizedWriteFailure(target, error);
13850
14819
  }
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);
14820
+ if (temporaryDirectory) {
14821
+ try {
14822
+ await rm(temporaryDirectory, { recursive: true, force: true });
14823
+ } catch (cleanupError) {
14824
+ if (failure)
14825
+ throw combinedCleanupFailure(failure, temporaryDirectory, cleanupError);
14826
+ throw new SimApiError(`Saved ${target}, but could not remove temporary directory ${temporaryDirectory}: ${cleanupError.message}`, 0);
14827
+ }
13861
14828
  }
14829
+ if (failure)
14830
+ throw failure;
14831
+ } finally {
14832
+ disposeSignalCleanup();
13862
14833
  }
13863
- if (failure)
13864
- throw failure;
13865
14834
  }
13866
14835
  async function saveToFile(body, target, force) {
13867
14836
  return saveStagedFile(body, target, force);
@@ -14172,6 +15141,7 @@ function renderCell2(value, format) {
14172
15141
  }
14173
15142
  var COLUMNS = (CLI_CONTRACT.listLogs?.columns ?? []).map((spec) => ({
14174
15143
  header: spec.header,
15144
+ floor: Math.min(MAX_CELL_WIDTH2, spec.minWidth ?? 0),
14175
15145
  value: (row) => renderCell2(at2(row, spec.path ?? spec.header), spec.format)
14176
15146
  }));
14177
15147
  function oneLine2(value) {
@@ -14188,15 +15158,15 @@ function clamp2(value, width) {
14188
15158
  function createTableWriter() {
14189
15159
  let widths = null;
14190
15160
  return (rows) => {
14191
- const lines = rows.map((row) => COLUMNS.map((column) => oneLine2(column.value(row))));
15161
+ const lines = rows.map((row) => COLUMNS.map((column) => clamp2(oneLine2(column.value(row)), MAX_CELL_WIDTH2)));
14192
15162
  if (!widths) {
14193
- widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
15163
+ widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(column.floor, visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
14194
15164
  const header = widths;
14195
15165
  console.log(source_default.dim(COLUMNS.map((column, index) => pad2(column.header.toUpperCase(), header[index])).join(" ").trimEnd()));
14196
15166
  }
14197
15167
  const locked = widths;
14198
15168
  for (const line of lines) {
14199
- console.log(line.map((cell, index) => pad2(clamp2(cell, locked[index]), locked[index])).join(" ").trimEnd());
15169
+ console.log(line.map((cell, index) => pad2(cell, locked[index])).join(" ").trimEnd());
14200
15170
  }
14201
15171
  };
14202
15172
  }
@@ -14341,173 +15311,72 @@ function attachLogsFollow(logs) {
14341
15311
  Each run prints once, when it is first seen, so its status is the status it had
14342
15312
  at that moment. With --output json every run is a JSON object on its own line
14343
15313
  (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)`);
15314
+ never close one; --output yaml emits a --- separated document stream. Progress
15315
+ and retries go to stderr, leaving stdout a clean stream of rows. Ctrl-C stops the
15316
+ follow.
15317
+
15318
+ Examples:
15319
+ $ sim logs follow --level error
15320
+ $ sim logs follow --workflow wf_123 -n 0
15321
+ $ sim --output json logs follow | jq -r '.runId'
15322
+ `).action(async (options, command) => {
15323
+ const lines = nonNegativeInteger(options.lines, "--lines");
15324
+ const delay = intervalMs(options.interval);
15325
+ const { client, profile } = clientFrom(command);
15326
+ const path = V2_OPERATIONS.listLogs.path;
15327
+ const query = {
15328
+ workspaceId: client.requireWorkspace(),
15329
+ workflowIds: options.workflow?.length ? options.workflow.join(",") : undefined,
15330
+ folderPaths: options.folder?.length ? options.folder.map(encodeFolderPath).join(",") : undefined,
15331
+ triggers: options.trigger?.length ? options.trigger.join(",") : undefined,
15332
+ level: options.level,
15333
+ details: options.details,
15334
+ sortBy: "startedAt",
15335
+ sortOrder: "desc"
15336
+ };
15337
+ const write = createWriter(profile.output);
15338
+ const status = followStatus();
15339
+ const interrupt = watchForInterrupt();
15340
+ const state = { seen: new Map, floor: null };
15341
+ try {
15342
+ const seed = await collectUnprinted(client, path, query, state, Math.max(lines, 1), 1);
15343
+ remember(state, seed.rows);
15344
+ state.floor = seed.rows.at(-1)?.startedAt ?? null;
15345
+ if (seed.truncated && seed.rows.length < lines) {
15346
+ status.warn(`asked for ${lines} earlier runs but a page holds ${seed.rows.length}; following from there — see sim logs list for more`);
14502
15347
  }
14503
- } else {
14504
- command.requiredOption("--body <json|@file>", "Request body as JSON (or @path / @- to read a file or stdin) (required)");
15348
+ write(lines > 0 ? seed.rows.slice(0, lines).reverse() : []);
15349
+ let failures = 0;
15350
+ while (!interrupt.interrupted()) {
15351
+ await waitFor(failures === 0 ? delay : Math.min(delay * 2 ** failures, MAX_BACKOFF_MS), interrupt.interrupted);
15352
+ if (interrupt.interrupted())
15353
+ break;
15354
+ let fresh;
15355
+ try {
15356
+ fresh = await collectUnprinted(client, path, query, state, POLL_PAGE_SIZE, MAX_PAGES_PER_POLL);
15357
+ } catch (error) {
15358
+ if (!isTransient(error))
15359
+ throw error;
15360
+ failures += 1;
15361
+ const next = Math.min(delay * 2 ** failures, MAX_BACKOFF_MS);
15362
+ status.note(`poll failed (${error.message}); retrying in ${inSeconds(next)}s…`);
15363
+ continue;
15364
+ }
15365
+ failures = 0;
15366
+ status.clear();
15367
+ if (fresh.truncated) {
15368
+ status.warn(`more than ${MAX_PAGES_PER_POLL * POLL_PAGE_SIZE} runs arrived at once; older ones were skipped — see sim logs list`);
15369
+ }
15370
+ if (fresh.rows.length === 0)
15371
+ continue;
15372
+ remember(state, fresh.rows);
15373
+ write(fresh.rows.reverse());
15374
+ }
15375
+ } finally {
15376
+ status.clear();
15377
+ interrupt.dispose();
14505
15378
  }
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
- }
15379
+ });
14511
15380
  }
14512
15381
 
14513
15382
  // src/commands/protocol/resource-directory.ts
@@ -14663,13 +15532,16 @@ function validateTargetOptions(options) {
14663
15532
  return intoExisting;
14664
15533
  }
14665
15534
  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) => {
15535
+ 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
15536
  const { client, profile } = clientFrom(command);
14668
15537
  const workspaceId = client.requireWorkspace();
14669
15538
  if (Boolean(path) === Boolean(options.fileId)) {
14670
15539
  throw new SimApiError("Pass exactly one of <path> or --file-id <id>", 0);
14671
15540
  }
14672
15541
  const intoExisting = validateTargetOptions(options);
15542
+ if (intoExisting && options.mode === "replace" && options.yes !== true) {
15543
+ 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);
15544
+ }
14673
15545
  const local = path ? await localFile(path) : null;
14674
15546
  const source = local ? {
14675
15547
  type: "upload",
@@ -14724,202 +15596,18 @@ function attachTableImport(tables) {
14724
15596
  });
14725
15597
  return;
14726
15598
  }
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
15599
+ const finished = await watchImport(client, workspaceId, job);
15600
+ if (finished.status !== "completed") {
15601
+ throw new SimApiError(`Import ${finished.status}${finished.error ? `: ${finished.error}` : ""}`, 0);
15602
+ }
15603
+ printProtocolResult(profile.output, {
15604
+ id: finished.id,
15605
+ status: finished.status,
15606
+ tableId: finished.tableId,
15607
+ rowsProcessed: finished.rowsProcessed,
15608
+ ...rejectionFields(finished)
15609
+ });
14919
15610
  });
14920
- const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14921
- if (failure)
14922
- throw new SimApiError(failure, 0);
14923
15611
  }
14924
15612
 
14925
15613
  // src/commands/protocol/workflow-run-follow.ts
@@ -15141,6 +15829,9 @@ function followOrDelegate(previous) {
15141
15829
  command.setOptionValue("run", selection);
15142
15830
  const flags = command.optsWithGlobals();
15143
15831
  if (flags.follow !== true) {
15832
+ if (Array.isArray(flags.selectOutput) && flags.selectOutput.length > 0) {
15833
+ 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);
15834
+ }
15144
15835
  if (flags.includeThinking === true || flags.includeToolCalls === true) {
15145
15836
  throw new SimApiError("--include-thinking and --include-tool-calls describe a stream; add --follow", 0);
15146
15837
  }
@@ -15455,6 +16146,8 @@ function validateWorkspaceOnlyFlag(flag, value, scope) {
15455
16146
  async function readSecretValue(options) {
15456
16147
  if (options.value !== undefined)
15457
16148
  return validateSecretValue(readValueArgument(options.value));
16149
+ if (options.description !== undefined || options.unredacted !== undefined)
16150
+ return;
15458
16151
  try {
15459
16152
  return validateSecretValue(await promptSecret());
15460
16153
  } catch (error) {
@@ -15464,7 +16157,10 @@ async function readSecretValue(options) {
15464
16157
  return process.exit(CANCELLED_EXIT_CODE);
15465
16158
  }
15466
16159
  }
15467
- async function setSecret(name, options, command) {
16160
+ async function setSecret(name, options, command, redactionSpellings) {
16161
+ if (redactionSpellings.size > 1) {
16162
+ throw new SimApiError("Pass either --unredacted or --no-unredacted, not both: they are one setting, and commander keeps only whichever came last.", 0);
16163
+ }
15468
16164
  const description = validateWorkspaceOnlyFlag("description", options.description, options.scope);
15469
16165
  const unredacted = validateWorkspaceOnlyFlag("unredacted", options.unredacted, options.scope);
15470
16166
  const value = await readSecretValue(options);
@@ -15475,7 +16171,7 @@ async function setSecret(name, options, command) {
15475
16171
  body: {
15476
16172
  workspaceId: client.requireWorkspace(),
15477
16173
  scope: options.scope,
15478
- value,
16174
+ ...value === undefined ? {} : { value },
15479
16175
  description,
15480
16176
  ...unredacted === undefined ? {} : { unredacted }
15481
16177
  }
@@ -15486,257 +16182,8 @@ function attachSecretCommands(program2) {
15486
16182
  const secrets = program2.commands.find((command) => command.name() === "secrets");
15487
16183
  if (!secrets)
15488
16184
  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()));
16185
+ const redactionSpellings = new Set;
16186
+ 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
16187
  }
15741
16188
 
15742
16189
  // src/program.ts
@@ -15816,6 +16263,7 @@ function buildProgram(options = {}) {
15816
16263
  attachProtocolCommands(program2);
15817
16264
  attachSecretCommands(program2);
15818
16265
  program2.addHelpText("after", HELP_EPILOGUE);
16266
+ refuseHelpAfterUnknownCommand(program2);
15819
16267
  assertNoReservedProgramFlags(program2);
15820
16268
  return program2;
15821
16269
  }