@lamplitisles/codex-for-love 0.4.3 → 0.5.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.
@@ -1,7 +1,8 @@
1
1
  import process$1 from "node:process";
2
- import { createHash, randomInt, randomUUID } from "node:crypto";
3
- import { mkdir, readFile, rename, rmdir, unlink, writeFile } from "node:fs/promises";
4
2
  import { dirname, join, resolve } from "node:path";
3
+ import { createHash, randomInt, randomUUID } from "node:crypto";
4
+ import { access, mkdir, readFile, rename, rmdir, unlink, writeFile } from "node:fs/promises";
5
+ import { isIP } from "node:net";
5
6
  import { execFile } from "node:child_process";
6
7
  import { promisify } from "node:util";
7
8
  //#region \0rolldown/runtime.js
@@ -21885,7 +21886,8 @@ function partnerPaths(workspace) {
21885
21886
  database: join(managedRoot, "session.sqlite"),
21886
21887
  relationshipJournal: join(managedRoot, "relationship.jsonl"),
21887
21888
  attachments: join(managedRoot, "attachments"),
21888
- audio: join(managedRoot, "audio")
21889
+ audio: join(managedRoot, "audio"),
21890
+ conversationImages: join(managedRoot, "conversation-images.json")
21889
21891
  };
21890
21892
  }
21891
21893
  //#endregion
@@ -22688,6 +22690,7 @@ const schema = object({
22688
22690
  user: string().min(1)
22689
22691
  }).strict().optional(),
22690
22692
  port: number().int().min(1024).max(65535).default(3082),
22693
+ listen_host: string().refine((value) => isIP(value) === 4, "Listen host must be an IPv4 address").default("127.0.0.1"),
22691
22694
  codex: object({
22692
22695
  command: string().min(1).default("codex-app-server"),
22693
22696
  model: string().min(1).default(DEFAULT_CODEX_MODEL),
@@ -22948,11 +22951,72 @@ async function synthesizeSpeech(options) {
22948
22951
  }
22949
22952
  }
22950
22953
  //#endregion
22954
+ //#region apps/partner/runtime/conversation-images.ts
22955
+ const CURSOR_BYTES = 40;
22956
+ const CURSOR_TOKEN = /^[A-Za-z0-9_-]{54}$/u;
22957
+ const IMAGE_ID = /^[a-f0-9]{64}$/u;
22958
+ function cursorFor(image) {
22959
+ if (!Number.isSafeInteger(image.created) || image.created < 0 || !IMAGE_ID.test(image.id)) throw new Error("Invalid conversation image cursor data");
22960
+ const value = Buffer.alloc(CURSOR_BYTES);
22961
+ value.writeBigUInt64BE(BigInt(image.created));
22962
+ Buffer.from(image.id, "hex").copy(value, 8);
22963
+ return value.toString("base64url");
22964
+ }
22965
+ function readCursor(cursor) {
22966
+ if (!cursor) return void 0;
22967
+ if (!CURSOR_TOKEN.test(cursor)) return void 0;
22968
+ try {
22969
+ const value = Buffer.from(cursor, "base64url");
22970
+ if (value.length !== CURSOR_BYTES) return void 0;
22971
+ const created = Number(value.readBigUInt64BE());
22972
+ if (!Number.isSafeInteger(created)) return void 0;
22973
+ return [created, value.subarray(8).toString("hex")];
22974
+ } catch {
22975
+ return;
22976
+ }
22977
+ }
22978
+ function pageConversationImages(images, limit = 5, cursor) {
22979
+ const after = readCursor(cursor);
22980
+ if (cursor && !after) throw new Error("Invalid conversation image cursor");
22981
+ const ordered = images.filter((image) => !after || image.created < after[0] || image.created === after[0] && image.id < after[1]).sort((a, b) => b.created - a.created || b.id.localeCompare(a.id));
22982
+ const page = ordered.slice(0, limit);
22983
+ return {
22984
+ images: page,
22985
+ ...ordered.length > page.length ? { nextCursor: cursorFor(page.at(-1)) } : {}
22986
+ };
22987
+ }
22988
+ async function readConversationImages(path) {
22989
+ try {
22990
+ const value = JSON.parse(await readFile(path, "utf8"));
22991
+ return Array.isArray(value) ? value : [];
22992
+ } catch (error) {
22993
+ if (error.code === "ENOENT") return [];
22994
+ throw error;
22995
+ }
22996
+ }
22997
+ async function withAvailability(images) {
22998
+ return Promise.all(images.map(async (image) => {
22999
+ try {
23000
+ await access(image.path);
23001
+ return {
23002
+ ...image,
23003
+ available: true
23004
+ };
23005
+ } catch {
23006
+ return {
23007
+ ...image,
23008
+ available: false
23009
+ };
23010
+ }
23011
+ }));
23012
+ }
23013
+ //#endregion
22951
23014
  //#region apps/partner/runtime/companion-mcp.ts
22952
23015
  const workspace = process.argv[2];
22953
23016
  if (!workspace) throw new Error("Companion MCP requires a workspace path");
22954
23017
  const configPath = process.argv[3];
22955
23018
  const journal = partnerPaths(workspace).relationshipJournal;
23019
+ const conversationImages = partnerPaths(workspace).conversationImages;
22956
23020
  const server = new McpServer({
22957
23021
  name: "companion",
22958
23022
  version: "0.1.0"
@@ -22995,6 +23059,30 @@ server.registerTool("read_relationship_history", {
22995
23059
  type: "text",
22996
23060
  text: JSON.stringify((await readRelationshipJournal(journal)).reverse().slice(0, canonicalizeHistoryRead(input)))
22997
23061
  }] }));
23062
+ server.registerTool("list_photos", {
23063
+ description: "List our shared photo library: human-sent, Agent-generated, and restored historical conversation images, newest first in bounded pages. Returned local paths may be inspected deliberately with native tools.",
23064
+ inputSchema: {
23065
+ limit: number().int().min(1).max(50).optional(),
23066
+ cursor: string().regex(/^[A-Za-z0-9_-]{54}$/u).optional()
23067
+ }
23068
+ }, async ({ limit, cursor }) => {
23069
+ const page = pageConversationImages(await withAvailability(await readConversationImages(conversationImages)), limit ?? 5, cursor);
23070
+ return { content: [{
23071
+ type: "text",
23072
+ text: JSON.stringify({
23073
+ images: page.images.map(({ id, filename, path, created, origin, available }) => ({
23074
+ id,
23075
+ filename,
23076
+ path,
23077
+ directory: dirname(path),
23078
+ created,
23079
+ origin,
23080
+ available
23081
+ })),
23082
+ ...page.nextCursor ? { nextCursor: page.nextCursor } : {}
23083
+ })
23084
+ }] };
23085
+ });
22998
23086
  server.registerTool("roll_dice", {
22999
23087
  description: "Roll dice with an optional modifier and label.",
23000
23088
  inputSchema: {