@tailor-platform/sdk 1.76.1 → 1.76.2
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 +34 -0
- package/dist/application-6zsDLGKv.mjs +3 -0
- package/dist/{application-BUfYmf0O.mjs → application-CqjCcZ2n.mjs} +372 -1673
- package/dist/application-CqjCcZ2n.mjs.map +1 -0
- package/dist/cli/cache/bundle-cache.d.mts +2 -1
- package/dist/cli/cache/store.d.mts +1 -1
- package/dist/cli/commands/api/index.d.mts +1 -0
- package/dist/cli/commands/deploy/aigateway.d.mts +5 -1
- package/dist/cli/commands/deploy/application.d.mts +7 -1
- package/dist/cli/commands/deploy/apply-phases.d.mts +14 -1
- package/dist/cli/commands/deploy/auth.d.mts +5 -1
- package/dist/cli/commands/deploy/deploy.d.mts +5 -0
- package/dist/cli/commands/deploy/executor.d.mts +7 -1
- package/dist/cli/commands/deploy/function-registry.d.mts +5 -0
- package/dist/cli/commands/deploy/idp.d.mts +5 -1
- package/dist/cli/commands/deploy/resolver.d.mts +5 -1
- package/dist/cli/commands/deploy/secret-manager.d.mts +5 -1
- package/dist/cli/commands/deploy/staticwebsite.d.mts +5 -1
- package/dist/cli/commands/deploy/tailordb/index.d.mts +8 -1
- package/dist/cli/commands/deploy/types.d.mts +3 -1
- package/dist/cli/commands/deploy/workflow-execution-policy.d.mts +3 -1
- package/dist/cli/commands/deploy/workflow.d.mts +4 -1
- package/dist/cli/commands/executor/get.d.mts +0 -1
- package/dist/cli/commands/executor/jobs.d.mts +0 -1
- package/dist/cli/commands/executor/list.d.mts +0 -1
- package/dist/cli/commands/executor/transform.d.mts +1 -0
- package/dist/cli/commands/executor/trigger.d.mts +0 -1
- package/dist/cli/commands/executor/webhook.d.mts +0 -1
- package/dist/cli/commands/function/get.d.mts +0 -1
- package/dist/cli/commands/function/list.d.mts +0 -1
- package/dist/cli/commands/function/transform.d.mts +1 -0
- package/dist/cli/commands/generate/service.d.mts +5 -1
- package/dist/cli/commands/generate/types.d.mts +3 -1
- package/dist/cli/commands/machineuser/list.d.mts +0 -1
- package/dist/cli/commands/machineuser/token.d.mts +0 -1
- package/dist/cli/commands/oauth2client/get.d.mts +0 -1
- package/dist/cli/commands/oauth2client/list.d.mts +0 -1
- package/dist/cli/commands/oauth2client/transform.d.mts +1 -0
- package/dist/cli/commands/organization/folder/create.d.mts +0 -1
- package/dist/cli/commands/organization/folder/delete.d.mts +0 -1
- package/dist/cli/commands/organization/folder/get.d.mts +0 -1
- package/dist/cli/commands/organization/folder/list.d.mts +0 -1
- package/dist/cli/commands/organization/folder/update.d.mts +0 -1
- package/dist/cli/commands/organization/get.d.mts +0 -1
- package/dist/cli/commands/organization/list.d.mts +0 -1
- package/dist/cli/commands/organization/transform.d.mts +2 -0
- package/dist/cli/commands/organization/tree.d.mts +0 -1
- package/dist/cli/commands/organization/update.d.mts +0 -1
- package/dist/cli/commands/remove.d.mts +0 -1
- package/dist/cli/commands/show.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/config.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +1 -1
- package/dist/cli/commands/tailordb/truncate.d.mts +0 -1
- package/dist/cli/commands/workflow/executions.d.mts +0 -1
- package/dist/cli/commands/workflow/get.d.mts +2 -1
- package/dist/cli/commands/workflow/list.d.mts +0 -1
- package/dist/cli/commands/workflow/resume.d.mts +0 -1
- package/dist/cli/commands/workflow/start.d.mts +0 -1
- package/dist/cli/commands/workflow/status.d.mts +1 -0
- package/dist/cli/commands/workflow/transform.d.mts +1 -0
- package/dist/cli/commands/workflow/wait.d.mts +0 -1
- package/dist/cli/commands/workflow/waiter.d.mts +1 -1
- package/dist/cli/commands/workspace/app/health.d.mts +0 -1
- package/dist/cli/commands/workspace/app/list.d.mts +0 -1
- package/dist/cli/commands/workspace/app/transform.d.mts +2 -0
- package/dist/cli/commands/workspace/create.d.mts +0 -1
- package/dist/cli/commands/workspace/delete.d.mts +0 -1
- package/dist/cli/commands/workspace/get.d.mts +0 -1
- package/dist/cli/commands/workspace/list.d.mts +0 -1
- package/dist/cli/commands/workspace/restore.d.mts +0 -1
- package/dist/cli/commands/workspace/transform.d.mts +2 -0
- package/dist/cli/commands/workspace/user/invite.d.mts +0 -1
- package/dist/cli/commands/workspace/user/list.d.mts +0 -1
- package/dist/cli/commands/workspace/user/remove.d.mts +0 -1
- package/dist/cli/commands/workspace/user/transform.d.mts +1 -0
- package/dist/cli/commands/workspace/user/update.d.mts +0 -1
- package/dist/cli/index.d.mts +3 -1
- package/dist/cli/index.mjs +599 -42
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +2 -0
- package/dist/cli/lib.mjs +3 -3
- package/dist/cli/query/index.d.mts +0 -1
- package/dist/cli/services/application.d.mts +12 -1
- package/dist/cli/services/auth/service.d.mts +2 -0
- package/dist/cli/services/executor/service.d.mts +1 -1
- package/dist/cli/services/http-adapter/bundler.d.mts +3 -1
- package/dist/cli/services/http-adapter/service.d.mts +2 -1
- package/dist/cli/services/resolver/service.d.mts +1 -1
- package/dist/cli/services/tailordb/service.d.mts +3 -1
- package/dist/cli/services/workflow/bundler.d.mts +2 -1
- package/dist/cli/services/workflow/service.d.mts +1 -1
- package/dist/cli/shared/args.d.mts +1 -1
- package/dist/cli/shared/client.d.mts +1 -1
- package/dist/cli/shared/config-loader.d.mts +0 -1
- package/dist/cli/shared/context.d.mts +2 -1
- package/dist/cli/shared/script-executor.d.mts +0 -1
- package/dist/cli/shared/seed-chunker.d.mts +7 -3
- package/dist/cli/shared/type-generator.d.mts +0 -1
- package/dist/completion/zsh-worker.zsh +1 -1
- package/dist/configure/config/index.d.mts +0 -1
- package/dist/configure/config/types.d.mts +2 -2
- package/dist/configure/index.d.mts +2 -1
- package/dist/configure/index.mjs +54 -4
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/aigateway/index.d.mts +0 -1
- package/dist/configure/services/aigateway/types.d.mts +0 -1
- package/dist/configure/services/auth/index.d.mts +0 -1
- package/dist/configure/services/auth/types.d.mts +14 -13
- package/dist/configure/services/executor/executor.d.mts +0 -1
- package/dist/configure/services/executor/index.d.mts +1 -0
- package/dist/configure/services/executor/operation.d.mts +1 -2
- package/dist/configure/services/executor/trigger/event.d.mts +0 -1
- package/dist/configure/services/executor/trigger/index.d.mts +0 -1
- package/dist/configure/services/executor/trigger/schedule.d.mts +0 -1
- package/dist/configure/services/executor/trigger/webhook.d.mts +0 -1
- package/dist/configure/services/http-adapter/http-adapter.d.mts +1 -2
- package/dist/configure/services/idp/index.d.mts +0 -1
- package/dist/configure/services/idp/permission.d.mts +4 -5
- package/dist/configure/services/idp/types.d.mts +0 -1
- package/dist/configure/services/index.d.mts +5 -0
- package/dist/configure/services/resolver/resolver.d.mts +0 -1
- package/dist/configure/services/resolver/types.d.mts +0 -1
- package/dist/configure/services/secrets/index.d.mts +0 -1
- package/dist/configure/services/secrets/types.d.mts +0 -1
- package/dist/configure/services/staticwebsite/index.d.mts +0 -1
- package/dist/configure/services/staticwebsite/types.d.mts +0 -1
- package/dist/configure/services/tailordb/permission.d.mts +4 -5
- package/dist/configure/services/tailordb/schema.d.mts +21 -8
- package/dist/configure/services/tailordb/types.d.mts +4 -5
- package/dist/configure/services/workflow/execution-policy.d.mts +0 -1
- package/dist/configure/services/workflow/execution-policy.types.d.mts +0 -1
- package/dist/configure/services/workflow/index.d.mts +1 -0
- package/dist/configure/services/workflow/job.d.mts +0 -1
- package/dist/configure/services/workflow/wait-point.d.mts +0 -1
- package/dist/configure/services/workflow/workflow.d.mts +0 -1
- package/dist/configure/types/field-runtime.d.mts +1 -1
- package/dist/configure/types/field.d.mts +1 -2
- package/dist/configure/types/field.types.d.mts +1 -2
- package/dist/configure/types/type.d.mts +1 -1
- package/dist/configure/user.d.mts +0 -1
- package/dist/{crashreport-ayWANEZ6.mjs → crashreport-DZDr-TvH.mjs} +3 -2
- package/dist/{crashreport-ayWANEZ6.mjs.map → crashreport-DZDr-TvH.mjs.map} +1 -1
- package/dist/{crashreport-Dy4ZzSkd.mjs → crashreport-np4-_DX2.mjs} +1 -1
- package/dist/{field-runtime-9bfXBAOK.mjs → field-runtime-D4QWRiB1.mjs} +66 -46
- package/dist/field-runtime-D4QWRiB1.mjs.map +1 -0
- package/dist/{globals-CcU1ONiK.mjs → globals-z6zYMjlr.mjs} +2 -2
- package/dist/{globals-CcU1ONiK.mjs.map → globals-z6zYMjlr.mjs.map} +1 -1
- package/dist/kysely/index.d.mts +3 -4
- package/dist/parser/service/auth/index.d.mts +1 -1
- package/dist/parser/service/auth/schema.d.mts +1 -0
- package/dist/parser/service/http-adapter/index.d.mts +1 -1
- package/dist/parser/service/idp/types.d.mts +1 -0
- package/dist/parser/service/tailordb/types.d.mts +0 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +0 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +0 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +0 -1
- package/dist/plugin/builtin/seed/index.d.mts +0 -1
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/plugin/get-generated-type.d.mts +0 -1
- package/dist/plugin/manager.d.mts +3 -1
- package/dist/plugin/types.d.mts +0 -1
- package/dist/plugin/with-context.d.mts +0 -1
- package/dist/{registry-NfSW0BRo.mjs → registry-CTgoVWc_.mjs} +4 -49
- package/dist/registry-CTgoVWc_.mjs.map +1 -0
- package/dist/runtime/aigateway.d.mts +0 -2
- package/dist/runtime/authconnection.d.mts +0 -2
- package/dist/runtime/globals.d.mts +0 -1
- package/dist/runtime/index.d.mts +0 -1
- package/dist/runtime/types.d.mts +16 -7
- package/dist/{runtime-_vUgjiqT.mjs → runtime-DlklyuHN.mjs} +1301 -716
- package/dist/runtime-DlklyuHN.mjs.map +1 -0
- package/dist/{schema-DrVyN-LP.mjs → schema-DbTR1c6M.mjs} +3 -2
- package/dist/schema-DbTR1c6M.mjs.map +1 -0
- package/dist/{secret-file-VSVGy1V0.mjs → secret-file-DBqZhjFQ.mjs} +2 -27
- package/dist/{secret-file-VSVGy1V0.mjs.map → secret-file-DBqZhjFQ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +3 -2
- package/dist/{seed-fm0LeYP2.mjs → seed-DFFigL8V.mjs} +3 -13
- package/dist/seed-DFFigL8V.mjs.map +1 -0
- package/dist/service_pb-BoLM0X4K.mjs +167 -0
- package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
- package/dist/{service_pb-B2FF59sP.mjs → service_pb-r1T7kpJc.mjs} +1 -1
- package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +0 -1
- package/dist/test-env-key-IPapa1KJ.mjs +49 -0
- package/dist/test-env-key-IPapa1KJ.mjs.map +1 -0
- package/dist/type-source-DH_LH20p.mjs +13 -0
- package/dist/type-source-DH_LH20p.mjs.map +1 -0
- package/dist/types/aigateway.generated.d.mts +5 -2
- package/dist/types/auth-connection.generated.d.mts +11 -5
- package/dist/types/auth.generated.d.mts +111 -51
- package/dist/types/executor.generated.d.mts +59 -27
- package/dist/types/helpers.d.mts +5 -5
- package/dist/types/http-adapter.generated.d.mts +11 -5
- package/dist/types/idp.generated.d.mts +20 -9
- package/dist/types/resolver.generated.d.mts +19 -9
- package/dist/types/staticwebsite.generated.d.mts +7 -3
- package/dist/types/tailordb.generated.d.mts +47 -22
- package/dist/types/workflow.generated.d.mts +9 -4
- package/dist/{types-BwzB2okW.mjs → types-BzJ7A_fn.mjs} +2 -2
- package/dist/{types-BwzB2okW.mjs.map → types-BzJ7A_fn.mjs.map} +1 -1
- package/dist/types-CG9H7HuE.mjs +4 -0
- package/dist/user-agent-DX2Jnw0-.mjs +28 -0
- package/dist/user-agent-DX2Jnw0-.mjs.map +1 -0
- package/dist/utils/test/index.d.mts +0 -1
- package/dist/utils/test/index.mjs +1 -1
- package/dist/utils/test/mock.d.mts +0 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/index.d.mts +1 -1
- package/dist/vitest/index.mjs +2 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock-kysely.d.mts +0 -1
- package/dist/vitest/mocks/aigateway.d.mts +2 -2
- package/dist/vitest/mocks/authconnection.d.mts +2 -2
- package/dist/vitest/mocks/idp.d.mts +2 -2
- package/dist/vitest/mocks/secretmanager.d.mts +3 -1
- package/dist/vitest/mocks/tailordb.d.mts +6 -3
- package/dist/vitest/mocks/workflow.d.mts +11 -6
- package/dist/vitest/setup.mjs +1 -1
- package/dist/{service_pb-B5w9rjPY.mjs → workspace_resource_pb-Db3fv68L.mjs} +57 -165
- package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
- package/docs/services/tailordb.md +10 -1
- package/package.json +11 -11
- package/dist/application-BUfYmf0O.mjs.map +0 -1
- package/dist/application-DbvD5UUz.mjs +0 -3
- package/dist/cli/shared/trigger-context.d.mts +0 -1
- package/dist/field-runtime-9bfXBAOK.mjs.map +0 -1
- package/dist/job-D-PbD1P3.mjs +0 -55
- package/dist/job-D-PbD1P3.mjs.map +0 -1
- package/dist/registry-NfSW0BRo.mjs.map +0 -1
- package/dist/resource_pb-BRv7AMXK.mjs +0 -49
- package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
- package/dist/runtime-_vUgjiqT.mjs.map +0 -1
- package/dist/schema-DrVyN-LP.mjs.map +0 -1
- package/dist/seed-fm0LeYP2.mjs.map +0 -1
- package/dist/service_pb-B5w9rjPY.mjs.map +0 -1
- package/dist/types-KlawuVKa.mjs +0 -4
|
@@ -1,27 +1,35 @@
|
|
|
1
|
-
import { t as db } from "./schema-
|
|
2
|
-
import {
|
|
1
|
+
import { t as db } from "./schema-DbTR1c6M.mjs";
|
|
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
4
|
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-BEiZZ3qT.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { o as loadFilesWithIgnores, t as createExecutorService } from "./service-BCRJ-3NV.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-CqjCcZ2n.mjs";
|
|
6
|
+
import { o as loadFilesWithIgnores, s as functionSchema, t as createExecutorService } from "./service-BCRJ-3NV.mjs";
|
|
7
|
+
import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "./enum-constants-j9QBF0cB.mjs";
|
|
7
8
|
import { t as multiline } from "./multiline-sfHpTZZK.mjs";
|
|
9
|
+
import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "./file-utils-DcyIPFQh.mjs";
|
|
10
|
+
import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "./kysely-type-D-zflMGf.mjs";
|
|
11
|
+
import { n as seedPlugin, t as SeedGeneratorID } from "./seed-DFFigL8V.mjs";
|
|
8
12
|
import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
|
|
9
|
-
import {
|
|
13
|
+
import { t as userAgent } from "./user-agent-DX2Jnw0-.mjs";
|
|
10
14
|
import { n as isCLIError, t as createCLIError } from "./errors-Dtf2WPaW.mjs";
|
|
11
|
-
import {
|
|
15
|
+
import { n as tightenSecretFilePermissions, r as writeSecretFile } from "./secret-file-DBqZhjFQ.mjs";
|
|
12
16
|
import { r as withSpan } from "./telemetry-CdqJEzkj.mjs";
|
|
13
17
|
import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
|
|
14
18
|
import { z } from "zod";
|
|
15
|
-
import {
|
|
19
|
+
import { create, fromJson, toJson } from "@bufbuild/protobuf";
|
|
16
20
|
import * as fs$1 from "node:fs";
|
|
17
|
-
import {
|
|
21
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { parseEnv } from "node:util";
|
|
18
23
|
import { ValueSchema, timestampDate } from "@bufbuild/protobuf/wkt";
|
|
19
24
|
import * as path from "pathe";
|
|
20
25
|
import { formatDistanceToNowStrict } from "date-fns";
|
|
21
26
|
import { getBorderCharacters, table } from "table";
|
|
22
27
|
import { pathToFileURL } from "node:url";
|
|
28
|
+
import * as os from "node:os";
|
|
23
29
|
import { tmpdir } from "node:os";
|
|
30
|
+
import { parseTOML, parseYAML, stringifyYAML } from "confbox";
|
|
24
31
|
import { findUpSync } from "find-up-simple";
|
|
32
|
+
import { lt } from "semver";
|
|
25
33
|
import { xdgConfig } from "xdg-basedir";
|
|
26
34
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
27
35
|
import pLimit from "p-limit";
|
|
@@ -47,6 +55,256 @@ import { createPrompt } from "@toiroakr/read-multiline";
|
|
|
47
55
|
import { astVisitor, parse } from "pgsql-ast-parser";
|
|
48
56
|
import { parse as parse$1 } from "@0no-co/graphql.web";
|
|
49
57
|
|
|
58
|
+
//#region src/cli/shared/args.ts
|
|
59
|
+
const unitToMs = {
|
|
60
|
+
ms: 1,
|
|
61
|
+
s: 1e3,
|
|
62
|
+
m: 60 * 1e3
|
|
63
|
+
};
|
|
64
|
+
const durationPattern = /^(\d+)(ms|s|m)$/;
|
|
65
|
+
/**
|
|
66
|
+
* Schema for duration string validation (e.g., "3s", "500ms", "1m")
|
|
67
|
+
* Only validates format; use parseDuration() to convert to milliseconds
|
|
68
|
+
*/
|
|
69
|
+
const durationArg = z.string().refine((val) => durationPattern.test(val), { message: "Invalid duration format. Expected format: '3s', '500ms', '1m'" }).refine((val) => {
|
|
70
|
+
const match = val.match(durationPattern);
|
|
71
|
+
if (!match) return false;
|
|
72
|
+
const digits = match[1];
|
|
73
|
+
return digits !== void 0 && parseInt(digits, 10) > 0;
|
|
74
|
+
}, { message: "Duration must be greater than 0" });
|
|
75
|
+
/**
|
|
76
|
+
* Parse a validated duration string into milliseconds
|
|
77
|
+
* @param duration - Duration string (e.g., "3s", "500ms", "1m")
|
|
78
|
+
* @returns Duration in milliseconds
|
|
79
|
+
*/
|
|
80
|
+
function parseDuration(duration) {
|
|
81
|
+
const match = assertDefined(duration.match(durationPattern), `invalid duration format: ${duration}`);
|
|
82
|
+
const value = parseInt(assertDefined(match[1], "duration digits group missing"), 10);
|
|
83
|
+
const unit = assertDefined(match[2], "duration unit group missing");
|
|
84
|
+
return value * unitToMs[unit];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Schema for positive integer validation (from string input)
|
|
88
|
+
* Transforms the string to a number
|
|
89
|
+
*/
|
|
90
|
+
const positiveIntArg = z.coerce.number().int().positive();
|
|
91
|
+
/**
|
|
92
|
+
* Schema for non-negative integer validation (from string input).
|
|
93
|
+
* Accepts 0 (used for `--limit 0` to disable the limit).
|
|
94
|
+
*/
|
|
95
|
+
const nonNegativeIntArg = z.coerce.number().int().nonnegative();
|
|
96
|
+
/**
|
|
97
|
+
* Schema for sort order (`asc` or `desc`).
|
|
98
|
+
*/
|
|
99
|
+
const orderArg = z.enum(["asc", "desc"]);
|
|
100
|
+
/**
|
|
101
|
+
* Translate a CLI `--order` value into the proto `PageDirection` enum.
|
|
102
|
+
* Returns `undefined` when the user did not specify an order so that
|
|
103
|
+
* callers can omit the field and fall back to the server default.
|
|
104
|
+
* @param order - Order string from CLI args (`"asc"` | `"desc"` | undefined)
|
|
105
|
+
* @returns PageDirection, or undefined when `order` is undefined
|
|
106
|
+
*/
|
|
107
|
+
function toPageDirection(order) {
|
|
108
|
+
if (order === void 0) return void 0;
|
|
109
|
+
return order === "asc" ? PageDirection.ASC : PageDirection.DESC;
|
|
110
|
+
}
|
|
111
|
+
function hasMachineUserFlag(argv) {
|
|
112
|
+
return argv.slice(0, argv.indexOf("--") === -1 ? argv.length : argv.indexOf("--")).some((token) => token === "-m" || token.startsWith("-m=") || token === "--machine-user" || token.startsWith("--machine-user=") || token === "--machineUser" || token.startsWith("--machineUser=") || token === "--machineuser" || token.startsWith("--machineuser="));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolve whether a parsed machine user value came from an explicit CLI option or env fallback.
|
|
116
|
+
* @param machineUser - Parsed machine user value
|
|
117
|
+
* @param argv - Raw CLI argv, excluding the executable and script path
|
|
118
|
+
* @param options - Source resolution options
|
|
119
|
+
* @returns Machine user input source, or undefined when no value was parsed
|
|
120
|
+
*/
|
|
121
|
+
function resolveMachineUserInputSource(machineUser, argv = process.argv.slice(2), options = {}) {
|
|
122
|
+
if (machineUser === void 0) return void 0;
|
|
123
|
+
if (options.valueIsExplicit) return "option";
|
|
124
|
+
if (hasMachineUserFlag(argv)) return "option";
|
|
125
|
+
return process.env.TAILOR_PLATFORM_MACHINE_USER_NAME === machineUser ? "env" : "option";
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Load env files from parsed arguments.
|
|
129
|
+
* Processes --env-file first, then --env-file-if-exists.
|
|
130
|
+
*
|
|
131
|
+
* Follows Node.js --env-file behavior:
|
|
132
|
+
* - Variables already set in the environment are NOT overwritten
|
|
133
|
+
* - Variables from later files override those from earlier files
|
|
134
|
+
* @param envFiles - Required env file path(s) that must exist
|
|
135
|
+
* @param envFilesIfExists - Optional env file path(s) that are loaded if they exist
|
|
136
|
+
*/
|
|
137
|
+
function loadEnvFiles(envFiles, envFilesIfExists) {
|
|
138
|
+
const originalEnvKeys = new Set(Object.keys(process.env));
|
|
139
|
+
const load = (files, required) => {
|
|
140
|
+
for (const file of [files ?? []].flat()) {
|
|
141
|
+
const envPath = path.resolve(process.cwd(), file);
|
|
142
|
+
if (!fs$1.existsSync(envPath)) {
|
|
143
|
+
if (required) throw new Error(`Environment file not found: ${envPath}`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const parsed = parseEnv(fs$1.readFileSync(envPath, "utf-8"));
|
|
147
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
148
|
+
if (originalEnvKeys.has(key)) continue;
|
|
149
|
+
process.env[key] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
load(envFiles, true);
|
|
154
|
+
load(envFilesIfExists, false);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Common arguments for all CLI commands
|
|
158
|
+
*
|
|
159
|
+
* NOTE: --env-file and --env-file-if-exists collide with Node.js flags due to a bug
|
|
160
|
+
* (https://github.com/nodejs/node/issues/54232). Node.js parses these even after the
|
|
161
|
+
* script path, causing warnings (twice due to tsx loader).
|
|
162
|
+
*/
|
|
163
|
+
const commonArgs = {
|
|
164
|
+
"env-file": arg(z.string().optional(), {
|
|
165
|
+
alias: "e",
|
|
166
|
+
description: "Path to the environment file (error if not found)",
|
|
167
|
+
completion: {
|
|
168
|
+
type: "file",
|
|
169
|
+
matcher: [".env.*", ".env"]
|
|
170
|
+
}
|
|
171
|
+
}),
|
|
172
|
+
"env-file-if-exists": arg(z.string().optional(), {
|
|
173
|
+
description: "Path to the environment file (ignored if not found)",
|
|
174
|
+
completion: {
|
|
175
|
+
type: "file",
|
|
176
|
+
matcher: [".env.*", ".env"]
|
|
177
|
+
},
|
|
178
|
+
effect: (_value, { args }) => {
|
|
179
|
+
loadEnvFiles(args["env-file"], args["env-file-if-exists"]);
|
|
180
|
+
}
|
|
181
|
+
}),
|
|
182
|
+
verbose: arg(z.boolean().default(false), {
|
|
183
|
+
description: "Enable verbose logging",
|
|
184
|
+
effect: (value) => {
|
|
185
|
+
verboseMode = value;
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
json: arg(z.boolean().default(false), {
|
|
189
|
+
alias: "j",
|
|
190
|
+
description: "Output as JSON",
|
|
191
|
+
effect: (value) => {
|
|
192
|
+
logger.jsonMode = value;
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Arguments for commands that require workspace context
|
|
198
|
+
*/
|
|
199
|
+
const workspaceArgs = {
|
|
200
|
+
"workspace-id": arg(z.string().optional(), {
|
|
201
|
+
alias: "w",
|
|
202
|
+
description: "Workspace ID",
|
|
203
|
+
env: "TAILOR_PLATFORM_WORKSPACE_ID",
|
|
204
|
+
completion: { type: "none" }
|
|
205
|
+
}),
|
|
206
|
+
profile: arg(z.string().optional(), {
|
|
207
|
+
alias: "p",
|
|
208
|
+
description: "Workspace profile",
|
|
209
|
+
env: "TAILOR_PLATFORM_PROFILE",
|
|
210
|
+
completion: { type: "none" }
|
|
211
|
+
})
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Shared config arg for commands that accept a config file path
|
|
215
|
+
*/
|
|
216
|
+
const configArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
217
|
+
alias: "c",
|
|
218
|
+
description: "Path to SDK config file",
|
|
219
|
+
env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
|
|
220
|
+
completion: {
|
|
221
|
+
type: "file",
|
|
222
|
+
extensions: ["ts"]
|
|
223
|
+
}
|
|
224
|
+
}) };
|
|
225
|
+
/**
|
|
226
|
+
* Shared config arg for commands that accept one or more comma-separated config file paths
|
|
227
|
+
*/
|
|
228
|
+
const multiConfigArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
229
|
+
alias: "c",
|
|
230
|
+
description: "Path to SDK config file. Use comma-separated paths to deploy multiple apps together.",
|
|
231
|
+
env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
|
|
232
|
+
completion: {
|
|
233
|
+
type: "file",
|
|
234
|
+
extensions: ["ts"]
|
|
235
|
+
}
|
|
236
|
+
}) };
|
|
237
|
+
/**
|
|
238
|
+
* Arguments for commands that interact with deployed resources (includes config)
|
|
239
|
+
*/
|
|
240
|
+
const deploymentArgs = {
|
|
241
|
+
...workspaceArgs,
|
|
242
|
+
...configArg
|
|
243
|
+
};
|
|
244
|
+
/**
|
|
245
|
+
* Arguments for commands that require confirmation
|
|
246
|
+
*/
|
|
247
|
+
const confirmationArgs = { yes: arg(z.boolean().default(false), {
|
|
248
|
+
alias: "y",
|
|
249
|
+
description: "Skip confirmation prompts"
|
|
250
|
+
}) };
|
|
251
|
+
/**
|
|
252
|
+
* Arguments for commands that require organization context
|
|
253
|
+
*/
|
|
254
|
+
const organizationArgs = { "organization-id": arg(z.string(), {
|
|
255
|
+
alias: "o",
|
|
256
|
+
description: "Organization ID",
|
|
257
|
+
env: "TAILOR_PLATFORM_ORGANIZATION_ID",
|
|
258
|
+
completion: { type: "none" }
|
|
259
|
+
}) };
|
|
260
|
+
/**
|
|
261
|
+
* Arguments for list commands that accept `--order` / `--limit`. Sort
|
|
262
|
+
* order defaults to `desc` (newest first) because most callers want the
|
|
263
|
+
* latest items; pass `--order asc` to opt in to ascending order. The
|
|
264
|
+
* limit is unbounded by default so existing invocations keep returning
|
|
265
|
+
* every item; pass `--limit N` to cap the result size.
|
|
266
|
+
* @param defaultOrder - Default value for `--order` (defaults to `"desc"`)
|
|
267
|
+
* @returns Argument shape suitable for spreading into a command schema
|
|
268
|
+
*/
|
|
269
|
+
const paginationArgs = (defaultOrder = "desc") => ({
|
|
270
|
+
order: arg(orderArg.default(defaultOrder), { description: "Sort order (asc or desc)" }),
|
|
271
|
+
limit: arg(nonNegativeIntArg.optional(), {
|
|
272
|
+
alias: "l",
|
|
273
|
+
description: "Maximum number of items to return (0 or omit: unlimited)"
|
|
274
|
+
})
|
|
275
|
+
});
|
|
276
|
+
/**
|
|
277
|
+
* Arguments for time-series log list commands. Defaults to newest-first
|
|
278
|
+
* (`desc`) and a 50-item cap so that listing stays responsive on busy
|
|
279
|
+
* workspaces. Pass `--limit 0` to disable the cap and fetch all entries.
|
|
280
|
+
*/
|
|
281
|
+
const pagedLogArgs = {
|
|
282
|
+
order: arg(orderArg.default("desc"), { description: "Sort order (asc or desc)" }),
|
|
283
|
+
limit: arg(nonNegativeIntArg.default(50), {
|
|
284
|
+
alias: "l",
|
|
285
|
+
description: "Maximum number of items to return (0: unlimited)"
|
|
286
|
+
})
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Arguments for commands that require folder context
|
|
290
|
+
*/
|
|
291
|
+
const folderArgs = { "folder-id": arg(z.string(), {
|
|
292
|
+
alias: "f",
|
|
293
|
+
description: "Folder ID",
|
|
294
|
+
env: "TAILOR_PLATFORM_FOLDER_ID",
|
|
295
|
+
completion: { type: "none" }
|
|
296
|
+
}) };
|
|
297
|
+
let verboseMode = false;
|
|
298
|
+
/**
|
|
299
|
+
* Returns whether verbose mode is enabled.
|
|
300
|
+
* Used by the global cleanup handler which doesn't have access to parsed args.
|
|
301
|
+
* @returns Whether verbose mode is enabled
|
|
302
|
+
*/
|
|
303
|
+
function isVerbose() {
|
|
304
|
+
return verboseMode;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
//#endregion
|
|
50
308
|
//#region src/cli/shared/command.ts
|
|
51
309
|
/**
|
|
52
310
|
* defineCommand with global args type (CommonArgsType).
|
|
@@ -55,6 +313,846 @@ import { parse as parse$1 } from "@0no-co/graphql.web";
|
|
|
55
313
|
*/
|
|
56
314
|
const defineAppCommand = createDefineCommand();
|
|
57
315
|
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/parser/app-config/schema.ts
|
|
318
|
+
const envValueSchema = z.union([
|
|
319
|
+
z.string(),
|
|
320
|
+
z.number(),
|
|
321
|
+
z.boolean()
|
|
322
|
+
]);
|
|
323
|
+
const LogLevelSchema = z.enum(LOG_LEVELS);
|
|
324
|
+
const logLevelSchema = z.string().refine((value) => LogLevelSchema.safeParse(value.trim().toUpperCase()).success, { message: `'logLevel' must be one of: ${LOG_LEVELS.join(", ")}.` });
|
|
325
|
+
/**
|
|
326
|
+
* Structural validation schema for `defineConfig({...})`. Validates only
|
|
327
|
+
* top-level fields with platform-side constraints (notably `id`); fields
|
|
328
|
+
* that carry SDK builder objects (`auth`, `idp`, `db`, ...) are accepted
|
|
329
|
+
* as opaque values, since their internal shapes are validated by their
|
|
330
|
+
* own factory functions and parser-level schemas.
|
|
331
|
+
*
|
|
332
|
+
* The `id` is auto-managed by `deploy` and stored as a plain UUID. A
|
|
333
|
+
* label-compatible prefix is added at the metadata boundary, so user-facing
|
|
334
|
+
* configs only need to carry a UUID.
|
|
335
|
+
*/
|
|
336
|
+
const AppConfigSchema = z.object({
|
|
337
|
+
id: z.uuid({ message: "'id' must be a UUID." }).optional(),
|
|
338
|
+
name: z.string().min(1, { message: "'name' must be a non-empty string." }),
|
|
339
|
+
env: z.record(z.string(), envValueSchema).optional(),
|
|
340
|
+
cors: z.array(z.string()).optional(),
|
|
341
|
+
allowedIpAddresses: z.array(z.string()).optional(),
|
|
342
|
+
disableIntrospection: z.boolean().optional(),
|
|
343
|
+
inlineSourcemap: z.boolean().optional(),
|
|
344
|
+
logLevel: logLevelSchema.optional(),
|
|
345
|
+
db: z.unknown().optional(),
|
|
346
|
+
resolver: z.unknown().optional(),
|
|
347
|
+
idp: z.unknown().optional(),
|
|
348
|
+
auth: z.unknown().optional(),
|
|
349
|
+
executor: z.unknown().optional(),
|
|
350
|
+
workflow: z.unknown().optional(),
|
|
351
|
+
httpAdapter: z.unknown().optional(),
|
|
352
|
+
staticWebsites: z.unknown().optional(),
|
|
353
|
+
aiGateways: z.unknown().optional(),
|
|
354
|
+
secrets: z.unknown().optional()
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/parser/generator-config/schema.ts
|
|
359
|
+
const DependencyKindSchema = z.enum([
|
|
360
|
+
"tailordb",
|
|
361
|
+
"resolver",
|
|
362
|
+
"executor"
|
|
363
|
+
]);
|
|
364
|
+
const KyselyTypeConfigSchema = z.tuple([z.literal("@tailor-platform/kysely-type"), z.object({ distPath: z.string() })]);
|
|
365
|
+
const SeedConfigSchema = z.tuple([z.literal("@tailor-platform/seed"), z.object({
|
|
366
|
+
distPath: z.string(),
|
|
367
|
+
machineUserName: z.string().optional(),
|
|
368
|
+
disableIdpUserSync: z.object({
|
|
369
|
+
userToIdp: z.boolean().optional(),
|
|
370
|
+
idpToUser: z.boolean().optional()
|
|
371
|
+
}).optional()
|
|
372
|
+
})]);
|
|
373
|
+
const EnumConstantsConfigSchema = z.tuple([z.literal("@tailor-platform/enum-constants"), z.object({ distPath: z.string() })]);
|
|
374
|
+
const FileUtilsConfigSchema = z.tuple([z.literal("@tailor-platform/file-utils"), z.object({ distPath: z.string() })]);
|
|
375
|
+
const CodeGeneratorSchema = z.object({
|
|
376
|
+
id: z.string(),
|
|
377
|
+
description: z.string(),
|
|
378
|
+
dependencies: z.array(DependencyKindSchema),
|
|
379
|
+
processType: z.function().optional(),
|
|
380
|
+
processResolver: z.function().optional(),
|
|
381
|
+
processExecutor: z.function().optional(),
|
|
382
|
+
processTailorDBNamespace: z.function().optional(),
|
|
383
|
+
processResolverNamespace: z.function().optional(),
|
|
384
|
+
aggregate: z.function({ output: z.any() })
|
|
385
|
+
});
|
|
386
|
+
const BaseGeneratorConfigSchema = z.union([
|
|
387
|
+
KyselyTypeConfigSchema,
|
|
388
|
+
SeedConfigSchema,
|
|
389
|
+
EnumConstantsConfigSchema,
|
|
390
|
+
FileUtilsConfigSchema,
|
|
391
|
+
CodeGeneratorSchema
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/parser/plugin-config/schema.ts
|
|
396
|
+
const PluginConfigSchema = z.object({
|
|
397
|
+
id: z.string(),
|
|
398
|
+
description: z.string(),
|
|
399
|
+
importPath: z.string().optional(),
|
|
400
|
+
pluginConfig: z.unknown().optional(),
|
|
401
|
+
typeConfigRequired: z.union([z.boolean(), functionSchema]).optional(),
|
|
402
|
+
onTypeLoaded: functionSchema.optional(),
|
|
403
|
+
onNamespaceLoaded: functionSchema.optional(),
|
|
404
|
+
onTailorDBReady: functionSchema.optional(),
|
|
405
|
+
onResolverReady: functionSchema.optional(),
|
|
406
|
+
onExecutorReady: functionSchema.optional()
|
|
407
|
+
}).passthrough().refine((p) => {
|
|
408
|
+
return !(p.onTypeLoaded || p.onNamespaceLoaded) || !!p.importPath;
|
|
409
|
+
}, { message: "importPath is required when plugin has definition-time hooks (onTypeLoaded/onNamespaceLoaded)" }).transform((plugin) => plugin);
|
|
410
|
+
|
|
411
|
+
//#endregion
|
|
412
|
+
//#region src/plugin/builtin/registry.ts
|
|
413
|
+
const builtinPlugins = /* @__PURE__ */ new Map([
|
|
414
|
+
[KyselyGeneratorID, (options) => kyselyTypePlugin(options)],
|
|
415
|
+
[SeedGeneratorID, (options) => seedPlugin(options)],
|
|
416
|
+
[EnumConstantsGeneratorID, (options) => enumConstantsPlugin(options)],
|
|
417
|
+
[FileUtilsGeneratorID, (options) => fileUtilsPlugin(options)]
|
|
418
|
+
]);
|
|
419
|
+
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/cli/shared/token-store.ts
|
|
422
|
+
const SERVICE_NAME = "tailor-platform-cli";
|
|
423
|
+
let entryClass;
|
|
424
|
+
async function getEntryClass() {
|
|
425
|
+
if (entryClass !== void 0) return entryClass;
|
|
426
|
+
try {
|
|
427
|
+
const mod = await import("@napi-rs/keyring");
|
|
428
|
+
const probe = new mod.Entry(SERVICE_NAME, "__probe__");
|
|
429
|
+
probe.setPassword("probe");
|
|
430
|
+
probe.deletePassword();
|
|
431
|
+
entryClass = mod.Entry;
|
|
432
|
+
} catch {
|
|
433
|
+
logger.warn("System keyring is not available. Tokens will be stored in the config file. Set TAILOR_PLATFORM_TOKEN environment variable for CI environments.");
|
|
434
|
+
entryClass = false;
|
|
435
|
+
}
|
|
436
|
+
return entryClass;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Check whether the OS keyring is available and functional.
|
|
440
|
+
* @returns true if keyring is available
|
|
441
|
+
*/
|
|
442
|
+
async function isKeyringAvailable() {
|
|
443
|
+
return await getEntryClass() !== false;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Load tokens from the OS keyring for a given account.
|
|
447
|
+
* @param account - User identifier (e.g. email or client ID)
|
|
448
|
+
* @returns Token data or undefined if not found or keyring unavailable
|
|
449
|
+
*/
|
|
450
|
+
async function loadKeyringTokens(account) {
|
|
451
|
+
const Entry = await getEntryClass();
|
|
452
|
+
if (!Entry) return void 0;
|
|
453
|
+
try {
|
|
454
|
+
const raw = new Entry(SERVICE_NAME, account).getPassword();
|
|
455
|
+
if (raw === null) return void 0;
|
|
456
|
+
return JSON.parse(raw);
|
|
457
|
+
} catch {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Save tokens to the OS keyring for a given account.
|
|
463
|
+
* @param account - User identifier (e.g. email or client ID)
|
|
464
|
+
* @param tokens - Token data to store
|
|
465
|
+
*/
|
|
466
|
+
async function saveKeyringTokens(account, tokens) {
|
|
467
|
+
const Entry = await getEntryClass();
|
|
468
|
+
if (!Entry) return;
|
|
469
|
+
new Entry(SERVICE_NAME, account).setPassword(JSON.stringify(tokens));
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Delete tokens from the OS keyring for a given account.
|
|
473
|
+
* @param account - User identifier (e.g. email or client ID)
|
|
474
|
+
*/
|
|
475
|
+
async function deleteKeyringTokens(account) {
|
|
476
|
+
const Entry = await getEntryClass();
|
|
477
|
+
if (!Entry) return;
|
|
478
|
+
try {
|
|
479
|
+
new Entry(SERVICE_NAME, account).deletePassword();
|
|
480
|
+
} catch {}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/cli/shared/context.ts
|
|
485
|
+
const pfProfileSchema = z.object({
|
|
486
|
+
user: z.string(),
|
|
487
|
+
workspace_id: z.string(),
|
|
488
|
+
readonly: z.boolean().optional(),
|
|
489
|
+
machine_user: z.string().optional(),
|
|
490
|
+
machine_user_override: z.enum(["allow", "deny"]).optional(),
|
|
491
|
+
platform_url: z.url().optional(),
|
|
492
|
+
oauth2_client_id: z.string().optional(),
|
|
493
|
+
console_url: z.url().optional()
|
|
494
|
+
});
|
|
495
|
+
const pfUserSchemaV1 = z.object({
|
|
496
|
+
access_token: z.string(),
|
|
497
|
+
refresh_token: z.string().optional(),
|
|
498
|
+
token_expires_at: z.string()
|
|
499
|
+
});
|
|
500
|
+
const pfUserKeyringSchema = z.object({
|
|
501
|
+
storage: z.literal("keyring"),
|
|
502
|
+
token_expires_at: z.string()
|
|
503
|
+
});
|
|
504
|
+
const pfUserFileSchema = z.object({
|
|
505
|
+
storage: z.literal("file"),
|
|
506
|
+
token_expires_at: z.string(),
|
|
507
|
+
access_token: z.string(),
|
|
508
|
+
refresh_token: z.string().optional()
|
|
509
|
+
});
|
|
510
|
+
const pfUserSchemaV2 = z.discriminatedUnion("storage", [pfUserKeyringSchema, pfUserFileSchema]);
|
|
511
|
+
const pfConfigSchemaV1 = z.object({
|
|
512
|
+
version: z.literal(1),
|
|
513
|
+
users: z.partialRecord(z.string(), pfUserSchemaV1),
|
|
514
|
+
profiles: z.partialRecord(z.string(), pfProfileSchema),
|
|
515
|
+
current_user: z.string().nullable()
|
|
516
|
+
});
|
|
517
|
+
const V2_CONFIG_VERSION = 2;
|
|
518
|
+
const LATEST_CONFIG_VERSION = 3;
|
|
519
|
+
const V2_MIN_SDK_VERSION = "1.29.0";
|
|
520
|
+
const V3_MIN_SDK_VERSION = "1.70.0";
|
|
521
|
+
const semverSchema = z.templateLiteral([
|
|
522
|
+
z.number().int(),
|
|
523
|
+
".",
|
|
524
|
+
z.number().int(),
|
|
525
|
+
".",
|
|
526
|
+
z.number().int()
|
|
527
|
+
]);
|
|
528
|
+
const pfConfigSchema = z.object({
|
|
529
|
+
version: z.union([z.literal(V2_CONFIG_VERSION), z.literal(LATEST_CONFIG_VERSION)]),
|
|
530
|
+
min_sdk_version: semverSchema,
|
|
531
|
+
latest_version: z.number().int().optional(),
|
|
532
|
+
latest_min_sdk_version: semverSchema.optional(),
|
|
533
|
+
users: z.partialRecord(z.string(), pfUserSchemaV2),
|
|
534
|
+
profiles: z.partialRecord(z.string(), pfProfileSchema),
|
|
535
|
+
current_user: z.string().nullable()
|
|
536
|
+
});
|
|
537
|
+
function platformConfigPath() {
|
|
538
|
+
if (!xdgConfig) throw new Error("User home directory not found");
|
|
539
|
+
return path.join(xdgConfig, "tailor-platform", "config.yaml");
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Convert stored profile platform fields to platform client settings.
|
|
543
|
+
* @param profile - Profile platform settings
|
|
544
|
+
* @returns Platform client settings
|
|
545
|
+
*/
|
|
546
|
+
function platformConfigFromProfile(profile) {
|
|
547
|
+
const config = {
|
|
548
|
+
...profile?.platform_url ? { platformUrl: profile.platform_url } : {},
|
|
549
|
+
...profile?.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
|
|
550
|
+
...profile?.console_url ? { consoleUrl: profile.console_url } : {}
|
|
551
|
+
};
|
|
552
|
+
return Object.keys(config).length > 0 ? config : void 0;
|
|
553
|
+
}
|
|
554
|
+
function platformUserKey(user, config) {
|
|
555
|
+
const platformUrl = getPlatformBaseUrl(config);
|
|
556
|
+
if (platformUrl === normalizeBaseUrl("https://api.tailor.tech")) return user;
|
|
557
|
+
return `${platformUrl}|${user}`;
|
|
558
|
+
}
|
|
559
|
+
function canUseLegacyUserKey(platformUrl) {
|
|
560
|
+
try {
|
|
561
|
+
return getPlatformBaseUrl() === platformUrl;
|
|
562
|
+
} catch {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function findUserEntry(config, user, platformConfig, opts = {}) {
|
|
567
|
+
const userKey = platformUserKey(user, platformConfig);
|
|
568
|
+
const userEntry = config.users[userKey];
|
|
569
|
+
if (userEntry) return {
|
|
570
|
+
userKey,
|
|
571
|
+
userEntry
|
|
572
|
+
};
|
|
573
|
+
const platformUrl = getPlatformBaseUrl(platformConfig);
|
|
574
|
+
if (userKey !== user && (opts.allowLegacyUserKey === false || !canUseLegacyUserKey(platformUrl))) return {
|
|
575
|
+
userKey,
|
|
576
|
+
userEntry
|
|
577
|
+
};
|
|
578
|
+
const legacyEntry = config.users[user];
|
|
579
|
+
return legacyEntry ? {
|
|
580
|
+
userKey: user,
|
|
581
|
+
userEntry: legacyEntry
|
|
582
|
+
} : {
|
|
583
|
+
userKey,
|
|
584
|
+
userEntry
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Resolve the config user key that would be used for a user on the selected platform.
|
|
589
|
+
* @param config - Platform config
|
|
590
|
+
* @param user - User name
|
|
591
|
+
* @param platformConfig - Optional platform connection settings
|
|
592
|
+
* @param opts - Token lookup options
|
|
593
|
+
* @returns Resolved config user key
|
|
594
|
+
*/
|
|
595
|
+
function resolveUserTokenKey(config, user, platformConfig, opts) {
|
|
596
|
+
return findUserEntry(config, user, platformConfig, opts).userKey;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Check whether tokens are registered for a user on the selected platform.
|
|
600
|
+
* @param config - Platform config
|
|
601
|
+
* @param user - User name
|
|
602
|
+
* @param platformConfig - Optional platform connection settings
|
|
603
|
+
* @param opts - Token lookup options
|
|
604
|
+
* @returns True when the user has a registered token entry
|
|
605
|
+
*/
|
|
606
|
+
function hasUserTokenEntry(config, user, platformConfig, opts) {
|
|
607
|
+
return findUserEntry(config, user, platformConfig, opts).userEntry !== void 0;
|
|
608
|
+
}
|
|
609
|
+
function hasUserKeyForName(users, user) {
|
|
610
|
+
return users[user] !== void 0 || Object.keys(users).some((key) => key.endsWith(`|${user}`));
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Check whether any platform has tokens registered for a user.
|
|
614
|
+
* @param config - Platform config
|
|
615
|
+
* @param user - User name
|
|
616
|
+
* @returns True when the user has a token entry for any platform
|
|
617
|
+
*/
|
|
618
|
+
function hasAnyUserTokenEntry(config, user) {
|
|
619
|
+
return hasUserKeyForName(config.users, user);
|
|
620
|
+
}
|
|
621
|
+
function hasCurrentUserEntry(users, currentUser) {
|
|
622
|
+
return hasUserKeyForName(users, currentUser);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Migrate a v1 config to v2.
|
|
626
|
+
* Tokens are kept in the config file (storage: "file") during migration.
|
|
627
|
+
* They will be moved to the OS keyring on next login or token refresh.
|
|
628
|
+
* @param v1Config - v1 configuration to migrate
|
|
629
|
+
* @returns Migrated v2 configuration
|
|
630
|
+
*/
|
|
631
|
+
function migrateV1ToV2(v1Config) {
|
|
632
|
+
const users = {};
|
|
633
|
+
for (const [name, v1User] of Object.entries(v1Config.users)) {
|
|
634
|
+
if (!v1User) continue;
|
|
635
|
+
users[name] = {
|
|
636
|
+
access_token: v1User.access_token,
|
|
637
|
+
refresh_token: v1User.refresh_token,
|
|
638
|
+
token_expires_at: v1User.token_expires_at,
|
|
639
|
+
storage: "file"
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
version: V2_CONFIG_VERSION,
|
|
644
|
+
min_sdk_version: V2_MIN_SDK_VERSION,
|
|
645
|
+
users,
|
|
646
|
+
profiles: v1Config.profiles,
|
|
647
|
+
current_user: v1Config.current_user
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
async function warnIfNewerConfigAvailable(config) {
|
|
651
|
+
if (!config.latest_min_sdk_version) return;
|
|
652
|
+
if (lt((await readPackageJson()).version ?? "0.0.0", config.latest_min_sdk_version)) logger.warn(multiline`
|
|
653
|
+
A newer config version (${String(config.latest_version)}) is available.
|
|
654
|
+
Please update your SDK to >= ${config.latest_min_sdk_version}: pnpm update @tailor-platform/sdk
|
|
655
|
+
`);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Read Tailor Platform CLI configuration, migrating from tailorctl or v1 if necessary.
|
|
659
|
+
* @returns Parsed platform configuration
|
|
660
|
+
*/
|
|
661
|
+
async function readPlatformConfig() {
|
|
662
|
+
const configPath = platformConfigPath();
|
|
663
|
+
if (!fs$1.existsSync(configPath)) {
|
|
664
|
+
logger.warn(`Config not found at ${configPath}, migrating from tailorctl config...`);
|
|
665
|
+
const tcConfig = readTailorctlConfig();
|
|
666
|
+
const v1Config = tcConfig ? fromTailorctlConfig(tcConfig) : {
|
|
667
|
+
version: 1,
|
|
668
|
+
users: {},
|
|
669
|
+
profiles: {},
|
|
670
|
+
current_user: null
|
|
671
|
+
};
|
|
672
|
+
writePlatformConfig(v1Config);
|
|
673
|
+
return migrateV1ToV2(v1Config);
|
|
674
|
+
}
|
|
675
|
+
const rawConfig = parseYAML(fs$1.readFileSync(configPath, "utf-8"));
|
|
676
|
+
tightenSecretFilePermissions(configPath);
|
|
677
|
+
const version = rawConfig != null && typeof rawConfig === "object" && "version" in rawConfig ? rawConfig.version : void 0;
|
|
678
|
+
if (typeof version === "number" && version > LATEST_CONFIG_VERSION) {
|
|
679
|
+
const minSdk = "min_sdk_version" in rawConfig ? String(rawConfig.min_sdk_version) : void 0;
|
|
680
|
+
const updateHint = minSdk ? `Please update your SDK to >= ${minSdk}: pnpm update @tailor-platform/sdk` : "Please update your SDK: pnpm update @tailor-platform/sdk";
|
|
681
|
+
throw new Error(multiline`
|
|
682
|
+
Config file uses version ${String(version)}, but this SDK only supports up to version ${String(LATEST_CONFIG_VERSION)}.
|
|
683
|
+
${updateHint}
|
|
684
|
+
`);
|
|
685
|
+
}
|
|
686
|
+
const configResult = pfConfigSchema.safeParse(rawConfig);
|
|
687
|
+
if (configResult.success) {
|
|
688
|
+
await warnIfNewerConfigAvailable(configResult.data);
|
|
689
|
+
return configResult.data;
|
|
690
|
+
}
|
|
691
|
+
const v1Result = pfConfigSchemaV1.safeParse(rawConfig);
|
|
692
|
+
if (v1Result.success) return migrateV1ToV2(v1Result.data);
|
|
693
|
+
throw new Error(multiline`
|
|
694
|
+
Failed to parse config file at ${configPath}.
|
|
695
|
+
The file may be corrupted or created by an incompatible SDK version.
|
|
696
|
+
`);
|
|
697
|
+
}
|
|
698
|
+
function toV1ForDisk(config) {
|
|
699
|
+
const users = {};
|
|
700
|
+
for (const [name, entry] of Object.entries(config.users)) {
|
|
701
|
+
if (!entry || entry.storage === "keyring") continue;
|
|
702
|
+
users[name] = {
|
|
703
|
+
access_token: entry.access_token,
|
|
704
|
+
refresh_token: entry.refresh_token,
|
|
705
|
+
token_expires_at: entry.token_expires_at
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const currentUser = config.current_user && hasCurrentUserEntry(users, config.current_user) ? config.current_user : null;
|
|
709
|
+
return {
|
|
710
|
+
version: 1,
|
|
711
|
+
users,
|
|
712
|
+
profiles: config.profiles,
|
|
713
|
+
current_user: currentUser
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function hasProfilePlatformSettings(config) {
|
|
717
|
+
return Object.values(config.profiles).some((profile) => profile?.platform_url !== void 0 || profile?.oauth2_client_id !== void 0 || profile?.console_url !== void 0);
|
|
718
|
+
}
|
|
719
|
+
function hasScopedUserKeys(config) {
|
|
720
|
+
return Object.keys(config.users).some((userKey) => userKey.includes("|"));
|
|
721
|
+
}
|
|
722
|
+
function toLatestForDisk(config) {
|
|
723
|
+
return {
|
|
724
|
+
...config.version === 1 ? migrateV1ToV2(config) : config,
|
|
725
|
+
version: LATEST_CONFIG_VERSION,
|
|
726
|
+
min_sdk_version: V3_MIN_SDK_VERSION
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Write Tailor Platform CLI configuration to disk.
|
|
731
|
+
* By default, file-backed configs without newer fields are converted to V1 for
|
|
732
|
+
* backward compatibility, so an older SDK can still read the file. Configs
|
|
733
|
+
* containing a keyring user are kept in V2 or later because the keyring storage
|
|
734
|
+
* variant is not representable in V1. Configs containing profile-level Platform
|
|
735
|
+
* settings or platform-scoped user tokens are written in the latest
|
|
736
|
+
* min-SDK-gated format because older SDKs would silently drop or misread those
|
|
737
|
+
* settings.
|
|
738
|
+
* Set TAILOR_USE_KEYRING to write the current in-memory format unconditionally.
|
|
739
|
+
*
|
|
740
|
+
* The config file may contain access/refresh tokens when the OS keyring is
|
|
741
|
+
* unavailable, so it is written via {@link writeSecretFile} so other users
|
|
742
|
+
* on the host cannot read it.
|
|
743
|
+
* @param config - Platform configuration to write
|
|
744
|
+
*/
|
|
745
|
+
function writePlatformConfig(config) {
|
|
746
|
+
const configPath = platformConfigPath();
|
|
747
|
+
const hasKeyringUser = config.version !== 1 && Object.values(config.users).some((u) => u?.storage === "keyring");
|
|
748
|
+
writeSecretFile(configPath, stringifyYAML(hasProfilePlatformSettings(config) || hasScopedUserKeys(config) ? toLatestForDisk(config) : config.version !== 1 && !process.env.TAILOR_USE_KEYRING && !hasKeyringUser ? toV1ForDisk(config) : config));
|
|
749
|
+
}
|
|
750
|
+
const tcContextConfigSchema = z.object({
|
|
751
|
+
username: z.string().optional(),
|
|
752
|
+
controlplaneaccesstoken: z.string().optional(),
|
|
753
|
+
controlplanerefreshtoken: z.string().optional(),
|
|
754
|
+
controlplanetokenexpiresat: z.string().optional(),
|
|
755
|
+
workspaceid: z.string().optional()
|
|
756
|
+
});
|
|
757
|
+
const tcConfigSchema = z.object({ global: z.object({ context: z.string().optional() }).optional() }).catchall(tcContextConfigSchema.optional());
|
|
758
|
+
function readTailorctlConfig() {
|
|
759
|
+
const configPath = path.join(os.homedir(), ".tailorctl", "config");
|
|
760
|
+
if (!fs$1.existsSync(configPath)) return;
|
|
761
|
+
const rawConfig = parseTOML(fs$1.readFileSync(configPath, "utf-8"));
|
|
762
|
+
return tcConfigSchema.parse(rawConfig);
|
|
763
|
+
}
|
|
764
|
+
function fromTailorctlConfig(config) {
|
|
765
|
+
const users = {};
|
|
766
|
+
const profiles = {};
|
|
767
|
+
let currentUser = null;
|
|
768
|
+
const currentContext = config.global?.context || "default";
|
|
769
|
+
for (const [key, val] of Object.entries(config)) {
|
|
770
|
+
if (key === "global") continue;
|
|
771
|
+
const context = val;
|
|
772
|
+
if (!context.username || !context.controlplaneaccesstoken || !context.controlplanerefreshtoken || !context.controlplanetokenexpiresat || !context.workspaceid) continue;
|
|
773
|
+
if (key === currentContext) currentUser = context.username;
|
|
774
|
+
profiles[key] = {
|
|
775
|
+
user: context.username,
|
|
776
|
+
workspace_id: context.workspaceid
|
|
777
|
+
};
|
|
778
|
+
const user = users[context.username];
|
|
779
|
+
if (!user || new Date(user.token_expires_at) < new Date(context.controlplanetokenexpiresat)) users[context.username] = {
|
|
780
|
+
access_token: context.controlplaneaccesstoken,
|
|
781
|
+
refresh_token: context.controlplanerefreshtoken,
|
|
782
|
+
token_expires_at: context.controlplanetokenexpiresat
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
version: 1,
|
|
787
|
+
users,
|
|
788
|
+
profiles,
|
|
789
|
+
current_user: currentUser
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function validateUUID(value, source) {
|
|
793
|
+
const result = z.uuid().safeParse(value);
|
|
794
|
+
if (!result.success) throw new Error(`Invalid value from ${source}: must be a valid UUID`);
|
|
795
|
+
return result.data;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Load workspace ID from command options, environment variables, or platform config.
|
|
799
|
+
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
800
|
+
* Priority: opts/workspaceId > env/workspaceId > opts/profile > error
|
|
801
|
+
* @param opts - Workspace and profile options
|
|
802
|
+
* @returns Resolved workspace ID
|
|
803
|
+
*/
|
|
804
|
+
async function loadWorkspaceId(opts) {
|
|
805
|
+
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
806
|
+
if (opts?.workspaceId) return validateUUID(opts.workspaceId, "--workspace-id option");
|
|
807
|
+
if (process.env.TAILOR_PLATFORM_WORKSPACE_ID) return validateUUID(process.env.TAILOR_PLATFORM_WORKSPACE_ID, "TAILOR_PLATFORM_WORKSPACE_ID environment variable");
|
|
808
|
+
if (profile) {
|
|
809
|
+
const wsId = (await readPlatformConfig()).profiles[profile]?.workspace_id;
|
|
810
|
+
if (!wsId) throw new Error(`Profile "${profile}" not found`);
|
|
811
|
+
return validateUUID(wsId, `profile "${profile}"`);
|
|
812
|
+
}
|
|
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
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Load machine user name from command options, environment variables, or platform config.
|
|
820
|
+
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
821
|
+
* Priority: opts/machineUser > env/TAILOR_PLATFORM_MACHINE_USER_NAME > opts/profile (profile default) > undefined.
|
|
822
|
+
* An explicitly empty `opts.machineUser` is rejected with a CLIError (`MACHINE_USER_NAME_EMPTY`) rather than falling back to the env var or profile default.
|
|
823
|
+
* When the active profile has `machine_user_override: "deny"`, an explicit value that differs from the profile's machine user throws a CLIError with code `PROFILE_MACHINE_USER_OVERRIDE_DENIED`.
|
|
824
|
+
* @param opts - Machine user and profile options
|
|
825
|
+
* @returns Resolved machine user name, or undefined if not set
|
|
826
|
+
*/
|
|
827
|
+
async function loadMachineUserName(opts) {
|
|
828
|
+
if (opts?.machineUser === "") throw createCLIError({
|
|
829
|
+
code: "MACHINE_USER_NAME_EMPTY",
|
|
830
|
+
message: "Machine user name cannot be empty.",
|
|
831
|
+
suggestion: "Pass a non-empty machine user name, or omit the option to use the environment variable or profile default."
|
|
832
|
+
});
|
|
833
|
+
const envMachineUser = process.env.TAILOR_PLATFORM_MACHINE_USER_NAME || void 0;
|
|
834
|
+
const explicitMachineUser = opts?.machineUser ? {
|
|
835
|
+
source: opts.machineUserSource ?? "option",
|
|
836
|
+
value: opts.machineUser
|
|
837
|
+
} : envMachineUser ? {
|
|
838
|
+
source: "env",
|
|
839
|
+
value: envMachineUser
|
|
840
|
+
} : void 0;
|
|
841
|
+
const explicit = explicitMachineUser?.value;
|
|
842
|
+
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
843
|
+
if (!profile) return explicit;
|
|
844
|
+
const entry = (await readPlatformConfig()).profiles[profile];
|
|
845
|
+
if (!entry) {
|
|
846
|
+
if (explicit) return explicit;
|
|
847
|
+
throw new Error(`Profile "${profile}" not found`);
|
|
848
|
+
}
|
|
849
|
+
if (entry.machine_user && entry.machine_user_override === "deny") {
|
|
850
|
+
if (explicit && explicit !== entry.machine_user) {
|
|
851
|
+
const details = explicitMachineUser.source === "env" ? `The machine user is being set to "${explicit}" via the TAILOR_PLATFORM_MACHINE_USER_NAME environment variable, which conflicts with this profile's pinned machine user "${entry.machine_user}".` : `This profile fixes the machine user to "${entry.machine_user}" for application-data commands.`;
|
|
852
|
+
throw createCLIError({
|
|
853
|
+
code: "PROFILE_MACHINE_USER_OVERRIDE_DENIED",
|
|
854
|
+
message: `Profile "${profile}" denies overriding the machine user.`,
|
|
855
|
+
details,
|
|
856
|
+
suggestion: `Omit the machine user option, unset TAILOR_PLATFORM_MACHINE_USER_NAME, or run 'tailor-sdk profile update ${profile} --machine-user-override allow'.`
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return entry.machine_user;
|
|
860
|
+
}
|
|
861
|
+
return explicit || entry.machine_user;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Load access token from environment variables, command options, or platform config.
|
|
865
|
+
* In CLI context, profile env fallback is also handled by politty's arg env option.
|
|
866
|
+
* Priority: env/TAILOR_PLATFORM_TOKEN > env/TAILOR_TOKEN (deprecated) > opts/profile > env/profile > config/currentUser > error
|
|
867
|
+
* @param opts - Profile options
|
|
868
|
+
* @returns Resolved access token
|
|
869
|
+
*/
|
|
870
|
+
async function loadAccessToken(opts) {
|
|
871
|
+
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
872
|
+
const envToken = process.env.TAILOR_PLATFORM_TOKEN ?? process.env.TAILOR_TOKEN;
|
|
873
|
+
if (envToken && !process.env.TAILOR_PLATFORM_TOKEN) logger.warn("TAILOR_TOKEN is deprecated. Please use TAILOR_PLATFORM_TOKEN instead.");
|
|
874
|
+
if (envToken) {
|
|
875
|
+
rememberPlatformConfigForToken(envToken, await loadPlatformClientConfig({
|
|
876
|
+
profile,
|
|
877
|
+
allowMissingProfile: true
|
|
878
|
+
}));
|
|
879
|
+
return envToken;
|
|
880
|
+
}
|
|
881
|
+
const pfConfig = await readPlatformConfig();
|
|
882
|
+
const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
|
|
883
|
+
if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
|
|
884
|
+
const user = profileEntry?.user ?? pfConfig.current_user;
|
|
885
|
+
if (!user) throw new Error(multiline`
|
|
886
|
+
Tailor Platform token not found.
|
|
887
|
+
Please specify token via TAILOR_PLATFORM_TOKEN environment variable or login using 'tailor-sdk login' command.
|
|
888
|
+
`);
|
|
889
|
+
return await fetchLatestToken(pfConfig, user, profileEntry ? platformConfigFromProfile(profileEntry) : void 0);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Load platform connection settings from the active profile.
|
|
893
|
+
* @param opts - Profile options
|
|
894
|
+
* @returns Resolved platform connection settings, or undefined for the default environment
|
|
895
|
+
*/
|
|
896
|
+
async function loadPlatformClientConfig(opts) {
|
|
897
|
+
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
898
|
+
if (!profile) return;
|
|
899
|
+
let pfConfig;
|
|
900
|
+
try {
|
|
901
|
+
pfConfig = await readPlatformConfig();
|
|
902
|
+
} catch (error) {
|
|
903
|
+
if (opts?.allowMissingProfile) return;
|
|
904
|
+
throw error;
|
|
905
|
+
}
|
|
906
|
+
const profileEntry = pfConfig.profiles[profile];
|
|
907
|
+
if (!profileEntry) {
|
|
908
|
+
if (opts?.allowMissingProfile) return;
|
|
909
|
+
throw new Error(`Profile "${profile}" not found`);
|
|
910
|
+
}
|
|
911
|
+
return platformConfigFromProfile(profileEntry);
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Load the Tailor Platform Console base URL from environment variables or the active profile.
|
|
915
|
+
* @param opts - Profile options
|
|
916
|
+
* @returns Resolved Console base URL
|
|
917
|
+
*/
|
|
918
|
+
async function loadConsoleBaseUrl(opts) {
|
|
919
|
+
return getConsoleBaseUrl(await loadPlatformClientConfig(opts));
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Resolve the actual token values for a user, reading from keyring or config as appropriate.
|
|
923
|
+
* @param userEntry - User entry from the config
|
|
924
|
+
* @param user - User identifier
|
|
925
|
+
* @param label - User-facing identifier used in error messages
|
|
926
|
+
* @returns Access token and optional refresh token
|
|
927
|
+
*/
|
|
928
|
+
async function resolveTokens(userEntry, user, label = user) {
|
|
929
|
+
if (userEntry.storage === "keyring") {
|
|
930
|
+
const tokens = await loadKeyringTokens(user);
|
|
931
|
+
if (!tokens) throw new Error(multiline`
|
|
932
|
+
Credentials not found in OS keyring for "${label}".
|
|
933
|
+
Please run 'tailor-sdk login' and try again.
|
|
934
|
+
`);
|
|
935
|
+
return tokens;
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
accessToken: userEntry.access_token,
|
|
939
|
+
refreshToken: userEntry.refresh_token
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Save tokens for a user, writing to keyring or config as appropriate.
|
|
944
|
+
* @param config - Platform config
|
|
945
|
+
* @param user - User identifier
|
|
946
|
+
* @param tokens - Token data to save
|
|
947
|
+
* @param tokens.accessToken - Access token to persist
|
|
948
|
+
* @param tokens.refreshToken - Refresh token to persist when available
|
|
949
|
+
* @param expiresAt - Token expiration date
|
|
950
|
+
* @param platformConfig - Optional platform connection settings
|
|
951
|
+
*/
|
|
952
|
+
async function saveUserTokens(config, user, tokens, expiresAt, platformConfig) {
|
|
953
|
+
const userKey = platformUserKey(user, platformConfig);
|
|
954
|
+
if (process.env.TAILOR_USE_KEYRING && await isKeyringAvailable()) {
|
|
955
|
+
await saveKeyringTokens(userKey, tokens);
|
|
956
|
+
config.users[userKey] = {
|
|
957
|
+
token_expires_at: expiresAt,
|
|
958
|
+
storage: "keyring"
|
|
959
|
+
};
|
|
960
|
+
} else config.users[userKey] = {
|
|
961
|
+
access_token: tokens.accessToken,
|
|
962
|
+
refresh_token: tokens.refreshToken,
|
|
963
|
+
token_expires_at: expiresAt,
|
|
964
|
+
storage: "file"
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Delete tokens for a user from keyring if applicable.
|
|
969
|
+
* @param config - Platform config
|
|
970
|
+
* @param user - User identifier
|
|
971
|
+
* @param platformConfig - Optional platform connection settings
|
|
972
|
+
* @param opts - Token lookup options
|
|
973
|
+
*/
|
|
974
|
+
async function deleteUserTokens(config, user, platformConfig, opts) {
|
|
975
|
+
const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
|
|
976
|
+
if (userEntry?.storage === "keyring") await deleteKeyringTokens(userKey);
|
|
977
|
+
delete config.users[userKey];
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Resolve stored tokens for a user on the selected platform.
|
|
981
|
+
* @param config - Platform config
|
|
982
|
+
* @param user - User name
|
|
983
|
+
* @param platformConfig - Optional platform connection settings
|
|
984
|
+
* @param opts - Token lookup options
|
|
985
|
+
* @returns Stored user entry and token values, or undefined when the user is not logged in
|
|
986
|
+
*/
|
|
987
|
+
async function loadStoredUserTokens(config, user, platformConfig, opts) {
|
|
988
|
+
const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
|
|
989
|
+
if (!userEntry) return void 0;
|
|
990
|
+
return {
|
|
991
|
+
userEntry,
|
|
992
|
+
...await resolveTokens(userEntry, userKey, user)
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Fetch the latest access token, refreshing if necessary.
|
|
997
|
+
* @param config - Platform config
|
|
998
|
+
* @param user - User name
|
|
999
|
+
* @param platformConfig - Optional platform connection settings
|
|
1000
|
+
* @returns Latest access token
|
|
1001
|
+
*/
|
|
1002
|
+
async function fetchLatestToken(config, user, platformConfig) {
|
|
1003
|
+
const { userKey, userEntry } = findUserEntry(config, user, platformConfig);
|
|
1004
|
+
if (!userEntry) throw new Error(multiline`
|
|
1005
|
+
User "${user}" not found.
|
|
1006
|
+
Please verify your user name and login using 'tailor-sdk login' command.
|
|
1007
|
+
`);
|
|
1008
|
+
const tokens = await resolveTokens(userEntry, userKey, user);
|
|
1009
|
+
if (new Date(userEntry.token_expires_at) > /* @__PURE__ */ new Date()) {
|
|
1010
|
+
rememberPlatformConfigForToken(tokens.accessToken, platformConfig);
|
|
1011
|
+
return tokens.accessToken;
|
|
1012
|
+
}
|
|
1013
|
+
if (!tokens.refreshToken) throw new Error(multiline`
|
|
1014
|
+
Token expired.
|
|
1015
|
+
Please run 'tailor-sdk login' and try again.
|
|
1016
|
+
`);
|
|
1017
|
+
const client = initOAuth2Client(platformConfig);
|
|
1018
|
+
let resp;
|
|
1019
|
+
try {
|
|
1020
|
+
resp = await client.refreshToken({
|
|
1021
|
+
accessToken: tokens.accessToken,
|
|
1022
|
+
refreshToken: tokens.refreshToken,
|
|
1023
|
+
expiresAt: Date.parse(userEntry.token_expires_at)
|
|
1024
|
+
});
|
|
1025
|
+
} catch {
|
|
1026
|
+
throw new Error(multiline`
|
|
1027
|
+
Failed to refresh token. Your session may have expired.
|
|
1028
|
+
Please run 'tailor-sdk login' and try again.
|
|
1029
|
+
`);
|
|
1030
|
+
}
|
|
1031
|
+
const newExpiresAt = new Date(assertDefined(resp.expiresAt, "token refresh response missing expiresAt")).toISOString();
|
|
1032
|
+
await saveUserTokens(config, user, {
|
|
1033
|
+
accessToken: resp.accessToken,
|
|
1034
|
+
refreshToken: resp.refreshToken ?? void 0
|
|
1035
|
+
}, newExpiresAt, platformConfig);
|
|
1036
|
+
if (userKey !== platformUserKey(user, platformConfig)) {
|
|
1037
|
+
if (userEntry.storage === "keyring") await deleteKeyringTokens(userKey);
|
|
1038
|
+
delete config.users[userKey];
|
|
1039
|
+
}
|
|
1040
|
+
writePlatformConfig(config);
|
|
1041
|
+
rememberPlatformConfigForToken(resp.accessToken, platformConfig);
|
|
1042
|
+
return resp.accessToken;
|
|
1043
|
+
}
|
|
1044
|
+
const DEFAULT_CONFIG_FILENAME = "tailor.config.ts";
|
|
1045
|
+
/**
|
|
1046
|
+
* Load config path from command options, environment variables, or search parent directories.
|
|
1047
|
+
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
1048
|
+
* Priority: opts/config > env/config > search parent directories
|
|
1049
|
+
* @param configPath - Optional explicit config path
|
|
1050
|
+
* @returns Resolved config path or undefined
|
|
1051
|
+
*/
|
|
1052
|
+
function loadConfigPath(configPath) {
|
|
1053
|
+
if (configPath) return configPath;
|
|
1054
|
+
if (process.env.TAILOR_PLATFORM_SDK_CONFIG_PATH) return process.env.TAILOR_PLATFORM_SDK_CONFIG_PATH;
|
|
1055
|
+
return findUpSync(DEFAULT_CONFIG_FILENAME);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
//#endregion
|
|
1059
|
+
//#region src/cli/shared/mock.ts
|
|
1060
|
+
/**
|
|
1061
|
+
* Install a stub `globalThis.tailordb` so that user code loaded by the CLI
|
|
1062
|
+
* (e.g. via `createGetDB` in `@tailor-platform/sdk/kysely`) can reference
|
|
1063
|
+
* `tailordb.Client` without hitting a `ReferenceError`. The CLI never
|
|
1064
|
+
* actually executes the user code paths that issue queries, so a no-op
|
|
1065
|
+
* client suffices.
|
|
1066
|
+
*
|
|
1067
|
+
* Exposed as a function (rather than a top-level statement) so that
|
|
1068
|
+
* `package.json#sideEffects` can keep the file marked side-effect-free
|
|
1069
|
+
* without bundlers eliminating the install step.
|
|
1070
|
+
*/
|
|
1071
|
+
function installCliTailordbStub() {
|
|
1072
|
+
globalThis.tailordb = { Client: class {
|
|
1073
|
+
constructor(_config) {}
|
|
1074
|
+
async connect() {}
|
|
1075
|
+
async end() {}
|
|
1076
|
+
async queryObject() {
|
|
1077
|
+
return {};
|
|
1078
|
+
}
|
|
1079
|
+
} };
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
//#endregion
|
|
1083
|
+
//#region src/cli/shared/config-loader.ts
|
|
1084
|
+
const GeneratorConfigSchema = CodeGeneratorSchema.brand("CodeGenerator");
|
|
1085
|
+
/**
|
|
1086
|
+
* Load Tailor configuration file and associated generators and plugins.
|
|
1087
|
+
* @param configPath - Optional explicit config path
|
|
1088
|
+
* @param options - Optional module import behavior.
|
|
1089
|
+
* @returns Loaded config, generators, plugins, and config path
|
|
1090
|
+
*/
|
|
1091
|
+
async function loadConfig(configPath, options = {}) {
|
|
1092
|
+
installCliTailordbStub();
|
|
1093
|
+
const foundPath = loadConfigPath(configPath);
|
|
1094
|
+
if (!foundPath) throw new Error("Configuration file not found: tailor.config.ts not found in current or parent directories");
|
|
1095
|
+
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
1096
|
+
if (!fs$1.existsSync(resolvedPath)) throw new Error(`Configuration file not found: ${configPath}`);
|
|
1097
|
+
const configUrl = pathToFileURL(resolvedPath);
|
|
1098
|
+
if (options.importNonce) configUrl.searchParams.set("tailorImportNonce", options.importNonce);
|
|
1099
|
+
const configModule = await import(configUrl.href);
|
|
1100
|
+
if (!configModule || !configModule.default) throw new Error("Invalid Tailor config module: default export not found");
|
|
1101
|
+
const validated = AppConfigSchema.safeParse(configModule.default);
|
|
1102
|
+
if (!validated.success) {
|
|
1103
|
+
const issues = validated.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
|
|
1104
|
+
throw new Error(`Invalid Tailor config in ${resolvedPath}:\n${issues}`);
|
|
1105
|
+
}
|
|
1106
|
+
const allGenerators = [];
|
|
1107
|
+
const allPlugins = [];
|
|
1108
|
+
for (const value of Object.values(configModule)) if (Array.isArray(value)) {
|
|
1109
|
+
const generatorParsed = value.reduce((acc, item) => {
|
|
1110
|
+
if (!acc.success) return acc;
|
|
1111
|
+
const baseResult = BaseGeneratorConfigSchema.safeParse(item);
|
|
1112
|
+
if (baseResult.success && Array.isArray(baseResult.data)) {
|
|
1113
|
+
const [id, options] = baseResult.data;
|
|
1114
|
+
const pluginFactory = builtinPlugins.get(id);
|
|
1115
|
+
if (pluginFactory) {
|
|
1116
|
+
acc.convertedPlugins.push(pluginFactory(options));
|
|
1117
|
+
return acc;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
const result = GeneratorConfigSchema.safeParse(item);
|
|
1121
|
+
if (result.success) acc.items.push(result.data);
|
|
1122
|
+
else acc.success = false;
|
|
1123
|
+
return acc;
|
|
1124
|
+
}, {
|
|
1125
|
+
success: true,
|
|
1126
|
+
items: [],
|
|
1127
|
+
convertedPlugins: []
|
|
1128
|
+
});
|
|
1129
|
+
if (generatorParsed.success && (generatorParsed.items.length > 0 || generatorParsed.convertedPlugins.length > 0)) {
|
|
1130
|
+
allGenerators.push(...generatorParsed.items);
|
|
1131
|
+
allPlugins.push(...generatorParsed.convertedPlugins);
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
const pluginParsed = value.reduce((acc, item) => {
|
|
1135
|
+
if (!acc.success) return acc;
|
|
1136
|
+
const result = PluginConfigSchema.safeParse(item);
|
|
1137
|
+
if (result.success) acc.items.push(result.data);
|
|
1138
|
+
else acc.success = false;
|
|
1139
|
+
return acc;
|
|
1140
|
+
}, {
|
|
1141
|
+
success: true,
|
|
1142
|
+
items: []
|
|
1143
|
+
});
|
|
1144
|
+
if (pluginParsed.success && pluginParsed.items.length > 0) allPlugins.push(...pluginParsed.items);
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
config: {
|
|
1148
|
+
...configModule.default,
|
|
1149
|
+
path: resolvedPath
|
|
1150
|
+
},
|
|
1151
|
+
generators: allGenerators,
|
|
1152
|
+
plugins: allPlugins
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
58
1156
|
//#endregion
|
|
59
1157
|
//#region src/cli/shared/readonly-guard.ts
|
|
60
1158
|
/**
|
|
@@ -128,561 +1226,6 @@ async function apiCall(options) {
|
|
|
128
1226
|
};
|
|
129
1227
|
}
|
|
130
1228
|
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/cli/commands/api/proto-reflect.ts
|
|
133
|
-
const UNARY_METHODS = OperatorService.methods.filter((m) => m.methodKind === "unary");
|
|
134
|
-
function listMethodNames() {
|
|
135
|
-
return UNARY_METHODS.map((m) => m.name).toSorted();
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Returns every accepted form of the `endpoint` positional — bare method name
|
|
139
|
-
* and fully-qualified `service/Method`. politty's expand completion keys its
|
|
140
|
-
* static table by the literal endpoint string, so both forms must appear in
|
|
141
|
-
* `choices` or the user typing the fully-qualified name gets no `--field`
|
|
142
|
-
* completion candidates back at TAB time.
|
|
143
|
-
* @returns Sorted list of accepted endpoint values
|
|
144
|
-
*/
|
|
145
|
-
function listMethodChoices() {
|
|
146
|
-
return UNARY_METHODS.flatMap((m) => [m.name, `${OperatorService.typeName}/${m.name}`]).toSorted();
|
|
147
|
-
}
|
|
148
|
-
function getMethodDescriptor(methodName) {
|
|
149
|
-
return UNARY_METHODS.find((m) => m.name === methodName);
|
|
150
|
-
}
|
|
151
|
-
function extractMethodName(endpoint) {
|
|
152
|
-
if (!endpoint.includes("/")) return endpoint;
|
|
153
|
-
return endpoint.split("/").pop() ?? endpoint;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Returns the nested message descriptor when the field is a message, a list of
|
|
157
|
-
* messages, or a map with message values. Otherwise returns undefined.
|
|
158
|
-
* @param field - The proto field descriptor to inspect
|
|
159
|
-
* @returns The nested message descriptor, or undefined when the field isn't message-shaped
|
|
160
|
-
*/
|
|
161
|
-
function nestedMessage(field) {
|
|
162
|
-
if (field.fieldKind === "message") return field.message;
|
|
163
|
-
if (field.fieldKind === "list" && field.listKind === "message") return field.message;
|
|
164
|
-
if (field.fieldKind === "map" && field.mapKind === "message") return field.message;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Most `google.protobuf.*` well-known types serialize as scalars in proto JSON
|
|
168
|
-
* (Timestamp/Duration → RFC3339/duration string, FieldMask → comma-joined
|
|
169
|
-
* string, *Value wrappers → underlying primitive, etc.), so we must not let
|
|
170
|
-
* `--field` drill into their internal fields. Treating them as leaves keeps
|
|
171
|
-
* the completion and the body shape consistent with what the server expects.
|
|
172
|
-
* @param message - The proto message descriptor to inspect
|
|
173
|
-
* @returns True when the message is a well-known type
|
|
174
|
-
*/
|
|
175
|
-
function isWellKnownType(message) {
|
|
176
|
-
return message.typeName.startsWith("google.protobuf.");
|
|
177
|
-
}
|
|
178
|
-
const UNREPRESENTABLE_WELL_KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
179
|
-
"google.protobuf.Struct",
|
|
180
|
-
"google.protobuf.Value",
|
|
181
|
-
"google.protobuf.ListValue",
|
|
182
|
-
"google.protobuf.NullValue",
|
|
183
|
-
"google.protobuf.Any",
|
|
184
|
-
"google.protobuf.Empty"
|
|
185
|
-
]);
|
|
186
|
-
/**
|
|
187
|
-
* @param message - The proto message descriptor to inspect
|
|
188
|
-
* @returns True when the message is a well-known type that `--field` cannot represent
|
|
189
|
-
*/
|
|
190
|
-
function isUnrepresentableWellKnownType(message) {
|
|
191
|
-
return UNREPRESENTABLE_WELL_KNOWN_TYPES.has(message.typeName);
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Pre-enumerates every `--field` completion candidate for `methodName`,
|
|
195
|
-
* walking the input message tree to produce a flat list that the shell can
|
|
196
|
-
* prefix-filter at TAB time. Used by the `expand` completion variant so no
|
|
197
|
-
* Node process is spawned per keystroke.
|
|
198
|
-
*
|
|
199
|
-
* For each leaf field, emits `key=` (key completion). Enum leaves additionally
|
|
200
|
-
* emit `key=ENUM_VALUE` per value, bool leaves emit `key=true` and
|
|
201
|
-
* `key=false`. For nested messages, emits `key.` (drill-down) and recurses.
|
|
202
|
-
*
|
|
203
|
-
* Returns an empty array when `methodName` is unknown so the `expand`
|
|
204
|
-
* generator stays exception-free at script-generation time.
|
|
205
|
-
* @param methodName - Name of the unary RPC whose input message is being walked
|
|
206
|
-
* @returns Flat list of `{ value, description }` candidates
|
|
207
|
-
*/
|
|
208
|
-
function enumerateAllFieldCompletions(methodName) {
|
|
209
|
-
const method = getMethodDescriptor(methodName);
|
|
210
|
-
if (!method) return [];
|
|
211
|
-
const candidates = [];
|
|
212
|
-
const visited = /* @__PURE__ */ new Set();
|
|
213
|
-
function walk(message, prefix) {
|
|
214
|
-
visited.add(message);
|
|
215
|
-
for (const field of message.fields) {
|
|
216
|
-
if (field.fieldKind === "list" || field.fieldKind === "map") continue;
|
|
217
|
-
const fullKey = prefix + field.localName;
|
|
218
|
-
if (field.fieldKind === "message") {
|
|
219
|
-
if (isUnrepresentableWellKnownType(field.message)) continue;
|
|
220
|
-
if (!isWellKnownType(field.message)) {
|
|
221
|
-
const nested = field.message;
|
|
222
|
-
candidates.push({
|
|
223
|
-
value: `${fullKey}.`,
|
|
224
|
-
description: `${fullKey} (message)`
|
|
225
|
-
});
|
|
226
|
-
if (!visited.has(nested)) walk(nested, `${fullKey}.`);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
candidates.push({
|
|
231
|
-
value: `${fullKey}=`,
|
|
232
|
-
description: `Set ${fullKey}`
|
|
233
|
-
});
|
|
234
|
-
if (field.fieldKind === "enum") for (const v of field.enum.values) candidates.push({
|
|
235
|
-
value: `${fullKey}=${v.name}`,
|
|
236
|
-
description: v.name
|
|
237
|
-
});
|
|
238
|
-
else if (field.fieldKind === "scalar" && field.scalar === ScalarType.BOOL) {
|
|
239
|
-
candidates.push({
|
|
240
|
-
value: `${fullKey}=true`,
|
|
241
|
-
description: "true"
|
|
242
|
-
});
|
|
243
|
-
candidates.push({
|
|
244
|
-
value: `${fullKey}=false`,
|
|
245
|
-
description: "false"
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
visited.delete(message);
|
|
250
|
-
}
|
|
251
|
-
walk(method.input, "");
|
|
252
|
-
return candidates;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Resolves the leaf `DescField` at a dotted `--field` path within an input
|
|
256
|
-
* message tree. Returns undefined when any segment is missing or when a
|
|
257
|
-
* non-leaf segment isn't a singular message — callers fall back to treating
|
|
258
|
-
* the value as a string in that case.
|
|
259
|
-
* @param input - The RPC input message descriptor
|
|
260
|
-
* @param path - Dot-split path segments to follow (e.g. ["application", "name"])
|
|
261
|
-
* @returns The leaf field descriptor, or undefined when the path doesn't resolve
|
|
262
|
-
*/
|
|
263
|
-
function resolveLeafField(input, path) {
|
|
264
|
-
let message = input;
|
|
265
|
-
for (let i = 0; i < path.length; i++) {
|
|
266
|
-
const field = message.fields.find((f) => f.localName === path[i]);
|
|
267
|
-
if (!field) return void 0;
|
|
268
|
-
if (i === path.length - 1) {
|
|
269
|
-
if (field.fieldKind === "message" && isUnrepresentableWellKnownType(field.message)) return;
|
|
270
|
-
return field;
|
|
271
|
-
}
|
|
272
|
-
if (field.fieldKind !== "message") return void 0;
|
|
273
|
-
if (isWellKnownType(field.message)) return void 0;
|
|
274
|
-
message = field.message;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
//#endregion
|
|
279
|
-
//#region src/cli/commands/api/render.ts
|
|
280
|
-
const SCALAR_LABEL = {
|
|
281
|
-
[ScalarType.DOUBLE]: "double",
|
|
282
|
-
[ScalarType.FLOAT]: "float",
|
|
283
|
-
[ScalarType.INT64]: "int64",
|
|
284
|
-
[ScalarType.UINT64]: "uint64",
|
|
285
|
-
[ScalarType.INT32]: "int32",
|
|
286
|
-
[ScalarType.FIXED64]: "fixed64",
|
|
287
|
-
[ScalarType.FIXED32]: "fixed32",
|
|
288
|
-
[ScalarType.BOOL]: "bool",
|
|
289
|
-
[ScalarType.STRING]: "string",
|
|
290
|
-
[ScalarType.BYTES]: "bytes",
|
|
291
|
-
[ScalarType.UINT32]: "uint32",
|
|
292
|
-
[ScalarType.SFIXED32]: "sfixed32",
|
|
293
|
-
[ScalarType.SFIXED64]: "sfixed64",
|
|
294
|
-
[ScalarType.SINT32]: "sint32",
|
|
295
|
-
[ScalarType.SINT64]: "sint64"
|
|
296
|
-
};
|
|
297
|
-
function shortName(typeName) {
|
|
298
|
-
const dot = typeName.lastIndexOf(".");
|
|
299
|
-
return dot < 0 ? typeName : typeName.slice(dot + 1);
|
|
300
|
-
}
|
|
301
|
-
function scalarLabel(scalar) {
|
|
302
|
-
return SCALAR_LABEL[scalar] ?? `scalar(${scalar})`;
|
|
303
|
-
}
|
|
304
|
-
function enumLabel(enumDesc) {
|
|
305
|
-
return `enum ${shortName(enumDesc.typeName)}`;
|
|
306
|
-
}
|
|
307
|
-
function enumOf(field) {
|
|
308
|
-
if (field.fieldKind === "enum") return field.enum;
|
|
309
|
-
if (field.fieldKind === "list" && field.listKind === "enum") return field.enum;
|
|
310
|
-
}
|
|
311
|
-
function describeFieldType(field) {
|
|
312
|
-
switch (field.fieldKind) {
|
|
313
|
-
case "scalar": return scalarLabel(field.scalar);
|
|
314
|
-
case "enum": return enumLabel(field.enum);
|
|
315
|
-
case "message": return shortName(field.message.typeName);
|
|
316
|
-
case "list": {
|
|
317
|
-
let inner;
|
|
318
|
-
if (field.listKind === "scalar") inner = scalarLabel(field.scalar);
|
|
319
|
-
else if (field.listKind === "enum") inner = enumLabel(field.enum);
|
|
320
|
-
else inner = shortName(field.message.typeName);
|
|
321
|
-
return `repeated ${inner}`;
|
|
322
|
-
}
|
|
323
|
-
case "map": {
|
|
324
|
-
const keyType = scalarLabel(field.mapKey);
|
|
325
|
-
let valType;
|
|
326
|
-
if (field.mapKind === "scalar") valType = scalarLabel(field.scalar);
|
|
327
|
-
else if (field.mapKind === "enum") valType = enumLabel(field.enum);
|
|
328
|
-
else valType = shortName(field.message.typeName);
|
|
329
|
-
return `map<${keyType}, ${valType}>`;
|
|
330
|
-
}
|
|
331
|
-
default: return "<unknown>";
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function fieldToJson(field, visited) {
|
|
335
|
-
const json = {
|
|
336
|
-
name: field.localName,
|
|
337
|
-
protoName: field.name,
|
|
338
|
-
type: describeFieldType(field),
|
|
339
|
-
fieldKind: field.fieldKind,
|
|
340
|
-
repeated: field.fieldKind === "list"
|
|
341
|
-
};
|
|
342
|
-
if (field.oneof) json.oneof = field.oneof.name;
|
|
343
|
-
const fieldEnum = enumOf(field);
|
|
344
|
-
if (fieldEnum) json.enumValues = fieldEnum.values.map((v) => v.name);
|
|
345
|
-
const nested = nestedMessage(field);
|
|
346
|
-
if (nested && !visited.has(nested)) {
|
|
347
|
-
visited.add(nested);
|
|
348
|
-
json.message = {
|
|
349
|
-
typeName: nested.typeName,
|
|
350
|
-
fields: nested.fields.map((f) => fieldToJson(f, visited))
|
|
351
|
-
};
|
|
352
|
-
visited.delete(nested);
|
|
353
|
-
} else if (nested) json.message = {
|
|
354
|
-
typeName: nested.typeName,
|
|
355
|
-
fields: [],
|
|
356
|
-
recursive: true
|
|
357
|
-
};
|
|
358
|
-
return json;
|
|
359
|
-
}
|
|
360
|
-
function renderInspectJson(method) {
|
|
361
|
-
const visited = /* @__PURE__ */ new Set([method.input]);
|
|
362
|
-
return {
|
|
363
|
-
method: method.name,
|
|
364
|
-
input: {
|
|
365
|
-
typeName: method.input.typeName,
|
|
366
|
-
fields: method.input.fields.map((f) => fieldToJson(f, visited))
|
|
367
|
-
},
|
|
368
|
-
output: { typeName: method.output.typeName }
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
function renderFieldText(field, indent, visited) {
|
|
372
|
-
const lines = [];
|
|
373
|
-
const oneofTag = field.oneof ? ` (oneof ${field.oneof.name})` : "";
|
|
374
|
-
lines.push(`${indent}${field.localName}: ${describeFieldType(field)}${oneofTag}`);
|
|
375
|
-
const fieldEnum = enumOf(field);
|
|
376
|
-
if (fieldEnum) {
|
|
377
|
-
const values = fieldEnum.values.map((v) => v.name).join(", ");
|
|
378
|
-
lines.push(`${indent} values: ${values}`);
|
|
379
|
-
}
|
|
380
|
-
const nested = nestedMessage(field);
|
|
381
|
-
if (nested && !visited.has(nested)) {
|
|
382
|
-
visited.add(nested);
|
|
383
|
-
for (const sub of nested.fields) lines.push(...renderFieldText(sub, `${indent} `, visited));
|
|
384
|
-
visited.delete(nested);
|
|
385
|
-
} else if (nested) lines.push(`${indent} …(recursive ${nested.typeName})`);
|
|
386
|
-
return lines;
|
|
387
|
-
}
|
|
388
|
-
function renderInspectText(method) {
|
|
389
|
-
const lines = [];
|
|
390
|
-
lines.push(`${method.name}`);
|
|
391
|
-
lines.push(` request: ${method.input.typeName}`);
|
|
392
|
-
const visited = /* @__PURE__ */ new Set([method.input]);
|
|
393
|
-
for (const f of method.input.fields) lines.push(...renderFieldText(f, " ", visited));
|
|
394
|
-
lines.push(` response: ${method.output.typeName}`);
|
|
395
|
-
return lines.join("\n");
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
//#endregion
|
|
399
|
-
//#region src/cli/commands/api/inspect.ts
|
|
400
|
-
const inspectCommand = defineAppCommand({
|
|
401
|
-
name: "inspect",
|
|
402
|
-
description: "Print the input message tree of an OperatorService endpoint.",
|
|
403
|
-
notes: "Combine with the global `--json` flag for a machine-readable descriptor. Recursive type references and `oneof` membership are annotated. Use `tailor-sdk api list` to discover endpoint names.",
|
|
404
|
-
examples: [{
|
|
405
|
-
cmd: "GetApplication",
|
|
406
|
-
desc: "Show fields of GetApplicationRequest."
|
|
407
|
-
}, {
|
|
408
|
-
cmd: "CreateExecutorExecutor",
|
|
409
|
-
desc: "Inspect a deeply nested input with `(oneof config)` annotations."
|
|
410
|
-
}],
|
|
411
|
-
args: z.object({ endpoint: arg(z.string(), {
|
|
412
|
-
positional: true,
|
|
413
|
-
description: "API endpoint to inspect (e.g., 'GetApplication' or 'tailor.v1.OperatorService/GetApplication').",
|
|
414
|
-
completion: { custom: { choices: listMethodNames() } }
|
|
415
|
-
}) }).strict(),
|
|
416
|
-
run: (args) => {
|
|
417
|
-
const methodName = extractMethodName(args.endpoint);
|
|
418
|
-
const method = getMethodDescriptor(methodName);
|
|
419
|
-
if (!method) throw createCLIError({
|
|
420
|
-
message: `unknown method: ${methodName}`,
|
|
421
|
-
suggestion: "Run `tailor-sdk api list` to see available methods.",
|
|
422
|
-
command: "api inspect"
|
|
423
|
-
});
|
|
424
|
-
if (logger.jsonMode) logger.out(renderInspectJson(method));
|
|
425
|
-
else logger.out(renderInspectText(method));
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
//#endregion
|
|
430
|
-
//#region src/cli/commands/api/list.ts
|
|
431
|
-
const listCommand$10 = defineAppCommand({
|
|
432
|
-
name: "list",
|
|
433
|
-
description: "List all invocable OperatorService methods.",
|
|
434
|
-
notes: "Only single-request (non-streaming) methods are listed, because the CLI issues a single JSON request and reads one JSON response.",
|
|
435
|
-
args: z.object({}).strict(),
|
|
436
|
-
run: () => {
|
|
437
|
-
const names = listMethodNames();
|
|
438
|
-
if (logger.jsonMode) logger.out(names);
|
|
439
|
-
else for (const name of names) logger.out(name);
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
//#endregion
|
|
444
|
-
//#region src/cli/commands/api/index.ts
|
|
445
|
-
function resolveNamespaceName(methodName, config) {
|
|
446
|
-
if (/Auth|Tenant|UserProfile/.test(methodName)) return config.auth?.name;
|
|
447
|
-
if (/IdP/.test(methodName)) {
|
|
448
|
-
if (config.idp?.length === 1) return assertDefined(config.idp[0], "idp config missing").name;
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (/TailorDB/.test(methodName)) {
|
|
452
|
-
const keys = Object.keys(config.db ?? {});
|
|
453
|
-
if (keys.length === 1) return keys[0];
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
if (/Pipeline/.test(methodName)) {
|
|
457
|
-
const keys = Object.keys(config.resolver ?? {});
|
|
458
|
-
if (keys.length === 1) return keys[0];
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Parse a JSON body string as a plain object for field injection.
|
|
464
|
-
* Returns undefined for invalid JSON or non-object values (null, arrays, primitives),
|
|
465
|
-
* so callers can fall back to sending the raw string unchanged.
|
|
466
|
-
* @param body - Raw body string
|
|
467
|
-
* @returns Parsed object or undefined
|
|
468
|
-
*/
|
|
469
|
-
function parseBodyAsObject(body) {
|
|
470
|
-
let parsed;
|
|
471
|
-
try {
|
|
472
|
-
parsed = JSON.parse(body);
|
|
473
|
-
} catch {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
|
|
477
|
-
return parsed;
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Set a dotted path on a body object, replacing non-object intermediates as
|
|
481
|
-
* needed. `--field` takes precedence over `--body`, so collisions overwrite.
|
|
482
|
-
* @param obj - The body object to mutate
|
|
483
|
-
* @param path - Dot-split path segments (e.g. ["application", "name"])
|
|
484
|
-
* @param value - Value to assign at the leaf
|
|
485
|
-
*/
|
|
486
|
-
function setNestedPath(obj, path, value) {
|
|
487
|
-
let cursor = obj;
|
|
488
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
489
|
-
const key = assertDefined(path[i], "path segment missing");
|
|
490
|
-
const next = cursor[key];
|
|
491
|
-
if (typeof next !== "object" || next === null || Array.isArray(next)) cursor[key] = {};
|
|
492
|
-
cursor = cursor[key];
|
|
493
|
-
}
|
|
494
|
-
cursor[assertDefined(path[path.length - 1], "path last segment missing")] = value;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Coerces a raw `--field` string value to match the leaf proto type so the
|
|
498
|
-
* JSON body sends a properly typed value. Today we only coerce bool — the
|
|
499
|
-
* completion candidates explicitly suggest `=true`/`=false`, so sending the
|
|
500
|
-
* literal string "true" would be a visible mismatch. Other scalars are left
|
|
501
|
-
* as strings: proto JSON accepts string forms for ints/floats/int64/etc.,
|
|
502
|
-
* and we'd rather pass through what the user typed than silently coerce.
|
|
503
|
-
* @param field - The resolved leaf field descriptor, or undefined when the path didn't resolve
|
|
504
|
-
* @param raw - The raw string value after `=`
|
|
505
|
-
* @returns The value to write into the body object
|
|
506
|
-
*/
|
|
507
|
-
function coerceFieldValue(field, raw) {
|
|
508
|
-
if (field && field.fieldKind === "scalar" && field.scalar === ScalarType.BOOL) {
|
|
509
|
-
if (raw === "true") return true;
|
|
510
|
-
if (raw === "false") return false;
|
|
511
|
-
throw new Error(`Invalid value for bool field: '${raw}'. Expected 'true' or 'false'.`);
|
|
512
|
-
}
|
|
513
|
-
return raw;
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Collapse top-level body keys that address a known input field by its
|
|
517
|
-
* protobuf name (snake_case) or JSON name to the field's `localName`. protojson
|
|
518
|
-
* accepts every alias, so converging them here lets the presence checks below
|
|
519
|
-
* reason in a single namespace — otherwise a `--body` written as
|
|
520
|
-
* `{"workspace_id": ...}` slips past the camelCase check and we inject a second
|
|
521
|
-
* `workspaceId`, which the server rejects as a duplicate field. When both forms
|
|
522
|
-
* are present the canonical key wins and the alias is dropped.
|
|
523
|
-
* @param body - The parsed body object to mutate
|
|
524
|
-
* @param fields - The target endpoint's input fields
|
|
525
|
-
* @returns Whether any key was rewritten
|
|
526
|
-
*/
|
|
527
|
-
function normalizeBodyFieldKeys(body, fields) {
|
|
528
|
-
const aliasToLocal = /* @__PURE__ */ new Map();
|
|
529
|
-
for (const f of fields) {
|
|
530
|
-
aliasToLocal.set(f.name, f.localName);
|
|
531
|
-
aliasToLocal.set(f.jsonName, f.localName);
|
|
532
|
-
}
|
|
533
|
-
let changed = false;
|
|
534
|
-
for (const key of Object.keys(body)) {
|
|
535
|
-
const local = aliasToLocal.get(key);
|
|
536
|
-
if (!local || local === key) continue;
|
|
537
|
-
if (!Object.hasOwn(body, local)) body[local] = body[key];
|
|
538
|
-
delete body[key];
|
|
539
|
-
changed = true;
|
|
540
|
-
}
|
|
541
|
-
return changed;
|
|
542
|
-
}
|
|
543
|
-
const FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set([
|
|
544
|
-
"__proto__",
|
|
545
|
-
"constructor",
|
|
546
|
-
"prototype"
|
|
547
|
-
]);
|
|
548
|
-
const fieldArg = z.string().transform((val, ctx) => {
|
|
549
|
-
const eq = val.indexOf("=");
|
|
550
|
-
if (eq < 0) {
|
|
551
|
-
ctx.addIssue({
|
|
552
|
-
code: z.ZodIssueCode.custom,
|
|
553
|
-
message: `Invalid field format: '${val}'. Expected format: 'key=value' or 'a.b.c=value'`
|
|
554
|
-
});
|
|
555
|
-
return z.NEVER;
|
|
556
|
-
}
|
|
557
|
-
const key = val.slice(0, eq);
|
|
558
|
-
if (key.length === 0) {
|
|
559
|
-
ctx.addIssue({
|
|
560
|
-
code: z.ZodIssueCode.custom,
|
|
561
|
-
message: "Field key cannot be empty"
|
|
562
|
-
});
|
|
563
|
-
return z.NEVER;
|
|
564
|
-
}
|
|
565
|
-
const segments = key.split(".");
|
|
566
|
-
if (segments.some((seg) => seg.length === 0)) {
|
|
567
|
-
ctx.addIssue({
|
|
568
|
-
code: z.ZodIssueCode.custom,
|
|
569
|
-
message: `Invalid field key: '${key}'. Dotted segments cannot be empty`
|
|
570
|
-
});
|
|
571
|
-
return z.NEVER;
|
|
572
|
-
}
|
|
573
|
-
const forbidden = segments.find((seg) => FORBIDDEN_SEGMENTS.has(seg));
|
|
574
|
-
if (forbidden) {
|
|
575
|
-
ctx.addIssue({
|
|
576
|
-
code: z.ZodIssueCode.custom,
|
|
577
|
-
message: `Invalid field key: '${key}'. Segment '${forbidden}' is not allowed.`
|
|
578
|
-
});
|
|
579
|
-
return z.NEVER;
|
|
580
|
-
}
|
|
581
|
-
return {
|
|
582
|
-
path: segments,
|
|
583
|
-
value: val.slice(eq + 1)
|
|
584
|
-
};
|
|
585
|
-
});
|
|
586
|
-
const apiCommand = defineAppCommand({
|
|
587
|
-
name: "api",
|
|
588
|
-
description: "Call Tailor Platform API endpoints directly.",
|
|
589
|
-
notes: `Use \`tailor-sdk api list\` to enumerate invocable methods and \`tailor-sdk api inspect <endpoint>\` to print an endpoint's input message tree (combine with \`--json\` for machine-readable output).
|
|
590
|
-
|
|
591
|
-
The request body is inferred from the target endpoint's request schema, and commonly required fields are auto-injected so they can be omitted from \`--body\`:
|
|
592
|
-
|
|
593
|
-
- \`workspaceId\` — resolved from \`-w\` / \`TAILOR_PLATFORM_WORKSPACE_ID\` / the selected profile.
|
|
594
|
-
- \`namespaceName\` — resolved from \`tailor.config.ts\` based on the endpoint's service:
|
|
595
|
-
- Auth / Tenant / UserProfile endpoints use \`auth.name\`.
|
|
596
|
-
- IdP / TailorDB / Pipeline endpoints use the sole configured namespace when exactly one is defined.
|
|
597
|
-
|
|
598
|
-
Values already present in \`--body\` are never overridden. If a value cannot be resolved (e.g. no config found), injection is silently skipped and the server-side validation error takes precedence.
|
|
599
|
-
|
|
600
|
-
Use \`--field key=value\` (repeatable) to set request body fields without writing JSON. Dotted keys (e.g. \`application.name=foo\`) build nested objects. \`--field\` overrides matching fields in \`--body\` and tab-completes from the endpoint's request schema.`,
|
|
601
|
-
examples: [
|
|
602
|
-
{
|
|
603
|
-
cmd: "GetApplication -b '{\"applicationName\":\"app-1\"}'",
|
|
604
|
-
desc: "Call an endpoint; workspaceId is auto-injected."
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
cmd: "GetApplication -f applicationName=app-1",
|
|
608
|
-
desc: "Same as above, using --field instead of --body."
|
|
609
|
-
},
|
|
610
|
-
{
|
|
611
|
-
cmd: "list",
|
|
612
|
-
desc: "List all invocable OperatorService methods."
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
cmd: "inspect GetApplication",
|
|
616
|
-
desc: "Show the input message tree for an endpoint."
|
|
617
|
-
}
|
|
618
|
-
],
|
|
619
|
-
subCommands: {
|
|
620
|
-
list: listCommand$10,
|
|
621
|
-
inspect: inspectCommand
|
|
622
|
-
},
|
|
623
|
-
args: z.object({
|
|
624
|
-
...workspaceArgs,
|
|
625
|
-
...configArg,
|
|
626
|
-
body: arg(z.string().default("{}"), {
|
|
627
|
-
alias: "b",
|
|
628
|
-
description: "Request body as JSON."
|
|
629
|
-
}),
|
|
630
|
-
field: arg(fieldArg.array().optional(), {
|
|
631
|
-
alias: "f",
|
|
632
|
-
description: "Set a body field as `key=value` (repeatable; dotted keys nest). Overrides --body.",
|
|
633
|
-
completion: { custom: { expand: {
|
|
634
|
-
dependsOn: ["endpoint"],
|
|
635
|
-
enumerate: ({ endpoint }) => enumerateAllFieldCompletions(extractMethodName(endpoint ?? ""))
|
|
636
|
-
} } }
|
|
637
|
-
}),
|
|
638
|
-
endpoint: arg(z.string(), {
|
|
639
|
-
positional: true,
|
|
640
|
-
description: "API endpoint to call (e.g., 'GetApplication' or 'tailor.v1.OperatorService/GetApplication').",
|
|
641
|
-
completion: { custom: { choices: listMethodChoices() } }
|
|
642
|
-
})
|
|
643
|
-
}).strict(),
|
|
644
|
-
run: async (args) => {
|
|
645
|
-
await assertWritable({ profile: args.profile });
|
|
646
|
-
const methodName = extractMethodName(args.endpoint);
|
|
647
|
-
const method = getMethodDescriptor(methodName);
|
|
648
|
-
const parsedBody = parseBodyAsObject(args.body);
|
|
649
|
-
let mutated = false;
|
|
650
|
-
if (args.field && args.field.length > 0) {
|
|
651
|
-
if (!parsedBody) throw new Error("--field requires --body to be a JSON object (or omitted).");
|
|
652
|
-
for (const f of args.field) {
|
|
653
|
-
const leaf = method ? resolveLeafField(method.input, f.path) : void 0;
|
|
654
|
-
setNestedPath(parsedBody, f.path, coerceFieldValue(leaf, f.value));
|
|
655
|
-
}
|
|
656
|
-
mutated = true;
|
|
657
|
-
}
|
|
658
|
-
if (parsedBody && method) {
|
|
659
|
-
if (normalizeBodyFieldKeys(parsedBody, method.input.fields)) mutated = true;
|
|
660
|
-
const fieldNames = method.input.fields.map((f) => f.localName);
|
|
661
|
-
if (fieldNames.includes("workspaceId") && !Object.hasOwn(parsedBody, "workspaceId")) try {
|
|
662
|
-
parsedBody.workspaceId = await loadWorkspaceId({
|
|
663
|
-
workspaceId: args["workspace-id"],
|
|
664
|
-
profile: args.profile
|
|
665
|
-
});
|
|
666
|
-
mutated = true;
|
|
667
|
-
} catch {}
|
|
668
|
-
if (fieldNames.includes("namespaceName") && !Object.hasOwn(parsedBody, "namespaceName")) try {
|
|
669
|
-
const { config } = await loadConfig(args.config);
|
|
670
|
-
const ns = resolveNamespaceName(methodName, config);
|
|
671
|
-
if (ns) {
|
|
672
|
-
parsedBody.namespaceName = ns;
|
|
673
|
-
mutated = true;
|
|
674
|
-
}
|
|
675
|
-
} catch {}
|
|
676
|
-
}
|
|
677
|
-
const result = await apiCall({
|
|
678
|
-
profile: args.profile,
|
|
679
|
-
endpoint: args.endpoint,
|
|
680
|
-
body: mutated && parsedBody ? JSON.stringify(parsedBody) : args.body
|
|
681
|
-
});
|
|
682
|
-
logger.out(JSON.stringify(result.data, null, 2));
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
|
|
686
1229
|
//#endregion
|
|
687
1230
|
//#region src/cli/shared/prompt.ts
|
|
688
1231
|
/**
|
|
@@ -2296,35 +2839,59 @@ const SecretsStateSchema = z.object({
|
|
|
2296
2839
|
vaults: z.record(z.string(), z.record(z.string(), z.string())),
|
|
2297
2840
|
connections: z.record(z.string(), z.string()).optional()
|
|
2298
2841
|
});
|
|
2842
|
+
const PersistedSecretsStateSchema = z.object({
|
|
2843
|
+
version: z.literal(1),
|
|
2844
|
+
workspaceId: z.string(),
|
|
2845
|
+
applicationKey: z.string(),
|
|
2846
|
+
state: SecretsStateSchema
|
|
2847
|
+
});
|
|
2299
2848
|
/**
|
|
2300
|
-
* Get the file path for
|
|
2301
|
-
* @
|
|
2849
|
+
* Get the file path for one workspace and application's secrets state JSON.
|
|
2850
|
+
* @param scope - Workspace and application identity for the deployment
|
|
2851
|
+
* @returns Absolute path to the scoped state file
|
|
2302
2852
|
*/
|
|
2303
|
-
function getSecretsStatePath() {
|
|
2304
|
-
|
|
2853
|
+
function getSecretsStatePath(scope) {
|
|
2854
|
+
const scopeHash = hashValue(JSON.stringify([scope.workspaceId, applicationStateKey(scope)]));
|
|
2855
|
+
return path.join(getDistDir(), "secrets-state", `${scopeHash}.json`);
|
|
2305
2856
|
}
|
|
2306
|
-
|
|
2307
|
-
* Load secrets hash state from disk.
|
|
2308
|
-
* @returns Persisted state, or empty state if file is missing or corrupted
|
|
2309
|
-
*/
|
|
2310
|
-
function loadSecretsState() {
|
|
2311
|
-
const filePath = getSecretsStatePath();
|
|
2312
|
-
if (!existsSync(filePath)) return { vaults: {} };
|
|
2857
|
+
function loadPersistedSecretsState(scope) {
|
|
2313
2858
|
try {
|
|
2314
|
-
const raw = readFileSync(
|
|
2315
|
-
|
|
2859
|
+
const raw = readFileSync(getSecretsStatePath(scope), "utf-8");
|
|
2860
|
+
const persistedState = PersistedSecretsStateSchema.parse(JSON.parse(raw));
|
|
2861
|
+
if (persistedState.workspaceId !== scope.workspaceId || persistedState.applicationKey !== applicationStateKey(scope)) return;
|
|
2862
|
+
return persistedState;
|
|
2316
2863
|
} catch {
|
|
2317
|
-
return
|
|
2864
|
+
return;
|
|
2318
2865
|
}
|
|
2319
2866
|
}
|
|
2867
|
+
function applicationStateKey(scope) {
|
|
2868
|
+
if (!scope.applicationId) throw new Error(`Application "${scope.applicationName}" has no stable id for secrets state`);
|
|
2869
|
+
return `id:${scope.applicationId}`;
|
|
2870
|
+
}
|
|
2320
2871
|
/**
|
|
2321
|
-
*
|
|
2872
|
+
* Load secrets hash state for one workspace and application from disk.
|
|
2873
|
+
* @param scope - Workspace and application identity for the deployment
|
|
2874
|
+
* @returns Persisted state, or empty state if the scope is missing or the file is invalid
|
|
2875
|
+
*/
|
|
2876
|
+
function loadSecretsState(scope) {
|
|
2877
|
+
if (!scope.applicationId) return { vaults: {} };
|
|
2878
|
+
return loadPersistedSecretsState(scope)?.state ?? { vaults: {} };
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Save secrets hash state for one workspace and application to disk.
|
|
2882
|
+
* @param scope - Workspace and application identity for the deployment
|
|
2322
2883
|
* @param state - The secrets state to persist
|
|
2323
2884
|
*/
|
|
2324
|
-
function saveSecretsState(state) {
|
|
2325
|
-
|
|
2885
|
+
function saveSecretsState(scope, state) {
|
|
2886
|
+
if (!scope.applicationId) return;
|
|
2887
|
+
const filePath = getSecretsStatePath(scope);
|
|
2326
2888
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2327
|
-
writeFileSync(filePath, JSON.stringify(
|
|
2889
|
+
writeFileSync(filePath, JSON.stringify({
|
|
2890
|
+
version: 1,
|
|
2891
|
+
workspaceId: scope.workspaceId,
|
|
2892
|
+
applicationKey: applicationStateKey(scope),
|
|
2893
|
+
state
|
|
2894
|
+
}, null, 2), "utf-8");
|
|
2328
2895
|
}
|
|
2329
2896
|
/**
|
|
2330
2897
|
* Compute SHA-256 hex digest of a value.
|
|
@@ -2390,6 +2957,11 @@ function buildUpdateMask(existing, desired, secretChanged) {
|
|
|
2390
2957
|
* @returns Planned changes for auth connections
|
|
2391
2958
|
*/
|
|
2392
2959
|
async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
2960
|
+
const stateScope = {
|
|
2961
|
+
workspaceId,
|
|
2962
|
+
applicationId: appId,
|
|
2963
|
+
applicationName: appName
|
|
2964
|
+
};
|
|
2393
2965
|
const changeSet = createChangeSet("Auth connections");
|
|
2394
2966
|
const conflicts = [];
|
|
2395
2967
|
const unmanaged = [];
|
|
@@ -2413,7 +2985,7 @@ async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
|
2413
2985
|
allLabels: metadata?.labels
|
|
2414
2986
|
};
|
|
2415
2987
|
}));
|
|
2416
|
-
const state = loadSecretsState();
|
|
2988
|
+
const state = loadSecretsState(stateScope);
|
|
2417
2989
|
for (const [name, config] of Object.entries(desiredConnections)) {
|
|
2418
2990
|
const existing = existingConnections[name];
|
|
2419
2991
|
const metaRequest = await buildMetaRequest({
|
|
@@ -2474,7 +3046,8 @@ async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
|
2474
3046
|
changeSet,
|
|
2475
3047
|
conflicts,
|
|
2476
3048
|
unmanaged,
|
|
2477
|
-
resourceOwners
|
|
3049
|
+
resourceOwners,
|
|
3050
|
+
stateScope
|
|
2478
3051
|
};
|
|
2479
3052
|
}
|
|
2480
3053
|
/**
|
|
@@ -2484,7 +3057,7 @@ async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
|
2484
3057
|
* @param phase - Apply phase
|
|
2485
3058
|
*/
|
|
2486
3059
|
async function applyAuthConnections(client, result, phase) {
|
|
2487
|
-
const { changeSet } = result;
|
|
3060
|
+
const { changeSet, stateScope } = result;
|
|
2488
3061
|
if (phase === "create-update") {
|
|
2489
3062
|
await Promise.all(changeSet.creates.map(async (create) => {
|
|
2490
3063
|
await client.createAuthConnection(create.request);
|
|
@@ -2498,26 +3071,29 @@ async function applyAuthConnections(client, result, phase) {
|
|
|
2498
3071
|
await Promise.all(changeSet.updates.map(async (update) => {
|
|
2499
3072
|
await client.setMetadata(update.metaRequest);
|
|
2500
3073
|
}));
|
|
2501
|
-
const
|
|
2502
|
-
if (
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
3074
|
+
const secretReplaces = changeSet.replaces.filter((replace) => replace.updateRequest.updateMask?.paths?.includes("oauth2.client_secret"));
|
|
3075
|
+
if (changeSet.creates.length > 0 || secretReplaces.length > 0) {
|
|
3076
|
+
const state = loadSecretsState(stateScope);
|
|
3077
|
+
if (!state.connections) state.connections = {};
|
|
3078
|
+
for (const create of changeSet.creates) {
|
|
3079
|
+
const conn = create.request.connection;
|
|
3080
|
+
if (conn?.config?.case === "oauth2") state.connections[create.name] = hashValue(conn.config.value.clientSecret ?? "");
|
|
3081
|
+
}
|
|
3082
|
+
for (const replace of secretReplaces) {
|
|
3083
|
+
const conn = replace.updateRequest.connection;
|
|
3084
|
+
if (conn?.config?.case === "oauth2") state.connections[replace.name] = hashValue(conn.config.value.clientSecret ?? "");
|
|
3085
|
+
}
|
|
3086
|
+
saveSecretsState(stateScope, state);
|
|
2510
3087
|
}
|
|
2511
|
-
saveSecretsState(state);
|
|
2512
3088
|
} else {
|
|
2513
3089
|
await Promise.all(changeSet.deletes.map(async (del) => {
|
|
2514
3090
|
await client.deleteAuthConnection(del.request);
|
|
2515
3091
|
}));
|
|
2516
3092
|
if (changeSet.deletes.length > 0) {
|
|
2517
|
-
const state = loadSecretsState();
|
|
3093
|
+
const state = loadSecretsState(stateScope);
|
|
2518
3094
|
if (state.connections) {
|
|
2519
3095
|
for (const del of changeSet.deletes) delete state.connections[del.name];
|
|
2520
|
-
saveSecretsState(state);
|
|
3096
|
+
saveSecretsState(stateScope, state);
|
|
2521
3097
|
}
|
|
2522
3098
|
}
|
|
2523
3099
|
}
|
|
@@ -3680,7 +4256,10 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3680
4256
|
await Promise.all([...changeSet.machineUser.creates.map((create) => client.createAuthMachineUser(create.request)), ...changeSet.machineUser.updates.map((update) => client.updateAuthMachineUser(update.request))]);
|
|
3681
4257
|
};
|
|
3682
4258
|
const applyCreateUpdateDependents = async () => {
|
|
3683
|
-
await applyAuthConnections(client, {
|
|
4259
|
+
await applyAuthConnections(client, {
|
|
4260
|
+
changeSet: changeSet.connection,
|
|
4261
|
+
stateScope: result.connectionStateScope
|
|
4262
|
+
}, "create-update");
|
|
3684
4263
|
await Promise.all([...changeSet.idpConfig.creates.map(async (create) => {
|
|
3685
4264
|
if (create.idpConfig.kind === "BuiltInIdP") assertDefined(create.request.idpConfig, "request missing idpConfig").config = await protoBuiltinIdPConfig(client, assertDefined(create.request.workspaceId, "request missing workspaceId"), create.idpConfig);
|
|
3686
4265
|
return client.createAuthIDPConfig(create.request);
|
|
@@ -3723,7 +4302,10 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3723
4302
|
await Promise.all(changeSet.tenantConfig.deletes.map((del) => client.deleteTenantConfig(del.request)));
|
|
3724
4303
|
await Promise.all(changeSet.userProfileConfig.deletes.map((del) => client.deleteUserProfileConfig(del.request)));
|
|
3725
4304
|
await Promise.all(changeSet.idpConfig.deletes.map((del) => client.deleteAuthIDPConfig(del.request)));
|
|
3726
|
-
await applyAuthConnections(client, {
|
|
4305
|
+
await applyAuthConnections(client, {
|
|
4306
|
+
changeSet: changeSet.connection,
|
|
4307
|
+
stateScope: result.connectionStateScope
|
|
4308
|
+
}, "delete-resources");
|
|
3727
4309
|
} else if (phase === "delete-services") await Promise.all(changeSet.service.deletes.map((del) => client.deleteAuthService(del.request)));
|
|
3728
4310
|
}
|
|
3729
4311
|
/**
|
|
@@ -3767,7 +4349,8 @@ async function planAuth(context) {
|
|
|
3767
4349
|
},
|
|
3768
4350
|
conflicts: [...conflicts, ...connectionResult.conflicts],
|
|
3769
4351
|
unmanaged: [...unmanaged, ...connectionResult.unmanaged],
|
|
3770
|
-
resourceOwners: /* @__PURE__ */ new Set([...resourceOwners, ...connectionResult.resourceOwners])
|
|
4352
|
+
resourceOwners: /* @__PURE__ */ new Set([...resourceOwners, ...connectionResult.resourceOwners]),
|
|
4353
|
+
connectionStateScope: connectionResult.stateScope
|
|
3771
4354
|
};
|
|
3772
4355
|
}
|
|
3773
4356
|
async function planServices$2(client, workspaceId, appName, appId, auths, forceApplyAll = false) {
|
|
@@ -5643,7 +6226,12 @@ async function planSecretManager(context) {
|
|
|
5643
6226
|
getName: (resource) => resource.name,
|
|
5644
6227
|
getTrn: (name) => resourceTrn(workspaceId, "vault", name)
|
|
5645
6228
|
});
|
|
5646
|
-
const
|
|
6229
|
+
const stateScope = {
|
|
6230
|
+
workspaceId,
|
|
6231
|
+
applicationId: application.id,
|
|
6232
|
+
applicationName: application.name
|
|
6233
|
+
};
|
|
6234
|
+
const state = loadSecretsState(stateScope);
|
|
5647
6235
|
const skippedSecrets = [];
|
|
5648
6236
|
await Promise.all(secretVaults.map(async (vault) => {
|
|
5649
6237
|
const vaultName = vault.vaultName;
|
|
@@ -5752,7 +6340,8 @@ async function planSecretManager(context) {
|
|
|
5752
6340
|
skippedSecrets,
|
|
5753
6341
|
conflicts,
|
|
5754
6342
|
unmanaged,
|
|
5755
|
-
resourceOwners
|
|
6343
|
+
resourceOwners,
|
|
6344
|
+
stateScope
|
|
5756
6345
|
};
|
|
5757
6346
|
}
|
|
5758
6347
|
/**
|
|
@@ -5760,11 +6349,11 @@ async function planSecretManager(context) {
|
|
|
5760
6349
|
* @param client - Operator client instance
|
|
5761
6350
|
* @param result - Planned secret changes
|
|
5762
6351
|
* @param phase - Apply phase
|
|
5763
|
-
* @param application - Application
|
|
6352
|
+
* @param application - Application used for ownership metadata and hash state persistence
|
|
5764
6353
|
* @returns Promise that resolves when secret changes are applied
|
|
5765
6354
|
*/
|
|
5766
6355
|
async function applySecretManager(client, result, phase = "create-update", application) {
|
|
5767
|
-
const { vaultChangeSet, secretChangeSet } = result;
|
|
6356
|
+
const { vaultChangeSet, secretChangeSet, stateScope } = result;
|
|
5768
6357
|
if (phase === "create-update") {
|
|
5769
6358
|
await Promise.all(vaultChangeSet.creates.map(async (create) => {
|
|
5770
6359
|
await client.createSecretManagerVault(vaultCreateRequest(create));
|
|
@@ -5787,13 +6376,14 @@ async function applySecretManager(client, result, phase = "create-update", appli
|
|
|
5787
6376
|
}));
|
|
5788
6377
|
await Promise.all(secretChangeSet.creates.map((create) => client.createSecretManagerSecret(secretCreateRequest(create))));
|
|
5789
6378
|
await Promise.all(secretChangeSet.updates.map((update) => client.updateSecretManagerSecret(secretUpdateRequest(update))));
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
6379
|
+
const secretHashUpdates = [...secretChangeSet.creates, ...secretChangeSet.updates];
|
|
6380
|
+
if (application && secretHashUpdates.length > 0) {
|
|
6381
|
+
const state = loadSecretsState(stateScope);
|
|
6382
|
+
for (const secret of secretHashUpdates) {
|
|
6383
|
+
if (!Object.hasOwn(state.vaults, secret.vaultName)) state.vaults[secret.vaultName] = {};
|
|
6384
|
+
assertDefined(state.vaults[secret.vaultName], "vault state entry missing")[secret.secretName] = hashValue(secret.value);
|
|
5795
6385
|
}
|
|
5796
|
-
saveSecretsState(state);
|
|
6386
|
+
saveSecretsState(stateScope, state);
|
|
5797
6387
|
}
|
|
5798
6388
|
} else {
|
|
5799
6389
|
await Promise.all(secretChangeSet.deletes.map((del) => client.deleteSecretManagerSecret({
|
|
@@ -5806,13 +6396,13 @@ async function applySecretManager(client, result, phase = "create-update", appli
|
|
|
5806
6396
|
secretmanagerVaultName: del.name
|
|
5807
6397
|
})));
|
|
5808
6398
|
if (secretChangeSet.deletes.length > 0 || vaultChangeSet.deletes.length > 0) {
|
|
5809
|
-
const state = loadSecretsState();
|
|
6399
|
+
const state = loadSecretsState(stateScope);
|
|
5810
6400
|
for (const del of secretChangeSet.deletes) if (Object.hasOwn(state.vaults, del.vaultName)) {
|
|
5811
6401
|
delete assertDefined(state.vaults[del.vaultName], "vault state entry missing")[del.secretName];
|
|
5812
6402
|
if (Object.keys(assertDefined(state.vaults[del.vaultName], "vault state entry missing")).length === 0) delete state.vaults[del.vaultName];
|
|
5813
6403
|
}
|
|
5814
6404
|
for (const del of vaultChangeSet.deletes) delete state.vaults[del.name];
|
|
5815
|
-
saveSecretsState(state);
|
|
6405
|
+
saveSecretsState(stateScope, state);
|
|
5816
6406
|
}
|
|
5817
6407
|
}
|
|
5818
6408
|
}
|
|
@@ -10311,11 +10901,11 @@ async function registerJobFunctions(client, changeSet, appName, appId, unchanged
|
|
|
10311
10901
|
const allUsedJobNames = /* @__PURE__ */ new Set();
|
|
10312
10902
|
unchangedWorkflowJobNames.forEach((jobName) => allUsedJobNames.add(jobName));
|
|
10313
10903
|
for (const item of [...changeSet.creates, ...changeSet.updates]) for (const jobName of item.usedJobNames) allUsedJobNames.add(jobName);
|
|
10314
|
-
const existingJobFunctions = await fetchAll(async (pageToken,
|
|
10904
|
+
const existingJobFunctions = await fetchAll(async (pageToken, maxPageSize) => {
|
|
10315
10905
|
const response = await client.listWorkflowJobFunctions({
|
|
10316
10906
|
workspaceId,
|
|
10317
10907
|
pageToken,
|
|
10318
|
-
pageSize:
|
|
10908
|
+
pageSize: maxPageSize
|
|
10319
10909
|
});
|
|
10320
10910
|
return [response.jobFunctions.map((j) => j.name), response.nextPageToken];
|
|
10321
10911
|
});
|
|
@@ -10495,11 +11085,11 @@ async function planWorkflow(client, workspaceId, appName, appId, workflows, main
|
|
|
10495
11085
|
}
|
|
10496
11086
|
async function planWorkflowJobFunctionDeletes(params) {
|
|
10497
11087
|
const { client, workspaceId, appName, appId, retainedWorkflowJobNames } = params;
|
|
10498
|
-
const existingJobFunctions = await fetchAll(async (pageToken,
|
|
11088
|
+
const existingJobFunctions = await fetchAll(async (pageToken, maxPageSize) => {
|
|
10499
11089
|
const response = await client.listWorkflowJobFunctions({
|
|
10500
11090
|
workspaceId,
|
|
10501
11091
|
pageToken,
|
|
10502
|
-
pageSize:
|
|
11092
|
+
pageSize: maxPageSize
|
|
10503
11093
|
});
|
|
10504
11094
|
return [response.jobFunctions.map((jobFunction) => jobFunction.name), response.nextPageToken];
|
|
10505
11095
|
});
|
|
@@ -11118,13 +11708,14 @@ function validateItems(params) {
|
|
|
11118
11708
|
*
|
|
11119
11709
|
* Collections not validated: idp client, tailorDB gqlPermission, functionRegistry — no
|
|
11120
11710
|
* buf.validate annotations.
|
|
11121
|
-
* Application cors and IdP userAuthPolicy.allowedReturnOrigins receive
|
|
11122
|
-
* handling: static-website URL placeholders are resolved at apply time, so the
|
|
11711
|
+
* Application cors, AIGateway cors, and IdP userAuthPolicy.allowedReturnOrigins receive
|
|
11712
|
+
* special handling: static-website URL placeholders are resolved at apply time, so the
|
|
11123
11713
|
* relevant origin/URL constraints would false-positive on `<name>:url` entries
|
|
11124
|
-
* here. Application cors
|
|
11125
|
-
*
|
|
11126
|
-
*
|
|
11127
|
-
*
|
|
11714
|
+
* here. Application cors and AIGateway cors are dropped entirely, forgoing their
|
|
11715
|
+
* per-item URL/origin constraints for the whole list rather than substituting
|
|
11716
|
+
* placeholders; IdP `allowedReturnOrigins` instead substitutes placeholder entries
|
|
11717
|
+
* with a dummy origin so the per-item regex and the cross-field `enable_mfa requires
|
|
11718
|
+
* ≥1 origin` rule still get exercised on the rest of the payload.
|
|
11128
11719
|
* Workflow jobFunctions map excluded: versions are registered at apply time (registerJobFunctions)
|
|
11129
11720
|
* and the map field carries no min_items constraint. Job names are validated separately via
|
|
11130
11721
|
* CreateWorkflowJobFunctionRequestSchema using usedJobNames from the workflow change set.
|
|
@@ -11134,7 +11725,7 @@ function validateItems(params) {
|
|
|
11134
11725
|
* @param input - Plan results from the plan phase
|
|
11135
11726
|
*/
|
|
11136
11727
|
async function validatePlan(input) {
|
|
11137
|
-
const { tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow, workflowExecutionPolicy, secretManager } = input;
|
|
11728
|
+
const { tailorDB, staticWebsite, aiGateway, idp, auth, pipeline, app, executor, workflow, workflowExecutionPolicy, secretManager } = input;
|
|
11138
11729
|
const validator = createValidator();
|
|
11139
11730
|
const violations = [];
|
|
11140
11731
|
function creates(schema, kind, items) {
|
|
@@ -11187,6 +11778,20 @@ async function validatePlan(input) {
|
|
|
11187
11778
|
creates(CreateStaticWebsiteRequestSchema, "StaticWebsite", staticWebsite.changeSet.creates);
|
|
11188
11779
|
updates(UpdateStaticWebsiteRequestSchema, "StaticWebsite", staticWebsite.changeSet.updates);
|
|
11189
11780
|
creates(AddCustomDomainRequestSchema, "StaticWebsite custom domain", staticWebsite.customDomainChangeSet.creates);
|
|
11781
|
+
creates(CreateAIGatewayRequestSchema, "AIGateway", aiGateway.changeSet.creates.map((item) => ({
|
|
11782
|
+
name: item.name,
|
|
11783
|
+
request: {
|
|
11784
|
+
...item.request,
|
|
11785
|
+
cors: void 0
|
|
11786
|
+
}
|
|
11787
|
+
})));
|
|
11788
|
+
updates(UpdateAIGatewayRequestSchema, "AIGateway", aiGateway.changeSet.updates.map((item) => ({
|
|
11789
|
+
name: item.name,
|
|
11790
|
+
request: {
|
|
11791
|
+
...item.request,
|
|
11792
|
+
cors: void 0
|
|
11793
|
+
}
|
|
11794
|
+
})));
|
|
11190
11795
|
const placeholderOriginReplacement = "https://placeholder.invalid";
|
|
11191
11796
|
const substituteIdpReturnOrigins = (item) => {
|
|
11192
11797
|
const request = item.request;
|
|
@@ -11972,50 +12577,25 @@ function deploymentPlanResults(deployment) {
|
|
|
11972
12577
|
const { application: _application, ...results } = deployment;
|
|
11973
12578
|
return results;
|
|
11974
12579
|
}
|
|
12580
|
+
/**
|
|
12581
|
+
* Every `PlanResults` entry except `app` carries ownership-tracking fields;
|
|
12582
|
+
* deriving the list from `results` itself (instead of naming each key) keeps
|
|
12583
|
+
* these collectors in sync with `PlannedDeployment` as resource types are added.
|
|
12584
|
+
* @param results - Plan results for a single deployment
|
|
12585
|
+
* @returns The ownership-tracking plan entries, `app` excluded
|
|
12586
|
+
*/
|
|
12587
|
+
function ownershipTrackedPlans(results) {
|
|
12588
|
+
const { app: _app, ...rest } = results;
|
|
12589
|
+
return Object.values(rest);
|
|
12590
|
+
}
|
|
11975
12591
|
function collectOwnerConflicts(results) {
|
|
11976
|
-
return
|
|
11977
|
-
...results.functionRegistry.conflicts,
|
|
11978
|
-
...results.tailorDB.conflicts,
|
|
11979
|
-
...results.staticWebsite.conflicts,
|
|
11980
|
-
...results.aiGateway.conflicts,
|
|
11981
|
-
...results.idp.conflicts,
|
|
11982
|
-
...results.auth.conflicts,
|
|
11983
|
-
...results.pipeline.conflicts,
|
|
11984
|
-
...results.executor.conflicts,
|
|
11985
|
-
...results.workflow.conflicts,
|
|
11986
|
-
...results.workflowExecutionPolicy.conflicts,
|
|
11987
|
-
...results.secretManager.conflicts
|
|
11988
|
-
];
|
|
12592
|
+
return ownershipTrackedPlans(results).flatMap((plan) => plan.conflicts);
|
|
11989
12593
|
}
|
|
11990
12594
|
function collectUnmanagedResources(results) {
|
|
11991
|
-
return
|
|
11992
|
-
...results.functionRegistry.unmanaged,
|
|
11993
|
-
...results.tailorDB.unmanaged,
|
|
11994
|
-
...results.staticWebsite.unmanaged,
|
|
11995
|
-
...results.aiGateway.unmanaged,
|
|
11996
|
-
...results.idp.unmanaged,
|
|
11997
|
-
...results.auth.unmanaged,
|
|
11998
|
-
...results.pipeline.unmanaged,
|
|
11999
|
-
...results.executor.unmanaged,
|
|
12000
|
-
...results.workflow.unmanaged,
|
|
12001
|
-
...results.workflowExecutionPolicy.unmanaged,
|
|
12002
|
-
...results.secretManager.unmanaged
|
|
12003
|
-
];
|
|
12595
|
+
return ownershipTrackedPlans(results).flatMap((plan) => plan.unmanaged);
|
|
12004
12596
|
}
|
|
12005
12597
|
function collectResourceOwners(results) {
|
|
12006
|
-
return
|
|
12007
|
-
...results.functionRegistry.resourceOwners,
|
|
12008
|
-
...results.tailorDB.resourceOwners,
|
|
12009
|
-
...results.staticWebsite.resourceOwners,
|
|
12010
|
-
...results.aiGateway.resourceOwners,
|
|
12011
|
-
...results.idp.resourceOwners,
|
|
12012
|
-
...results.auth.resourceOwners,
|
|
12013
|
-
...results.pipeline.resourceOwners,
|
|
12014
|
-
...results.executor.resourceOwners,
|
|
12015
|
-
...results.workflow.resourceOwners,
|
|
12016
|
-
...results.workflowExecutionPolicy.resourceOwners,
|
|
12017
|
-
...results.secretManager.resourceOwners
|
|
12018
|
-
]);
|
|
12598
|
+
return new Set(ownershipTrackedPlans(results).flatMap((plan) => [...plan.resourceOwners]));
|
|
12019
12599
|
}
|
|
12020
12600
|
function collectImportantResourceDeletions(results) {
|
|
12021
12601
|
const importantDeletions = [];
|
|
@@ -16430,62 +17010,67 @@ async function execRemove(client, workspaceId, application, config, confirm) {
|
|
|
16430
17010
|
forRemoval: true,
|
|
16431
17011
|
config
|
|
16432
17012
|
};
|
|
16433
|
-
const
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16442
|
-
|
|
16443
|
-
|
|
17013
|
+
const plans = {
|
|
17014
|
+
tailorDB: await planTailorDB(ctx),
|
|
17015
|
+
staticWebsite: await planStaticWebsite(ctx),
|
|
17016
|
+
aiGateway: await planAIGateway(ctx),
|
|
17017
|
+
idp: await planIdP(ctx),
|
|
17018
|
+
auth: await planAuth(ctx),
|
|
17019
|
+
pipeline: await planPipeline(ctx),
|
|
17020
|
+
app: await planApplication(ctx),
|
|
17021
|
+
executor: await planExecutor(ctx),
|
|
17022
|
+
workflow: await planWorkflow(client, workspaceId, application.name, application.id, {}, {}),
|
|
17023
|
+
workflowExecutionPolicy: await planWorkflowJobFunctionExecutionPolicy(client, workspaceId, application.name, application.id, {}),
|
|
17024
|
+
functionRegistry: await planFunctionRegistry(client, workspaceId, application.name, application.id, []),
|
|
17025
|
+
secretManager: await planSecretManager(ctx)
|
|
17026
|
+
};
|
|
16444
17027
|
const removeLines = [
|
|
16445
|
-
...functionRegistry.changeSet.lines(),
|
|
16446
|
-
...staticWebsite.changeSet.lines(),
|
|
16447
|
-
...aiGateway.changeSet.lines(),
|
|
16448
|
-
...app.lines(),
|
|
16449
|
-
...tailorDB.changeSet.service.lines(),
|
|
16450
|
-
...tailorDB.changeSet.type.lines(),
|
|
16451
|
-
...tailorDB.changeSet.gqlPermission.lines(),
|
|
16452
|
-
...pipeline.changeSet.service.lines(),
|
|
16453
|
-
...pipeline.changeSet.resolver.lines(),
|
|
16454
|
-
...executor.changeSet.lines(),
|
|
16455
|
-
...workflow.changeSet.lines(),
|
|
16456
|
-
...
|
|
16457
|
-
...idp.changeSet.
|
|
16458
|
-
...
|
|
16459
|
-
...auth.changeSet.
|
|
16460
|
-
...auth.changeSet.
|
|
16461
|
-
...auth.changeSet.
|
|
16462
|
-
...auth.changeSet.
|
|
16463
|
-
...auth.changeSet.
|
|
16464
|
-
...auth.changeSet.
|
|
16465
|
-
...auth.changeSet.
|
|
16466
|
-
...auth.changeSet.
|
|
16467
|
-
...auth.changeSet.
|
|
16468
|
-
...
|
|
16469
|
-
...secretManager.
|
|
17028
|
+
...plans.functionRegistry.changeSet.lines(),
|
|
17029
|
+
...plans.staticWebsite.changeSet.lines(),
|
|
17030
|
+
...plans.aiGateway.changeSet.lines(),
|
|
17031
|
+
...plans.app.lines(),
|
|
17032
|
+
...plans.tailorDB.changeSet.service.lines(),
|
|
17033
|
+
...plans.tailorDB.changeSet.type.lines(),
|
|
17034
|
+
...plans.tailorDB.changeSet.gqlPermission.lines(),
|
|
17035
|
+
...plans.pipeline.changeSet.service.lines(),
|
|
17036
|
+
...plans.pipeline.changeSet.resolver.lines(),
|
|
17037
|
+
...plans.executor.changeSet.lines(),
|
|
17038
|
+
...plans.workflow.changeSet.lines(),
|
|
17039
|
+
...plans.workflowExecutionPolicy.changeSet.lines(),
|
|
17040
|
+
...plans.idp.changeSet.service.lines(),
|
|
17041
|
+
...plans.idp.changeSet.client.lines(),
|
|
17042
|
+
...plans.auth.changeSet.service.lines(),
|
|
17043
|
+
...plans.auth.changeSet.idpConfig.lines(),
|
|
17044
|
+
...plans.auth.changeSet.userProfileConfig.lines(),
|
|
17045
|
+
...plans.auth.changeSet.tenantConfig.lines(),
|
|
17046
|
+
...plans.auth.changeSet.machineUser.lines(),
|
|
17047
|
+
...plans.auth.changeSet.oauth2Client.lines(),
|
|
17048
|
+
...plans.auth.changeSet.authHook.lines(),
|
|
17049
|
+
...plans.auth.changeSet.scim.lines(),
|
|
17050
|
+
...plans.auth.changeSet.scimResource.lines(),
|
|
17051
|
+
...plans.auth.changeSet.connection.lines(),
|
|
17052
|
+
...plans.secretManager.vaultChangeSet.lines(),
|
|
17053
|
+
...plans.secretManager.secretChangeSet.lines()
|
|
16470
17054
|
];
|
|
16471
17055
|
if (removeLines.length > 0) logger.log(removeLines.join("\n"));
|
|
16472
|
-
if (tailorDB.changeSet.service.deletes.length === 0 && staticWebsite.changeSet.deletes.length === 0 && aiGateway.changeSet.deletes.length === 0 && idp.changeSet.service.deletes.length === 0 && auth.changeSet.service.deletes.length === 0 && pipeline.changeSet.service.deletes.length === 0 && app.deletes.length === 0 && executor.changeSet.deletes.length === 0 && workflow.changeSet.deletes.length === 0 && functionRegistry.changeSet.deletes.length === 0 && secretManager.vaultChangeSet.deletes.length === 0 && secretManager.secretChangeSet.deletes.length === 0) return;
|
|
17056
|
+
if (plans.tailorDB.changeSet.service.deletes.length === 0 && plans.staticWebsite.changeSet.deletes.length === 0 && plans.aiGateway.changeSet.deletes.length === 0 && plans.idp.changeSet.service.deletes.length === 0 && plans.auth.changeSet.service.deletes.length === 0 && plans.pipeline.changeSet.service.deletes.length === 0 && plans.app.deletes.length === 0 && plans.executor.changeSet.deletes.length === 0 && plans.workflow.changeSet.deletes.length === 0 && plans.workflowExecutionPolicy.changeSet.deletes.length === 0 && plans.functionRegistry.changeSet.deletes.length === 0 && plans.secretManager.vaultChangeSet.deletes.length === 0 && plans.secretManager.secretChangeSet.deletes.length === 0) return;
|
|
16473
17057
|
if (confirm) await confirm();
|
|
16474
|
-
await applyWorkflow(client, workflow, "delete");
|
|
16475
|
-
await
|
|
16476
|
-
await
|
|
16477
|
-
await
|
|
16478
|
-
await
|
|
16479
|
-
await
|
|
16480
|
-
await applyPipeline(client, pipeline, "delete-
|
|
16481
|
-
await
|
|
16482
|
-
await applyAuth(client, auth, "delete-
|
|
16483
|
-
await
|
|
16484
|
-
await applyIdP(client, idp, "delete-
|
|
16485
|
-
await
|
|
16486
|
-
await applyTailorDB(client, tailorDB, "delete-
|
|
16487
|
-
await
|
|
16488
|
-
await
|
|
17058
|
+
await applyWorkflow(client, plans.workflow, "delete");
|
|
17059
|
+
await applyWorkflowJobFunctionExecutionPolicy(client, plans.workflowExecutionPolicy, "delete");
|
|
17060
|
+
await applyExecutor(client, plans.executor, "delete");
|
|
17061
|
+
await applyStaticWebsite(client, plans.staticWebsite, "delete");
|
|
17062
|
+
await applyAIGateway(client, plans.aiGateway, "delete");
|
|
17063
|
+
await applyApplication(client, plans.app, "delete");
|
|
17064
|
+
await applyPipeline(client, plans.pipeline, "delete-resources");
|
|
17065
|
+
await applyPipeline(client, plans.pipeline, "delete-services");
|
|
17066
|
+
await applyAuth(client, plans.auth, "delete-resources");
|
|
17067
|
+
await applyAuth(client, plans.auth, "delete-services");
|
|
17068
|
+
await applyIdP(client, plans.idp, "delete-resources");
|
|
17069
|
+
await applyIdP(client, plans.idp, "delete-services");
|
|
17070
|
+
await applyTailorDB(client, plans.tailorDB, "delete-resources");
|
|
17071
|
+
await applyTailorDB(client, plans.tailorDB, "delete-services");
|
|
17072
|
+
await applyFunctionRegistry(client, workspaceId, plans.functionRegistry, "delete");
|
|
17073
|
+
await applySecretManager(client, plans.secretManager, "delete");
|
|
16489
17074
|
}
|
|
16490
17075
|
/**
|
|
16491
17076
|
* Remove all resources managed by the current application.
|
|
@@ -17293,7 +17878,7 @@ async function generate(options) {
|
|
|
17293
17878
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
17294
17879
|
let pluginManager;
|
|
17295
17880
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
17296
|
-
const { defineApplication } = await import("./application-
|
|
17881
|
+
const { defineApplication } = await import("./application-6zsDLGKv.mjs");
|
|
17297
17882
|
const application = defineApplication({
|
|
17298
17883
|
config,
|
|
17299
17884
|
pluginManager
|
|
@@ -19560,5 +20145,5 @@ function isDeno() {
|
|
|
19560
20145
|
}
|
|
19561
20146
|
|
|
19562
20147
|
//#endregion
|
|
19563
|
-
export { getOrganization as $, compareSnapshotWithRemote as $t, listCommand$3 as A,
|
|
19564
|
-
//# sourceMappingURL=runtime-
|
|
20148
|
+
export { getOrganization as $, multiConfigArg as $n, compareSnapshotWithRemote as $t, listCommand$3 as A, apiCall as An, getExecutorWaitFailureMessage as At, show as B, loadStoredUserTokens as Bn, listWorkflowExecutions as Bt, listCommand$2 as C, hasChanges as Cn, listWebhookExecutors as Ct, waitWorkflowExecution as D, PluginManager as Dn, listCommand$9 as Dt, waitCommand as E, sdkNameLabelKey as En, triggerExecutor as Et, generateCommand as F, hasAnyUserTokenEntry as Fn, startWorkflow as Ft, removeCommand$1 as G, saveUserTokens as Gn, deploy as Gt, extractOwnedNamespaces as H, platformConfigFromProfile as Hn, formatKeyValueTable as Ht, generateMigrationScript as I, hasUserTokenEntry as In, getCommand$5 as It, organizationTree as J, commonArgs as Jn, waitForExecution as Jt, updateCommand$1 as K, writePlatformConfig as Kn, ensureConfigId as Kt, writeDbTypesFile as L, loadAccessToken as Ln, getWorkflow as Lt, truncate as M, loadConfig as Mn, listExecutorJobs as Mt, truncateCommand as N, deleteUserTokens as Nn, watchExecutorJob as Nt, resumeCommand as O, generateUserTypes as On, listExecutors as Ot, generate as P, fetchLatestToken as Pn, startCommand as Pt, getCommand$1 as Q, isVerbose as Qn, parseMigrationLabelNumber as Qt, getConfiguredEditorCommand as R, loadConsoleBaseUrl as Rn, executionsCommand as Rt, listApps as S, formatMigrationDiff as Sn, getFunctionRegistry as St, healthCommand as T, resourceTrn as Tn, triggerCommand as Tt, logBetaWarning as U, readPlatformConfig as Un, getCommand$6 as Ut, showCommand as V, loadWorkspaceId as Vn, functionExecutionStatusToString as Vt, remove as W, resolveUserTokenKey as Wn, getExecutor as Wt, listCommand$4 as X, confirmationArgs as Xn, MIGRATION_LABEL_KEY as Xt, treeCommand as Y, configArg as Yn, bundleMigrationScript as Yt, listOrganizations as Z, deploymentArgs as Zn, handleOptionalToRequiredError as Zt, getWorkspace as _, loadDiff as _n, listMachineUsers as _t, updateUser as a, MIGRATE_FILE_NAME as an, getFolder as at, createCommand as b, parseMigrationNumberArg as bn, listFunctionRegistries as bt, listCommand as c, compareLocalTypesWithSnapshot as cn, createCommand$1 as ct, inviteUser as d, getLatestMigrationNumber as dn, listOAuth2Clients as dt, generateAllTypeManifestsFromSnapshot as en, pagedLogArgs as er, updateCommand$2 as et, restoreCommand as f, getMigrationDirPath as fn, getCommand$3 as ft, getCommand as g, isValidMigrationNumber as gn, listCommand$7 as gt, listWorkspaces as h, getNextMigrationNumber as hn, tokenCommand as ht, updateCommand as i, INITIAL_SCHEMA_NUMBER as in, workspaceArgs as ir, getCommand$2 as it, listWorkflows as j, assertWritable as jn, jobsCommand as jt, resumeWorkflow as k, prompt as kn, getExecutorJob as kt, listUsers as l, compareSnapshots as ln, createFolder as lt, listCommand$1 as m, getMigrationFiles as mn, getMachineUserToken as mt, query as n, DB_TYPES_FILE_NAME as nn, resolveMachineUserInputSource as nr, listCommand$5 as nt, removeCommand as o, SCHEMA_FILE_NAME as on, deleteCommand$1 as ot, restoreWorkspace as p, getMigrationFilePath as pn, getOAuth2Client as pt, updateOrganization as q, defineAppCommand as qn, executeScript as qt, queryCommand as r, DIFF_FILE_NAME as rn, toPageDirection as rr, listFolders as rt, removeUser as s, assertValidMigrationFiles as sn, deleteFolder as st, isNativeTypeScriptRuntime as t, protoGqlPermission as tn, paginationArgs as tr, updateFolder as tt, inviteCommand as u, createSnapshotFromLocalTypes as un, listCommand$6 as ut, deleteCommand as v, reconstructSnapshotFromMigrations as vn, generate$1 as vt, getAppHealth as w, getNamespacesWithMigrations as wn, webhookCommand as wt, createWorkspace as x, formatDiffSummary as xn, getCommand$4 as xt, deleteWorkspace as y, formatMigrationNumber as yn, listCommand$8 as yt, openInConfiguredEditor as z, loadMachineUserName as zn, getWorkflowExecution as zt };
|
|
20149
|
+
//# sourceMappingURL=runtime-DlklyuHN.mjs.map
|