@dsh-enhanced/lark-channel 0.1.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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +342 -0
  3. package/bin/dsh-lark-setup.js +8 -0
  4. package/bin/dsh-supervised-growth-setup.js +8 -0
  5. package/cordis.patch.yml +21 -0
  6. package/lib/adapter.d.ts +135 -0
  7. package/lib/adapter.d.ts.map +1 -0
  8. package/lib/adapter.js +965 -0
  9. package/lib/adapter.js.map +1 -0
  10. package/lib/approval.d.ts +26 -0
  11. package/lib/approval.d.ts.map +1 -0
  12. package/lib/approval.js +104 -0
  13. package/lib/approval.js.map +1 -0
  14. package/lib/config.d.ts +21 -0
  15. package/lib/config.d.ts.map +1 -0
  16. package/lib/config.js +56 -0
  17. package/lib/config.js.map +1 -0
  18. package/lib/group-thread.d.ts +11 -0
  19. package/lib/group-thread.d.ts.map +1 -0
  20. package/lib/group-thread.js +22 -0
  21. package/lib/group-thread.js.map +1 -0
  22. package/lib/index.d.ts +27 -0
  23. package/lib/index.d.ts.map +1 -0
  24. package/lib/index.js +30 -0
  25. package/lib/index.js.map +1 -0
  26. package/lib/launchd.d.ts +44 -0
  27. package/lib/launchd.d.ts.map +1 -0
  28. package/lib/launchd.js +205 -0
  29. package/lib/launchd.js.map +1 -0
  30. package/lib/model-picker.d.ts +24 -0
  31. package/lib/model-picker.d.ts.map +1 -0
  32. package/lib/model-picker.js +95 -0
  33. package/lib/model-picker.js.map +1 -0
  34. package/lib/normalize.d.ts +12 -0
  35. package/lib/normalize.d.ts.map +1 -0
  36. package/lib/normalize.js +77 -0
  37. package/lib/normalize.js.map +1 -0
  38. package/lib/permission-picker.d.ts +29 -0
  39. package/lib/permission-picker.d.ts.map +1 -0
  40. package/lib/permission-picker.js +145 -0
  41. package/lib/permission-picker.js.map +1 -0
  42. package/lib/progress.d.ts +15 -0
  43. package/lib/progress.d.ts.map +1 -0
  44. package/lib/progress.js +137 -0
  45. package/lib/progress.js.map +1 -0
  46. package/lib/resident.d.ts +13 -0
  47. package/lib/resident.d.ts.map +1 -0
  48. package/lib/resident.js +41 -0
  49. package/lib/resident.js.map +1 -0
  50. package/lib/sdk.d.ts +131 -0
  51. package/lib/sdk.d.ts.map +1 -0
  52. package/lib/sdk.js +945 -0
  53. package/lib/sdk.js.map +1 -0
  54. package/lib/service.d.ts +30 -0
  55. package/lib/service.d.ts.map +1 -0
  56. package/lib/service.js +157 -0
  57. package/lib/service.js.map +1 -0
  58. package/lib/setup-materialization.d.ts +15 -0
  59. package/lib/setup-materialization.d.ts.map +1 -0
  60. package/lib/setup-materialization.js +221 -0
  61. package/lib/setup-materialization.js.map +1 -0
  62. package/lib/setup-profile.d.ts +23 -0
  63. package/lib/setup-profile.d.ts.map +1 -0
  64. package/lib/setup-profile.js +1126 -0
  65. package/lib/setup-profile.js.map +1 -0
  66. package/lib/setup.d.ts +206 -0
  67. package/lib/setup.d.ts.map +1 -0
  68. package/lib/setup.js +2143 -0
  69. package/lib/setup.js.map +1 -0
  70. package/lib/supervised-growth-profile.d.ts +74 -0
  71. package/lib/supervised-growth-profile.d.ts.map +1 -0
  72. package/lib/supervised-growth-profile.js +485 -0
  73. package/lib/supervised-growth-profile.js.map +1 -0
  74. package/lib/supervised-growth-setup.d.ts +85 -0
  75. package/lib/supervised-growth-setup.d.ts.map +1 -0
  76. package/lib/supervised-growth-setup.js +371 -0
  77. package/lib/supervised-growth-setup.js.map +1 -0
  78. package/lib/systemd.d.ts +39 -0
  79. package/lib/systemd.d.ts.map +1 -0
  80. package/lib/systemd.js +142 -0
  81. package/lib/systemd.js.map +1 -0
  82. package/lib/tool-approval.d.ts +22 -0
  83. package/lib/tool-approval.d.ts.map +1 -0
  84. package/lib/tool-approval.js +107 -0
  85. package/lib/tool-approval.js.map +1 -0
  86. package/lib/types.d.ts +158 -0
  87. package/lib/types.d.ts.map +1 -0
  88. package/lib/types.js +11 -0
  89. package/lib/types.js.map +1 -0
  90. package/lib/version.d.ts +2 -0
  91. package/lib/version.d.ts.map +1 -0
  92. package/lib/version.js +2 -0
  93. package/lib/version.js.map +1 -0
  94. package/lib/windows-task.d.ts +39 -0
  95. package/lib/windows-task.d.ts.map +1 -0
  96. package/lib/windows-task.js +117 -0
  97. package/lib/windows-task.js.map +1 -0
  98. package/lib/ws-card-callback.d.ts +7 -0
  99. package/lib/ws-card-callback.d.ts.map +1 -0
  100. package/lib/ws-card-callback.js +51 -0
  101. package/lib/ws-card-callback.js.map +1 -0
  102. package/package.json +96 -0
package/lib/launchd.js ADDED
@@ -0,0 +1,205 @@
1
+ import { accessSync, constants } from 'node:fs';
2
+ import { access, chmod, mkdir, open, rename, unlink } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, isAbsolute, join } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ const profilePattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
7
+ function xml(value) {
8
+ return value
9
+ .replaceAll('&', '&')
10
+ .replaceAll('<', '&lt;')
11
+ .replaceAll('>', '&gt;')
12
+ .replaceAll('"', '&quot;')
13
+ .replaceAll("'", '&apos;');
14
+ }
15
+ function requireAbsolute(value, label) {
16
+ if (!isAbsolute(value))
17
+ throw new Error(`lark-channel setup: ${label} must be absolute`);
18
+ return value;
19
+ }
20
+ export function launchAgentPaths(input) {
21
+ if (!profilePattern.test(input.profile))
22
+ throw new Error('lark-channel setup: invalid profile');
23
+ const home = requireAbsolute(input.home ?? homedir(), 'home');
24
+ const dshHome = requireAbsolute(input.dshHome, 'DSH_HOME');
25
+ const label = `ai.deepseek.dsh.profile.${input.profile}`;
26
+ return {
27
+ label,
28
+ plistPath: join(home, 'Library', 'LaunchAgents', `${label}.plist`),
29
+ stdoutPath: join(dshHome, 'logs', `${input.profile}-host.log`),
30
+ stderrPath: join(dshHome, 'logs', `${input.profile}-host.error.log`),
31
+ };
32
+ }
33
+ export function createLaunchAgent(input) {
34
+ const strings = [input.dshHome, input.profileDirectory, input.nodePath, input.dshPath,
35
+ input.stdoutPath, input.stderrPath];
36
+ if (strings.some(value => !isAbsolute(value))) {
37
+ throw new Error('lark-channel setup: LaunchAgent paths must be absolute');
38
+ }
39
+ return `<?xml version="1.0" encoding="UTF-8"?>
40
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
41
+ <plist version="1.0">
42
+ <dict>
43
+ <key>Label</key>
44
+ <string>${xml(input.label)}</string>
45
+ <key>ProgramArguments</key>
46
+ <array>
47
+ <string>${xml(input.nodePath)}</string>
48
+ <string>--disable-warning=ExperimentalWarning</string>
49
+ <string>${xml(input.dshPath)}</string>
50
+ <string>--profile</string>
51
+ <string>${xml(input.profile)}</string>
52
+ <string>--no-open</string>
53
+ </array>
54
+ <key>EnvironmentVariables</key>
55
+ <dict>
56
+ <key>DSH_HOME</key>
57
+ <string>${xml(input.dshHome)}</string>
58
+ <key>PATH</key>
59
+ <string>${xml(input.path)}</string>
60
+ </dict>
61
+ <key>WorkingDirectory</key>
62
+ <string>${xml(input.profileDirectory)}</string>
63
+ <key>StandardOutPath</key>
64
+ <string>${xml(input.stdoutPath)}</string>
65
+ <key>StandardErrorPath</key>
66
+ <string>${xml(input.stderrPath)}</string>
67
+ <key>LimitLoadToSessionType</key>
68
+ <string>Aqua</string>
69
+ <key>ProcessType</key>
70
+ <string>Background</string>
71
+ <key>RunAtLoad</key>
72
+ <true/>
73
+ <key>KeepAlive</key>
74
+ <true/>
75
+ <key>ThrottleInterval</key>
76
+ <integer>10</integer>
77
+ <key>ExitTimeOut</key>
78
+ <integer>30</integer>
79
+ <key>Umask</key>
80
+ <integer>63</integer>
81
+ </dict>
82
+ </plist>
83
+ `;
84
+ }
85
+ function findExecutable(name, pathValue) {
86
+ for (const directory of (pathValue ?? '').split(':')) {
87
+ if (!isAbsolute(directory))
88
+ continue;
89
+ const candidate = join(directory, name);
90
+ try {
91
+ // Synchronous resolution happens once during setup and avoids invoking a shell.
92
+ accessSync(candidate, constants.X_OK);
93
+ return candidate;
94
+ }
95
+ catch { }
96
+ }
97
+ throw new Error(`lark-channel setup: cannot find ${name} in PATH`);
98
+ }
99
+ function servicePath(nodePath, dshPath, current) {
100
+ const values = [dirname(nodePath), dirname(dshPath), ...(current ?? '').split(':'),
101
+ '/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
102
+ return [...new Set(values.filter(value => isAbsolute(value)))].join(':');
103
+ }
104
+ function defaultRun(command, args) {
105
+ return spawnSync(command, [...args], {
106
+ encoding: 'utf8',
107
+ env: { PATH: '/usr/bin:/bin' },
108
+ maxBuffer: 256 * 1024,
109
+ stdio: ['ignore', 'ignore', 'pipe'],
110
+ });
111
+ }
112
+ function commandError(action, result) {
113
+ const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8');
114
+ const detail = stderr?.trim().slice(0, 1_000);
115
+ return new Error(`lark-channel setup: ${action} failed${detail ? `: ${detail}` : ''}`);
116
+ }
117
+ function isBootstrapEio(result) {
118
+ const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8');
119
+ return result.status === 5 || /Bootstrap failed:\s*5:\s*Input\/output error/iu.test(stderr ?? '');
120
+ }
121
+ function defaultWait(milliseconds) {
122
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
123
+ }
124
+ async function writePlist(path, value, run) {
125
+ const temporary = `${path}.tmp-${process.pid}`;
126
+ const file = await open(temporary, 'w', 0o600);
127
+ try {
128
+ await file.writeFile(value, 'utf8');
129
+ await file.sync();
130
+ }
131
+ finally {
132
+ await file.close();
133
+ }
134
+ const validation = run('/usr/bin/plutil', ['-lint', temporary]);
135
+ if (validation.status !== 0) {
136
+ await unlink(temporary).catch(() => { });
137
+ throw commandError('LaunchAgent plist validation', validation);
138
+ }
139
+ await rename(temporary, path);
140
+ await chmod(path, 0o600);
141
+ }
142
+ export async function installDshLaunchAgent(input, options = {}) {
143
+ if ((options.platform ?? process.platform) !== 'darwin') {
144
+ throw new Error('lark-channel setup: automatic resident service currently requires macOS launchd');
145
+ }
146
+ const uid = options.uid ?? process.getuid?.();
147
+ if (!Number.isSafeInteger(uid) || uid === undefined || uid < 1) {
148
+ throw new Error('lark-channel setup: cannot resolve the launchd user domain');
149
+ }
150
+ const paths = launchAgentPaths({
151
+ ...(options.home === undefined ? {} : { home: options.home }),
152
+ dshHome: input.dshHome,
153
+ profile: input.profile,
154
+ });
155
+ const profileDirectory = join(input.dshHome, 'profiles', input.profile);
156
+ await access(profileDirectory, constants.R_OK);
157
+ const currentPath = options.path ?? process.env.PATH;
158
+ const nodePath = options.nodePath ?? findExecutable('node', currentPath);
159
+ const dshPath = options.dshPath ?? findExecutable('dsh', currentPath);
160
+ const path = servicePath(nodePath, dshPath, currentPath);
161
+ const run = options.run ?? defaultRun;
162
+ const wait = options.wait ?? defaultWait;
163
+ await mkdir(dirname(paths.plistPath), { recursive: true, mode: 0o700 });
164
+ await mkdir(dirname(paths.stdoutPath), { recursive: true, mode: 0o700 });
165
+ await writePlist(paths.plistPath, createLaunchAgent({
166
+ ...paths,
167
+ dshHome: input.dshHome,
168
+ profile: input.profile,
169
+ profileDirectory,
170
+ nodePath,
171
+ dshPath,
172
+ path,
173
+ }), run);
174
+ const domain = `gui/${uid}`;
175
+ const target = `${domain}/${paths.label}`;
176
+ run('/bin/launchctl', ['bootout', target]);
177
+ const enable = run('/bin/launchctl', ['enable', target]);
178
+ if (enable.status !== 0)
179
+ throw commandError('launchd enable', enable);
180
+ const retryDelays = [250, 750];
181
+ let bootstrap = { status: null };
182
+ for (let attempt = 0; attempt <= retryDelays.length; attempt += 1) {
183
+ bootstrap = run('/bin/launchctl', ['bootstrap', domain, paths.plistPath]);
184
+ if (bootstrap.status !== 0 && !isBootstrapEio(bootstrap)) {
185
+ throw commandError('launchd bootstrap', bootstrap);
186
+ }
187
+ // `bootstrap` may return EIO after committing the job, or even return zero
188
+ // while an earlier bootout is still removing it. The job registry is the
189
+ // source of truth; require it to remain visible across a short settle window.
190
+ if (run('/bin/launchctl', ['print', target]).status === 0) {
191
+ await wait(250);
192
+ if (run('/bin/launchctl', ['print', target]).status === 0) {
193
+ return { ...paths, target };
194
+ }
195
+ continue;
196
+ }
197
+ const delayMs = retryDelays[attempt];
198
+ if (delayMs !== undefined)
199
+ await wait(delayMs);
200
+ }
201
+ if (bootstrap.status !== 0)
202
+ throw commandError('launchd bootstrap', bootstrap);
203
+ throw new Error('lark-channel setup: launchd bootstrap succeeded but the service was not registered');
204
+ }
205
+ //# sourceMappingURL=launchd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launchd.js","sourceRoot":"","sources":["../src/launchd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface LarkModelPickerActionPayload {
2
+ version: 3;
3
+ operationId: string;
4
+ bindingId: string;
5
+ expiresAt: number;
6
+ chatId: string;
7
+ provider: string;
8
+ model: string;
9
+ effort: string | null;
10
+ action: LarkModelPickerCallbackAction;
11
+ revision: number;
12
+ }
13
+ export type LarkModelPickerCallbackAction = 'confirm' | 'effort' | 'model' | 'provider';
14
+ export interface LarkModelPickerCallbackValue {
15
+ modelPicker: string;
16
+ }
17
+ export declare class LarkModelPickerError extends Error {
18
+ readonly code: 'expired' | 'invalid';
19
+ constructor(code: 'expired' | 'invalid', message: string);
20
+ }
21
+ export declare function parseLarkModelPickerCallback(value: unknown): LarkModelPickerCallbackValue;
22
+ export declare function signLarkModelPickerAction(secret: string, input: LarkModelPickerActionPayload): string;
23
+ export declare function verifyLarkModelPickerAction(secret: string, token: string, now: number): LarkModelPickerActionPayload;
24
+ //# sourceMappingURL=model-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-picker.d.ts","sourceRoot":"","sources":["../src/model-picker.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,CAAC,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,6BAA6B,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,6BAA6B,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAA;AAGvF,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS;gBAA3B,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM;CAIlE;AAyCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,4BAA4B,CAWzF;AAMD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,4BAA4B,GAAG,MAAM,CAIrG;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,4BAA4B,CAyB9B"}
@@ -0,0 +1,95 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ const callbackActions = ['confirm', 'effort', 'model', 'provider'];
3
+ export class LarkModelPickerError extends Error {
4
+ code;
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ this.name = 'LarkModelPickerError';
9
+ }
10
+ }
11
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._@:-]{0,255}$/u;
12
+ const providerPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/u;
13
+ const base64urlPattern = /^[A-Za-z0-9_-]+$/u;
14
+ function routePart(value, maxLength) {
15
+ return typeof value === 'string' && value.length >= 1 && value.length <= maxLength
16
+ && !/[\s\p{Cc}]/u.test(value);
17
+ }
18
+ function validateSecret(secret) {
19
+ if (typeof secret !== 'string' || Buffer.byteLength(secret, 'utf8') < 16) {
20
+ throw new LarkModelPickerError('invalid', 'model picker signing key is invalid');
21
+ }
22
+ }
23
+ function canonical(input) {
24
+ if (input === null || typeof input !== 'object' || Array.isArray(input)
25
+ || Object.keys(input).some(field => ![
26
+ 'version', 'operationId', 'bindingId', 'expiresAt', 'chatId', 'provider', 'model', 'effort', 'action', 'revision',
27
+ ].includes(field))
28
+ || Object.keys(input).length !== 10
29
+ || input.version !== 3
30
+ || typeof input.operationId !== 'string' || !identifierPattern.test(input.operationId)
31
+ || typeof input.bindingId !== 'string' || !identifierPattern.test(input.bindingId)
32
+ || typeof input.chatId !== 'string' || !identifierPattern.test(input.chatId)
33
+ || typeof input.provider !== 'string' || !providerPattern.test(input.provider)
34
+ || !routePart(input.model, 512)
35
+ || (input.effort !== null && !routePart(input.effort, 128))
36
+ || !callbackActions.includes(input.action)
37
+ || !Number.isSafeInteger(input.revision) || input.revision < 0
38
+ || !Number.isSafeInteger(input.expiresAt) || input.expiresAt < 1) {
39
+ throw new LarkModelPickerError('invalid', 'model picker action payload is invalid');
40
+ }
41
+ return { version: 3, operationId: input.operationId, bindingId: input.bindingId,
42
+ expiresAt: input.expiresAt, chatId: input.chatId,
43
+ provider: input.provider, model: input.model, effort: input.effort, action: input.action,
44
+ revision: input.revision };
45
+ }
46
+ export function parseLarkModelPickerCallback(value) {
47
+ if (value === null || typeof value !== 'object' || Array.isArray(value)
48
+ || Object.keys(value).length !== 1
49
+ || Object.keys(value)[0] !== 'modelPicker') {
50
+ throw new LarkModelPickerError('invalid', 'model picker callback value is invalid');
51
+ }
52
+ const callback = value;
53
+ if (typeof callback.modelPicker !== 'string') {
54
+ throw new LarkModelPickerError('invalid', 'model picker callback value is invalid');
55
+ }
56
+ return { modelPicker: callback.modelPicker };
57
+ }
58
+ function signature(secret, encoded) {
59
+ return createHmac('sha256', secret).update(encoded).digest();
60
+ }
61
+ export function signLarkModelPickerAction(secret, input) {
62
+ validateSecret(secret);
63
+ const encoded = Buffer.from(JSON.stringify(canonical(input)), 'utf8').toString('base64url');
64
+ return `${encoded}.${signature(secret, encoded).toString('base64url')}`;
65
+ }
66
+ export function verifyLarkModelPickerAction(secret, token, now) {
67
+ validateSecret(secret);
68
+ if (typeof token !== 'string' || token.length < 40 || token.length > 8_192 || !Number.isSafeInteger(now)) {
69
+ throw new LarkModelPickerError('invalid', 'model picker action token is invalid');
70
+ }
71
+ const parts = token.split('.');
72
+ if (parts.length !== 2 || !base64urlPattern.test(parts[0]) || !base64urlPattern.test(parts[1])) {
73
+ throw new LarkModelPickerError('invalid', 'model picker action token is invalid');
74
+ }
75
+ const supplied = Buffer.from(parts[1], 'base64url');
76
+ const decoded = Buffer.from(parts[0], 'base64url');
77
+ const expected = signature(secret, parts[0]);
78
+ if (supplied.toString('base64url') !== parts[1] || decoded.toString('base64url') !== parts[0]
79
+ || supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) {
80
+ throw new LarkModelPickerError('invalid', 'model picker action signature is invalid');
81
+ }
82
+ let payload;
83
+ try {
84
+ payload = canonical(JSON.parse(decoded.toString('utf8')));
85
+ }
86
+ catch (error) {
87
+ if (error instanceof LarkModelPickerError)
88
+ throw error;
89
+ throw new LarkModelPickerError('invalid', 'model picker action payload is invalid');
90
+ }
91
+ if (now >= payload.expiresAt)
92
+ throw new LarkModelPickerError('expired', 'model picker action expired');
93
+ return payload;
94
+ }
95
+ //# sourceMappingURL=model-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-picker.js","sourceRoot":"","sources":["../src/model-picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { InboundEnvelope } from '@dsh-enhanced/assistant-delivery';
2
+ import type { LarkInboundConfig, LarkMessage } from './types.js';
3
+ export type LarkIgnoreReason = 'bot-mention-required' | 'empty-content' | 'stale-event';
4
+ export type LarkNormalizeResult = {
5
+ outcome: 'accept';
6
+ envelope: InboundEnvelope;
7
+ } | {
8
+ outcome: 'ignore';
9
+ reason: LarkIgnoreReason;
10
+ };
11
+ export declare function normalizeLarkMessage(config: LarkInboundConfig, input: LarkMessage, now: number): LarkNormalizeResult;
12
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEhE,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,eAAe,GAAG,aAAa,CAAA;AAEvF,MAAM,MAAM,mBAAmB,GAC3B;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,GAChD;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAA;AA4BnD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,GACV,mBAAmB,CA+CrB"}
@@ -0,0 +1,77 @@
1
+ import { larkProviderReplyThread, larkTopLevelSenderThread } from './group-thread.js';
2
+ const keyPattern = /^[A-Za-z0-9][A-Za-z0-9._@/-]{0,255}$/u;
3
+ const providerCapabilityPattern = /^[A-Za-z0-9][A-Za-z0-9._@-]{0,255}$/u;
4
+ function key(value, field) {
5
+ const normalized = value.trim();
6
+ if (!keyPattern.test(normalized))
7
+ throw new Error(`lark-channel: ${field} is invalid`);
8
+ return normalized;
9
+ }
10
+ function providerCapability(value, field) {
11
+ const normalized = value.trim();
12
+ if (!providerCapabilityPattern.test(normalized))
13
+ throw new Error(`lark-channel: ${field} is invalid`);
14
+ return normalized;
15
+ }
16
+ function content(value, maxTextBytes) {
17
+ if (/\p{Cc}/u.test(value.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ''))) {
18
+ throw new Error('lark-channel: message contains a forbidden control character');
19
+ }
20
+ const normalized = value.replaceAll('\r\n', '\n').trim();
21
+ if (Buffer.byteLength(normalized, 'utf8') > maxTextBytes) {
22
+ throw new Error('lark-channel: message exceeds maxTextBytes');
23
+ }
24
+ return normalized;
25
+ }
26
+ export function normalizeLarkMessage(config, input, now) {
27
+ if (!Number.isSafeInteger(input.createTime) || input.createTime < 0) {
28
+ throw new Error('lark-channel: createTime is invalid');
29
+ }
30
+ if (now - input.createTime > config.staleAfterMs)
31
+ return { outcome: 'ignore', reason: 'stale-event' };
32
+ if (input.chatType === 'group' && config.requireMentionInGroups && !input.mentionedBot) {
33
+ return { outcome: 'ignore', reason: 'bot-mention-required' };
34
+ }
35
+ if (input.resources.length > 10)
36
+ throw new Error('lark-channel: message has too many attachment descriptors');
37
+ const normalizedText = content(input.content, config.maxTextBytes);
38
+ if (normalizedText.length === 0 && input.resources.length === 0)
39
+ return { outcome: 'ignore', reason: 'empty-content' };
40
+ const text = normalizedText;
41
+ const account = key(config.account, 'account');
42
+ const tenant = key(config.tenant, 'tenant');
43
+ const eventId = providerCapability(input.messageId, 'messageId');
44
+ const chat = key(input.chatId, 'chatId');
45
+ const user = key(input.senderId, 'senderId');
46
+ const principal = { channel: 'lark', account, tenant, user };
47
+ const conversation = input.chatType === 'p2p'
48
+ ? { channel: 'lark', account, tenant, kind: 'dm', chat }
49
+ : { channel: 'lark', account, tenant, kind: 'group', chat,
50
+ thread: input.rootId === undefined
51
+ ? larkTopLevelSenderThread(user)
52
+ : larkProviderReplyThread(key(input.rootId, 'rootId')) };
53
+ return {
54
+ outcome: 'accept',
55
+ envelope: {
56
+ channel: 'lark',
57
+ account,
58
+ eventId,
59
+ occurredAt: input.createTime,
60
+ principal,
61
+ conversation,
62
+ kind: text.startsWith('/') ? 'command' : 'text',
63
+ text,
64
+ metadata: {
65
+ providerMessageId: eventId,
66
+ rawContentType: key(input.rawContentType, 'rawContentType'),
67
+ resourceCount: String(Math.min(input.resources.length, 999)),
68
+ },
69
+ ...(input.resources.length === 0 ? {} : { attachments: input.resources.map(resource => ({
70
+ resourceType: resource.type,
71
+ providerRef: providerCapability(resource.fileKey, 'resource.fileKey'),
72
+ ...(resource.fileName === undefined ? {} : { fileName: resource.fileName }),
73
+ })) }),
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export type LarkPermissionLevel = 'ask' | 'auto' | 'full';
2
+ export interface LarkPermissionPickerActionPayload {
3
+ version: 2;
4
+ channel: string;
5
+ account: string;
6
+ tenant: string;
7
+ operationId: string;
8
+ bindingId: string;
9
+ bindingVersion: number;
10
+ sessionId: string;
11
+ chatId: string;
12
+ ownerUser: string;
13
+ issuedAt: number;
14
+ expiresAt: number;
15
+ expectedStateHash: string;
16
+ emergencyStopVersion: number;
17
+ level: LarkPermissionLevel;
18
+ }
19
+ export interface LarkPermissionPickerCallbackValue {
20
+ permissionPicker: string;
21
+ }
22
+ export declare class LarkPermissionPickerError extends Error {
23
+ readonly code: 'expired' | 'invalid';
24
+ constructor(code: 'expired' | 'invalid', message: string);
25
+ }
26
+ export declare function parseLarkPermissionPickerCallback(value: unknown): LarkPermissionPickerCallbackValue;
27
+ export declare function signLarkPermissionPickerAction(secret: string, input: LarkPermissionPickerActionPayload): string;
28
+ export declare function verifyLarkPermissionPickerAction(secret: string, token: string, now: number): LarkPermissionPickerActionPayload;
29
+ //# sourceMappingURL=permission-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-picker.d.ts","sourceRoot":"","sources":["../src/permission-picker.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;AAEzD,MAAM,WAAW,iCAAiC;IAChD,OAAO,EAAE,CAAC,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,KAAK,EAAE,mBAAmB,CAAA;CAC3B;AAED,MAAM,WAAW,iCAAiC;IAChD,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS;gBAA3B,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM;CAIlE;AAyFD,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,iCAAiC,CAanG;AAMD,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iCAAiC,GACvC,MAAM,CAMR;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,iCAAiC,CA2BnC"}
@@ -0,0 +1,145 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ export class LarkPermissionPickerError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = 'LarkPermissionPickerError';
8
+ }
9
+ }
10
+ const TOKEN_MIN_CHARS = 40;
11
+ const TOKEN_MAX_CHARS = 4_096;
12
+ const SIGNATURE_DOMAIN = 'dsh-lark-permission-picker:v2\0';
13
+ const payloadFields = [
14
+ 'version', 'channel', 'account', 'tenant', 'operationId', 'bindingId', 'bindingVersion',
15
+ 'sessionId', 'chatId', 'ownerUser', 'issuedAt', 'expiresAt', 'expectedStateHash',
16
+ 'emergencyStopVersion', 'level',
17
+ ];
18
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._@:-]{0,255}$/u;
19
+ const routeComponentPattern = /^[A-Za-z0-9][A-Za-z0-9._@/-]{0,255}$/u;
20
+ const stateHashPattern = /^[a-f0-9]{64}$/u;
21
+ const base64urlPattern = /^[A-Za-z0-9_-]+$/u;
22
+ function invalid(message) {
23
+ return new LarkPermissionPickerError('invalid', message);
24
+ }
25
+ function validateSecret(secret) {
26
+ if (typeof secret !== 'string' || Buffer.byteLength(secret, 'utf8') < 16) {
27
+ throw invalid('permission picker signing key is invalid');
28
+ }
29
+ }
30
+ function routeComponent(value) {
31
+ return typeof value === 'string' && routeComponentPattern.test(value);
32
+ }
33
+ function identifier(value) {
34
+ return typeof value === 'string' && identifierPattern.test(value);
35
+ }
36
+ function canonical(input) {
37
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
38
+ throw invalid('permission picker action payload is invalid');
39
+ }
40
+ const keys = Reflect.ownKeys(input);
41
+ if (keys.length !== payloadFields.length
42
+ || keys.some(field => typeof field !== 'string' || !payloadFields.includes(field))
43
+ || input.version !== 2
44
+ || !routeComponent(input.channel)
45
+ || !routeComponent(input.account)
46
+ || !routeComponent(input.tenant)
47
+ || !identifier(input.operationId)
48
+ || !identifier(input.bindingId)
49
+ || !Number.isSafeInteger(input.bindingVersion) || input.bindingVersion < 1
50
+ || !identifier(input.sessionId)
51
+ || !identifier(input.chatId)
52
+ || !identifier(input.ownerUser)
53
+ || !Number.isSafeInteger(input.issuedAt) || input.issuedAt < 1
54
+ || !Number.isSafeInteger(input.expiresAt) || input.expiresAt <= input.issuedAt
55
+ || typeof input.expectedStateHash !== 'string' || !stateHashPattern.test(input.expectedStateHash)
56
+ || !Number.isSafeInteger(input.emergencyStopVersion) || input.emergencyStopVersion < 0
57
+ || (input.level !== 'ask' && input.level !== 'auto' && input.level !== 'full')) {
58
+ throw invalid('permission picker action payload is invalid');
59
+ }
60
+ return {
61
+ version: 2,
62
+ channel: input.channel,
63
+ account: input.account,
64
+ tenant: input.tenant,
65
+ operationId: input.operationId,
66
+ bindingId: input.bindingId,
67
+ bindingVersion: input.bindingVersion,
68
+ sessionId: input.sessionId,
69
+ chatId: input.chatId,
70
+ ownerUser: input.ownerUser,
71
+ issuedAt: input.issuedAt,
72
+ expiresAt: input.expiresAt,
73
+ expectedStateHash: input.expectedStateHash,
74
+ emergencyStopVersion: input.emergencyStopVersion,
75
+ level: input.level,
76
+ };
77
+ }
78
+ function tokenParts(value) {
79
+ if (typeof value !== 'string' || value.length < TOKEN_MIN_CHARS || value.length > TOKEN_MAX_CHARS) {
80
+ return undefined;
81
+ }
82
+ const parts = value.split('.');
83
+ if (parts.length !== 2 || !base64urlPattern.test(parts[0]) || !base64urlPattern.test(parts[1])) {
84
+ return undefined;
85
+ }
86
+ return [parts[0], parts[1]];
87
+ }
88
+ export function parseLarkPermissionPickerCallback(value) {
89
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
90
+ throw invalid('permission picker callback value is invalid');
91
+ }
92
+ const keys = Reflect.ownKeys(value);
93
+ if (keys.length !== 1 || keys[0] !== 'permissionPicker') {
94
+ throw invalid('permission picker callback value is invalid');
95
+ }
96
+ const callback = value;
97
+ if (tokenParts(callback.permissionPicker) === undefined) {
98
+ throw invalid('permission picker callback value is invalid');
99
+ }
100
+ return { permissionPicker: callback.permissionPicker };
101
+ }
102
+ function signature(secret, encoded) {
103
+ return createHmac('sha256', secret).update(SIGNATURE_DOMAIN).update(encoded).digest();
104
+ }
105
+ export function signLarkPermissionPickerAction(secret, input) {
106
+ validateSecret(secret);
107
+ const encoded = Buffer.from(JSON.stringify(canonical(input)), 'utf8').toString('base64url');
108
+ const token = `${encoded}.${signature(secret, encoded).toString('base64url')}`;
109
+ if (token.length > TOKEN_MAX_CHARS)
110
+ throw invalid('permission picker action token is invalid');
111
+ return token;
112
+ }
113
+ export function verifyLarkPermissionPickerAction(secret, token, now) {
114
+ validateSecret(secret);
115
+ if (!Number.isSafeInteger(now))
116
+ throw invalid('permission picker action token is invalid');
117
+ const parts = tokenParts(token);
118
+ if (parts === undefined)
119
+ throw invalid('permission picker action token is invalid');
120
+ const supplied = Buffer.from(parts[1], 'base64url');
121
+ const decoded = Buffer.from(parts[0], 'base64url');
122
+ const expected = signature(secret, parts[0]);
123
+ if (supplied.toString('base64url') !== parts[1]
124
+ || decoded.toString('base64url') !== parts[0]
125
+ || supplied.length !== expected.length
126
+ || !timingSafeEqual(supplied, expected)) {
127
+ throw invalid('permission picker action signature is invalid');
128
+ }
129
+ let payload;
130
+ try {
131
+ payload = canonical(JSON.parse(decoded.toString('utf8')));
132
+ }
133
+ catch (error) {
134
+ if (error instanceof LarkPermissionPickerError)
135
+ throw error;
136
+ throw invalid('permission picker action payload is invalid');
137
+ }
138
+ const canonicalEncoded = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url');
139
+ if (parts[0] !== canonicalEncoded)
140
+ throw invalid('permission picker action payload is not canonical');
141
+ if (now >= payload.expiresAt)
142
+ throw new LarkPermissionPickerError('expired', 'permission picker action expired');
143
+ return payload;
144
+ }
145
+ //# sourceMappingURL=permission-picker.js.map