@wirecat/zoom-cli 0.55.0 → 0.57.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/README.md +4 -4
- package/dist/bin/zm.js +35 -5
- package/dist/bundle/all-profiles-EKHWTGOY.js +25 -0
- package/dist/bundle/answer-policy-KH2IFXPN.js +96 -0
- package/dist/bundle/anthropic-VL54IUGF.js +70 -0
- package/dist/bundle/bun-ADJVI4HY.js +169 -0
- package/dist/bundle/bun-sqlite-2SDA3YOT.js +69 -0
- package/dist/bundle/chunk-2IKY7VBH.js +9 -0
- package/dist/bundle/chunk-2WOGZDZG.js +431 -0
- package/dist/bundle/chunk-4R7HYNZS.js +73 -0
- package/dist/bundle/chunk-4VCA7QU3.js +538 -0
- package/dist/bundle/chunk-4YLS4YLS.js +29 -0
- package/dist/bundle/chunk-5D4AEUVA.js +24783 -0
- package/dist/bundle/chunk-5F7IOESV.js +281 -0
- package/dist/bundle/chunk-5QESMCQK.js +122 -0
- package/dist/bundle/chunk-6M6FJV5D.js +138 -0
- package/dist/bundle/chunk-6MX5T6AQ.js +17 -0
- package/dist/bundle/chunk-6P3YHT54.js +385 -0
- package/dist/bundle/chunk-7A6PZUDR.js +938 -0
- package/dist/bundle/chunk-7GUG6UZS.js +884 -0
- package/dist/bundle/chunk-7JZJMHQY.js +487 -0
- package/dist/bundle/chunk-A4ETXPMZ.js +98 -0
- package/dist/bundle/chunk-ADLS6FVG.js +24600 -0
- package/dist/bundle/chunk-AE4BOXH7.js +5292 -0
- package/dist/bundle/chunk-AN3GVZTU.js +76 -0
- package/dist/bundle/chunk-BAYNCCNM.js +215 -0
- package/dist/bundle/chunk-BW2WPOXP.js +166 -0
- package/dist/bundle/chunk-BW52AJ4R.js +3387 -0
- package/dist/bundle/chunk-CB5UTASG.js +18 -0
- package/dist/bundle/chunk-CLMXBW53.js +1028 -0
- package/dist/bundle/chunk-DCTXOMOR.js +35 -0
- package/dist/bundle/chunk-DFDN6AUB.js +21 -0
- package/dist/bundle/chunk-EOQFWJJD.js +1095 -0
- package/dist/bundle/chunk-G4R4ZIGD.js +3569 -0
- package/dist/bundle/chunk-G4VZDINN.js +161 -0
- package/dist/bundle/chunk-GARCOIB6.js +409 -0
- package/dist/bundle/chunk-GKMGS7G5.js +1938 -0
- package/dist/bundle/chunk-HCNMFFG3.js +82 -0
- package/dist/bundle/chunk-HG5KRV55.js +1970 -0
- package/dist/bundle/chunk-HKYLSIZ2.js +82 -0
- package/dist/bundle/chunk-HTQTIKWP.js +928 -0
- package/dist/bundle/chunk-I4VQDXFR.js +100 -0
- package/dist/bundle/chunk-ICSKEDPI.js +36139 -0
- package/dist/bundle/chunk-IP4OK7RQ.js +24 -0
- package/dist/bundle/chunk-IVQ6342B.js +2 -0
- package/dist/bundle/chunk-K72SEMYI.js +160 -0
- package/dist/bundle/chunk-LHRCJYFC.js +19 -0
- package/dist/bundle/chunk-LPTIQLDW.js +192 -0
- package/dist/bundle/chunk-LQ3T2ZIX.js +124 -0
- package/dist/bundle/chunk-LSVI3A5X.js +836 -0
- package/dist/bundle/chunk-LW2G4U6W.js +570 -0
- package/dist/bundle/chunk-LXB6ONWC.js +3517 -0
- package/dist/bundle/chunk-LY26NW3I.js +6848 -0
- package/dist/bundle/chunk-MAEFLDHG.js +6092 -0
- package/dist/bundle/chunk-MZH4GDR4.js +45 -0
- package/dist/bundle/chunk-NN5B24KP.js +1109 -0
- package/dist/bundle/chunk-NO7CJEYV.js +22 -0
- package/dist/bundle/chunk-NTDWAAGL.js +18435 -0
- package/dist/bundle/chunk-NW3X3G5X.js +264 -0
- package/dist/bundle/chunk-O66V2WHK.js +48 -0
- package/dist/bundle/chunk-OCGPZHXW.js +25 -0
- package/dist/bundle/chunk-OIHDCGV4.js +151 -0
- package/dist/bundle/chunk-OVSWVPXL.js +8190 -0
- package/dist/bundle/chunk-PIN5RZ3D.js +63 -0
- package/dist/bundle/chunk-QLIB3PEC.js +1146 -0
- package/dist/bundle/chunk-QTR6D5HT.js +58 -0
- package/dist/bundle/chunk-QUJPQMHJ.js +2315 -0
- package/dist/bundle/chunk-R3HPOCZD.js +188 -0
- package/dist/bundle/chunk-RMV4C2YL.js +482 -0
- package/dist/bundle/chunk-RRRBMQ2E.js +844 -0
- package/dist/bundle/chunk-S4S7E54J.js +60 -0
- package/dist/bundle/chunk-SB6WOSHG.js +1109 -0
- package/dist/bundle/chunk-SE75DPBY.js +31 -0
- package/dist/bundle/chunk-SQJ7EH2D.js +97 -0
- package/dist/bundle/chunk-TKCHUT2H.js +422 -0
- package/dist/bundle/chunk-TLQUKHEM.js +193 -0
- package/dist/bundle/chunk-TZ2MKETI.js +100 -0
- package/dist/bundle/chunk-UC44ZMQV.js +78 -0
- package/dist/bundle/chunk-UI5UTMSZ.js +275 -0
- package/dist/bundle/chunk-VPPYYWOH.js +630 -0
- package/dist/bundle/chunk-WJHK5D7Z.js +35612 -0
- package/dist/bundle/chunk-WQ24UC44.js +239 -0
- package/dist/bundle/chunk-X5DFDGWS.js +2 -0
- package/dist/bundle/chunk-Y5E2WNWB.js +35 -0
- package/dist/bundle/chunk-YB4DMKYX.js +119 -0
- package/dist/bundle/chunk-Z4TZ6TAF.js +288 -0
- package/dist/bundle/chunk-ZVVMGPBJ.js +50 -0
- package/dist/bundle/cli-72EQATJF.js +825 -0
- package/dist/bundle/command-contract-4MHSZUN3.js +38 -0
- package/dist/bundle/config-GFIJK6G2.js +79 -0
- package/dist/bundle/dist-IBNHCQXT.js +136 -0
- package/dist/bundle/dist-XUFQCJXW.js +7577 -0
- package/dist/bundle/echarts-RFTK737T.js +42501 -0
- package/dist/bundle/embed-7QYA2ZZE.js +35 -0
- package/dist/bundle/embedder-YNN6PCHG.js +108 -0
- package/dist/bundle/first-run-EVSRQKZJ.js +23 -0
- package/dist/bundle/first-screen-RG4GUKW7.js +36 -0
- package/dist/bundle/folders-JEPOMYM4.js +13 -0
- package/dist/bundle/instructions-FHNL7CMW.js +9 -0
- package/dist/bundle/main-NYGJXPH7.js +130 -0
- package/dist/bundle/mcp-IXFC2VVR.js +165 -0
- package/dist/bundle/mcp-VGSZDCKK.js +189 -0
- package/dist/bundle/mcp-XKP2PXLS.js +92 -0
- package/dist/bundle/meetings-IBPI4MM4.js +215 -0
- package/dist/bundle/node-P4E63KXA.js +2340 -0
- package/dist/bundle/node-TLZSIHIJ.js +21 -0
- package/dist/bundle/node-ZMCACJVS.js +176 -0
- package/dist/bundle/node-sqlite-ZNGOBA5Q.js +45 -0
- package/dist/bundle/oauth-Q6LKF7DB.js +17 -0
- package/dist/bundle/open-LJ332Y5O.js +19 -0
- package/dist/bundle/output-file-RYQZ3RTE.js +63 -0
- package/dist/bundle/process-ZPTLNVQS.js +91 -0
- package/dist/bundle/program-IBRQBQ6W.js +5845 -0
- package/dist/bundle/prompt-RT7GUUXH.js +12 -0
- package/dist/bundle/recognize-QXGE7YJH.js +190 -0
- package/dist/bundle/remote-FD4MZQV3.js +26 -0
- package/dist/bundle/rerank-V6AVV755.js +49 -0
- package/dist/bundle/run-log-RE2EI3UU.js +111 -0
- package/dist/bundle/serve-HIJTFAW7.js +19 -0
- package/dist/bundle/server-3QOXOYYL.js +53 -0
- package/dist/bundle/server-WLJCU7KK.js +812 -0
- package/dist/bundle/services-KC5KQFBS.js +330 -0
- package/dist/bundle/speech-5JOXLYDA.js +44 -0
- package/dist/bundle/state-PPGRB3T6.js +33 -0
- package/dist/bundle/store-LODVDPK3.js +141 -0
- package/dist/bundle/store-WB5BICTG.js +124 -0
- package/dist/bundle/store-connection-EG6Z5ITP.js +76 -0
- package/dist/bundle/store-maintenance-command-Q5WMFUZ2.js +54 -0
- package/dist/bundle/streaming-UGFFNOXL.js +15 -0
- package/dist/bundle/tokens-AF5CPY2F.js +17 -0
- package/dist/bundle/zm.js +8 -0
- package/dist/commands.js +2 -2
- package/dist/commands.js.map +1 -1
- package/dist/config-issues.d.ts.map +1 -1
- package/dist/config-issues.js +3 -0
- package/dist/config-issues.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -1
- package/dist/contract-overrides.d.ts.map +1 -1
- package/dist/contract-overrides.js +34 -45
- package/dist/contract-overrides.js.map +1 -1
- package/dist/first-screen.js +1 -1
- package/dist/first-screen.js.map +1 -1
- package/dist/mcp-command.d.ts +5 -3
- package/dist/mcp-command.d.ts.map +1 -1
- package/dist/mcp-command.js +73 -15
- package/dist/mcp-command.js.map +1 -1
- package/dist/program-bindings.d.ts.map +1 -1
- package/dist/program-bindings.js +1 -0
- package/dist/program-bindings.js.map +1 -1
- package/dist/recording-archive.d.ts.map +1 -1
- package/dist/recording-archive.js +9 -4
- package/dist/recording-archive.js.map +1 -1
- package/dist/remote-http.d.ts +2 -0
- package/dist/remote-http.d.ts.map +1 -1
- package/dist/remote-http.js +21 -11
- package/dist/remote-http.js.map +1 -1
- package/dist/request-events.d.ts +12 -0
- package/dist/request-events.d.ts.map +1 -1
- package/dist/request-events.js +56 -4
- package/dist/request-events.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +4 -1
- package/dist/run.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -5
- package/skills/zoom-cli/SKILL.md +3 -1
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
import { createRequire as __bundleRequire } from "node:module";
|
|
2
|
+
const require = __bundleRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
profileAccountKey,
|
|
5
|
+
profilePattern
|
|
6
|
+
} from "./chunk-6P3YHT54.js";
|
|
7
|
+
import {
|
|
8
|
+
checkCancelled
|
|
9
|
+
} from "./chunk-A4ETXPMZ.js";
|
|
10
|
+
import {
|
|
11
|
+
createMeetingSearchCommand,
|
|
12
|
+
meetingHitSearchRequest,
|
|
13
|
+
meetingSearchArguments,
|
|
14
|
+
meetingSearchMeta
|
|
15
|
+
} from "./chunk-QLIB3PEC.js";
|
|
16
|
+
import {
|
|
17
|
+
openOrdinaryFile
|
|
18
|
+
} from "./chunk-ICSKEDPI.js";
|
|
19
|
+
import {
|
|
20
|
+
Command,
|
|
21
|
+
FETCH,
|
|
22
|
+
LOCAL_READ,
|
|
23
|
+
NO_EFFECTS,
|
|
24
|
+
annotate,
|
|
25
|
+
answerSchema,
|
|
26
|
+
itemOf,
|
|
27
|
+
listOf,
|
|
28
|
+
metaNow,
|
|
29
|
+
metaOf,
|
|
30
|
+
overrideSharedContract
|
|
31
|
+
} from "./chunk-MAEFLDHG.js";
|
|
32
|
+
import {
|
|
33
|
+
parseDuration
|
|
34
|
+
} from "./chunk-EOQFWJJD.js";
|
|
35
|
+
import {
|
|
36
|
+
canonicalMeetingReference,
|
|
37
|
+
meetingHitsList,
|
|
38
|
+
parseMeetingReference,
|
|
39
|
+
validateMeetingContextChange,
|
|
40
|
+
validateMeetingHitSearch
|
|
41
|
+
} from "./chunk-LXB6ONWC.js";
|
|
42
|
+
import {
|
|
43
|
+
CliError,
|
|
44
|
+
createOutputBuffer,
|
|
45
|
+
errorObjectSchema,
|
|
46
|
+
isCliError,
|
|
47
|
+
jsonSchemaOf,
|
|
48
|
+
union
|
|
49
|
+
} from "./chunk-AE4BOXH7.js";
|
|
50
|
+
import {
|
|
51
|
+
__name
|
|
52
|
+
} from "./chunk-ZVVMGPBJ.js";
|
|
53
|
+
|
|
54
|
+
// dist/local-transcription.js
|
|
55
|
+
import { isAbsolute } from "node:path";
|
|
56
|
+
|
|
57
|
+
// node_modules/.pnpm/@wirecat+cli-core@0.469.0_@wirecat+cli-base@0.65.0_commander@15.0.0_typescript@7.0.2_vi_80b586dbc8b099637a6e461856c2ee6b/node_modules/@wirecat/cli-core/dist/speech/wav.js
|
|
58
|
+
import { createHash } from "node:crypto";
|
|
59
|
+
import { basename, resolve } from "node:path";
|
|
60
|
+
var prepared = /* @__PURE__ */ new WeakMap();
|
|
61
|
+
var checkCancelled2 = /* @__PURE__ */ __name((signal) => {
|
|
62
|
+
if (!signal?.aborted)
|
|
63
|
+
return;
|
|
64
|
+
if (signal.reason instanceof CliError)
|
|
65
|
+
throw signal.reason;
|
|
66
|
+
const timedOut = signal.reason instanceof Error && signal.reason.name === "TimeoutError";
|
|
67
|
+
throw new CliError(timedOut ? "timeout" : "cancelled", timedOut ? "command timed out" : "command cancelled");
|
|
68
|
+
}, "checkCancelled");
|
|
69
|
+
var invalid = /* @__PURE__ */ __name(() => {
|
|
70
|
+
throw new CliError("validation_error", "local transcription requires PCM16 mono/stereo WAV at 16 or 48 kHz; convert the media locally to PCM WAV first");
|
|
71
|
+
}, "invalid");
|
|
72
|
+
var validate = /* @__PURE__ */ __name((options) => {
|
|
73
|
+
if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1 || options.maxBytes > 4 * 1024 ** 3 || !Number.isSafeInteger(options.maxDurationMs) || options.maxDurationMs < 1 || options.maxDurationMs > 24 * 36e5)
|
|
74
|
+
throw new CliError("validation_error", "audio byte and duration budgets must be positive bounded integers");
|
|
75
|
+
checkCancelled2(options.signal);
|
|
76
|
+
}, "validate");
|
|
77
|
+
var exact = /* @__PURE__ */ __name(async (file, offset, size, signal) => {
|
|
78
|
+
const bytes2 = Buffer.alloc(size);
|
|
79
|
+
let read = 0;
|
|
80
|
+
while (read < size) {
|
|
81
|
+
checkCancelled2(signal);
|
|
82
|
+
const result = await file.read(bytes2, read, size - read, offset + read);
|
|
83
|
+
if (!result.bytesRead)
|
|
84
|
+
throw new CliError("validation_error", "WAV changed or was truncated while reading");
|
|
85
|
+
read += result.bytesRead;
|
|
86
|
+
}
|
|
87
|
+
return bytes2;
|
|
88
|
+
}, "exact");
|
|
89
|
+
var layoutOf = /* @__PURE__ */ __name(async (file, size, options) => {
|
|
90
|
+
if (size < 44 || size > options.maxBytes)
|
|
91
|
+
invalid();
|
|
92
|
+
const header = await exact(file, 0, 12, options.signal);
|
|
93
|
+
if (header.toString("ascii", 0, 4) !== "RIFF" || header.toString("ascii", 8, 12) !== "WAVE" || header.readUInt32LE(4) + 8 !== size)
|
|
94
|
+
invalid();
|
|
95
|
+
let format;
|
|
96
|
+
let data;
|
|
97
|
+
let at = 12;
|
|
98
|
+
let chunks = 0;
|
|
99
|
+
while (at < size) {
|
|
100
|
+
checkCancelled2(options.signal);
|
|
101
|
+
if (++chunks > 1024 || size - at < 8)
|
|
102
|
+
invalid();
|
|
103
|
+
const chunk = await exact(file, at, 8, options.signal);
|
|
104
|
+
const kind = chunk.toString("ascii", 0, 4);
|
|
105
|
+
const length = chunk.readUInt32LE(4);
|
|
106
|
+
const start = at + 8;
|
|
107
|
+
const next = start + length + length % 2;
|
|
108
|
+
if (next > size)
|
|
109
|
+
invalid();
|
|
110
|
+
if (kind === "fmt ") {
|
|
111
|
+
if (format || ![16, 18].includes(length))
|
|
112
|
+
invalid();
|
|
113
|
+
const bytes2 = await exact(file, start, length, options.signal);
|
|
114
|
+
const channels = bytes2.readUInt16LE(2);
|
|
115
|
+
const sampleRate = bytes2.readUInt32LE(4);
|
|
116
|
+
if (bytes2.readUInt16LE(0) !== 1 || ![1, 2].includes(channels) || ![16e3, 48e3].includes(sampleRate) || bytes2.readUInt16LE(14) !== 16 || bytes2.readUInt16LE(12) !== channels * 2 || bytes2.readUInt32LE(8) !== sampleRate * channels * 2 || length === 18 && bytes2.readUInt16LE(16) !== 0)
|
|
117
|
+
invalid();
|
|
118
|
+
format = { sampleRate, channels };
|
|
119
|
+
} else if (kind === "data") {
|
|
120
|
+
if (data || !length)
|
|
121
|
+
invalid();
|
|
122
|
+
data = { offset: start, size: length };
|
|
123
|
+
}
|
|
124
|
+
at = next;
|
|
125
|
+
}
|
|
126
|
+
if (!format || !data || data.size % (format.channels * 2) !== 0)
|
|
127
|
+
return invalid();
|
|
128
|
+
const frames = data.size / (format.channels * 2);
|
|
129
|
+
if (Math.ceil(frames * 1e3 / format.sampleRate) > options.maxDurationMs)
|
|
130
|
+
throw new CliError("validation_error", "WAV exceeds the configured duration limit");
|
|
131
|
+
return { ...format, dataOffset: data.offset, dataSize: data.size, frames };
|
|
132
|
+
}, "layoutOf");
|
|
133
|
+
var hashRange = /* @__PURE__ */ __name(async (file, hash, start, end, signal) => {
|
|
134
|
+
let at = start;
|
|
135
|
+
while (at < end) {
|
|
136
|
+
const bytes2 = await exact(file, at, Math.min(65536, end - at), signal);
|
|
137
|
+
hash.update(bytes2);
|
|
138
|
+
at += bytes2.length;
|
|
139
|
+
}
|
|
140
|
+
}, "hashRange");
|
|
141
|
+
var hashFile = /* @__PURE__ */ __name(async (file, size, signal) => {
|
|
142
|
+
const hash = createHash("sha256");
|
|
143
|
+
await hashRange(file, hash, 0, size, signal);
|
|
144
|
+
if ((await file.stat()).size !== size)
|
|
145
|
+
throw new CliError("validation_error", "WAV changed while verifying its content");
|
|
146
|
+
return hash.digest("hex");
|
|
147
|
+
}, "hashFile");
|
|
148
|
+
var preflightWavAudio = /* @__PURE__ */ __name(async (path, input) => {
|
|
149
|
+
const options = { ...input };
|
|
150
|
+
validate(options);
|
|
151
|
+
if (typeof path !== "string" || !path.trim() || /\0/.test(path))
|
|
152
|
+
throw new CliError("validation_error", "local audio file is required");
|
|
153
|
+
const absolute = resolve(path);
|
|
154
|
+
const file = await openOrdinaryFile(absolute);
|
|
155
|
+
try {
|
|
156
|
+
const stat = await file.stat({ bigint: true });
|
|
157
|
+
const size = Number(stat.size);
|
|
158
|
+
const layout = await layoutOf(file, size, options);
|
|
159
|
+
const contentHash = await hashFile(file, size, options.signal);
|
|
160
|
+
const after = await file.stat({ bigint: true });
|
|
161
|
+
if (stat.size !== after.size || stat.mtimeNs !== after.mtimeNs)
|
|
162
|
+
throw new CliError("validation_error", "WAV changed while preparing the transcription");
|
|
163
|
+
const media = Object.freeze({
|
|
164
|
+
id: createHash("sha256").update(basename(absolute)).digest("hex"),
|
|
165
|
+
contentHash,
|
|
166
|
+
size,
|
|
167
|
+
durationMs: Math.ceil(layout.frames * 1e3 / layout.sampleRate),
|
|
168
|
+
format: "wav",
|
|
169
|
+
sampleRate: layout.sampleRate,
|
|
170
|
+
channels: layout.channels
|
|
171
|
+
});
|
|
172
|
+
const plan = Object.freeze({ media });
|
|
173
|
+
prepared.set(plan, { path: absolute, dev: stat.dev, ino: stat.ino, layout });
|
|
174
|
+
return plan;
|
|
175
|
+
} finally {
|
|
176
|
+
await file.close();
|
|
177
|
+
}
|
|
178
|
+
}, "preflightWavAudio");
|
|
179
|
+
var openWavAudio = /* @__PURE__ */ __name(async (plan, input) => {
|
|
180
|
+
const options = { ...input };
|
|
181
|
+
validate(options);
|
|
182
|
+
const known = prepared.get(plan);
|
|
183
|
+
if (!known)
|
|
184
|
+
throw new CliError("validation_error", "audio plan was not prepared by this adapter");
|
|
185
|
+
const file = await openOrdinaryFile(known.path);
|
|
186
|
+
try {
|
|
187
|
+
const stat = await file.stat({ bigint: true });
|
|
188
|
+
if (stat.dev !== known.dev || stat.ino !== known.ino || Number(stat.size) !== plan.media.size)
|
|
189
|
+
throw new CliError("validation_error", "WAV identity changed before transcription");
|
|
190
|
+
const layout = await layoutOf(file, plan.media.size, options);
|
|
191
|
+
if (JSON.stringify(layout) !== JSON.stringify(known.layout) || await hashFile(file, plan.media.size, options.signal) !== plan.media.contentHash)
|
|
192
|
+
throw new CliError("validation_error", "WAV content changed before transcription");
|
|
193
|
+
let closed = false;
|
|
194
|
+
let consumed = false;
|
|
195
|
+
return {
|
|
196
|
+
async *windows() {
|
|
197
|
+
if (closed || consumed)
|
|
198
|
+
throw new CliError("configuration_error", "audio source can be consumed once before closing");
|
|
199
|
+
consumed = true;
|
|
200
|
+
const hash = createHash("sha256");
|
|
201
|
+
await hashRange(file, hash, 0, layout.dataOffset, options.signal);
|
|
202
|
+
const step = layout.sampleRate / 16e3;
|
|
203
|
+
for (let frame = 0; frame < layout.frames; ) {
|
|
204
|
+
checkCancelled2(options.signal);
|
|
205
|
+
const count = Math.min(layout.sampleRate * 30, layout.frames - frame);
|
|
206
|
+
const bytes2 = await exact(file, layout.dataOffset + frame * layout.channels * 2, count * layout.channels * 2, options.signal);
|
|
207
|
+
hash.update(bytes2);
|
|
208
|
+
const samples = new Float32Array(Math.floor(count / step));
|
|
209
|
+
for (let index = 0; index < samples.length; index++) {
|
|
210
|
+
let sum = 0;
|
|
211
|
+
for (let offset = 0; offset < step; offset++)
|
|
212
|
+
for (let channel = 0; channel < layout.channels; channel++)
|
|
213
|
+
sum += bytes2.readInt16LE(((index * step + offset) * layout.channels + channel) * 2);
|
|
214
|
+
samples[index] = sum / (32768 * step * layout.channels);
|
|
215
|
+
}
|
|
216
|
+
const startMs = Math.floor(frame * 1e3 / layout.sampleRate);
|
|
217
|
+
frame += count;
|
|
218
|
+
if (samples.length)
|
|
219
|
+
yield { startMs, endMs: Math.ceil(frame * 1e3 / layout.sampleRate), samples };
|
|
220
|
+
}
|
|
221
|
+
await hashRange(file, hash, layout.dataOffset + layout.dataSize, plan.media.size, options.signal);
|
|
222
|
+
if (hash.digest("hex") !== plan.media.contentHash || (await file.stat()).size !== plan.media.size)
|
|
223
|
+
throw new CliError("validation_error", "WAV changed during recognition; no transcript may be stored");
|
|
224
|
+
},
|
|
225
|
+
async close() {
|
|
226
|
+
if (!closed) {
|
|
227
|
+
closed = true;
|
|
228
|
+
await file.close();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
} catch (error) {
|
|
233
|
+
await file.close();
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}, "openWavAudio");
|
|
237
|
+
|
|
238
|
+
// dist/media-decode.js
|
|
239
|
+
import { execFile } from "node:child_process";
|
|
240
|
+
import { mkdtemp, open, rm } from "node:fs/promises";
|
|
241
|
+
import { tmpdir } from "node:os";
|
|
242
|
+
import { join } from "node:path";
|
|
243
|
+
var readyWav = /* @__PURE__ */ __name(async (file) => {
|
|
244
|
+
const handle = await open(file, "r").catch(() => {
|
|
245
|
+
throw new CliError("not_found", "the audio or video file could not be opened");
|
|
246
|
+
});
|
|
247
|
+
try {
|
|
248
|
+
const head = Buffer.alloc(4096);
|
|
249
|
+
const { bytesRead } = await handle.read(head, 0, head.length, 0);
|
|
250
|
+
if (bytesRead < 12 || head.toString("ascii", 0, 4) !== "RIFF" || head.toString("ascii", 8, 12) !== "WAVE")
|
|
251
|
+
return false;
|
|
252
|
+
for (let at = 12; at + 8 <= bytesRead; at += 8 + head.readUInt32LE(at + 4) + head.readUInt32LE(at + 4) % 2) {
|
|
253
|
+
if (head.toString("ascii", at, at + 4) !== "fmt " || at + 24 > bytesRead)
|
|
254
|
+
continue;
|
|
255
|
+
const format = head.readUInt16LE(at + 8);
|
|
256
|
+
const channels = head.readUInt16LE(at + 10);
|
|
257
|
+
const rate = head.readUInt32LE(at + 12);
|
|
258
|
+
const bits = head.readUInt16LE(at + 22);
|
|
259
|
+
return format === 1 && bits === 16 && (channels === 1 || channels === 2) && (rate === 16e3 || rate === 48e3);
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
} finally {
|
|
263
|
+
await handle.close();
|
|
264
|
+
}
|
|
265
|
+
}, "readyWav");
|
|
266
|
+
var wavFor = /* @__PURE__ */ __name(async (file, options = {}) => {
|
|
267
|
+
if (await readyWav(file))
|
|
268
|
+
return { path: file, decoded: false, release: /* @__PURE__ */ __name(async () => {
|
|
269
|
+
}, "release") };
|
|
270
|
+
const directory = await mkdtemp(join(tmpdir(), "zm-decode-"));
|
|
271
|
+
const release = /* @__PURE__ */ __name(() => rm(directory, { recursive: true, force: true }), "release");
|
|
272
|
+
const path = join(directory, "audio.wav");
|
|
273
|
+
const args = ["-nostdin", "-hide_banner", "-loglevel", "error", "-i", file, "-vn", "-ac", "1", "-ar", "16000"];
|
|
274
|
+
args.push("-c:a", "pcm_s16le", "-fflags", "+bitexact", "-flags:a", "+bitexact", "-map_metadata", "-1", path);
|
|
275
|
+
try {
|
|
276
|
+
await new Promise((resolve2, reject) => execFile(options.ffmpeg ?? "ffmpeg", args, { signal: options.signal, maxBuffer: 1024 * 1024 }, (error, _out, err) => {
|
|
277
|
+
if (!error)
|
|
278
|
+
return resolve2();
|
|
279
|
+
if (error.code === "ENOENT")
|
|
280
|
+
return reject(new CliError("configuration_error", "decoding a recording that is not WAV needs ffmpeg on PATH; zm does not install it"));
|
|
281
|
+
if (options.signal?.aborted)
|
|
282
|
+
return reject(new CliError("cancelled", "decoding was cancelled"));
|
|
283
|
+
reject(new CliError("validation_error", `ffmpeg could not decode the file: ${String(err).trim().slice(0, 300)}`));
|
|
284
|
+
}));
|
|
285
|
+
return { path, decoded: true, release };
|
|
286
|
+
} catch (error) {
|
|
287
|
+
await release();
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}, "wavFor");
|
|
291
|
+
|
|
292
|
+
// dist/local-transcription.js
|
|
293
|
+
var validateLocalTranscriptionOptions = /* @__PURE__ */ __name((input) => {
|
|
294
|
+
const options = { ...input };
|
|
295
|
+
if (typeof options.model !== "string" || !options.model.trim() || options.model.length > 200 || /[\0\r\n]/.test(options.model))
|
|
296
|
+
throw new CliError("validation_error", "local transcription requires an explicit speech model");
|
|
297
|
+
if (options.modelsDirectory !== void 0 && (typeof options.modelsDirectory !== "string" || !isAbsolute(options.modelsDirectory) || options.modelsDirectory.length > 4096 || /[\0\r\n]/.test(options.modelsDirectory)))
|
|
298
|
+
throw new CliError("validation_error", "speech model directory must be an absolute bounded path");
|
|
299
|
+
for (const [value, max, label] of [
|
|
300
|
+
[options.maxBytes, 4 * 1024 ** 3, "media bytes"],
|
|
301
|
+
[options.maxDurationMs, 24 * 36e5, "media duration"],
|
|
302
|
+
[options.maxSegments, 1e5, "segment count"],
|
|
303
|
+
[options.maxTextBytes, 64 * 1024 ** 2, "transcript text bytes"]
|
|
304
|
+
])
|
|
305
|
+
if (value !== void 0 && (!Number.isSafeInteger(value) || value < 1 || value > max))
|
|
306
|
+
throw new CliError("validation_error", `invalid ${label} budget`);
|
|
307
|
+
for (const value of [options.apply, options.dryRun])
|
|
308
|
+
if (value !== void 0 && typeof value !== "boolean")
|
|
309
|
+
throw new CliError("validation_error", "transcription apply and dry-run options must be booleans");
|
|
310
|
+
return options;
|
|
311
|
+
}, "validateLocalTranscriptionOptions");
|
|
312
|
+
var compactLocalTranscriptionReceipt = /* @__PURE__ */ __name((receipt) => {
|
|
313
|
+
const append = receipt.append;
|
|
314
|
+
if (!receipt.applied || !append || !Number.isSafeInteger(append.meeting.meeting.id) || append.meeting.meeting.id < 1)
|
|
315
|
+
throw new CliError("outcome_unknown", "transcription did not return a usable committed acknowledgement");
|
|
316
|
+
return {
|
|
317
|
+
applied: true,
|
|
318
|
+
accountId: receipt.accountId,
|
|
319
|
+
externalId: receipt.externalId,
|
|
320
|
+
meetingId: append.meeting.meeting.id,
|
|
321
|
+
created: append.created,
|
|
322
|
+
insertedTranscripts: append.insertedTranscripts,
|
|
323
|
+
replayedTranscripts: append.replayedTranscripts,
|
|
324
|
+
supersededTranscripts: append.supersededTranscripts,
|
|
325
|
+
segments: receipt.segments,
|
|
326
|
+
mediaSha256: receipt.media.contentHash,
|
|
327
|
+
engine: receipt.media.recognition.engine,
|
|
328
|
+
model: receipt.media.recognition.model,
|
|
329
|
+
modelVersion: receipt.media.recognition.version ?? null,
|
|
330
|
+
timing: receipt.media.recognition.timing ?? "recognition-segments"
|
|
331
|
+
};
|
|
332
|
+
}, "compactLocalTranscriptionReceipt");
|
|
333
|
+
var engineFailure = /* @__PURE__ */ __name(async (model, work) => {
|
|
334
|
+
try {
|
|
335
|
+
return await work();
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (error instanceof Error)
|
|
338
|
+
throw error;
|
|
339
|
+
throw new CliError("configuration_error", `the speech engine could not run ${model}; \`zm models download ${model}\` checks its files and repairs them`);
|
|
340
|
+
}
|
|
341
|
+
}, "engineFailure");
|
|
342
|
+
var loadSpeech = /* @__PURE__ */ __name(async () => {
|
|
343
|
+
const speech = await import("./speech-5JOXLYDA.js");
|
|
344
|
+
return { describe: speech.localSpeechRecognitionModel, open: speech.openLocalSpeechRecognizer };
|
|
345
|
+
}, "loadSpeech");
|
|
346
|
+
var createWavTranscriptionSource = /* @__PURE__ */ __name((file, options) => {
|
|
347
|
+
const selected = { ...options, ...options.env ? { env: { ...options.env } } : {} };
|
|
348
|
+
let plan;
|
|
349
|
+
let recognition;
|
|
350
|
+
let speech;
|
|
351
|
+
return {
|
|
352
|
+
async describe(context) {
|
|
353
|
+
checkCancelled(context.signal);
|
|
354
|
+
speech = await (selected.speech ?? loadSpeech)();
|
|
355
|
+
recognition = Object.freeze({ ...speech.describe(selected.model) });
|
|
356
|
+
plan = await preflightWavAudio(file, context);
|
|
357
|
+
return Object.freeze({
|
|
358
|
+
...plan.media,
|
|
359
|
+
recognition: Object.freeze({
|
|
360
|
+
engine: recognition.engine,
|
|
361
|
+
model: recognition.model,
|
|
362
|
+
version: recognition.version,
|
|
363
|
+
timing: "fixed-windows"
|
|
364
|
+
})
|
|
365
|
+
});
|
|
366
|
+
},
|
|
367
|
+
async *segments(media, context) {
|
|
368
|
+
if (!plan || !recognition || !speech || media.contentHash !== plan.media.contentHash || media.id !== plan.media.id || media.size !== plan.media.size || media.durationMs !== plan.media.durationMs || media.recognition.engine !== recognition.engine || media.recognition.model !== recognition.model || media.recognition.version !== recognition.version)
|
|
369
|
+
throw new CliError("validation_error", "transcription media descriptor does not match its prepared audio and model");
|
|
370
|
+
const audio = await openWavAudio(plan, context);
|
|
371
|
+
let model;
|
|
372
|
+
try {
|
|
373
|
+
checkCancelled(context.signal);
|
|
374
|
+
const port = speech;
|
|
375
|
+
const chosen = recognition.model;
|
|
376
|
+
model = await engineFailure(chosen, () => port.open(chosen, {
|
|
377
|
+
directory: selected.modelsDirectory,
|
|
378
|
+
env: selected.env,
|
|
379
|
+
signal: context.signal
|
|
380
|
+
}));
|
|
381
|
+
if (model.engine !== recognition.engine || model.model !== recognition.model || model.version !== recognition.version)
|
|
382
|
+
throw new CliError("validation_error", "installed speech recognizer changed after the media preview");
|
|
383
|
+
for await (const window of audio.windows()) {
|
|
384
|
+
checkCancelled(context.signal);
|
|
385
|
+
const recognizer = model;
|
|
386
|
+
const text = await engineFailure(recognition.model, () => recognizer.recognize(window.samples, { signal: context.signal }));
|
|
387
|
+
checkCancelled(context.signal);
|
|
388
|
+
yield { startMs: window.startMs, endMs: window.endMs, text };
|
|
389
|
+
}
|
|
390
|
+
} finally {
|
|
391
|
+
try {
|
|
392
|
+
await model?.close();
|
|
393
|
+
} finally {
|
|
394
|
+
await audio.close();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}, "createWavTranscriptionSource");
|
|
400
|
+
var createLocalTranscriptionCapabilities = /* @__PURE__ */ __name((store, input = {}) => {
|
|
401
|
+
const accountId = store.accountId;
|
|
402
|
+
if (!Number.isSafeInteger(accountId) || accountId < 1)
|
|
403
|
+
throw new CliError("validation_error", "local transcription requires a selected existing account");
|
|
404
|
+
const options = { ...input, ...input.env ? { env: { ...input.env } } : {} };
|
|
405
|
+
const resolveMeeting = store.resolveMeeting.bind(store);
|
|
406
|
+
const appendTranscriptsWithReceipt = store.appendTranscriptsWithReceipt.bind(store);
|
|
407
|
+
return {
|
|
408
|
+
async transcribe(reference, file, raw) {
|
|
409
|
+
const request = validateLocalTranscriptionOptions(raw);
|
|
410
|
+
checkCancelled(request.signal);
|
|
411
|
+
const resolved = await resolveMeeting(reference, request.signal);
|
|
412
|
+
const found = resolved && {
|
|
413
|
+
accountId: resolved.accountId,
|
|
414
|
+
meetingId: resolved.meetingId,
|
|
415
|
+
externalId: resolved.externalId
|
|
416
|
+
};
|
|
417
|
+
if (!found || found.accountId !== accountId || !Number.isSafeInteger(found.meetingId) || found.meetingId < 1 || typeof found.externalId !== "string" || !found.externalId.trim())
|
|
418
|
+
throw new CliError("not_found", "meeting was not found in the selected account");
|
|
419
|
+
const target = Object.freeze({ accountId, externalId: found.externalId, now: options.now?.() ?? Date.now() });
|
|
420
|
+
const audio = await wavFor(file, { signal: request.signal });
|
|
421
|
+
try {
|
|
422
|
+
const source = createWavTranscriptionSource(audio.path, {
|
|
423
|
+
model: request.model,
|
|
424
|
+
modelsDirectory: request.modelsDirectory,
|
|
425
|
+
env: options.env,
|
|
426
|
+
speech: options.speech
|
|
427
|
+
});
|
|
428
|
+
const { transcribeMeeting } = await import("./meetings-IBPI4MM4.js");
|
|
429
|
+
return await transcribeMeeting(source, {
|
|
430
|
+
appendTranscriptsWithReceipt(value) {
|
|
431
|
+
if (value.accountId !== accountId || value.externalId !== target.externalId)
|
|
432
|
+
throw new CliError("validation_error", "transcription append must retain its selected target");
|
|
433
|
+
return appendTranscriptsWithReceipt(value);
|
|
434
|
+
}
|
|
435
|
+
}, target, request);
|
|
436
|
+
} finally {
|
|
437
|
+
await audio.release();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}, "createLocalTranscriptionCapabilities");
|
|
442
|
+
|
|
443
|
+
// dist/meeting-context.js
|
|
444
|
+
var integer = /* @__PURE__ */ __name((value, min, max) => Number.isSafeInteger(value) && value >= min && value <= max, "integer");
|
|
445
|
+
var validateEvidenceOptions = /* @__PURE__ */ __name((options = {}) => {
|
|
446
|
+
if (!integer(options.cues ?? 100, 1, 1e3) || !integer(options.bytes ?? 65536, 2, 4194304) || !integer(options.maxReadBytes ?? 4194304, 1, 67108864) || !integer(options.maxTranscriptPages ?? 100, 1, 1e3) || options.afterTranscriptId !== void 0 && !integer(options.afterTranscriptId, 1, Number.MAX_SAFE_INTEGER) || options.after !== void 0 && options.afterTranscriptId !== void 0 || options.after !== void 0 && (typeof options.after !== "string" || !options.after.trim()))
|
|
447
|
+
throw new CliError("validation_error", "meeting evidence requires 1\u20131000 cues and 2\u20134194304 bytes");
|
|
448
|
+
}, "validateEvidenceOptions");
|
|
449
|
+
var validatePersonContext = /* @__PURE__ */ __name((person, options = {}) => {
|
|
450
|
+
validateEvidenceOptions(options);
|
|
451
|
+
if (typeof person !== "string" || !/^person:[1-9]\d*$/.test(person) || !integer(Number(person.slice(7)), 1, Number.MAX_SAFE_INTEGER) || !integer(options.meetings ?? 10, 1, 100) || !integer(options.scanLimit ?? 1e3, 1, 1e3) || options.since !== void 0 && !Number.isSafeInteger(options.since))
|
|
452
|
+
throw new CliError("validation_error", "person meeting context requires person:<positive ID> and valid bounded limits");
|
|
453
|
+
}, "validatePersonContext");
|
|
454
|
+
var validateTaskKind = /* @__PURE__ */ __name((kind) => {
|
|
455
|
+
if (!["question", "request", "mention", "promise"].includes(kind))
|
|
456
|
+
throw new CliError("validation_error", "task kind must be question, request, mention or promise");
|
|
457
|
+
return kind;
|
|
458
|
+
}, "validateTaskKind");
|
|
459
|
+
var validateTaskProposal = /* @__PURE__ */ __name((value) => {
|
|
460
|
+
if (typeof value !== "object" || value === null || !("applied" in value) || value.applied !== false || !("persistence" in value) || value.persistence !== "proposal-only")
|
|
461
|
+
throw new CliError("invalid_response", "task proposal must be unapplied and proposal-only");
|
|
462
|
+
}, "validateTaskProposal");
|
|
463
|
+
var loadProducer = /* @__PURE__ */ __name(async () => {
|
|
464
|
+
const [domain, services] = await Promise.all([import("./dist-IBNHCQXT.js"), import("./services-KC5KQFBS.js")]);
|
|
465
|
+
return { ...services, ...domain };
|
|
466
|
+
}, "loadProducer");
|
|
467
|
+
var createMeetingContextCapabilities = /* @__PURE__ */ __name((store, meetings, accountId, load = loadProducer) => {
|
|
468
|
+
if (!integer(accountId, 1, Number.MAX_SAFE_INTEGER))
|
|
469
|
+
throw new CliError("validation_error", "meeting context requires a selected positive account ID");
|
|
470
|
+
const ownedReference = /* @__PURE__ */ __name((api, reference) => {
|
|
471
|
+
if (api.parseMeetingReference(reference).accountId !== accountId)
|
|
472
|
+
throw new CliError("not_found", "meeting reference is unavailable for the selected account");
|
|
473
|
+
return api.canonicalMeetingReference(reference);
|
|
474
|
+
}, "ownedReference");
|
|
475
|
+
return {
|
|
476
|
+
passage: /* @__PURE__ */ __name(async (reference, options = {}) => {
|
|
477
|
+
checkCancelled(options.signal);
|
|
478
|
+
const api = await load();
|
|
479
|
+
checkCancelled(options.signal);
|
|
480
|
+
const owned = ownedReference(api, reference);
|
|
481
|
+
const result = await api.readMeetingPassage(meetings, accountId, owned, options);
|
|
482
|
+
checkCancelled(options.signal);
|
|
483
|
+
return result;
|
|
484
|
+
}, "passage"),
|
|
485
|
+
evidence: /* @__PURE__ */ __name(async (reference, options = {}) => {
|
|
486
|
+
validateEvidenceOptions(options);
|
|
487
|
+
checkCancelled(options.signal);
|
|
488
|
+
const api = await load();
|
|
489
|
+
checkCancelled(options.signal);
|
|
490
|
+
const owned = ownedReference(api, reference);
|
|
491
|
+
const result = await api.readMeetingEvidence(meetings, accountId, owned, {
|
|
492
|
+
cues: options.cues,
|
|
493
|
+
bytes: options.bytes,
|
|
494
|
+
signal: options.signal,
|
|
495
|
+
after: options.after === void 0 ? void 0 : ownedReference(api, options.after),
|
|
496
|
+
afterTranscriptId: options.afterTranscriptId,
|
|
497
|
+
maxReadBytes: options.maxReadBytes,
|
|
498
|
+
maxTranscriptPages: options.maxTranscriptPages
|
|
499
|
+
});
|
|
500
|
+
checkCancelled(options.signal);
|
|
501
|
+
return result;
|
|
502
|
+
}, "evidence"),
|
|
503
|
+
personContext: /* @__PURE__ */ __name(async (person, options = {}) => {
|
|
504
|
+
validatePersonContext(person, options);
|
|
505
|
+
checkCancelled(options.signal);
|
|
506
|
+
const api = await load();
|
|
507
|
+
checkCancelled(options.signal);
|
|
508
|
+
const personStore = {
|
|
509
|
+
people: store.people,
|
|
510
|
+
involvements: store.involvements,
|
|
511
|
+
meetings
|
|
512
|
+
};
|
|
513
|
+
const result = await api.personMeetingContext(personStore, person, {
|
|
514
|
+
accountIds: [accountId],
|
|
515
|
+
meetings: options.meetings,
|
|
516
|
+
cues: options.cues,
|
|
517
|
+
bytes: options.bytes,
|
|
518
|
+
scanLimit: options.scanLimit,
|
|
519
|
+
since: options.since,
|
|
520
|
+
signal: options.signal
|
|
521
|
+
});
|
|
522
|
+
checkCancelled(options.signal);
|
|
523
|
+
return result;
|
|
524
|
+
}, "personContext"),
|
|
525
|
+
taskProposal: /* @__PURE__ */ __name(async (reference, kind, signal) => {
|
|
526
|
+
validateTaskKind(kind);
|
|
527
|
+
checkCancelled(signal);
|
|
528
|
+
const api = await load();
|
|
529
|
+
checkCancelled(signal);
|
|
530
|
+
const owned = ownedReference(api, reference);
|
|
531
|
+
const result = await api.proposeMeetingTask(meetings, accountId, owned, kind);
|
|
532
|
+
checkCancelled(signal);
|
|
533
|
+
validateTaskProposal(result);
|
|
534
|
+
return result;
|
|
535
|
+
}, "taskProposal")
|
|
536
|
+
};
|
|
537
|
+
}, "createMeetingContextCapabilities");
|
|
538
|
+
|
|
539
|
+
// dist/meeting-integration.js
|
|
540
|
+
var integrationReference = /* @__PURE__ */ __name((reference) => {
|
|
541
|
+
const parsed = parseMeetingReference(reference);
|
|
542
|
+
if (parsed.transcriptId !== void 0)
|
|
543
|
+
throw new CliError("validation_error", "this operation requires a root meeting reference");
|
|
544
|
+
return { parsed, reference: canonicalMeetingReference(reference) };
|
|
545
|
+
}, "integrationReference");
|
|
546
|
+
|
|
547
|
+
// dist/answers.js
|
|
548
|
+
import { randomUUID } from "node:crypto";
|
|
549
|
+
var answerSchema2 = answerSchema();
|
|
550
|
+
var outputSchema = { ...jsonSchemaOf(union([answerSchema2, errorObjectSchema])) };
|
|
551
|
+
var toolAnswerSchema = { ...outputSchema, type: "object" };
|
|
552
|
+
var readMetadata = /* @__PURE__ */ __name((now = Date.now, runId = randomUUID()) => {
|
|
553
|
+
const started = now();
|
|
554
|
+
return () => metaNow({ run_id: runId, duration_ms: Math.max(0, Math.round(now() - started)) });
|
|
555
|
+
}, "readMetadata");
|
|
556
|
+
|
|
557
|
+
// dist/execution.js
|
|
558
|
+
var duration = /* @__PURE__ */ __name((text, source = "duration") => {
|
|
559
|
+
const value = parseDuration(text, source);
|
|
560
|
+
if (!Number.isSafeInteger(value) || value > 2147483647)
|
|
561
|
+
throw new CliError("validation_error", "duration exceeds the supported timer range");
|
|
562
|
+
return value;
|
|
563
|
+
}, "duration");
|
|
564
|
+
var bytes = /* @__PURE__ */ __name((text) => {
|
|
565
|
+
if (!/^\d+$/.test(text) || !Number.isSafeInteger(Number(text)))
|
|
566
|
+
throw new CliError("validation_error", "expected a nonnegative whole byte count");
|
|
567
|
+
return Number(text);
|
|
568
|
+
}, "bytes");
|
|
569
|
+
var longRunning = /* @__PURE__ */ new WeakSet();
|
|
570
|
+
var runsLong = /* @__PURE__ */ __name((command) => {
|
|
571
|
+
longRunning.add(command);
|
|
572
|
+
return command;
|
|
573
|
+
}, "runsLong");
|
|
574
|
+
var commandDeadline = /* @__PURE__ */ __name((action) => {
|
|
575
|
+
const { lifetime } = metaOf(action);
|
|
576
|
+
if (lifetime === "persistent")
|
|
577
|
+
return void 0;
|
|
578
|
+
if (lifetime === "interactive")
|
|
579
|
+
return 3e5;
|
|
580
|
+
return longRunning.has(action) ? 36e5 : 3e4;
|
|
581
|
+
}, "commandDeadline");
|
|
582
|
+
var sharedExecution = /* @__PURE__ */ __name((action, environment, now, runId) => {
|
|
583
|
+
const max = action.optsWithGlobals().maxOutputBytes;
|
|
584
|
+
return {
|
|
585
|
+
action,
|
|
586
|
+
signal: environment.signal,
|
|
587
|
+
meta: readMetadata(now, runId),
|
|
588
|
+
output: createOutputBuffer({ maxOutputBytes: max === void 0 ? void 0 : Number(max) })
|
|
589
|
+
};
|
|
590
|
+
}, "sharedExecution");
|
|
591
|
+
|
|
592
|
+
// dist/meeting-semantic-commands.js
|
|
593
|
+
var bounded = /* @__PURE__ */ __name((value, maximum) => value === void 0 || Number.isSafeInteger(value) && value > 0 && value <= maximum, "bounded");
|
|
594
|
+
var validateMeetingModel = /* @__PURE__ */ __name((options) => {
|
|
595
|
+
if (typeof options.model !== "string" || !options.model.trim() || options.model.length > 1e3)
|
|
596
|
+
throw new CliError("validation_error", "select an explicit embedding model");
|
|
597
|
+
if (options.baseUrl === void 0) {
|
|
598
|
+
if (options.dims !== void 0)
|
|
599
|
+
throw new CliError("validation_error", "dimensions require an explicit remote base URL");
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
let url;
|
|
603
|
+
try {
|
|
604
|
+
url = new URL(options.baseUrl);
|
|
605
|
+
} catch {
|
|
606
|
+
throw new CliError("validation_error", "embedding base URL must be an HTTP or HTTPS URL");
|
|
607
|
+
}
|
|
608
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || !/^[\w./:-]{1,200}$/.test(options.model) || options.dims === void 0 || !bounded(options.dims, 8192))
|
|
609
|
+
throw new CliError("validation_error", "remote embeddings require an HTTP base URL without credentials and explicit dimensions");
|
|
610
|
+
}, "validateMeetingModel");
|
|
611
|
+
var validateMeetingSemantic = /* @__PURE__ */ __name((query, options) => {
|
|
612
|
+
validateMeetingModel(options);
|
|
613
|
+
if (typeof query !== "string" || !query.trim() || Buffer.byteLength(query, "utf8") > 65536 || !bounded(options.limit, 1e3) || !bounded(options.maxChunks, 1e5) || !bounded(options.maxRows, 1e5) || !bounded(options.maxTextBytes, 64 * 1024 * 1024))
|
|
614
|
+
throw new CliError("validation_error", "meeting semantic search requires a nonempty bounded query and positive limits");
|
|
615
|
+
}, "validateMeetingSemantic");
|
|
616
|
+
var validateMeetingEmbeddings = /* @__PURE__ */ __name((options) => {
|
|
617
|
+
validateMeetingModel(options);
|
|
618
|
+
if (!bounded(options.limit, 1e3) || !bounded(options.afterTranscriptId, Number.MAX_SAFE_INTEGER) || !bounded(options.maxRows, 1e5) || !bounded(options.maxTextBytes, 64 * 1024 * 1024) || options.afterHash !== void 0 && !/^[a-f0-9]{64}$/.test(options.afterHash))
|
|
619
|
+
throw new CliError("validation_error", "meeting embedding generation requires valid bounded limits and cursors");
|
|
620
|
+
}, "validateMeetingEmbeddings");
|
|
621
|
+
var addMeetingEmbeddingCommand = /* @__PURE__ */ __name((program, deps) => {
|
|
622
|
+
const meetings = program.commands.find((command) => command.name() === "meetings");
|
|
623
|
+
if (!meetings)
|
|
624
|
+
throw new CliError("configuration_error", "meeting command group is unavailable");
|
|
625
|
+
const model = /* @__PURE__ */ __name((command) => command.requiredOption("--model <model>", "embedding model: an installed local one, or one served at --base-url").option("--base-url <url>", "remote embedding service; OpenAI reads OPENAI_API_KEY").option("--dims <n>", "vector size the remote service returns, in dimensions", bytes), "model");
|
|
626
|
+
const budget = /* @__PURE__ */ __name((command) => command.option("--limit <n>", "how many results or items in one batch, 1 through 1000", bytes).option("--max-rows <n>", "most transcript lines to read, 1 through 100000", bytes).option("--max-text-bytes <bytes>", "most transcript text to read, in bytes (default 4 MiB)", bytes), "budget");
|
|
627
|
+
annotate(budget(model(meetings.command("embeddings").description("preview building meeting vectors; --yes builds one batch"))).option("--after-transcript-id <id>", "continue splitting transcripts after this one", bytes).option("--after-hash <hash>", "continue embedding after this passage").option("--yes", "split transcripts and build their vectors; otherwise only preview").action(async (options) => {
|
|
628
|
+
validateMeetingEmbeddings(options);
|
|
629
|
+
if (options.baseUrl !== void 0 && deps.offline())
|
|
630
|
+
throw new CliError("configuration_error", "remote embeddings require network access; remove --offline");
|
|
631
|
+
const api = await deps.capabilities();
|
|
632
|
+
const result = await api.generate({ ...options, signal: deps.signal() });
|
|
633
|
+
if (options.yes)
|
|
634
|
+
await deps.receipt(itemOf(result));
|
|
635
|
+
else
|
|
636
|
+
deps.output(itemOf(result));
|
|
637
|
+
}), { origin: "handwritten", effects: FETCH, examples: ["zm personal meetings embeddings --model e5-small --json"] });
|
|
638
|
+
}, "addMeetingEmbeddingCommand");
|
|
639
|
+
|
|
640
|
+
// dist/meeting-search-commands.js
|
|
641
|
+
var FLAG = {
|
|
642
|
+
meetingId: "--meeting-id",
|
|
643
|
+
identityId: "--identity-id",
|
|
644
|
+
personId: "--person-id",
|
|
645
|
+
eventId: "--event-id",
|
|
646
|
+
seriesId: "--series-id",
|
|
647
|
+
project: "--project",
|
|
648
|
+
tag: "--tag",
|
|
649
|
+
topicName: "--topic-name",
|
|
650
|
+
sinceTime: "--since-time",
|
|
651
|
+
untilTime: "--until-time",
|
|
652
|
+
cursor: "--cursor",
|
|
653
|
+
maxReadBytes: "--max-read-bytes",
|
|
654
|
+
model: "--model",
|
|
655
|
+
baseUrl: "--base-url",
|
|
656
|
+
dims: "--dims",
|
|
657
|
+
maxChunks: "--max-chunks",
|
|
658
|
+
maxRows: "--max-rows",
|
|
659
|
+
maxTextBytes: "--max-text-bytes"
|
|
660
|
+
};
|
|
661
|
+
var refuse = /* @__PURE__ */ __name((options, keys, mode) => {
|
|
662
|
+
const given = keys.filter((key) => options[key] !== void 0).map((key) => FLAG[key]);
|
|
663
|
+
if (given.length)
|
|
664
|
+
throw new CliError("validation_error", `${given.join(", ")} do not apply ${mode}`);
|
|
665
|
+
}, "refuse");
|
|
666
|
+
var recordingHit = /* @__PURE__ */ __name((directory) => (match) => {
|
|
667
|
+
const meetingStart = Date.parse(match.meeting.startedAt ?? "");
|
|
668
|
+
const assetStart = Date.parse(match.asset.startedAt ?? "");
|
|
669
|
+
const lead = assetStart - meetingStart;
|
|
670
|
+
return {
|
|
671
|
+
kind: "meeting",
|
|
672
|
+
ref: `recording:${match.asset.id}/${match.position}`,
|
|
673
|
+
provider: "zoom",
|
|
674
|
+
account: directory,
|
|
675
|
+
title: match.meeting.title,
|
|
676
|
+
timestamp: Number.isFinite(meetingStart) ? new Date(meetingStart).toISOString() : null,
|
|
677
|
+
excerpt: match.text,
|
|
678
|
+
sender_name: match.speaker,
|
|
679
|
+
offset_ms: match.startMs + (Number.isFinite(lead) && lead > 0 ? lead : 0)
|
|
680
|
+
};
|
|
681
|
+
}, "recordingHit");
|
|
682
|
+
var combinedList = /* @__PURE__ */ __name((found, limit, args) => listOf("search meetings", {
|
|
683
|
+
items: found.items.map((hit) => ({
|
|
684
|
+
ref: hit.ref,
|
|
685
|
+
meeting: hit.ref.split("/").slice(0, 2).join("/"),
|
|
686
|
+
scope: hit.scope,
|
|
687
|
+
excerpt: hit.text,
|
|
688
|
+
offset_ms: hit.startMs,
|
|
689
|
+
by: hit.by,
|
|
690
|
+
score: hit.score,
|
|
691
|
+
...hit.passage ? { passage: hit.passage } : {},
|
|
692
|
+
...hit.match ? { match: hit.match } : {},
|
|
693
|
+
...hit.corrections ? { corrections: hit.corrections } : {},
|
|
694
|
+
source: {
|
|
695
|
+
title: hit.source.title,
|
|
696
|
+
started_at: hit.source.startedAt === null ? null : new Date(hit.source.startedAt).toISOString(),
|
|
697
|
+
url: hit.source.url
|
|
698
|
+
}
|
|
699
|
+
})),
|
|
700
|
+
limit,
|
|
701
|
+
has_more: found.hasMore,
|
|
702
|
+
paging: "cursor",
|
|
703
|
+
...found.nextCursor ? { cursor: found.nextCursor } : {},
|
|
704
|
+
args,
|
|
705
|
+
missing: [
|
|
706
|
+
...found.coverage.meaningScanComplete ? [] : [{ field: "items", why: `meaning compared only ${found.coverage.scanned} passages; raise --max-chunks` }],
|
|
707
|
+
...found.words.refused ? [{ field: "items", why: `found by meaning only: ${found.words.refused}` }] : []
|
|
708
|
+
]
|
|
709
|
+
}), "combinedList");
|
|
710
|
+
var withMeaningOptions = /* @__PURE__ */ __name((command) => overrideSharedContract(command, "Zoom explicitly extends meeting search with semantic ranking and bounded embedding-provider controls.", (one) => {
|
|
711
|
+
one.description("find words in stored meetings, or by meaning with --model").option("--model <model>", "rank words and meaning together with this embedding model, building missing vectors first").option("--base-url <url>", "remote embedding service for --model; OpenAI reads OPENAI_API_KEY").option("--dims <n>", "vector size the remote service returns, in dimensions", bytes).option("--max-chunks <n>", "by meaning: most stored passages to compare (default 10000)", bytes).option("--max-rows <n>", "by meaning: most transcript lines to read, 1 through 100000", bytes).option("--max-text-bytes <bytes>", "by meaning: most transcript text to read (default 4 MiB)", bytes);
|
|
712
|
+
}), "withMeaningOptions");
|
|
713
|
+
var meetingSearchDefinition = /* @__PURE__ */ __name(() => {
|
|
714
|
+
const program = new Command();
|
|
715
|
+
const command = withMeaningOptions(createMeetingSearchCommand(program));
|
|
716
|
+
return { program, command };
|
|
717
|
+
}, "meetingSearchDefinition");
|
|
718
|
+
var addMeetingSearchCommands = /* @__PURE__ */ __name((program, deps) => {
|
|
719
|
+
const execute = /* @__PURE__ */ __name(async (query, options) => {
|
|
720
|
+
const request = meetingHitSearchRequest(query, options, deps.signal());
|
|
721
|
+
if (options.model !== void 0) {
|
|
722
|
+
refuse(options, ["maxReadBytes"], "to a search by meaning");
|
|
723
|
+
const { limit, after, maxReadBytes: _bytes, signal, ...selection } = request;
|
|
724
|
+
const meaning = {
|
|
725
|
+
model: options.model,
|
|
726
|
+
baseUrl: options.baseUrl,
|
|
727
|
+
dims: options.dims,
|
|
728
|
+
limit: limit ?? 20,
|
|
729
|
+
maxChunks: options.maxChunks,
|
|
730
|
+
after,
|
|
731
|
+
selection,
|
|
732
|
+
maxRows: options.maxRows,
|
|
733
|
+
maxTextBytes: options.maxTextBytes,
|
|
734
|
+
buildMissing: true,
|
|
735
|
+
signal
|
|
736
|
+
};
|
|
737
|
+
validateMeetingSemantic(query, meaning);
|
|
738
|
+
if (meaning.baseUrl !== void 0 && deps.offline())
|
|
739
|
+
throw new CliError("configuration_error", "remote embeddings require network access; remove --offline");
|
|
740
|
+
return combinedList(await (await deps.meaning()).search(query, meaning), meaning.limit, meetingSearchArguments(query, options));
|
|
741
|
+
}
|
|
742
|
+
refuse(options, ["baseUrl", "dims", "maxChunks", "maxRows", "maxTextBytes"], "without --model");
|
|
743
|
+
return meetingHitsList(await (await deps.words())(query, request), request.limit ?? 100, "search meetings", meetingSearchArguments(query, options), meetingSearchMeta(options));
|
|
744
|
+
}, "execute");
|
|
745
|
+
const command = withMeaningOptions(createMeetingSearchCommand(program)).action(async (query, options) => {
|
|
746
|
+
deps.output(await execute(query, options));
|
|
747
|
+
});
|
|
748
|
+
overrideSharedContract(command, "Zoom meeting search may build missing embeddings, so its effects and examples include that explicit mode.", (one) => {
|
|
749
|
+
annotate(one, {
|
|
750
|
+
origin: "handwritten",
|
|
751
|
+
effects: FETCH,
|
|
752
|
+
examples: [
|
|
753
|
+
'zm personal search meetings "example plan" --since-time 2026-09-01 --json',
|
|
754
|
+
'zm personal search meetings "example plan" --model e5-small --json'
|
|
755
|
+
]
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
const search = program.commands.find((child) => child.name() === "search") ?? annotate(program.command("search"), { effects: NO_EFFECTS });
|
|
759
|
+
const recordings = search.command("recordings").description("find words in the transcripts of a downloaded archive folder, before any import").argument("<directory>", "archive folder").argument("<query...>", "words to find as one phrase, ignoring case").option("--limit <n>", "how many hits, 1 through 1000 (default 100)", bytes).action(async (directory, words, options) => {
|
|
760
|
+
const found = await deps.archive(directory, words.join(" "), { limit: options.limit, signal: deps.signal() });
|
|
761
|
+
deps.output({
|
|
762
|
+
items: found.items.map(recordingHit(directory)),
|
|
763
|
+
page: { mode: "page", limit: found.limit, has_more: found.hasMore }
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
annotate(recordings, {
|
|
767
|
+
origin: "handwritten",
|
|
768
|
+
effects: LOCAL_READ,
|
|
769
|
+
output: { entity: "hit" },
|
|
770
|
+
examples: ['zm personal search recordings ./zoom-archive "example plan" --limit 100 --offline --json']
|
|
771
|
+
});
|
|
772
|
+
return execute;
|
|
773
|
+
}, "addMeetingSearchCommands");
|
|
774
|
+
|
|
775
|
+
// dist/meeting-semantic.js
|
|
776
|
+
var loadProducer2 = /* @__PURE__ */ __name(async () => import("./services-KC5KQFBS.js"), "loadProducer");
|
|
777
|
+
var createMeetingSemanticCapabilities = /* @__PURE__ */ __name((store, accountId, options = {}) => {
|
|
778
|
+
if (!Number.isSafeInteger(accountId) || accountId < 1)
|
|
779
|
+
throw new CliError("validation_error", "meeting vectors require a selected positive account ID");
|
|
780
|
+
const load = options.load ?? loadProducer2;
|
|
781
|
+
const choice = /* @__PURE__ */ __name((api, input) => input.baseUrl === void 0 ? input.model : { remote: api.createMeetingRemoteModel({ model: input.model, baseUrl: input.baseUrl, dims: input.dims }) }, "choice");
|
|
782
|
+
const budget = /* @__PURE__ */ __name((input) => ({
|
|
783
|
+
maxRows: input.maxRows,
|
|
784
|
+
maxTextBytes: input.maxTextBytes,
|
|
785
|
+
signal: input.signal
|
|
786
|
+
}), "budget");
|
|
787
|
+
const buildMissing = /* @__PURE__ */ __name(async (api, embedder, input) => {
|
|
788
|
+
let afterTranscriptId;
|
|
789
|
+
for (; ; ) {
|
|
790
|
+
checkCancelled(input.signal);
|
|
791
|
+
const page = await store.meetingVectors.rebuild({ accountId, limit: 1e3, afterTranscriptId, ...budget(input) });
|
|
792
|
+
if (!page.hasMore || page.nextTranscriptId === void 0)
|
|
793
|
+
break;
|
|
794
|
+
afterTranscriptId = page.nextTranscriptId;
|
|
795
|
+
}
|
|
796
|
+
let afterHash;
|
|
797
|
+
for (; ; ) {
|
|
798
|
+
checkCancelled(input.signal);
|
|
799
|
+
const batch = await api.applyMeetingEmbedding(store.meetingVectors, accountId, embedder, {
|
|
800
|
+
afterHash,
|
|
801
|
+
...budget(input)
|
|
802
|
+
});
|
|
803
|
+
if (!batch.hasMore || batch.nextHash === void 0)
|
|
804
|
+
return;
|
|
805
|
+
afterHash = batch.nextHash;
|
|
806
|
+
}
|
|
807
|
+
}, "buildMissing");
|
|
808
|
+
return {
|
|
809
|
+
search: /* @__PURE__ */ __name(async (query, input) => {
|
|
810
|
+
validateMeetingSemantic(query, input);
|
|
811
|
+
checkCancelled(input.signal);
|
|
812
|
+
const api = await load();
|
|
813
|
+
checkCancelled(input.signal);
|
|
814
|
+
const selected = choice(api, input);
|
|
815
|
+
const embedder = await api.openMeetingEmbedder(selected, { env: options.env, signal: input.signal });
|
|
816
|
+
try {
|
|
817
|
+
if (input.buildMissing)
|
|
818
|
+
await buildMissing(api, embedder, input);
|
|
819
|
+
return await api.searchMeetingsCombined(store, embedder, query, {
|
|
820
|
+
...input.selection,
|
|
821
|
+
accountId,
|
|
822
|
+
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
823
|
+
...input.after === void 0 ? {} : { after: input.after },
|
|
824
|
+
...input.maxChunks === void 0 ? {} : { maxChunks: input.maxChunks },
|
|
825
|
+
...input.maxRows === void 0 ? {} : { maxRows: input.maxRows },
|
|
826
|
+
...input.maxTextBytes === void 0 ? {} : { maxTextBytes: input.maxTextBytes },
|
|
827
|
+
...input.signal ? { signal: input.signal } : {}
|
|
828
|
+
});
|
|
829
|
+
} finally {
|
|
830
|
+
await embedder.close();
|
|
831
|
+
}
|
|
832
|
+
}, "search"),
|
|
833
|
+
generate: /* @__PURE__ */ __name(async (input) => {
|
|
834
|
+
validateMeetingEmbeddings(input);
|
|
835
|
+
checkCancelled(input.signal);
|
|
836
|
+
const api = await load();
|
|
837
|
+
checkCancelled(input.signal);
|
|
838
|
+
const selected = choice(api, input);
|
|
839
|
+
const descriptor = api.meetingEmbeddingModel(selected);
|
|
840
|
+
if (input.yes !== true)
|
|
841
|
+
return api.proposeMeetingEmbedding(store.meetingVectors, accountId, descriptor, { signal: input.signal });
|
|
842
|
+
const embedder = await api.openMeetingEmbedder(selected, { env: options.env, signal: input.signal });
|
|
843
|
+
try {
|
|
844
|
+
checkCancelled(input.signal);
|
|
845
|
+
const rebuild = await store.meetingVectors.rebuild({
|
|
846
|
+
accountId,
|
|
847
|
+
limit: input.limit,
|
|
848
|
+
afterTranscriptId: input.afterTranscriptId,
|
|
849
|
+
maxRows: input.maxRows,
|
|
850
|
+
maxTextBytes: input.maxTextBytes,
|
|
851
|
+
signal: input.signal
|
|
852
|
+
});
|
|
853
|
+
checkCancelled(input.signal);
|
|
854
|
+
const result = await api.applyMeetingEmbedding(store.meetingVectors, accountId, embedder, {
|
|
855
|
+
maxChunks: input.limit,
|
|
856
|
+
afterHash: input.afterHash,
|
|
857
|
+
maxRows: input.maxRows,
|
|
858
|
+
maxTextBytes: input.maxTextBytes,
|
|
859
|
+
signal: input.signal
|
|
860
|
+
});
|
|
861
|
+
return { ...result, applied: true, model: descriptor, rebuild };
|
|
862
|
+
} finally {
|
|
863
|
+
await embedder.close();
|
|
864
|
+
}
|
|
865
|
+
}, "generate")
|
|
866
|
+
};
|
|
867
|
+
}, "createMeetingSemanticCapabilities");
|
|
868
|
+
|
|
869
|
+
// dist/unified-search.js
|
|
870
|
+
var resources = ["messages", "mail", "notes", "meetings"];
|
|
871
|
+
var validateUnifiedSearch = /* @__PURE__ */ __name((input, meetingsOnly = false) => {
|
|
872
|
+
const limit = input.limit ?? 100;
|
|
873
|
+
const maxMeetings = input.maxMeetings ?? 100;
|
|
874
|
+
const only = input.only ?? ["meetings"];
|
|
875
|
+
if (typeof input.text !== "string" || !input.text.trim() || !Number.isSafeInteger(limit) || limit < 1 || limit > 1e3 || maxMeetings !== "all" && (!Number.isSafeInteger(maxMeetings) || maxMeetings < 1) || input.offset !== void 0 && (!Number.isSafeInteger(input.offset) || input.offset < 0) || !Array.isArray(only) || !only.length || only.some((resource) => !resources.includes(resource)) || meetingsOnly && (only.length !== 1 || only[0] !== "meetings") || input.exact !== void 0 && typeof input.exact !== "boolean")
|
|
876
|
+
throw new CliError("validation_error", "expected nonempty search text, supported resources, a limit from 1 through 1000 and a whole number of meetings, or all");
|
|
877
|
+
if (input.meetingCursor !== void 0) {
|
|
878
|
+
const cursor = input.meetingCursor;
|
|
879
|
+
if (typeof cursor !== "object" || cursor === null || Array.isArray(cursor) || Object.keys(cursor).some((key) => !["accountId", "query", "meetingOffset", "hitOffset"].includes(key)) || only.length !== 1 || only[0] !== "meetings" || typeof cursor.query !== "string" || !cursor.query.trim() || !Number.isSafeInteger(cursor.accountId) || cursor.accountId < 1 || !Number.isSafeInteger(cursor.meetingOffset) || cursor.meetingOffset < 0 || cursor.meetingOffset > Number.MAX_SAFE_INTEGER - 2 || !Number.isSafeInteger(cursor.hitOffset) || cursor.hitOffset < 0)
|
|
880
|
+
throw new CliError("validation_error", "meeting continuation requires a source-only cursor bound to its account and query");
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
text: input.text,
|
|
884
|
+
limit,
|
|
885
|
+
maxMeetings,
|
|
886
|
+
only: [...new Set(only)],
|
|
887
|
+
...input.offset === void 0 ? {} : { offset: input.offset },
|
|
888
|
+
exact: input.exact,
|
|
889
|
+
meetingCursor: input.meetingCursor,
|
|
890
|
+
signal: input.signal
|
|
891
|
+
};
|
|
892
|
+
}, "validateUnifiedSearch");
|
|
893
|
+
var runUnifiedSearch = /* @__PURE__ */ __name(async (search, input, meetingsOnly = false) => {
|
|
894
|
+
const request = validateUnifiedSearch(input, meetingsOnly);
|
|
895
|
+
checkCancelled(request.signal);
|
|
896
|
+
const result = await search(request);
|
|
897
|
+
checkCancelled(request.signal);
|
|
898
|
+
return result;
|
|
899
|
+
}, "runUnifiedSearch");
|
|
900
|
+
|
|
901
|
+
// dist/store-connection.js
|
|
902
|
+
var storeUnavailable = /* @__PURE__ */ __name(async () => {
|
|
903
|
+
throw new CliError("configuration_error", "the shared SQLite meeting store is not available yet");
|
|
904
|
+
}, "storeUnavailable");
|
|
905
|
+
var unavailableStore = {
|
|
906
|
+
saveMeeting: storeUnavailable,
|
|
907
|
+
account: storeUnavailable,
|
|
908
|
+
meetings: storeUnavailable,
|
|
909
|
+
meeting: storeUnavailable,
|
|
910
|
+
participants: storeUnavailable,
|
|
911
|
+
search: storeUnavailable,
|
|
912
|
+
events: storeUnavailable,
|
|
913
|
+
queryEvents: storeUnavailable,
|
|
914
|
+
eventCandidates: storeUnavailable,
|
|
915
|
+
createEvent: storeUnavailable,
|
|
916
|
+
createEventSeries: storeUnavailable,
|
|
917
|
+
setEventSeries: storeUnavailable,
|
|
918
|
+
linkMeeting: storeUnavailable,
|
|
919
|
+
cursor: storeUnavailable,
|
|
920
|
+
setCursor: storeUnavailable
|
|
921
|
+
};
|
|
922
|
+
var connectSelectedProfile = /* @__PURE__ */ __name((connect, profile, intent, context) => {
|
|
923
|
+
if (!profile)
|
|
924
|
+
throw new CliError("validation_error", "name a profile: `zm <profile> \u2026`");
|
|
925
|
+
return connect({ profile, externalId: profileAccountKey(profile), intent, ...context ? { context } : {} });
|
|
926
|
+
}, "connectSelectedProfile");
|
|
927
|
+
var createStoreConnector = /* @__PURE__ */ __name((options = {}) => async (request) => {
|
|
928
|
+
const { profile, externalId, intent, context } = request ?? {};
|
|
929
|
+
if (typeof profile !== "string" || !profilePattern.test(profile) || externalId !== profileAccountKey(profile) || intent !== "read" && intent !== "ingestion" && intent !== "preparation" || context !== void 0 && (intent !== "preparation" || typeof context.resolve !== "function" || typeof context.assert !== "function"))
|
|
930
|
+
throw new CliError("validation_error", "expected a selected Zoom profile and read or ingestion intent");
|
|
931
|
+
let store;
|
|
932
|
+
try {
|
|
933
|
+
const open2 = options.openStore ?? (await import("./store-LODVDPK3.js")).openStore;
|
|
934
|
+
store = await open2({
|
|
935
|
+
path: options.path,
|
|
936
|
+
env: options.env,
|
|
937
|
+
now: options.now,
|
|
938
|
+
...intent === "preparation" ? { readOnly: true } : {}
|
|
939
|
+
});
|
|
940
|
+
const connected = store;
|
|
941
|
+
const meetings = connected.meetings;
|
|
942
|
+
const key = { provider: "zoom", account: externalId };
|
|
943
|
+
const accountId = intent === "ingestion" ? await connected.accounts.saveAccount(key, { name: profile }) : (await connected.accounts.storedAccount(key)).id;
|
|
944
|
+
if (!Number.isSafeInteger(accountId) || accountId < 1)
|
|
945
|
+
throw new CliError("configuration_error", "the selected meeting account is unavailable");
|
|
946
|
+
const ownAccount = /* @__PURE__ */ __name((input, what) => {
|
|
947
|
+
if (input.accountId !== accountId)
|
|
948
|
+
throw new CliError("validation_error", `${what} account is selected by the connection`);
|
|
949
|
+
return input;
|
|
950
|
+
}, "ownAccount");
|
|
951
|
+
let cachedBatchReader;
|
|
952
|
+
const batchReader = /* @__PURE__ */ __name(async () => {
|
|
953
|
+
if (!cachedBatchReader) {
|
|
954
|
+
const { createMeetingBatchReader } = await import("./services-KC5KQFBS.js");
|
|
955
|
+
cachedBatchReader = createMeetingBatchReader(connected, accountId);
|
|
956
|
+
}
|
|
957
|
+
return cachedBatchReader;
|
|
958
|
+
}, "batchReader");
|
|
959
|
+
return {
|
|
960
|
+
meetingPreparation: {
|
|
961
|
+
scope: key,
|
|
962
|
+
create: /* @__PURE__ */ __name(async (upcoming) => {
|
|
963
|
+
const { createMeetingPreparationService } = await import("./services-KC5KQFBS.js");
|
|
964
|
+
const reader = connected.contextEvidence;
|
|
965
|
+
if (context && !reader)
|
|
966
|
+
throw new CliError("configuration_error", "the installed store does not support bounded communications");
|
|
967
|
+
const communications = context && reader ? {
|
|
968
|
+
reader,
|
|
969
|
+
selections: await context.resolve({
|
|
970
|
+
storedAccount: connected.accounts.storedAccount.bind(connected.accounts),
|
|
971
|
+
accountById: connected.meetings.account.bind(connected.meetings)
|
|
972
|
+
})
|
|
973
|
+
} : void 0;
|
|
974
|
+
const service = createMeetingPreparationService(key, accountId, {
|
|
975
|
+
...communications ? { communications } : {},
|
|
976
|
+
meetings,
|
|
977
|
+
upcoming,
|
|
978
|
+
projectDiscovery: { vocabulary: connected.knowledge, search: meetings }
|
|
979
|
+
});
|
|
980
|
+
return service.prepare.bind(service);
|
|
981
|
+
}, "create")
|
|
982
|
+
},
|
|
983
|
+
store: meetings,
|
|
984
|
+
accountId,
|
|
985
|
+
close: /* @__PURE__ */ __name(() => connected.close(), "close"),
|
|
986
|
+
appendTranscriptsWithReceipt: /* @__PURE__ */ __name((input) => meetings.appendTranscriptsWithReceipt(ownAccount(input, "append")), "appendTranscriptsWithReceipt"),
|
|
987
|
+
meetingProjectCandidates: /* @__PURE__ */ __name(async (reference, request2) => {
|
|
988
|
+
const { createStoredMeetingProjectCandidateReader } = await import("./services-KC5KQFBS.js");
|
|
989
|
+
const read = createStoredMeetingProjectCandidateReader(key, accountId, {
|
|
990
|
+
meetings,
|
|
991
|
+
vocabulary: connected.knowledge
|
|
992
|
+
});
|
|
993
|
+
return read(reference, request2);
|
|
994
|
+
}, "meetingProjectCandidates"),
|
|
995
|
+
meetingProjectEvidence: /* @__PURE__ */ __name(async (project, request2) => {
|
|
996
|
+
const { readMeetingProjectDiscovery } = await import("./services-KC5KQFBS.js");
|
|
997
|
+
return readMeetingProjectDiscovery({
|
|
998
|
+
vocabulary: connected.knowledge,
|
|
999
|
+
search: { searchHits: /* @__PURE__ */ __name((query, hits) => meetings.searchHits(query, hits), "searchHits") }
|
|
1000
|
+
}, project, { ...request2, accountId });
|
|
1001
|
+
}, "meetingProjectEvidence"),
|
|
1002
|
+
meetingTranscription: createLocalTranscriptionCapabilities({
|
|
1003
|
+
accountId,
|
|
1004
|
+
resolveMeeting: /* @__PURE__ */ __name(async (value, signal) => {
|
|
1005
|
+
const reference = /^[1-9]\d*$/.test(value) ? `meeting:${accountId}/${value}` : value;
|
|
1006
|
+
const target = integrationReference(reference);
|
|
1007
|
+
if (target.parsed.accountId !== accountId)
|
|
1008
|
+
return null;
|
|
1009
|
+
const meeting = await meetings.meetingMetadata({ accountId, meetingId: target.parsed.meetingId, signal });
|
|
1010
|
+
if (meeting.accountId !== accountId || meeting.id !== target.parsed.meetingId || meeting.deletedAt !== null)
|
|
1011
|
+
return null;
|
|
1012
|
+
return { accountId, meetingId: meeting.id, externalId: meeting.externalId };
|
|
1013
|
+
}, "resolveMeeting"),
|
|
1014
|
+
appendTranscriptsWithReceipt: /* @__PURE__ */ __name((input) => meetings.appendTranscriptsWithReceipt(ownAccount(input, "transcription")), "appendTranscriptsWithReceipt")
|
|
1015
|
+
}, { env: options.env, now: options.now }),
|
|
1016
|
+
meetingBatch: {
|
|
1017
|
+
index: /* @__PURE__ */ __name(async (input) => (await batchReader()).index(input), "index"),
|
|
1018
|
+
search: /* @__PURE__ */ __name(async (query, input) => (await batchReader()).search(query, input), "search"),
|
|
1019
|
+
passage: /* @__PURE__ */ __name(async (reference, input) => (await batchReader()).passage(reference, input), "passage")
|
|
1020
|
+
},
|
|
1021
|
+
meetingSearch: /* @__PURE__ */ __name(async (query, request2) => {
|
|
1022
|
+
validateMeetingHitSearch(query, request2);
|
|
1023
|
+
const { searchMeetingHits } = await import("./services-KC5KQFBS.js");
|
|
1024
|
+
return searchMeetingHits({ meetings, knowledge: connected.knowledge }, query, { ...request2, accountId });
|
|
1025
|
+
}, "meetingSearch"),
|
|
1026
|
+
meetingIntegration: {
|
|
1027
|
+
change: /* @__PURE__ */ __name(async (reference, input, mutation = {}) => {
|
|
1028
|
+
const source = integrationReference(reference);
|
|
1029
|
+
if (source.parsed.accountId !== accountId)
|
|
1030
|
+
throw new CliError("not_found", "meeting is unavailable for the selected account");
|
|
1031
|
+
const change = validateMeetingContextChange(input);
|
|
1032
|
+
if (change.action !== "event.link" && change.action !== "person.link")
|
|
1033
|
+
throw new CliError("validation_error", "meeting linking supports only explicit event or person changes");
|
|
1034
|
+
const api = await import("./services-KC5KQFBS.js");
|
|
1035
|
+
await api.resolveMeetingReference(meetings, accountId, source.reference, { signal: mutation.signal });
|
|
1036
|
+
const service = await api.createMeetingContextService({
|
|
1037
|
+
accounts: connected.accounts,
|
|
1038
|
+
meetings,
|
|
1039
|
+
notes: connected.notes,
|
|
1040
|
+
knowledge: connected.knowledge,
|
|
1041
|
+
memories: connected.memories,
|
|
1042
|
+
facts: connected.facts
|
|
1043
|
+
}, key);
|
|
1044
|
+
if (service.accountId !== accountId || service.scope.provider !== key.provider || service.scope.account !== key.account)
|
|
1045
|
+
throw new CliError("invalid_response", "meeting context service returned an inconsistent account");
|
|
1046
|
+
return service.change(source.parsed.meetingId, change, {
|
|
1047
|
+
apply: mutation.apply,
|
|
1048
|
+
dryRun: mutation.dryRun,
|
|
1049
|
+
signal: mutation.signal
|
|
1050
|
+
});
|
|
1051
|
+
}, "change")
|
|
1052
|
+
},
|
|
1053
|
+
unifiedSearch: /* @__PURE__ */ __name(async (input) => {
|
|
1054
|
+
const request2 = validateUnifiedSearch(input);
|
|
1055
|
+
const { searchAllIncludingMeetings } = await import("./services-KC5KQFBS.js");
|
|
1056
|
+
return searchAllIncludingMeetings(connected, key, { ...request2, meetingAccount: key, env: options.env });
|
|
1057
|
+
}, "unifiedSearch"),
|
|
1058
|
+
meetingContext: createMeetingContextCapabilities(connected, meetings, accountId),
|
|
1059
|
+
meetingSemantic: createMeetingSemanticCapabilities(connected, accountId, { env: options.env }),
|
|
1060
|
+
meetingSearches: await (async () => {
|
|
1061
|
+
const { meetingSearchesService } = await import("./services-KC5KQFBS.js");
|
|
1062
|
+
return meetingSearchesService(async () => connected.searches);
|
|
1063
|
+
})(),
|
|
1064
|
+
meetingStats: /* @__PURE__ */ __name(async (request2) => {
|
|
1065
|
+
const { readMeetingStats } = await import("./services-KC5KQFBS.js");
|
|
1066
|
+
return readMeetingStats(connected, { ...request2, accountId });
|
|
1067
|
+
}, "meetingStats"),
|
|
1068
|
+
meetingReview: /* @__PURE__ */ __name(async (request2) => {
|
|
1069
|
+
const { readMeetingReview } = await import("./services-KC5KQFBS.js");
|
|
1070
|
+
return readMeetingReview(connected, { ...request2, accountId });
|
|
1071
|
+
}, "meetingReview"),
|
|
1072
|
+
meetingLinks: /* @__PURE__ */ __name(async (request2) => {
|
|
1073
|
+
const { readMeetingLinks } = await import("./services-KC5KQFBS.js");
|
|
1074
|
+
return readMeetingLinks(connected, { ...request2, accountId });
|
|
1075
|
+
}, "meetingLinks")
|
|
1076
|
+
};
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
await store?.close().catch(() => void 0);
|
|
1079
|
+
if (isCliError(error) && error.code === "not_found")
|
|
1080
|
+
throw new CliError("configuration_error", "no stored meetings for the selected profile; import transcripts or pull first");
|
|
1081
|
+
if (isCliError(error))
|
|
1082
|
+
throw error;
|
|
1083
|
+
throw new CliError("configuration_error", "could not connect to the shared meeting store; check MESSAGING_STORE and the installed store version");
|
|
1084
|
+
}
|
|
1085
|
+
}, "createStoreConnector");
|
|
1086
|
+
|
|
1087
|
+
export {
|
|
1088
|
+
duration,
|
|
1089
|
+
bytes,
|
|
1090
|
+
runsLong,
|
|
1091
|
+
commandDeadline,
|
|
1092
|
+
sharedExecution,
|
|
1093
|
+
validateUnifiedSearch,
|
|
1094
|
+
runUnifiedSearch,
|
|
1095
|
+
validateLocalTranscriptionOptions,
|
|
1096
|
+
compactLocalTranscriptionReceipt,
|
|
1097
|
+
validateEvidenceOptions,
|
|
1098
|
+
validatePersonContext,
|
|
1099
|
+
validateTaskKind,
|
|
1100
|
+
validateTaskProposal,
|
|
1101
|
+
integrationReference,
|
|
1102
|
+
addMeetingEmbeddingCommand,
|
|
1103
|
+
meetingSearchDefinition,
|
|
1104
|
+
addMeetingSearchCommands,
|
|
1105
|
+
storeUnavailable,
|
|
1106
|
+
unavailableStore,
|
|
1107
|
+
connectSelectedProfile,
|
|
1108
|
+
createStoreConnector
|
|
1109
|
+
};
|