@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.
- package/README.md +4 -3
- package/dist/cli.mjs +84 -20
- 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
|
|
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
|
|
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" ?
|
|
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 =
|
|
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
|
-
|
|
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([
|
|
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.
|
|
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);
|