@turmind/halo 2.2.0 → 2.2.2
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/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/1989-3b2faa2731d973c4.js +1 -0
- package/admin-out/_next/static/chunks/app/page-10a4afac9cbc27a2.js +1 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +4 -4
- package/bundled-docs/design/background-dispatch.md +1 -1
- package/bundled-docs/design/canvas-extensions.md +1 -1
- package/bundled-docs/design/cron.md +4 -4
- package/bundled-docs/design/node.md +7 -1
- package/bundled-docs/design/session.md +2 -2
- package/bundled-docs/design/stt-local.md +34 -10
- package/bundled-docs/design/ws.md +7 -5
- package/bundled-docs/dev/api.md +5 -4
- package/bundled-docs/dev/deploy.md +1 -1
- package/bundled-docs/guide/agents.md +1 -1
- package/bundled-docs/guide/node.md +45 -6
- package/bundled-docs/requirements/agent.md +1 -1
- package/bundled-docs/requirements/workspace.md +3 -3
- package/dist/index.js +347 -47
- package/package.json +1 -1
- package/templates/skills/cron/SKILL.md +46 -21
- package/templates/skills/cron/manage-cron.py +10 -13
- package/admin-out/_next/static/chunks/1989-dab5d5c24b8f4c9f.js +0 -1
- package/admin-out/_next/static/chunks/app/page-cca81a2ed69100f5.js +0 -1
- /package/admin-out/_next/static/{Rs9tIYSSITPLxv274DU9f → u9pg60rITCj73MBYeXsW1}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{Rs9tIYSSITPLxv274DU9f → u9pg60rITCj73MBYeXsW1}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -1929,7 +1929,7 @@ var init_init = __esm({
|
|
|
1929
1929
|
path8.resolve(__dirname, "..", "bundled-docs"),
|
|
1930
1930
|
path8.resolve(__dirname, "..", "..", "..", ".halo", "docs")
|
|
1931
1931
|
];
|
|
1932
|
-
TEMPLATE_VERSION =
|
|
1932
|
+
TEMPLATE_VERSION = 119;
|
|
1933
1933
|
VERSION_FILE = ".template-version";
|
|
1934
1934
|
SKIP_NAMES = /* @__PURE__ */ new Set([".DS_Store", "schema.sql", "__pycache__", ".pytest_cache"]);
|
|
1935
1935
|
BUILTIN_AGENT_IDS = /* @__PURE__ */ new Set([
|
|
@@ -16976,7 +16976,7 @@ async function initObservability(haloProcess = "server") {
|
|
|
16976
16976
|
return true;
|
|
16977
16977
|
}
|
|
16978
16978
|
const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
|
|
16979
|
-
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.2.
|
|
16979
|
+
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.2.2", haloProcess);
|
|
16980
16980
|
console.log(`[Observability] OTLP export \u2192 ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT} (service.name=${process.env.OTEL_SERVICE_NAME}, halo.process=${haloProcess}, capture_content=${config.observability.captureContent})`);
|
|
16981
16981
|
return true;
|
|
16982
16982
|
}
|
|
@@ -28938,6 +28938,23 @@ var init_handler = __esm({
|
|
|
28938
28938
|
});
|
|
28939
28939
|
|
|
28940
28940
|
// ../core/dist/protocol/session-message.js
|
|
28941
|
+
function inferMessageType(msg) {
|
|
28942
|
+
if (msg.type)
|
|
28943
|
+
return msg.type;
|
|
28944
|
+
if (msg.role === "user")
|
|
28945
|
+
return "user";
|
|
28946
|
+
if (msg.role === "assistant")
|
|
28947
|
+
return "assistant";
|
|
28948
|
+
if (msg.toolName)
|
|
28949
|
+
return "tool_call";
|
|
28950
|
+
if (msg.toolOutput !== void 0 && !msg.toolName)
|
|
28951
|
+
return "tool_result";
|
|
28952
|
+
if (msg.usage)
|
|
28953
|
+
return "usage";
|
|
28954
|
+
if (msg.systemPrompt)
|
|
28955
|
+
return "context";
|
|
28956
|
+
return "notification";
|
|
28957
|
+
}
|
|
28941
28958
|
var init_session_message = __esm({
|
|
28942
28959
|
"../core/dist/protocol/session-message.js"() {
|
|
28943
28960
|
"use strict";
|
|
@@ -29990,7 +30007,7 @@ function createSttModelManager(opts = {}) {
|
|
|
29990
30007
|
}
|
|
29991
30008
|
};
|
|
29992
30009
|
}
|
|
29993
|
-
var SENSE_VOICE_MODEL, SENSE_VOICE_TOKENS, SILERO_VAD_MODEL, HF_SENSE_VOICE, MS_SENSE_VOICE, STT_MODEL_FILES, sttModels;
|
|
30010
|
+
var SENSE_VOICE_MODEL, SENSE_VOICE_TOKENS, SILERO_VAD_MODEL, PYANNOTE_SEG_MODEL, CAMPLUS_MODEL, HF_SENSE_VOICE, MS_SENSE_VOICE, STT_MODEL_FILES, sttModels;
|
|
29994
30011
|
var init_models = __esm({
|
|
29995
30012
|
"../server/dist/stt/models.js"() {
|
|
29996
30013
|
"use strict";
|
|
@@ -29998,6 +30015,8 @@ var init_models = __esm({
|
|
|
29998
30015
|
SENSE_VOICE_MODEL = "sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx";
|
|
29999
30016
|
SENSE_VOICE_TOKENS = "sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt";
|
|
30000
30017
|
SILERO_VAD_MODEL = "silero-vad-v4/silero_vad.onnx";
|
|
30018
|
+
PYANNOTE_SEG_MODEL = "pyannote-segmentation-3-0/model.int8.onnx";
|
|
30019
|
+
CAMPLUS_MODEL = "campplus-zh-en-advanced/3dspeaker_speech_campplus_sv_zh_en_16k-common_advanced.onnx";
|
|
30001
30020
|
HF_SENSE_VOICE = "https://huggingface.co/csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/resolve/2365baeacb507f821a0c8120fcee3d484dba7a07";
|
|
30002
30021
|
MS_SENSE_VOICE = "https://www.modelscope.cn/api/v1/models/pengzhendong/sherpa-onnx-sense-voice-zh-en-ja-ko-yue/repo?Revision=master&FilePath=";
|
|
30003
30022
|
STT_MODEL_FILES = [
|
|
@@ -30027,29 +30046,205 @@ var init_models = __esm({
|
|
|
30027
30046
|
{ name: "huggingface", url: "https://huggingface.co/csukuangfj/vad/resolve/fba88cd2e921609e7675c3aaf51e0b9b295da4bc/silero_vad.onnx" },
|
|
30028
30047
|
{ name: "modelscope", url: "https://www.modelscope.cn/api/v1/models/pengzhendong/silero-vad/repo?Revision=master&FilePath=v4/silero_vad.onnx" }
|
|
30029
30048
|
]
|
|
30049
|
+
},
|
|
30050
|
+
{
|
|
30051
|
+
path: PYANNOTE_SEG_MODEL,
|
|
30052
|
+
size: 1540506,
|
|
30053
|
+
sha256: "d582f4b4c6b48205de7e0643c57df0df5615a3c176189be3fc461e9d18827b5d",
|
|
30054
|
+
sources: [
|
|
30055
|
+
{ name: "huggingface", url: "https://huggingface.co/csukuangfj/sherpa-onnx-pyannote-segmentation-3-0/resolve/9403a6902bb58e3d5ae8c7e77c3422de279db2e0/model.int8.onnx" },
|
|
30056
|
+
{ name: "modelscope", url: "https://www.modelscope.cn/api/v1/models/pengzhendong/sherpa-onnx-pyannote-segmentation-3-0/repo?Revision=master&FilePath=model.int8.onnx" }
|
|
30057
|
+
]
|
|
30058
|
+
},
|
|
30059
|
+
{
|
|
30060
|
+
path: CAMPLUS_MODEL,
|
|
30061
|
+
size: 28281164,
|
|
30062
|
+
sha256: "aa3cfc16963a10586a9393f5035d6d6b57e98d358b347f80c2a30bf4f00ceba2",
|
|
30063
|
+
sources: [
|
|
30064
|
+
{ name: "huggingface", url: "https://huggingface.co/csukuangfj/speaker-embedding-models/resolve/0743f301363dec56491a490f6d6cbc9d67f9a3bf/3dspeaker_speech_campplus_sv_zh_en_16k-common_advanced.onnx" },
|
|
30065
|
+
{ name: "modelscope", url: "https://www.modelscope.cn/api/v1/models/lijiu09/speaker-embedding-models/repo?Revision=master&FilePath=3dspeaker_speech_campplus_sv_zh_en_16k-common_advanced.onnx" },
|
|
30066
|
+
{ name: "github", url: "https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_campplus_sv_zh_en_16k-common_advanced.onnx" }
|
|
30067
|
+
]
|
|
30030
30068
|
}
|
|
30031
30069
|
];
|
|
30032
30070
|
sttModels = createSttModelManager();
|
|
30033
30071
|
}
|
|
30034
30072
|
});
|
|
30035
30073
|
|
|
30074
|
+
// ../server/dist/stt/speakers.js
|
|
30075
|
+
function normalize(v) {
|
|
30076
|
+
let n = 0;
|
|
30077
|
+
for (const x of v)
|
|
30078
|
+
n += x * x;
|
|
30079
|
+
n = Math.sqrt(n) || 1;
|
|
30080
|
+
return v.map((x) => x / n);
|
|
30081
|
+
}
|
|
30082
|
+
function cosine(a, b) {
|
|
30083
|
+
let d = 0, x = 0, y = 0;
|
|
30084
|
+
for (let i = 0; i < a.length; i++) {
|
|
30085
|
+
d += a[i] * b[i];
|
|
30086
|
+
x += a[i] * a[i];
|
|
30087
|
+
y += b[i] * b[i];
|
|
30088
|
+
}
|
|
30089
|
+
return x && y ? d / Math.sqrt(x * y) : 0;
|
|
30090
|
+
}
|
|
30091
|
+
function createSpeakerTracker(threshold = SPEAKER_MATCH_THRESHOLD) {
|
|
30092
|
+
const centroids = [];
|
|
30093
|
+
return {
|
|
30094
|
+
/** One local cluster (its embedding + seconds of audio) → speaker index. */
|
|
30095
|
+
assign(embedding, seconds) {
|
|
30096
|
+
const e = normalize(embedding);
|
|
30097
|
+
let best = -1;
|
|
30098
|
+
let bestSim = -2;
|
|
30099
|
+
centroids.forEach((c, k) => {
|
|
30100
|
+
const s = cosine(c, e);
|
|
30101
|
+
if (s > bestSim) {
|
|
30102
|
+
bestSim = s;
|
|
30103
|
+
best = k;
|
|
30104
|
+
}
|
|
30105
|
+
});
|
|
30106
|
+
if (best < 0 || bestSim < threshold && seconds >= MIN_NEW_SPEAKER_S) {
|
|
30107
|
+
centroids.push(e);
|
|
30108
|
+
return centroids.length - 1;
|
|
30109
|
+
}
|
|
30110
|
+
if (bestSim >= threshold) {
|
|
30111
|
+
const c = centroids[best];
|
|
30112
|
+
for (let i = 0; i < c.length; i++)
|
|
30113
|
+
c[i] += e[i];
|
|
30114
|
+
}
|
|
30115
|
+
return best;
|
|
30116
|
+
}
|
|
30117
|
+
};
|
|
30118
|
+
}
|
|
30119
|
+
function runAt(runs, t2) {
|
|
30120
|
+
let hit = null;
|
|
30121
|
+
for (const r of runs)
|
|
30122
|
+
if (r.start <= t2 && t2 <= r.end && (!hit || r.start > hit.start))
|
|
30123
|
+
hit = r;
|
|
30124
|
+
if (hit)
|
|
30125
|
+
return hit;
|
|
30126
|
+
let best = runs[0];
|
|
30127
|
+
let dist = Infinity;
|
|
30128
|
+
for (const r of runs) {
|
|
30129
|
+
const d = Math.min(Math.abs(t2 - r.start), Math.abs(t2 - r.end));
|
|
30130
|
+
if (d < dist) {
|
|
30131
|
+
dist = d;
|
|
30132
|
+
best = r;
|
|
30133
|
+
}
|
|
30134
|
+
}
|
|
30135
|
+
return best;
|
|
30136
|
+
}
|
|
30137
|
+
function splitBySpeakerRuns(text6, tokens, timestamps, runs, duration) {
|
|
30138
|
+
const audio = /* @__PURE__ */ new Map();
|
|
30139
|
+
for (const r of runs)
|
|
30140
|
+
audio.set(r.speaker, (audio.get(r.speaker) ?? 0) + r.end - r.start);
|
|
30141
|
+
const top = [...audio.entries()].reduce((a, b) => b[1] > a[1] ? b : a)[0];
|
|
30142
|
+
const whole = [{ start: 0, end: duration, text: text6, speaker: top }];
|
|
30143
|
+
if (audio.size === 1 || !tokens || !timestamps || tokens.length !== timestamps.length || tokens.join("").trim() !== text6)
|
|
30144
|
+
return whole;
|
|
30145
|
+
const pieces = [];
|
|
30146
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
30147
|
+
const cur = pieces.at(-1);
|
|
30148
|
+
if (!WORD.test(tokens[i])) {
|
|
30149
|
+
if (cur)
|
|
30150
|
+
cur.last = i;
|
|
30151
|
+
continue;
|
|
30152
|
+
}
|
|
30153
|
+
const run = runAt(runs, timestamps[i]);
|
|
30154
|
+
if (cur && cur.speaker === run.speaker) {
|
|
30155
|
+
cur.last = i;
|
|
30156
|
+
continue;
|
|
30157
|
+
}
|
|
30158
|
+
pieces.push(cur ? { speaker: run.speaker, first: i, last: i, at: Math.min(Math.max(run.start, timestamps[cur.last]), timestamps[i]) } : { speaker: run.speaker, first: 0, last: i, at: 0 });
|
|
30159
|
+
}
|
|
30160
|
+
for (let k = pieces.length - 1; k >= 0 && pieces.length > 1; k--) {
|
|
30161
|
+
if ((pieces[k + 1]?.at ?? duration) > pieces[k].at)
|
|
30162
|
+
continue;
|
|
30163
|
+
if (k > 0)
|
|
30164
|
+
pieces[k - 1].last = pieces[k].last;
|
|
30165
|
+
else
|
|
30166
|
+
Object.assign(pieces[1], { first: pieces[0].first, at: 0 });
|
|
30167
|
+
pieces.splice(k, 1);
|
|
30168
|
+
}
|
|
30169
|
+
for (let k = pieces.length - 1; k > 0; k--) {
|
|
30170
|
+
if (pieces[k].speaker !== pieces[k - 1].speaker)
|
|
30171
|
+
continue;
|
|
30172
|
+
pieces[k - 1].last = pieces[k].last;
|
|
30173
|
+
pieces.splice(k, 1);
|
|
30174
|
+
}
|
|
30175
|
+
if (pieces.length === 0)
|
|
30176
|
+
return whole;
|
|
30177
|
+
return pieces.map((p, k) => ({
|
|
30178
|
+
start: p.at,
|
|
30179
|
+
end: pieces[k + 1]?.at ?? duration,
|
|
30180
|
+
text: tokens.slice(p.first, p.last + 1).join("").trim(),
|
|
30181
|
+
speaker: p.speaker
|
|
30182
|
+
}));
|
|
30183
|
+
}
|
|
30184
|
+
var SPEAKER_MATCH_THRESHOLD, MIN_NEW_SPEAKER_S, WORD;
|
|
30185
|
+
var init_speakers = __esm({
|
|
30186
|
+
"../server/dist/stt/speakers.js"() {
|
|
30187
|
+
"use strict";
|
|
30188
|
+
SPEAKER_MATCH_THRESHOLD = 0.55;
|
|
30189
|
+
MIN_NEW_SPEAKER_S = 1;
|
|
30190
|
+
WORD = /[\p{L}\p{N}]/u;
|
|
30191
|
+
}
|
|
30192
|
+
});
|
|
30193
|
+
|
|
30036
30194
|
// ../server/dist/stt/engine.js
|
|
30037
30195
|
import os8 from "node:os";
|
|
30196
|
+
function withTimeout(p, ms) {
|
|
30197
|
+
let timer2;
|
|
30198
|
+
const timeout = new Promise((_, reject) => {
|
|
30199
|
+
timer2 = setTimeout(() => reject(new Error("timed out")), ms);
|
|
30200
|
+
});
|
|
30201
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer2));
|
|
30202
|
+
}
|
|
30038
30203
|
function createSttEngine(deps = {}) {
|
|
30039
30204
|
const loadSherpa = deps.loadSherpa ?? defaultLoadSherpa;
|
|
30040
30205
|
const models = deps.models ?? sttModels;
|
|
30041
30206
|
let sherpa = null;
|
|
30042
30207
|
let recognizer = null;
|
|
30043
30208
|
let loading = null;
|
|
30209
|
+
let diarizer = null;
|
|
30210
|
+
let extractor = null;
|
|
30211
|
+
let diarLoading = null;
|
|
30044
30212
|
const queue = [];
|
|
30045
30213
|
let decoding = false;
|
|
30046
|
-
function load2() {
|
|
30047
|
-
if (recognizer)
|
|
30048
|
-
|
|
30049
|
-
|
|
30050
|
-
|
|
30051
|
-
|
|
30052
|
-
|
|
30214
|
+
async function load2(opts = {}) {
|
|
30215
|
+
if (!recognizer)
|
|
30216
|
+
await (loading ??= doLoad().finally(() => {
|
|
30217
|
+
loading = null;
|
|
30218
|
+
}));
|
|
30219
|
+
if (opts.speakers && !diarizer)
|
|
30220
|
+
await (diarLoading ??= doLoadDiarizer().finally(() => {
|
|
30221
|
+
diarLoading = null;
|
|
30222
|
+
}));
|
|
30223
|
+
}
|
|
30224
|
+
async function doLoadDiarizer() {
|
|
30225
|
+
const t0 = Date.now();
|
|
30226
|
+
try {
|
|
30227
|
+
const embedding = { model: models.filePath(CAMPLUS_MODEL), numThreads: NUM_THREADS, provider: "cpu", debug: 0 };
|
|
30228
|
+
const d = new sherpa.OfflineSpeakerDiarization({
|
|
30229
|
+
segmentation: { pyannote: { model: models.filePath(PYANNOTE_SEG_MODEL) }, numThreads: NUM_THREADS, provider: "cpu", debug: 0 },
|
|
30230
|
+
embedding,
|
|
30231
|
+
clustering: { numClusters: -1, threshold: DIAR_CLUSTER_THRESHOLD },
|
|
30232
|
+
minDurationOn: DIAR_MIN_ON_S,
|
|
30233
|
+
minDurationOff: DIAR_MIN_OFF_S
|
|
30234
|
+
});
|
|
30235
|
+
extractor = new sherpa.SpeakerEmbeddingExtractor(embedding);
|
|
30236
|
+
diarizer = d;
|
|
30237
|
+
console.log(`[STT] speaker diarizer loaded in ${Date.now() - t0}ms`);
|
|
30238
|
+
} catch (err) {
|
|
30239
|
+
console.log(`[STT] speaker diarizer load failed: ${errMsg2(err)}`);
|
|
30240
|
+
throw new SttError("engine", `speaker labelling unavailable: ${errMsg2(err)}`);
|
|
30241
|
+
}
|
|
30242
|
+
}
|
|
30243
|
+
function embed(samples) {
|
|
30244
|
+
const s = extractor.createStream();
|
|
30245
|
+
s.acceptWaveform({ samples: samples.subarray(0, EMBED_MAX_S * SAMPLE_RATE), sampleRate: SAMPLE_RATE });
|
|
30246
|
+
s.inputFinished();
|
|
30247
|
+
return extractor.compute(s, false);
|
|
30053
30248
|
}
|
|
30054
30249
|
async function doLoad() {
|
|
30055
30250
|
const t0 = Date.now();
|
|
@@ -30079,20 +30274,32 @@ function createSttEngine(deps = {}) {
|
|
|
30079
30274
|
decoding = true;
|
|
30080
30275
|
while (queue.length > 0) {
|
|
30081
30276
|
const job = queue.shift();
|
|
30277
|
+
let phase = "decode";
|
|
30082
30278
|
try {
|
|
30083
30279
|
const stream = recognizer.createStream();
|
|
30084
30280
|
stream.acceptWaveform({ samples: job.samples, sampleRate: SAMPLE_RATE });
|
|
30085
30281
|
await recognizer.decodeAsync(stream);
|
|
30086
|
-
|
|
30282
|
+
const result = recognizer.getResult(stream);
|
|
30283
|
+
let diar = null;
|
|
30284
|
+
if (job.owner.wantsSpeakers() && (result.text ?? "").trim()) {
|
|
30285
|
+
phase = "speaker diarization";
|
|
30286
|
+
diar = await withTimeout(sherpa.offlineSpeakerDiarizationProcessAsync(diarizer.handle, job.samples, () => {
|
|
30287
|
+
}), DIAR_TIMEOUT_MS);
|
|
30288
|
+
}
|
|
30289
|
+
await job.owner.done(job, result, diar);
|
|
30087
30290
|
} catch (err) {
|
|
30088
|
-
job.owner.fail(new SttError("engine",
|
|
30291
|
+
job.owner.fail(new SttError("engine", `${phase} failed: ${errMsg2(err)}`));
|
|
30089
30292
|
}
|
|
30090
30293
|
}
|
|
30091
30294
|
decoding = false;
|
|
30092
30295
|
}
|
|
30093
|
-
function open(handlers) {
|
|
30296
|
+
function open(handlers, opts = {}) {
|
|
30094
30297
|
if (!sherpa)
|
|
30095
30298
|
throw new SttError("engine", "speech engine not loaded");
|
|
30299
|
+
if (opts.speakers && !diarizer)
|
|
30300
|
+
throw new SttError("engine", "speaker labelling not loaded");
|
|
30301
|
+
const speakers = opts.speakers ? createSpeakerTracker() : null;
|
|
30302
|
+
const labels = /* @__PURE__ */ new Map();
|
|
30096
30303
|
let vad;
|
|
30097
30304
|
try {
|
|
30098
30305
|
vad = new sherpa.Vad({
|
|
@@ -30148,16 +30355,67 @@ function createSttEngine(deps = {}) {
|
|
|
30148
30355
|
handlers.onError(err);
|
|
30149
30356
|
checkDrain();
|
|
30150
30357
|
}
|
|
30358
|
+
async function speakerRuns(samples, diar) {
|
|
30359
|
+
const duration = samples.length / SAMPLE_RATE;
|
|
30360
|
+
const tick = async () => {
|
|
30361
|
+
await new Promise((r) => setImmediate(r));
|
|
30362
|
+
return !stopped;
|
|
30363
|
+
};
|
|
30364
|
+
if (diar.length === 0)
|
|
30365
|
+
return await tick() ? [{ start: 0, end: duration, speaker: speakers.assign(embed(samples), duration) }] : null;
|
|
30366
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
30367
|
+
for (const d of [...diar].sort((a, b) => a.start - b.start))
|
|
30368
|
+
clusters.set(d.speaker, [...clusters.get(d.speaker) ?? [], d]);
|
|
30369
|
+
const seconds = (segs) => segs.reduce((n, d) => n + d.end - d.start, 0);
|
|
30370
|
+
const runs = [];
|
|
30371
|
+
for (const segs of [...clusters.values()].sort((a, b) => seconds(b) - seconds(a))) {
|
|
30372
|
+
const parts = segs.map((d) => samples.subarray(Math.round(d.start * SAMPLE_RATE), Math.round(d.end * SAMPLE_RATE)));
|
|
30373
|
+
const audio = new Float32Array(Math.min(parts.reduce((n, p) => n + p.length, 0), EMBED_MAX_S * SAMPLE_RATE));
|
|
30374
|
+
let at = 0;
|
|
30375
|
+
for (const p of parts) {
|
|
30376
|
+
if (at >= audio.length)
|
|
30377
|
+
break;
|
|
30378
|
+
audio.set(p.subarray(0, audio.length - at), at);
|
|
30379
|
+
at += p.length;
|
|
30380
|
+
}
|
|
30381
|
+
if (!await tick())
|
|
30382
|
+
return null;
|
|
30383
|
+
const speaker = speakers.assign(embed(audio), seconds(segs));
|
|
30384
|
+
for (const d of segs)
|
|
30385
|
+
runs.push({ start: d.start, end: d.end, speaker });
|
|
30386
|
+
}
|
|
30387
|
+
return runs;
|
|
30388
|
+
}
|
|
30151
30389
|
const owner = {
|
|
30152
|
-
|
|
30390
|
+
wantsSpeakers: () => speakers !== null && !stopped,
|
|
30391
|
+
async done(job, result, diar) {
|
|
30392
|
+
const text6 = (result.text ?? "").trim();
|
|
30393
|
+
let pieces = null;
|
|
30394
|
+
if (text6 && diar && !stopped) {
|
|
30395
|
+
try {
|
|
30396
|
+
const runs = await speakerRuns(job.samples, diar);
|
|
30397
|
+
if (runs)
|
|
30398
|
+
pieces = splitBySpeakerRuns(text6, result.tokens, result.timestamps, runs, job.samples.length / SAMPLE_RATE);
|
|
30399
|
+
} catch (err) {
|
|
30400
|
+
failStream(new SttError("engine", `speaker labelling failed: ${errMsg2(err)}`));
|
|
30401
|
+
}
|
|
30402
|
+
}
|
|
30153
30403
|
pendingSamples -= job.samples.length;
|
|
30154
30404
|
inFlight2--;
|
|
30155
30405
|
if (!stopped) {
|
|
30156
30406
|
segments++;
|
|
30157
30407
|
decodedSamples += job.samples.length;
|
|
30158
|
-
const
|
|
30159
|
-
|
|
30160
|
-
|
|
30408
|
+
const lang = (result.lang ?? "").replace(/<\|([^|]*)\|>/g, "$1");
|
|
30409
|
+
const start3 = job.start / SAMPLE_RATE;
|
|
30410
|
+
const end = job.end / SAMPLE_RATE;
|
|
30411
|
+
if (pieces) {
|
|
30412
|
+
for (const p of pieces) {
|
|
30413
|
+
if (!labels.has(p.speaker))
|
|
30414
|
+
labels.set(p.speaker, String(labels.size));
|
|
30415
|
+
handlers.onFinal({ start: round3(start3 + p.start), end: round3(Math.min(start3 + p.end, end)), text: p.text, lang, speaker: labels.get(p.speaker) });
|
|
30416
|
+
}
|
|
30417
|
+
} else if (text6)
|
|
30418
|
+
handlers.onFinal({ start: round3(start3), end: round3(end), text: text6, lang });
|
|
30161
30419
|
}
|
|
30162
30420
|
checkDrain();
|
|
30163
30421
|
},
|
|
@@ -30246,16 +30504,17 @@ function createSttEngine(deps = {}) {
|
|
|
30246
30504
|
dropQueued();
|
|
30247
30505
|
checkDrain();
|
|
30248
30506
|
},
|
|
30249
|
-
stats: () => ({ segments, audioSeconds: round3(decodedSamples / SAMPLE_RATE) })
|
|
30507
|
+
stats: () => ({ segments, audioSeconds: round3(decodedSamples / SAMPLE_RATE), ...speakers ? { speakers: labels.size } : {} })
|
|
30250
30508
|
};
|
|
30251
30509
|
}
|
|
30252
30510
|
return { load: load2, open };
|
|
30253
30511
|
}
|
|
30254
|
-
var SAMPLE_RATE, VAD_WINDOW, VAD_THRESHOLD, VAD_MIN_SILENCE_S, VAD_MIN_SPEECH_S, VAD_MAX_SPEECH_S, PAD_BEFORE_S, PAD_AFTER_S, RING_S, MAX_QUEUED_SEGMENTS, MAX_PENDING_AUDIO_S, NUM_THREADS, SttError, defaultLoadSherpa, errMsg2, round3, sttEngine;
|
|
30512
|
+
var SAMPLE_RATE, VAD_WINDOW, VAD_THRESHOLD, VAD_MIN_SILENCE_S, VAD_MIN_SPEECH_S, VAD_MAX_SPEECH_S, PAD_BEFORE_S, PAD_AFTER_S, RING_S, MAX_QUEUED_SEGMENTS, MAX_PENDING_AUDIO_S, NUM_THREADS, DIAR_CLUSTER_THRESHOLD, DIAR_MIN_ON_S, DIAR_MIN_OFF_S, EMBED_MAX_S, DIAR_TIMEOUT_MS, SttError, defaultLoadSherpa, errMsg2, round3, sttEngine;
|
|
30255
30513
|
var init_engine = __esm({
|
|
30256
30514
|
"../server/dist/stt/engine.js"() {
|
|
30257
30515
|
"use strict";
|
|
30258
30516
|
init_models();
|
|
30517
|
+
init_speakers();
|
|
30259
30518
|
SAMPLE_RATE = 16e3;
|
|
30260
30519
|
VAD_WINDOW = 512;
|
|
30261
30520
|
VAD_THRESHOLD = 0.5;
|
|
@@ -30268,6 +30527,11 @@ var init_engine = __esm({
|
|
|
30268
30527
|
MAX_QUEUED_SEGMENTS = 32;
|
|
30269
30528
|
MAX_PENDING_AUDIO_S = 60;
|
|
30270
30529
|
NUM_THREADS = Math.min(4, Math.max(1, Math.floor(os8.availableParallelism() / 2)));
|
|
30530
|
+
DIAR_CLUSTER_THRESHOLD = 0.6;
|
|
30531
|
+
DIAR_MIN_ON_S = 0.3;
|
|
30532
|
+
DIAR_MIN_OFF_S = 0.5;
|
|
30533
|
+
EMBED_MAX_S = 6;
|
|
30534
|
+
DIAR_TIMEOUT_MS = 6e4;
|
|
30271
30535
|
SttError = class extends Error {
|
|
30272
30536
|
code;
|
|
30273
30537
|
constructor(code, message) {
|
|
@@ -30276,7 +30540,10 @@ var init_engine = __esm({
|
|
|
30276
30540
|
this.name = "SttError";
|
|
30277
30541
|
}
|
|
30278
30542
|
};
|
|
30279
|
-
defaultLoadSherpa = async () =>
|
|
30543
|
+
defaultLoadSherpa = async () => {
|
|
30544
|
+
const [m, addon] = await Promise.all([import("sherpa-onnx-node"), import("sherpa-onnx-node/addon.js")]);
|
|
30545
|
+
return { ...m.default, offlineSpeakerDiarizationProcessAsync: addon.default.offlineSpeakerDiarizationProcessAsync };
|
|
30546
|
+
};
|
|
30280
30547
|
errMsg2 = (err) => err instanceof Error ? err.message : String(err);
|
|
30281
30548
|
round3 = (s) => Math.round(s * 1e3) / 1e3;
|
|
30282
30549
|
sttEngine = createSttEngine();
|
|
@@ -30308,7 +30575,9 @@ function createSttProxy(deps = {}) {
|
|
|
30308
30575
|
}
|
|
30309
30576
|
});
|
|
30310
30577
|
wss2.on("connection", (ws, req) => {
|
|
30311
|
-
const
|
|
30578
|
+
const q = parseQuery2(req);
|
|
30579
|
+
const extId = q.get("ext");
|
|
30580
|
+
const speakers = q.get("speakers") === "1";
|
|
30312
30581
|
const openedAt = Date.now();
|
|
30313
30582
|
let closed = false;
|
|
30314
30583
|
let failed = false;
|
|
@@ -30332,14 +30601,14 @@ function createSttProxy(deps = {}) {
|
|
|
30332
30601
|
};
|
|
30333
30602
|
async function startEngine() {
|
|
30334
30603
|
try {
|
|
30335
|
-
await engine.load();
|
|
30604
|
+
await engine.load({ speakers });
|
|
30336
30605
|
if (closed || failed || ended)
|
|
30337
30606
|
return;
|
|
30338
30607
|
stream = engine.open({
|
|
30339
30608
|
// Recognized text goes to the client only — never to the log.
|
|
30340
30609
|
onFinal: (f) => send2({ type: "final", ...f }),
|
|
30341
30610
|
onError: (err) => fail2(err.code, err.message)
|
|
30342
|
-
});
|
|
30611
|
+
}, { speakers });
|
|
30343
30612
|
} catch (err) {
|
|
30344
30613
|
if (err instanceof SttError)
|
|
30345
30614
|
fail2(err.code, err.message);
|
|
@@ -30364,7 +30633,7 @@ function createSttProxy(deps = {}) {
|
|
|
30364
30633
|
else if (s.state === "error")
|
|
30365
30634
|
fail2("model", s.message ?? "model download failed");
|
|
30366
30635
|
};
|
|
30367
|
-
console.log(`[STT] stream open ext=${extId}`);
|
|
30636
|
+
console.log(`[STT] stream open ext=${extId}${speakers ? " speakers=1" : ""}`);
|
|
30368
30637
|
ws.on("message", (data, isBinary) => {
|
|
30369
30638
|
if (ended || failed)
|
|
30370
30639
|
return;
|
|
@@ -30400,7 +30669,7 @@ function createSttProxy(deps = {}) {
|
|
|
30400
30669
|
unsubscribe = null;
|
|
30401
30670
|
stream?.abort();
|
|
30402
30671
|
const st = stream?.stats();
|
|
30403
|
-
console.log(`[STT] stream closed after ${((Date.now() - openedAt) / 1e3).toFixed(1)}s ext=${extId}${st ? ` segments=${st.segments} audio=${st.audioSeconds.toFixed(1)}s` : ""}`);
|
|
30672
|
+
console.log(`[STT] stream closed after ${((Date.now() - openedAt) / 1e3).toFixed(1)}s ext=${extId}${st ? ` segments=${st.segments} audio=${st.audioSeconds.toFixed(1)}s${st.speakers !== void 0 ? ` speakers=${st.speakers}` : ""}` : ""}`);
|
|
30404
30673
|
});
|
|
30405
30674
|
const initial = models.status();
|
|
30406
30675
|
if (initial.state === "ready") {
|
|
@@ -31566,6 +31835,12 @@ var init_terminal_manager = __esm({
|
|
|
31566
31835
|
|
|
31567
31836
|
// ../server/dist/ws/handler.js
|
|
31568
31837
|
import path78 from "node:path";
|
|
31838
|
+
function adminSnapshotRows(rows) {
|
|
31839
|
+
return rows.filter((m) => {
|
|
31840
|
+
const t2 = inferMessageType(m);
|
|
31841
|
+
return t2 !== "tool_call" && t2 !== "tool_result";
|
|
31842
|
+
});
|
|
31843
|
+
}
|
|
31569
31844
|
function setupWebSocketHandler(deps) {
|
|
31570
31845
|
const { wss: wss2, registry: registry2 } = deps;
|
|
31571
31846
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -31689,8 +31964,10 @@ function setupWebSocketHandler(deps) {
|
|
|
31689
31964
|
}
|
|
31690
31965
|
if (client.subscriptions.has(targetSessionId)) {
|
|
31691
31966
|
const state = getState(client, targetSessionId);
|
|
31692
|
-
const
|
|
31693
|
-
|
|
31967
|
+
const log2 = state?.messageLog ?? [];
|
|
31968
|
+
const deletedIds = log2.filter((m) => m.deleted).map((m) => m.id);
|
|
31969
|
+
const userRows = log2.filter((m) => m.role === "user" && !m.taskId).length;
|
|
31970
|
+
sendJson(ws, { type: "exchange:deleted", sessionId: targetSessionId, deletedIds, userOrdinal: msg.userOrdinal, userRows });
|
|
31694
31971
|
}
|
|
31695
31972
|
}
|
|
31696
31973
|
function createEventListener(client, sessionId) {
|
|
@@ -32074,7 +32351,7 @@ ${msg.message}` : markers.join("\n");
|
|
|
32074
32351
|
const state2 = getState(client2, sid);
|
|
32075
32352
|
const ctxConfig = await sm.getContextConfig(sid);
|
|
32076
32353
|
const running = sm.isSessionRunning(sid);
|
|
32077
|
-
const messages3 = state2 ? running ?
|
|
32354
|
+
const messages3 = state2 ? adminSnapshotRows(running ? state2.messageLog : createSaveSnapshot(state2)) : [];
|
|
32078
32355
|
const detachedSession = sm.getSessionById(sid);
|
|
32079
32356
|
sendJson(ws, { type: "state:snapshot", snapshot: { recentMessages: messages3, sessionId: sid, maxContextTokens: ctxConfig.maxTokens, agentId: detachedSession?.agentId, archiveCount: archiveCountFor(client2, sid, detachedSession?.agentId), accessLevel: detachedSession?.accessLevel ?? null } });
|
|
32080
32357
|
if (state2 && state2.contextTokens > 0) {
|
|
@@ -32131,7 +32408,7 @@ ${msg.message}` : markers.join("\n");
|
|
|
32131
32408
|
}
|
|
32132
32409
|
}
|
|
32133
32410
|
const state = sid ? getState(client2, sid) : null;
|
|
32134
|
-
const messages2 = state ?
|
|
32411
|
+
const messages2 = state ? adminSnapshotRows(createSaveSnapshot(state)) : [];
|
|
32135
32412
|
sendJson(ws, { type: "state:snapshot", snapshot: { recentMessages: messages2, sessionId: sid, maxContextTokens, agentId, archiveCount: sid ? archiveCountFor(client2, sid, agentId) : 0, accessLevel } });
|
|
32136
32413
|
if (state && state.contextTokens > 0) {
|
|
32137
32414
|
sendJson(ws, { type: "chat:usage", contextTokens: state.contextTokens, outputTokens: state.outputTokens, sessionId: sid });
|
|
@@ -32161,6 +32438,7 @@ var CLIENT_SILENCE_LIMIT_MS, detachedSessions;
|
|
|
32161
32438
|
var init_handler2 = __esm({
|
|
32162
32439
|
"../server/dist/ws/handler.js"() {
|
|
32163
32440
|
"use strict";
|
|
32441
|
+
init_session_types();
|
|
32164
32442
|
init_sandbox();
|
|
32165
32443
|
init_ui_log_builder();
|
|
32166
32444
|
init_config();
|
|
@@ -32291,7 +32569,7 @@ function buildCard(workspace, interfaceUrl, caps) {
|
|
|
32291
32569
|
return {
|
|
32292
32570
|
name: raw.name,
|
|
32293
32571
|
description: raw.description,
|
|
32294
|
-
version: typeof raw.version === "string" ? raw.version : "2.2.
|
|
32572
|
+
version: typeof raw.version === "string" ? raw.version : "2.2.2",
|
|
32295
32573
|
supportedInterfaces: [{ url: interfaceUrl, protocolBinding: "JSONRPC", protocolVersion: A2A_VERSION }],
|
|
32296
32574
|
capabilities: { streaming: caps.streaming, pushNotifications: true, extendedAgentCard: false },
|
|
32297
32575
|
...caps.tokenAuth === false ? {} : {
|
|
@@ -34046,6 +34324,8 @@ async function runJob(jobId, triggerKind) {
|
|
|
34046
34324
|
kickPendingEvents(jobId);
|
|
34047
34325
|
return runId;
|
|
34048
34326
|
};
|
|
34327
|
+
if (triggerKind === "manual" && !job.userPrompt)
|
|
34328
|
+
return skip("nothing to run: the job has no prompt, and run-now carries no webhook events");
|
|
34049
34329
|
if (_inflight.has(jobId))
|
|
34050
34330
|
return skip("previous run still in progress");
|
|
34051
34331
|
if (_inflightSessions.has(sessionKeyFor(job.workspacePath, sessionId)))
|
|
@@ -34131,9 +34411,9 @@ async function runJob(jobId, triggerKind) {
|
|
|
34131
34411
|
const events = webhook ? claimPendingEvents(jobId, runId, sessionId) : [];
|
|
34132
34412
|
try {
|
|
34133
34413
|
const texts = events.map((ev) => eventText(ev, job.workspacePath, false));
|
|
34134
|
-
const stamp = host.appendUserMessage(sessionId, [job.userPrompt, ...texts.map((t2) => t2.ui)].join("\n\n"));
|
|
34414
|
+
const stamp = host.appendUserMessage(sessionId, [job.userPrompt, ...texts.map((t2) => t2.ui)].filter(Boolean).join("\n\n"));
|
|
34135
34415
|
stampEvents(events.map((ev) => ev.id), stamp);
|
|
34136
|
-
await host.sendUserMessage(sessionId, [UNATTENDED_PREAMBLE2, job.userPrompt, ...texts.map((t2) => t2.model)].join("\n\n"), void 0, void 0, stamp);
|
|
34416
|
+
await host.sendUserMessage(sessionId, [UNATTENDED_PREAMBLE2, job.userPrompt, ...texts.map((t2) => t2.model)].filter(Boolean).join("\n\n"), void 0, void 0, stamp);
|
|
34137
34417
|
} catch (err) {
|
|
34138
34418
|
untrackRun(runId);
|
|
34139
34419
|
tracked = false;
|
|
@@ -34531,7 +34811,7 @@ function createCronRoutes() {
|
|
|
34531
34811
|
const body = await c.req.json().catch(() => null);
|
|
34532
34812
|
if (!body)
|
|
34533
34813
|
return c.json({ error: "invalid body" }, 400);
|
|
34534
|
-
for (const k of ["workspacePath", "agentId"
|
|
34814
|
+
for (const k of ["workspacePath", "agentId"]) {
|
|
34535
34815
|
if (!body[k] || typeof body[k] !== "string")
|
|
34536
34816
|
return c.json({ error: `${k} required` }, 400);
|
|
34537
34817
|
}
|
|
@@ -34540,6 +34820,11 @@ function createCronRoutes() {
|
|
|
34540
34820
|
const hasSchedule = typeof body.schedule === "string" && body.schedule.trim().length > 0;
|
|
34541
34821
|
const hasRunAt = typeof body.runAt === "number" && Number.isFinite(body.runAt);
|
|
34542
34822
|
const webhook = body.webhook === true;
|
|
34823
|
+
const userPrompt = body.userPrompt ?? "";
|
|
34824
|
+
if (typeof userPrompt !== "string")
|
|
34825
|
+
return c.json({ error: "userPrompt must be a string" }, 400);
|
|
34826
|
+
if (!userPrompt && !webhook)
|
|
34827
|
+
return c.json({ error: "userPrompt required" }, 400);
|
|
34543
34828
|
if (!hasSchedule && !hasRunAt && !webhook)
|
|
34544
34829
|
return c.json({ error: "schedule, runAt or webhook required" }, 400);
|
|
34545
34830
|
if (hasSchedule && hasRunAt)
|
|
@@ -34570,8 +34855,6 @@ function createCronRoutes() {
|
|
|
34570
34855
|
const stateless = body.stateless === true;
|
|
34571
34856
|
if (stateless && session.sessionId)
|
|
34572
34857
|
return c.json({ error: STATELESS_SESSION_ERR }, 400);
|
|
34573
|
-
if (webhook && session.sessionId)
|
|
34574
|
-
return c.json({ error: WEBHOOK_SESSION_ERR }, 400);
|
|
34575
34858
|
const folder = await parseWorkingDir(body.workspacePath, body.workingDir);
|
|
34576
34859
|
if ("error" in folder)
|
|
34577
34860
|
return c.json({ error: folder.error }, 400);
|
|
@@ -34598,7 +34881,7 @@ function createCronRoutes() {
|
|
|
34598
34881
|
label: body.label && body.label.trim().length > 0 ? body.label.trim() : null,
|
|
34599
34882
|
workspacePath: body.workspacePath,
|
|
34600
34883
|
agentId: body.agentId,
|
|
34601
|
-
userPrompt
|
|
34884
|
+
userPrompt,
|
|
34602
34885
|
schedule: hasSchedule ? body.schedule : "",
|
|
34603
34886
|
runAt: hasRunAt ? body.runAt : null,
|
|
34604
34887
|
webhookSecret: webhook ? newWebhookSecret() : null,
|
|
@@ -34694,8 +34977,12 @@ function createCronRoutes() {
|
|
|
34694
34977
|
if (!nextWebhookSecret && nextSchedule.trim().length === 0 && typeof nextRunAt !== "number") {
|
|
34695
34978
|
return c.json({ error: "schedule, runAt or webhook required" }, 400);
|
|
34696
34979
|
}
|
|
34697
|
-
if (
|
|
34698
|
-
return c.json({ error:
|
|
34980
|
+
if (body.userPrompt !== void 0 && typeof body.userPrompt !== "string")
|
|
34981
|
+
return c.json({ error: "userPrompt must be a string" }, 400);
|
|
34982
|
+
const nextPrompt = body.userPrompt ?? existing.userPrompt;
|
|
34983
|
+
if (!nextWebhookSecret && !nextPrompt && (body.userPrompt !== void 0 || existing.webhookSecret !== null)) {
|
|
34984
|
+
return c.json({ error: "userPrompt required (only a webhook job may leave it empty)" }, 400);
|
|
34985
|
+
}
|
|
34699
34986
|
for (const k of ["activeFrom", "activeUntil"]) {
|
|
34700
34987
|
const err = validateActiveBound(k, body[k]);
|
|
34701
34988
|
if (err)
|
|
@@ -34720,7 +35007,7 @@ function createCronRoutes() {
|
|
|
34720
35007
|
if (body.agentId !== void 0)
|
|
34721
35008
|
patch.agentId = body.agentId;
|
|
34722
35009
|
if (body.userPrompt !== void 0)
|
|
34723
|
-
patch.userPrompt =
|
|
35010
|
+
patch.userPrompt = nextPrompt;
|
|
34724
35011
|
if (body.schedule !== void 0 || clearSchedule)
|
|
34725
35012
|
patch.schedule = nextSchedule;
|
|
34726
35013
|
if (body.runAt !== void 0 || clearRunAt)
|
|
@@ -34871,7 +35158,7 @@ function computeNextRun(j) {
|
|
|
34871
35158
|
return null;
|
|
34872
35159
|
}
|
|
34873
35160
|
}
|
|
34874
|
-
var
|
|
35161
|
+
var TIMEOUT_SEC_MIN, TIMEOUT_SEC_MAX, STATELESS_SESSION_ERR;
|
|
34875
35162
|
var init_cron = __esm({
|
|
34876
35163
|
"../server/dist/routes/cron.js"() {
|
|
34877
35164
|
"use strict";
|
|
@@ -34881,7 +35168,6 @@ var init_cron = __esm({
|
|
|
34881
35168
|
init_dispatcher();
|
|
34882
35169
|
init_broadcast();
|
|
34883
35170
|
init_session_id();
|
|
34884
|
-
WEBHOOK_SESSION_ERR = "a webhook job runs in its own session (or a new one every run), not a picked sessionId";
|
|
34885
35171
|
TIMEOUT_SEC_MIN = 60;
|
|
34886
35172
|
TIMEOUT_SEC_MAX = 21600;
|
|
34887
35173
|
STATELESS_SESSION_ERR = "stateless (new session every run) and sessionId are mutually exclusive";
|
|
@@ -35381,7 +35667,7 @@ function createCoreChecker(opts) {
|
|
|
35381
35667
|
}
|
|
35382
35668
|
function getCoreStatus(force) {
|
|
35383
35669
|
checker ??= createCoreChecker({
|
|
35384
|
-
haloVersion: "2.2.
|
|
35670
|
+
haloVersion: "2.2.2",
|
|
35385
35671
|
fetchLatest: fetchNpmLatest,
|
|
35386
35672
|
onChange: (status) => broadcast({ type: "updates:changed", scope: "core", status })
|
|
35387
35673
|
});
|
|
@@ -35549,7 +35835,7 @@ function hubBase() {
|
|
|
35549
35835
|
}
|
|
35550
35836
|
function serverChecker() {
|
|
35551
35837
|
checker2 ??= createExtensionsChecker({
|
|
35552
|
-
haloVersion: "2.2.
|
|
35838
|
+
haloVersion: "2.2.2",
|
|
35553
35839
|
installed: () => {
|
|
35554
35840
|
const s = getSnapshot();
|
|
35555
35841
|
return [...s.extensions, ...s.disabled];
|
|
@@ -41920,7 +42206,7 @@ var init_dist2 = __esm({
|
|
|
41920
42206
|
init_git_ssh();
|
|
41921
42207
|
__dirname3 = path93.dirname(fileURLToPath3(import.meta.url));
|
|
41922
42208
|
PORT = config.server.port;
|
|
41923
|
-
HALO_VERSION = "2.2.
|
|
42209
|
+
HALO_VERSION = "2.2.2";
|
|
41924
42210
|
PROJECT_ROOT = findProjectRoot();
|
|
41925
42211
|
HALO_HOME4 = path93.join(homedir25(), ".halo");
|
|
41926
42212
|
GIT_SHA = readGitSha();
|
|
@@ -42073,8 +42359,21 @@ var init_dist2 = __esm({
|
|
|
42073
42359
|
}
|
|
42074
42360
|
FRONTEND_DIR = resolveFrontendDir();
|
|
42075
42361
|
if (!AGENTCORE) {
|
|
42076
|
-
app.use("/*", serveStatic({
|
|
42362
|
+
app.use("/*", serveStatic({
|
|
42363
|
+
root: path93.relative(process.cwd(), FRONTEND_DIR),
|
|
42364
|
+
// Next's `/_next/static/` files are content-hashed (under a buildId dir),
|
|
42365
|
+
// so they can be cached forever. Only on a found file — the SPA fallback
|
|
42366
|
+
// below must stay uncached, or a missing chunk caches index.html as js.
|
|
42367
|
+
// `c.res.headers`, not `c.header`: node-server calls onFound after the
|
|
42368
|
+
// body is built, so only c.res (merged into the result by compose) lands.
|
|
42369
|
+
onFound: (_path, c) => {
|
|
42370
|
+
if (c.req.path.startsWith("/_next/static/"))
|
|
42371
|
+
c.res.headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
42372
|
+
}
|
|
42373
|
+
}));
|
|
42077
42374
|
app.get("/*", (c) => {
|
|
42375
|
+
if (c.req.path.startsWith("/_next/static/"))
|
|
42376
|
+
return c.notFound();
|
|
42078
42377
|
const indexPath = path93.join(FRONTEND_DIR, "index.html");
|
|
42079
42378
|
try {
|
|
42080
42379
|
const html = fs88.readFileSync(indexPath, "utf-8");
|
|
@@ -42102,6 +42401,7 @@ var init_dist2 = __esm({
|
|
|
42102
42401
|
});
|
|
42103
42402
|
wss = new WebSocketServer5({
|
|
42104
42403
|
noServer: true,
|
|
42404
|
+
perMessageDeflate: { zlibDeflateOptions: { level: 1 }, threshold: 1024 },
|
|
42105
42405
|
verifyClient: (info, callback) => {
|
|
42106
42406
|
const token = getTokenFromCookieHeader(info.req.headers.cookie);
|
|
42107
42407
|
if (isAuthenticated(token)) {
|
|
@@ -43981,7 +44281,7 @@ function runNode(opts) {
|
|
|
43981
44281
|
shellTimeoutMs: config.timeout.shellExec,
|
|
43982
44282
|
os: process.platform,
|
|
43983
44283
|
hostname: os13.hostname(),
|
|
43984
|
-
version: "2.2.
|
|
44284
|
+
version: "2.2.2"
|
|
43985
44285
|
};
|
|
43986
44286
|
const exec2 = createNodeExecutor({ root, base, allowShell }, log2);
|
|
43987
44287
|
const instance = crypto17.randomUUID();
|
|
@@ -44497,7 +44797,7 @@ function installUserUnit(opts, run = runCommand) {
|
|
|
44497
44797
|
}
|
|
44498
44798
|
|
|
44499
44799
|
// src/index.ts
|
|
44500
|
-
var VERSION = "2.2.
|
|
44800
|
+
var VERSION = "2.2.2";
|
|
44501
44801
|
var HELP_TOP = `Usage: halo <command> [options]
|
|
44502
44802
|
|
|
44503
44803
|
Commands:
|