sim 2.1.2-preview.47.1 → 2.1.2-preview.49.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 +543 -128
  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;
@@ -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,11 @@ 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"];
2416
-
2417
- class ProfileConfigError extends Error {
2418
- constructor(message) {
2419
- super(message);
2420
- this.name = "ProfileConfigError";
2448
+ var FORBIDDEN_IN_VALUE_GLOBAL = new RegExp(FORBIDDEN_IN_VALUE.source, "g");
2449
+ var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
2450
+ function validateProfileName(name) {
2451
+ if (!PROFILE_NAME_PATTERN.test(name)) {
2452
+ throw new ProfileConfigError(`Invalid profile name "${name.replace(FORBIDDEN_IN_VALUE_GLOBAL, " ")}". Use letters, numbers, dots, underscores, or hyphens, starting with a letter or number.`);
2421
2453
  }
2422
2454
  }
2423
2455
  function configSectionName(profile) {
@@ -2541,18 +2573,31 @@ function deleteProfile(profile) {
2541
2573
  return { config, credentials };
2542
2574
  }
2543
2575
  function normalizeEndpoint(endpoint, source) {
2544
- const trimmed = endpoint.replace(/\/+$/, "");
2576
+ const trimmed = endpoint.trim().replace(/\/+$/, "");
2577
+ if (FORBIDDEN_IN_VALUE.test(trimmed)) {
2578
+ throw new ProfileConfigError(`Invalid endpoint "${endpoint.replace(FORBIDDEN_IN_VALUE_GLOBAL, " ")}" from ${source}. An endpoint cannot contain line breaks or control characters.`);
2579
+ }
2545
2580
  let parsed;
2546
2581
  try {
2547
2582
  parsed = new URL(trimmed);
2548
2583
  } catch {
2549
- throw new ProfileConfigError(`Invalid endpoint "${endpoint}" from ${source}. Use an absolute URL, e.g. ${DEFAULT_ENDPOINT} or http://localhost:3000`);
2584
+ throw new ProfileConfigError(`Invalid endpoint "${endpoint.replace(FORBIDDEN_IN_VALUE_GLOBAL, " ")}" from ${source}. Use an absolute URL, e.g. ${DEFAULT_ENDPOINT} or http://localhost:3000`);
2550
2585
  }
2551
2586
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2552
2587
  throw new ProfileConfigError(`Unsupported endpoint scheme "${parsed.protocol.replace(/:$/, "")}" from ${source}. Use http or https, e.g. ${DEFAULT_ENDPOINT}`);
2553
2588
  }
2554
2589
  return trimmed;
2555
2590
  }
2591
+ function normalizeWorkspaceId(workspaceId, source) {
2592
+ const trimmed = workspaceId.trim();
2593
+ if (!trimmed) {
2594
+ throw new ProfileConfigError(`Empty workspace id from ${source}.`);
2595
+ }
2596
+ if (FORBIDDEN_IN_VALUE.test(trimmed)) {
2597
+ throw new ProfileConfigError(`Invalid workspace id "${trimmed.replace(FORBIDDEN_IN_VALUE_GLOBAL, " ")}" from ${source}. A workspace id cannot contain line breaks or control characters.`);
2598
+ }
2599
+ return trimmed;
2600
+ }
2556
2601
  function resolve(candidates, fallback, fallbackSource) {
2557
2602
  for (const [source, value] of candidates) {
2558
2603
  if (value !== null && value !== undefined && value !== "")
@@ -2565,6 +2610,9 @@ function resolveProfile(overrides = {}) {
2565
2610
  const name = named || DEFAULT_PROFILE;
2566
2611
  if (named && !overrides.allowUnknownProfile)
2567
2612
  requireKnownProfile(named);
2613
+ if (named && overrides.allowUnknownProfile && !listProfiles().includes(named)) {
2614
+ validateProfileName(named);
2615
+ }
2568
2616
  const config = readConfigProfile(name);
2569
2617
  const authProfile = resolveAuthenticationProfileName(name);
2570
2618
  const authConfig = authProfile === name ? config : readConfigProfile(authProfile);
@@ -6518,7 +6566,8 @@ var V2_OPERATIONS = {
6518
6566
  version: "Numeric deployment version."
6519
6567
  },
6520
6568
  responseMode: "json",
6521
- summary: "Activate Workflow Version"
6569
+ summary: "Activate Workflow Version",
6570
+ personalKeyOnly: true
6522
6571
  },
6523
6572
  addTableColumn: {
6524
6573
  method: "POST",
@@ -6565,6 +6614,7 @@ var V2_OPERATIONS = {
6565
6614
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6566
6615
  responseMode: "json",
6567
6616
  summary: "Index Workspace Files",
6617
+ personalKeyOnly: true,
6568
6618
  body: {
6569
6619
  workspaceId: {
6570
6620
  kind: "string",
@@ -6585,6 +6635,7 @@ var V2_OPERATIONS = {
6585
6635
  pathParamDocs: { workflowId: "Unique workflow identifier." },
6586
6636
  responseMode: "json",
6587
6637
  summary: "Apply Workflow Operations",
6638
+ personalKeyOnly: true,
6588
6639
  query: {
6589
6640
  dryRun: {
6590
6641
  kind: "boolean",
@@ -6684,6 +6735,7 @@ var V2_OPERATIONS = {
6684
6735
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6685
6736
  responseMode: "json",
6686
6737
  summary: "Bulk Save Tag Definitions",
6738
+ personalKeyOnly: true,
6687
6739
  body: {
6688
6740
  workspaceId: {
6689
6741
  kind: "string",
@@ -6707,6 +6759,7 @@ var V2_OPERATIONS = {
6707
6759
  },
6708
6760
  responseMode: "json",
6709
6761
  summary: "Bulk Update Chunks",
6762
+ personalKeyOnly: true,
6710
6763
  body: {
6711
6764
  workspaceId: {
6712
6765
  kind: "string",
@@ -6722,7 +6775,7 @@ var V2_OPERATIONS = {
6722
6775
  chunkIds: {
6723
6776
  kind: "array",
6724
6777
  required: true,
6725
- describe: "Chunks to operate on, by identifier. Ids outside the document are ignored."
6778
+ 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
6779
  }
6727
6780
  }
6728
6781
  },
@@ -6733,6 +6786,7 @@ var V2_OPERATIONS = {
6733
6786
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
6734
6787
  responseMode: "json",
6735
6788
  summary: "Bulk Enable or Disable Documents",
6789
+ personalKeyOnly: true,
6736
6790
  body: {
6737
6791
  workspaceId: {
6738
6792
  kind: "string",
@@ -6957,6 +7011,7 @@ var V2_OPERATIONS = {
6957
7011
  pathParams: [],
6958
7012
  responseMode: "json",
6959
7013
  summary: "Create Credential Connection",
7014
+ personalKeyOnly: true,
6960
7015
  body: {
6961
7016
  workspaceId: {
6962
7017
  kind: "string",
@@ -7132,6 +7187,7 @@ var V2_OPERATIONS = {
7132
7187
  },
7133
7188
  responseMode: "json",
7134
7189
  summary: "Create Chunk",
7190
+ personalKeyOnly: true,
7135
7191
  body: {
7136
7192
  workspaceId: {
7137
7193
  kind: "string",
@@ -7157,6 +7213,7 @@ var V2_OPERATIONS = {
7157
7213
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7158
7214
  responseMode: "json",
7159
7215
  summary: "Create Knowledge Connector",
7216
+ personalKeyOnly: true,
7160
7217
  body: {
7161
7218
  workspaceId: {
7162
7219
  kind: "string",
@@ -7272,6 +7329,7 @@ var V2_OPERATIONS = {
7272
7329
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7273
7330
  responseMode: "json",
7274
7331
  summary: "Create Tag",
7332
+ personalKeyOnly: true,
7275
7333
  body: {
7276
7334
  workspaceId: {
7277
7335
  kind: "string",
@@ -7379,6 +7437,7 @@ var V2_OPERATIONS = {
7379
7437
  pathParams: [],
7380
7438
  responseMode: "json",
7381
7439
  summary: "Create Service-Account Credential",
7440
+ personalKeyOnly: true,
7382
7441
  body: {
7383
7442
  workspaceId: {
7384
7443
  kind: "string",
@@ -7417,6 +7476,7 @@ var V2_OPERATIONS = {
7417
7476
  pathParams: [],
7418
7477
  responseMode: "json",
7419
7478
  summary: "Create Skill",
7479
+ personalKeyOnly: true,
7420
7480
  body: {
7421
7481
  workspaceId: {
7422
7482
  kind: "string",
@@ -7632,6 +7692,7 @@ var V2_OPERATIONS = {
7632
7692
  pathParams: [],
7633
7693
  responseMode: "json",
7634
7694
  summary: "Create Workflow MCP Server",
7695
+ personalKeyOnly: true,
7635
7696
  body: {
7636
7697
  workspaceId: {
7637
7698
  kind: "string",
@@ -7662,6 +7723,7 @@ var V2_OPERATIONS = {
7662
7723
  pathParamDocs: { credentialId: "Credential to disconnect." },
7663
7724
  responseMode: "json",
7664
7725
  summary: "Disconnect Credential",
7726
+ personalKeyOnly: true,
7665
7727
  query: {
7666
7728
  workspaceId: {
7667
7729
  kind: "string",
@@ -7752,6 +7814,7 @@ var V2_OPERATIONS = {
7752
7814
  },
7753
7815
  responseMode: "json",
7754
7816
  summary: "Delete Chunk",
7817
+ personalKeyOnly: true,
7755
7818
  query: {
7756
7819
  workspaceId: {
7757
7820
  kind: "string",
@@ -7770,6 +7833,7 @@ var V2_OPERATIONS = {
7770
7833
  },
7771
7834
  responseMode: "json",
7772
7835
  summary: "Delete Knowledge Connector",
7836
+ personalKeyOnly: true,
7773
7837
  query: {
7774
7838
  workspaceId: {
7775
7839
  kind: "string",
@@ -7840,6 +7904,7 @@ var V2_OPERATIONS = {
7840
7904
  },
7841
7905
  responseMode: "json",
7842
7906
  summary: "Delete Tag",
7907
+ personalKeyOnly: true,
7843
7908
  query: {
7844
7909
  workspaceId: {
7845
7910
  kind: "string",
@@ -7855,6 +7920,7 @@ var V2_OPERATIONS = {
7855
7920
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
7856
7921
  responseMode: "json",
7857
7922
  summary: "Delete Tag Definitions",
7923
+ personalKeyOnly: true,
7858
7924
  query: {
7859
7925
  workspaceId: {
7860
7926
  kind: "string",
@@ -7886,9 +7952,10 @@ var V2_OPERATIONS = {
7886
7952
  method: "DELETE",
7887
7953
  path: "/api/v2/secrets/[name]",
7888
7954
  pathParams: ["name"],
7889
- pathParamDocs: { name: "Secret to create, replace, or delete." },
7955
+ pathParamDocs: { name: "Secret to delete." },
7890
7956
  responseMode: "json",
7891
7957
  summary: "Delete Secret",
7958
+ personalKeyOnly: true,
7892
7959
  query: {
7893
7960
  workspaceId: {
7894
7961
  kind: "string",
@@ -7912,6 +7979,7 @@ var V2_OPERATIONS = {
7912
7979
  },
7913
7980
  responseMode: "json",
7914
7981
  summary: "Delete Skill",
7982
+ personalKeyOnly: true,
7915
7983
  query: {
7916
7984
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." }
7917
7985
  }
@@ -8022,7 +8090,8 @@ var V2_OPERATIONS = {
8022
8090
  pathParams: ["workflowId"],
8023
8091
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8024
8092
  responseMode: "json",
8025
- summary: "Delete Workflow Chat Deployment"
8093
+ summary: "Delete Workflow Chat Deployment",
8094
+ personalKeyOnly: true
8026
8095
  },
8027
8096
  deleteWorkflowFolder: {
8028
8097
  method: "DELETE",
@@ -8072,7 +8141,8 @@ var V2_OPERATIONS = {
8072
8141
  pathParams: ["serverId"],
8073
8142
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8074
8143
  responseMode: "json",
8075
- summary: "Delete Workflow MCP Server"
8144
+ summary: "Delete Workflow MCP Server",
8145
+ personalKeyOnly: true
8076
8146
  },
8077
8147
  deployWorkflow: {
8078
8148
  method: "POST",
@@ -8081,6 +8151,7 @@ var V2_OPERATIONS = {
8081
8151
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8082
8152
  responseMode: "json",
8083
8153
  summary: "Deploy Workflow",
8154
+ personalKeyOnly: true,
8084
8155
  body: {
8085
8156
  name: { kind: "string", describe: "Optional label for the deployment version." },
8086
8157
  description: {
@@ -8096,6 +8167,7 @@ var V2_OPERATIONS = {
8096
8167
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8097
8168
  responseMode: "json",
8098
8169
  summary: "Publish Workflow As MCP Tool",
8170
+ personalKeyOnly: true,
8099
8171
  body: {
8100
8172
  workflowId: {
8101
8173
  kind: "string",
@@ -8236,6 +8308,7 @@ var V2_OPERATIONS = {
8236
8308
  pathParamDocs: { auditLogId: "Audit-log entry identifier." },
8237
8309
  responseMode: "json",
8238
8310
  summary: "Get Audit Log",
8311
+ personalKeyOnly: true,
8239
8312
  query: {
8240
8313
  organizationId: {
8241
8314
  kind: "string",
@@ -8301,7 +8374,7 @@ var V2_OPERATIONS = {
8301
8374
  kind: "enum",
8302
8375
  values: ["active", "archived"],
8303
8376
  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."
8377
+ 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
8378
  }
8306
8379
  }
8307
8380
  },
@@ -8364,6 +8437,7 @@ var V2_OPERATIONS = {
8364
8437
  },
8365
8438
  responseMode: "json",
8366
8439
  summary: "Get Chunk",
8440
+ personalKeyOnly: true,
8367
8441
  query: {
8368
8442
  workspaceId: {
8369
8443
  kind: "string",
@@ -8382,6 +8456,7 @@ var V2_OPERATIONS = {
8382
8456
  },
8383
8457
  responseMode: "json",
8384
8458
  summary: "Get Knowledge Connector",
8459
+ personalKeyOnly: true,
8385
8460
  query: {
8386
8461
  workspaceId: {
8387
8462
  kind: "string",
@@ -8489,6 +8564,7 @@ var V2_OPERATIONS = {
8489
8564
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
8490
8565
  responseMode: "json",
8491
8566
  summary: "Get Next Tag Slot",
8567
+ personalKeyOnly: true,
8492
8568
  query: {
8493
8569
  workspaceId: {
8494
8570
  kind: "string",
@@ -8656,7 +8732,8 @@ var V2_OPERATIONS = {
8656
8732
  pathParams: ["workflowId"],
8657
8733
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8658
8734
  responseMode: "json",
8659
- summary: "Get Workflow Chat Deployment"
8735
+ summary: "Get Workflow Chat Deployment",
8736
+ personalKeyOnly: true
8660
8737
  },
8661
8738
  getWorkflowDeployment: {
8662
8739
  method: "GET",
@@ -8672,7 +8749,8 @@ var V2_OPERATIONS = {
8672
8749
  pathParams: ["serverId"],
8673
8750
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
8674
8751
  responseMode: "json",
8675
- summary: "Get Workflow MCP Server"
8752
+ summary: "Get Workflow MCP Server",
8753
+ personalKeyOnly: true
8676
8754
  },
8677
8755
  getWorkflowRun: {
8678
8756
  method: "GET",
@@ -8739,6 +8817,7 @@ var V2_OPERATIONS = {
8739
8817
  },
8740
8818
  responseMode: "json",
8741
8819
  summary: "Grant Skill Editor",
8820
+ personalKeyOnly: true,
8742
8821
  body: {
8743
8822
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
8744
8823
  email: {
@@ -8779,6 +8858,7 @@ var V2_OPERATIONS = {
8779
8858
  pathParams: [],
8780
8859
  responseMode: "json",
8781
8860
  summary: "List Audit Logs",
8861
+ personalKeyOnly: true,
8782
8862
  query: {
8783
8863
  action: { kind: "string", describe: "Filter by exact action name." },
8784
8864
  resourceType: {
@@ -9116,7 +9196,7 @@ var V2_OPERATIONS = {
9116
9196
  kind: "enum",
9117
9197
  values: ["active", "archived"],
9118
9198
  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."
9199
+ 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
9200
  }
9121
9201
  }
9122
9202
  },
@@ -9158,7 +9238,7 @@ var V2_OPERATIONS = {
9158
9238
  kind: "enum",
9159
9239
  values: ["active", "archived"],
9160
9240
  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."
9241
+ 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
9242
  },
9163
9243
  search: {
9164
9244
  kind: "string",
@@ -9246,6 +9326,7 @@ var V2_OPERATIONS = {
9246
9326
  },
9247
9327
  responseMode: "json",
9248
9328
  summary: "List Chunks",
9329
+ personalKeyOnly: true,
9249
9330
  query: {
9250
9331
  workspaceId: {
9251
9332
  kind: "string",
@@ -9295,6 +9376,7 @@ var V2_OPERATIONS = {
9295
9376
  },
9296
9377
  responseMode: "json",
9297
9378
  summary: "List Knowledge Connector Documents",
9379
+ personalKeyOnly: true,
9298
9380
  query: {
9299
9381
  workspaceId: {
9300
9382
  kind: "string",
@@ -9323,6 +9405,7 @@ var V2_OPERATIONS = {
9323
9405
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9324
9406
  responseMode: "json",
9325
9407
  summary: "List Knowledge Connectors",
9408
+ personalKeyOnly: true,
9326
9409
  query: {
9327
9410
  workspaceId: {
9328
9411
  kind: "string",
@@ -9466,6 +9549,7 @@ var V2_OPERATIONS = {
9466
9549
  pathParamDocs: { knowledgeBaseId: "Unique knowledge base identifier." },
9467
9550
  responseMode: "json",
9468
9551
  summary: "List Tag Usage",
9552
+ personalKeyOnly: true,
9469
9553
  query: {
9470
9554
  workspaceId: {
9471
9555
  kind: "string",
@@ -9624,6 +9708,7 @@ var V2_OPERATIONS = {
9624
9708
  pathParamDocs: { mcpServerId: "Unique MCP server identifier." },
9625
9709
  responseMode: "json",
9626
9710
  summary: "List MCP Server Tools",
9711
+ personalKeyOnly: true,
9627
9712
  query: {
9628
9713
  workspaceId: {
9629
9714
  kind: "string",
@@ -9642,6 +9727,7 @@ var V2_OPERATIONS = {
9642
9727
  pathParams: [],
9643
9728
  responseMode: "json",
9644
9729
  summary: "List Secrets",
9730
+ personalKeyOnly: true,
9645
9731
  query: {
9646
9732
  workspaceId: {
9647
9733
  kind: "string",
@@ -9834,7 +9920,7 @@ var V2_OPERATIONS = {
9834
9920
  kind: "enum",
9835
9921
  values: ["active", "archived"],
9836
9922
  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."
9923
+ 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
9924
  },
9839
9925
  folderPath: {
9840
9926
  kind: "string",
@@ -9977,6 +10063,7 @@ var V2_OPERATIONS = {
9977
10063
  pathParams: [],
9978
10064
  responseMode: "json",
9979
10065
  summary: "List Workflow MCP Servers",
10066
+ personalKeyOnly: true,
9980
10067
  query: {
9981
10068
  workspaceId: {
9982
10069
  kind: "string",
@@ -10012,7 +10099,8 @@ var V2_OPERATIONS = {
10012
10099
  pathParams: ["serverId"],
10013
10100
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
10014
10101
  responseMode: "json",
10015
- summary: "List Workflow MCP Tools"
10102
+ summary: "List Workflow MCP Tools",
10103
+ personalKeyOnly: true
10016
10104
  },
10017
10105
  listWorkflowRuns: {
10018
10106
  method: "GET",
@@ -10372,6 +10460,7 @@ var V2_OPERATIONS = {
10372
10460
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10373
10461
  responseMode: "json",
10374
10462
  summary: "Create or Replace Workflow Chat Deployment",
10463
+ personalKeyOnly: true,
10375
10464
  body: {
10376
10465
  identifier: {
10377
10466
  kind: "string",
@@ -10424,6 +10513,7 @@ var V2_OPERATIONS = {
10424
10513
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10425
10514
  responseMode: "json",
10426
10515
  summary: "Replace Workflow State",
10516
+ personalKeyOnly: true,
10427
10517
  query: {
10428
10518
  dryRun: {
10429
10519
  kind: "boolean",
@@ -10477,7 +10567,7 @@ var V2_OPERATIONS = {
10477
10567
  path: {
10478
10568
  kind: "string",
10479
10569
  required: true,
10480
- describe: "Path of the archived folder to restore, as reported by `GET /api/v2/files/folders?scope=archived`."
10570
+ describe: "Path of the archived folder to restore, as reported by an archived-scope folder list."
10481
10571
  }
10482
10572
  }
10483
10573
  },
@@ -10522,7 +10612,7 @@ var V2_OPERATIONS = {
10522
10612
  path: {
10523
10613
  kind: "string",
10524
10614
  required: true,
10525
- describe: "Path the folder held when `DELETE /api/v2/tables/folders` archived it."
10615
+ describe: "Path the folder held when a folder delete archived it."
10526
10616
  }
10527
10617
  }
10528
10618
  },
@@ -10562,7 +10652,8 @@ var V2_OPERATIONS = {
10562
10652
  version: "Numeric deployment version, or `active` for the currently live version."
10563
10653
  },
10564
10654
  responseMode: "json",
10565
- summary: "Revert Workflow To Version"
10655
+ summary: "Revert Workflow To Version",
10656
+ personalKeyOnly: true
10566
10657
  },
10567
10658
  revokeSkillEditor: {
10568
10659
  method: "DELETE",
@@ -10573,6 +10664,7 @@ var V2_OPERATIONS = {
10573
10664
  },
10574
10665
  responseMode: "json",
10575
10666
  summary: "Revoke Skill Editor",
10667
+ personalKeyOnly: true,
10576
10668
  query: {
10577
10669
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
10578
10670
  email: {
@@ -10589,6 +10681,7 @@ var V2_OPERATIONS = {
10589
10681
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10590
10682
  responseMode: "json",
10591
10683
  summary: "Rollback Workflow",
10684
+ personalKeyOnly: true,
10592
10685
  body: {
10593
10686
  version: {
10594
10687
  kind: "integer",
@@ -10683,9 +10776,10 @@ var V2_OPERATIONS = {
10683
10776
  method: "PUT",
10684
10777
  path: "/api/v2/secrets/[name]",
10685
10778
  pathParams: ["name"],
10686
- pathParamDocs: { name: "Secret to create, replace, or delete." },
10779
+ pathParamDocs: { name: "Secret to create or replace." },
10687
10780
  responseMode: "json",
10688
10781
  summary: "Set Secret",
10782
+ personalKeyOnly: true,
10689
10783
  body: {
10690
10784
  workspaceId: {
10691
10785
  kind: "string",
@@ -10700,8 +10794,7 @@ var V2_OPERATIONS = {
10700
10794
  },
10701
10795
  value: {
10702
10796
  kind: "string",
10703
- required: true,
10704
- describe: "Write-only secret value. It is never returned."
10797
+ 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
10798
  },
10706
10799
  description: {
10707
10800
  kind: "string",
@@ -10723,6 +10816,7 @@ var V2_OPERATIONS = {
10723
10816
  },
10724
10817
  responseMode: "json",
10725
10818
  summary: "Sync Knowledge Connector",
10819
+ personalKeyOnly: true,
10726
10820
  body: {
10727
10821
  workspaceId: {
10728
10822
  kind: "string",
@@ -10760,7 +10854,8 @@ var V2_OPERATIONS = {
10760
10854
  pathParams: ["workflowId"],
10761
10855
  pathParamDocs: { workflowId: "Unique workflow identifier." },
10762
10856
  responseMode: "json",
10763
- summary: "Undeploy Workflow"
10857
+ summary: "Undeploy Workflow",
10858
+ personalKeyOnly: true
10764
10859
  },
10765
10860
  undeployWorkflowMcpTool: {
10766
10861
  method: "DELETE",
@@ -10771,7 +10866,8 @@ var V2_OPERATIONS = {
10771
10866
  workflowId: "Workflow published as a tool on this server."
10772
10867
  },
10773
10868
  responseMode: "json",
10774
- summary: "Unpublish Workflow MCP Tool"
10869
+ summary: "Unpublish Workflow MCP Tool",
10870
+ personalKeyOnly: true
10775
10871
  },
10776
10872
  unzipFile: {
10777
10873
  method: "POST",
@@ -10791,6 +10887,7 @@ var V2_OPERATIONS = {
10791
10887
  pathParamDocs: { credentialId: "Credential to update." },
10792
10888
  responseMode: "json",
10793
10889
  summary: "Update Credential",
10890
+ personalKeyOnly: true,
10794
10891
  query: {
10795
10892
  workspaceId: {
10796
10893
  kind: "string",
@@ -10892,6 +10989,7 @@ var V2_OPERATIONS = {
10892
10989
  },
10893
10990
  responseMode: "json",
10894
10991
  summary: "Update Chunk",
10992
+ personalKeyOnly: true,
10895
10993
  body: {
10896
10994
  workspaceId: {
10897
10995
  kind: "string",
@@ -10918,6 +11016,7 @@ var V2_OPERATIONS = {
10918
11016
  },
10919
11017
  responseMode: "json",
10920
11018
  summary: "Update Knowledge Connector",
11019
+ personalKeyOnly: true,
10921
11020
  body: {
10922
11021
  workspaceId: {
10923
11022
  kind: "string",
@@ -10949,6 +11048,7 @@ var V2_OPERATIONS = {
10949
11048
  },
10950
11049
  responseMode: "json",
10951
11050
  summary: "Update Knowledge Connector Documents",
11051
+ personalKeyOnly: true,
10952
11052
  body: {
10953
11053
  workspaceId: {
10954
11054
  kind: "string",
@@ -10978,6 +11078,7 @@ var V2_OPERATIONS = {
10978
11078
  },
10979
11079
  responseMode: "json",
10980
11080
  summary: "Update Document",
11081
+ personalKeyOnly: true,
10981
11082
  body: {
10982
11083
  workspaceId: {
10983
11084
  kind: "string",
@@ -11022,6 +11123,7 @@ var V2_OPERATIONS = {
11022
11123
  },
11023
11124
  responseMode: "json",
11024
11125
  summary: "Update Tag",
11126
+ personalKeyOnly: true,
11025
11127
  body: {
11026
11128
  workspaceId: {
11027
11129
  kind: "string",
@@ -11126,6 +11228,7 @@ var V2_OPERATIONS = {
11126
11228
  },
11127
11229
  responseMode: "json",
11128
11230
  summary: "Update Skill",
11231
+ personalKeyOnly: true,
11129
11232
  body: {
11130
11233
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
11131
11234
  name: { kind: "string", describe: "New kebab-case skill name." },
@@ -11263,6 +11366,7 @@ var V2_OPERATIONS = {
11263
11366
  pathParamDocs: { serverId: "Unique workflow-MCP server identifier." },
11264
11367
  responseMode: "json",
11265
11368
  summary: "Update Workflow MCP Server",
11369
+ personalKeyOnly: true,
11266
11370
  body: {
11267
11371
  name: { kind: "string", describe: "Server display name, shown to connecting MCP clients." },
11268
11372
  description: { kind: "string", describe: "New server description, or null to clear it." },
@@ -11279,6 +11383,7 @@ var V2_OPERATIONS = {
11279
11383
  pathParamDocs: { workflowId: "Unique workflow identifier." },
11280
11384
  responseMode: "json",
11281
11385
  summary: "Update Workflow Public API Access",
11386
+ personalKeyOnly: true,
11282
11387
  body: {
11283
11388
  isPublicApi: {
11284
11389
  kind: "boolean",
@@ -11327,6 +11432,7 @@ var V2_OPERATIONS = {
11327
11432
  pathParamDocs: { fileId: "File identifier." },
11328
11433
  responseMode: "json",
11329
11434
  summary: "Enable or Disable File Share",
11435
+ personalKeyOnly: true,
11330
11436
  body: {
11331
11437
  workspaceId: { kind: "string", required: true, describe: "Workspace that owns the file." },
11332
11438
  isActive: {
@@ -11361,7 +11467,7 @@ var V2_OPERATIONS = {
11361
11467
  data: {
11362
11468
  kind: "object",
11363
11469
  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}`."
11470
+ 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
11471
  },
11366
11472
  conflictTarget: { kind: "string", describe: "Unique column used to detect a conflict." }
11367
11473
  }
@@ -11369,7 +11475,6 @@ var V2_OPERATIONS = {
11369
11475
  };
11370
11476
 
11371
11477
  // src/commands/auth.ts
11372
- var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
11373
11478
  var MAX_INTERACTIVE_WORKSPACES = 1000;
11374
11479
  function openBrowser(url) {
11375
11480
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : [process.platform === "darwin" ? "open" : "xdg-open", [url]];
@@ -11410,13 +11515,16 @@ function selectedProfileName(command) {
11410
11515
  return globalsOf(command).profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
11411
11516
  }
11412
11517
  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
- }
11518
+ validateProfileName(profileName);
11416
11519
  if (listProfiles().includes(profileName)) {
11417
11520
  throw new SimApiError(`Profile "${profileName}" already exists. Remove it first with: sim logout --all --profile ${profileName}`, 0);
11418
11521
  }
11419
11522
  }
11523
+ function requireStorableKey(apiKey) {
11524
+ if (typeof apiKey !== "string" || !apiKey || apiKey !== apiKey.trim() || FORBIDDEN_IN_VALUE.test(apiKey)) {
11525
+ throw new SimApiError("The server returned a malformed API key. Nothing was stored; check the endpoint.", 0);
11526
+ }
11527
+ }
11420
11528
  function requireStoredAuthentication(profile) {
11421
11529
  const authProfile = resolveAuthenticationProfileName(profile.name);
11422
11530
  const storedKey = readCredentialsProfile(authProfile).api_key;
@@ -11476,7 +11584,7 @@ function addProfileCommand() {
11476
11584
  const workspace = workspaceId ? await getWorkspaceById(client, workspaceId) : await chooseWorkspace(client);
11477
11585
  writeConfigProfile(profileName, {
11478
11586
  auth_profile: authProfile,
11479
- workspace: workspace.id
11587
+ workspace: normalizeWorkspaceId(workspace.id, "the workspace response")
11480
11588
  });
11481
11589
  console.log(source_default.green(`✓ Added profile "${profileName}" in ${configPath()}`));
11482
11590
  console.log(` Workspace: ${safeOneLine(workspace.name)} (${workspace.id})`);
@@ -11518,12 +11626,13 @@ Waiting for approval…`));
11518
11626
  if (key.scope !== scope) {
11519
11627
  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
11628
  }
11521
- writeCredentialsProfile(profile.name, key.apiKey);
11522
11629
  const settings = {
11523
11630
  endpoint: profile.endpoint,
11524
- workspace: key.workspaceId ?? null
11631
+ workspace: key.workspaceId == null ? null : normalizeWorkspaceId(key.workspaceId, "the login response")
11525
11632
  };
11633
+ requireStorableKey(key.apiKey);
11526
11634
  writeConfigProfile(profile.name, settings);
11635
+ writeCredentialsProfile(profile.name, key.apiKey);
11527
11636
  console.log(source_default.green(`
11528
11637
  ✓ Logged in. Key stored in ${credentialsPath()}`));
11529
11638
  if (key.workspaceBound && key.workspaceId) {
@@ -11687,30 +11796,62 @@ function whoamiCommand() {
11687
11796
  process.exitCode = exitCode;
11688
11797
  });
11689
11798
  }
11799
+ var PROFILE_COLUMNS = [
11800
+ { header: "", value: (row) => row.active ? source_default.green("*") : " " },
11801
+ { header: "profile", value: (row) => text(row.name) },
11802
+ { header: "key", value: (row) => row.error ? text(null) : row.hasKey ? "yes" : "no" },
11803
+ { header: "auth", value: (row) => text(row.authProfile) },
11804
+ { header: "error", value: (row) => row.error ? source_default.red(safeOneLine(row.error)) : text(null) }
11805
+ ];
11806
+ function buildProfileRow(name, active) {
11807
+ try {
11808
+ const authProfile = resolveAuthenticationProfileName(name);
11809
+ return {
11810
+ name,
11811
+ active,
11812
+ hasKey: Boolean(readCredentialsProfile(authProfile).api_key),
11813
+ authProfile,
11814
+ error: null
11815
+ };
11816
+ } catch (error) {
11817
+ if (!(error instanceof ProfileConfigError))
11818
+ throw error;
11819
+ return { name, active, hasKey: false, authProfile: null, error: error.message };
11820
+ }
11821
+ }
11822
+ function profileListingContext(command) {
11823
+ try {
11824
+ const profile = profileFrom(command);
11825
+ return { activeName: profile.name, output: profile.output };
11826
+ } catch (error) {
11827
+ if (!(error instanceof ProfileConfigError))
11828
+ throw error;
11829
+ const globals = globalsOf(command);
11830
+ const named = globals.profile || process.env.SIM_PROFILE;
11831
+ if (named && named !== DEFAULT_PROFILE && !listProfiles().includes(named))
11832
+ throw error;
11833
+ const requested = globals.output ?? process.env.SIM_OUTPUT;
11834
+ if (requested && !OUTPUT_FORMATS.includes(requested))
11835
+ throw error;
11836
+ return {
11837
+ activeName: named || DEFAULT_PROFILE,
11838
+ output: requested ? requested : "table"
11839
+ };
11840
+ }
11841
+ }
11690
11842
  function profilesCommand() {
11691
11843
  const command = new Command("profiles").alias("profile").description("List profiles or add a workspace profile that shares a stored login");
11692
11844
  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"));
11845
+ const { activeName, output } = profileListingContext(actionCommand);
11846
+ const rows = listProfiles().map((name) => buildProfileRow(name, name === activeName));
11847
+ if (rows.length === 0) {
11848
+ if (output === "table")
11849
+ console.log(source_default.dim("No profiles yet. Run: sim login"));
11850
+ else
11851
+ printList(output, rows, PROFILE_COLUMNS);
11696
11852
  return;
11697
11853
  }
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
- }
11854
+ printList(output, rows, PROFILE_COLUMNS);
11714
11855
  };
11715
11856
  command.action(printProfiles);
11716
11857
  command.addCommand(new Command("list").description("List configured profiles").action(printProfiles));
@@ -11719,6 +11860,11 @@ function profilesCommand() {
11719
11860
  }
11720
11861
 
11721
11862
  // src/commands/configure.ts
11863
+ var GLOBAL_FLAG_TWINS = [
11864
+ { option: "endpoint", flag: "--endpoint", setFlag: "--set-endpoint" },
11865
+ { option: "workspace", flag: "-w, --workspace", setFlag: "--set-workspace" },
11866
+ { option: "output", flag: "--output", setFlag: "--set-output" }
11867
+ ];
11722
11868
  function requireValue(value, flag, key) {
11723
11869
  if (value !== undefined && value.trim() === "") {
11724
11870
  throw new SimApiError(`${flag} requires a value. To remove it, run: sim configure --unset ${key}`, 0);
@@ -11726,6 +11872,13 @@ function requireValue(value, flag, key) {
11726
11872
  }
11727
11873
  function configureCommand() {
11728
11874
  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) => {
11875
+ const globals = globalsOf(command);
11876
+ for (const { option, flag, setFlag } of GLOBAL_FLAG_TWINS) {
11877
+ const value = globals[option];
11878
+ if (value === undefined)
11879
+ continue;
11880
+ throw new SimApiError(`${flag} applies to a single command and is not stored. To save it, run: sim configure ${setFlag} ${value}`, 0);
11881
+ }
11729
11882
  const profile = profileFrom(command, { allowUnknownProfile: true });
11730
11883
  const authProfile = resolveAuthenticationProfileName(profile.name);
11731
11884
  const updates = {};
@@ -11738,8 +11891,9 @@ function configureCommand() {
11738
11891
  }
11739
11892
  updates.endpoint = normalizeEndpoint(options.setEndpoint, "--set-endpoint");
11740
11893
  }
11741
- if (options.setWorkspace)
11742
- updates.workspace = options.setWorkspace;
11894
+ if (options.setWorkspace) {
11895
+ updates.workspace = normalizeWorkspaceId(options.setWorkspace, "--set-workspace");
11896
+ }
11743
11897
  if (options.setOutput) {
11744
11898
  if (!OUTPUT_FORMATS.includes(options.setOutput)) {
11745
11899
  throw new SimApiError(`Unknown output format "${options.setOutput}". Use one of: ${OUTPUT_FORMATS.join(", ")}`, 0);
@@ -11766,6 +11920,11 @@ function configureCommand() {
11766
11920
  }
11767
11921
  return;
11768
11922
  }
11923
+ const removalOnly = Object.values(updates).every((value) => value === null);
11924
+ if (removalOnly && Object.keys(readConfigProfile(profile.name)).length === 0) {
11925
+ console.log(source_default.dim(`No settings stored for profile "${profile.name}".`));
11926
+ return;
11927
+ }
11769
11928
  writeConfigProfile(profile.name, updates);
11770
11929
  console.log(source_default.green(`✓ Updated profile "${profile.name}" in ${configPath()}`));
11771
11930
  });
@@ -11858,7 +12017,7 @@ var CLI_CONTRACT = {
11858
12017
  listBillingLogs: {
11859
12018
  command: "billing logs",
11860
12019
  allWorkspaces: true,
11861
- describe: "List credit usage events",
12020
+ describe: "List credit usage events (a personal API key reports only your own events; a workspace API key reports every member's)",
11862
12021
  flags: {
11863
12022
  source: { describe: "Filter by usage source; sim-chat combines Copilot and workspace chat" },
11864
12023
  period: { describe: "Billing period" },
@@ -12014,14 +12173,14 @@ var CLI_CONTRACT = {
12014
12173
  }
12015
12174
  },
12016
12175
  columns: [
12017
- { header: "started", path: "startedAt", format: "timestamp" },
12018
- { header: "status" },
12176
+ { header: "started", path: "startedAt", format: "timestamp", minWidth: 19 },
12177
+ { header: "status", minWidth: 9 },
12019
12178
  { header: "level" },
12020
- { header: "trigger" },
12021
- { header: "workflow", path: "workflow.name" },
12179
+ { header: "trigger", minWidth: 12 },
12180
+ { header: "workflow", path: "workflow.name", minWidth: 24 },
12022
12181
  { header: "duration", path: "totalDurationMs", format: "duration" },
12023
- { header: "cost", path: "cost.total", format: "cost" },
12024
- { header: "run", path: "runId" }
12182
+ { header: "cost", path: "cost.total", format: "cost", minWidth: 8 },
12183
+ { header: "run", path: "runId", minWidth: 36 }
12025
12184
  ]
12026
12185
  },
12027
12186
  getLog: {
@@ -12769,6 +12928,23 @@ var CLI_CONTRACT = {
12769
12928
  }
12770
12929
  }
12771
12930
  },
12931
+ listTableDispatches: {
12932
+ columns: [
12933
+ { header: "id" },
12934
+ { header: "status" },
12935
+ { header: "mode" },
12936
+ { header: "max rows", path: "limit.max" },
12937
+ { header: "processed", path: "processedCount" },
12938
+ { header: "manual", path: "isManualRun", format: "bool" },
12939
+ { header: "requested", path: "requestedAt", format: "timestamp" },
12940
+ { header: "completed", path: "completedAt", format: "timestamp" },
12941
+ { header: "canceled", path: "canceledAt", format: "timestamp" },
12942
+ { header: "groups", path: "scope.groupIds", format: "count" },
12943
+ { header: "rows", path: "scope.rowIds", format: "count" },
12944
+ { header: "filtered", path: "scope.filtered", format: "bool" },
12945
+ { header: "excluded", path: "scope.excludeRowIds", format: "count" }
12946
+ ]
12947
+ },
12772
12948
  runRowEnrichment: {
12773
12949
  command: "tables rows enrich",
12774
12950
  describe: "Run one row’s enrichment group"
@@ -12777,8 +12953,16 @@ var CLI_CONTRACT = {
12777
12953
  createTableImportPartUrls: { hidden: true },
12778
12954
  completeTableImport: { hidden: true },
12779
12955
  getTableImport: { flags: TRANSFER_TOKEN_OMITTED },
12780
- cancelTableImport: { command: "tables imports cancel", flags: TRANSFER_TOKEN_OMITTED },
12781
- cancelTableExport: { command: "tables exports cancel" },
12956
+ cancelTableImport: {
12957
+ command: "tables imports cancel",
12958
+ flags: TRANSFER_TOKEN_OMITTED,
12959
+ describe: "Stop a running import",
12960
+ 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."
12961
+ },
12962
+ cancelTableExport: {
12963
+ command: "tables exports cancel",
12964
+ describe: "Stop a running export"
12965
+ },
12782
12966
  tableExportDownload: {
12783
12967
  command: "tables exports download",
12784
12968
  describe: "Get the download URL for a finished export"
@@ -12800,7 +12984,7 @@ var CLI_CONTRACT = {
12800
12984
  selectedOutputs: {
12801
12985
  name: "select-output",
12802
12986
  list: true,
12803
- describe: "Return blockName.field values (e.g. agent_1.content); missing fields are omitted"
12987
+ describe: "Return blockName.field values from the streamed result (e.g. agent_1.content), requires --follow; missing fields are omitted"
12804
12988
  },
12805
12989
  stream: { omit: true },
12806
12990
  includeThinking: { omit: true },
@@ -12824,7 +13008,7 @@ var CLI_CONTRACT = {
12824
13008
  selectedOutputs: {
12825
13009
  name: "select-output",
12826
13010
  list: true,
12827
- describe: "Include blockName.field values in JSON or YAML output (e.g. agent_1.content)"
13011
+ describe: "Include blockId or blockId.path values in JSON or YAML output; block names are not resolved on a finished run"
12828
13012
  }
12829
13013
  },
12830
13014
  fields: [
@@ -12935,6 +13119,13 @@ var PROFILE_INJECTED_FIELD = "workspaceId";
12935
13119
  function isProfileWorkspacePath(commandSpec, param) {
12936
13120
  return commandSpec.profileWorkspacePath === true && param === PROFILE_INJECTED_FIELD;
12937
13121
  }
13122
+ function cursorSlot(operationSpec) {
13123
+ if (operationSpec.query && "cursor" in operationSpec.query)
13124
+ return "query";
13125
+ if (operationSpec.body && "cursor" in operationSpec.body)
13126
+ return "body";
13127
+ return null;
13128
+ }
12938
13129
  var JSON_KINDS = new Set(["object", "array", "unknown"]);
12939
13130
  function flagSpecFor(operation, field) {
12940
13131
  return CLI_CONTRACT[operation]?.flags?.[field] ?? {};
@@ -13001,6 +13192,9 @@ function readStdin() {
13001
13192
  }
13002
13193
  return Buffer.concat(chunks).toString("utf8");
13003
13194
  }
13195
+ function literalAtHint(error, path) {
13196
+ return error?.code === "ENOENT" ? `. To pass the literal value @${path}, write @@${path}` : "";
13197
+ }
13004
13198
  function readArgumentSource(raw, flagName) {
13005
13199
  if (raw.startsWith("@@"))
13006
13200
  return { text: raw.slice(1), from: "" };
@@ -13020,7 +13214,7 @@ function readArgumentSource(raw, flagName) {
13020
13214
  try {
13021
13215
  return { text: readFileSync3(path, "utf8"), from: ` (read from ${path})` };
13022
13216
  } catch (error) {
13023
- throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}`, 0);
13217
+ throw new SimApiError(`--${flagName} cannot read ${path}: ${error.message}${literalAtHint(error, path)}`, 0);
13024
13218
  }
13025
13219
  }
13026
13220
  function readListValues(raw, flagName) {
@@ -13113,6 +13307,7 @@ function coerce(raw, field, flag, flagName) {
13113
13307
  return encodeFolderPath(raw);
13114
13308
  return raw;
13115
13309
  }
13310
+ var NO_WORKSPACE_FALLBACK = "No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>";
13116
13311
  function asQueryValue(value) {
13117
13312
  if (value === null || value === undefined)
13118
13313
  return;
@@ -13133,7 +13328,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13133
13328
  const value = profileWorkspacePath ? workspaceId : pathFlag ? flags[camel(flagName)] : positional[positionalIndex++];
13134
13329
  if (value === undefined || value === null) {
13135
13330
  if (profileWorkspacePath) {
13136
- throw new SimApiError("No workspace set. Pass --workspace, or run: sim configure --set-workspace <id>", 0);
13331
+ throw new SimApiError(NO_WORKSPACE_FALLBACK, 0);
13137
13332
  }
13138
13333
  throw new SimApiError(pathFlag ? `--${flagName} is required` : `Missing <${argumentName}>`, 0);
13139
13334
  }
@@ -13145,6 +13340,7 @@ function buildRequest(operation, positional, flags, workspaceId) {
13145
13340
  const query = {};
13146
13341
  const body = {};
13147
13342
  const headers = {};
13343
+ const paginatedLimit = cursorSlot(spec) !== null;
13148
13344
  for (const slot of ["query", "body", "headers"]) {
13149
13345
  for (const [field, descriptor] of Object.entries(spec[slot] ?? {})) {
13150
13346
  const flag = flagSpecFor(operation, field);
@@ -13154,10 +13350,13 @@ function buildRequest(operation, positional, flags, workspaceId) {
13154
13350
  const omitProfileWorkspace = commandSpec.allWorkspaces && flags.allWorkspaces === true;
13155
13351
  const provided = field === PROFILE_INJECTED_FIELD ? omitProfileWorkspace ? undefined : workspaceId : flags[camel(flagName)];
13156
13352
  const raw = provided ?? flag.requestDefault;
13353
+ if (slot === "query" && typeof raw === "string" && raw.trim() === "" && !(field === "limit" && paginatedLimit)) {
13354
+ throw new SimApiError(`--${flagName} cannot be empty`, 0);
13355
+ }
13157
13356
  const value = coerce(raw ?? undefined, descriptor, flag, flagName);
13158
13357
  if (value === undefined) {
13159
13358
  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);
13359
+ throw new SimApiError(field === PROFILE_INJECTED_FIELD ? NO_WORKSPACE_FALLBACK : `--${flagName} is required`, 0);
13161
13360
  }
13162
13361
  continue;
13163
13362
  }
@@ -13333,7 +13532,8 @@ function at(row, path) {
13333
13532
  function decodeFolderPath(value) {
13334
13533
  return value.split("/").map((segment) => {
13335
13534
  try {
13336
- return decodeURIComponent(segment);
13535
+ const decoded = decodeURIComponent(segment);
13536
+ return decoded.includes("/") ? segment : decoded;
13337
13537
  } catch {
13338
13538
  return segment;
13339
13539
  }
@@ -13464,8 +13664,10 @@ function unwrapResource(data) {
13464
13664
  const [, value] = entries[0];
13465
13665
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
13466
13666
  }
13467
- function renderPage(format, rows, spec, envelope) {
13667
+ function renderPage(format, rows, spec, envelope, options = {}) {
13468
13668
  writePageNote(spec, envelope);
13669
+ writeEnvelopeTruncation(envelope);
13670
+ writeCursorTruncation(rows.length, options.truncated === true);
13469
13671
  printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
13470
13672
  }
13471
13673
  function writePageNote(spec, envelope) {
@@ -13477,7 +13679,48 @@ function writePageNote(spec, envelope) {
13477
13679
  process.stderr.write(source_default.dim(`${spec.pageNote.label}: ${String(value)}
13478
13680
  `));
13479
13681
  }
13480
- function renderResult(operation, format, raw, spec, options = {}) {
13682
+ var TRUNCATION_FLAG = /^truncated$|^[A-Za-z0-9]+Truncated$/;
13683
+ var NEGATED_TRUNCATION_FLAG = /^(?:not|un|non|never)Truncated$|(?:Not|Un|Non|Never)Truncated$/;
13684
+ function truncationFlags(container) {
13685
+ if (!container || typeof container !== "object" || Array.isArray(container))
13686
+ return [];
13687
+ return Object.entries(container).filter(([key, value]) => value === true && TRUNCATION_FLAG.test(key) && !NEGATED_TRUNCATION_FLAG.test(key)).map(([key]) => key);
13688
+ }
13689
+ function responseTruncationFlags(envelope) {
13690
+ return [...truncationFlags(envelope), ...truncationFlags(at(envelope, "data"))];
13691
+ }
13692
+ function foldPageEnvelope(current, page) {
13693
+ if (current === undefined)
13694
+ return page;
13695
+ const raised = truncationFlags(page);
13696
+ if (raised.length === 0 || !current || typeof current !== "object")
13697
+ return current;
13698
+ return {
13699
+ ...current,
13700
+ ...Object.fromEntries(raised.map((flag) => [flag, true]))
13701
+ };
13702
+ }
13703
+ function spellOut(flag) {
13704
+ return flag.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().trim();
13705
+ }
13706
+ function clippedSubject(flag) {
13707
+ const subject = flag.replace(/^truncated$|Truncated$/, "");
13708
+ return subject ? `the ${spellOut(subject)} it returned` : "this result";
13709
+ }
13710
+ function writeEnvelopeTruncation(envelope) {
13711
+ for (const flag of responseTruncationFlags(envelope)) {
13712
+ process.stderr.write(source_default.dim(`${spellOut(flag)}: the server clipped ${clippedSubject(flag)}, so the answer is incomplete
13713
+ `));
13714
+ }
13715
+ }
13716
+ function writeCursorTruncation(count, truncated) {
13717
+ if (!truncated)
13718
+ return;
13719
+ process.stderr.write(source_default.dim(`showing the first ${count}; more results exist — re-run with --limit 0 for all
13720
+ `));
13721
+ }
13722
+ function renderResult(operation, format, raw, spec, options = {}, envelope) {
13723
+ writeEnvelopeTruncation(envelope);
13481
13724
  if (spec.document) {
13482
13725
  printDocument(format, raw);
13483
13726
  return;
@@ -13620,8 +13863,8 @@ function attachCredentialCommands(program2) {
13620
13863
  if (!credentials)
13621
13864
  throw new Error("The generated credentials command group is missing");
13622
13865
  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 }));
13866
+ 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 (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));
13867
+ 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 (required)").action(async (providerId, options, command) => createConnectionLink(command, { providerId, displayName: options.name }));
13625
13868
  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 }));
13626
13869
  }
13627
13870
 
@@ -13775,7 +14018,7 @@ Examples:
13775
14018
 
13776
14019
  // src/commands/protocol/files-get.ts
13777
14020
  import { once as once2 } from "node:events";
13778
- import { createWriteStream } from "node:fs";
14021
+ import { createWriteStream, rmSync } from "node:fs";
13779
14022
  import { link, lstat, mkdtemp, readlink, rename, rm } from "node:fs/promises";
13780
14023
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
13781
14024
  import { Readable } from "node:stream";
@@ -13831,37 +14074,66 @@ async function streamToFile(body, file, reportedPath = file.path) {
13831
14074
  throw writeFailure(reportedPath, error);
13832
14075
  }
13833
14076
  }
14077
+ var STAGE_SIGNALS = ["SIGINT", "SIGTERM"];
14078
+ function reRaise(signal) {
14079
+ process.kill(process.pid, signal);
14080
+ }
14081
+ function removeStagingOnSignal(stagingDirectory, terminate = reRaise) {
14082
+ const installed = STAGE_SIGNALS.map((signal) => {
14083
+ const onSignal = () => {
14084
+ process.off(signal, onSignal);
14085
+ const directory = stagingDirectory();
14086
+ if (directory) {
14087
+ try {
14088
+ rmSync(directory, { recursive: true, force: true });
14089
+ } catch {}
14090
+ }
14091
+ terminate(signal);
14092
+ };
14093
+ process.on(signal, onSignal);
14094
+ return [signal, onSignal];
14095
+ });
14096
+ return () => {
14097
+ for (const [signal, onSignal] of installed)
14098
+ process.off(signal, onSignal);
14099
+ };
14100
+ }
13834
14101
  async function saveStagedFile(body, target, force) {
13835
14102
  let temporaryDirectory = null;
13836
14103
  let failure = null;
14104
+ const disposeSignalCleanup = removeStagingOnSignal(() => temporaryDirectory);
13837
14105
  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;
14106
+ try {
14107
+ const publicationTarget = force ? await forcedPublicationTarget(target) : target;
14108
+ temporaryDirectory = await mkdtemp(join2(dirname2(publicationTarget), ".sim-download-"));
14109
+ const temporaryPath = join2(temporaryDirectory, "payload");
14110
+ await streamToFile(body, createWriteStream(temporaryPath, { flags: "wx" }), target);
14111
+ if (force) {
14112
+ await rename(temporaryPath, publicationTarget);
14113
+ } else {
14114
+ try {
14115
+ await link(temporaryPath, publicationTarget);
14116
+ } catch (error) {
14117
+ throw unsupportedAtomicPublish(target, error) ?? error;
14118
+ }
13849
14119
  }
14120
+ } catch (error) {
14121
+ failure = normalizedWriteFailure(target, error);
13850
14122
  }
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);
14123
+ if (temporaryDirectory) {
14124
+ try {
14125
+ await rm(temporaryDirectory, { recursive: true, force: true });
14126
+ } catch (cleanupError) {
14127
+ if (failure)
14128
+ throw combinedCleanupFailure(failure, temporaryDirectory, cleanupError);
14129
+ throw new SimApiError(`Saved ${target}, but could not remove temporary directory ${temporaryDirectory}: ${cleanupError.message}`, 0);
14130
+ }
13861
14131
  }
14132
+ if (failure)
14133
+ throw failure;
14134
+ } finally {
14135
+ disposeSignalCleanup();
13862
14136
  }
13863
- if (failure)
13864
- throw failure;
13865
14137
  }
13866
14138
  async function saveToFile(body, target, force) {
13867
14139
  return saveStagedFile(body, target, force);
@@ -14172,6 +14444,7 @@ function renderCell2(value, format) {
14172
14444
  }
14173
14445
  var COLUMNS = (CLI_CONTRACT.listLogs?.columns ?? []).map((spec) => ({
14174
14446
  header: spec.header,
14447
+ floor: Math.min(MAX_CELL_WIDTH2, spec.minWidth ?? 0),
14175
14448
  value: (row) => renderCell2(at2(row, spec.path ?? spec.header), spec.format)
14176
14449
  }));
14177
14450
  function oneLine2(value) {
@@ -14188,15 +14461,15 @@ function clamp2(value, width) {
14188
14461
  function createTableWriter() {
14189
14462
  let widths = null;
14190
14463
  return (rows) => {
14191
- const lines = rows.map((row) => COLUMNS.map((column) => oneLine2(column.value(row))));
14464
+ const lines = rows.map((row) => COLUMNS.map((column) => clamp2(oneLine2(column.value(row)), MAX_CELL_WIDTH2)));
14192
14465
  if (!widths) {
14193
- widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
14466
+ widths = COLUMNS.map((column, index) => Math.min(MAX_CELL_WIDTH2, Math.max(column.floor, visibleWidth(column.header), ...lines.map((line) => visibleWidth(line[index])))));
14194
14467
  const header = widths;
14195
14468
  console.log(source_default.dim(COLUMNS.map((column, index) => pad2(column.header.toUpperCase(), header[index])).join(" ").trimEnd()));
14196
14469
  }
14197
14470
  const locked = widths;
14198
14471
  for (const line of lines) {
14199
- console.log(line.map((cell, index) => pad2(clamp2(cell, locked[index]), locked[index])).join(" ").trimEnd());
14472
+ console.log(line.map((cell, index) => pad2(cell, locked[index])).join(" ").trimEnd());
14200
14473
  }
14201
14474
  };
14202
14475
  }
@@ -14421,7 +14694,8 @@ var WIRE_VOCABULARY_SENTENCE = /\s*The listed spellings[^.]*\.\s*/g;
14421
14694
  function withoutWireVocabulary(documented) {
14422
14695
  return documented.replace(WIRE_VOCABULARY_SENTENCE, " ").trim();
14423
14696
  }
14424
- function addFieldOption(command, operation, field, descriptor, slot) {
14697
+ var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
14698
+ function addFieldOption(command, operation, field, descriptor, slot, paginates) {
14425
14699
  if (field === PROFILE_INJECTED_FIELD || field === "cursor")
14426
14700
  return;
14427
14701
  const flag = flagSpecFor(operation, field);
@@ -14429,11 +14703,11 @@ function addFieldOption(command, operation, field, descriptor, slot) {
14429
14703
  return;
14430
14704
  const name = flagNameFor(operation, field);
14431
14705
  const short = flag.short ? `-${flag.short}, ` : "";
14432
- if (field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
14706
+ if (paginates && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
14433
14707
  command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
14434
14708
  return;
14435
14709
  }
14436
- const documented = describeField(flag, descriptor, name, field);
14710
+ const documented = `${describeField(flag, descriptor, name, field)}${field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
14437
14711
  if (descriptor.kind === "boolean" || flag.boolean) {
14438
14712
  const booleanDoc = withoutWireVocabulary(documented);
14439
14713
  if (descriptor.required) {
@@ -14451,7 +14725,7 @@ function addFieldOption(command, operation, field, descriptor, slot) {
14451
14725
  const placeholder = takesList ? "<value...>" : flag.rowCap ? "<n>" : wantsJson ? "<json|@file>" : "<value>";
14452
14726
  const choices = flag.choices ?? descriptor.values;
14453
14727
  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) : ""}`;
14728
+ 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) : ""}`;
14455
14729
  const renamedFrom = flag.renamedFrom ?? [];
14456
14730
  const option = new Option(`${short}--${name} ${placeholder}`, describe);
14457
14731
  if (flag.hidden)
@@ -14480,13 +14754,14 @@ function addOperationOptions(command, operation, commandSpec, operationSpec) {
14480
14754
  const short = flag.short ? `-${flag.short}, ` : "";
14481
14755
  command.addOption(new Option(`${short}--${name} <${flag.placeholder ?? "value"}>`, `${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll("-", " ")}`} (required)`).makeOptionMandatory());
14482
14756
  }
14757
+ const paginates = cursorSlot(operationSpec) !== null;
14483
14758
  for (const slot of ["query", "body", "headers"]) {
14484
14759
  for (const [field, descriptor] of Object.entries(operationSpec[slot] ?? {})) {
14485
14760
  if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
14486
14761
  continue;
14487
14762
  if (commandSpec.positionals?.includes(field))
14488
14763
  continue;
14489
- addFieldOption(command, operation, field, descriptor, slot);
14764
+ addFieldOption(command, operation, field, descriptor, slot, paginates);
14490
14765
  }
14491
14766
  }
14492
14767
  if (commandSpec.allWorkspaces) {
@@ -14663,13 +14938,16 @@ function validateTargetOptions(options) {
14663
14938
  return intoExisting;
14664
14939
  }
14665
14940
  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) => {
14941
+ 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
14942
  const { client, profile } = clientFrom(command);
14668
14943
  const workspaceId = client.requireWorkspace();
14669
14944
  if (Boolean(path) === Boolean(options.fileId)) {
14670
14945
  throw new SimApiError("Pass exactly one of <path> or --file-id <id>", 0);
14671
14946
  }
14672
14947
  const intoExisting = validateTargetOptions(options);
14948
+ if (intoExisting && options.mode === "replace" && options.yes !== true) {
14949
+ 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);
14950
+ }
14673
14951
  const local = path ? await localFile(path) : null;
14674
14952
  const source = local ? {
14675
14953
  type: "upload",
@@ -14784,6 +15062,14 @@ var BULK_OUTCOME_CHECKS = {
14784
15062
  return null;
14785
15063
  return `Deleted nothing: none of the ${requested} requested ${requested === 1 ? "file was" : "files were"} deleted.`;
14786
15064
  },
15065
+ addWorkspaceFilesToKnowledgeBase: (payload) => {
15066
+ if (lengthOf(payload.added) > 0)
15067
+ return null;
15068
+ const failed = lengthOf(payload.failed);
15069
+ if (failed === 0)
15070
+ return null;
15071
+ return `Indexed nothing: none of the ${failed} requested ${failed === 1 ? "file was" : "files were"} added.`;
15072
+ },
14787
15073
  bulkDeleteTables: (payload) => {
14788
15074
  const items = payload.deletedItems;
14789
15075
  const deleted = countOf(items?.tables) + countOf(items?.folders);
@@ -14794,6 +15080,15 @@ var BULK_OUTCOME_CHECKS = {
14794
15080
  return null;
14795
15081
  return `Deleted nothing: ${missed} of ${missed} ${missed === 1 ? "item was" : "items were"} not found or could not be deleted.`;
14796
15082
  },
15083
+ bulkUpdateKnowledgeChunks: (payload, body) => {
15084
+ if (countOf(payload.processed) > 0)
15085
+ return null;
15086
+ const requested = lengthOf(body?.chunkIds);
15087
+ if (requested === 0)
15088
+ return null;
15089
+ const reported2 = payload.errors?.[0];
15090
+ return typeof reported2 === "string" && reported2 ? safeOneLine(reported2) : `Updated nothing: none of the ${requested} requested ${requested === 1 ? "chunk" : "chunks"} matched.`;
15091
+ },
14797
15092
  moveTables: (payload) => {
14798
15093
  if (lengthOf(payload.moved) > 0)
14799
15094
  return null;
@@ -14825,12 +15120,18 @@ function bulkFailureMessage(operation, payload, body) {
14825
15120
  return null;
14826
15121
  return check(payload, body);
14827
15122
  }
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;
15123
+ var EXCLUSIVE_CAP_FIELDS = {
15124
+ deleteTableRows: { cap: "limit", ids: "rowIds" }
15125
+ };
15126
+ function assertCapIsUsable(operation, flags) {
15127
+ const exclusive = EXCLUSIVE_CAP_FIELDS[operation];
15128
+ if (!exclusive)
15129
+ return;
15130
+ const cap = flagNameFor(operation, exclusive.cap);
15131
+ const ids = flagNameFor(operation, exclusive.ids);
15132
+ if (flags[camel(cap)] === undefined || flags[camel(ids)] === undefined)
15133
+ return;
15134
+ throw new SimApiError(`--${cap} caps a --filter match and does nothing to an explicit --${ids} list; pass one, not both`, 0);
14834
15135
  }
14835
15136
  function foldRenamedFlags(operation, commandSpec, flags) {
14836
15137
  for (const [field, flag] of Object.entries(commandSpec.flags ?? {})) {
@@ -14864,6 +15165,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14864
15165
  requestFlags[camel(flagNameFor(operation, field))] = invocation[pathPositionalCount + index];
14865
15166
  }
14866
15167
  foldRenamedFlags(operation, commandSpec, requestFlags);
15168
+ assertCapIsUsable(operation, requestFlags);
14867
15169
  if (commandSpec.confirm && !requestFlags.yes && requestFlags.dryRun !== true) {
14868
15170
  throw new SimApiError(`${commandSpec.confirm} Re-run with --yes to confirm.`, 0);
14869
15171
  }
@@ -14873,12 +15175,14 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14873
15175
  const { client, profile } = clientFrom(host);
14874
15176
  const hasWorkspaceField = Boolean(operationSpec.query && PROFILE_INJECTED_FIELD in operationSpec.query || operationSpec.body && PROFILE_INJECTED_FIELD in operationSpec.body);
14875
15177
  const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
14876
- const request = buildRequest(operation, positional, requestFlags, hasWorkspaceField && !omitsWorkspace ? client.requireWorkspace() : profile.workspaceId);
15178
+ const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
15179
+ const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14877
15180
  const paging = cursorSlot(operationSpec);
14878
15181
  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);
15182
+ const limitText = String(requestFlags.limit ?? DEFAULT_LIMIT).trim();
15183
+ const rawLimit = limitText === "" ? Number.NaN : Number(limitText);
15184
+ if (!Number.isInteger(rawLimit) || rawLimit < 0) {
15185
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14882
15186
  }
14883
15187
  const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
14884
15188
  const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
@@ -14895,7 +15199,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14895
15199
  query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
14896
15200
  body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
14897
15201
  });
14898
- envelope ??= page;
15202
+ envelope = foldPageEnvelope(envelope, page);
14899
15203
  rows.push(...page.data);
14900
15204
  cursor = page.nextCursor;
14901
15205
  if (cursor && rows.length < limit)
@@ -14904,7 +15208,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14904
15208
  } finally {
14905
15209
  progress.finish();
14906
15210
  }
14907
- renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope);
15211
+ renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope, { truncated: Boolean(cursor) });
14908
15212
  return;
14909
15213
  }
14910
15214
  const result = await client.request(request.path, {
@@ -14914,9 +15218,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14914
15218
  body: request.body
14915
15219
  });
14916
15220
  const payload = result?.data ?? result;
14917
- renderResult(operation, profile.output, payload, commandSpec, {
14918
- expandedTrace: requestFlags.trace === true
14919
- });
15221
+ renderResult(operation, profile.output, payload, commandSpec, { expandedTrace: requestFlags.trace === true }, result);
14920
15222
  const failure = runFailureMessage(operation, payload) ?? bulkFailureMessage(operation, payload, request.body);
14921
15223
  if (failure)
14922
15224
  throw new SimApiError(failure, 0);
@@ -15141,6 +15443,9 @@ function followOrDelegate(previous) {
15141
15443
  command.setOptionValue("run", selection);
15142
15444
  const flags = command.optsWithGlobals();
15143
15445
  if (flags.follow !== true) {
15446
+ if (Array.isArray(flags.selectOutput) && flags.selectOutput.length > 0) {
15447
+ 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);
15448
+ }
15144
15449
  if (flags.includeThinking === true || flags.includeToolCalls === true) {
15145
15450
  throw new SimApiError("--include-thinking and --include-tool-calls describe a stream; add --follow", 0);
15146
15451
  }
@@ -15455,6 +15760,8 @@ function validateWorkspaceOnlyFlag(flag, value, scope) {
15455
15760
  async function readSecretValue(options) {
15456
15761
  if (options.value !== undefined)
15457
15762
  return validateSecretValue(readValueArgument(options.value));
15763
+ if (options.description !== undefined || options.unredacted !== undefined)
15764
+ return;
15458
15765
  try {
15459
15766
  return validateSecretValue(await promptSecret());
15460
15767
  } catch (error) {
@@ -15464,7 +15771,10 @@ async function readSecretValue(options) {
15464
15771
  return process.exit(CANCELLED_EXIT_CODE);
15465
15772
  }
15466
15773
  }
15467
- async function setSecret(name, options, command) {
15774
+ async function setSecret(name, options, command, redactionSpellings) {
15775
+ if (redactionSpellings.size > 1) {
15776
+ throw new SimApiError("Pass either --unredacted or --no-unredacted, not both: they are one setting, and commander keeps only whichever came last.", 0);
15777
+ }
15468
15778
  const description = validateWorkspaceOnlyFlag("description", options.description, options.scope);
15469
15779
  const unredacted = validateWorkspaceOnlyFlag("unredacted", options.unredacted, options.scope);
15470
15780
  const value = await readSecretValue(options);
@@ -15475,7 +15785,7 @@ async function setSecret(name, options, command) {
15475
15785
  body: {
15476
15786
  workspaceId: client.requireWorkspace(),
15477
15787
  scope: options.scope,
15478
- value,
15788
+ ...value === undefined ? {} : { value },
15479
15789
  description,
15480
15790
  ...unredacted === undefined ? {} : { unredacted }
15481
15791
  }
@@ -15486,7 +15796,85 @@ function attachSecretCommands(program2) {
15486
15796
  const secrets = program2.commands.find((command) => command.name() === "secrets");
15487
15797
  if (!secrets)
15488
15798
  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));
15799
+ const redactionSpellings = new Set;
15800
+ 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 (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));
15801
+ }
15802
+
15803
+ // src/runtime/naming.ts
15804
+ var WIRE_IDENTIFIER = /^[a-z]+[A-Z]/;
15805
+ function spellingFor(operation, commandSpec, operationSpec, field) {
15806
+ if (field === PROFILE_INJECTED_FIELD)
15807
+ return "--workspace";
15808
+ if (field === "cursor")
15809
+ return null;
15810
+ if (operationSpec.pathParams.includes(field)) {
15811
+ return commandSpec.pathFlags?.[field] ? `--${pathFlagNameFor(commandSpec, field)}` : `<${commandSpec.pathArgumentNames?.[field] ?? field}>`;
15812
+ }
15813
+ if (commandSpec.positionals?.includes(field))
15814
+ return `<${flagNameFor(operation, field)}>`;
15815
+ if (flagSpecFor(operation, field).omit)
15816
+ return null;
15817
+ if (commandSpec.requestFields && !commandSpec.requestFields.includes(field))
15818
+ return null;
15819
+ const declared = operationSpec.query && field in operationSpec.query || operationSpec.body && field in operationSpec.body || operationSpec.headers && field in operationSpec.headers;
15820
+ if (!declared)
15821
+ return null;
15822
+ return `--${flagNameFor(operation, field)}`;
15823
+ }
15824
+ function typeableFields(operation, commandSpec, operationSpec) {
15825
+ const spellings = new Map;
15826
+ const fields = [
15827
+ ...operationSpec.pathParams,
15828
+ ...Object.keys(operationSpec.query ?? {}),
15829
+ ...Object.keys(operationSpec.body ?? {}),
15830
+ ...Object.keys(operationSpec.headers ?? {})
15831
+ ];
15832
+ for (const field of fields) {
15833
+ if (spellings.has(field))
15834
+ continue;
15835
+ const spelling = spellingFor(operation, commandSpec, operationSpec, field);
15836
+ if (spelling)
15837
+ spellings.set(field, spelling);
15838
+ }
15839
+ return spellings;
15840
+ }
15841
+ function retypeMessage(message, spellings) {
15842
+ let retyped = message;
15843
+ for (const [field, spelling] of spellings) {
15844
+ if (!WIRE_IDENTIFIER.test(field))
15845
+ continue;
15846
+ retyped = retyped.replaceAll(new RegExp(`\\b${field}\\b`, "g"), spelling);
15847
+ }
15848
+ return retyped;
15849
+ }
15850
+ function retypeDetails(details, spellings) {
15851
+ if (Array.isArray(details))
15852
+ return details.map((issue2) => retypeDetails(issue2, spellings));
15853
+ if (!details || typeof details !== "object")
15854
+ return details;
15855
+ const issue = details;
15856
+ const retyped = { ...issue };
15857
+ if (Array.isArray(issue.path) && issue.path.length > 0) {
15858
+ const [head, ...rest] = issue.path.map(String);
15859
+ const spelling = spellings.get(head);
15860
+ if (spelling)
15861
+ retyped.path = [spelling, ...rest];
15862
+ }
15863
+ if (typeof issue.message === "string") {
15864
+ retyped.message = retypeMessage(issue.message, spellings);
15865
+ }
15866
+ if (Array.isArray(issue.errors)) {
15867
+ retyped.errors = retypeDetails(issue.errors, spellings);
15868
+ }
15869
+ return retyped;
15870
+ }
15871
+ function retypeApiError(error, operation, commandSpec, operationSpec) {
15872
+ if (!(error instanceof SimApiError) || error.status === 0)
15873
+ return error;
15874
+ const spellings = typeableFields(operation, commandSpec, operationSpec);
15875
+ if (spellings.size === 0)
15876
+ return error;
15877
+ return new SimApiError(retypeMessage(error.message, spellings), error.status, error.code, error.details === undefined ? undefined : retypeDetails(error.details, spellings));
15490
15878
  }
15491
15879
 
15492
15880
  // src/runtime/build.ts
@@ -15565,6 +15953,29 @@ function assertNoReservedProgramFlags(program2) {
15565
15953
  for (const child of program2.commands)
15566
15954
  walk(child, []);
15567
15955
  }
15956
+ function refuseHelpAfterUnknownCommand(program2) {
15957
+ const walk = (command) => {
15958
+ const internals = command;
15959
+ const dispatchesOnly = command.commands.length > 0 && !internals._actionHandler && command.registeredArguments.length === 0;
15960
+ if (dispatchesOnly) {
15961
+ const known = new Set(["help"]);
15962
+ for (const child of command.commands) {
15963
+ known.add(child.name());
15964
+ for (const alias of child.aliases())
15965
+ known.add(alias);
15966
+ }
15967
+ command.on("beforeHelp", () => {
15968
+ const first = command.args[0];
15969
+ if (first === undefined || first.startsWith("-") || known.has(first))
15970
+ return;
15971
+ internals.unknownCommand();
15972
+ });
15973
+ }
15974
+ for (const child of command.commands)
15975
+ walk(child);
15976
+ };
15977
+ walk(program2);
15978
+ }
15568
15979
  function configureOperation(command, operation, spec) {
15569
15980
  const operationSpec = V2_OPERATIONS[operation];
15570
15981
  command.allowExcessArguments(false);
@@ -15625,10 +16036,13 @@ function configureOperation(command, operation, spec) {
15625
16036
  }
15626
16037
  }
15627
16038
  }
15628
- command.description(spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`);
16039
+ const described = spec.describe ?? operationSpec.summary ?? `${operationSpec.method} ${operationSpec.path}`;
16040
+ command.description(operationSpec.personalKeyOnly ? `${described} (personal API key required)` : described);
15629
16041
  addOperationOptions(command, operation, spec, operationSpec);
15630
16042
  assertNoReservedFlags(command, operation);
15631
- command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation));
16043
+ command.action((...invocation) => executeOperation(operation, spec, operationSpec, invocation).catch((error) => {
16044
+ throw retypeApiError(error, operation, spec, operationSpec);
16045
+ }));
15632
16046
  return command;
15633
16047
  }
15634
16048
  function buildLeaf(operation, spec, leafName) {
@@ -15816,6 +16230,7 @@ function buildProgram(options = {}) {
15816
16230
  attachProtocolCommands(program2);
15817
16231
  attachSecretCommands(program2);
15818
16232
  program2.addHelpText("after", HELP_EPILOGUE);
16233
+ refuseHelpAfterUnknownCommand(program2);
15819
16234
  assertNoReservedProgramFlags(program2);
15820
16235
  return program2;
15821
16236
  }