@wirecat/zoom-cli 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -9
- package/dist/agent-skill.d.ts +4 -0
- package/dist/agent-skill.d.ts.map +1 -0
- package/dist/agent-skill.js +16 -0
- package/dist/agent-skill.js.map +1 -0
- package/dist/api.d.ts +3 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +10 -41
- package/dist/api.js.map +1 -1
- package/dist/archive-commands.d.ts +32 -0
- package/dist/archive-commands.d.ts.map +1 -0
- package/dist/archive-commands.js +182 -0
- package/dist/archive-commands.js.map +1 -0
- package/dist/archive-transcripts.d.ts +33 -0
- package/dist/archive-transcripts.d.ts.map +1 -0
- package/dist/archive-transcripts.js +131 -0
- package/dist/archive-transcripts.js.map +1 -0
- package/dist/bin/zm.js +53 -3
- package/dist/bin/zm.js.map +1 -1
- package/dist/commands.d.ts +12 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +96 -0
- package/dist/commands.js.map +1 -0
- package/dist/errors.d.ts +4 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -4
- package/dist/errors.js.map +1 -1
- package/dist/execution.d.ts +16 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +49 -0
- package/dist/execution.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/login.d.ts +1 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +13 -1
- package/dist/login.js.map +1 -1
- package/dist/mcp-server.d.ts +16 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +172 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/output-file.d.ts +4 -0
- package/dist/output-file.d.ts.map +1 -0
- package/dist/output-file.js +55 -0
- package/dist/output-file.js.map +1 -0
- package/dist/program.d.ts +22 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +443 -44
- package/dist/program.js.map +1 -1
- package/dist/recording-archive.d.ts +62 -0
- package/dist/recording-archive.d.ts.map +1 -0
- package/dist/recording-archive.js +519 -0
- package/dist/recording-archive.js.map +1 -0
- package/dist/recording-sync.d.ts +15 -0
- package/dist/recording-sync.d.ts.map +1 -0
- package/dist/recording-sync.js +49 -0
- package/dist/recording-sync.js.map +1 -0
- package/dist/recordings.d.ts +47 -0
- package/dist/recordings.d.ts.map +1 -0
- package/dist/recordings.js +124 -0
- package/dist/recordings.js.map +1 -0
- package/dist/remote-http.d.ts +23 -0
- package/dist/remote-http.d.ts.map +1 -0
- package/dist/remote-http.js +113 -0
- package/dist/remote-http.js.map +1 -0
- package/dist/remote-meetings.d.ts +58 -0
- package/dist/remote-meetings.d.ts.map +1 -0
- package/dist/remote-meetings.js +151 -0
- package/dist/remote-meetings.js.map +1 -0
- package/dist/store-connection.d.ts +9 -0
- package/dist/store-connection.d.ts.map +1 -0
- package/dist/store-connection.js +39 -0
- package/dist/store-connection.js.map +1 -0
- package/dist/streaming.d.ts +6 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +86 -0
- package/dist/streaming.js.map +1 -0
- package/dist/tokens.d.ts +6 -4
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +48 -18
- package/dist/tokens.js.map +1 -1
- package/dist/watch-commands.d.ts +25 -0
- package/dist/watch-commands.d.ts.map +1 -0
- package/dist/watch-commands.js +52 -0
- package/dist/watch-commands.js.map +1 -0
- package/package.json +22 -12
- package/skills/zoom-cli/SKILL.md +85 -0
package/dist/program.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CliError, createDeadline, createRenderer, exitCodeFor, fieldsOf, GENERIC_FAILURE, projectFields, realSleep, } from "@wirecat/cli-core";
|
|
2
|
+
import { importFiles, listed, MeetingError, pull } from "@wirecat/cli-meetings";
|
|
2
3
|
import { addMeetingCommands } from "@wirecat/cli-meetings/cli";
|
|
3
4
|
import { Command, CommanderError } from "commander";
|
|
5
|
+
import { addZoomSkillCommands } from "./agent-skill.js";
|
|
4
6
|
import { ZoomApiSource } from "./api.js";
|
|
7
|
+
import { addArchiveCommands, } from "./archive-commands.js";
|
|
8
|
+
import { readArchiveTranscripts, searchArchiveTranscripts } from "./archive-transcripts.js";
|
|
9
|
+
import { addStandardCommands, annotateMeetingCommands } from "./commands.js";
|
|
5
10
|
import { profileAccountKey, readConfig, setConfig } from "./config.js";
|
|
6
|
-
import { ZoomError } from "./errors.js";
|
|
11
|
+
import { checkCancelled, ZoomError } from "./errors.js";
|
|
12
|
+
import { bytes, duration, resetCliOptions, startExecution } from "./execution.js";
|
|
7
13
|
import { FilesSource, importFile, readDownloadedFiles, validateDownloadedFiles } from "./files.js";
|
|
8
14
|
import { login } from "./login.js";
|
|
15
|
+
import { getZoomProfile, inspectArchive, inspectArchiveLocks, recoverArchiveLocks, verifyArchive, ZoomRecordings, } from "./recordings.js";
|
|
16
|
+
import { ZoomRemoteMeetings } from "./remote-meetings.js";
|
|
17
|
+
import { emitReceipt } from "./streaming.js";
|
|
9
18
|
import { VERSION } from "./version.js";
|
|
19
|
+
import { addWatchCommand } from "./watch-commands.js";
|
|
10
20
|
const unavailable = async () => {
|
|
11
21
|
throw new ZoomError("configuration_error", "the shared SQLite meeting store is not available yet");
|
|
12
22
|
};
|
|
@@ -25,17 +35,64 @@ const unavailableStore = {
|
|
|
25
35
|
cursor: unavailable,
|
|
26
36
|
setCursor: unavailable,
|
|
27
37
|
};
|
|
28
|
-
const render = (
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
const render = (value, format, deps, fields) => {
|
|
39
|
+
if (fields)
|
|
40
|
+
value = projectFields(value, fields);
|
|
41
|
+
const chunks = [];
|
|
42
|
+
const renderer = createRenderer({
|
|
43
|
+
format: format === "text" ? (deps.outputIsTTY ? "pretty" : "json") : format,
|
|
44
|
+
color: false,
|
|
45
|
+
streams: { data: (text) => chunks.push(`${text}\n`), diagnostic: deps.diagnostic },
|
|
46
|
+
});
|
|
47
|
+
if (format === "jsonl" &&
|
|
48
|
+
typeof value === "object" &&
|
|
49
|
+
value !== null &&
|
|
50
|
+
"items" in value &&
|
|
51
|
+
Array.isArray(value.items))
|
|
52
|
+
renderer.stream(value.items);
|
|
53
|
+
else
|
|
54
|
+
renderer.result(value);
|
|
55
|
+
return chunks.join("");
|
|
32
56
|
};
|
|
33
57
|
export const createProgram = (deps) => {
|
|
34
58
|
let config;
|
|
35
59
|
const getConfig = () => (config ??= readConfig(deps.configPath));
|
|
36
60
|
let connection;
|
|
37
61
|
let parsing = false;
|
|
38
|
-
let
|
|
62
|
+
let execution;
|
|
63
|
+
let fields;
|
|
64
|
+
let activeTokens;
|
|
65
|
+
const currentExecution = () => execution;
|
|
66
|
+
const signal = () => currentExecution()?.deadline.signal ?? deps.signal;
|
|
67
|
+
const fetchWithSignal = (fetcher) => (input, init) => {
|
|
68
|
+
checkCancelled(signal());
|
|
69
|
+
if (program.opts().offline)
|
|
70
|
+
throw new ZoomError("configuration_error", "the command requires network access; remove --offline");
|
|
71
|
+
const operation = signal();
|
|
72
|
+
return fetcher(input, {
|
|
73
|
+
...init,
|
|
74
|
+
signal: operation && init?.signal ? AbortSignal.any([operation, init.signal]) : (operation ?? init?.signal),
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const tokens = () => (activeTokens ??= deps.tokens.withSignal(signal(), fetchWithSignal(deps.tokens.options.fetch)));
|
|
78
|
+
const buffer = (text) => {
|
|
79
|
+
if (!execution)
|
|
80
|
+
throw new ZoomError("configuration_error", "command execution is not initialized");
|
|
81
|
+
execution.output.data(text);
|
|
82
|
+
};
|
|
83
|
+
const guardedStore = (store) => new Proxy(store, {
|
|
84
|
+
get(target, key, receiver) {
|
|
85
|
+
const method = Reflect.get(target, key, receiver);
|
|
86
|
+
return typeof method === "function"
|
|
87
|
+
? async (...args) => {
|
|
88
|
+
checkCancelled(signal());
|
|
89
|
+
const result = await method.apply(target, args);
|
|
90
|
+
checkCancelled(signal());
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
: method;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
39
96
|
let selectedStore = deps.store;
|
|
40
97
|
let selectedAccountId = deps.accountId;
|
|
41
98
|
const requireAccount = () => {
|
|
@@ -50,15 +107,47 @@ export const createProgram = (deps) => {
|
|
|
50
107
|
.option("--json", "one JSON value")
|
|
51
108
|
.option("--jsonl", "one JSON object per item")
|
|
52
109
|
.option("--no-input", "disable interactive login")
|
|
110
|
+
.option("--interactive", "permit browser approval in an interactive terminal")
|
|
111
|
+
.option("--quiet", "suppress non-error diagnostics")
|
|
112
|
+
.option("--fields <paths>", "project result fields, such as id,title")
|
|
113
|
+
.option("--timeout <duration>", "command deadline: reads 30s, login 5m, downloads 1h; watch/MCP have no default", duration)
|
|
114
|
+
.option("--max-output-bytes <bytes>", "bound rendered result bytes (default 4194304; zero is unlimited)", bytes)
|
|
115
|
+
.option("--offline", "refuse network requests")
|
|
53
116
|
.exitOverride()
|
|
54
117
|
.configureOutput({ writeOut: deps.data, writeErr: deps.diagnostic, outputError: () => { } });
|
|
118
|
+
const api = () => {
|
|
119
|
+
if (program.opts().offline)
|
|
120
|
+
throw new ZoomError("configuration_error", "the command requires network access; remove --offline");
|
|
121
|
+
if (!getConfig().clientId)
|
|
122
|
+
throw new ZoomError("configuration_error", "run zm config set zoom.clientId <Public Client ID> first");
|
|
123
|
+
};
|
|
55
124
|
program.hook("preAction", async (_root, action) => {
|
|
56
|
-
const config = getConfig();
|
|
57
125
|
const opts = action.optsWithGlobals();
|
|
58
126
|
if (opts.json && opts.jsonl)
|
|
59
127
|
throw new ZoomError("validation_error", "choose --json or --jsonl");
|
|
60
|
-
|
|
128
|
+
fields = opts.fields === undefined ? undefined : fieldsOf(opts.fields);
|
|
129
|
+
execution = startExecution(action, { signal: deps.signal, sleep: deps.sleep });
|
|
130
|
+
checkCancelled(signal());
|
|
131
|
+
let family = action;
|
|
132
|
+
while (family.parent && family.parent !== program)
|
|
133
|
+
family = family.parent;
|
|
134
|
+
if ([
|
|
135
|
+
"login",
|
|
136
|
+
"commands",
|
|
137
|
+
"complete",
|
|
138
|
+
"doctor",
|
|
139
|
+
"config",
|
|
140
|
+
"session",
|
|
141
|
+
"recordings",
|
|
142
|
+
"remote-meetings",
|
|
143
|
+
"serve",
|
|
144
|
+
"skill",
|
|
145
|
+
"watch",
|
|
146
|
+
].includes(family.name()))
|
|
61
147
|
return;
|
|
148
|
+
const config = getConfig();
|
|
149
|
+
if (opts.offline && action.name() === "pull" && config.source === "api" && !deps.source)
|
|
150
|
+
throw new ZoomError("configuration_error", "API pull requires network access; remove --offline");
|
|
62
151
|
if (!selectedStore) {
|
|
63
152
|
if (!deps.connectStore)
|
|
64
153
|
return unavailable();
|
|
@@ -81,7 +170,32 @@ export const createProgram = (deps) => {
|
|
|
81
170
|
}
|
|
82
171
|
requireAccount();
|
|
83
172
|
});
|
|
84
|
-
const output = (value) =>
|
|
173
|
+
const output = (value) => buffer(render(value, program.opts().json ? "json" : program.opts().jsonl ? "jsonl" : "text", deps, fields));
|
|
174
|
+
const outputItems = async (values) => {
|
|
175
|
+
const format = program.opts().jsonl ? "jsonl" : program.opts().json || !deps.outputIsTTY ? "json" : "text";
|
|
176
|
+
const metadata = listed([]);
|
|
177
|
+
delete metadata.items;
|
|
178
|
+
let count = 0;
|
|
179
|
+
if (format === "json")
|
|
180
|
+
buffer('{"items":[');
|
|
181
|
+
for await (const value of values) {
|
|
182
|
+
checkCancelled(signal());
|
|
183
|
+
const projected = fields ? projectFields(listed([value]), fields).items[0] : value;
|
|
184
|
+
if (format === "json") {
|
|
185
|
+
if (count)
|
|
186
|
+
buffer(",");
|
|
187
|
+
buffer(render(projected, "json", deps).trimEnd());
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
buffer(render(projected, format, deps));
|
|
191
|
+
count++;
|
|
192
|
+
}
|
|
193
|
+
metadata.limit = count;
|
|
194
|
+
if (format === "json")
|
|
195
|
+
buffer(`],${JSON.stringify(metadata).slice(1)}\n`);
|
|
196
|
+
else if (format === "text")
|
|
197
|
+
buffer(render(count ? metadata : listed([]), "text", deps));
|
|
198
|
+
};
|
|
85
199
|
const cfg = program.command("config").description("local Zoom configuration");
|
|
86
200
|
cfg
|
|
87
201
|
.command("show")
|
|
@@ -105,13 +219,25 @@ export const createProgram = (deps) => {
|
|
|
105
219
|
const config = getConfig();
|
|
106
220
|
if (!config.clientId)
|
|
107
221
|
throw new ZoomError("configuration_error", "run zm config set zoom.clientId <Public Client ID> first");
|
|
108
|
-
|
|
222
|
+
const globals = program.opts();
|
|
223
|
+
if (globals.offline)
|
|
224
|
+
throw new ZoomError("configuration_error", "login requires network access; remove --offline");
|
|
225
|
+
const headless = deps.interactive === false ||
|
|
226
|
+
Boolean(deps.env?.CI) ||
|
|
227
|
+
(deps.interactive !== undefined &&
|
|
228
|
+
(globals.json || globals.jsonl || deps.outputIsTTY === false) &&
|
|
229
|
+
!globals.interactive);
|
|
230
|
+
if (globals.input === false || headless)
|
|
109
231
|
throw new ZoomError("configuration_error", "login requires browser approval; remove --no-input");
|
|
110
232
|
output(await login({
|
|
111
|
-
tokens:
|
|
233
|
+
tokens: tokens(),
|
|
234
|
+
signal: signal(),
|
|
112
235
|
openBrowser: (url) => deps.openBrowser(url, opts.browser),
|
|
113
236
|
readPaste: deps.readPaste,
|
|
114
|
-
showUrl: (url) =>
|
|
237
|
+
showUrl: (url) => {
|
|
238
|
+
if (!program.opts().quiet)
|
|
239
|
+
deps.diagnostic(`Open this URL to approve Zoom login:\n${url}\n`);
|
|
240
|
+
},
|
|
115
241
|
}));
|
|
116
242
|
});
|
|
117
243
|
let activeSource;
|
|
@@ -121,7 +247,7 @@ export const createProgram = (deps) => {
|
|
|
121
247
|
activeSource ??= (async () => {
|
|
122
248
|
const config = getConfig();
|
|
123
249
|
if (config.source === "api")
|
|
124
|
-
return new ZoomApiSource(
|
|
250
|
+
return new ZoomApiSource(tokens(), fetchWithSignal(deps.fetch));
|
|
125
251
|
if (!config.filesFolder)
|
|
126
252
|
throw new ZoomError("configuration_error", "set files.folder before pulling from files");
|
|
127
253
|
return new FilesSource(await (deps.readFiles ?? readDownloadedFiles)(config.filesFolder), config.filesTimezone);
|
|
@@ -157,9 +283,9 @@ export const createProgram = (deps) => {
|
|
|
157
283
|
return (await getSource()).files(id);
|
|
158
284
|
},
|
|
159
285
|
};
|
|
160
|
-
|
|
286
|
+
const meetingCommands = {
|
|
161
287
|
get store() {
|
|
162
|
-
return selectedStore ?? unavailableStore;
|
|
288
|
+
return guardedStore(selectedStore ?? unavailableStore);
|
|
163
289
|
},
|
|
164
290
|
rootIngestion: true,
|
|
165
291
|
get accountId() {
|
|
@@ -172,24 +298,305 @@ export const createProgram = (deps) => {
|
|
|
172
298
|
},
|
|
173
299
|
now: deps.now,
|
|
174
300
|
write: (value, format) => {
|
|
175
|
-
|
|
301
|
+
buffer(render(value, format, deps, fields));
|
|
302
|
+
},
|
|
303
|
+
writeFile: deps.writeFile
|
|
304
|
+
? async (path, content) => {
|
|
305
|
+
checkCancelled(signal());
|
|
306
|
+
await deps.writeFile?.(path, content, { signal: signal() });
|
|
307
|
+
checkCancelled(signal());
|
|
308
|
+
}
|
|
309
|
+
: undefined,
|
|
310
|
+
};
|
|
311
|
+
addMeetingCommands(program, meetingCommands);
|
|
312
|
+
addStandardCommands(program, {
|
|
313
|
+
output,
|
|
314
|
+
discovery: (value) => buffer(render(value, program.opts().json ? "json" : program.opts().jsonl ? "jsonl" : "text", deps)),
|
|
315
|
+
fields: () => fields,
|
|
316
|
+
raw: buffer,
|
|
317
|
+
doctor: () => {
|
|
318
|
+
const config = getConfig();
|
|
319
|
+
return {
|
|
320
|
+
version: VERSION,
|
|
321
|
+
runtime: { node: process.versions.node },
|
|
322
|
+
config: { readable: true, path: deps.configPath, source: config.source },
|
|
323
|
+
dataProfile: config.accountProfile,
|
|
324
|
+
authentication: { configured: config.clientId !== null, state: "not_checked" },
|
|
325
|
+
database: "not_checked",
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
sessionStatus: async (check) => {
|
|
329
|
+
const config = getConfig();
|
|
330
|
+
if (!config.clientId)
|
|
331
|
+
return { configured: false, state: "not_configured", dataProfile: config.accountProfile, verified: false };
|
|
332
|
+
if (check) {
|
|
333
|
+
if (program.opts().offline)
|
|
334
|
+
throw new ZoomError("configuration_error", "session verification requires network access; remove --offline");
|
|
335
|
+
api();
|
|
336
|
+
return {
|
|
337
|
+
configured: true,
|
|
338
|
+
state: "verified",
|
|
339
|
+
dataProfile: config.accountProfile,
|
|
340
|
+
verified: true,
|
|
341
|
+
profile: await (deps.checkSession
|
|
342
|
+
? deps.checkSession({ signal: signal() })
|
|
343
|
+
: getZoomProfile(tokens(), fetchWithSignal(deps.fetch), { signal: signal() })),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
const saved = deps.tokens.load();
|
|
347
|
+
return {
|
|
348
|
+
configured: true,
|
|
349
|
+
state: saved ? (saved.expiresAt > (deps.now?.() ?? Date.now()) ? "saved" : "expired") : "missing",
|
|
350
|
+
dataProfile: config.accountProfile,
|
|
351
|
+
verified: false,
|
|
352
|
+
...(saved ? { expiresAt: saved.expiresAt, scopes: saved.scopes } : {}),
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
let watchConfig;
|
|
357
|
+
let watchSince;
|
|
358
|
+
const watchSleep = deps.sleep;
|
|
359
|
+
const receipt = async (value) => {
|
|
360
|
+
const text = render(value, program.opts().jsonl ? "jsonl" : "text", deps, fields);
|
|
361
|
+
const operation = signal();
|
|
362
|
+
const grace = operation.aborted ? createDeadline({ timeoutMs: 1000 }) : undefined;
|
|
363
|
+
try {
|
|
364
|
+
await emitReceipt(text, program.opts().maxOutputBytes, async (serialized) => {
|
|
365
|
+
if (deps.streamData)
|
|
366
|
+
await deps.streamData(serialized, grace?.signal ?? operation);
|
|
367
|
+
else
|
|
368
|
+
deps.data(serialized);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
finally {
|
|
372
|
+
grace?.dispose();
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
addWatchCommand(program, {
|
|
376
|
+
signal: () => signal(),
|
|
377
|
+
receipt,
|
|
378
|
+
outputIsTTY: deps.outputIsTTY,
|
|
379
|
+
sleep: watchSleep ? (ms, abort) => watchSleep(ms, abort, "retry") : undefined,
|
|
380
|
+
prepare: (folder, flags) => {
|
|
381
|
+
const config = { ...getConfig() };
|
|
382
|
+
const source = flags.source ?? (folder !== undefined ? "files" : config.source);
|
|
383
|
+
const plan = {
|
|
384
|
+
source,
|
|
385
|
+
since: flags.since,
|
|
386
|
+
lookbackDays: flags.lookbackDays ?? 0,
|
|
387
|
+
intervalMs: flags.interval,
|
|
388
|
+
folder: folder ?? config.filesFolder ?? undefined,
|
|
389
|
+
};
|
|
390
|
+
if (source === "files") {
|
|
391
|
+
if (flags.since !== undefined || flags.lookbackDays !== undefined)
|
|
392
|
+
throw new ZoomError("validation_error", "since and lookback days apply only to API watching");
|
|
393
|
+
if (!plan.folder?.trim())
|
|
394
|
+
throw new ZoomError("configuration_error", "set files.folder or provide a folder for file watching");
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
if (folder !== undefined)
|
|
398
|
+
throw new ZoomError("validation_error", "API watching does not accept a folder");
|
|
399
|
+
if (program.opts().offline)
|
|
400
|
+
throw new ZoomError("configuration_error", "API watching requires network access; remove --offline");
|
|
401
|
+
if (!deps.source) {
|
|
402
|
+
if (!config.clientId)
|
|
403
|
+
throw new ZoomError("configuration_error", "run zm config set zoom.clientId <Public Client ID> first");
|
|
404
|
+
if (tokens().options.clientId !== config.clientId)
|
|
405
|
+
throw new ZoomError("configuration_error", "the OAuth client changed before watch started; retry with matching configuration");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (!deps.store && !config.accountProfile)
|
|
409
|
+
throw new ZoomError("configuration_error", "select a local profile with zm config set account.profile <label>");
|
|
410
|
+
watchConfig = config;
|
|
411
|
+
watchSince = plan.since;
|
|
412
|
+
return plan;
|
|
176
413
|
},
|
|
414
|
+
ingest: async (plan, operation) => {
|
|
415
|
+
checkCancelled(operation);
|
|
416
|
+
const config = watchConfig;
|
|
417
|
+
if (!config)
|
|
418
|
+
throw new ZoomError("configuration_error", "watch configuration is unavailable");
|
|
419
|
+
let cycle;
|
|
420
|
+
let failure;
|
|
421
|
+
let result;
|
|
422
|
+
try {
|
|
423
|
+
if (!deps.store) {
|
|
424
|
+
if (!deps.connectStore)
|
|
425
|
+
return unavailable();
|
|
426
|
+
if (!config.accountProfile)
|
|
427
|
+
throw new ZoomError("configuration_error", "select a local profile with zm config set account.profile <label>");
|
|
428
|
+
try {
|
|
429
|
+
cycle = await deps.connectStore({
|
|
430
|
+
profile: config.accountProfile,
|
|
431
|
+
externalId: profileAccountKey(config.accountProfile),
|
|
432
|
+
intent: "ingestion",
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
if (error instanceof CliError || error instanceof MeetingError)
|
|
437
|
+
throw error;
|
|
438
|
+
throw new ZoomError("configuration_error", "could not open the shared meeting store");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
checkCancelled(operation);
|
|
442
|
+
const accountId = cycle?.accountId ?? deps.accountId;
|
|
443
|
+
if (!Number.isSafeInteger(accountId) || !accountId || accountId < 1)
|
|
444
|
+
throw new ZoomError("configuration_error", "the selected meeting account is unavailable");
|
|
445
|
+
const store = guardedStore(cycle?.store ?? deps.store ?? unavailableStore);
|
|
446
|
+
if (plan.source === "files") {
|
|
447
|
+
const files = validateDownloadedFiles(await (deps.readFiles ?? readDownloadedFiles)(plan.folder ?? ""), config.filesTimezone);
|
|
448
|
+
checkCancelled(operation);
|
|
449
|
+
result = await importFiles(files.map((file) => importFile(file, accountId, config.filesTimezone)), store, deps.now?.());
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
const original = deps.source ??
|
|
453
|
+
new ZoomApiSource(tokens(), fetchWithSignal(deps.fetch), {
|
|
454
|
+
now: deps.now,
|
|
455
|
+
sleep: (ms, abort) => (deps.sleep ?? realSleep)(ms, AbortSignal.any([operation, abort]), "retry"),
|
|
456
|
+
});
|
|
457
|
+
let sourceFailure;
|
|
458
|
+
const source = new Proxy(original, {
|
|
459
|
+
get(target, key, receiver) {
|
|
460
|
+
const method = Reflect.get(target, key, receiver);
|
|
461
|
+
return typeof method === "function"
|
|
462
|
+
? async (...args) => {
|
|
463
|
+
if (sourceFailure)
|
|
464
|
+
throw sourceFailure;
|
|
465
|
+
checkCancelled(operation);
|
|
466
|
+
try {
|
|
467
|
+
const value = await method.apply(target, args);
|
|
468
|
+
checkCancelled(operation);
|
|
469
|
+
return value;
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
if (error instanceof CliError && error.code === "outcome_unknown") {
|
|
473
|
+
sourceFailure = error;
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
checkCancelled(operation);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
: method;
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
let cursorAdvanced = false;
|
|
484
|
+
const trackingStore = new Proxy(store, {
|
|
485
|
+
get(target, key, receiver) {
|
|
486
|
+
if (key === "setCursor")
|
|
487
|
+
return async (...args) => {
|
|
488
|
+
await target.setCursor(...args);
|
|
489
|
+
cursorAdvanced = true;
|
|
490
|
+
};
|
|
491
|
+
return Reflect.get(target, key, receiver);
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
result = await pull(source, trackingStore, {
|
|
495
|
+
accountId,
|
|
496
|
+
since: watchSince,
|
|
497
|
+
lookbackMs: plan.lookbackDays * 86400000,
|
|
498
|
+
now: deps.now?.(),
|
|
499
|
+
});
|
|
500
|
+
if (sourceFailure)
|
|
501
|
+
throw sourceFailure;
|
|
502
|
+
checkCancelled(operation);
|
|
503
|
+
if (cursorAdvanced)
|
|
504
|
+
watchSince = undefined;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
failure = error;
|
|
509
|
+
}
|
|
510
|
+
finally {
|
|
511
|
+
if (cycle) {
|
|
512
|
+
try {
|
|
513
|
+
await cycle.close();
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
failure ??= new ZoomError("configuration_error", "could not close the shared meeting store");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (failure)
|
|
521
|
+
throw failure;
|
|
522
|
+
if (!result)
|
|
523
|
+
return unavailable();
|
|
524
|
+
return result;
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
const retrySleep = deps.sleep;
|
|
528
|
+
const remoteOptions = {
|
|
529
|
+
now: deps.now,
|
|
530
|
+
sleep: retrySleep ? (ms, abort) => retrySleep(ms, abort, "retry") : undefined,
|
|
531
|
+
};
|
|
532
|
+
addArchiveCommands(program, {
|
|
533
|
+
output,
|
|
534
|
+
outputItems,
|
|
535
|
+
transcripts: deps.archiveTranscripts ?? { read: readArchiveTranscripts, search: searchArchiveTranscripts },
|
|
536
|
+
receipt,
|
|
537
|
+
outputIsTTY: deps.outputIsTTY,
|
|
538
|
+
api,
|
|
539
|
+
signal,
|
|
540
|
+
now: deps.now ?? Date.now,
|
|
541
|
+
recordings: () => deps.recordings ?? new ZoomRecordings(tokens(), fetchWithSignal(deps.fetch), remoteOptions),
|
|
542
|
+
remoteMeetings: () => deps.remoteMeetings ?? new ZoomRemoteMeetings(tokens(), fetchWithSignal(deps.fetch), remoteOptions),
|
|
543
|
+
archive: deps.archive ?? {
|
|
544
|
+
inspect: inspectArchive,
|
|
545
|
+
verify: verifyArchive,
|
|
546
|
+
locks: inspectArchiveLocks,
|
|
547
|
+
recover: recoverArchiveLocks,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
addZoomSkillCommands(program, () => {
|
|
551
|
+
const renderer = createRenderer({
|
|
552
|
+
format: program.opts().json ? "json" : program.opts().jsonl ? "jsonl" : deps.outputIsTTY ? "pretty" : "json",
|
|
553
|
+
color: false,
|
|
554
|
+
quiet: Boolean(program.opts().quiet),
|
|
555
|
+
streams: { data: buffer, diagnostic: deps.diagnostic },
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
renderer: { ...renderer, result: output, stream: (items) => output({ items: [...items] }) },
|
|
559
|
+
streams: { data: (text) => buffer(`${text}\n`), diagnostic: deps.diagnostic },
|
|
560
|
+
env: deps.env,
|
|
561
|
+
};
|
|
177
562
|
});
|
|
563
|
+
program
|
|
564
|
+
.command("serve")
|
|
565
|
+
.description("serve read-only meeting tools over MCP stdio")
|
|
566
|
+
.requiredOption("--mcp", "use the MCP JSON-RPC protocol")
|
|
567
|
+
.action(async () => {
|
|
568
|
+
const opts = program.opts();
|
|
569
|
+
if (opts.json || opts.jsonl || opts.fields)
|
|
570
|
+
throw new ZoomError("validation_error", "MCP stdio controls its own output; omit --json, --jsonl and --fields");
|
|
571
|
+
if (!deps.serveMcp)
|
|
572
|
+
throw new ZoomError("configuration_error", "the MCP server adapter is not available");
|
|
573
|
+
await deps.serveMcp({ signal: signal(), maxResultBytes: program.opts().maxOutputBytes });
|
|
574
|
+
});
|
|
575
|
+
annotateMeetingCommands(program);
|
|
178
576
|
const parseAsync = program.parseAsync.bind(program);
|
|
179
577
|
program.parseAsync = async (...args) => {
|
|
180
578
|
if (parsing)
|
|
181
579
|
throw new ZoomError("validation_error", "concurrent command parsing is unavailable");
|
|
580
|
+
resetCliOptions(program);
|
|
182
581
|
parsing = true;
|
|
183
582
|
config = undefined;
|
|
184
583
|
activeSource = undefined;
|
|
185
|
-
|
|
584
|
+
execution = undefined;
|
|
585
|
+
activeTokens = undefined;
|
|
586
|
+
watchConfig = undefined;
|
|
587
|
+
watchSince = undefined;
|
|
588
|
+
fields = undefined;
|
|
186
589
|
let result = program;
|
|
187
590
|
let failed = false;
|
|
188
591
|
let failure;
|
|
592
|
+
const running = parseAsync(...args);
|
|
593
|
+
void running.catch(() => { });
|
|
189
594
|
try {
|
|
190
|
-
|
|
595
|
+
const active = currentExecution();
|
|
596
|
+
result = active ? await active.deadline.race(() => running) : await running;
|
|
191
597
|
}
|
|
192
598
|
catch (error) {
|
|
599
|
+
await running.catch(() => { });
|
|
193
600
|
failed = true;
|
|
194
601
|
failure = error;
|
|
195
602
|
}
|
|
@@ -210,14 +617,23 @@ export const createProgram = (deps) => {
|
|
|
210
617
|
}
|
|
211
618
|
}
|
|
212
619
|
}
|
|
620
|
+
if (!failed) {
|
|
621
|
+
try {
|
|
622
|
+
checkCancelled(signal());
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
failed = true;
|
|
626
|
+
failure = error;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
currentExecution()?.deadline.dispose();
|
|
213
630
|
parsing = false;
|
|
214
631
|
if (failed) {
|
|
215
|
-
|
|
632
|
+
currentExecution()?.output.discard();
|
|
216
633
|
throw failure;
|
|
217
634
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
meetingOutput = [];
|
|
635
|
+
currentExecution()?.output.flush(deps.data);
|
|
636
|
+
execution = undefined;
|
|
221
637
|
return result;
|
|
222
638
|
};
|
|
223
639
|
return program;
|
|
@@ -233,28 +649,11 @@ export const run = async (args, deps) => {
|
|
|
233
649
|
"code" in error &&
|
|
234
650
|
["commander.helpDisplayed", "commander.help", "commander.version"].includes(String(error.code)))
|
|
235
651
|
return 0;
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
? "validation_error"
|
|
240
|
-
: "generic_failure";
|
|
241
|
-
const message = error instanceof ZoomError || error instanceof MeetingError
|
|
242
|
-
? error.message
|
|
243
|
-
: "command failed; use --help for usage";
|
|
652
|
+
const typed = error instanceof CliError || error instanceof MeetingError;
|
|
653
|
+
const code = typed ? error.code : error instanceof CommanderError ? "validation_error" : "generic_failure";
|
|
654
|
+
const message = typed ? error.message : "command failed; use --help for usage";
|
|
244
655
|
deps.diagnostic(`${JSON.stringify({ error: { code, message } })}\n`);
|
|
245
|
-
return code === "
|
|
246
|
-
? 3
|
|
247
|
-
: code === "authentication_error"
|
|
248
|
-
? 4
|
|
249
|
-
: code === "provider_error"
|
|
250
|
-
? 11
|
|
251
|
-
: code === "invalid_response"
|
|
252
|
-
? 13
|
|
253
|
-
: code === "not_found"
|
|
254
|
-
? 6
|
|
255
|
-
: code === "generic_failure"
|
|
256
|
-
? 1
|
|
257
|
-
: 2;
|
|
656
|
+
return code === "generic_failure" ? GENERIC_FAILURE : exitCodeFor(code);
|
|
258
657
|
}
|
|
259
658
|
};
|
|
260
659
|
//# sourceMappingURL=program.js.map
|