@dashai/cli 0.7.1 → 0.8.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/dist/bin.js CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, watch, readdirSync, chmodSync, renameSync, mkdtempSync, statSync } from 'fs';
3
3
  import { tmpdir, platform, homedir } from 'os';
4
- import { resolve, join, dirname, basename, relative } from 'path';
4
+ import { resolve, join, relative, dirname, basename, sep } from 'path';
5
5
  import pc from 'picocolors';
6
6
  import { intro, log, select, isCancel, cancel, spinner, outro, text, multiselect, confirm } from '@clack/prompts';
7
7
  import { Command, Option } from 'commander';
8
8
  import { spawn, spawnSync } from 'child_process';
9
9
  import open from 'open';
10
+ import AdmZip from 'adm-zip';
10
11
  import * as tar from 'tar';
11
12
  import { pathToFileURL } from 'url';
12
13
 
@@ -3223,13 +3224,13 @@ async function resolveDevPort(requested) {
3223
3224
  }
3224
3225
  async function isPortFree(port) {
3225
3226
  const { createServer } = await import('net');
3226
- return new Promise((resolve12) => {
3227
+ return new Promise((resolve13) => {
3227
3228
  const server = createServer();
3228
3229
  server.once("error", () => {
3229
- resolve12(false);
3230
+ resolve13(false);
3230
3231
  });
3231
3232
  server.once("listening", () => {
3232
- server.close(() => resolve12(true));
3233
+ server.close(() => resolve13(true));
3233
3234
  });
3234
3235
  server.listen(port, "0.0.0.0");
3235
3236
  });
@@ -3426,7 +3427,7 @@ async function loginCommand(options) {
3426
3427
  return 0;
3427
3428
  }
3428
3429
  function sleep(ms) {
3429
- return new Promise((resolve12) => setTimeout(resolve12, ms));
3430
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
3430
3431
  }
3431
3432
  function formatSeconds(seconds) {
3432
3433
  if (seconds < 60) return `${seconds}s`;
@@ -4107,6 +4108,129 @@ async function moduleConvertCommand(opts) {
4107
4108
  return 1;
4108
4109
  }
4109
4110
  }
4111
+ var IGNORE_DIRS = /* @__PURE__ */ new Set([
4112
+ "node_modules",
4113
+ ".git",
4114
+ ".next",
4115
+ ".turbo",
4116
+ ".vercel",
4117
+ "dist",
4118
+ "build",
4119
+ "out",
4120
+ "coverage",
4121
+ ".cache"
4122
+ ]);
4123
+ var MAX_FILES = 5e3;
4124
+ var MAX_TOTAL_BYTES = 25 * 1024 * 1024;
4125
+ function deriveSlug2(input) {
4126
+ return input.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
4127
+ }
4128
+ function isLocalEnvSecret(name) {
4129
+ if (name.endsWith(".example")) return false;
4130
+ return name === ".env" || name.startsWith(".env.");
4131
+ }
4132
+ function exportModule(opts) {
4133
+ const dir = resolve(opts.dir);
4134
+ const manifestPath2 = join(dir, "module.json");
4135
+ if (!existsSync(manifestPath2)) {
4136
+ throw new Error(
4137
+ "No module.json found \u2014 point at a module directory (run `dashwise module convert` first if needed)."
4138
+ );
4139
+ }
4140
+ let manifest;
4141
+ try {
4142
+ manifest = JSON.parse(readFileSync(manifestPath2, "utf-8"));
4143
+ } catch (err) {
4144
+ throw new Error(`module.json is not valid JSON: ${err.message}`);
4145
+ }
4146
+ const slug = manifest.slug?.trim() || deriveSlug2(manifest.name?.trim() || basename(dir));
4147
+ const name = manifest.name?.trim() || slug;
4148
+ const outPath = resolve(opts.out ?? `${slug}.zip`);
4149
+ const zip = new AdmZip();
4150
+ let fileCount = 0;
4151
+ let totalBytes = 0;
4152
+ const walk2 = (abs) => {
4153
+ let entries;
4154
+ try {
4155
+ entries = readdirSync(abs);
4156
+ } catch {
4157
+ return;
4158
+ }
4159
+ for (const entry of entries) {
4160
+ const p = join(abs, entry);
4161
+ let st;
4162
+ try {
4163
+ st = statSync(p);
4164
+ } catch {
4165
+ continue;
4166
+ }
4167
+ if (st.isDirectory()) {
4168
+ if (IGNORE_DIRS.has(entry)) continue;
4169
+ walk2(p);
4170
+ continue;
4171
+ }
4172
+ if (isLocalEnvSecret(entry)) continue;
4173
+ if (resolve(p) === outPath) continue;
4174
+ const rel = relative(dir, p).split(sep).join("/");
4175
+ const data = readFileSync(p);
4176
+ zip.addFile(rel, data);
4177
+ fileCount += 1;
4178
+ totalBytes += data.length;
4179
+ if (fileCount > MAX_FILES) {
4180
+ throw new Error(
4181
+ `Too many files (> ${MAX_FILES}); the import limit would reject this archive. Is a build-output dir not being ignored?`
4182
+ );
4183
+ }
4184
+ }
4185
+ };
4186
+ walk2(dir);
4187
+ if (fileCount === 0 || !zip.getEntry("module.json")) {
4188
+ throw new Error("Nothing to export (no module.json reached the archive).");
4189
+ }
4190
+ zip.writeZip(outPath);
4191
+ return {
4192
+ slug,
4193
+ name,
4194
+ outPath,
4195
+ fileCount,
4196
+ totalBytes,
4197
+ exceedsImportLimits: totalBytes > MAX_TOTAL_BYTES
4198
+ };
4199
+ }
4200
+ function humanSize(bytes) {
4201
+ if (bytes < 1024) return `${bytes} B`;
4202
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
4203
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
4204
+ }
4205
+ async function moduleExportCommand(opts) {
4206
+ try {
4207
+ const result = exportModule({
4208
+ dir: opts.dir ?? process.cwd(),
4209
+ ...opts.out !== void 0 ? { out: opts.out } : {}
4210
+ });
4211
+ console.log(
4212
+ `Exported "${result.name}" \u2192 ${relative(process.cwd(), result.outPath) || result.outPath} (${result.fileCount} files, ${humanSize(result.totalBytes)}).`
4213
+ );
4214
+ console.log(
4215
+ '\nImport-ready: an operator can deploy it via the HQ admin panel ("Import ZIP")\nor POST /api/admin/modules/import/zip. module.json is at the archive root.'
4216
+ );
4217
+ console.log(
4218
+ "Note: binary assets (images/fonts) are dropped on import (text-only commit);\nnode_modules, build output, .git, and local .env files were excluded."
4219
+ );
4220
+ if (result.exceedsImportLimits) {
4221
+ console.warn(
4222
+ `
4223
+ \u26A0 Uncompressed content exceeds the import's 25 MiB cap \u2014 the ZIP may be rejected.`
4224
+ );
4225
+ }
4226
+ return 0;
4227
+ } catch (err) {
4228
+ console.error(
4229
+ `export failed: ${err instanceof Error ? err.message : String(err)}`
4230
+ );
4231
+ return 1;
4232
+ }
4233
+ }
4110
4234
 
4111
4235
  // src/commands/module/register.ts
4112
4236
  init_config();
@@ -6456,7 +6580,7 @@ function formatTimestamp(iso) {
6456
6580
  return iso.slice(0, 19).replace("T", " ");
6457
6581
  }
6458
6582
  function sleep2(ms) {
6459
- return new Promise((resolve12) => setTimeout(resolve12, ms));
6583
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
6460
6584
  }
6461
6585
 
6462
6586
  // src/commands/module/action-log.ts
@@ -6550,7 +6674,7 @@ async function runTail2(installationId, options) {
6550
6674
  }
6551
6675
  dim(`(tail: transient error \u2014 ${err.message})`);
6552
6676
  }
6553
- await new Promise((resolve12) => setTimeout(resolve12, POLL_INTERVAL_MS2));
6677
+ await new Promise((resolve13) => setTimeout(resolve13, POLL_INTERVAL_MS2));
6554
6678
  }
6555
6679
  }
6556
6680
  function printTable(rows) {
@@ -6767,6 +6891,16 @@ moduleCmd.command("register [dir]").description(
6767
6891
  });
6768
6892
  }
6769
6893
  );
6894
+ moduleCmd.command("export [dir]").description(
6895
+ 'Package a local module into a portable <slug>.zip (module.json at the root, deps/build-output/VCS/local-.env excluded) \u2014 the exact shape the operator ZIP-import (HQ "Import ZIP" / POST /api/admin/modules/import/zip) expects. Closes the build \u2192 export \u2192 operator-deploy round-trip.'
6896
+ ).option("--out <path>", "Output ZIP path (default: <slug>.zip in the current directory).").action(
6897
+ async (dir, cmdOpts) => {
6898
+ process.exitCode = await moduleExportCommand({
6899
+ ...dir !== void 0 ? { dir } : {},
6900
+ ...cmdOpts.out !== void 0 ? { out: cmdOpts.out } : {}
6901
+ });
6902
+ }
6903
+ );
6770
6904
  moduleCmd.command("generate").description("Regenerate typed wrappers from the local module.json into node_modules/@dashai/generated/.").option("--dir <path>", "Project root (default: current directory).").option("--dry-run", "Print summary without writing files.").action(async (cmdOpts) => {
6771
6905
  process.exitCode = await moduleGenerateCommand({
6772
6906
  ...cmdOpts.dir !== void 0 ? { dir: cmdOpts.dir } : {},
@@ -7132,7 +7266,7 @@ fieldCmd.command("set-type <table-slug> <field-slug> <new-type>").description(
7132
7266
  }
7133
7267
  })();
7134
7268
  function getVersion() {
7135
- return "0.7.1";
7269
+ return "0.8.1";
7136
7270
  }
7137
7271
  function parseIntOption(value) {
7138
7272
  const n = Number.parseInt(value, 10);