@brainpilot/app 0.0.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.
Files changed (53) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.d.ts.map +1 -0
  3. package/dist/bin.js +10 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/commands/down.d.ts +17 -0
  6. package/dist/commands/down.d.ts.map +1 -0
  7. package/dist/commands/down.js +33 -0
  8. package/dist/commands/down.js.map +1 -0
  9. package/dist/commands/init.d.ts +23 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +32 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/logs.d.ts +18 -0
  14. package/dist/commands/logs.d.ts.map +1 -0
  15. package/dist/commands/logs.js +78 -0
  16. package/dist/commands/logs.js.map +1 -0
  17. package/dist/commands/status.d.ts +26 -0
  18. package/dist/commands/status.d.ts.map +1 -0
  19. package/dist/commands/status.js +71 -0
  20. package/dist/commands/status.js.map +1 -0
  21. package/dist/commands/up.d.ts +61 -0
  22. package/dist/commands/up.d.ts.map +1 -0
  23. package/dist/commands/up.js +150 -0
  24. package/dist/commands/up.js.map +1 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +19 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/paths.d.ts +38 -0
  30. package/dist/paths.d.ts.map +1 -0
  31. package/dist/paths.js +51 -0
  32. package/dist/paths.js.map +1 -0
  33. package/dist/process-control.d.ts +33 -0
  34. package/dist/process-control.d.ts.map +1 -0
  35. package/dist/process-control.js +95 -0
  36. package/dist/process-control.js.map +1 -0
  37. package/dist/program.d.ts +30 -0
  38. package/dist/program.d.ts.map +1 -0
  39. package/dist/program.js +123 -0
  40. package/dist/program.js.map +1 -0
  41. package/dist/scaffold.d.ts +23 -0
  42. package/dist/scaffold.d.ts.map +1 -0
  43. package/dist/scaffold.js +110 -0
  44. package/dist/scaffold.js.map +1 -0
  45. package/dist/spawn-backend.d.ts +9 -0
  46. package/dist/spawn-backend.d.ts.map +1 -0
  47. package/dist/spawn-backend.js +48 -0
  48. package/dist/spawn-backend.js.map +1 -0
  49. package/dist/web-dist.d.ts +7 -0
  50. package/dist/web-dist.d.ts.map +1 -0
  51. package/dist/web-dist.js +23 -0
  52. package/dist/web-dist.js.map +1 -0
  53. package/package.json +33 -0
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @brainpilot/cli — Docker-free local one-click launch (§11A).
3
+ *
4
+ * Public surface: the command functions (for programmatic/test use) plus a
5
+ * `run(argv)` that drives the commander program.
6
+ */
7
+ export { up, buildStartServerOptions, ProviderKeyError, PortInUseError } from "./commands/up.js";
8
+ export { down } from "./commands/down.js";
9
+ export { status } from "./commands/status.js";
10
+ export { init } from "./commands/init.js";
11
+ export { logs, tailFile } from "./commands/logs.js";
12
+ export { resolveDataDir, dataPaths, resolvePaths, DEFAULT_DATA_DIR_NAME, } from "./paths.js";
13
+ export { scaffold, isScaffolded, DEFAULT_PORT } from "./scaffold.js";
14
+ export { writePid, readPid, removePid, isRunning, stop, } from "./process-control.js";
15
+ export { resolveWebDist } from "./web-dist.js";
16
+ export { spawnDetachedBackend, resolveBackendServerPath } from "./spawn-backend.js";
17
+ export { buildProgram, run } from "./program.js";
18
+ export const CLI_NAME = "@brainpilot/cli";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,EAAE,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjG,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EACL,cAAc,EACd,SAAS,EACT,YAAY,EACZ,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EACL,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,IAAI,GACL,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,CAAC,MAAM,QAAQ,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,38 @@
1
+ export interface ResolveDataDirOptions {
2
+ /** Explicit `--dir <path>` value (highest priority). */
3
+ dir?: string;
4
+ /** Environment (defaults to `process.env`). */
5
+ env?: Record<string, string | undefined>;
6
+ /** Base for relative resolution (defaults to `process.cwd()`). */
7
+ cwd?: string;
8
+ }
9
+ /** Default data-root directory name created under the launch cwd. */
10
+ export declare const DEFAULT_DATA_DIR_NAME = "brainpilot";
11
+ /**
12
+ * Resolve the absolute data-root directory.
13
+ * Precedence: `--dir` > `BP_DATA_DIR` > `<cwd>/brainpilot`.
14
+ */
15
+ export declare function resolveDataDir(options?: ResolveDataDirOptions): string;
16
+ /** Well-known paths under a resolved data dir (§11A.2). */
17
+ export interface DataPaths {
18
+ dataDir: string;
19
+ bpTemplate: string;
20
+ bpTemplateAgents: string;
21
+ bpTemplateSettings: string;
22
+ bpTemplateMcpServers: string;
23
+ bp: string;
24
+ workspaces: string;
25
+ brainpilotConfig: string;
26
+ dotenv: string;
27
+ runtimeDir: string;
28
+ logsDir: string;
29
+ backendLog: string;
30
+ runtimeLog: string;
31
+ backendPid: string;
32
+ runtimePid: string;
33
+ }
34
+ /** Build all derived paths from a data dir. Pure — no filesystem access. */
35
+ export declare function dataPaths(dataDir: string): DataPaths;
36
+ /** Convenience: resolve data dir + derived paths in one call. */
37
+ export declare function resolvePaths(options?: ResolveDataDirOptions): DataPaths;
38
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAQ1E;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAqBpD;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B,GAAG,SAAS,CAE3E"}
package/dist/paths.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * paths.ts — resolve the data dir and the well-known files/dirs underneath it
3
+ * (TS_PI_REFACTOR_DESIGN §11A.2). Precedence for the data dir:
4
+ * explicit `--dir` > `BP_DATA_DIR` env > `./brainpilot` under cwd.
5
+ */
6
+ import { resolve, join } from "node:path";
7
+ /** Default data-root directory name created under the launch cwd. */
8
+ export const DEFAULT_DATA_DIR_NAME = "brainpilot";
9
+ /**
10
+ * Resolve the absolute data-root directory.
11
+ * Precedence: `--dir` > `BP_DATA_DIR` > `<cwd>/brainpilot`.
12
+ */
13
+ export function resolveDataDir(options = {}) {
14
+ const env = options.env ?? process.env;
15
+ const cwd = options.cwd ?? process.cwd();
16
+ const explicit = options.dir?.trim();
17
+ if (explicit)
18
+ return resolve(cwd, explicit);
19
+ const fromEnv = env.BP_DATA_DIR?.trim();
20
+ if (fromEnv)
21
+ return resolve(cwd, fromEnv);
22
+ return resolve(cwd, DEFAULT_DATA_DIR_NAME);
23
+ }
24
+ /** Build all derived paths from a data dir. Pure — no filesystem access. */
25
+ export function dataPaths(dataDir) {
26
+ const bpTemplate = join(dataDir, "bp_template");
27
+ const runtimeDir = join(dataDir, ".runtime");
28
+ const logsDir = join(runtimeDir, "logs");
29
+ return {
30
+ dataDir,
31
+ bpTemplate,
32
+ bpTemplateAgents: join(bpTemplate, "agents"),
33
+ bpTemplateSettings: join(bpTemplate, "settings.json"),
34
+ bpTemplateMcpServers: join(bpTemplate, "mcp_servers.json"),
35
+ bp: join(dataDir, ".bp"),
36
+ workspaces: join(dataDir, "workspaces"),
37
+ brainpilotConfig: join(dataDir, "brainpilot.config.json"),
38
+ dotenv: join(dataDir, ".env"),
39
+ runtimeDir,
40
+ logsDir,
41
+ backendLog: join(logsDir, "backend.log"),
42
+ runtimeLog: join(logsDir, "runtime.log"),
43
+ backendPid: join(runtimeDir, "backend.pid"),
44
+ runtimePid: join(runtimeDir, "runtime.pid"),
45
+ };
46
+ }
47
+ /** Convenience: resolve data dir + derived paths in one call. */
48
+ export function resolvePaths(options = {}) {
49
+ return dataPaths(resolveDataDir(options));
50
+ }
51
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAW1C,qEAAqE;AACrE,MAAM,CAAC,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAElD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,UAAiC,EAAE;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACrC,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;AAC7C,CAAC;AAqBD,4EAA4E;AAC5E,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO;QACL,OAAO;QACP,UAAU;QACV,gBAAgB,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC5C,kBAAkB,EAAE,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC;QACrD,oBAAoB,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC;QAC1D,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;QACxB,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;QACvC,gBAAgB,EAAE,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC;QACzD,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;QAC7B,UAAU;QACV,OAAO;QACP,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QACxC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QACxC,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC;QAC3C,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAC,UAAiC,EAAE;IAC9D,OAAO,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,33 @@
1
+ export interface ProcessControlDeps {
2
+ /** Probe liveness. Defaults to `process.kill(pid, 0)`. */
3
+ isAlive?: (pid: number) => boolean;
4
+ /** Send a signal. Defaults to `process.kill`. */
5
+ signal?: (pid: number, sig: NodeJS.Signals) => void;
6
+ /** Sleep impl (injectable for tests). */
7
+ sleep?: (ms: number) => Promise<void>;
8
+ }
9
+ /** Write a pid to a file, creating parent dirs. */
10
+ export declare function writePid(pidFile: string, pid: number): Promise<void>;
11
+ /** Read a pid from a file, or null if missing/unparseable. */
12
+ export declare function readPid(pidFile: string): Promise<number | null>;
13
+ /** Remove a pid file (no error if absent). */
14
+ export declare function removePid(pidFile: string): Promise<void>;
15
+ /** Is the process recorded in `pidFile` currently alive? */
16
+ export declare function isRunning(pidFile: string, deps?: ProcessControlDeps): Promise<boolean>;
17
+ export interface StopResult {
18
+ /** Was a live process found and stopped (or already gone)? */
19
+ stopped: boolean;
20
+ /** The pid that was targeted, if any. */
21
+ pid: number | null;
22
+ /** True if SIGKILL was needed after SIGTERM timed out. */
23
+ forced: boolean;
24
+ }
25
+ /**
26
+ * Gracefully stop the process recorded in `pidFile`: SIGTERM, wait up to
27
+ * `timeoutMs`, then SIGKILL. Removes the pid file afterward.
28
+ */
29
+ export declare function stop(pidFile: string, options?: {
30
+ timeoutMs?: number;
31
+ pollMs?: number;
32
+ } & ProcessControlDeps): Promise<StopResult>;
33
+ //# sourceMappingURL=process-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-control.d.ts","sourceRoot":"","sources":["../src/process-control.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;IACpD,yCAAyC;IACzC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAmBD,mDAAmD;AACnD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED,8DAA8D;AAC9D,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQrE;AAED,8CAA8C;AAC9C,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,4DAA4D;AAC5D,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAuB,GACzE,OAAO,CAAC,UAAU,CAAC,CA2CrB"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * process-control.ts — pid file lifecycle for detached `up` + `down`/`status`
3
+ * (TS_PI_REFACTOR_DESIGN §11A.5). Pure-ish: process probing/signalling is
4
+ * injectable so tests run against a fake process table.
5
+ */
6
+ import { writeFile, readFile, rm, mkdir } from "node:fs/promises";
7
+ import { dirname } from "node:path";
8
+ const realIsAlive = (pid) => {
9
+ try {
10
+ process.kill(pid, 0);
11
+ return true;
12
+ }
13
+ catch (err) {
14
+ // ESRCH = no such process; EPERM = exists but not ours (treat as alive).
15
+ return err.code === "EPERM";
16
+ }
17
+ };
18
+ const realSignal = (pid, sig) => {
19
+ process.kill(pid, sig);
20
+ };
21
+ const realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
22
+ /** Write a pid to a file, creating parent dirs. */
23
+ export async function writePid(pidFile, pid) {
24
+ await mkdir(dirname(pidFile), { recursive: true });
25
+ await writeFile(pidFile, String(pid), "utf8");
26
+ }
27
+ /** Read a pid from a file, or null if missing/unparseable. */
28
+ export async function readPid(pidFile) {
29
+ try {
30
+ const raw = (await readFile(pidFile, "utf8")).trim();
31
+ const pid = Number.parseInt(raw, 10);
32
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /** Remove a pid file (no error if absent). */
39
+ export async function removePid(pidFile) {
40
+ await rm(pidFile, { force: true });
41
+ }
42
+ /** Is the process recorded in `pidFile` currently alive? */
43
+ export async function isRunning(pidFile, deps = {}) {
44
+ const pid = await readPid(pidFile);
45
+ if (pid === null)
46
+ return false;
47
+ return (deps.isAlive ?? realIsAlive)(pid);
48
+ }
49
+ /**
50
+ * Gracefully stop the process recorded in `pidFile`: SIGTERM, wait up to
51
+ * `timeoutMs`, then SIGKILL. Removes the pid file afterward.
52
+ */
53
+ export async function stop(pidFile, options = {}) {
54
+ const isAlive = options.isAlive ?? realIsAlive;
55
+ const signal = options.signal ?? realSignal;
56
+ const sleep = options.sleep ?? realSleep;
57
+ const timeoutMs = options.timeoutMs ?? 10_000;
58
+ const pollMs = options.pollMs ?? 100;
59
+ const pid = await readPid(pidFile);
60
+ if (pid === null) {
61
+ return { stopped: false, pid: null, forced: false };
62
+ }
63
+ if (!isAlive(pid)) {
64
+ await removePid(pidFile);
65
+ return { stopped: true, pid, forced: false };
66
+ }
67
+ try {
68
+ signal(pid, "SIGTERM");
69
+ }
70
+ catch {
71
+ // process vanished between probe and signal — treat as stopped.
72
+ await removePid(pidFile);
73
+ return { stopped: true, pid, forced: false };
74
+ }
75
+ const deadline = Date.now() + timeoutMs;
76
+ while (Date.now() < deadline) {
77
+ if (!isAlive(pid)) {
78
+ await removePid(pidFile);
79
+ return { stopped: true, pid, forced: false };
80
+ }
81
+ await sleep(pollMs);
82
+ }
83
+ // Still alive — force kill.
84
+ let forced = false;
85
+ try {
86
+ signal(pid, "SIGKILL");
87
+ forced = true;
88
+ }
89
+ catch {
90
+ // already gone
91
+ }
92
+ await removePid(pidFile);
93
+ return { stopped: true, pid, forced };
94
+ }
95
+ //# sourceMappingURL=process-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-control.js","sourceRoot":"","sources":["../src/process-control.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * program.ts — commander wiring for the `brainpilot` / `bnpt` CLI (§11A.4).
3
+ * `buildProgram()` returns a configured Command; `run(argv)` parses + dispatches.
4
+ * Kept separate from bin.ts so it can be invoked programmatically in tests.
5
+ */
6
+ import { Command } from "commander";
7
+ import { up } from "./commands/up.js";
8
+ import { down } from "./commands/down.js";
9
+ import { status } from "./commands/status.js";
10
+ import { init } from "./commands/init.js";
11
+ import { logs } from "./commands/logs.js";
12
+ /** Hooks injectable for tests so `run()` never touches a real server/process. */
13
+ export interface ProgramDeps {
14
+ upFn?: typeof up;
15
+ downFn?: typeof down;
16
+ statusFn?: typeof status;
17
+ initFn?: typeof init;
18
+ logsFn?: typeof logs;
19
+ log?: (msg: string) => void;
20
+ /** Override process.exit (tests). */
21
+ onError?: (err: Error) => void;
22
+ }
23
+ export declare function buildProgram(deps?: ProgramDeps): Command;
24
+ /**
25
+ * Parse `argv` and dispatch. `argv` excludes node + script (commander's
26
+ * `from: "user"`). Returns the commander result; throws are surfaced unless an
27
+ * `onError` hook is provided.
28
+ */
29
+ export declare function run(argv: string[], deps?: ProgramDeps): Promise<void>;
30
+ //# sourceMappingURL=program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AAUD,wBAAgB,YAAY,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAqF5D;AAED;;;;GAIG;AACH,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * program.ts — commander wiring for the `brainpilot` / `bnpt` CLI (§11A.4).
3
+ * `buildProgram()` returns a configured Command; `run(argv)` parses + dispatches.
4
+ * Kept separate from bin.ts so it can be invoked programmatically in tests.
5
+ */
6
+ import { Command } from "commander";
7
+ import pc from "picocolors";
8
+ import { up } from "./commands/up.js";
9
+ import { down } from "./commands/down.js";
10
+ import { status } from "./commands/status.js";
11
+ import { init } from "./commands/init.js";
12
+ import { logs } from "./commands/logs.js";
13
+ import { spawnDetachedBackend } from "./spawn-backend.js";
14
+ function parsePort(value) {
15
+ const n = Number.parseInt(value, 10);
16
+ if (!Number.isInteger(n) || n <= 0 || n > 65535) {
17
+ throw new Error(`Invalid port: ${value}`);
18
+ }
19
+ return n;
20
+ }
21
+ export function buildProgram(deps = {}) {
22
+ const program = new Command();
23
+ const upFn = deps.upFn ?? up;
24
+ const downFn = deps.downFn ?? down;
25
+ const statusFn = deps.statusFn ?? status;
26
+ const initFn = deps.initFn ?? init;
27
+ const logsFn = deps.logsFn ?? logs;
28
+ program
29
+ .name("brainpilot")
30
+ .description("BrainPilot — Docker-free local launcher (§11A)")
31
+ .version("0.1.0");
32
+ program
33
+ .command("up")
34
+ .description("Start the BrainPilot backend (+runtime) and open the UI")
35
+ .option("-d, --dir <path>", "data directory (default ./brainpilot)")
36
+ .option("-p, --port <n>", "backend port (runtime uses port+1)", parsePort)
37
+ .option("--foreground", "run the backend in-process (do not detach)", false)
38
+ .option("--detach", "run the backend as a background process", false)
39
+ .option("--no-open", "do not open the browser")
40
+ .action(async (opts) => {
41
+ const foreground = Boolean(opts.foreground) && !opts.detach;
42
+ await upFn({
43
+ dir: opts.dir,
44
+ port: opts.port,
45
+ foreground,
46
+ open: opts.open,
47
+ }, { spawnDetached: spawnDetachedBackend });
48
+ // In foreground mode the server keeps the event loop alive.
49
+ });
50
+ program
51
+ .command("down")
52
+ .description("Stop the background BrainPilot backend")
53
+ .option("-d, --dir <path>", "data directory (default ./brainpilot)")
54
+ .action(async (opts) => {
55
+ await downFn({ dir: opts.dir });
56
+ });
57
+ program
58
+ .command("status")
59
+ .description("Report whether BrainPilot is running + health/metrics")
60
+ .option("-d, --dir <path>", "data directory (default ./brainpilot)")
61
+ .option("-p, --port <n>", "backend port", parsePort)
62
+ .action(async (opts) => {
63
+ await statusFn({ dir: opts.dir, port: opts.port });
64
+ });
65
+ program
66
+ .command("init")
67
+ .description("Scaffold the launch directory + persist a provider key")
68
+ .option("-d, --dir <path>", "data directory (default ./brainpilot)")
69
+ .option("-p, --port <n>", "default backend port", parsePort)
70
+ .option("--api-key <key>", "provider API key to persist")
71
+ .option("--model <id>", "default model id")
72
+ .action(async (opts) => {
73
+ await initFn({
74
+ dir: opts.dir,
75
+ port: opts.port,
76
+ apiKey: opts.apiKey,
77
+ model: opts.model,
78
+ });
79
+ });
80
+ program
81
+ .command("logs")
82
+ .description("Tail the backend/runtime logs")
83
+ .option("-d, --dir <path>", "data directory (default ./brainpilot)")
84
+ .option("--runtime", "show the runtime log instead of the backend log", false)
85
+ .option("-n, --lines <n>", "trailing lines to show", (v) => parseInt(v, 10), 200)
86
+ .option("-f, --follow", "follow the log (tail -f)", false)
87
+ .action(async (opts) => {
88
+ await logsFn({
89
+ dir: opts.dir,
90
+ which: opts.runtime ? "runtime" : "backend",
91
+ lines: opts.lines,
92
+ follow: opts.follow,
93
+ });
94
+ });
95
+ return program;
96
+ }
97
+ /**
98
+ * Parse `argv` and dispatch. `argv` excludes node + script (commander's
99
+ * `from: "user"`). Returns the commander result; throws are surfaced unless an
100
+ * `onError` hook is provided.
101
+ */
102
+ export async function run(argv, deps = {}) {
103
+ const program = buildProgram(deps);
104
+ program.exitOverride();
105
+ try {
106
+ await program.parseAsync(argv, { from: "user" });
107
+ }
108
+ catch (err) {
109
+ const log = deps.log ?? ((m) => console.error(m));
110
+ // commander's help/version throw with these codes — not real errors.
111
+ const code = err.code;
112
+ if (code === "commander.helpDisplayed" || code === "commander.version") {
113
+ return;
114
+ }
115
+ if (deps.onError) {
116
+ deps.onError(err);
117
+ return;
118
+ }
119
+ log(pc.red(err.message));
120
+ throw err;
121
+ }
122
+ }
123
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type DataPaths } from "./paths.js";
2
+ /** Default backend port (§11A.5 决策 D). Runtime uses port+1 (stride-2 §16). */
3
+ export declare const DEFAULT_PORT = 9001;
4
+ export interface ScaffoldOptions {
5
+ /** Default backend port baked into brainpilot.config.json. */
6
+ port?: number;
7
+ /** Default provider name baked into brainpilot.config.json. */
8
+ provider?: string;
9
+ }
10
+ export interface ScaffoldResult {
11
+ paths: DataPaths;
12
+ /** Files that were freshly created (absent before). */
13
+ created: string[];
14
+ }
15
+ /**
16
+ * Create the launch directory tree under `dataDir`. Idempotent — only writes
17
+ * files that don't yet exist. Returns the list of newly created files so the
18
+ * caller can report "scaffolded" vs "already present".
19
+ */
20
+ export declare function scaffold(dataDir: string, options?: ScaffoldOptions): Promise<ScaffoldResult>;
21
+ /** Whether the data dir already has a `bp_template/` (used by `up` to decide). */
22
+ export declare function isScaffolded(dataDir: string): Promise<boolean>;
23
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAYA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD,8EAA8E;AAC9E,eAAO,MAAM,YAAY,OAAO,CAAC;AAuEjC,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA2CzB;AAED,kFAAkF;AAClF,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * scaffold.ts — materialize the `./brainpilot/` launch directory tree
3
+ * (TS_PI_REFACTOR_DESIGN §11A.2). Creating a default editable `bp_template/`
4
+ * (agents prompts + example settings/mcp config) and a default
5
+ * `brainpilot.config.json`.
6
+ *
7
+ * Idempotent: existing files are never overwritten, protecting user edits
8
+ * (§11A.4 step 2 "幂等,已存在不覆盖").
9
+ */
10
+ import { mkdir, writeFile, access } from "node:fs/promises";
11
+ import { constants as FS } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { dataPaths } from "./paths.js";
14
+ /** Default backend port (§11A.5 决策 D). Runtime uses port+1 (stride-2 §16). */
15
+ export const DEFAULT_PORT = 9001;
16
+ async function exists(path) {
17
+ try {
18
+ await access(path, FS.F_OK);
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ /** Write a file only if it does not already exist. Returns true if written. */
26
+ async function writeIfAbsent(path, content) {
27
+ if (await exists(path))
28
+ return false;
29
+ await writeFile(path, content, "utf8");
30
+ return true;
31
+ }
32
+ /** Default principal agent prompt — minimal, user-editable. */
33
+ const PRINCIPAL_PROMPT = `# Principal Agent (PI)
34
+
35
+ You are the Principal Investigator — the user-facing orchestrator of the
36
+ BrainPilot multi-agent system. You decompose the user's request, delegate to
37
+ expert agents via \`send_message\`, and synthesize their results into a final
38
+ answer.
39
+
40
+ Keep the user informed of progress. Be concise and rigorous.
41
+ `;
42
+ const PRINCIPAL_SETTINGS = JSON.stringify({
43
+ provider: "anthropic",
44
+ model: "claude-sonnet-4-6",
45
+ timeoutMs: 120000,
46
+ maxRetries: 2,
47
+ }, null, 2);
48
+ const PRINCIPAL_MANIFEST = JSON.stringify({
49
+ role: "principal",
50
+ parent: null,
51
+ allowedTools: ["send_message", "create_agent", "destroy_agent", "record_trace"],
52
+ }, null, 2);
53
+ /** session-level default Pi SDK settings template (§11A.2). */
54
+ const TEMPLATE_SETTINGS_EXAMPLE = JSON.stringify({
55
+ provider: "anthropic",
56
+ model: "claude-sonnet-4-6",
57
+ apiKey: "",
58
+ baseUrl: "",
59
+ }, null, 2);
60
+ /** MCP server config template (§11A.2). */
61
+ const TEMPLATE_MCP_EXAMPLE = JSON.stringify({
62
+ mcpServers: {},
63
+ }, null, 2);
64
+ /**
65
+ * Create the launch directory tree under `dataDir`. Idempotent — only writes
66
+ * files that don't yet exist. Returns the list of newly created files so the
67
+ * caller can report "scaffolded" vs "already present".
68
+ */
69
+ export async function scaffold(dataDir, options = {}) {
70
+ const p = dataPaths(dataDir);
71
+ const created = [];
72
+ // ① Directory skeleton.
73
+ const principalDir = join(p.bpTemplateAgents, "principal");
74
+ await mkdir(p.dataDir, { recursive: true });
75
+ await mkdir(principalDir, { recursive: true });
76
+ await mkdir(p.bp, { recursive: true });
77
+ await mkdir(p.workspaces, { recursive: true });
78
+ await mkdir(p.logsDir, { recursive: true });
79
+ // ② Default bp_template/agents/principal/* (user-editable).
80
+ const writes = [
81
+ [join(principalDir, "prompt.md"), PRINCIPAL_PROMPT],
82
+ [join(principalDir, "settings.json"), PRINCIPAL_SETTINGS],
83
+ [join(principalDir, "manifest.json"), PRINCIPAL_MANIFEST],
84
+ // ③ session-level template defaults.
85
+ [p.bpTemplateSettings, TEMPLATE_SETTINGS_EXAMPLE],
86
+ [join(p.bpTemplate, "settings.example.json"), TEMPLATE_SETTINGS_EXAMPLE],
87
+ [p.bpTemplateMcpServers, TEMPLATE_MCP_EXAMPLE],
88
+ [join(p.bpTemplate, "mcp_servers.example.json"), TEMPLATE_MCP_EXAMPLE],
89
+ // ④ CLI global config.
90
+ [
91
+ p.brainpilotConfig,
92
+ JSON.stringify({
93
+ port: options.port ?? DEFAULT_PORT,
94
+ dataDir: ".",
95
+ provider: { name: options.provider ?? "anthropic" },
96
+ logLevel: "info",
97
+ }, null, 2),
98
+ ],
99
+ ];
100
+ for (const [path, content] of writes) {
101
+ if (await writeIfAbsent(path, content))
102
+ created.push(path);
103
+ }
104
+ return { paths: p, created };
105
+ }
106
+ /** Whether the data dir already has a `bp_template/` (used by `up` to decide). */
107
+ export async function isScaffolded(dataDir) {
108
+ return exists(dataPaths(dataDir).bpTemplate);
109
+ }
110
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ResolvedUpConfig } from "./commands/up.js";
2
+ /** Resolve `@brainpilot/backend-core/server` to an absolute path. */
3
+ export declare function resolveBackendServerPath(from?: string): string;
4
+ /**
5
+ * Spawn the backend as a detached child. Returns the child pid. Env injects
6
+ * BP_DATA_DIR + BP_MODE=single + the resolved ports (§11A.4 step 4).
7
+ */
8
+ export declare function spawnDetachedBackend(cfg: ResolvedUpConfig): Promise<number>;
9
+ //# sourceMappingURL=spawn-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-backend.d.ts","sourceRoot":"","sources":["../src/spawn-backend.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,qEAAqE;AACrE,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAG/E;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * spawn-backend.ts — default detached spawn used by `up --detach`. Launches the
3
+ * backend-core server entry as a background process, redirecting stdout/stderr
4
+ * to `.runtime/logs/backend.log`, and returns its pid (§11A.4 step 7 / §11A.5).
5
+ *
6
+ * The backend's own LocalProcessOrchestrator spawns the runtime; we only manage
7
+ * the backend process here.
8
+ */
9
+ import { spawn } from "node:child_process";
10
+ import { openSync, mkdirSync } from "node:fs";
11
+ import { createRequire } from "node:module";
12
+ import { dirname } from "node:path";
13
+ import { dataPaths } from "./paths.js";
14
+ /** Resolve `@brainpilot/backend-core/server` to an absolute path. */
15
+ export function resolveBackendServerPath(from = import.meta.url) {
16
+ const require = createRequire(from);
17
+ return require.resolve("@brainpilot/backend-core/server");
18
+ }
19
+ /**
20
+ * Spawn the backend as a detached child. Returns the child pid. Env injects
21
+ * BP_DATA_DIR + BP_MODE=single + the resolved ports (§11A.4 step 4).
22
+ */
23
+ export async function spawnDetachedBackend(cfg) {
24
+ const p = dataPaths(cfg.dataDir);
25
+ mkdirSync(p.logsDir, { recursive: true });
26
+ const out = openSync(p.backendLog, "a");
27
+ const serverPath = resolveBackendServerPath();
28
+ const child = spawn(process.execPath, [serverPath], {
29
+ detached: true,
30
+ stdio: ["ignore", out, out],
31
+ cwd: dirname(cfg.dataDir),
32
+ env: {
33
+ ...process.env,
34
+ BP_DATA_DIR: cfg.dataDir,
35
+ BP_MODE: "single",
36
+ BP_PORT: String(cfg.port),
37
+ PORT: String(cfg.port),
38
+ AGENT_RUNTIME_PORT: String(cfg.runtimePort),
39
+ ...(cfg.webDist ? { BP_WEB_ROOT: cfg.webDist } : {}),
40
+ },
41
+ });
42
+ child.unref();
43
+ if (child.pid === undefined) {
44
+ throw new Error("Failed to spawn backend process (no pid).");
45
+ }
46
+ return child.pid;
47
+ }
48
+ //# sourceMappingURL=spawn-backend.js.map