aos-harness 0.8.0 → 0.8.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": "aos-harness",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Agentic Orchestration System — assemble AI agents into deliberation and execution teams",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -38,18 +38,18 @@
38
38
  "test": "bun run src/index.ts validate"
39
39
  },
40
40
  "dependencies": {
41
- "@aos-harness/adapter-shared": "0.8.0",
42
- "@aos-harness/runtime": "0.8.0",
41
+ "@aos-harness/adapter-shared": "0.8.1",
42
+ "@aos-harness/runtime": "0.8.1",
43
43
  "@clack/prompts": "^1.2.0",
44
44
  "@modelcontextprotocol/sdk": "^1.29.0",
45
45
  "js-yaml": "^4.1.0",
46
46
  "yaml": "^2.8.3"
47
47
  },
48
48
  "peerDependencies": {
49
- "@aos-harness/claude-code-adapter": ">=0.8.0 <1.0.0",
50
- "@aos-harness/codex-adapter": ">=0.8.0 <1.0.0",
51
- "@aos-harness/gemini-adapter": ">=0.8.0 <1.0.0",
52
- "@aos-harness/pi-adapter": ">=0.8.0 <1.0.0"
49
+ "@aos-harness/claude-code-adapter": ">=0.8.1 <1.0.0",
50
+ "@aos-harness/codex-adapter": ">=0.8.1 <1.0.0",
51
+ "@aos-harness/gemini-adapter": ">=0.8.1 <1.0.0",
52
+ "@aos-harness/pi-adapter": ">=0.8.1 <1.0.0"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@aos-harness/claude-code-adapter": {
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { join, dirname } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
+ import { pathToFileURL } from "node:url";
17
18
  import { tmpdir } from "node:os";
18
19
  import { readFileSync } from "node:fs";
19
20
  import readline from "node:readline";
@@ -31,6 +32,7 @@ import { loadAgent } from "@aos-harness/runtime/config-loader";
31
32
  import { resolveTemplate } from "@aos-harness/runtime/template-resolver";
32
33
  import { startBridgeServer } from "./bridge-server";
33
34
  import { renderTextGauge, renderRoundOneLiner } from "./gauges";
35
+ import { getAdapterDir } from "./utils";
34
36
 
35
37
  export interface AdapterSessionConfig {
36
38
  platform: string; // "claude-code" | "gemini" | "codex"
@@ -147,29 +149,30 @@ async function loadAdapterRuntime(platform: string): Promise<any> {
147
149
  const entry = ADAPTER_MAP[platform];
148
150
  if (!entry) throw new Error(`Unknown adapter: ${platform}`);
149
151
 
150
- async function readAdapterVersion(fromPath: string): Promise<string> {
151
- try {
152
- const pkgUrl = new URL("../package.json", fromPath).href;
153
- const { readFile } = await import("node:fs/promises");
154
- const raw = await readFile(new URL(pkgUrl), "utf-8");
155
- return (JSON.parse(raw) as { version: string }).version ?? "unknown";
156
- } catch {
157
- return "unknown";
158
- }
152
+ const adapterDir = getAdapterDir(platform);
153
+ if (!adapterDir) {
154
+ printMissingAdapterError(entry.package);
155
+ process.exit(2);
159
156
  }
160
157
 
161
158
  let mod: any;
162
159
  try {
163
- mod = await import(entry.package);
160
+ mod = await import(pathToFileURL(join(adapterDir, "src", "index.ts")).href);
164
161
  } catch (err) {
165
162
  if (isModuleNotFound(err)) {
166
163
  printMissingAdapterError(entry.package);
167
164
  process.exit(2);
168
165
  }
169
- throw err; // real load error — surface it
166
+ throw err;
167
+ }
168
+
169
+ let version = "unknown";
170
+ try {
171
+ const raw = readFileSync(join(adapterDir, "package.json"), "utf-8");
172
+ version = (JSON.parse(raw) as { version?: string }).version ?? "unknown";
173
+ } catch {
174
+ version = "unknown";
170
175
  }
171
- const resolved = (import.meta as any).resolve?.(entry.package) ?? entry.package;
172
- const version = await readAdapterVersion(resolved);
173
176
  console.error(`[adapter] loaded ${entry.package}@${version}`);
174
177
  maybeWarnVersionMismatch(entry.package, version);
175
178
  return mod[entry.className];
@@ -63,6 +63,7 @@ export interface ScanEnvironmentOptions {
63
63
  npmGlobalDir?: string | null;
64
64
  probeVendorCli?: (adapter: AdapterName, meta: AdapterMetadata) => Promise<VendorCliReadiness>;
65
65
  resolveAdapterDir?: (adapter: AdapterName) => string | null;
66
+ findBinary?: (name: string) => string | null;
66
67
  }
67
68
 
68
69
  function detectPackageManager(): PackageManager {
@@ -310,6 +311,7 @@ export async function scanEnvironment(options: ScanEnvironmentOptions = {}): Pro
310
311
  const resolveAdapterDir = options.resolveAdapterDir ?? getAdapterDir;
311
312
  const bunGlobalDir = getBunGlobalDir(env, options.bunGlobalDir);
312
313
  const npmGlobalDir = getNpmGlobalDir(env, options.npmGlobalDir);
314
+ const findBinary = options.findBinary ?? ((name: string) => Bun.which(name) ?? null);
313
315
 
314
316
  const adapters = {} as Record<AdapterName, AdapterReadiness>;
315
317
  const notes: string[] = [];
@@ -341,6 +343,10 @@ export async function scanEnvironment(options: ScanEnvironmentOptions = {}): Pro
341
343
  }
342
344
 
343
345
  const socketPath = getMempalaceSocket(env);
346
+ const mempalaceBinary = findBinary("mempalace");
347
+ if (mempalaceBinary && !existsSync(socketPath)) {
348
+ notes.push(`mempalace: binary found at ${mempalaceBinary}, but socket ${socketPath} was not detected. Set MEMPALACE_SOCKET if MemPalace uses a custom socket path.`);
349
+ }
344
350
 
345
351
  return {
346
352
  packageManager: detectPackageManager(),
@@ -349,6 +355,8 @@ export async function scanEnvironment(options: ScanEnvironmentOptions = {}): Pro
349
355
  mempalace: {
350
356
  available: existsSync(socketPath),
351
357
  socketPath,
358
+ binaryInstalled: !!mempalaceBinary,
359
+ binaryPath: mempalaceBinary ?? undefined,
352
360
  },
353
361
  },
354
362
  notes,
package/src/init-types.ts CHANGED
@@ -39,6 +39,8 @@ export interface AdapterReadiness {
39
39
  export interface MemoryBackendScan {
40
40
  available: boolean;
41
41
  socketPath: string;
42
+ binaryInstalled: boolean;
43
+ binaryPath?: string;
42
44
  }
43
45
 
44
46
  export interface ScanReport {
package/src/prompts.ts CHANGED
@@ -38,6 +38,11 @@ function formatAdapterLine(adapter: AdapterName, readiness: AdapterReadiness): s
38
38
  }
39
39
 
40
40
  export function renderScanReport(scan: ScanReport): string {
41
+ const memorySummary = scan.memory.mempalace.available
42
+ ? `available (${scan.memory.mempalace.socketPath})`
43
+ : scan.memory.mempalace.binaryInstalled
44
+ ? `installed at ${scan.memory.mempalace.binaryPath}; socket not detected at ${scan.memory.mempalace.socketPath}`
45
+ : `not-detected (${scan.memory.mempalace.socketPath})`;
41
46
  const lines = [
42
47
  `Package manager: ${scan.packageManager}`,
43
48
  "",
@@ -49,9 +54,7 @@ export function renderScanReport(scan: ScanReport): string {
49
54
  }
50
55
 
51
56
  lines.push("");
52
- lines.push(
53
- `Memory: mempalace ${scan.memory.mempalace.available ? "available" : "not-detected"} (${scan.memory.mempalace.socketPath})`,
54
- );
57
+ lines.push(`Memory: mempalace ${memorySummary}`);
55
58
 
56
59
  if (scan.notes.length > 0) {
57
60
  lines.push("");
package/src/utils.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import { join, normalize, resolve, sep, dirname } from "node:path";
6
6
  import { existsSync, readdirSync, readFileSync } from "node:fs";
7
7
  import { fileURLToPath } from "node:url";
8
+ import { homedir } from "node:os";
8
9
 
9
10
 
10
11
  /**
@@ -129,7 +130,65 @@ export function detectProject(startDir: string): string | null {
129
130
  * (via import.meta.resolve). Post-0.6.0 this is the primary path —
130
131
  * the CLI no longer bundles adapter source.
131
132
  */
132
- export function getAdapterDir(adapterName: string): string | null {
133
+ function resolvePackageRootFromEntry(pkgName: string, entryRef: string): string | null {
134
+ const pathRef = entryRef.startsWith("file://") ? fileURLToPath(entryRef) : entryRef;
135
+ let dir = resolve(pathRef, "..");
136
+ const fsRoot = resolve("/");
137
+
138
+ while (dir !== fsRoot) {
139
+ const pkgJson = join(dir, "package.json");
140
+ if (existsSync(pkgJson)) {
141
+ try {
142
+ const contents = JSON.parse(readFileSync(pkgJson, "utf-8")) as { name?: string };
143
+ if (contents.name === pkgName) {
144
+ return dir;
145
+ }
146
+ } catch {
147
+ // Keep walking up.
148
+ }
149
+ }
150
+ const parent = resolve(dir, "..");
151
+ if (parent === dir) break;
152
+ dir = parent;
153
+ }
154
+
155
+ return null;
156
+ }
157
+
158
+ function getGlobalPackageDir(pkgName: string, env: Record<string, string | undefined> = process.env): string | null {
159
+ const searchRoots = [
160
+ env.AOS_BUN_GLOBAL_DIR,
161
+ join(homedir(), ".bun", "install", "global", "node_modules"),
162
+ env.AOS_NPM_GLOBAL_DIR,
163
+ env.npm_config_prefix ? join(env.npm_config_prefix, "lib", "node_modules") : null,
164
+ ].filter((value): value is string => typeof value === "string" && value.length > 0);
165
+
166
+ if (!env.AOS_NPM_GLOBAL_DIR && !env.npm_config_prefix) {
167
+ const npmPrefix = Bun.spawnSync(["npm", "prefix", "-g"], {
168
+ stdin: "ignore",
169
+ stdout: "pipe",
170
+ stderr: "ignore",
171
+ });
172
+ const out = npmPrefix.stdout.toString().trim();
173
+ if (out) {
174
+ searchRoots.push(join(out, "lib", "node_modules"));
175
+ }
176
+ }
177
+
178
+ const seen = new Set<string>();
179
+ for (const root of searchRoots) {
180
+ if (seen.has(root)) continue;
181
+ seen.add(root);
182
+ const candidate = join(root, pkgName);
183
+ if (existsSync(join(candidate, "package.json"))) {
184
+ return candidate;
185
+ }
186
+ }
187
+
188
+ return null;
189
+ }
190
+
191
+ export function getAdapterDir(adapterName: string, env: Record<string, string | undefined> = process.env): string | null {
133
192
  // 1. Monorepo dev layout
134
193
  const monorepoDir = resolve(import.meta.dir, "../..", "adapters", adapterName);
135
194
  if (existsSync(join(monorepoDir, "src", "index.ts"))) {
@@ -137,42 +196,24 @@ export function getAdapterDir(adapterName: string): string | null {
137
196
  }
138
197
 
139
198
  // 2. Installed @aos-harness/<name>-adapter package
199
+ const pkgName = `@aos-harness/${adapterName}-adapter`;
140
200
  try {
141
- const pkgName = `@aos-harness/${adapterName}-adapter`;
142
201
  const resolver = (import.meta as any).resolve;
143
- if (typeof resolver !== "function") return null;
144
- // import.meta.resolve returns a file:// URL to the package's main entry
145
- // (e.g., .../node_modules/@aos-harness/pi-adapter/src/index.ts). Strip
146
- // to the package root.
147
- const mainUrl: string = resolver(pkgName);
148
- if (!mainUrl.startsWith("file://")) return null;
149
- const mainPath = mainUrl.slice("file://".length);
150
- // Walk up until we find a package.json with the matching name
151
- let dir = resolve(mainPath, "..");
152
- const fsRoot = resolve("/");
153
- while (dir !== fsRoot) {
154
- const pkgJson = join(dir, "package.json");
155
- if (existsSync(pkgJson)) {
156
- try {
157
- // Confirm it's the right package; if so, return dir.
158
- const contents = JSON.parse(
159
- require("node:fs").readFileSync(pkgJson, "utf-8"),
160
- ) as { name?: string };
161
- if (contents.name === pkgName) {
162
- if (existsSync(join(dir, "src", "index.ts"))) return dir;
163
- }
164
- } catch {
165
- // Fall through — keep walking up.
166
- }
202
+ if (typeof resolver === "function") {
203
+ const resolvedDir = resolvePackageRootFromEntry(pkgName, resolver(pkgName));
204
+ if (resolvedDir && existsSync(join(resolvedDir, "src", "index.ts"))) {
205
+ return resolvedDir;
167
206
  }
168
- const parent = resolve(dir, "..");
169
- if (parent === dir) break;
170
- dir = parent;
171
207
  }
172
208
  } catch {
173
- // import.meta.resolve throws if the package isn't installed.
174
- return null;
209
+ // Fall through to explicit global package directory detection.
175
210
  }
211
+
212
+ const globalDir = getGlobalPackageDir(pkgName, env);
213
+ if (globalDir && existsSync(join(globalDir, "src", "index.ts"))) {
214
+ return globalDir;
215
+ }
216
+
176
217
  return null;
177
218
  }
178
219