@remnic/core 9.12.0 → 9.13.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/dist/access-cli.js +1 -1
- package/dist/{chunk-3VBXP5HS.js → chunk-5RZHHANR.js} +195 -14
- package/dist/chunk-5RZHHANR.js.map +1 -0
- package/dist/index.d.ts +105 -1
- package/dist/index.js +9 -1
- package/dist/orchestrator.js +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/meetings/detect.test.ts +366 -0
- package/src/meetings/detect.ts +287 -0
- package/src/meetings/index.ts +8 -0
- package/src/meetings/types.ts +76 -0
- package/dist/chunk-3VBXP5HS.js.map +0 -1
package/dist/access-cli.js
CHANGED
|
@@ -2835,8 +2835,8 @@ function utcDateKeysForLocalDay(date, timeZone) {
|
|
|
2835
2835
|
const hourMs = 36e5;
|
|
2836
2836
|
const scanStart = date.getTime() - 48 * hourMs;
|
|
2837
2837
|
const scanEnd = date.getTime() + 48 * hourMs;
|
|
2838
|
-
for (let
|
|
2839
|
-
const candidate = new Date(
|
|
2838
|
+
for (let ms2 = scanStart; ms2 <= scanEnd; ms2 += hourMs) {
|
|
2839
|
+
const candidate = new Date(ms2);
|
|
2840
2840
|
if (formatDateInTimeZone(candidate, timeZone) === targetLocalDate) {
|
|
2841
2841
|
keys.add(utcDateKey(candidate));
|
|
2842
2842
|
}
|
|
@@ -22206,8 +22206,8 @@ function isProcessAlive(pid) {
|
|
|
22206
22206
|
return true;
|
|
22207
22207
|
}
|
|
22208
22208
|
}
|
|
22209
|
-
function sleepSync(
|
|
22210
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0,
|
|
22209
|
+
function sleepSync(ms2) {
|
|
22210
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms2);
|
|
22211
22211
|
}
|
|
22212
22212
|
function createPersonalSpace(baseDir, memoryDirOverride) {
|
|
22213
22213
|
const homeDir = baseDir ?? resolveHomeDir();
|
|
@@ -23295,18 +23295,18 @@ function computeDwell(snapshots) {
|
|
|
23295
23295
|
}
|
|
23296
23296
|
return dwell;
|
|
23297
23297
|
}
|
|
23298
|
-
function formatDurationMinutes(
|
|
23299
|
-
return `${Math.round(
|
|
23298
|
+
function formatDurationMinutes(ms2) {
|
|
23299
|
+
return `${Math.round(ms2 / 6e4)}m`;
|
|
23300
23300
|
}
|
|
23301
23301
|
function clockHhMm(iso, timezone) {
|
|
23302
|
-
const
|
|
23303
|
-
if (!Number.isFinite(
|
|
23302
|
+
const ms2 = Date.parse(iso);
|
|
23303
|
+
if (!Number.isFinite(ms2)) return "??:??";
|
|
23304
23304
|
const parts = new Intl.DateTimeFormat("en-GB", {
|
|
23305
23305
|
timeZone: timezone,
|
|
23306
23306
|
hour: "2-digit",
|
|
23307
23307
|
minute: "2-digit",
|
|
23308
23308
|
hour12: false
|
|
23309
|
-
}).formatToParts(new Date(
|
|
23309
|
+
}).formatToParts(new Date(ms2));
|
|
23310
23310
|
const hour = parts.find((p) => p.type === "hour")?.value ?? "00";
|
|
23311
23311
|
const minute = parts.find((p) => p.type === "minute")?.value ?? "00";
|
|
23312
23312
|
return `${hour}:${minute}`;
|
|
@@ -23329,8 +23329,8 @@ function perAppSection(ordered, dwell) {
|
|
|
23329
23329
|
if (rows.length === 0) {
|
|
23330
23330
|
lines.push("_No activity recorded._");
|
|
23331
23331
|
} else {
|
|
23332
|
-
for (const [app,
|
|
23333
|
-
lines.push(`- ${app}: ${formatDurationMinutes(
|
|
23332
|
+
for (const [app, ms2] of rows) {
|
|
23333
|
+
lines.push(`- ${app}: ${formatDurationMinutes(ms2)}`);
|
|
23334
23334
|
}
|
|
23335
23335
|
}
|
|
23336
23336
|
return lines.join("\n");
|
|
@@ -23485,8 +23485,185 @@ function parseMachinesList(raw) {
|
|
|
23485
23485
|
return raw.replace(/^\[/, "").replace(/\]$/, "").split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
23486
23486
|
}
|
|
23487
23487
|
|
|
23488
|
-
// src/
|
|
23488
|
+
// src/meetings/detect.ts
|
|
23489
23489
|
import { createHash as createHash10 } from "crypto";
|
|
23490
|
+
var DEFAULT_MEETING_APP_PATTERNS = [
|
|
23491
|
+
"zoom.us",
|
|
23492
|
+
"Zoom",
|
|
23493
|
+
"Microsoft Teams",
|
|
23494
|
+
"teams.microsoft.com",
|
|
23495
|
+
"meet.google.com",
|
|
23496
|
+
"Webex",
|
|
23497
|
+
"Slack",
|
|
23498
|
+
// huddle windows
|
|
23499
|
+
"FaceTime"
|
|
23500
|
+
];
|
|
23501
|
+
var DEFAULT_MEETINGS_DETECTION_CONFIG = {
|
|
23502
|
+
appPatterns: [...DEFAULT_MEETING_APP_PATTERNS],
|
|
23503
|
+
minOverlapMinutes: 2,
|
|
23504
|
+
audioOnlyMinMinutes: 15,
|
|
23505
|
+
mergeGapMinutes: 2
|
|
23506
|
+
};
|
|
23507
|
+
function ms(iso) {
|
|
23508
|
+
if (typeof iso !== "string") return Number.NaN;
|
|
23509
|
+
if (!Number.isFinite(Date.parse(iso))) return Number.NaN;
|
|
23510
|
+
const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](?:0\d:[0-5]\d|1[0-3]:[0-5]\d|14:00))$/);
|
|
23511
|
+
if (m === null) return Number.NaN;
|
|
23512
|
+
const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
|
|
23513
|
+
const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
|
|
23514
|
+
if (day < 1 || day > daysInMonth || hour > 23 || minute > 59 || second > 59) return Number.NaN;
|
|
23515
|
+
return Date.parse(iso);
|
|
23516
|
+
}
|
|
23517
|
+
function overlapMs(aStart, aEnd, bStart, bEnd) {
|
|
23518
|
+
const start = Math.max(aStart, bStart);
|
|
23519
|
+
const end = Math.min(aEnd, bEnd);
|
|
23520
|
+
return end > start ? end - start : 0;
|
|
23521
|
+
}
|
|
23522
|
+
function meetingId(date, startUtc) {
|
|
23523
|
+
if (!isValidDay(date)) {
|
|
23524
|
+
throw new RangeError(`meetings: invalid day "${date}" for a meeting id; expected a real YYYY-MM-DD.`);
|
|
23525
|
+
}
|
|
23526
|
+
const startMs = ms(startUtc);
|
|
23527
|
+
if (Number.isNaN(startMs)) {
|
|
23528
|
+
throw new RangeError(`meetings: invalid meeting start "${startUtc}" for a meeting id.`);
|
|
23529
|
+
}
|
|
23530
|
+
const anchor = new Date(startMs).toISOString();
|
|
23531
|
+
const hash = createHash10("sha256").update(`${date}|${anchor}`, "utf8").digest("hex").slice(0, 8);
|
|
23532
|
+
return `mtg-${date}-${hash}`;
|
|
23533
|
+
}
|
|
23534
|
+
function isFinitePair(a, b) {
|
|
23535
|
+
return Number.isFinite(a) && Number.isFinite(b) && b > a;
|
|
23536
|
+
}
|
|
23537
|
+
function combineDetection(a, b) {
|
|
23538
|
+
if (a === "app+audio" || b === "app+audio") return "app+audio";
|
|
23539
|
+
if (a === "provider" || b === "provider") return "provider";
|
|
23540
|
+
return "audio";
|
|
23541
|
+
}
|
|
23542
|
+
function buildCandidates(audioWindows, appSpans, config) {
|
|
23543
|
+
const minOverlapMs = config.minOverlapMinutes * 6e4;
|
|
23544
|
+
const audioOnlyMs = config.audioOnlyMinMinutes * 6e4;
|
|
23545
|
+
const candidates = [];
|
|
23546
|
+
for (const window of audioWindows) {
|
|
23547
|
+
const startMs = ms(window.startUtc);
|
|
23548
|
+
const endMs = ms(window.endUtc);
|
|
23549
|
+
if (!isFinitePair(startMs, endMs)) continue;
|
|
23550
|
+
if (window.providerMeeting === true) {
|
|
23551
|
+
candidates.push({
|
|
23552
|
+
startMs,
|
|
23553
|
+
endMs,
|
|
23554
|
+
detectionSource: "provider",
|
|
23555
|
+
sources: [window.source],
|
|
23556
|
+
...window.title !== void 0 ? { title: window.title } : {}
|
|
23557
|
+
});
|
|
23558
|
+
continue;
|
|
23559
|
+
}
|
|
23560
|
+
let bestApp;
|
|
23561
|
+
for (const span of appSpans) {
|
|
23562
|
+
const spanStart = ms(span.startUtc);
|
|
23563
|
+
const spanEnd = ms(span.endUtc);
|
|
23564
|
+
if (!isFinitePair(spanStart, spanEnd)) continue;
|
|
23565
|
+
const overlap = overlapMs(startMs, endMs, spanStart, spanEnd);
|
|
23566
|
+
if (overlap <= 0 || overlap < minOverlapMs) continue;
|
|
23567
|
+
if (bestApp === void 0 || overlap > bestApp.overlap) {
|
|
23568
|
+
bestApp = { span, overlap };
|
|
23569
|
+
} else if (overlap === bestApp.overlap) {
|
|
23570
|
+
const bestStart = ms(bestApp.span.startUtc);
|
|
23571
|
+
const better = spanStart < bestStart || spanStart === bestStart && (span.app < bestApp.span.app || span.app === bestApp.span.app && spanEnd < ms(bestApp.span.endUtc));
|
|
23572
|
+
if (better) bestApp = { span, overlap };
|
|
23573
|
+
}
|
|
23574
|
+
}
|
|
23575
|
+
if (bestApp !== void 0) {
|
|
23576
|
+
candidates.push({
|
|
23577
|
+
startMs,
|
|
23578
|
+
endMs,
|
|
23579
|
+
app: bestApp.span.app,
|
|
23580
|
+
detectionSource: "app+audio",
|
|
23581
|
+
sources: [window.source],
|
|
23582
|
+
...window.title !== void 0 ? { title: window.title } : {}
|
|
23583
|
+
});
|
|
23584
|
+
continue;
|
|
23585
|
+
}
|
|
23586
|
+
if (endMs - startMs >= audioOnlyMs && Number.isInteger(window.distinctNonWearerSpeakers) && window.distinctNonWearerSpeakers >= 2) {
|
|
23587
|
+
candidates.push({
|
|
23588
|
+
startMs,
|
|
23589
|
+
endMs,
|
|
23590
|
+
detectionSource: "audio",
|
|
23591
|
+
sources: [window.source],
|
|
23592
|
+
...window.title !== void 0 ? { title: window.title } : {}
|
|
23593
|
+
});
|
|
23594
|
+
}
|
|
23595
|
+
}
|
|
23596
|
+
return candidates;
|
|
23597
|
+
}
|
|
23598
|
+
var DETECTION_RANK = {
|
|
23599
|
+
"app+audio": 0,
|
|
23600
|
+
provider: 1,
|
|
23601
|
+
audio: 2
|
|
23602
|
+
};
|
|
23603
|
+
function candidateOrder(a, b) {
|
|
23604
|
+
return a.startMs - b.startMs || a.endMs - b.endMs || DETECTION_RANK[a.detectionSource] - DETECTION_RANK[b.detectionSource] || (a.app ?? "").localeCompare(b.app ?? "") || (a.title ?? "").localeCompare(b.title ?? "") || (a.sources[0] ?? "").localeCompare(b.sources[0] ?? "");
|
|
23605
|
+
}
|
|
23606
|
+
function mergeCandidates(candidates, mergeGapMs) {
|
|
23607
|
+
const sorted = [...candidates].sort(candidateOrder);
|
|
23608
|
+
const merged = [];
|
|
23609
|
+
for (const candidate of sorted) {
|
|
23610
|
+
const prev = merged[merged.length - 1];
|
|
23611
|
+
const overlaps = prev !== void 0 && candidate.startMs < prev.endMs;
|
|
23612
|
+
const sameAppAdjacent = prev !== void 0 && prev.app !== void 0 && prev.app === candidate.app && candidate.startMs - prev.endMs <= mergeGapMs;
|
|
23613
|
+
if (prev !== void 0 && (overlaps || sameAppAdjacent)) {
|
|
23614
|
+
prev.endMs = Math.max(prev.endMs, candidate.endMs);
|
|
23615
|
+
prev.app = prev.app ?? candidate.app;
|
|
23616
|
+
prev.detectionSource = combineDetection(prev.detectionSource, candidate.detectionSource);
|
|
23617
|
+
prev.sources = [.../* @__PURE__ */ new Set([...prev.sources, ...candidate.sources])];
|
|
23618
|
+
prev.title = prev.title ?? candidate.title;
|
|
23619
|
+
continue;
|
|
23620
|
+
}
|
|
23621
|
+
merged.push({ ...candidate, sources: [...candidate.sources] });
|
|
23622
|
+
}
|
|
23623
|
+
return merged;
|
|
23624
|
+
}
|
|
23625
|
+
function assertFiniteNonNegative(name, value) {
|
|
23626
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
23627
|
+
throw new RangeError(`meetings config "${name}" must be a finite, non-negative number (got ${value}).`);
|
|
23628
|
+
}
|
|
23629
|
+
}
|
|
23630
|
+
function isValidDay(date) {
|
|
23631
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
23632
|
+
if (m === null) return false;
|
|
23633
|
+
const [, year, month, day] = m.map(Number);
|
|
23634
|
+
const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
|
|
23635
|
+
return day >= 1 && day <= daysInMonth;
|
|
23636
|
+
}
|
|
23637
|
+
function validateConfig(config) {
|
|
23638
|
+
assertFiniteNonNegative("minOverlapMinutes", config.minOverlapMinutes);
|
|
23639
|
+
assertFiniteNonNegative("audioOnlyMinMinutes", config.audioOnlyMinMinutes);
|
|
23640
|
+
assertFiniteNonNegative("mergeGapMinutes", config.mergeGapMinutes);
|
|
23641
|
+
}
|
|
23642
|
+
function detectMeetings(input, config = DEFAULT_MEETINGS_DETECTION_CONFIG) {
|
|
23643
|
+
validateConfig(config);
|
|
23644
|
+
if (!isValidDay(input.date)) {
|
|
23645
|
+
throw new RangeError(`meetings: invalid day "${input.date}"; expected a real YYYY-MM-DD.`);
|
|
23646
|
+
}
|
|
23647
|
+
const candidates = buildCandidates(input.audioWindows, input.appSpans, config);
|
|
23648
|
+
const merged = mergeCandidates(candidates, config.mergeGapMinutes * 6e4);
|
|
23649
|
+
return merged.map((candidate) => {
|
|
23650
|
+
const startUtc = new Date(candidate.startMs).toISOString();
|
|
23651
|
+
const endUtc = new Date(candidate.endMs).toISOString();
|
|
23652
|
+
return {
|
|
23653
|
+
id: meetingId(input.date, startUtc),
|
|
23654
|
+
date: input.date,
|
|
23655
|
+
startUtc,
|
|
23656
|
+
endUtc,
|
|
23657
|
+
...candidate.app !== void 0 ? { app: candidate.app } : {},
|
|
23658
|
+
detectionSource: candidate.detectionSource,
|
|
23659
|
+
sources: [...candidate.sources].sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
|
|
23660
|
+
...candidate.title !== void 0 ? { title: candidate.title } : {}
|
|
23661
|
+
};
|
|
23662
|
+
});
|
|
23663
|
+
}
|
|
23664
|
+
|
|
23665
|
+
// src/session-summaries/index.ts
|
|
23666
|
+
import { createHash as createHash11 } from "crypto";
|
|
23490
23667
|
import { lstat as lstat2, mkdir as mkdir8, readFile as readFile6, readdir as readdir4, rename, rm, stat as stat4, writeFile as writeFile7 } from "fs/promises";
|
|
23491
23668
|
import path31 from "path";
|
|
23492
23669
|
|
|
@@ -23988,7 +24165,7 @@ var DEFAULT_MAX_FILES = 5e3;
|
|
|
23988
24165
|
var DEFAULT_MAX_SESSIONS = 500;
|
|
23989
24166
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".jsonl"]);
|
|
23990
24167
|
function shortHash(value, length = 16) {
|
|
23991
|
-
return
|
|
24168
|
+
return createHash11("sha256").update(value).digest("hex").slice(0, length);
|
|
23992
24169
|
}
|
|
23993
24170
|
async function listTranscriptFiles(root, maxFiles) {
|
|
23994
24171
|
const out = [];
|
|
@@ -24577,6 +24754,10 @@ export {
|
|
|
24577
24754
|
composeActivityDigestMeta,
|
|
24578
24755
|
serializeActivityDigest,
|
|
24579
24756
|
parseActivityDigest,
|
|
24757
|
+
DEFAULT_MEETING_APP_PATTERNS,
|
|
24758
|
+
DEFAULT_MEETINGS_DETECTION_CONFIG,
|
|
24759
|
+
meetingId,
|
|
24760
|
+
detectMeetings,
|
|
24580
24761
|
registerLocalSessionSourceAdapter,
|
|
24581
24762
|
getLocalSessionSourceAdapter,
|
|
24582
24763
|
listLocalSessionSourceAdapters,
|
|
@@ -24592,4 +24773,4 @@ export {
|
|
|
24592
24773
|
blendGraphExpandedRecallScore,
|
|
24593
24774
|
Orchestrator
|
|
24594
24775
|
};
|
|
24595
|
-
//# sourceMappingURL=chunk-
|
|
24776
|
+
//# sourceMappingURL=chunk-5RZHHANR.js.map
|