@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.
- package/README.md +4 -3
- package/dist/cli.mjs +100 -27
- 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>] [--app <id>] [--trigger <TYPE>] [--dir <path>]`
|
|
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 (
|
|
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 =
|
|
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([
|
|
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,
|
|
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,
|
|
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
|
-
|
|
15065
|
-
|
|
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
|
|
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
|
|
15300
|
+
const pinToApplicationId = opts.pinToApp ?? process.env["SOFTR_PINNED_APP_ID"];
|
|
15284
15301
|
const created = await createWorkflow(client, {
|
|
15285
15302
|
workspaceId,
|
|
15286
|
-
|
|
15287
|
-
|
|
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}${
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
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