openfused 0.3.6 → 0.3.9
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 +1 -1
- package/dist/cli.js +48 -9
- package/dist/mcp.js +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +5 -4
- package/dist/store.d.ts +1 -0
- package/dist/store.js +10 -1
- package/dist/sync.js +3 -0
- package/package.json +3 -3
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -6,13 +6,13 @@ import { watchInbox, watchContext, watchSync } from "./watch.js";
|
|
|
6
6
|
import { syncAll, syncOne, deliverOne } from "./sync.js";
|
|
7
7
|
import * as registry from "./registry.js";
|
|
8
8
|
import { fingerprint } from "./crypto.js";
|
|
9
|
-
import { resolve } from "node:path";
|
|
9
|
+
import { resolve, join } from "node:path";
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
|
-
const VERSION = "0.3.
|
|
11
|
+
const VERSION = "0.3.9";
|
|
12
12
|
const program = new Command();
|
|
13
13
|
program
|
|
14
14
|
.name("openfuse")
|
|
15
|
-
.description("
|
|
15
|
+
.description("The file protocol for AI agent context. Encrypted, signed, peer-to-peer.")
|
|
16
16
|
.version(VERSION);
|
|
17
17
|
// --- init ---
|
|
18
18
|
program
|
|
@@ -399,19 +399,25 @@ key
|
|
|
399
399
|
console.log(`Key already in keyring (fingerprint: ${fp})`);
|
|
400
400
|
return;
|
|
401
401
|
}
|
|
402
|
+
const autoTrust = config.autoTrust ?? false;
|
|
402
403
|
config.keyring.push({
|
|
403
404
|
name,
|
|
404
405
|
address: opts.address ?? "",
|
|
405
406
|
signingKey,
|
|
406
407
|
encryptionKey: opts.encryptionKey,
|
|
407
408
|
fingerprint: fp,
|
|
408
|
-
trusted:
|
|
409
|
+
trusted: autoTrust,
|
|
409
410
|
added: new Date().toISOString(),
|
|
410
411
|
});
|
|
411
412
|
await store.writeConfig(config);
|
|
412
413
|
console.log(`Imported key for: ${name}`);
|
|
413
414
|
console.log(` Fingerprint: ${fp}`);
|
|
414
|
-
|
|
415
|
+
if (autoTrust) {
|
|
416
|
+
console.log(` Auto-trusted (workspace mode)`);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
console.log(`\nKey is NOT trusted yet. Run: openfuse key trust ${name}`);
|
|
420
|
+
}
|
|
415
421
|
});
|
|
416
422
|
key
|
|
417
423
|
.command("trust <name>")
|
|
@@ -554,13 +560,46 @@ program
|
|
|
554
560
|
console.log(`Imported key for ${manifest.name} from registry [untrusted]`);
|
|
555
561
|
console.log(` Run \`openfuse key trust ${manifest.name}\` to trust`);
|
|
556
562
|
}
|
|
557
|
-
await store.sendInbox(name, message);
|
|
558
|
-
|
|
563
|
+
const filename = await store.sendInbox(name, message);
|
|
564
|
+
// Try direct HTTP delivery if endpoint is http(s)
|
|
565
|
+
if (manifest.endpoint.startsWith("http")) {
|
|
566
|
+
try {
|
|
567
|
+
const body = await readFile(join(store.root, "outbox", filename), "utf-8");
|
|
568
|
+
const r = await fetch(`${manifest.endpoint.replace(/\/$/, "")}/inbox`, {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body,
|
|
572
|
+
});
|
|
573
|
+
if (r.ok) {
|
|
574
|
+
// Archive to .sent/
|
|
575
|
+
const { mkdir, rename } = await import("node:fs/promises");
|
|
576
|
+
const sentDir = join(store.root, "outbox", ".sent");
|
|
577
|
+
await mkdir(sentDir, { recursive: true });
|
|
578
|
+
await rename(join(store.root, "outbox", filename), join(sentDir, filename));
|
|
579
|
+
console.log(`Delivered to ${name}.`);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
console.log(`Queued for ${name}. Endpoint returned ${r.status}. Will deliver on next sync.`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
console.log(`Queued for ${name}. Will deliver on next sync.`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
console.log(`Queued for ${name}. Run \`openfuse sync\` to deliver.`);
|
|
591
|
+
}
|
|
559
592
|
}
|
|
560
593
|
catch {
|
|
561
594
|
// Not in registry — send as a peer message
|
|
562
|
-
await store.sendInbox(name, message);
|
|
563
|
-
|
|
595
|
+
const filename = await store.sendInbox(name, message);
|
|
596
|
+
const delivered = await deliverOne(store, name, filename);
|
|
597
|
+
if (delivered) {
|
|
598
|
+
console.log(`Delivered to ${name}.`);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
console.log(`Queued for ${name}. Run \`openfuse sync\` to deliver.`);
|
|
602
|
+
}
|
|
564
603
|
}
|
|
565
604
|
});
|
|
566
605
|
program.parse();
|
package/dist/mcp.js
CHANGED
|
@@ -23,7 +23,7 @@ const storeDir = process.env.OPENFUSE_DIR || process.argv[3] || ".";
|
|
|
23
23
|
const store = new ContextStore(resolve(storeDir));
|
|
24
24
|
const server = new McpServer({
|
|
25
25
|
name: "openfuse",
|
|
26
|
-
version: "0.3.
|
|
26
|
+
version: "0.3.9",
|
|
27
27
|
});
|
|
28
28
|
// --- Context ---
|
|
29
29
|
server.tool("context_read", "Read the agent's CONTEXT.md (working memory)", async () => {
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ContextStore } from "./store.js";
|
|
2
|
-
export declare const DEFAULT_REGISTRY = "https://registry.
|
|
2
|
+
export declare const DEFAULT_REGISTRY = "https://openfuse-registry.wzmcghee.workers.dev";
|
|
3
3
|
export interface Manifest {
|
|
4
4
|
name: string;
|
|
5
5
|
endpoint: string;
|
package/dist/registry.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// This is TOFU (Trust On First Use) done right: the registry distributes keys,
|
|
8
8
|
// but never asserts trust. Trust is a local decision.
|
|
9
9
|
import { signMessage, fingerprint } from "./crypto.js";
|
|
10
|
-
|
|
10
|
+
// Custom domain pending propagation — use Worker URL as fallback
|
|
11
|
+
export const DEFAULT_REGISTRY = "https://openfuse-registry.wzmcghee.workers.dev";
|
|
11
12
|
export function resolveRegistry(flag) {
|
|
12
13
|
return flag || process.env.OPENFUSE_REGISTRY || DEFAULT_REGISTRY;
|
|
13
14
|
}
|
|
@@ -44,13 +45,13 @@ export async function register(store, endpoint, registry) {
|
|
|
44
45
|
// Discovery: try DNS TXT first (decentralized, no registry needed), fall back to Worker API.
|
|
45
46
|
// DNS format: v=of1 e={endpoint} pk={pubkey} ek={agekey} fp={fingerprint}
|
|
46
47
|
// Self-hosted: _openfuse.{name}.{their-domain} — user manages their own TXT records.
|
|
47
|
-
// Our zone: _openfuse.{name}.openfused.
|
|
48
|
+
// Our zone: _openfuse.{name}.openfused.net — managed by the registry Worker on registration.
|
|
48
49
|
export async function discover(name, registry) {
|
|
49
50
|
// If name contains a dot, it's a domain — try DNS TXT directly
|
|
50
|
-
// Otherwise try DNS at openfused.
|
|
51
|
+
// Otherwise try DNS at openfused.net, then fall back to registry API
|
|
51
52
|
const dnsNames = name.includes(".")
|
|
52
53
|
? [`_openfuse.${name}`]
|
|
53
|
-
: [`_openfuse.${name}.openfused.
|
|
54
|
+
: [`_openfuse.${name}.openfused.net`];
|
|
54
55
|
for (const dnsName of dnsNames) {
|
|
55
56
|
const manifest = await discoverViaDns(dnsName, name);
|
|
56
57
|
if (manifest)
|
package/dist/store.d.ts
CHANGED
package/dist/store.js
CHANGED
|
@@ -71,12 +71,15 @@ export class ContextStore {
|
|
|
71
71
|
await writeFile(destPath, content);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
// Workspaces auto-trust: all imported keys are trusted by default.
|
|
75
|
+
// Safe because workspaces are private — you control who joins.
|
|
74
76
|
const config = {
|
|
75
77
|
id,
|
|
76
78
|
name,
|
|
77
79
|
created: new Date().toISOString(),
|
|
78
80
|
peers: [],
|
|
79
81
|
keyring: [],
|
|
82
|
+
autoTrust: true,
|
|
80
83
|
};
|
|
81
84
|
await this.writeConfig(config);
|
|
82
85
|
}
|
|
@@ -193,7 +196,13 @@ export class ContextStore {
|
|
|
193
196
|
const signed = deserializeSignedMessage(raw);
|
|
194
197
|
if (signed) {
|
|
195
198
|
const sigValid = verifyMessage(signed);
|
|
196
|
-
|
|
199
|
+
// autoTrust (workspace mode): any key in keyring is trusted, but key must still
|
|
200
|
+
// be present — prevents random internet keys from appearing verified in a workspace
|
|
201
|
+
// that's accidentally exposed to the network.
|
|
202
|
+
const inKeyring = config.keyring.some((k) => k.signingKey.trim() === signed.publicKey.trim());
|
|
203
|
+
const trusted = config.autoTrust
|
|
204
|
+
? inKeyring
|
|
205
|
+
: config.keyring.some((k) => k.trusted && k.signingKey.trim() === signed.publicKey.trim());
|
|
197
206
|
const verified = sigValid && trusted;
|
|
198
207
|
let content;
|
|
199
208
|
if (signed.encrypted) {
|
package/dist/sync.js
CHANGED
|
@@ -35,6 +35,9 @@ function parseUrl(url) {
|
|
|
35
35
|
if (/[;|`$]/.test(host)) {
|
|
36
36
|
throw new Error("Invalid SSH URL: host contains shell metacharacters");
|
|
37
37
|
}
|
|
38
|
+
if (/[;|`$&(){}]/.test(path)) {
|
|
39
|
+
throw new Error("Invalid SSH URL: path contains shell metacharacters");
|
|
40
|
+
}
|
|
38
41
|
return { type: "ssh", host, path };
|
|
39
42
|
}
|
|
40
43
|
throw new Error(`Unknown URL scheme: ${url}. Use http:// or ssh://`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openfused",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.9",
|
|
4
|
+
"description": "The file protocol for AI agent context. Encrypted, signed, peer-to-peer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"ai",
|
|
48
48
|
"agent",
|
|
49
49
|
"context",
|
|
50
|
-
"
|
|
50
|
+
"messaging",
|
|
51
51
|
"fuse",
|
|
52
52
|
"decentralized",
|
|
53
53
|
"openclaw",
|