mycomesh-consumer 0.1.32 → 0.1.34

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/README.md CHANGED
@@ -6,9 +6,9 @@ Gateway dependency.
6
6
 
7
7
  ## Install
8
8
 
9
- Requirements: Node.js 20 or newer. The package includes the official Codex
10
- dependency and the small cryptographic dependencies needed for V8 EIP-712
11
- payment-key authorizations.
9
+ Requirements: Node.js 20 or newer. The package contains only the small
10
+ cryptographic dependencies needed for V8 EIP-712 payment-key authorizations;
11
+ Codex is an independent client.
12
12
 
13
13
  ```sh
14
14
  npm install --global mycomesh-consumer
@@ -21,9 +21,9 @@ For a checkout:
21
21
  npm install --global ./packages/mycomesh-cli
22
22
  ```
23
23
 
24
- The command starts `127.0.0.1:8110`, prints and optionally opens the local
25
- credentials page, waits for a healthy Settlement V8 Relay, and launches Codex
26
- with a one-run `mycomesh` provider configuration. It does not edit Codex files.
24
+ The command starts the Consumer API on `127.0.0.1:8110`, prints and optionally
25
+ opens the local credentials page, and stays available for any
26
+ OpenAI-compatible client. It does not start or configure Codex.
27
27
 
28
28
  The page exposes only the local API URL, payment key/export, prepaid balance,
29
29
  key operations, and local consumption history. It has no conversation or
@@ -31,12 +31,21 @@ request-session UI. The payment key is generated once and stored at
31
31
  `~/.mycomesh/consumer/payment-key` with mode `0600`; set
32
32
  `MYCOMESH_CONSUMER_DATA_DIR` to choose another directory.
33
33
 
34
- ## Service-only mode
34
+ ## Connect a client
35
35
 
36
- Keep the native Consumer running without launching Codex:
36
+ The default command is already service-only. Load the export block into a
37
+ shell and then use Codex or another compatible client:
37
38
 
38
39
  ```sh
39
- mycomesh-consumer --no-codex
40
+ mycomesh-consumer --no-browser
41
+ eval "$(curl -sS http://127.0.0.1:8110/credentials)"
42
+ codex
43
+ ```
44
+
45
+ The optional convenience wrapper is explicit:
46
+
47
+ ```sh
48
+ mycomesh-consumer --codex
40
49
  ```
41
50
 
42
51
  Use `--no-browser` on a headless machine. Stop the process without deleting
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mycomesh-consumer",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Native Node.js MycoMesh Consumer and OpenAI-compatible request CLI.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,7 +24,6 @@
24
24
  "dependencies": {
25
25
  "@noble/curves": "1.9.1",
26
26
  "@noble/hashes": "1.8.0",
27
- "@openai/codex": "0.146.0",
28
27
  "undici": "6.28.0"
29
28
  },
30
29
  "scripts": {
package/src/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { once } from "node:events";
2
2
  import { readFile } from "node:fs/promises";
3
3
 
4
- const CLI_VERSION = "0.1.32";
4
+ const CLI_VERSION = "0.1.33";
5
5
  // The npm client is local-first. A public Gateway is an explicit override;
6
6
  // the default remains the loopback Consumer edge so a blocked domain cannot
7
7
  // strand the Codex client.
@@ -949,7 +949,7 @@ function writeJson(response, status, value, headers = {}) {
949
949
  response.end(body);
950
950
  }
951
951
 
952
- async function handleInference(state, request, response, path) {
952
+ async function handleInference(state, request, response, path, alphaSearchQuery) {
953
953
  const authorization = String(request.headers.authorization || "");
954
954
  if (!sameSecret(authorization, `Bearer ${state.paymentKey}`)) {
955
955
  writeJson(response, 401, openaiError("invalid MycoMesh payment key", "invalid_api_key"));
@@ -960,6 +960,15 @@ async function handleInference(state, request, response, path) {
960
960
  writeJson(response, 400, openaiError(error.message, "invalid_request_error"));
961
961
  return;
962
962
  }
963
+ const alphaSearch = alphaSearchQuery !== undefined;
964
+ if (alphaSearch) {
965
+ body = {
966
+ model: body.model,
967
+ input: [{ type: "mycomesh_alpha_search_request", request: body, query: alphaSearchQuery }],
968
+ max_output_tokens: body.max_output_tokens ?? 2000,
969
+ };
970
+ path = "/v1/responses";
971
+ }
963
972
  if (path.endsWith("/responses/compact") && !hasCompactionTrigger(body.input)) {
964
973
  const items = Array.isArray(body.input) ? [...body.input] : [];
965
974
  if (body.input !== undefined && body.input !== "" && !Array.isArray(body.input)) {
@@ -975,6 +984,15 @@ async function handleInference(state, request, response, path) {
975
984
  return;
976
985
  }
977
986
  for (const [name, value] of Object.entries(result.headers || {})) response.setHeader(name, value);
987
+ if (alphaSearch) {
988
+ const payload = result.payload?.mycomesh_alpha_search_response;
989
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
990
+ writeJson(response, 502, openaiError("Provider returned an invalid alpha/search response", "mycomesh_provider_error"));
991
+ return;
992
+ }
993
+ writeJson(response, 200, payload, result.headers);
994
+ return;
995
+ }
978
996
  if (body.stream === true) {
979
997
  response.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache", connection: "keep-alive", "x-mycomesh-streaming-mode": "buffered" });
980
998
  const events = path.endsWith("/chat/completions")
@@ -1039,6 +1057,9 @@ export function createConsumerServer(state, { host = "127.0.0.1", port = 8110 }
1039
1057
  : "/v1/responses";
1040
1058
  await handleInference(state, request, response, relayPath); return;
1041
1059
  }
1060
+ if (request.method === "POST" && ["/alpha/search", "/v1/alpha/search", "/backend-api/codex/alpha/search"].includes(path)) {
1061
+ await handleInference(state, request, response, "/v1/responses", Object.fromEntries(url.searchParams)); return;
1062
+ }
1042
1063
  writeJson(response, 404, openaiError("route not found", "invalid_request_error"));
1043
1064
  } catch (error) {
1044
1065
  if (!response.headersSent) writeJson(response, 500, openaiError(error.message)); else response.destroy();
package/src/consumer.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { spawn as defaultSpawn } from "node:child_process";
2
- import { createRequire } from "node:module";
3
2
  import { existsSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
4
3
  import { join } from "node:path";
5
4
  import * as readline from "node:readline/promises";
@@ -12,7 +11,7 @@ import {
12
11
  createConsumerServer,
13
12
  } from "./consumer-runtime.mjs";
14
13
 
15
- export const CONSUMER_RELEASE_VERSION = "0.1.32";
14
+ export const CONSUMER_RELEASE_VERSION = "0.1.34";
16
15
  export const API_COMMANDS = new Set(["health", "models", "responses", "chat"]);
17
16
  const API_VALUE_OPTIONS = new Set([
18
17
  "--base-url",
@@ -37,10 +36,11 @@ exposes an OpenAI-compatible loopback API.
37
36
 
38
37
  Options:
39
38
  --no-browser Print the local credentials URL without opening it
40
- --no-codex Keep only the Consumer API running
39
+ --codex Start an optional Codex client after Relay readiness
40
+ --no-codex Keep only the Consumer API running (default)
41
41
  --stop Stop a previously started native Consumer
42
42
  --reset-local Confirm and delete the local Consumer data directory
43
- --codex-command PATH Codex executable (default: bundled Codex or codex)
43
+ --codex-command PATH Codex executable for --codex (default: codex on PATH)
44
44
  --ready-timeout SEC Relay readiness timeout (default: 1800)
45
45
  --data-dir DIR Payment key and history directory
46
46
  --relay URLS Comma-separated Relay URLs for automatic failover
@@ -123,7 +123,10 @@ export async function main(argv, dependencies = {}) {
123
123
  return code;
124
124
  } catch (error) {
125
125
  const exitCode = error instanceof ConsumerCliError ? error.exitCode : 1;
126
- stderr.write(`mycomesh consumer: ${error instanceof Error ? error.message : String(error)}\n`);
126
+ const message = error?.code === "EADDRINUSE"
127
+ ? `port ${error.port || "requested"} is already in use; stop the old Consumer or choose --port with a matching --base-url`
128
+ : error instanceof Error ? error.message : String(error);
129
+ stderr.write(`mycomesh consumer: ${message}\n`);
127
130
  return exitCode;
128
131
  }
129
132
  }
@@ -131,10 +134,11 @@ export async function main(argv, dependencies = {}) {
131
134
  export function parseArguments(argv, env = process.env) {
132
135
  const parsed = {
133
136
  baseUrl: env.MYCOMESH_CONSUMER_PUBLIC_BASE_URL || DEFAULT_BASE_URL,
137
+ baseUrlExplicit: Boolean(env.MYCOMESH_CONSUMER_PUBLIC_BASE_URL),
134
138
  dataDir: env.MYCOMESH_CONSUMER_DATA_DIR || join(env.HOME || process.cwd(), ".mycomesh", "consumer"),
135
139
  relayUrls: env.MYCOMESH_V8_RELAY_URLS || env.MYCOMESH_CONSUMER_RELAY_URL || DEFAULT_RELAY_URL,
136
140
  proxy: env.MYCOMESH_CONSUMER_PROXY || "",
137
- codexCommand: env.MYCOMESH_CODEX_COMMAND || bundledCodexCommand(),
141
+ codexCommand: env.MYCOMESH_CODEX_COMMAND || "codex",
138
142
  readyTimeout: parsePositive(env.MYCOMESH_CONSUMER_READY_TIMEOUT_SECONDS || "1800", "ready timeout", 86400),
139
143
  maxFeeUnits: parsePositive(env.MYCOMESH_V8_MAX_FEE_UNITS || String(DEFAULT_MAX_FEE_UNITS), "max fee"),
140
144
  host: env.MYCOMESH_CONSUMER_HOST || "127.0.0.1",
@@ -142,7 +146,7 @@ export function parseArguments(argv, env = process.env) {
142
146
  port: parsePositive(env.MYCOMESH_CONSUMER_PORT || "8110", "port", 65535),
143
147
  scheme: "http",
144
148
  noBrowser: false,
145
- noCodex: false,
149
+ noCodex: env.MYCOMESH_CONSUMER_START_CODEX !== "1",
146
150
  stop: false,
147
151
  resetLocal: false,
148
152
  dryRun: false,
@@ -156,6 +160,7 @@ export function parseArguments(argv, env = process.env) {
156
160
  if (token === "-h" || token === "--help") { parsed.help = true; continue; }
157
161
  if (token === "-v" || token === "--version") { parsed.version = true; continue; }
158
162
  if (token === "--no-browser") { parsed.noBrowser = true; continue; }
163
+ if (token === "--codex") { parsed.noCodex = false; continue; }
159
164
  if (token === "--no-codex") { parsed.noCodex = true; continue; }
160
165
  if (token === "--stop") { parsed.stop = true; continue; }
161
166
  if (token === "--reset-local") { parsed.resetLocal = true; continue; }
@@ -167,7 +172,7 @@ export function parseArguments(argv, env = process.env) {
167
172
  if (!options.has(name)) throw new ConsumerCliError(`unknown option: ${token}`, 2);
168
173
  if (value === undefined) { index += 1; value = argv[index]; }
169
174
  if (!value) throw new ConsumerCliError(`${name} requires a value`, 2);
170
- if (name === "--base-url") parsed.baseUrl = value;
175
+ if (name === "--base-url") { parsed.baseUrl = value; parsed.baseUrlExplicit = true; }
171
176
  if (name === "--data-dir") parsed.dataDir = value;
172
177
  if (name === "--relay") parsed.relayUrls = value;
173
178
  if (name === "--proxy") parsed.proxy = value;
@@ -177,6 +182,7 @@ export function parseArguments(argv, env = process.env) {
177
182
  if (name === "--port") parsed.port = parsePositive(value, name, 65535);
178
183
  if (name === "--max-fee") parsed.maxFeeUnits = parsePositive(value, name);
179
184
  }
185
+ if (!parsed.baseUrlExplicit) parsed.baseUrl = `http://${parsed.hostForUrl}:${parsed.port}/v1`;
180
186
  try { new URL(parsed.baseUrl); } catch { throw new ConsumerCliError("--base-url must be an absolute URL", 2); }
181
187
  return parsed;
182
188
  }
@@ -188,10 +194,6 @@ function parsePositive(value, label, maximum = Number.MAX_SAFE_INTEGER) {
188
194
  return parsed;
189
195
  }
190
196
 
191
- function bundledCodexCommand() {
192
- try { return createRequire(import.meta.url).resolve("@openai/codex/bin/codex.js"); } catch { return "codex"; }
193
- }
194
-
195
197
  export function isApiInvocation(argv) {
196
198
  for (let index = 0; index < argv.length; index += 1) {
197
199
  const token = argv[index];