@tailor-platform/sdk 1.68.0 → 1.70.0

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 (117) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/dist/application-BakHtldG.mjs +4 -0
  3. package/dist/application-Df5_I83n.mjs +6432 -0
  4. package/dist/application-Df5_I83n.mjs.map +1 -0
  5. package/dist/cli/erd-viewer-assets/app.js +279 -36
  6. package/dist/cli/erd-viewer-assets/index.html +4 -0
  7. package/dist/cli/erd-viewer-assets/styles.css +252 -5
  8. package/dist/cli/index.mjs +650 -98
  9. package/dist/cli/index.mjs.map +1 -1
  10. package/dist/cli/lib.d.mts +247 -160
  11. package/dist/cli/lib.mjs +3 -3
  12. package/dist/cli/lib.mjs.map +1 -1
  13. package/dist/cli/skills.mjs +1 -1
  14. package/dist/completion/zsh-worker.zsh +175 -24
  15. package/dist/configure/index.d.mts +5 -5
  16. package/dist/configure/index.mjs +12 -6
  17. package/dist/configure/index.mjs.map +1 -1
  18. package/dist/{crashreport-u9y2npiy.mjs → crashreport-BqyvFk-_.mjs} +2 -2
  19. package/dist/{crashreport-u9y2npiy.mjs.map → crashreport-BqyvFk-_.mjs.map} +1 -1
  20. package/dist/{crashreport-6jpCceOF.mjs → crashreport-BwF8cHF0.mjs} +1 -1
  21. package/dist/enum-constants-C7DaWeQo.mjs.map +1 -1
  22. package/dist/field-C4zdJLW5.mjs.map +1 -1
  23. package/dist/file-utils-BHPxPXmn.mjs.map +1 -1
  24. package/dist/{idp-BlBPtXJ-.d.mts → idp-BmYwCXnJ.d.mts} +30 -3
  25. package/dist/{idp-BZPqpcYY.mjs → idp-ynUfzwpz.mjs} +9 -1
  26. package/dist/idp-ynUfzwpz.mjs.map +1 -0
  27. package/dist/{index-DvEUb3pX.d.mts → index-BAEaAqmz.d.mts} +112 -53
  28. package/dist/{index-CklcVeMG.d.mts → index-C-vsbx27.d.mts} +2 -2
  29. package/dist/{index-hXoO-AOC.d.mts → index-CKI0eZP6.d.mts} +2 -2
  30. package/dist/{index-DYhnxXYR.d.mts → index-CrqOgUF2.d.mts} +2 -2
  31. package/dist/{index-DlDRSzFZ.d.mts → index-DESLU9kI.d.mts} +2 -2
  32. package/dist/{index-DRhMpdnA.d.mts → index-dKNk8hjo.d.mts} +2 -2
  33. package/dist/job-BpsFXPbi.mjs.map +1 -1
  34. package/dist/{kysely-type-D1e0Vwkd.mjs → kysely-type-CSoZxVKN.mjs} +2 -2
  35. package/dist/{kysely-type-D1e0Vwkd.mjs.map → kysely-type-CSoZxVKN.mjs.map} +1 -1
  36. package/dist/{logger-DpJyJvNz.mjs → logger-DKF-JsAK.mjs} +3 -3
  37. package/dist/{logger-DpJyJvNz.mjs.map → logger-DKF-JsAK.mjs.map} +1 -1
  38. package/dist/{mock-DMgIygjE.mjs → mock-wf5qeZLi.mjs} +19 -9
  39. package/dist/mock-wf5qeZLi.mjs.map +1 -0
  40. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  41. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  42. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  43. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  44. package/dist/plugin/builtin/seed/index.d.mts +1 -1
  45. package/dist/plugin/index.d.mts +1 -1
  46. package/dist/plugin/index.mjs.map +1 -1
  47. package/dist/registry-D0uB0OrK.mjs.map +1 -1
  48. package/dist/{repl-editor-CJG3sz7A.mjs → repl-editor-DD5YP5mt.mjs} +4 -4
  49. package/dist/{repl-editor-CJG3sz7A.mjs.map → repl-editor-DD5YP5mt.mjs.map} +1 -1
  50. package/dist/runtime/globals.d.mts +3 -2
  51. package/dist/runtime/idp.d.mts +2 -2
  52. package/dist/runtime/idp.mjs +1 -1
  53. package/dist/runtime/index.d.mts +2 -2
  54. package/dist/runtime/index.mjs +1 -1
  55. package/dist/{runtime-DxaBq6U8.mjs → runtime-CSY0eD4_.mjs} +411 -221
  56. package/dist/runtime-CSY0eD4_.mjs.map +1 -0
  57. package/dist/{schema-1msIhXwA.mjs → schema-C4fkpWV_.mjs} +9 -15
  58. package/dist/schema-C4fkpWV_.mjs.map +1 -0
  59. package/dist/seed-YAbtMy65.mjs.map +1 -1
  60. package/dist/{service-wI3Hvrgx.mjs → service-B2Jd9CxS.mjs} +2 -2
  61. package/dist/service-B2Jd9CxS.mjs.map +1 -0
  62. package/dist/service-CRaa4Joe.mjs +4 -0
  63. package/dist/{service-DMohAx8a.mjs → service-DDWgZL_L2.mjs} +2 -2
  64. package/dist/service-DDWgZL_L2.mjs.map +1 -0
  65. package/dist/service_pb-DGSmn-aF.mjs +4 -0
  66. package/dist/{application-WpWwTyk9.mjs → service_pb-DSNjrcbW.mjs} +22 -6176
  67. package/dist/service_pb-DSNjrcbW.mjs.map +1 -0
  68. package/dist/telemetry-BQbbVo2t.mjs.map +1 -1
  69. package/dist/{types-2Be3wSMc.mjs → types-32lUMToj.mjs} +1 -1
  70. package/dist/{types-CmzfQP_m.mjs → types-D4QMmNWh.mjs} +1 -12
  71. package/dist/types-D4QMmNWh.mjs.map +1 -0
  72. package/dist/{types-Bzr0RQME.d.mts → types-Dynq4AJv.d.mts} +2 -2
  73. package/dist/{types-DZrtN6-H.d.mts → types-rj8YJcEe.d.mts} +5 -2
  74. package/dist/utils/test/index.d.mts +2 -2
  75. package/dist/utils/test/index.mjs.map +1 -1
  76. package/dist/vitest/environment.mjs +1 -1
  77. package/dist/vitest/environment.mjs.map +1 -1
  78. package/dist/vitest/index.mjs +4 -4
  79. package/dist/vitest/index.mjs.map +1 -1
  80. package/dist/vitest/setup.mjs +1 -1
  81. package/dist/{workflow.generated-1S50BhEb.d.mts → workflow.generated-DJULCuRr.d.mts} +274 -174
  82. package/docs/cli/application.md +39 -201
  83. package/docs/cli/auth.md +12 -256
  84. package/docs/cli/completion.md +0 -24
  85. package/docs/cli/crashreport.md +0 -58
  86. package/docs/cli/executor.md +2 -166
  87. package/docs/cli/function.md +2 -118
  88. package/docs/cli/organization.md +1 -211
  89. package/docs/cli/query.md +0 -20
  90. package/docs/cli/secret.md +70 -250
  91. package/docs/cli/setup.md +2 -41
  92. package/docs/cli/skills.md +0 -39
  93. package/docs/cli/staticwebsite.md +24 -172
  94. package/docs/cli/tailordb.md +25 -251
  95. package/docs/cli/upgrade.md +0 -20
  96. package/docs/cli/user.md +41 -246
  97. package/docs/cli/workflow.md +30 -189
  98. package/docs/cli/workspace.md +164 -537
  99. package/docs/cli-reference.md +61 -37
  100. package/docs/configuration.md +7 -1
  101. package/docs/github-actions.md +27 -0
  102. package/docs/multi-environment.md +22 -0
  103. package/docs/services/aigateway.md +4 -2
  104. package/docs/services/http-adapter.md +16 -1
  105. package/docs/services/idp.md +55 -2
  106. package/docs/services/staticwebsite.md +7 -1
  107. package/package.json +23 -18
  108. package/dist/application-Djeezk3m.mjs +0 -4
  109. package/dist/application-WpWwTyk9.mjs.map +0 -1
  110. package/dist/idp-BZPqpcYY.mjs.map +0 -1
  111. package/dist/mock-DMgIygjE.mjs.map +0 -1
  112. package/dist/runtime-DxaBq6U8.mjs.map +0 -1
  113. package/dist/schema-1msIhXwA.mjs.map +0 -1
  114. package/dist/service-BHQIerYh.mjs +0 -4
  115. package/dist/service-DMohAx8a.mjs.map +0 -1
  116. package/dist/service-wI3Hvrgx.mjs.map +0 -1
  117. package/dist/types-CmzfQP_m.mjs.map +0 -1
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { A as loadMachineUserName, B as fetchPlatformMachineUserToken, C as hashContent$1, D as fetchLatestToken, Dt as FunctionExecution_Type, E as deleteUserTokens, F as writePlatformConfig, H as initOAuth2Client, I as closeConnectionPool, L as fetchAll, M as readPlatformConfig, N as resolveTokens, O as loadAccessToken, P as saveUserTokens, S as getDistDir, T as loadConfig, U as initOperatorClient, V as fetchUserInfo, _ as createLogLevelTreeshakeOptions, a as WorkflowJobSchema, c as INVOKER_EXPR, cn as PATScope, ft as CustomDomainStatus, g as composeFunctionTreeshakeOptions, h as platformBundleDefinePlugin, i as resolveInlineSourcemap, j as loadWorkspaceId, o as ResolverSchema, t as defineApplication, tn as AuthInvokerSchema, v as resolveBundleLogLevel, z as fetchPaged } from "../application-WpWwTyk9.mjs";
3
+ import { C as CustomDomainStatus, R as FunctionExecution_Type, ft as AuthInvokerSchema, yt as PATScope } from "../service_pb-DSNjrcbW.mjs";
4
4
  import { t as assertDefined } from "../assert-CKfwrmCV.mjs";
5
- import { n as logger, r as styles } from "../logger-DpJyJvNz.mjs";
6
- import { $ as updateCommand$2, $t as protoGqlPermission, A as listCommand$12, At as jobsCommand, Bn as toPageDirection, Bt as functionExecutionStatusToString, C as listCommand$13, Cn as ensureConfigId, Ct as webhookCommand, Dn as generateUserTypes, E as waitCommand, En as PluginManager, Et as listCommand$6, F as generateCommand$1, Fn as confirmationArgs, Ft as getCommand$6, G as updateCommand$3, Gt as executeScript, H as logBetaWarning, Ht as getCommand$1, I as generateMigrationScript, In as deploymentArgs, J as treeCommand, Jt as MIGRATION_LABEL_KEY, L as writeDbTypesFile, Ln as isVerbose, Lt as executionsCommand, Mn as defineAppCommand, N as truncateCommand, Nn as commonArgs, Nt as startCommand, O as resumeCommand, On as prompt, Pn as configArg, Qt as generateAllTypeManifestsFromSnapshot, R as getConfiguredEditorCommand, Rn as pagedLogArgs, Sn as getNamespacesWithMigrations, T as healthCommand, Tn as sdkNameLabelKey, V as showCommand, Vn as workspaceArgs, Vt as formatKeyValueTable, W as removeCommand$1, Wt as deploy, Xt as parseMigrationLabelNumber, Y as listCommand$11, Yt as handleOptionalToRequiredError, Z as getCommand$5, Zt as compareSnapshotWithRemote, _n as formatMigrationNumber, _t as generate, an as assertValidMigrationFiles, at as deleteCommand$3, b as createCommand$4, bn as formatMigrationDiff, bt as getCommand$2, c as listCommand$14, cn as createSnapshotFromLocalTypes, dn as getMigrationFilePath, dt as getCommand$3, f as restoreCommand, fn as getMigrationFiles, g as getCommand$7, gn as reconstructSnapshotFromMigrations, hn as loadDiff, ht as listCommand$8, i as updateCommand$4, jn as assertWritable, kn as apiCommand, ln as getLatestMigrationNumber, lt as listCommand$9, m as listCommand$15, mn as isValidMigrationNumber, mt as tokenCommand, nn as INITIAL_SCHEMA_NUMBER, o as removeCommand, on as compareLocalTypesWithSnapshot, r as queryCommand, rt as getCommand$4, st as createCommand$3, t as isNativeTypeScriptRuntime, tt as listCommand$10, u as inviteCommand, v as deleteCommand$4, vn as parseMigrationNumberArg, vt as listCommand$7, wn as resourceTrn, wt as triggerCommand, xn as hasChanges, z as openInConfiguredEditor, zn as paginationArgs } from "../runtime-DxaBq6U8.mjs";
7
- import { n as ExecutorSchema } from "../service-wI3Hvrgx.mjs";
5
+ import { n as logger, r as styles } from "../logger-DKF-JsAK.mjs";
6
+ import { $t as generateAllTypeManifestsFromSnapshot, A as listCommand$12, An as apiCommand, Bn as paginationArgs, C as listCommand$13, Cn as getNamespacesWithMigrations, 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 workspaceArgs, Ht as formatKeyValueTable, I as generateMigrationScript, In as confirmationArgs, It as getCommand$6, K as updateCommand$3, Kt as executeScript, 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 compareSnapshotWithRemote, R as getConfiguredEditorCommand, Rn as isVerbose, Rt as executionsCommand, Sn as hasChanges, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Ut as getCommand$1, V as showCommand, Vn as toPageDirection, Vt as functionExecutionStatusToString, X as listCommand$11, Xt as handleOptionalToRequiredError, Y as treeCommand, Yt as MIGRATION_LABEL_KEY, Zt as parseMigrationLabelNumber, _n as reconstructSnapshotFromMigrations, b as createCommand$4, c as listCommand$14, ct as createCommand$3, en as protoGqlPermission, et as updateCommand$2, f as restoreCommand, fn as getMigrationFilePath, ft as getCommand$3, g as getCommand$7, gn as loadDiff, gt as listCommand$8, hn as isValidMigrationNumber, ht as tokenCommand, i as updateCommand$4, it as getCommand$4, jt as jobsCommand, kn as prompt, ln as createSnapshotFromLocalTypes, m as listCommand$15, nt as listCommand$10, o as removeCommand, on as assertValidMigrationFiles, ot as deleteCommand$3, pn as getMigrationFiles, r as queryCommand, rn as INITIAL_SCHEMA_NUMBER, sn as compareLocalTypesWithSnapshot, t as isNativeTypeScriptRuntime, u as inviteCommand, un as getLatestMigrationNumber, ut as listCommand$9, v as deleteCommand$4, vn as formatMigrationNumber, vt as generate, wn as ensureConfigId, wt as webhookCommand, xn as formatMigrationDiff, xt as getCommand$2, yn as parseMigrationNumberArg, yt as listCommand$7, z as openInConfiguredEditor, zn as pagedLogArgs } from "../runtime-CSY0eD4_.mjs";
7
+ import { $ as initOperatorClient, A as loadAccessToken, B as saveUserTokens, C as hashContent$1, D as fetchLatestToken, E as deleteUserTokens, F as loadStoredUserTokens, H as closeConnectionPool, I as loadWorkspaceId, J as fetchUserInfo, K as fetchPaged, L as platformConfigFromProfile, M as loadConsoleBaseUrl, N as loadMachineUserName, O as hasAnyUserTokenEntry, Q as initOAuth2Client, R as readPlatformConfig, S as getDistDir, T as loadConfig, U as defaultPlatformBaseUrl, V as writePlatformConfig, W as fetchAll, _ as createLogLevelTreeshakeOptions, a as WorkflowJobSchema, c as INVOKER_EXPR, et as isDefaultPlatform, g as composeFunctionTreeshakeOptions, h as platformBundleDefinePlugin, i as resolveInlineSourcemap, k as hasUserTokenEntry, o as ResolverSchema, q as fetchPlatformMachineUserToken, t as defineApplication, v as resolveBundleLogLevel, z as resolveUserTokenKey } from "../application-Df5_I83n.mjs";
8
+ import { n as ExecutorSchema } from "../service-B2Jd9CxS.mjs";
8
9
  import { t as multiline } from "../multiline-Cf9ODpr1.mjs";
9
10
  import { r as isPluginGeneratedType } from "../seed-YAbtMy65.mjs";
10
11
  import { t as readPackageJson } from "../package-json-DcQApfPQ.mjs";
11
12
  import { i as userAgent } from "../secret-file-eB3R3Fil.mjs";
12
13
  import { n as isCLIError } from "../errors-EsY4XO6O.mjs";
13
- import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-u9y2npiy.mjs";
14
+ import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-BqyvFk-_.mjs";
14
15
  import { arg, defineCommand, runCommand, runMain } from "politty";
15
16
  import { withCompletionCommand } from "politty/completion";
16
17
  import { z } from "zod";
@@ -254,7 +255,6 @@ const listAuthConnectionCommand = defineAppCommand({
254
255
 
255
256
  //#endregion
256
257
  //#region src/cli/commands/authconnection/open.ts
257
- const consoleBaseUrl$1 = "https://console.tailor.tech";
258
258
  const openAuthConnectionCommand = defineAppCommand({
259
259
  name: "open",
260
260
  description: "Open the auth connections page in the Tailor Platform Console.",
@@ -265,7 +265,11 @@ const openAuthConnectionCommand = defineAppCommand({
265
265
  profile: args.profile
266
266
  });
267
267
  const consolePath = `/workspaces/${workspaceId}/settings/connections`;
268
- const consoleUrl = new URL(consolePath, consoleBaseUrl$1).toString();
268
+ const consoleBaseUrl = await loadConsoleBaseUrl({
269
+ profile: args.profile,
270
+ ...args["workspace-id"] !== void 0 ? { allowMissingProfile: true } : {}
271
+ });
272
+ const consoleUrl = new URL(consolePath, consoleBaseUrl).toString();
269
273
  const jsonOutput = logger.jsonMode;
270
274
  logger.info("Opening auth connections page in Tailor Platform Console...");
271
275
  let opened = true;
@@ -1284,7 +1288,7 @@ async function detectFunctionType(options) {
1284
1288
  const rawInput = module.default.input;
1285
1289
  let inputSchema;
1286
1290
  if (rawInput) {
1287
- const { t } = await import("../types-2Be3wSMc.mjs");
1291
+ const { t } = await import("../types-32lUMToj.mjs");
1288
1292
  inputSchema = t.object(rawInput);
1289
1293
  }
1290
1294
  return {
@@ -1733,8 +1737,15 @@ const redirectUri = `http://localhost:${redirectPort}/callback`;
1733
1737
  function randomState() {
1734
1738
  return crypto.randomBytes(32).toString("base64url");
1735
1739
  }
1736
- const startAuthServer = async () => {
1737
- const client = initOAuth2Client();
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
+ }
1743
+ function shouldUpdateCurrentUser(profile, platformConfig) {
1744
+ if (!profile) return true;
1745
+ return isDefaultPlatform(platformConfig);
1746
+ }
1747
+ const startAuthServer = async (args = {}) => {
1748
+ const client = initOAuth2Client(args.platformConfig);
1738
1749
  const state = randomState();
1739
1750
  const codeVerifier = await generateCodeVerifier();
1740
1751
  return new Promise((resolve, reject) => {
@@ -1746,13 +1757,14 @@ const startAuthServer = async () => {
1746
1757
  state,
1747
1758
  codeVerifier
1748
1759
  });
1749
- const userInfo = await fetchUserInfo(tokens.accessToken);
1760
+ const userInfo = await fetchUserInfo(tokens.accessToken, args.platformConfig);
1761
+ assertProfileLoginUser(args, userInfo.email);
1750
1762
  const pfConfig = await readPlatformConfig();
1751
1763
  await saveUserTokens(pfConfig, userInfo.email, {
1752
1764
  accessToken: tokens.accessToken,
1753
1765
  refreshToken: tokens.refreshToken ?? void 0
1754
- }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString());
1755
- pfConfig.current_user = userInfo.email;
1766
+ }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), args.platformConfig);
1767
+ if (args.updateCurrentUser ?? true) pfConfig.current_user = userInfo.email;
1756
1768
  writePlatformConfig(pfConfig);
1757
1769
  res.writeHead(200, { "Content-Type": "application/json" });
1758
1770
  res.end(JSON.stringify({
@@ -1794,17 +1806,22 @@ const startAuthServer = async () => {
1794
1806
  });
1795
1807
  };
1796
1808
  async function loginAsMachineUser(args) {
1809
+ assertProfileLoginUser(args, args.clientId);
1797
1810
  const clientSecret = args.clientSecret ?? await prompt.password({ message: "Client secret" });
1798
- const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret);
1811
+ const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret, args.platformConfig);
1799
1812
  const pfConfig = await readPlatformConfig();
1800
- await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString());
1801
- pfConfig.current_user = args.clientId;
1813
+ await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), args.platformConfig);
1814
+ if (args.updateCurrentUser ?? true) pfConfig.current_user = args.clientId;
1802
1815
  writePlatformConfig(pfConfig);
1803
1816
  }
1804
1817
  const loginCommand = defineAppCommand({
1805
1818
  name: "login",
1806
1819
  description: "Login to Tailor Platform.",
1807
- args: z.xor([z.object({}).strict().describe("User Login"), z.object({
1820
+ args: z.xor([z.object({ profile: arg(z.string().optional(), {
1821
+ alias: "p",
1822
+ description: "Workspace profile whose platform settings should be used for login.",
1823
+ env: "TAILOR_PLATFORM_PROFILE"
1824
+ }) }).strict().describe("User Login"), z.object({
1808
1825
  "machine-user": arg(z.literal(true), {
1809
1826
  hiddenAlias: "machineuser",
1810
1827
  description: "Login as a platform machine user.",
@@ -1818,14 +1835,37 @@ const loginCommand = defineAppCommand({
1818
1835
  "client-secret": arg(z.string().optional(), {
1819
1836
  description: "Client secret",
1820
1837
  env: "TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET"
1838
+ }),
1839
+ profile: arg(z.string().optional(), {
1840
+ alias: "p",
1841
+ description: "Workspace profile whose platform settings should be used for login.",
1842
+ env: "TAILOR_PLATFORM_PROFILE"
1821
1843
  })
1822
1844
  }).strict().describe("Machine User Login")]),
1823
1845
  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;
1853
+ }
1854
+ const updateCurrentUser = shouldUpdateCurrentUser(args.profile, platformConfig);
1824
1855
  if ("machine-user" in args) await loginAsMachineUser({
1825
1856
  clientId: args.clientId,
1826
- clientSecret: args.clientSecret
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
1827
1868
  });
1828
- else await startAuthServer();
1829
1869
  logger.success("Successfully logged in to Tailor Platform.");
1830
1870
  await closeConnectionPool();
1831
1871
  }
@@ -1836,30 +1876,53 @@ const loginCommand = defineAppCommand({
1836
1876
  const logoutCommand = defineAppCommand({
1837
1877
  name: "logout",
1838
1878
  description: "Logout from Tailor Platform.",
1839
- args: z.object({}).strict(),
1840
- run: async () => {
1879
+ args: z.object({ profile: arg(z.string().optional(), {
1880
+ alias: "p",
1881
+ description: "Workspace profile whose platform settings should be used for logout.",
1882
+ env: "TAILOR_PLATFORM_PROFILE"
1883
+ }) }).strict(),
1884
+ run: async (args) => {
1885
+ const profile = args.profile || process.env.TAILOR_PLATFORM_PROFILE;
1841
1886
  const pfConfig = await readPlatformConfig();
1842
- const currentUser = pfConfig.current_user;
1843
- const userEntry = currentUser ? pfConfig.users[currentUser] : void 0;
1844
- if (!userEntry || !currentUser) {
1887
+ const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
1888
+ if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
1889
+ const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
1890
+ const currentUser = profileEntry ? profileEntry.user : pfConfig.current_user;
1891
+ const deletesDefaultToken = isDefaultPlatform(platformConfig);
1892
+ const lookupOptions = profile ? { allowLegacyUserKey: true } : void 0;
1893
+ if (!currentUser) {
1845
1894
  logger.info("You are not logged in.");
1846
1895
  return;
1847
1896
  }
1897
+ const hasDefaultUserToken = () => hasUserTokenEntry(pfConfig, currentUser, { platformUrl: defaultPlatformBaseUrl });
1898
+ const shouldClearCurrentUser = () => pfConfig.current_user === currentUser && (deletesDefaultToken ? !hasDefaultUserToken() : !hasAnyUserTokenEntry(pfConfig, currentUser) && !hasDefaultUserToken());
1899
+ let storedTokens;
1900
+ let tokenLoadFailed = false;
1848
1901
  try {
1849
- const { accessToken, refreshToken } = await resolveTokens(userEntry, currentUser);
1850
- const client = initOAuth2Client();
1851
- const tokenTypeHint = refreshToken ? "refresh_token" : "access_token";
1902
+ storedTokens = await loadStoredUserTokens(pfConfig, currentUser, platformConfig, lookupOptions);
1903
+ } catch (error) {
1904
+ tokenLoadFailed = true;
1905
+ logger.warn(`Failed to revoke token: ${error instanceof Error ? error.message : error}`);
1906
+ }
1907
+ if (!storedTokens && !tokenLoadFailed) {
1908
+ logger.info("You are not logged in.");
1909
+ if (shouldClearCurrentUser()) pfConfig.current_user = null;
1910
+ writePlatformConfig(pfConfig);
1911
+ return;
1912
+ }
1913
+ if (storedTokens) try {
1914
+ const client = initOAuth2Client(platformConfig);
1915
+ const tokenTypeHint = storedTokens.refreshToken ? "refresh_token" : "access_token";
1852
1916
  await client.revoke({
1853
- accessToken,
1854
- refreshToken: refreshToken ?? null,
1855
- expiresAt: Date.parse(userEntry.token_expires_at)
1917
+ accessToken: storedTokens.accessToken,
1918
+ refreshToken: storedTokens.refreshToken ?? null,
1919
+ expiresAt: Date.parse(storedTokens.userEntry.token_expires_at)
1856
1920
  }, tokenTypeHint);
1857
1921
  } catch (error) {
1858
1922
  logger.warn(`Failed to revoke token: ${error instanceof Error ? error.message : error}`);
1859
1923
  }
1860
- await deleteUserTokens(pfConfig, currentUser);
1861
- delete pfConfig.users[currentUser];
1862
- pfConfig.current_user = null;
1924
+ await deleteUserTokens(pfConfig, currentUser, platformConfig, lookupOptions);
1925
+ if (shouldClearCurrentUser()) pfConfig.current_user = null;
1863
1926
  writePlatformConfig(pfConfig);
1864
1927
  logger.success("Successfully logged out from Tailor Platform.");
1865
1928
  }
@@ -1895,7 +1958,6 @@ const oauth2clientCommand = defineCommand({
1895
1958
 
1896
1959
  //#endregion
1897
1960
  //#region src/cli/commands/open.ts
1898
- const consoleBaseUrl = "https://console.tailor.tech";
1899
1961
  const openCommand = defineAppCommand({
1900
1962
  name: "open",
1901
1963
  description: "Open Tailor Platform Console.",
@@ -1908,6 +1970,10 @@ const openCommand = defineAppCommand({
1908
1970
  const { config } = await loadConfig(args.config);
1909
1971
  const applicationName = config.name;
1910
1972
  const consolePath = `/workspaces/${workspaceId}/applications/${encodeURIComponent(applicationName)}/overview`;
1973
+ const consoleBaseUrl = await loadConsoleBaseUrl({
1974
+ profile: args.profile,
1975
+ ...args["workspace-id"] !== void 0 ? { allowMissingProfile: true } : {}
1976
+ });
1911
1977
  const consoleUrl = new URL(consolePath, consoleBaseUrl).toString();
1912
1978
  const jsonOutput = logger.jsonMode;
1913
1979
  logger.info("Opening Tailor Platform Console...");
@@ -1988,13 +2054,31 @@ const createCommand$2 = defineAppCommand({
1988
2054
  alias: "m",
1989
2055
  description: "Default machine user name for application-data commands (query, workflow start, function test-run, machineuser token)."
1990
2056
  }),
1991
- "machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user." })
2057
+ "machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user." }),
2058
+ "platform-url": arg(z.url().optional(), {
2059
+ description: "Platform API base URL for this profile.",
2060
+ env: "TAILOR_PLATFORM_URL"
2061
+ }),
2062
+ "oauth2-client-id": arg(z.string().optional(), {
2063
+ description: "OAuth2 client ID for logging in to this profile's platform.",
2064
+ env: "TAILOR_PLATFORM_OAUTH2_CLIENT_ID"
2065
+ }),
2066
+ "console-url": arg(z.url().optional(), {
2067
+ description: "Console base URL for this profile.",
2068
+ env: "TAILOR_PLATFORM_CONSOLE_URL"
2069
+ })
1992
2070
  }).strict(),
1993
2071
  run: async (args) => {
1994
2072
  if (args["machine-user-override"] === "deny" && !args["machine-user"]) throw new Error("--machine-user-override deny requires --machine-user.");
1995
2073
  const config = await readPlatformConfig();
1996
2074
  if (config.profiles[args.name]) throw new Error(`Profile "${args.name}" already exists.`);
1997
- const client = await initOperatorClient(await fetchLatestToken(config, args.user));
2075
+ const platformConfigInput = {
2076
+ ...args["platform-url"] ? { platformUrl: args["platform-url"] } : {},
2077
+ ...args["oauth2-client-id"] ? { oauth2ClientId: args["oauth2-client-id"] } : {},
2078
+ ...args["console-url"] ? { consoleUrl: args["console-url"] } : {}
2079
+ };
2080
+ const platformConfig = Object.keys(platformConfigInput).length > 0 ? platformConfigInput : void 0;
2081
+ const client = await initOperatorClient(await fetchLatestToken(config, args.user, platformConfig), platformConfig);
1998
2082
  if (!(await fetchAll(async (pageToken, maxPageSize) => {
1999
2083
  const { workspaces, nextPageToken } = await client.listWorkspaces({
2000
2084
  pageToken,
@@ -2007,7 +2091,10 @@ const createCommand$2 = defineAppCommand({
2007
2091
  workspace_id: args["workspace-id"],
2008
2092
  ...args.permission === "read" ? { readonly: true } : {},
2009
2093
  ...args["machine-user"] ? { machine_user: args["machine-user"] } : {},
2010
- ...args["machine-user-override"] === "deny" ? { machine_user_override: "deny" } : {}
2094
+ ...args["machine-user-override"] === "deny" ? { machine_user_override: "deny" } : {},
2095
+ ...args["platform-url"] ? { platform_url: args["platform-url"] } : {},
2096
+ ...args["oauth2-client-id"] ? { oauth2_client_id: args["oauth2-client-id"] } : {},
2097
+ ...args["console-url"] ? { console_url: args["console-url"] } : {}
2011
2098
  };
2012
2099
  writePlatformConfig(config);
2013
2100
  if (!args.json) logger.success(`Profile "${args.name}" created successfully.`);
@@ -2019,7 +2106,10 @@ const createCommand$2 = defineAppCommand({
2019
2106
  ...args["machine-user"] ? {
2020
2107
  machineUser: args["machine-user"],
2021
2108
  machineUserOverride: args["machine-user-override"] ?? "allow"
2022
- } : {}
2109
+ } : {},
2110
+ ...args["platform-url"] ? { platformUrl: args["platform-url"] } : {},
2111
+ ...args["oauth2-client-id"] ? { oauth2ClientId: args["oauth2-client-id"] } : {},
2112
+ ...args["console-url"] ? { consoleUrl: args["console-url"] } : {}
2023
2113
  };
2024
2114
  logger.out(profileInfo);
2025
2115
  }
@@ -2071,7 +2161,10 @@ const listCommand$4 = defineAppCommand({
2071
2161
  ...p.machine_user ? {
2072
2162
  machineUser: p.machine_user,
2073
2163
  machineUserOverride: p.machine_user_override ?? "allow"
2074
- } : {}
2164
+ } : {},
2165
+ ...p.platform_url ? { platformUrl: p.platform_url } : {},
2166
+ ...p.oauth2_client_id ? { oauth2ClientId: p.oauth2_client_id } : {},
2167
+ ...p.console_url ? { consoleUrl: p.console_url } : {}
2075
2168
  };
2076
2169
  });
2077
2170
  logger.out(profileInfos);
@@ -2101,25 +2194,38 @@ const updateCommand$1 = defineAppCommand({
2101
2194
  alias: "m",
2102
2195
  description: "Default machine user name for application-data commands (query, workflow start, function test-run, machineuser token). Pass an empty string to clear."
2103
2196
  }),
2104
- "machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user; 'allow' lifts the restriction." })
2197
+ "machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user; 'allow' lifts the restriction." }),
2198
+ "platform-url": arg(z.union([z.url(), z.literal("")]).optional(), { description: "Platform API base URL for this profile. Pass an empty string to clear." }),
2199
+ "oauth2-client-id": arg(z.string().optional(), { description: "OAuth2 client ID for logging in to this profile's platform. Pass an empty string to clear." }),
2200
+ "console-url": arg(z.union([z.url(), z.literal("")]).optional(), { description: "Console base URL for this profile. Pass an empty string to clear." })
2105
2201
  }).strict(),
2106
2202
  run: async (args) => {
2107
2203
  const config = await readPlatformConfig();
2108
2204
  const profile = config.profiles[args.name];
2109
2205
  if (!profile) throw new Error(`Profile "${args.name}" not found.`);
2110
- if (!args.user && !args["workspace-id"] && args.permission === void 0 && args["machine-user"] === void 0 && args["machine-user-override"] === void 0) throw new Error("Please provide at least one property to update.");
2206
+ if (!args.user && !args["workspace-id"] && args.permission === void 0 && args["machine-user"] === void 0 && args["machine-user-override"] === void 0 && args["platform-url"] === void 0 && args["oauth2-client-id"] === void 0 && args["console-url"] === void 0) throw new Error("Please provide at least one property to update.");
2111
2207
  const oldUser = profile.user;
2112
2208
  const newUser = args.user || oldUser;
2113
2209
  const oldWorkspaceId = profile.workspace_id;
2114
2210
  const newWorkspaceId = args["workspace-id"] || oldWorkspaceId;
2115
2211
  const finalMachineUser = args["machine-user"] === "" ? void 0 : args["machine-user"] ?? profile.machine_user;
2116
2212
  const finalOverride = args["machine-user-override"] === "allow" ? void 0 : args["machine-user-override"] ?? profile.machine_user_override;
2213
+ const finalPlatformUrl = args["platform-url"] === "" ? void 0 : args["platform-url"] ?? profile.platform_url;
2214
+ const finalOAuth2ClientId = args["oauth2-client-id"] === "" ? void 0 : args["oauth2-client-id"] ?? profile.oauth2_client_id;
2215
+ const finalConsoleUrl = args["console-url"] === "" ? void 0 : args["console-url"] ?? profile.console_url;
2216
+ const finalPlatformConfigInput = {
2217
+ ...finalPlatformUrl ? { platformUrl: finalPlatformUrl } : {},
2218
+ ...finalOAuth2ClientId ? { oauth2ClientId: finalOAuth2ClientId } : {},
2219
+ ...finalConsoleUrl ? { consoleUrl: finalConsoleUrl } : {}
2220
+ };
2221
+ const finalPlatformConfig = Object.keys(finalPlatformConfigInput).length > 0 ? finalPlatformConfigInput : void 0;
2222
+ const tokenLookupPlatformConfig = args["platform-url"] === "" ? platformConfigFromProfile(profile) : finalPlatformConfig;
2117
2223
  if ((args["machine-user"] !== void 0 || args["machine-user-override"] !== void 0) && finalOverride === "deny" && !finalMachineUser) {
2118
2224
  if (args["machine-user-override"] === "deny") throw new Error("--machine-user-override deny requires --machine-user.");
2119
2225
  throw new Error(`Cannot clear the machine user while machine-user-override is "deny". Also pass --machine-user-override allow.`);
2120
2226
  }
2121
- if (args.user !== void 0 || args["workspace-id"] !== void 0) {
2122
- const client = await initOperatorClient(await fetchLatestToken(config, newUser));
2227
+ if (args.user !== void 0 || args["workspace-id"] !== void 0 || args["platform-url"] !== void 0) {
2228
+ const client = await initOperatorClient(await fetchLatestToken(config, newUser, tokenLookupPlatformConfig), finalPlatformConfig);
2123
2229
  if (!(await fetchAll(async (pageToken, maxPageSize) => {
2124
2230
  const { workspaces, nextPageToken } = await client.listWorkspaces({
2125
2231
  pageToken,
@@ -2136,6 +2242,12 @@ const updateCommand$1 = defineAppCommand({
2136
2242
  else profile.machine_user = args["machine-user"];
2137
2243
  if (args["machine-user-override"] === "deny") profile.machine_user_override = "deny";
2138
2244
  else if (args["machine-user-override"] === "allow") delete profile.machine_user_override;
2245
+ if (args["platform-url"] !== void 0) if (args["platform-url"] === "") delete profile.platform_url;
2246
+ else profile.platform_url = args["platform-url"];
2247
+ if (args["oauth2-client-id"] !== void 0) if (args["oauth2-client-id"] === "") delete profile.oauth2_client_id;
2248
+ else profile.oauth2_client_id = args["oauth2-client-id"];
2249
+ if (args["console-url"] !== void 0) if (args["console-url"] === "") delete profile.console_url;
2250
+ else profile.console_url = args["console-url"];
2139
2251
  writePlatformConfig(config);
2140
2252
  if (!args.json) logger.success(`Profile "${args.name}" updated successfully`);
2141
2253
  const profileInfo = {
@@ -2146,7 +2258,10 @@ const updateCommand$1 = defineAppCommand({
2146
2258
  ...profile.machine_user ? {
2147
2259
  machineUser: profile.machine_user,
2148
2260
  machineUserOverride: profile.machine_user_override ?? "allow"
2149
- } : {}
2261
+ } : {},
2262
+ ...profile.platform_url ? { platformUrl: profile.platform_url } : {},
2263
+ ...profile.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
2264
+ ...profile.console_url ? { consoleUrl: profile.console_url } : {}
2150
2265
  };
2151
2266
  logger.out(profileInfo);
2152
2267
  }
@@ -2707,11 +2822,11 @@ function findTarget(lock, kind, workspaceName) {
2707
2822
 
2708
2823
  //#endregion
2709
2824
  //#region src/cli/commands/setup/branch.workflow.yml
2710
- var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2825
+ var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n # __ERD_PREVIEW_JOB_START__\n tailor-erd-preview-matrix:\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n namespaces: ${{ steps.tailor-erd-preview-matrix.outputs.namespaces }}\n base-app-dir: ${{ steps.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-erd-preview-matrix\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n WORKSPACE_NAME: \"__WORKSPACE_NAME__\"\n run: |\n set -euo pipefail\n node <<'NODE'\n const fs = require(\"node:fs\");\n\n const appDir = process.env.APP_DIR || \".\";\n const workspaceName = process.env.WORKSPACE_NAME;\n const output = process.env.GITHUB_OUTPUT;\n const dirPattern = /^[A-Za-z0-9._/-]+$/;\n const namespacePattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n function lockTarget(file) {\n try {\n const lock = JSON.parse(fs.readFileSync(file, \"utf8\"));\n return lock.targets?.find(\n (candidate) =>\n candidate.kind === \"branch\" &&\n candidate.workspaceName === workspaceName,\n );\n } catch (error) {\n if (error && error.code === \"ENOENT\") return undefined;\n throw error;\n }\n }\n\n function namespacesFromTarget(target, label) {\n const namespaces =\n target?.inputs?.erdPreview === true && Array.isArray(target.inputs?.erdNamespaces)\n ? target.inputs.erdNamespaces\n : [];\n for (const namespace of namespaces) {\n if (typeof namespace !== \"string\" || !namespacePattern.test(namespace)) {\n throw new Error(`Invalid ERD namespace in ${label}: ${String(namespace)}`);\n }\n }\n return namespaces;\n }\n\n function normalizeDir(value, label) {\n const raw = typeof value === \"string\" && value.length > 0 ? value : appDir;\n const normalized =\n raw.replaceAll(\"\\\\\", \"/\").replace(/\\/{2,}/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/$/, \"\") ||\n \".\";\n if (\n !dirPattern.test(normalized) ||\n normalized.startsWith(\"/\") ||\n normalized.split(\"/\").includes(\"..\")\n ) {\n throw new Error(`Invalid ERD app dir in ${label}: ${raw}`);\n }\n return normalized;\n }\n\n const headTarget = lockTarget(\".github/tailor-sdk.lock\");\n const baseTarget = lockTarget(\".tailor-erd-base/.github/tailor-sdk.lock\");\n const baseAppDir = normalizeDir(baseTarget?.inputs?.dir, \"base setup lock\");\n const namespaces = [\n ...new Set([\n ...namespacesFromTarget(headTarget, \"head setup lock\"),\n ...namespacesFromTarget(baseTarget, \"base setup lock\"),\n ]),\n ].sort((a, b) => a.localeCompare(b));\n\n if (namespaces.length === 0) {\n console.error(\"No ERD preview namespaces found in the head or base setup lock.\");\n process.exit(1);\n }\n\n fs.appendFileSync(output, `namespaces=${JSON.stringify(namespaces)}\\n`);\n fs.appendFileSync(output, `base-app-dir=${baseAppDir}\\n`);\n NODE\n\n tailor-erd-preview:\n needs: tailor-erd-preview-matrix\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n strategy:\n fail-fast: false\n matrix:\n namespace: ${{ fromJSON(needs.tailor-erd-preview-matrix.outputs.namespaces) }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-detect-base-package-manager\n shell: bash\n run: |\n set -euo pipefail\n cd .tailor-erd-base\n base_package_manager=\"$(\n if [ -f pnpm-lock.yaml ]; then\n echo pnpm\n elif [ -f yarn.lock ]; then\n echo yarn\n elif [ -f bun.lockb ] || [ -f bun.lock ]; then\n echo bun\n elif [ -f package-lock.json ]; then\n echo npm\n else\n echo \"__PACKAGE_MANAGER__\"\n fi\n )\"\n echo \"package-manager=$base_package_manager\" >> \"$GITHUB_OUTPUT\"\n if [ \"$base_package_manager\" = \"pnpm\" ]; then\n base_pnpm_version=\"$(node <<'NODE'\n const fs = require(\"node:fs\");\n\n const fallback = \"10\";\n\n function readManifest(file) {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch {\n return {};\n }\n }\n\n function pnpmVersion(manifest) {\n const packageManager = manifest.packageManager;\n if (typeof packageManager === \"string\" && packageManager.startsWith(\"pnpm@\")) {\n return packageManager.slice(\"pnpm@\".length).split(\"+\")[0];\n }\n\n const devPackageManager = manifest.devEngines?.packageManager;\n if (devPackageManager?.name === \"pnpm\" && devPackageManager.version) {\n return devPackageManager.version;\n }\n\n return undefined;\n }\n\n process.stdout.write(\n pnpmVersion(readManifest(\"package.json\")) ?? fallback,\n );\n NODE\n )\"\n echo \"pnpm-version=$base_pnpm_version\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-setup-base-pnpm\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'pnpm'\n uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9\n with:\n version: ${{ steps.tailor-detect-base-package-manager.outputs['pnpm-version'] }}\n package_json_file: .tailor-erd-base/package.json\n run_install: false\n - id: tailor-setup-base-node\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] != 'bun'\n uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n with:\n node-version-file: .tailor-erd-base/package.json\n - id: tailor-setup-base-yarn\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'yarn'\n shell: bash\n working-directory: .tailor-erd-base\n run: |\n set -euo pipefail\n corepack enable\n - id: tailor-setup-base-bun\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'bun'\n uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0\n - id: tailor-install-base\n shell: bash\n working-directory: .tailor-erd-base\n env:\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n run: |\n set -euo pipefail\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) pnpm install --frozen-lockfile ;;\n npm) npm ci ;;\n yarn)\n if grep -q '^__metadata:' yarn.lock; then\n yarn install --immutable\n else\n yarn install --frozen-lockfile\n fi\n ;;\n bun) bun install --frozen-lockfile ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n - id: tailor-restore-head-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-build-erd-preview\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n BASE_APP_DIR: ${{ needs.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n NAMESPACE: ${{ matrix.namespace }}\n run: |\n set -euo pipefail\n\n run_tailor_sdk() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec tailor-sdk \"$@\" ;;\n npm) npx tailor-sdk \"$@\" ;;\n yarn) yarn tailor-sdk \"$@\" ;;\n bun) bunx tailor-sdk \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n run_head_node() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec node \"$@\" ;;\n npm) node \"$@\" ;;\n yarn) yarn node \"$@\" ;;\n bun) node \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n tailor_sdk_bin=\"$(\n run_head_node - <<'NODE'\n const path = require(\"node:path\");\n const { createRequire } = require(\"node:module\");\n const appDir = process.env.APP_DIR || \".\";\n const githubWorkspace = process.env.GITHUB_WORKSPACE;\n if (!githubWorkspace) {\n throw new Error(\"GITHUB_WORKSPACE is required.\");\n }\n\n let lastError;\n for (const manifestPath of [\n path.join(githubWorkspace, appDir, \"package.json\"),\n path.join(githubWorkspace, \"package.json\"),\n ]) {\n try {\n const requireFromManifest = createRequire(manifestPath);\n process.stdout.write(\n path.join(\n path.dirname(requireFromManifest.resolve(\"@tailor-platform/sdk/cli\")),\n \"index.mjs\",\n ),\n );\n process.exit(0);\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError || new Error(\"Failed to resolve @tailor-platform/sdk/cli.\");\n NODE\n )\"\n\n run_head_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$GITHUB_WORKSPACE\" || exit 1\n case \"__PACKAGE_MANAGER__\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n run_base_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$command_cwd\" || exit 1\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n head_output=\"$RUNNER_TEMP/tailor-erd/head\"\n base_output=\"$RUNNER_TEMP/tailor-erd/base\"\n preview_output=\"$RUNNER_TEMP/tailor-erd/preview\"\n dts_output=\"$RUNNER_TEMP/tailor-erd/dts\"\n head_cli_runner=\"$RUNNER_TEMP/tailor-erd/run-head-tailor-sdk.mjs\"\n head_log=\"$RUNNER_TEMP/tailor-erd/head-$NAMESPACE.log\"\n base_log=\"$RUNNER_TEMP/tailor-erd/base-$NAMESPACE.log\"\n rm -rf \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n mkdir -p \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n cat > \"$head_cli_runner\" <<'NODE'\n import { pathToFileURL } from \"node:url\";\n\n const cliBin = process.env.TAILOR_SDK_BIN;\n const commandCwd = process.env.TAILOR_SDK_CWD;\n if (!cliBin || !commandCwd) {\n throw new Error(\"TAILOR_SDK_BIN and TAILOR_SDK_CWD are required.\");\n }\n\n process.chdir(commandCwd);\n process.argv = [process.argv[0], cliBin, ...process.argv.slice(2)];\n await import(pathToFileURL(cliBin).href);\n NODE\n\n head_config=\"$GITHUB_WORKSPACE/$APP_DIR/tailor.config.ts\"\n base_config=\"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR/tailor.config.ts\"\n head_html=\"$head_output/$NAMESPACE/dist/index.html\"\n base_html=\"$base_output/$NAMESPACE/dist/index.html\"\n preview_html=\"$preview_output/$NAMESPACE-viewer.html\"\n\n head_missing=\"false\"\n if [ ! -f \"$head_config\" ]; then\n echo \"Head ERD config not found; rendering base objects as removed.\"\n head_missing=\"true\"\n elif ! (\n cd \"$GITHUB_WORKSPACE/$APP_DIR\" || exit 1\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/head-$NAMESPACE.d.ts\" \\\n run_tailor_sdk tailordb erd export --config \"$head_config\" --namespace \"$NAMESPACE\" --output \"$head_output\"\n ) >\"$head_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$head_log\"; then\n cat \"$head_log\"\n echo \"Head ERD namespace '$NAMESPACE' not found; rendering base objects as removed.\"\n head_missing=\"true\"\n else\n cat \"$head_log\"\n exit 1\n fi\n elif [ -s \"$head_log\" ]; then\n cat \"$head_log\"\n fi\n\n base_missing=\"false\"\n if [ ! -f \"$base_config\" ]; then\n echo \"Base ERD config not found; rendering current objects as added.\"\n base_missing=\"true\"\n elif ! (\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/base-$NAMESPACE.d.ts\" \\\n run_base_tailor_sdk_bin \"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR\" tailordb erd export --config \"$base_config\" --namespace \"$NAMESPACE\" --output \"$base_output\"\n ) >\"$base_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$base_log\"; then\n cat \"$base_log\"\n echo \"Base ERD namespace '$NAMESPACE' not found; rendering current objects as added.\"\n base_missing=\"true\"\n else\n cat \"$base_log\"\n exit 1\n fi\n elif [ -s \"$base_log\" ]; then\n cat \"$base_log\"\n fi\n\n if [ \"$head_missing\" = \"true\" ] && [ \"$base_missing\" = \"true\" ]; then\n echo \"::error::ERD namespace '$NAMESPACE' was not found in head or base.\"\n exit 1\n fi\n if [ \"$head_missing\" = \"false\" ] && [ ! -f \"$head_html\" ]; then\n echo \"::error::Head ERD viewer was not generated at $head_html\"\n exit 1\n fi\n if [ \"$base_missing\" = \"false\" ] && [ ! -f \"$base_html\" ]; then\n echo \"::error::Base ERD viewer was not generated at $base_html\"\n exit 1\n fi\n\n diff_args=(--namespace \"$NAMESPACE\" --output \"$preview_html\")\n if [ \"$head_missing\" = \"false\" ]; then\n diff_args+=(--head-html \"$head_html\")\n fi\n if [ \"$base_missing\" = \"false\" ]; then\n diff_args+=(--base-html \"$base_html\")\n fi\n run_head_tailor_sdk_bin \"$GITHUB_WORKSPACE\" tailordb erd diff \"${diff_args[@]}\"\n - id: tailor-upload-erd-viewer\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n with:\n name: ${{ matrix.namespace }}-viewer.html\n path: ${{ runner.temp }}/tailor-erd/preview/${{ matrix.namespace }}-viewer.html\n if-no-files-found: error\n archive: false\n retention-days: 7\n\n # __ERD_PREVIEW_JOB_END__\n # __ERD_PREVIEW_COMMENT_JOB_START__\n tailor-erd-preview-comment:\n needs: tailor-erd-preview\n if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n actions: read\n pull-requests: write\n steps:\n - id: tailor-comment-erd-preview\n env:\n GH_TOKEN: ${{ github.token }}\n REPO: ${{ github.repository }}\n RUN_ID: ${{ github.run_id }}\n SERVER: ${{ github.server_url }}\n PR_NUMBER: ${{ github.event.pull_request.number }}\n BASE_REF: ${{ github.event.pull_request.base.ref }}\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\n MARKER: \"<!-- tailor-erd-preview: __WORKSPACE_NAME__ -->\"\n run: |\n set -euo pipefail\n\n current_head=$(gh api \"repos/$REPO/pulls/$PR_NUMBER\" --jq '.head.sha')\n if [ \"$current_head\" != \"$HEAD_SHA\" ]; then\n echo \"Skipping stale ERD preview comment for $HEAD_SHA; current head is $current_head.\"\n exit 0\n fi\n\n body=\"$MARKER\"$'\\n'\"### TailorDB ERD preview (__WORKSPACE_NAME__)\"$'\\n\\n'\n body+=\"Self-contained ERD viewer HTML artifacts for this pull request. Each viewer can switch between the current schema and a diff against base branch \\`$BASE_REF\\`.\"$'\\n'\n\n while IFS=$'\\t' read -r name id; do\n [ -z \"$name\" ] && continue\n if [[ \"$name\" != *-viewer.html ]]; then\n continue\n fi\n ns=${name%-viewer.html}\n label=\"$ns viewer\"\n body+=$'\\n'\"- **$label**: [$name]($SERVER/$REPO/actions/runs/$RUN_ID/artifacts/$id)\"\n done < <(gh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts\" --paginate \\\n --jq '.artifacts[] | select(.name | endswith(\"-viewer.html\")) | [.name, (.id|tostring)] | @tsv' | sort)\n\n existing=$(gh api \"repos/$REPO/issues/$PR_NUMBER/comments\" --paginate \\\n --jq 'map(select(.body | contains(env.MARKER))) | .[0].id // empty')\n if [ -n \"$existing\" ]; then\n gh api --method PATCH \"repos/$REPO/issues/comments/$existing\" -f body=\"$body\" >/dev/null\n else\n gh api --method POST \"repos/$REPO/issues/$PR_NUMBER/comments\" -f body=\"$body\" >/dev/null\n fi\n\n # __ERD_PREVIEW_COMMENT_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2711
2826
 
2712
2827
  //#endregion
2713
2828
  //#region src/cli/commands/setup/tag.workflow.yml
2714
- var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n target-branch: \"__BRANCH__\"\n # __TAG_GUARD_JOB_END__\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n\n tailor-deploy:\n needs: tailor-plan\n if: ${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}\n environment: __ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2829
+ var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n target-branch: \"__BRANCH__\"\n # __TAG_GUARD_JOB_END__\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n\n tailor-deploy:\n needs: tailor-plan\n if: ${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}\n environment: __ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2715
2830
 
2716
2831
  //#endregion
2717
2832
  //#region src/cli/commands/setup/templates.ts
@@ -2751,7 +2866,7 @@ function applyCommon(content, params) {
2751
2866
  const { workingDirectory, environment, packageManager } = params;
2752
2867
  let out = line(content, "HEADER", HEADER);
2753
2868
  out = line(out, "WORKING_DIRECTORY", workingDirectory ? `working-directory: ${workingDirectory}` : void 0);
2754
- return out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName).replaceAll("__ENVIRONMENT__", () => environment).replaceAll("__PACKAGE_MANAGER__", () => packageManager);
2869
+ return out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName).replaceAll("__ENVIRONMENT__", () => environment).replaceAll("__PACKAGE_MANAGER__", () => packageManager).replaceAll("__APP_DIR__", () => workingDirectory ?? ".");
2755
2870
  }
2756
2871
  /**
2757
2872
  * Render the branch-target deploy workflow.
@@ -2764,8 +2879,11 @@ function applyCommon(content, params) {
2764
2879
  */
2765
2880
  function renderBranchWorkflow(params) {
2766
2881
  const { branch, plan } = params;
2882
+ const erdPreview = plan ? params.erdPreview : null;
2767
2883
  let out = branch_workflow_default;
2768
2884
  out = block(out, "PLAN_JOB", plan);
2885
+ out = block(out, "ERD_PREVIEW_JOB", erdPreview !== null);
2886
+ out = block(out, "ERD_PREVIEW_COMMENT_JOB", erdPreview !== null);
2769
2887
  out = block(out, "PULL_REQUEST", plan);
2770
2888
  out = block(out, "DISPATCH_INPUTS", plan);
2771
2889
  out = line(out, "DEPLOY_IF", plan ? `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])` : void 0);
@@ -2773,6 +2891,7 @@ function renderBranchWorkflow(params) {
2773
2891
  out = applyCommon(out, params).replaceAll("__BRANCH__", () => branch);
2774
2892
  const generatedIds = [];
2775
2893
  if (plan) generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-generate-check", "tailor-plan/tailor-plan");
2894
+ if (erdPreview) generatedIds.push("tailor-erd-preview-matrix", "tailor-erd-preview-matrix/tailor-checkout", "tailor-erd-preview-matrix/tailor-checkout-base", "tailor-erd-preview-matrix/tailor-erd-preview-matrix", "tailor-erd-preview", "tailor-erd-preview/tailor-checkout", "tailor-erd-preview/tailor-setup", "tailor-erd-preview/tailor-checkout-base", "tailor-erd-preview/tailor-detect-base-package-manager", "tailor-erd-preview/tailor-setup-base-pnpm", "tailor-erd-preview/tailor-setup-base-node", "tailor-erd-preview/tailor-setup-base-yarn", "tailor-erd-preview/tailor-setup-base-bun", "tailor-erd-preview/tailor-install-base", "tailor-erd-preview/tailor-restore-head-setup", "tailor-erd-preview/tailor-build-erd-preview", "tailor-erd-preview/tailor-upload-erd-viewer", "tailor-erd-preview-comment", "tailor-erd-preview-comment/tailor-comment-erd-preview");
2776
2895
  generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-apply");
2777
2896
  return {
2778
2897
  content: out,
@@ -2842,6 +2961,15 @@ function findTargetDrift(target, state) {
2842
2961
  rule: "default-branch",
2843
2962
  message: `The workflow triggers on "${target.inputs.branch}" but the repository default branch is now "${state.defaultBranch}". If this is intentional, ignore this; otherwise re-run setup so the trigger matches the default branch.`
2844
2963
  });
2964
+ if (target.kind === "branch" && target.inputs.erdPreview && state.configExists) {
2965
+ const recorded = [...target.inputs.erdNamespaces ?? []].toSorted((a, b) => a.localeCompare(b));
2966
+ const current = state.erdNamespaces?.toSorted((a, b) => a.localeCompare(b)) ?? null;
2967
+ if (current === null || recorded.length !== current.length || recorded.some((namespace, index) => namespace !== current[index])) findings.push({
2968
+ target: id,
2969
+ rule: "erd-namespaces",
2970
+ message: "TailorDB namespaces for ERD preview changed. Re-run setup so the ERD preview matrix is regenerated."
2971
+ });
2972
+ }
2845
2973
  return findings;
2846
2974
  }
2847
2975
  function detectDefaultBranchSafe(cwd, run) {
@@ -2870,6 +2998,10 @@ function readHash(absFile) {
2870
2998
  return null;
2871
2999
  }
2872
3000
  }
3001
+ async function defaultLoadErdNamespaces$1(configPath) {
3002
+ const { config } = await loadConfig(configPath);
3003
+ return extractOwnedNamespaces(config);
3004
+ }
2873
3005
  /**
2874
3006
  * Audit the generated workflows for drift against the current config/repo
2875
3007
  * state. Read-only: never writes files, the lock, or the config.
@@ -2879,24 +3011,28 @@ function readHash(absFile) {
2879
3011
  * (per-rule ignore / continue-on-error); the CLI itself reports via exit code.
2880
3012
  * @param options - Check options
2881
3013
  */
2882
- function checkGitHub(options) {
3014
+ async function checkGitHub(options) {
2883
3015
  logBetaWarning("setup");
2884
3016
  const { outputDir } = options;
2885
3017
  const lock = readLock(outputDir);
2886
3018
  if (!lock || lock.targets.length === 0) throw new Error("No managed workflows found (.github/tailor-sdk.lock is missing or empty). Run `tailor-sdk setup` first.");
2887
3019
  const exists = options.configExistsAt ?? ((p) => fs$1.existsSync(p));
2888
3020
  const defaultBranch = detectDefaultBranchSafe(outputDir, options.gitRunner);
3021
+ const loadErdNamespaces = options.loadErdNamespaces ?? defaultLoadErdNamespaces$1;
2889
3022
  const findings = [];
2890
3023
  for (const target of lock.targets) {
2891
3024
  const absFile = resolveWithinRoot(outputDir, target.file);
2892
3025
  const currentHash = absFile === null ? null : readHash(absFile);
2893
3026
  const configAbs = resolveWithinRoot(outputDir, path.join(target.inputs.dir, "tailor.config.ts"));
3027
+ const configExists = configAbs !== null && exists(configAbs);
3028
+ const erdNamespaces = target.kind === "branch" && target.inputs.erdPreview && configAbs !== null && configExists ? await loadErdNamespaces(configAbs) : null;
2894
3029
  findings.push(...findTargetDrift(target, {
2895
3030
  fileExists: currentHash !== null,
2896
3031
  currentHash,
2897
- configExists: configAbs !== null && exists(configAbs),
3032
+ configExists,
2898
3033
  defaultBranch,
2899
- templateVersion: 2
3034
+ templateVersion: 5,
3035
+ erdNamespaces
2900
3036
  }));
2901
3037
  }
2902
3038
  const count = lock.targets.length;
@@ -2914,6 +3050,10 @@ async function defaultLoadConfigName(configPath) {
2914
3050
  const { config } = await loadConfig(configPath);
2915
3051
  return config.name;
2916
3052
  }
3053
+ async function defaultLoadErdNamespaces(configPath) {
3054
+ const { config } = await loadConfig(configPath);
3055
+ return extractOwnedNamespaces(config);
3056
+ }
2917
3057
  const WORKSPACE_NAME_RE = /^[a-z0-9-]+$/;
2918
3058
  function validateWorkspaceName(name) {
2919
3059
  if (name.length < 3 || name.length > 63 || !WORKSPACE_NAME_RE.test(name) || name.startsWith("-") || name.endsWith("-")) throw new Error(`Invalid workspace name "${name}". Names must be 3-63 characters of lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. Pass a valid name with --workspace-name.`);
@@ -2934,6 +3074,10 @@ const DIR_RE = /^[A-Za-z0-9._/-]+$/;
2934
3074
  function validateDir(dir) {
2935
3075
  if (!DIR_RE.test(dir)) throw new Error(`Invalid --dir "${dir}". Only letters, numbers, ".", "_", "/", and "-" are supported.`);
2936
3076
  }
3077
+ const ERD_NAMESPACE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
3078
+ function validateErdNamespaces(namespaces) {
3079
+ for (const namespace of namespaces) if (!ERD_NAMESPACE_RE.test(namespace)) throw new Error(`TailorDB namespace "${namespace}" cannot be used in --erd-preview. Only letters, numbers, '.', '_', and '-' are supported, and the name must start with a letter or number.`);
3080
+ }
2937
3081
  function escapesRoot(rel) {
2938
3082
  return rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../") || path.isAbsolute(rel);
2939
3083
  }
@@ -2967,6 +3111,7 @@ function resolveConfigPath(outputDir, dir) {
2967
3111
  */
2968
3112
  async function resolve$1(options) {
2969
3113
  if (options.tag && !options.plan) throw new Error("--no-plan cannot be combined with --tag (tag targets always run plan before deploy). Drop --no-plan or use a branch target.");
3114
+ if (options.erdPreview && (options.tag || !options.plan)) throw new Error("--erd-preview requires a branch target with plan enabled.");
2970
3115
  const dir = options.dir.replaceAll("\\", "/").replace(/\/{2,}/g, "/").replace(/^\.\//, "").replace(/\/$/, "") || ".";
2971
3116
  validateDir(dir);
2972
3117
  const workingDirectory = dir !== "." ? dir : void 0;
@@ -2983,6 +3128,12 @@ async function resolve$1(options) {
2983
3128
  let branch = null;
2984
3129
  let branchAutoDetected = false;
2985
3130
  let render;
3131
+ let erdNamespaces = [];
3132
+ if (options.erdPreview) {
3133
+ erdNamespaces = await (options.loadErdNamespaces ?? defaultLoadErdNamespaces)(configPath);
3134
+ if (erdNamespaces.length === 0) throw new Error("No TailorDB namespaces found for --erd-preview. Define owned db namespaces in tailor.config.ts.");
3135
+ validateErdNamespaces(erdNamespaces);
3136
+ }
2986
3137
  if (kind === "branch") {
2987
3138
  branchAutoDetected = options.branch === void 0;
2988
3139
  branch = options.branch ?? detectDefaultBranch(options.outputDir, options.gitRunner);
@@ -2993,7 +3144,8 @@ async function resolve$1(options) {
2993
3144
  workingDirectory,
2994
3145
  environment,
2995
3146
  packageManager,
2996
- plan: options.plan
3147
+ plan: options.plan,
3148
+ erdPreview: options.erdPreview ? { namespaces: erdNamespaces } : null
2997
3149
  });
2998
3150
  } else {
2999
3151
  branch = options.branch ?? null;
@@ -3015,7 +3167,9 @@ async function resolve$1(options) {
3015
3167
  environment,
3016
3168
  dir,
3017
3169
  packageManager,
3018
- plan: kind === "branch" ? options.plan : true
3170
+ plan: kind === "branch" ? options.plan : true,
3171
+ erdPreview: kind === "branch" ? options.erdPreview : false,
3172
+ erdNamespaces: kind === "branch" && options.erdPreview ? erdNamespaces : void 0
3019
3173
  };
3020
3174
  return {
3021
3175
  kind,
@@ -3023,6 +3177,7 @@ async function resolve$1(options) {
3023
3177
  branch,
3024
3178
  environment,
3025
3179
  packageManager,
3180
+ erdNamespaces,
3026
3181
  render,
3027
3182
  inputs,
3028
3183
  file,
@@ -3127,7 +3282,7 @@ async function setupGitHub(options) {
3127
3282
  kind: resolved.kind,
3128
3283
  workspaceName: resolved.workspaceName,
3129
3284
  file: resolved.file,
3130
- templateVersion: 2,
3285
+ templateVersion: 5,
3131
3286
  inputs: resolved.inputs,
3132
3287
  generatedIds: resolved.render.generatedIds,
3133
3288
  ejectedIds: existing?.ejectedIds ?? [],
@@ -3156,8 +3311,8 @@ const checkCommand = defineAppCommand({
3156
3311
  name: "check",
3157
3312
  description: "Audit generated workflows for drift against the current config/repo (read-only).",
3158
3313
  args: z.object({}).strict(),
3159
- run: () => {
3160
- checkGitHub({ outputDir: process.cwd() });
3314
+ run: async () => {
3315
+ await checkGitHub({ outputDir: process.cwd() });
3161
3316
  }
3162
3317
  });
3163
3318
  const setupCommand = defineAppCommand({
@@ -3177,6 +3332,7 @@ const setupCommand = defineAppCommand({
3177
3332
  "tag-pattern": arg(z.string().min(1).optional(), { description: "Tag glob to match (requires --tag; defaults to v*)" }),
3178
3333
  environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs (defaults to the workspace name)" }),
3179
3334
  "no-plan": arg(z.boolean().default(false), { description: "Disable the plan job for a branch target (cannot be combined with --tag)" }),
3335
+ "erd-preview": arg(z.boolean().default(false), { description: "Add PR ERD viewer artifacts with current/diff previews for TailorDB namespaces" }),
3180
3336
  dir: arg(z.string().min(1).default("."), {
3181
3337
  alias: "d",
3182
3338
  description: "App directory (for monorepo setups)"
@@ -3194,6 +3350,7 @@ const setupCommand = defineAppCommand({
3194
3350
  tagPattern: args["tag-pattern"] ?? "v*",
3195
3351
  environment: args.environment,
3196
3352
  plan: !args["no-plan"],
3353
+ erdPreview: args["erd-preview"],
3197
3354
  dir: args.dir,
3198
3355
  force: args.force,
3199
3356
  outputDir: process.cwd()
@@ -3331,7 +3488,7 @@ async function withTimeout(p, ms, message) {
3331
3488
  //#endregion
3332
3489
  //#region src/cli/commands/staticwebsite/deploy.ts
3333
3490
  const CHUNK_SIZE = 64 * 1024;
3334
- const IGNORED_FILES = new Set([
3491
+ const IGNORED_FILES = /* @__PURE__ */ new Set([
3335
3492
  ".DS_Store",
3336
3493
  "thumbs.db",
3337
3494
  "desktop.ini"
@@ -3971,6 +4128,9 @@ async function initErdDeployContext(args) {
3971
4128
  const VIEWER_ASSETS_DIR = "erd-viewer-assets";
3972
4129
  const STYLES_LINK = "<link rel=\"stylesheet\" href=\"./styles.css\" />";
3973
4130
  const APP_SCRIPT = "<script src=\"./app.js\" type=\"module\"><\/script>";
4131
+ function escapeHtml(value) {
4132
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
4133
+ }
3974
4134
  function assetDirCandidates() {
3975
4135
  const currentDir = path.dirname(fileURLToPath(import.meta.url));
3976
4136
  return [
@@ -3993,8 +4153,8 @@ function resolveViewerAssetsDir() {
3993
4153
  * are inlined as separately extractable blocks: a `<style>` element, a
3994
4154
  * `<script type="module">`, and a `<script type="application/json"
3995
4155
  * id="erd-schema">` data block. This renders without any sibling asset files
3996
- * and lets external tooling (e.g. a future ERD diff) pull out the schema via
3997
- * `JSON.parse`.
4156
+ * and lets external tooling (including the ERD diff command) pull out the
4157
+ * schema via `JSON.parse`.
3998
4158
  * @param options - Viewer build options.
3999
4159
  * @returns The self-contained HTML document.
4000
4160
  */
@@ -4005,8 +4165,10 @@ function buildViewerHtml(options) {
4005
4165
  const appJs = fs$1.readFileSync(path.join(assetsDir, "app.js"), "utf8");
4006
4166
  if (!html.includes(STYLES_LINK) || !html.includes(APP_SCRIPT)) throw new Error("ERD viewer index.html is missing expected asset references for inlining.");
4007
4167
  const embedScript = `<script type="application/json" id="erd-schema">${JSON.stringify(options.schema).replaceAll("<", "\\u003c")}<\/script>`;
4168
+ const currentSchemaScript = options.currentSchema === void 0 ? "" : `\n <script type="application/json" id="erd-current-schema">${JSON.stringify(options.currentSchema).replaceAll("<", "\\u003c")}<\/script>`;
4169
+ const diffScript = options.diff === void 0 ? "" : `\n <script type="application/json" id="erd-diff">${JSON.stringify(options.diff).replaceAll("<", "\\u003c")}<\/script>`;
4008
4170
  const inlineScript = `<script type="module">\n${appJs.replace(/<\/script/gi, "<\\/script")}\n<\/script>`;
4009
- return html.replace(STYLES_LINK, `<style>\n${css}\n</style>`).replace(APP_SCRIPT, `${embedScript}\n ${inlineScript}`);
4171
+ return html.replace("<title>TailorDB ERD</title>", `<title>${escapeHtml(options.title ?? "TailorDB ERD")}</title>`).replace(STYLES_LINK, `<style>\n${css}\n</style>`).replace(APP_SCRIPT, `${embedScript}${currentSchemaScript}${diffScript}\n ${inlineScript}`);
4010
4172
  }
4011
4173
  /**
4012
4174
  * Write the self-contained TailorDB ERD viewer to `<distDir>/index.html`.
@@ -4176,6 +4338,362 @@ const erdDeployCommand = defineAppCommand({
4176
4338
  }
4177
4339
  });
4178
4340
 
4341
+ //#endregion
4342
+ //#region src/cli/commands/tailordb/erd/diff.ts
4343
+ const SCHEMA_BLOCK_PATTERN = /<script type="application\/json" id="erd-schema">([\s\S]*?)<\/script>/;
4344
+ function stableValue(value) {
4345
+ if (Array.isArray(value)) return value.map((item) => stableValue(item));
4346
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, stableValue(item)]));
4347
+ return value;
4348
+ }
4349
+ function stableJson(value) {
4350
+ return JSON.stringify(stableValue(value));
4351
+ }
4352
+ function changedFields(base, head) {
4353
+ return [.../* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(head)])].toSorted((a, b) => a.localeCompare(b)).filter((key) => stableJson(base[key]) !== stableJson(head[key]));
4354
+ }
4355
+ function detailForChangedFields(fields) {
4356
+ return `Changed fields: ${fields.join(", ")}`;
4357
+ }
4358
+ function mapByName(items) {
4359
+ return new Map(items.map((item) => [item.name, item]));
4360
+ }
4361
+ function compareCommonNamed(baseItems, headItems, addChange) {
4362
+ const baseByName = mapByName(baseItems);
4363
+ const headByName = mapByName(headItems);
4364
+ const commonNames = [...baseByName.keys()].filter((name) => headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
4365
+ for (const name of commonNames) {
4366
+ const base = baseByName.get(name);
4367
+ const head = headByName.get(name);
4368
+ if (!base || !head) continue;
4369
+ const fields = changedFields(base, head);
4370
+ if (fields.length > 0) addChange(name, fields);
4371
+ }
4372
+ }
4373
+ function removedNames(baseItems, headItems) {
4374
+ const headByName = mapByName(headItems);
4375
+ return baseItems.map((item) => item.name).filter((name) => !headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
4376
+ }
4377
+ function addedNames(baseItems, headItems) {
4378
+ const baseByName = mapByName(baseItems);
4379
+ return headItems.map((item) => item.name).filter((name) => !baseByName.has(name)).toSorted((a, b) => a.localeCompare(b));
4380
+ }
4381
+ function tableMetadata(table) {
4382
+ return {
4383
+ backwardRelationships: table.backwardRelationships,
4384
+ description: table.description,
4385
+ forwardRelationships: table.forwardRelationships,
4386
+ pluralForm: table.pluralForm,
4387
+ source: table.source
4388
+ };
4389
+ }
4390
+ function pushChange(changes, change) {
4391
+ changes.push(change);
4392
+ }
4393
+ function diffColumns(changes, tableName, baseColumns, headColumns) {
4394
+ compareCommonNamed(baseColumns, headColumns, (name, fields) => {
4395
+ pushChange(changes, {
4396
+ action: "changed",
4397
+ entity: "column",
4398
+ path: `${tableName}.${name}`,
4399
+ detail: detailForChangedFields(fields)
4400
+ });
4401
+ });
4402
+ for (const name of removedNames(baseColumns, headColumns)) pushChange(changes, {
4403
+ action: "removed",
4404
+ entity: "column",
4405
+ path: `${tableName}.${name}`,
4406
+ detail: "Column removed"
4407
+ });
4408
+ for (const name of addedNames(baseColumns, headColumns)) pushChange(changes, {
4409
+ action: "added",
4410
+ entity: "column",
4411
+ path: `${tableName}.${name}`,
4412
+ detail: "Column added"
4413
+ });
4414
+ }
4415
+ function diffIndexes(changes, tableName, baseIndexes, headIndexes) {
4416
+ compareCommonNamed(baseIndexes, headIndexes, (name, fields) => {
4417
+ pushChange(changes, {
4418
+ action: "changed",
4419
+ entity: "index",
4420
+ path: `${tableName}.${name}`,
4421
+ detail: detailForChangedFields(fields)
4422
+ });
4423
+ });
4424
+ for (const name of removedNames(baseIndexes, headIndexes)) pushChange(changes, {
4425
+ action: "removed",
4426
+ entity: "index",
4427
+ path: `${tableName}.${name}`,
4428
+ detail: "Index removed"
4429
+ });
4430
+ for (const name of addedNames(baseIndexes, headIndexes)) pushChange(changes, {
4431
+ action: "added",
4432
+ entity: "index",
4433
+ path: `${tableName}.${name}`,
4434
+ detail: "Index added"
4435
+ });
4436
+ }
4437
+ function diffTables(changes, baseTables, headTables) {
4438
+ const baseByName = mapByName(baseTables);
4439
+ const headByName = mapByName(headTables);
4440
+ for (const name of removedNames(baseTables, headTables)) pushChange(changes, {
4441
+ action: "removed",
4442
+ entity: "table",
4443
+ path: name,
4444
+ detail: "Table removed"
4445
+ });
4446
+ for (const name of addedNames(baseTables, headTables)) pushChange(changes, {
4447
+ action: "added",
4448
+ entity: "table",
4449
+ path: name,
4450
+ detail: "Table added"
4451
+ });
4452
+ const commonNames = [...baseByName.keys()].filter((name) => headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
4453
+ for (const name of commonNames) {
4454
+ const base = baseByName.get(name);
4455
+ const head = headByName.get(name);
4456
+ if (!base || !head) continue;
4457
+ const tableFields = changedFields(tableMetadata(base), tableMetadata(head));
4458
+ if (tableFields.length > 0) pushChange(changes, {
4459
+ action: "changed",
4460
+ entity: "table",
4461
+ path: name,
4462
+ detail: detailForChangedFields(tableFields)
4463
+ });
4464
+ diffColumns(changes, name, base.columns, head.columns);
4465
+ diffIndexes(changes, name, base.indexes, head.indexes);
4466
+ }
4467
+ }
4468
+ function diffRelations(changes, baseRelations, headRelations) {
4469
+ compareCommonNamed(baseRelations, headRelations, (name, fields) => {
4470
+ pushChange(changes, {
4471
+ action: "changed",
4472
+ entity: "relation",
4473
+ path: name,
4474
+ detail: detailForChangedFields(fields)
4475
+ });
4476
+ });
4477
+ for (const name of removedNames(baseRelations, headRelations)) pushChange(changes, {
4478
+ action: "removed",
4479
+ entity: "relation",
4480
+ path: name,
4481
+ detail: "Relation removed"
4482
+ });
4483
+ for (const name of addedNames(baseRelations, headRelations)) pushChange(changes, {
4484
+ action: "added",
4485
+ entity: "relation",
4486
+ path: name,
4487
+ detail: "Relation added"
4488
+ });
4489
+ }
4490
+ function summarize(changes) {
4491
+ return {
4492
+ added: changes.filter((change) => change.action === "added").length,
4493
+ changed: changes.filter((change) => change.action === "changed").length,
4494
+ removed: changes.filter((change) => change.action === "removed").length
4495
+ };
4496
+ }
4497
+ function extractEmbeddedErdSchema(html) {
4498
+ const match = SCHEMA_BLOCK_PATTERN.exec(html);
4499
+ if (!match?.[1]) throw new Error("ERD schema block not found.");
4500
+ try {
4501
+ return JSON.parse(match[1]);
4502
+ } catch (error) {
4503
+ throw new Error(`Failed to parse ERD schema block: ${String(error)}`, { cause: error });
4504
+ }
4505
+ }
4506
+ function createEmptyErdSchema(options) {
4507
+ return {
4508
+ version: 1,
4509
+ namespace: options.namespace,
4510
+ generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
4511
+ revision: options.revision,
4512
+ source: "local",
4513
+ cleanRoom: {
4514
+ implementation: "tailor-sdk",
4515
+ notes: ["Synthetic empty schema used for ERD diff generation."]
4516
+ },
4517
+ tables: [],
4518
+ relations: []
4519
+ };
4520
+ }
4521
+ function buildErdSchemaDiff(options) {
4522
+ const { base, head } = options;
4523
+ if (base.namespace !== head.namespace) throw new Error(`Cannot diff ERD schemas from different namespaces: ${base.namespace}, ${head.namespace}`);
4524
+ const changes = [];
4525
+ diffTables(changes, base.tables, head.tables);
4526
+ diffRelations(changes, base.relations, head.relations);
4527
+ const summary = summarize(changes);
4528
+ return {
4529
+ namespace: head.namespace,
4530
+ baseRevision: base.revision,
4531
+ headRevision: head.revision,
4532
+ changed: changes.length > 0,
4533
+ summary,
4534
+ changes
4535
+ };
4536
+ }
4537
+ function mergeNamedByHead(baseItems, headItems) {
4538
+ const headByName = mapByName(headItems);
4539
+ const removedItems = baseItems.filter((item) => !headByName.has(item.name)).toSorted((a, b) => a.name.localeCompare(b.name));
4540
+ return [...headItems, ...removedItems];
4541
+ }
4542
+ function mergeDiffViewerTable(baseTable, headTable) {
4543
+ return {
4544
+ ...headTable,
4545
+ columns: mergeNamedByHead(baseTable.columns, headTable.columns),
4546
+ indexes: mergeNamedByHead(baseTable.indexes, headTable.indexes)
4547
+ };
4548
+ }
4549
+ function mergeDiffViewerTables(baseTables, headTables) {
4550
+ const baseByName = mapByName(baseTables);
4551
+ const headByName = mapByName(headTables);
4552
+ const merged = headTables.map((headTable) => {
4553
+ const baseTable = baseByName.get(headTable.name);
4554
+ return baseTable ? mergeDiffViewerTable(baseTable, headTable) : headTable;
4555
+ });
4556
+ const removedTables = baseTables.filter((table) => !headByName.has(table.name)).toSorted((a, b) => a.name.localeCompare(b.name));
4557
+ return [...merged, ...removedTables];
4558
+ }
4559
+ /**
4560
+ * Build the schema rendered by the diff viewer. The ordinary ERD viewer only
4561
+ * knows how to draw objects present in its schema, so the diff schema keeps
4562
+ * base-only tables, columns, indexes, and relations visible for removal
4563
+ * highlighting while preferring head-side metadata for unchanged objects.
4564
+ * @param options - Base and head schemas to merge for diff rendering.
4565
+ * @returns A TailorDB ERD schema suitable for the visual diff viewer.
4566
+ */
4567
+ function buildErdDiffViewerSchema(options) {
4568
+ const { base, head } = options;
4569
+ if (base.namespace !== head.namespace) throw new Error(`Cannot diff ERD schemas from different namespaces: ${base.namespace}, ${head.namespace}`);
4570
+ return {
4571
+ ...head,
4572
+ tables: mergeDiffViewerTables(base.tables, head.tables),
4573
+ relations: mergeNamedByHead(base.relations, head.relations)
4574
+ };
4575
+ }
4576
+ function renderErdDiffHtml(options) {
4577
+ return buildViewerHtml({
4578
+ schema: options.schema,
4579
+ currentSchema: options.currentSchema,
4580
+ diff: options.diff,
4581
+ title: `TailorDB ERD diff - ${options.diff.namespace}`
4582
+ });
4583
+ }
4584
+
4585
+ //#endregion
4586
+ //#region src/cli/commands/tailordb/erd/diff-command.ts
4587
+ function readSchema(filePath) {
4588
+ if (!filePath) return void 0;
4589
+ return extractEmbeddedErdSchema(fs$1.readFileSync(filePath, "utf8"));
4590
+ }
4591
+ function resolveNamespace(options) {
4592
+ const namespace = options.namespace ?? options.head?.namespace ?? options.base?.namespace;
4593
+ if (!namespace) throw new Error("Missing --namespace when one side of the ERD diff is omitted.");
4594
+ if (options.base && options.base.namespace !== namespace) throw new Error(`Base ERD namespace "${options.base.namespace}" does not match "${namespace}".`);
4595
+ if (options.head && options.head.namespace !== namespace) throw new Error(`Head ERD namespace "${options.head.namespace}" does not match "${namespace}".`);
4596
+ return namespace;
4597
+ }
4598
+ function writeFile(filePath, content) {
4599
+ fs$1.mkdirSync(path.dirname(filePath), { recursive: true });
4600
+ fs$1.writeFileSync(filePath, content, "utf8");
4601
+ }
4602
+ function writeErdDiff(options) {
4603
+ const base = readSchema(options.baseHtml);
4604
+ const head = readSchema(options.headHtml);
4605
+ if (!base && !head) throw new Error("At least one of --base-html or --head-html is required.");
4606
+ const namespace = resolveNamespace({
4607
+ namespace: options.namespace,
4608
+ base,
4609
+ head
4610
+ });
4611
+ const baseSchema = base ?? createEmptyErdSchema({
4612
+ namespace,
4613
+ revision: "missing-base"
4614
+ });
4615
+ const headSchema = head ?? createEmptyErdSchema({
4616
+ namespace,
4617
+ revision: "missing-head"
4618
+ });
4619
+ const diff = buildErdSchemaDiff({
4620
+ base: baseSchema,
4621
+ head: headSchema
4622
+ });
4623
+ const viewerSchema = buildErdDiffViewerSchema({
4624
+ base: baseSchema,
4625
+ head: headSchema
4626
+ });
4627
+ writeFile(options.outputHtml, renderErdDiffHtml({
4628
+ schema: viewerSchema,
4629
+ currentSchema: headSchema,
4630
+ diff
4631
+ }));
4632
+ if (options.outputJson) writeFile(options.outputJson, `${JSON.stringify(diff, null, 2)}\n`);
4633
+ return {
4634
+ namespace,
4635
+ outputHtml: options.outputHtml,
4636
+ outputJson: options.outputJson,
4637
+ diff
4638
+ };
4639
+ }
4640
+ const erdDiffCommand = defineAppCommand({
4641
+ name: "diff",
4642
+ description: "Render TailorDB ERD schema diff HTML from exported ERD viewers.",
4643
+ args: z.object({
4644
+ "base-html": arg(z.string().optional(), {
4645
+ description: "Base ERD viewer HTML file",
4646
+ completion: {
4647
+ type: "file",
4648
+ matcher: [".html"]
4649
+ }
4650
+ }),
4651
+ "head-html": arg(z.string().optional(), {
4652
+ description: "Head ERD viewer HTML file",
4653
+ completion: {
4654
+ type: "file",
4655
+ matcher: [".html"]
4656
+ }
4657
+ }),
4658
+ namespace: arg(z.string().optional(), {
4659
+ alias: "n",
4660
+ description: "TailorDB namespace name (defaults to the provided ERD schema namespace)"
4661
+ }),
4662
+ output: arg(z.string().min(1), {
4663
+ alias: "o",
4664
+ description: "Output ERD diff HTML file",
4665
+ completion: {
4666
+ type: "file",
4667
+ matcher: [".html"]
4668
+ }
4669
+ }),
4670
+ "output-json": arg(z.string().optional(), {
4671
+ description: "Optional output JSON file for the computed diff",
4672
+ completion: {
4673
+ type: "file",
4674
+ matcher: [".json"]
4675
+ }
4676
+ })
4677
+ }).strict(),
4678
+ run: (args) => {
4679
+ initErdCommand();
4680
+ const result = writeErdDiff({
4681
+ baseHtml: args["base-html"],
4682
+ headHtml: args["head-html"],
4683
+ namespace: args.namespace,
4684
+ outputHtml: args.output,
4685
+ outputJson: args["output-json"]
4686
+ });
4687
+ if (args.json) logger.out({
4688
+ namespace: result.namespace,
4689
+ outputHtml: result.outputHtml,
4690
+ outputJson: result.outputJson,
4691
+ summary: result.diff.summary
4692
+ });
4693
+ else logger.success(`Wrote ERD diff to ${path.relative(process.cwd(), result.outputHtml)}`);
4694
+ }
4695
+ });
4696
+
4179
4697
  //#endregion
4180
4698
  //#region src/cli/commands/tailordb/erd/serve.ts
4181
4699
  const DEFAULT_ERD_BASE_DIR = ".tailor-sdk/erd";
@@ -4536,6 +5054,7 @@ const erdCommand = defineCommand({
4536
5054
  description: "Generate TailorDB ERD viewer artifacts from local TailorDB schema. (beta)",
4537
5055
  subCommands: {
4538
5056
  export: erdExportCommand,
5057
+ diff: erdDiffCommand,
4539
5058
  serve: erdServeCommand,
4540
5059
  deploy: erdDeployCommand
4541
5060
  }
@@ -4872,7 +5391,7 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
4872
5391
  async function assertMigrationsReproduceLocalTypes(loaded, target) {
4873
5392
  const { config, plugins } = loaded;
4874
5393
  const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
4875
- const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-Djeezk3m.mjs");
5394
+ const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-BakHtldG.mjs");
4876
5395
  const application = defineApplication({
4877
5396
  config,
4878
5397
  pluginManager
@@ -4884,7 +5403,7 @@ async function assertMigrationsReproduceLocalTypes(loaded, target) {
4884
5403
  await service.processNamespacePlugins();
4885
5404
  }
4886
5405
  const pluginExecutorFiles = generatePluginFilesIfNeeded(pluginManager, application.tailorDBServices, config.path);
4887
- const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-BHQIerYh.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
5406
+ const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-CRaa4Joe.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
4888
5407
  await executorService?.loadExecutors();
4889
5408
  if (pluginExecutorFiles.length > 0) await executorService?.loadPluginExecutorFiles([...pluginExecutorFiles]);
4890
5409
  const executorUsedTypes = /* @__PURE__ */ new Set();
@@ -5161,7 +5680,7 @@ const upgradeCommand = defineAppCommand({
5161
5680
  run: async (args) => {
5162
5681
  const { initTelemetry } = await import("../telemetry-w92bvGdC.mjs");
5163
5682
  await initTelemetry();
5164
- const { upgrade } = await import("../service-DMohAx8a.mjs");
5683
+ const { upgrade } = await import("../service-DDWgZL_L2.mjs");
5165
5684
  await upgrade({
5166
5685
  from: args.from,
5167
5686
  dryRun: args["dry-run"],
@@ -5178,25 +5697,52 @@ const currentCommand = defineAppCommand({
5178
5697
  args: z.object({}).strict(),
5179
5698
  run: async () => {
5180
5699
  const config = await readPlatformConfig();
5700
+ const profile = process.env.TAILOR_PLATFORM_PROFILE;
5701
+ const profileEntry = profile ? config.profiles[profile] : void 0;
5702
+ if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
5703
+ const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
5704
+ const currentUser = profile ? profileEntry?.user ?? null : config.current_user;
5181
5705
  const jsonOutput = logger.jsonMode;
5182
- if (!config.current_user) throw new Error(multiline`
5706
+ if (!currentUser) throw new Error(multiline`
5183
5707
  Current user not set.
5184
5708
  Please login first using 'tailor-sdk login' command to register a user.
5185
5709
  `);
5186
- if (!config.users[config.current_user]) throw new Error(multiline`
5187
- Current user '${config.current_user}' not found in registered users.
5710
+ if (!hasUserTokenEntry(config, currentUser, platformConfig)) throw new Error(multiline`
5711
+ Current user '${currentUser}' not found in registered users.
5188
5712
  Please login again using 'tailor-sdk login' command to register the user.
5189
5713
  `);
5190
5714
  if (jsonOutput) {
5191
- logger.out({ user: config.current_user });
5715
+ logger.out({ user: currentUser });
5192
5716
  return;
5193
5717
  }
5194
- logger.out(config.current_user);
5718
+ logger.out(currentUser);
5195
5719
  }
5196
5720
  });
5197
5721
 
5198
5722
  //#endregion
5199
5723
  //#region src/cli/commands/user/list.ts
5724
+ function activeCurrentUserKey(config) {
5725
+ const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
5726
+ if (!activeProfile) {
5727
+ if (!config.current_user) return null;
5728
+ return resolveUserTokenKey(config, config.current_user);
5729
+ }
5730
+ const profile = config.profiles[activeProfile];
5731
+ if (!profile) return null;
5732
+ return resolveUserTokenKey(config, profile.user, platformConfigFromProfile(profile));
5733
+ }
5734
+ function toUserListInfo(userKey, currentUserKey) {
5735
+ const separatorIndex = userKey.indexOf("|");
5736
+ const platformUrl = separatorIndex === -1 ? null : userKey.slice(0, separatorIndex);
5737
+ return {
5738
+ user: separatorIndex === -1 ? userKey : userKey.slice(separatorIndex + 1),
5739
+ platformUrl,
5740
+ current: currentUserKey === userKey
5741
+ };
5742
+ }
5743
+ function formatUserListInfo(info) {
5744
+ return `${info.user}${info.platformUrl ? ` [${info.platformUrl}]` : ""}${info.current ? " (current)" : ""}`;
5745
+ }
5200
5746
  const listCommand$1 = defineAppCommand({
5201
5747
  name: "list",
5202
5748
  description: "List all users.",
@@ -5213,13 +5759,15 @@ const listCommand$1 = defineAppCommand({
5213
5759
  if (jsonOutput) logger.out([]);
5214
5760
  return;
5215
5761
  }
5762
+ const currentUserKey = activeCurrentUserKey(config);
5763
+ const userInfos = users.map((user) => toUserListInfo(user, currentUserKey));
5216
5764
  if (jsonOutput) {
5217
- logger.out(users);
5765
+ logger.out([...new Set(userInfos.map((userInfo) => userInfo.user))]);
5218
5766
  return;
5219
5767
  }
5220
- users.forEach((user) => {
5221
- if (user === config.current_user) logger.success(`${user} (current)`, { mode: "plain" });
5222
- else logger.log(user);
5768
+ userInfos.forEach((userInfo) => {
5769
+ if (userInfo.current) logger.success(formatUserListInfo(userInfo), { mode: "plain" });
5770
+ else logger.log(formatUserListInfo(userInfo));
5223
5771
  });
5224
5772
  }
5225
5773
  });
@@ -5280,6 +5828,24 @@ function printCreatedToken(name, token, write, action) {
5280
5828
  `);
5281
5829
  }
5282
5830
 
5831
+ //#endregion
5832
+ //#region src/cli/commands/user/pat/user.ts
5833
+ function resolvePatUser(config) {
5834
+ const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
5835
+ if (activeProfile) return config.profiles[activeProfile]?.user ?? null;
5836
+ return config.current_user;
5837
+ }
5838
+ async function createPatOperatorClient() {
5839
+ const config = await readPlatformConfig();
5840
+ const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
5841
+ const profileEntry = activeProfile ? config.profiles[activeProfile] : void 0;
5842
+ if (activeProfile && !profileEntry) throw new Error(`Profile "${activeProfile}" not found`);
5843
+ const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
5844
+ const user = resolvePatUser(config);
5845
+ if (!user) throw new Error("No user logged in.\nPlease login first using 'tailor-sdk login' command.");
5846
+ return await initOperatorClient(await fetchLatestToken(config, user, platformConfig), platformConfig);
5847
+ }
5848
+
5283
5849
  //#endregion
5284
5850
  //#region src/cli/commands/user/pat/create.ts
5285
5851
  const createCommand = defineAppCommand({
@@ -5297,12 +5863,7 @@ const createCommand = defineAppCommand({
5297
5863
  }).strict(),
5298
5864
  run: async (args) => {
5299
5865
  await assertWritable();
5300
- const config = await readPlatformConfig();
5301
- if (!config.current_user) throw new Error(multiline`
5302
- No user logged in.
5303
- Please login first using 'tailor-sdk login' command.
5304
- `);
5305
- const client = await initOperatorClient(await fetchLatestToken(config, config.current_user));
5866
+ const client = await createPatOperatorClient();
5306
5867
  const scopes = getScopesFromWriteFlag(args.write);
5307
5868
  const result = await client.createPersonalAccessToken({
5308
5869
  name: args.name,
@@ -5324,12 +5885,7 @@ const deleteCommand = defineAppCommand({
5324
5885
  }) }).strict(),
5325
5886
  run: async (args) => {
5326
5887
  await assertWritable();
5327
- const config = await readPlatformConfig();
5328
- if (!config.current_user) throw new Error(multiline`
5329
- No user logged in.
5330
- Please login first using 'tailor-sdk login' command.
5331
- `);
5332
- await (await initOperatorClient(await fetchLatestToken(config, config.current_user))).deletePersonalAccessToken({ name: args.name });
5888
+ await (await createPatOperatorClient()).deletePersonalAccessToken({ name: args.name });
5333
5889
  logger.success(`Personal access token "${args.name}" deleted successfully.`);
5334
5890
  }
5335
5891
  });
@@ -5342,12 +5898,7 @@ const listCommand = defineAppCommand({
5342
5898
  args: z.object({ ...paginationArgs() }).strict(),
5343
5899
  run: async (args) => {
5344
5900
  const jsonOutput = logger.jsonMode;
5345
- const config = await readPlatformConfig();
5346
- if (!config.current_user) throw new Error(multiline`
5347
- No user logged in.
5348
- Please login first using 'tailor-sdk login' command.
5349
- `);
5350
- const client = await initOperatorClient(await fetchLatestToken(config, config.current_user));
5901
+ const client = await createPatOperatorClient();
5351
5902
  const pageDirection = toPageDirection(args.order);
5352
5903
  const pats = await fetchPaged(async (pageToken, pageSize) => {
5353
5904
  const { personalAccessTokens, nextPageToken } = await client.listPersonalAccessTokens({
@@ -5395,12 +5946,7 @@ const updateCommand = defineAppCommand({
5395
5946
  }).strict(),
5396
5947
  run: async (args) => {
5397
5948
  await assertWritable();
5398
- const config = await readPlatformConfig();
5399
- if (!config.current_user) throw new Error(multiline`
5400
- No user logged in.
5401
- Please login first using 'tailor-sdk login' command.
5402
- `);
5403
- const client = await initOperatorClient(await fetchLatestToken(config, config.current_user));
5949
+ const client = await createPatOperatorClient();
5404
5950
  await client.deletePersonalAccessToken({ name: args.name });
5405
5951
  const scopes = getScopesFromWriteFlag(args.write);
5406
5952
  const result = await client.createPersonalAccessToken({
@@ -5439,11 +5985,17 @@ const switchCommand = defineAppCommand({
5439
5985
  }) }).strict(),
5440
5986
  run: async (args) => {
5441
5987
  const config = await readPlatformConfig();
5442
- if (!config.users[args.user]) throw new Error(multiline`
5988
+ const activeProfileName = process.env.TAILOR_PLATFORM_PROFILE;
5989
+ const activeProfileEntry = activeProfileName ? config.profiles[activeProfileName] : void 0;
5990
+ if (activeProfileName && !activeProfileEntry) throw new Error(`Profile "${activeProfileName}" not found`);
5991
+ const platformConfig = activeProfileEntry ? platformConfigFromProfile(activeProfileEntry) : void 0;
5992
+ if (args.user.includes("|")) throw new Error(`User "${args.user}" looks like a platform-scoped token key. Pass the user name without the platform URL and select the platform with TAILOR_PLATFORM_URL or a profile.`);
5993
+ if (!hasUserTokenEntry(config, args.user, platformConfig)) throw new Error(multiline`
5443
5994
  User "${args.user}" not found.
5444
5995
  Please login first using 'tailor-sdk login' command to register this user.
5445
5996
  `);
5446
- config.current_user = args.user;
5997
+ if (activeProfileEntry) activeProfileEntry.user = args.user;
5998
+ else config.current_user = args.user;
5447
5999
  writePlatformConfig(config);
5448
6000
  logger.success(`Current user set to "${args.user}" successfully.`);
5449
6001
  }
@@ -5588,7 +6140,7 @@ runMain(mainCommand, {
5588
6140
  if (isVerbose() && error.stack) logger.debug(`\nStack trace:\n${error.stack}`);
5589
6141
  } else logger.error(`Unknown error: ${error}`);
5590
6142
  if (!isCLIError(error) && (!(error instanceof Error) || error instanceof TypeError || error instanceof RangeError)) {
5591
- const { reportCrash } = await import("../crashreport-6jpCceOF.mjs");
6143
+ const { reportCrash } = await import("../crashreport-BwF8cHF0.mjs");
5592
6144
  await reportCrash(error, "handledError");
5593
6145
  }
5594
6146
  }