@softr/cli 0.1.0 → 0.1.1-se21216.7

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 +3 -3
  2. package/dist/cli.mjs +85 -63
  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.
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.
53
53
 
54
54
  ## Workflows
55
55
 
@@ -133,10 +133,10 @@ 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>]` | List the workflows you can access, one table per workspace. 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; `--app` keeps only those linked 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>] [--trigger <TYPE>] [--dir <path>]` | Create a workflow on the server with one trigger (default `WEBHOOK`) and the local directory for it. |
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, so the studio lists it with that app. |
140
140
  | `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
141
  | `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
142
  | `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
@@ -6098,9 +6098,9 @@ var SoftrApiError = class extends Error {
6098
6098
  body;
6099
6099
  hint;
6100
6100
  };
6101
- function hintFor(status) {
6101
+ function hintFor(status, client) {
6102
6102
  if (status === 401 || status === 403) {
6103
- return "The token was not accepted: revoked, from another environment, missing access to this workflow, or its statements do not grant this action. Run: softr login";
6103
+ return client.kind === "proxy" ? "The target refused the request: no access to this workflow, or its credential is gone." : "The token was not accepted: revoked, from another environment, missing access to this workflow, or its statements do not grant this action. Run: softr login";
6104
6104
  }
6105
6105
  if (status === 409) {
6106
6106
  return "The workflow changed on the server since the last pull. Pull again with --force, then re-apply your edits.";
@@ -6108,7 +6108,7 @@ function hintFor(status) {
6108
6108
  return void 0;
6109
6109
  }
6110
6110
  function authHeaders(client) {
6111
- return { Authorization: `Bearer ${client.token}` };
6111
+ return client.kind === "proxy" ? {} : { Authorization: `Bearer ${client.token}` };
6112
6112
  }
6113
6113
  async function send(client, service, method, path13, body) {
6114
6114
  const init = {
@@ -6129,7 +6129,7 @@ async function send(client, service, method, path13, body) {
6129
6129
  `${method} ${path13} failed with status ${response.status}${text ? `: ${text}` : ""}`,
6130
6130
  response.status,
6131
6131
  text,
6132
- hintFor(response.status)
6132
+ hintFor(response.status, client)
6133
6133
  );
6134
6134
  }
6135
6135
  async function apiRequest(client, method, path13, body) {
@@ -14397,8 +14397,8 @@ 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) {
14401
- const query = new URLSearchParams({ workspaceId, limit: "500" });
14400
+ function listWorkflows(client, workspaceId, applicationId) {
14401
+ const query = new URLSearchParams({ workspaceId, ...applicationId ? { applicationId } : {}, limit: "500" });
14402
14402
  return apiRequest(client, "GET", `/v1/workflows?${query}`);
14403
14403
  }
14404
14404
  function testAction(client, workflowId, hierarchyPath2, mode) {
@@ -14514,7 +14514,7 @@ function normalizeUrl(value, source) {
14514
14514
  } catch {
14515
14515
  throw new CliError(`${source} is not a valid URL: ${value}`);
14516
14516
  }
14517
- return url.origin;
14517
+ return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
14518
14518
  }
14519
14519
  function resolveTarget(remembered) {
14520
14520
  const pick = (envVar, rememberedValue, fallback) => {
@@ -14547,6 +14547,8 @@ function tokenKind(token) {
14547
14547
 
14548
14548
  // src/config/credentials.ts
14549
14549
  var TOKEN_ENV_VAR = "SOFTR_TOKEN";
14550
+ var AUTH_ENV_VAR = "SOFTR_AUTH";
14551
+ var PROXY_AUTH = "proxy";
14550
14552
  var REFRESH_MARGIN_MS = 5 * 60 * 1e3;
14551
14553
  function credentialsPath() {
14552
14554
  return path6.join(homedir(), ".softr", "credentials.json");
@@ -14611,6 +14613,9 @@ async function refreshedAccessToken(target, stored) {
14611
14613
  }
14612
14614
  }
14613
14615
  async function authenticatedClient(target) {
14616
+ if (process.env[AUTH_ENV_VAR] === PROXY_AUTH) {
14617
+ return { target, kind: "proxy" };
14618
+ }
14614
14619
  const fromEnv = process.env[TOKEN_ENV_VAR];
14615
14620
  if (fromEnv) {
14616
14621
  return { target, token: fromEnv, kind: tokenKind(fromEnv) };
@@ -14720,7 +14725,7 @@ function toRow(workflow, workspace) {
14720
14725
  updatedAt: workflow.updatedAt ?? null
14721
14726
  };
14722
14727
  }
14723
- async function collectWorkflows(client, workspaceId) {
14728
+ async function collectWorkflows(client, workspaceId, applicationId) {
14724
14729
  const workspaces = await listWorkspaces(client);
14725
14730
  const selected = workspaceId ? workspaces.filter((workspace) => workspace.id === workspaceId) : workspaces;
14726
14731
  if (workspaceId && !selected.length) {
@@ -14732,7 +14737,7 @@ async function collectWorkflows(client, workspaceId) {
14732
14737
  const listing = { workspaces: [], workflows: [], failures: [] };
14733
14738
  for (const workspace of selected) {
14734
14739
  try {
14735
- const page = await listWorkflows(client, workspace.id);
14740
+ const page = await listWorkflows(client, workspace.id, applicationId);
14736
14741
  listing.workspaces.push({ id: workspace.id, name: workspace.name });
14737
14742
  listing.workflows.push(...page.items.map((workflow) => toRow(workflow, workspace)));
14738
14743
  } catch (error) {
@@ -14783,16 +14788,16 @@ ${workflowTable(rows)}` : `${heading}
14783
14788
  }
14784
14789
  return blocks.join("\n\n");
14785
14790
  }
14786
- async function emitWorkflowList(options, client, workspaceId, human) {
14787
- const listing = await collectWorkflows(client, workspaceId);
14791
+ async function emitWorkflowList(options, client, workspaceId, human, applicationId) {
14792
+ const listing = await collectWorkflows(client, workspaceId, applicationId);
14788
14793
  emit(options, [human, renderWorkflows(listing)].filter(Boolean).join("\n\n"), { ...listing });
14789
14794
  }
14790
14795
  function registerList(program3) {
14791
- program3.command("list").description("List the workflows you can access, grouped by workspace").option("--workspace <id>", "only this workspace (default: $SOFTR_WORKSPACE_ID when set)").action(async (opts, command) => {
14796
+ program3.command("list").description("List the workflows you can access, grouped by workspace").option("--workspace <id>", "only this workspace (default: $SOFTR_WORKSPACE_ID when set)").option("--app <id>", "only the workflows linked to this application").action(async (opts, command) => {
14792
14797
  const options = globalOptions(command);
14793
14798
  const target = resolveTarget(await readState(process.cwd()));
14794
14799
  const client = await authenticatedClient(target);
14795
- await emitWorkflowList(options, client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"]);
14800
+ await emitWorkflowList(options, client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"], void 0, opts.app);
14796
14801
  });
14797
14802
  }
14798
14803
 
@@ -15263,58 +15268,62 @@ function registerInit(program3) {
15263
15268
  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(
15264
15269
  "--workspace <id>",
15265
15270
  "workspace to create it in (default: $SOFTR_WORKSPACE_ID, else your only or default workspace)"
15266
- ).option("--trigger <TYPE>", 'trigger type from "spec" (default WEBHOOK)', "WEBHOOK").option("--dir <path>", "directory to create (default: ./<title-slug>)").action(async (title, opts, command) => {
15267
- const options = globalOptions(command);
15268
- const cwd = process.cwd();
15269
- const dir = path10.resolve(cwd, opts.dir ?? slug(title));
15270
- if (await configExists(dir)) {
15271
- throw new CliError(`${path10.join(dir, CONFIG_FILE_NAME)} already exists.`, "Pick another --dir or title.");
15272
- }
15273
- const trigger = findCatalogEntry(opts.trigger, "trigger");
15274
- const target = resolveTarget();
15275
- const client = await authenticatedClient(target);
15276
- const workspaceId = await resolveWorkspace(client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"]);
15277
- const created = await createWorkflow(client, {
15278
- workspaceId,
15279
- applicationId: null,
15280
- title,
15281
- thumbnail: null,
15282
- triggers: [{ id: TRIGGER_ID, type: trigger.type, version: trigger.version, inputs: trigger.template }],
15283
- actions: [],
15284
- paths: [],
15285
- // Like the studio: the author's time zone, so schedules and waits mean what they read.
15286
- configuration: { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC" }
15287
- });
15288
- const workflow = await getWorkflow(client, created.id);
15289
- const config = { $schema: SCHEMA_POINTER, ...toLocalConfig(workflow) };
15290
- const configFile = await writeConfig(dir, config);
15291
- await writeSchemaFile(dir);
15292
- const syncedHash = definitionHash((await packWorkflow(config, dir)).packed);
15293
- await writeState(dir, {
15294
- ...target,
15295
- workflowId: created.id,
15296
- version: workflow.version,
15297
- draft: workflow.draft,
15298
- syncedHash
15299
- });
15300
- const studioUrl = `${target.studioUrl}/workflow/${created.id}`;
15301
- emit(
15302
- options,
15303
- `Created "${title}" (${created.id}) with a ${trigger.type} trigger "${TRIGGER_ID}" in workspace ${workspaceId}.
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(
15272
+ async (title, opts, command) => {
15273
+ const options = globalOptions(command);
15274
+ const cwd = process.cwd();
15275
+ const dir = path10.resolve(cwd, opts.dir ?? slug(title));
15276
+ if (await configExists(dir)) {
15277
+ throw new CliError(`${path10.join(dir, CONFIG_FILE_NAME)} already exists.`, "Pick another --dir or title.");
15278
+ }
15279
+ const trigger = findCatalogEntry(opts.trigger, "trigger");
15280
+ const target = resolveTarget();
15281
+ const client = await authenticatedClient(target);
15282
+ const workspaceId = await resolveWorkspace(client, opts.workspace ?? process.env["SOFTR_WORKSPACE_ID"]);
15283
+ const applicationId = opts.app ?? process.env["SOFTR_APP_ID"] ?? null;
15284
+ const created = await createWorkflow(client, {
15285
+ workspaceId,
15286
+ applicationId,
15287
+ title,
15288
+ thumbnail: null,
15289
+ triggers: [{ id: TRIGGER_ID, type: trigger.type, version: trigger.version, inputs: trigger.template }],
15290
+ actions: [],
15291
+ paths: [],
15292
+ // Like the studio: the author's time zone, so schedules and waits mean what they read.
15293
+ configuration: { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC" }
15294
+ });
15295
+ const workflow = await getWorkflow(client, created.id);
15296
+ const config = { $schema: SCHEMA_POINTER, ...toLocalConfig(workflow) };
15297
+ const configFile = await writeConfig(dir, config);
15298
+ await writeSchemaFile(dir);
15299
+ const syncedHash = definitionHash((await packWorkflow(config, dir)).packed);
15300
+ await writeState(dir, {
15301
+ ...target,
15302
+ workflowId: created.id,
15303
+ version: workflow.version,
15304
+ draft: workflow.draft,
15305
+ syncedHash
15306
+ });
15307
+ const studioUrl = `${target.studioUrl}/workflow/${created.id}`;
15308
+ emit(
15309
+ options,
15310
+ `Created "${title}" (${created.id}) with a ${trigger.type} trigger "${TRIGGER_ID}" in workspace ${workspaceId}${applicationId ? `, linked to application ${applicationId}` : ""}.
15304
15311
  ${path10.relative(cwd, configFile)}
15305
15312
  ${studioUrl}
15306
15313
  Next: softr workflows add <TYPE> --dir ${path10.relative(cwd, dir) || "."} (see "spec" for the types), then push and test.`,
15307
- {
15308
- workflowId: created.id,
15309
- workspaceId,
15310
- title,
15311
- triggerId: TRIGGER_ID,
15312
- triggerType: trigger.type,
15313
- dir,
15314
- studioUrl
15315
- }
15316
- );
15317
- });
15314
+ {
15315
+ workflowId: created.id,
15316
+ workspaceId,
15317
+ applicationId,
15318
+ title,
15319
+ triggerId: TRIGGER_ID,
15320
+ triggerType: trigger.type,
15321
+ dir,
15322
+ studioUrl
15323
+ }
15324
+ );
15325
+ }
15326
+ );
15318
15327
  }
15319
15328
 
15320
15329
  // src/commands/integrations.ts
@@ -15542,6 +15551,11 @@ function registerLogin(program3) {
15542
15551
  ).option("--no-browser", "browser login, but print the URL instead of opening it (for remote shells and agents)").action(async (opts, command) => {
15543
15552
  const options = globalOptions(command);
15544
15553
  const target = resolveTarget();
15554
+ if (process.env[AUTH_ENV_VAR] === "proxy") {
15555
+ throw new CliError(
15556
+ `Login is not needed: the target authenticates the requests itself ($${AUTH_ENV_VAR}=proxy).`
15557
+ );
15558
+ }
15545
15559
  const token = (opts.token ?? process.env[TOKEN_ENV_VAR] ?? "").trim();
15546
15560
  const saved = token ? pastedToken(token) : opts.browser ? await interactiveLogin(target) : await browserLogin(target, true);
15547
15561
  const identity = await identify({ target, token: saved.token, kind: saved.kind });
@@ -15561,6 +15575,14 @@ function registerLogin(program3) {
15561
15575
  const options = globalOptions(command);
15562
15576
  const target = resolveTarget();
15563
15577
  const client = await authenticatedClient(target);
15578
+ if (client.kind === "proxy") {
15579
+ emit(
15580
+ options,
15581
+ `Requests to ${targetHost(target)} carry no token: the target authenticates them itself ($${AUTH_ENV_VAR}=proxy).`,
15582
+ { kind: "proxy", host: targetHost(target) }
15583
+ );
15584
+ return;
15585
+ }
15564
15586
  const identity = await identify(client);
15565
15587
  const stored = process.env[TOKEN_ENV_VAR] ? void 0 : await storedToken(target);
15566
15588
  const expiry = stored?.expiresAt ? ` Expires ${stored.expiresAt.slice(0, 16).replace("T", " ")} UTC.` : "";
@@ -16672,7 +16694,7 @@ Pass --workspace <id> to a command, or set SOFTR_WORKSPACE_ID, to work in one of
16672
16694
  // package.json
16673
16695
  var package_default = {
16674
16696
  name: "@softr/cli",
16675
- version: "0.1.0",
16697
+ version: "0.1.1-se21216.7",
16676
16698
  description: "The Softr command line: build, test and publish Softr workflows from local files; more Softr APIs to come",
16677
16699
  keywords: [
16678
16700
  "softr",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1-se21216.7",
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",