primo-cli 0.1.6 → 0.1.7

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.
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import { createHash, randomInt } from 'crypto';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
- import { spawn } from 'child_process';
6
+ import { spawn, execFileSync } from 'child_process';
7
7
  import archiver from 'archiver';
8
8
  import extract from 'extract-zip';
9
9
  import { dump as dump_yaml, load as load_yaml } from 'js-yaml';
@@ -420,18 +420,51 @@ async function kill_port(port) {
420
420
  resolve(false);
421
421
  return;
422
422
  }
423
+ // Don't kill ourselves or our ancestors — lsof returns every PID
424
+ // holding the port, which on macOS includes parent processes that
425
+ // inherited the fd. SIGKILL'ing them takes this CLI down too.
426
+ const self_ancestry = get_self_ancestry();
427
+ let killed_any = false;
423
428
  for (const pid of pid_list) {
429
+ const pid_num = parseInt(pid, 10);
430
+ if (self_ancestry.has(pid_num))
431
+ continue;
424
432
  try {
425
- process.kill(parseInt(pid, 10), 'SIGKILL');
433
+ process.kill(pid_num, 'SIGKILL');
434
+ killed_any = true;
426
435
  }
427
436
  catch {
428
437
  // Process may have already exited
429
438
  }
430
439
  }
431
- resolve(true);
440
+ resolve(killed_any);
432
441
  });
433
442
  });
434
443
  }
444
+ function get_self_ancestry() {
445
+ const ancestry = new Set();
446
+ let pid = process.pid;
447
+ while (pid && pid > 1) {
448
+ ancestry.add(pid);
449
+ pid = get_parent_pid(pid);
450
+ if (pid && ancestry.has(pid))
451
+ break;
452
+ }
453
+ return ancestry;
454
+ }
455
+ function get_parent_pid(pid) {
456
+ try {
457
+ const result = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
458
+ encoding: 'utf-8',
459
+ stdio: ['ignore', 'pipe', 'ignore']
460
+ });
461
+ const parsed = parseInt(String(result).trim(), 10);
462
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
463
+ }
464
+ catch {
465
+ return undefined;
466
+ }
467
+ }
435
468
  // Fetch with timeout helper
436
469
  async function fetch_with_timeout(url, options = {}, timeout_ms = 10000) {
437
470
  const controller = new AbortController();
@@ -970,8 +1003,12 @@ export async function dev_server(options) {
970
1003
  console.log(chalk.dim(' Press Ctrl+C to stop'));
971
1004
  // Handle cleanup
972
1005
  const cleanup = async () => {
973
- if (is_cleaning_up)
974
- return;
1006
+ if (is_cleaning_up) {
1007
+ // Second Ctrl-C while we're still cleaning up — bail immediately
1008
+ // so the user isn't stuck waiting on an in-flight push/sync.
1009
+ console.log(chalk.dim('\n Force exit'));
1010
+ process.exit(130);
1011
+ }
975
1012
  is_cleaning_up = true;
976
1013
  console.log(chalk.dim('\n Shutting down...'));
977
1014
  for (const watcher of watchers) {
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
2
5
  import { Command } from 'commander';
3
6
  import chalk from 'chalk';
4
7
  import { init_workspace } from './commands/init.js';
@@ -12,11 +15,13 @@ import { login } from './commands/login.js';
12
15
  import { validate_site } from './commands/validate.js';
13
16
  import { deploy } from './commands/deploy.js';
14
17
  import { build_site } from './commands/build.js';
18
+ const pkg_path = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
19
+ const pkg_version = JSON.parse(fs.readFileSync(pkg_path, 'utf-8')).version;
15
20
  const program = new Command();
16
21
  program
17
22
  .name('primo')
18
23
  .description('Build sites visually, edit them anywhere')
19
- .version('0.1.3');
24
+ .version(pkg_version);
20
25
  // Top-level help: prepend a Deploy-vs-build-vs-push decision tree so first-time
21
26
  // users can pick a command without reading every description. Use 'before' so
22
27
  // it only renders for the root program, not subcommand help.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primo-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Local development CLI for Primo",
5
5
  "type": "module",
6
6
  "bin": {