@drops-it/dpm 1.0.6 → 1.1.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/README.md CHANGED
@@ -1,43 +1,42 @@
1
1
  # dpm
2
2
 
3
- **Drops Package Manager** — a fast, privacy-minded CLI for installing, publishing, and auditing Node packages on [dpm.drops-it.com](https://dpm.drops-it.com), with automatic npm fallback for anything not on the registry.
3
+ **Drops Package Manager** — a fast, privacy-minded CLI for installing, publishing, running, and auditing packages on [dpm.drops-it.com](https://dpm.drops-it.com), with automatic npm fallback for anything not on the registry.
4
4
 
5
5
  ```text
6
6
  ┌─────────────────────────────────────────┐
7
7
  │ dpm i lodash │
8
- │ → resolving 1 top-level dependency │
8
+ │ → installing dependencies… │
9
9
  │ ✔ added 1 package in 0.4s │
10
10
  │ found 0 vulnerabilities │
11
11
  └─────────────────────────────────────────┘
12
12
  ```
13
13
 
14
- Requires **Node.js 22+**.
14
+ The Windows installer ships a managed DPM runtime (used for install + `dpm run`). You never need to invoke another runtime by name.
15
15
 
16
16
  ---
17
17
 
18
18
  ## Install
19
19
 
20
- **From DPM (recommended)**
20
+ **Windows**
21
+
22
+ Download [`dpm.exe`](https://dpm.drops-it.com/download/dpm.exe) and run it once (double-click). It installs into your user profile, adds `dpm` to PATH, installs the DPM runtime, and creates a Start Menu shortcut.
21
23
 
22
24
  ```bash
23
- # after you already have any dpm binary once:
24
- dpm i -g dpm
25
+ # after install, open a NEW terminal:
26
+ dpm --help
27
+ dpm login
25
28
  ```
26
29
 
27
30
  **From npm**
28
31
 
29
32
  ```bash
30
- npm i -g @drops-it/dpm
33
+ npm install -g @drops-it/dpm
31
34
  ```
32
35
 
33
- **Windows launcher**
34
-
35
- Download [`dpm.exe`](https://dpm.drops-it.com/download/dpm.exe) — it boots the CLI if Node is installed.
36
-
37
- Verify:
36
+ **How To Update DPM** (once you already have any `dpm`)
38
37
 
39
38
  ```bash
40
- dpm --version
39
+ dpm i -g dpm
41
40
  ```
42
41
 
43
42
  ---
@@ -49,6 +48,7 @@ dpm login # browser device flow (Drops-IT)
49
48
  dpm init # create package.json
50
49
  dpm i lodash # add + install
51
50
  dpm i # install from package.json
51
+ dpm run starter # run a package.json script
52
52
  dpm publish --access public
53
53
  ```
54
54
 
@@ -74,6 +74,13 @@ dpm whoami
74
74
  | `dpm rm -g` | Remove the global **dpm** CLI + bin shims |
75
75
  | `dpm rm -g <pkg>` | Uninstall a global package |
76
76
  | `dpm update` | Re-resolve and reinstall |
77
+ | `dpm run <script>` | Run a `package.json` script |
78
+ | `dpm test` | Run tests |
79
+ | `dpm x` / `exec` | Execute a package binary |
80
+ | `dpm build` | Bundle with the DPM runtime |
81
+ | `dpm --watch <file>` | Run a file with reload (e.g. API server) |
82
+ | `dpm <file>` | Run a JS/TS file |
83
+ | `dpm create` / `repl` / `pm` / `link` / `unlink` / `outdated` / `patch` / `why` / `info` / `upgrade` | Toolkit commands (managed runtime) |
77
84
  | `dpm search <q>` | Search the registry |
78
85
  | `dpm view <name>` | Show package metadata |
79
86
  | `dpm publish [--access public\|private]` | Publish the current package |
@@ -86,9 +93,9 @@ dpm whoami
86
93
 
87
94
  ## Install experience
88
95
 
89
- - One live status line with a spinner while packages resolve / download / extract
96
+ - Fast parallel installs via the managed DPM runtime against the DPM registry (`/npm` facade)
90
97
  - Colorized success, warnings, and severity
91
- - Post-install **advisory report** (npm-compatible bulk advisories)
98
+ - Post-install **advisory report**
92
99
  - Fix hints: `dpm audit fix` / `dpm audit fix --force`
93
100
  - Transitive fixes can land in `package.json` `overrides` so they stick
94
101
 
@@ -105,11 +112,11 @@ dpm audit fix
105
112
  On the **first `dpm` command in a new terminal**, dpm checks the DPM registry for a newer CLI.
106
113
 
107
114
  ```text
108
- ▲ dpm update available: 1.0.3 → 1.0.4
115
+ ▲ dpm update available: 1.1.0 → 1.1.1
109
116
  Update with: dpm i -g dpm
110
117
  ```
111
118
 
112
- No daily nag. No npm noise — updates are driven by the DPM package `dpm`.
119
+ No daily nag. Updates are driven by the DPM package `dpm`.
113
120
 
114
121
  ---
115
122
 
package/dist/config.js CHANGED
@@ -4,16 +4,22 @@ import path from "node:path";
4
4
  import YAML from "yaml";
5
5
  const CONFIG_PATH = path.join(homedir(), ".dpmrc");
6
6
  export async function loadConfig() {
7
+ let config;
7
8
  try {
8
9
  const raw = await readFile(CONFIG_PATH, "utf8");
9
- return JSON.parse(raw);
10
+ config = JSON.parse(raw);
10
11
  }
11
12
  catch {
12
- return {
13
- registry: process.env.DPM_REGISTRY || "https://dpm.drops-it.com",
14
- token: process.env.DPM_TOKEN,
13
+ config = {
14
+ registry: "https://dpm.drops-it.com",
15
15
  };
16
16
  }
17
+ // Env wins (Pterodactyl bootstrap sets DPM_REGISTRY=npmjs).
18
+ if (process.env.DPM_REGISTRY)
19
+ config.registry = process.env.DPM_REGISTRY;
20
+ if (process.env.DPM_TOKEN)
21
+ config.token = process.env.DPM_TOKEN;
22
+ return config;
17
23
  }
18
24
  export async function saveConfig(config) {
19
25
  await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
package/dist/index.js CHANGED
@@ -3,11 +3,97 @@ import { exec } from "node:child_process";
3
3
  import { Command } from "commander";
4
4
  import { DpmApi } from "./api.js";
5
5
  import { loadConfig, saveConfig, writePackageJson } from "./config.js";
6
- import { addPackages, auditFix, installAll, installGlobalPackages, packTarball, removeGlobalPackages, removePackage, runAudit, } from "./install.js";
6
+ import { addPackages, auditFix, installGlobalPackages, packTarball, removeGlobalPackages, removePackage, runAudit, syncLockFromNodeModules, } from "./install.js";
7
+ import { ensureInstallConfig, runtimeInstallEnv, spawnRuntime } from "./runtime.js";
7
8
  import { createUi } from "./ui.js";
8
9
  import { CLI_VERSION, maybeNotifyUpdate } from "./updateCheck.js";
10
+ /**
11
+ * Commands handled by DPM itself (registry / auth / branded PM wrappers).
12
+ * Everything else — including all other runtime CLI commands — is forwarded.
13
+ */
14
+ const NATIVE_COMMANDS = new Set([
15
+ "config",
16
+ "login",
17
+ "logout",
18
+ "whoami",
19
+ "init",
20
+ "search",
21
+ "view",
22
+ "install",
23
+ "i",
24
+ "add",
25
+ "a",
26
+ "update",
27
+ "remove",
28
+ "rm",
29
+ "uninstall",
30
+ "audit",
31
+ "publish",
32
+ "run",
33
+ "test",
34
+ "x",
35
+ "exec",
36
+ "build",
37
+ "help",
38
+ ]);
39
+ function argvAfterCommand(names) {
40
+ const idx = process.argv.findIndex((a, i) => i >= 2 && names.includes(a));
41
+ if (idx < 0)
42
+ return process.argv.slice(2);
43
+ return process.argv.slice(idx + 1);
44
+ }
45
+ function shouldForwardToRuntime(argv) {
46
+ const args = argv.slice(2);
47
+ if (args.length === 0)
48
+ return false;
49
+ const first = args[0];
50
+ if (first === "-h" ||
51
+ first === "--help" ||
52
+ first === "-V" ||
53
+ first === "--version") {
54
+ return false;
55
+ }
56
+ if (first.startsWith("-"))
57
+ return true;
58
+ return !NATIVE_COMMANDS.has(first);
59
+ }
60
+ /** Spawns the managed runtime with registry env for package-manager ops. */
61
+ async function runtimePm(args) {
62
+ const config = await loadConfig();
63
+ await ensureInstallConfig(config, process.cwd());
64
+ return spawnRuntime({
65
+ args,
66
+ env: runtimeInstallEnv(config),
67
+ brandOutput: true,
68
+ });
69
+ }
70
+ /** Spawns the managed runtime for non-PM commands (run, test, build, …). */
71
+ async function runtimeRaw(args) {
72
+ return spawnRuntime({
73
+ args,
74
+ brandOutput: true,
75
+ });
76
+ }
9
77
  const program = new Command();
10
- program.name("dpm").description("Drops Package Manager").version(CLI_VERSION);
78
+ program
79
+ .name("dpm")
80
+ .description("Drops Package Manager — same commands as a full JS toolkit, plus DPM registry auth/publish")
81
+ .version(CLI_VERSION)
82
+ .addHelpText("after", `
83
+ Runtime (same surface as a modern JS toolkit; binary is managed by DPM):
84
+ dpm --watch <file> Run a file with reload
85
+ dpm <file> Run a JS/TS file
86
+ dpm create <template> Scaffold from a template
87
+ dpm repl Interactive REPL
88
+ dpm pm <subcommand> Package utilities (ls, cache, …)
89
+ dpm outdated List outdated deps
90
+ dpm link / unlink Link local packages
91
+ dpm patch / why / info Extra package tools
92
+ dpm upgrade Upgrade the managed DPM runtime
93
+
94
+ DPM registry:
95
+ dpm login | publish | search | view | config
96
+ `);
11
97
  program.hook("preAction", async () => {
12
98
  await maybeNotifyUpdate();
13
99
  });
@@ -81,17 +167,25 @@ program
81
167
  });
82
168
  program
83
169
  .command("init")
84
- .description("Create a package.json")
170
+ .description("Create a package.json (or pass flags through to the runtime init)")
171
+ .allowUnknownOption(true)
172
+ .argument("[args...]")
85
173
  .action(async () => {
86
- await writePackageJson({
87
- name: "my-package",
88
- version: "1.0.0",
89
- description: "",
90
- main: "index.js",
91
- license: "MIT",
92
- dependencies: {},
93
- });
94
- console.log("Created package.json");
174
+ const rest = argvAfterCommand(["init"]);
175
+ // No args → simple DPM scaffold; any flags/args → full runtime init
176
+ if (rest.length === 0) {
177
+ await writePackageJson({
178
+ name: "my-package",
179
+ version: "1.0.0",
180
+ description: "",
181
+ main: "index.js",
182
+ license: "MIT",
183
+ dependencies: {},
184
+ });
185
+ console.log("Created package.json");
186
+ return;
187
+ }
188
+ process.exit(await runtimeRaw(["init", ...rest]));
95
189
  });
96
190
  program
97
191
  .command("search")
@@ -118,53 +212,81 @@ program
118
212
  program
119
213
  .command("install")
120
214
  .alias("i")
121
- .argument("[packages...]", "optional packages to add (like npm i lodash)")
122
- .option("-g, --global", "install into the npm global prefix")
123
- .description("Install package.json deps, or add packages then install")
124
- .action(async (packages, opts) => {
125
- const api = new DpmApi(await loadConfig());
126
- const ui = createUi();
127
- if (opts.global) {
128
- await installGlobalPackages(api, packages, ui);
215
+ .description("Install deps via DPM registry (all runtime install flags supported)")
216
+ .allowUnknownOption(true)
217
+ .argument("[args...]")
218
+ .action(async () => {
219
+ const rest = argvAfterCommand(["install", "i"]);
220
+ const global = rest.includes("-g") || rest.includes("--global");
221
+ const packages = rest.filter((a) => !a.startsWith("-"));
222
+ if (global && packages.length > 0) {
223
+ const api = new DpmApi(await loadConfig());
224
+ await installGlobalPackages(api, packages, createUi());
129
225
  return;
130
226
  }
131
- if (packages.length > 0) {
132
- await addPackages(api, packages, process.cwd(), ui);
133
- return;
227
+ if (!global && packages.length > 0 && rest.every((a) => !a.startsWith("-") || a === packages[0])) {
228
+ // Plain `dpm i lodash` / `dpm install lodash@1` without flags → add flow
229
+ if (rest.every((a) => !a.startsWith("-"))) {
230
+ const api = new DpmApi(await loadConfig());
231
+ await addPackages(api, packages, process.cwd(), createUi());
232
+ return;
233
+ }
134
234
  }
135
- await installAll(api, process.cwd(), ui);
235
+ const code = await runtimePm(["install", ...rest]);
236
+ if (code !== 0)
237
+ process.exit(code);
238
+ await syncLockFromNodeModules(process.cwd());
136
239
  });
137
240
  program
138
241
  .command("add")
139
- .argument("<packages...>")
140
- .option("-g, --global", "install into the npm global prefix")
141
- .description("Add dependencies to package.json and install")
142
- .action(async (packages, opts) => {
143
- const api = new DpmApi(await loadConfig());
144
- const ui = createUi();
145
- if (opts.global) {
146
- await installGlobalPackages(api, packages, ui);
242
+ .alias("a")
243
+ .description("Add dependencies (runtime add flags supported)")
244
+ .allowUnknownOption(true)
245
+ .argument("[args...]")
246
+ .action(async () => {
247
+ const rest = argvAfterCommand(["add", "a"]);
248
+ const global = rest.includes("-g") || rest.includes("--global");
249
+ const packages = rest.filter((a) => !a.startsWith("-"));
250
+ if (global) {
251
+ const api = new DpmApi(await loadConfig());
252
+ await installGlobalPackages(api, packages, createUi());
253
+ return;
254
+ }
255
+ if (packages.length > 0 && rest.every((a) => !a.startsWith("-"))) {
256
+ const api = new DpmApi(await loadConfig());
257
+ await addPackages(api, packages, process.cwd(), createUi());
147
258
  return;
148
259
  }
149
- await addPackages(api, packages, process.cwd(), ui);
260
+ const code = await runtimePm(["add", ...rest]);
261
+ if (code !== 0)
262
+ process.exit(code);
263
+ await syncLockFromNodeModules(process.cwd());
150
264
  });
151
265
  program
152
266
  .command("update")
153
- .description("Re-resolve and install dependencies")
267
+ .description("Update dependencies (runtime update flags supported)")
268
+ .allowUnknownOption(true)
269
+ .argument("[args...]")
154
270
  .action(async () => {
155
- const api = new DpmApi(await loadConfig());
156
- await installAll(api, process.cwd(), createUi());
271
+ const rest = argvAfterCommand(["update"]);
272
+ const code = await runtimePm(["update", ...rest]);
273
+ if (code !== 0)
274
+ process.exit(code);
275
+ await syncLockFromNodeModules(process.cwd());
157
276
  });
158
277
  program
159
278
  .command("remove")
160
279
  .alias("rm")
161
280
  .alias("uninstall")
162
- .argument("[packages...]", "package to remove (omit with -g to remove the dpm CLI)")
163
- .option("-g, --global", "remove from the npm global prefix")
164
- .description("Remove a dependency, or uninstall the global dpm CLI")
165
- .action(async (packages, opts) => {
281
+ .description("Remove a dependency (or dpm rm -g for the global CLI)")
282
+ .allowUnknownOption(true)
283
+ .argument("[args...]")
284
+ .action(async () => {
285
+ const rest = argvAfterCommand(["remove", "rm", "uninstall"]);
286
+ const global = rest.includes("-g") || rest.includes("--global");
287
+ const packages = rest.filter((a) => !a.startsWith("-"));
166
288
  const ui = createUi();
167
- if (opts.global) {
289
+ if (global) {
168
290
  await removeGlobalPackages(packages, ui);
169
291
  return;
170
292
  }
@@ -172,13 +294,20 @@ program
172
294
  console.error("Usage: dpm rm <name> or dpm rm -g");
173
295
  process.exit(1);
174
296
  }
175
- for (const name of packages) {
176
- await removePackage(name, process.cwd(), ui);
297
+ if (rest.every((a) => !a.startsWith("-"))) {
298
+ for (const name of packages) {
299
+ await removePackage(name, process.cwd(), ui);
300
+ }
301
+ return;
177
302
  }
303
+ const code = await runtimePm(["remove", ...rest]);
304
+ if (code !== 0)
305
+ process.exit(code);
306
+ await syncLockFromNodeModules(process.cwd());
178
307
  });
179
308
  const auditCmd = program
180
309
  .command("audit")
181
- .description("Report known vulnerabilities (like npm audit)")
310
+ .description("Report known vulnerabilities")
182
311
  .action(async () => {
183
312
  const api = new DpmApi(await loadConfig());
184
313
  await runAudit(api, process.cwd(), createUi());
@@ -194,29 +323,82 @@ auditCmd
194
323
  program
195
324
  .command("publish")
196
325
  .option("--access <access>", "public or private")
326
+ .description("Publish the current package to the DPM registry")
197
327
  .action(async (opts) => {
198
328
  const api = new DpmApi(await loadConfig());
199
329
  const packed = await packTarball();
330
+ const manifest = { ...packed.manifest };
331
+ // CLI npm name maps to the DPM registry package `dpm`.
332
+ if (manifest.name === "@drops-it/dpm")
333
+ manifest.name = "dpm";
200
334
  const result = await api.publish({
201
- manifest: packed.manifest,
335
+ manifest,
202
336
  tarballBase64: packed.tarballBase64,
203
- access: opts.access,
337
+ access: opts.access ?? "public",
204
338
  });
205
339
  console.log(`Published ${result.name}@${result.version} (${result.access})`);
206
340
  });
207
341
  program
208
- .command("link")
209
- .description("Reserved for local workspace linking")
210
- .action(() => {
211
- console.log("Use file: dependencies or workspaces for now.");
342
+ .command("run")
343
+ .description("Run a package.json script or file")
344
+ .allowUnknownOption(true)
345
+ .argument("[args...]")
346
+ .action(async () => {
347
+ process.exit(await runtimeRaw(["run", ...argvAfterCommand(["run"])]));
348
+ });
349
+ program
350
+ .command("test")
351
+ .description("Run tests")
352
+ .allowUnknownOption(true)
353
+ .argument("[args...]")
354
+ .action(async () => {
355
+ process.exit(await runtimeRaw(["test", ...argvAfterCommand(["test"])]));
356
+ });
357
+ program
358
+ .command("x")
359
+ .alias("exec")
360
+ .description("Execute a package binary (installing if needed)")
361
+ .allowUnknownOption(true)
362
+ .argument("[args...]")
363
+ .action(async () => {
364
+ const name = process.argv.includes("exec") ? "exec" : "x";
365
+ // Prefer `x` for package bins; `exec` stays exec for shell-style
366
+ const cmd = name === "exec" ? "exec" : "x";
367
+ process.exit(await runtimeRaw([cmd, ...argvAfterCommand(["x", "exec"])]));
212
368
  });
213
369
  program
214
- .command("unlink")
215
- .description("Reserved for local workspace unlinking")
216
- .action(() => {
217
- console.log("Unlink placeholder — remove file: links from package.json.");
370
+ .command("build")
371
+ .description("Bundle with the DPM runtime")
372
+ .allowUnknownOption(true)
373
+ .argument("[args...]")
374
+ .action(async () => {
375
+ process.exit(await runtimeRaw(["build", ...argvAfterCommand(["build"])]));
218
376
  });
219
- program.parseAsync(process.argv).catch((err) => {
377
+ async function main() {
378
+ if (shouldForwardToRuntime(process.argv)) {
379
+ await maybeNotifyUpdate();
380
+ const args = process.argv.slice(2);
381
+ // Package-manager-ish forwards still get DPM registry config
382
+ const pmFirst = new Set([
383
+ "link",
384
+ "unlink",
385
+ "outdated",
386
+ "pm",
387
+ "patch",
388
+ "why",
389
+ "info",
390
+ "list",
391
+ "create",
392
+ ]);
393
+ const first = args[0] ?? "";
394
+ const code = pmFirst.has(first)
395
+ ? await runtimePm(args)
396
+ : await runtimeRaw(args);
397
+ process.exit(code);
398
+ }
399
+ await program.parseAsync(process.argv);
400
+ }
401
+ main().catch((err) => {
220
402
  console.error(err instanceof Error ? err.message : err);
221
403
  process.exit(1);
222
404
  });
package/dist/install.d.ts CHANGED
@@ -9,9 +9,7 @@ export type InstallLog = {
9
9
  clearStatus?: () => void;
10
10
  };
11
11
  export type InstallOptions = {
12
- /** Prefer these exact versions when they satisfy the requested range. */
13
12
  preferredVersions?: Record<string, string>;
14
- /** Skip the post-install vulnerability check (used by nested audit-fix reinstall). */
15
13
  skipAudit?: boolean;
16
14
  };
17
15
  /** Parse `name`, `name@1.2.3`, `name@^1`, `@scope/name`, `@scope/name@1.0.0`. */
@@ -19,20 +17,26 @@ export declare function parsePackageSpec(spec: string): {
19
17
  name: string;
20
18
  range: string;
21
19
  };
22
- export declare function installAll(api: DpmApi, cwd?: string, log?: InstallLog, options?: InstallOptions): Promise<Lockfile>;
20
+ /**
21
+ * Builds a DPM lock snapshot from installed node_modules for audit tooling.
22
+ */
23
+ export declare function syncLockFromNodeModules(cwd: string): Promise<Lockfile>;
24
+ /** Installs package.json dependencies via the managed DPM runtime. */
25
+ export declare function installAll(_api: DpmApi, cwd?: string, log?: InstallLog, options?: InstallOptions): Promise<Lockfile>;
26
+ /** Adds packages then installs via the managed DPM runtime. */
23
27
  export declare function addPackages(api: DpmApi, specs: string[], cwd?: string, log?: InstallLog): Promise<Lockfile>;
24
28
  /**
25
- * Installs packages into the npm global prefix (bins linked by npm).
26
- * Downloads through the DPM registry so installs count as DPM downloads.
29
+ * Installs packages into the global prefix via the managed DPM runtime.
27
30
  * Used for CLI self-updates: `dpm i -g dpm` / `dpm i -g @drops-it/dpm`.
28
31
  */
29
- export declare function installGlobalPackages(api: DpmApi, specs: string[], log?: InstallLog): Promise<void>;
32
+ export declare function installGlobalPackages(_api: DpmApi, specs: string[], log?: InstallLog): Promise<void>;
30
33
  /**
31
34
  * Uninstalls global packages (and cleans dpm bin shims when removing the CLI).
32
35
  * `dpm rm -g` with no names removes the dpm CLI itself.
33
36
  */
34
37
  export declare function removeGlobalPackages(specs: string[], log?: InstallLog): Promise<void>;
35
38
  export declare function addPackage(api: DpmApi, name: string, range?: string, cwd?: string, log?: InstallLog): Promise<Lockfile>;
39
+ /** Removes a local dependency via the managed DPM runtime. */
36
40
  export declare function removePackage(name: string, cwd?: string, log?: InstallLog): Promise<void>;
37
41
  /**
38
42
  * Runs a vulnerability audit against the current lockfile (or installs first).
@@ -42,12 +46,13 @@ export declare function runAudit(api: DpmApi, cwd?: string, log?: InstallLog): P
42
46
  fixes: ProposedFix[];
43
47
  }>;
44
48
  /**
45
- * Applies available advisory fixes (like `npm audit fix`), then reinstalls.
49
+ * Applies available advisory fixes, then reinstalls via the DPM runtime.
46
50
  */
47
51
  export declare function auditFix(api: DpmApi, cwd?: string, log?: InstallLog, force?: boolean): Promise<{
48
52
  fixed: ProposedFix[];
49
53
  lock: Lockfile;
50
54
  }>;
55
+ /** Packs the current package into a gzipped tarball for publish. */
51
56
  export declare function packTarball(cwd?: string): Promise<{
52
57
  manifest: Record<string, unknown>;
53
58
  tarballBase64: string;