sim 2.1.3-preview.50.1 → 2.1.3-preview.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +89 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2866,6 +2866,10 @@ function traceRequest(method, url, status, startedAt) {
2866
2866
  process.stderr.write(`${source_default.dim(`[sim] ${method} ${url} → ${status} ${Math.round(performance.now() - startedAt)}ms`)}
2867
2867
  `);
2868
2868
  }
2869
+ function withoutLeadingLabel(message, label) {
2870
+ const prefix = `${label}: `;
2871
+ return message.startsWith(prefix) ? message.slice(prefix.length) : message;
2872
+ }
2869
2873
  function formatApiErrorDetails(details) {
2870
2874
  const issues = [];
2871
2875
  const seen = new Set;
@@ -2903,7 +2907,10 @@ function formatApiErrorDetails(details) {
2903
2907
  const visible = kept.slice(0, 8);
2904
2908
  const lines = [
2905
2909
  " details:",
2906
- ...visible.map((issue) => ` ${issue.path.length > 0 ? issue.path.join(".") : "request"}: ${issue.message}`)
2910
+ ...visible.map((issue) => {
2911
+ const label = issue.path.length > 0 ? issue.path.join(".") : "request";
2912
+ return ` ${label}: ${withoutLeadingLabel(issue.message, label)}`;
2913
+ })
2907
2914
  ];
2908
2915
  if (kept.length > visible.length)
2909
2916
  lines.push(` … ${kept.length - visible.length} more issues`);
@@ -3041,10 +3048,13 @@ function pageProgress() {
3041
3048
  };
3042
3049
  }
3043
3050
  async function requestAllPages(client, path, options) {
3051
+ return (await requestPages(client, path, options)).items;
3052
+ }
3053
+ async function requestPages(client, path, options) {
3044
3054
  const { query, pageSize, limit: requestedLimit, ...requestOptions } = options;
3045
3055
  const limit = requestedLimit ?? Number.POSITIVE_INFINITY;
3046
3056
  if (limit <= 0)
3047
- return [];
3057
+ return { items: [], truncated: false };
3048
3058
  const items = [];
3049
3059
  const progress = pageProgress();
3050
3060
  let cursor = null;
@@ -3066,7 +3076,7 @@ async function requestAllPages(client, path, options) {
3066
3076
  } finally {
3067
3077
  progress.finish();
3068
3078
  }
3069
- return items.slice(0, limit);
3079
+ return { items: items.slice(0, limit), truncated: cursor !== null || items.length > limit };
3070
3080
  }
3071
3081
  function resolvePath(template, params = {}) {
3072
3082
  return template.replace(/\[([^\]]+)\]/g, (_match, key) => {
@@ -11909,7 +11919,7 @@ function configureCommand() {
11909
11919
  const value = globals[option];
11910
11920
  if (value === undefined)
11911
11921
  continue;
11912
- throw new SimApiError(`${flag} applies to a single command and is not stored. To save it, run: sim configure ${setFlag} ${value}`, 0);
11922
+ throw new SimApiError(`${flag} applies to a single command and is not stored. To save it, run: sim configure ${setFlag} ${redact(value)}`, 0);
11913
11923
  }
11914
11924
  const profile = profileFrom(command, { allowUnknownProfile: true });
11915
11925
  const authProfile = resolveAuthenticationProfileName(profile.name);
@@ -11970,7 +11980,7 @@ var TABLE_SORT_HELP = 'Ordered sort keys: [{"field":"createdAt","direction":"des
11970
11980
  var KNOWLEDGE_TAG_DEFINITIONS_HELP = 'Tag definitions: [{"tagSlot":"tag1","displayName":"category","fieldType":"text"}]';
11971
11981
  var CUSTOM_TOOL_SCHEMA_HELP = 'OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}}';
11972
11982
  var DISPATCH_ROW_LIMIT_HELP = "Stop after this many eligible rows have run (1-1,000,000). Omit for an unbounded run";
11973
- var WORKFLOW_OPERATIONS_HELP = 'Edits to apply, in a single batch, keyed by operation_type: [{"operation_type":"add","block_id":"my-fn","params":{"type":"function","name":"My Fn","inputs":{"code":"return {ok:true}"}}},{"operation_type":"edit","block_id":"<uuid>","params":{"name":"Renamed","connections":{"success":"my-fn"}}},{"operation_type":"delete","block_id":"<uuid>"}]. Also insert_into_subflow and extract_from_subflow, whose params carry {"subflowId":"<loop-id>"}';
11983
+ var WORKFLOW_OPERATIONS_HELP = 'Edits to apply, in a single batch, keyed by operation_type: [{"operation_type":"add","block_id":"my-fn","params":{"type":"function","name":"My Fn","inputs":{"code":"return {ok:true}"}}},{"operation_type":"edit","block_id":"<uuid>","params":{"name":"Renamed","connections":{"success":"my-fn"}}},{"operation_type":"delete","block_id":"<uuid>"}]. Also extract_from_subflow, whose params carry {"subflowId":"<loop-id>"}, and insert_into_subflow, which creates a block and so takes an add’s params plus that subflowId';
11974
11984
  var WORKFLOW_SET_BLOCK_ENABLED_HELP = 'Blocks to enable or disable, applied after --operations: [{"block_id":"<uuid>","enabled":false}]. Disabling a loop or parallel cascades to its unlocked descendants; enabling a block whose container is disabled is declined';
11975
11985
  var WORKFLOW_VARIABLE_OPERATIONS_HELP = 'Variable changes to apply in order, keyed by operation: [{"operation":"add","name":"my_var","type":"string","value":"hello"},{"operation":"edit","name":"my_var","value":"updated"},{"operation":"delete","name":"my_var"}]';
11976
11986
  var MCP_PARAMETER_DESCRIPTIONS_HELP = 'Per-field description overrides applied to the schema generated from the deployed workflow inputs, as [{"name":"email","description":"Customer email address"}]. A name matching no input field is ignored';
@@ -12046,7 +12056,7 @@ var CLI_CONTRACT = {
12046
12056
  listBillingLogs: {
12047
12057
  command: "billing logs",
12048
12058
  allWorkspaces: true,
12049
- describe: "List credit usage events (a personal API key reports only your own events; a workspace API key reports every member's)",
12059
+ describe: "List credit usage events (a personal API key reports only your own events; a workspace API key reports every member's in aggregate, unattributed)",
12050
12060
  flags: {
12051
12061
  source: { describe: "Filter by usage source; sim-chat combines Copilot and workspace chat" },
12052
12062
  period: { describe: "Billing period" },
@@ -12239,7 +12249,7 @@ var CLI_CONTRACT = {
12239
12249
  },
12240
12250
  getLogStats: {
12241
12251
  command: "logs stats",
12242
- describe: "Summarize run counts, failures, and cost over a window",
12252
+ describe: "Summarize run counts, failures and latency over a window",
12243
12253
  flags: LOG_LIST_FILTER_FLAGS,
12244
12254
  fields: [
12245
12255
  { header: "runs", path: "totalRuns" },
@@ -12567,7 +12577,7 @@ var CLI_CONTRACT = {
12567
12577
  listCustomTools: {
12568
12578
  columns: [
12569
12579
  { header: "id" },
12570
- { header: "name", path: "title" },
12580
+ { header: "title", path: "title" },
12571
12581
  { header: "description", path: "schema.function.description" },
12572
12582
  { header: "updated", path: "updatedAt", format: "timestamp" }
12573
12583
  ]
@@ -12799,6 +12809,7 @@ var CLI_CONTRACT = {
12799
12809
  ]
12800
12810
  },
12801
12811
  listFileFolders: {
12812
+ describe: "List folders",
12802
12813
  aliases: ["ls"],
12803
12814
  flags: {
12804
12815
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12806,6 +12817,7 @@ var CLI_CONTRACT = {
12806
12817
  columns: FOLDER_LIST_COLUMNS
12807
12818
  },
12808
12819
  listKnowledgeFolders: {
12820
+ describe: "List knowledge folders",
12809
12821
  aliases: ["ls"],
12810
12822
  flags: {
12811
12823
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12813,6 +12825,7 @@ var CLI_CONTRACT = {
12813
12825
  columns: FOLDER_LIST_COLUMNS
12814
12826
  },
12815
12827
  listTableFolders: {
12828
+ describe: "List table folders",
12816
12829
  aliases: ["ls"],
12817
12830
  flags: {
12818
12831
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -12820,6 +12833,7 @@ var CLI_CONTRACT = {
12820
12833
  columns: FOLDER_LIST_COLUMNS
12821
12834
  },
12822
12835
  listWorkflowFolders: {
12836
+ describe: "List workflow folders",
12823
12837
  aliases: ["ls"],
12824
12838
  flags: {
12825
12839
  parentPath: { ...FOLDER_PATH_INPUT, name: "parent", describe: "Direct parent folder path" }
@@ -13298,6 +13312,7 @@ function encodeFolderPath(value) {
13298
13312
  }
13299
13313
  }).join("/");
13300
13314
  }
13315
+ var FRACTIONAL_DIGITS = /\.\d*[1-9]/;
13301
13316
  function pathHint(raw) {
13302
13317
  if (raw.startsWith("@") || /^\s*[[{"\-\d]|^\s*(true|false|null)/.test(raw))
13303
13318
  return "";
@@ -13326,6 +13341,12 @@ function coerce(raw, field, flag, flagName) {
13326
13341
  const value = Number(raw);
13327
13342
  if (Number.isNaN(value))
13328
13343
  throw new SimApiError(`--${flagName} must be a number`, 0);
13344
+ if (field.kind === "integer" && (!Number.isInteger(value) || FRACTIONAL_DIGITS.test(String(raw)))) {
13345
+ throw new SimApiError(`--${flagName} must be a whole number`, 0);
13346
+ }
13347
+ if (field.kind === "integer" && !Number.isSafeInteger(value)) {
13348
+ throw new SimApiError(`--${flagName} is outside the whole-number range the API accepts (±${Number.MAX_SAFE_INTEGER})`, 0);
13349
+ }
13329
13350
  return value;
13330
13351
  }
13331
13352
  if (field.kind === "boolean" || flag.boolean)
@@ -13385,6 +13406,9 @@ function buildRequest(operation, positional, flags, workspaceId) {
13385
13406
  throw new SimApiError(`--${flagName} cannot be empty`, 0);
13386
13407
  }
13387
13408
  const value = coerce(raw ?? undefined, descriptor, flag, flagName);
13409
+ if (field === "limit" && !paginatedLimit && NUMERIC_KINDS.has(descriptor.kind) && typeof value === "number" && value < 1) {
13410
+ throw new SimApiError(`--${flagName} must be 1 or more`, 0);
13411
+ }
13388
13412
  if (value === undefined) {
13389
13413
  if (descriptor.required) {
13390
13414
  throw new SimApiError(field === PROFILE_INJECTED_FIELD ? NO_WORKSPACE_FALLBACK : `--${flagName} is required`, 0);
@@ -13533,7 +13557,7 @@ function addOperationOptions(command, operation, commandSpec, operationSpec) {
13533
13557
  }
13534
13558
  if (commandSpec.confirm) {
13535
13559
  const exemptedByDryRun = operationSpec.query?.dryRun !== undefined || operationSpec.body?.dryRun !== undefined;
13536
- command.option("-y, --yes", exemptedByDryRun ? "Confirm this destructive operation (required unless --dry-run)" : "Confirm this destructive operation (required)");
13560
+ command.option("-y, --yes", exemptedByDryRun ? "Confirm this operation (required unless --dry-run)" : "Confirm this operation (required)");
13537
13561
  }
13538
13562
  }
13539
13563
 
@@ -13993,6 +14017,14 @@ function bulkFailureMessage(operation, payload, body) {
13993
14017
  var EXCLUSIVE_CAP_FIELDS = {
13994
14018
  deleteTableRows: { cap: "limit", ids: "rowIds" }
13995
14019
  };
14020
+ function readPagedLimit(raw) {
14021
+ const text2 = String(raw ?? DEFAULT_LIMIT).trim();
14022
+ const value = text2 === "" ? Number.NaN : Number(text2);
14023
+ if (!Number.isInteger(value) || value < 0) {
14024
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14025
+ }
14026
+ return value;
14027
+ }
13996
14028
  function assertCapIsUsable(operation, flags) {
13997
14029
  const exclusive = EXCLUSIVE_CAP_FIELDS[operation];
13998
14030
  if (!exclusive)
@@ -14003,6 +14035,19 @@ function assertCapIsUsable(operation, flags) {
14003
14035
  return;
14004
14036
  throw new SimApiError(`--${cap} caps a --filter match and does nothing to an explicit --${ids} list; pass one, not both`, 0);
14005
14037
  }
14038
+ var REQUIRED_SELECTORS = {
14039
+ deleteTableRows: { fields: ["filter", "rowIds"], noun: "rows to delete" }
14040
+ };
14041
+ function assertSelectorIsUsable(operation, flags) {
14042
+ const selector = REQUIRED_SELECTORS[operation];
14043
+ if (!selector)
14044
+ return;
14045
+ const [first, second] = selector.fields.map((field) => flagNameFor(operation, field));
14046
+ const given = [first, second].filter((name) => flags[camel(name)] !== undefined);
14047
+ if (given.length === 1)
14048
+ return;
14049
+ throw new SimApiError(given.length === 0 ? `--${first} or --${second} is required to choose the ${selector.noun}` : `--${first} and --${second} choose the ${selector.noun} two different ways; pass one, not both`, 0);
14050
+ }
14006
14051
  function foldRenamedFlags(operation, commandSpec, flags) {
14007
14052
  for (const [field, flag] of Object.entries(commandSpec.flags ?? {})) {
14008
14053
  if (!flag.renamedFrom?.length)
@@ -14036,6 +14081,7 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14036
14081
  }
14037
14082
  foldRenamedFlags(operation, commandSpec, requestFlags);
14038
14083
  assertCapIsUsable(operation, requestFlags);
14084
+ assertSelectorIsUsable(operation, requestFlags);
14039
14085
  if (commandSpec.confirm && !requestFlags.yes && requestFlags.dryRun !== true) {
14040
14086
  throw new SimApiError(`${commandSpec.confirm} Re-run with --yes to confirm.`, 0);
14041
14087
  }
@@ -14046,15 +14092,11 @@ async function executeOperation(operation, commandSpec, operationSpec, invocatio
14046
14092
  const hasWorkspaceField = Boolean(operationSpec.query && PROFILE_INJECTED_FIELD in operationSpec.query || operationSpec.body && PROFILE_INJECTED_FIELD in operationSpec.body);
14047
14093
  const omitsWorkspace = commandSpec.allWorkspaces && requestFlags.allWorkspaces === true;
14048
14094
  const needsWorkspace = (hasWorkspaceField || commandSpec.profileWorkspacePath === true) && !omitsWorkspace;
14049
- const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14050
14095
  const paging = cursorSlot(operationSpec);
14096
+ const pagedLimit = paging ? readPagedLimit(requestFlags.limit) : 0;
14097
+ const request = buildRequest(operation, positional, requestFlags, needsWorkspace ? client.requireWorkspace() : profile.workspaceId);
14051
14098
  if (paging) {
14052
- const limitText = String(requestFlags.limit ?? DEFAULT_LIMIT).trim();
14053
- const rawLimit = limitText === "" ? Number.NaN : Number(limitText);
14054
- if (!Number.isInteger(rawLimit) || rawLimit < 0) {
14055
- throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
14056
- }
14057
- const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
14099
+ const limit = pagedLimit === 0 ? Number.POSITIVE_INFINITY : pagedLimit;
14058
14100
  const pageSize = Math.min(Number.isFinite(limit) ? limit : DEFAULT_LIMIT, DEFAULT_LIMIT);
14059
14101
  const pageLimit = "limit" in (operationSpec[paging] ?? {}) ? { limit: pageSize } : {};
14060
14102
  const rows = [];
@@ -14139,6 +14181,14 @@ function retypeMessage(message, spellings) {
14139
14181
  continue;
14140
14182
  retyped = retyped.replaceAll(new RegExp(`\\b${field}\\b`, "g"), spelling);
14141
14183
  }
14184
+ if (retyped === message)
14185
+ return message;
14186
+ for (const field of spellings.keys()) {
14187
+ if (WIRE_IDENTIFIER.test(field))
14188
+ continue;
14189
+ if (new RegExp(`\\b${field}\\b`).test(message))
14190
+ return message;
14191
+ }
14142
14192
  return retyped;
14143
14193
  }
14144
14194
  function retypeDetails(details, spellings) {
@@ -14662,6 +14712,12 @@ Examples:
14662
14712
  $ sim chat -c 3f2a… "Which of those run on a schedule?"
14663
14713
  $ sim --output json chat "Summarize yesterday's failed runs" | jq -r '.content'
14664
14714
  `).action(async (message, options, command) => {
14715
+ if (message.trim() === "") {
14716
+ throw new SimApiError("<message> cannot be empty", 0);
14717
+ }
14718
+ if (options.conversation !== undefined && options.conversation.trim() === "") {
14719
+ throw new SimApiError("-c/--conversation cannot be empty — pass the conversation id printed on stderr after each turn", 0);
14720
+ }
14665
14721
  const { client, profile } = clientFrom(command);
14666
14722
  const workspaceId = client.requireWorkspace();
14667
14723
  const response = await client.requestRaw(V2_OPERATIONS.chat.path, {
@@ -15070,8 +15126,15 @@ function uploadMetadata(options) {
15070
15126
  }
15071
15127
  return metadata;
15072
15128
  }
15129
+ var UPLOAD_RECIPES = [
15130
+ "default",
15131
+ "plain",
15132
+ "markdown",
15133
+ "code"
15134
+ ];
15135
+ var LANGUAGE_TAG_HELP = "Document language tag: hyphen-separated letter and digit subtags, for example en or en-US";
15073
15136
  function attachKnowledgeDocumentUpload(documents) {
15074
- documents.command("upload").argument("<knowledgeBaseId>", "Knowledge base to upload into").argument("<path>", "Local file to upload").allowExcessArguments(false).description("Upload a document to a knowledge base").option("--name <name>", "Store it under a different name").option("--tag <value...>", "Document tags, in tag1 through tag7 order").option("--recipe <name>", "Document processing recipe").option("--lang <code>", "Document language code").action(async (knowledgeBaseId, path, options, command) => {
15137
+ documents.command("upload").argument("<knowledgeBaseId>", "Knowledge base to upload into").argument("<path>", "Local file to upload").allowExcessArguments(false).description("Upload a document to a knowledge base").option("--name <name>", "Store it under a different name").option("--tag <value...>", "Document tags, in tag1 through tag7 order").addOption(new Option("--recipe <name>", "Document processing recipe").choices(UPLOAD_RECIPES)).option("--lang <code>", LANGUAGE_TAG_HELP).action(async (knowledgeBaseId, path, options, command) => {
15075
15138
  const { client, profile } = clientFrom(command);
15076
15139
  const workspaceId = client.requireWorkspace();
15077
15140
  const { name, size } = await localFile(path, options.name);
@@ -15290,7 +15353,7 @@ function isTransient(error) {
15290
15353
  function nonNegativeInteger(raw, flag) {
15291
15354
  const value = Number(raw);
15292
15355
  if (!Number.isSafeInteger(value) || value < 0) {
15293
- throw new SimApiError(`${flag} must be a non-negative integer`, 0);
15356
+ throw new SimApiError(`${flag} must be a whole number of 0 or more`, 0);
15294
15357
  }
15295
15358
  return value;
15296
15359
  }
@@ -15317,7 +15380,7 @@ follow.
15317
15380
 
15318
15381
  Examples:
15319
15382
  $ sim logs follow --level error
15320
- $ sim logs follow --workflow wf_123 -n 0
15383
+ $ sim logs follow --workflow 00000000-0000-4000-8000-000000000000 -n 0
15321
15384
  $ sim --output json logs follow | jq -r '.runId'
15322
15385
  `).action(async (options, command) => {
15323
15386
  const lines = nonNegativeInteger(options.lines, "--lines");
@@ -15399,9 +15462,9 @@ async function listResources(client, config, workspaceId, folderPath, search, li
15399
15462
  const paginated = "cursor" in V2_OPERATIONS[config.resources].query;
15400
15463
  if (!paginated) {
15401
15464
  const page = await client.request(path, { query });
15402
- return page.data.slice(0, limit);
15465
+ return { items: page.data.slice(0, limit), truncated: page.data.length > limit };
15403
15466
  }
15404
- return requestAllPages(client, path, {
15467
+ return requestPages(client, path, {
15405
15468
  query,
15406
15469
  pageSize: DEFAULT_LIMIT,
15407
15470
  limit
@@ -15436,7 +15499,7 @@ function attachResourceDirectoryCommands(group, config) {
15436
15499
  group.command("ls").argument("[path]", "Folder path to list; defaults to the root folder").allowExcessArguments(false).description(`List ${config.kind} resources and child folders together`).option("--search <text>", "Filter folders and resources by name").addOption(new Option("--limit <n>", "Maximum combined items to return (0 for everything)").default(String(DEFAULT_LIMIT))).action(async (path, options, command) => {
15437
15500
  const rawLimit = Number(options.limit);
15438
15501
  if (!Number.isSafeInteger(rawLimit) || rawLimit < 0) {
15439
- throw new SimApiError("--limit must be a non-negative integer", 0);
15502
+ throw new SimApiError("--limit must be a whole number of 0 or more (0 for everything)", 0);
15440
15503
  }
15441
15504
  const limit = rawLimit === 0 ? Number.POSITIVE_INFINITY : rawLimit;
15442
15505
  const folderPath = encodeFolderPath(path ?? "/");
@@ -15446,8 +15509,10 @@ function attachResourceDirectoryCommands(group, config) {
15446
15509
  listFolders(client, config.folders, workspaceId, folderPath, options.search),
15447
15510
  listResources(client, config, workspaceId, folderPath, options.search, limit)
15448
15511
  ]);
15449
- const entries = entriesFor(config, folders, resources);
15450
- printList(profile.output, entries.slice(0, limit), COLUMNS2);
15512
+ const entries = entriesFor(config, folders, resources.items);
15513
+ const shown = entries.slice(0, limit);
15514
+ writeCursorTruncation(shown.length, resources.truncated || entries.length > limit);
15515
+ printList(profile.output, shown, COLUMNS2);
15451
15516
  });
15452
15517
  group.command("mkdir").argument("<path>", "Folder path to create; the leading / is optional").allowExcessArguments(false).description(`Create a ${config.kind} directory at a path`).action(async (path, _options, command) => {
15453
15518
  const { client, profile } = clientFrom(command);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim",
3
- "version": "2.1.3-preview.50.1",
3
+ "version": "2.1.3-preview.51.1",
4
4
  "description": "Sim CLI - talk to the Sim API from your terminal",
5
5
  "type": "module",
6
6
  "bin": {