sim 2.1.8-preview.100.1 → 2.1.8-preview.102.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +16 -1
  2. package/dist/index.js +67 -30
  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
@@ -4763,15 +4763,21 @@ function pageProgress() {
4763
4763
  }
4764
4764
  };
4765
4765
  }
4766
- async function requestAllPages(client, path, options) {
4767
- 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);
4768
4773
  }
4769
- async function requestPages(client, path, options) {
4774
+ async function requestAllPages(client, path, options) {
4770
4775
  const { query, pageSize, limit: requestedLimit, ...requestOptions } = options;
4771
4776
  const limit = requestedLimit ?? Number.POSITIVE_INFINITY;
4772
4777
  if (limit <= 0)
4773
- return { items: [], truncated: false };
4778
+ return [];
4774
4779
  const items = [];
4780
+ const seenCursors = new Set;
4775
4781
  const progress = pageProgress();
4776
4782
  let cursor = null;
4777
4783
  try {
@@ -4784,6 +4790,7 @@ async function requestPages(client, path, options) {
4784
4790
  cursor
4785
4791
  }
4786
4792
  });
4793
+ assertCursorAdvances(page.nextCursor, seenCursors);
4787
4794
  items.push(...page.data);
4788
4795
  cursor = page.nextCursor;
4789
4796
  if (cursor && items.length < limit)
@@ -4792,7 +4799,7 @@ async function requestPages(client, path, options) {
4792
4799
  } finally {
4793
4800
  progress.finish();
4794
4801
  }
4795
- return { items: items.slice(0, limit), truncated: cursor !== null || items.length > limit };
4802
+ return items.slice(0, limit);
4796
4803
  }
4797
4804
  function resolvePath(template, params = {}) {
4798
4805
  return template.replace(/\[([^\]]+)\]/g, (_match, key) => {
@@ -16075,7 +16082,30 @@ function buildRequest(operation, positional, flags, workspaceId) {
16075
16082
  }
16076
16083
 
16077
16084
  // src/runtime/options.ts
16078
- var DEFAULT_LIMIT = 100;
16085
+ var DEFAULT_PAGE_SIZE = 100;
16086
+ var COMPLETE_LIST_OPERATIONS = new Set([
16087
+ "listBlocks",
16088
+ "listChatDeployments",
16089
+ "listCredentials",
16090
+ "listCustomTools",
16091
+ "listFiles",
16092
+ "listKnowledgeBases",
16093
+ "listKnowledgeConnectors",
16094
+ "listMcpServers",
16095
+ "listSandboxes",
16096
+ "listSecrets",
16097
+ "listSkillEditors",
16098
+ "listSkills",
16099
+ "listTables",
16100
+ "listTools",
16101
+ "listWorkflowMcpServers",
16102
+ "listWorkflows",
16103
+ "listWorkspaceMembers",
16104
+ "listWorkspaces"
16105
+ ]);
16106
+ function defaultListLimit(operation) {
16107
+ return COMPLETE_LIST_OPERATIONS.has(operation) ? 0 : 100;
16108
+ }
16079
16109
  function describeField(flag, descriptor, name, field) {
16080
16110
  return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll("-", " ") || field}`;
16081
16111
  }
@@ -16088,15 +16118,21 @@ function withoutWireVocabulary(documented) {
16088
16118
  }
16089
16119
  var NON_PAGINATED_LIMIT_HINT = " (caps a --filter match only; omit it to act on every match, and note 0 is not accepted)";
16090
16120
  function addFieldOption(command, operation, field, descriptor, slot, paginates, capsAFilter) {
16091
- if (field === PROFILE_INJECTED_FIELD || field === "cursor")
16121
+ if (field === PROFILE_INJECTED_FIELD)
16092
16122
  return;
16123
+ if (field === "cursor") {
16124
+ if (paginates && defaultListLimit(operation) > 0) {
16125
+ command.option("--cursor <value>", "Continue from nextCursor returned by a previous result");
16126
+ }
16127
+ return;
16128
+ }
16093
16129
  const flag = flagSpecFor(operation, field);
16094
16130
  if (flag.omit)
16095
16131
  return;
16096
16132
  const name = flagNameFor(operation, field);
16097
16133
  const short = flag.short ? `-${flag.short}, ` : "";
16098
16134
  if (paginates && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer")) {
16099
- command.option("--limit <n>", "Maximum items to return (0 for everything)", String(DEFAULT_LIMIT));
16135
+ command.option("--limit <n>", "Maximum items to return (0 for everything)", String(defaultListLimit(operation)));
16100
16136
  return;
16101
16137
  }
16102
16138
  const documented = `${describeField(flag, descriptor, name, field)}${capsAFilter && field === "limit" && (descriptor.kind === "number" || descriptor.kind === "integer") ? NON_PAGINATED_LIMIT_HINT : ""}`;
@@ -16456,11 +16492,10 @@ function unwrapResource(data) {
16456
16492
  const [, value] = entries[0];
16457
16493
  return value && typeof value === "object" && !Array.isArray(value) ? value : data;
16458
16494
  }
16459
- function renderPage(format, rows, spec, envelope, options = {}) {
16495
+ function renderPage(format, page, spec, envelope) {
16460
16496
  writePageNote(spec, envelope);
16461
16497
  writeEnvelopeTruncation(envelope);
16462
- writeCursorTruncation(rows.length, options.truncated === true);
16463
- printList(format, rows, spec.columns ? columnsFrom(spec.columns) : inferColumns(rows, spec.expand));
16498
+ printList(format, page.data, spec.columns ? columnsFrom(spec.columns) : inferColumns(page.data, spec.expand), page);
16464
16499
  }
16465
16500
  function writePageNote(spec, envelope) {
16466
16501
  if (!spec.pageNote)
@@ -16505,12 +16540,6 @@ function writeEnvelopeTruncation(envelope) {
16505
16540
  `));
16506
16541
  }
16507
16542
  }
16508
- function writeCursorTruncation(count, truncated) {
16509
- if (!truncated)
16510
- return;
16511
- process.stderr.write(source_default.dim(`showing the first ${count}; more results exist — re-run with --limit 0 for all
16512
- `));
16513
- }
16514
16543
  function renderResult(operation, format, raw, spec, options = {}, envelope) {
16515
16544
  writeEnvelopeTruncation(envelope);
16516
16545
  if (spec.document) {
@@ -16647,8 +16676,8 @@ function bulkFailureMessage(operation, payload, body) {
16647
16676
  var EXCLUSIVE_CAP_FIELDS = {
16648
16677
  deleteTableRows: { cap: "limit", ids: "rowIds" }
16649
16678
  };
16650
- function readPagedLimit(raw) {
16651
- const text = String(raw ?? DEFAULT_LIMIT).trim();
16679
+ function readPagedLimit(raw, operation) {
16680
+ const text = String(raw ?? defaultListLimit(operation)).trim();
16652
16681
  const value = text === "" ? Number.NaN : Number(text);
16653
16682
  if (!Number.isInteger(value) || value < 0) {
16654
16683
  throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
@@ -16723,24 +16752,33 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
16723
16752
  const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
16724
16753
  const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
16725
16754
  const paging = cursorSlot(operationSpec);
16726
- const pagedLimit = paging ? readPagedLimit(requestFlags.limit) : 0;
16755
+ const pagedLimit = paging ? readPagedLimit(requestFlags.limit, operation) : 0;
16727
16756
  const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
16728
16757
  if (paging) {
16758
+ const initialCursor = request[paging]?.cursor;
16759
+ if (initialCursor !== undefined && (typeof initialCursor !== "string" || initialCursor.trim() === "")) {
16760
+ throw new SimApiError("--cursor must be a non-empty string", 0);
16761
+ }
16729
16762
  const limit = pagedLimit === 0 ? Number.POSITIVE_INFINITY : pagedLimit;
16730
- const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
16731
- const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
16732
16763
  const rows = [];
16764
+ const seenCursors = new Set(initialCursor ? [initialCursor] : []);
16733
16765
  const progress = pageProgress();
16734
- let cursor = null;
16766
+ let cursor = initialCursor ?? null;
16735
16767
  let envelope;
16736
16768
  try {
16737
16769
  do {
16770
+ const pageSize = Math.min(DEFAULT_PAGE_SIZE, limit - rows.length);
16771
+ const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
16738
16772
  const page = await client.request(request.path, {
16739
16773
  method: operationSpec.method,
16740
16774
  headers: request.headers,
16741
16775
  query: paging === "query" ? { ...request.query, ...pageLimit, cursor } : request.query,
16742
16776
  body: paging === "body" ? { ...request.body ?? {}, ...pageLimit, ...cursor ? { cursor } : {} } : request.body
16743
16777
  });
16778
+ if (page.data.length > pageSize) {
16779
+ throw new SimApiError(`The API returned ${page.data.length} items for a page limit of ${pageSize}; nextCursor would skip unreturned items.`, 0);
16780
+ }
16781
+ assertCursorAdvances(page.nextCursor, seenCursors);
16744
16782
  envelope = foldPageEnvelope(envelope, page);
16745
16783
  rows.push(...page.data);
16746
16784
  cursor = page.nextCursor;
@@ -16750,7 +16788,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
16750
16788
  } finally {
16751
16789
  progress.finish();
16752
16790
  }
16753
- renderPage(profile.output, Number.isFinite(limit) ? rows.slice(0, limit) : rows, commandSpec, envelope, { truncated: Boolean(cursor) });
16791
+ renderPage(profile.output, { data: rows, nextCursor: cursor }, commandSpec, envelope);
16754
16792
  return;
16755
16793
  }
16756
16794
  const result = await client.request(request.path, {
@@ -18102,11 +18140,11 @@ async function listResources(client, config, workspaceId, folderPath, search, li
18102
18140
  const paginated = "cursor" in V2_OPERATIONS[config.resources].query;
18103
18141
  if (!paginated) {
18104
18142
  const page = await client.request(path, { query });
18105
- return { items: page.data.slice(0, limit), truncated: page.data.length > limit };
18143
+ return page.data.slice(0, limit);
18106
18144
  }
18107
- return requestPages(client, path, {
18145
+ return requestAllPages(client, path, {
18108
18146
  query,
18109
- pageSize: DEFAULT_LIMIT,
18147
+ pageSize: DEFAULT_PAGE_SIZE,
18110
18148
  limit
18111
18149
  });
18112
18150
  }
@@ -18136,7 +18174,7 @@ function entriesFor(config, folders, resources) {
18136
18174
  ].sort((left, right) => left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind));
18137
18175
  }
18138
18176
  function attachResourceDirectoryCommands(group, config) {
18139
- 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) => {
18177
+ 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) => {
18140
18178
  const rawLimit = Number(options.limit);
18141
18179
  if (!Number.isSafeInteger(rawLimit) || rawLimit < 0) {
18142
18180
  throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
@@ -18149,9 +18187,8 @@ function attachResourceDirectoryCommands(group, config) {
18149
18187
  listFolders(client, config.folders, workspaceId, folderPath, options.search),
18150
18188
  listResources(client, config, workspaceId, folderPath, options.search, limit)
18151
18189
  ]);
18152
- const entries = entriesFor(config, folders, resources.items);
18190
+ const entries = entriesFor(config, folders, resources);
18153
18191
  const shown = entries.slice(0, limit);
18154
- writeCursorTruncation(shown.length, resources.truncated || entries.length > limit);
18155
18192
  printList(profile.output, shown, COLUMNS2);
18156
18193
  });
18157
18194
  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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim",
3
- "version": "2.1.8-preview.100.1",
3
+ "version": "2.1.8-preview.102.1",
4
4
  "description": "Sim CLI - talk to the Sim API from your terminal",
5
5
  "type": "module",
6
6
  "bin": {