aicq-codex 0.1.0 → 0.1.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 (2) hide show
  1. package/mcp/server.js +46 -12
  2. package/package.json +1 -1
package/mcp/server.js CHANGED
@@ -395,15 +395,46 @@ const TOOLS = [
395
395
 
396
396
  // ── MCP plumbing ──────────────────────────────────────────────────────
397
397
 
398
+ // ── stdout protocol guard (MUST run before any lib import logs) ─────────
399
+ // The MCP stdio transport reserves stdout exclusively for JSON-RPC frames.
400
+ // Library modules still use console.log; route every such line to stderr so
401
+ // strict parsers (rmcp) never see non-protocol bytes on the wire.
402
+ const __stderrWrite = process.stderr.write.bind(process.stderr);
403
+ const __stdoutWrite = process.stdout.write.bind(process.stdout);
404
+ console.log = (...args) => {
405
+ const line = args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ");
406
+ __stderrWrite(`[AICQ MCP][out] ${line}\n`);
407
+ };
408
+
398
409
  async function main() {
399
410
  let ready = false;
400
- try {
401
- await ensureInitialized();
402
- ready = true;
403
- } catch (e) {
404
- // Don't crash codex startup just because the network is down: expose
405
- // tools anyway and let handlers surface actionable errors.
406
- log(`Initialization deferred (${e.message}); tools will retry lazily.`);
411
+
412
+ // Single-flight init lock: concurrent tool calls must not race the
413
+ // bootstrap sequence (module-level _db/_identity/_chat get assigned
414
+ // progressively; a second entrant sees _db set and skips ahead).
415
+ // Single-flight shared by BOTH the background bootstrap and every lazy
416
+ // tool call. Two independent entry points used to race each other: a tool
417
+ // could observe `_db` already set while `_chat` was still undefined and
418
+ // skip ahead ("Cannot read properties of undefined (sendMessage)").
419
+ let initPromise = null;
420
+ function startBootstrap() {
421
+ if (!initPromise) {
422
+ initPromise = (async () => {
423
+ try {
424
+ await ensureInitialized();
425
+ ready = true;
426
+ log(`Plugin runtime initialized (v${PLUGIN_VERSION})`);
427
+ } catch (e) {
428
+ initPromise = null; // allow retry on next tool call
429
+ log(`Initialization deferred (${e.message}); tools will retry lazily.`);
430
+ }
431
+ })();
432
+ }
433
+ return initPromise;
434
+ }
435
+ function ensureReadyOnce() {
436
+ if (ready && _connected) return Promise.resolve();
437
+ return startBootstrap();
407
438
  }
408
439
 
409
440
  const server = new Server(
@@ -411,6 +442,10 @@ async function main() {
411
442
  { capabilities: { tools: {} } }
412
443
  );
413
444
 
445
+ // FIX (startup handshake race): handlers registered BEFORE connect, and
446
+ // connect happens BEFORE heavy E2EE/network bootstrap so the MCP client's
447
+ // `initialize` / `tools/list` are answered immediately. CallTool retries
448
+ // initialization lazily via the `ready` flag.
414
449
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
415
450
  tools: TOOLS.map(({ name, description, inputSchema }) => ({
416
451
  name, description, inputSchema,
@@ -427,10 +462,7 @@ async function main() {
427
462
  };
428
463
  }
429
464
  try {
430
- if (!ready) {
431
- await ensureInitialized();
432
- ready = true;
433
- }
465
+ await ensureReadyOnce();
434
466
  const result = await tool.run(req.params.arguments || {});
435
467
  return {
436
468
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -446,7 +478,9 @@ async function main() {
446
478
 
447
479
  const transport = new StdioServerTransport();
448
480
  await server.connect(transport);
449
- log(`stdio MCP server ready (plugin v${PLUGIN_VERSION})`);
481
+ log(`stdio transport attached; bootstrapping runtime in background`);
482
+ // fire-and-forget; failures reported via log()
483
+ void startBootstrap();
450
484
  }
451
485
 
452
486
  main().catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicq-codex",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "AICQ End-to-end Encrypted Chat plugin for OpenAI Codex — Agent Plugins v1 manifest + stdio MCP server exposing AICQ friend management, chat, file transfer, and group messaging tools",
5
5
  "type": "module",
6
6
  "main": "./mcp/server.js",