@awebai/claude-channel 0.1.3 → 0.2.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.
- package/.claude-plugin/plugin.json +11 -3
- package/dist/index.js +25164 -228
- package/package.json +7 -8
- package/dist/api/chat.d.ts +0 -21
- package/dist/api/chat.js +0 -38
- package/dist/api/client.d.ts +0 -15
- package/dist/api/client.js +0 -57
- package/dist/api/events.d.ts +0 -23
- package/dist/api/events.js +0 -110
- package/dist/api/mail.d.ts +0 -26
- package/dist/api/mail.js +0 -40
- package/dist/config.d.ts +0 -15
- package/dist/config.js +0 -66
- package/dist/identity/did.d.ts +0 -6
- package/dist/identity/did.js +0 -30
- package/dist/identity/keys.d.ts +0 -2
- package/dist/identity/keys.js +0 -18
- package/dist/identity/pinstore.d.ts +0 -22
- package/dist/identity/pinstore.js +0 -68
- package/dist/identity/signing.d.ts +0 -36
- package/dist/identity/signing.js +0 -161
- package/dist/index.d.ts +0 -7
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awebai/claude-channel",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
6
|
"bin": "./dist/index.js",
|
|
8
7
|
"files": [
|
|
9
8
|
"dist",
|
|
@@ -14,20 +13,20 @@
|
|
|
14
13
|
"package.json"
|
|
15
14
|
],
|
|
16
15
|
"scripts": {
|
|
17
|
-
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
18
|
-
"
|
|
16
|
+
"build": "rm -rf dist && tsc -p tsconfig.json --noEmit && npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js",
|
|
17
|
+
"sync-plugin-version": "node -e \"const p=JSON.parse(require('fs').readFileSync('.claude-plugin/plugin.json','utf8')); p.version=require('./package.json').version; require('fs').writeFileSync('.claude-plugin/plugin.json',JSON.stringify(p,null,2)+'\\n')\"",
|
|
18
|
+
"prepublishOnly": "npm run build && npm run sync-plugin-version",
|
|
19
19
|
"test": "vitest run --exclude test/integration.test.ts",
|
|
20
20
|
"test:integration": "vitest run test/integration.test.ts",
|
|
21
21
|
"start": "tsx src/index.ts"
|
|
22
22
|
},
|
|
23
|
-
"dependencies": {
|
|
23
|
+
"dependencies": {},
|
|
24
|
+
"devDependencies": {
|
|
24
25
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
25
26
|
"@noble/ed25519": "^2.2.3",
|
|
26
27
|
"@noble/hashes": "^2.0.1",
|
|
27
28
|
"bs58": "^6.0.0",
|
|
28
|
-
"js-yaml": "^4.1.0"
|
|
29
|
-
},
|
|
30
|
-
"devDependencies": {
|
|
29
|
+
"js-yaml": "^4.1.0",
|
|
31
30
|
"@types/js-yaml": "^4.0.9",
|
|
32
31
|
"@types/node": "^24.5.2",
|
|
33
32
|
"tsx": "^4.20.6",
|
package/dist/api/chat.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { APIClient } from "./client.js";
|
|
2
|
-
import type { VerificationStatus } from "../identity/signing.js";
|
|
3
|
-
export interface ChatMessage {
|
|
4
|
-
message_id: string;
|
|
5
|
-
from_agent: string;
|
|
6
|
-
from_address?: string;
|
|
7
|
-
to_address?: string;
|
|
8
|
-
body: string;
|
|
9
|
-
timestamp: string;
|
|
10
|
-
sender_leaving: boolean;
|
|
11
|
-
from_did?: string;
|
|
12
|
-
to_did?: string;
|
|
13
|
-
from_stable_id?: string;
|
|
14
|
-
to_stable_id?: string;
|
|
15
|
-
signature?: string;
|
|
16
|
-
signing_key_id?: string;
|
|
17
|
-
signed_payload?: string;
|
|
18
|
-
verification_status?: VerificationStatus;
|
|
19
|
-
}
|
|
20
|
-
export declare function fetchHistory(client: APIClient, sessionId: string, unreadOnly?: boolean, limit?: number): Promise<ChatMessage[]>;
|
|
21
|
-
export declare function markRead(client: APIClient, sessionId: string, upToMessageId: string): Promise<void>;
|
package/dist/api/chat.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { verifyMessage, verifySignedPayload } from "../identity/signing.js";
|
|
2
|
-
export async function fetchHistory(client, sessionId, unreadOnly = false, limit = 50) {
|
|
3
|
-
const params = new URLSearchParams();
|
|
4
|
-
if (unreadOnly)
|
|
5
|
-
params.set("unread_only", "true");
|
|
6
|
-
if (limit > 0)
|
|
7
|
-
params.set("limit", String(limit));
|
|
8
|
-
const resp = await client.get(`/v1/chat/sessions/${encodeURIComponent(sessionId)}/messages?${params}`);
|
|
9
|
-
for (const msg of resp.messages) {
|
|
10
|
-
msg.verification_status = await verifyChatMessage(msg);
|
|
11
|
-
}
|
|
12
|
-
return resp.messages;
|
|
13
|
-
}
|
|
14
|
-
export async function markRead(client, sessionId, upToMessageId) {
|
|
15
|
-
await client.post(`/v1/chat/sessions/${encodeURIComponent(sessionId)}/read`, { up_to_message_id: upToMessageId });
|
|
16
|
-
}
|
|
17
|
-
async function verifyChatMessage(msg) {
|
|
18
|
-
if (msg.signed_payload && msg.signature && msg.from_did) {
|
|
19
|
-
return verifySignedPayload(msg.signed_payload, msg.signature, msg.from_did, msg.signing_key_id || "");
|
|
20
|
-
}
|
|
21
|
-
const from = msg.from_address || msg.from_agent;
|
|
22
|
-
const env = {
|
|
23
|
-
from,
|
|
24
|
-
from_did: msg.from_did || "",
|
|
25
|
-
to: msg.to_address || "",
|
|
26
|
-
to_did: msg.to_did || "",
|
|
27
|
-
type: "chat",
|
|
28
|
-
subject: "",
|
|
29
|
-
body: msg.body,
|
|
30
|
-
timestamp: msg.timestamp,
|
|
31
|
-
from_stable_id: msg.from_stable_id,
|
|
32
|
-
to_stable_id: msg.to_stable_id,
|
|
33
|
-
message_id: msg.message_id,
|
|
34
|
-
signature: msg.signature,
|
|
35
|
-
signing_key_id: msg.signing_key_id,
|
|
36
|
-
};
|
|
37
|
-
return verifyMessage(env);
|
|
38
|
-
}
|
package/dist/api/client.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export declare class APIClient {
|
|
2
|
-
private baseURL;
|
|
3
|
-
private apiKey;
|
|
4
|
-
constructor(baseURL: string, apiKey: string);
|
|
5
|
-
get<T>(path: string): Promise<T>;
|
|
6
|
-
post<T>(path: string, body?: unknown): Promise<T>;
|
|
7
|
-
private request;
|
|
8
|
-
/** Open an SSE stream. Returns the raw Response for streaming. */
|
|
9
|
-
openSSE(path: string): Promise<Response>;
|
|
10
|
-
}
|
|
11
|
-
export declare class APIError extends Error {
|
|
12
|
-
statusCode: number;
|
|
13
|
-
body: string;
|
|
14
|
-
constructor(statusCode: number, body: string);
|
|
15
|
-
}
|
package/dist/api/client.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export class APIClient {
|
|
2
|
-
baseURL;
|
|
3
|
-
apiKey;
|
|
4
|
-
constructor(baseURL, apiKey) {
|
|
5
|
-
this.baseURL = baseURL;
|
|
6
|
-
this.apiKey = apiKey;
|
|
7
|
-
}
|
|
8
|
-
async get(path) {
|
|
9
|
-
return this.request("GET", path);
|
|
10
|
-
}
|
|
11
|
-
async post(path, body) {
|
|
12
|
-
return this.request("POST", path, body);
|
|
13
|
-
}
|
|
14
|
-
async request(method, path, body) {
|
|
15
|
-
const url = this.baseURL + path;
|
|
16
|
-
const headers = {
|
|
17
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
18
|
-
Accept: "application/json",
|
|
19
|
-
};
|
|
20
|
-
const init = { method, headers };
|
|
21
|
-
if (body !== undefined) {
|
|
22
|
-
headers["Content-Type"] = "application/json";
|
|
23
|
-
init.body = JSON.stringify(body);
|
|
24
|
-
}
|
|
25
|
-
const resp = await fetch(url, init);
|
|
26
|
-
if (!resp.ok) {
|
|
27
|
-
const text = await resp.text().catch(() => "");
|
|
28
|
-
throw new APIError(resp.status, text);
|
|
29
|
-
}
|
|
30
|
-
return resp.json();
|
|
31
|
-
}
|
|
32
|
-
/** Open an SSE stream. Returns the raw Response for streaming. */
|
|
33
|
-
async openSSE(path) {
|
|
34
|
-
const url = this.baseURL + path;
|
|
35
|
-
const resp = await fetch(url, {
|
|
36
|
-
headers: {
|
|
37
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
38
|
-
Accept: "text/event-stream",
|
|
39
|
-
"Cache-Control": "no-cache",
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
if (!resp.ok) {
|
|
43
|
-
const text = await resp.text().catch(() => "");
|
|
44
|
-
throw new APIError(resp.status, text);
|
|
45
|
-
}
|
|
46
|
-
return resp;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
export class APIError extends Error {
|
|
50
|
-
statusCode;
|
|
51
|
-
body;
|
|
52
|
-
constructor(statusCode, body) {
|
|
53
|
-
super(body ? `aweb: http ${statusCode}: ${body}` : `aweb: http ${statusCode}`);
|
|
54
|
-
this.statusCode = statusCode;
|
|
55
|
-
this.body = body;
|
|
56
|
-
}
|
|
57
|
-
}
|
package/dist/api/events.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { APIClient } from "./client.js";
|
|
2
|
-
export type AgentEventType = "connected" | "mail_message" | "chat_message" | "control_pause" | "control_resume" | "control_interrupt" | "work_available" | "claim_update" | "claim_removed" | "error";
|
|
3
|
-
export interface AgentEvent {
|
|
4
|
-
type: AgentEventType;
|
|
5
|
-
agent_id?: string;
|
|
6
|
-
project_id?: string;
|
|
7
|
-
message_id?: string;
|
|
8
|
-
from_alias?: string;
|
|
9
|
-
session_id?: string;
|
|
10
|
-
subject?: string;
|
|
11
|
-
signal_id?: string;
|
|
12
|
-
task_id?: string;
|
|
13
|
-
title?: string;
|
|
14
|
-
status?: string;
|
|
15
|
-
text?: string;
|
|
16
|
-
sender_waiting?: boolean;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Consume the agent event stream (GET /v1/events/stream).
|
|
20
|
-
* Yields parsed AgentEvent objects. Reconnects on stream end.
|
|
21
|
-
*/
|
|
22
|
-
export declare function streamAgentEvents(client: APIClient, signal: AbortSignal): AsyncGenerator<AgentEvent>;
|
|
23
|
-
export declare function parseAgentEvent(eventName: string, data: string): AgentEvent | null;
|
package/dist/api/events.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consume the agent event stream (GET /v1/events/stream).
|
|
3
|
-
* Yields parsed AgentEvent objects. Reconnects on stream end.
|
|
4
|
-
*/
|
|
5
|
-
export async function* streamAgentEvents(client, signal) {
|
|
6
|
-
while (!signal.aborted) {
|
|
7
|
-
const deadline = new Date(Date.now() + 5 * 60 * 1000).toISOString();
|
|
8
|
-
let resp;
|
|
9
|
-
try {
|
|
10
|
-
resp = await client.openSSE(`/v1/events/stream?deadline=${encodeURIComponent(deadline)}`);
|
|
11
|
-
}
|
|
12
|
-
catch (err) {
|
|
13
|
-
if (signal.aborted)
|
|
14
|
-
return;
|
|
15
|
-
// Back off on connection failure
|
|
16
|
-
await sleep(5000, signal);
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
yield* parseSSEResponse(resp, signal);
|
|
21
|
-
}
|
|
22
|
-
catch (err) {
|
|
23
|
-
if (signal.aborted)
|
|
24
|
-
return;
|
|
25
|
-
// Stream ended or errored — reconnect after brief pause
|
|
26
|
-
await sleep(1000, signal);
|
|
27
|
-
}
|
|
28
|
-
finally {
|
|
29
|
-
resp.body?.cancel().catch(() => { });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async function* parseSSEResponse(resp, signal) {
|
|
34
|
-
const reader = resp.body?.getReader();
|
|
35
|
-
if (!reader)
|
|
36
|
-
return;
|
|
37
|
-
const decoder = new TextDecoder();
|
|
38
|
-
let buffer = "";
|
|
39
|
-
let currentEvent = "";
|
|
40
|
-
let dataLines = [];
|
|
41
|
-
try {
|
|
42
|
-
while (!signal.aborted) {
|
|
43
|
-
const { done, value } = await reader.read();
|
|
44
|
-
if (done)
|
|
45
|
-
break;
|
|
46
|
-
buffer += decoder.decode(value, { stream: true });
|
|
47
|
-
const lines = buffer.split("\n");
|
|
48
|
-
buffer = lines.pop() || "";
|
|
49
|
-
for (const rawLine of lines) {
|
|
50
|
-
const line = rawLine.replace(/\r$/, "");
|
|
51
|
-
if (line === "") {
|
|
52
|
-
// Empty line = event boundary
|
|
53
|
-
if (currentEvent || dataLines.length > 0) {
|
|
54
|
-
const event = parseAgentEvent(currentEvent, dataLines.join("\n"));
|
|
55
|
-
if (event)
|
|
56
|
-
yield event;
|
|
57
|
-
currentEvent = "";
|
|
58
|
-
dataLines = [];
|
|
59
|
-
}
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (line.startsWith(":"))
|
|
63
|
-
continue; // comment
|
|
64
|
-
if (line.startsWith("event:")) {
|
|
65
|
-
currentEvent = line.slice(6).trim();
|
|
66
|
-
}
|
|
67
|
-
else if (line.startsWith("data:")) {
|
|
68
|
-
dataLines.push(line.slice(5).trim());
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
finally {
|
|
74
|
-
reader.releaseLock();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const KNOWN_TYPES = new Set([
|
|
78
|
-
"connected", "mail_message", "chat_message",
|
|
79
|
-
"control_pause", "control_resume", "control_interrupt",
|
|
80
|
-
"work_available", "claim_update", "claim_removed", "error",
|
|
81
|
-
"actionable_mail", "actionable_chat",
|
|
82
|
-
]);
|
|
83
|
-
export function parseAgentEvent(eventName, data) {
|
|
84
|
-
eventName = eventName.trim();
|
|
85
|
-
if (!eventName)
|
|
86
|
-
return null;
|
|
87
|
-
if (!KNOWN_TYPES.has(eventName))
|
|
88
|
-
return null;
|
|
89
|
-
if (eventName === "actionable_mail")
|
|
90
|
-
eventName = "mail_message";
|
|
91
|
-
if (eventName === "actionable_chat")
|
|
92
|
-
eventName = "chat_message";
|
|
93
|
-
try {
|
|
94
|
-
const payload = JSON.parse(data);
|
|
95
|
-
return { ...payload, type: eventName };
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return { type: eventName };
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
function sleep(ms, signal) {
|
|
102
|
-
return new Promise((resolve) => {
|
|
103
|
-
if (signal.aborted) {
|
|
104
|
-
resolve();
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const timer = setTimeout(resolve, ms);
|
|
108
|
-
signal.addEventListener("abort", () => { clearTimeout(timer); resolve(); }, { once: true });
|
|
109
|
-
});
|
|
110
|
-
}
|
package/dist/api/mail.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { APIClient } from "./client.js";
|
|
2
|
-
import type { VerificationStatus } from "../identity/signing.js";
|
|
3
|
-
export interface InboxMessage {
|
|
4
|
-
message_id: string;
|
|
5
|
-
from_agent_id: string;
|
|
6
|
-
from_alias: string;
|
|
7
|
-
to_alias?: string;
|
|
8
|
-
from_address?: string;
|
|
9
|
-
to_address?: string;
|
|
10
|
-
subject: string;
|
|
11
|
-
body: string;
|
|
12
|
-
priority: string;
|
|
13
|
-
thread_id?: string;
|
|
14
|
-
read_at?: string;
|
|
15
|
-
created_at: string;
|
|
16
|
-
from_did?: string;
|
|
17
|
-
to_did?: string;
|
|
18
|
-
from_stable_id?: string;
|
|
19
|
-
to_stable_id?: string;
|
|
20
|
-
signature?: string;
|
|
21
|
-
signing_key_id?: string;
|
|
22
|
-
signed_payload?: string;
|
|
23
|
-
verification_status?: VerificationStatus;
|
|
24
|
-
}
|
|
25
|
-
export declare function fetchInbox(client: APIClient, unreadOnly?: boolean, limit?: number): Promise<InboxMessage[]>;
|
|
26
|
-
export declare function ackMessage(client: APIClient, messageId: string): Promise<void>;
|
package/dist/api/mail.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { verifyMessage, verifySignedPayload } from "../identity/signing.js";
|
|
2
|
-
export async function fetchInbox(client, unreadOnly = true, limit = 50) {
|
|
3
|
-
const params = new URLSearchParams();
|
|
4
|
-
if (unreadOnly)
|
|
5
|
-
params.set("unread_only", "true");
|
|
6
|
-
if (limit > 0)
|
|
7
|
-
params.set("limit", String(limit));
|
|
8
|
-
const resp = await client.get(`/v1/messages/inbox?${params}`);
|
|
9
|
-
// Verify signatures on received messages
|
|
10
|
-
for (const msg of resp.messages) {
|
|
11
|
-
msg.verification_status = await verifyInboxMessage(msg);
|
|
12
|
-
}
|
|
13
|
-
return resp.messages;
|
|
14
|
-
}
|
|
15
|
-
export async function ackMessage(client, messageId) {
|
|
16
|
-
await client.post(`/v1/messages/${encodeURIComponent(messageId)}/ack`);
|
|
17
|
-
}
|
|
18
|
-
async function verifyInboxMessage(msg) {
|
|
19
|
-
if (msg.signed_payload && msg.signature && msg.from_did) {
|
|
20
|
-
return verifySignedPayload(msg.signed_payload, msg.signature, msg.from_did, msg.signing_key_id || "");
|
|
21
|
-
}
|
|
22
|
-
const from = msg.from_address || msg.from_alias;
|
|
23
|
-
const to = msg.to_address || msg.to_alias || "";
|
|
24
|
-
const env = {
|
|
25
|
-
from,
|
|
26
|
-
from_did: msg.from_did || "",
|
|
27
|
-
to,
|
|
28
|
-
to_did: msg.to_did || "",
|
|
29
|
-
type: "mail",
|
|
30
|
-
subject: msg.subject,
|
|
31
|
-
body: msg.body,
|
|
32
|
-
timestamp: msg.created_at,
|
|
33
|
-
from_stable_id: msg.from_stable_id,
|
|
34
|
-
to_stable_id: msg.to_stable_id,
|
|
35
|
-
message_id: msg.message_id,
|
|
36
|
-
signature: msg.signature,
|
|
37
|
-
signing_key_id: msg.signing_key_id,
|
|
38
|
-
};
|
|
39
|
-
return verifyMessage(env);
|
|
40
|
-
}
|
package/dist/config.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface AgentConfig {
|
|
2
|
-
baseURL: string;
|
|
3
|
-
apiKey: string;
|
|
4
|
-
did: string;
|
|
5
|
-
stableID: string;
|
|
6
|
-
address: string;
|
|
7
|
-
alias: string;
|
|
8
|
-
projectSlug: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Resolve agent configuration from the aw config files.
|
|
12
|
-
* Resolution order: worktree context → global config.
|
|
13
|
-
* Reads the same files as the Go aw client.
|
|
14
|
-
*/
|
|
15
|
-
export declare function resolveConfig(workdir: string): Promise<AgentConfig>;
|
package/dist/config.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import yaml from "js-yaml";
|
|
5
|
-
/**
|
|
6
|
-
* Resolve agent configuration from the aw config files.
|
|
7
|
-
* Resolution order: worktree context → global config.
|
|
8
|
-
* Reads the same files as the Go aw client.
|
|
9
|
-
*/
|
|
10
|
-
export async function resolveConfig(workdir) {
|
|
11
|
-
const globalPath = process.env.AW_CONFIG_PATH || join(homedir(), ".config", "aw", "config.yaml");
|
|
12
|
-
const contextPath = join(workdir, ".aw", "context");
|
|
13
|
-
const workspacePath = join(workdir, ".aw", "workspace.yaml");
|
|
14
|
-
const globalConfig = await readYAML(globalPath);
|
|
15
|
-
if (!globalConfig?.accounts || Object.keys(globalConfig.accounts).length === 0) {
|
|
16
|
-
throw new Error(`no accounts configured in ${globalPath}`);
|
|
17
|
-
}
|
|
18
|
-
// Determine account name from worktree context or first available
|
|
19
|
-
let accountName;
|
|
20
|
-
const context = await readYAML(contextPath);
|
|
21
|
-
if (context?.default_account) {
|
|
22
|
-
accountName = context.default_account;
|
|
23
|
-
}
|
|
24
|
-
else if (context?.client_default_accounts?.aw) {
|
|
25
|
-
accountName = context.client_default_accounts.aw;
|
|
26
|
-
}
|
|
27
|
-
if (!accountName) {
|
|
28
|
-
accountName = Object.keys(globalConfig.accounts)[0];
|
|
29
|
-
}
|
|
30
|
-
const account = globalConfig.accounts[accountName];
|
|
31
|
-
if (!account) {
|
|
32
|
-
throw new Error(`account "${accountName}" not found in ${globalPath}`);
|
|
33
|
-
}
|
|
34
|
-
if (!account.api_key) {
|
|
35
|
-
throw new Error(`account "${accountName}" has no api_key`);
|
|
36
|
-
}
|
|
37
|
-
// Resolve server URL
|
|
38
|
-
const serverName = account.server || "default";
|
|
39
|
-
const server = globalConfig.servers?.[serverName];
|
|
40
|
-
const baseURL = server?.url || "https://app.aweb.ai";
|
|
41
|
-
// Resolve address
|
|
42
|
-
const namespace = account.namespace_slug || "";
|
|
43
|
-
const alias = account.alias || "";
|
|
44
|
-
const address = namespace && alias ? `${namespace}/${alias}` : "";
|
|
45
|
-
// Read workspace config for project slug
|
|
46
|
-
const workspace = await readYAML(workspacePath);
|
|
47
|
-
const projectSlug = account.default_project || workspace?.project_slug || "";
|
|
48
|
-
return {
|
|
49
|
-
baseURL,
|
|
50
|
-
apiKey: account.api_key,
|
|
51
|
-
did: account.did || "",
|
|
52
|
-
stableID: account.stable_id || "",
|
|
53
|
-
address,
|
|
54
|
-
alias,
|
|
55
|
-
projectSlug,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
async function readYAML(path) {
|
|
59
|
-
try {
|
|
60
|
-
const content = await readFile(path, "utf-8");
|
|
61
|
-
return yaml.load(content) || null;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
package/dist/identity/did.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/** Encode an Ed25519 public key as a did:key DID string. */
|
|
2
|
-
export declare function computeDIDKey(publicKey: Uint8Array): string;
|
|
3
|
-
/** Decode a did:key DID string to an Ed25519 public key. */
|
|
4
|
-
export declare function extractPublicKey(did: string): Uint8Array;
|
|
5
|
-
/** Derive the canonical did:aw stable identifier from an Ed25519 public key. */
|
|
6
|
-
export declare function computeStableID(publicKey: Uint8Array): string;
|
package/dist/identity/did.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import bs58 from "bs58";
|
|
3
|
-
const DID_KEY_PREFIX = "did:key:z";
|
|
4
|
-
const ED25519_MULTICODEC = new Uint8Array([0xed, 0x01]);
|
|
5
|
-
/** Encode an Ed25519 public key as a did:key DID string. */
|
|
6
|
-
export function computeDIDKey(publicKey) {
|
|
7
|
-
const buf = new Uint8Array(2 + publicKey.length);
|
|
8
|
-
buf.set(ED25519_MULTICODEC);
|
|
9
|
-
buf.set(publicKey, 2);
|
|
10
|
-
return DID_KEY_PREFIX + bs58.encode(buf);
|
|
11
|
-
}
|
|
12
|
-
/** Decode a did:key DID string to an Ed25519 public key. */
|
|
13
|
-
export function extractPublicKey(did) {
|
|
14
|
-
if (!did.startsWith(DID_KEY_PREFIX)) {
|
|
15
|
-
throw new Error(`invalid did:key: missing prefix "${DID_KEY_PREFIX}"`);
|
|
16
|
-
}
|
|
17
|
-
const decoded = bs58.decode(did.slice(DID_KEY_PREFIX.length));
|
|
18
|
-
if (decoded.length !== 34) {
|
|
19
|
-
throw new Error(`invalid did:key: expected 34 bytes, got ${decoded.length}`);
|
|
20
|
-
}
|
|
21
|
-
if (decoded[0] !== 0xed || decoded[1] !== 0x01) {
|
|
22
|
-
throw new Error(`invalid did:key: expected Ed25519 multicodec 0xed01, got 0x${decoded[0].toString(16).padStart(2, "0")}${decoded[1].toString(16).padStart(2, "0")}`);
|
|
23
|
-
}
|
|
24
|
-
return decoded.slice(2);
|
|
25
|
-
}
|
|
26
|
-
/** Derive the canonical did:aw stable identifier from an Ed25519 public key. */
|
|
27
|
-
export function computeStableID(publicKey) {
|
|
28
|
-
const hash = createHash("sha256").update(publicKey).digest();
|
|
29
|
-
return "did:aw:" + bs58.encode(hash.subarray(0, 20));
|
|
30
|
-
}
|
package/dist/identity/keys.d.ts
DELETED
package/dist/identity/keys.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
/** Load an Ed25519 private key seed from a PEM file. */
|
|
3
|
-
export async function loadSigningKey(path) {
|
|
4
|
-
const content = await readFile(path, "utf-8");
|
|
5
|
-
const match = content.match(/-----BEGIN ED25519 PRIVATE KEY-----\r?\n([\s\S]+?)\r?\n-----END ED25519 PRIVATE KEY-----/);
|
|
6
|
-
if (!match) {
|
|
7
|
-
throw new Error(`no ED25519 PRIVATE KEY PEM block in ${path}`);
|
|
8
|
-
}
|
|
9
|
-
const b64 = match[1].replace(/\s/g, "");
|
|
10
|
-
const bin = atob(b64);
|
|
11
|
-
const bytes = new Uint8Array(bin.length);
|
|
12
|
-
for (let i = 0; i < bin.length; i++)
|
|
13
|
-
bytes[i] = bin.charCodeAt(i);
|
|
14
|
-
if (bytes.length !== 32) {
|
|
15
|
-
throw new Error(`invalid seed size ${bytes.length} in ${path}`);
|
|
16
|
-
}
|
|
17
|
-
return bytes;
|
|
18
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export type PinResult = "ok" | "new" | "mismatch" | "skipped";
|
|
2
|
-
export interface Pin {
|
|
3
|
-
address: string;
|
|
4
|
-
handle: string;
|
|
5
|
-
stable_id?: string;
|
|
6
|
-
did_key?: string;
|
|
7
|
-
first_seen: string;
|
|
8
|
-
last_seen: string;
|
|
9
|
-
server: string;
|
|
10
|
-
}
|
|
11
|
-
export declare class PinStore {
|
|
12
|
-
pins: Map<string, Pin>;
|
|
13
|
-
addresses: Map<string, string>;
|
|
14
|
-
/** Check whether a DID matches the stored pin for an address. */
|
|
15
|
-
checkPin(address: string, did: string, lifetime: string): PinResult;
|
|
16
|
-
/** Record or update a TOFU pin. */
|
|
17
|
-
storePin(did: string, address: string, handle: string, server: string): void;
|
|
18
|
-
/** Serialize to YAML (compatible with Go's known_agents.yaml). */
|
|
19
|
-
toYAML(): string;
|
|
20
|
-
/** Deserialize from YAML. */
|
|
21
|
-
static fromYAML(content: string): PinStore;
|
|
22
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import yaml from "js-yaml";
|
|
2
|
-
export class PinStore {
|
|
3
|
-
pins = new Map();
|
|
4
|
-
addresses = new Map();
|
|
5
|
-
/** Check whether a DID matches the stored pin for an address. */
|
|
6
|
-
checkPin(address, did, lifetime) {
|
|
7
|
-
if (lifetime === "ephemeral")
|
|
8
|
-
return "skipped";
|
|
9
|
-
const pinnedDID = this.addresses.get(address);
|
|
10
|
-
if (pinnedDID === undefined)
|
|
11
|
-
return "new";
|
|
12
|
-
if (pinnedDID === did)
|
|
13
|
-
return "ok";
|
|
14
|
-
return "mismatch";
|
|
15
|
-
}
|
|
16
|
-
/** Record or update a TOFU pin. */
|
|
17
|
-
storePin(did, address, handle, server) {
|
|
18
|
-
const now = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
19
|
-
const existing = this.pins.get(did);
|
|
20
|
-
if (existing) {
|
|
21
|
-
if (existing.address !== address) {
|
|
22
|
-
this.addresses.delete(existing.address);
|
|
23
|
-
this.addresses.set(address, did);
|
|
24
|
-
existing.address = address;
|
|
25
|
-
}
|
|
26
|
-
existing.last_seen = now;
|
|
27
|
-
existing.handle = handle;
|
|
28
|
-
existing.server = server;
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
this.pins.set(did, {
|
|
32
|
-
address,
|
|
33
|
-
handle,
|
|
34
|
-
first_seen: now,
|
|
35
|
-
last_seen: now,
|
|
36
|
-
server,
|
|
37
|
-
});
|
|
38
|
-
this.addresses.set(address, did);
|
|
39
|
-
}
|
|
40
|
-
/** Serialize to YAML (compatible with Go's known_agents.yaml). */
|
|
41
|
-
toYAML() {
|
|
42
|
-
const pinsObj = {};
|
|
43
|
-
for (const [k, v] of this.pins)
|
|
44
|
-
pinsObj[k] = v;
|
|
45
|
-
const addrsObj = {};
|
|
46
|
-
for (const [k, v] of this.addresses)
|
|
47
|
-
addrsObj[k] = v;
|
|
48
|
-
return yaml.dump({ pins: pinsObj, addresses: addrsObj });
|
|
49
|
-
}
|
|
50
|
-
/** Deserialize from YAML. */
|
|
51
|
-
static fromYAML(content) {
|
|
52
|
-
const data = yaml.load(content);
|
|
53
|
-
const store = new PinStore();
|
|
54
|
-
if (!data)
|
|
55
|
-
return store;
|
|
56
|
-
if (data.pins) {
|
|
57
|
-
for (const [k, v] of Object.entries(data.pins)) {
|
|
58
|
-
store.pins.set(k, v);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (data.addresses) {
|
|
62
|
-
for (const [k, v] of Object.entries(data.addresses)) {
|
|
63
|
-
store.addresses.set(k, v);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return store;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export interface MessageEnvelope {
|
|
2
|
-
from: string;
|
|
3
|
-
from_did: string;
|
|
4
|
-
to: string;
|
|
5
|
-
to_did: string;
|
|
6
|
-
type: string;
|
|
7
|
-
subject: string;
|
|
8
|
-
body: string;
|
|
9
|
-
timestamp: string;
|
|
10
|
-
from_stable_id?: string;
|
|
11
|
-
to_stable_id?: string;
|
|
12
|
-
message_id?: string;
|
|
13
|
-
signature?: string;
|
|
14
|
-
signing_key_id?: string;
|
|
15
|
-
}
|
|
16
|
-
export type VerificationStatus = "verified" | "verified_custodial" | "unverified" | "failed" | "identity_mismatch";
|
|
17
|
-
/**
|
|
18
|
-
* Build the canonical JSON payload for message signing.
|
|
19
|
-
* Fields are sorted lexicographically, no whitespace, minimal escaping.
|
|
20
|
-
* Optional fields (from_stable_id, message_id, to_stable_id) are omitted when empty.
|
|
21
|
-
*/
|
|
22
|
-
export declare function canonicalJSON(env: MessageEnvelope): string;
|
|
23
|
-
/** Sign a message envelope. Returns base64 signature (no padding). */
|
|
24
|
-
export declare function signMessage(seed: Uint8Array, env: MessageEnvelope): Promise<string>;
|
|
25
|
-
/**
|
|
26
|
-
* Verify a message envelope signature.
|
|
27
|
-
* Returns 'unverified' if DID or signature is missing.
|
|
28
|
-
* Returns 'failed' if signature doesn't verify.
|
|
29
|
-
* Returns 'verified' if valid.
|
|
30
|
-
*/
|
|
31
|
-
export declare function verifyMessage(env: MessageEnvelope): Promise<VerificationStatus>;
|
|
32
|
-
/**
|
|
33
|
-
* Verify a signature against a pre-computed canonical payload string.
|
|
34
|
-
* Use when the server returns signed_payload alongside the message.
|
|
35
|
-
*/
|
|
36
|
-
export declare function verifySignedPayload(signedPayload: string, signatureB64: string, fromDID: string, signingKeyID: string): Promise<VerificationStatus>;
|