@celilo/cli 0.3.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Celilo — home lab orchestration CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,10 +22,10 @@ describe('celilo events command handlers', () => {
22
22
  beforeEach(() => {
23
23
  dir = mkdtempSync(join(tmpdir(), 'events-cmd-test-'));
24
24
  dbPath = join(dir, 'events.db');
25
- process.env.CELILO_EVENT_BUS_PATH = dbPath;
25
+ process.env.EVENT_BUS_DB = dbPath;
26
26
  });
27
27
  afterEach(() => {
28
- process.env.CELILO_EVENT_BUS_PATH = undefined;
28
+ process.env.EVENT_BUS_DB = undefined;
29
29
  try {
30
30
  rmSync(dir, { recursive: true, force: true });
31
31
  } catch {
@@ -96,7 +96,8 @@ export function getDbPath(): string {
96
96
  * Get the SQLite event-bus database file path.
97
97
  *
98
98
  * Priority:
99
- * 1. CELILO_EVENT_BUS_PATH environment variable (explicit override)
99
+ * 1. EVENT_BUS_DB environment variable (the bus library's native name —
100
+ * used by the event-bus CLI, defineHandler, and any standalone tool)
100
101
  * 2. <data-dir>/events.db (platform-specific)
101
102
  *
102
103
  * Kept separate from the main celilo.db so the bus library remains
@@ -105,8 +106,8 @@ export function getDbPath(): string {
105
106
  * @returns Absolute path to event-bus database
106
107
  */
107
108
  export function getEventBusPath(): string {
108
- if (process.env.CELILO_EVENT_BUS_PATH) {
109
- return process.env.CELILO_EVENT_BUS_PATH;
109
+ if (process.env.EVENT_BUS_DB) {
110
+ return process.env.EVENT_BUS_DB;
110
111
  }
111
112
  return join(getDataDir(), 'events.db');
112
113
  }
@@ -23,6 +23,25 @@ import {
23
23
  import { parseJsonWithValidation } from '../validation/schemas';
24
24
  import { cleanupTempDir, extractPackage, verifyPackageIntegrity } from './packaging/extract';
25
25
 
26
+ /**
27
+ * Phase-timing helper for `celilo module import`. Set CELILO_IMPORT_DEBUG=1
28
+ * to get a phase-by-phase breakdown to stderr — useful when an import is
29
+ * inexplicably slow (e.g. a Docker bind-mount fsync amplification, a stale
30
+ * lockfile triggering a fresh bun install, etc.).
31
+ *
32
+ * Zero overhead when disabled: the wrapper just calls the inner fn.
33
+ */
34
+ async function timedPhase<T>(label: string, fn: () => Promise<T> | T): Promise<T> {
35
+ if (!process.env.CELILO_IMPORT_DEBUG) return await fn();
36
+ const start = Date.now();
37
+ try {
38
+ return await fn();
39
+ } finally {
40
+ const ms = Date.now() - start;
41
+ process.stderr.write(`[import-timing] ${label.padEnd(36)} ${ms}ms\n`);
42
+ }
43
+ }
44
+
26
45
  /**
27
46
  * Module import options
28
47
  */
@@ -547,7 +566,7 @@ export async function importModule(options: ModuleImportOptions): Promise<Module
547
566
 
548
567
  // Execution: Copy files
549
568
  try {
550
- await copyModuleFiles(actualSourcePath, targetPath);
569
+ await timedPhase('copyModuleFiles', () => copyModuleFiles(actualSourcePath, targetPath));
551
570
  } catch (error) {
552
571
  if (tempDir) await cleanupTempDir(tempDir);
553
572
  return {
@@ -563,7 +582,9 @@ export async function importModule(options: ModuleImportOptions): Promise<Module
563
582
  // If no package.json exists but hook scripts do, auto-generate one with
564
583
  // just the framework dep — smooths migration for existing modules.
565
584
  try {
566
- await installScriptDependencies(targetPath, manifest);
585
+ await timedPhase('installScriptDependencies', () =>
586
+ installScriptDependencies(targetPath, manifest),
587
+ );
567
588
  } catch (error) {
568
589
  // Non-fatal: the module is importable without deps, but hooks
569
590
  // will fail at runtime. Warn and continue.
@@ -574,7 +595,7 @@ export async function importModule(options: ModuleImportOptions): Promise<Module
574
595
 
575
596
  // Execution: Insert to database
576
597
  try {
577
- await insertModuleToDb(manifest, targetPath, db);
598
+ await timedPhase('insertModuleToDb', () => insertModuleToDb(manifest, targetPath, db));
578
599
  } catch (error) {
579
600
  if (tempDir) await cleanupTempDir(tempDir);
580
601
  return {
@@ -586,8 +607,10 @@ export async function importModule(options: ModuleImportOptions): Promise<Module
586
607
 
587
608
  // Execution: Register capabilities if module provides them
588
609
  if (manifest.provides?.capabilities && manifest.provides.capabilities.length > 0) {
589
- const { registerModuleCapabilities } = await import('../capabilities/registration');
590
- const capResult = await registerModuleCapabilities(manifest.id, manifest, db.$client, flags);
610
+ const capResult = await timedPhase('registerModuleCapabilities', async () => {
611
+ const { registerModuleCapabilities } = await import('../capabilities/registration');
612
+ return registerModuleCapabilities(manifest.id, manifest, db.$client, flags);
613
+ });
591
614
 
592
615
  if (!capResult.success) {
593
616
  if (tempDir) await cleanupTempDir(tempDir);
@@ -604,8 +627,10 @@ export async function importModule(options: ModuleImportOptions): Promise<Module
604
627
  // can re-run the import after fixing the bus, and bus.subscribe is
605
628
  // idempotent.
606
629
  try {
607
- const { registerModuleSubscriptions } = await import('../services/module-subscriptions');
608
- registerModuleSubscriptions(manifest, targetPath);
630
+ await timedPhase('registerModuleSubscriptions', async () => {
631
+ const { registerModuleSubscriptions } = await import('../services/module-subscriptions');
632
+ registerModuleSubscriptions(manifest, targetPath);
633
+ });
609
634
  } catch (error) {
610
635
  const msg = error instanceof Error ? error.message : String(error);
611
636
  log.warn(`Failed to register event-bus subscriptions: ${msg}`);
@@ -17,10 +17,10 @@ describe('celilo lifecycle events', () => {
17
17
  beforeEach(() => {
18
18
  dir = mkdtempSync(join(tmpdir(), 'celilo-events-test-'));
19
19
  dbPath = join(dir, 'events.db');
20
- process.env.CELILO_EVENT_BUS_PATH = dbPath;
20
+ process.env.EVENT_BUS_DB = dbPath;
21
21
  });
22
22
  afterEach(() => {
23
- process.env.CELILO_EVENT_BUS_PATH = undefined;
23
+ process.env.EVENT_BUS_DB = undefined;
24
24
  try {
25
25
  rmSync(dir, { recursive: true, force: true });
26
26
  } catch {
@@ -92,7 +92,7 @@ describe('celilo lifecycle events', () => {
92
92
  });
93
93
 
94
94
  it('does not throw when the bus path is unwritable', () => {
95
- process.env.CELILO_EVENT_BUS_PATH = '/proc/no/such/place/events.db';
95
+ process.env.EVENT_BUS_DB = '/proc/no/such/place/events.db';
96
96
  expect(() => emitDeployStarted({ module: 'x', startedAt: 0 })).not.toThrow();
97
97
  });
98
98
  });
@@ -23,7 +23,7 @@ describe('renderSystemdUnit', () => {
23
23
  expect(out).toContain(
24
24
  'ExecStart=/usr/local/bin/celilo events run --poll-ms 1000 --concurrency 4',
25
25
  );
26
- expect(out).toContain('Environment=CELILO_EVENT_BUS_PATH=/var/lib/celilo/events.db');
26
+ expect(out).toContain('Environment=EVENT_BUS_DB=/var/lib/celilo/events.db');
27
27
  expect(out).toContain('Restart=on-failure');
28
28
  expect(out).toContain('WantedBy=default.target');
29
29
  });
@@ -141,9 +141,9 @@ describe('installDaemon / uninstallDaemon roundtrip', () => {
141
141
  pollMs: 500,
142
142
  });
143
143
  const written = readFileSync(second.unitPath, 'utf-8');
144
- expect(written).toContain('CELILO_EVENT_BUS_PATH=/db2');
144
+ expect(written).toContain('EVENT_BUS_DB=/db2');
145
145
  expect(written).toContain('--poll-ms 500');
146
- expect(written).not.toContain('CELILO_EVENT_BUS_PATH=/db1');
146
+ expect(written).not.toContain('EVENT_BUS_DB=/db1');
147
147
  });
148
148
 
149
149
  it('uninstall on a missing unit reports not-removed', () => {
@@ -119,7 +119,7 @@ Type=simple
119
119
  ExecStart=${input.celiloPath} events run --poll-ms ${input.pollMs} --concurrency ${input.concurrency}
120
120
  Restart=on-failure
121
121
  RestartSec=10s
122
- Environment=CELILO_EVENT_BUS_PATH=${input.busDbPath}
122
+ Environment=EVENT_BUS_DB=${input.busDbPath}
123
123
  # stdout/stderr are captured by journalctl --user -u celilo-events.service.
124
124
  StandardOutput=journal
125
125
  StandardError=journal
@@ -151,7 +151,7 @@ export function renderLaunchdPlist(input: UnitInputs): string {
151
151
  </array>
152
152
  <key>EnvironmentVariables</key>
153
153
  <dict>
154
- <key>CELILO_EVENT_BUS_PATH</key>
154
+ <key>EVENT_BUS_DB</key>
155
155
  <string>${input.busDbPath}</string>
156
156
  </dict>
157
157
  <key>RunAtLoad</key>
@@ -78,10 +78,10 @@ describe('register / unregister roundtrip', () => {
78
78
  beforeEach(() => {
79
79
  dir = mkdtempSync(join(tmpdir(), 'modsubs-test-'));
80
80
  dbPath = join(dir, 'events.db');
81
- process.env.CELILO_EVENT_BUS_PATH = dbPath;
81
+ process.env.EVENT_BUS_DB = dbPath;
82
82
  });
83
83
  afterEach(() => {
84
- process.env.CELILO_EVENT_BUS_PATH = undefined;
84
+ process.env.EVENT_BUS_DB = undefined;
85
85
  try {
86
86
  rmSync(dir, { recursive: true, force: true });
87
87
  } catch {