@softr/cli 0.1.1 → 0.1.2

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 +84 -20
  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_PINNED_APP_ID` pins 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>] [--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` pins it to an application, so the app lists 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
@@ -14401,6 +14401,12 @@ 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
  }
@@ -14465,6 +14471,9 @@ function authorizeUrl(target, redirectUri, state, challenge) {
14465
14471
  }).toString();
14466
14472
  return url.toString();
14467
14473
  }
14474
+ function refreshFailureHint(status) {
14475
+ return status === 401 ? "The OAuth grant is gone (revoked or older than 30 days). Run: softr login" : "The studio could not renew the login right now; try again in a moment.";
14476
+ }
14468
14477
  async function tokenRequest(target, form) {
14469
14478
  const path13 = "/v1/oauth/token";
14470
14479
  const response = await fetch(`${target.studioApiUrl}${path13}`, {
@@ -14478,7 +14487,7 @@ async function tokenRequest(target, form) {
14478
14487
  `POST ${path13} failed with status ${response.status}${text ? `: ${text}` : ""}`,
14479
14488
  response.status,
14480
14489
  text,
14481
- form["grant_type"] === "refresh_token" ? "The OAuth grant is gone (revoked or older than 30 days). Run: softr login" : void 0
14490
+ form["grant_type"] === "refresh_token" ? refreshFailureHint(response.status) : void 0
14482
14491
  );
14483
14492
  }
14484
14493
  const tokens = await response.json();
@@ -14725,7 +14734,8 @@ function toRow(workflow, workspace) {
14725
14734
  live: workflow.enabledVersion ?? null,
14726
14735
  version: workflow.version,
14727
14736
  draft: workflow.draft,
14728
- updatedAt: workflow.updatedAt ?? null
14737
+ updatedAt: workflow.updatedAt ?? null,
14738
+ triggers: (workflow.triggerSummaries ?? []).map(({ id, type }) => ({ id, type }))
14729
14739
  };
14730
14740
  }
14731
14741
  async function collectWorkflows(client, { workspaceId, applicationId } = {}) {
@@ -14757,16 +14767,24 @@ async function collectWorkflows(client, { workspaceId, applicationId } = {}) {
14757
14767
  }
14758
14768
  return listing;
14759
14769
  }
14760
- var TITLE_WIDTH = 42;
14770
+ var TITLE_WIDTH = 34;
14761
14771
  function workflowTable(rows) {
14762
14772
  const table = new import_cli_table3.default({
14763
- head: ["ID", "Title", "Status", "Version", "Draft"],
14764
- colWidths: [38, TITLE_WIDTH, 14, 9, 7],
14773
+ head: ["ID", "Title", "Triggers", "Status", "Version", "Draft"],
14774
+ // Only the free-text title wraps; the other columns size to their content, so trigger types are never cut off.
14775
+ colWidths: [null, TITLE_WIDTH, null, null, null, null],
14765
14776
  wordWrap: true,
14766
14777
  style: { head: [], border: [], "padding-left": 1, "padding-right": 1 }
14767
14778
  });
14768
14779
  for (const row of rows) {
14769
- table.push([row.id, row.title, row.live === null ? "not enabled" : `live v${row.live}`, row.version, row.draft]);
14780
+ table.push([
14781
+ row.id,
14782
+ row.title,
14783
+ row.triggers.map((trigger) => trigger.type).join("\n") || "-",
14784
+ row.live === null ? "not enabled" : `live v${row.live}`,
14785
+ row.version,
14786
+ row.draft
14787
+ ]);
14770
14788
  }
14771
14789
  return table.toString();
14772
14790
  }
@@ -16071,6 +16089,18 @@ type: ${type}${example3} (${status})`, {
16071
16089
  import { stdin as stdin2, stdout } from "node:process";
16072
16090
  import { createInterface as createInterface2 } from "node:readline/promises";
16073
16091
 
16092
+ // src/project/open-workflow.ts
16093
+ async function openWorkflow(dir, missingIdHint = "Pull an existing workflow or create one with init first.") {
16094
+ const config = await readConfig(dir);
16095
+ if (!config.id) {
16096
+ throw new CliError('softr-workflows.jsonc has no "id".', missingIdHint);
16097
+ }
16098
+ const state = await readState(dir);
16099
+ const target = resolveTarget(state);
16100
+ const client = await authenticatedClient(target);
16101
+ return { config, id: config.id, state, target, client };
16102
+ }
16103
+
16074
16104
  // src/project/readiness.ts
16075
16105
  var REASONS = {
16076
16106
  REFERENCED_NODE_UNKNOWN: "no node with that id",
@@ -16128,19 +16158,6 @@ async function assess(client, workflowId) {
16128
16158
  }
16129
16159
 
16130
16160
  // src/commands/publish.ts
16131
- async function openWorkflow(dir) {
16132
- const config = await readConfig(dir);
16133
- if (!config.id) {
16134
- throw new CliError(
16135
- 'softr-workflows.jsonc has no "id".',
16136
- "Pull an existing workflow or create one with init first."
16137
- );
16138
- }
16139
- const state = await readState(dir);
16140
- const target = resolveTarget(state);
16141
- const client = await authenticatedClient(target);
16142
- return { config, id: config.id, state, target, client };
16143
- }
16144
16161
  function issuesOf(assessment, config) {
16145
16162
  return [
16146
16163
  ...assessment.testIssues,
@@ -16258,6 +16275,52 @@ ${renderIssues(issues)}` : "every node tested on current data"}`
16258
16275
  });
16259
16276
  }
16260
16277
 
16278
+ // src/commands/pin.ts
16279
+ async function pinTarget(opts) {
16280
+ const applicationId = opts.app ?? process.env["SOFTR_PINNED_APP_ID"];
16281
+ if (!applicationId) {
16282
+ throw new CliError("No application given.", "Pass --app <applicationId> or set SOFTR_PINNED_APP_ID.");
16283
+ }
16284
+ if (opts.id && opts.dir) {
16285
+ throw new CliError("Pass either --id or --dir, not both.");
16286
+ }
16287
+ if (opts.id) {
16288
+ return { applicationId, workflowId: opts.id, client: await authenticatedClient(resolveTarget()) };
16289
+ }
16290
+ const { id, client } = await openWorkflow(
16291
+ await findWorkflowDir(process.cwd(), opts.dir),
16292
+ "Pass --id <workflowId>, or pull or init the workflow first."
16293
+ );
16294
+ return { applicationId, workflowId: id, client };
16295
+ }
16296
+ function withPinOptions(command) {
16297
+ 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)");
16298
+ }
16299
+ function registerPin(program3) {
16300
+ withPinOptions(
16301
+ program3.command("pin").description("Pin the workflow to an application of its workspace; it then lists under that app")
16302
+ ).action(async (opts, command) => {
16303
+ const { applicationId, workflowId, client } = await pinTarget(opts);
16304
+ await pinWorkflow(client, workflowId, applicationId);
16305
+ emit(globalOptions(command), `Pinned workflow ${workflowId} to application ${applicationId}.`, {
16306
+ workflowId,
16307
+ applicationId,
16308
+ pinned: true
16309
+ });
16310
+ });
16311
+ withPinOptions(program3.command("unpin").description("Remove the workflow from an application")).action(
16312
+ async (opts, command) => {
16313
+ const { applicationId, workflowId, client } = await pinTarget(opts);
16314
+ await unpinWorkflow(client, workflowId, applicationId);
16315
+ emit(globalOptions(command), `Unpinned workflow ${workflowId} from application ${applicationId}.`, {
16316
+ workflowId,
16317
+ applicationId,
16318
+ pinned: false
16319
+ });
16320
+ }
16321
+ );
16322
+ }
16323
+
16261
16324
  // src/commands/push.ts
16262
16325
  import path12 from "node:path";
16263
16326
 
@@ -16703,7 +16766,7 @@ Pass --workspace <id> to a command, or set SOFTR_WORKSPACE_ID, to work in one of
16703
16766
  // package.json
16704
16767
  var package_default = {
16705
16768
  name: "@softr/cli",
16706
- version: "0.1.1",
16769
+ version: "0.1.2",
16707
16770
  description: "The Softr command line: build, test and publish Softr workflows from local files; more Softr APIs to come",
16708
16771
  keywords: [
16709
16772
  "softr",
@@ -16770,6 +16833,7 @@ registerAdd(workflows);
16770
16833
  registerEdit(workflows);
16771
16834
  registerUpgrade(workflows);
16772
16835
  registerPublish(workflows);
16836
+ registerPin(workflows);
16773
16837
  registerSpec(workflows);
16774
16838
  registerSchema(workflows);
16775
16839
  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",
3
+ "version": "0.1.2",
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",