@forwardimpact/outpost 3.1.4 → 3.2.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.
@@ -3,24 +3,15 @@
3
3
  // bag, threaded into src/outpost.js's dispatch via run(runtime, version).
4
4
  import "@forwardimpact/libpreflight/node22";
5
5
 
6
- import { readFileSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
-
6
+ import { resolveVersion } from "@forwardimpact/libcli";
10
7
  import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
11
8
 
12
9
  import { run } from "../src/outpost.js";
13
10
 
14
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
-
16
- // In compiled binaries (bun build --compile), `bun build --define` injects the
17
- // version string here so the readFileSync branch is eliminated as dead code.
18
- // Source execution falls through to package.json.
19
- const VERSION =
20
- process.env.OUTPOST_VERSION ||
21
- JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"))
22
- .version;
23
-
24
11
  const runtime = createDefaultRuntime();
25
- const code = await run(runtime, VERSION);
12
+ const version = resolveVersion({
13
+ packageJsonUrl: new URL("../package.json", import.meta.url),
14
+ runtime,
15
+ });
16
+ const code = await run(runtime, version);
26
17
  if (code) runtime.proc.exit(code);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/outpost",
3
- "version": "3.1.4",
3
+ "version": "3.2.0",
4
4
  "description": "Personal operations center — context from email, calendar, and knowledge assembled so preparation is continuous, not a morning scramble.",
5
5
  "homepage": "https://www.forwardimpact.team",
6
6
  "repository": {
@@ -50,7 +50,6 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@forwardimpact/libcli": "^0.1.0",
53
- "@forwardimpact/libmacos": "^0.1.0",
54
53
  "@forwardimpact/libpreflight": "^0.1.0",
55
54
  "@forwardimpact/libtelemetry": "^0.1.33",
56
55
  "@forwardimpact/libutil": "^0.1.0"
@@ -58,10 +57,16 @@
58
57
  "devDependencies": {
59
58
  "@forwardimpact/libmock": "^0.1.0"
60
59
  },
60
+ "optionalDependencies": {
61
+ "@forwardimpact/libmacos": "^0.1.0"
62
+ },
61
63
  "engines": {
62
64
  "bun": ">=1.2.0",
63
65
  "node": ">=22.0.0"
64
66
  },
67
+ "os": [
68
+ "darwin"
69
+ ],
65
70
  "publishConfig": {
66
71
  "access": "public"
67
72
  }
@@ -16,6 +16,7 @@ export class AgentRunner {
16
16
  #fs;
17
17
  #proc;
18
18
  #clock;
19
+ #runtime;
19
20
 
20
21
  /**
21
22
  * @param {Object | (() => Object | Promise<{default?: Object}>)} spawn -
@@ -43,6 +44,7 @@ export class AgentRunner {
43
44
  this.#fs = runtime.fs;
44
45
  this.#proc = runtime.proc;
45
46
  this.#clock = runtime.clock;
47
+ this.#runtime = runtime;
46
48
  this.#activeChildren = new Set();
47
49
  }
48
50
 
@@ -181,14 +183,19 @@ export class AgentRunner {
181
183
  spawnArgs,
182
184
  env,
183
185
  kbPath,
186
+ this.#runtime,
184
187
  );
185
188
  this.#activeChildren.add(pid);
186
189
 
187
- const exitCode = await spawnMod.waitForExit(pid);
190
+ const exitCode = await spawnMod.waitForExit(
191
+ pid,
192
+ undefined,
193
+ this.#runtime,
194
+ );
188
195
  this.#activeChildren.delete(pid);
189
196
 
190
- const stdout = spawnMod.readOutput(stdoutFile);
191
- const stderr = spawnMod.readOutput(stderrFile);
197
+ const stdout = spawnMod.readOutput(stdoutFile, this.#runtime);
198
+ const stderr = spawnMod.readOutput(stderrFile, this.#runtime);
192
199
 
193
200
  if (exitCode === 0) {
194
201
  this.#log(
package/src/kb-manager.js CHANGED
@@ -6,11 +6,10 @@ import { join, dirname, resolve } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { createLogger } from "@forwardimpact/libtelemetry";
8
8
 
9
- const logger = createLogger("outpost");
10
-
11
9
  /** Manage knowledge base lifecycle including initialization, updates, and settings merging. */
12
10
  export class KBManager {
13
11
  #fs;
12
+ #logger;
14
13
 
15
14
  /**
16
15
  * @param {import("@forwardimpact/libutil/runtime").Runtime} runtime
@@ -21,6 +20,7 @@ export class KBManager {
21
20
  if (!runtime?.fs) throw new Error("runtime.fs is required");
22
21
  if (!logFn) throw new Error("logFn is required");
23
22
  this.#fs = runtime.fs;
23
+ this.#logger = createLogger("outpost", runtime);
24
24
  }
25
25
 
26
26
  /**
@@ -74,12 +74,12 @@ export class KBManager {
74
74
  */
75
75
  async copyBundledFiles(tpl, dest) {
76
76
  await this.#fs.copyFile(join(tpl, "CLAUDE.md"), join(dest, "CLAUDE.md"));
77
- logger.info(` Updated CLAUDE.md`);
77
+ this.#logger.info(` Updated CLAUDE.md`);
78
78
 
79
79
  const apmSrc = join(tpl, "apm.yml");
80
80
  if (await this.#exists(apmSrc)) {
81
81
  await this.#fs.copyFile(apmSrc, join(dest, "apm.yml"));
82
- logger.info(` Updated apm.yml`);
82
+ this.#logger.info(` Updated apm.yml`);
83
83
  }
84
84
 
85
85
  await this.mergeSettings(tpl, dest);
@@ -96,7 +96,9 @@ export class KBManager {
96
96
  const names = entries.map((d) =>
97
97
  sub === "agents" ? d.name.replace(".md", "") : d.name,
98
98
  );
99
- logger.info(` Updated ${names.length} ${sub}: ${names.join(", ")}`);
99
+ this.#logger.info(
100
+ ` Updated ${names.length} ${sub}: ${names.join(", ")}`,
101
+ );
100
102
  }
101
103
  }
102
104
 
@@ -141,7 +143,7 @@ export class KBManager {
141
143
  if (!(await this.#exists(destPath))) {
142
144
  await this.#ensureDir(join(dest, ".claude"));
143
145
  await this.#fs.copyFile(src, destPath);
144
- logger.info(` Created settings.json`);
146
+ this.#logger.info(` Created settings.json`);
145
147
  return;
146
148
  }
147
149
 
@@ -154,9 +156,9 @@ export class KBManager {
154
156
 
155
157
  if (added > 0) {
156
158
  await this.#writeJSON(destPath, existing);
157
- logger.info(` Updated settings.json (${added} new entries)`);
159
+ this.#logger.info(` Updated settings.json (${added} new entries)`);
158
160
  } else {
159
- logger.info(` Settings up to date`);
161
+ this.#logger.info(` Settings up to date`);
160
162
  }
161
163
  }
162
164
 
@@ -193,7 +195,7 @@ export class KBManager {
193
195
 
194
196
  await this.copyBundledFiles(templateDir, dest);
195
197
 
196
- logger.info(
198
+ this.#logger.info(
197
199
  `Knowledge base initialized at ${dest}\n\nNext steps:\n 1. Edit ${dest}/USER.md with your name, email, and domain\n 2. cd ${dest} && npx apm install\n 3. claude`,
198
200
  );
199
201
  return { ok: true, value: { dest } };
@@ -215,7 +217,7 @@ export class KBManager {
215
217
  };
216
218
  }
217
219
  await this.copyBundledFiles(templateDir, dest);
218
- logger.info(`\nKnowledge base updated: ${dest}`);
220
+ this.#logger.info(`\nKnowledge base updated: ${dest}`);
219
221
  return { ok: true, value: { dest } };
220
222
  }
221
223
 
package/src/outpost.js CHANGED
@@ -22,8 +22,6 @@ import { createCli } from "@forwardimpact/libcli";
22
22
  import { createLogger } from "@forwardimpact/libtelemetry";
23
23
  import { isoTimestamp } from "@forwardimpact/libutil";
24
24
 
25
- const logger = createLogger("outpost");
26
-
27
25
  import { StateManager } from "./state-manager.js";
28
26
  import { AgentRunner } from "./agent-runner.js";
29
27
  import { Scheduler, formatLocalTime } from "./scheduler.js";
@@ -131,6 +129,7 @@ function renderAgentStatus(name, agent, s, kbMissing) {
131
129
  */
132
130
  export async function run(runtime, version) {
133
131
  const { fs, proc, clock } = runtime;
132
+ const logger = createLogger("outpost", runtime);
134
133
 
135
134
  /**
136
135
  * Async existence check via the one fs surface this module uses.
@@ -401,7 +400,7 @@ export async function run(runtime, version) {
401
400
  }
402
401
 
403
402
  // --- CLI entry point -------------------------------------------------------
404
- const cli = createCli(buildDefinition(version));
403
+ const cli = createCli(buildDefinition(version), { runtime });
405
404
  const parsed = cli.parse(proc.argv.slice(2));
406
405
  if (!parsed) return 0;
407
406
 
@@ -5,8 +5,6 @@
5
5
  import { createServer } from "node:net";
6
6
  import { createConnection } from "node:net";
7
7
  import { createLogger } from "@forwardimpact/libtelemetry";
8
-
9
- const logger = createLogger("outpost");
10
8
  import { join, resolve } from "node:path";
11
9
  import { homedir } from "node:os";
12
10
  import { computeNextWakeAt, nowFromClock } from "./scheduler.js";
@@ -326,6 +324,7 @@ export class SocketServer {
326
324
  export async function requestShutdown(socketPath, runtime) {
327
325
  if (!runtime?.fsSync) throw new Error("runtime.fsSync is required");
328
326
  if (!runtime?.clock) throw new Error("runtime.clock is required");
327
+ const logger = createLogger("outpost", runtime);
329
328
  if (!runtime.fsSync.existsSync(socketPath)) {
330
329
  logger.info("Daemon not running (no socket).");
331
330
  return false;