sim 2.1.8-preview.98.1 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +16 -1
  2. package/dist/index.js +229 -113
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -220,11 +220,26 @@ will consume the result, and `text` for tab-separated shell output:
220
220
 
221
221
  ```bash
222
222
  sim workflows list --output json
223
- sim logs list --output json | jq -r '.[].runId'
223
+ sim logs list --output json | jq -r '.data[].runId'
224
224
  SIM_OUTPUT=yaml sim tables get <tableId>
225
225
  sim configure --set-output json
226
226
  ```
227
227
 
228
+ Paginated lists return `{ "data": [...], "nextCursor": "..." }` in JSON and YAML.
229
+ `nextCursor` is `null` when no pages remain. Resource lists and directory `ls`
230
+ fetch every page by default; use `--limit N` to cap them. Table rows (including
231
+ queries), logs, audit/billing events, workflow runs/versions, and knowledge
232
+ documents/chunks keep a default limit of 100. Use `--limit 0` to fetch every page
233
+ of those datasets, or pass the returned `nextCursor` to `--cursor` to continue
234
+ with another bounded result. Keep the same resource, filters, and sort order
235
+ when resuming; stop when `nextCursor` is `null`. Results accumulate in memory
236
+ before printing, so large datasets need an explicit limit or filter.
237
+
238
+ ```bash
239
+ sim tables rows list <tableId> --limit 100 --output json
240
+ sim tables rows list <tableId> --limit 100 --cursor "$nextCursor" --output json
241
+ ```
242
+
228
243
  JSON-valued options accept inline JSON, a file prefixed with `@`, or stdin with
229
244
  `@-`:
230
245
 
package/dist/index.js CHANGED
@@ -4460,6 +4460,22 @@ function toApiError(url, status, contentType, raw) {
4460
4460
  function truncate(value, max) {
4461
4461
  return value.length <= max ? value : `${value.slice(0, max)}…`;
4462
4462
  }
4463
+ function transportErrorMessage(error) {
4464
+ const messages = [];
4465
+ const seen = new Set;
4466
+ let current = error;
4467
+ while (current && typeof current === "object" && messages.length < 4 && !seen.has(current)) {
4468
+ seen.add(current);
4469
+ const candidate = current;
4470
+ const message = typeof candidate.message === "string" ? truncate(candidate.message.replace(/\s+/g, " ").trim(), 300) : "";
4471
+ const code = typeof candidate.code === "string" ? candidate.code : "";
4472
+ const detail = `${message}${code && !message.includes(code) ? ` (${code})` : ""}`;
4473
+ if (detail && messages.at(-1) !== detail)
4474
+ messages.push(detail);
4475
+ current = candidate.cause;
4476
+ }
4477
+ return messages.join(": ") || "Unknown network error";
4478
+ }
4463
4479
  function namesKeyScopeRefusal(error) {
4464
4480
  if (typeof error.code === "string" && KEY_SCOPE_REFUSALS.has(error.code))
4465
4481
  return true;
@@ -4683,7 +4699,7 @@ class SimClient {
4683
4699
  if (timeout?.aborted) {
4684
4700
  throw new SimApiError(`${url} did not answer within ${timeoutMs / 1000}s. ${RAISE_TIMEOUT_HINT}`, 0);
4685
4701
  }
4686
- throw new SimApiError(`Could not reach ${this.profile.endpoint}: ${cause.message}`, 0);
4702
+ throw new SimApiError(`Could not reach ${this.profile.endpoint}: ${transportErrorMessage(cause)}`, 0);
4687
4703
  }
4688
4704
  if (trace)
4689
4705
  traceRequest(method, url, response.status, startedAt);
@@ -4747,15 +4763,21 @@ function pageProgress() {
4747
4763
  }
4748
4764
  };
4749
4765
  }
4750
- async function requestAllPages(client, path, options) {
4751
- return (await requestPages(client, path, options)).items;
4766
+ function assertCursorAdvances(cursor, seenCursors) {
4767
+ if (cursor === null)
4768
+ return;
4769
+ if (seenCursors.has(cursor)) {
4770
+ throw new SimApiError("The API returned a repeated pagination cursor; cannot continue.", 0);
4771
+ }
4772
+ seenCursors.add(cursor);
4752
4773
  }
4753
- async function requestPages(client, path, options) {
4774
+ async function requestAllPages(client, path, options) {
4754
4775
  const { query, pageSize, limit: requestedLimit, ...requestOptions } = options;
4755
4776
  const limit = requestedLimit ?? Number.POSITIVE_INFINITY;
4756
4777
  if (limit <= 0)
4757
- return { items: [], truncated: false };
4778
+ return [];
4758
4779
  const items = [];
4780
+ const seenCursors = new Set;
4759
4781
  const progress = pageProgress();
4760
4782
  let cursor = null;
4761
4783
  try {
@@ -4768,6 +4790,7 @@ async function requestPages(client, path, options) {
4768
4790
  cursor
4769
4791
  }
4770
4792
  });
4793
+ assertCursorAdvances(page.nextCursor, seenCursors);
4771
4794
  items.push(...page.data);
4772
4795
  cursor = page.nextCursor;
4773
4796
  if (cursor && items.length < limit)
@@ -4776,7 +4799,7 @@ async function requestPages(client, path, options) {
4776
4799
  } finally {
4777
4800
  progress.finish();
4778
4801
  }
4779
- return { items: items.slice(0, limit), truncated: cursor !== null || items.length > limit };
4802
+ return items.slice(0, limit);
4780
4803
  }
4781
4804
  function resolvePath(template, params = {}) {
4782
4805
  return template.replace(/\[([^\]]+)\]/g, (_match, key) => {
@@ -8700,6 +8723,7 @@ var V2_OPERATIONS = {
8700
8723
  pathParamDocs: { workflowId: "Unique workflow identifier." },
8701
8724
  responseMode: "json",
8702
8725
  summary: "Apply Workflow Operations",
8726
+ workspaceKeyUnsupported: true,
8703
8727
  query: {
8704
8728
  dryRun: {
8705
8729
  kind: "boolean",
@@ -8788,7 +8812,7 @@ var V2_OPERATIONS = {
8788
8812
  },
8789
8813
  folderPaths: {
8790
8814
  kind: "string",
8791
- describe: "Folder paths to include with all their descendants, comma-separated. At most 100 entries, and the files they resolve to count against the same 100-file download ceiling. A path that matches no folder is rejected rather than ignored."
8815
+ describe: "Comma-separated folder paths whose contents are included recursively. Up to 100 paths; resolved files share the 100-file download limit. Unknown paths are rejected."
8792
8816
  }
8793
8817
  }
8794
8818
  },
@@ -9447,7 +9471,7 @@ var V2_OPERATIONS = {
9447
9471
  kind: "enum",
9448
9472
  values: ["streamable-http"],
9449
9473
  default: "streamable-http",
9450
- describe: "Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create."
9474
+ describe: "Transport protocol. Defaults to `streamable-http` on creation."
9451
9475
  },
9452
9476
  url: {
9453
9477
  kind: "string",
@@ -9466,17 +9490,17 @@ var V2_OPERATIONS = {
9466
9490
  timeout: {
9467
9491
  kind: "integer",
9468
9492
  default: 30000,
9469
- describe: "Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create."
9493
+ describe: "Per-request timeout in milliseconds. Defaults to 30000 on creation."
9470
9494
  },
9471
9495
  retries: {
9472
9496
  kind: "integer",
9473
9497
  default: 3,
9474
- describe: "Number of retries per request. Applied server-side as 3 when omitted on create."
9498
+ describe: "Number of retries per request. Defaults to 3 on creation."
9475
9499
  },
9476
9500
  enabled: {
9477
9501
  kind: "boolean",
9478
9502
  default: true,
9479
- describe: "Whether the server tools are available to workflows. Applied server-side as true when omitted on create."
9503
+ describe: "Whether workflows can use the server's tools. Defaults to true on creation."
9480
9504
  },
9481
9505
  oauthClientId: {
9482
9506
  kind: "string",
@@ -10374,7 +10398,7 @@ var V2_OPERATIONS = {
10374
10398
  input: {
10375
10399
  kind: "object",
10376
10400
  default: {},
10377
- describe: "Arguments for the tool, keyed by the parameter ids the tool catalog publishes for it. A parameter whose visibility is `user-only` also accepts an environment-variable reference written as the whole value, `{{VAR_NAME}}`, resolved server-side against the workspace environment; any other value is sent verbatim."
10401
+ describe: "Tool arguments keyed by published parameter IDs. For `user-only` parameters, a whole-value `{{VAR_NAME}}` reference resolves a workspace environment variable. Other values pass through unchanged."
10378
10402
  },
10379
10403
  credentialId: {
10380
10404
  kind: "string",
@@ -10409,7 +10433,7 @@ var V2_OPERATIONS = {
10409
10433
  },
10410
10434
  executionTimeoutSeconds: {
10411
10435
  kind: "integer",
10412
- describe: "Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true."
10436
+ describe: "Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`."
10413
10437
  },
10414
10438
  stream: {
10415
10439
  kind: "boolean",
@@ -10418,7 +10442,7 @@ var V2_OPERATIONS = {
10418
10442
  },
10419
10443
  selectedOutputs: {
10420
10444
  kind: "array",
10421
- describe: "Block output references to include in a streamed response. Use `<blockName>.<outputPath>` for the executed workflow or `<childWorkflowId>.<blockName>.<outputPath>` for a child workflow; block names are normalized workflow reference names. Selecting a child workflow applies to every invocation of it. Requires `stream: true` it shapes the streamed envelope only, so it is rejected on a sync request and when `async` is true. To narrow a finished run, pass `selectedOutputs` to the run resource instead."
10445
+ describe: "Output references for streaming: `<blockName>.<outputPath>` or `<childWorkflowId>.<blockName>.<outputPath>`, using normalized block names. Child references apply to every invocation. Requires `stream: true` and rejects synchronous or async requests. Use `selectedOutputs` with Get Workflow Run to narrow an existing run."
10422
10446
  },
10423
10447
  includeThinking: {
10424
10448
  kind: "boolean",
@@ -10442,7 +10466,7 @@ var V2_OPERATIONS = {
10442
10466
  headers: {
10443
10467
  "x-run-id": {
10444
10468
  kind: "string",
10445
- describe: 'Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: "RUN_ID_CONFLICT"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.'
10469
+ describe: "Run ID for API-key or OAuth callers; ignored for anonymous requests. Reuse it after an uncertain response: a claimed ID returns `409` with `RUN_ID_CONFLICT`, without replaying results. Check Get Workflow Run, but `404` can persist while the ID remains claimed and does not establish whether execution started. Do not automatically restart with a fresh or omitted ID; either can start another run."
10446
10470
  },
10447
10471
  "x-sim-via": {
10448
10472
  kind: "string",
@@ -10666,7 +10690,7 @@ var V2_OPERATIONS = {
10666
10690
  },
10667
10691
  folderPaths: {
10668
10692
  kind: "string",
10669
- describe: "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
10693
+ describe: "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches."
10670
10694
  },
10671
10695
  triggers: {
10672
10696
  kind: "string",
@@ -10688,7 +10712,7 @@ var V2_OPERATIONS = {
10688
10712
  segmentCount: {
10689
10713
  kind: "integer",
10690
10714
  default: 72,
10691
- describe: "Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty."
10715
+ describe: "Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets."
10692
10716
  }
10693
10717
  }
10694
10718
  },
@@ -11144,7 +11168,7 @@ var V2_OPERATIONS = {
11144
11168
  source: {
11145
11169
  kind: "enum",
11146
11170
  values: ["builtin", "custom"],
11147
- describe: "Restrict to shipped blocks or to this workspaces deployed custom blocks."
11171
+ describe: "Restrict to built-in blocks or this workspace's deployed custom blocks."
11148
11172
  },
11149
11173
  sortBy: {
11150
11174
  kind: "enum",
@@ -11343,7 +11367,7 @@ var V2_OPERATIONS = {
11343
11367
  },
11344
11368
  parentPath: {
11345
11369
  kind: "string",
11346
- describe: "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
11370
+ describe: "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches."
11347
11371
  },
11348
11372
  search: {
11349
11373
  kind: "string",
@@ -11405,7 +11429,7 @@ var V2_OPERATIONS = {
11405
11429
  },
11406
11430
  folderPath: {
11407
11431
  kind: "string",
11408
- describe: "Restrict results to files inside this folder — its direct children, or its whole subtree when `recursive` is true. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
11432
+ describe: "Restrict files to this folder, including subfolders when `recursive` is true. Unknown folder paths contribute no matches."
11409
11433
  },
11410
11434
  recursive: {
11411
11435
  kind: "enum",
@@ -11423,7 +11447,7 @@ var V2_OPERATIONS = {
11423
11447
  "n",
11424
11448
  "disabled"
11425
11449
  ],
11426
- describe: "Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected."
11450
+ describe: "Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter."
11427
11451
  },
11428
11452
  scope: {
11429
11453
  kind: "enum",
@@ -11474,11 +11498,11 @@ var V2_OPERATIONS = {
11474
11498
  kind: "enum",
11475
11499
  values: ["active", "archived"],
11476
11500
  default: "active",
11477
- describe: "Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/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."
11501
+ describe: "Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches."
11478
11502
  },
11479
11503
  folderPath: {
11480
11504
  kind: "string",
11481
- describe: "Restrict results to knowledge bases in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
11505
+ describe: "Restrict results to knowledge bases in this folder. Unknown folder paths contribute no matches."
11482
11506
  },
11483
11507
  search: {
11484
11508
  kind: "string",
@@ -11698,7 +11722,7 @@ var V2_OPERATIONS = {
11698
11722
  },
11699
11723
  parentPath: {
11700
11724
  kind: "string",
11701
- describe: "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
11725
+ describe: "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches."
11702
11726
  },
11703
11727
  search: {
11704
11728
  kind: "string",
@@ -11849,7 +11873,7 @@ var V2_OPERATIONS = {
11849
11873
  },
11850
11874
  folderPaths: {
11851
11875
  kind: "string",
11852
- describe: "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree, so `/prod` also selects runs in `/prod/nested`. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
11876
+ describe: "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches."
11853
11877
  }
11854
11878
  }
11855
11879
  },
@@ -11908,7 +11932,7 @@ var V2_OPERATIONS = {
11908
11932
  },
11909
11933
  refresh: {
11910
11934
  kind: "boolean",
11911
- describe: "Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip."
11935
+ describe: "Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools."
11912
11936
  }
11913
11937
  }
11914
11938
  },
@@ -12086,7 +12110,7 @@ var V2_OPERATIONS = {
12086
12110
  },
12087
12111
  parentPath: {
12088
12112
  kind: "string",
12089
- describe: "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
12113
+ describe: "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches."
12090
12114
  },
12091
12115
  search: {
12092
12116
  kind: "string",
@@ -12150,7 +12174,7 @@ var V2_OPERATIONS = {
12150
12174
  },
12151
12175
  folderPath: {
12152
12176
  kind: "string",
12153
- describe: "Restrict results to tables in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
12177
+ describe: "Restrict results to tables in this folder. Unknown folder paths contribute no matches."
12154
12178
  },
12155
12179
  search: {
12156
12180
  kind: "string",
@@ -12252,7 +12276,7 @@ var V2_OPERATIONS = {
12252
12276
  },
12253
12277
  parentPath: {
12254
12278
  kind: "string",
12255
- describe: "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
12279
+ describe: "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches."
12256
12280
  },
12257
12281
  search: {
12258
12282
  kind: "string",
@@ -12387,7 +12411,7 @@ var V2_OPERATIONS = {
12387
12411
  },
12388
12412
  folderPath: {
12389
12413
  kind: "string",
12390
- describe: "Restrict results to workflows in this folder path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
12414
+ describe: "Restrict results to workflows in this folder path. Unknown folder paths contribute no matches."
12391
12415
  },
12392
12416
  deployedOnly: {
12393
12417
  kind: "boolean",
@@ -12694,6 +12718,7 @@ var V2_OPERATIONS = {
12694
12718
  pathParamDocs: { workflowId: "Unique workflow identifier." },
12695
12719
  responseMode: "json",
12696
12720
  summary: "Create or Replace Workflow Chat Deployment",
12721
+ workspaceKeyUnsupported: true,
12697
12722
  body: {
12698
12723
  identifier: {
12699
12724
  kind: "string",
@@ -12746,6 +12771,7 @@ var V2_OPERATIONS = {
12746
12771
  pathParamDocs: { workflowId: "Unique workflow identifier." },
12747
12772
  responseMode: "json",
12748
12773
  summary: "Replace Workflow State",
12774
+ workspaceKeyUnsupported: true,
12749
12775
  query: {
12750
12776
  dryRun: {
12751
12777
  kind: "boolean",
@@ -13182,6 +13208,11 @@ var V2_OPERATIONS = {
13182
13208
  },
13183
13209
  apiToken: { kind: "string", describe: "Write-only provider API token." },
13184
13210
  domain: { kind: "string", describe: "Provider account domain." },
13211
+ atlassianProduct: {
13212
+ kind: "enum",
13213
+ values: ["jira", "confluence"],
13214
+ describe: "Atlassian product to verify; defaults to Jira on create and preserves the saved product on reconnect."
13215
+ },
13185
13216
  signingSecret: { kind: "string", describe: "Write-only webhook signing secret." },
13186
13217
  botToken: { kind: "string", describe: "Write-only bot token." },
13187
13218
  clientId: { kind: "string", describe: "OAuth client identifier." },
@@ -13432,7 +13463,7 @@ var V2_OPERATIONS = {
13432
13463
  kind: "enum",
13433
13464
  values: ["streamable-http"],
13434
13465
  default: "streamable-http",
13435
- describe: "Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create."
13466
+ describe: "Transport protocol. Defaults to `streamable-http` on creation."
13436
13467
  },
13437
13468
  url: {
13438
13469
  kind: "string",
@@ -13450,17 +13481,17 @@ var V2_OPERATIONS = {
13450
13481
  timeout: {
13451
13482
  kind: "integer",
13452
13483
  default: 30000,
13453
- describe: "Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create."
13484
+ describe: "Per-request timeout in milliseconds. Defaults to 30000 on creation."
13454
13485
  },
13455
13486
  retries: {
13456
13487
  kind: "integer",
13457
13488
  default: 3,
13458
- describe: "Number of retries per request. Applied server-side as 3 when omitted on create."
13489
+ describe: "Number of retries per request. Defaults to 3 on creation."
13459
13490
  },
13460
13491
  enabled: {
13461
13492
  kind: "boolean",
13462
13493
  default: true,
13463
- describe: "Whether the server tools are available to workflows. Applied server-side as true when omitted on create."
13494
+ describe: "Whether workflows can use the server's tools. Defaults to true on creation."
13464
13495
  },
13465
13496
  oauthClientId: {
13466
13497
  kind: "string",
@@ -16056,7 +16087,30 @@ function buildRequest(operation, positional, flags, workspaceId) {
16056
16087
  }
16057
16088
 
16058
16089
  // src/runtime/options.ts
16059
- var DEFAULT_LIMIT = 100;
16090
+ var DEFAULT_PAGE_SIZE = 100;
16091
+ var COMPLETE_LIST_OPERATIONS = new Set([
16092
+ "listBlocks",
16093
+ "listChatDeployments",
16094
+ "listCredentials",
16095
+ "listCustomTools",
16096
+ "listFiles",
16097
+ "listKnowledgeBases",
16098
+ "listKnowledgeConnectors",
16099
+ "listMcpServers",
16100
+ "listSandboxes",
16101
+ "listSecrets",
16102
+ "listSkillEditors",
16103
+ "listSkills",
16104
+ "listTables",
16105
+ "listTools",
16106
+ "listWorkflowMcpServers",
16107
+ "listWorkflows",
16108
+ "listWorkspaceMembers",
16109
+ "listWorkspaces"
16110
+ ]);
16111
+ function defaultListLimit(operation) {
16112
+ return COMPLETE_LIST_OPERATIONS.has(operation) ? 0 : 100;
16113
+ }
16060
16114
  function describeField(flag, descriptor, name, field) {
16061
16115
  return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
16062
16116
  }
@@ -16069,15 +16123,21 @@ function withoutWireVocabulary(documented) {
16069
16123
  }
16070
16124
  var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
16071
16125
  function addFieldOption(command, operation, field, descriptor, slot, paginates, capsAFilter) {
16072
- if (field === PROFILE_INJECTED_FIELD || field === "cursor")
16126
+ if (field === PROFILE_INJECTED_FIELD)
16127
+ return;
16128
+ if (field === "cursor") {
16129
+ if (paginates && defaultListLimit(operation) > 0) {
16130
+ command.option("--cursor <value>", "Continue from nextCursor returned by a previous result");
16131
+ }
16073
16132
  return;
16133
+ }
16074
16134
  const flag = flagSpecFor(operation, field);
16075
16135
  if (flag.omit)
16076
16136
  return;
16077
16137
  const name = flagNameFor(operation, field);
16078
16138
  const short = flag.short ? `-${flag.short}, ` : "";
16079
16139
  if (paginates && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
16080
- command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
16140
+ command.option("--limit <n>", "Maximum items to return (0 for everything)", String(defaultListLimit(operation)));
16081
16141
  return;
16082
16142
  }
16083
16143
  const documented = `${describeField(flag, descriptor, name, field)}${capsAFilter && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
@@ -16437,11 +16497,10 @@ function unwrapResource(data) {
16437
16497
  const [, value] = entries[0];
16438
16498
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
16439
16499
  }
16440
- function renderPage(format, rows, spec, envelope, options = {}) {
16500
+ function renderPage(format, page, spec, envelope) {
16441
16501
  writePageNote(spec, envelope);
16442
16502
  writeEnvelopeTruncation(envelope);
16443
- writeCursorTruncation(rows.length, options.truncated === true);
16444
- printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
16503
+ printList(format, page.data, spec.columns ? columnsFrom(spec.columns) : inferColumns(page.data, spec.expand), page);
16445
16504
  }
16446
16505
  function writePageNote(spec, envelope) {
16447
16506
  if (!spec.pageNote)
@@ -16486,12 +16545,6 @@ function writeEnvelopeTruncation(envelope) {
16486
16545
  `));
16487
16546
  }
16488
16547
  }
16489
- function writeCursorTruncation(count, truncated) {
16490
- if (!truncated)
16491
- return;
16492
- process.stderr.write(source_default.dim(`showing the first ${count}; more results exist — re-run with --limit 0 for all
16493
- `));
16494
- }
16495
16548
  function renderResult(operation, format, raw, spec, options = {}, envelope) {
16496
16549
  writeEnvelopeTruncation(envelope);
16497
16550
  if (spec.document) {
@@ -16628,8 +16681,8 @@ function bulkFailureMessage(operation, payload, body) {
16628
16681
  var EXCLUSIVE_CAP_FIELDS = {
16629
16682
  deleteTableRows: { cap: "limit", ids: "rowIds" }
16630
16683
  };
16631
- function readPagedLimit(raw) {
16632
- const text = String(raw ?? DEFAULT_LIMIT).trim();
16684
+ function readPagedLimit(raw, operation) {
16685
+ const text = String(raw ?? defaultListLimit(operation)).trim();
16633
16686
  const value = text === "" ? Number.NaN : Number(text);
16634
16687
  if (!Number.isInteger(value) || value < 0) {
16635
16688
  throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
@@ -16704,24 +16757,33 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
16704
16757
  const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
16705
16758
  const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
16706
16759
  const paging = cursorSlot(operationSpec);
16707
- const pagedLimit = paging ? readPagedLimit(requestFlags.limit) : 0;
16760
+ const pagedLimit = paging ? readPagedLimit(requestFlags.limit, operation) : 0;
16708
16761
  const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
16709
16762
  if (paging) {
16763
+ const initialCursor = request[paging]?.cursor;
16764
+ if (initialCursor !== undefined && (typeof initialCursor !== "string" || initialCursor.trim() === "")) {
16765
+ throw new SimApiError("--cursor must be a non-empty string", 0);
16766
+ }
16710
16767
  const limit = pagedLimit === 0 ? Number.POSITIVE_INFINITY : pagedLimit;
16711
- const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
16712
- const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
16713
16768
  const rows = [];
16769
+ const seenCursors = new Set(initialCursor ? [initialCursor] : []);
16714
16770
  const progress = pageProgress();
16715
- let cursor = null;
16771
+ let cursor = initialCursor ?? null;
16716
16772
  let envelope;
16717
16773
  try {
16718
16774
  do {
16775
+ const pageSize = Math.min(DEFAULT_PAGE_SIZE, limit - rows.length);
16776
+ const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
16719
16777
  const page = await client.request(request.path, {
16720
16778
  method: operationSpec.method,
16721
16779
  headers: request.headers,
16722
16780
  query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
16723
16781
  body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
16724
16782
  });
16783
+ if (page.data.length > pageSize) {
16784
+ throw new SimApiError(`The API returned ${page.data.length} items for a page limit of ${pageSize}; nextCursor would skip unreturned items.`, 0);
16785
+ }
16786
+ assertCursorAdvances(page.nextCursor, seenCursors);
16725
16787
  envelope = foldPageEnvelope(envelope, page);
16726
16788
  rows.push(...page.data);
16727
16789
  cursor = page.nextCursor;
@@ -16731,7 +16793,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
16731
16793
  } finally {
16732
16794
  progress.finish();
16733
16795
  }
16734
- renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope, { truncated: Boolean(cursor) });
16796
+ renderPage(profile.output, { data: rows, nextCursor: cursor }, commandSpec, envelope);
16735
16797
  return;
16736
16798
  }
16737
16799
  const result = await client.request(request.path, {
@@ -17239,6 +17301,47 @@ function attachCredentialCommands(program) {
17239
17301
  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 }));
17240
17302
  }
17241
17303
 
17304
+ // src/http/ndjson.ts
17305
+ async function* readNdjson(body, protocol) {
17306
+ if (!body) {
17307
+ throw new SimApiError(`${protocol} ended without a response body`, 0);
17308
+ }
17309
+ const reader = body.getReader();
17310
+ const decoder = new TextDecoder;
17311
+ let buffer = "";
17312
+ const parse = (line) => {
17313
+ const trimmed = line.trim();
17314
+ if (!trimmed)
17315
+ return;
17316
+ try {
17317
+ return JSON.parse(trimmed);
17318
+ } catch {
17319
+ throw new SimApiError(`${protocol} returned malformed data`, 0);
17320
+ }
17321
+ };
17322
+ try {
17323
+ while (true) {
17324
+ const { done, value } = await reader.read();
17325
+ buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
17326
+ const lines = buffer.split(`
17327
+ `);
17328
+ buffer = done ? "" : lines.pop() ?? "";
17329
+ for (const line of lines) {
17330
+ const event = parse(line);
17331
+ if (event !== undefined)
17332
+ yield event;
17333
+ }
17334
+ if (done)
17335
+ return;
17336
+ }
17337
+ } finally {
17338
+ reader.cancel().catch(() => {
17339
+ return;
17340
+ });
17341
+ reader.releaseLock();
17342
+ }
17343
+ }
17344
+
17242
17345
  // src/commands/protocol/result.ts
17243
17346
  function printProtocolResult(format, result) {
17244
17347
  const fields = Object.entries(result).map(([key, value]) => [key, text(value)]);
@@ -17246,72 +17349,28 @@ function printProtocolResult(format, result) {
17246
17349
  }
17247
17350
 
17248
17351
  // src/commands/protocol/chat.ts
17249
- function parseChatStreamLine(line) {
17250
- const trimmed = line.trim();
17251
- if (!trimmed)
17252
- return;
17253
- try {
17254
- return JSON.parse(trimmed);
17255
- } catch {
17256
- throw new SimApiError("Chat stream returned malformed data", 0);
17257
- }
17258
- }
17259
17352
  async function readChatStream(response, onChunk) {
17260
- if (!response.body) {
17261
- throw new SimApiError("Chat stream ended without a response body", 0);
17262
- }
17263
- const reader = response.body.getReader();
17264
- const decoder = new TextDecoder;
17265
- let buffer = "";
17266
- let finalResult;
17267
- const processLine = (line) => {
17268
- const event = parseChatStreamLine(line);
17269
- if (!event || event.type === "heartbeat")
17270
- return false;
17353
+ for await (const value of readNdjson(response.body, "Chat stream")) {
17354
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
17355
+ throw new SimApiError("Chat stream returned an unknown event", 0);
17356
+ }
17357
+ const event = value;
17358
+ if (event.type === "heartbeat")
17359
+ continue;
17271
17360
  if (event.type === "chunk") {
17272
17361
  if (event.content)
17273
17362
  onChunk(sanitize(event.content));
17274
- return false;
17363
+ continue;
17275
17364
  }
17276
17365
  if (event.type === "error") {
17277
17366
  throw new SimApiError(event.error || "Chat request failed", 0);
17278
17367
  }
17279
17368
  if (event.type === "final") {
17280
- finalResult = event.data;
17281
- return true;
17369
+ return event.data;
17282
17370
  }
17283
17371
  throw new SimApiError("Chat stream returned an unknown event", 0);
17284
- };
17285
- try {
17286
- let ended = false;
17287
- while (!ended) {
17288
- const { done, value } = await reader.read();
17289
- if (done)
17290
- break;
17291
- buffer += decoder.decode(value, { stream: true });
17292
- const lines = buffer.split(`
17293
- `);
17294
- buffer = lines.pop() ?? "";
17295
- for (const line of lines) {
17296
- if (processLine(line)) {
17297
- ended = true;
17298
- break;
17299
- }
17300
- }
17301
- }
17302
- if (!ended) {
17303
- buffer += decoder.decode();
17304
- processLine(buffer);
17305
- }
17306
- if (!finalResult) {
17307
- throw new SimApiError("Chat stream ended without a final result", 0);
17308
- }
17309
- return finalResult;
17310
- } finally {
17311
- reader.cancel().catch(() => {
17312
- return;
17313
- });
17314
17372
  }
17373
+ throw new SimApiError("Chat stream ended without a final result", 0);
17315
17374
  }
17316
17375
  function ignoreBrokenPipe(stream) {
17317
17376
  const onError = (error) => {
@@ -18086,11 +18145,11 @@ async function listResources(client, config, workspaceId, folderPath, search, li
18086
18145
  const paginated = "cursor" in V2_OPERATIONS[config.resources].query;
18087
18146
  if (!paginated) {
18088
18147
  const page = await client.request(path, { query });
18089
- return { items: page.data.slice(0, limit), truncated: page.data.length > limit };
18148
+ return page.data.slice(0, limit);
18090
18149
  }
18091
- return requestPages(client, path, {
18150
+ return requestAllPages(client, path, {
18092
18151
  query,
18093
- pageSize: DEFAULT_LIMIT,
18152
+ pageSize: DEFAULT_PAGE_SIZE,
18094
18153
  limit
18095
18154
  });
18096
18155
  }
@@ -18120,7 +18179,7 @@ function entriesFor(config, folders, resources) {
18120
18179
  ].sort((left, right) => left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind));
18121
18180
  }
18122
18181
  function attachResourceDirectoryCommands(group, config) {
18123
- group.command("ls").argument("[path]", "Folder path to list; defaults to the root folder").allowExcessArguments(false).description(`List ${config.kind} resources and child folders together`).option("--search <text>", "Filter folders and resources by name").addOption(new Option("--limit <n>", "Maximum combined items to return (0 for everything)").default(String(DEFAULT_LIMIT))).action(async (path, options, command) => {
18182
+ group.command("ls").argument("[path]", "Folder path to list; defaults to the root folder").allowExcessArguments(false).description(`List ${config.kind} resources and child folders together`).option("--search <text>", "Filter folders and resources by name").addOption(new Option("--limit <n>", "Maximum combined items to return (0 for everything)").default("0")).action(async (path, options, command) => {
18124
18183
  const rawLimit = Number(options.limit);
18125
18184
  if (!Number.isSafeInteger(rawLimit) || rawLimit < 0) {
18126
18185
  throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
@@ -18133,9 +18192,8 @@ function attachResourceDirectoryCommands(group, config) {
18133
18192
  listFolders(client, config.folders, workspaceId, folderPath, options.search),
18134
18193
  listResources(client, config, workspaceId, folderPath, options.search, limit)
18135
18194
  ]);
18136
- const entries = entriesFor(config, folders, resources.items);
18195
+ const entries = entriesFor(config, folders, resources);
18137
18196
  const shown = entries.slice(0, limit);
18138
- writeCursorTruncation(shown.length, resources.truncated || entries.length > limit);
18139
18197
  printList(profile.output, shown, COLUMNS2);
18140
18198
  });
18141
18199
  group.command("mkdir").argument("<path>", "Folder path to create; the leading / is optional").allowExcessArguments(false).description(`Create a ${config.kind} directory at a path`).action(async (path, _options, command) => {
@@ -18302,6 +18360,7 @@ function attachTableImport(tables) {
18302
18360
  // src/commands/protocol/workflow-run-follow.ts
18303
18361
  var AGENT_STREAM_PROTOCOL_HEADER = "x-sim-stream-protocol";
18304
18362
  var AGENT_STREAM_PROTOCOL_V1 = "agent-events-v1";
18363
+ var WORKFLOW_RESULT_STREAM_CONTENT_TYPE = "application/x-ndjson";
18305
18364
  var DONE_SENTINEL = "[DONE]";
18306
18365
  function resolveWorkflowRunSelection(flags) {
18307
18366
  const manual = flags.manual === true;
@@ -18353,6 +18412,59 @@ function stringField(frame, key) {
18353
18412
  const value = frame[key];
18354
18413
  return typeof value === "string" ? value : null;
18355
18414
  }
18415
+ async function readWorkflowResult(response) {
18416
+ const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
18417
+ if (!contentType.includes(WORKFLOW_RESULT_STREAM_CONTENT_TYPE)) {
18418
+ let envelope;
18419
+ try {
18420
+ envelope = await response.json();
18421
+ } catch {
18422
+ throw new SimApiError(`Workflow run returned malformed JSON${contentType ? ` as ${contentType}` : ""}`, response.status);
18423
+ }
18424
+ if (!isRecord(envelope)) {
18425
+ throw new SimApiError("Workflow run returned an invalid result envelope", response.status);
18426
+ }
18427
+ return isRecord(envelope.data) ? envelope.data : envelope;
18428
+ }
18429
+ for await (const value of readNdjson(response.body, "Workflow result stream")) {
18430
+ if (!isRecord(value) || typeof value.type !== "string") {
18431
+ throw new SimApiError("Workflow result stream returned an unknown event", response.status);
18432
+ }
18433
+ if (value.type === "heartbeat")
18434
+ continue;
18435
+ if (value.type === "error") {
18436
+ throw new SimApiError(safeOneLine(typeof value.error === "string" ? value.error : "Workflow run failed"), typeof value.status === "number" ? value.status : 0, typeof value.code === "string" ? value.code : null);
18437
+ }
18438
+ if (value.type === "final" && isRecord(value.data))
18439
+ return value.data;
18440
+ throw new SimApiError("Workflow result stream returned an unknown event", response.status);
18441
+ }
18442
+ throw new SimApiError("Workflow result stream ended without a final result", response.status);
18443
+ }
18444
+ async function runWithResultStream(workflowId, command) {
18445
+ const flags = command.optsWithGlobals();
18446
+ const { client, profile } = clientFrom(command);
18447
+ const operation = V2_OPERATIONS.executeWorkflow;
18448
+ const commandSpec = CLI_CONTRACT.executeWorkflow ?? {};
18449
+ try {
18450
+ const request = buildRequest("executeWorkflow", [workflowId], flags, profile.workspaceId);
18451
+ const response = await client.requestRaw(request.path, {
18452
+ method: operation.method,
18453
+ query: request.query,
18454
+ body: request.body,
18455
+ headers: { ...request.headers, accept: WORKFLOW_RESULT_STREAM_CONTENT_TYPE }
18456
+ });
18457
+ const payload = await readWorkflowResult(response);
18458
+ renderResult("executeWorkflow", profile.output, payload, commandSpec, {
18459
+ expandedTrace: flags.trace === true
18460
+ });
18461
+ const failure = runFailureMessage("executeWorkflow", payload);
18462
+ if (failure)
18463
+ throw new SimApiError(failure, 0);
18464
+ } catch (error) {
18465
+ throw retypeApiError(error, "executeWorkflow", commandSpec, operation);
18466
+ }
18467
+ }
18356
18468
  async function* sseData(body) {
18357
18469
  const reader = body.getReader();
18358
18470
  const decoder = new TextDecoder;
@@ -18524,6 +18636,10 @@ function followOrDelegate(previous) {
18524
18636
  if (flags.includeThinking === true || flags.includeToolCalls === true) {
18525
18637
  throw new SimApiError("--include-thinking and --include-tool-calls describe a stream; add --follow", 0);
18526
18638
  }
18639
+ if (flags.async !== true) {
18640
+ await runWithResultStream(workflowId, command);
18641
+ return;
18642
+ }
18527
18643
  if (previous) {
18528
18644
  await previous(command.processedArgs);
18529
18645
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim",
3
- "version": "2.1.8-preview.98.1",
3
+ "version": "2.1.8",
4
4
  "description": "Sim CLI - talk to the Sim API from your terminal",
5
5
  "type": "module",
6
6
  "bin": {