@softr/cli 0.1.1-se21216.9 → 0.1.2-se21873.13

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 +4 -3
  2. package/dist/cli.mjs +100 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,7 +49,7 @@ ones. A workflow directory remembers the target it was pulled from.
49
49
  | `workspaces` | The workspaces the stored token reaches, with your role in each and the default one. Commands that take `--workspace` default to `SOFTR_WORKSPACE_ID` when it is set, else to your only or default workspace. |
50
50
  | `integrations [--workspace <id>] [--type <TYPE>]` | List the workspace's connected integrations (id, name, type, account, auth kind) from the studio API; the id goes into a node's `integrationId` input. Inside a workflow directory the workspace is the workflow's; `--type` filters. Credentials are never shown. |
51
51
 
52
- Every command accepts `--json`. `SOFTR_TOKEN` overrides the stored token; `SOFTR_WORKSPACE_ID` is the default workspace; `SOFTR_APP_ID` links the workflows `init` creates to that application.
52
+ Every command accepts `--json`. `SOFTR_TOKEN` overrides the stored token; `SOFTR_WORKSPACE_ID` is the default workspace.
53
53
 
54
54
  ## Workflows
55
55
 
@@ -133,10 +133,11 @@ node still on 1.0.0 or 1.1.0 keeps its synchronous runtime without `fetch`, and
133
133
 
134
134
  | Command | What it does |
135
135
  | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
- | `workflows list [--workspace <id>] [--app <id>]` | List the workflows you can access, one table per workspace; `--app` keeps only those linked to that application. A workspace whose listing fails is reported at the end and skipped. |
136
+ | `workflows list [--workspace <id>] [--app <id>]` | List the workflows you can access, one table per workspace, with their trigger types; `--json` gives each trigger's id too (`triggers`), the `triggerId` app code fires a workflow by. `--app` keeps only those pinned to that application. A workspace whose listing fails is reported at the end and skipped. |
137
137
  | `workflows pull [<workflowId>] [--dir <path>] [--force]` | Download a workflow into a directory (new `./<title-slug>/` by default). Without an id, list what you can pull. |
138
138
  | `workflows push [--dir <path>] [--dry-run] [--force]` | Inline `@@` references and code files, save as a new draft. Sends nothing when the files match the last sync, and refuses when the server copy changed since then (`--force` overrides both). Warns about placeholders that no saved test data backs. |
139
- | `workflows init <title> [--workspace <id>] [--app <id>] [--trigger <TYPE>] [--dir <path>]` | Create a workflow on the server with one trigger (default `WEBHOOK`) and the local directory for it; `--app` links it to an application: the app lists it, and the app's members may view it. |
139
+ | `workflows init <title> [--workspace <id>] [--pin-to-app <id>] [--trigger <TYPE>] [--dir <path>]` | Create a workflow on the server with one trigger (default `WEBHOOK`) and the local directory for it; `--pin-to-app` (default `SOFTR_PINNED_APP_ID`) pins it to an application of the same workspace, so the app lists it. |
140
+ | `workflows pin [--app <id>] [--id <workflowId>] [--dir <path>]` / `unpin` | Pin an existing workflow to an application of its workspace, or remove it from the app. The app defaults to `SOFTR_PINNED_APP_ID`, the workflow to the one in the directory. |
140
141
  | `workflows upgrade [<nodeId>...] [--dir <path>]` | Move nodes on an older version of their type to the current one (CUSTOM_CODE 1.0.0 and 1.1.0 become 1.2.0: the integrations list is added, the code file keeps its body and gets the new header). Every command that touches a workflow lists such nodes in a note. |
141
142
  | `workflows add <TYPE> [--id <id>] [--after <nodeId>] [--in-loop <loopId>] [--to <actionId>] [--lang JAVASCRIPT\|PYTHON] [--title <t>]` | Append an action after a node (default: the only node without a successor) and the path to it, or append a trigger (`--to` connects it). Custom code gets `actions/<id>.js\|py` with an empty function. Comments in the config survive. |
142
143
  | `workflows replace <nodeId> <TYPE>` | Give a node another type of the same kind: id, title and paths stay, inputs start from the new template. |
package/dist/cli.mjs CHANGED
@@ -14397,10 +14397,16 @@ function updateWorkflow(client, workflowId, body) {
14397
14397
  function getWorkflowOutputs(client, workflowId) {
14398
14398
  return apiRequest(client, "GET", `/v1/workflows/${workflowId}/outputs`);
14399
14399
  }
14400
- function listWorkflows(client, workspaceId, applicationId) {
14400
+ function listWorkflows(client, workspaceId, { applicationId } = {}) {
14401
14401
  const query = new URLSearchParams({ workspaceId, ...applicationId ? { applicationId } : {}, limit: "500" });
14402
14402
  return apiRequest(client, "GET", `/v1/workflows?${query}`);
14403
14403
  }
14404
+ function pinWorkflow(client, workflowId, applicationId) {
14405
+ return apiRequestVoid(client, "POST", `/v1/workflows/${workflowId}/pinned-applications/${applicationId}`);
14406
+ }
14407
+ function unpinWorkflow(client, workflowId, applicationId) {
14408
+ return apiRequestVoid(client, "DELETE", `/v1/workflows/${workflowId}/pinned-applications/${applicationId}`);
14409
+ }
14404
14410
  function testAction(client, workflowId, hierarchyPath2, mode) {
14405
14411
  return apiRequest(client, "POST", `/v1/workflows/${workflowId}/outputs/${mode}/actions/${hierarchyPath2}`, {});
14406
14412
  }
@@ -14549,6 +14555,9 @@ function tokenKind(token) {
14549
14555
  var TOKEN_ENV_VAR = "SOFTR_TOKEN";
14550
14556
  var AUTH_ENV_VAR = "SOFTR_AUTH";
14551
14557
  var PROXY_AUTH = "proxy";
14558
+ function isProxyAuth() {
14559
+ return process.env[AUTH_ENV_VAR] === PROXY_AUTH;
14560
+ }
14552
14561
  var REFRESH_MARGIN_MS = 5 * 60 * 1e3;
14553
14562
  function credentialsPath() {
14554
14563
  return path6.join(homedir(), ".softr", "credentials.json");
@@ -14613,7 +14622,7 @@ async function refreshedAccessToken(target, stored) {
14613
14622
  }
14614
14623
  }
14615
14624
  async function authenticatedClient(target) {
14616
- if (process.env[AUTH_ENV_VAR] === PROXY_AUTH) {
14625
+ if (isProxyAuth()) {
14617
14626
  return { target, kind: "proxy" };
14618
14627
  }
14619
14628
  const fromEnv = process.env[TOKEN_ENV_VAR];
@@ -14722,10 +14731,11 @@ function toRow(workflow, workspace) {
14722
14731
  live: workflow.enabledVersion ?? null,
14723
14732
  version: workflow.version,
14724
14733
  draft: workflow.draft,
14725
- updatedAt: workflow.updatedAt ?? null
14734
+ updatedAt: workflow.updatedAt ?? null,
14735
+ triggers: (workflow.triggerSummaries ?? []).map(({ id, type }) => ({ id, type }))
14726
14736
  };
14727
14737
  }
14728
- async function collectWorkflows(client, workspaceId, applicationId) {
14738
+ async function collectWorkflows(client, { workspaceId, applicationId } = {}) {
14729
14739
  const workspaces = await listWorkspaces(client);
14730
14740
  const selected = workspaceId ? workspaces.filter((workspace) => workspace.id === workspaceId) : workspaces;
14731
14741
  if (workspaceId && !selected.length) {
@@ -14737,7 +14747,7 @@ async function collectWorkflows(client, workspaceId, applicationId) {
14737
14747
  const listing = { workspaces: [], workflows: [], failures: [] };
14738
14748
  for (const workspace of selected) {
14739
14749
  try {
14740
- const page = await listWorkflows(client, workspace.id, applicationId);
14750
+ const page = await listWorkflows(client, workspace.id, { applicationId });
14741
14751
  listing.workspaces.push({ id: workspace.id, name: workspace.name });
14742
14752
  listing.workflows.push(...page.items.map((workflow) => toRow(workflow, workspace)));
14743
14753
  } catch (error) {
@@ -14754,16 +14764,23 @@ async function collectWorkflows(client, workspaceId, applicationId) {
14754
14764
  }
14755
14765
  return listing;
14756
14766
  }
14757
- var TITLE_WIDTH = 42;
14767
+ var TITLE_WIDTH = 34;
14758
14768
  function workflowTable(rows) {
14759
14769
  const table = new import_cli_table3.default({
14760
- head: ["ID", "Title", "Status", "Version", "Draft"],
14761
- colWidths: [38, TITLE_WIDTH, 14, 9, 7],
14770
+ head: ["ID", "Title", "Triggers", "Status", "Version", "Draft"],
14771
+ colWidths: [38, TITLE_WIDTH, 30, 14, 9, 7],
14762
14772
  wordWrap: true,
14763
14773
  style: { head: [], border: [], "padding-left": 1, "padding-right": 1 }
14764
14774
  });
14765
14775
  for (const row of rows) {
14766
- table.push([row.id, row.title, row.live === null ? "not enabled" : `live v${row.live}`, row.version, row.draft]);
14776
+ table.push([
14777
+ row.id,
14778
+ row.title,
14779
+ row.triggers.map((trigger) => trigger.type).join("\n"),
14780
+ row.live === null ? "not enabled" : `live v${row.live}`,
14781
+ row.version,
14782
+ row.draft
14783
+ ]);
14767
14784
  }
14768
14785
  return table.toString();
14769
14786
  }
@@ -14788,8 +14805,8 @@ ${workflowTable(rows)}` : `${heading}
14788
14805
  }
14789
14806
  return blocks.join("\n\n");
14790
14807
  }
14791
- async function emitWorkflowList(options, client, workspaceId, human, applicationId) {
14792
- const listing = await collectWorkflows(client, workspaceId, applicationId);
14808
+ async function emitWorkflowList(options, client, { workspaceId, applicationId, human } = {}) {
14809
+ const listing = await collectWorkflows(client, { workspaceId, applicationId });
14793
14810
  emit(options, [human, renderWorkflows(listing)].filter(Boolean).join("\n\n"), { ...listing });
14794
14811
  }
14795
14812
  function registerList(program3) {
@@ -14797,7 +14814,10 @@ function registerList(program3) {
14797
14814
  const options = globalOptions(command);
14798
14815
  const target = resolveTarget(await readState(process.cwd()));
14799
14816
  const client = await authenticatedClient(target);
14800
- await emitWorkflowList(options, client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"], void 0, opts.app);
14817
+ await emitWorkflowList(options, client, {
14818
+ workspaceId: opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"],
14819
+ applicationId: opts.app
14820
+ });
14801
14821
  });
14802
14822
  }
14803
14823
 
@@ -15060,12 +15080,9 @@ function registerPull(program3) {
15060
15080
  const target = resolveTarget(remembered);
15061
15081
  const client = await authenticatedClient(target);
15062
15082
  if (!workflowId) {
15063
- await emitWorkflowList(
15064
- options,
15065
- client,
15066
- void 0,
15067
- "Pick a workflow and run: softr workflows pull <workflowId>"
15068
- );
15083
+ await emitWorkflowList(options, client, {
15084
+ human: "Pick a workflow and run: softr workflows pull <workflowId>"
15085
+ });
15069
15086
  return;
15070
15087
  }
15071
15088
  const workflow = await getWorkflow(client, workflowId);
@@ -15268,7 +15285,7 @@ function registerInit(program3) {
15268
15285
  program3.command("init").description("Create a new workflow with one trigger, on the server and as a local directory").argument("<title>", "the workflow title").option(
15269
15286
  "--workspace <id>",
15270
15287
  "workspace to create it in (default: $SOFTR_WORKSPACE_ID, else your only or default workspace)"
15271
- ).option("--app <id>", "application to link the workflow to (default: $SOFTR_APP_ID when set)").option("--trigger <TYPE>", 'trigger type from "spec" (default WEBHOOK)', "WEBHOOK").option("--dir <path>", "directory to create (default: ./<title-slug>)").action(
15288
+ ).option("--pin-to-app <id>", "application to pin the workflow to (default: $SOFTR_PINNED_APP_ID when set)").option("--trigger <TYPE>", 'trigger type from "spec" (default WEBHOOK)', "WEBHOOK").option("--dir <path>", "directory to create (default: ./<title-slug>)").action(
15272
15289
  async (title, opts, command) => {
15273
15290
  const options = globalOptions(command);
15274
15291
  const cwd = process.cwd();
@@ -15280,12 +15297,11 @@ function registerInit(program3) {
15280
15297
  const target = resolveTarget();
15281
15298
  const client = await authenticatedClient(target);
15282
15299
  const workspaceId = await resolveWorkspace(client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"]);
15283
- const applicationId = opts.app ?? process.env["SOFTR_APP_ID"] ?? null;
15300
+ const pinToApplicationId = opts.pinToApp ?? process.env["SOFTR_PINNED_APP_ID"];
15284
15301
  const created = await createWorkflow(client, {
15285
15302
  workspaceId,
15286
- // Both halves of a link: the workflow belongs to the app (its members may view it), and the app lists it.
15287
- applicationId,
15288
- ...applicationId ? { pinToApplicationId: applicationId } : {},
15303
+ applicationId: null,
15304
+ ...pinToApplicationId ? { pinToApplicationId } : {},
15289
15305
  title,
15290
15306
  thumbnail: null,
15291
15307
  triggers: [{ id: TRIGGER_ID, type: trigger.type, version: trigger.version, inputs: trigger.template }],
@@ -15309,14 +15325,14 @@ function registerInit(program3) {
15309
15325
  const studioUrl = `${target.studioUrl}/workflow/${created.id}`;
15310
15326
  emit(
15311
15327
  options,
15312
- `Created "${title}" (${created.id}) with a ${trigger.type} trigger "${TRIGGER_ID}" in workspace ${workspaceId}${applicationId ? `, linked to application ${applicationId}` : ""}.
15328
+ `Created "${title}" (${created.id}) with a ${trigger.type} trigger "${TRIGGER_ID}" in workspace ${workspaceId}${pinToApplicationId ? `, linked to application ${pinToApplicationId}` : ""}.
15313
15329
  ${path10.relative(cwd, configFile)}
15314
15330
  ${studioUrl}
15315
15331
  Next: softr workflows add <TYPE> --dir ${path10.relative(cwd, dir) || "."} (see "spec" for the types), then push and test.`,
15316
15332
  {
15317
15333
  workflowId: created.id,
15318
15334
  workspaceId,
15319
- applicationId,
15335
+ pinnedToApplicationId: pinToApplicationId ?? null,
15320
15336
  title,
15321
15337
  triggerId: TRIGGER_ID,
15322
15338
  triggerType: trigger.type,
@@ -15553,7 +15569,7 @@ function registerLogin(program3) {
15553
15569
  ).option("--no-browser", "browser login, but print the URL instead of opening it (for remote shells and agents)").action(async (opts, command) => {
15554
15570
  const options = globalOptions(command);
15555
15571
  const target = resolveTarget();
15556
- if (process.env[AUTH_ENV_VAR] === "proxy") {
15572
+ if (isProxyAuth()) {
15557
15573
  throw new CliError(
15558
15574
  `Login is not needed: the target authenticates the requests itself ($${AUTH_ENV_VAR}=proxy).`
15559
15575
  );
@@ -15566,6 +15582,11 @@ function registerLogin(program3) {
15566
15582
  program3.command("logout").description("Forget the stored token of the current target").action(async (_opts, command) => {
15567
15583
  const options = globalOptions(command);
15568
15584
  const target = resolveTarget();
15585
+ if (isProxyAuth()) {
15586
+ throw new CliError(
15587
+ `Nothing to log out from: the target authenticates the requests itself ($${AUTH_ENV_VAR}=proxy).`
15588
+ );
15589
+ }
15569
15590
  const removed = await forgetToken(target);
15570
15591
  emit(
15571
15592
  options,
@@ -16251,6 +16272,57 @@ ${renderIssues(issues)}` : "every node tested on current data"}`
16251
16272
  });
16252
16273
  }
16253
16274
 
16275
+ // src/commands/pin.ts
16276
+ async function pinTarget(opts) {
16277
+ const applicationId = opts.app ?? process.env["SOFTR_PINNED_APP_ID"];
16278
+ if (!applicationId) {
16279
+ throw new CliError("No application given.", "Pass --app <applicationId> or set SOFTR_PINNED_APP_ID.");
16280
+ }
16281
+ if (opts.id && opts.dir) {
16282
+ throw new CliError("Pass either --id or --dir, not both.");
16283
+ }
16284
+ if (opts.id) {
16285
+ return { applicationId, workflowId: opts.id, client: await authenticatedClient(resolveTarget()) };
16286
+ }
16287
+ const dir = await findWorkflowDir(process.cwd(), opts.dir);
16288
+ const config = await readConfig(dir);
16289
+ if (!config.id) {
16290
+ throw new CliError(
16291
+ 'softr-workflows.jsonc has no "id".',
16292
+ "Pass --id <workflowId>, or pull or init the workflow first."
16293
+ );
16294
+ }
16295
+ const target = resolveTarget(await readState(dir));
16296
+ return { applicationId, workflowId: config.id, client: await authenticatedClient(target) };
16297
+ }
16298
+ function withPinOptions(command) {
16299
+ return command.option("--app <applicationId>", "the application (default: $SOFTR_PINNED_APP_ID)").option("--id <workflowId>", "the workflow (default: the one in the workflow directory)").option("--dir <path>", "workflow directory (default: the current directory)");
16300
+ }
16301
+ function registerPin(program3) {
16302
+ withPinOptions(
16303
+ program3.command("pin").description("Pin the workflow to an application of its workspace; it then lists under that app")
16304
+ ).action(async (opts, command) => {
16305
+ const { applicationId, workflowId, client } = await pinTarget(opts);
16306
+ await pinWorkflow(client, workflowId, applicationId);
16307
+ emit(globalOptions(command), `Pinned workflow ${workflowId} to application ${applicationId}.`, {
16308
+ workflowId,
16309
+ applicationId,
16310
+ pinned: true
16311
+ });
16312
+ });
16313
+ withPinOptions(program3.command("unpin").description("Remove the workflow from an application")).action(
16314
+ async (opts, command) => {
16315
+ const { applicationId, workflowId, client } = await pinTarget(opts);
16316
+ await unpinWorkflow(client, workflowId, applicationId);
16317
+ emit(globalOptions(command), `Unpinned workflow ${workflowId} from application ${applicationId}.`, {
16318
+ workflowId,
16319
+ applicationId,
16320
+ pinned: false
16321
+ });
16322
+ }
16323
+ );
16324
+ }
16325
+
16254
16326
  // src/commands/push.ts
16255
16327
  import path12 from "node:path";
16256
16328
 
@@ -16696,7 +16768,7 @@ Pass --workspace <id> to a command, or set SOFTR_WORKSPACE_ID, to work in one of
16696
16768
  // package.json
16697
16769
  var package_default = {
16698
16770
  name: "@softr/cli",
16699
- version: "0.1.1-se21216.9",
16771
+ version: "0.1.2-se21873.13",
16700
16772
  description: "The Softr command line: build, test and publish Softr workflows from local files; more Softr APIs to come",
16701
16773
  keywords: [
16702
16774
  "softr",
@@ -16763,6 +16835,7 @@ registerAdd(workflows);
16763
16835
  registerEdit(workflows);
16764
16836
  registerUpgrade(workflows);
16765
16837
  registerPublish(workflows);
16838
+ registerPin(workflows);
16766
16839
  registerSpec(workflows);
16767
16840
  registerSchema(workflows);
16768
16841
  program2.parseAsync(process.argv).catch(reportError);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/cli",
3
- "version": "0.1.1-se21216.9",
3
+ "version": "0.1.2-se21873.13",
4
4
  "description": "The Softr command line: build, test and publish Softr workflows from local files; more Softr APIs to come",
5
5
  "keywords": [
6
6
  "softr",