@snaptrude/create-snaptrude-plugin 0.0.4 → 0.0.6

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/dist/index.cjs CHANGED
@@ -23,13 +23,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  mod
24
24
  ));
25
25
 
26
- // src/index.ts
27
- var import_prompts = require("@inquirer/prompts");
28
-
29
26
  // package.json
30
27
  var package_default = {
31
28
  name: "@snaptrude/create-snaptrude-plugin",
32
- version: "0.0.4",
29
+ version: "0.0.6",
33
30
  type: "module",
34
31
  main: "./dist/index.js",
35
32
  module: "./dist/index.js",
@@ -59,6 +56,7 @@ var package_default = {
59
56
  "@inquirer/prompts": "^8.3.0",
60
57
  commander: "^14.0.3",
61
58
  "simple-git": "^3.32.3",
59
+ tar: "^7.5.11",
62
60
  "update-check": "^1.5.4"
63
61
  }
64
62
  };
@@ -66,23 +64,41 @@ var package_default = {
66
64
  // src/index.ts
67
65
  var import_update_check = __toESM(require("update-check"), 1);
68
66
  var import_extra_typings = require("@commander-js/extra-typings");
67
+
68
+ // src/create.ts
69
+ var import_prompts = require("@inquirer/prompts");
69
70
  var import_simple_git = __toESM(require("simple-git"), 1);
70
71
  var import_promises = require("fs/promises");
71
72
  var import_node_path = require("path");
72
73
  var import_node_os = require("os");
73
- var CHECK_UPDATE = false;
74
- var DEFAULT_VERBOSE = false;
74
+ var import_node_crypto = require("crypto");
75
75
  var DEFAULT_PLUGIN_NAME = "my-plugin";
76
- async function updatePackageJson(targetDir, pluginName) {
77
- const packageJsonPath = (0, import_node_path.join)(targetDir, "package.json");
78
- const packageJson = await (0, import_promises.readFile)(packageJsonPath, "utf8");
79
- const packageJsonObject = JSON.parse(packageJson);
80
- packageJsonObject.name = pluginName;
81
- await (0, import_promises.writeFile)(packageJsonPath, JSON.stringify(packageJsonObject, null, 2));
82
- }
76
+ var EXAMPLES_REPO = "https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git";
77
+ var EXAMPLES_REPO_COMMIT = "9bb0b4d1d1a76217437f838ff23613bde2e3e79e";
78
+ var EXAMPLE_PATH_IN_REPO = "snaptrude-plugin";
83
79
  var UNWANTED_DIRS = [];
84
80
  var UNWANTED_FILES = [];
85
- async function removeUnwantedFiles(targetDir, verbose = true) {
81
+ async function askPluginName() {
82
+ return (0, import_prompts.input)({
83
+ message: "What is the name of your plugin?",
84
+ required: true,
85
+ default: DEFAULT_PLUGIN_NAME,
86
+ validate: (value) => {
87
+ if (!/^[a-z0-9-]+$/.test(value)) {
88
+ return "Plugin name must only contain lowercase letters, numbers, and hyphens";
89
+ }
90
+ return true;
91
+ }
92
+ });
93
+ }
94
+ async function updatePkgJson(targetDir, pluginName) {
95
+ const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
96
+ const pkg = JSON.parse(await (0, import_promises.readFile)(pkgPath, "utf8"));
97
+ pkg.name = pluginName;
98
+ pkg.id = (0, import_node_crypto.randomUUID)();
99
+ await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2));
100
+ }
101
+ async function removeUnwantedFiles(targetDir, verbose) {
86
102
  if (verbose) console.log(`Removing unwanted files from ${targetDir}`);
87
103
  for (const dir of UNWANTED_DIRS) {
88
104
  await (0, import_promises.rm)((0, import_node_path.join)(targetDir, dir), { recursive: true, force: true });
@@ -91,10 +107,7 @@ async function removeUnwantedFiles(targetDir, verbose = true) {
91
107
  await (0, import_promises.rm)((0, import_node_path.join)(targetDir, file), { force: true });
92
108
  }
93
109
  }
94
- var EXAMPLES_REPO = "https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git";
95
- var EXAMPLES_REPO_COMMIT = "9678047c224cae18fd45ab5b49bb614a34b6ac40";
96
- var EXAMPLE_PATH_IN_REPO = "snaptrude-plugin";
97
- async function createExampleRepo(targetDir, verbose = true) {
110
+ async function cloneExample(targetDir, verbose) {
98
111
  if (verbose) console.log(`Creating example repo in ${targetDir}`);
99
112
  const tmpDir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "snaptrude-plugin-"));
100
113
  if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`);
@@ -102,14 +115,11 @@ async function createExampleRepo(targetDir, verbose = true) {
102
115
  const git = (0, import_simple_git.default)(tmpDir);
103
116
  if (verbose) console.log(`Cloning example repo to ${tmpDir}`);
104
117
  await git.clone(EXAMPLES_REPO, ".", ["--no-checkout", "--depth=1"]);
105
- if (verbose)
106
- console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`);
118
+ if (verbose) console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`);
107
119
  await git.fetch(["origin", EXAMPLES_REPO_COMMIT, "--depth=1"]);
108
- if (verbose)
109
- console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`);
120
+ if (verbose) console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`);
110
121
  await git.checkout(EXAMPLES_REPO_COMMIT);
111
- if (verbose)
112
- console.log(`Copying example directory to target: ${targetDir}`);
122
+ if (verbose) console.log(`Copying example directory to target: ${targetDir}`);
113
123
  await (0, import_promises.cp)((0, import_node_path.join)(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true });
114
124
  } catch (error) {
115
125
  console.error("Error cloning example repo:", error);
@@ -119,51 +129,164 @@ async function createExampleRepo(targetDir, verbose = true) {
119
129
  await (0, import_promises.rm)(tmpDir, { recursive: true, force: true });
120
130
  }
121
131
  }
122
- async function createPlugin(targetDir, pluginName, verbose = true) {
123
- await createExampleRepo(targetDir, verbose);
124
- await removeUnwantedFiles(targetDir, verbose);
125
- await updatePackageJson(targetDir, pluginName);
126
- if (verbose) console.log(`Plugin created successfully in ${targetDir}`);
132
+ async function handleCreate(opts) {
133
+ const pluginName = opts.name ?? await askPluginName();
134
+ const pluginDir = (0, import_node_path.join)(process.cwd(), pluginName);
135
+ if (opts.verbose) console.log(`Creating plugin: ${pluginName}`);
136
+ await (0, import_promises.mkdir)(pluginDir, { recursive: true });
137
+ await cloneExample(pluginDir, opts.verbose);
138
+ await removeUnwantedFiles(pluginDir, opts.verbose);
139
+ await updatePkgJson(pluginDir, pluginName);
140
+ console.log(`Plugin "${pluginName}" created successfully in ${pluginDir}`);
127
141
  }
128
- async function runCliMode() {
129
- const program = new import_extra_typings.Command().version(package_default.version).addOption(
130
- new import_extra_typings.Option("-v, --verbose", "Verbose output").default(DEFAULT_VERBOSE)
131
- ).addOption(new import_extra_typings.Option("-n, --name <name>", "The name of the plugin"));
132
- program.parse();
133
- return program.opts();
142
+
143
+ // src/register.ts
144
+ var import_promises2 = require("fs/promises");
145
+ var import_node_path2 = require("path");
146
+ var import_node_crypto2 = require("crypto");
147
+ var import_node_child_process = require("child_process");
148
+ var import_tar = require("tar");
149
+ async function pathExists(p) {
150
+ try {
151
+ await (0, import_promises2.access)(p);
152
+ return true;
153
+ } catch {
154
+ return false;
155
+ }
134
156
  }
135
- async function runInteractiveMode() {
136
- const pluginName = await (0, import_prompts.input)({
137
- message: "What is the name of your plugin?",
138
- required: true,
139
- default: DEFAULT_PLUGIN_NAME,
140
- validate: (value) => {
141
- if (!/^[a-z0-9-]+$/.test(value)) {
142
- return "Plugin name must only contain lowercase letters, numbers, and hyphens";
143
- }
144
- return true;
145
- }
157
+ function createBundle(buildDir, entries, verbose) {
158
+ if (verbose) {
159
+ console.log(`Bundling ${entries.length} entries: ${entries.join(", ")}`);
160
+ }
161
+ return new Promise((res, rej) => {
162
+ const chunks = [];
163
+ const stream = (0, import_tar.create)({ gzip: true, cwd: buildDir }, entries);
164
+ stream.on("data", (chunk) => chunks.push(chunk));
165
+ stream.on("end", () => res(Buffer.concat(chunks)));
166
+ stream.on("error", rej);
146
167
  });
147
- return { pluginName };
148
168
  }
169
+ async function uploadBundle(apiUrl, token, manifest, tarBuffer, checksum) {
170
+ const formData = new FormData();
171
+ formData.append(
172
+ "bundle",
173
+ new Blob([new Uint8Array(tarBuffer)], {
174
+ type: "application/gzip"
175
+ }),
176
+ "bundle.tar.gz"
177
+ );
178
+ formData.append("manifest", JSON.stringify(manifest));
179
+ formData.append("checksum", checksum);
180
+ const res = await fetch(`${apiUrl}/plugins/register`, {
181
+ method: "POST",
182
+ headers: { Authorization: `Bearer ${token}` },
183
+ body: formData
184
+ });
185
+ if (!res.ok) {
186
+ const err = await res.json().catch(() => ({ error: res.statusText }));
187
+ console.error(`Error registering plugin: ${err.error}`);
188
+ process.exit(1);
189
+ }
190
+ return await res.json();
191
+ }
192
+ function runBuild(verbose) {
193
+ const cmd = `npm run build:prod`;
194
+ console.log(`Building plugin (${cmd})...`);
195
+ try {
196
+ (0, import_node_child_process.execSync)(cmd, {
197
+ cwd: process.cwd(),
198
+ stdio: verbose ? "inherit" : "pipe"
199
+ });
200
+ } catch (err) {
201
+ console.error("Build failed:");
202
+ if (!verbose && err instanceof Error && "stderr" in err) {
203
+ console.error(err.stderr?.toString());
204
+ }
205
+ process.exit(1);
206
+ }
207
+ }
208
+ async function handleRegister(opts) {
209
+ if (!opts.skipBuild) {
210
+ runBuild(opts.verbose);
211
+ }
212
+ const buildDir = (0, import_node_path2.resolve)(opts.path);
213
+ const rawToken = opts.token ?? process.env.SNAPTRUDE_TOKEN;
214
+ const token = rawToken?.startsWith("Bearer ") ? rawToken.slice(7) : rawToken;
215
+ const apiUrl = opts.apiUrl ?? process.env.SNAPTRUDE_API_URL;
216
+ if (!token) {
217
+ console.error(
218
+ "Error: Auth token is required. Use --token <token> or set SNAPTRUDE_TOKEN env var."
219
+ );
220
+ process.exit(1);
221
+ }
222
+ if (!apiUrl) {
223
+ console.error(
224
+ "Error: API URL is required. Use --api-url <url> or set SNAPTRUDE_API_URL env var."
225
+ );
226
+ process.exit(1);
227
+ }
228
+ const manifestPath = (0, import_node_path2.join)(buildDir, "manifest.json");
229
+ if (!await pathExists(buildDir)) {
230
+ console.error(`Error: Build directory not found: ${buildDir}`);
231
+ process.exit(1);
232
+ }
233
+ if (!await pathExists(manifestPath)) {
234
+ console.error(`Error: manifest.json not found in ${buildDir}`);
235
+ process.exit(1);
236
+ }
237
+ const manifest = JSON.parse(
238
+ await (0, import_promises2.readFile)(manifestPath, "utf8")
239
+ );
240
+ if (!manifest.name || !manifest.version) {
241
+ console.error("Error: manifest.json must contain name and version fields");
242
+ process.exit(1);
243
+ }
244
+ if (opts.verbose) console.log(`Reading build from: ${buildDir}`);
245
+ console.log("Creating bundle...");
246
+ const entries = (await (0, import_promises2.readdir)(buildDir)).filter((e) => e !== "manifest.json");
247
+ if (entries.length === 0) {
248
+ throw new Error("No files to bundle (only manifest.json found)");
249
+ }
250
+ const tarBuffer = await createBundle(buildDir, entries, opts.verbose);
251
+ const checksum = (0, import_node_crypto2.createHash)("sha256").update(tarBuffer).digest("hex");
252
+ if (opts.verbose) {
253
+ console.log(
254
+ `Bundle size: ${(tarBuffer.length / 1024).toFixed(1)} KB, checksum: ${checksum}`
255
+ );
256
+ }
257
+ console.log("Registering plugin...");
258
+ const result = await uploadBundle(apiUrl, token, manifest, tarBuffer, checksum);
259
+ console.log("");
260
+ console.log("Plugin registered successfully!");
261
+ console.log(` ID: ${result.id}`);
262
+ console.log(` Name: ${result.name}`);
263
+ console.log(` Version: ${result.version}`);
264
+ console.log(` Bundle: ${result.bundleUrl}`);
265
+ console.log(` Status: ${result.reviewStatus}`);
266
+ }
267
+
268
+ // src/index.ts
269
+ var CHECK_UPDATE = false;
149
270
  async function main() {
150
271
  if (CHECK_UPDATE) {
151
272
  const update = await (0, import_update_check.default)(package_default);
152
- if (update)
273
+ if (update) {
153
274
  console.log(
154
275
  `Update available: ${update.latest} (current: ${package_default.version})`
155
276
  );
277
+ }
156
278
  }
157
- console.log(`${package_default.name} v${package_default.version}`);
158
- const cliOpts = await runCliMode();
159
- const cwd = process.cwd();
160
- if (cliOpts.verbose) console.log("Verbose output enabled");
161
- if (cliOpts.verbose) console.log("Current working directory:", cwd);
162
- const pluginName = cliOpts.name ?? (await runInteractiveMode()).pluginName;
163
- const pluginDir = (0, import_node_path.join)(cwd, pluginName);
164
- if (cliOpts.verbose) console.log(`Creating plugin: ${pluginName}`);
165
- await (0, import_promises.mkdir)(pluginDir, { recursive: true });
166
- await createPlugin(pluginDir, pluginName, cliOpts.verbose);
279
+ const program = new import_extra_typings.Command().name("create-snaptrude-plugin").version(package_default.version).description("Snaptrude plugin CLI \u2014 scaffold and register plugins");
280
+ program.command("create", { isDefault: true }).description("Scaffold a new Snaptrude plugin project").addOption(new import_extra_typings.Option("-v, --verbose", "Verbose output").default(false)).addOption(new import_extra_typings.Option("-n, --name <name>", "Plugin name")).action(handleCreate);
281
+ program.command("register").description("Build, bundle, and register a plugin with Snaptrude").addOption(
282
+ new import_extra_typings.Option("-p, --path <dir>", "Path to the build output directory").default("./dist")
283
+ ).addOption(new import_extra_typings.Option("-t, --token <token>", "Auth token (or set SNAPTRUDE_TOKEN env var)")).addOption(
284
+ new import_extra_typings.Option(
285
+ "--api-url <url>",
286
+ "Snaptrude API base URL (or set SNAPTRUDE_API_URL env var)"
287
+ ).default("https://save2.snaptru.de")
288
+ ).addOption(new import_extra_typings.Option("--skip-build", "Skip the build step").default(false)).addOption(new import_extra_typings.Option("-v, --verbose", "Verbose output").default(false)).action(handleRegister);
289
+ await program.parseAsync();
167
290
  }
168
291
  main();
169
292
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\n\nimport { input } from \"@inquirer/prompts\"\nimport packageJson from \"../package.json\"\nimport updateCheck from \"update-check\"\nimport { Command, Option } from \"@commander-js/extra-typings\"\nimport simpleGit from \"simple-git\"\nimport { cp, mkdir, readFile, rm, writeFile, mkdtemp } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { tmpdir } from \"node:os\"\n\n/**\n * Check for updates.\n */\nconst CHECK_UPDATE = false\n\nconst DEFAULT_VERBOSE = false\nconst DEFAULT_PLUGIN_NAME = \"my-plugin\"\n\nasync function updatePackageJson(targetDir: string, pluginName: string) {\n const packageJsonPath = join(targetDir, \"package.json\")\n const packageJson = await readFile(packageJsonPath, \"utf8\")\n const packageJsonObject = JSON.parse(packageJson)\n packageJsonObject.name = pluginName\n await writeFile(packageJsonPath, JSON.stringify(packageJsonObject, null, 2))\n}\n\nconst UNWANTED_DIRS: string[] = []\nconst UNWANTED_FILES: string[] = []\n\nasync function removeUnwantedFiles(targetDir: string, verbose: boolean = true) {\n if (verbose) console.log(`Removing unwanted files from ${targetDir}`)\n for (const dir of UNWANTED_DIRS) {\n await rm(join(targetDir, dir), { recursive: true, force: true })\n }\n for (const file of UNWANTED_FILES) {\n await rm(join(targetDir, file), { force: true })\n }\n}\n\nconst EXAMPLES_REPO =\n \"https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git\"\nconst EXAMPLES_REPO_COMMIT = \"9678047c224cae18fd45ab5b49bb614a34b6ac40\"\nconst EXAMPLE_PATH_IN_REPO = \"snaptrude-plugin\"\n\nasync function createExampleRepo(targetDir: string, verbose: boolean = true) {\n if (verbose) console.log(`Creating example repo in ${targetDir}`)\n const tmpDir = await mkdtemp(join(tmpdir(), \"snaptrude-plugin-\"))\n if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`)\n try {\n const git = simpleGit(tmpDir)\n if (verbose) console.log(`Cloning example repo to ${tmpDir}`)\n // Shallow clone the repo into the temp directory\n await git.clone(EXAMPLES_REPO, \".\", [\"--no-checkout\", \"--depth=1\"])\n\n if (verbose)\n console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.fetch([\"origin\", EXAMPLES_REPO_COMMIT, \"--depth=1\"])\n if (verbose)\n console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.checkout(EXAMPLES_REPO_COMMIT)\n if (verbose)\n console.log(`Copying example directory to target: ${targetDir}`)\n await cp(join(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true })\n } catch (error) {\n console.error(\"Error cloning example repo:\", error)\n throw error\n } finally {\n if (verbose) console.log(`Cleaning up temporary directory: ${tmpDir}`)\n await rm(tmpDir, { recursive: true, force: true })\n }\n}\n\nasync function createPlugin(\n targetDir: string,\n pluginName: string,\n verbose: boolean = true,\n) {\n await createExampleRepo(targetDir, verbose)\n await removeUnwantedFiles(targetDir, verbose)\n await updatePackageJson(targetDir, pluginName)\n if (verbose) console.log(`Plugin created successfully in ${targetDir}`)\n}\n\nasync function runCliMode() {\n const program = new Command()\n .version(packageJson.version)\n .addOption(\n new Option(\"-v, --verbose\", \"Verbose output\").default(DEFAULT_VERBOSE),\n )\n .addOption(new Option(\"-n, --name <name>\", \"The name of the plugin\"))\n program.parse()\n return program.opts()\n}\n\nasync function runInteractiveMode() {\n const pluginName = await input({\n message: \"What is the name of your plugin?\",\n required: true,\n default: DEFAULT_PLUGIN_NAME,\n validate: (value) => {\n if (!/^[a-z0-9-]+$/.test(value)) {\n return \"Plugin name must only contain lowercase letters, numbers, and hyphens\"\n }\n return true\n },\n })\n return { pluginName }\n}\n\nasync function main() {\n if (CHECK_UPDATE) {\n const update = await updateCheck(packageJson)\n if (update)\n console.log(\n `Update available: ${update.latest} (current: ${packageJson.version})`,\n )\n }\n console.log(`${packageJson.name} v${packageJson.version}`)\n\n const cliOpts = await runCliMode()\n const cwd = process.cwd()\n if (cliOpts.verbose) console.log(\"Verbose output enabled\")\n if (cliOpts.verbose) console.log(\"Current working directory:\", cwd)\n const pluginName = cliOpts.name ?? (await runInteractiveMode()).pluginName\n const pluginDir = join(cwd, pluginName)\n if (cliOpts.verbose) console.log(`Creating plugin: ${pluginName}`)\n await mkdir(pluginDir, { recursive: true })\n await createPlugin(pluginDir, pluginName, cliOpts.verbose)\n}\n\nmain()\n","{\n \"name\": \"@snaptrude/create-snaptrude-plugin\",\n \"version\": \"0.0.4\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"bin\": {\n \"create-snaptrude-plugin\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"check-types\": \"tsc --noEmit\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean-dist\": \"rm -rf dist\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.5.4\"\n },\n \"dependencies\": {\n \"@commander-js/extra-typings\": \"^14.0.0\",\n \"@inquirer/prompts\": \"^8.3.0\",\n \"commander\": \"^14.0.3\",\n \"simple-git\": \"^3.32.3\",\n \"update-check\": \"^1.5.4\"\n }\n}\n"],"mappings":"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","names":["simpleGit","updateCheck"]}
1
+ {"version":3,"sources":["../package.json","../src/index.ts","../src/create.ts","../src/register.ts"],"sourcesContent":["{\n \"name\": \"@snaptrude/create-snaptrude-plugin\",\n \"version\": \"0.0.6\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"bin\": {\n \"create-snaptrude-plugin\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"check-types\": \"tsc --noEmit\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean-dist\": \"rm -rf dist\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.5.4\"\n },\n \"dependencies\": {\n \"@commander-js/extra-typings\": \"^14.0.0\",\n \"@inquirer/prompts\": \"^8.3.0\",\n \"commander\": \"^14.0.3\",\n \"simple-git\": \"^3.32.3\",\n \"tar\": \"^7.5.11\",\n \"update-check\": \"^1.5.4\"\n }\n}\n","#!/usr/bin/env node\n\nimport packageJson from \"../package.json\"\nimport updateCheck from \"update-check\"\nimport { Command, Option } from \"@commander-js/extra-typings\"\nimport { handleCreate } from \"./create\"\nimport { handleRegister } from \"./register\"\n\nconst CHECK_UPDATE = false\n\nasync function main() {\n if (CHECK_UPDATE) {\n const update = await updateCheck(packageJson)\n if (update) {\n console.log(\n `Update available: ${update.latest} (current: ${packageJson.version})`,\n )\n }\n }\n\n const program = new Command()\n .name(\"create-snaptrude-plugin\")\n .version(packageJson.version)\n .description(\"Snaptrude plugin CLI — scaffold and register plugins\")\n\n program\n .command(\"create\", { isDefault: true })\n .description(\"Scaffold a new Snaptrude plugin project\")\n .addOption(new Option(\"-v, --verbose\", \"Verbose output\").default(false))\n .addOption(new Option(\"-n, --name <name>\", \"Plugin name\"))\n .action(handleCreate)\n\n program\n .command(\"register\")\n .description(\"Build, bundle, and register a plugin with Snaptrude\")\n .addOption(\n new Option(\"-p, --path <dir>\", \"Path to the build output directory\").default(\"./dist\"),\n )\n .addOption(new Option(\"-t, --token <token>\", \"Auth token (or set SNAPTRUDE_TOKEN env var)\"))\n .addOption(\n new Option(\n \"--api-url <url>\",\n \"Snaptrude API base URL (or set SNAPTRUDE_API_URL env var)\",\n ).default(\"https://save2.snaptru.de\"),\n )\n .addOption(new Option(\"--skip-build\", \"Skip the build step\").default(false))\n .addOption(new Option(\"-v, --verbose\", \"Verbose output\").default(false))\n .action(handleRegister)\n\n await program.parseAsync()\n}\n\nmain()\n","import { input } from \"@inquirer/prompts\"\nimport simpleGit from \"simple-git\"\nimport { cp, mkdir, readFile, rm, writeFile, mkdtemp } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { tmpdir } from \"node:os\"\nimport { randomUUID } from \"node:crypto\"\n\nconst DEFAULT_PLUGIN_NAME = \"my-plugin\"\n\nconst EXAMPLES_REPO =\n \"https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git\"\nconst EXAMPLES_REPO_COMMIT = \"9bb0b4d1d1a76217437f838ff23613bde2e3e79e\"\nconst EXAMPLE_PATH_IN_REPO = \"snaptrude-plugin\"\n\nconst UNWANTED_DIRS: string[] = []\nconst UNWANTED_FILES: string[] = []\n\nasync function askPluginName(): Promise<string> {\n return input({\n message: \"What is the name of your plugin?\",\n required: true,\n default: DEFAULT_PLUGIN_NAME,\n validate: (value) => {\n if (!/^[a-z0-9-]+$/.test(value)) {\n return \"Plugin name must only contain lowercase letters, numbers, and hyphens\"\n }\n return true\n },\n })\n}\n\nasync function updatePkgJson(targetDir: string, pluginName: string) {\n const pkgPath = join(targetDir, \"package.json\")\n const pkg = JSON.parse(await readFile(pkgPath, \"utf8\"))\n pkg.name = pluginName\n pkg.id = randomUUID()\n await writeFile(pkgPath, JSON.stringify(pkg, null, 2))\n}\n\nasync function removeUnwantedFiles(targetDir: string, verbose: boolean) {\n if (verbose) console.log(`Removing unwanted files from ${targetDir}`)\n for (const dir of UNWANTED_DIRS) {\n await rm(join(targetDir, dir), { recursive: true, force: true })\n }\n for (const file of UNWANTED_FILES) {\n await rm(join(targetDir, file), { force: true })\n }\n}\n\nasync function cloneExample(targetDir: string, verbose: boolean) {\n if (verbose) console.log(`Creating example repo in ${targetDir}`)\n const tmpDir = await mkdtemp(join(tmpdir(), \"snaptrude-plugin-\"))\n if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`)\n try {\n const git = simpleGit(tmpDir)\n if (verbose) console.log(`Cloning example repo to ${tmpDir}`)\n await git.clone(EXAMPLES_REPO, \".\", [\"--no-checkout\", \"--depth=1\"])\n if (verbose) console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.fetch([\"origin\", EXAMPLES_REPO_COMMIT, \"--depth=1\"])\n if (verbose) console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.checkout(EXAMPLES_REPO_COMMIT)\n if (verbose) console.log(`Copying example directory to target: ${targetDir}`)\n await cp(join(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true })\n } catch (error) {\n console.error(\"Error cloning example repo:\", error)\n throw error\n } finally {\n if (verbose) console.log(`Cleaning up temporary directory: ${tmpDir}`)\n await rm(tmpDir, { recursive: true, force: true })\n }\n}\n\nexport async function handleCreate(opts: { verbose: boolean; name?: string }) {\n const pluginName = opts.name ?? (await askPluginName())\n const pluginDir = join(process.cwd(), pluginName)\n\n if (opts.verbose) console.log(`Creating plugin: ${pluginName}`)\n await mkdir(pluginDir, { recursive: true })\n await cloneExample(pluginDir, opts.verbose)\n await removeUnwantedFiles(pluginDir, opts.verbose)\n await updatePkgJson(pluginDir, pluginName)\n\n console.log(`Plugin \"${pluginName}\" created successfully in ${pluginDir}`)\n}\n","import { readFile, readdir, access } from \"node:fs/promises\"\nimport { join, resolve } from \"node:path\"\nimport { createHash } from \"node:crypto\"\nimport { execSync } from \"node:child_process\"\nimport { create as tarCreate } from \"tar\"\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p)\n return true\n } catch {\n return false\n }\n}\n\nfunction createBundle(buildDir: string, entries: string[], verbose: boolean): Promise<Buffer> {\n if (verbose) {\n console.log(`Bundling ${entries.length} entries: ${entries.join(\", \")}`)\n }\n\n return new Promise<Buffer>((res, rej) => {\n const chunks: Buffer[] = []\n const stream = tarCreate({ gzip: true, cwd: buildDir }, entries)\n stream.on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n stream.on(\"end\", () => res(Buffer.concat(chunks)))\n stream.on(\"error\", rej)\n })\n}\n\ninterface PluginManifest {\n name: string\n version: string\n description?: string\n apiVersion?: string\n workerUrl?: string\n uiUrl?: string\n id?: string\n bundleUrl?: string\n [key: string]: unknown\n}\n\ninterface RegisterResult {\n id: string\n name: string\n version: string\n bundleUrl: string\n reviewStatus: string\n}\n\nasync function uploadBundle(\n apiUrl: string,\n token: string,\n manifest: PluginManifest,\n tarBuffer: Buffer,\n checksum: string,\n): Promise<RegisterResult> {\n const formData = new FormData()\n formData.append(\n \"bundle\",\n new Blob([new Uint8Array(tarBuffer) as unknown as ArrayBuffer], {\n type: \"application/gzip\",\n }),\n \"bundle.tar.gz\",\n )\n formData.append(\"manifest\", JSON.stringify(manifest))\n formData.append(\"checksum\", checksum)\n\n const res = await fetch(`${apiUrl}/plugins/register`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: res.statusText }))\n console.error(`Error registering plugin: ${(err as { error: string }).error}`)\n process.exit(1)\n }\n\n return (await res.json()) as RegisterResult\n}\n\n// function detectPackageManager(): \"pnpm\" | \"yarn\" | \"npm\" {\n// for (const [lockfile, pm] of [\n// [\"pnpm-lock.yaml\", \"pnpm\"],\n// [\"yarn.lock\", \"yarn\"],\n// [\"package-lock.json\", \"npm\"],\n// ] as const) {\n// try {\n// const fullPath = join(process.cwd(), lockfile)\n// require(\"node:fs\").accessSync(fullPath)\n// return pm\n// } catch {\n// // not found, try next\n// }\n// }\n// return \"npm\"\n// }\n\nfunction runBuild(verbose: boolean) {\n // const pm = detectPackageManager()\n const cmd = `npm run build:prod`\n console.log(`Building plugin (${cmd})...`)\n try {\n execSync(cmd, {\n cwd: process.cwd(),\n stdio: verbose ? \"inherit\" : \"pipe\",\n })\n } catch (err) {\n console.error(\"Build failed:\")\n if (!verbose && err instanceof Error && \"stderr\" in err) {\n console.error((err as { stderr: Buffer }).stderr?.toString())\n }\n process.exit(1)\n }\n}\n\nexport async function handleRegister(opts: {\n path: string\n token?: string\n apiUrl: string\n verbose: boolean\n skipBuild: boolean\n}) {\n if (!opts.skipBuild) {\n runBuild(opts.verbose)\n }\n\n const buildDir = resolve(opts.path)\n const rawToken = opts.token ?? process.env.SNAPTRUDE_TOKEN\n const token = rawToken?.startsWith(\"Bearer \") ? rawToken.slice(7) : rawToken\n const apiUrl = opts.apiUrl ?? process.env.SNAPTRUDE_API_URL\n\n if (!token) {\n console.error(\n \"Error: Auth token is required. Use --token <token> or set SNAPTRUDE_TOKEN env var.\",\n )\n process.exit(1)\n }\n if (!apiUrl) {\n console.error(\n \"Error: API URL is required. Use --api-url <url> or set SNAPTRUDE_API_URL env var.\",\n )\n process.exit(1)\n }\n\n const manifestPath = join(buildDir, \"manifest.json\")\n if (!(await pathExists(buildDir))) {\n console.error(`Error: Build directory not found: ${buildDir}`)\n process.exit(1)\n }\n if (!(await pathExists(manifestPath))) {\n console.error(`Error: manifest.json not found in ${buildDir}`)\n process.exit(1)\n }\n\n const manifest: PluginManifest = JSON.parse(\n await readFile(manifestPath, \"utf8\"),\n )\n if (!manifest.name || !manifest.version) {\n console.error(\"Error: manifest.json must contain name and version fields\")\n process.exit(1)\n }\n\n if (opts.verbose) console.log(`Reading build from: ${buildDir}`)\n\n console.log(\"Creating bundle...\")\n const entries = (await readdir(buildDir)).filter((e) => e !== \"manifest.json\")\n if (entries.length === 0) {\n throw new Error(\"No files to bundle (only manifest.json found)\")\n }\n\n const tarBuffer = await createBundle(buildDir, entries, opts.verbose)\n const checksum = createHash(\"sha256\").update(tarBuffer).digest(\"hex\")\n if (opts.verbose) {\n console.log(\n `Bundle size: ${(tarBuffer.length / 1024).toFixed(1)} KB, checksum: ${checksum}`,\n )\n }\n\n console.log(\"Registering plugin...\")\n const result = await uploadBundle(apiUrl, token, manifest, tarBuffer, checksum)\n\n console.log(\"\")\n console.log(\"Plugin registered successfully!\")\n console.log(` ID: ${result.id}`)\n console.log(` Name: ${result.name}`)\n console.log(` Version: ${result.version}`)\n console.log(` Bundle: ${result.bundleUrl}`)\n console.log(` Status: ${result.reviewStatus}`)\n}\n"],"mappings":"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","names":["simpleGit","import_promises","import_node_path","import_node_crypto","tarCreate","updateCheck"]}
package/dist/index.js CHANGED
@@ -1,12 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/index.ts
4
- import { input } from "@inquirer/prompts";
5
-
6
3
  // package.json
7
4
  var package_default = {
8
5
  name: "@snaptrude/create-snaptrude-plugin",
9
- version: "0.0.4",
6
+ version: "0.0.6",
10
7
  type: "module",
11
8
  main: "./dist/index.js",
12
9
  module: "./dist/index.js",
@@ -36,6 +33,7 @@ var package_default = {
36
33
  "@inquirer/prompts": "^8.3.0",
37
34
  commander: "^14.0.3",
38
35
  "simple-git": "^3.32.3",
36
+ tar: "^7.5.11",
39
37
  "update-check": "^1.5.4"
40
38
  }
41
39
  };
@@ -43,23 +41,41 @@ var package_default = {
43
41
  // src/index.ts
44
42
  import updateCheck from "update-check";
45
43
  import { Command, Option } from "@commander-js/extra-typings";
44
+
45
+ // src/create.ts
46
+ import { input } from "@inquirer/prompts";
46
47
  import simpleGit from "simple-git";
47
48
  import { cp, mkdir, readFile, rm, writeFile, mkdtemp } from "fs/promises";
48
49
  import { join } from "path";
49
50
  import { tmpdir } from "os";
50
- var CHECK_UPDATE = false;
51
- var DEFAULT_VERBOSE = false;
51
+ import { randomUUID } from "crypto";
52
52
  var DEFAULT_PLUGIN_NAME = "my-plugin";
53
- async function updatePackageJson(targetDir, pluginName) {
54
- const packageJsonPath = join(targetDir, "package.json");
55
- const packageJson = await readFile(packageJsonPath, "utf8");
56
- const packageJsonObject = JSON.parse(packageJson);
57
- packageJsonObject.name = pluginName;
58
- await writeFile(packageJsonPath, JSON.stringify(packageJsonObject, null, 2));
59
- }
53
+ var EXAMPLES_REPO = "https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git";
54
+ var EXAMPLES_REPO_COMMIT = "9bb0b4d1d1a76217437f838ff23613bde2e3e79e";
55
+ var EXAMPLE_PATH_IN_REPO = "snaptrude-plugin";
60
56
  var UNWANTED_DIRS = [];
61
57
  var UNWANTED_FILES = [];
62
- async function removeUnwantedFiles(targetDir, verbose = true) {
58
+ async function askPluginName() {
59
+ return input({
60
+ message: "What is the name of your plugin?",
61
+ required: true,
62
+ default: DEFAULT_PLUGIN_NAME,
63
+ validate: (value) => {
64
+ if (!/^[a-z0-9-]+$/.test(value)) {
65
+ return "Plugin name must only contain lowercase letters, numbers, and hyphens";
66
+ }
67
+ return true;
68
+ }
69
+ });
70
+ }
71
+ async function updatePkgJson(targetDir, pluginName) {
72
+ const pkgPath = join(targetDir, "package.json");
73
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
74
+ pkg.name = pluginName;
75
+ pkg.id = randomUUID();
76
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
77
+ }
78
+ async function removeUnwantedFiles(targetDir, verbose) {
63
79
  if (verbose) console.log(`Removing unwanted files from ${targetDir}`);
64
80
  for (const dir of UNWANTED_DIRS) {
65
81
  await rm(join(targetDir, dir), { recursive: true, force: true });
@@ -68,10 +84,7 @@ async function removeUnwantedFiles(targetDir, verbose = true) {
68
84
  await rm(join(targetDir, file), { force: true });
69
85
  }
70
86
  }
71
- var EXAMPLES_REPO = "https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git";
72
- var EXAMPLES_REPO_COMMIT = "9678047c224cae18fd45ab5b49bb614a34b6ac40";
73
- var EXAMPLE_PATH_IN_REPO = "snaptrude-plugin";
74
- async function createExampleRepo(targetDir, verbose = true) {
87
+ async function cloneExample(targetDir, verbose) {
75
88
  if (verbose) console.log(`Creating example repo in ${targetDir}`);
76
89
  const tmpDir = await mkdtemp(join(tmpdir(), "snaptrude-plugin-"));
77
90
  if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`);
@@ -79,14 +92,11 @@ async function createExampleRepo(targetDir, verbose = true) {
79
92
  const git = simpleGit(tmpDir);
80
93
  if (verbose) console.log(`Cloning example repo to ${tmpDir}`);
81
94
  await git.clone(EXAMPLES_REPO, ".", ["--no-checkout", "--depth=1"]);
82
- if (verbose)
83
- console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`);
95
+ if (verbose) console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`);
84
96
  await git.fetch(["origin", EXAMPLES_REPO_COMMIT, "--depth=1"]);
85
- if (verbose)
86
- console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`);
97
+ if (verbose) console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`);
87
98
  await git.checkout(EXAMPLES_REPO_COMMIT);
88
- if (verbose)
89
- console.log(`Copying example directory to target: ${targetDir}`);
99
+ if (verbose) console.log(`Copying example directory to target: ${targetDir}`);
90
100
  await cp(join(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true });
91
101
  } catch (error) {
92
102
  console.error("Error cloning example repo:", error);
@@ -96,51 +106,164 @@ async function createExampleRepo(targetDir, verbose = true) {
96
106
  await rm(tmpDir, { recursive: true, force: true });
97
107
  }
98
108
  }
99
- async function createPlugin(targetDir, pluginName, verbose = true) {
100
- await createExampleRepo(targetDir, verbose);
101
- await removeUnwantedFiles(targetDir, verbose);
102
- await updatePackageJson(targetDir, pluginName);
103
- if (verbose) console.log(`Plugin created successfully in ${targetDir}`);
109
+ async function handleCreate(opts) {
110
+ const pluginName = opts.name ?? await askPluginName();
111
+ const pluginDir = join(process.cwd(), pluginName);
112
+ if (opts.verbose) console.log(`Creating plugin: ${pluginName}`);
113
+ await mkdir(pluginDir, { recursive: true });
114
+ await cloneExample(pluginDir, opts.verbose);
115
+ await removeUnwantedFiles(pluginDir, opts.verbose);
116
+ await updatePkgJson(pluginDir, pluginName);
117
+ console.log(`Plugin "${pluginName}" created successfully in ${pluginDir}`);
104
118
  }
105
- async function runCliMode() {
106
- const program = new Command().version(package_default.version).addOption(
107
- new Option("-v, --verbose", "Verbose output").default(DEFAULT_VERBOSE)
108
- ).addOption(new Option("-n, --name <name>", "The name of the plugin"));
109
- program.parse();
110
- return program.opts();
119
+
120
+ // src/register.ts
121
+ import { readFile as readFile2, readdir, access } from "fs/promises";
122
+ import { join as join2, resolve } from "path";
123
+ import { createHash } from "crypto";
124
+ import { execSync } from "child_process";
125
+ import { create as tarCreate } from "tar";
126
+ async function pathExists(p) {
127
+ try {
128
+ await access(p);
129
+ return true;
130
+ } catch {
131
+ return false;
132
+ }
111
133
  }
112
- async function runInteractiveMode() {
113
- const pluginName = await input({
114
- message: "What is the name of your plugin?",
115
- required: true,
116
- default: DEFAULT_PLUGIN_NAME,
117
- validate: (value) => {
118
- if (!/^[a-z0-9-]+$/.test(value)) {
119
- return "Plugin name must only contain lowercase letters, numbers, and hyphens";
120
- }
121
- return true;
122
- }
134
+ function createBundle(buildDir, entries, verbose) {
135
+ if (verbose) {
136
+ console.log(`Bundling ${entries.length} entries: ${entries.join(", ")}`);
137
+ }
138
+ return new Promise((res, rej) => {
139
+ const chunks = [];
140
+ const stream = tarCreate({ gzip: true, cwd: buildDir }, entries);
141
+ stream.on("data", (chunk) => chunks.push(chunk));
142
+ stream.on("end", () => res(Buffer.concat(chunks)));
143
+ stream.on("error", rej);
123
144
  });
124
- return { pluginName };
125
145
  }
146
+ async function uploadBundle(apiUrl, token, manifest, tarBuffer, checksum) {
147
+ const formData = new FormData();
148
+ formData.append(
149
+ "bundle",
150
+ new Blob([new Uint8Array(tarBuffer)], {
151
+ type: "application/gzip"
152
+ }),
153
+ "bundle.tar.gz"
154
+ );
155
+ formData.append("manifest", JSON.stringify(manifest));
156
+ formData.append("checksum", checksum);
157
+ const res = await fetch(`${apiUrl}/plugins/register`, {
158
+ method: "POST",
159
+ headers: { Authorization: `Bearer ${token}` },
160
+ body: formData
161
+ });
162
+ if (!res.ok) {
163
+ const err = await res.json().catch(() => ({ error: res.statusText }));
164
+ console.error(`Error registering plugin: ${err.error}`);
165
+ process.exit(1);
166
+ }
167
+ return await res.json();
168
+ }
169
+ function runBuild(verbose) {
170
+ const cmd = `npm run build:prod`;
171
+ console.log(`Building plugin (${cmd})...`);
172
+ try {
173
+ execSync(cmd, {
174
+ cwd: process.cwd(),
175
+ stdio: verbose ? "inherit" : "pipe"
176
+ });
177
+ } catch (err) {
178
+ console.error("Build failed:");
179
+ if (!verbose && err instanceof Error && "stderr" in err) {
180
+ console.error(err.stderr?.toString());
181
+ }
182
+ process.exit(1);
183
+ }
184
+ }
185
+ async function handleRegister(opts) {
186
+ if (!opts.skipBuild) {
187
+ runBuild(opts.verbose);
188
+ }
189
+ const buildDir = resolve(opts.path);
190
+ const rawToken = opts.token ?? process.env.SNAPTRUDE_TOKEN;
191
+ const token = rawToken?.startsWith("Bearer ") ? rawToken.slice(7) : rawToken;
192
+ const apiUrl = opts.apiUrl ?? process.env.SNAPTRUDE_API_URL;
193
+ if (!token) {
194
+ console.error(
195
+ "Error: Auth token is required. Use --token <token> or set SNAPTRUDE_TOKEN env var."
196
+ );
197
+ process.exit(1);
198
+ }
199
+ if (!apiUrl) {
200
+ console.error(
201
+ "Error: API URL is required. Use --api-url <url> or set SNAPTRUDE_API_URL env var."
202
+ );
203
+ process.exit(1);
204
+ }
205
+ const manifestPath = join2(buildDir, "manifest.json");
206
+ if (!await pathExists(buildDir)) {
207
+ console.error(`Error: Build directory not found: ${buildDir}`);
208
+ process.exit(1);
209
+ }
210
+ if (!await pathExists(manifestPath)) {
211
+ console.error(`Error: manifest.json not found in ${buildDir}`);
212
+ process.exit(1);
213
+ }
214
+ const manifest = JSON.parse(
215
+ await readFile2(manifestPath, "utf8")
216
+ );
217
+ if (!manifest.name || !manifest.version) {
218
+ console.error("Error: manifest.json must contain name and version fields");
219
+ process.exit(1);
220
+ }
221
+ if (opts.verbose) console.log(`Reading build from: ${buildDir}`);
222
+ console.log("Creating bundle...");
223
+ const entries = (await readdir(buildDir)).filter((e) => e !== "manifest.json");
224
+ if (entries.length === 0) {
225
+ throw new Error("No files to bundle (only manifest.json found)");
226
+ }
227
+ const tarBuffer = await createBundle(buildDir, entries, opts.verbose);
228
+ const checksum = createHash("sha256").update(tarBuffer).digest("hex");
229
+ if (opts.verbose) {
230
+ console.log(
231
+ `Bundle size: ${(tarBuffer.length / 1024).toFixed(1)} KB, checksum: ${checksum}`
232
+ );
233
+ }
234
+ console.log("Registering plugin...");
235
+ const result = await uploadBundle(apiUrl, token, manifest, tarBuffer, checksum);
236
+ console.log("");
237
+ console.log("Plugin registered successfully!");
238
+ console.log(` ID: ${result.id}`);
239
+ console.log(` Name: ${result.name}`);
240
+ console.log(` Version: ${result.version}`);
241
+ console.log(` Bundle: ${result.bundleUrl}`);
242
+ console.log(` Status: ${result.reviewStatus}`);
243
+ }
244
+
245
+ // src/index.ts
246
+ var CHECK_UPDATE = false;
126
247
  async function main() {
127
248
  if (CHECK_UPDATE) {
128
249
  const update = await updateCheck(package_default);
129
- if (update)
250
+ if (update) {
130
251
  console.log(
131
252
  `Update available: ${update.latest} (current: ${package_default.version})`
132
253
  );
254
+ }
133
255
  }
134
- console.log(`${package_default.name} v${package_default.version}`);
135
- const cliOpts = await runCliMode();
136
- const cwd = process.cwd();
137
- if (cliOpts.verbose) console.log("Verbose output enabled");
138
- if (cliOpts.verbose) console.log("Current working directory:", cwd);
139
- const pluginName = cliOpts.name ?? (await runInteractiveMode()).pluginName;
140
- const pluginDir = join(cwd, pluginName);
141
- if (cliOpts.verbose) console.log(`Creating plugin: ${pluginName}`);
142
- await mkdir(pluginDir, { recursive: true });
143
- await createPlugin(pluginDir, pluginName, cliOpts.verbose);
256
+ const program = new Command().name("create-snaptrude-plugin").version(package_default.version).description("Snaptrude plugin CLI \u2014 scaffold and register plugins");
257
+ program.command("create", { isDefault: true }).description("Scaffold a new Snaptrude plugin project").addOption(new Option("-v, --verbose", "Verbose output").default(false)).addOption(new Option("-n, --name <name>", "Plugin name")).action(handleCreate);
258
+ program.command("register").description("Build, bundle, and register a plugin with Snaptrude").addOption(
259
+ new Option("-p, --path <dir>", "Path to the build output directory").default("./dist")
260
+ ).addOption(new Option("-t, --token <token>", "Auth token (or set SNAPTRUDE_TOKEN env var)")).addOption(
261
+ new Option(
262
+ "--api-url <url>",
263
+ "Snaptrude API base URL (or set SNAPTRUDE_API_URL env var)"
264
+ ).default("https://save2.snaptru.de")
265
+ ).addOption(new Option("--skip-build", "Skip the build step").default(false)).addOption(new Option("-v, --verbose", "Verbose output").default(false)).action(handleRegister);
266
+ await program.parseAsync();
144
267
  }
145
268
  main();
146
269
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\n\nimport { input } from \"@inquirer/prompts\"\nimport packageJson from \"../package.json\"\nimport updateCheck from \"update-check\"\nimport { Command, Option } from \"@commander-js/extra-typings\"\nimport simpleGit from \"simple-git\"\nimport { cp, mkdir, readFile, rm, writeFile, mkdtemp } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { tmpdir } from \"node:os\"\n\n/**\n * Check for updates.\n */\nconst CHECK_UPDATE = false\n\nconst DEFAULT_VERBOSE = false\nconst DEFAULT_PLUGIN_NAME = \"my-plugin\"\n\nasync function updatePackageJson(targetDir: string, pluginName: string) {\n const packageJsonPath = join(targetDir, \"package.json\")\n const packageJson = await readFile(packageJsonPath, \"utf8\")\n const packageJsonObject = JSON.parse(packageJson)\n packageJsonObject.name = pluginName\n await writeFile(packageJsonPath, JSON.stringify(packageJsonObject, null, 2))\n}\n\nconst UNWANTED_DIRS: string[] = []\nconst UNWANTED_FILES: string[] = []\n\nasync function removeUnwantedFiles(targetDir: string, verbose: boolean = true) {\n if (verbose) console.log(`Removing unwanted files from ${targetDir}`)\n for (const dir of UNWANTED_DIRS) {\n await rm(join(targetDir, dir), { recursive: true, force: true })\n }\n for (const file of UNWANTED_FILES) {\n await rm(join(targetDir, file), { force: true })\n }\n}\n\nconst EXAMPLES_REPO =\n \"https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git\"\nconst EXAMPLES_REPO_COMMIT = \"9678047c224cae18fd45ab5b49bb614a34b6ac40\"\nconst EXAMPLE_PATH_IN_REPO = \"snaptrude-plugin\"\n\nasync function createExampleRepo(targetDir: string, verbose: boolean = true) {\n if (verbose) console.log(`Creating example repo in ${targetDir}`)\n const tmpDir = await mkdtemp(join(tmpdir(), \"snaptrude-plugin-\"))\n if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`)\n try {\n const git = simpleGit(tmpDir)\n if (verbose) console.log(`Cloning example repo to ${tmpDir}`)\n // Shallow clone the repo into the temp directory\n await git.clone(EXAMPLES_REPO, \".\", [\"--no-checkout\", \"--depth=1\"])\n\n if (verbose)\n console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.fetch([\"origin\", EXAMPLES_REPO_COMMIT, \"--depth=1\"])\n if (verbose)\n console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.checkout(EXAMPLES_REPO_COMMIT)\n if (verbose)\n console.log(`Copying example directory to target: ${targetDir}`)\n await cp(join(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true })\n } catch (error) {\n console.error(\"Error cloning example repo:\", error)\n throw error\n } finally {\n if (verbose) console.log(`Cleaning up temporary directory: ${tmpDir}`)\n await rm(tmpDir, { recursive: true, force: true })\n }\n}\n\nasync function createPlugin(\n targetDir: string,\n pluginName: string,\n verbose: boolean = true,\n) {\n await createExampleRepo(targetDir, verbose)\n await removeUnwantedFiles(targetDir, verbose)\n await updatePackageJson(targetDir, pluginName)\n if (verbose) console.log(`Plugin created successfully in ${targetDir}`)\n}\n\nasync function runCliMode() {\n const program = new Command()\n .version(packageJson.version)\n .addOption(\n new Option(\"-v, --verbose\", \"Verbose output\").default(DEFAULT_VERBOSE),\n )\n .addOption(new Option(\"-n, --name <name>\", \"The name of the plugin\"))\n program.parse()\n return program.opts()\n}\n\nasync function runInteractiveMode() {\n const pluginName = await input({\n message: \"What is the name of your plugin?\",\n required: true,\n default: DEFAULT_PLUGIN_NAME,\n validate: (value) => {\n if (!/^[a-z0-9-]+$/.test(value)) {\n return \"Plugin name must only contain lowercase letters, numbers, and hyphens\"\n }\n return true\n },\n })\n return { pluginName }\n}\n\nasync function main() {\n if (CHECK_UPDATE) {\n const update = await updateCheck(packageJson)\n if (update)\n console.log(\n `Update available: ${update.latest} (current: ${packageJson.version})`,\n )\n }\n console.log(`${packageJson.name} v${packageJson.version}`)\n\n const cliOpts = await runCliMode()\n const cwd = process.cwd()\n if (cliOpts.verbose) console.log(\"Verbose output enabled\")\n if (cliOpts.verbose) console.log(\"Current working directory:\", cwd)\n const pluginName = cliOpts.name ?? (await runInteractiveMode()).pluginName\n const pluginDir = join(cwd, pluginName)\n if (cliOpts.verbose) console.log(`Creating plugin: ${pluginName}`)\n await mkdir(pluginDir, { recursive: true })\n await createPlugin(pluginDir, pluginName, cliOpts.verbose)\n}\n\nmain()\n","{\n \"name\": \"@snaptrude/create-snaptrude-plugin\",\n \"version\": \"0.0.4\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"bin\": {\n \"create-snaptrude-plugin\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"check-types\": \"tsc --noEmit\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean-dist\": \"rm -rf dist\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.5.4\"\n },\n \"dependencies\": {\n \"@commander-js/extra-typings\": \"^14.0.0\",\n \"@inquirer/prompts\": \"^8.3.0\",\n \"commander\": \"^14.0.3\",\n \"simple-git\": \"^3.32.3\",\n \"update-check\": \"^1.5.4\"\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../package.json","../src/index.ts","../src/create.ts","../src/register.ts"],"sourcesContent":["{\n \"name\": \"@snaptrude/create-snaptrude-plugin\",\n \"version\": \"0.0.6\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"bin\": {\n \"create-snaptrude-plugin\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"check-types\": \"tsc --noEmit\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean-dist\": \"rm -rf dist\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.5.4\"\n },\n \"dependencies\": {\n \"@commander-js/extra-typings\": \"^14.0.0\",\n \"@inquirer/prompts\": \"^8.3.0\",\n \"commander\": \"^14.0.3\",\n \"simple-git\": \"^3.32.3\",\n \"tar\": \"^7.5.11\",\n \"update-check\": \"^1.5.4\"\n }\n}\n","#!/usr/bin/env node\n\nimport packageJson from \"../package.json\"\nimport updateCheck from \"update-check\"\nimport { Command, Option } from \"@commander-js/extra-typings\"\nimport { handleCreate } from \"./create\"\nimport { handleRegister } from \"./register\"\n\nconst CHECK_UPDATE = false\n\nasync function main() {\n if (CHECK_UPDATE) {\n const update = await updateCheck(packageJson)\n if (update) {\n console.log(\n `Update available: ${update.latest} (current: ${packageJson.version})`,\n )\n }\n }\n\n const program = new Command()\n .name(\"create-snaptrude-plugin\")\n .version(packageJson.version)\n .description(\"Snaptrude plugin CLI — scaffold and register plugins\")\n\n program\n .command(\"create\", { isDefault: true })\n .description(\"Scaffold a new Snaptrude plugin project\")\n .addOption(new Option(\"-v, --verbose\", \"Verbose output\").default(false))\n .addOption(new Option(\"-n, --name <name>\", \"Plugin name\"))\n .action(handleCreate)\n\n program\n .command(\"register\")\n .description(\"Build, bundle, and register a plugin with Snaptrude\")\n .addOption(\n new Option(\"-p, --path <dir>\", \"Path to the build output directory\").default(\"./dist\"),\n )\n .addOption(new Option(\"-t, --token <token>\", \"Auth token (or set SNAPTRUDE_TOKEN env var)\"))\n .addOption(\n new Option(\n \"--api-url <url>\",\n \"Snaptrude API base URL (or set SNAPTRUDE_API_URL env var)\",\n ).default(\"https://save2.snaptru.de\"),\n )\n .addOption(new Option(\"--skip-build\", \"Skip the build step\").default(false))\n .addOption(new Option(\"-v, --verbose\", \"Verbose output\").default(false))\n .action(handleRegister)\n\n await program.parseAsync()\n}\n\nmain()\n","import { input } from \"@inquirer/prompts\"\nimport simpleGit from \"simple-git\"\nimport { cp, mkdir, readFile, rm, writeFile, mkdtemp } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { tmpdir } from \"node:os\"\nimport { randomUUID } from \"node:crypto\"\n\nconst DEFAULT_PLUGIN_NAME = \"my-plugin\"\n\nconst EXAMPLES_REPO =\n \"https://bitbucket.org/snaptrude/snaptrude-plugin-examples.git\"\nconst EXAMPLES_REPO_COMMIT = \"9bb0b4d1d1a76217437f838ff23613bde2e3e79e\"\nconst EXAMPLE_PATH_IN_REPO = \"snaptrude-plugin\"\n\nconst UNWANTED_DIRS: string[] = []\nconst UNWANTED_FILES: string[] = []\n\nasync function askPluginName(): Promise<string> {\n return input({\n message: \"What is the name of your plugin?\",\n required: true,\n default: DEFAULT_PLUGIN_NAME,\n validate: (value) => {\n if (!/^[a-z0-9-]+$/.test(value)) {\n return \"Plugin name must only contain lowercase letters, numbers, and hyphens\"\n }\n return true\n },\n })\n}\n\nasync function updatePkgJson(targetDir: string, pluginName: string) {\n const pkgPath = join(targetDir, \"package.json\")\n const pkg = JSON.parse(await readFile(pkgPath, \"utf8\"))\n pkg.name = pluginName\n pkg.id = randomUUID()\n await writeFile(pkgPath, JSON.stringify(pkg, null, 2))\n}\n\nasync function removeUnwantedFiles(targetDir: string, verbose: boolean) {\n if (verbose) console.log(`Removing unwanted files from ${targetDir}`)\n for (const dir of UNWANTED_DIRS) {\n await rm(join(targetDir, dir), { recursive: true, force: true })\n }\n for (const file of UNWANTED_FILES) {\n await rm(join(targetDir, file), { force: true })\n }\n}\n\nasync function cloneExample(targetDir: string, verbose: boolean) {\n if (verbose) console.log(`Creating example repo in ${targetDir}`)\n const tmpDir = await mkdtemp(join(tmpdir(), \"snaptrude-plugin-\"))\n if (verbose) console.log(`Create temporary directory for clone: ${tmpDir}`)\n try {\n const git = simpleGit(tmpDir)\n if (verbose) console.log(`Cloning example repo to ${tmpDir}`)\n await git.clone(EXAMPLES_REPO, \".\", [\"--no-checkout\", \"--depth=1\"])\n if (verbose) console.log(`Fetching specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.fetch([\"origin\", EXAMPLES_REPO_COMMIT, \"--depth=1\"])\n if (verbose) console.log(`Checking out specific commit: ${EXAMPLES_REPO_COMMIT}`)\n await git.checkout(EXAMPLES_REPO_COMMIT)\n if (verbose) console.log(`Copying example directory to target: ${targetDir}`)\n await cp(join(tmpDir, EXAMPLE_PATH_IN_REPO), targetDir, { recursive: true })\n } catch (error) {\n console.error(\"Error cloning example repo:\", error)\n throw error\n } finally {\n if (verbose) console.log(`Cleaning up temporary directory: ${tmpDir}`)\n await rm(tmpDir, { recursive: true, force: true })\n }\n}\n\nexport async function handleCreate(opts: { verbose: boolean; name?: string }) {\n const pluginName = opts.name ?? (await askPluginName())\n const pluginDir = join(process.cwd(), pluginName)\n\n if (opts.verbose) console.log(`Creating plugin: ${pluginName}`)\n await mkdir(pluginDir, { recursive: true })\n await cloneExample(pluginDir, opts.verbose)\n await removeUnwantedFiles(pluginDir, opts.verbose)\n await updatePkgJson(pluginDir, pluginName)\n\n console.log(`Plugin \"${pluginName}\" created successfully in ${pluginDir}`)\n}\n","import { readFile, readdir, access } from \"node:fs/promises\"\nimport { join, resolve } from \"node:path\"\nimport { createHash } from \"node:crypto\"\nimport { execSync } from \"node:child_process\"\nimport { create as tarCreate } from \"tar\"\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p)\n return true\n } catch {\n return false\n }\n}\n\nfunction createBundle(buildDir: string, entries: string[], verbose: boolean): Promise<Buffer> {\n if (verbose) {\n console.log(`Bundling ${entries.length} entries: ${entries.join(\", \")}`)\n }\n\n return new Promise<Buffer>((res, rej) => {\n const chunks: Buffer[] = []\n const stream = tarCreate({ gzip: true, cwd: buildDir }, entries)\n stream.on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n stream.on(\"end\", () => res(Buffer.concat(chunks)))\n stream.on(\"error\", rej)\n })\n}\n\ninterface PluginManifest {\n name: string\n version: string\n description?: string\n apiVersion?: string\n workerUrl?: string\n uiUrl?: string\n id?: string\n bundleUrl?: string\n [key: string]: unknown\n}\n\ninterface RegisterResult {\n id: string\n name: string\n version: string\n bundleUrl: string\n reviewStatus: string\n}\n\nasync function uploadBundle(\n apiUrl: string,\n token: string,\n manifest: PluginManifest,\n tarBuffer: Buffer,\n checksum: string,\n): Promise<RegisterResult> {\n const formData = new FormData()\n formData.append(\n \"bundle\",\n new Blob([new Uint8Array(tarBuffer) as unknown as ArrayBuffer], {\n type: \"application/gzip\",\n }),\n \"bundle.tar.gz\",\n )\n formData.append(\"manifest\", JSON.stringify(manifest))\n formData.append(\"checksum\", checksum)\n\n const res = await fetch(`${apiUrl}/plugins/register`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: res.statusText }))\n console.error(`Error registering plugin: ${(err as { error: string }).error}`)\n process.exit(1)\n }\n\n return (await res.json()) as RegisterResult\n}\n\n// function detectPackageManager(): \"pnpm\" | \"yarn\" | \"npm\" {\n// for (const [lockfile, pm] of [\n// [\"pnpm-lock.yaml\", \"pnpm\"],\n// [\"yarn.lock\", \"yarn\"],\n// [\"package-lock.json\", \"npm\"],\n// ] as const) {\n// try {\n// const fullPath = join(process.cwd(), lockfile)\n// require(\"node:fs\").accessSync(fullPath)\n// return pm\n// } catch {\n// // not found, try next\n// }\n// }\n// return \"npm\"\n// }\n\nfunction runBuild(verbose: boolean) {\n // const pm = detectPackageManager()\n const cmd = `npm run build:prod`\n console.log(`Building plugin (${cmd})...`)\n try {\n execSync(cmd, {\n cwd: process.cwd(),\n stdio: verbose ? \"inherit\" : \"pipe\",\n })\n } catch (err) {\n console.error(\"Build failed:\")\n if (!verbose && err instanceof Error && \"stderr\" in err) {\n console.error((err as { stderr: Buffer }).stderr?.toString())\n }\n process.exit(1)\n }\n}\n\nexport async function handleRegister(opts: {\n path: string\n token?: string\n apiUrl: string\n verbose: boolean\n skipBuild: boolean\n}) {\n if (!opts.skipBuild) {\n runBuild(opts.verbose)\n }\n\n const buildDir = resolve(opts.path)\n const rawToken = opts.token ?? process.env.SNAPTRUDE_TOKEN\n const token = rawToken?.startsWith(\"Bearer \") ? rawToken.slice(7) : rawToken\n const apiUrl = opts.apiUrl ?? process.env.SNAPTRUDE_API_URL\n\n if (!token) {\n console.error(\n \"Error: Auth token is required. Use --token <token> or set SNAPTRUDE_TOKEN env var.\",\n )\n process.exit(1)\n }\n if (!apiUrl) {\n console.error(\n \"Error: API URL is required. Use --api-url <url> or set SNAPTRUDE_API_URL env var.\",\n )\n process.exit(1)\n }\n\n const manifestPath = join(buildDir, \"manifest.json\")\n if (!(await pathExists(buildDir))) {\n console.error(`Error: Build directory not found: ${buildDir}`)\n process.exit(1)\n }\n if (!(await pathExists(manifestPath))) {\n console.error(`Error: manifest.json not found in ${buildDir}`)\n process.exit(1)\n }\n\n const manifest: PluginManifest = JSON.parse(\n await readFile(manifestPath, \"utf8\"),\n )\n if (!manifest.name || !manifest.version) {\n console.error(\"Error: manifest.json must contain name and version fields\")\n process.exit(1)\n }\n\n if (opts.verbose) console.log(`Reading build from: ${buildDir}`)\n\n console.log(\"Creating bundle...\")\n const entries = (await readdir(buildDir)).filter((e) => e !== \"manifest.json\")\n if (entries.length === 0) {\n throw new Error(\"No files to bundle (only manifest.json found)\")\n }\n\n const tarBuffer = await createBundle(buildDir, entries, opts.verbose)\n const checksum = createHash(\"sha256\").update(tarBuffer).digest(\"hex\")\n if (opts.verbose) {\n console.log(\n `Bundle size: ${(tarBuffer.length / 1024).toFixed(1)} KB, checksum: ${checksum}`,\n )\n }\n\n console.log(\"Registering plugin...\")\n const result = await uploadBundle(apiUrl, token, manifest, tarBuffer, checksum)\n\n console.log(\"\")\n console.log(\"Plugin registered successfully!\")\n console.log(` ID: ${result.id}`)\n console.log(` Name: ${result.name}`)\n console.log(` Version: ${result.version}`)\n console.log(` Bundle: ${result.bundleUrl}`)\n console.log(` Status: ${result.reviewStatus}`)\n}\n"],"mappings":"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","names":["readFile","join"]}
@@ -0,0 +1,5 @@
1
+ export declare function handleCreate(opts: {
2
+ verbose: boolean;
3
+ name?: string;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/create.ts"],"names":[],"mappings":"AAwEA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,iBAW3E"}
@@ -0,0 +1,8 @@
1
+ export declare function handleRegister(opts: {
2
+ path: string;
3
+ token?: string;
4
+ apiUrl: string;
5
+ verbose: boolean;
6
+ skipBuild: boolean;
7
+ }): Promise<void>;
8
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/register.ts"],"names":[],"mappings":"AAqHA,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;CACnB,iBAmEA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snaptrude/create-snaptrude-plugin",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -24,6 +24,7 @@
24
24
  "@inquirer/prompts": "^8.3.0",
25
25
  "commander": "^14.0.3",
26
26
  "simple-git": "^3.32.3",
27
+ "tar": "^7.5.11",
27
28
  "update-check": "^1.5.4"
28
29
  },
29
30
  "scripts": {