@tailor-platform/sdk 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @tailor-platform/sdk
2
2
 
3
+ ## 0.18.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#174](https://github.com/tailor-platform/sdk/pull/174) [`67483e1`](https://github.com/tailor-platform/sdk/commit/67483e19128b19bb26eea3a99e7f23f304255f14) Thanks [@riku99](https://github.com/riku99)! - Add staticwebsite deploy command to the tailor-sdk CLI for static web hosting
8
+
9
+ Add staticwebsite get command to inspect static website details (name, URL, allowed IP addresses, etc.) from the CLI.
10
+
11
+ Add staticwebsite list command to list static websites (including workspace ID, URL, and allowed IP address count) from the CLI.
12
+
3
13
  ## 0.18.0
4
14
 
5
15
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  /// <reference path="./../user-defined.d.ts" />
2
2
 
3
- import { AppConfig, CodeGeneratorBase, Executor, Generator, IdProviderConfig, OAuth2ClientInput, Resolver, TailorDBTypeConfig } from "../types-DgaCdTug.mjs";
3
+ import { AppConfig, CodeGeneratorBase, Executor, Generator, IdProviderConfig, OAuth2ClientInput, Resolver, TailorDBTypeConfig } from "../types-Dz5wcR2h.mjs";
4
4
  import "citty";
5
5
  import { z } from "zod";
6
6
  import "@bufbuild/protobuf/wkt";
package/dist/cli/api.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { apply, createWorkspace, deleteWorkspace, generate, generateUserTypes, getMachineUserToken, getOAuth2Client, getWorkflow, getWorkflowExecution, listMachineUsers, listOAuth2Clients, listWorkflowExecutions, listWorkflows, listWorkspaces, loadAccessToken, loadConfig, loadWorkspaceId, remove, resumeWorkflow, show, startWorkflow } from "../resume-ChDChtAZ.mjs";
1
+ import { apply, createWorkspace, deleteWorkspace, generate, generateUserTypes, getMachineUserToken, getOAuth2Client, getWorkflow, getWorkflowExecution, listMachineUsers, listOAuth2Clients, listWorkflowExecutions, listWorkflows, listWorkspaces, loadAccessToken, loadConfig, loadWorkspaceId, remove, resumeWorkflow, show, startWorkflow } from "../resume-8Y9mmXHa.mjs";
2
2
  import "../job-CL8myeqs.mjs";
3
3
  import { register } from "node:module";
4
4
 
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { PATScope, applyCommand, commonArgs, createCommand, deleteCommand, executionsCommand, fetchAll, fetchLatestToken, fetchUserInfo, generateCommand, getCommand, getCommand$1, initOAuth2Client, initOperatorClient, jsonArgs, listCommand as listCommand$1, listCommand$1 as listCommand$6, listCommand$2 as listCommand$7, listCommand$3 as listCommand, loadAccessToken, loadConfig, loadWorkspaceId, printData, readPackageJson, readPlatformConfig, removeCommand, resumeCommand, showCommand, startCommand, tokenCommand, withCommonArgs, writePlatformConfig } from "../resume-ChDChtAZ.mjs";
2
+ import { PATScope, applyCommand, commonArgs, createCommand, deleteCommand, executionsCommand, fetchAll, fetchLatestToken, fetchUserInfo, generateCommand, getCommand, getCommand$1 as getCommand$2, initOAuth2Client, initOperatorClient, jsonArgs, listCommand as listCommand$1, listCommand$1 as listCommand$7, listCommand$2 as listCommand$8, listCommand$3 as listCommand, loadAccessToken, loadConfig, loadWorkspaceId, printData, readPackageJson, readPlatformConfig, removeCommand, resumeCommand, showCommand, startCommand, tokenCommand, withCommonArgs, writePlatformConfig } from "../resume-8Y9mmXHa.mjs";
3
3
  import "../job-CL8myeqs.mjs";
4
4
  import { register } from "node:module";
5
5
  import { defineCommand, runCommand, runMain } from "citty";
6
+ import * as path from "path";
7
+ import * as fs from "fs";
8
+ import pLimit from "p-limit";
6
9
  import * as crypto from "node:crypto";
7
10
  import ml from "multiline-ts";
8
- import { consola as consola$1 } from "consola";
11
+ import consola, { consola as consola$1, createConsola } from "consola";
9
12
  import { generateCodeVerifier } from "@badgateway/oauth2-client";
10
13
  import { timestampDate } from "@bufbuild/protobuf/wkt";
11
14
  import { Code, ConnectError } from "@connectrpc/connect";
@@ -13,6 +16,8 @@ import chalk from "chalk";
13
16
  import { spawnSync } from "node:child_process";
14
17
  import * as http from "node:http";
15
18
  import open from "open";
19
+ import { lookup } from "mime-types";
20
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
16
21
 
17
22
  //#region src/cli/init.ts
18
23
  const detectPackageManager = () => {
@@ -184,11 +189,11 @@ const machineuserCommand = defineCommand({
184
189
  description: "Manage machine users"
185
190
  },
186
191
  subCommands: {
187
- list: listCommand$7,
192
+ list: listCommand$8,
188
193
  token: tokenCommand
189
194
  },
190
195
  async run() {
191
- await runCommand(listCommand$7, { rawArgs: [] });
196
+ await runCommand(listCommand$8, { rawArgs: [] });
192
197
  }
193
198
  });
194
199
 
@@ -200,11 +205,11 @@ const oauth2clientCommand = defineCommand({
200
205
  description: "Manage OAuth2 clients"
201
206
  },
202
207
  subCommands: {
203
- get: getCommand$1,
204
- list: listCommand$6
208
+ get: getCommand$2,
209
+ list: listCommand$7
205
210
  },
206
211
  async run() {
207
- await runCommand(listCommand$6, { rawArgs: [] });
212
+ await runCommand(listCommand$7, { rawArgs: [] });
208
213
  }
209
214
  });
210
215
 
@@ -286,7 +291,7 @@ const deleteCommand$3 = defineCommand({
286
291
 
287
292
  //#endregion
288
293
  //#region src/cli/profile/list.ts
289
- const listCommand$5 = defineCommand({
294
+ const listCommand$6 = defineCommand({
290
295
  meta: {
291
296
  name: "list",
292
297
  description: "List all profiles"
@@ -378,11 +383,11 @@ const profileCommand = defineCommand({
378
383
  subCommands: {
379
384
  create: createCommand$3,
380
385
  delete: deleteCommand$3,
381
- list: listCommand$5,
386
+ list: listCommand$6,
382
387
  update: updateCommand$1
383
388
  },
384
389
  async run() {
385
- await runCommand(listCommand$5, { rawArgs: [] });
390
+ await runCommand(listCommand$6, { rawArgs: [] });
386
391
  }
387
392
  });
388
393
 
@@ -777,7 +782,7 @@ async function vaultList(options) {
777
782
  return [vaults, nextPageToken];
778
783
  })).map(vaultInfo);
779
784
  }
780
- const listCommand$4 = defineCommand({
785
+ const listCommand$5 = defineCommand({
781
786
  meta: {
782
787
  name: "list",
783
788
  description: "List Secret Manager vaults"
@@ -815,10 +820,10 @@ const vaultCommand = defineCommand({
815
820
  subCommands: {
816
821
  create: createCommand$2,
817
822
  delete: deleteCommand$2,
818
- list: listCommand$4
823
+ list: listCommand$5
819
824
  },
820
825
  async run() {
821
- await runCommand(listCommand$4, { rawArgs: [] });
826
+ await runCommand(listCommand$5, { rawArgs: [] });
822
827
  }
823
828
  });
824
829
 
@@ -841,6 +846,329 @@ const secretCommand = defineCommand({
841
846
  }
842
847
  });
843
848
 
849
+ //#endregion
850
+ //#region src/cli/progress.ts
851
+ function createProgress(label, total) {
852
+ let current = 0;
853
+ const update = () => {
854
+ current += 1;
855
+ const percent = Math.round(current / total * 100);
856
+ process.stdout.write(`\r${label} ${current}/${total} (${percent}%)`);
857
+ };
858
+ const finish = () => {
859
+ process.stdout.write("\n");
860
+ };
861
+ return {
862
+ update,
863
+ finish
864
+ };
865
+ }
866
+ async function withTimeout(p, ms, message) {
867
+ return await Promise.race([p, setTimeout$1(ms).then(() => {
868
+ throw new Error(message);
869
+ })]);
870
+ }
871
+
872
+ //#endregion
873
+ //#region src/cli/staticwebsite/deploy.ts
874
+ const noTimeLogger = createConsola({ formatOptions: { date: false } });
875
+ const CHUNK_SIZE = 64 * 1024;
876
+ const IGNORED_FILES = new Set([
877
+ ".DS_Store",
878
+ "thumbs.db",
879
+ "desktop.ini"
880
+ ]);
881
+ function shouldIgnoreFile(filePath) {
882
+ const fileName = path.basename(filePath).toLowerCase();
883
+ return IGNORED_FILES.has(fileName);
884
+ }
885
+ async function deployStaticWebsite(client, workspaceId, name, distDir, showProgress = true) {
886
+ const { deploymentId } = await client.createDeployment({
887
+ workspaceId,
888
+ name
889
+ });
890
+ if (!deploymentId) throw new Error("createDeployment returned empty deploymentId");
891
+ const skippedFiles = await uploadDirectory(client, workspaceId, deploymentId, distDir, showProgress);
892
+ const { url } = await client.publishDeployment({
893
+ workspaceId,
894
+ deploymentId
895
+ });
896
+ if (!url) throw new Error("publishDeployment returned empty url");
897
+ return {
898
+ url,
899
+ skippedFiles
900
+ };
901
+ }
902
+ async function uploadDirectory(client, workspaceId, deploymentId, rootDir, showProgress) {
903
+ const files = await collectFiles(rootDir);
904
+ if (files.length === 0) {
905
+ consola.warn(`No files found under ${rootDir}`);
906
+ return [];
907
+ }
908
+ const limit = pLimit(5);
909
+ const total = files.length;
910
+ const progress = showProgress ? createProgress("Uploading files", total) : void 0;
911
+ const skippedFiles = [];
912
+ await Promise.all(files.map((relativePath) => limit(async () => {
913
+ await uploadSingleFile(client, workspaceId, deploymentId, rootDir, relativePath, skippedFiles);
914
+ if (progress) progress.update();
915
+ })));
916
+ if (progress) progress.finish();
917
+ return skippedFiles;
918
+ }
919
+ async function collectFiles(rootDir, currentDir = "") {
920
+ const dirPath = path.join(rootDir, currentDir);
921
+ const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
922
+ const files = [];
923
+ for (const entry of entries) {
924
+ const rel = path.join(currentDir, entry.name);
925
+ if (entry.isDirectory()) {
926
+ const sub = await collectFiles(rootDir, rel);
927
+ files.push(...sub);
928
+ } else if (entry.isFile() && !entry.isSymbolicLink() && !shouldIgnoreFile(rel)) files.push(rel);
929
+ }
930
+ return files;
931
+ }
932
+ async function uploadSingleFile(client, workspaceId, deploymentId, rootDir, relativePath, skippedFiles) {
933
+ const absPath = path.join(rootDir, relativePath);
934
+ const filePath = relativePath.split(path.sep).join("/");
935
+ const mime = lookup(filePath);
936
+ if (!mime) {
937
+ skippedFiles.push(`${filePath} (unsupported content type; no MIME mapping found)`);
938
+ return;
939
+ }
940
+ const contentType = mime;
941
+ const readStream = fs.createReadStream(absPath, { highWaterMark: CHUNK_SIZE });
942
+ async function* requestStream() {
943
+ yield { payload: {
944
+ case: "initialMetadata",
945
+ value: {
946
+ workspaceId,
947
+ deploymentId,
948
+ filePath,
949
+ contentType
950
+ }
951
+ } };
952
+ for await (const chunk of readStream) yield { payload: {
953
+ case: "chunkData",
954
+ value: chunk
955
+ } };
956
+ }
957
+ async function uploadWithLogging() {
958
+ try {
959
+ await client.uploadFile(requestStream());
960
+ } catch (error) {
961
+ if (error instanceof ConnectError && error.code === Code.InvalidArgument) {
962
+ skippedFiles.push(`${filePath} (server rejected file as invalid: ${error.message})`);
963
+ return;
964
+ }
965
+ throw error;
966
+ }
967
+ }
968
+ await withTimeout(uploadWithLogging(), 2 * 6e4, `Upload timed out for "${filePath}"`);
969
+ }
970
+ function logSkippedFiles(skippedFiles) {
971
+ if (skippedFiles.length === 0) return;
972
+ noTimeLogger.log("⚠️WARNING: Deployment completed, but some files failed to upload. These files may have unsupported content types or other validation issues. Please review the list below:");
973
+ for (const file of skippedFiles) noTimeLogger.log(` - ${file}`);
974
+ }
975
+ const deployCommand = defineCommand({
976
+ meta: {
977
+ name: "deploy",
978
+ description: "Deploy a static website"
979
+ },
980
+ args: {
981
+ ...commonArgs,
982
+ ...jsonArgs,
983
+ "workspace-id": {
984
+ type: "string",
985
+ description: "Workspace ID",
986
+ alias: "w"
987
+ },
988
+ profile: {
989
+ type: "string",
990
+ description: "Workspace profile",
991
+ alias: "p"
992
+ },
993
+ name: {
994
+ type: "string",
995
+ description: "Static website name",
996
+ alias: "n",
997
+ required: true
998
+ },
999
+ dir: {
1000
+ type: "string",
1001
+ description: "Path to the static website files",
1002
+ alias: "d",
1003
+ required: true
1004
+ }
1005
+ },
1006
+ run: withCommonArgs(async (args) => {
1007
+ consola.info(`Deploying static website "${args.name}" from directory: ${args.dir}`);
1008
+ const accessToken = await loadAccessToken({
1009
+ useProfile: true,
1010
+ profile: args.profile
1011
+ });
1012
+ const client = await initOperatorClient(accessToken);
1013
+ const name = args.name;
1014
+ const dir = path.resolve(process.cwd(), args.dir);
1015
+ const workspaceId = args["workspace-id"];
1016
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) throw new Error(`Directory not found or not a directory: ${dir}`);
1017
+ const { url, skippedFiles } = await withTimeout(deployStaticWebsite(client, workspaceId, name, dir, !args.json), 10 * 6e4, "Deployment timed out after 10 minutes.");
1018
+ if (args.json) console.log(JSON.stringify({
1019
+ name,
1020
+ workspaceId,
1021
+ url,
1022
+ skippedFiles
1023
+ }));
1024
+ else {
1025
+ consola.success(`Static website "${name}" deployed successfully. URL: ${url}`);
1026
+ logSkippedFiles(skippedFiles);
1027
+ }
1028
+ })
1029
+ });
1030
+
1031
+ //#endregion
1032
+ //#region src/cli/staticwebsite/get.ts
1033
+ const getCommand$1 = defineCommand({
1034
+ meta: {
1035
+ name: "get",
1036
+ description: "Get static website details"
1037
+ },
1038
+ args: {
1039
+ ...commonArgs,
1040
+ ...jsonArgs,
1041
+ name: {
1042
+ type: "positional",
1043
+ description: "Static website name",
1044
+ required: true
1045
+ },
1046
+ "workspace-id": {
1047
+ type: "string",
1048
+ description: "Workspace ID",
1049
+ alias: "w"
1050
+ },
1051
+ profile: {
1052
+ type: "string",
1053
+ description: "Workspace profile",
1054
+ alias: "p"
1055
+ }
1056
+ },
1057
+ run: withCommonArgs(async (args) => {
1058
+ const accessToken = await loadAccessToken({
1059
+ useProfile: true,
1060
+ profile: args.profile
1061
+ });
1062
+ const client = await initOperatorClient(accessToken);
1063
+ const workspaceId = loadWorkspaceId({
1064
+ workspaceId: args["workspace-id"],
1065
+ profile: args.profile
1066
+ });
1067
+ const notFoundErrorMessage = `Static website "${args.name}" not found.`;
1068
+ try {
1069
+ const { staticwebsite } = await client.getStaticWebsite({
1070
+ workspaceId,
1071
+ name: args.name
1072
+ });
1073
+ if (!staticwebsite) throw new Error(notFoundErrorMessage);
1074
+ const info = {
1075
+ workspaceId,
1076
+ name: staticwebsite.name,
1077
+ description: staticwebsite.description,
1078
+ url: staticwebsite.url,
1079
+ allowedIpAddresses: args.json ? staticwebsite.allowedIpAddresses : staticwebsite.allowedIpAddresses.join("\n")
1080
+ };
1081
+ printData(info, args.json);
1082
+ } catch (error) {
1083
+ if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(notFoundErrorMessage);
1084
+ throw error;
1085
+ }
1086
+ })
1087
+ });
1088
+
1089
+ //#endregion
1090
+ //#region src/cli/staticwebsite/list.ts
1091
+ async function listStaticWebsites(options) {
1092
+ const accessToken = await loadAccessToken({
1093
+ useProfile: true,
1094
+ profile: options?.profile
1095
+ });
1096
+ const client = await initOperatorClient(accessToken);
1097
+ const workspaceId = loadWorkspaceId({
1098
+ workspaceId: options?.workspaceId,
1099
+ profile: options?.profile
1100
+ });
1101
+ return (await fetchAll(async (pageToken) => {
1102
+ const { staticwebsites, nextPageToken } = await client.listStaticWebsites({
1103
+ workspaceId,
1104
+ pageToken
1105
+ });
1106
+ return [staticwebsites, nextPageToken];
1107
+ })).map((site) => ({
1108
+ workspaceId,
1109
+ name: site.name,
1110
+ description: site.description,
1111
+ url: site.url ?? "",
1112
+ allowedIpAddresses: site.allowedIpAddresses
1113
+ }));
1114
+ }
1115
+ const listCommand$4 = defineCommand({
1116
+ meta: {
1117
+ name: "list",
1118
+ description: "List static websites"
1119
+ },
1120
+ args: {
1121
+ ...commonArgs,
1122
+ ...jsonArgs,
1123
+ "workspace-id": {
1124
+ type: "string",
1125
+ description: "Workspace ID",
1126
+ alias: "w"
1127
+ },
1128
+ profile: {
1129
+ type: "string",
1130
+ description: "Workspace profile",
1131
+ alias: "p"
1132
+ }
1133
+ },
1134
+ run: withCommonArgs(async (args) => {
1135
+ const websites = await listStaticWebsites({
1136
+ workspaceId: args["workspace-id"],
1137
+ profile: args.profile
1138
+ });
1139
+ const formatted = args.json ? websites : websites.map(({ allowedIpAddresses,...rest }) => {
1140
+ if (allowedIpAddresses.length === 0) return {
1141
+ ...rest,
1142
+ allowedIpAddresses: "No allowed IP addresses"
1143
+ };
1144
+ const count = allowedIpAddresses.length;
1145
+ const label = count === 1 ? "1 IP address" : `${count} IP addresses`;
1146
+ return {
1147
+ ...rest,
1148
+ allowedIpAddresses: label
1149
+ };
1150
+ });
1151
+ printData(formatted, args.json);
1152
+ })
1153
+ });
1154
+
1155
+ //#endregion
1156
+ //#region src/cli/staticwebsite/index.ts
1157
+ const staticwebsiteCommand = defineCommand({
1158
+ meta: {
1159
+ name: "staticwebsite",
1160
+ description: "Manage static websites"
1161
+ },
1162
+ subCommands: {
1163
+ deploy: deployCommand,
1164
+ get: getCommand$1,
1165
+ list: listCommand$4
1166
+ },
1167
+ async run() {
1168
+ await runCommand(listCommand$4, { rawArgs: [] });
1169
+ }
1170
+ });
1171
+
844
1172
  //#endregion
845
1173
  //#region src/cli/tailordb/truncate.ts
846
1174
  async function truncateSingleType(options, client) {
@@ -1400,9 +1728,10 @@ const mainCommand = defineCommand({
1400
1728
  oauth2client: oauth2clientCommand,
1401
1729
  profile: profileCommand,
1402
1730
  remove: removeCommand,
1731
+ secret: secretCommand,
1403
1732
  show: showCommand,
1733
+ staticwebsite: staticwebsiteCommand,
1404
1734
  tailordb: tailordbCommand,
1405
- secret: secretCommand,
1406
1735
  user: userCommand,
1407
1736
  workflow: workflowCommand,
1408
1737
  workspace: workspaceCommand