@tailor-platform/sdk 1.76.2 → 1.78.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.
- package/CHANGELOG.md +26 -0
- package/dist/{application-CqjCcZ2n.mjs → application-CUxVA7Fs.mjs} +8 -8
- package/dist/application-CUxVA7Fs.mjs.map +1 -0
- package/dist/application-Dp_-ioiP.mjs +3 -0
- package/dist/cli/commands/deploy/deploy.d.mts +8 -3
- package/dist/cli/commands/show.d.mts +9 -3
- package/dist/cli/commands/workspace/create.d.mts +3 -0
- package/dist/cli/commands/workspace/list.d.mts +1 -0
- package/dist/cli/commands/workspace/transform.d.mts +2 -0
- package/dist/cli/index.mjs +69 -22
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +2 -2
- package/dist/cli/lib.mjs +2 -2
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/shared/workspace-name.d.mts +1 -0
- package/dist/cli/skills.mjs +1 -1
- package/dist/completion/zsh-worker.zsh +18 -1
- package/dist/configure/index.mjs +198 -4
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/idp/index.d.mts +1 -4
- package/dist/{crashreport-DZDr-TvH.mjs → crashreport-BNWUM6Kf.mjs} +2 -2
- package/dist/{crashreport-DZDr-TvH.mjs.map → crashreport-BNWUM6Kf.mjs.map} +1 -1
- package/dist/{crashreport-np4-_DX2.mjs → crashreport-BafWvjjX.mjs} +1 -1
- package/dist/errors-DVFS0AZk.mjs +100 -0
- package/dist/errors-DVFS0AZk.mjs.map +1 -0
- package/dist/{globals-z6zYMjlr.mjs → globals-CvizbGLY.mjs} +2 -2
- package/dist/{globals-z6zYMjlr.mjs.map → globals-CvizbGLY.mjs.map} +1 -1
- package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
- package/dist/logger-BwS4ppwO.mjs.map +1 -0
- package/dist/{registry-CTgoVWc_.mjs → registry-CYBRvfaU.mjs} +2 -2
- package/dist/{registry-CTgoVWc_.mjs.map → registry-CYBRvfaU.mjs.map} +1 -1
- package/dist/{runtime-DlklyuHN.mjs → runtime-BD2vYJFT.mjs} +1169 -539
- package/dist/runtime-BD2vYJFT.mjs.map +1 -0
- package/dist/{schema-DbTR1c6M.mjs → schema-U6DxoZ8D.mjs} +203 -3
- package/dist/schema-U6DxoZ8D.mjs.map +1 -0
- package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
- package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
- package/dist/service-CghpdV-9.mjs +3 -0
- package/dist/{service-DBFZQloR.mjs → service-Cp0I7ZFe.mjs} +3 -3
- package/dist/{service-DBFZQloR.mjs.map → service-Cp0I7ZFe.mjs.map} +1 -1
- package/dist/{service-BCRJ-3NV.mjs → service-D12iQGcS.mjs} +2 -2
- package/dist/{service-BCRJ-3NV.mjs.map → service-D12iQGcS.mjs.map} +1 -1
- package/dist/{test-env-key-IPapa1KJ.mjs → test-env-key-DuZycyWM.mjs} +2 -2
- package/dist/{test-env-key-IPapa1KJ.mjs.map → test-env-key-DuZycyWM.mjs.map} +1 -1
- package/dist/types/auth.generated.d.mts +9 -36
- package/dist/types/executor.generated.d.mts +19 -59
- package/dist/types/idp.generated.d.mts +518 -517
- package/dist/types/resolver.generated.d.mts +6 -2
- package/dist/types/tailordb.generated.d.mts +8 -7
- package/dist/utils/test/index.mjs +1 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/index.d.mts +8 -8
- package/dist/vitest/index.mjs +600 -186
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock.d.mts +8 -8
- package/dist/vitest/mocks/aigateway.d.mts +14 -4
- package/dist/vitest/mocks/authconnection.d.mts +14 -4
- package/dist/vitest/mocks/file.d.mts +24 -6
- package/dist/vitest/mocks/iconv.d.mts +28 -4
- package/dist/vitest/mocks/idp.d.mts +25 -9
- package/dist/vitest/mocks/secretmanager.d.mts +13 -4
- package/dist/vitest/mocks/tailordb.d.mts +48 -7
- package/dist/vitest/mocks/workflow.d.mts +41 -9
- package/dist/vitest/setup.mjs +2 -2
- package/docs/cli/application.md +58 -11
- package/docs/cli/setup.md +1 -1
- package/docs/cli/workspace.md +6 -4
- package/docs/cli-reference.md +12 -8
- package/docs/runtime.md +3 -4
- package/docs/services/aigateway.md +2 -0
- package/docs/services/tailordb.md +42 -0
- package/docs/services/workflow.md +1 -1
- package/docs/testing.md +103 -89
- package/package.json +7 -7
- package/dist/application-6zsDLGKv.mjs +0 -3
- package/dist/application-CqjCcZ2n.mjs.map +0 -1
- package/dist/errors-Dtf2WPaW.mjs +0 -42
- package/dist/errors-Dtf2WPaW.mjs.map +0 -1
- package/dist/field-runtime-D4QWRiB1.mjs +0 -203
- package/dist/field-runtime-D4QWRiB1.mjs.map +0 -1
- package/dist/logger-BEiZZ3qT.mjs.map +0 -1
- package/dist/runtime-DlklyuHN.mjs.map +0 -1
- package/dist/schema-DbTR1c6M.mjs.map +0 -1
- package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
- package/dist/service-CPMlIg3X.mjs +0 -3
- package/dist/types-BzJ7A_fn.mjs +0 -199
- package/dist/types-BzJ7A_fn.mjs.map +0 -1
- package/dist/types-CG9H7HuE.mjs +0 -4
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { t as db } from "./schema-
|
|
1
|
+
import { t as db } from "./schema-U6DxoZ8D.mjs";
|
|
2
2
|
import { $ as CreateAuthIDPConfigRequestSchema, A as CreatePipelineResolverRequestSchema, At as TenantProviderConfig_TenantProviderType, B as IdPPermissionOperator, Bt as CreateAIGatewayRequestSchema, C as UpdateStaticWebsiteRequestSchema, Ct as AuthOAuth2Client_ClientType, D as CreateSecretManagerVaultRequestSchema, Dt as AuthSCIMAttribute_Uniqueness, E as CreateSecretManagerSecretRequestSchema, Et as AuthSCIMAttribute_Type, F as PipelineResolver_OperationType, Ft as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, G as CreateExecutorExecutorRequestSchema, Gt as ConditionSchema, H as FunctionExecution_Status, I as CreateIdPServiceRequestSchema, It as UpdateApplicationRequestSchema, J as ExecutorJobStatus, Jt as PageDirection, K as UpdateExecutorExecutorRequestSchema, Kt as Condition_Operator, L as UpdateIdPServiceRequestSchema, M as UpdatePipelineResolverRequestSchema, N as UpdatePipelineServiceRequestSchema, O as UpdateSecretManagerSecretRequestSchema, Ot as AuthSCIMConfig_AuthorizationType, Pt as CreateApplicationRequestSchema, Q as CreateAuthHookRequestSchema, Rt as ApplicationSchemaUpdateAttemptStatus, S as CreateStaticWebsiteRequestSchema, St as AuthInvokerSchema, Tt as AuthSCIMAttribute_Mutability, V as IdPPermissionPermit, Vt as UpdateAIGatewayRequestSchema, X as ExecutorTriggerType, Y as ExecutorTargetType, Z as CreateAuthConnectionRequestSchema, _ as TailorDBGQLPermission_Permit, a as CreateWorkflowRequestSchema, at as CreateTenantConfigRequestSchema, b as TailorDBType_PermitAction, bt as AuthHookPoint, ct as UpdateAuthHookRequestSchema, d as CreateTailorDBServiceRequestSchema, dt as UpdateAuthOAuth2ClientRequestSchema, et as CreateAuthMachineUserRequestSchema, f as CreateTailorDBTypeRequestSchema, ft as UpdateAuthSCIMConfigRequestSchema, g as TailorDBGQLPermission_Operator, gt as UpdateUserProfileConfigRequestSchema, h as TailorDBGQLPermission_Action, ht as UpdateTenantConfigRequestSchema, i as CreateWorkflowJobFunctionRequestSchema, it as CreateAuthServiceRequestSchema, j as CreatePipelineServiceRequestSchema, jt as UserProfileProviderConfig_UserProfileProviderType, l as WorkflowExecution_Status, lt as UpdateAuthIDPConfigRequestSchema, mt as UpdateAuthServiceRequestSchema, nt as CreateAuthSCIMConfigRequestSchema, o as UpdateWorkflowJobFunctionExecutionPolicyRequestSchema, ot as CreateUserProfileConfigRequestSchema, p as UpdateTailorDBTypeRequestSchema, pt as UpdateAuthSCIMResourceRequestSchema, qt as FilterSchema, r as CreateWorkflowJobFunctionExecutionPolicyRequestSchema, rt as CreateAuthSCIMResourceRequestSchema, s as UpdateWorkflowRequestSchema, st as UpdateAuthConnectionRequestSchema, t as WorkspacePlatformUserRole, tt as CreateAuthOAuth2ClientRequestSchema, u as WorkflowJobExecution_Status, ut as UpdateAuthMachineUserRequestSchema, v as TailorDBType_Permission_Operator, vt as AuthConnection_Status, wt as AuthOAuth2Client_GrantType, x as AddCustomDomainRequestSchema, xt as AuthIDPConfig_AuthType, y as TailorDBType_Permission_Permit, yt as AuthConnection_Type, z as IdPLang, zt as Subgraph_ServiceType } from "./workspace_resource_pb-Db3fv68L.mjs";
|
|
3
3
|
import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
|
|
4
|
-
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-
|
|
5
|
-
import { A as fetchAllTolerant, B as initOperatorClient, C as createBundleCache, E as hashFile, F as getConsoleBaseUrl, G as byName, H as normalizeBaseUrl, I as getOAuth2ClientId, K as LOG_LEVELS, L as getOrNull, M as fetchPaged, O as defaultPlatformBaseUrl, R as getPlatformBaseUrl, S as hasGenerationHooks, T as hashContent, U as rememberPlatformConfigForToken, V as isDefaultPlatform, W as resolveStaticWebsiteUrls, _ as platformBundleDefinePlugin, d as buildExecutorArgsExpr, f as buildResolverOperationHookExpr, g as stringifyFunction, h as TailorDBTypeSchema, i as resolverBundleKey, j as fetchMachineUserToken, k as fetchAll, l as HTTP_METHODS, m as assertUniqueTailorDBTypeNamesWithExternal, n as generatePluginFilesIfNeeded, o as WorkflowJobFunctionExecutionPolicySchema, p as assertUniqueLocalTailorDBTypeNames, r as loadApplication, t as defineApplication, w as getDistDir, x as getPluginGenerationDependencies, z as initOAuth2Client } from "./application-
|
|
6
|
-
import { o as loadFilesWithIgnores, s as functionSchema, t as createExecutorService } from "./service-
|
|
4
|
+
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-BwS4ppwO.mjs";
|
|
5
|
+
import { A as fetchAllTolerant, B as initOperatorClient, C as createBundleCache, E as hashFile, F as getConsoleBaseUrl, G as byName, H as normalizeBaseUrl, I as getOAuth2ClientId, K as LOG_LEVELS, L as getOrNull, M as fetchPaged, O as defaultPlatformBaseUrl, R as getPlatformBaseUrl, S as hasGenerationHooks, T as hashContent, U as rememberPlatformConfigForToken, V as isDefaultPlatform, W as resolveStaticWebsiteUrls, _ as platformBundleDefinePlugin, d as buildExecutorArgsExpr, f as buildResolverOperationHookExpr, g as stringifyFunction, h as TailorDBTypeSchema, i as resolverBundleKey, j as fetchMachineUserToken, k as fetchAll, l as HTTP_METHODS, m as assertUniqueTailorDBTypeNamesWithExternal, n as generatePluginFilesIfNeeded, o as WorkflowJobFunctionExecutionPolicySchema, p as assertUniqueLocalTailorDBTypeNames, r as loadApplication, t as defineApplication, w as getDistDir, x as getPluginGenerationDependencies, z as initOAuth2Client } from "./application-CUxVA7Fs.mjs";
|
|
6
|
+
import { o as loadFilesWithIgnores, s as functionSchema, t as createExecutorService } from "./service-D12iQGcS.mjs";
|
|
7
7
|
import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "./enum-constants-j9QBF0cB.mjs";
|
|
8
8
|
import { t as multiline } from "./multiline-sfHpTZZK.mjs";
|
|
9
9
|
import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "./file-utils-DcyIPFQh.mjs";
|
|
@@ -11,7 +11,7 @@ import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "./kysely-type-D-z
|
|
|
11
11
|
import { n as seedPlugin, t as SeedGeneratorID } from "./seed-DFFigL8V.mjs";
|
|
12
12
|
import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
|
|
13
13
|
import { t as userAgent } from "./user-agent-DX2Jnw0-.mjs";
|
|
14
|
-
import {
|
|
14
|
+
import { r as isCLIError, t as createCLIError } from "./errors-DVFS0AZk.mjs";
|
|
15
15
|
import { n as tightenSecretFilePermissions, r as writeSecretFile } from "./secret-file-DBqZhjFQ.mjs";
|
|
16
16
|
import { r as withSpan } from "./telemetry-CdqJEzkj.mjs";
|
|
17
17
|
import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
|
|
@@ -22,6 +22,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
22
22
|
import { parseEnv } from "node:util";
|
|
23
23
|
import { ValueSchema, timestampDate } from "@bufbuild/protobuf/wkt";
|
|
24
24
|
import * as path from "pathe";
|
|
25
|
+
import { basename, dirname, join, resolve } from "pathe";
|
|
25
26
|
import { formatDistanceToNowStrict } from "date-fns";
|
|
26
27
|
import { getBorderCharacters, table } from "table";
|
|
27
28
|
import { pathToFileURL } from "node:url";
|
|
@@ -35,13 +36,13 @@ import { Code, ConnectError } from "@connectrpc/connect";
|
|
|
35
36
|
import pLimit from "p-limit";
|
|
36
37
|
import { resolveTSConfig } from "pkg-types";
|
|
37
38
|
import * as crypto$1 from "node:crypto";
|
|
38
|
-
import { createHash } from "node:crypto";
|
|
39
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
39
40
|
import { ExitPromptError } from "@inquirer/core";
|
|
40
|
-
import { confirm, input, password } from "@inquirer/prompts";
|
|
41
|
+
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
41
42
|
import { isCI } from "std-env";
|
|
42
43
|
import * as rolldown from "rolldown";
|
|
43
44
|
import * as fs from "node:fs/promises";
|
|
44
|
-
import { glob } from "node:fs/promises";
|
|
45
|
+
import { glob, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
45
46
|
import { parseSync } from "oxc-parser";
|
|
46
47
|
import * as inflection from "inflection";
|
|
47
48
|
import { pathToString } from "@bufbuild/protobuf/reflect";
|
|
@@ -802,18 +803,27 @@ function validateUUID(value, source) {
|
|
|
802
803
|
* @returns Resolved workspace ID
|
|
803
804
|
*/
|
|
804
805
|
async function loadWorkspaceId(opts) {
|
|
806
|
+
const workspaceId = await tryLoadWorkspaceId(opts);
|
|
807
|
+
if (workspaceId) return workspaceId;
|
|
808
|
+
throw new Error(multiline`
|
|
809
|
+
Workspace ID not found.
|
|
810
|
+
Please specify workspace ID via --workspace-id option or TAILOR_PLATFORM_WORKSPACE_ID environment variable.
|
|
811
|
+
`);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Load a workspace ID when one is explicitly configured.
|
|
815
|
+
* @param opts - Workspace and profile options
|
|
816
|
+
* @returns Resolved workspace ID, or undefined when no source is configured
|
|
817
|
+
*/
|
|
818
|
+
async function tryLoadWorkspaceId(opts) {
|
|
805
819
|
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
806
|
-
if (opts?.workspaceId) return validateUUID(opts.workspaceId, "--workspace-id option");
|
|
820
|
+
if (opts?.workspaceId !== void 0) return validateUUID(opts.workspaceId, "--workspace-id option");
|
|
807
821
|
if (process.env.TAILOR_PLATFORM_WORKSPACE_ID) return validateUUID(process.env.TAILOR_PLATFORM_WORKSPACE_ID, "TAILOR_PLATFORM_WORKSPACE_ID environment variable");
|
|
808
822
|
if (profile) {
|
|
809
823
|
const wsId = (await readPlatformConfig()).profiles[profile]?.workspace_id;
|
|
810
824
|
if (!wsId) throw new Error(`Profile "${profile}" not found`);
|
|
811
825
|
return validateUUID(wsId, `profile "${profile}"`);
|
|
812
826
|
}
|
|
813
|
-
throw new Error(multiline`
|
|
814
|
-
Workspace ID not found.
|
|
815
|
-
Please specify workspace ID via --workspace-id option or TAILOR_PLATFORM_WORKSPACE_ID environment variable.
|
|
816
|
-
`);
|
|
817
827
|
}
|
|
818
828
|
/**
|
|
819
829
|
* Load machine user name from command options, environment variables, or platform config.
|
|
@@ -1228,14 +1238,18 @@ async function apiCall(options) {
|
|
|
1228
1238
|
|
|
1229
1239
|
//#endregion
|
|
1230
1240
|
//#region src/cli/shared/prompt.ts
|
|
1241
|
+
function canPrompt() {
|
|
1242
|
+
return !isCI && process.stdin.isTTY === true && process.stdout.isTTY === true && !logger.jsonMode;
|
|
1243
|
+
}
|
|
1231
1244
|
/**
|
|
1232
1245
|
* Wraps a prompt function with CI guard and cancellation handling.
|
|
1233
1246
|
* @param fn - A prompt function from `@inquirer/prompts`
|
|
1247
|
+
* @param unavailableMessage - Message used when interactive input is unavailable
|
|
1234
1248
|
* @returns A wrapped function that throws in CI and exits on cancel
|
|
1235
1249
|
*/
|
|
1236
|
-
function withGuard(fn) {
|
|
1250
|
+
function withGuard(fn, unavailableMessage) {
|
|
1237
1251
|
return async (...args) => {
|
|
1238
|
-
if (
|
|
1252
|
+
if (!canPrompt()) throw new CIPromptError(unavailableMessage);
|
|
1239
1253
|
try {
|
|
1240
1254
|
return await fn(...args);
|
|
1241
1255
|
} catch (error) {
|
|
@@ -1245,9 +1259,10 @@ function withGuard(fn) {
|
|
|
1245
1259
|
};
|
|
1246
1260
|
}
|
|
1247
1261
|
const prompt = {
|
|
1248
|
-
confirm: withGuard(confirm),
|
|
1262
|
+
confirm: withGuard(confirm, "Interactive confirmations are not available in this environment. Use --yes to skip confirmation prompts, or provide the required options explicitly."),
|
|
1249
1263
|
text: withGuard(input),
|
|
1250
|
-
password: withGuard(password)
|
|
1264
|
+
password: withGuard(password),
|
|
1265
|
+
select: withGuard(select)
|
|
1251
1266
|
};
|
|
1252
1267
|
|
|
1253
1268
|
//#endregion
|
|
@@ -11935,132 +11950,957 @@ async function validatePlan(input) {
|
|
|
11935
11950
|
}
|
|
11936
11951
|
|
|
11937
11952
|
//#endregion
|
|
11938
|
-
//#region src/cli/
|
|
11953
|
+
//#region src/cli/shared/profile-name.ts
|
|
11954
|
+
const profileNameSchema = z.string().min(1, "Profile must not be empty");
|
|
11955
|
+
|
|
11956
|
+
//#endregion
|
|
11957
|
+
//#region src/cli/shared/workspace-name.ts
|
|
11958
|
+
const workspaceNameSchema = z.string().min(3, "Name must be at least 3 characters").max(63, "Name must be at most 63 characters").regex(/^[a-z0-9-]+$/, "Name can only contain lowercase letters, numbers, and hyphens").refine((name) => !name.startsWith("-") && !name.endsWith("-"), "Name cannot start or end with a hyphen");
|
|
11939
11959
|
/**
|
|
11940
|
-
*
|
|
11941
|
-
*
|
|
11942
|
-
*
|
|
11943
|
-
* Executors that omit `idp` while multiple IdPs exist are skipped here; the
|
|
11944
|
-
* apply pipeline throws a clearer error for them later.
|
|
11945
|
-
* @param application - Loaded application
|
|
11946
|
-
* @returns Set of IdP names targeted by idpUser triggers
|
|
11960
|
+
* Validate a workspace name for use in an interactive prompt.
|
|
11961
|
+
* @param name - Candidate workspace name
|
|
11962
|
+
* @returns True when valid, otherwise a validation message
|
|
11947
11963
|
*/
|
|
11948
|
-
function
|
|
11949
|
-
const
|
|
11950
|
-
|
|
11951
|
-
for (const executor of Object.values(application.executorService?.executors ?? {})) {
|
|
11952
|
-
if (executor.trigger.kind !== "idpUser") continue;
|
|
11953
|
-
if (executor.trigger.idp != null) targets.add(executor.trigger.idp);
|
|
11954
|
-
else if (idps.size === 1) {
|
|
11955
|
-
const [idp] = idps;
|
|
11956
|
-
if (idp) targets.add(idp);
|
|
11957
|
-
}
|
|
11958
|
-
}
|
|
11959
|
-
return targets;
|
|
11964
|
+
function validateWorkspaceName(name) {
|
|
11965
|
+
const result = workspaceNameSchema.safeParse(name);
|
|
11966
|
+
return result.success ? true : assertDefined(result.error.issues[0], "Zod returned no issues").message;
|
|
11960
11967
|
}
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11968
|
+
|
|
11969
|
+
//#endregion
|
|
11970
|
+
//#region src/cli/shared/format.ts
|
|
11971
|
+
/**
|
|
11972
|
+
* Format a protobuf Timestamp to ISO string.
|
|
11973
|
+
* @param timestamp - Protobuf timestamp
|
|
11974
|
+
* @returns Date object or null if invalid
|
|
11975
|
+
*/
|
|
11976
|
+
function formatTimestamp(timestamp) {
|
|
11977
|
+
if (!timestamp) return null;
|
|
11978
|
+
const date = timestampDate(timestamp);
|
|
11979
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
11980
|
+
return date;
|
|
11965
11981
|
}
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11982
|
+
/**
|
|
11983
|
+
* Formats a table with consistent single-line border style.
|
|
11984
|
+
* Use this instead of importing `table` directly.
|
|
11985
|
+
* @param data - Table data
|
|
11986
|
+
* @param config - Table configuration
|
|
11987
|
+
* @returns Formatted table string
|
|
11988
|
+
*/
|
|
11989
|
+
function formatTable(data, config) {
|
|
11990
|
+
return table(data, {
|
|
11991
|
+
...config,
|
|
11992
|
+
border: getBorderCharacters("norc")
|
|
11993
|
+
});
|
|
11978
11994
|
}
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
const namespace = findResolverNamespace(target.application, executor.trigger.resolverName) ?? resolveSameRunNamespace(visibleNamespaces, executor.trigger.resolverName, "Resolver");
|
|
11987
|
-
if (namespace && ownerNamespaces.has(namespace)) usedResolvers.add(executor.trigger.resolverName);
|
|
11988
|
-
}
|
|
11989
|
-
}
|
|
11990
|
-
return usedResolvers;
|
|
11995
|
+
/**
|
|
11996
|
+
* Formats a key-value table with single-line border style.
|
|
11997
|
+
* @param data - Key-value pairs
|
|
11998
|
+
* @returns Formatted key-value table string
|
|
11999
|
+
*/
|
|
12000
|
+
function formatKeyValueTable(data) {
|
|
12001
|
+
return formatTable(data, { singleLine: true });
|
|
11991
12002
|
}
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
12003
|
+
/**
|
|
12004
|
+
* Format a Date or ISO timestamp string as a human-readable relative time.
|
|
12005
|
+
* @param value - Date object, ISO date string, or null
|
|
12006
|
+
* @returns Relative time (e.g., "5 minutes ago") or "N/A" for null/invalid
|
|
12007
|
+
*/
|
|
12008
|
+
function humanizeRelativeTime(value) {
|
|
12009
|
+
if (value === null) return "N/A";
|
|
12010
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
12011
|
+
if (Number.isNaN(date.getTime())) return typeof value === "string" ? value : "N/A";
|
|
12012
|
+
return formatDistanceToNowStrict(date, { addSuffix: true });
|
|
11996
12013
|
}
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12014
|
+
|
|
12015
|
+
//#endregion
|
|
12016
|
+
//#region src/cli/commands/workspace/transform.ts
|
|
12017
|
+
const workspaceInfo = (workspace, folderName) => {
|
|
12018
|
+
const info = {
|
|
12019
|
+
id: workspace.id,
|
|
12020
|
+
name: workspace.name,
|
|
12021
|
+
region: workspace.region,
|
|
12022
|
+
...workspace.organizationId ? { organizationId: workspace.organizationId } : {},
|
|
12023
|
+
...workspace.folderId ? { folderId: workspace.folderId } : {},
|
|
12024
|
+
createdAt: formatTimestamp(workspace.createTime),
|
|
12025
|
+
updatedAt: formatTimestamp(workspace.updateTime)
|
|
12026
|
+
};
|
|
12027
|
+
return folderName ? {
|
|
12028
|
+
...info,
|
|
12029
|
+
folderName
|
|
12030
|
+
} : info;
|
|
12031
|
+
};
|
|
12032
|
+
const workspaceDetails = (workspace, folderName) => {
|
|
12033
|
+
return {
|
|
12034
|
+
...workspaceInfo(workspace, folderName),
|
|
12035
|
+
deleteProtection: workspace.deleteProtection,
|
|
12036
|
+
organizationId: workspace.organizationId,
|
|
12037
|
+
folderId: workspace.folderId
|
|
12038
|
+
};
|
|
12039
|
+
};
|
|
12040
|
+
async function resolveWorkspaceFolderName(client, workspace) {
|
|
12041
|
+
if (!workspace.organizationId || !workspace.folderId) return void 0;
|
|
12042
|
+
try {
|
|
12043
|
+
return (await client.getOrganizationFolder({
|
|
12044
|
+
organizationId: workspace.organizationId,
|
|
12045
|
+
folderId: workspace.folderId
|
|
12046
|
+
})).folder?.name || void 0;
|
|
12047
|
+
} catch (error) {
|
|
12048
|
+
if (error instanceof ConnectError && (error.code === Code.NotFound || error.code === Code.PermissionDenied)) return;
|
|
12049
|
+
logger.warn(`Failed to resolve workspace folder name: ${error}`);
|
|
12000
12050
|
return;
|
|
12001
12051
|
}
|
|
12002
|
-
namespaces.set(key, namespace);
|
|
12003
12052
|
}
|
|
12004
|
-
function
|
|
12005
|
-
const
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12053
|
+
function createWorkspaceFolderNameResolver(client) {
|
|
12054
|
+
const cache = /* @__PURE__ */ new Map();
|
|
12055
|
+
return (workspace) => {
|
|
12056
|
+
if (!workspace.organizationId || !workspace.folderId) return Promise.resolve(void 0);
|
|
12057
|
+
const cacheKey = `${workspace.organizationId}/${workspace.folderId}`;
|
|
12058
|
+
const cached = cache.get(cacheKey);
|
|
12059
|
+
if (cached) return cached;
|
|
12060
|
+
const promise = resolveWorkspaceFolderName(client, workspace);
|
|
12061
|
+
cache.set(cacheKey, promise);
|
|
12062
|
+
return promise;
|
|
12063
|
+
};
|
|
12013
12064
|
}
|
|
12014
|
-
function
|
|
12015
|
-
|
|
12065
|
+
async function workspaceInfoWithFolderName(client, workspace) {
|
|
12066
|
+
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
12067
|
+
return workspaceInfo(workspace, folderName);
|
|
12016
12068
|
}
|
|
12017
|
-
function
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
resourceKey: typeName
|
|
12021
|
-
};
|
|
12069
|
+
async function workspaceDetailsWithFolderName(client, workspace) {
|
|
12070
|
+
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
12071
|
+
return workspaceDetails(workspace, folderName);
|
|
12022
12072
|
}
|
|
12023
|
-
function
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
visibleKeysOf: collectApplicationTailorDBNamespaces,
|
|
12028
|
-
resourcesOf: tailorDBTypeResources,
|
|
12029
|
-
createResult: () => /* @__PURE__ */ new Map(),
|
|
12030
|
-
addResource: addPossiblyAmbiguousNamespace
|
|
12031
|
-
});
|
|
12073
|
+
async function workspaceInfosWithFolderNames(client, workspaces) {
|
|
12074
|
+
const resolveFolderName = createWorkspaceFolderNameResolver(client);
|
|
12075
|
+
const limit = pLimit(5);
|
|
12076
|
+
return Promise.all(workspaces.map((workspace) => limit(async () => workspaceInfo(workspace, await resolveFolderName(workspace)))));
|
|
12032
12077
|
}
|
|
12033
|
-
function
|
|
12034
|
-
return
|
|
12078
|
+
function workspaceDisplayName(workspace) {
|
|
12079
|
+
return workspace.folderName ? `${workspace.folderName}/${workspace.name}` : workspace.name;
|
|
12035
12080
|
}
|
|
12036
|
-
function
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12081
|
+
function createWorkspaceNameTransformer(nameKey, folderNameKey) {
|
|
12082
|
+
return (value, item) => {
|
|
12083
|
+
const workspace = item;
|
|
12084
|
+
const name = workspace[nameKey];
|
|
12085
|
+
const folderName = workspace[folderNameKey];
|
|
12086
|
+
if (typeof name === "string" && typeof folderName === "string") return workspaceDisplayName({
|
|
12087
|
+
name,
|
|
12088
|
+
folderName
|
|
12089
|
+
});
|
|
12090
|
+
return String(value ?? "");
|
|
12040
12091
|
};
|
|
12041
12092
|
}
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12093
|
+
const workspaceNameTransformer = createWorkspaceNameTransformer("name", "folderName");
|
|
12094
|
+
|
|
12095
|
+
//#endregion
|
|
12096
|
+
//#region src/cli/commands/workspace/create.ts
|
|
12097
|
+
/**
|
|
12098
|
+
* Schema for workspace creation options
|
|
12099
|
+
* - name: 3-63 chars, lowercase alphanumeric and hyphens, cannot start/end with hyphen
|
|
12100
|
+
* - organizationId, folderId: optional UUIDs
|
|
12101
|
+
*/
|
|
12102
|
+
const createWorkspaceOptionsSchema = z.object({
|
|
12103
|
+
name: workspaceNameSchema,
|
|
12104
|
+
region: z.string(),
|
|
12105
|
+
deleteProtection: z.boolean().optional(),
|
|
12106
|
+
organizationId: z.uuid().optional(),
|
|
12107
|
+
folderId: z.uuid().optional(),
|
|
12108
|
+
profile: profileNameSchema.optional()
|
|
12109
|
+
});
|
|
12110
|
+
const validateRegion = async (region, client) => {
|
|
12111
|
+
const availableRegions = await client.listAvailableWorkspaceRegions({});
|
|
12112
|
+
if (!availableRegions.regions.includes(region)) throw new Error(`Region must be one of: ${availableRegions.regions.join(", ")}.`);
|
|
12113
|
+
};
|
|
12114
|
+
function profilePlatformSettings(platformConfig) {
|
|
12115
|
+
const hasOAuth2ClientId = platformConfig?.oauth2ClientId || process.env.TAILOR_PLATFORM_OAUTH2_CLIENT_ID;
|
|
12116
|
+
const hasConsoleUrl = platformConfig?.consoleUrl || process.env.TAILOR_PLATFORM_CONSOLE_URL;
|
|
12117
|
+
return {
|
|
12118
|
+
...isDefaultPlatform(platformConfig) ? {} : { platform_url: getPlatformBaseUrl(platformConfig) },
|
|
12119
|
+
...hasOAuth2ClientId ? { oauth2_client_id: getOAuth2ClientId(platformConfig) } : {},
|
|
12120
|
+
...hasConsoleUrl ? { console_url: getConsoleBaseUrl(platformConfig) } : {}
|
|
12058
12121
|
};
|
|
12059
12122
|
}
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12123
|
+
/**
|
|
12124
|
+
* Create a new workspace with the given options.
|
|
12125
|
+
* @param options - Workspace creation options
|
|
12126
|
+
* @returns Created workspace info
|
|
12127
|
+
*/
|
|
12128
|
+
async function createWorkspace$1(options) {
|
|
12129
|
+
const validated = validateCreateWorkspaceOptions(options);
|
|
12130
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: validated.profile }), await loadPlatformClientConfig({ profile: validated.profile }));
|
|
12131
|
+
await validateRegion(validated.region, client);
|
|
12132
|
+
return createValidatedWorkspaceWithClient(client, validated);
|
|
12133
|
+
}
|
|
12134
|
+
/**
|
|
12135
|
+
* Create a workspace after its local options and region have been validated.
|
|
12136
|
+
* @param client - Authenticated Operator client
|
|
12137
|
+
* @param options - Validated workspace creation options
|
|
12138
|
+
* @returns Created workspace info
|
|
12139
|
+
*/
|
|
12140
|
+
async function createValidatedWorkspaceWithClient(client, options) {
|
|
12141
|
+
return workspaceInfoWithFolderName(client, assertDefined((await client.createWorkspace({
|
|
12142
|
+
workspaceName: options.name,
|
|
12143
|
+
workspaceRegion: options.region,
|
|
12144
|
+
deleteProtection: options.deleteProtection ?? false,
|
|
12145
|
+
organizationId: options.organizationId,
|
|
12146
|
+
folderId: options.folderId
|
|
12147
|
+
})).workspace, "createWorkspace response missing workspace"));
|
|
12148
|
+
}
|
|
12149
|
+
/**
|
|
12150
|
+
* Validate workspace creation options without making API calls.
|
|
12151
|
+
* @param options - Workspace creation options
|
|
12152
|
+
* @returns Validated workspace creation options
|
|
12153
|
+
*/
|
|
12154
|
+
function validateCreateWorkspaceOptions(options) {
|
|
12155
|
+
const result = createWorkspaceOptionsSchema.safeParse(options);
|
|
12156
|
+
if (!result.success) throw new Error(assertDefined(result.error.issues[0], "Zod returned no issues").message);
|
|
12157
|
+
return result.data;
|
|
12158
|
+
}
|
|
12159
|
+
const createCommand$1 = defineAppCommand({
|
|
12160
|
+
name: "create",
|
|
12161
|
+
description: "Create a new Tailor Platform workspace.",
|
|
12162
|
+
args: z.object({
|
|
12163
|
+
name: arg(z.string(), {
|
|
12164
|
+
alias: "n",
|
|
12165
|
+
description: "Workspace name"
|
|
12166
|
+
}),
|
|
12167
|
+
region: arg(z.string(), {
|
|
12168
|
+
alias: "r",
|
|
12169
|
+
description: "Workspace region (us-west, asia-northeast)"
|
|
12170
|
+
}),
|
|
12171
|
+
"delete-protection": arg(z.boolean().default(false), {
|
|
12172
|
+
alias: "d",
|
|
12173
|
+
description: "Enable delete protection"
|
|
12174
|
+
}),
|
|
12175
|
+
"organization-id": arg(z.string().optional(), {
|
|
12176
|
+
alias: "o",
|
|
12177
|
+
description: "Organization ID to workspace associate with",
|
|
12178
|
+
env: "TAILOR_PLATFORM_ORGANIZATION_ID"
|
|
12179
|
+
}),
|
|
12180
|
+
"folder-id": arg(z.string().optional(), {
|
|
12181
|
+
alias: "f",
|
|
12182
|
+
description: "Folder ID to workspace associate with",
|
|
12183
|
+
env: "TAILOR_PLATFORM_FOLDER_ID"
|
|
12184
|
+
}),
|
|
12185
|
+
"profile-name": arg(z.string().optional(), {
|
|
12186
|
+
alias: "p",
|
|
12187
|
+
description: "Profile name to create"
|
|
12188
|
+
}),
|
|
12189
|
+
profile: arg(profileNameSchema.optional(), {
|
|
12190
|
+
description: "Workspace profile used for authentication and Platform selection",
|
|
12191
|
+
env: "TAILOR_PLATFORM_PROFILE"
|
|
12192
|
+
}),
|
|
12193
|
+
"profile-user": arg(z.string().optional(), { description: "User email for the profile (defaults to current user)" }),
|
|
12194
|
+
permission: arg(z.enum(["write", "read"]).default("write"), { description: "Profile permission (requires --profile-name). 'read' blocks all write commands while the profile is active." })
|
|
12195
|
+
}).strict(),
|
|
12196
|
+
run: async (args) => {
|
|
12197
|
+
await assertWritable({ profile: args.profile });
|
|
12198
|
+
const profileName = args["profile-name"];
|
|
12199
|
+
let profileSetup;
|
|
12200
|
+
if (profileName) {
|
|
12201
|
+
const config = await readPlatformConfig();
|
|
12202
|
+
if (config.profiles[profileName]) throw new Error(`Profile "${profileName}" already exists.`);
|
|
12203
|
+
const activeProfileName = args.profile;
|
|
12204
|
+
const activeProfileEntry = activeProfileName ? config.profiles[activeProfileName] : void 0;
|
|
12205
|
+
const platformConfig = activeProfileEntry ? platformConfigFromProfile(activeProfileEntry) : void 0;
|
|
12206
|
+
const profileUser = args["profile-user"] || activeProfileEntry?.user || config.current_user;
|
|
12207
|
+
if (!profileUser) throw new Error("Current user not found. Please login or specify --profile-user to create a profile.");
|
|
12208
|
+
if (!hasUserTokenEntry(config, profileUser, platformConfig)) throw new Error(`User "${profileUser}" not found.\nPlease verify your user name and login using 'tailor-sdk login' command.`);
|
|
12209
|
+
profileSetup = {
|
|
12210
|
+
name: profileName,
|
|
12211
|
+
user: profileUser,
|
|
12212
|
+
platformSettings: profilePlatformSettings(platformConfig)
|
|
12213
|
+
};
|
|
12214
|
+
}
|
|
12215
|
+
const workspace = await createWorkspace$1({
|
|
12216
|
+
name: args.name,
|
|
12217
|
+
region: args.region,
|
|
12218
|
+
deleteProtection: args["delete-protection"],
|
|
12219
|
+
organizationId: args["organization-id"],
|
|
12220
|
+
folderId: args["folder-id"],
|
|
12221
|
+
profile: args.profile
|
|
12222
|
+
});
|
|
12223
|
+
let profileInfo;
|
|
12224
|
+
if (profileSetup) {
|
|
12225
|
+
const config = await readPlatformConfig();
|
|
12226
|
+
const platformSettings = profileSetup.platformSettings;
|
|
12227
|
+
config.profiles[profileSetup.name] = {
|
|
12228
|
+
user: profileSetup.user,
|
|
12229
|
+
workspace_id: workspace.id,
|
|
12230
|
+
...args.permission === "read" ? { readonly: true } : {},
|
|
12231
|
+
...platformSettings
|
|
12232
|
+
};
|
|
12233
|
+
writePlatformConfig(config);
|
|
12234
|
+
profileInfo = {
|
|
12235
|
+
name: profileSetup.name,
|
|
12236
|
+
user: profileSetup.user,
|
|
12237
|
+
workspaceId: workspace.id,
|
|
12238
|
+
permission: args.permission,
|
|
12239
|
+
...platformSettings.platform_url ? { platformUrl: platformSettings.platform_url } : {},
|
|
12240
|
+
...platformSettings.oauth2_client_id ? { oauth2ClientId: platformSettings.oauth2_client_id } : {},
|
|
12241
|
+
...platformSettings.console_url ? { consoleUrl: platformSettings.console_url } : {}
|
|
12242
|
+
};
|
|
12243
|
+
if (!args.json) logger.success(`Profile "${profileSetup.name}" created successfully.`);
|
|
12244
|
+
}
|
|
12245
|
+
if (!args.json) logger.success(`Workspace "${workspaceDisplayName(workspace)}" created successfully.`);
|
|
12246
|
+
if (args.json && profileInfo) {
|
|
12247
|
+
logger.out({
|
|
12248
|
+
...workspace,
|
|
12249
|
+
profile: profileInfo
|
|
12250
|
+
});
|
|
12251
|
+
return;
|
|
12252
|
+
}
|
|
12253
|
+
logger.out(workspace, { display: {
|
|
12254
|
+
name: workspaceNameTransformer,
|
|
12255
|
+
folderName: null
|
|
12256
|
+
} });
|
|
12257
|
+
if (profileInfo) {
|
|
12258
|
+
logger.out("Profile:");
|
|
12259
|
+
logger.out(profileInfo);
|
|
12260
|
+
}
|
|
12261
|
+
}
|
|
12262
|
+
});
|
|
12263
|
+
|
|
12264
|
+
//#endregion
|
|
12265
|
+
//#region src/cli/commands/workspace/list.ts
|
|
12266
|
+
/**
|
|
12267
|
+
* List workspaces with an optional order and limit.
|
|
12268
|
+
* @param options - Workspace listing options
|
|
12269
|
+
* @returns List of workspaces
|
|
12270
|
+
*/
|
|
12271
|
+
async function listWorkspaces(options) {
|
|
12272
|
+
const profile = profileNameSchema.optional().parse(options?.profile);
|
|
12273
|
+
return listWorkspacesWithClient(await initOperatorClient(await loadAccessToken({ profile }), await loadPlatformClientConfig({ profile })), options);
|
|
12274
|
+
}
|
|
12275
|
+
/**
|
|
12276
|
+
* List workspaces using an existing Operator client.
|
|
12277
|
+
* @param client - Authenticated Operator client
|
|
12278
|
+
* @param options - Workspace listing options
|
|
12279
|
+
* @returns List of workspaces
|
|
12280
|
+
*/
|
|
12281
|
+
async function listWorkspacesWithClient(client, options) {
|
|
12282
|
+
const pageDirection = toPageDirection(options?.order);
|
|
12283
|
+
return workspaceInfosWithFolderNames(client, await fetchPaged(async (pageToken, pageSize) => {
|
|
12284
|
+
const { workspaces, nextPageToken } = await client.listWorkspaces({
|
|
12285
|
+
pageToken,
|
|
12286
|
+
pageSize,
|
|
12287
|
+
pageDirection
|
|
12288
|
+
});
|
|
12289
|
+
return [workspaces, nextPageToken];
|
|
12290
|
+
}, { limit: options?.limit }));
|
|
12291
|
+
}
|
|
12292
|
+
const listCommand$9 = defineAppCommand({
|
|
12293
|
+
name: "list",
|
|
12294
|
+
description: "List all Tailor Platform workspaces.",
|
|
12295
|
+
args: z.object({
|
|
12296
|
+
...paginationArgs(),
|
|
12297
|
+
profile: arg(profileNameSchema.optional(), {
|
|
12298
|
+
description: "Workspace profile used for authentication and Platform selection",
|
|
12299
|
+
env: "TAILOR_PLATFORM_PROFILE"
|
|
12300
|
+
})
|
|
12301
|
+
}).strict(),
|
|
12302
|
+
run: async (args) => {
|
|
12303
|
+
const workspaces = await listWorkspaces({
|
|
12304
|
+
order: args.order,
|
|
12305
|
+
limit: args.limit,
|
|
12306
|
+
profile: args.profile
|
|
12307
|
+
});
|
|
12308
|
+
logger.out(workspaces, { display: {
|
|
12309
|
+
name: workspaceNameTransformer,
|
|
12310
|
+
folderName: null,
|
|
12311
|
+
organizationId: null,
|
|
12312
|
+
folderId: null,
|
|
12313
|
+
updatedAt: null
|
|
12314
|
+
} });
|
|
12315
|
+
}
|
|
12316
|
+
});
|
|
12317
|
+
|
|
12318
|
+
//#endregion
|
|
12319
|
+
//#region src/cli/commands/deploy/workspace-context.ts
|
|
12320
|
+
const workspaceContextSchema = z.object({
|
|
12321
|
+
version: z.literal(1),
|
|
12322
|
+
platformUrl: z.url(),
|
|
12323
|
+
applicationId: z.string().min(1).optional(),
|
|
12324
|
+
workspaceId: z.uuid()
|
|
12325
|
+
});
|
|
12326
|
+
function defaultConfigPath() {
|
|
12327
|
+
return resolve(process.cwd(), "tailor.config.ts");
|
|
12328
|
+
}
|
|
12329
|
+
function contextPath(configPath) {
|
|
12330
|
+
return join(dirname(configPath), ".tailor-sdk", `${basename(configPath)}.context.json`);
|
|
12331
|
+
}
|
|
12332
|
+
/**
|
|
12333
|
+
* Load the project workspace context when it belongs to the current platform.
|
|
12334
|
+
* Missing, malformed, and cross-platform state is ignored.
|
|
12335
|
+
* @param platformUrl - Current Platform API base URL
|
|
12336
|
+
* @param configPath - Configuration file whose workspace selection is loaded
|
|
12337
|
+
* @param applicationId - Application identity that must match the saved context
|
|
12338
|
+
* @returns Valid context for the current platform, or undefined
|
|
12339
|
+
*/
|
|
12340
|
+
async function loadWorkspaceContext(platformUrl, configPath = defaultConfigPath(), applicationId) {
|
|
12341
|
+
let contents;
|
|
12342
|
+
try {
|
|
12343
|
+
contents = await readFile(contextPath(configPath), "utf8");
|
|
12344
|
+
} catch (error) {
|
|
12345
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
12346
|
+
throw error;
|
|
12347
|
+
}
|
|
12348
|
+
let value;
|
|
12349
|
+
try {
|
|
12350
|
+
value = JSON.parse(contents);
|
|
12351
|
+
} catch {
|
|
12352
|
+
return;
|
|
12353
|
+
}
|
|
12354
|
+
const result = workspaceContextSchema.safeParse(value);
|
|
12355
|
+
if (!result.success || result.data.platformUrl !== platformUrl || applicationId !== void 0 && result.data.applicationId !== applicationId) return;
|
|
12356
|
+
return result.data;
|
|
12357
|
+
}
|
|
12358
|
+
/**
|
|
12359
|
+
* Persist the selected workspace as project-local SDK state.
|
|
12360
|
+
* @param context - Workspace context to persist
|
|
12361
|
+
* @param configPath - Configuration file whose workspace selection is persisted
|
|
12362
|
+
* @param applicationId - Application identity stored with the workspace selection
|
|
12363
|
+
*/
|
|
12364
|
+
async function saveWorkspaceContext(context, configPath = defaultConfigPath(), applicationId) {
|
|
12365
|
+
const validated = workspaceContextSchema.parse({
|
|
12366
|
+
...context,
|
|
12367
|
+
...applicationId === void 0 ? {} : { applicationId }
|
|
12368
|
+
});
|
|
12369
|
+
const stateDirectory = join(dirname(configPath), ".tailor-sdk");
|
|
12370
|
+
const targetPath = contextPath(configPath);
|
|
12371
|
+
const serialized = `${JSON.stringify(validated, null, 2)}\n`;
|
|
12372
|
+
try {
|
|
12373
|
+
if (await readFile(targetPath, "utf8") === serialized) return;
|
|
12374
|
+
} catch {}
|
|
12375
|
+
const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
12376
|
+
await mkdir(stateDirectory, { recursive: true });
|
|
12377
|
+
try {
|
|
12378
|
+
await writeFile(temporaryPath, serialized, { mode: 384 });
|
|
12379
|
+
await rename(temporaryPath, targetPath);
|
|
12380
|
+
} catch (error) {
|
|
12381
|
+
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
12382
|
+
throw error;
|
|
12383
|
+
}
|
|
12384
|
+
}
|
|
12385
|
+
|
|
12386
|
+
//#endregion
|
|
12387
|
+
//#region src/cli/commands/deploy/workspace.ts
|
|
12388
|
+
function suggestedWorkspaceName() {
|
|
12389
|
+
const name = basename(process.cwd()).toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, 63).replace(/-+$/, "");
|
|
12390
|
+
return validateWorkspaceName(name) === true ? name : "my-workspace";
|
|
12391
|
+
}
|
|
12392
|
+
function executableAction(args) {
|
|
12393
|
+
return {
|
|
12394
|
+
command: "tailor-sdk",
|
|
12395
|
+
args
|
|
12396
|
+
};
|
|
12397
|
+
}
|
|
12398
|
+
function deployArgs(options) {
|
|
12399
|
+
return options.deployArgs ?? ["deploy"];
|
|
12400
|
+
}
|
|
12401
|
+
function workspaceCommandArgs(options) {
|
|
12402
|
+
return options.workspaceCommandArgs ?? [];
|
|
12403
|
+
}
|
|
12404
|
+
function createDeployArgs(options, name, region) {
|
|
12405
|
+
return [
|
|
12406
|
+
...deployArgs(options),
|
|
12407
|
+
"--create-workspace",
|
|
12408
|
+
"--workspace-name",
|
|
12409
|
+
name,
|
|
12410
|
+
"--workspace-region",
|
|
12411
|
+
region,
|
|
12412
|
+
...options.organizationId ? ["--organization-id", options.organizationId] : [],
|
|
12413
|
+
...options.folderId ? ["--folder-id", options.folderId] : []
|
|
12414
|
+
];
|
|
12415
|
+
}
|
|
12416
|
+
function selectDeployArgs(options) {
|
|
12417
|
+
return [
|
|
12418
|
+
...deployArgs(options),
|
|
12419
|
+
"--workspace-id",
|
|
12420
|
+
"<workspace-id>"
|
|
12421
|
+
];
|
|
12422
|
+
}
|
|
12423
|
+
function workspaceLabel(workspace) {
|
|
12424
|
+
const organization = workspace.organizationId ?? "personal";
|
|
12425
|
+
return `${workspaceDisplayName(workspace)} (${workspace.region}, org: ${organization}, id: ${workspace.id})`;
|
|
12426
|
+
}
|
|
12427
|
+
function workspaceMatchesRequestedIdentity(workspace, options) {
|
|
12428
|
+
return options.workspaceName === workspace.name && options.workspaceRegion === workspace.region && options.organizationId === workspace.organizationId && options.folderId === workspace.folderId;
|
|
12429
|
+
}
|
|
12430
|
+
function workspaceIdentity(workspace) {
|
|
12431
|
+
const { id, name, region, organizationId, folderId } = workspace;
|
|
12432
|
+
return {
|
|
12433
|
+
id,
|
|
12434
|
+
name,
|
|
12435
|
+
region,
|
|
12436
|
+
organizationId,
|
|
12437
|
+
folderId
|
|
12438
|
+
};
|
|
12439
|
+
}
|
|
12440
|
+
function projectContextTargets(contextTargets) {
|
|
12441
|
+
if (!contextTargets) return void 0;
|
|
12442
|
+
return [...new Map(contextTargets.map((target) => [target.configPath, target])).values()];
|
|
12443
|
+
}
|
|
12444
|
+
async function loadProjectContexts(platformUrl, contextTargets) {
|
|
12445
|
+
const targets = projectContextTargets(contextTargets);
|
|
12446
|
+
if (!targets || targets.length === 0) {
|
|
12447
|
+
const context = await loadWorkspaceContext(platformUrl);
|
|
12448
|
+
return context ? [context] : [];
|
|
12449
|
+
}
|
|
12450
|
+
return (await Promise.all(targets.map(({ configPath, applicationId }) => loadWorkspaceContext(platformUrl, configPath, applicationId)))).filter((context) => context !== void 0);
|
|
12451
|
+
}
|
|
12452
|
+
async function persistWorkspaceContext(context, contextTargets) {
|
|
12453
|
+
const targets = projectContextTargets(contextTargets);
|
|
12454
|
+
if (!targets || targets.length === 0) {
|
|
12455
|
+
await saveWorkspaceContext(context);
|
|
12456
|
+
return;
|
|
12457
|
+
}
|
|
12458
|
+
const failures = (await Promise.allSettled(targets.map(({ configPath, applicationId }) => saveWorkspaceContext(context, configPath, applicationId)))).filter((result) => result.status === "rejected");
|
|
12459
|
+
if (failures.length > 0) throw new Error(failures.map(({ reason }) => reason instanceof Error ? reason.message : String(reason)).join("; "));
|
|
12460
|
+
}
|
|
12461
|
+
async function rememberWorkspaceContext(context, options, failurePolicy = "error-on-partial") {
|
|
12462
|
+
if (options.dryRun) return;
|
|
12463
|
+
try {
|
|
12464
|
+
await persistWorkspaceContext(context, options.contextTargets);
|
|
12465
|
+
} catch (error) {
|
|
12466
|
+
if ((projectContextTargets(options.contextTargets)?.length ?? 0) > 1 && failurePolicy === "error-on-partial") throw createCLIError({
|
|
12467
|
+
code: "WORKSPACE_CONTEXT_SAVE_FAILED",
|
|
12468
|
+
message: "The workspace selection could not be saved for every configuration file.",
|
|
12469
|
+
details: error instanceof Error ? error.message : String(error),
|
|
12470
|
+
suggestion: "Fix project state permissions, then rerun deploy with the workspace ID.",
|
|
12471
|
+
next: executableAction([
|
|
12472
|
+
...deployArgs(options),
|
|
12473
|
+
"--workspace-id",
|
|
12474
|
+
context.workspaceId
|
|
12475
|
+
]),
|
|
12476
|
+
context: {
|
|
12477
|
+
workspaceId: context.workspaceId,
|
|
12478
|
+
configPaths: options.contextTargets?.map(({ configPath }) => configPath)
|
|
12479
|
+
}
|
|
12480
|
+
});
|
|
12481
|
+
logger.warn(`Could not save the project workspace selection (${error instanceof Error ? error.message : String(error)}). Continue with --workspace-id ${context.workspaceId} on later runs.`);
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
async function useWorkspace(client, platformUrl, workspace, options, failurePolicy = "error-on-partial") {
|
|
12485
|
+
await rememberWorkspaceContext({
|
|
12486
|
+
version: 1,
|
|
12487
|
+
platformUrl,
|
|
12488
|
+
workspaceId: workspace.id
|
|
12489
|
+
}, options, failurePolicy);
|
|
12490
|
+
logger.info(`Using workspace: ${workspaceLabel(workspace)}`);
|
|
12491
|
+
return {
|
|
12492
|
+
client,
|
|
12493
|
+
workspaceId: workspace.id
|
|
12494
|
+
};
|
|
12495
|
+
}
|
|
12496
|
+
function useRememberedWorkspace(client, workspace) {
|
|
12497
|
+
logger.warn(`Using saved workspace selection: ${workspaceLabel(workspace)}`);
|
|
12498
|
+
return {
|
|
12499
|
+
client,
|
|
12500
|
+
workspaceId: workspace.id
|
|
12501
|
+
};
|
|
12502
|
+
}
|
|
12503
|
+
const createNewWorkspaceSelection = "create-new-workspace";
|
|
12504
|
+
async function chooseWorkspace(client, platformUrl, workspaces, options) {
|
|
12505
|
+
const workspaceId = await prompt.select({
|
|
12506
|
+
message: "Select a workspace",
|
|
12507
|
+
choices: [...workspaces.map((workspace) => ({
|
|
12508
|
+
name: workspaceLabel(workspace),
|
|
12509
|
+
value: workspace.id
|
|
12510
|
+
})), {
|
|
12511
|
+
name: "Create new workspace",
|
|
12512
|
+
value: createNewWorkspaceSelection
|
|
12513
|
+
}]
|
|
12514
|
+
});
|
|
12515
|
+
if (workspaceId === createNewWorkspaceSelection) return createWorkspaceForDeploy(client, platformUrl, options);
|
|
12516
|
+
const workspace = workspaces.find(({ id }) => id === workspaceId);
|
|
12517
|
+
if (!workspace) throw new Error("Selected workspace was not found");
|
|
12518
|
+
return useWorkspace(client, platformUrl, workspace, options);
|
|
12519
|
+
}
|
|
12520
|
+
function invalidCreateOptionsError(options, name, region, details) {
|
|
12521
|
+
return createCLIError({
|
|
12522
|
+
code: "WORKSPACE_CREATE_OPTIONS_INVALID",
|
|
12523
|
+
message: "Workspace creation options are invalid.",
|
|
12524
|
+
details,
|
|
12525
|
+
suggestion: "Correct the workspace creation options and rerun deploy.",
|
|
12526
|
+
next: executableAction(createDeployArgs(options, name, region))
|
|
12527
|
+
});
|
|
12528
|
+
}
|
|
12529
|
+
async function createWorkspace(client, platformUrl, options, availableRegions, validatedOptions) {
|
|
12530
|
+
let name = options.workspaceName;
|
|
12531
|
+
let region = options.workspaceRegion;
|
|
12532
|
+
const interactive = canPrompt();
|
|
12533
|
+
if (interactive) {
|
|
12534
|
+
name ??= await prompt.text({
|
|
12535
|
+
message: "Workspace name",
|
|
12536
|
+
default: suggestedWorkspaceName(),
|
|
12537
|
+
validate: validateWorkspaceName
|
|
12538
|
+
});
|
|
12539
|
+
region ??= await prompt.select({
|
|
12540
|
+
message: "Workspace region",
|
|
12541
|
+
choices: availableRegions.map((value) => ({
|
|
12542
|
+
name: value,
|
|
12543
|
+
value
|
|
12544
|
+
}))
|
|
12545
|
+
});
|
|
12546
|
+
} else {
|
|
12547
|
+
const missingOptions = [...name === void 0 ? ["--workspace-name"] : [], ...region === void 0 ? ["--workspace-region"] : []];
|
|
12548
|
+
if (missingOptions.length > 0) throw createCLIError({
|
|
12549
|
+
code: "WORKSPACE_CREATE_OPTIONS_REQUIRED",
|
|
12550
|
+
message: "Workspace creation requires a name and region in non-interactive mode.",
|
|
12551
|
+
suggestion: "Provide every workspace creation option and rerun deploy.",
|
|
12552
|
+
next: executableAction(createDeployArgs(options, name ?? "<name>", region ?? "<region>")),
|
|
12553
|
+
context: { missingOptions }
|
|
12554
|
+
});
|
|
12555
|
+
}
|
|
12556
|
+
if (!name || !region) throw new Error("Workspace creation options were not resolved");
|
|
12557
|
+
let validated = validatedOptions;
|
|
12558
|
+
if (!validated) {
|
|
12559
|
+
try {
|
|
12560
|
+
validated = validateCreateWorkspaceOptions({
|
|
12561
|
+
name,
|
|
12562
|
+
region,
|
|
12563
|
+
organizationId: options.organizationId,
|
|
12564
|
+
folderId: options.folderId
|
|
12565
|
+
});
|
|
12566
|
+
} catch (error) {
|
|
12567
|
+
throw invalidCreateOptionsError(options, name, region, error instanceof Error ? error.message : String(error));
|
|
12568
|
+
}
|
|
12569
|
+
if (!availableRegions.includes(region)) throw invalidCreateOptionsError(options, name, region, `Region must be one of: ${availableRegions.join(", ")}.`);
|
|
12570
|
+
}
|
|
12571
|
+
if (interactive) {
|
|
12572
|
+
const scope = [options.organizationId ? `organization: ${options.organizationId}` : void 0, options.folderId ? `folder: ${options.folderId}` : void 0].filter((value) => value !== void 0);
|
|
12573
|
+
if (!await prompt.confirm({
|
|
12574
|
+
message: `Create workspace "${name}" in ${region}${scope.length > 0 ? ` (${scope.join(", ")})` : ""}?`,
|
|
12575
|
+
default: true
|
|
12576
|
+
})) throw createCLIError({
|
|
12577
|
+
code: "WORKSPACE_CREATION_CANCELLED",
|
|
12578
|
+
message: "Workspace creation was cancelled."
|
|
12579
|
+
});
|
|
12580
|
+
}
|
|
12581
|
+
let workspace;
|
|
12582
|
+
try {
|
|
12583
|
+
workspace = await createValidatedWorkspaceWithClient(client, validated);
|
|
12584
|
+
} catch (error) {
|
|
12585
|
+
if (error instanceof ConnectError && ![
|
|
12586
|
+
Code.Canceled,
|
|
12587
|
+
Code.Unknown,
|
|
12588
|
+
Code.DeadlineExceeded,
|
|
12589
|
+
Code.Aborted,
|
|
12590
|
+
Code.Internal,
|
|
12591
|
+
Code.Unavailable,
|
|
12592
|
+
Code.DataLoss
|
|
12593
|
+
].includes(error.code)) throw error;
|
|
12594
|
+
throw createCLIError({
|
|
12595
|
+
code: "WORKSPACE_CREATION_FAILED",
|
|
12596
|
+
message: "Workspace creation did not complete successfully.",
|
|
12597
|
+
details: error instanceof Error ? error.message : String(error),
|
|
12598
|
+
suggestion: "The outcome may be uncertain. List workspaces before retrying creation to avoid duplicates.",
|
|
12599
|
+
next: executableAction([
|
|
12600
|
+
"workspace",
|
|
12601
|
+
"list",
|
|
12602
|
+
...workspaceCommandArgs(options),
|
|
12603
|
+
"--json"
|
|
12604
|
+
])
|
|
12605
|
+
});
|
|
12606
|
+
}
|
|
12607
|
+
await rememberWorkspaceContext({
|
|
12608
|
+
version: 1,
|
|
12609
|
+
platformUrl,
|
|
12610
|
+
workspaceId: workspace.id
|
|
12611
|
+
}, options);
|
|
12612
|
+
logger.success(`Created workspace: ${workspaceLabel(workspace)}`);
|
|
12613
|
+
logger.info(`Reuse this workspace with: tailor-sdk deploy --workspace-id ${workspace.id}`);
|
|
12614
|
+
logger.info(`Or set TAILOR_PLATFORM_WORKSPACE_ID=${workspace.id}.`);
|
|
12615
|
+
return {
|
|
12616
|
+
client,
|
|
12617
|
+
workspaceId: workspace.id
|
|
12618
|
+
};
|
|
12619
|
+
}
|
|
12620
|
+
async function createWorkspaceForDeploy(client, platformUrl, options, requestedRegions, validatedOptions) {
|
|
12621
|
+
const regions = requestedRegions ?? (await client.listAvailableWorkspaceRegions({})).regions;
|
|
12622
|
+
if (options.dryRun) throw createCLIError({
|
|
12623
|
+
code: "WORKSPACE_CREATION_DISABLED_IN_DRY_RUN",
|
|
12624
|
+
message: "Dry-run cannot create the workspace required to build a deployment plan.",
|
|
12625
|
+
suggestion: "Create a workspace explicitly, then rerun the same dry-run with its workspace ID.",
|
|
12626
|
+
context: { availableRegions: regions }
|
|
12627
|
+
});
|
|
12628
|
+
return createWorkspace(client, platformUrl, options, regions, validatedOptions);
|
|
12629
|
+
}
|
|
12630
|
+
/**
|
|
12631
|
+
* Resolve or provision the workspace used by deploy.
|
|
12632
|
+
* Explicit configuration wins over project context and account discovery.
|
|
12633
|
+
* @param options - Deploy workspace selection and creation options
|
|
12634
|
+
* @returns Authenticated client and resolved workspace ID
|
|
12635
|
+
*/
|
|
12636
|
+
async function resolveDeployWorkspace(options = {}) {
|
|
12637
|
+
const createOptionNames = [options.workspaceName !== void 0 ? "--workspace-name" : void 0, options.workspaceRegion !== void 0 ? "--workspace-region" : void 0].filter((name) => name !== void 0);
|
|
12638
|
+
if (!options.createWorkspace && createOptionNames.length > 0) throw createCLIError({
|
|
12639
|
+
code: "WORKSPACE_CREATE_FLAG_REQUIRED",
|
|
12640
|
+
message: "Workspace creation options require --create-workspace.",
|
|
12641
|
+
suggestion: "Add --create-workspace or remove the workspace creation options.",
|
|
12642
|
+
next: executableAction(createDeployArgs(options, options.workspaceName ?? "<name>", options.workspaceRegion ?? "<region>")),
|
|
12643
|
+
context: { options: createOptionNames }
|
|
12644
|
+
});
|
|
12645
|
+
if (options.createWorkspace && options.workspaceName !== void 0) {
|
|
12646
|
+
const nameValidation = validateWorkspaceName(options.workspaceName);
|
|
12647
|
+
if (nameValidation !== true) throw invalidCreateOptionsError(options, options.workspaceName, options.workspaceRegion ?? "<region>", nameValidation);
|
|
12648
|
+
}
|
|
12649
|
+
if (options.createWorkspace && options.workspaceRegion === "") throw invalidCreateOptionsError(options, options.workspaceName ?? "<name>", options.workspaceRegion, "Region must not be empty.");
|
|
12650
|
+
const explicitWorkspaceId = await tryLoadWorkspaceId({
|
|
12651
|
+
workspaceId: options.workspaceId,
|
|
12652
|
+
profile: options.profile
|
|
12653
|
+
});
|
|
12654
|
+
const accessToken = await loadAccessToken({ profile: options.profile });
|
|
12655
|
+
const platformConfig = await loadPlatformClientConfig({
|
|
12656
|
+
profile: options.profile,
|
|
12657
|
+
allowMissingProfile: explicitWorkspaceId !== void 0
|
|
12658
|
+
});
|
|
12659
|
+
const platformUrl = getPlatformBaseUrl(platformConfig);
|
|
12660
|
+
const client = await initOperatorClient(accessToken, platformConfig);
|
|
12661
|
+
if (explicitWorkspaceId) {
|
|
12662
|
+
let response;
|
|
12663
|
+
try {
|
|
12664
|
+
response = await client.getWorkspace({ workspaceId: explicitWorkspaceId });
|
|
12665
|
+
} catch (error) {
|
|
12666
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw createCLIError({
|
|
12667
|
+
code: "WORKSPACE_NOT_FOUND",
|
|
12668
|
+
message: `Workspace "${explicitWorkspaceId}" was not found.`
|
|
12669
|
+
});
|
|
12670
|
+
throw error;
|
|
12671
|
+
}
|
|
12672
|
+
if (!response.workspace) throw createCLIError({
|
|
12673
|
+
code: "WORKSPACE_NOT_FOUND",
|
|
12674
|
+
message: `Workspace "${explicitWorkspaceId}" was not found.`
|
|
12675
|
+
});
|
|
12676
|
+
return useWorkspace(client, platformUrl, workspaceInfo(response.workspace), options, "warn");
|
|
12677
|
+
}
|
|
12678
|
+
let requestedCreateOptions;
|
|
12679
|
+
if (options.createWorkspace && options.workspaceName !== void 0 && options.workspaceRegion !== void 0) try {
|
|
12680
|
+
requestedCreateOptions = validateCreateWorkspaceOptions({
|
|
12681
|
+
name: options.workspaceName,
|
|
12682
|
+
region: options.workspaceRegion,
|
|
12683
|
+
organizationId: options.organizationId,
|
|
12684
|
+
folderId: options.folderId
|
|
12685
|
+
});
|
|
12686
|
+
} catch (error) {
|
|
12687
|
+
throw invalidCreateOptionsError(options, options.workspaceName, options.workspaceRegion, error instanceof Error ? error.message : String(error));
|
|
12688
|
+
}
|
|
12689
|
+
const [contexts, workspaces] = await Promise.all([loadProjectContexts(platformUrl, options.contextTargets), listWorkspacesWithClient(client)]);
|
|
12690
|
+
const interactive = canPrompt();
|
|
12691
|
+
const contextWorkspaceIds = new Set(contexts.map(({ workspaceId }) => workspaceId));
|
|
12692
|
+
const linkedWorkspace = contextWorkspaceIds.size === 1 ? workspaces.find(({ id }) => contextWorkspaceIds.has(id)) : void 0;
|
|
12693
|
+
if (linkedWorkspace && (!options.createWorkspace || workspaceMatchesRequestedIdentity(linkedWorkspace, options))) {
|
|
12694
|
+
const contextTargetCount = projectContextTargets(options.contextTargets)?.length ?? 1;
|
|
12695
|
+
return contexts.length === contextTargetCount ? useRememberedWorkspace(client, linkedWorkspace) : useWorkspace(client, platformUrl, linkedWorkspace, options);
|
|
12696
|
+
}
|
|
12697
|
+
const onlyWorkspace = workspaces.length === 1 ? workspaces[0] : void 0;
|
|
12698
|
+
const canReuseOnlyWorkspace = onlyWorkspace !== void 0 && requestedCreateOptions !== void 0 && workspaceMatchesRequestedIdentity(onlyWorkspace, options);
|
|
12699
|
+
let requestedRegions;
|
|
12700
|
+
if (requestedCreateOptions && !canReuseOnlyWorkspace) {
|
|
12701
|
+
requestedRegions = await client.listAvailableWorkspaceRegions({});
|
|
12702
|
+
if (!requestedRegions.regions.includes(requestedCreateOptions.region)) throw invalidCreateOptionsError(options, requestedCreateOptions.name, requestedCreateOptions.region, `Region must be one of: ${requestedRegions.regions.join(", ")}.`);
|
|
12703
|
+
}
|
|
12704
|
+
if (workspaces.length === 0 && (interactive || options.createWorkspace || contexts.length === 0)) {
|
|
12705
|
+
const regions = requestedRegions?.regions ?? (await client.listAvailableWorkspaceRegions({})).regions;
|
|
12706
|
+
if (options.dryRun || interactive || options.createWorkspace) return createWorkspaceForDeploy(client, platformUrl, options, regions, requestedCreateOptions);
|
|
12707
|
+
throw createCLIError({
|
|
12708
|
+
code: "WORKSPACE_NOT_FOUND",
|
|
12709
|
+
message: "No workspaces are available for this account.",
|
|
12710
|
+
suggestion: "Create one during deploy by providing the workspace name and one of the available regions.",
|
|
12711
|
+
next: executableAction(createDeployArgs(options, "<name>", "<region>")),
|
|
12712
|
+
context: { availableRegions: regions }
|
|
12713
|
+
});
|
|
12714
|
+
}
|
|
12715
|
+
if (contextWorkspaceIds.size > 1) {
|
|
12716
|
+
const canReplaceStaleContexts = options.createWorkspace === true && options.workspaceName !== void 0 && options.workspaceRegion !== void 0 && workspaces.length === 0;
|
|
12717
|
+
if (!interactive && !canReplaceStaleContexts) throw createCLIError({
|
|
12718
|
+
code: "WORKSPACE_CONTEXT_CONFLICT",
|
|
12719
|
+
message: "The deployed configuration files are linked to different workspaces.",
|
|
12720
|
+
suggestion: "Choose one workspace explicitly for the combined deployment.",
|
|
12721
|
+
next: executableAction(selectDeployArgs(options)),
|
|
12722
|
+
context: { savedWorkspaceIds: [...contextWorkspaceIds] }
|
|
12723
|
+
});
|
|
12724
|
+
if (workspaces.length > 0 && !options.createWorkspace) return chooseWorkspace(client, platformUrl, workspaces, options);
|
|
12725
|
+
}
|
|
12726
|
+
if (!linkedWorkspace && contexts.length > 0) {
|
|
12727
|
+
const explicitlyEnsuringSingleTarget = options.createWorkspace === true && options.workspaceName !== void 0 && options.workspaceRegion !== void 0 && workspaces.length <= 1;
|
|
12728
|
+
if (!interactive && !explicitlyEnsuringSingleTarget) throw createCLIError({
|
|
12729
|
+
code: "WORKSPACE_CONTEXT_STALE",
|
|
12730
|
+
message: "The saved project workspace is no longer available.",
|
|
12731
|
+
suggestion: "Choose an available workspace explicitly before deploying.",
|
|
12732
|
+
next: executableAction(selectDeployArgs(options)),
|
|
12733
|
+
context: {
|
|
12734
|
+
savedWorkspaceIds: [...contextWorkspaceIds],
|
|
12735
|
+
workspaces: workspaces.map(workspaceIdentity)
|
|
12736
|
+
}
|
|
12737
|
+
});
|
|
12738
|
+
if (workspaces.length > 0 && interactive && !options.createWorkspace) return chooseWorkspace(client, platformUrl, workspaces, options);
|
|
12739
|
+
}
|
|
12740
|
+
if (workspaces.length === 1) {
|
|
12741
|
+
const [workspace] = workspaces;
|
|
12742
|
+
if (!workspace) throw new Error("Workspace discovery returned an invalid result");
|
|
12743
|
+
if (options.createWorkspace && !workspaceMatchesRequestedIdentity(workspace, options)) throw createCLIError({
|
|
12744
|
+
code: "WORKSPACE_CREATE_CONFLICT",
|
|
12745
|
+
message: "The existing workspace does not match the requested workspace.",
|
|
12746
|
+
suggestion: "Use the existing workspace, or run the explicit workspace create command to create another one.",
|
|
12747
|
+
next: executableAction([
|
|
12748
|
+
"workspace",
|
|
12749
|
+
"create",
|
|
12750
|
+
...workspaceCommandArgs(options),
|
|
12751
|
+
...options.workspaceCommandJson ? ["--json"] : [],
|
|
12752
|
+
"--name",
|
|
12753
|
+
options.workspaceName ?? "<name>",
|
|
12754
|
+
"--region",
|
|
12755
|
+
options.workspaceRegion ?? "<region>",
|
|
12756
|
+
...options.organizationId ? ["--organization-id", options.organizationId] : [],
|
|
12757
|
+
...options.folderId ? ["--folder-id", options.folderId] : []
|
|
12758
|
+
]),
|
|
12759
|
+
context: { existingWorkspace: workspaceIdentity(workspace) }
|
|
12760
|
+
});
|
|
12761
|
+
if (interactive && !options.createWorkspace) return chooseWorkspace(client, platformUrl, workspaces, options);
|
|
12762
|
+
return useWorkspace(client, platformUrl, workspace, options);
|
|
12763
|
+
}
|
|
12764
|
+
if (workspaces.length > 1) {
|
|
12765
|
+
if (!interactive) throw createCLIError({
|
|
12766
|
+
code: "WORKSPACE_SELECTION_REQUIRED",
|
|
12767
|
+
message: "Multiple workspaces are available.",
|
|
12768
|
+
suggestion: "Choose one explicitly for non-interactive deployment.",
|
|
12769
|
+
next: executableAction(selectDeployArgs(options)),
|
|
12770
|
+
context: { workspaces: workspaces.map(workspaceIdentity) }
|
|
12771
|
+
});
|
|
12772
|
+
return chooseWorkspace(client, platformUrl, workspaces, options);
|
|
12773
|
+
}
|
|
12774
|
+
throw new Error("Workspace discovery returned an invalid result");
|
|
12775
|
+
}
|
|
12776
|
+
|
|
12777
|
+
//#endregion
|
|
12778
|
+
//#region src/cli/commands/deploy/deploy.ts
|
|
12779
|
+
/**
|
|
12780
|
+
* Resolve the set of IdP names that have at least one executor subscribed to
|
|
12781
|
+
* their user events. When an executor's idpUser trigger omits the `idp` option
|
|
12782
|
+
* and exactly one IdP is configured, that IdP is implicitly the target.
|
|
12783
|
+
* Executors that omit `idp` while multiple IdPs exist are skipped here; the
|
|
12784
|
+
* apply pipeline throws a clearer error for them later.
|
|
12785
|
+
* @param application - Loaded application
|
|
12786
|
+
* @returns Set of IdP names targeted by idpUser triggers
|
|
12787
|
+
*/
|
|
12788
|
+
function collectIdpUserTriggerTargets(application) {
|
|
12789
|
+
const targets = /* @__PURE__ */ new Set();
|
|
12790
|
+
const idps = collectApplicationIdpNames(application);
|
|
12791
|
+
for (const executor of Object.values(application.executorService?.executors ?? {})) {
|
|
12792
|
+
if (executor.trigger.kind !== "idpUser") continue;
|
|
12793
|
+
if (executor.trigger.idp != null) targets.add(executor.trigger.idp);
|
|
12794
|
+
else if (idps.size === 1) {
|
|
12795
|
+
const [idp] = idps;
|
|
12796
|
+
if (idp) targets.add(idp);
|
|
12797
|
+
}
|
|
12798
|
+
}
|
|
12799
|
+
return targets;
|
|
12800
|
+
}
|
|
12801
|
+
function collectDeployIdpUserTriggerTargets(targets) {
|
|
12802
|
+
const triggerTargets = /* @__PURE__ */ new Set();
|
|
12803
|
+
for (const target of targets) for (const idpName of collectIdpUserTriggerTargets(target.application)) triggerTargets.add(idpName);
|
|
12804
|
+
return triggerTargets;
|
|
12805
|
+
}
|
|
12806
|
+
function collectExecutorUsedTailorDBTypes(owner, targets) {
|
|
12807
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
12808
|
+
const ownerNamespaces = new Set(owner.application.tailorDBServices.map((service) => service.namespace));
|
|
12809
|
+
const applications = targets.map((target) => target.application);
|
|
12810
|
+
for (const target of targets) {
|
|
12811
|
+
const visibleNamespaces = collectVisibleTailorDBTypeNamespaces(target.application, applications);
|
|
12812
|
+
for (const executor of Object.values(target.application.executorService?.executors ?? {})) if (executor.trigger.kind === "tailordb") {
|
|
12813
|
+
const namespace = findTailorDBNamespace(target.application, executor.trigger.typeName) ?? resolveSameRunNamespace(visibleNamespaces, executor.trigger.typeName, "TailorDB type");
|
|
12814
|
+
if (namespace && ownerNamespaces.has(namespace)) usedTypes.add(executor.trigger.typeName);
|
|
12815
|
+
}
|
|
12816
|
+
}
|
|
12817
|
+
return usedTypes;
|
|
12818
|
+
}
|
|
12819
|
+
function collectExecutorUsedResolvers(owner, targets) {
|
|
12820
|
+
const usedResolvers = /* @__PURE__ */ new Set();
|
|
12821
|
+
const ownerNamespaces = new Set(owner.application.resolverServices.map((service) => service.namespace));
|
|
12822
|
+
const applications = targets.map((target) => target.application);
|
|
12823
|
+
for (const target of targets) {
|
|
12824
|
+
const visibleNamespaces = collectVisibleResolverNamespaces(target.application, applications);
|
|
12825
|
+
for (const executor of Object.values(target.application.executorService?.executors ?? {})) if (executor.trigger.kind === "resolverExecuted") {
|
|
12826
|
+
const namespace = findResolverNamespace(target.application, executor.trigger.resolverName) ?? resolveSameRunNamespace(visibleNamespaces, executor.trigger.resolverName, "Resolver");
|
|
12827
|
+
if (namespace && ownerNamespaces.has(namespace)) usedResolvers.add(executor.trigger.resolverName);
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
return usedResolvers;
|
|
12831
|
+
}
|
|
12832
|
+
function collectExpectedLocalStaticWebsiteNames(targets) {
|
|
12833
|
+
const websiteNames = /* @__PURE__ */ new Set();
|
|
12834
|
+
for (const target of targets) for (const website of target.application.staticWebsiteServices) websiteNames.add(website.name);
|
|
12835
|
+
return websiteNames;
|
|
12836
|
+
}
|
|
12837
|
+
function addPossiblyAmbiguousNamespace(namespaces, key, namespace) {
|
|
12838
|
+
if (namespaces.has(key)) {
|
|
12839
|
+
if (namespaces.get(key) !== namespace) namespaces.set(key, void 0);
|
|
12840
|
+
return;
|
|
12841
|
+
}
|
|
12842
|
+
namespaces.set(key, namespace);
|
|
12843
|
+
}
|
|
12844
|
+
function collectVisibleResources(params) {
|
|
12845
|
+
const { application, applications, visibleKeysOf, resourcesOf, createResult, addResource } = params;
|
|
12846
|
+
const visibleKeys = visibleKeysOf(application);
|
|
12847
|
+
const result = createResult();
|
|
12848
|
+
for (const candidate of applications) for (const resource of resourcesOf(candidate)) {
|
|
12849
|
+
if (!visibleKeys.has(resource.visibilityKey)) continue;
|
|
12850
|
+
addResource(result, resource.resourceKey, resource.visibilityKey);
|
|
12851
|
+
}
|
|
12852
|
+
return result;
|
|
12853
|
+
}
|
|
12854
|
+
function collectApplicationTailorDBNamespaces(application) {
|
|
12855
|
+
return /* @__PURE__ */ new Set([...application.tailorDBServices.map((service) => service.namespace), ...application.externalTailorDBNamespaces]);
|
|
12856
|
+
}
|
|
12857
|
+
function* tailorDBTypeResources(application) {
|
|
12858
|
+
for (const service of application.tailorDBServices) for (const typeName of Object.keys(service.types)) yield {
|
|
12859
|
+
visibilityKey: service.namespace,
|
|
12860
|
+
resourceKey: typeName
|
|
12861
|
+
};
|
|
12862
|
+
}
|
|
12863
|
+
function collectVisibleTailorDBTypeNamespaces(application, applications) {
|
|
12864
|
+
return collectVisibleResources({
|
|
12865
|
+
application,
|
|
12866
|
+
applications,
|
|
12867
|
+
visibleKeysOf: collectApplicationTailorDBNamespaces,
|
|
12868
|
+
resourcesOf: tailorDBTypeResources,
|
|
12869
|
+
createResult: () => /* @__PURE__ */ new Map(),
|
|
12870
|
+
addResource: addPossiblyAmbiguousNamespace
|
|
12871
|
+
});
|
|
12872
|
+
}
|
|
12873
|
+
function collectApplicationResolverNamespaces(application) {
|
|
12874
|
+
return new Set(application.subgraphs.filter((subgraph) => subgraph.Type === "pipeline").map((subgraph) => subgraph.Name));
|
|
12875
|
+
}
|
|
12876
|
+
function* idpNameResources(application) {
|
|
12877
|
+
for (const name of collectApplicationIdpNames(application)) yield {
|
|
12878
|
+
visibilityKey: name,
|
|
12879
|
+
resourceKey: name
|
|
12880
|
+
};
|
|
12881
|
+
}
|
|
12882
|
+
function collectVisibleIdpNames(application, applications) {
|
|
12883
|
+
return collectVisibleResources({
|
|
12884
|
+
application,
|
|
12885
|
+
applications,
|
|
12886
|
+
visibleKeysOf: collectApplicationIdpNames,
|
|
12887
|
+
resourcesOf: idpNameResources,
|
|
12888
|
+
createResult: () => /* @__PURE__ */ new Set(),
|
|
12889
|
+
addResource: (names, name) => {
|
|
12890
|
+
names.add(name);
|
|
12891
|
+
}
|
|
12892
|
+
});
|
|
12893
|
+
}
|
|
12894
|
+
function* resolverResources(application) {
|
|
12895
|
+
for (const service of application.resolverServices) for (const resolver of Object.values(service.resolvers)) yield {
|
|
12896
|
+
visibilityKey: service.namespace,
|
|
12897
|
+
resourceKey: resolver.name
|
|
12898
|
+
};
|
|
12899
|
+
}
|
|
12900
|
+
function collectVisibleResolverNamespaces(application, applications) {
|
|
12901
|
+
return collectVisibleResources({
|
|
12902
|
+
application,
|
|
12903
|
+
applications,
|
|
12064
12904
|
visibleKeysOf: collectApplicationResolverNamespaces,
|
|
12065
12905
|
resourcesOf: resolverResources,
|
|
12066
12906
|
createResult: () => /* @__PURE__ */ new Map(),
|
|
@@ -12351,19 +13191,40 @@ function parseDeployConfigPaths(configPath) {
|
|
|
12351
13191
|
if (configPaths.some((entry) => entry.length === 0)) throw new Error("--config must contain one or more non-empty config paths.");
|
|
12352
13192
|
return configPaths;
|
|
12353
13193
|
}
|
|
13194
|
+
function recoveryEnvironmentArgs(options, cliContext) {
|
|
13195
|
+
return [
|
|
13196
|
+
...cliContext?.envFile ? ["--env-file", path.resolve(process.cwd(), cliContext.envFile)] : [],
|
|
13197
|
+
...cliContext?.envFileIfExists ? ["--env-file-if-exists", path.resolve(process.cwd(), cliContext.envFileIfExists)] : [],
|
|
13198
|
+
...options?.profile ? ["--profile", options.profile] : []
|
|
13199
|
+
];
|
|
13200
|
+
}
|
|
13201
|
+
function recoveryOutputArgs(cliContext) {
|
|
13202
|
+
return [...cliContext?.verbose ? ["--verbose"] : [], ...cliContext?.json || logger.jsonMode ? ["--json"] : []];
|
|
13203
|
+
}
|
|
13204
|
+
function retryDeployArgs(options, configPaths, cliContext) {
|
|
13205
|
+
return [
|
|
13206
|
+
"deploy",
|
|
13207
|
+
"--config",
|
|
13208
|
+
configPaths.join(","),
|
|
13209
|
+
...recoveryEnvironmentArgs(options, cliContext),
|
|
13210
|
+
...options?.dryRun ? ["--dry-run"] : [],
|
|
13211
|
+
...options?.yes ? ["--yes"] : [],
|
|
13212
|
+
...options?.noSchemaCheck ? ["--no-schema-check"] : [],
|
|
13213
|
+
...options?.noValidate ? ["--no-validate"] : [],
|
|
13214
|
+
...options?.noCache ? ["--no-cache"] : [],
|
|
13215
|
+
...options?.cleanCache ? ["--clean-cache"] : [],
|
|
13216
|
+
...recoveryOutputArgs(cliContext)
|
|
13217
|
+
];
|
|
13218
|
+
}
|
|
13219
|
+
function workspaceRecoveryArgs(options, cliContext) {
|
|
13220
|
+
return [...recoveryEnvironmentArgs(options, cliContext), ...cliContext?.verbose ? ["--verbose"] : []];
|
|
13221
|
+
}
|
|
12354
13222
|
async function buildDeploymentTarget(params) {
|
|
12355
|
-
const { configPath, dryRun, buildOnly, noCache, packageVersion, cacheDir } = params;
|
|
12356
|
-
const { config, plugins } =
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
if (fs$1.existsSync(resolvedPath)) await ensureConfigIdForDeploy({
|
|
12361
|
-
configPath: resolvedPath,
|
|
12362
|
-
dryRun,
|
|
12363
|
-
buildOnly
|
|
12364
|
-
});
|
|
12365
|
-
}
|
|
12366
|
-
return loadConfig(configPath);
|
|
13223
|
+
const { configPath, loadedConfig, dryRun, buildOnly, noCache, packageVersion, cacheDir } = params;
|
|
13224
|
+
const { config, plugins } = loadedConfig ?? await loadDeployConfig({
|
|
13225
|
+
configPath,
|
|
13226
|
+
dryRun,
|
|
13227
|
+
buildOnly
|
|
12367
13228
|
});
|
|
12368
13229
|
const configDir = path.dirname(config.path);
|
|
12369
13230
|
const lockfilePath = findUpSync("pnpm-lock.yaml", { cwd: configDir }) ?? findUpSync("package-lock.json", { cwd: configDir }) ?? findUpSync("yarn.lock", { cwd: configDir }) ?? findUpSync("bun.lock", { cwd: configDir });
|
|
@@ -12404,11 +13265,27 @@ async function buildDeploymentTarget(params) {
|
|
|
12404
13265
|
bundledScripts
|
|
12405
13266
|
};
|
|
12406
13267
|
}
|
|
13268
|
+
async function loadDeployConfig(params) {
|
|
13269
|
+
const { configPath, dryRun, buildOnly } = params;
|
|
13270
|
+
return withSpan("build.loadConfig", async () => {
|
|
13271
|
+
const foundPath = loadConfigPath(configPath);
|
|
13272
|
+
if (foundPath) {
|
|
13273
|
+
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
13274
|
+
if (fs$1.existsSync(resolvedPath)) await ensureConfigIdForDeploy({
|
|
13275
|
+
configPath: resolvedPath,
|
|
13276
|
+
dryRun,
|
|
13277
|
+
buildOnly
|
|
13278
|
+
});
|
|
13279
|
+
}
|
|
13280
|
+
return loadConfig(configPath);
|
|
13281
|
+
});
|
|
13282
|
+
}
|
|
12407
13283
|
async function buildDeploymentTargets(params) {
|
|
12408
|
-
const { configPaths, buildTarget = buildDeploymentTarget, ...targetParams } = params;
|
|
12409
|
-
return Promise.all(configPaths.map((configPath) => buildTarget({
|
|
13284
|
+
const { configPaths, loadedConfigs, buildTarget = buildDeploymentTarget, ...targetParams } = params;
|
|
13285
|
+
return Promise.all(configPaths.map((configPath, index) => buildTarget({
|
|
12410
13286
|
...targetParams,
|
|
12411
|
-
configPath
|
|
13287
|
+
configPath,
|
|
13288
|
+
loadedConfig: loadedConfigs?.[index]
|
|
12412
13289
|
})));
|
|
12413
13290
|
}
|
|
12414
13291
|
function setBundledScript(target, name, code, kind) {
|
|
@@ -12987,16 +13864,41 @@ async function validateDeploymentPlans(deployments) {
|
|
|
12987
13864
|
}
|
|
12988
13865
|
/**
|
|
12989
13866
|
* Deploy the configured application to the Tailor platform.
|
|
12990
|
-
* @param options -
|
|
13867
|
+
* @param options - Deploy execution options
|
|
13868
|
+
* @param cliContext - Global CLI arguments to preserve in recovery actions
|
|
12991
13869
|
* @returns Promise that resolves to `{ bundledScripts }` when `buildOnly` is true, otherwise void
|
|
12992
13870
|
*/
|
|
12993
|
-
async function
|
|
13871
|
+
async function deployInternal(options, cliContext) {
|
|
12994
13872
|
return withSpan("deploy", async (rootSpan) => {
|
|
12995
13873
|
rootSpan.setAttribute("deploy.dry_run", options?.dryRun ?? false);
|
|
12996
13874
|
const configPaths = parseDeployConfigPaths(options?.configPath);
|
|
12997
|
-
const
|
|
12998
|
-
|
|
12999
|
-
|
|
13875
|
+
const dryRun = options?.dryRun ?? false;
|
|
13876
|
+
const buildOnly = options?.buildOnly ?? parseBoolean(process.env.TAILOR_PLATFORM_SDK_BUILD_ONLY) === true;
|
|
13877
|
+
const preflightConfigs = buildOnly ? [] : await withSpan("config.preflight", () => Promise.all(configPaths.map((configPath) => loadDeployConfig({
|
|
13878
|
+
configPath,
|
|
13879
|
+
dryRun,
|
|
13880
|
+
buildOnly
|
|
13881
|
+
}))));
|
|
13882
|
+
const resolvedConfigPaths = preflightConfigs.map(({ config }) => config.path);
|
|
13883
|
+
const workspaceContextTargets = preflightConfigs.map(({ config }) => ({
|
|
13884
|
+
configPath: config.path,
|
|
13885
|
+
applicationId: config.id ?? `name:${config.name}`
|
|
13886
|
+
}));
|
|
13887
|
+
const workspace = buildOnly ? void 0 : await resolveDeployWorkspace({
|
|
13888
|
+
workspaceId: options?.workspaceId,
|
|
13889
|
+
profile: options?.profile,
|
|
13890
|
+
createWorkspace: options?.createWorkspace,
|
|
13891
|
+
workspaceName: options?.workspaceName,
|
|
13892
|
+
workspaceRegion: options?.workspaceRegion,
|
|
13893
|
+
organizationId: options?.organizationId,
|
|
13894
|
+
folderId: options?.folderId,
|
|
13895
|
+
dryRun,
|
|
13896
|
+
contextTargets: workspaceContextTargets,
|
|
13897
|
+
deployArgs: retryDeployArgs(options, resolvedConfigPaths, cliContext),
|
|
13898
|
+
workspaceCommandArgs: workspaceRecoveryArgs(options, cliContext),
|
|
13899
|
+
workspaceCommandJson: cliContext?.json || logger.jsonMode
|
|
13900
|
+
});
|
|
13901
|
+
const targets = await withSpan("build", async () => {
|
|
13000
13902
|
const noCache = options?.noCache ?? false;
|
|
13001
13903
|
const packageJson = await readPackageJson();
|
|
13002
13904
|
const cacheDir = path.resolve(getDistDir(), "cache");
|
|
@@ -13007,25 +13909,20 @@ async function deploy(options) {
|
|
|
13007
13909
|
});
|
|
13008
13910
|
logger.info("Bundle cache cleaned");
|
|
13009
13911
|
}
|
|
13010
|
-
return {
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
buildOnly
|
|
13020
|
-
};
|
|
13912
|
+
return await buildDeploymentTargets({
|
|
13913
|
+
configPaths,
|
|
13914
|
+
loadedConfigs: preflightConfigs,
|
|
13915
|
+
dryRun,
|
|
13916
|
+
buildOnly,
|
|
13917
|
+
noCache,
|
|
13918
|
+
packageVersion: packageJson.version ?? "unknown",
|
|
13919
|
+
cacheDir
|
|
13920
|
+
});
|
|
13021
13921
|
});
|
|
13022
13922
|
if (buildOnly) return { bundledScripts: mergeBundledScripts(targets) };
|
|
13023
13923
|
assertUniqueGlobalResourceNames(targets);
|
|
13024
|
-
|
|
13025
|
-
const workspaceId =
|
|
13026
|
-
workspaceId: options?.workspaceId,
|
|
13027
|
-
profile: options?.profile
|
|
13028
|
-
});
|
|
13924
|
+
if (!workspace) throw new Error("Workspace was not resolved");
|
|
13925
|
+
const { client, workspaceId } = workspace;
|
|
13029
13926
|
rootSpan.setAttribute("app.name", targets.map((target) => target.application.name).join(","));
|
|
13030
13927
|
rootSpan.setAttribute("workspace.id", workspaceId);
|
|
13031
13928
|
const deployments = await planDeploymentTargets({
|
|
@@ -13035,7 +13932,6 @@ async function deploy(options) {
|
|
|
13035
13932
|
workspaceId,
|
|
13036
13933
|
noSchemaCheck: options?.noSchemaCheck
|
|
13037
13934
|
});
|
|
13038
|
-
const dryRun = options?.dryRun ?? false;
|
|
13039
13935
|
const yes = options?.yes ?? false;
|
|
13040
13936
|
dropCrossDeploymentManagedDeletes(deployments);
|
|
13041
13937
|
await withSpan("confirm", async () => {
|
|
@@ -13059,6 +13955,23 @@ async function deploy(options) {
|
|
|
13059
13955
|
else logger.success("Successfully applied changes.");
|
|
13060
13956
|
});
|
|
13061
13957
|
}
|
|
13958
|
+
/**
|
|
13959
|
+
* Deploy using the programmatic CLI API.
|
|
13960
|
+
* @param options - Deploy execution options
|
|
13961
|
+
* @returns Deploy result
|
|
13962
|
+
*/
|
|
13963
|
+
function deploy(options) {
|
|
13964
|
+
return deployInternal(options);
|
|
13965
|
+
}
|
|
13966
|
+
/**
|
|
13967
|
+
* Deploy from the command adapter while preserving global CLI arguments in recovery actions.
|
|
13968
|
+
* @param options - Deploy execution options
|
|
13969
|
+
* @param cliContext - Global CLI arguments already applied by the command runner
|
|
13970
|
+
* @returns Deploy result
|
|
13971
|
+
*/
|
|
13972
|
+
function deployFromCLI(options, cliContext) {
|
|
13973
|
+
return deployInternal(options, cliContext);
|
|
13974
|
+
}
|
|
13062
13975
|
|
|
13063
13976
|
//#endregion
|
|
13064
13977
|
//#region src/cli/commands/executor/status.ts
|
|
@@ -13448,52 +14361,6 @@ const getCommand$6 = defineAppCommand({
|
|
|
13448
14361
|
}
|
|
13449
14362
|
});
|
|
13450
14363
|
|
|
13451
|
-
//#endregion
|
|
13452
|
-
//#region src/cli/shared/format.ts
|
|
13453
|
-
/**
|
|
13454
|
-
* Format a protobuf Timestamp to ISO string.
|
|
13455
|
-
* @param timestamp - Protobuf timestamp
|
|
13456
|
-
* @returns Date object or null if invalid
|
|
13457
|
-
*/
|
|
13458
|
-
function formatTimestamp(timestamp) {
|
|
13459
|
-
if (!timestamp) return null;
|
|
13460
|
-
const date = timestampDate(timestamp);
|
|
13461
|
-
if (Number.isNaN(date.getTime())) return null;
|
|
13462
|
-
return date;
|
|
13463
|
-
}
|
|
13464
|
-
/**
|
|
13465
|
-
* Formats a table with consistent single-line border style.
|
|
13466
|
-
* Use this instead of importing `table` directly.
|
|
13467
|
-
* @param data - Table data
|
|
13468
|
-
* @param config - Table configuration
|
|
13469
|
-
* @returns Formatted table string
|
|
13470
|
-
*/
|
|
13471
|
-
function formatTable(data, config) {
|
|
13472
|
-
return table(data, {
|
|
13473
|
-
...config,
|
|
13474
|
-
border: getBorderCharacters("norc")
|
|
13475
|
-
});
|
|
13476
|
-
}
|
|
13477
|
-
/**
|
|
13478
|
-
* Formats a key-value table with single-line border style.
|
|
13479
|
-
* @param data - Key-value pairs
|
|
13480
|
-
* @returns Formatted key-value table string
|
|
13481
|
-
*/
|
|
13482
|
-
function formatKeyValueTable(data) {
|
|
13483
|
-
return formatTable(data, { singleLine: true });
|
|
13484
|
-
}
|
|
13485
|
-
/**
|
|
13486
|
-
* Format a Date or ISO timestamp string as a human-readable relative time.
|
|
13487
|
-
* @param value - Date object, ISO date string, or null
|
|
13488
|
-
* @returns Relative time (e.g., "5 minutes ago") or "N/A" for null/invalid
|
|
13489
|
-
*/
|
|
13490
|
-
function humanizeRelativeTime(value) {
|
|
13491
|
-
if (value === null) return "N/A";
|
|
13492
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
13493
|
-
if (Number.isNaN(date.getTime())) return typeof value === "string" ? value : "N/A";
|
|
13494
|
-
return formatDistanceToNowStrict(date, { addSuffix: true });
|
|
13495
|
-
}
|
|
13496
|
-
|
|
13497
14364
|
//#endregion
|
|
13498
14365
|
//#region src/cli/shared/function-execution.ts
|
|
13499
14366
|
/**
|
|
@@ -14892,7 +15759,7 @@ async function listExecutors(options) {
|
|
|
14892
15759
|
return [executors, nextPageToken];
|
|
14893
15760
|
}, { limit: options?.limit })).map((e) => toExecutorListInfo(e));
|
|
14894
15761
|
}
|
|
14895
|
-
const listCommand$
|
|
15762
|
+
const listCommand$8 = defineAppCommand({
|
|
14896
15763
|
name: "list",
|
|
14897
15764
|
description: "List all executors",
|
|
14898
15765
|
args: z.object({
|
|
@@ -15317,7 +16184,7 @@ async function listFunctionRegistries(options) {
|
|
|
15317
16184
|
}
|
|
15318
16185
|
}, { limit })).map(functionRegistryInfo);
|
|
15319
16186
|
}
|
|
15320
|
-
const listCommand$
|
|
16187
|
+
const listCommand$7 = defineAppCommand({
|
|
15321
16188
|
name: "list",
|
|
15322
16189
|
description: "List function registries in a workspace",
|
|
15323
16190
|
args: z.object({
|
|
@@ -16262,7 +17129,7 @@ async function listMachineUsers(options) {
|
|
|
16262
17129
|
return [machineUsers, nextPageToken];
|
|
16263
17130
|
}, { limit: options?.limit })).map(machineUserInfo);
|
|
16264
17131
|
}
|
|
16265
|
-
const listCommand$
|
|
17132
|
+
const listCommand$6 = defineAppCommand({
|
|
16266
17133
|
name: "list",
|
|
16267
17134
|
description: "List all machine users in the application.",
|
|
16268
17135
|
args: z.object({
|
|
@@ -16478,7 +17345,7 @@ async function listOAuth2Clients(options) {
|
|
|
16478
17345
|
return [oauth2Clients, nextPageToken];
|
|
16479
17346
|
}, { limit: options?.limit })).map(toOAuth2ClientInfo);
|
|
16480
17347
|
}
|
|
16481
|
-
const listCommand$
|
|
17348
|
+
const listCommand$5 = defineAppCommand({
|
|
16482
17349
|
name: "list",
|
|
16483
17350
|
description: "List all OAuth2 clients in the application.",
|
|
16484
17351
|
args: z.object({
|
|
@@ -16548,7 +17415,7 @@ async function createFolder(options) {
|
|
|
16548
17415
|
if (!response.folder) throw new Error("Failed to create folder.");
|
|
16549
17416
|
return folderInfo(response.folder);
|
|
16550
17417
|
}
|
|
16551
|
-
const createCommand
|
|
17418
|
+
const createCommand = defineAppCommand({
|
|
16552
17419
|
name: "create",
|
|
16553
17420
|
description: "Create a new folder in an organization.",
|
|
16554
17421
|
args: z.object({
|
|
@@ -16696,7 +17563,7 @@ async function listFolders(options) {
|
|
|
16696
17563
|
return [response.folders, response.nextPageToken];
|
|
16697
17564
|
}, { limit })).map(folderListInfo);
|
|
16698
17565
|
}
|
|
16699
|
-
const listCommand$
|
|
17566
|
+
const listCommand$4 = defineAppCommand({
|
|
16700
17567
|
name: "list",
|
|
16701
17568
|
description: "List folders in an organization.",
|
|
16702
17569
|
args: z.object({
|
|
@@ -16805,7 +17672,7 @@ async function listOrganizations(options) {
|
|
|
16805
17672
|
if (limit !== void 0) return results.slice(0, limit);
|
|
16806
17673
|
return results;
|
|
16807
17674
|
}
|
|
16808
|
-
const listCommand$
|
|
17675
|
+
const listCommand$3 = defineAppCommand({
|
|
16809
17676
|
name: "list",
|
|
16810
17677
|
description: "List organizations you belong to.",
|
|
16811
17678
|
args: z.object({ limit: arg(positiveIntArg.optional(), {
|
|
@@ -17095,141 +17962,63 @@ const removeCommand$1 = defineAppCommand({
|
|
|
17095
17962
|
profile: args.profile,
|
|
17096
17963
|
configPath: args.config
|
|
17097
17964
|
});
|
|
17098
|
-
logger.info(`Planning removal of resources managed by "${application.name}"...`);
|
|
17099
|
-
logger.newline();
|
|
17100
|
-
await execRemove(client, workspaceId, application, config, async () => {
|
|
17101
|
-
if (!args.yes) {
|
|
17102
|
-
if (!await prompt.confirm({
|
|
17103
|
-
message: "Are you sure you want to remove all resources?",
|
|
17104
|
-
default: false
|
|
17105
|
-
})) throw new Error(multiline`
|
|
17106
|
-
Remove cancelled. No resources were deleted.
|
|
17107
|
-
To override, run again and confirm, or use --yes flag.
|
|
17108
|
-
`);
|
|
17109
|
-
} else logger.success("Removing all resources (--yes flag specified)...");
|
|
17110
|
-
});
|
|
17111
|
-
logger.success(`Successfully removed all resources managed by "${application.name}".`);
|
|
17112
|
-
}
|
|
17113
|
-
});
|
|
17114
|
-
|
|
17115
|
-
//#endregion
|
|
17116
|
-
//#region src/cli/shared/beta.ts
|
|
17117
|
-
/**
|
|
17118
|
-
* Warn that a feature is in beta.
|
|
17119
|
-
* @param {string} featureName - Name of the beta feature (e.g., "tailordb erd", "tailordb migration")
|
|
17120
|
-
*/
|
|
17121
|
-
function logBetaWarning(featureName) {
|
|
17122
|
-
logger.warn(`The '${featureName}' command is a beta feature and may introduce breaking changes in future releases.`);
|
|
17123
|
-
logger.newline();
|
|
17124
|
-
}
|
|
17125
|
-
|
|
17126
|
-
//#endregion
|
|
17127
|
-
//#region src/cli/shared/config.ts
|
|
17128
|
-
/**
|
|
17129
|
-
* Extracts every namespace key declared under `config.db`, including those
|
|
17130
|
-
* declared with `{ external: true }`.
|
|
17131
|
-
* @param config - Loaded application configuration.
|
|
17132
|
-
* @returns Namespace names in insertion order.
|
|
17133
|
-
*/
|
|
17134
|
-
function extractAllNamespaces(config) {
|
|
17135
|
-
const namespaces = /* @__PURE__ */ new Set();
|
|
17136
|
-
if (config.db) for (const namespaceName of Object.keys(config.db)) namespaces.add(namespaceName);
|
|
17137
|
-
return Array.from(namespaces);
|
|
17138
|
-
}
|
|
17139
|
-
/**
|
|
17140
|
-
* Extracts namespace keys under `config.db` that this app owns
|
|
17141
|
-
* (i.e. not declared with `{ external: true }`). Use this for destructive
|
|
17142
|
-
* operations like `tailordb truncate --all` to avoid touching namespaces
|
|
17143
|
-
* owned by other apps.
|
|
17144
|
-
* @param config - Loaded application configuration.
|
|
17145
|
-
* @returns Owned namespace names in insertion order.
|
|
17146
|
-
*/
|
|
17147
|
-
function extractOwnedNamespaces(config) {
|
|
17148
|
-
const namespaces = /* @__PURE__ */ new Set();
|
|
17149
|
-
if (config.db) for (const [namespaceName, nsConfig] of Object.entries(config.db)) {
|
|
17150
|
-
if ("external" in nsConfig) continue;
|
|
17151
|
-
namespaces.add(namespaceName);
|
|
17152
|
-
}
|
|
17153
|
-
return Array.from(namespaces);
|
|
17154
|
-
}
|
|
17155
|
-
|
|
17156
|
-
//#endregion
|
|
17157
|
-
//#region src/cli/commands/workspace/transform.ts
|
|
17158
|
-
const workspaceInfo = (workspace, folderName) => {
|
|
17159
|
-
const info = {
|
|
17160
|
-
id: workspace.id,
|
|
17161
|
-
name: workspace.name,
|
|
17162
|
-
region: workspace.region,
|
|
17163
|
-
createdAt: formatTimestamp(workspace.createTime),
|
|
17164
|
-
updatedAt: formatTimestamp(workspace.updateTime)
|
|
17165
|
-
};
|
|
17166
|
-
return folderName ? {
|
|
17167
|
-
...info,
|
|
17168
|
-
folderName
|
|
17169
|
-
} : info;
|
|
17170
|
-
};
|
|
17171
|
-
const workspaceDetails = (workspace, folderName) => {
|
|
17172
|
-
return {
|
|
17173
|
-
...workspaceInfo(workspace, folderName),
|
|
17174
|
-
deleteProtection: workspace.deleteProtection,
|
|
17175
|
-
organizationId: workspace.organizationId,
|
|
17176
|
-
folderId: workspace.folderId
|
|
17177
|
-
};
|
|
17178
|
-
};
|
|
17179
|
-
async function resolveWorkspaceFolderName(client, workspace) {
|
|
17180
|
-
if (!workspace.organizationId || !workspace.folderId) return void 0;
|
|
17181
|
-
try {
|
|
17182
|
-
return (await client.getOrganizationFolder({
|
|
17183
|
-
organizationId: workspace.organizationId,
|
|
17184
|
-
folderId: workspace.folderId
|
|
17185
|
-
})).folder?.name || void 0;
|
|
17186
|
-
} catch (error) {
|
|
17187
|
-
if (error instanceof ConnectError && (error.code === Code.NotFound || error.code === Code.PermissionDenied)) return;
|
|
17188
|
-
logger.warn(`Failed to resolve workspace folder name: ${error}`);
|
|
17189
|
-
return;
|
|
17190
|
-
}
|
|
17191
|
-
}
|
|
17192
|
-
function createWorkspaceFolderNameResolver(client) {
|
|
17193
|
-
const cache = /* @__PURE__ */ new Map();
|
|
17194
|
-
return (workspace) => {
|
|
17195
|
-
if (!workspace.organizationId || !workspace.folderId) return Promise.resolve(void 0);
|
|
17196
|
-
const cacheKey = `${workspace.organizationId}/${workspace.folderId}`;
|
|
17197
|
-
const cached = cache.get(cacheKey);
|
|
17198
|
-
if (cached) return cached;
|
|
17199
|
-
const promise = resolveWorkspaceFolderName(client, workspace);
|
|
17200
|
-
cache.set(cacheKey, promise);
|
|
17201
|
-
return promise;
|
|
17202
|
-
};
|
|
17203
|
-
}
|
|
17204
|
-
async function workspaceInfoWithFolderName(client, workspace) {
|
|
17205
|
-
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
17206
|
-
return workspaceInfo(workspace, folderName);
|
|
17207
|
-
}
|
|
17208
|
-
async function workspaceDetailsWithFolderName(client, workspace) {
|
|
17209
|
-
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
17210
|
-
return workspaceDetails(workspace, folderName);
|
|
17211
|
-
}
|
|
17212
|
-
async function workspaceInfosWithFolderNames(client, workspaces) {
|
|
17213
|
-
const resolveFolderName = createWorkspaceFolderNameResolver(client);
|
|
17214
|
-
const limit = pLimit(5);
|
|
17215
|
-
return Promise.all(workspaces.map((workspace) => limit(async () => workspaceInfo(workspace, await resolveFolderName(workspace)))));
|
|
17965
|
+
logger.info(`Planning removal of resources managed by "${application.name}"...`);
|
|
17966
|
+
logger.newline();
|
|
17967
|
+
await execRemove(client, workspaceId, application, config, async () => {
|
|
17968
|
+
if (!args.yes) {
|
|
17969
|
+
if (!await prompt.confirm({
|
|
17970
|
+
message: "Are you sure you want to remove all resources?",
|
|
17971
|
+
default: false
|
|
17972
|
+
})) throw new Error(multiline`
|
|
17973
|
+
Remove cancelled. No resources were deleted.
|
|
17974
|
+
To override, run again and confirm, or use --yes flag.
|
|
17975
|
+
`);
|
|
17976
|
+
} else logger.success("Removing all resources (--yes flag specified)...");
|
|
17977
|
+
});
|
|
17978
|
+
logger.success(`Successfully removed all resources managed by "${application.name}".`);
|
|
17979
|
+
}
|
|
17980
|
+
});
|
|
17981
|
+
|
|
17982
|
+
//#endregion
|
|
17983
|
+
//#region src/cli/shared/beta.ts
|
|
17984
|
+
/**
|
|
17985
|
+
* Warn that a feature is in beta.
|
|
17986
|
+
* @param {string} featureName - Name of the beta feature (e.g., "tailordb erd", "tailordb migration")
|
|
17987
|
+
*/
|
|
17988
|
+
function logBetaWarning(featureName) {
|
|
17989
|
+
logger.warn(`The '${featureName}' command is a beta feature and may introduce breaking changes in future releases.`);
|
|
17990
|
+
logger.newline();
|
|
17216
17991
|
}
|
|
17217
|
-
|
|
17218
|
-
|
|
17992
|
+
|
|
17993
|
+
//#endregion
|
|
17994
|
+
//#region src/cli/shared/config.ts
|
|
17995
|
+
/**
|
|
17996
|
+
* Extracts every namespace key declared under `config.db`, including those
|
|
17997
|
+
* declared with `{ external: true }`.
|
|
17998
|
+
* @param config - Loaded application configuration.
|
|
17999
|
+
* @returns Namespace names in insertion order.
|
|
18000
|
+
*/
|
|
18001
|
+
function extractAllNamespaces(config) {
|
|
18002
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
18003
|
+
if (config.db) for (const namespaceName of Object.keys(config.db)) namespaces.add(namespaceName);
|
|
18004
|
+
return Array.from(namespaces);
|
|
17219
18005
|
}
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
|
|
17223
|
-
|
|
17224
|
-
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
18006
|
+
/**
|
|
18007
|
+
* Extracts namespace keys under `config.db` that this app owns
|
|
18008
|
+
* (i.e. not declared with `{ external: true }`). Use this for destructive
|
|
18009
|
+
* operations like `tailordb truncate --all` to avoid touching namespaces
|
|
18010
|
+
* owned by other apps.
|
|
18011
|
+
* @param config - Loaded application configuration.
|
|
18012
|
+
* @returns Owned namespace names in insertion order.
|
|
18013
|
+
*/
|
|
18014
|
+
function extractOwnedNamespaces(config) {
|
|
18015
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
18016
|
+
if (config.db) for (const [namespaceName, nsConfig] of Object.entries(config.db)) {
|
|
18017
|
+
if ("external" in nsConfig) continue;
|
|
18018
|
+
namespaces.add(namespaceName);
|
|
18019
|
+
}
|
|
18020
|
+
return Array.from(namespaces);
|
|
17231
18021
|
}
|
|
17232
|
-
const workspaceNameTransformer = createWorkspaceNameTransformer("name", "folderName");
|
|
17233
18022
|
|
|
17234
18023
|
//#endregion
|
|
17235
18024
|
//#region src/cli/commands/show.ts
|
|
@@ -17246,10 +18035,27 @@ function applicationInfo(app) {
|
|
|
17246
18035
|
updatedAt: app.updateTime ? timestampDate(app.updateTime) : null
|
|
17247
18036
|
};
|
|
17248
18037
|
}
|
|
18038
|
+
async function fetchAIGateways(client, workspaceId, names) {
|
|
18039
|
+
return (await Promise.all(names.map(async (name) => {
|
|
18040
|
+
try {
|
|
18041
|
+
const { aigateway } = await client.getAIGateway({
|
|
18042
|
+
workspaceId,
|
|
18043
|
+
aigatewayName: name
|
|
18044
|
+
});
|
|
18045
|
+
return aigateway ? {
|
|
18046
|
+
name: aigateway.name,
|
|
18047
|
+
url: aigateway.url
|
|
18048
|
+
} : void 0;
|
|
18049
|
+
} catch (error) {
|
|
18050
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
18051
|
+
throw error;
|
|
18052
|
+
}
|
|
18053
|
+
}))).filter((gateway) => gateway !== void 0);
|
|
18054
|
+
}
|
|
17249
18055
|
/**
|
|
17250
18056
|
* Show applied application information for the current workspace.
|
|
17251
18057
|
* @param options - Show options
|
|
17252
|
-
* @returns
|
|
18058
|
+
* @returns Deployed application, workspace, and AI Gateway information
|
|
17253
18059
|
*/
|
|
17254
18060
|
async function show(options) {
|
|
17255
18061
|
const client = await initOperatorClient(await loadAccessToken({ profile: options?.profile }));
|
|
@@ -17258,10 +18064,15 @@ async function show(options) {
|
|
|
17258
18064
|
profile: options?.profile
|
|
17259
18065
|
});
|
|
17260
18066
|
const { config } = await loadConfig(options?.configPath);
|
|
17261
|
-
const
|
|
17262
|
-
|
|
17263
|
-
|
|
17264
|
-
|
|
18067
|
+
const aiGatewayNames = config.aiGateways?.length ? [...new Set(config.aiGateways.map((gateway) => gateway.name))] : [];
|
|
18068
|
+
const [workspaceResp, resp, aiGateways] = await Promise.all([
|
|
18069
|
+
client.getWorkspace({ workspaceId }),
|
|
18070
|
+
client.getApplication({
|
|
18071
|
+
workspaceId,
|
|
18072
|
+
applicationName: config.name
|
|
18073
|
+
}),
|
|
18074
|
+
fetchAIGateways(client, workspaceId, aiGatewayNames)
|
|
18075
|
+
]);
|
|
17265
18076
|
const { name, ...appInfo } = applicationInfo(assertDefined(resp.application, `application "${config.name}" not found in workspace`));
|
|
17266
18077
|
const workspace = workspaceResp.workspace;
|
|
17267
18078
|
const workspaceFolderName = workspace ? await resolveWorkspaceFolderName(client, workspace) : "";
|
|
@@ -17271,7 +18082,8 @@ async function show(options) {
|
|
|
17271
18082
|
workspaceName: workspace?.name ?? "",
|
|
17272
18083
|
...workspaceFolderName ? { workspaceFolderName } : {},
|
|
17273
18084
|
workspaceRegion: workspace?.region ?? "",
|
|
17274
|
-
...appInfo
|
|
18085
|
+
...appInfo,
|
|
18086
|
+
aiGateways
|
|
17275
18087
|
};
|
|
17276
18088
|
}
|
|
17277
18089
|
const showWorkspaceNameTransformer = createWorkspaceNameTransformer("workspaceName", "workspaceFolderName");
|
|
@@ -17878,7 +18690,7 @@ async function generate(options) {
|
|
|
17878
18690
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
17879
18691
|
let pluginManager;
|
|
17880
18692
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
17881
|
-
const { defineApplication } = await import("./application-
|
|
18693
|
+
const { defineApplication } = await import("./application-Dp_-ioiP.mjs");
|
|
17882
18694
|
const application = defineApplication({
|
|
17883
18695
|
config,
|
|
17884
18696
|
pluginManager
|
|
@@ -18238,7 +19050,7 @@ async function listWorkflows(options) {
|
|
|
18238
19050
|
return [workflows, nextPageToken];
|
|
18239
19051
|
}, { limit: options?.limit })).map(toWorkflowListInfo);
|
|
18240
19052
|
}
|
|
18241
|
-
const listCommand$
|
|
19053
|
+
const listCommand$2 = defineAppCommand({
|
|
18242
19054
|
name: "list",
|
|
18243
19055
|
description: "List all workflows in the workspace.",
|
|
18244
19056
|
args: z.object({
|
|
@@ -18571,7 +19383,7 @@ async function listApps(options) {
|
|
|
18571
19383
|
return [applications, nextPageToken];
|
|
18572
19384
|
}, { limit })).map(appInfo);
|
|
18573
19385
|
}
|
|
18574
|
-
const listCommand$
|
|
19386
|
+
const listCommand$1 = defineAppCommand({
|
|
18575
19387
|
name: "list",
|
|
18576
19388
|
description: "List applications in a workspace",
|
|
18577
19389
|
args: z.object({
|
|
@@ -18594,152 +19406,6 @@ const listCommand$2 = defineAppCommand({
|
|
|
18594
19406
|
}
|
|
18595
19407
|
});
|
|
18596
19408
|
|
|
18597
|
-
//#endregion
|
|
18598
|
-
//#region src/cli/commands/workspace/create.ts
|
|
18599
|
-
/**
|
|
18600
|
-
* Schema for workspace creation options
|
|
18601
|
-
* - name: 3-63 chars, lowercase alphanumeric and hyphens, cannot start/end with hyphen
|
|
18602
|
-
* - organizationId, folderId: optional UUIDs
|
|
18603
|
-
*/
|
|
18604
|
-
const createWorkspaceOptionsSchema = z.object({
|
|
18605
|
-
name: z.string().min(3, "Name must be at least 3 characters").max(63, "Name must be at most 63 characters").regex(/^[a-z0-9-]+$/, "Name can only contain lowercase letters, numbers, and hyphens").refine((n) => !n.startsWith("-") && !n.endsWith("-"), "Name cannot start or end with a hyphen"),
|
|
18606
|
-
region: z.string(),
|
|
18607
|
-
deleteProtection: z.boolean().optional(),
|
|
18608
|
-
organizationId: z.uuid().optional(),
|
|
18609
|
-
folderId: z.uuid().optional()
|
|
18610
|
-
});
|
|
18611
|
-
const validateRegion = async (region, client) => {
|
|
18612
|
-
const availableRegions = await client.listAvailableWorkspaceRegions({});
|
|
18613
|
-
if (!availableRegions.regions.includes(region)) throw new Error(`Region must be one of: ${availableRegions.regions.join(", ")}.`);
|
|
18614
|
-
};
|
|
18615
|
-
function profilePlatformSettings(platformConfig) {
|
|
18616
|
-
const hasOAuth2ClientId = platformConfig?.oauth2ClientId || process.env.TAILOR_PLATFORM_OAUTH2_CLIENT_ID;
|
|
18617
|
-
const hasConsoleUrl = platformConfig?.consoleUrl || process.env.TAILOR_PLATFORM_CONSOLE_URL;
|
|
18618
|
-
return {
|
|
18619
|
-
...isDefaultPlatform(platformConfig) ? {} : { platform_url: getPlatformBaseUrl(platformConfig) },
|
|
18620
|
-
...hasOAuth2ClientId ? { oauth2_client_id: getOAuth2ClientId(platformConfig) } : {},
|
|
18621
|
-
...hasConsoleUrl ? { console_url: getConsoleBaseUrl(platformConfig) } : {}
|
|
18622
|
-
};
|
|
18623
|
-
}
|
|
18624
|
-
/**
|
|
18625
|
-
* Create a new workspace with the given options.
|
|
18626
|
-
* @param options - Workspace creation options
|
|
18627
|
-
* @returns Created workspace info
|
|
18628
|
-
*/
|
|
18629
|
-
async function createWorkspace(options) {
|
|
18630
|
-
const result = createWorkspaceOptionsSchema.safeParse(options);
|
|
18631
|
-
if (!result.success) throw new Error(assertDefined(result.error.issues[0], "Zod returned no issues").message);
|
|
18632
|
-
const validated = result.data;
|
|
18633
|
-
const client = await initOperatorClient(await loadAccessToken());
|
|
18634
|
-
await validateRegion(validated.region, client);
|
|
18635
|
-
return workspaceInfoWithFolderName(client, assertDefined((await client.createWorkspace({
|
|
18636
|
-
workspaceName: validated.name,
|
|
18637
|
-
workspaceRegion: validated.region,
|
|
18638
|
-
deleteProtection: validated.deleteProtection ?? false,
|
|
18639
|
-
organizationId: validated.organizationId,
|
|
18640
|
-
folderId: validated.folderId
|
|
18641
|
-
})).workspace, "createWorkspace response missing workspace"));
|
|
18642
|
-
}
|
|
18643
|
-
const createCommand = defineAppCommand({
|
|
18644
|
-
name: "create",
|
|
18645
|
-
description: "Create a new Tailor Platform workspace.",
|
|
18646
|
-
args: z.object({
|
|
18647
|
-
name: arg(z.string(), {
|
|
18648
|
-
alias: "n",
|
|
18649
|
-
description: "Workspace name"
|
|
18650
|
-
}),
|
|
18651
|
-
region: arg(z.string(), {
|
|
18652
|
-
alias: "r",
|
|
18653
|
-
description: "Workspace region (us-west, asia-northeast)"
|
|
18654
|
-
}),
|
|
18655
|
-
"delete-protection": arg(z.boolean().default(false), {
|
|
18656
|
-
alias: "d",
|
|
18657
|
-
description: "Enable delete protection"
|
|
18658
|
-
}),
|
|
18659
|
-
"organization-id": arg(z.string().optional(), {
|
|
18660
|
-
alias: "o",
|
|
18661
|
-
description: "Organization ID to workspace associate with",
|
|
18662
|
-
env: "TAILOR_PLATFORM_ORGANIZATION_ID"
|
|
18663
|
-
}),
|
|
18664
|
-
"folder-id": arg(z.string().optional(), {
|
|
18665
|
-
alias: "f",
|
|
18666
|
-
description: "Folder ID to workspace associate with",
|
|
18667
|
-
env: "TAILOR_PLATFORM_FOLDER_ID"
|
|
18668
|
-
}),
|
|
18669
|
-
"profile-name": arg(z.string().optional(), {
|
|
18670
|
-
alias: "p",
|
|
18671
|
-
description: "Profile name to create"
|
|
18672
|
-
}),
|
|
18673
|
-
"profile-user": arg(z.string().optional(), { description: "User email for the profile (defaults to current user)" }),
|
|
18674
|
-
permission: arg(z.enum(["write", "read"]).default("write"), { description: "Profile permission (requires --profile-name). 'read' blocks all write commands while the profile is active." })
|
|
18675
|
-
}).strict(),
|
|
18676
|
-
run: async (args) => {
|
|
18677
|
-
await assertWritable();
|
|
18678
|
-
const profileName = args["profile-name"];
|
|
18679
|
-
let profileSetup;
|
|
18680
|
-
if (profileName) {
|
|
18681
|
-
const config = await readPlatformConfig();
|
|
18682
|
-
if (config.profiles[profileName]) throw new Error(`Profile "${profileName}" already exists.`);
|
|
18683
|
-
const activeProfileName = process.env.TAILOR_PLATFORM_PROFILE;
|
|
18684
|
-
const activeProfileEntry = activeProfileName ? config.profiles[activeProfileName] : void 0;
|
|
18685
|
-
const platformConfig = activeProfileEntry ? platformConfigFromProfile(activeProfileEntry) : void 0;
|
|
18686
|
-
const profileUser = args["profile-user"] || activeProfileEntry?.user || config.current_user;
|
|
18687
|
-
if (!profileUser) throw new Error("Current user not found. Please login or specify --profile-user to create a profile.");
|
|
18688
|
-
if (!hasUserTokenEntry(config, profileUser, platformConfig)) throw new Error(`User "${profileUser}" not found.\nPlease verify your user name and login using 'tailor-sdk login' command.`);
|
|
18689
|
-
profileSetup = {
|
|
18690
|
-
name: profileName,
|
|
18691
|
-
user: profileUser,
|
|
18692
|
-
platformSettings: profilePlatformSettings(platformConfig)
|
|
18693
|
-
};
|
|
18694
|
-
}
|
|
18695
|
-
const workspace = await createWorkspace({
|
|
18696
|
-
name: args.name,
|
|
18697
|
-
region: args.region,
|
|
18698
|
-
deleteProtection: args["delete-protection"],
|
|
18699
|
-
organizationId: args["organization-id"],
|
|
18700
|
-
folderId: args["folder-id"]
|
|
18701
|
-
});
|
|
18702
|
-
let profileInfo;
|
|
18703
|
-
if (profileSetup) {
|
|
18704
|
-
const config = await readPlatformConfig();
|
|
18705
|
-
const platformSettings = profileSetup.platformSettings;
|
|
18706
|
-
config.profiles[profileSetup.name] = {
|
|
18707
|
-
user: profileSetup.user,
|
|
18708
|
-
workspace_id: workspace.id,
|
|
18709
|
-
...args.permission === "read" ? { readonly: true } : {},
|
|
18710
|
-
...platformSettings
|
|
18711
|
-
};
|
|
18712
|
-
writePlatformConfig(config);
|
|
18713
|
-
profileInfo = {
|
|
18714
|
-
name: profileSetup.name,
|
|
18715
|
-
user: profileSetup.user,
|
|
18716
|
-
workspaceId: workspace.id,
|
|
18717
|
-
permission: args.permission,
|
|
18718
|
-
...platformSettings.platform_url ? { platformUrl: platformSettings.platform_url } : {},
|
|
18719
|
-
...platformSettings.oauth2_client_id ? { oauth2ClientId: platformSettings.oauth2_client_id } : {},
|
|
18720
|
-
...platformSettings.console_url ? { consoleUrl: platformSettings.console_url } : {}
|
|
18721
|
-
};
|
|
18722
|
-
if (!args.json) logger.success(`Profile "${profileSetup.name}" created successfully.`);
|
|
18723
|
-
}
|
|
18724
|
-
if (!args.json) logger.success(`Workspace "${workspaceDisplayName(workspace)}" created successfully.`);
|
|
18725
|
-
if (args.json && profileInfo) {
|
|
18726
|
-
logger.out({
|
|
18727
|
-
...workspace,
|
|
18728
|
-
profile: profileInfo
|
|
18729
|
-
});
|
|
18730
|
-
return;
|
|
18731
|
-
}
|
|
18732
|
-
logger.out(workspace, { display: {
|
|
18733
|
-
name: workspaceNameTransformer,
|
|
18734
|
-
folderName: null
|
|
18735
|
-
} });
|
|
18736
|
-
if (profileInfo) {
|
|
18737
|
-
logger.out("Profile:");
|
|
18738
|
-
logger.out(profileInfo);
|
|
18739
|
-
}
|
|
18740
|
-
}
|
|
18741
|
-
});
|
|
18742
|
-
|
|
18743
19409
|
//#endregion
|
|
18744
19410
|
//#region src/cli/commands/workspace/delete.ts
|
|
18745
19411
|
const deleteWorkspaceOptionsSchema = z.object({ workspaceId: z.uuid({ message: "workspace-id must be a valid UUID" }) });
|
|
@@ -18853,42 +19519,6 @@ const getCommand = defineAppCommand({
|
|
|
18853
19519
|
}
|
|
18854
19520
|
});
|
|
18855
19521
|
|
|
18856
|
-
//#endregion
|
|
18857
|
-
//#region src/cli/commands/workspace/list.ts
|
|
18858
|
-
/**
|
|
18859
|
-
* List workspaces with an optional order and limit.
|
|
18860
|
-
* @param options - Workspace listing options
|
|
18861
|
-
* @returns List of workspaces
|
|
18862
|
-
*/
|
|
18863
|
-
async function listWorkspaces(options) {
|
|
18864
|
-
const client = await initOperatorClient(await loadAccessToken());
|
|
18865
|
-
const pageDirection = toPageDirection(options?.order);
|
|
18866
|
-
return workspaceInfosWithFolderNames(client, await fetchPaged(async (pageToken, pageSize) => {
|
|
18867
|
-
const { workspaces, nextPageToken } = await client.listWorkspaces({
|
|
18868
|
-
pageToken,
|
|
18869
|
-
pageSize,
|
|
18870
|
-
pageDirection
|
|
18871
|
-
});
|
|
18872
|
-
return [workspaces, nextPageToken];
|
|
18873
|
-
}, { limit: options?.limit }));
|
|
18874
|
-
}
|
|
18875
|
-
const listCommand$1 = defineAppCommand({
|
|
18876
|
-
name: "list",
|
|
18877
|
-
description: "List all Tailor Platform workspaces.",
|
|
18878
|
-
args: z.object({ ...paginationArgs() }).strict(),
|
|
18879
|
-
run: async (args) => {
|
|
18880
|
-
const workspaces = await listWorkspaces({
|
|
18881
|
-
order: args.order,
|
|
18882
|
-
limit: args.limit
|
|
18883
|
-
});
|
|
18884
|
-
logger.out(workspaces, { display: {
|
|
18885
|
-
name: workspaceNameTransformer,
|
|
18886
|
-
folderName: null,
|
|
18887
|
-
updatedAt: null
|
|
18888
|
-
} });
|
|
18889
|
-
}
|
|
18890
|
-
});
|
|
18891
|
-
|
|
18892
19522
|
//#endregion
|
|
18893
19523
|
//#region src/cli/commands/workspace/restore.ts
|
|
18894
19524
|
const restoreWorkspaceOptionsSchema = z.object({ workspaceId: z.uuid({ message: "workspace-id must be a valid UUID" }) });
|
|
@@ -20145,5 +20775,5 @@ function isDeno() {
|
|
|
20145
20775
|
}
|
|
20146
20776
|
|
|
20147
20777
|
//#endregion
|
|
20148
|
-
export {
|
|
20149
|
-
//# sourceMappingURL=runtime-
|
|
20778
|
+
export { listFolders as $, deploymentArgs as $n, handleOptionalToRequiredError as $t, generate as A, generateUserTypes as An, startCommand as At, remove as B, loadConsoleBaseUrl as Bn, deploy as Bt, waitWorkflowExecution as C, formatDiffSummary as Cn, listCommand$8 as Ct, listWorkflows as D, resourceTrn as Dn, jobsCommand as Dt, listCommand$2 as E, getNamespacesWithMigrations as En, getExecutorWaitFailureMessage as Et, openInConfiguredEditor as F, deleteUserTokens as Fn, getWorkflowExecution as Ft, treeCommand as G, readPlatformConfig as Gn, createWorkspace$1 as Gt, updateCommand$1 as H, loadStoredUserTokens as Hn, listCommand$9 as Ht, show as I, fetchLatestToken as In, listWorkflowExecutions as It, getCommand$1 as J, writePlatformConfig as Jn, ensureConfigId as Jt, listCommand$3 as K, resolveUserTokenKey as Kn, formatKeyValueTable as Kt, showCommand as L, hasAnyUserTokenEntry as Ln, functionExecutionStatusToString as Lt, generateMigrationScript as M, apiCall as Mn, getCommand$5 as Mt, writeDbTypesFile as N, assertWritable as Nn, getWorkflow as Nt, truncate as O, sdkNameLabelKey as On, listExecutorJobs as Ot, getConfiguredEditorCommand as P, loadConfig as Pn, executionsCommand as Pt, listCommand$4 as Q, confirmationArgs as Qn, MIGRATION_LABEL_KEY as Qt, extractOwnedNamespaces as R, hasUserTokenEntry as Rn, getCommand$6 as Rt, waitCommand as S, parseMigrationNumberArg as Sn, triggerExecutor as St, resumeWorkflow as T, hasChanges as Tn, getExecutorJob as Tt, updateOrganization as U, loadWorkspaceId as Un, listWorkspaces as Ut, removeCommand$1 as V, loadMachineUserName as Vn, deployFromCLI as Vt, organizationTree as W, platformConfigFromProfile as Wn, createCommand$1 as Wt, updateCommand$2 as X, commonArgs as Xn, waitForExecution as Xt, getOrganization as Y, defineAppCommand as Yn, executeScript as Yt, updateFolder as Z, configArg as Zn, bundleMigrationScript as Zt, deleteWorkspace as _, getNextMigrationNumber as _n, getCommand$4 as _t, updateUser as a, DIFF_FILE_NAME as an, toPageDirection as ar, createFolder as at, getAppHealth as b, reconstructSnapshotFromMigrations as bn, webhookCommand as bt, listCommand as c, SCHEMA_FILE_NAME as cn, getCommand$3 as ct, inviteUser as d, compareSnapshots as dn, tokenCommand as dt, parseMigrationLabelNumber as en, isVerbose as er, getCommand$2 as et, restoreCommand as f, createSnapshotFromLocalTypes as fn, listCommand$6 as ft, deleteCommand as g, getMigrationFiles as gn, listFunctionRegistries as gt, getWorkspace as h, getMigrationFilePath as hn, listCommand$7 as ht, updateCommand as i, DB_TYPES_FILE_NAME as in, resolveMachineUserInputSource as ir, createCommand as it, generateCommand as j, prompt as jn, startWorkflow as jt, truncateCommand as k, PluginManager as kn, watchExecutorJob as kt, listUsers as l, assertValidMigrationFiles as ln, getOAuth2Client as lt, getCommand as m, getMigrationDirPath as mn, generate$1 as mt, query as n, generateAllTypeManifestsFromSnapshot as nn, pagedLogArgs as nr, deleteCommand$1 as nt, removeCommand as o, INITIAL_SCHEMA_NUMBER as on, workspaceArgs as or, listCommand$5 as ot, restoreWorkspace as p, getLatestMigrationNumber as pn, listMachineUsers as pt, listOrganizations as q, saveUserTokens as qn, workspaceNameSchema as qt, queryCommand as r, protoGqlPermission as rn, paginationArgs as rr, deleteFolder as rt, removeUser as s, MIGRATE_FILE_NAME as sn, listOAuth2Clients as st, isNativeTypeScriptRuntime as t, compareSnapshotWithRemote as tn, multiConfigArg as tr, getFolder as tt, inviteCommand as u, compareLocalTypesWithSnapshot as un, getMachineUserToken as ut, listApps as v, isValidMigrationNumber as vn, getFunctionRegistry as vt, resumeCommand as w, formatMigrationDiff as wn, listExecutors as wt, healthCommand as x, formatMigrationNumber as xn, triggerCommand as xt, listCommand$1 as y, loadDiff as yn, listWebhookExecutors as yt, logBetaWarning as z, loadAccessToken as zn, getExecutor as zt };
|
|
20779
|
+
//# sourceMappingURL=runtime-BD2vYJFT.mjs.map
|