@withone/cli 2.12.0 → 2.14.0

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.
@@ -78,6 +78,42 @@ function intersectAllowlists(a, b) {
78
78
  if (b.includes("*")) return a;
79
79
  return a.filter((x) => b.includes(x));
80
80
  }
81
+ function checkManagementAccess(settings, op, what) {
82
+ const permissions = settings.permissions ?? "admin";
83
+ if (settings.knowledgeAgent) {
84
+ throw new AccessDeniedError(`${what} is disabled (knowledge-only mode).`);
85
+ }
86
+ const method = op === "delete" ? "DELETE" : "POST";
87
+ if (!isMethodAllowed(method, permissions)) {
88
+ throw new AccessDeniedError(`${what} needs ${op === "delete" ? 'the "admin"' : 'the "write" or "admin"'} permission level (current: "${permissions}").`);
89
+ }
90
+ }
91
+ function allowlistsUnrestricted(settings) {
92
+ return (settings.actionIds ?? ["*"]).includes("*") && (settings.connectionKeys ?? ["*"]).includes("*");
93
+ }
94
+ async function checkRelayActions(settings, actions, methodOf) {
95
+ if (actions === void 0 || actions === null) return;
96
+ if (!Array.isArray(actions)) throw new AccessDeniedError("--actions must be a JSON array.");
97
+ for (const [i, raw] of actions.entries()) {
98
+ const action = raw ?? {};
99
+ if (action.type === "passthrough") {
100
+ const actionId = String(action.actionId ?? "");
101
+ const connectionKey = String(action.connectionKey ?? "");
102
+ if (!actionId || !connectionKey) {
103
+ throw new AccessDeniedError(`Relay action [${i}] needs an actionId and a connectionKey.`);
104
+ }
105
+ checkPassthroughAccess({ ...settings, permissions: "admin", knowledgeAgent: false }, { actionId, connectionKey, method: "GET" });
106
+ const headers = action.headers && typeof action.headers === "object" ? action.headers : void 0;
107
+ checkPassthroughAccess(settings, { actionId, connectionKey, method: await methodOf(actionId), headers });
108
+ continue;
109
+ }
110
+ if (!allowlistsUnrestricted(settings)) {
111
+ throw new AccessDeniedError(
112
+ `Relay action [${i}] of type "${String(action.type)}" forwards events outside the connection and action allowlists, so it is allowed only when both are "*".`
113
+ );
114
+ }
115
+ }
116
+ }
81
117
 
82
118
  // src/lib/config.ts
83
119
  function configDir() {
@@ -535,8 +571,11 @@ export {
535
571
  PERMISSION_METHODS,
536
572
  isMethodAllowed,
537
573
  isActionAllowed,
574
+ isConnectionAllowed,
538
575
  IDENTITY_HEADERS,
539
576
  checkPassthroughAccess,
577
+ checkManagementAccess,
578
+ checkRelayActions,
540
579
  getProjectRoot,
541
580
  getProjectConfigPath,
542
581
  getGlobalConfigPath,
@@ -2,8 +2,8 @@ import {
2
2
  defaultSearchableText,
3
3
  embed,
4
4
  embedBatch
5
- } from "./chunk-EITBMRUB.js";
6
- import "./chunk-EWHFGWB3.js";
5
+ } from "./chunk-2OIJGF77.js";
6
+ import "./chunk-XUAUDMQE.js";
7
7
  export {
8
8
  defaultSearchableText,
9
9
  embed,
@@ -12,10 +12,10 @@ import {
12
12
  stripStepsAlias,
13
13
  summarizeFlowInputs,
14
14
  walkSteps
15
- } from "./chunk-7ZBRP3BJ.js";
15
+ } from "./chunk-IM3OA7CU.js";
16
16
  import "./chunk-44CV5IMX.js";
17
17
  import "./chunk-Q6PWTATS.js";
18
- import "./chunk-EWHFGWB3.js";
18
+ import "./chunk-XUAUDMQE.js";
19
19
  export {
20
20
  FlowRunner,
21
21
  collectStepTypes,
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  describeInstallContext,
15
15
  drainQueue,
16
16
  flowRequiresBash,
17
+ flowUsesCodeModules,
17
18
  flush,
18
19
  flushUsageRollups,
19
20
  formatAge,
@@ -39,10 +40,10 @@ import {
39
40
  validateActionInput,
40
41
  walkSteps,
41
42
  writeCache
42
- } from "./chunk-7ZBRP3BJ.js";
43
+ } from "./chunk-IM3OA7CU.js";
43
44
  import {
44
45
  memSqlCommand
45
- } from "./chunk-DGQOAFXJ.js";
46
+ } from "./chunk-TSMVFVQ3.js";
46
47
  import {
47
48
  collectIdentityKeys,
48
49
  countRecords,
@@ -73,7 +74,7 @@ import {
73
74
  writeDraftProfile,
74
75
  writePageToMemory,
75
76
  writeProfile
76
- } from "./chunk-WZTPSXHP.js";
77
+ } from "./chunk-LEJKEGB6.js";
77
78
  import {
78
79
  getByDotPath
79
80
  } from "./chunk-44CV5IMX.js";
@@ -87,7 +88,7 @@ import {
87
88
  requireMemoryInit,
88
89
  semanticSearchUpgradeHint,
89
90
  semanticSearchUpgradeLine
90
- } from "./chunk-J7JWBH45.js";
91
+ } from "./chunk-B5YDLVY5.js";
91
92
  import {
92
93
  cancel,
93
94
  createSpinner,
@@ -109,7 +110,7 @@ import {
109
110
  listBackendPlugins,
110
111
  loadBackendFromConfig,
111
112
  updateRecord
112
- } from "./chunk-6IRGRC3G.js";
113
+ } from "./chunk-4H5L3SBY.js";
113
114
  import {
114
115
  DEFAULT_MEMORY_CONFIG,
115
116
  defaultSearchableText,
@@ -119,9 +120,11 @@ import {
119
120
  memoryConfigExists,
120
121
  setOpenAiApiKey,
121
122
  updateMemoryConfig
122
- } from "./chunk-EITBMRUB.js";
123
+ } from "./chunk-2OIJGF77.js";
123
124
  import {
124
125
  PERMISSION_METHODS,
126
+ checkManagementAccess,
127
+ checkRelayActions,
125
128
  configExists,
126
129
  ensureWhoAmI,
127
130
  getAccessControl,
@@ -137,6 +140,7 @@ import {
137
140
  globalConfigExists,
138
141
  homeDir,
139
142
  isActionAllowed,
143
+ isConnectionAllowed,
140
144
  isMethodAllowed,
141
145
  projectConfigExists,
142
146
  readConfig,
@@ -148,7 +152,7 @@ import {
148
152
  updateApiBase,
149
153
  updateWhoAmI,
150
154
  writeConfig
151
- } from "./chunk-EWHFGWB3.js";
155
+ } from "./chunk-XUAUDMQE.js";
152
156
 
153
157
  // src/cli.ts
154
158
  import { createRequire } from "module";
@@ -2448,6 +2452,11 @@ async function connectionDeleteCommand(connectionKey, options) {
2448
2452
  if (!apiKey) {
2449
2453
  error("Not configured. Run `one init` first.");
2450
2454
  }
2455
+ try {
2456
+ checkManagementAccess(getAccessControlFromAllSources(), "delete", "Deleting a connection");
2457
+ } catch (error2) {
2458
+ error(error2 instanceof Error ? error2.message : String(error2));
2459
+ }
2451
2460
  const api = new OneApi(apiKey, getApiBase());
2452
2461
  const spinner = createSpinner();
2453
2462
  spinner.start("Finding connection...");
@@ -4949,6 +4958,14 @@ ${preflightErrors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}`);
4949
4958
  }
4950
4959
  error(msg);
4951
4960
  }
4961
+ if (!options.allowCode && flowUsesCodeModules(flow2)) {
4962
+ const msg = `Workflow "${flow2.key}" contains code.module steps, which run with full Node access. Re-run with --allow-code to permit them.`;
4963
+ if (isAgentMode()) {
4964
+ json({ error: msg, requiresCode: true, flowKey: flow2.key });
4965
+ process.exit(1);
4966
+ }
4967
+ error(msg);
4968
+ }
4952
4969
  if (options.stopAfter) {
4953
4970
  const ids = /* @__PURE__ */ new Set();
4954
4971
  walkSteps(flow2.steps, (s) => {
@@ -5011,6 +5028,7 @@ ${pc8.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
5011
5028
  mock: options.mock,
5012
5029
  verbose: options.verbose,
5013
5030
  allowBash: options.allowBash,
5031
+ allowCode: options.allowCode,
5014
5032
  skipValidation: options.skipValidation,
5015
5033
  stopAfter: options.stopAfter,
5016
5034
  rootDir,
@@ -5121,7 +5139,7 @@ async function flowListCommand() {
5121
5139
  layout: f.layout,
5122
5140
  inputCount: String(f.inputCount),
5123
5141
  stepCount: String(f.stepCount),
5124
- flags: f.requiresBash ? "--allow-bash" : ""
5142
+ flags: [f.requiresBash ? "--allow-bash" : "", f.usesCodeModules ? "--allow-code" : ""].filter(Boolean).join(" ")
5125
5143
  }))
5126
5144
  );
5127
5145
  console.log();
@@ -5197,6 +5215,14 @@ async function flowResumeCommand(runId, options = {}) {
5197
5215
  }
5198
5216
  error(msg);
5199
5217
  }
5218
+ if (!options.allowCode && flowUsesCodeModules(flow2)) {
5219
+ const msg = `Workflow "${flow2.key}" contains code.module steps, which run with full Node access. Re-run with --allow-code to permit them.`;
5220
+ if (isAgentMode()) {
5221
+ json({ error: msg, requiresCode: true, flowKey: flow2.key, runId });
5222
+ process.exit(1);
5223
+ }
5224
+ error(msg);
5225
+ }
5200
5226
  const runner = FlowRunner.fromRunState(state);
5201
5227
  const onEvent = (event) => {
5202
5228
  if (isAgentMode()) {
@@ -5209,7 +5235,8 @@ async function flowResumeCommand(runId, options = {}) {
5209
5235
  const context = await runner.resume(flow2, api, permissions, actionIds, {
5210
5236
  onEvent,
5211
5237
  rootDir,
5212
- allowBash: options.allowBash
5238
+ allowBash: options.allowBash,
5239
+ allowCode: options.allowCode
5213
5240
  });
5214
5241
  spinner.stop("Workflow completed");
5215
5242
  if (isAgentMode()) {
@@ -5525,9 +5552,45 @@ function getConfig3() {
5525
5552
  if (!apiKey) {
5526
5553
  error("Not configured. Run `one init` first.");
5527
5554
  }
5528
- const ac = getAccessControlFromAllSources();
5529
- const connectionKeys = ac.connectionKeys || ["*"];
5530
- return { apiKey, connectionKeys };
5555
+ const settings = getAccessControlFromAllSources();
5556
+ const connectionKeys = settings.connectionKeys || ["*"];
5557
+ return { apiKey, connectionKeys, settings };
5558
+ }
5559
+ async function connectionIdFilter(api, settings) {
5560
+ const keys = settings.connectionKeys ?? ["*"];
5561
+ if (keys.includes("*")) return () => true;
5562
+ const connections = await api.listConnections();
5563
+ const ids = new Set(connections.filter((c) => isConnectionAllowed(c.key, keys)).map((c) => c.id));
5564
+ return (id) => ids.has(id);
5565
+ }
5566
+ function restricted(settings) {
5567
+ return !(settings.connectionKeys ?? ["*"]).includes("*");
5568
+ }
5569
+ async function allowedEndpoint(api, settings, id) {
5570
+ const allowed = await connectionIdFilter(api, settings);
5571
+ const endpoint = await api.getRelayEndpoint(id);
5572
+ if (!allowed(endpoint.connectionId)) {
5573
+ throw new Error(`Relay endpoint "${id}" is on a connection that is not allowed.`);
5574
+ }
5575
+ return endpoint;
5576
+ }
5577
+ async function allowedEvent(api, settings, id) {
5578
+ const allowed = await connectionIdFilter(api, settings);
5579
+ const event = await api.getRelayEvent(id);
5580
+ if (!allowed(event.connectionId)) {
5581
+ throw new Error(`Relay event "${id}" is on a connection that is not allowed.`);
5582
+ }
5583
+ return event;
5584
+ }
5585
+ function actionMethod(api) {
5586
+ return async (actionId) => (await resolveActionDetails(api, actionId)).details.method;
5587
+ }
5588
+ function guard(check) {
5589
+ try {
5590
+ check();
5591
+ } catch (error2) {
5592
+ error(error2 instanceof Error ? error2.message : String(error2));
5593
+ }
5531
5594
  }
5532
5595
  function parseJsonArg3(value, argName) {
5533
5596
  try {
@@ -5537,7 +5600,8 @@ function parseJsonArg3(value, argName) {
5537
5600
  }
5538
5601
  }
5539
5602
  async function relayCreateCommand(options) {
5540
- const { apiKey, connectionKeys } = getConfig3();
5603
+ const { apiKey, connectionKeys, settings } = getConfig3();
5604
+ guard(() => checkManagementAccess(settings, "write", "Creating a relay endpoint"));
5541
5605
  if (!connectionKeys.includes("*") && !connectionKeys.includes(options.connectionKey)) {
5542
5606
  error(`Connection key "${options.connectionKey}" is not allowed.`);
5543
5607
  }
@@ -5577,7 +5641,7 @@ async function relayCreateCommand(options) {
5577
5641
  }
5578
5642
  }
5579
5643
  async function relayListCommand(options) {
5580
- const { apiKey } = getConfig3();
5644
+ const { apiKey, settings } = getConfig3();
5581
5645
  const api = new OneApi(apiKey, getApiBase());
5582
5646
  const spinner = createSpinner();
5583
5647
  spinner.start("Loading relay endpoints...");
@@ -5586,10 +5650,13 @@ async function relayListCommand(options) {
5586
5650
  if (options.limit) query.limit = options.limit;
5587
5651
  if (options.page) query.page = options.page;
5588
5652
  const result = await api.listRelayEndpoints(query);
5589
- const endpoints = result.rows || [];
5653
+ const allowed = await connectionIdFilter(api, settings);
5654
+ const endpoints = (result.rows || []).filter((e) => allowed(e.connectionId));
5590
5655
  if (isAgentMode()) {
5591
5656
  json({
5592
- total: result.total,
5657
+ // With a connection allowlist the server's total also counts rows
5658
+ // the allowlist hides, so report only what is shown.
5659
+ total: restricted(settings) ? endpoints.length : result.total,
5593
5660
  showing: endpoints.length,
5594
5661
  endpoints: endpoints.map((e) => ({
5595
5662
  id: e.id,
@@ -5630,12 +5697,12 @@ async function relayListCommand(options) {
5630
5697
  }
5631
5698
  }
5632
5699
  async function relayGetCommand(id) {
5633
- const { apiKey } = getConfig3();
5700
+ const { apiKey, settings } = getConfig3();
5634
5701
  const api = new OneApi(apiKey, getApiBase());
5635
5702
  const spinner = createSpinner();
5636
5703
  spinner.start("Loading relay endpoint...");
5637
5704
  try {
5638
- const result = await api.getRelayEndpoint(id);
5705
+ const result = await allowedEndpoint(api, settings, id);
5639
5706
  if (isAgentMode()) {
5640
5707
  json(result);
5641
5708
  return;
@@ -5661,7 +5728,8 @@ async function relayGetCommand(id) {
5661
5728
  }
5662
5729
  }
5663
5730
  async function relayUpdateCommand(id, options) {
5664
- const { apiKey } = getConfig3();
5731
+ const { apiKey, settings } = getConfig3();
5732
+ guard(() => checkManagementAccess(settings, "write", "Updating a relay endpoint"));
5665
5733
  const api = new OneApi(apiKey, getApiBase());
5666
5734
  const spinner = createSpinner();
5667
5735
  spinner.start("Updating relay endpoint...");
@@ -5672,6 +5740,9 @@ async function relayUpdateCommand(id, options) {
5672
5740
  if (options.eventFilters) body.eventFilters = parseJsonArg3(options.eventFilters, "--event-filters");
5673
5741
  if (options.tags) body.tags = parseJsonArg3(options.tags, "--tags");
5674
5742
  if (options.actions) body.actions = parseJsonArg3(options.actions, "--actions");
5743
+ const existing = await allowedEndpoint(api, settings, id);
5744
+ const effective = body.actions ?? (options.active === true ? existing.actions : void 0);
5745
+ await checkRelayActions(settings, effective, actionMethod(api));
5675
5746
  const result = await api.updateRelayEndpoint(id, body);
5676
5747
  if (isAgentMode()) {
5677
5748
  json(result);
@@ -5688,11 +5759,13 @@ async function relayUpdateCommand(id, options) {
5688
5759
  }
5689
5760
  }
5690
5761
  async function relayDeleteCommand(id) {
5691
- const { apiKey } = getConfig3();
5762
+ const { apiKey, settings } = getConfig3();
5763
+ guard(() => checkManagementAccess(settings, "delete", "Deleting a relay endpoint"));
5692
5764
  const api = new OneApi(apiKey, getApiBase());
5693
5765
  const spinner = createSpinner();
5694
5766
  spinner.start("Deleting relay endpoint...");
5695
5767
  try {
5768
+ await allowedEndpoint(api, settings, id);
5696
5769
  const result = await api.deleteRelayEndpoint(id);
5697
5770
  if (isAgentMode()) {
5698
5771
  json({ deleted: true, id: result.id });
@@ -5707,12 +5780,15 @@ async function relayDeleteCommand(id) {
5707
5780
  }
5708
5781
  }
5709
5782
  async function relayActivateCommand(id, options) {
5710
- const { apiKey } = getConfig3();
5783
+ const { apiKey, settings } = getConfig3();
5784
+ guard(() => checkManagementAccess(settings, "write", "Activating a relay endpoint"));
5711
5785
  const api = new OneApi(apiKey, getApiBase());
5712
5786
  const spinner = createSpinner();
5713
5787
  spinner.start("Activating relay endpoint...");
5714
5788
  try {
5715
5789
  const actions2 = parseJsonArg3(options.actions, "--actions");
5790
+ await allowedEndpoint(api, settings, id);
5791
+ await checkRelayActions(settings, actions2, actionMethod(api));
5716
5792
  const body = { actions: actions2 };
5717
5793
  if (options.webhookSecret) body.webhookSecret = options.webhookSecret;
5718
5794
  const result = await api.activateRelayEndpoint(id, body);
@@ -5731,7 +5807,7 @@ async function relayActivateCommand(id, options) {
5731
5807
  }
5732
5808
  }
5733
5809
  async function relayEventsCommand(options) {
5734
- const { apiKey } = getConfig3();
5810
+ const { apiKey, settings } = getConfig3();
5735
5811
  const api = new OneApi(apiKey, getApiBase());
5736
5812
  const spinner = createSpinner();
5737
5813
  spinner.start("Loading relay events...");
@@ -5744,10 +5820,13 @@ async function relayEventsCommand(options) {
5744
5820
  if (options.after) query.after = options.after;
5745
5821
  if (options.before) query.before = options.before;
5746
5822
  const result = await api.listRelayEvents(query);
5747
- const events = result.rows || [];
5823
+ const allowed = await connectionIdFilter(api, settings);
5824
+ const events = (result.rows || []).filter((e) => allowed(e.connectionId));
5748
5825
  if (isAgentMode()) {
5749
5826
  json({
5750
- total: result.total,
5827
+ // With a connection allowlist the server's total also counts rows
5828
+ // the allowlist hides, so report only what is shown.
5829
+ total: restricted(settings) ? events.length : result.total,
5751
5830
  showing: events.length,
5752
5831
  events: events.map((e) => ({
5753
5832
  id: e.id,
@@ -5783,12 +5862,12 @@ async function relayEventsCommand(options) {
5783
5862
  }
5784
5863
  }
5785
5864
  async function relayEventGetCommand(id) {
5786
- const { apiKey } = getConfig3();
5865
+ const { apiKey, settings } = getConfig3();
5787
5866
  const api = new OneApi(apiKey, getApiBase());
5788
5867
  const spinner = createSpinner();
5789
5868
  spinner.start("Loading relay event...");
5790
5869
  try {
5791
- const result = await api.getRelayEvent(id);
5870
+ const result = await allowedEvent(api, settings, id);
5792
5871
  if (isAgentMode()) {
5793
5872
  json(result);
5794
5873
  return;
@@ -5811,11 +5890,13 @@ async function relayDeliveriesCommand(options) {
5811
5890
  if (!options.endpointId && !options.eventId) {
5812
5891
  error("Provide either --endpoint-id or --event-id");
5813
5892
  }
5814
- const { apiKey } = getConfig3();
5893
+ const { apiKey, settings } = getConfig3();
5815
5894
  const api = new OneApi(apiKey, getApiBase());
5816
5895
  const spinner = createSpinner();
5817
5896
  spinner.start("Loading deliveries...");
5818
5897
  try {
5898
+ if (options.endpointId) await allowedEndpoint(api, settings, options.endpointId);
5899
+ else await allowedEvent(api, settings, options.eventId);
5819
5900
  const deliveries = options.endpointId ? await api.listRelayEndpointDeliveries(options.endpointId) : await api.listRelayEventDeliveries(options.eventId);
5820
5901
  const items = Array.isArray(deliveries) ? deliveries : deliveries.rows || [];
5821
5902
  if (isAgentMode()) {
@@ -7427,7 +7508,7 @@ async function syncModel(api, profile, options) {
7427
7508
  updateModelState(platform, model, { status: "failed", pagesProcessed, lastCursor }),
7428
7509
  (async () => {
7429
7510
  try {
7430
- const { getBackend: getBackend2 } = await import("./runtime-WB4J6RQK.js");
7511
+ const { getBackend: getBackend2 } = await import("./runtime-2OVXKNJS.js");
7431
7512
  const backend = await getBackend2();
7432
7513
  await Promise.race([
7433
7514
  backend.close(),
@@ -7793,7 +7874,7 @@ async function syncModel(api, profile, options) {
7793
7874
  db.exec(`DROP TABLE IF EXISTS _seen_ids`);
7794
7875
  }
7795
7876
  if (options.toMemory !== false) {
7796
- const backend = await (await import("./runtime-WB4J6RQK.js")).getBackend();
7877
+ const backend = await (await import("./runtime-2OVXKNJS.js")).getBackend();
7797
7878
  const type = `${platform}/${model}`;
7798
7879
  const existing = await backend.listKeysByType(type);
7799
7880
  const sourcePrefix = `${type}:`;
@@ -7894,7 +7975,7 @@ async function syncModel(api, profile, options) {
7894
7975
  let statusCounts;
7895
7976
  if (options.toMemory !== false) {
7896
7977
  try {
7897
- const backend = await (await import("./runtime-WB4J6RQK.js")).getBackend();
7978
+ const backend = await (await import("./runtime-2OVXKNJS.js")).getBackend();
7898
7979
  const typeName = `${platform}/${model}`;
7899
7980
  const [active, archived] = await Promise.all([
7900
7981
  backend.count(typeName, { status: "active" }),
@@ -9616,7 +9697,7 @@ ${result.total} results`);
9616
9697
  }
9617
9698
  }
9618
9699
  async function syncSqlCommand(platformModel, sql) {
9619
- const { syncSqlCommand: runSyncSql } = await import("./sql-BNSA2GZM.js");
9700
+ const { syncSqlCommand: runSyncSql } = await import("./sql-KRQUWOPT.js");
9620
9701
  await runSyncSql(platformModel, sql);
9621
9702
  }
9622
9703
  async function syncDeleteCommand(platformModel, options) {
@@ -9694,7 +9775,7 @@ async function syncDeleteCommand(platformModel, options) {
9694
9775
  async function maybeAutoMigrateLegacy(platform, models) {
9695
9776
  const dbSize = getDatabaseSize(platform);
9696
9777
  if (!dbSize || dbSize === "0 B") return;
9697
- const { getBackend: getBackend2 } = await import("./runtime-WB4J6RQK.js");
9778
+ const { getBackend: getBackend2 } = await import("./runtime-2OVXKNJS.js");
9698
9779
  const backend = await getBackend2();
9699
9780
  let memoryHasData = false;
9700
9781
  for (const model of models) {
@@ -9710,7 +9791,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
9710
9791
  ` detected legacy .one/sync/data/${platform}.db (${dbSize}) \u2014 auto-migrating into memory before sync.
9711
9792
  `
9712
9793
  );
9713
- const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-KKNH3745.js");
9794
+ const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-RJ7F2LPT.js");
9714
9795
  await memMigrateCommand3({ platform, yes: true });
9715
9796
  return;
9716
9797
  }
@@ -9719,7 +9800,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
9719
9800
  initialValue: true
9720
9801
  });
9721
9802
  if (p6.isCancel(shouldMigrate) || !shouldMigrate) return;
9722
- const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-KKNH3745.js");
9803
+ const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-RJ7F2LPT.js");
9723
9804
  await memMigrateCommand2({ platform, yes: true });
9724
9805
  }
9725
9806
  async function syncSuggestSearchableCommand(platformModel, options = {}) {
@@ -9780,7 +9861,7 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
9780
9861
  async function syncListCommand(platform) {
9781
9862
  const profiles = listProfiles(platform);
9782
9863
  const state = await readSyncState();
9783
- const { getBackend: getBackend2 } = await import("./runtime-WB4J6RQK.js");
9864
+ const { getBackend: getBackend2 } = await import("./runtime-2OVXKNJS.js");
9784
9865
  const backend = await getBackend2();
9785
9866
  const syncs = await Promise.all(profiles.map(async (p9) => {
9786
9867
  const modelState = state[p9.platform]?.[p9.model];
@@ -10056,7 +10137,7 @@ function registerSyncSubcommands(sync) {
10056
10137
  await syncSqlCommand(platformModel, sql);
10057
10138
  });
10058
10139
  sync.command("schema <platform/model>").description("Inspect the JSON structure of synced records (field paths, types, examples) \u2014 useful before writing `sync sql` queries").action(async (platformModel) => {
10059
- const { syncSchemaCommand } = await import("./schema-SDZZ7XZT.js");
10140
+ const { syncSchemaCommand } = await import("./schema-FIIT33OD.js");
10060
10141
  await syncSchemaCommand(platformModel);
10061
10142
  });
10062
10143
  sync.command("delete <platform/model>").description('Delete records from local sync data (e.g. one sync delete notion/pages --id "abc-123")').option("--id <value>", "Delete record by ID").option("--where <conditions>", 'Delete records matching conditions (e.g. "status=archived")').option("--where-sql <predicate>", `Delete using a raw SQL WHERE clause (e.g. "json_extract(data, '$.type') = 'promotion'")`).option("--yes", "Skip confirmation prompt").action(async (platformModel, options) => {
@@ -10833,7 +10914,7 @@ async function memDoctorCommand() {
10833
10914
  }
10834
10915
  if (cfg.embedding.provider === "openai") {
10835
10916
  try {
10836
- const { embed: embed2 } = await import("./embedding-7Q22D23K.js");
10917
+ const { embed: embed2 } = await import("./embedding-YIXO45HW.js");
10837
10918
  const result = await embed2("connectivity check");
10838
10919
  checks.push({
10839
10920
  name: "OpenAI embedding provider reachable",
@@ -11374,15 +11455,16 @@ one --agent flow list # List all workflows
11374
11455
 
11375
11456
  **Key concepts:**
11376
11457
  - Workflows live at \`.one/flows/<key>/flow.json\` (folder layout \u2014 REQUIRED for new flows). Flows can be organized into subdirectory groups: \`.one/flows/<group>/<key>/flow.json\`. Reference them as \`group/key\` or just the bare key. The legacy \`.one/flows/<key>.flow.json\` single-file layout is DEPRECATED but still loads for backward compatibility
11377
- - Code steps can reference an external \`.mjs\` module under the flow's \`lib/\` folder (stdin JSON in, stdout JSON out) \u2014 keeps JS out of JSON strings and makes flows shareable
11458
+ - Code steps can reference an external \`.mjs\` module under the flow's \`lib/\` folder (stdin JSON in, stdout JSON out). Modules have full Node access, so flows that use them need \`--allow-code\`
11459
+ - Inline JavaScript (transforms, conditions, \`if\`/\`unless\`, \`code.source\`) runs in a sandbox: no files, programs, network, timers or environment of its own, and no flag needed
11378
11460
  - 12 step types: action, transform, code, condition, loop, parallel, file-read, file-write, while, flow, paginate, bash
11379
11461
  - Data wiring via selectors: \`$.input.param\`, \`$.steps.stepId.response\`, \`$.loop.item\`
11380
11462
  - AI analysis via bash steps: \`claude --print --output-format json\` with \`parseEnvelope: true\` (unwraps the CLI envelope + fences + preamble into clean parsed JSON; use instead of \`parseJson\` for claude steps)
11381
- - Use \`--allow-bash\` to enable bash steps, \`--mock\` for dry-run with realistic mock responses (uses example data from action schemas)
11463
+ - Use \`--allow-bash\` to enable bash steps, \`--allow-code\` to enable \`code.module\` steps, \`--mock\` for dry-run with realistic mock responses (uses example data from action schemas)
11382
11464
  - Use \`--skip-validation\` to bypass input validation on action steps
11383
11465
  - Use \`--output-file <path>\` to stream the full result to a file instead of stdout \u2014 for large results that would otherwise be truncated or hit the JSON string-size limit; stdout (and \`--agent\` output) then carries an \`outputFile\` pointer instead of inline \`steps\`
11384
11466
  - Step-level \`if\`/\`unless\` (and \`while\`/\`condition\` steps) are null-safe: a condition referencing a skipped or not-yet-run step's output (e.g. \`$.steps.maybeSkipped.output.x\`) evaluates to \`false\` instead of crashing the flow
11385
- - Debugging: \`--dry-run\` resolves each step's interpolations and shows what they evaluate to without executing (\`$.steps.*\` refs are reported as deferred); \`--stop-after <stepId>\` runs up to a step then stops; \`--dry-run --stop-after <stepId>\` runs earlier steps for real then dry-resolves the target against their output; \`flow inspect <runId>\` shows a past run's per-step outputs (post-mortem, no re-run; \`--full\` for untruncated)
11467
+ - Debugging: \`--dry-run\` resolves each step's interpolations and shows what they evaluate to without executing actions, code, bash or file steps (transform and condition expressions are evaluated, in the sandbox; \`$.steps.*\` refs are reported as deferred); \`--stop-after <stepId>\` runs up to a step then stops; \`--dry-run --stop-after <stepId>\` runs earlier steps for real then dry-resolves the target against their output; \`flow inspect <runId>\` shows a past run's per-step outputs (post-mortem, no re-run; \`--full\` for untruncated)
11386
11468
 
11387
11469
  ### 3. Relay \u2014 Webhook event forwarding between platforms
11388
11470
  Receive webhooks from platforms (Stripe, GitHub, Airtable, Attio, Google Calendar) and forward event data to any connected platform using passthrough actions with Handlebars templates. No middleware, no code.
@@ -13822,7 +13904,7 @@ var flow = program.command("flow").alias("f").description("Create, execute, and
13822
13904
  flow.command("create [key]").description("Create a new workflow from JSON definition").option("--definition <json>", "Workflow definition as JSON string").option("-o, --output <path>", "Custom output path (default .one/flows/<key>/flow.json)").action(async (key, options) => {
13823
13905
  await flowCreateCommand(key, options);
13824
13906
  });
13825
- flow.command("execute <keyOrPath>").alias("x").description("Execute a workflow by key or file path").option("-i, --input <name=value>", "Input parameter (repeatable)", collect, []).option("--dry-run", "Resolve each step's interpolations and show what they evaluate to, without executing any step ($.steps.* refs resolve at runtime)").option("--mock", "With --dry-run: execute transforms/code with realistic mock API responses").option("--stop-after <stepId>", "Execute steps up to and including <stepId>, then stop (later steps are not run). With --dry-run, runs earlier steps for real and dry-resolves <stepId> against their output without executing it. See: one flow inspect").option("--skip-validation", "Skip input validation against action schemas").option("--allow-bash", "Allow bash step execution (disabled by default for security)").option("-v, --verbose", "Show full request/response for each step").option("--output-file <path>", "Write the full result to a file (streamed) instead of stdout \u2014 avoids truncation/string-limit errors for large results; stdout/agent output then carries an outputFile pointer").action(async (keyOrPath, options) => {
13907
+ flow.command("execute <keyOrPath>").alias("x").description("Execute a workflow by key or file path").option("-i, --input <name=value>", "Input parameter (repeatable)", collect, []).option("--dry-run", "Resolve each step's interpolations and show what they evaluate to, without running actions, code, bash or file steps. Transform and condition expressions are evaluated in the sandbox ($.steps.* refs resolve at runtime)").option("--mock", "With --dry-run: execute transforms/code with realistic mock API responses").option("--stop-after <stepId>", "Execute steps up to and including <stepId>, then stop (later steps are not run). With --dry-run, runs earlier steps for real and dry-resolves <stepId> against their output without executing it. See: one flow inspect").option("--skip-validation", "Skip input validation against action schemas").option("--allow-bash", "Allow bash step execution (disabled by default for security)").option("--allow-code", "Allow code.module steps, which run a .mjs file with full Node access (disabled by default for security). Inline transforms, conditions and code.source run sandboxed and need no flag").option("-v, --verbose", "Show full request/response for each step").option("--output-file <path>", "Write the full result to a file (streamed) instead of stdout \u2014 avoids truncation/string-limit errors for large results; stdout/agent output then carries an outputFile pointer").action(async (keyOrPath, options) => {
13826
13908
  await flowExecuteCommand(keyOrPath, options);
13827
13909
  });
13828
13910
  flow.command("list").alias("ls").description("List all workflows in .one/flows/").action(async () => {
@@ -13831,7 +13913,7 @@ flow.command("list").alias("ls").description("List all workflows in .one/flows/"
13831
13913
  flow.command("validate <keyOrPath>").description("Validate a workflow JSON file").action(async (keyOrPath) => {
13832
13914
  await flowValidateCommand(keyOrPath);
13833
13915
  });
13834
- flow.command("resume <runId>").description("Resume a paused or failed workflow run").option("--allow-bash", "Allow bash step execution (disabled by default for security) \u2014 required to resume any flow containing bash steps").action(async (runId, options) => {
13916
+ flow.command("resume <runId>").description("Resume a paused or failed workflow run").option("--allow-bash", "Allow bash step execution (disabled by default for security) \u2014 required to resume any flow containing bash steps").option("--allow-code", "Allow code.module steps (full Node access) \u2014 required to resume any flow containing them").action(async (runId, options) => {
13835
13917
  await flowResumeCommand(runId, options);
13836
13918
  });
13837
13919
  flow.command("runs [flowKey]").description("List workflow runs (optionally filtered by flow key)").action(async (flowKey) => {
@@ -3,13 +3,13 @@ import {
3
3
  dotPathToJsonbExpr,
4
4
  memMigrateCommand,
5
5
  reviveStringifiedJson
6
- } from "./chunk-WZTPSXHP.js";
6
+ } from "./chunk-LEJKEGB6.js";
7
7
  import "./chunk-44CV5IMX.js";
8
- import "./chunk-J7JWBH45.js";
8
+ import "./chunk-B5YDLVY5.js";
9
9
  import "./chunk-Q6PWTATS.js";
10
- import "./chunk-6IRGRC3G.js";
11
- import "./chunk-EITBMRUB.js";
12
- import "./chunk-EWHFGWB3.js";
10
+ import "./chunk-4H5L3SBY.js";
11
+ import "./chunk-2OIJGF77.js";
12
+ import "./chunk-XUAUDMQE.js";
13
13
  export {
14
14
  buildIdentityMap,
15
15
  dotPathToJsonbExpr,
@@ -5,9 +5,9 @@ import {
5
5
  resetBackendSingleton,
6
6
  updateRecord,
7
7
  upsertRecord
8
- } from "./chunk-6IRGRC3G.js";
9
- import "./chunk-EITBMRUB.js";
10
- import "./chunk-EWHFGWB3.js";
8
+ } from "./chunk-4H5L3SBY.js";
9
+ import "./chunk-2OIJGF77.js";
10
+ import "./chunk-XUAUDMQE.js";
11
11
  export {
12
12
  addRecord,
13
13
  closeBackendIfCached,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  okJson,
3
3
  requireMemoryInit
4
- } from "./chunk-J7JWBH45.js";
4
+ } from "./chunk-B5YDLVY5.js";
5
5
  import {
6
6
  isAgentMode,
7
7
  note
8
8
  } from "./chunk-Q6PWTATS.js";
9
9
  import {
10
10
  getBackend
11
- } from "./chunk-6IRGRC3G.js";
12
- import "./chunk-EITBMRUB.js";
13
- import "./chunk-EWHFGWB3.js";
11
+ } from "./chunk-4H5L3SBY.js";
12
+ import "./chunk-2OIJGF77.js";
13
+ import "./chunk-XUAUDMQE.js";
14
14
 
15
15
  // src/lib/memory/sync/schema.ts
16
16
  import pc from "picocolors";
@@ -0,0 +1,13 @@
1
+ import {
2
+ memSqlCommand,
3
+ syncSqlCommand
4
+ } from "./chunk-TSMVFVQ3.js";
5
+ import "./chunk-B5YDLVY5.js";
6
+ import "./chunk-Q6PWTATS.js";
7
+ import "./chunk-4H5L3SBY.js";
8
+ import "./chunk-2OIJGF77.js";
9
+ import "./chunk-XUAUDMQE.js";
10
+ export {
11
+ memSqlCommand,
12
+ syncSqlCommand
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "2.12.0",
3
+ "version": "2.14.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [