@moxxy/plugin-channel-signal 0.27.0
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/LICENSE +21 -0
- package/dist/channel/chunker.d.ts +69 -0
- package/dist/channel/chunker.d.ts.map +1 -0
- package/dist/channel/chunker.js +133 -0
- package/dist/channel/chunker.js.map +1 -0
- package/dist/channel/turn-runner.d.ts +33 -0
- package/dist/channel/turn-runner.d.ts.map +1 -0
- package/dist/channel/turn-runner.js +70 -0
- package/dist/channel/turn-runner.js.map +1 -0
- package/dist/channel/voice.d.ts +37 -0
- package/dist/channel/voice.d.ts.map +1 -0
- package/dist/channel/voice.js +88 -0
- package/dist/channel/voice.js.map +1 -0
- package/dist/channel.d.ts +154 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +468 -0
- package/dist/channel.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +215 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonrpc.d.ts +53 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +167 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/keys.d.ts +37 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +55 -0
- package/dist/keys.js.map +1 -0
- package/dist/pair-flow.d.ts +21 -0
- package/dist/pair-flow.d.ts.map +1 -0
- package/dist/pair-flow.js +136 -0
- package/dist/pair-flow.js.map +1 -0
- package/dist/permission.d.ts +21 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +27 -0
- package/dist/permission.js.map +1 -0
- package/dist/schema.d.ts +4295 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +84 -0
- package/dist/schema.js.map +1 -0
- package/dist/setup-wizard.d.ts +14 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +85 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/sidecar.d.ts +137 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +421 -0
- package/dist/sidecar.js.map +1 -0
- package/package.json +89 -0
- package/src/channel/chunker.test.ts +135 -0
- package/src/channel/chunker.ts +147 -0
- package/src/channel/turn-runner.ts +97 -0
- package/src/channel/voice.test.ts +136 -0
- package/src/channel/voice.ts +118 -0
- package/src/channel.test.ts +364 -0
- package/src/channel.ts +607 -0
- package/src/index.ts +289 -0
- package/src/jsonrpc.test.ts +128 -0
- package/src/jsonrpc.ts +198 -0
- package/src/keys.test.ts +45 -0
- package/src/keys.ts +56 -0
- package/src/pair-flow.ts +161 -0
- package/src/permission.ts +36 -0
- package/src/schema.ts +98 -0
- package/src/setup-wizard.ts +98 -0
- package/src/sidecar.test.ts +276 -0
- package/src/sidecar.ts +511 -0
- package/src/subcommands.test.ts +206 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { defineChannel, definePlugin } from '@moxxy/sdk';
|
|
2
|
+
import { SignalChannel } from './channel.js';
|
|
3
|
+
import { SIGNAL_ACCOUNT_ENV, SIGNAL_ACCOUNT_KEY, SIGNAL_ALLOWED_SENDERS_KEY, parseAllowedSenders, } from './keys.js';
|
|
4
|
+
import { SIGNAL_CLI_INSTALL_HINT, findSignalCliOnPath, listSignalAccounts, signalCliDataDir, } from './sidecar.js';
|
|
5
|
+
import { runSignalWizard } from './setup-wizard.js';
|
|
6
|
+
import { runSignalPairFlow } from './pair-flow.js';
|
|
7
|
+
export { SignalChannel, } from './channel.js';
|
|
8
|
+
export { buildSignalPermissionResolver } from './permission.js';
|
|
9
|
+
export { SignalRpcClient } from './jsonrpc.js';
|
|
10
|
+
export { SignalSidecar, startLinkProcess, listSignalAccounts, findSignalCliOnPath, signalCliDataDir, signalCliAttachmentsDir, SIGNAL_CLI_INSTALL_HINT, } from './sidecar.js';
|
|
11
|
+
export { receiveParamsSchema, envelopeSchema, attachmentSchema, MAX_INBOUND_TEXT_CHARS, } from './schema.js';
|
|
12
|
+
export { ChunkedSender, takeChunk, splitForSignal, SIGNAL_CHUNK_SOFT_LIMIT, SIGNAL_CHUNK_HARD_LIMIT, } from './channel/chunker.js';
|
|
13
|
+
export { SIGNAL_ACCOUNT_KEY, SIGNAL_ACCOUNT_ENV, SIGNAL_ALLOWED_SENDERS_KEY, parseAllowedSenders, normalizeSender, E164_RE, } from './keys.js';
|
|
14
|
+
/**
|
|
15
|
+
* Build the Signal channel plugin with a host-injected vault (mirrors the
|
|
16
|
+
* Telegram/Slack plugins' vault injection).
|
|
17
|
+
*/
|
|
18
|
+
export function buildSignalPlugin(opts) {
|
|
19
|
+
return makeSignalPlugin(() => opts.vault);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Discovery-loadable default export: resolves the vault from the inter-plugin
|
|
23
|
+
* service registry in `onInit` (the vault plugin publishes `'vault'`). Requires
|
|
24
|
+
* `@moxxy/plugin-vault` to load first (declared in `package.json`
|
|
25
|
+
* `moxxy.requirements`). The channel + subcommands read the vault via
|
|
26
|
+
* `getVault()`, so resolution is deferred to call time — after `onInit` wired it.
|
|
27
|
+
*/
|
|
28
|
+
export const signalPlugin = (() => {
|
|
29
|
+
let resolved = null;
|
|
30
|
+
const getVault = () => {
|
|
31
|
+
if (!resolved) {
|
|
32
|
+
throw new Error('@moxxy/plugin-channel-signal: the "vault" service is unavailable — @moxxy/plugin-vault must load first');
|
|
33
|
+
}
|
|
34
|
+
return resolved;
|
|
35
|
+
};
|
|
36
|
+
const hooks = {
|
|
37
|
+
onInit: (ctx) => {
|
|
38
|
+
resolved = ctx.services.require('vault');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
return makeSignalPlugin(getVault, hooks);
|
|
42
|
+
})();
|
|
43
|
+
export default signalPlugin;
|
|
44
|
+
function readStringArray(value) {
|
|
45
|
+
if (Array.isArray(value))
|
|
46
|
+
return value.map((x) => String(x));
|
|
47
|
+
if (typeof value === 'string' && value.trim()) {
|
|
48
|
+
return value
|
|
49
|
+
.split(',')
|
|
50
|
+
.map((s) => s.trim())
|
|
51
|
+
.filter(Boolean);
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function makeSignalPlugin(getVault, hooks) {
|
|
56
|
+
return definePlugin({
|
|
57
|
+
name: '@moxxy/plugin-channel-signal',
|
|
58
|
+
version: '0.0.0',
|
|
59
|
+
...(hooks ? { hooks } : {}),
|
|
60
|
+
channels: [
|
|
61
|
+
defineChannel({
|
|
62
|
+
name: 'signal',
|
|
63
|
+
description: 'Signal messenger channel via a signal-cli JSON-RPC sidecar. Links as a secondary device (QR); Note-to-Self + allow-listed senders drive the agent.',
|
|
64
|
+
// A linked device sees ALL the account owner's messages, so this
|
|
65
|
+
// channel runs on its own dedicated, isolated runner (separate socket +
|
|
66
|
+
// sticky session), like Slack — the bot keeps its own persistent
|
|
67
|
+
// history apart from the user's desktop/TUI work.
|
|
68
|
+
dedicatedRunner: true,
|
|
69
|
+
sessionSource: 'signal',
|
|
70
|
+
// Self-described config so a control surface (TUI `/channels`, `moxxy
|
|
71
|
+
// channels start`, the desktop panel) can configure + run Signal
|
|
72
|
+
// without a hardcoded table.
|
|
73
|
+
config: {
|
|
74
|
+
fields: [
|
|
75
|
+
{
|
|
76
|
+
name: 'account',
|
|
77
|
+
label: 'Account number (E.164)',
|
|
78
|
+
vaultKey: SIGNAL_ACCOUNT_KEY,
|
|
79
|
+
required: true,
|
|
80
|
+
secret: false,
|
|
81
|
+
placeholder: '+15551234567',
|
|
82
|
+
help: 'The Signal account moxxy links to as a secondary device (needs signal-cli on PATH)',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
hasRequestUrl: false,
|
|
86
|
+
runHint: 'Scan the QR with your phone (Signal → Settings → Linked Devices → Link New Device); then message your own "Note to Self" to talk to moxxy.',
|
|
87
|
+
connect: {
|
|
88
|
+
kind: 'qr',
|
|
89
|
+
title: 'Link your Signal',
|
|
90
|
+
hint: 'On your phone: Signal → Settings → Linked Devices → Link New Device, then scan the QR.',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
create: (deps) => {
|
|
94
|
+
const options = deps.options;
|
|
95
|
+
const channelOpts = {
|
|
96
|
+
vault: getVault(),
|
|
97
|
+
...(typeof options?.['account'] === 'string' ? { account: options['account'] } : {}),
|
|
98
|
+
...(typeof options?.['binary'] === 'string' ? { binary: options['binary'] } : {}),
|
|
99
|
+
...(() => {
|
|
100
|
+
const tools = readStringArray(options?.['allowedTools']);
|
|
101
|
+
return tools ? { allowedTools: tools } : {};
|
|
102
|
+
})(),
|
|
103
|
+
...(() => {
|
|
104
|
+
const senders = readStringArray(options?.['allowedSenders']);
|
|
105
|
+
return senders ? { allowedSenders: senders } : {};
|
|
106
|
+
})(),
|
|
107
|
+
logger: deps.logger,
|
|
108
|
+
};
|
|
109
|
+
return new SignalChannel(channelOpts);
|
|
110
|
+
},
|
|
111
|
+
isAvailable: async () => {
|
|
112
|
+
// Gate 1: the signal-cli binary. A pure PATH scan (no spawn — this
|
|
113
|
+
// runs on every `moxxy channels list` / `moxxy doctor`), and a miss
|
|
114
|
+
// must NEVER crash discovery: return a friendly install hint.
|
|
115
|
+
try {
|
|
116
|
+
if (!findSignalCliOnPath()) {
|
|
117
|
+
return { ok: false, reason: SIGNAL_CLI_INSTALL_HINT };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { ok: false, reason: SIGNAL_CLI_INSTALL_HINT };
|
|
122
|
+
}
|
|
123
|
+
// Gate 2: an account number (env first — the vault may not be wired
|
|
124
|
+
// in a probe/listing context).
|
|
125
|
+
if (process.env[SIGNAL_ACCOUNT_ENV]?.trim())
|
|
126
|
+
return { ok: true };
|
|
127
|
+
try {
|
|
128
|
+
if (await getVault().has(SIGNAL_ACCOUNT_KEY))
|
|
129
|
+
return { ok: true };
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
/* vault unavailable in a probe context — fall through */
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
reason: `No Signal account configured. Run \`moxxy channels signal setup\`, set ${SIGNAL_ACCOUNT_ENV}, or store one in the vault as '${SIGNAL_ACCOUNT_KEY}'.`,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
interactiveCommand: 'setup',
|
|
140
|
+
subcommands: {
|
|
141
|
+
setup: {
|
|
142
|
+
description: 'Interactive setup: check signal-cli, store the account number, pick the tool allow-list, then link + start. Shown by default for `moxxy signal` on a TTY.',
|
|
143
|
+
run: async (ctx) => {
|
|
144
|
+
if (process.stdin.isTTY !== true) {
|
|
145
|
+
// Headless: just start the channel (account must already be
|
|
146
|
+
// configured + linked).
|
|
147
|
+
return ctx.startChannel();
|
|
148
|
+
}
|
|
149
|
+
return runSignalWizard(ctx);
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
pair: {
|
|
153
|
+
description: 'Link this machine to your Signal account: prints a QR to scan from Signal → Settings → Linked Devices — the same mechanism the desktop uses.',
|
|
154
|
+
run: async (ctx) => {
|
|
155
|
+
if (process.stdin.isTTY !== true) {
|
|
156
|
+
process.stderr.write('Pairing needs a TTY to show the QR. Run `moxxy channels signal pair` on a workstation, or pair from the desktop Channels panel.\n');
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
return runSignalPairFlow(ctx);
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
status: {
|
|
163
|
+
description: 'Report signal-cli / account / linking / allow-list state as JSON.',
|
|
164
|
+
run: async (ctx) => {
|
|
165
|
+
const vault = ctx.deps.vault;
|
|
166
|
+
if (!vault) {
|
|
167
|
+
process.stderr.write('vault unavailable\n');
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
const binary = findSignalCliOnPath();
|
|
171
|
+
const account = process.env[SIGNAL_ACCOUNT_ENV]?.trim() || (await vault.get(SIGNAL_ACCOUNT_KEY));
|
|
172
|
+
const allowedSenders = parseAllowedSenders(await vault.get(SIGNAL_ALLOWED_SENDERS_KEY));
|
|
173
|
+
// Linked state needs a one-shot signal-cli spawn (slow JVM);
|
|
174
|
+
// report null when the probe isn't possible/fails.
|
|
175
|
+
let linked = null;
|
|
176
|
+
if (binary && account) {
|
|
177
|
+
try {
|
|
178
|
+
linked = (await listSignalAccounts({ binary })).includes(account);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
linked = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
process.stdout.write(JSON.stringify({
|
|
185
|
+
binaryFound: binary != null,
|
|
186
|
+
account: account || null,
|
|
187
|
+
linked,
|
|
188
|
+
allowedSenders,
|
|
189
|
+
dataDir: signalCliDataDir(),
|
|
190
|
+
}, null, 2) + '\n');
|
|
191
|
+
return 0;
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
unpair: {
|
|
195
|
+
description: "Forget the stored account + sender allow-list. (signal-cli's own linked-device store is untouched; remove the device from Signal → Linked Devices on your phone to fully unlink.)",
|
|
196
|
+
run: async (ctx) => {
|
|
197
|
+
const vault = ctx.deps.vault;
|
|
198
|
+
if (!vault) {
|
|
199
|
+
process.stderr.write('vault unavailable\n');
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
const removedAccount = await vault.delete(SIGNAL_ACCOUNT_KEY);
|
|
203
|
+
await vault.delete(SIGNAL_ALLOWED_SENDERS_KEY);
|
|
204
|
+
process.stdout.write(removedAccount
|
|
205
|
+
? 'unpaired (vault cleared). To fully unlink, remove the "moxxy" device in Signal → Settings → Linked Devices on your phone.\n'
|
|
206
|
+
: 'no account was configured\n');
|
|
207
|
+
return 0;
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
],
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal newline-delimited JSON-RPC 2.0 client for the signal-cli daemon
|
|
3
|
+
* socket (`signal-cli -a <account> daemon --socket <path>`).
|
|
4
|
+
*
|
|
5
|
+
* The daemon speaks one JSON object per line over the UNIX socket:
|
|
6
|
+
* → {"jsonrpc":"2.0","id":"…","method":"send","params":{…}}
|
|
7
|
+
* ← {"jsonrpc":"2.0","id":"…","result":{…}} | {"…","error":{code,message}}
|
|
8
|
+
* ← {"jsonrpc":"2.0","method":"receive","params":{"envelope":{…}}} (notification)
|
|
9
|
+
*
|
|
10
|
+
* The transport is injected as a plain duplex-ish stream so tests never open a
|
|
11
|
+
* real socket (mirrors the browser sidecar's injectable spawn).
|
|
12
|
+
*/
|
|
13
|
+
/** The stream slice this client needs — `net.Socket` satisfies it. */
|
|
14
|
+
export interface RpcStream {
|
|
15
|
+
write(data: string): unknown;
|
|
16
|
+
on(event: 'data', listener: (chunk: Buffer | string) => void): unknown;
|
|
17
|
+
on(event: 'close', listener: () => void): unknown;
|
|
18
|
+
on(event: 'error', listener: (err: Error) => void): unknown;
|
|
19
|
+
end?(): unknown;
|
|
20
|
+
destroy?(): unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface RpcLogger {
|
|
23
|
+
warn?(msg: string, meta?: Record<string, unknown>): void;
|
|
24
|
+
}
|
|
25
|
+
export interface SignalRpcClientOptions {
|
|
26
|
+
readonly stream: RpcStream;
|
|
27
|
+
/** Per-request timeout. signal-cli sends synchronously to the Signal server,
|
|
28
|
+
* so allow generous headroom. Default 30s. */
|
|
29
|
+
readonly requestTimeoutMs?: number;
|
|
30
|
+
readonly logger?: RpcLogger;
|
|
31
|
+
}
|
|
32
|
+
export declare class SignalRpcClient {
|
|
33
|
+
private readonly stream;
|
|
34
|
+
private readonly requestTimeoutMs;
|
|
35
|
+
private readonly logger;
|
|
36
|
+
private readonly pending;
|
|
37
|
+
private readonly notificationListeners;
|
|
38
|
+
private readonly closeListeners;
|
|
39
|
+
private buffer;
|
|
40
|
+
private nextId;
|
|
41
|
+
private closed;
|
|
42
|
+
constructor(opts: SignalRpcClientOptions);
|
|
43
|
+
/** Subscribe to a JSON-RPC notification method (e.g. 'receive'). */
|
|
44
|
+
onNotification(method: string, listener: (params: unknown) => void): () => void;
|
|
45
|
+
/** Fires once when the underlying stream closes/errors (daemon died). */
|
|
46
|
+
onClose(listener: (reason: string) => void): () => void;
|
|
47
|
+
request(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
48
|
+
close(): void;
|
|
49
|
+
private teardown;
|
|
50
|
+
private onData;
|
|
51
|
+
private handleLine;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=jsonrpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IACvE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;IAClD,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAC5D,GAAG,CAAC,IAAI,OAAO,CAAC;IAChB,OAAO,CAAC,IAAI,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B;mDAC+C;IAC/C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAcD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqD;IAC3F,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuC;IACtE,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAS;gBAEX,IAAI,EAAE,sBAAsB;IASxC,oEAAoE;IACpE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAU/E,yEAAyE;IACzE,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAKvD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkC/E,KAAK,IAAI,IAAI;IAUb,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,UAAU;CAsCnB"}
|
package/dist/jsonrpc.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal newline-delimited JSON-RPC 2.0 client for the signal-cli daemon
|
|
3
|
+
* socket (`signal-cli -a <account> daemon --socket <path>`).
|
|
4
|
+
*
|
|
5
|
+
* The daemon speaks one JSON object per line over the UNIX socket:
|
|
6
|
+
* → {"jsonrpc":"2.0","id":"…","method":"send","params":{…}}
|
|
7
|
+
* ← {"jsonrpc":"2.0","id":"…","result":{…}} | {"…","error":{code,message}}
|
|
8
|
+
* ← {"jsonrpc":"2.0","method":"receive","params":{"envelope":{…}}} (notification)
|
|
9
|
+
*
|
|
10
|
+
* The transport is injected as a plain duplex-ish stream so tests never open a
|
|
11
|
+
* real socket (mirrors the browser sidecar's injectable spawn).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Cap on a single buffered line. Receive envelopes are small (attachments are
|
|
15
|
+
* written to signal-cli's data dir, not inlined), so anything beyond this is a
|
|
16
|
+
* malformed/hostile peer; we drop the buffer instead of growing unbounded.
|
|
17
|
+
*/
|
|
18
|
+
const MAX_LINE_BUFFER = 8 * 1024 * 1024;
|
|
19
|
+
export class SignalRpcClient {
|
|
20
|
+
stream;
|
|
21
|
+
requestTimeoutMs;
|
|
22
|
+
logger;
|
|
23
|
+
pending = new Map();
|
|
24
|
+
notificationListeners = new Map();
|
|
25
|
+
closeListeners = new Set();
|
|
26
|
+
buffer = '';
|
|
27
|
+
nextId = 1;
|
|
28
|
+
closed = false;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
this.stream = opts.stream;
|
|
31
|
+
this.requestTimeoutMs = opts.requestTimeoutMs ?? 30_000;
|
|
32
|
+
this.logger = opts.logger;
|
|
33
|
+
this.stream.on('data', (chunk) => this.onData(chunk));
|
|
34
|
+
this.stream.on('close', () => this.teardown('socket closed'));
|
|
35
|
+
this.stream.on('error', (err) => this.teardown(`socket error: ${err.message}`));
|
|
36
|
+
}
|
|
37
|
+
/** Subscribe to a JSON-RPC notification method (e.g. 'receive'). */
|
|
38
|
+
onNotification(method, listener) {
|
|
39
|
+
let set = this.notificationListeners.get(method);
|
|
40
|
+
if (!set) {
|
|
41
|
+
set = new Set();
|
|
42
|
+
this.notificationListeners.set(method, set);
|
|
43
|
+
}
|
|
44
|
+
set.add(listener);
|
|
45
|
+
return () => set.delete(listener);
|
|
46
|
+
}
|
|
47
|
+
/** Fires once when the underlying stream closes/errors (daemon died). */
|
|
48
|
+
onClose(listener) {
|
|
49
|
+
this.closeListeners.add(listener);
|
|
50
|
+
return () => this.closeListeners.delete(listener);
|
|
51
|
+
}
|
|
52
|
+
request(method, params = {}) {
|
|
53
|
+
if (this.closed) {
|
|
54
|
+
return Promise.reject(new Error(`signal-cli rpc closed (cannot call ${method})`));
|
|
55
|
+
}
|
|
56
|
+
const id = String(this.nextId++);
|
|
57
|
+
const payload = JSON.stringify({ jsonrpc: '2.0', id, method, params });
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
// Per-call timeout so a wedged daemon never strands the caller.
|
|
60
|
+
const timer = setTimeout(() => {
|
|
61
|
+
if (this.pending.delete(id)) {
|
|
62
|
+
reject(new Error(`signal-cli rpc "${method}" timed out after ${this.requestTimeoutMs}ms`));
|
|
63
|
+
}
|
|
64
|
+
}, this.requestTimeoutMs);
|
|
65
|
+
timer.unref?.();
|
|
66
|
+
this.pending.set(id, {
|
|
67
|
+
resolve: (v) => {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
resolve(v);
|
|
70
|
+
},
|
|
71
|
+
reject: (e) => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
reject(e);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
this.stream.write(payload + '\n');
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
this.pending.delete(id);
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
close() {
|
|
87
|
+
this.teardown('client closed');
|
|
88
|
+
try {
|
|
89
|
+
this.stream.end?.();
|
|
90
|
+
this.stream.destroy?.();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
/* best-effort */
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
teardown(reason) {
|
|
97
|
+
if (this.closed)
|
|
98
|
+
return;
|
|
99
|
+
this.closed = true;
|
|
100
|
+
for (const [, p] of this.pending)
|
|
101
|
+
p.reject(new Error(`signal-cli rpc: ${reason}`));
|
|
102
|
+
this.pending.clear();
|
|
103
|
+
for (const listener of this.closeListeners) {
|
|
104
|
+
try {
|
|
105
|
+
listener(reason);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* listener errors must not break teardown */
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.closeListeners.clear();
|
|
112
|
+
}
|
|
113
|
+
onData(chunk) {
|
|
114
|
+
this.buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
115
|
+
let nl;
|
|
116
|
+
while ((nl = this.buffer.indexOf('\n')) !== -1) {
|
|
117
|
+
const line = this.buffer.slice(0, nl);
|
|
118
|
+
this.buffer = this.buffer.slice(nl + 1);
|
|
119
|
+
if (line.trim())
|
|
120
|
+
this.handleLine(line);
|
|
121
|
+
}
|
|
122
|
+
if (this.buffer.length > MAX_LINE_BUFFER) {
|
|
123
|
+
this.logger?.warn?.('signal rpc: dropped oversized un-delimited line', {
|
|
124
|
+
bytes: this.buffer.length,
|
|
125
|
+
});
|
|
126
|
+
this.buffer = '';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
handleLine(line) {
|
|
130
|
+
let msg;
|
|
131
|
+
try {
|
|
132
|
+
msg = JSON.parse(line);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
this.logger?.warn?.('signal rpc: ignoring unparseable line');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Notification: has a method, no id.
|
|
139
|
+
if (msg.method !== undefined && msg.id === undefined) {
|
|
140
|
+
const listeners = this.notificationListeners.get(msg.method);
|
|
141
|
+
if (!listeners)
|
|
142
|
+
return;
|
|
143
|
+
for (const listener of listeners) {
|
|
144
|
+
try {
|
|
145
|
+
listener(msg.params);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
this.logger?.warn?.('signal rpc: notification listener threw', { err: String(err) });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Response: match by id.
|
|
154
|
+
const id = msg.id !== undefined ? String(msg.id) : null;
|
|
155
|
+
const p = id ? this.pending.get(id) : undefined;
|
|
156
|
+
if (!p || !id)
|
|
157
|
+
return; // late/unknown reply — ignore
|
|
158
|
+
this.pending.delete(id);
|
|
159
|
+
if (msg.error) {
|
|
160
|
+
p.reject(new Error(`signal-cli rpc error ${msg.error.code ?? ''}: ${msg.error.message ?? 'unknown'}`));
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
p.resolve(msg.result);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=jsonrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"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"}
|
package/dist/keys.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault keys + validation shared by the channel, its subcommands, and the
|
|
3
|
+
* interactive setup wizard. Kept in their own module (the telegram/slack
|
|
4
|
+
* `keys.ts` pattern) so wizard / pair-flow helpers can import them without
|
|
5
|
+
* pulling in the plugin's full index.
|
|
6
|
+
*
|
|
7
|
+
* Note on secret custody: signal-cli keeps the actual Signal identity keys +
|
|
8
|
+
* message store in ITS OWN data dir (`$XDG_DATA_HOME/signal-cli/`, i.e.
|
|
9
|
+
* `~/.local/share/signal-cli/` by default — see {@link signalCliDataDir}).
|
|
10
|
+
* The moxxy vault only stores the account NUMBER and the sender allow-list;
|
|
11
|
+
* there is no API token in this channel at all.
|
|
12
|
+
*/
|
|
13
|
+
/** Vault key for the linked Signal account number (E.164). */
|
|
14
|
+
export declare const SIGNAL_ACCOUNT_KEY = "signal_account";
|
|
15
|
+
/** Env override for the account number — beats the vault (shared precedence). */
|
|
16
|
+
export declare const SIGNAL_ACCOUNT_ENV = "MOXXY_SIGNAL_ACCOUNT";
|
|
17
|
+
/**
|
|
18
|
+
* Vault key for the sender allow-list: a JSON array of E.164 numbers and/or
|
|
19
|
+
* Signal account UUIDs that may drive the session. The linked account's own
|
|
20
|
+
* "Note to Self" is allowed implicitly and does not need an entry here.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SIGNAL_ALLOWED_SENDERS_KEY = "signal_allowed_senders";
|
|
23
|
+
/** E.164 shape: `+` then 7–15 digits, no leading zero. */
|
|
24
|
+
export declare const E164_RE: RegExp;
|
|
25
|
+
/** Loose UUID shape (Signal ACIs are UUIDv4; accept any hex-dash UUID). */
|
|
26
|
+
export declare const UUID_RE: RegExp;
|
|
27
|
+
/** Canonical sender identity for allow-list comparisons: trimmed, UUIDs lowercased. */
|
|
28
|
+
export declare function normalizeSender(raw: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Parse the stored allow-list. Returns `[]` for a missing OR corrupt value
|
|
31
|
+
* rather than throwing — a corrupt vault entry must degrade to "nobody extra
|
|
32
|
+
* is allowed" (fail closed), never crash the channel or silently allow.
|
|
33
|
+
* Non-string entries and entries that look like neither an E.164 number nor a
|
|
34
|
+
* UUID are dropped.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseAllowedSenders(raw: string | null | undefined): string[];
|
|
37
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AACnD,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AACzD;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE,0DAA0D;AAC1D,eAAO,MAAM,OAAO,QAAsB,CAAC;AAE3C,2EAA2E;AAC3E,eAAO,MAAM,OAAO,QAAkF,CAAC;AAEvG,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAY5E"}
|
package/dist/keys.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault keys + validation shared by the channel, its subcommands, and the
|
|
3
|
+
* interactive setup wizard. Kept in their own module (the telegram/slack
|
|
4
|
+
* `keys.ts` pattern) so wizard / pair-flow helpers can import them without
|
|
5
|
+
* pulling in the plugin's full index.
|
|
6
|
+
*
|
|
7
|
+
* Note on secret custody: signal-cli keeps the actual Signal identity keys +
|
|
8
|
+
* message store in ITS OWN data dir (`$XDG_DATA_HOME/signal-cli/`, i.e.
|
|
9
|
+
* `~/.local/share/signal-cli/` by default — see {@link signalCliDataDir}).
|
|
10
|
+
* The moxxy vault only stores the account NUMBER and the sender allow-list;
|
|
11
|
+
* there is no API token in this channel at all.
|
|
12
|
+
*/
|
|
13
|
+
/** Vault key for the linked Signal account number (E.164). */
|
|
14
|
+
export const SIGNAL_ACCOUNT_KEY = 'signal_account';
|
|
15
|
+
/** Env override for the account number — beats the vault (shared precedence). */
|
|
16
|
+
export const SIGNAL_ACCOUNT_ENV = 'MOXXY_SIGNAL_ACCOUNT';
|
|
17
|
+
/**
|
|
18
|
+
* Vault key for the sender allow-list: a JSON array of E.164 numbers and/or
|
|
19
|
+
* Signal account UUIDs that may drive the session. The linked account's own
|
|
20
|
+
* "Note to Self" is allowed implicitly and does not need an entry here.
|
|
21
|
+
*/
|
|
22
|
+
export const SIGNAL_ALLOWED_SENDERS_KEY = 'signal_allowed_senders';
|
|
23
|
+
/** E.164 shape: `+` then 7–15 digits, no leading zero. */
|
|
24
|
+
export const E164_RE = /^\+[1-9]\d{6,14}$/;
|
|
25
|
+
/** Loose UUID shape (Signal ACIs are UUIDv4; accept any hex-dash UUID). */
|
|
26
|
+
export const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
27
|
+
/** Canonical sender identity for allow-list comparisons: trimmed, UUIDs lowercased. */
|
|
28
|
+
export function normalizeSender(raw) {
|
|
29
|
+
const trimmed = raw.trim();
|
|
30
|
+
return UUID_RE.test(trimmed) ? trimmed.toLowerCase() : trimmed;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse the stored allow-list. Returns `[]` for a missing OR corrupt value
|
|
34
|
+
* rather than throwing — a corrupt vault entry must degrade to "nobody extra
|
|
35
|
+
* is allowed" (fail closed), never crash the channel or silently allow.
|
|
36
|
+
* Non-string entries and entries that look like neither an E.164 number nor a
|
|
37
|
+
* UUID are dropped.
|
|
38
|
+
*/
|
|
39
|
+
export function parseAllowedSenders(raw) {
|
|
40
|
+
if (!raw)
|
|
41
|
+
return [];
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
if (!Array.isArray(parsed))
|
|
45
|
+
return [];
|
|
46
|
+
return parsed
|
|
47
|
+
.filter((x) => typeof x === 'string')
|
|
48
|
+
.map(normalizeSender)
|
|
49
|
+
.filter((x) => E164_RE.test(x) || UUID_RE.test(x));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=keys.js.map
|
package/dist/keys.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AACnD,iFAAiF;AACjF,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AACzD;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAEnE,0DAA0D;AAC1D,MAAM,CAAC,MAAM,OAAO,GAAG,mBAAmB,CAAC;AAE3C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,OAAO,GAAG,+EAA+E,CAAC;AAEvG,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAA8B;IAChE,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,MAAM;aACV,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;aACjD,GAAG,CAAC,eAAe,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ChannelSubcommandContext } from '@moxxy/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Drive the Signal linked-device pairing end-to-end in a terminal — the SAME
|
|
4
|
+
* mechanism the desktop Channels panel uses (the channel's `pair: true` link
|
|
5
|
+
* window), just rendered inline here.
|
|
6
|
+
*
|
|
7
|
+
* Steps:
|
|
8
|
+
* 1. Build a SignalChannel from the subcommand ctx and wire the session's
|
|
9
|
+
* permission resolver.
|
|
10
|
+
* 2. Subscribe to "linked" BEFORE starting so a fast scan can't race us.
|
|
11
|
+
* 3. Start the channel in pairing mode — it spawns `signal-cli link` and
|
|
12
|
+
* publishes the `sgnl://linkdevice…` URI as `channel.requestUrl`.
|
|
13
|
+
* 4. Render that URI as a scannable QR (+ the raw URI).
|
|
14
|
+
* 5. On the phone: Signal → Settings → Linked Devices → Link New Device →
|
|
15
|
+
* scan; the channel stores the account and boots the daemon.
|
|
16
|
+
* 6. Keep the channel running until the user Ctrl-Cs.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runSignalPairFlow(ctx: ChannelSubcommandContext, overrides?: {
|
|
19
|
+
readonly allowedTools?: ReadonlyArray<string>;
|
|
20
|
+
}): Promise<number>;
|
|
21
|
+
//# sourceMappingURL=pair-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair-flow.d.ts","sourceRoot":"","sources":["../src/pair-flow.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAQnF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,GAAE;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAO,GAChE,OAAO,CAAC,MAAM,CAAC,CAkGjB"}
|