dev-cockpit 0.1.0

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.
Files changed (169) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +131 -0
  4. package/bin/dev-cockpit.mjs +20 -0
  5. package/dist/buildCli.d.ts +17 -0
  6. package/dist/buildCli.d.ts.map +1 -0
  7. package/dist/buildCli.js +107 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +2 -0
  11. package/dist/cockpit/Cockpit.d.ts +33 -0
  12. package/dist/cockpit/Cockpit.d.ts.map +1 -0
  13. package/dist/cockpit/Cockpit.js +73 -0
  14. package/dist/cockpit/Footer.d.ts +22 -0
  15. package/dist/cockpit/Footer.d.ts.map +1 -0
  16. package/dist/cockpit/Footer.js +33 -0
  17. package/dist/cockpit/TabBar.d.ts +3 -0
  18. package/dist/cockpit/TabBar.d.ts.map +1 -0
  19. package/dist/cockpit/TabBar.js +12 -0
  20. package/dist/cockpit/help/content.d.ts +12 -0
  21. package/dist/cockpit/help/content.d.ts.map +1 -0
  22. package/dist/cockpit/help/content.js +22 -0
  23. package/dist/cockpit/help/loader.d.ts +65 -0
  24. package/dist/cockpit/help/loader.d.ts.map +1 -0
  25. package/dist/cockpit/help/loader.js +118 -0
  26. package/dist/cockpit/help/renderer.d.ts +16 -0
  27. package/dist/cockpit/help/renderer.d.ts.map +1 -0
  28. package/dist/cockpit/help/renderer.js +35 -0
  29. package/dist/cockpit/help/types.d.ts +12 -0
  30. package/dist/cockpit/help/types.d.ts.map +1 -0
  31. package/dist/cockpit/help/types.js +1 -0
  32. package/dist/cockpit/hooks/useCockpitStore.d.ts +3 -0
  33. package/dist/cockpit/hooks/useCockpitStore.d.ts.map +1 -0
  34. package/dist/cockpit/hooks/useCockpitStore.js +5 -0
  35. package/dist/cockpit/hooks/useGlobalKeys.d.ts +56 -0
  36. package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -0
  37. package/dist/cockpit/hooks/useGlobalKeys.js +173 -0
  38. package/dist/cockpit/panes/FilterModal.d.ts +3 -0
  39. package/dist/cockpit/panes/FilterModal.d.ts.map +1 -0
  40. package/dist/cockpit/panes/FilterModal.js +22 -0
  41. package/dist/cockpit/panes/Health.d.ts +13 -0
  42. package/dist/cockpit/panes/Health.d.ts.map +1 -0
  43. package/dist/cockpit/panes/Health.js +30 -0
  44. package/dist/cockpit/panes/Help.d.ts +14 -0
  45. package/dist/cockpit/panes/Help.d.ts.map +1 -0
  46. package/dist/cockpit/panes/Help.js +81 -0
  47. package/dist/cockpit/panes/Output.d.ts +14 -0
  48. package/dist/cockpit/panes/Output.d.ts.map +1 -0
  49. package/dist/cockpit/panes/Output.js +108 -0
  50. package/dist/cockpit/panes/Repos.d.ts +3 -0
  51. package/dist/cockpit/panes/Repos.d.ts.map +1 -0
  52. package/dist/cockpit/panes/Repos.js +48 -0
  53. package/dist/cockpit/panes/SearchModal.d.ts +3 -0
  54. package/dist/cockpit/panes/SearchModal.d.ts.map +1 -0
  55. package/dist/cockpit/panes/SearchModal.js +31 -0
  56. package/dist/cockpit/state/store.d.ts +93 -0
  57. package/dist/cockpit/state/store.d.ts.map +1 -0
  58. package/dist/cockpit/state/store.js +111 -0
  59. package/dist/cockpit/tab-state.d.ts +4 -0
  60. package/dist/cockpit/tab-state.d.ts.map +1 -0
  61. package/dist/cockpit/tab-state.js +7 -0
  62. package/dist/commands/dev.d.ts +20 -0
  63. package/dist/commands/dev.d.ts.map +1 -0
  64. package/dist/commands/dev.js +158 -0
  65. package/dist/commands/doctor.d.ts +20 -0
  66. package/dist/commands/doctor.d.ts.map +1 -0
  67. package/dist/commands/doctor.js +66 -0
  68. package/dist/commands/init-config-wizard.d.ts +84 -0
  69. package/dist/commands/init-config-wizard.d.ts.map +1 -0
  70. package/dist/commands/init-config-wizard.js +818 -0
  71. package/dist/commands/init-config.d.ts +35 -0
  72. package/dist/commands/init-config.d.ts.map +1 -0
  73. package/dist/commands/init-config.js +131 -0
  74. package/dist/commands/mount.d.ts +48 -0
  75. package/dist/commands/mount.d.ts.map +1 -0
  76. package/dist/commands/mount.js +150 -0
  77. package/dist/core/config.d.ts +391 -0
  78. package/dist/core/config.d.ts.map +1 -0
  79. package/dist/core/config.js +152 -0
  80. package/dist/core/logger.d.ts +6 -0
  81. package/dist/core/logger.d.ts.map +1 -0
  82. package/dist/core/logger.js +38 -0
  83. package/dist/core/notifier.d.ts +23 -0
  84. package/dist/core/notifier.d.ts.map +1 -0
  85. package/dist/core/notifier.js +100 -0
  86. package/dist/core/paths.d.ts +15 -0
  87. package/dist/core/paths.d.ts.map +1 -0
  88. package/dist/core/paths.js +18 -0
  89. package/dist/core/subprocess.d.ts +20 -0
  90. package/dist/core/subprocess.d.ts.map +1 -0
  91. package/dist/core/subprocess.js +82 -0
  92. package/dist/core/types.d.ts +125 -0
  93. package/dist/core/types.d.ts.map +1 -0
  94. package/dist/core/types.js +1 -0
  95. package/dist/docker/highlights.d.ts +48 -0
  96. package/dist/docker/highlights.d.ts.map +1 -0
  97. package/dist/docker/highlights.js +79 -0
  98. package/dist/docker/logs.d.ts +84 -0
  99. package/dist/docker/logs.d.ts.map +1 -0
  100. package/dist/docker/logs.js +172 -0
  101. package/dist/docker/restart.d.ts +26 -0
  102. package/dist/docker/restart.d.ts.map +1 -0
  103. package/dist/docker/restart.js +45 -0
  104. package/dist/docker/stack-trace.d.ts +25 -0
  105. package/dist/docker/stack-trace.d.ts.map +1 -0
  106. package/dist/docker/stack-trace.js +44 -0
  107. package/dist/health/builtin.d.ts +8 -0
  108. package/dist/health/builtin.d.ts.map +1 -0
  109. package/dist/health/builtin.js +144 -0
  110. package/dist/health/context.d.ts +3 -0
  111. package/dist/health/context.d.ts.map +1 -0
  112. package/dist/health/context.js +31 -0
  113. package/dist/health/notify-resolver.d.ts +18 -0
  114. package/dist/health/notify-resolver.d.ts.map +1 -0
  115. package/dist/health/notify-resolver.js +28 -0
  116. package/dist/health/registry.d.ts +20 -0
  117. package/dist/health/registry.d.ts.map +1 -0
  118. package/dist/health/registry.js +64 -0
  119. package/dist/health/remediations.d.ts +6 -0
  120. package/dist/health/remediations.d.ts.map +1 -0
  121. package/dist/health/remediations.js +41 -0
  122. package/dist/health/runner.d.ts +4 -0
  123. package/dist/health/runner.d.ts.map +1 -0
  124. package/dist/health/runner.js +22 -0
  125. package/dist/health/scheduler.d.ts +41 -0
  126. package/dist/health/scheduler.d.ts.map +1 -0
  127. package/dist/health/scheduler.js +107 -0
  128. package/dist/health/types.d.ts +73 -0
  129. package/dist/health/types.d.ts.map +1 -0
  130. package/dist/health/types.js +1 -0
  131. package/dist/health/useHealth.d.ts +40 -0
  132. package/dist/health/useHealth.d.ts.map +1 -0
  133. package/dist/health/useHealth.js +122 -0
  134. package/dist/index.d.ts +50 -0
  135. package/dist/index.d.ts.map +1 -0
  136. package/dist/index.js +53 -0
  137. package/dist/ink.d.ts +3 -0
  138. package/dist/ink.d.ts.map +1 -0
  139. package/dist/ink.js +1 -0
  140. package/dist/lint/reactive.d.ts +38 -0
  141. package/dist/lint/reactive.d.ts.map +1 -0
  142. package/dist/lint/reactive.js +131 -0
  143. package/dist/react.d.ts +3 -0
  144. package/dist/react.d.ts.map +1 -0
  145. package/dist/react.js +1 -0
  146. package/dist/runCockpit.d.ts +34 -0
  147. package/dist/runCockpit.d.ts.map +1 -0
  148. package/dist/runCockpit.js +75 -0
  149. package/dist/watchers/manager.d.ts +63 -0
  150. package/dist/watchers/manager.d.ts.map +1 -0
  151. package/dist/watchers/manager.js +239 -0
  152. package/dist/watchers/path-mapper.d.ts +23 -0
  153. package/dist/watchers/path-mapper.d.ts.map +1 -0
  154. package/dist/watchers/path-mapper.js +29 -0
  155. package/dist/watchers/types.d.ts +22 -0
  156. package/dist/watchers/types.d.ts.map +1 -0
  157. package/dist/watchers/types.js +9 -0
  158. package/docs/commands.md +71 -0
  159. package/docs/config-reference.md +20 -0
  160. package/docs/getting-started.md +39 -0
  161. package/docs/health.md +120 -0
  162. package/docs/index.md +13 -0
  163. package/docs/init-config.md +46 -0
  164. package/docs/mount.md +55 -0
  165. package/docs/notifications.md +39 -0
  166. package/docs/panes.md +45 -0
  167. package/docs/watchers.md +27 -0
  168. package/examples/cockpit.yaml +116 -0
  169. package/package.json +91 -0
@@ -0,0 +1,100 @@
1
+ export function detectTransitions(prev, next) {
2
+ const prevMap = new Map();
3
+ for (const s of prev) {
4
+ prevMap.set(s.id, s);
5
+ }
6
+ const transitions = [];
7
+ for (const nextStatus of next) {
8
+ const prevStatus = prevMap.get(nextStatus.id);
9
+ if (!prevStatus) {
10
+ if (nextStatus.severity === 'error') {
11
+ transitions.push({
12
+ event: 'health-failed',
13
+ id: nextStatus.id,
14
+ label: nextStatus.label,
15
+ detail: nextStatus.detail,
16
+ });
17
+ }
18
+ continue;
19
+ }
20
+ const wasOk = prevStatus.severity !== 'error';
21
+ const isOk = nextStatus.severity !== 'error';
22
+ if (wasOk && !isOk) {
23
+ transitions.push({
24
+ event: 'health-failed',
25
+ id: nextStatus.id,
26
+ label: nextStatus.label,
27
+ detail: nextStatus.detail,
28
+ });
29
+ }
30
+ else if (!wasOk && isOk) {
31
+ transitions.push({
32
+ event: 'health-recovered',
33
+ id: nextStatus.id,
34
+ label: nextStatus.label,
35
+ detail: nextStatus.detail,
36
+ });
37
+ }
38
+ }
39
+ return transitions;
40
+ }
41
+ export function notify(transition, opts) {
42
+ const { config, sessionEnabled, sender, appName = 'cockpit' } = opts;
43
+ if (!sessionEnabled)
44
+ return;
45
+ if (!config.enabled)
46
+ return;
47
+ if (config.exclude.includes(transition.event))
48
+ return;
49
+ const title = `${appName} — ${transition.label}`;
50
+ const message = transition.detail;
51
+ if (sender) {
52
+ sender(title, message);
53
+ return;
54
+ }
55
+ defaultPlatformNotify(title, message);
56
+ }
57
+ /**
58
+ * OS notification via a detached child process. The child runs independently
59
+ * (`detached: true`, `stdio: 'ignore'`, `unref()`) so it never enters the
60
+ * parent's event loop — the cockpit can exit while the notification helper
61
+ * is still flushing without leaving an unreaped ChildProcess handle.
62
+ *
63
+ * Platforms:
64
+ * - darwin: `osascript -e 'display notification …'`
65
+ * - linux: `notify-send`
66
+ * - other: silently skipped
67
+ */
68
+ function defaultPlatformNotify(title, message) {
69
+ // Lazy require so test environments don't pull child_process at import time.
70
+ void import('node:child_process').then(({ spawn }) => {
71
+ try {
72
+ let child = null;
73
+ if (process.platform === 'darwin') {
74
+ const safeTitle = title.replace(/(["\\])/g, '\\$1');
75
+ const safeMsg = message.replace(/(["\\])/g, '\\$1');
76
+ child = spawn('osascript', ['-e', `display notification "${safeMsg}" with title "${safeTitle}"`], { detached: true, stdio: 'ignore' });
77
+ }
78
+ else if (process.platform === 'linux') {
79
+ child = spawn('notify-send', [title, message], {
80
+ detached: true,
81
+ stdio: 'ignore',
82
+ });
83
+ }
84
+ child?.unref();
85
+ }
86
+ catch {
87
+ // Notification is best-effort. Don't crash the cockpit if the helper
88
+ // binary is missing (e.g. headless Linux without notify-send).
89
+ }
90
+ });
91
+ }
92
+ export function notifyTransitions(prev, next, opts) {
93
+ const transitions = detectTransitions(prev, next);
94
+ for (const t of transitions) {
95
+ notify(t, opts);
96
+ }
97
+ }
98
+ export function emitEvent(event, label, detail, opts) {
99
+ notify({ event, id: String(event), label, detail }, opts);
100
+ }
@@ -0,0 +1,15 @@
1
+ export interface StatePaths {
2
+ /** Root state directory for this workspace: `<xdg_state_home>/<appName>/<hash>/` */
3
+ stateDir: string;
4
+ /** Lock file to prevent parallel cockpits on the same workspace. */
5
+ lockFile: string;
6
+ /** Structured log file for this workspace's session. */
7
+ logFile: string;
8
+ }
9
+ export interface StatePathsOptions {
10
+ /** Used as the directory name under XDG_STATE_HOME (e.g. `~/.local/state/<appName>/...`). */
11
+ appName: string;
12
+ }
13
+ export declare function hashWorkspacePath(workspaceRoot: string): string;
14
+ export declare function getStatePaths(workspaceRoot: string, opts: StatePathsOptions): StatePaths;
15
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,UAAU,CAcxF"}
@@ -0,0 +1,18 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ export function hashWorkspacePath(workspaceRoot) {
6
+ return crypto.createHash('sha256').update(workspaceRoot).digest('hex').slice(0, 8);
7
+ }
8
+ export function getStatePaths(workspaceRoot, opts) {
9
+ const xdgStateHome = process.env['XDG_STATE_HOME'] ?? path.join(os.homedir(), '.local', 'state');
10
+ const hash = hashWorkspacePath(workspaceRoot);
11
+ const stateDir = path.join(xdgStateHome, opts.appName, hash);
12
+ fs.mkdirSync(stateDir, { recursive: true });
13
+ return {
14
+ stateDir,
15
+ lockFile: path.join(stateDir, 'cockpit.lock'),
16
+ logFile: path.join(stateDir, 'cockpit.log'),
17
+ };
18
+ }
@@ -0,0 +1,20 @@
1
+ import { type Subprocess } from 'execa';
2
+ export interface SpawnOptions {
3
+ cwd?: string;
4
+ env?: Record<string, string>;
5
+ onStdout?: (line: string) => void;
6
+ onStderr?: (line: string) => void;
7
+ }
8
+ export interface ProcessHandle {
9
+ exitCode: Promise<number>;
10
+ kill: (signal?: NodeJS.Signals) => void;
11
+ child: Subprocess;
12
+ }
13
+ export declare function spawnStream(cmd: string, args: string[], opts?: SpawnOptions): ProcessHandle;
14
+ /**
15
+ * Send SIGTERM to every still-running spawnStream child and await each one's
16
+ * reap. After {@link sigkillAfterMs} the holdouts are SIGKILLed so the sweep
17
+ * cannot hang indefinitely on an unresponsive subprocess.
18
+ */
19
+ export declare function killAllSpawned(sigkillAfterMs?: number): Promise<void>;
20
+ //# sourceMappingURL=subprocess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subprocess.d.ts","sourceRoot":"","sources":["../../src/core/subprocess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,EAAE,UAAU,CAAC;CACnB;AAWD,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,YAAiB,GACtB,aAAa,CA0Cf;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,cAAc,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBzE"}
@@ -0,0 +1,82 @@
1
+ import { execa } from 'execa';
2
+ /**
3
+ * Module-level registry of every currently-running spawnStream child. Each
4
+ * spawn auto-registers; auto-deregisters when the child exits. Callers that
5
+ * own a long-running session (typically the `dev` command) sweep this on
6
+ * teardown via {@link killAllSpawned} so no rogue subprocess keeps the Node
7
+ * event loop alive after the cockpit unmounts.
8
+ */
9
+ const ACTIVE = new Set();
10
+ export function spawnStream(cmd, args, opts = {}) {
11
+ const child = execa(cmd, args, {
12
+ cwd: opts.cwd,
13
+ env: opts.env ? { ...process.env, ...opts.env } : process.env,
14
+ all: false,
15
+ lines: true,
16
+ reject: false,
17
+ stdout: 'pipe',
18
+ stderr: 'pipe',
19
+ });
20
+ if (opts.onStdout && child.stdout) {
21
+ child.stdout.on('data', (chunk) => {
22
+ const text = chunk.toString();
23
+ for (const line of text.split('\n')) {
24
+ if (line)
25
+ opts.onStdout(line);
26
+ }
27
+ });
28
+ }
29
+ if (opts.onStderr && child.stderr) {
30
+ child.stderr.on('data', (chunk) => {
31
+ const text = chunk.toString();
32
+ for (const line of text.split('\n')) {
33
+ if (line)
34
+ opts.onStderr(line);
35
+ }
36
+ });
37
+ }
38
+ const exitCode = child.then((result) => result.exitCode ?? 0);
39
+ const handle = {
40
+ exitCode,
41
+ kill: (signal = 'SIGTERM') => {
42
+ child.kill(signal);
43
+ },
44
+ child,
45
+ };
46
+ ACTIVE.add(handle);
47
+ void exitCode.finally(() => ACTIVE.delete(handle));
48
+ return handle;
49
+ }
50
+ /**
51
+ * Send SIGTERM to every still-running spawnStream child and await each one's
52
+ * reap. After {@link sigkillAfterMs} the holdouts are SIGKILLed so the sweep
53
+ * cannot hang indefinitely on an unresponsive subprocess.
54
+ */
55
+ export async function killAllSpawned(sigkillAfterMs = 1500) {
56
+ if (ACTIVE.size === 0)
57
+ return;
58
+ const handles = Array.from(ACTIVE);
59
+ for (const h of handles)
60
+ h.kill('SIGTERM');
61
+ const reaps = handles.map((h) => h.exitCode.catch(() => undefined));
62
+ let timer = null;
63
+ const guard = new Promise((resolve) => {
64
+ timer = setTimeout(() => {
65
+ for (const h of handles) {
66
+ try {
67
+ h.kill('SIGKILL');
68
+ }
69
+ catch {
70
+ // already dead
71
+ }
72
+ }
73
+ resolve();
74
+ }, sigkillAfterMs);
75
+ });
76
+ await Promise.race([Promise.all(reaps), guard]);
77
+ if (timer)
78
+ clearTimeout(timer);
79
+ // After the SIGKILL fallback, drain the original exitCodes so handles are
80
+ // fully released from the event loop.
81
+ await Promise.all(reaps);
82
+ }
@@ -0,0 +1,125 @@
1
+ import type { Command } from 'commander';
2
+ import type { z } from 'zod';
3
+ import type { HealthCheck } from '../health/types.js';
4
+ import type { BaseCockpitConfig } from './config.js';
5
+ export type Severity = 'ok' | 'warn' | 'error';
6
+ export type RepoStatus = 'idle' | 'running' | 'failing';
7
+ export type LintStatus = 'unknown' | 'pass' | 'fail';
8
+ /** Open: profiles can use any string; built-in panes treat 'docker' as a special kind that disables watch/lint actions. */
9
+ export type RepoKind = 'repo' | 'docker' | (string & {});
10
+ export interface RepoState {
11
+ name: string;
12
+ status: RepoStatus;
13
+ kind: RepoKind;
14
+ }
15
+ export interface HealthStatus {
16
+ id: string;
17
+ label: string;
18
+ severity: Severity;
19
+ detail: string;
20
+ remediationKey: string | null;
21
+ }
22
+ export interface OutputLine {
23
+ ts: number;
24
+ source: string;
25
+ severity: 'info' | 'warn' | 'error';
26
+ text: string;
27
+ }
28
+ export type BuiltinNotifiableEvent = 'health-failed' | 'health-recovered' | 'build-failed' | 'build-recovered';
29
+ export type NotifiableEvent = BuiltinNotifiableEvent | (string & {});
30
+ export interface Transition {
31
+ event: NotifiableEvent;
32
+ id: string;
33
+ label: string;
34
+ detail: string;
35
+ }
36
+ export interface Watcher {
37
+ id: string;
38
+ label?: string;
39
+ command: string;
40
+ cwd?: string;
41
+ env?: Record<string, string>;
42
+ color?: string;
43
+ restartOn?: string[];
44
+ notify?: false | {
45
+ onTransitionTo?: NotifiableEvent[];
46
+ };
47
+ }
48
+ export interface Service {
49
+ name: string;
50
+ tail?: boolean;
51
+ }
52
+ export interface Repo {
53
+ id: string;
54
+ path: string;
55
+ label?: string;
56
+ }
57
+ export interface Mount {
58
+ hostPath: string;
59
+ containerPath: string;
60
+ }
61
+ export interface HelpSource {
62
+ id: string;
63
+ title?: string;
64
+ path?: string;
65
+ content?: string;
66
+ omit?: boolean;
67
+ }
68
+ export interface Workspace {
69
+ root: string;
70
+ }
71
+ /** Dispatched action that briefly banners the active operation. */
72
+ export interface ProfileAsyncActionDispatch {
73
+ label: string;
74
+ promise: Promise<void>;
75
+ }
76
+ export interface ProfileCockpitHandlers {
77
+ /** Triggered by `r` on the Repos pane. Return null to skip. */
78
+ runRepoAction?: (repoKey: string, repo: RepoState) => ProfileAsyncActionDispatch | null;
79
+ /** Triggered by `w` for repo-kind entries. Fire-and-forget. */
80
+ onWatchToggle?: (repoKey: string, repo: RepoState) => void;
81
+ /** Triggered by `l` for repo-kind entries. Fire-and-forget. */
82
+ onLint?: (repoKey: string, repo: RepoState) => void;
83
+ /** Triggered by `e` on the Output pane with the most-recent file:line error (or null). */
84
+ onOpenError?: (error: {
85
+ file: string;
86
+ line: number;
87
+ service: string;
88
+ text: string;
89
+ } | null) => void;
90
+ /** Per-tab footer legend overrides. */
91
+ footerLegends?: Record<string, string>;
92
+ }
93
+ export interface ProfileBootContext {
94
+ /** Parsed cockpit.yaml. Profile narrows the `profile.<appName>` block via configSchemaExt. */
95
+ config: BaseCockpitConfig;
96
+ /** Resolved workspace root (profile.discoverer's result, or the config file's dir). */
97
+ workspaceRoot: string;
98
+ }
99
+ export interface ProfileBootResult {
100
+ /** Cockpit pane handlers + footer overrides. */
101
+ cockpitHandlers?: ProfileCockpitHandlers;
102
+ /** fs-event source piped into useHealth (typically WatcherManager.subscribeFsEvents). */
103
+ subscribeFsEvents?: (listener: (filePath: string) => void) => () => void;
104
+ /** Awaited at teardown — release any owned subprocesses, watchers, timers. */
105
+ cleanup?: () => Promise<void> | void;
106
+ }
107
+ export type ProfileBootHook = (ctx: ProfileBootContext) => ProfileBootResult | Promise<ProfileBootResult>;
108
+ export interface Profile {
109
+ appName: string;
110
+ discoverer?: () => Workspace;
111
+ /**
112
+ * Returns repos for the Repos pane. Receives the same context as `boot`
113
+ * so the profile can read its validated `profile.<appName>` namespace.
114
+ */
115
+ reposProvider?: (ctx: ProfileBootContext) => Repo[];
116
+ healthChecks?: HealthCheck[];
117
+ setupCli?: (program: Command) => void;
118
+ helpSources?: HelpSource[];
119
+ defaultHelpPage?: string;
120
+ configSchemaExt?: z.ZodObject<z.ZodRawShape>;
121
+ mountCandidatesProvider?: () => Mount[];
122
+ /** Lifecycle hook — see ProfileBootContext / ProfileBootResult. */
123
+ boot?: ProfileBootHook;
124
+ }
125
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Highlight pattern matcher for log lines.
3
+ *
4
+ * Pure: no I/O. Patterns are compiled once via `compileHighlights` and the
5
+ * resulting matcher is cheap to call per line.
6
+ *
7
+ * Severity inference: lines containing "fatal", "error", "exception", or
8
+ * "stack trace" → 'error'. Lines containing "warning" → 'warn'. Otherwise
9
+ * 'info'.
10
+ *
11
+ * Default patterns ship a domain-neutral minimum (ERROR / FATAL / WARN /
12
+ * Stack trace). Consumers add language-specific patterns via config or a
13
+ * profile-specific default.
14
+ */
15
+ export type HighlightSeverity = 'info' | 'warn' | 'error';
16
+ export interface HighlightMatch {
17
+ matched: boolean;
18
+ severity: HighlightSeverity;
19
+ /** The first matching pattern source, or null. */
20
+ pattern: string | null;
21
+ /**
22
+ * True when the matched pattern represents a NEW error event (eligible for
23
+ * a notification). False for continuation lines like "Stack trace" that
24
+ * accompany an already-notified fatal.
25
+ */
26
+ isInitiator: boolean;
27
+ }
28
+ export interface CompiledHighlights {
29
+ patterns: Array<{
30
+ source: string;
31
+ regex: RegExp;
32
+ }>;
33
+ match: (line: string) => HighlightMatch;
34
+ }
35
+ /**
36
+ * Domain-neutral default highlight patterns. Case-insensitive substring
37
+ * matches. Consumers who want language-specific shapes (PHP fatals,
38
+ * stack frames, framework-specific markers) add them via config.
39
+ */
40
+ export declare const DEFAULT_HIGHLIGHT_PATTERNS: readonly string[];
41
+ /**
42
+ * Compile a list of pattern sources into a matcher. Each source is treated
43
+ * as a case-insensitive substring (regex metacharacters escaped).
44
+ */
45
+ export declare function compileHighlights(sources: readonly string[]): CompiledHighlights;
46
+ /** One-shot match without an explicit compile step. Slower per call. */
47
+ export declare function matchHighlights(line: string, patterns: readonly string[]): HighlightMatch;
48
+ //# sourceMappingURL=highlights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlights.d.ts","sourceRoot":"","sources":["../../src/docker/highlights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,kDAAkD;IAClD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC;CACzC;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAKvD,CAAC;AA4BF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CAsBhF;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CAEzF"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Highlight pattern matcher for log lines.
3
+ *
4
+ * Pure: no I/O. Patterns are compiled once via `compileHighlights` and the
5
+ * resulting matcher is cheap to call per line.
6
+ *
7
+ * Severity inference: lines containing "fatal", "error", "exception", or
8
+ * "stack trace" → 'error'. Lines containing "warning" → 'warn'. Otherwise
9
+ * 'info'.
10
+ *
11
+ * Default patterns ship a domain-neutral minimum (ERROR / FATAL / WARN /
12
+ * Stack trace). Consumers add language-specific patterns via config or a
13
+ * profile-specific default.
14
+ */
15
+ /**
16
+ * Domain-neutral default highlight patterns. Case-insensitive substring
17
+ * matches. Consumers who want language-specific shapes (PHP fatals,
18
+ * stack frames, framework-specific markers) add them via config.
19
+ */
20
+ export const DEFAULT_HIGHLIGHT_PATTERNS = [
21
+ 'ERROR',
22
+ 'FATAL',
23
+ 'WARN',
24
+ 'Stack trace',
25
+ ];
26
+ function inferSeverity(source) {
27
+ const lower = source.toLowerCase();
28
+ if (lower.includes('fatal') ||
29
+ lower.includes('error') ||
30
+ lower.includes('exception') ||
31
+ lower.includes('stack trace')) {
32
+ return 'error';
33
+ }
34
+ if (lower.includes('warning') || lower.includes('warn'))
35
+ return 'warn';
36
+ return 'info';
37
+ }
38
+ /**
39
+ * Initiator = a NEW error event (worth notifying). Continuation = a
40
+ * follow-on line (stack frame, trace header) that accompanies an
41
+ * initiator already notified — should still highlight in Output but
42
+ * shouldn't trigger its own notification.
43
+ */
44
+ function isInitiatorPattern(source) {
45
+ const lower = source.toLowerCase();
46
+ if (lower.includes('stack trace'))
47
+ return false;
48
+ return true;
49
+ }
50
+ /**
51
+ * Compile a list of pattern sources into a matcher. Each source is treated
52
+ * as a case-insensitive substring (regex metacharacters escaped).
53
+ */
54
+ export function compileHighlights(sources) {
55
+ const patterns = sources.map((source) => ({
56
+ source,
57
+ regex: new RegExp(source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'),
58
+ }));
59
+ return {
60
+ patterns,
61
+ match(line) {
62
+ for (const p of patterns) {
63
+ if (p.regex.test(line)) {
64
+ return {
65
+ matched: true,
66
+ severity: inferSeverity(p.source),
67
+ pattern: p.source,
68
+ isInitiator: isInitiatorPattern(p.source),
69
+ };
70
+ }
71
+ }
72
+ return { matched: false, severity: 'info', pattern: null, isInitiator: false };
73
+ },
74
+ };
75
+ }
76
+ /** One-shot match without an explicit compile step. Slower per call. */
77
+ export function matchHighlights(line, patterns) {
78
+ return compileHighlights(patterns).match(line);
79
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * DockerLogTailer — tails `docker compose logs -f` for configured services,
3
+ * routes lines into the Output pane, surfaces highlight matches into the
4
+ * Recent Errors ring buffer, and emits OS notifications via the shared
5
+ * notifier.
6
+ *
7
+ * Discipline:
8
+ * - Subprocess goes through `core/subprocess.spawnStream` (DRY).
9
+ * - Notifications go through `core/notifier.emitEvent` (DRY).
10
+ * - Notification debounce: identical errors (same file:line OR same text
11
+ * fingerprint) within `DEBOUNCE_MS` produce ONE notification, not N.
12
+ *
13
+ * Line format: `docker compose logs --no-color` emits `<service> | <body>`
14
+ * (variable spaces). The leading service token is parsed; the rest is the
15
+ * line body.
16
+ */
17
+ import { spawnStream } from '../core/subprocess.js';
18
+ import { type NotifyOptions } from '../core/notifier.js';
19
+ import type { OutputLine, RecentError } from '../cockpit/state/store.js';
20
+ import type { NotifiableEvent } from '../core/types.js';
21
+ /** Debounce window for identical errors (default 30s). */
22
+ export declare const DEBOUNCE_MS = 30000;
23
+ /** Liveness threshold — a service silent for this long is treated as down. */
24
+ export declare const SILENT_THRESHOLD_MS = 90000;
25
+ export interface DockerLogTailerOptions {
26
+ workspaceRoot: string;
27
+ services: readonly string[];
28
+ /** Pattern sources from cockpit.yaml `highlights[]` (or defaults). */
29
+ highlightPatterns: readonly string[];
30
+ /** Push a line into the Output pane. */
31
+ appendOutput: (line: OutputLine) => void;
32
+ /** Push a recent-error entry. */
33
+ pushRecentError: (err: RecentError) => void;
34
+ /**
35
+ * Notification options. Read fresh on every notify so the session-toggle
36
+ * keystroke takes effect immediately.
37
+ */
38
+ notifyOpts: () => NotifyOptions;
39
+ /** Optional explicit compose-file path. If absent, docker auto-discovers. */
40
+ composeFile?: string;
41
+ /**
42
+ * Event name emitted on a highlighted error initiator. Defaults to
43
+ * 'docker-error'. Consumers with existing `notifications.exclude` lists
44
+ * keyed on a specific name can override (e.g. 'php-fatal').
45
+ */
46
+ errorEventName?: NotifiableEvent;
47
+ /** Inject a custom spawner for tests. */
48
+ spawn?: typeof spawnStream;
49
+ /** Inject a clock for tests (defaults to Date.now). */
50
+ now?: () => number;
51
+ }
52
+ export declare class DockerLogTailer {
53
+ private readonly opts;
54
+ private handle;
55
+ private compiled;
56
+ private spawn;
57
+ private now;
58
+ /** fingerprint → timestamp of last notify. Used for debounce. */
59
+ private lastNotified;
60
+ private serviceState;
61
+ private liveCheckInterval;
62
+ constructor(opts: DockerLogTailerOptions);
63
+ /**
64
+ * Resolve the docker-compose file path. If `opts.composeFile` is set
65
+ * and exists, use it. Otherwise return null and let docker auto-discover.
66
+ */
67
+ private resolveComposeFile;
68
+ start(): void;
69
+ stop(): Promise<void>;
70
+ /**
71
+ * Parse a single docker-compose log line: `<service> | <body>`.
72
+ * Returns null if the line doesn't match (e.g. compose preamble).
73
+ */
74
+ parseLine(raw: string): {
75
+ service: string;
76
+ body: string;
77
+ } | null;
78
+ /** Public for testing — exercise per-line logic without a real subprocess. */
79
+ handleLine(raw: string): void;
80
+ private maybeNotifyError;
81
+ /** If a tracked service has been silent past the threshold, notify once. */
82
+ private checkLiveness;
83
+ }
84
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/docker/logs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,WAAW,EAAsB,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,0DAA0D;AAC1D,eAAO,MAAM,WAAW,QAAS,CAAC;AAElC,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,sEAAsE;IACtE,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,wCAAwC;IACxC,YAAY,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACzC,iCAAiC;IACjC,eAAe,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5C;;;OAGG;IACH,UAAU,EAAE,MAAM,aAAa,CAAC;IAChC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,WAAW,CAAC;IAC3B,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAOD,qBAAa,eAAe;IAUd,OAAO,CAAC,QAAQ,CAAC,IAAI;IATjC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,GAAG,CAAe;IAC1B,iEAAiE;IACjE,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,iBAAiB,CAA+B;gBAE3B,IAAI,EAAE,sBAAsB;IASzD;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAU1B,KAAK,IAAI,IAAI;IAwBP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAMhE,8EAA8E;IAC9E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAwC7B,OAAO,CAAC,gBAAgB;IAqBxB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;CAetB"}