sim 2.1.0-dev.24.1 → 2.1.0-dev.32.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 -7
  2. package/dist/index.js +520 -33
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,9 +12,9 @@ Full documentation: **https://docs.sim.ai/cli**
12
12
 
13
13
  ## Profiles
14
14
 
15
- Profiles work like the AWS CLI: one identity and one set of defaults per named
16
- profile, selected with `-P`, `--profile`, or `SIM_PROFILE`. This is what lets you keep
17
- production and a local dev stack side by side without re-authenticating.
15
+ Profiles work like the AWS CLI and are selected with `-P`, `--profile`, or
16
+ `SIM_PROFILE`. A profile normally owns one identity and one set of defaults; a
17
+ workspace profile can instead share a stored identity through `auth_profile`.
18
18
 
19
19
  Non-secret settings live in `~/.sim/config`:
20
20
 
@@ -27,6 +27,10 @@ output = table
27
27
  [profile dev]
28
28
  endpoint = http://localhost:3000
29
29
  workspace = ws_local
30
+
31
+ [profile acme]
32
+ auth_profile = default
33
+ workspace = ws_acme
30
34
  ```
31
35
 
32
36
  Keys live in `~/.sim/credentials`, written `0600`:
@@ -45,7 +49,8 @@ The section-naming asymmetry — `[profile dev]` in config, `[dev]` in credentia
45
49
  ```bash
46
50
  sim configure --set-endpoint http://localhost:3000 --profile dev
47
51
  sim configure --set-workspace ws_local --profile dev
48
- sim profiles # list them; * marks the active one
52
+ sim profiles # list them; * marks the active one
53
+ sim profile add acme --workspace ws_acme # share the active stored login
49
54
  sim whoami # resolved values, where each came from, and whether they work
50
55
  ```
51
56
 
@@ -62,7 +67,7 @@ Each setting resolves independently, first match wins:
62
67
  indefinitely) and `SIM_DEBUG=1` traces requests to stderr. Node ignores
63
68
  `HTTPS_PROXY` unless `NODE_USE_ENV_PROXY=1` is also set, on Node 22.21+ or
64
69
  24.5+; the CLI warns when a proxy is configured but will not be used.
65
- | 3 | `~/.sim/config` / `~/.sim/credentials` for the selected profile |
70
+ | 3 | `~/.sim/config` for the selected profile and credentials for its `auth_profile`, when set |
66
71
  | 4 | Built-in default (`https://www.sim.ai`, `table`) |
67
72
 
68
73
  Formats are listed under [Output formats](#output-formats).
@@ -112,8 +117,11 @@ the key can access.
112
117
  `sim login --workspace <id>` preselects a workspace in the picker, and an
113
118
  existing profile's workspace preselects itself on re-login.
114
119
 
115
- `sim logout` removes the stored key. It does not revoke it — do that in
116
- Settings API keys.
120
+ `sim logout` removes the stored key. A shared workspace profile cannot remove
121
+ its authentication profile's key; use `sim logout --all --profile <name>` to
122
+ remove only the workspace profile. An authentication profile cannot be removed
123
+ entirely while workspace profiles reference it. Logging out does not revoke a
124
+ key — do that in Settings → API keys.
117
125
 
118
126
  ## Commands
119
127
 
@@ -147,6 +155,7 @@ sim logs get <runId>
147
155
  sim audit-logs list --organization <organizationId> [--all-workspaces]
148
156
  sim audit-logs get <id> --organization <organizationId>
149
157
 
158
+ sim workspaces list
150
159
  sim workspaces get
151
160
  sim workspaces members
152
161
 
package/dist/index.js CHANGED
@@ -2432,6 +2432,33 @@ function readConfigProfile(profile) {
2432
2432
  function readCredentialsProfile(profile) {
2433
2433
  return getSection(readIni(credentialsPath()), profile) ?? {};
2434
2434
  }
2435
+ function resolveAuthenticationProfileName(profile) {
2436
+ const config = readConfigProfile(profile);
2437
+ if (!Object.hasOwn(config, "auth_profile"))
2438
+ return profile;
2439
+ const authProfile = config.auth_profile.trim();
2440
+ if (!authProfile) {
2441
+ throw new ProfileConfigError(`Profile "${profile}" has an empty auth_profile.`);
2442
+ }
2443
+ if (authProfile === profile) {
2444
+ throw new ProfileConfigError(`Profile "${profile}" cannot use itself as auth_profile. Remove the auth_profile setting instead.`);
2445
+ }
2446
+ if (Object.hasOwn(config, "endpoint")) {
2447
+ throw new ProfileConfigError(`Profile "${profile}" cannot set both auth_profile and endpoint. Set the endpoint on authentication profile "${authProfile}".`);
2448
+ }
2449
+ if (readCredentialsProfile(profile).api_key) {
2450
+ throw new ProfileConfigError(`Profile "${profile}" cannot set both auth_profile and its own API key. Remove one of them.`);
2451
+ }
2452
+ const authConfig = readConfigProfile(authProfile);
2453
+ const credentials = readCredentialsProfile(authProfile);
2454
+ if (Object.keys(authConfig).length === 0 && Object.keys(credentials).length === 0) {
2455
+ throw new ProfileConfigError(`Profile "${profile}" references missing auth_profile "${authProfile}".`);
2456
+ }
2457
+ if (Object.hasOwn(authConfig, "auth_profile")) {
2458
+ throw new ProfileConfigError(`Profile "${profile}" references auth_profile "${authProfile}", which also has auth_profile set. Authentication profile references cannot be chained.`);
2459
+ }
2460
+ return authProfile;
2461
+ }
2435
2462
  function listProfiles() {
2436
2463
  const names = new Set;
2437
2464
  for (const section of listSections(readIni(configPath()))) {
@@ -2445,6 +2472,9 @@ function listProfiles() {
2445
2472
  }
2446
2473
  return [...names].sort();
2447
2474
  }
2475
+ function listAuthenticationDependents(authProfile) {
2476
+ return listProfiles().filter((profile) => profile !== authProfile && readConfigProfile(profile).auth_profile?.trim() === authProfile);
2477
+ }
2448
2478
  function writeConfigProfile(profile, values) {
2449
2479
  const doc = readIni(configPath());
2450
2480
  setSectionValues(doc, configSectionName(profile), values);
@@ -2489,11 +2519,13 @@ function resolve(candidates, fallback, fallbackSource) {
2489
2519
  function resolveProfile(overrides = {}) {
2490
2520
  const name = overrides.profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
2491
2521
  const config = readConfigProfile(name);
2492
- const credentials = readCredentialsProfile(name);
2522
+ const authProfile = resolveAuthenticationProfileName(name);
2523
+ const authConfig = authProfile === name ? config : readConfigProfile(authProfile);
2524
+ const credentials = readCredentialsProfile(authProfile);
2493
2525
  const endpoint = resolve([
2494
2526
  ["flag", overrides.endpoint],
2495
2527
  ["env", process.env.SIM_ENDPOINT],
2496
- ["config", config.endpoint]
2528
+ ["config", authConfig.endpoint]
2497
2529
  ], DEFAULT_ENDPOINT, "default");
2498
2530
  const apiKey = resolve([
2499
2531
  ["flag", overrides.apiKey],
@@ -6759,6 +6791,40 @@ var V2_OPERATIONS = {
6759
6791
  }
6760
6792
  }
6761
6793
  },
6794
+ createKnowledgeConnector: {
6795
+ method: "POST",
6796
+ path: "/api/v2/knowledge/[id]/connectors",
6797
+ pathParams: ["id"],
6798
+ pathParamDocs: { id: "Unique knowledge base identifier." },
6799
+ responseMode: "json",
6800
+ summary: "Create Knowledge Connector",
6801
+ body: {
6802
+ workspaceId: {
6803
+ kind: "string",
6804
+ required: true,
6805
+ describe: "Workspace that owns the knowledge base."
6806
+ },
6807
+ connectorType: { kind: "string", required: true, describe: "Registered connector type." },
6808
+ credentialId: {
6809
+ kind: "string",
6810
+ describe: "OAuth credential identifier for connectors that require OAuth."
6811
+ },
6812
+ apiKey: {
6813
+ kind: "string",
6814
+ describe: "Write-only API key for connectors that use API-key authentication."
6815
+ },
6816
+ sourceConfig: {
6817
+ kind: "object",
6818
+ required: true,
6819
+ describe: "Connector-specific source selection and filtering configuration."
6820
+ },
6821
+ syncIntervalMinutes: {
6822
+ kind: "integer",
6823
+ default: 1440,
6824
+ describe: "Scheduled synchronization interval in minutes; zero disables scheduling."
6825
+ }
6826
+ }
6827
+ },
6762
6828
  createKnowledgeDocumentUpload: {
6763
6829
  method: "POST",
6764
6830
  path: "/api/v2/knowledge/[id]/documents/uploads",
@@ -6923,22 +6989,11 @@ var V2_OPERATIONS = {
6923
6989
  kind: "string",
6924
6990
  describe: "Required only when provider discovery requests a client-generated ID."
6925
6991
  },
6926
- serviceAccountJson: {
6992
+ credentials: {
6927
6993
  kind: "string",
6928
- describe: "Write-only Google service-account JSON key."
6929
- },
6930
- apiToken: { kind: "string", describe: "Write-only provider API token." },
6931
- domain: { kind: "string", describe: "Provider account domain." },
6932
- signingSecret: { kind: "string", describe: "Write-only webhook signing secret." },
6933
- botToken: { kind: "string", describe: "Write-only bot token." },
6934
- clientId: { kind: "string", describe: "OAuth client identifier." },
6935
- clientSecret: { kind: "string", describe: "Write-only OAuth client secret." },
6936
- certificateId: { kind: "string", describe: "Provider certificate mapping identifier." },
6937
- orgId: { kind: "string", describe: "Provider organization ID." },
6938
- dataCenter: { kind: "string", describe: "Provider data center." },
6939
- authMethod: { kind: "string", describe: "Provider authentication method." },
6940
- privateKey: { kind: "string", describe: "Write-only PEM private key." },
6941
- username: { kind: "string", describe: "Provider run-as username." }
6994
+ required: true,
6995
+ describe: "Write-only JSON object string containing the fields declared by credential-provider discovery."
6996
+ }
6942
6997
  }
6943
6998
  },
6944
6999
  createSkill: {
@@ -7206,6 +7261,28 @@ var V2_OPERATIONS = {
7206
7261
  }
7207
7262
  }
7208
7263
  },
7264
+ deleteKnowledgeConnector: {
7265
+ method: "DELETE",
7266
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]",
7267
+ pathParams: ["id", "connectorId"],
7268
+ pathParamDocs: {
7269
+ id: "Knowledge base that owns the connector.",
7270
+ connectorId: "Connector selected for the operation."
7271
+ },
7272
+ responseMode: "json",
7273
+ summary: "Delete Knowledge Connector",
7274
+ query: {
7275
+ workspaceId: {
7276
+ kind: "string",
7277
+ required: true,
7278
+ describe: "Workspace that owns the knowledge base."
7279
+ },
7280
+ deleteDocuments: {
7281
+ kind: "boolean",
7282
+ describe: "Also permanently delete documents produced by this connector."
7283
+ }
7284
+ }
7285
+ },
7209
7286
  deleteKnowledgeDocument: {
7210
7287
  method: "DELETE",
7211
7288
  path: "/api/v2/knowledge/[id]/documents/[documentId]",
@@ -7632,6 +7709,24 @@ var V2_OPERATIONS = {
7632
7709
  }
7633
7710
  }
7634
7711
  },
7712
+ getKnowledgeConnector: {
7713
+ method: "GET",
7714
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]",
7715
+ pathParams: ["id", "connectorId"],
7716
+ pathParamDocs: {
7717
+ id: "Knowledge base that owns the connector.",
7718
+ connectorId: "Connector selected for the operation."
7719
+ },
7720
+ responseMode: "json",
7721
+ summary: "Get Knowledge Connector",
7722
+ query: {
7723
+ workspaceId: {
7724
+ kind: "string",
7725
+ required: true,
7726
+ describe: "Workspace that owns the knowledge base."
7727
+ }
7728
+ }
7729
+ },
7635
7730
  getKnowledgeDocument: {
7636
7731
  method: "GET",
7637
7732
  path: "/api/v2/knowledge/[id]/documents/[documentId]",
@@ -7799,6 +7894,24 @@ var V2_OPERATIONS = {
7799
7894
  responseMode: "json",
7800
7895
  summary: "Get Workspace"
7801
7896
  },
7897
+ grantSkillEditor: {
7898
+ method: "POST",
7899
+ path: "/api/v2/skills/[id]/editors",
7900
+ pathParams: ["id"],
7901
+ pathParamDocs: {
7902
+ id: "Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`."
7903
+ },
7904
+ responseMode: "json",
7905
+ summary: "Grant Skill Editor",
7906
+ body: {
7907
+ workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
7908
+ email: {
7909
+ kind: "string",
7910
+ required: true,
7911
+ describe: "Email address of a current workspace member."
7912
+ }
7913
+ }
7914
+ },
7802
7915
  importWorkflow: {
7803
7916
  method: "POST",
7804
7917
  path: "/api/v2/workflows/import",
@@ -8071,7 +8184,25 @@ var V2_OPERATIONS = {
8071
8184
  },
8072
8185
  folderPath: {
8073
8186
  kind: "string",
8074
- describe: "Restrict results to files directly inside this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error."
8187
+ 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."
8188
+ },
8189
+ recursive: {
8190
+ kind: "enum",
8191
+ values: [
8192
+ "true",
8193
+ "1",
8194
+ "yes",
8195
+ "on",
8196
+ "y",
8197
+ "enabled",
8198
+ "false",
8199
+ "0",
8200
+ "no",
8201
+ "off",
8202
+ "n",
8203
+ "disabled"
8204
+ ],
8205
+ 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."
8075
8206
  },
8076
8207
  scope: {
8077
8208
  kind: "enum",
@@ -8149,6 +8280,73 @@ var V2_OPERATIONS = {
8149
8280
  }
8150
8281
  }
8151
8282
  },
8283
+ listKnowledgeConnectorDocuments: {
8284
+ method: "GET",
8285
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]/documents",
8286
+ pathParams: ["id", "connectorId"],
8287
+ pathParamDocs: {
8288
+ id: "Knowledge base that owns the connector.",
8289
+ connectorId: "Connector selected for the operation."
8290
+ },
8291
+ responseMode: "json",
8292
+ summary: "List Knowledge Connector Documents",
8293
+ query: {
8294
+ workspaceId: {
8295
+ kind: "string",
8296
+ required: true,
8297
+ describe: "Workspace that owns the knowledge base."
8298
+ },
8299
+ includeExcluded: {
8300
+ kind: "boolean",
8301
+ describe: "Include documents explicitly excluded by a user."
8302
+ },
8303
+ limit: {
8304
+ kind: "integer",
8305
+ default: 50,
8306
+ describe: "Maximum connector documents to return per page. Must be a whole number from 1 to 100. Defaults to 50."
8307
+ },
8308
+ cursor: {
8309
+ kind: "string",
8310
+ describe: "Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor."
8311
+ }
8312
+ }
8313
+ },
8314
+ listKnowledgeConnectors: {
8315
+ method: "GET",
8316
+ path: "/api/v2/knowledge/[id]/connectors",
8317
+ pathParams: ["id"],
8318
+ pathParamDocs: { id: "Unique knowledge base identifier." },
8319
+ responseMode: "json",
8320
+ summary: "List Knowledge Connectors",
8321
+ query: {
8322
+ workspaceId: {
8323
+ kind: "string",
8324
+ required: true,
8325
+ describe: "Workspace that owns the knowledge base."
8326
+ },
8327
+ sortBy: {
8328
+ kind: "enum",
8329
+ values: ["connectorType", "createdAt", "updatedAt"],
8330
+ default: "createdAt",
8331
+ describe: "Field used to sort the result."
8332
+ },
8333
+ sortOrder: {
8334
+ kind: "enum",
8335
+ values: ["asc", "desc"],
8336
+ default: "desc",
8337
+ describe: "Sort direction."
8338
+ },
8339
+ limit: {
8340
+ kind: "integer",
8341
+ default: 50,
8342
+ describe: "Maximum connectors to return per page. Must be a whole number from 1 to 100. Defaults to 50."
8343
+ },
8344
+ cursor: {
8345
+ kind: "string",
8346
+ describe: "Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor."
8347
+ }
8348
+ }
8349
+ },
8152
8350
  listKnowledgeDocuments: {
8153
8351
  method: "GET",
8154
8352
  path: "/api/v2/knowledge/[id]/documents",
@@ -8444,6 +8642,40 @@ var V2_OPERATIONS = {
8444
8642
  }
8445
8643
  }
8446
8644
  },
8645
+ listSkillEditors: {
8646
+ method: "GET",
8647
+ path: "/api/v2/skills/[id]/editors",
8648
+ pathParams: ["id"],
8649
+ pathParamDocs: {
8650
+ id: "Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`."
8651
+ },
8652
+ responseMode: "json",
8653
+ summary: "List Skill Editors",
8654
+ query: {
8655
+ workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
8656
+ sortBy: {
8657
+ kind: "enum",
8658
+ values: ["email", "name"],
8659
+ default: "email",
8660
+ describe: "Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order."
8661
+ },
8662
+ sortOrder: {
8663
+ kind: "enum",
8664
+ values: ["asc", "desc"],
8665
+ default: "asc",
8666
+ describe: "Sort direction."
8667
+ },
8668
+ limit: {
8669
+ kind: "integer",
8670
+ default: 50,
8671
+ describe: "Maximum skill editors to return per page. Must be a whole number from 1 to 100. Defaults to 50."
8672
+ },
8673
+ cursor: {
8674
+ kind: "string",
8675
+ describe: "Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor."
8676
+ }
8677
+ }
8678
+ },
8447
8679
  listSkills: {
8448
8680
  method: "GET",
8449
8681
  path: "/api/v2/skills",
@@ -8756,6 +8988,36 @@ var V2_OPERATIONS = {
8756
8988
  }
8757
8989
  }
8758
8990
  },
8991
+ listWorkspaces: {
8992
+ method: "GET",
8993
+ path: "/api/v2/workspaces",
8994
+ pathParams: [],
8995
+ responseMode: "json",
8996
+ summary: "List Workspaces",
8997
+ query: {
8998
+ sortBy: {
8999
+ kind: "enum",
9000
+ values: ["name", "createdAt", "updatedAt"],
9001
+ default: "createdAt",
9002
+ describe: "Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order."
9003
+ },
9004
+ sortOrder: {
9005
+ kind: "enum",
9006
+ values: ["asc", "desc"],
9007
+ default: "desc",
9008
+ describe: "Sort direction."
9009
+ },
9010
+ limit: {
9011
+ kind: "integer",
9012
+ default: 50,
9013
+ describe: "Maximum workspaces to return per page. Must be a whole number from 1 to 100. Defaults to 50."
9014
+ },
9015
+ cursor: {
9016
+ kind: "string",
9017
+ describe: "Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor."
9018
+ }
9019
+ }
9020
+ },
8759
9021
  moveFileItems: {
8760
9022
  method: "POST",
8761
9023
  path: "/api/v2/files/move",
@@ -8782,7 +9044,7 @@ var V2_OPERATIONS = {
8782
9044
  workspaceId: { kind: "string", required: true, describe: "Unique workspace identifier." },
8783
9045
  predicate: {
8784
9046
  kind: "unknown",
8785
- describe: 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.'
9047
+ describe: 'A single `{ field, op, value }` condition or a recursive `all`/`any` group; either form is normalized to a grouped predicate after validation. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.'
8786
9048
  },
8787
9049
  sort: { kind: "array", describe: "Ordered table-row sort specification." },
8788
9050
  limit: {
@@ -8803,7 +9065,7 @@ var V2_OPERATIONS = {
8803
9065
  workspaceId: { kind: "string", required: true, describe: "Unique workspace identifier." },
8804
9066
  predicate: {
8805
9067
  kind: "unknown",
8806
- describe: 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.'
9068
+ describe: 'A single `{ field, op, value }` condition or a recursive `all`/`any` group; either form is normalized to a grouped predicate after validation. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.'
8807
9069
  }
8808
9070
  }
8809
9071
  },
@@ -8914,6 +9176,24 @@ var V2_OPERATIONS = {
8914
9176
  input: { kind: "unknown", describe: "Input supplied to the paused workflow block." }
8915
9177
  }
8916
9178
  },
9179
+ revokeSkillEditor: {
9180
+ method: "DELETE",
9181
+ path: "/api/v2/skills/[id]/editors",
9182
+ pathParams: ["id"],
9183
+ pathParamDocs: {
9184
+ id: "Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`."
9185
+ },
9186
+ responseMode: "json",
9187
+ summary: "Revoke Skill Editor",
9188
+ query: {
9189
+ workspaceId: { kind: "string", required: true, describe: "Workspace that owns the skill." },
9190
+ email: {
9191
+ kind: "string",
9192
+ required: true,
9193
+ describe: "Email address of a current workspace member."
9194
+ }
9195
+ }
9196
+ },
8917
9197
  rollbackWorkflow: {
8918
9198
  method: "POST",
8919
9199
  path: "/api/v2/workflows/[id]/rollback",
@@ -9053,6 +9333,29 @@ var V2_OPERATIONS = {
9053
9333
  }
9054
9334
  }
9055
9335
  },
9336
+ syncKnowledgeConnector: {
9337
+ method: "POST",
9338
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]/sync",
9339
+ pathParams: ["id", "connectorId"],
9340
+ pathParamDocs: {
9341
+ id: "Knowledge base that owns the connector.",
9342
+ connectorId: "Connector selected for the operation."
9343
+ },
9344
+ responseMode: "json",
9345
+ summary: "Sync Knowledge Connector",
9346
+ body: {
9347
+ workspaceId: {
9348
+ kind: "string",
9349
+ required: true,
9350
+ describe: "Workspace that owns the knowledge base."
9351
+ },
9352
+ rehydrate: {
9353
+ kind: "boolean",
9354
+ default: false,
9355
+ describe: "Re-fetch and re-index every existing connector document."
9356
+ }
9357
+ }
9358
+ },
9056
9359
  tableExportDownload: {
9057
9360
  method: "GET",
9058
9361
  path: "/api/v2/tables/exports/[exportId]/download",
@@ -9135,6 +9438,66 @@ var V2_OPERATIONS = {
9135
9438
  folderPath: { kind: "string", describe: "New containing-folder path." }
9136
9439
  }
9137
9440
  },
9441
+ updateKnowledgeConnector: {
9442
+ method: "PATCH",
9443
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]",
9444
+ pathParams: ["id", "connectorId"],
9445
+ pathParamDocs: {
9446
+ id: "Knowledge base that owns the connector.",
9447
+ connectorId: "Connector selected for the operation."
9448
+ },
9449
+ responseMode: "json",
9450
+ summary: "Update Knowledge Connector",
9451
+ body: {
9452
+ workspaceId: {
9453
+ kind: "string",
9454
+ required: true,
9455
+ describe: "Workspace that owns the knowledge base."
9456
+ },
9457
+ sourceConfig: {
9458
+ kind: "object",
9459
+ describe: "Replacement source selection and filtering configuration."
9460
+ },
9461
+ syncIntervalMinutes: {
9462
+ kind: "integer",
9463
+ describe: "New scheduled synchronization interval in minutes."
9464
+ },
9465
+ status: {
9466
+ kind: "enum",
9467
+ values: ["active", "paused"],
9468
+ describe: "New connector state."
9469
+ }
9470
+ }
9471
+ },
9472
+ updateKnowledgeConnectorDocuments: {
9473
+ method: "PATCH",
9474
+ path: "/api/v2/knowledge/[id]/connectors/[connectorId]/documents",
9475
+ pathParams: ["id", "connectorId"],
9476
+ pathParamDocs: {
9477
+ id: "Knowledge base that owns the connector.",
9478
+ connectorId: "Connector selected for the operation."
9479
+ },
9480
+ responseMode: "json",
9481
+ summary: "Update Knowledge Connector Documents",
9482
+ body: {
9483
+ workspaceId: {
9484
+ kind: "string",
9485
+ required: true,
9486
+ describe: "Workspace that owns the knowledge base."
9487
+ },
9488
+ operation: {
9489
+ kind: "enum",
9490
+ required: true,
9491
+ values: ["restore", "exclude"],
9492
+ describe: "Whether to restore or exclude the selected documents."
9493
+ },
9494
+ documentIds: {
9495
+ kind: "array",
9496
+ required: true,
9497
+ describe: "Connector document identifiers to update."
9498
+ }
9499
+ }
9500
+ },
9138
9501
  updateKnowledgeDocument: {
9139
9502
  method: "PATCH",
9140
9503
  path: "/api/v2/knowledge/[id]/documents/[documentId]",
@@ -9463,6 +9826,8 @@ var V2_OPERATIONS = {
9463
9826
  };
9464
9827
 
9465
9828
  // src/commands/auth.ts
9829
+ var PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
9830
+ var MAX_INTERACTIVE_WORKSPACES = 1000;
9466
9831
  function openBrowser(url) {
9467
9832
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : [process.platform === "darwin" ? "open" : "xdg-open", [url]];
9468
9833
  try {
@@ -9498,9 +9863,91 @@ async function confirmProfileOverwrite(profileName) {
9498
9863
  prompt.close();
9499
9864
  }
9500
9865
  }
9866
+ function selectedProfileName(command) {
9867
+ return globalsOf(command).profile || process.env.SIM_PROFILE || DEFAULT_PROFILE;
9868
+ }
9869
+ function validateNewProfileName(profileName) {
9870
+ if (!PROFILE_NAME_PATTERN.test(profileName)) {
9871
+ throw new SimApiError(`Invalid profile name "${profileName}". Use letters, numbers, dots, underscores, or hyphens, starting with a letter or number.`, 0);
9872
+ }
9873
+ if (listProfiles().includes(profileName)) {
9874
+ throw new SimApiError(`Profile "${profileName}" already exists. Remove it first with: sim logout --all --profile ${profileName}`, 0);
9875
+ }
9876
+ }
9877
+ function requireStoredAuthentication(profile) {
9878
+ const authProfile = resolveAuthenticationProfileName(profile.name);
9879
+ const storedKey = readCredentialsProfile(authProfile).api_key;
9880
+ if (profile.sources.apiKey !== "credentials" || !storedKey) {
9881
+ throw new SimApiError(`Cannot create a shared profile from "${profile.name}": the active API key is not stored. Run: sim login --profile ${authProfile}`, 0);
9882
+ }
9883
+ if (profile.sources.endpoint === "flag" || profile.sources.endpoint === "env") {
9884
+ throw new SimApiError(`Cannot create a shared profile from "${profile.name}": the active endpoint comes from ${profile.sources.endpoint}. Save it with: sim configure --profile ${authProfile} --set-endpoint ${profile.endpoint}`, 0);
9885
+ }
9886
+ return authProfile;
9887
+ }
9888
+ async function getWorkspaceById(client, workspaceId) {
9889
+ const operation = V2_OPERATIONS.getWorkspace;
9890
+ const response = await client.request(resolvePath(operation.path, { workspaceId }), { method: operation.method });
9891
+ return response.data;
9892
+ }
9893
+ async function chooseWorkspace(client) {
9894
+ if (!process.stdin.isTTY) {
9895
+ throw new SimApiError("No workspace provided. Pass --workspace <id> when creating a profile non-interactively.", 0);
9896
+ }
9897
+ const operation = V2_OPERATIONS.listWorkspaces;
9898
+ const workspaces = await requestAllPages(client, operation.path, {
9899
+ method: operation.method,
9900
+ query: { sortBy: "name", sortOrder: "asc" },
9901
+ pageSize: 100,
9902
+ limit: MAX_INTERACTIVE_WORKSPACES + 1
9903
+ });
9904
+ if (workspaces.length === 0) {
9905
+ throw new SimApiError("The active API key cannot access any workspaces.", 0);
9906
+ }
9907
+ if (workspaces.length > MAX_INTERACTIVE_WORKSPACES) {
9908
+ throw new SimApiError(`The active API key can access more than ${MAX_INTERACTIVE_WORKSPACES} workspaces, which is too many to show interactively. Pass --workspace <id> instead.`, 0);
9909
+ }
9910
+ console.log(`
9911
+ Available workspaces:`);
9912
+ for (const [index, workspace] of workspaces.entries()) {
9913
+ console.log(` ${index + 1}) ${safeOneLine(workspace.name)} (${workspace.id})`);
9914
+ }
9915
+ const prompt = createInterface({ input: process.stdin, output: process.stderr });
9916
+ try {
9917
+ const answer = await prompt.question(`Choose a workspace [1-${workspaces.length}]: `);
9918
+ const selected = Number(answer.trim());
9919
+ if (!Number.isInteger(selected) || selected < 1 || selected > workspaces.length) {
9920
+ throw new SimApiError(`Invalid workspace selection "${safeOneLine(answer)}". Choose a number from 1 to ${workspaces.length}.`, 0);
9921
+ }
9922
+ return workspaces[selected - 1];
9923
+ } finally {
9924
+ prompt.close();
9925
+ }
9926
+ }
9927
+ function addProfileCommand() {
9928
+ return new Command("add").description("Add a workspace profile that shares the active stored login").argument("<name>", "Name for the new profile").option("-w, --workspace <id>", "Existing workspace to use; omit for an interactive picker").action(async (profileName, _options, command) => {
9929
+ validateNewProfileName(profileName);
9930
+ const { client, profile } = clientFrom(command);
9931
+ const authProfile = requireStoredAuthentication(profile);
9932
+ const workspaceId = globalsOf(command).workspace;
9933
+ const workspace = workspaceId ? await getWorkspaceById(client, workspaceId) : await chooseWorkspace(client);
9934
+ writeConfigProfile(profileName, {
9935
+ auth_profile: authProfile,
9936
+ workspace: workspace.id
9937
+ });
9938
+ console.log(source_default.green(`✓ Added profile "${profileName}" in ${configPath()}`));
9939
+ console.log(` Workspace: ${safeOneLine(workspace.name)} (${workspace.id})`);
9940
+ console.log(` Authentication: ${authProfile}`);
9941
+ console.log(source_default.dim(` Try: sim --profile ${profileName} whoami`));
9942
+ });
9943
+ }
9501
9944
  function loginCommand() {
9502
9945
  return new Command("login").description("Authorize this terminal and store an API key for the profile").option("--scope <scope>", "Key space to mint from: platform or copilot", "platform").option("--no-browser", "Print the URL instead of opening a browser").option("-y, --yes", "Overwrite an existing profile without prompting").action(async (options, command) => {
9503
9946
  const profile = profileFrom(command);
9947
+ const authProfile = resolveAuthenticationProfileName(profile.name);
9948
+ if (authProfile !== profile.name) {
9949
+ throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Run: sim login --profile ${authProfile}`, 0);
9950
+ }
9504
9951
  if (options.scope !== "platform" && options.scope !== "copilot") {
9505
9952
  throw new SimApiError(`Unknown scope "${options.scope}". Use platform or copilot.`, 0);
9506
9953
  }
@@ -9547,16 +9994,25 @@ Waiting for approval…`));
9547
9994
  }
9548
9995
  function logoutCommand() {
9549
9996
  return new Command("logout").description("Remove the profile's stored API key").option("--all", "Remove the profile entirely, including its settings").action((options, command) => {
9550
- const profile = profileFrom(command);
9551
9997
  if (options.all) {
9552
- const removed = deleteProfile(profile.name);
9998
+ const profileName = selectedProfileName(command);
9999
+ const dependents = listAuthenticationDependents(profileName);
10000
+ if (dependents.length > 0) {
10001
+ throw new SimApiError(`Cannot remove authentication profile "${profileName}" because it is used by: ${dependents.join(", ")}. Remove those profiles first.`, 0);
10002
+ }
10003
+ const removed = deleteProfile(profileName);
9553
10004
  if (!removed.config && !removed.credentials) {
9554
- console.log(source_default.dim(`Nothing stored for profile "${profile.name}".`));
10005
+ console.log(source_default.dim(`Nothing stored for profile "${profileName}".`));
9555
10006
  return;
9556
10007
  }
9557
- console.log(source_default.green(`✓ Removed profile "${profile.name}".`));
10008
+ console.log(source_default.green(`✓ Removed profile "${profileName}".`));
9558
10009
  return;
9559
10010
  }
10011
+ const profile = profileFrom(command);
10012
+ const authProfile = resolveAuthenticationProfileName(profile.name);
10013
+ if (authProfile !== profile.name) {
10014
+ throw new SimApiError(`Profile "${profile.name}" shares authentication with "${authProfile}". Log out of the authentication profile instead: sim logout --profile ${authProfile}`, 0);
10015
+ }
9560
10016
  if (!readCredentialsProfile(profile.name).api_key) {
9561
10017
  console.log(source_default.dim(`No stored key for profile "${profile.name}".`));
9562
10018
  return;
@@ -9664,27 +10120,38 @@ function whoamiCommand() {
9664
10120
  });
9665
10121
  }
9666
10122
  function profilesCommand() {
9667
- return new Command("profiles").alias("profile").description("List the profiles defined in the config and credentials files").action((_options, command) => {
10123
+ const command = new Command("profiles").alias("profile").description("List profiles or add a workspace profile that shares a stored login");
10124
+ const printProfiles = (_options, actionCommand) => {
9668
10125
  const profiles = listProfiles();
9669
10126
  if (profiles.length === 0) {
9670
10127
  console.log(source_default.dim("No profiles yet. Run: sim login"));
9671
10128
  return;
9672
10129
  }
9673
- const active = profileFrom(command).name;
10130
+ const active = selectedProfileName(actionCommand);
9674
10131
  for (const name of profiles) {
9675
10132
  const marker = name === active ? source_default.green("*") : " ";
9676
- const hasKey = Boolean(readCredentialsProfile(name).api_key);
9677
- console.log(`${marker} ${name}${hasKey ? "" : source_default.dim(" (no key)")}`);
10133
+ const authProfile = resolveAuthenticationProfileName(name);
10134
+ const hasKey = Boolean(readCredentialsProfile(authProfile).api_key);
10135
+ const authentication = authProfile === name ? "" : source_default.dim(` (auth: ${authProfile})`);
10136
+ console.log(`${marker} ${name}${hasKey ? "" : source_default.dim(" (no key)")}${authentication}`);
9678
10137
  }
9679
- });
10138
+ };
10139
+ command.action(printProfiles);
10140
+ command.addCommand(new Command("list").description("List configured profiles").action(printProfiles));
10141
+ command.addCommand(addProfileCommand());
10142
+ return command;
9680
10143
  }
9681
10144
 
9682
10145
  // src/commands/configure.ts
9683
10146
  function configureCommand() {
9684
10147
  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) => {
9685
10148
  const profile = profileFrom(command);
10149
+ const authProfile = resolveAuthenticationProfileName(profile.name);
9686
10150
  const updates = {};
9687
10151
  if (options.setEndpoint) {
10152
+ if (authProfile !== profile.name) {
10153
+ throw new SimApiError(`Profile "${profile.name}" shares its endpoint with authentication profile "${authProfile}". Run: sim configure --profile ${authProfile} --set-endpoint ${options.setEndpoint}`, 0);
10154
+ }
9688
10155
  updates.endpoint = normalizeEndpoint(options.setEndpoint, "--set-endpoint");
9689
10156
  }
9690
10157
  if (options.setWorkspace)
@@ -9699,6 +10166,9 @@ function configureCommand() {
9699
10166
  if (!["endpoint", "workspace", "output"].includes(key)) {
9700
10167
  throw new SimApiError(`Cannot unset "${key}". Use endpoint, workspace, or output.`, 0);
9701
10168
  }
10169
+ if (key === "endpoint" && authProfile !== profile.name) {
10170
+ throw new SimApiError(`Profile "${profile.name}" shares its endpoint with authentication profile "${authProfile}". Run: sim configure --profile ${authProfile} --unset endpoint`, 0);
10171
+ }
9702
10172
  updates[key] = null;
9703
10173
  }
9704
10174
  if (Object.keys(updates).length === 0) {
@@ -9723,6 +10193,7 @@ import { existsSync as existsSync2, readFileSync as readFileSync3, readSync } fr
9723
10193
  // src/contract/commands.ts
9724
10194
  var TABLE_NAME_HELP = "Identifier: letters, numbers, and underscores; cannot start with a number";
9725
10195
  var TABLE_FILTER_HELP = 'Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull';
10196
+ var TABLE_READ_FILTER_HELP = 'Condition: {"field":"status","op":"eq","value":"active"}. Groups: {"all":[{"field":"status","op":"eq","value":"active"}]} or {"any":[{"field":"status","op":"eq","value":"active"}]}; group entries may also be nested groups. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull';
9726
10197
  var TABLE_SORT_HELP = 'Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc)';
9727
10198
  var CUSTOM_TOOL_SCHEMA_HELP = 'OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}}';
9728
10199
  var FOLDER_PATH_INPUT = {
@@ -9825,6 +10296,15 @@ var CLI_CONTRACT = {
9825
10296
  selectAll: { boolean: true, describe: "Apply to every document in the knowledge base" }
9826
10297
  }
9827
10298
  },
10299
+ listKnowledgeConnectorDocuments: {
10300
+ command: "knowledge connectors documents list"
10301
+ },
10302
+ updateKnowledgeConnectorDocuments: {
10303
+ command: "knowledge connectors documents update",
10304
+ flags: {
10305
+ documentIds: { name: "document", list: true }
10306
+ }
10307
+ },
9828
10308
  undeployWorkflow: {
9829
10309
  command: "workflows undeploy",
9830
10310
  describe: "Take a workflow out of deployment"
@@ -9846,11 +10326,17 @@ var CLI_CONTRACT = {
9846
10326
  pathArgumentNames: KNOWLEDGE_BASE_PATH_ARGUMENT,
9847
10327
  confirm: "This deletes the document and its embeddings."
9848
10328
  },
10329
+ deleteKnowledgeConnector: {
10330
+ confirm: "This deletes the connector; --delete-documents also deletes its synchronized documents."
10331
+ },
9849
10332
  deleteFile: { confirm: "This archives the file." },
9850
10333
  deleteCredential: {
9851
10334
  confirm: "This disconnects the credential and removes its stored authentication."
9852
10335
  },
9853
10336
  deleteSkill: { confirm: "This deletes the skill." },
10337
+ revokeSkillEditor: {
10338
+ confirm: "This revokes the explicit skill editor grant for the selected email."
10339
+ },
9854
10340
  deleteCustomTool: { confirm: "This deletes the custom tool." },
9855
10341
  deleteMcpServer: {
9856
10342
  confirm: "This removes the MCP server and the tools it provides."
@@ -9946,7 +10432,7 @@ var CLI_CONTRACT = {
9946
10432
  queryRows: {
9947
10433
  command: "tables rows query",
9948
10434
  flags: {
9949
- predicate: { name: "filter", json: true, describe: TABLE_FILTER_HELP },
10435
+ predicate: { name: "filter", json: true, describe: TABLE_READ_FILTER_HELP },
9950
10436
  sort: { json: true, describe: TABLE_SORT_HELP }
9951
10437
  },
9952
10438
  expand: "data"
@@ -10374,7 +10860,7 @@ var CLI_CONTRACT = {
10374
10860
  name: "filter",
10375
10861
  renamedFrom: ["predicate"],
10376
10862
  json: true,
10377
- describe: TABLE_FILTER_HELP
10863
+ describe: TABLE_READ_FILTER_HELP
10378
10864
  }
10379
10865
  }
10380
10866
  },
@@ -11133,7 +11619,7 @@ async function createServiceAccount(command, providerId, options) {
11133
11619
  if (provider.requiresClientGeneratedCredentialId && !options.id) {
11134
11620
  throw new SimApiError(`--id is required for ${providerId}.`, 0);
11135
11621
  }
11136
- const credentials = credentialValues(provider, options.credentials);
11622
+ const credentialFields = credentialValues(provider, options.credentials);
11137
11623
  const operation = V2_OPERATIONS.createServiceAccountCredential;
11138
11624
  const response = await client.request(operation.path, {
11139
11625
  method: operation.method,
@@ -11144,7 +11630,7 @@ async function createServiceAccount(command, providerId, options) {
11144
11630
  displayName: options.name,
11145
11631
  ...options.description ? { description: options.description } : {},
11146
11632
  ...options.id ? { id: options.id } : {},
11147
- ...credentials
11633
+ credentials: JSON.stringify(credentialFields)
11148
11634
  }
11149
11635
  });
11150
11636
  renderResult("createServiceAccountCredential", profile.output, response.data, SERVICE_ACCOUNT_RESULT);
@@ -12908,6 +13394,7 @@ with -P, --profile, or SIM_PROFILE.
12908
13394
 
12909
13395
  Examples:
12910
13396
  $ sim login Authorize the default profile
13397
+ $ sim profile add acme --workspace ws_123 Reuse that login for a workspace
12911
13398
  $ sim login --profile dev --endpoint http://localhost:3000
12912
13399
  $ sim workflows list
12913
13400
  $ sim logs list --level error --limit 20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim",
3
- "version": "2.1.0-dev.24.1",
3
+ "version": "2.1.0-dev.32.1",
4
4
  "description": "Sim CLI - talk to the Sim API from your terminal",
5
5
  "type": "module",
6
6
  "bin": {