openfused 0.4.3 → 0.4.5

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
@@ -68,12 +68,10 @@ history/ — archived [DONE] context
68
68
  openfuse context
69
69
  openfuse context --append "## Update\nFinished the research phase."
70
70
 
71
- # Mark work as done, then compact to history/ (TS CLI only)
72
- # (edit CONTEXT.md, add [DONE] to the header, then:)
71
+ # Mark work as done, then compact to history/# (edit CONTEXT.md, add [DONE] to the header, then:)
73
72
  openfuse compact
74
73
 
75
- # Add validity windows to time-sensitive context (TS CLI only)
76
- # <!-- validity: 6h --> for task state, 1d for sprint, 3d for architecture
74
+ # Add validity windows to time-sensitive context# <!-- validity: 6h --> for task state, 1d for sprint, 3d for architecture
77
75
  openfuse validate # scan for stale entries
78
76
  openfuse compact --prune-stale # archive expired validity windows
79
77
 
package/dist/cli.js CHANGED
@@ -185,15 +185,17 @@ inbox
185
185
  .option("-d, --dir <path>", "Context store directory", ".")
186
186
  .action(async (peerId, message, opts) => {
187
187
  const store = new ContextStore(resolve(opts.dir));
188
- const filename = await store.sendInbox(peerId, message);
189
- // Try immediate delivery if peer is reachable, deliver now
190
- const delivered = await deliverOne(store, peerId, filename);
191
- if (delivered) {
192
- console.log(`Delivered to ${peerId}.`);
193
- }
194
- else {
195
- console.log(`Queued for ${peerId}. Will deliver on next sync.`);
188
+ await store.sendInbox(peerId, message);
189
+ // Find the outbox file we just created
190
+ const outboxFile = findNewestOutboxFile(store.root, peerId);
191
+ if (outboxFile) {
192
+ const delivered = await deliverOne(store, peerId, outboxFile);
193
+ if (delivered) {
194
+ console.log(`Delivered to ${peerId}.`);
195
+ return;
196
+ }
196
197
  }
198
+ console.log(`Queued for ${peerId}. Will deliver on next sync.`);
197
199
  });
198
200
  // --- watch ---
199
201
  program
package/dist/sync.js CHANGED
@@ -247,7 +247,9 @@ async function syncHttp(store, peer, baseUrl, peerDir) {
247
247
  // Sanitize outboxFile — it comes from the remote peer's response and could
248
248
  // contain path traversal characters (e.g., "../../inbox/important.json").
249
249
  const rawOutboxFile = msg._outboxFile || "";
250
- const outboxFile = rawOutboxFile.replace(/[^a-zA-Z0-9_\-. ]/g, "");
250
+ // Allow / for subdir paths (e.g., "name-FP/filename.json") but strip
251
+ // path traversal (..) and other dangerous chars.
252
+ const outboxFile = rawOutboxFile.replace(/\.\./g, "").replace(/[^a-zA-Z0-9_\-./ ]/g, "");
251
253
  const dest = join(inboxDir, fname);
252
254
  if (!existsSync(dest)) {
253
255
  // Strip the _outboxFile metadata before saving
@@ -340,9 +342,14 @@ async function syncSsh(store, peer, host, remotePath, peerDir) {
340
342
  const pulled = [];
341
343
  const pushed = [];
342
344
  const errors = [];
345
+ const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
343
346
  for (const file of ["CONTEXT.md", "PROFILE.md"]) {
344
347
  try {
345
348
  await execFile("rsync", ["-az", `${host}:${remotePath}/${file}`, join(peerDir, file)]);
349
+ // Wrap in unverified tags — SSH-synced content is untrusted external input
350
+ const raw = sanitizePeerContent(await readFile(join(peerDir, file), "utf-8"));
351
+ const wrapped = `<external_content_unverified from="${esc(peer.name)}" file="${esc(file)}">\n${raw}\n</external_content_unverified>`;
352
+ await writeFile(join(peerDir, file), wrapped);
346
353
  pulled.push(file);
347
354
  }
348
355
  catch (e) {
@@ -354,6 +361,17 @@ async function syncSsh(store, peer, host, remotePath, peerDir) {
354
361
  await mkdir(localDir, { recursive: true });
355
362
  try {
356
363
  await execFile("rsync", ["-az", "--delete", `${host}:${remotePath}/${dir}/`, `${localDir}/`]);
364
+ // Wrap each file in unverified tags
365
+ const { readdirSync } = await import("node:fs");
366
+ for (const fname of readdirSync(localDir)) {
367
+ const fpath = join(localDir, fname);
368
+ try {
369
+ const raw = sanitizePeerContent(await readFile(fpath, "utf-8"));
370
+ const wrapped = `<external_content_unverified from="${esc(peer.name)}" file="${esc(`${dir}/${fname}`)}">\n${raw}\n</external_content_unverified>`;
371
+ await writeFile(fpath, wrapped);
372
+ }
373
+ catch { }
374
+ }
357
375
  pulled.push(`${dir}/`);
358
376
  }
359
377
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfused",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "mcpName": "io.github.openfused/openfuse-mcp",
5
5
  "description": "The file protocol for AI agent context. Encrypted, signed, peer-to-peer.",
6
6
  "license": "MIT",
Binary file