@withone/cli 1.38.0 → 1.39.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.
package/README.md CHANGED
@@ -330,6 +330,8 @@ one sync init stripe balanceTransactions --config '{"onInsert":"one flow execute
330
330
  one sync run stripe --full-refresh
331
331
  ```
332
332
 
333
+ > **Sync uses passthrough actions only.** Profiles referencing a custom/composer action are rejected at runtime. `sync models` already filters to passthrough-only; if a model has no passthrough list endpoint, compose a flow instead of syncing.
334
+
333
335
  | Subcommand | What it does |
334
336
  |------------|-------------|
335
337
  | `install` / `doctor` | Install + verify the SQLite engine |
package/dist/index.js CHANGED
@@ -4666,6 +4666,9 @@ function parseActionType(actionKey) {
4666
4666
  if (parts.length < 5) return null;
4667
4667
  return parts[4];
4668
4668
  }
4669
+ function isCustomAction(tags) {
4670
+ return !!tags && tags.includes("custom");
4671
+ }
4669
4672
  async function discoverModels(api, platform) {
4670
4673
  const actions2 = await api.listAvailableActions(platform);
4671
4674
  const listActionTypes = /* @__PURE__ */ new Set(["get_many", "list", "get_all"]);
@@ -4674,6 +4677,7 @@ async function discoverModels(api, platform) {
4674
4677
  const actionType = parseActionType(action.key);
4675
4678
  if (!actionType) continue;
4676
4679
  if (!listActionTypes.has(actionType)) continue;
4680
+ if (isCustomAction(action.tags)) continue;
4677
4681
  const modelName = action.modelName;
4678
4682
  if (!modelName) continue;
4679
4683
  if (modelMap.has(modelName)) {
@@ -4697,7 +4701,7 @@ async function discoverModels(api, platform) {
4697
4701
  try {
4698
4702
  const searchResults = await api.searchActions(platform, model.displayName, "execute");
4699
4703
  const resolved = searchResults.find(
4700
- (a) => a.path === model.listAction.path && a.method === model.listAction.method
4704
+ (a) => a.path === model.listAction.path && a.method === model.listAction.method && !isCustomAction(a.tags)
4701
4705
  );
4702
4706
  if (resolved?.systemId) {
4703
4707
  model.listAction.actionId = resolved.systemId;
@@ -4706,7 +4710,8 @@ async function discoverModels(api, platform) {
4706
4710
  }
4707
4711
  })
4708
4712
  );
4709
- return models.sort((a, b) => a.name.localeCompare(b.name));
4713
+ const syncable = models.filter((m) => m.listAction.actionId.startsWith("conn_mod_def::"));
4714
+ return syncable.sort((a, b) => a.name.localeCompare(b.name));
4710
4715
  }
4711
4716
 
4712
4717
  // src/lib/sync/profile.ts
@@ -5524,6 +5529,11 @@ async function enrichPhase(api, db, config2, model, idField, connectionKey, plat
5524
5529
  `Enrich: could not load action ${config2.actionId}: ${err instanceof Error ? err.message : String(err)}`
5525
5530
  );
5526
5531
  }
5532
+ if (detailAction.tags?.includes("custom")) {
5533
+ throw new Error(
5534
+ `Enrich does not support custom actions. Action ${config2.actionId} is tagged "custom". Use a passthrough detail endpoint \u2014 run 'one actions search ${platform} "<model> get"' to find one.`
5535
+ );
5536
+ }
5527
5537
  let concurrency = config2.concurrency ?? DEFAULT_CONCURRENCY;
5528
5538
  let enriched = 0;
5529
5539
  let skipped = 0;
@@ -5913,6 +5923,11 @@ async function syncModel(api, profile, options) {
5913
5923
  }
5914
5924
  }
5915
5925
  const actionDetails = await api.getActionDetails(profile.actionId);
5926
+ if (actionDetails.tags?.includes("custom")) {
5927
+ throw new Error(
5928
+ `Sync does not support custom actions. Action ${profile.actionId} is tagged "custom". Use a passthrough action \u2014 run 'one actions search ${platform} "${model}"' to find one, or compose a flow that chains passthrough calls if the logic is complex.`
5929
+ );
5930
+ }
5916
5931
  const maxPages = options.maxPages ?? Infinity;
5917
5932
  let tableCreated = tableExists(db, model);
5918
5933
  let currentPageQueryParams = { ...queryParams };
@@ -6272,6 +6287,14 @@ async function testSyncProfile(api, profile) {
6272
6287
  });
6273
6288
  return report;
6274
6289
  }
6290
+ if (actionDetails.tags?.includes("custom")) {
6291
+ checks.push({
6292
+ name: "action is passthrough (not custom)",
6293
+ ok: false,
6294
+ detail: `Action ${profile.actionId} is tagged "custom". Sync only supports passthrough actions. Run 'one actions search ${profile.platform} "${profile.model}"' to find one.`
6295
+ });
6296
+ return report;
6297
+ }
6275
6298
  let responseData;
6276
6299
  try {
6277
6300
  const result = await api.executePassthroughRequest({
@@ -8582,23 +8605,29 @@ one --agent sync profiles stripe # filter by platform
8582
8605
 
8583
8606
  When a built-in exists, \`sync init\` uses it automatically \u2014 no inference needed, no manual config. The agent just needs to match the user's intent to a profile description.
8584
8607
 
8585
- ## Action Resolution
8608
+ ## Action Resolution \u2014 custom actions are hard-blocked
8586
8609
 
8587
- Sync profiles MUST prefer passthrough actions over custom actions.
8588
- Custom actions add server-side fan-out and transformation that causes timeouts
8589
- and payload size failures at scale. The sync engine handles pagination, retries,
8590
- rate limiting, and enrichment locally \u2014 a server-side middleware layer on top
8591
- of that creates problems, not value.
8610
+ Sync refuses to run against custom/composer actions (tag \`custom\`). Both
8611
+ the list action and any enrich detail action in a profile MUST be passthrough.
8612
+ \`sync run\` loads the action's knowledge, checks for the \`custom\` tag, and
8613
+ aborts with a clear error pointing at the passthrough alternative.
8592
8614
 
8593
- When resolving actions for sync profiles:
8594
- 1. Search with knowledge mode (not execute mode) to include passthrough actions
8595
- 2. Prefer GET passthrough endpoints (e.g. /gmail/v1/users/{userId}/threads)
8596
- over POST custom endpoints (e.g. /gmail/get-threads)
8597
- 3. Use enrich config for per-record detail fetching instead of relying on
8598
- custom actions that fan out server-side
8599
- 4. Only fall back to custom actions when no passthrough equivalent exists
8615
+ Why the block:
8616
+ - Custom actions run on a small shared fleet that collapses under sync-scale load
8617
+ - Custom list endpoints often expect filters in the body and silently return
8618
+ unfiltered or empty results (sync sends params as query/path only by design)
8619
+ - The sync engine already handles pagination, retry, rate limiting, and per-record
8620
+ enrichment locally \u2014 server-side fan-out on top of that creates 5xx, not value
8600
8621
 
8601
- This applies to sync models discovery, sync init, and enrich action selection.
8622
+ How to build a profile:
8623
+ 1. \`one actions search <platform> "<model>"\` surfaces passthrough actions.
8624
+ \`sync init\`'s auto-infer also drops customs before offering choices.
8625
+ 2. Prefer GET passthrough endpoints (e.g. /gmail/v1/users/{userId}/threads)
8626
+ over POST custom endpoints (e.g. /gmail/get-threads).
8627
+ 3. If no passthrough list action exists for a model, that model can't be
8628
+ synced. Compose a flow that chains passthrough calls instead. Custom
8629
+ actions are for one-off agent use only \u2014 never sync, never flow.
8630
+ 4. The enrich \`actionId\` is held to the same rule: must be passthrough.
8602
8631
 
8603
8632
  ## Workflow: init \u2192 run \u2192 query
8604
8633
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.38.0",
3
+ "version": "1.39.1",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -173,6 +173,8 @@ one --agent sync list stripe # progress + freshness
173
173
  one sync schedule add stripe --every 1h
174
174
  ```
175
175
 
176
+ **Sync rejects custom actions** — profiles must use passthrough. `sync init` only surfaces passthrough models; `sync run` aborts if the list or enrich action is tagged `custom`. If no passthrough exists, compose a flow instead.
177
+
176
178
  **Advanced features** (enrich, transform, exclude, identityKey, hooks, --full-refresh, --where-sql delete, cursor resume): run `one guide sync` for the full reference.
177
179
 
178
180
  ## Beyond Single Actions