@decibelsystems/tools 2.2.0-beta.0 → 3.0.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/CHANGELOG.md +160 -0
- package/dist/agentPresence.d.ts +80 -0
- package/dist/agentPresence.d.ts.map +1 -1
- package/dist/agentPresence.js +177 -0
- package/dist/agentPresence.js.map +1 -1
- package/dist/agentic/index.d.ts +18 -8
- package/dist/agentic/index.d.ts.map +1 -1
- package/dist/agentic/index.js +18 -144
- package/dist/agentic/index.js.map +1 -1
- package/dist/agentic/types.d.ts +106 -0
- package/dist/agentic/types.d.ts.map +1 -1
- package/dist/agentic/types.js +77 -1
- package/dist/agentic/types.js.map +1 -1
- package/dist/architectAdrs.d.ts +14 -2
- package/dist/architectAdrs.d.ts.map +1 -1
- package/dist/architectAdrs.js +9 -3
- package/dist/architectAdrs.js.map +1 -1
- package/dist/client/transports.d.ts.map +1 -1
- package/dist/client/transports.js +13 -3
- package/dist/client/transports.js.map +1 -1
- package/dist/daemon.d.ts +19 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +181 -38
- package/dist/daemon.js.map +1 -1
- package/dist/daemonConfig.d.ts +24 -0
- package/dist/daemonConfig.d.ts.map +1 -1
- package/dist/daemonConfig.js +46 -12
- package/dist/daemonConfig.js.map +1 -1
- package/dist/decibelPaths.d.ts.map +1 -1
- package/dist/decibelPaths.js +39 -6
- package/dist/decibelPaths.js.map +1 -1
- package/dist/domain/issue.d.ts +77 -0
- package/dist/domain/issue.d.ts.map +1 -0
- package/dist/domain/issue.js +134 -0
- package/dist/domain/issue.js.map +1 -0
- package/dist/domain/issueCodec.d.ts +39 -0
- package/dist/domain/issueCodec.d.ts.map +1 -0
- package/dist/domain/issueCodec.js +298 -0
- package/dist/domain/issueCodec.js.map +1 -0
- package/dist/domain/issueRepository.d.ts +91 -0
- package/dist/domain/issueRepository.d.ts.map +1 -0
- package/dist/domain/issueRepository.js +257 -0
- package/dist/domain/issueRepository.js.map +1 -0
- package/dist/facades/definitions.d.ts +0 -1
- package/dist/facades/definitions.d.ts.map +1 -1
- package/dist/facades/definitions.js +94 -113
- package/dist/facades/definitions.js.map +1 -1
- package/dist/facades/index.d.ts +2 -1
- package/dist/facades/index.d.ts.map +1 -1
- package/dist/facades/index.js +1 -1
- package/dist/facades/index.js.map +1 -1
- package/dist/facades/types.d.ts +17 -0
- package/dist/facades/types.d.ts.map +1 -1
- package/dist/httpArgs.d.ts +11 -0
- package/dist/httpArgs.d.ts.map +1 -0
- package/dist/httpArgs.js +39 -0
- package/dist/httpArgs.js.map +1 -0
- package/dist/httpServer.d.ts +1 -12
- package/dist/httpServer.d.ts.map +1 -1
- package/dist/httpServer.js +97 -96
- package/dist/httpServer.js.map +1 -1
- package/dist/kernel.d.ts +46 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +311 -26
- package/dist/kernel.js.map +1 -1
- package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
- package/dist/lib/agent-services/assumptions.js +8 -2
- package/dist/lib/agent-services/assumptions.js.map +1 -1
- package/dist/lib/atomicWrite.d.ts +26 -0
- package/dist/lib/atomicWrite.d.ts.map +1 -0
- package/dist/lib/atomicWrite.js +73 -0
- package/dist/lib/atomicWrite.js.map +1 -0
- package/dist/lib/envelope.d.ts +31 -0
- package/dist/lib/envelope.d.ts.map +1 -0
- package/dist/lib/envelope.js +80 -0
- package/dist/lib/envelope.js.map +1 -0
- package/dist/lib/fileLock.d.ts +38 -0
- package/dist/lib/fileLock.d.ts.map +1 -0
- package/dist/lib/fileLock.js +124 -0
- package/dist/lib/fileLock.js.map +1 -0
- package/dist/lib/recordIdAllocator.d.ts +86 -0
- package/dist/lib/recordIdAllocator.d.ts.map +1 -0
- package/dist/lib/recordIdAllocator.js +161 -0
- package/dist/lib/recordIdAllocator.js.map +1 -0
- package/dist/projectRegistry.d.ts.map +1 -1
- package/dist/projectRegistry.js +11 -0
- package/dist/projectRegistry.js.map +1 -1
- package/dist/runtime/circuitBreaker.d.ts +95 -0
- package/dist/runtime/circuitBreaker.d.ts.map +1 -0
- package/dist/runtime/circuitBreaker.js +178 -0
- package/dist/runtime/circuitBreaker.js.map +1 -0
- package/dist/runtime/ensureRuntime.d.ts +103 -0
- package/dist/runtime/ensureRuntime.d.ts.map +1 -0
- package/dist/runtime/ensureRuntime.js +281 -0
- package/dist/runtime/ensureRuntime.js.map +1 -0
- package/dist/runtime/extensions.d.ts +57 -0
- package/dist/runtime/extensions.d.ts.map +1 -0
- package/dist/runtime/extensions.js +216 -0
- package/dist/runtime/extensions.js.map +1 -0
- package/dist/runtime/localHttp.d.ts +24 -0
- package/dist/runtime/localHttp.d.ts.map +1 -0
- package/dist/runtime/localHttp.js +84 -0
- package/dist/runtime/localHttp.js.map +1 -0
- package/dist/runtime/projectResolution.d.ts +29 -0
- package/dist/runtime/projectResolution.d.ts.map +1 -0
- package/dist/runtime/projectResolution.js +67 -0
- package/dist/runtime/projectResolution.js.map +1 -0
- package/dist/runtime/protocol.d.ts +62 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +74 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/sentinelIssues.d.ts +26 -2
- package/dist/sentinelIssues.d.ts.map +1 -1
- package/dist/sentinelIssues.js +125 -64
- package/dist/sentinelIssues.js.map +1 -1
- package/dist/server.js +59 -12
- package/dist/server.js.map +1 -1
- package/dist/tools/agentic/index.d.ts.map +1 -1
- package/dist/tools/agentic/index.js +28 -2
- package/dist/tools/agentic/index.js.map +1 -1
- package/dist/tools/architect/index.d.ts.map +1 -1
- package/dist/tools/architect/index.js +7 -2
- package/dist/tools/architect/index.js.map +1 -1
- package/dist/tools/context.d.ts +10 -5
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +45 -18
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
- package/dist/tools/coordinator/coordinator.js +48 -19
- package/dist/tools/coordinator/coordinator.js.map +1 -1
- package/dist/tools/crit.d.ts +2 -1
- package/dist/tools/crit.d.ts.map +1 -1
- package/dist/tools/crit.js +19 -4
- package/dist/tools/crit.js.map +1 -1
- package/dist/tools/designer/index.d.ts.map +1 -1
- package/dist/tools/designer/index.js +4 -3
- package/dist/tools/designer/index.js.map +1 -1
- package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
- package/dist/tools/designer/lateral-tools.js +4 -2
- package/dist/tools/designer/lateral-tools.js.map +1 -1
- package/dist/tools/designer.d.ts +2 -1
- package/dist/tools/designer.d.ts.map +1 -1
- package/dist/tools/designer.js +24 -12
- package/dist/tools/designer.js.map +1 -1
- package/dist/tools/dojo.d.ts +3 -2
- package/dist/tools/dojo.d.ts.map +1 -1
- package/dist/tools/dojo.js +64 -22
- package/dist/tools/dojo.js.map +1 -1
- package/dist/tools/friction.d.ts.map +1 -1
- package/dist/tools/friction.js +16 -6
- package/dist/tools/friction.js.map +1 -1
- package/dist/tools/guardian.d.ts.map +1 -1
- package/dist/tools/guardian.js +6 -2
- package/dist/tools/guardian.js.map +1 -1
- package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
- package/dist/tools/hygiene/oracle-hygiene.js +8 -2
- package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +27 -18
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/learnings.d.ts +2 -1
- package/dist/tools/learnings.d.ts.map +1 -1
- package/dist/tools/learnings.js +31 -15
- package/dist/tools/learnings.js.map +1 -1
- package/dist/tools/oracle.d.ts.map +1 -1
- package/dist/tools/oracle.js +19 -6
- package/dist/tools/oracle.js.map +1 -1
- package/dist/tools/policy.d.ts +2 -1
- package/dist/tools/policy.d.ts.map +1 -1
- package/dist/tools/policy.js +28 -9
- package/dist/tools/policy.js.map +1 -1
- package/dist/tools/postoffice/index.d.ts +10 -0
- package/dist/tools/postoffice/index.d.ts.map +1 -0
- package/dist/tools/postoffice/index.js +218 -0
- package/dist/tools/postoffice/index.js.map +1 -0
- package/dist/tools/postoffice.d.ts +78 -0
- package/dist/tools/postoffice.d.ts.map +1 -0
- package/dist/tools/postoffice.js +199 -0
- package/dist/tools/postoffice.js.map +1 -0
- package/dist/tools/provenance.d.ts +7 -0
- package/dist/tools/provenance.d.ts.map +1 -1
- package/dist/tools/provenance.js +42 -16
- package/dist/tools/provenance.js.map +1 -1
- package/dist/tools/sentinel/index.d.ts +1 -0
- package/dist/tools/sentinel/index.d.ts.map +1 -1
- package/dist/tools/sentinel/index.js +88 -4
- package/dist/tools/sentinel/index.js.map +1 -1
- package/dist/tools/sentinel.d.ts +102 -1
- package/dist/tools/sentinel.d.ts.map +1 -1
- package/dist/tools/sentinel.js +544 -328
- package/dist/tools/sentinel.js.map +1 -1
- package/dist/tools/shared/index.d.ts +2 -1
- package/dist/tools/shared/index.d.ts.map +1 -1
- package/dist/tools/shared/index.js +2 -1
- package/dist/tools/shared/index.js.map +1 -1
- package/dist/tools/shared/response.d.ts +24 -0
- package/dist/tools/shared/response.d.ts.map +1 -1
- package/dist/tools/shared/response.js +61 -7
- package/dist/tools/shared/response.js.map +1 -1
- package/dist/tools/shared/storeRead.d.ts +153 -0
- package/dist/tools/shared/storeRead.d.ts.map +1 -0
- package/dist/tools/shared/storeRead.js +242 -0
- package/dist/tools/shared/storeRead.js.map +1 -0
- package/dist/tools/testSpec.d.ts +3 -2
- package/dist/tools/testSpec.d.ts.map +1 -1
- package/dist/tools/testSpec.js +51 -14
- package/dist/tools/testSpec.js.map +1 -1
- package/dist/tools/vector.d.ts +33 -1
- package/dist/tools/vector.d.ts.map +1 -1
- package/dist/tools/vector.js +31 -5
- package/dist/tools/vector.js.map +1 -1
- package/dist/tools/zoom/index.d.ts +15 -0
- package/dist/tools/zoom/index.d.ts.map +1 -0
- package/dist/tools/zoom/index.js +174 -0
- package/dist/tools/zoom/index.js.map +1 -0
- package/dist/tools/zoom.d.ts +234 -0
- package/dist/tools/zoom.d.ts.map +1 -0
- package/dist/tools/zoom.js +627 -0
- package/dist/tools/zoom.js.map +1 -0
- package/dist/transports/bridge.d.ts +1 -1
- package/dist/transports/bridge.d.ts.map +1 -1
- package/dist/transports/bridge.js +2 -0
- package/dist/transports/bridge.js.map +1 -1
- package/dist/transports/http.d.ts +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +3 -1
- package/dist/transports/http.js.map +1 -1
- package/dist/transports/index.d.ts +1 -0
- package/dist/transports/index.d.ts.map +1 -1
- package/dist/transports/index.js +1 -0
- package/dist/transports/index.js.map +1 -1
- package/dist/transports/mcp.d.ts +1 -1
- package/dist/transports/mcp.d.ts.map +1 -1
- package/dist/transports/mcp.js +7 -3
- package/dist/transports/mcp.js.map +1 -1
- package/dist/transports/stdio.d.ts +1 -1
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +2 -0
- package/dist/transports/stdio.js.map +1 -1
- package/dist/transports/thinStdio.d.ts +22 -0
- package/dist/transports/thinStdio.d.ts.map +1 -0
- package/dist/transports/thinStdio.js +218 -0
- package/dist/transports/thinStdio.js.map +1 -0
- package/dist/transports/types.d.ts +8 -1
- package/dist/transports/types.d.ts.map +1 -1
- package/dist/types/agent-services.d.ts +7 -0
- package/dist/types/agent-services.d.ts.map +1 -1
- package/package.json +9 -4
- package/templates/com.decibel.daemon.plist +6 -1
- package/dist/tools/deck.d.ts +0 -11
- package/dist/tools/deck.d.ts.map +0 -1
- package/dist/tools/deck.js +0 -1656
- package/dist/tools/deck.js.map +0 -1
- package/dist/tools/mother.d.ts +0 -11
- package/dist/tools/mother.d.ts.map +0 -1
- package/dist/tools/mother.js +0 -503
- package/dist/tools/mother.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -3
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -189
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/senken.d.ts +0 -11
- package/dist/tools/senken.d.ts.map +0 -1
- package/dist/tools/senken.js +0 -482
- package/dist/tools/senken.js.map +0 -1
- package/dist/tools/terminal.d.ts +0 -17
- package/dist/tools/terminal.d.ts.map +0 -1
- package/dist/tools/terminal.js +0 -968
- package/dist/tools/terminal.js.map +0 -1
- package/dist/tools/toolsIndex.d.ts +0 -5
- package/dist/tools/toolsIndex.d.ts.map +0 -1
- package/dist/tools/toolsIndex.js +0 -37
- package/dist/tools/toolsIndex.js.map +0 -1
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Zoom AI Companion meeting-summary ingestion — EPIC-0036
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Ported from plasiv/bin/pull-zoom-summaries.py (424 lines, stdlib-only Python).
|
|
5
|
+
// The Python stays where it is: a crontab entry outside that repo still points
|
|
6
|
+
// at its path, and its fate is Ben's call, not this port's.
|
|
7
|
+
//
|
|
8
|
+
// This is a REWRITE, not a translation. The nightly cron driving the original
|
|
9
|
+
// has never once succeeded — 18 log lines, all "Operation not permitted", from
|
|
10
|
+
// cron lacking macOS TCC Full Disk Access to /Volumes/Ashitaka plus
|
|
11
|
+
// /usr/bin/python3 resolving through the Xcode shim. Every summary on disk was
|
|
12
|
+
// pulled by hand, and the routed path has been run manually about once. So the
|
|
13
|
+
// source is a specification, not a proven implementation, and the four things
|
|
14
|
+
// it got wrong (no token refresh, no backoff, uuid-only dedup, first-rule-wins
|
|
15
|
+
// routing) are fixed here rather than carried across.
|
|
16
|
+
//
|
|
17
|
+
// What IS carried across byte-identically, because dedup depends on it: the
|
|
18
|
+
// five frontmatter keys, the filename convention, and the practice of reading
|
|
19
|
+
// meeting_uuid back out of the first 600 characters of each file on disk.
|
|
20
|
+
// Change any of those and a re-pull duplicates everything already written.
|
|
21
|
+
//
|
|
22
|
+
// Zero new npm deps — Node >=18 global fetch plus fs.
|
|
23
|
+
// ============================================================================
|
|
24
|
+
import fs from 'fs';
|
|
25
|
+
import path from 'path';
|
|
26
|
+
import os from 'os';
|
|
27
|
+
import { loadConfig } from '../daemonConfig.js';
|
|
28
|
+
const API = 'https://api.zoom.us/v2';
|
|
29
|
+
const TOKEN_URL = 'https://zoom.us/oauth/token';
|
|
30
|
+
/** How much of each file to read when hunting for its meeting_uuid. Matches the
|
|
31
|
+
* Python's 600, which is comfortably past the end of a five-key frontmatter
|
|
32
|
+
* block and short enough that indexing a full directory stays cheap. */
|
|
33
|
+
const FRONTMATTER_SCAN_BYTES = 600;
|
|
34
|
+
export class ZoomError extends Error {
|
|
35
|
+
code;
|
|
36
|
+
hint;
|
|
37
|
+
constructor(code, message, hint) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = 'ZoomError';
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.hint = hint;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Credentials
|
|
46
|
+
// ============================================================================
|
|
47
|
+
function credentialsFile() {
|
|
48
|
+
return path.join(os.homedir(), '.decibel', 'zoom-credentials.json');
|
|
49
|
+
}
|
|
50
|
+
function legacyCredentialsFile() {
|
|
51
|
+
return path.join(os.homedir(), '.config', 'zoom-summaries', 'credentials.json');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolution order: environment, then the config.yaml `zoom` block, then the
|
|
55
|
+
* standalone JSON file, then the legacy path.
|
|
56
|
+
*
|
|
57
|
+
* The JSON file is where plasiv's credentials actually live today (mode 600),
|
|
58
|
+
* so it stays supported rather than being migrated out from under a working
|
|
59
|
+
* setup. config.yaml is the location EPIC-0036 asks for and is preferred for
|
|
60
|
+
* anything new — it is the same file the daemon already reads secrets from.
|
|
61
|
+
*
|
|
62
|
+
* Environment wins only when ALL THREE values are present. A half-set
|
|
63
|
+
* environment falling through to a complete file beats erroring on a partial
|
|
64
|
+
* override, and mixing the two sources produces an account_id from one Zoom app
|
|
65
|
+
* with a secret from another, which fails at the token call with a message that
|
|
66
|
+
* blames the wrong thing.
|
|
67
|
+
*/
|
|
68
|
+
export function loadCredentials() {
|
|
69
|
+
const env = {
|
|
70
|
+
account_id: process.env.ZOOM_ACCOUNT_ID,
|
|
71
|
+
client_id: process.env.ZOOM_CLIENT_ID,
|
|
72
|
+
client_secret: process.env.ZOOM_CLIENT_SECRET,
|
|
73
|
+
};
|
|
74
|
+
if (env.account_id && env.client_id && env.client_secret) {
|
|
75
|
+
return env;
|
|
76
|
+
}
|
|
77
|
+
const found = { ...stripUndefined(env) };
|
|
78
|
+
const fromConfig = loadConfig().zoom;
|
|
79
|
+
if (fromConfig) {
|
|
80
|
+
found.account_id = found.account_id || fromConfig.account_id;
|
|
81
|
+
found.client_id = found.client_id || fromConfig.client_id;
|
|
82
|
+
found.client_secret = found.client_secret || fromConfig.client_secret;
|
|
83
|
+
}
|
|
84
|
+
for (const file of [credentialsFile(), legacyCredentialsFile()]) {
|
|
85
|
+
if (found.account_id && found.client_id && found.client_secret)
|
|
86
|
+
break;
|
|
87
|
+
if (!fs.existsSync(file))
|
|
88
|
+
continue;
|
|
89
|
+
try {
|
|
90
|
+
const onDisk = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
91
|
+
found.account_id = found.account_id || onDisk.account_id;
|
|
92
|
+
found.client_id = found.client_id || onDisk.client_id;
|
|
93
|
+
found.client_secret = found.client_secret || onDisk.client_secret;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
throw new ZoomError('credentials_unreadable', `${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, 'Expected {"account_id": "...", "client_id": "...", "client_secret": "..."}');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const trimmed = {};
|
|
100
|
+
for (const [k, v] of Object.entries(found)) {
|
|
101
|
+
if (typeof v === 'string' && v.trim())
|
|
102
|
+
trimmed[k] = v.trim();
|
|
103
|
+
}
|
|
104
|
+
const missing = ['account_id', 'client_id', 'client_secret'].filter(k => !trimmed[k]);
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
throw new ZoomError('credentials_missing', `Missing Zoom credentials: ${missing.join(', ')}`, `Set ZOOM_ACCOUNT_ID / ZOOM_CLIENT_ID / ZOOM_CLIENT_SECRET, add a zoom block to ~/.decibel/config.yaml, or write them to ${credentialsFile()}`);
|
|
107
|
+
}
|
|
108
|
+
// The original ships a credentials template with PASTE_ placeholders. Catching
|
|
109
|
+
// them here turns "Zoom rejected the token request (400)" into something that
|
|
110
|
+
// names the actual problem.
|
|
111
|
+
const placeholder = Object.keys(trimmed)
|
|
112
|
+
.filter(k => trimmed[k].startsWith('PASTE_'));
|
|
113
|
+
if (placeholder.length > 0) {
|
|
114
|
+
throw new ZoomError('credentials_placeholder', `Credential values are still placeholders: ${placeholder.join(', ')}`, `Fill them in at ${credentialsFile()} or in ~/.decibel/config.yaml`);
|
|
115
|
+
}
|
|
116
|
+
return trimmed;
|
|
117
|
+
}
|
|
118
|
+
function stripUndefined(obj) {
|
|
119
|
+
const out = {};
|
|
120
|
+
for (const [k, v] of Object.entries(obj))
|
|
121
|
+
if (v)
|
|
122
|
+
out[k] = v;
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
let cachedToken = null;
|
|
126
|
+
/** Refresh this long before the token actually expires, so a request in flight
|
|
127
|
+
* when the clock rolls over doesn't 401. */
|
|
128
|
+
const TOKEN_SKEW_MS = 60_000;
|
|
129
|
+
/**
|
|
130
|
+
* Server-to-Server OAuth. The original fetched one token per run and never
|
|
131
|
+
* looked at it again; S2S tokens last an hour, so a backfill long enough to
|
|
132
|
+
* matter outlives its own credential and dies partway through with a 401 that
|
|
133
|
+
* looks like a permissions problem. This caches with an expiry and refetches.
|
|
134
|
+
*/
|
|
135
|
+
export async function getToken(creds, now = Date.now()) {
|
|
136
|
+
if (cachedToken && cachedToken.expiresAt - TOKEN_SKEW_MS > now) {
|
|
137
|
+
return cachedToken.token;
|
|
138
|
+
}
|
|
139
|
+
const basic = Buffer.from(`${creds.client_id}:${creds.client_secret}`).toString('base64');
|
|
140
|
+
const url = `${TOKEN_URL}?${new URLSearchParams({
|
|
141
|
+
grant_type: 'account_credentials',
|
|
142
|
+
account_id: creds.account_id,
|
|
143
|
+
})}`;
|
|
144
|
+
const res = await fetch(url, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: { Authorization: `Basic ${basic}` },
|
|
147
|
+
});
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
const body = await res.text().catch(() => '');
|
|
150
|
+
throw new ZoomError('token_rejected', `Zoom rejected the token request (${res.status} ${res.statusText}). ${body}`, 'Common causes: the app is created but not Activated; account_id is a user ID rather than the Account ID on the App Credentials tab; or a client_id/secret typo.');
|
|
151
|
+
}
|
|
152
|
+
const payload = (await res.json());
|
|
153
|
+
if (!payload.access_token) {
|
|
154
|
+
throw new ZoomError('token_missing', 'Zoom returned no access_token', `Response keys: ${Object.keys(payload).join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
cachedToken = {
|
|
157
|
+
token: payload.access_token,
|
|
158
|
+
expiresAt: now + (payload.expires_in ?? 3600) * 1000,
|
|
159
|
+
};
|
|
160
|
+
return cachedToken.token;
|
|
161
|
+
}
|
|
162
|
+
/** Test seam, and the escape hatch after a credential change mid-process. */
|
|
163
|
+
export function clearTokenCache() {
|
|
164
|
+
cachedToken = null;
|
|
165
|
+
}
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// HTTP — with backoff
|
|
168
|
+
// ============================================================================
|
|
169
|
+
const MAX_ATTEMPTS = 5;
|
|
170
|
+
const BASE_BACKOFF_MS = 500;
|
|
171
|
+
const defaultSleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
172
|
+
/**
|
|
173
|
+
* The original had no retry, no backoff, no 429 handling and no concurrency
|
|
174
|
+
* cap — a serial loop issuing one detail call per meeting. Its clean log proves
|
|
175
|
+
* nothing, because it has only ever run at hand-driven volume; an account-wide
|
|
176
|
+
* backfill is the first time this would meet a rate limit.
|
|
177
|
+
*
|
|
178
|
+
* Retries 429 and 5xx with exponential backoff, honouring Retry-After when Zoom
|
|
179
|
+
* sends one. 4xx other than 429 is not retried — a 401 or a 404 does not get
|
|
180
|
+
* better by asking again.
|
|
181
|
+
*/
|
|
182
|
+
export async function apiGet(urlPath, token, params, deps = {}) {
|
|
183
|
+
const doFetch = deps.fetchImpl ?? fetch;
|
|
184
|
+
const sleep = deps.sleep ?? defaultSleep;
|
|
185
|
+
let url = API + urlPath;
|
|
186
|
+
if (params) {
|
|
187
|
+
const qs = new URLSearchParams();
|
|
188
|
+
for (const [k, v] of Object.entries(params))
|
|
189
|
+
qs.set(k, String(v));
|
|
190
|
+
url += `?${qs}`;
|
|
191
|
+
}
|
|
192
|
+
let lastError = '';
|
|
193
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
194
|
+
const res = await doFetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
195
|
+
if (res.ok)
|
|
196
|
+
return (await res.json());
|
|
197
|
+
const body = await res.text().catch(() => '');
|
|
198
|
+
lastError = `GET ${urlPath} -> ${res.status} ${body}`;
|
|
199
|
+
const retryable = res.status === 429 || res.status >= 500;
|
|
200
|
+
if (!retryable || attempt === MAX_ATTEMPTS) {
|
|
201
|
+
throw new ZoomError(res.status === 429 ? 'rate_limited' : 'api_error', lastError, res.status === 429
|
|
202
|
+
? 'Zoom rate limit reached after retries. Narrow the date window and run again.'
|
|
203
|
+
: undefined);
|
|
204
|
+
}
|
|
205
|
+
const retryAfter = Number(res.headers.get('retry-after'));
|
|
206
|
+
const wait = Number.isFinite(retryAfter) && retryAfter > 0
|
|
207
|
+
? retryAfter * 1000
|
|
208
|
+
: BASE_BACKOFF_MS * 2 ** (attempt - 1);
|
|
209
|
+
await sleep(wait);
|
|
210
|
+
}
|
|
211
|
+
throw new ZoomError('api_error', lastError);
|
|
212
|
+
}
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// UUID encoding
|
|
215
|
+
// ============================================================================
|
|
216
|
+
/**
|
|
217
|
+
* Zoom requires meeting UUIDs to be DOUBLE URL-encoded when they start with "/"
|
|
218
|
+
* or contain "//", and single-encoded otherwise.
|
|
219
|
+
*
|
|
220
|
+
* UNVERIFIED AGAINST REAL DATA: none of the 26 files on the plasiv disk carry a
|
|
221
|
+
* uuid that triggers the double branch, so that path comes from Zoom's docs
|
|
222
|
+
* rather than from a case anyone here has seen. The single branch is confirmed —
|
|
223
|
+
* it produces the same %3D%3D that appears inside the task permalinks Zoom
|
|
224
|
+
* embeds in its own summary markdown.
|
|
225
|
+
*/
|
|
226
|
+
export function encodeMeetingUuid(uuid) {
|
|
227
|
+
const once = encodeURIComponent(uuid);
|
|
228
|
+
if (uuid.startsWith('/') || uuid.includes('//')) {
|
|
229
|
+
return encodeURIComponent(once);
|
|
230
|
+
}
|
|
231
|
+
return once;
|
|
232
|
+
}
|
|
233
|
+
// ============================================================================
|
|
234
|
+
// List
|
|
235
|
+
// ============================================================================
|
|
236
|
+
/**
|
|
237
|
+
* The list envelope is documented as `summaries`. When that key is absent this
|
|
238
|
+
* falls back to whatever list-of-dicts the payload does carry and says so,
|
|
239
|
+
* because the failure mode being defended against is a silent zero-result run:
|
|
240
|
+
* an envelope rename would otherwise read as "no meetings", which is
|
|
241
|
+
* indistinguishable from a quiet week.
|
|
242
|
+
*/
|
|
243
|
+
export function extractSummaryList(payload, notes = []) {
|
|
244
|
+
if (Array.isArray(payload.summaries))
|
|
245
|
+
return payload.summaries;
|
|
246
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
247
|
+
if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && value[0] !== null) {
|
|
248
|
+
notes.push(`summaries arrived under '${key}', expected 'summaries'`);
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (payload.total_records) {
|
|
253
|
+
notes.push(`API reported ${payload.total_records} records but no list was found. Raw keys: ${Object.keys(payload).sort().join(', ')}`);
|
|
254
|
+
}
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
export async function listSummaries(token, from, to, notes = [], deps = {}) {
|
|
258
|
+
const summaries = [];
|
|
259
|
+
let pageToken = '';
|
|
260
|
+
// Bounded rather than while(true): a next_page_token that never clears is a
|
|
261
|
+
// hang with no output, which is the worst failure shape for a sync.
|
|
262
|
+
for (let page = 0; page < 200; page++) {
|
|
263
|
+
const params = { from, to, page_size: 300 };
|
|
264
|
+
if (pageToken)
|
|
265
|
+
params.next_page_token = pageToken;
|
|
266
|
+
const payload = await apiGet('/meetings/meeting_summaries', token, params, deps);
|
|
267
|
+
summaries.push(...extractSummaryList(payload, notes));
|
|
268
|
+
pageToken = payload.next_page_token || '';
|
|
269
|
+
if (!pageToken)
|
|
270
|
+
return summaries;
|
|
271
|
+
}
|
|
272
|
+
notes.push('stopped after 200 pages — next_page_token never cleared');
|
|
273
|
+
return summaries;
|
|
274
|
+
}
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Rendering
|
|
277
|
+
// ============================================================================
|
|
278
|
+
/**
|
|
279
|
+
* Zoom returns the whole summary as markdown in `summary_content` on modern
|
|
280
|
+
* accounts. The split fields are the deprecated shape.
|
|
281
|
+
*
|
|
282
|
+
* UNVERIFIED AGAINST REAL DATA: the split branch has never been exercised here.
|
|
283
|
+
* Every one of the 26 files on the plasiv disk orders its sections Quick recap,
|
|
284
|
+
* Next steps, per-person groups, Collaboration, Summary — and the split branch
|
|
285
|
+
* structurally cannot put next_steps second, because it appends them last. That
|
|
286
|
+
* ordering is the proof the account is on summary_content. The split branch
|
|
287
|
+
* below is written from Zoom's field names, not from anything observed, and is
|
|
288
|
+
* kept only so an older account degrades into readable output instead of an
|
|
289
|
+
* empty file.
|
|
290
|
+
*/
|
|
291
|
+
export function renderSummary(detail) {
|
|
292
|
+
const content = (detail.summary_content || '').trim();
|
|
293
|
+
if (content)
|
|
294
|
+
return content;
|
|
295
|
+
const parts = [];
|
|
296
|
+
const overview = (detail.summary_overview || '').trim();
|
|
297
|
+
if (overview)
|
|
298
|
+
parts.push(`## Quick recap\n\n${overview}`);
|
|
299
|
+
for (const section of detail.summary_details || []) {
|
|
300
|
+
const label = (section.label || '').trim();
|
|
301
|
+
const body = (section.summary || '').trim();
|
|
302
|
+
if (!body)
|
|
303
|
+
continue;
|
|
304
|
+
parts.push(label ? `### ${label}\n\n${body}` : body);
|
|
305
|
+
}
|
|
306
|
+
const steps = detail.next_steps || [];
|
|
307
|
+
if (steps.length > 0) {
|
|
308
|
+
// Coerced rather than assumed to be strings: on this account next steps
|
|
309
|
+
// arrive as markdown bullets inside summary_content and this array is never
|
|
310
|
+
// populated, so its element type is genuinely unknown.
|
|
311
|
+
parts.push(`## Next steps\n\n${steps.map(s => `- ${String(s).trim()}`).join('\n')}`);
|
|
312
|
+
}
|
|
313
|
+
return parts.join('\n\n').trim();
|
|
314
|
+
}
|
|
315
|
+
// ============================================================================
|
|
316
|
+
// Frontmatter and filenames — carried across byte-identically
|
|
317
|
+
// ============================================================================
|
|
318
|
+
export function startOf(summary) {
|
|
319
|
+
return summary.meeting_start_time || summary.summary_start_time || '';
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Zoom ignores from/to on the summaries list endpoint, so the window has to be
|
|
323
|
+
* enforced here or it is a lie. An entry with no start time is kept rather than
|
|
324
|
+
* dropped — a missing timestamp is a reason to look at it, not to hide it.
|
|
325
|
+
*/
|
|
326
|
+
export function inRange(summary, from, to) {
|
|
327
|
+
const start = startOf(summary).slice(0, 10);
|
|
328
|
+
if (!start)
|
|
329
|
+
return true;
|
|
330
|
+
return from <= start && start <= to;
|
|
331
|
+
}
|
|
332
|
+
/** Five keys, same order, same spelling as the Python. The dedup index reads
|
|
333
|
+
* meeting_uuid back out of this block, so the format is load-bearing. */
|
|
334
|
+
export function buildFrontmatter(summary, status) {
|
|
335
|
+
return [
|
|
336
|
+
'---',
|
|
337
|
+
'source: zoom-ai-companion',
|
|
338
|
+
`topic: "${(summary.meeting_topic || '').replace(/"/g, "'")}"`,
|
|
339
|
+
`start: ${startOf(summary)}`,
|
|
340
|
+
`meeting_uuid: ${summary.meeting_uuid || ''}`,
|
|
341
|
+
`status: ${status}`,
|
|
342
|
+
'---',
|
|
343
|
+
'',
|
|
344
|
+
].join('\n');
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* "{topic} {YYYY-MM-DD HH_MM}Z.md", topic stripped of characters that are
|
|
348
|
+
* illegal in a filename on some filesystem or other. Verified against the real
|
|
349
|
+
* file in the unrouted bucket: this produces "Ben - Pete 2026-08-12 17_04Z.md".
|
|
350
|
+
*/
|
|
351
|
+
export function filenameFor(summary) {
|
|
352
|
+
const topic = summary.meeting_topic || 'Zoom meeting';
|
|
353
|
+
const start = startOf(summary);
|
|
354
|
+
let stamp = '';
|
|
355
|
+
if (start) {
|
|
356
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(start);
|
|
357
|
+
if (match) {
|
|
358
|
+
const [, y, mo, d, h, mi] = match;
|
|
359
|
+
stamp = ` ${y}-${mo}-${d} ${h}_${mi}Z`;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
stamp = ` ${start.slice(0, 10)}`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const safe = topic.replace(/[/\\:*?"<>|]/g, '-').trim();
|
|
366
|
+
return `${safe}${stamp}.md`;
|
|
367
|
+
}
|
|
368
|
+
// ============================================================================
|
|
369
|
+
// Routing
|
|
370
|
+
// ============================================================================
|
|
371
|
+
function registryPath() {
|
|
372
|
+
return process.env.DECIBEL_REGISTRY_PATH || path.join(os.homedir(), '.decibel', 'projects.json');
|
|
373
|
+
}
|
|
374
|
+
export function unroutedDir() {
|
|
375
|
+
return path.join(os.homedir(), '.decibel', 'meetings', 'unrouted');
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Projects carrying a `zoom` block in the registry:
|
|
379
|
+
*
|
|
380
|
+
* "zoom": { "match": ["plasiv"], "out": "meetings/raw" }
|
|
381
|
+
*
|
|
382
|
+
* Aliases are deliberately NOT matched. They are tool-call addresses — "dt",
|
|
383
|
+
* "dv" — and a two-letter needle matches almost any meeting title.
|
|
384
|
+
*
|
|
385
|
+
* Needles are sorted longest-first so routing is deterministic by specificity.
|
|
386
|
+
* The original took the first rule that matched in registry order, which means
|
|
387
|
+
* a project whose needle is a substring of another project's needle silently
|
|
388
|
+
* stole its meetings depending on file ordering.
|
|
389
|
+
*/
|
|
390
|
+
export function loadRoutes() {
|
|
391
|
+
const file = registryPath();
|
|
392
|
+
if (!fs.existsSync(file))
|
|
393
|
+
return [];
|
|
394
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
395
|
+
const routes = [];
|
|
396
|
+
for (const entry of data.projects || []) {
|
|
397
|
+
const needles = (entry.zoom?.match || []).filter(Boolean).map(n => n.toLowerCase());
|
|
398
|
+
if (needles.length === 0)
|
|
399
|
+
continue;
|
|
400
|
+
routes.push({
|
|
401
|
+
id: entry.id,
|
|
402
|
+
match: needles.sort((a, b) => b.length - a.length),
|
|
403
|
+
out: path.join(entry.path, entry.zoom?.out || 'meetings/raw'),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
// Longest single needle first, so "plasiv design" beats "plasiv".
|
|
407
|
+
return routes.sort((a, b) => (b.match[0]?.length || 0) - (a.match[0]?.length || 0));
|
|
408
|
+
}
|
|
409
|
+
export function routeFor(summary, routes) {
|
|
410
|
+
const topic = (summary.meeting_topic || '').toLowerCase();
|
|
411
|
+
let best = null;
|
|
412
|
+
for (const route of routes) {
|
|
413
|
+
for (const needle of route.match) {
|
|
414
|
+
if (topic.includes(needle) && (!best || needle.length > best.length)) {
|
|
415
|
+
best = { route, length: needle.length };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return best?.route ?? null;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Index meeting_uuid out of the first 600 bytes of every .md in the given
|
|
423
|
+
* directories, keyed on uuid AND start time.
|
|
424
|
+
*
|
|
425
|
+
* Two deliberate departures from the original:
|
|
426
|
+
*
|
|
427
|
+
* 1. UUID ALONE IS NOT THE KEY. If Zoom reuses a uuid across occurrences of a
|
|
428
|
+
* recurring meeting, a uuid-keyed index silently skips the second occurrence
|
|
429
|
+
* as already-seen. Nobody has observed that here and the Python does not
|
|
430
|
+
* defend against it; keying on uuid+start costs nothing and closes it.
|
|
431
|
+
*
|
|
432
|
+
* 2. THE UNROUTED BUCKET IS NOT INDEXED HERE. See reclaim handling in
|
|
433
|
+
* syncMeetings — an unrouted entry is UNCLAIMED, not SEEN.
|
|
434
|
+
*/
|
|
435
|
+
export function indexKnownMeetings(dirs) {
|
|
436
|
+
const seen = new Map();
|
|
437
|
+
for (const dir of dirs) {
|
|
438
|
+
if (!fs.existsSync(dir))
|
|
439
|
+
continue;
|
|
440
|
+
for (const name of fs.readdirSync(dir)) {
|
|
441
|
+
if (!name.endsWith('.md'))
|
|
442
|
+
continue;
|
|
443
|
+
const file = path.join(dir, name);
|
|
444
|
+
let head;
|
|
445
|
+
try {
|
|
446
|
+
const fd = fs.openSync(file, 'r');
|
|
447
|
+
const buf = Buffer.alloc(FRONTMATTER_SCAN_BYTES);
|
|
448
|
+
const read = fs.readSync(fd, buf, 0, FRONTMATTER_SCAN_BYTES, 0);
|
|
449
|
+
fs.closeSync(fd);
|
|
450
|
+
head = buf.subarray(0, read).toString('utf-8');
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const uuid = /^meeting_uuid:\s*(\S+)\s*$/m.exec(head)?.[1];
|
|
456
|
+
if (!uuid)
|
|
457
|
+
continue;
|
|
458
|
+
const start = /^start:\s*(\S+)\s*$/m.exec(head)?.[1] || '';
|
|
459
|
+
seen.set(dedupKey(uuid, start), { file, uuid });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return seen;
|
|
463
|
+
}
|
|
464
|
+
export function dedupKey(uuid, start) {
|
|
465
|
+
return `${uuid}|${start}`;
|
|
466
|
+
}
|
|
467
|
+
/** The stub left in the unrouted bucket. Carries enough to see what is waiting
|
|
468
|
+
* — and nothing else.
|
|
469
|
+
*
|
|
470
|
+
* ISS-0123: a routed run does not merely READ non-client meetings, it WRITES
|
|
471
|
+
* them. The bucket on the plasiv machine holds a personal two-person meeting
|
|
472
|
+
* with its full body on disk, put there by a single hand-run. Storing a stub
|
|
473
|
+
* keeps the "never silently dropped" guarantee without spilling the content of
|
|
474
|
+
* every meeting in the account onto the filesystem as a side effect of pulling
|
|
475
|
+
* client ones.
|
|
476
|
+
*
|
|
477
|
+
* This is only safe BECAUSE the unrouted bucket is excluded from the dedup
|
|
478
|
+
* index. A stub in a seen-index would satisfy dedup forever and the real body
|
|
479
|
+
* would never arrive. Privacy stub and dedup fix are one change, not two. */
|
|
480
|
+
export function buildUnroutedStub(summary) {
|
|
481
|
+
return [
|
|
482
|
+
'---',
|
|
483
|
+
'source: zoom-ai-companion',
|
|
484
|
+
`topic: "${(summary.meeting_topic || '').replace(/"/g, "'")}"`,
|
|
485
|
+
`start: ${startOf(summary)}`,
|
|
486
|
+
`meeting_uuid: ${summary.meeting_uuid || ''}`,
|
|
487
|
+
'status: unrouted',
|
|
488
|
+
'---',
|
|
489
|
+
'',
|
|
490
|
+
'No project routing rule matched this meeting, so only its identity is',
|
|
491
|
+
'recorded here — the summary body was deliberately not written to disk.',
|
|
492
|
+
'',
|
|
493
|
+
'Add a match rule to the project in ~/.decibel/projects.json:',
|
|
494
|
+
'',
|
|
495
|
+
' "zoom": { "match": ["<needle>"], "out": "meetings/raw" }',
|
|
496
|
+
'',
|
|
497
|
+
'and run the sync again. The full summary will be fetched into the project',
|
|
498
|
+
'and this stub removed.',
|
|
499
|
+
'',
|
|
500
|
+
].join('\n');
|
|
501
|
+
}
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// Sync
|
|
504
|
+
// ============================================================================
|
|
505
|
+
function isoDay(d) {
|
|
506
|
+
return d.toISOString().slice(0, 10);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Pull summaries and write them where they belong.
|
|
510
|
+
*
|
|
511
|
+
* The routed path is the interesting one and the least exercised — one manual
|
|
512
|
+
* run in its life — so it is written here from the spec rather than trusted
|
|
513
|
+
* from the source.
|
|
514
|
+
*/
|
|
515
|
+
export async function syncMeetings(opts = {}, deps = {}) {
|
|
516
|
+
const notes = [];
|
|
517
|
+
const now = deps.now ?? new Date();
|
|
518
|
+
const to = opts.to || isoDay(now);
|
|
519
|
+
const from = opts.from || isoDay(new Date(now.getTime() - (opts.days ?? 30) * 86_400_000));
|
|
520
|
+
const status = opts.status || 'new';
|
|
521
|
+
const token = deps.token ?? (await getToken(deps.credentials ?? loadCredentials()));
|
|
522
|
+
const all = await listSummaries(token, from, to, notes, deps);
|
|
523
|
+
const returned = all.length;
|
|
524
|
+
let summaries = all.filter(s => inRange(s, from, to));
|
|
525
|
+
if (returned !== summaries.length) {
|
|
526
|
+
notes.push(`API returned ${returned} records ignoring from/to; ${summaries.length} fall in range (Zoom does not honour the window on this endpoint)`);
|
|
527
|
+
}
|
|
528
|
+
if (opts.match) {
|
|
529
|
+
const needle = opts.match.toLowerCase();
|
|
530
|
+
summaries = summaries.filter(s => (s.meeting_topic || '').toLowerCase().includes(needle));
|
|
531
|
+
}
|
|
532
|
+
const routes = opts.route ? (deps.routes ?? loadRoutes()) : [];
|
|
533
|
+
if (opts.route && routes.length === 0) {
|
|
534
|
+
throw new ZoomError('no_routes', `No projects in ${registryPath()} carry a zoom block`, 'Add one: "zoom": { "match": ["plasiv"], "out": "meetings/raw" }');
|
|
535
|
+
}
|
|
536
|
+
const singleOut = path.resolve(opts.out || path.join(process.cwd(), 'meetings', 'raw'));
|
|
537
|
+
// ISS-0152: the seen-index covers real destinations ONLY. The unrouted bucket
|
|
538
|
+
// is indexed separately, as a claim list rather than a skip list.
|
|
539
|
+
const destinations = opts.route ? routes.map(r => r.out) : [singleOut];
|
|
540
|
+
const seen = indexKnownMeetings(destinations);
|
|
541
|
+
const unclaimed = opts.route ? indexKnownMeetings([unroutedDir()]) : new Map();
|
|
542
|
+
const result = {
|
|
543
|
+
from, to, returned,
|
|
544
|
+
in_range: summaries.length,
|
|
545
|
+
written: 0, skipped: 0, empty: 0, unrouted: 0, reclaimed: 0,
|
|
546
|
+
dry_run: !!opts.dryRun,
|
|
547
|
+
routes: opts.route ? routes.map(r => ({ id: r.id, match: r.match, out: r.out })) : undefined,
|
|
548
|
+
files: [],
|
|
549
|
+
notes,
|
|
550
|
+
};
|
|
551
|
+
for (const summary of summaries) {
|
|
552
|
+
const uuid = String(summary.meeting_uuid || summary.meeting_id || '');
|
|
553
|
+
const key = dedupKey(uuid, startOf(summary));
|
|
554
|
+
const route = opts.route ? routeFor(summary, routes) : null;
|
|
555
|
+
const dest = opts.route ? (route ? route.out : unroutedDir()) : singleOut;
|
|
556
|
+
const filename = filenameFor(summary);
|
|
557
|
+
const target = path.join(dest, filename);
|
|
558
|
+
if (!route && opts.route)
|
|
559
|
+
result.unrouted++;
|
|
560
|
+
// A stub sitting in the unrouted bucket for a meeting that NOW routes
|
|
561
|
+
// somewhere is the reclaim case. It must not count as seen, or adding the
|
|
562
|
+
// routing rule would never pull the body in — the bug this port exists to
|
|
563
|
+
// fix.
|
|
564
|
+
const stub = unclaimed.get(key);
|
|
565
|
+
const reclaiming = !!(route && stub);
|
|
566
|
+
if (!reclaiming && seen.has(key) && !opts.force) {
|
|
567
|
+
result.skipped++;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (!reclaiming && fs.existsSync(target) && !opts.force) {
|
|
571
|
+
result.skipped++;
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
// An unrouted meeting that is STILL unrouted needs no second stub.
|
|
575
|
+
if (!route && opts.route && stub && !opts.force) {
|
|
576
|
+
result.skipped++;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
const action = reclaiming ? 'reclaim' : !route && opts.route ? 'stub' : 'write';
|
|
580
|
+
if (opts.dryRun) {
|
|
581
|
+
result.files.push({ name: filename, dest, project: route?.id ?? null, action: `would ${action}` });
|
|
582
|
+
result.written++;
|
|
583
|
+
if (reclaiming)
|
|
584
|
+
result.reclaimed++;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
// Unrouted meetings are never fetched in detail. Not fetching is the point:
|
|
588
|
+
// the body is what should not be on disk.
|
|
589
|
+
if (!route && opts.route) {
|
|
590
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
591
|
+
fs.writeFileSync(target, buildUnroutedStub(summary));
|
|
592
|
+
result.files.push({ name: filename, dest, project: null, action: 'stub' });
|
|
593
|
+
result.written++;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const detail = await apiGet(`/meetings/${encodeMeetingUuid(uuid)}/meeting_summary`, token, undefined, deps);
|
|
597
|
+
const body = renderSummary(detail);
|
|
598
|
+
// A meeting with no AI Companion summary yet is expected, not a failure.
|
|
599
|
+
// Writing an empty file would poison the dedup index against the real one.
|
|
600
|
+
if (!body) {
|
|
601
|
+
result.empty++;
|
|
602
|
+
result.files.push({ name: filename, dest, project: route?.id ?? null, action: 'empty' });
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
606
|
+
const head = opts.noFrontmatter ? '' : buildFrontmatter(summary, status);
|
|
607
|
+
fs.writeFileSync(target, head + body + '\n');
|
|
608
|
+
// Remove the stub only after the real file is safely written, so a crash
|
|
609
|
+
// between the two leaves the meeting claimable rather than lost.
|
|
610
|
+
if (reclaiming && stub) {
|
|
611
|
+
try {
|
|
612
|
+
fs.unlinkSync(stub.file);
|
|
613
|
+
result.reclaimed++;
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
notes.push(`wrote ${filename} but could not remove the unrouted stub at ${stub.file}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
result.files.push({ name: filename, dest, project: route?.id ?? null, action });
|
|
620
|
+
result.written++;
|
|
621
|
+
}
|
|
622
|
+
if (result.unrouted > 0) {
|
|
623
|
+
notes.push(`${result.unrouted} meeting(s) matched no project rule. Identity recorded in ${unroutedDir()}, body deliberately not written. Add a zoom.match entry in ${registryPath()} and re-run to claim them.`);
|
|
624
|
+
}
|
|
625
|
+
return result;
|
|
626
|
+
}
|
|
627
|
+
//# sourceMappingURL=zoom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoom.js","sourceRoot":"","sources":["../../src/tools/zoom.ts"],"names":[],"mappings":"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"}
|