@maincode-ai/channel-base 0.18.3

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.
@@ -0,0 +1,41 @@
1
+ export class GroupGate {
2
+ policy;
3
+ groups;
4
+ constructor(policy = 'disabled', groups = {}) {
5
+ this.policy = policy;
6
+ this.groups = groups;
7
+ }
8
+ /**
9
+ * Full group check: policy + allowlist + mention gating.
10
+ * Evaluation order:
11
+ * 1. groupPolicy (disabled → drop)
12
+ * 2. group allowlist (allowlist mode, no match → drop)
13
+ * 3. mention gating (requireMention + not mentioned → drop silently)
14
+ *
15
+ * Mention gating runs before sender gate so that unmentioned messages
16
+ * in groups don't trigger pairing flows.
17
+ */
18
+ check(envelope) {
19
+ if (!envelope.isGroup) {
20
+ return { allowed: true };
21
+ }
22
+ if (this.policy === 'disabled') {
23
+ return { allowed: false, reason: 'disabled' };
24
+ }
25
+ if (this.policy === 'allowlist') {
26
+ // In allowlist mode, "*" is only a default config — not a wildcard allow.
27
+ // The group must be explicitly listed by ID.
28
+ if (!this.groups[envelope.chatId]) {
29
+ return { allowed: false, reason: 'not_allowlisted' };
30
+ }
31
+ }
32
+ // Per-group config, falling back to "*" defaults, then built-in defaults
33
+ const groupConfig = this.groups[envelope.chatId] || this.groups['*'] || {};
34
+ const requireMention = groupConfig.requireMention ?? true;
35
+ if (requireMention && !envelope.isMentioned && !envelope.isReplyToBot) {
36
+ return { allowed: false, reason: 'mention_required' };
37
+ }
38
+ return { allowed: true };
39
+ }
40
+ }
41
+ //# sourceMappingURL=GroupGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupGate.js","sourceRoot":"","sources":["../src/GroupGate.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,SAAS;IACZ,MAAM,CAAc;IACpB,MAAM,CAA8B;IAE5C,YACE,SAAsB,UAAU,EAChC,SAAsC,EAAE;QAExC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,QAAkB;QACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAChD,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAChC,0EAA0E;YAC1E,6CAA6C;YAC7C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;YACvD,CAAC;QACH,CAAC;QAED,yEAAyE;QACzE,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3E,MAAM,cAAc,GAAG,WAAW,CAAC,cAAc,IAAI,IAAI,CAAC;QAE1D,IAAI,cAAc,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YACtE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ export interface PairingRequest {
2
+ senderId: string;
3
+ senderName: string;
4
+ code: string;
5
+ createdAt: number;
6
+ }
7
+ export declare class PairingStore {
8
+ private dir;
9
+ private pendingPath;
10
+ private allowlistPath;
11
+ constructor(channelName: string);
12
+ isApproved(senderId: string): boolean;
13
+ /**
14
+ * Create a pairing request for an unknown sender.
15
+ * Returns the code if created, or null if the pending cap is reached.
16
+ * If the sender already has a non-expired pending request, returns that code.
17
+ */
18
+ createRequest(senderId: string, senderName: string): string | null;
19
+ /**
20
+ * Approve a pairing request by code.
21
+ * Returns the sender ID if found, or null if not found / expired.
22
+ */
23
+ approve(code: string): PairingRequest | null;
24
+ listPending(): PairingRequest[];
25
+ getAllowlist(): string[];
26
+ private ensureDir;
27
+ private readPending;
28
+ private writePending;
29
+ private readAllowlist;
30
+ private writeAllowlist;
31
+ }
@@ -0,0 +1,115 @@
1
+ import * as crypto from 'node:crypto';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { getGlobalMatildaDir } from './paths.js';
5
+ // Alphabet without ambiguous chars: 0/O, 1/I
6
+ const SAFE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
7
+ const CODE_LENGTH = 8;
8
+ const EXPIRY_MS = 60 * 60 * 1000; // 1 hour
9
+ const MAX_PENDING = 3;
10
+ export class PairingStore {
11
+ dir;
12
+ pendingPath;
13
+ allowlistPath;
14
+ constructor(channelName) {
15
+ this.dir = path.join(getGlobalMatildaDir(), 'channels');
16
+ this.pendingPath = path.join(this.dir, `${channelName}-pairing.json`);
17
+ this.allowlistPath = path.join(this.dir, `${channelName}-allowlist.json`);
18
+ }
19
+ isApproved(senderId) {
20
+ const list = this.readAllowlist();
21
+ return list.includes(senderId);
22
+ }
23
+ /**
24
+ * Create a pairing request for an unknown sender.
25
+ * Returns the code if created, or null if the pending cap is reached.
26
+ * If the sender already has a non-expired pending request, returns that code.
27
+ */
28
+ createRequest(senderId, senderName) {
29
+ const pending = this.readPending();
30
+ // Purge expired
31
+ const now = Date.now();
32
+ const active = pending.filter((r) => now - r.createdAt < EXPIRY_MS);
33
+ // Check if sender already has a pending request
34
+ const existing = active.find((r) => r.senderId === senderId);
35
+ if (existing) {
36
+ return existing.code;
37
+ }
38
+ // Cap check
39
+ if (active.length >= MAX_PENDING) {
40
+ return null;
41
+ }
42
+ const code = generateCode();
43
+ active.push({ senderId, senderName, code, createdAt: now });
44
+ this.writePending(active);
45
+ return code;
46
+ }
47
+ /**
48
+ * Approve a pairing request by code.
49
+ * Returns the sender ID if found, or null if not found / expired.
50
+ */
51
+ approve(code) {
52
+ const pending = this.readPending();
53
+ const now = Date.now();
54
+ const idx = pending.findIndex((r) => r.code === code.toUpperCase() && now - r.createdAt < EXPIRY_MS);
55
+ if (idx === -1)
56
+ return null;
57
+ const request = pending[idx];
58
+ pending.splice(idx, 1);
59
+ this.writePending(pending);
60
+ // Add to allowlist
61
+ const list = this.readAllowlist();
62
+ if (!list.includes(request.senderId)) {
63
+ list.push(request.senderId);
64
+ this.writeAllowlist(list);
65
+ }
66
+ return request;
67
+ }
68
+ listPending() {
69
+ const pending = this.readPending();
70
+ const now = Date.now();
71
+ return pending.filter((r) => now - r.createdAt < EXPIRY_MS);
72
+ }
73
+ getAllowlist() {
74
+ return this.readAllowlist();
75
+ }
76
+ ensureDir() {
77
+ if (!fs.existsSync(this.dir)) {
78
+ fs.mkdirSync(this.dir, { recursive: true });
79
+ }
80
+ }
81
+ readPending() {
82
+ try {
83
+ const data = fs.readFileSync(this.pendingPath, 'utf-8');
84
+ return JSON.parse(data);
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ }
90
+ writePending(requests) {
91
+ this.ensureDir();
92
+ fs.writeFileSync(this.pendingPath, JSON.stringify(requests, null, 2));
93
+ }
94
+ readAllowlist() {
95
+ try {
96
+ const data = fs.readFileSync(this.allowlistPath, 'utf-8');
97
+ return JSON.parse(data);
98
+ }
99
+ catch {
100
+ return [];
101
+ }
102
+ }
103
+ writeAllowlist(list) {
104
+ this.ensureDir();
105
+ fs.writeFileSync(this.allowlistPath, JSON.stringify(list, null, 2));
106
+ }
107
+ }
108
+ function generateCode() {
109
+ let code = '';
110
+ for (let i = 0; i < CODE_LENGTH; i++) {
111
+ code += SAFE_ALPHABET[crypto.randomInt(SAFE_ALPHABET.length)];
112
+ }
113
+ return code;
114
+ }
115
+ //# sourceMappingURL=PairingStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PairingStore.js","sourceRoot":"","sources":["../src/PairingStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SenderPolicy } from './types.js';
2
+ import type { PairingStore } from './PairingStore.js';
3
+ export interface SenderCheckResult {
4
+ allowed: boolean;
5
+ pairingCode?: string | null;
6
+ }
7
+ export declare class SenderGate {
8
+ private policy;
9
+ private allowedUsers;
10
+ private pairingStore;
11
+ constructor(policy: SenderPolicy, allowedUsers?: string[], pairingStore?: PairingStore);
12
+ check(senderId: string, senderName?: string): SenderCheckResult;
13
+ }
@@ -0,0 +1,34 @@
1
+ export class SenderGate {
2
+ policy;
3
+ allowedUsers;
4
+ pairingStore;
5
+ constructor(policy, allowedUsers = [], pairingStore) {
6
+ this.policy = policy;
7
+ this.allowedUsers = new Set(allowedUsers);
8
+ this.pairingStore = pairingStore || null;
9
+ }
10
+ check(senderId, senderName) {
11
+ switch (this.policy) {
12
+ case 'open':
13
+ return { allowed: true };
14
+ case 'allowlist':
15
+ return { allowed: this.allowedUsers.has(senderId) };
16
+ case 'pairing': {
17
+ // Check static allowlist first
18
+ if (this.allowedUsers.has(senderId)) {
19
+ return { allowed: true };
20
+ }
21
+ // Check dynamic approved list
22
+ if (this.pairingStore?.isApproved(senderId)) {
23
+ return { allowed: true };
24
+ }
25
+ // Generate pairing code
26
+ const code = this.pairingStore?.createRequest(senderId, senderName || senderId);
27
+ return { allowed: false, pairingCode: code ?? null };
28
+ }
29
+ default:
30
+ throw new Error(`Unknown sender policy: ${this.policy}`);
31
+ }
32
+ }
33
+ }
34
+ //# sourceMappingURL=SenderGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SenderGate.js","sourceRoot":"","sources":["../src/SenderGate.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,UAAU;IACb,MAAM,CAAe;IACrB,YAAY,CAAc;IAC1B,YAAY,CAAsB;IAE1C,YACE,MAAoB,EACpB,eAAyB,EAAE,EAC3B,YAA2B;QAE3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1C,IAAI,CAAC,YAAY,GAAG,YAAY,IAAI,IAAI,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,QAAgB,EAAE,UAAmB;QACzC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,MAAM;gBACT,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YAC3B,KAAK,WAAW;gBACd,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,+BAA+B;gBAC/B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC3B,CAAC;gBACD,8BAA8B;gBAC9B,IAAI,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC3B,CAAC;gBACD,wBAAwB;gBACxB,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAC3C,QAAQ,EACR,UAAU,IAAI,QAAQ,CACvB,CAAC;gBACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;YACvD,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ import type { SessionScope, SessionTarget } from './types.js';
2
+ import type { AcpBridge } from './AcpBridge.js';
3
+ export declare class SessionRouter {
4
+ private toSession;
5
+ private toTarget;
6
+ private toCwd;
7
+ private bridge;
8
+ private defaultCwd;
9
+ private defaultScope;
10
+ private channelScopes;
11
+ private persistPath;
12
+ constructor(bridge: AcpBridge, defaultCwd: string, scope?: SessionScope, persistPath?: string);
13
+ /** Replace the bridge instance (used after crash recovery restart). */
14
+ setBridge(bridge: AcpBridge): void;
15
+ /** Set scope override for a specific channel. */
16
+ setChannelScope(channelName: string, scope: SessionScope): void;
17
+ private routingKey;
18
+ /**
19
+ * Sender-scoped key prefix for by-sender (no chatId) scans. The trailing
20
+ * ':' delimiter ensures a sender id matches as a whole segment — without it
21
+ * sender "bob" would also match another sender "bobby"
22
+ * (key `${channelName}:bobby:${chatId}`).
23
+ */
24
+ private senderPrefix;
25
+ resolve(channelName: string, senderId: string, chatId: string, threadId?: string, cwd?: string): Promise<string>;
26
+ getTarget(sessionId: string): SessionTarget | undefined;
27
+ hasSession(channelName: string, senderId: string, chatId?: string): boolean;
28
+ /**
29
+ * Remove session(s) for the given sender. Returns the removed session IDs.
30
+ */
31
+ removeSession(channelName: string, senderId: string, chatId?: string): string[];
32
+ private deleteByKey;
33
+ /** Get all session entries for crash recovery. */
34
+ getAll(): Array<{
35
+ key: string;
36
+ sessionId: string;
37
+ target: SessionTarget;
38
+ }>;
39
+ /**
40
+ * Restore session mappings from a previous bridge.
41
+ * Called after bridge restart — attempts loadSession for each saved mapping.
42
+ * Failed loads are silently dropped (new session on next message).
43
+ */
44
+ restoreSessions(): Promise<{
45
+ restored: number;
46
+ failed: number;
47
+ }>;
48
+ /** Clear in-memory state and delete persist file. Used on clean shutdown. */
49
+ clearAll(): void;
50
+ private persist;
51
+ }
@@ -0,0 +1,195 @@
1
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
2
+ export class SessionRouter {
3
+ toSession = new Map(); // routing key → session ID
4
+ toTarget = new Map(); // session ID → target
5
+ toCwd = new Map(); // session ID → cwd
6
+ bridge;
7
+ defaultCwd;
8
+ defaultScope;
9
+ channelScopes = new Map();
10
+ persistPath;
11
+ constructor(bridge, defaultCwd, scope = 'user', persistPath) {
12
+ this.bridge = bridge;
13
+ this.defaultCwd = defaultCwd;
14
+ this.defaultScope = scope;
15
+ this.persistPath = persistPath;
16
+ }
17
+ /** Replace the bridge instance (used after crash recovery restart). */
18
+ setBridge(bridge) {
19
+ this.bridge = bridge;
20
+ }
21
+ /** Set scope override for a specific channel. */
22
+ setChannelScope(channelName, scope) {
23
+ this.channelScopes.set(channelName, scope);
24
+ }
25
+ routingKey(channelName, senderId, chatId, threadId) {
26
+ const scope = this.channelScopes.get(channelName) || this.defaultScope;
27
+ switch (scope) {
28
+ case 'thread':
29
+ return `${channelName}:${threadId || chatId}`;
30
+ case 'single':
31
+ return `${channelName}:__single__`;
32
+ case 'user':
33
+ default:
34
+ return `${channelName}:${senderId}:${chatId}`;
35
+ }
36
+ }
37
+ /**
38
+ * Sender-scoped key prefix for by-sender (no chatId) scans. The trailing
39
+ * ':' delimiter ensures a sender id matches as a whole segment — without it
40
+ * sender "bob" would also match another sender "bobby"
41
+ * (key `${channelName}:bobby:${chatId}`).
42
+ */
43
+ senderPrefix(channelName, senderId) {
44
+ return `${channelName}:${senderId}:`;
45
+ }
46
+ async resolve(channelName, senderId, chatId, threadId, cwd) {
47
+ const key = this.routingKey(channelName, senderId, chatId, threadId);
48
+ const existing = this.toSession.get(key);
49
+ if (existing) {
50
+ return existing;
51
+ }
52
+ const sessionCwd = cwd || this.defaultCwd;
53
+ const sessionId = await this.bridge.newSession(sessionCwd);
54
+ this.toSession.set(key, sessionId);
55
+ this.toTarget.set(sessionId, { channelName, senderId, chatId, threadId });
56
+ this.toCwd.set(sessionId, sessionCwd);
57
+ this.persist();
58
+ return sessionId;
59
+ }
60
+ getTarget(sessionId) {
61
+ return this.toTarget.get(sessionId);
62
+ }
63
+ hasSession(channelName, senderId, chatId) {
64
+ // If chatId is provided, do an exact lookup; otherwise prefix-scan for any
65
+ // session belonging to this sender on this channel.
66
+ if (chatId) {
67
+ return this.toSession.has(this.routingKey(channelName, senderId, chatId));
68
+ }
69
+ const prefix = this.senderPrefix(channelName, senderId);
70
+ for (const k of this.toSession.keys()) {
71
+ if (k.startsWith(prefix))
72
+ return true;
73
+ }
74
+ return false;
75
+ }
76
+ /**
77
+ * Remove session(s) for the given sender. Returns the removed session IDs.
78
+ */
79
+ removeSession(channelName, senderId, chatId) {
80
+ const removedIds = [];
81
+ if (chatId) {
82
+ const key = this.routingKey(channelName, senderId, chatId);
83
+ const sessionId = this.deleteByKey(key);
84
+ if (sessionId)
85
+ removedIds.push(sessionId);
86
+ }
87
+ else {
88
+ // No chatId: remove all sessions for this sender on this channel.
89
+ const prefix = this.senderPrefix(channelName, senderId);
90
+ for (const k of [...this.toSession.keys()]) {
91
+ if (k.startsWith(prefix)) {
92
+ const sessionId = this.deleteByKey(k);
93
+ if (sessionId)
94
+ removedIds.push(sessionId);
95
+ }
96
+ }
97
+ }
98
+ if (removedIds.length > 0)
99
+ this.persist();
100
+ return removedIds;
101
+ }
102
+ deleteByKey(key) {
103
+ const sessionId = this.toSession.get(key);
104
+ if (!sessionId)
105
+ return null;
106
+ this.toSession.delete(key);
107
+ this.toTarget.delete(sessionId);
108
+ this.toCwd.delete(sessionId);
109
+ return sessionId;
110
+ }
111
+ /** Get all session entries for crash recovery. */
112
+ getAll() {
113
+ const entries = [];
114
+ for (const [key, sessionId] of this.toSession) {
115
+ const target = this.toTarget.get(sessionId);
116
+ if (target) {
117
+ entries.push({ key, sessionId, target });
118
+ }
119
+ }
120
+ return entries;
121
+ }
122
+ /**
123
+ * Restore session mappings from a previous bridge.
124
+ * Called after bridge restart — attempts loadSession for each saved mapping.
125
+ * Failed loads are silently dropped (new session on next message).
126
+ */
127
+ async restoreSessions() {
128
+ if (!this.persistPath || !existsSync(this.persistPath)) {
129
+ return { restored: 0, failed: 0 };
130
+ }
131
+ let entries;
132
+ try {
133
+ entries = JSON.parse(readFileSync(this.persistPath, 'utf-8'));
134
+ }
135
+ catch {
136
+ return { restored: 0, failed: 0 };
137
+ }
138
+ let restored = 0;
139
+ let failed = 0;
140
+ for (const [key, entry] of Object.entries(entries)) {
141
+ try {
142
+ const sessionId = await this.bridge.loadSession(entry.sessionId, entry.cwd);
143
+ this.toSession.set(key, sessionId);
144
+ this.toTarget.set(sessionId, entry.target);
145
+ this.toCwd.set(sessionId, entry.cwd);
146
+ restored++;
147
+ }
148
+ catch {
149
+ // Session can't be loaded — will create fresh on next message
150
+ failed++;
151
+ }
152
+ }
153
+ // Update persist file to only include successfully restored sessions
154
+ if (failed > 0) {
155
+ this.persist();
156
+ }
157
+ return { restored, failed };
158
+ }
159
+ /** Clear in-memory state and delete persist file. Used on clean shutdown. */
160
+ clearAll() {
161
+ this.toSession.clear();
162
+ this.toTarget.clear();
163
+ this.toCwd.clear();
164
+ if (this.persistPath && existsSync(this.persistPath)) {
165
+ try {
166
+ unlinkSync(this.persistPath);
167
+ }
168
+ catch {
169
+ // best-effort
170
+ }
171
+ }
172
+ }
173
+ persist() {
174
+ if (!this.persistPath)
175
+ return;
176
+ const data = {};
177
+ for (const [key, sessionId] of this.toSession) {
178
+ const target = this.toTarget.get(sessionId);
179
+ if (target) {
180
+ data[key] = {
181
+ sessionId,
182
+ target,
183
+ cwd: this.toCwd.get(sessionId) || this.defaultCwd,
184
+ };
185
+ }
186
+ }
187
+ try {
188
+ writeFileSync(this.persistPath, JSON.stringify(data, null, 2), 'utf-8');
189
+ }
190
+ catch {
191
+ // best-effort — don't break message flow for persistence failure
192
+ }
193
+ }
194
+ }
195
+ //# sourceMappingURL=SessionRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionRouter.js","sourceRoot":"","sources":["../src/SessionRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export { getGlobalMatildaDir, resolvePath } from './paths.js';
2
+ export { AcpBridge } from './AcpBridge.js';
3
+ export type { AcpBridgeOptions, AvailableCommand, ToolCallEvent, } from './AcpBridge.js';
4
+ export { DaemonChannelBridge } from './DaemonChannelBridge.js';
5
+ export type { DaemonChannelBridgeOptions, DaemonChannelEvent, DaemonChannelSessionClient, DaemonChannelSessionFactory, DaemonChannelSessionFactoryRequest, DaemonPromptCompleteEvent, DaemonPermissionRequestEvent, DaemonPermissionResolvedEvent, } from './DaemonChannelBridge.js';
6
+ export { BlockStreamer } from './BlockStreamer.js';
7
+ export type { BlockStreamerOptions } from './BlockStreamer.js';
8
+ export { ChannelBase } from './ChannelBase.js';
9
+ export type { ChannelBaseOptions } from './ChannelBase.js';
10
+ export { PairingStore } from './PairingStore.js';
11
+ export type { PairingRequest } from './PairingStore.js';
12
+ export { GroupGate } from './GroupGate.js';
13
+ export type { GroupCheckResult } from './GroupGate.js';
14
+ export { SenderGate } from './SenderGate.js';
15
+ export type { SenderCheckResult } from './SenderGate.js';
16
+ export { SessionRouter } from './SessionRouter.js';
17
+ export type { Attachment, BlockStreamingChunkConfig, BlockStreamingCoalesceConfig, ChannelConfig, ChannelPlugin, ChannelType, DispatchMode, Envelope, GroupConfig, GroupPolicy, SenderPolicy, SessionScope, SessionTarget, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { getGlobalMatildaDir, resolvePath } from './paths.js';
2
+ export { AcpBridge } from './AcpBridge.js';
3
+ export { DaemonChannelBridge } from './DaemonChannelBridge.js';
4
+ export { BlockStreamer } from './BlockStreamer.js';
5
+ export { ChannelBase } from './ChannelBase.js';
6
+ export { PairingStore } from './PairingStore.js';
7
+ export { GroupGate } from './GroupGate.js';
8
+ export { SenderGate } from './SenderGate.js';
9
+ export { SessionRouter } from './SessionRouter.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAW/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Expands tilde and resolves relative paths to absolute.
3
+ * Mirrors Storage.resolvePath() in packages/core.
4
+ */
5
+ export declare function resolvePath(dir: string): string;
6
+ /**
7
+ * Returns the global Matilda home directory (config, credentials, etc.).
8
+ *
9
+ * Priority: MATILDA_HOME env var > ~/.matilda
10
+ *
11
+ * This mirrors packages/core Storage.getGlobalMatildaDir() without importing
12
+ * from core to avoid cross-package dependencies.
13
+ */
14
+ export declare function getGlobalMatildaDir(): string;
package/dist/paths.js ADDED
@@ -0,0 +1,43 @@
1
+ import * as path from 'node:path';
2
+ import * as os from 'node:os';
3
+ /**
4
+ * Expands tilde and resolves relative paths to absolute.
5
+ * Mirrors Storage.resolvePath() in packages/core.
6
+ */
7
+ export function resolvePath(dir) {
8
+ let resolved = dir;
9
+ if (resolved === '~' ||
10
+ resolved.startsWith('~/') ||
11
+ resolved.startsWith('~\\')) {
12
+ const relativeSegments = resolved === '~'
13
+ ? []
14
+ : resolved
15
+ .slice(2)
16
+ .split(/[/\\]+/)
17
+ .filter(Boolean);
18
+ resolved = path.join(os.homedir(), ...relativeSegments);
19
+ }
20
+ if (!path.isAbsolute(resolved)) {
21
+ resolved = path.resolve(resolved);
22
+ }
23
+ return resolved;
24
+ }
25
+ /**
26
+ * Returns the global Matilda home directory (config, credentials, etc.).
27
+ *
28
+ * Priority: MATILDA_HOME env var > ~/.matilda
29
+ *
30
+ * This mirrors packages/core Storage.getGlobalMatildaDir() without importing
31
+ * from core to avoid cross-package dependencies.
32
+ */
33
+ export function getGlobalMatildaDir() {
34
+ const envDir = process.env['MATILDA_HOME'];
35
+ if (envDir) {
36
+ return resolvePath(envDir);
37
+ }
38
+ const homeDir = os.homedir();
39
+ return homeDir
40
+ ? path.join(homeDir, '.matilda')
41
+ : path.join(os.tmpdir(), '.matilda');
42
+ }
43
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,QAAQ,GAAG,GAAG,CAAC;IACnB,IACE,QAAQ,KAAK,GAAG;QAChB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;QACzB,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,EAC1B,CAAC;QACD,MAAM,gBAAgB,GACpB,QAAQ,KAAK,GAAG;YACd,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,QAAQ;iBACL,KAAK,CAAC,CAAC,CAAC;iBACR,KAAK,CAAC,QAAQ,CAAC;iBACf,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,gBAAgB,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC7B,OAAO,OAAO;QACZ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;QAChC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC;AACzC,CAAC"}