@tailor-platform/sdk 1.75.0 → 1.76.1

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.
@@ -0,0 +1,3 @@
1
+ import { n as generatePluginFilesIfNeeded, r as loadApplication, t as defineApplication } from "./application-BUfYmf0O.mjs";
2
+
3
+ export { defineApplication, generatePluginFilesIfNeeded };
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { B as FunctionExecution_Type, Ct as PATScope, T as CustomDomainStatus, gt as AuthInvokerSchema } from "../service_pb-4unFyubn.mjs";
3
2
  import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
4
3
  import { n as logger, r as styles } from "../logger-BEiZZ3qT.mjs";
5
- import { $t as compareSnapshotWithRemote, A as listCommand$12, An as apiCommand, Bn as pagedLogArgs, C as listCommand$13, Cn as hasChanges, Dn as PluginManager, Dt as listCommand$6, E as waitCommand, En as sdkNameLabelKey, F as generateCommand$1, Fn as configArg, G as removeCommand$1, Gt as deploy, H as extractOwnedNamespaces, Hn as toPageDirection, Ht as formatKeyValueTable, I as generateMigrationScript, In as confirmationArgs, It as getCommand$6, K as updateCommand$3, Kt as ensureConfigId, L as writeDbTypesFile, Ln as deploymentArgs, Mn as assertWritable, N as truncateCommand, Nn as defineAppCommand, O as resumeCommand, On as generateUserTypes, Pn as commonArgs, Pt as startCommand, Q as getCommand$5, Qt as parseMigrationLabelNumber, R as getConfiguredEditorCommand, Rn as isVerbose, Rt as executionsCommand, Sn as formatMigrationDiff, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Un as workspaceArgs, Ut as getCommand$1, V as showCommand, Vn as paginationArgs, Vt as functionExecutionStatusToString, X as listCommand$11, Xt as MIGRATION_LABEL_KEY, Y as treeCommand, Zt as handleOptionalToRequiredError, _n as loadDiff, b as createCommand$4, bn as parseMigrationNumberArg, c as listCommand$14, cn as compareLocalTypesWithSnapshot, ct as createCommand$3, dn as getLatestMigrationNumber, en as generateAllTypeManifestsFromSnapshot, et as updateCommand$2, f as restoreCommand, ft as getCommand$3, g as getCommand$7, gn as isValidMigrationNumber, gt as listCommand$8, ht as tokenCommand, i as updateCommand$4, in as INITIAL_SCHEMA_NUMBER, it as getCommand$4, jt as jobsCommand, kn as prompt, m as listCommand$15, mn as getMigrationFiles, nt as listCommand$10, o as removeCommand, ot as deleteCommand$4, pn as getMigrationFilePath, qt as executeScript, r as queryCommand, sn as assertValidMigrationFiles, t as isNativeTypeScriptRuntime, tn as protoGqlPermission, u as inviteCommand, un as createSnapshotFromLocalTypes, ut as listCommand$9, v as deleteCommand$5, vn as reconstructSnapshotFromMigrations, vt as generate, wn as getNamespacesWithMigrations, wt as webhookCommand, xt as getCommand$2, yn as formatMigrationNumber, yt as listCommand$7, z as openInConfiguredEditor, zn as multiConfigArg } from "../runtime-CJ5usBOu.mjs";
6
- import { A as hasAnyUserTokenEntry, B as readPlatformConfig, D as loadConfig, F as loadMachineUserName, G as defaultPlatformBaseUrl, H as saveUserTokens, K as fetchAll, L as loadStoredUserTokens, M as loadAccessToken, O as deleteUserTokens, P as loadConsoleBaseUrl, R as loadWorkspaceId, T as hashContent$1, U as writePlatformConfig, V as resolveUserTokenKey, W as closeConnectionPool, X as fetchPlatformMachineUserToken, Y as fetchPaged, Z as fetchUserInfo, _ as platformBundleDefinePlugin, a as resolveInlineSourcemap, b as resolveBundleLogLevel, c as ResolverSchema, et as getOrNull, it as isDefaultPlatform, j as hasUserTokenEntry, k as fetchLatestToken, nt as initOAuth2Client, q as fetchAllTolerant, rt as initOperatorClient, s as WorkflowJobSchema, t as defineApplication, u as INVOKER_EXPR, v as composeFunctionTreeshakeOptions, w as getDistDir, y as createLogLevelTreeshakeOptions, z as platformConfigFromProfile } from "../application-DYshsH-K.mjs";
4
+ import { A as hasAnyUserTokenEntry, B as readPlatformConfig, Ct as toPageDirection, D as loadConfig, F as loadMachineUserName, G as defaultPlatformBaseUrl, H as saveUserTokens, K as fetchAll, L as loadStoredUserTokens, M as loadAccessToken, O as deleteUserTokens, P as loadConsoleBaseUrl, R as loadWorkspaceId, St as resolveMachineUserInputSource, T as hashContent$1, U as writePlatformConfig, V as resolveUserTokenKey, W as closeConnectionPool, X as fetchPlatformMachineUserToken, Y as fetchPaged, Z as fetchUserInfo, _ as platformBundleDefinePlugin, a as resolveInlineSourcemap, b as resolveBundleLogLevel, c as ResolverSchema, ct as configArg, et as getOrNull, it as isDefaultPlatform, j as hasUserTokenEntry, k as fetchLatestToken, lt as confirmationArgs, mt as multiConfigArg, nt as initOAuth2Client, pt as isVerbose, q as fetchAllTolerant, rt as initOperatorClient, s as WorkflowJobSchema, st as commonArgs, t as defineApplication, u as INVOKER_EXPR, ut as deploymentArgs, v as composeFunctionTreeshakeOptions, vt as pagedLogArgs, w as getDistDir, wt as workspaceArgs, y as createLogLevelTreeshakeOptions, yt as paginationArgs, z as platformConfigFromProfile } from "../application-BUfYmf0O.mjs";
5
+ import { $t as compareSnapshotWithRemote, A as listCommand$12, An as apiCommand, C as listCommand$13, Cn as hasChanges, Dn as PluginManager, Dt as listCommand$6, E as waitCommand, En as sdkNameLabelKey, F as generateCommand$1, G as removeCommand$1, Gt as deploy, H as extractOwnedNamespaces, Ht as formatKeyValueTable, I as generateMigrationScript, It as getCommand$6, K as updateCommand$3, Kt as ensureConfigId, L as writeDbTypesFile, Mn as assertWritable, N as truncateCommand, Nn as defineAppCommand, O as resumeCommand, On as generateUserTypes, Pt as startCommand, Q as getCommand$5, Qt as parseMigrationLabelNumber, R as getConfiguredEditorCommand, Rt as executionsCommand, Sn as formatMigrationDiff, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Ut as getCommand$1, V as showCommand, Vt as functionExecutionStatusToString, X as listCommand$11, Xt as MIGRATION_LABEL_KEY, Y as treeCommand, Zt as handleOptionalToRequiredError, _n as loadDiff, b as createCommand$4, bn as parseMigrationNumberArg, c as listCommand$14, cn as compareLocalTypesWithSnapshot, ct as createCommand$3, dn as getLatestMigrationNumber, en as generateAllTypeManifestsFromSnapshot, et as updateCommand$2, f as restoreCommand, ft as getCommand$3, g as getCommand$7, gn as isValidMigrationNumber, gt as listCommand$8, ht as tokenCommand, i as updateCommand$4, in as INITIAL_SCHEMA_NUMBER, it as getCommand$4, jt as jobsCommand, kn as prompt, m as listCommand$15, mn as getMigrationFiles, nt as listCommand$10, o as removeCommand, ot as deleteCommand$4, pn as getMigrationFilePath, qt as executeScript, r as queryCommand, sn as assertValidMigrationFiles, t as isNativeTypeScriptRuntime, tn as protoGqlPermission, u as inviteCommand, un as createSnapshotFromLocalTypes, ut as listCommand$9, v as deleteCommand$5, vn as reconstructSnapshotFromMigrations, vt as generate, wn as getNamespacesWithMigrations, wt as webhookCommand, xt as getCommand$2, yn as formatMigrationNumber, yt as listCommand$7, z as openInConfiguredEditor } from "../runtime-_vUgjiqT.mjs";
7
6
  import { n as ExecutorSchema } from "../service-BCRJ-3NV.mjs";
8
7
  import { t as multiline } from "../multiline-sfHpTZZK.mjs";
9
8
  import { r as isPluginGeneratedType } from "../seed-fm0LeYP2.mjs";
10
9
  import { t as readPackageJson } from "../package-json-8b0O9TlX.mjs";
11
10
  import { i as userAgent } from "../secret-file-VSVGy1V0.mjs";
12
11
  import { n as isCLIError } from "../errors-Dtf2WPaW.mjs";
12
+ import { B as FunctionExecution_Type, Ct as PATScope, T as CustomDomainStatus, gt as AuthInvokerSchema } from "../service_pb-B5w9rjPY.mjs";
13
13
  import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-ayWANEZ6.mjs";
14
14
  import { arg, defineCommand, runCommand, runMain } from "politty";
15
15
  import { withCompletionCommand } from "politty/completion";
@@ -1440,6 +1440,7 @@ When a \`.js\` file is provided, detection and bundling are skipped and the file
1440
1440
  const authNamespace = resolveAuthNamespace(config.auth);
1441
1441
  const machineUserName = await resolveMachineUserName({
1442
1442
  cliMachineUser: args["machine-user"],
1443
+ cliMachineUserSource: resolveMachineUserInputSource(args["machine-user"]),
1443
1444
  profile: args.profile,
1444
1445
  authConfig: config.auth
1445
1446
  });
@@ -1556,9 +1557,10 @@ function resolveAuthNamespace(authConfig) {
1556
1557
  * @returns Resolved machine user name
1557
1558
  */
1558
1559
  async function resolveMachineUserName(options) {
1559
- const { cliMachineUser, profile, authConfig } = options;
1560
+ const { cliMachineUser, cliMachineUserSource, profile, authConfig } = options;
1560
1561
  const resolved = await loadMachineUserName({
1561
1562
  machineUser: cliMachineUser,
1563
+ machineUserSource: cliMachineUserSource,
1562
1564
  profile
1563
1565
  });
1564
1566
  if (resolved) return resolved;
@@ -1737,8 +1739,47 @@ const redirectUri = `http://localhost:${redirectPort}/callback`;
1737
1739
  function randomState() {
1738
1740
  return crypto.randomBytes(32).toString("base64url");
1739
1741
  }
1740
- function assertProfileLoginUser(args, authenticatedUser) {
1741
- if (args.profile && args.profileUser && authenticatedUser !== args.profileUser) throw new Error(`Profile "${args.profile}" is configured for "${args.profileUser}", but login authenticated "${authenticatedUser}".`);
1742
+ function getProfileUserMismatch(args, authenticatedUser) {
1743
+ if (!args.profile || !args.profileUser || authenticatedUser === args.profileUser) return;
1744
+ return {
1745
+ profile: args.profile,
1746
+ oldUser: args.profileUser,
1747
+ authenticatedUser,
1748
+ loginMode: args.loginMode ?? "user"
1749
+ };
1750
+ }
1751
+ function quoteCommandArg(value, platform = process.platform) {
1752
+ if (platform === "win32") {
1753
+ if (/^[A-Za-z0-9_./:@+=,-]+$/.test(value)) return value;
1754
+ return;
1755
+ }
1756
+ return `'${value.replaceAll("'", "'\\''")}'`;
1757
+ }
1758
+ function profileUpdateCommand(mismatch) {
1759
+ const profileArg = quoteCommandArg(mismatch.profile);
1760
+ const userArg = quoteCommandArg(mismatch.authenticatedUser);
1761
+ if (profileArg && userArg) return `tailor-sdk profile update --user ${userArg} -- ${profileArg}`;
1762
+ return [
1763
+ "tailor-sdk profile update --user <authenticated-user> -- <profile>",
1764
+ `profile = ${JSON.stringify(mismatch.profile)}`,
1765
+ `authenticated user = ${JSON.stringify(mismatch.authenticatedUser)}`
1766
+ ].join("\n");
1767
+ }
1768
+ function retryInstruction(mismatch) {
1769
+ if (mismatch.loginMode === "machine-user") return "Then retry the original machine-user login command.";
1770
+ const profileArg = quoteCommandArg(mismatch.profile);
1771
+ if (!profileArg) return "Then retry the browser login with the same profile value.";
1772
+ return `Then run:\n tailor-sdk login --profile ${profileArg}`;
1773
+ }
1774
+ function profileUserMismatchError(mismatch) {
1775
+ const updateCommand = profileUpdateCommand(mismatch);
1776
+ const nextStep = retryInstruction(mismatch);
1777
+ return new Error(multiline`
1778
+ Profile "${mismatch.profile}" is configured for "${mismatch.oldUser}", but login authenticated "${mismatch.authenticatedUser}".
1779
+ The authenticated user has been saved. To use it with this profile, run:
1780
+ ${updateCommand}
1781
+ ${nextStep}
1782
+ `);
1742
1783
  }
1743
1784
  function shouldUpdateCurrentUser(profile, platformConfig) {
1744
1785
  if (!profile) return true;
@@ -1758,12 +1799,16 @@ const startAuthServer = async (args = {}) => {
1758
1799
  codeVerifier
1759
1800
  });
1760
1801
  const userInfo = await fetchUserInfo(tokens.accessToken, args.platformConfig);
1761
- assertProfileLoginUser(args, userInfo.email);
1762
1802
  const pfConfig = await readPlatformConfig();
1763
1803
  await saveUserTokens(pfConfig, userInfo.email, {
1764
1804
  accessToken: tokens.accessToken,
1765
1805
  refreshToken: tokens.refreshToken ?? void 0
1766
1806
  }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), args.platformConfig);
1807
+ const mismatch = getProfileUserMismatch(args, userInfo.email);
1808
+ if (mismatch) {
1809
+ writePlatformConfig(pfConfig);
1810
+ throw profileUserMismatchError(mismatch);
1811
+ }
1767
1812
  if (args.updateCurrentUser ?? true) pfConfig.current_user = userInfo.email;
1768
1813
  writePlatformConfig(pfConfig);
1769
1814
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -1806,11 +1851,15 @@ const startAuthServer = async (args = {}) => {
1806
1851
  });
1807
1852
  };
1808
1853
  async function loginAsMachineUser(args) {
1809
- assertProfileLoginUser(args, args.clientId);
1810
1854
  const clientSecret = args.clientSecret ?? await prompt.password({ message: "Client secret" });
1811
1855
  const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret, args.platformConfig);
1812
1856
  const pfConfig = await readPlatformConfig();
1813
1857
  await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), args.platformConfig);
1858
+ const mismatch = getProfileUserMismatch(args, args.clientId);
1859
+ if (mismatch) {
1860
+ writePlatformConfig(pfConfig);
1861
+ throw profileUserMismatchError(mismatch);
1862
+ }
1814
1863
  if (args.updateCurrentUser ?? true) pfConfig.current_user = args.clientId;
1815
1864
  writePlatformConfig(pfConfig);
1816
1865
  }
@@ -1843,31 +1892,36 @@ const loginCommand = defineAppCommand({
1843
1892
  })
1844
1893
  }).strict().describe("Machine User Login")]),
1845
1894
  run: async (args) => {
1846
- let platformConfig;
1847
- let profileUser;
1848
- if ("profile" in args && args.profile) {
1849
- const profileEntry = (await readPlatformConfig()).profiles[args.profile];
1850
- if (!profileEntry) throw new Error(`Profile "${args.profile}" not found`);
1851
- platformConfig = platformConfigFromProfile(profileEntry);
1852
- profileUser = profileEntry.user;
1895
+ try {
1896
+ let platformConfig;
1897
+ let profileUser;
1898
+ if ("profile" in args && args.profile) {
1899
+ const profileEntry = (await readPlatformConfig()).profiles[args.profile];
1900
+ if (!profileEntry) throw new Error(`Profile "${args.profile}" not found`);
1901
+ platformConfig = platformConfigFromProfile(profileEntry);
1902
+ profileUser = profileEntry.user;
1903
+ }
1904
+ const updateCurrentUser = shouldUpdateCurrentUser(args.profile, platformConfig);
1905
+ if ("machine-user" in args) await loginAsMachineUser({
1906
+ clientId: args.clientId,
1907
+ clientSecret: args.clientSecret,
1908
+ profile: args.profile,
1909
+ profileUser,
1910
+ platformConfig,
1911
+ updateCurrentUser,
1912
+ loginMode: "machine-user"
1913
+ });
1914
+ else await startAuthServer({
1915
+ profile: args.profile,
1916
+ profileUser,
1917
+ platformConfig,
1918
+ updateCurrentUser,
1919
+ loginMode: "user"
1920
+ });
1921
+ logger.success("Successfully logged in to Tailor Platform.");
1922
+ } finally {
1923
+ await closeConnectionPool();
1853
1924
  }
1854
- const updateCurrentUser = shouldUpdateCurrentUser(args.profile, platformConfig);
1855
- if ("machine-user" in args) await loginAsMachineUser({
1856
- clientId: args.clientId,
1857
- clientSecret: args.clientSecret,
1858
- profile: args.profile,
1859
- profileUser,
1860
- platformConfig,
1861
- updateCurrentUser
1862
- });
1863
- else await startAuthServer({
1864
- profile: args.profile,
1865
- profileUser,
1866
- platformConfig,
1867
- updateCurrentUser
1868
- });
1869
- logger.success("Successfully logged in to Tailor Platform.");
1870
- await closeConnectionPool();
1871
1925
  }
1872
1926
  });
1873
1927
 
@@ -2822,7 +2876,7 @@ function findTarget(lock, kind, workspaceName) {
2822
2876
 
2823
2877
  //#endregion
2824
2878
  //#region src/cli/commands/setup/action.yml
2825
- var action_default = "# __HEADER__\nname: __WORKSPACE_NAME__\ndescription: >-\n Deploy __WORKSPACE_NAME__ to Tailor Platform.\n Checkout and dependency installation are expected to be handled by the caller.\n\ninputs:\n workspace-id:\n description: Tailor Platform workspace id\n required: true\n name:\n description: Workspace name (used in Slack notifications)\n required: true\n working-directory:\n description: App directory relative to the repository root\n required: false\n default: \".\"\n platform-client-id:\n description: Machine-user client id\n required: true\n platform-client-secret:\n description: Machine-user client secret\n required: true\n slack-token:\n description: Slack bot token\n required: false\n slack-channel-id:\n description: Slack channel id\n required: false\n user-mapping:\n description: JSON string mapping GitHub usernames to Slack user IDs\n required: false\n package-manager:\n description: Package manager used for dependency installation (pnpm, npm, yarn, bun)\n required: false\n default: pnpm\n\noutputs:\n app-url:\n description: Deployed application URL\n value: ${{ steps.tailor-apply.outputs.app-url }}\n\nruns:\n using: composite\n steps:\n # __STATIC_WEBSITE_BUILD_START__\n - id: build-site\n shell: bash\n run: |\n # Replace with your static website build command.\n # Example: pnpm --filter <package-name> run build:static\n # Note: this run: command is user-editable. Custom build commands are preserved across\n # reruns and are not treated as drift by setup check. Other edits to this file are flagged\n # as drift and will be overwritten if you re-run setup action with --force.\n true\n # __STATIC_WEBSITE_BUILD_END__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ inputs.workspace-id }}\n working-directory: ${{ inputs.working-directory }}\n package-manager: ${{ inputs.package-manager }}\n platform-client-id: ${{ inputs.platform-client-id }}\n platform-client-secret: ${{ inputs.platform-client-secret }}\n - id: tailor-notify\n if: failure() && inputs.slack-token != '' && inputs.slack-channel-id != ''\n uses: tailor-platform/actions/notify@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n provider: slack\n status: failure\n workspace-name: ${{ inputs.name }}\n slack-token: ${{ inputs.slack-token }}\n slack-channel-id: ${{ inputs.slack-channel-id }}\n user-mapping: ${{ inputs.user-mapping }}\n";
2879
+ var action_default = "# __HEADER__\nname: __WORKSPACE_NAME__\ndescription: >-\n Deploy __WORKSPACE_NAME__ to Tailor Platform.\n Checkout and dependency installation are expected to be handled by the caller.\n\ninputs:\n workspace-id:\n description: Tailor Platform workspace id\n required: true\n name:\n description: Workspace name (used in Slack notifications)\n required: true\n working-directory:\n description: App directory relative to the repository root\n required: false\n default: \".\"\n config:\n description: SDK config path, or comma-separated config paths for multi-config deploys\n required: false\n default: \"tailor.config.ts\"\n deploy:\n description: Whether to run the Tailor deploy step after build steps\n required: false\n default: \"true\"\n build-site:\n description: Whether to run the static website build slot when present\n required: false\n default: \"true\"\n platform-client-id:\n description: Machine-user client id\n required: true\n platform-client-secret:\n description: Machine-user client secret\n required: true\n slack-token:\n description: Slack bot token\n required: false\n slack-channel-id:\n description: Slack channel id\n required: false\n user-mapping:\n description: JSON string mapping GitHub usernames to Slack user IDs\n required: false\n package-manager:\n description: Package manager used for dependency installation (pnpm, npm, yarn, bun)\n required: false\n default: pnpm\n\noutputs:\n app-url:\n description: Deployed application URL\n value: ${{ steps.tailor-apply.outputs.app-url }}\n\nruns:\n using: composite\n steps:\n # __STATIC_WEBSITE_BUILD_START__\n - id: build-site\n if: inputs.build-site == 'true'\n shell: bash\n run: |\n # Replace with your static website build command.\n # Example: pnpm --filter <package-name> run build:static\n # Note: this run: command is user-editable. Custom build commands are preserved across\n # reruns and are not treated as drift by setup check. Other edits to this file are flagged\n # as drift and will be overwritten if you re-run setup action with --force.\n true\n # __STATIC_WEBSITE_BUILD_END__\n - id: tailor-apply\n if: inputs.deploy == 'true'\n uses: tailor-platform/actions/deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n env:\n TAILOR_PLATFORM_SDK_CONFIG_PATH: ${{ inputs.config }}\n with:\n workspace-id: ${{ inputs.workspace-id }}\n working-directory: ${{ inputs.working-directory }}\n package-manager: ${{ inputs.package-manager }}\n platform-client-id: ${{ inputs.platform-client-id }}\n platform-client-secret: ${{ inputs.platform-client-secret }}\n - id: tailor-notify\n if: failure() && inputs.slack-token != '' && inputs.slack-channel-id != ''\n uses: tailor-platform/actions/notify@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n provider: slack\n status: failure\n workspace-name: ${{ inputs.name }}\n slack-token: ${{ inputs.slack-token }}\n slack-channel-id: ${{ inputs.slack-channel-id }}\n user-mapping: ${{ inputs.user-mapping }}\n";
2826
2880
 
2827
2881
  //#endregion
2828
2882
  //#region src/cli/commands/setup/branch.workflow.yml
@@ -3019,7 +3073,7 @@ const ACTIONS_SHA = "e74173d4bdd931d036ad359cf5f7729fc6aa7067";
3019
3073
  * @returns Rendered YAML and the list of managed job/step ids
3020
3074
  */
3021
3075
  function renderCoordinateWorkflow(params) {
3022
- const { coordinatorName, kind, apps, environment, packageManager } = params;
3076
+ const { coordinatorName, kind, actionGroups, environment, packageManager } = params;
3023
3077
  const isTag = kind === "tag";
3024
3078
  const branch = params.branch;
3025
3079
  const tagPattern = params.tagPattern ?? "v*";
@@ -3054,29 +3108,60 @@ function renderCoordinateWorkflow(params) {
3054
3108
  ` with:`,
3055
3109
  ` package-manager: ${packageManager}`
3056
3110
  ].join("\n");
3057
- const perAppPlanSteps = apps.flatMap((app) => {
3111
+ const configPath = (app) => app.dir === "." ? "tailor.config.ts" : `${app.dir}/tailor.config.ts`;
3112
+ const groupConfigPath = (group) => group.apps.map(configPath).join(",");
3113
+ const firstApp = (group) => {
3114
+ const [app] = group.apps;
3115
+ if (!app) throw new Error("Coordinate action groups must contain at least one app.");
3116
+ return app;
3117
+ };
3118
+ const singleAppPlanStep = (group, app) => {
3058
3119
  const wd = app.dir !== "." ? app.dir : void 0;
3059
3120
  const wdLine = wd ? `\n working-directory: ${wd}` : "";
3060
3121
  return [
3061
- `- id: tailor-generate-check-${app.name}`,
3062
- ` uses: tailor-platform/actions/generate-check@${ACTIONS_SHA} # v1.5.1`,
3063
- ` with:`,
3064
- ` package-manager: ${packageManager}${wdLine}`,
3065
- `- id: tailor-plan-${app.name}`,
3122
+ `- id: tailor-plan-${group.id}`,
3066
3123
  ` if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork`,
3067
3124
  ` uses: tailor-platform/actions/plan@${ACTIONS_SHA} # v1.5.1`,
3068
3125
  ` with:`,
3069
3126
  ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3070
3127
  ` package-manager: ${packageManager}${wdLine}`,
3071
- ` label: ${coordinatorName}/${app.name}`,
3128
+ ` label: ${coordinatorName}/${group.id}`,
3072
3129
  ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3073
3130
  ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3074
3131
  ` github-token: \${{ secrets.GITHUB_TOKEN }}`
3075
3132
  ];
3133
+ };
3134
+ const multiConfigPlanStep = (group) => [
3135
+ `- id: tailor-plan-${group.id}`,
3136
+ ` if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork`,
3137
+ ` uses: tailor-platform/actions/plan@${ACTIONS_SHA} # v1.5.1`,
3138
+ ` env:`,
3139
+ ` TAILOR_PLATFORM_SDK_CONFIG_PATH: ${groupConfigPath(group)}`,
3140
+ ` with:`,
3141
+ ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3142
+ ` package-manager: ${packageManager}`,
3143
+ ` label: ${coordinatorName}/${group.id}`,
3144
+ ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3145
+ ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3146
+ ` github-token: \${{ secrets.GITHUB_TOKEN }}`
3147
+ ];
3148
+ const perAppPlanSteps = actionGroups.flatMap((group) => {
3149
+ const generateCheckSteps = group.apps.flatMap((app) => {
3150
+ const wd = app.dir !== "." ? app.dir : void 0;
3151
+ const wdLine = wd ? `\n working-directory: ${wd}` : "";
3152
+ return [
3153
+ `- id: tailor-generate-check-${app.name}`,
3154
+ ` uses: tailor-platform/actions/generate-check@${ACTIONS_SHA} # v1.5.1`,
3155
+ ` with:`,
3156
+ ` package-manager: ${packageManager}${wdLine}`
3157
+ ];
3158
+ });
3159
+ const planStep = group.apps.length === 1 ? singleAppPlanStep(group, firstApp(group)) : multiConfigPlanStep(group);
3160
+ return [...generateCheckSteps, ...planStep];
3076
3161
  }).join("\n");
3077
3162
  out = line(out, "PER_APP_PLAN_STEPS", `${driftCheckStep}\n${perAppPlanSteps}`);
3078
- const deploySteps = apps.flatMap((app) => [
3079
- `- id: tailor-deploy-${app.name}`,
3163
+ const singleAppDeployStep = (group, app) => [
3164
+ `- id: tailor-deploy-${group.id}`,
3080
3165
  ` uses: ./.github/actions/tailor-${app.name}`,
3081
3166
  ` with:`,
3082
3167
  ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
@@ -3088,7 +3173,39 @@ function renderCoordinateWorkflow(params) {
3088
3173
  ` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
3089
3174
  ` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`,
3090
3175
  ` # editable: api-url:`
3091
- ]).join("\n");
3176
+ ];
3177
+ const buildSiteStep = (app) => [
3178
+ `- id: tailor-build-site-${app.name}`,
3179
+ ` uses: ./.github/actions/tailor-${app.name}`,
3180
+ ` with:`,
3181
+ ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3182
+ ` name: ${app.name}`,
3183
+ ` working-directory: ${app.dir}`,
3184
+ ` deploy: "false"`,
3185
+ ` package-manager: ${packageManager}`,
3186
+ ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3187
+ ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3188
+ ` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
3189
+ ` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`
3190
+ ];
3191
+ const multiConfigDeployStep = (group) => [
3192
+ ...group.apps.filter((app) => app.hasStaticWebsites).flatMap(buildSiteStep),
3193
+ `- id: tailor-deploy-${group.id}`,
3194
+ ` uses: ./.github/actions/tailor-${firstApp(group).name}`,
3195
+ ` with:`,
3196
+ ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3197
+ ` name: ${coordinatorName}/${group.id}`,
3198
+ ` working-directory: .`,
3199
+ ` config: ${groupConfigPath(group)}`,
3200
+ ` build-site: "false"`,
3201
+ ` package-manager: ${packageManager}`,
3202
+ ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3203
+ ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3204
+ ` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
3205
+ ` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`,
3206
+ ` # editable: api-url:`
3207
+ ];
3208
+ const deploySteps = actionGroups.flatMap((group) => group.apps.length === 1 ? singleAppDeployStep(group, firstApp(group)) : multiConfigDeployStep(group)).join("\n");
3092
3209
  out = line(out, "PER_APP_DEPLOY_STEPS", deploySteps);
3093
3210
  out = out.replaceAll("__WORKSPACE_NAME__", () => coordinatorName);
3094
3211
  out = out.replaceAll("__ENVIRONMENT__", () => environment);
@@ -3097,9 +3214,15 @@ function renderCoordinateWorkflow(params) {
3097
3214
  const generatedIds = [];
3098
3215
  if (isTag && branch !== void 0) generatedIds.push("tailor-tag-guard", "tailor-tag-guard/tailor-checkout", "tailor-tag-guard/tailor-tag-guard");
3099
3216
  generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-drift-check");
3100
- for (const app of apps) generatedIds.push(`tailor-plan/tailor-generate-check-${app.name}`, `tailor-plan/tailor-plan-${app.name}`);
3217
+ for (const group of actionGroups) {
3218
+ for (const app of group.apps) generatedIds.push(`tailor-plan/tailor-generate-check-${app.name}`);
3219
+ generatedIds.push(`tailor-plan/tailor-plan-${group.id}`);
3220
+ }
3101
3221
  generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup");
3102
- for (const app of apps) generatedIds.push(`tailor-deploy/tailor-deploy-${app.name}`);
3222
+ for (const group of actionGroups) {
3223
+ for (const app of group.apps) if (group.apps.length > 1 && app.hasStaticWebsites) generatedIds.push(`tailor-deploy/tailor-build-site-${app.name}`);
3224
+ generatedIds.push(`tailor-deploy/tailor-deploy-${group.id}`);
3225
+ }
3103
3226
  return {
3104
3227
  content: out,
3105
3228
  generatedIds
@@ -3137,6 +3260,25 @@ function renderTailorSetupAction(params) {
3137
3260
 
3138
3261
  //#endregion
3139
3262
  //#region src/cli/commands/setup/generate.ts
3263
+ function actionName(input) {
3264
+ return input.startsWith("tailor-") ? input.slice(7) : input;
3265
+ }
3266
+ function splitActionGroup(input) {
3267
+ const names = input.split(",").map((entry) => actionName(entry.trim()));
3268
+ if (names.some((name) => name.length === 0)) throw new Error("--action must contain one or more non-empty action names.");
3269
+ return names;
3270
+ }
3271
+ function uniqueGroupId(names, usedIds) {
3272
+ const base = names.join("-");
3273
+ let id = base;
3274
+ let suffix = 2;
3275
+ while (usedIds.has(id)) {
3276
+ id = `${base}-${suffix}`;
3277
+ suffix += 1;
3278
+ }
3279
+ usedIds.add(id);
3280
+ return id;
3281
+ }
3140
3282
  async function defaultLoadConfigName(configPath) {
3141
3283
  const { config } = await loadConfig(configPath);
3142
3284
  return config.name;
@@ -3327,7 +3469,7 @@ async function resolve$1(options) {
3327
3469
  configPath
3328
3470
  };
3329
3471
  }
3330
- const BUILD_SITE_RUN_RE = /([ \t]*- id: build-site\n[ \t]+shell: bash\n[ \t]+run: \|)([\s\S]*?)(\n[ \t]*- |\n*$)/;
3472
+ const BUILD_SITE_RUN_RE = /([ \t]*- id: build-site\n(?:[ \t]+if: [^\n]+\n)?[ \t]+shell: bash\n[ \t]+run: \|)([\s\S]*?)(\n[ \t]*- |\n*$)/;
3331
3473
  /**
3332
3474
  * Strip the mutable run: body of the build-site step before hashing.
3333
3475
  * When hasStaticWebsites is true the step is present; users are expected to
@@ -3469,7 +3611,7 @@ async function setupTarget(options) {
3469
3611
  kind: resolved.kind,
3470
3612
  workspaceName: resolved.workspaceName,
3471
3613
  file: resolved.file,
3472
- templateVersion: 6,
3614
+ templateVersion: 7,
3473
3615
  inputs: resolved.inputs,
3474
3616
  generatedIds: resolved.render.generatedIds,
3475
3617
  ejectedIds: existing?.ejectedIds ?? [],
@@ -3527,23 +3669,33 @@ async function setupCoordinate(options) {
3527
3669
  const packageManager = detectPackageManager(outputDir);
3528
3670
  const lock = readLock(outputDir);
3529
3671
  if (!lock) throw new Error(".github/tailor-sdk.lock not found. Run `tailor-sdk setup action --name <name>` for each app before running setup coordinate.");
3672
+ const actionTargets = new Map(lock.targets.filter((t) => t.kind === "action").map((target) => [target.workspaceName, target]));
3530
3673
  const seenNames = /* @__PURE__ */ new Set();
3531
- const apps = actions.map((actionName) => {
3532
- const name = actionName.startsWith("tailor-") ? actionName.slice(7) : actionName;
3533
- if (seenNames.has(name)) throw new Error(`Duplicate --action "${name}". Each composite action can only appear once in a coordinator.`);
3534
- seenNames.add(name);
3535
- const entry = lock.targets.find((t) => t.kind === "action" && t.workspaceName === name);
3536
- if (!entry) throw new Error(`Action target "${name}" not found in .github/tailor-sdk.lock. Run \`tailor-sdk setup action --name ${name}\` first.`);
3537
- validateDir(entry.inputs.dir);
3674
+ const usedGroupIds = /* @__PURE__ */ new Set();
3675
+ const actionGroups = actions.map((actionGroup) => {
3676
+ const names = splitActionGroup(actionGroup);
3677
+ const apps = names.map((name) => {
3678
+ if (seenNames.has(name)) throw new Error(`Duplicate --action "${name}". Each composite action can only appear once in a coordinator.`);
3679
+ seenNames.add(name);
3680
+ const entry = actionTargets.get(name);
3681
+ if (!entry) throw new Error(`Action target "${name}" not found in .github/tailor-sdk.lock. Run \`tailor-sdk setup action --name ${name}\` first.`);
3682
+ if (names.length > 1 && entry.templateVersion < 7) throw new Error(`Action target "${name}" was generated with an older setup template. Run \`tailor-sdk setup action --name ${name} --force\` before grouping it in setup coordinate.`);
3683
+ validateDir(entry.inputs.dir);
3684
+ return {
3685
+ name,
3686
+ dir: entry.inputs.dir,
3687
+ hasStaticWebsites: entry.inputs.hasStaticWebsites
3688
+ };
3689
+ });
3538
3690
  return {
3539
- name,
3540
- dir: entry.inputs.dir
3691
+ id: uniqueGroupId(names, usedGroupIds),
3692
+ apps
3541
3693
  };
3542
3694
  });
3543
3695
  const render = renderCoordinateWorkflow({
3544
3696
  coordinatorName,
3545
3697
  kind: coordinateKind,
3546
- apps,
3698
+ actionGroups,
3547
3699
  branch: branch ?? void 0,
3548
3700
  tagPattern: coordinateKind === "tag" ? tagPattern : void 0,
3549
3701
  environment,
@@ -3579,7 +3731,7 @@ async function setupCoordinate(options) {
3579
3731
  kind: "coordinate",
3580
3732
  workspaceName: coordinatorName,
3581
3733
  file,
3582
- templateVersion: 6,
3734
+ templateVersion: 7,
3583
3735
  inputs: {
3584
3736
  branch,
3585
3737
  branchAutoDetected: coordinateKind === "branch" ? branchAutoDetected : branch !== null ? false : void 0,
@@ -3587,7 +3739,7 @@ async function setupCoordinate(options) {
3587
3739
  environment,
3588
3740
  dir: ".",
3589
3741
  packageManager,
3590
- actionDirs: apps.map((a) => a.dir)
3742
+ actionDirs: actionGroups.flatMap((group) => group.apps.map((a) => a.dir))
3591
3743
  },
3592
3744
  generatedIds: render.generatedIds,
3593
3745
  ejectedIds: existing?.ejectedIds ?? [],
@@ -3611,8 +3763,8 @@ async function setupCoordinate(options) {
3611
3763
  logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env ${environment}`);
3612
3764
  logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env ${environment}`);
3613
3765
  logger.newline();
3614
- logger.log(`2. Provision each workspace and set TAILOR_PLATFORM_WORKSPACE_ID on the "${environment}" environment:`);
3615
- logger.log(" tailor-sdk workspace create # one per app; copy the id");
3766
+ logger.log(`2. Provision the target workspace and set TAILOR_PLATFORM_WORKSPACE_ID on the "${environment}" environment:`);
3767
+ logger.log(" tailor-sdk workspace create # if it does not exist yet; copy the id");
3616
3768
  logger.log(` gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env ${environment}`);
3617
3769
  logger.newline();
3618
3770
  logger.log("3. Commit the generated files:");
@@ -3767,7 +3919,7 @@ async function checkGitHub(options) {
3767
3919
  currentHash,
3768
3920
  configExists,
3769
3921
  defaultBranch,
3770
- templateVersion: 6,
3922
+ templateVersion: 7,
3771
3923
  erdNamespaces,
3772
3924
  hasMigrations,
3773
3925
  hasSeeds,
@@ -3805,7 +3957,7 @@ function resolveMatch(outputDir, targets, input) {
3805
3957
  }
3806
3958
  function warnCoordinatorReferences(allTargets, deletedKeys, match) {
3807
3959
  if (match.target.kind !== "action") return;
3808
- for (const coordinator of allTargets) if (coordinator.kind === "coordinate" && coordinator.inputs.actionDirs?.includes(match.target.inputs.dir) && !deletedKeys.has(targetKey(coordinator))) logger.warn(`Coordinator "${coordinator.workspaceName}" still references this action's directory (${match.target.inputs.dir}). Re-run \`tailor-sdk setup coordinate\` without --action ${match.target.workspaceName} to remove it there too.`);
3960
+ for (const coordinator of allTargets) if (coordinator.kind === "coordinate" && coordinator.inputs.actionDirs?.includes(match.target.inputs.dir) && !deletedKeys.has(targetKey(coordinator))) logger.warn(`Coordinator "${coordinator.workspaceName}" still references this action's directory (${match.target.inputs.dir}). Remove \`${match.target.workspaceName}\` from the relevant \`--action\` value and re-run \`tailor-sdk setup coordinate\` to update it.`);
3809
3961
  }
3810
3962
  /**
3811
3963
  * Delete managed workflow/action files and their `.github/tailor-sdk.lock` entries.
@@ -3873,7 +4025,7 @@ const coordinateCommand = defineAppCommand({
3873
4025
  alias: "n",
3874
4026
  description: "Coordinator name (used in the generated workflow file name and job names)"
3875
4027
  }),
3876
- action: arg(z.array(z.string().min(1)).min(1), { description: "Composite action to include (can be specified multiple times). tailor- prefix optional." }),
4028
+ action: arg(z.array(z.string().min(1)).min(1), { description: "Composite action to include. Repeat for separate deploy steps, or use commas to deploy actions as one multi-config group. tailor- prefix optional." }),
3877
4029
  branch: arg(z.string().min(1).optional(), { description: "Branch target: deploy trigger branch (defaults to the detected default branch)" }),
3878
4030
  tag: arg(z.boolean().default(false), { description: "Generate a tag target coordinator" }),
3879
4031
  environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs" }),
@@ -6070,7 +6222,7 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
6070
6222
  async function assertMigrationsReproduceLocalTypes(loaded, target) {
6071
6223
  const { config, plugins } = loaded;
6072
6224
  const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
6073
- const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-BJKNlv8c.mjs");
6225
+ const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-DbvD5UUz.mjs");
6074
6226
  const application = defineApplication({
6075
6227
  config,
6076
6228
  pluginManager