@filmwhisper/mcp-server 0.1.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/LICENSE +21 -0
- package/dist/__tests__/data-integrity.test.d.ts +2 -0
- package/dist/__tests__/data-integrity.test.d.ts.map +1 -0
- package/dist/__tests__/data-integrity.test.js +171 -0
- package/dist/__tests__/data-integrity.test.js.map +1 -0
- package/dist/__tests__/engine/ipc-contract.test.d.ts +2 -0
- package/dist/__tests__/engine/ipc-contract.test.d.ts.map +1 -0
- package/dist/__tests__/engine/ipc-contract.test.js +122 -0
- package/dist/__tests__/engine/ipc-contract.test.js.map +1 -0
- package/dist/__tests__/fw-project-init.test.d.ts +2 -0
- package/dist/__tests__/fw-project-init.test.d.ts.map +1 -0
- package/dist/__tests__/fw-project-init.test.js +96 -0
- package/dist/__tests__/fw-project-init.test.js.map +1 -0
- package/dist/__tests__/helpers/mock-engine.d.ts +2 -0
- package/dist/__tests__/helpers/mock-engine.d.ts.map +1 -0
- package/dist/__tests__/helpers/mock-engine.js +81 -0
- package/dist/__tests__/helpers/mock-engine.js.map +1 -0
- package/dist/__tests__/r1-quality.test.d.ts +2 -0
- package/dist/__tests__/r1-quality.test.d.ts.map +1 -0
- package/dist/__tests__/r1-quality.test.js +539 -0
- package/dist/__tests__/r1-quality.test.js.map +1 -0
- package/dist/__tests__/search-fts5.test.d.ts +2 -0
- package/dist/__tests__/search-fts5.test.d.ts.map +1 -0
- package/dist/__tests__/search-fts5.test.js +135 -0
- package/dist/__tests__/search-fts5.test.js.map +1 -0
- package/dist/__tests__/tools/assemble.test.d.ts +2 -0
- package/dist/__tests__/tools/assemble.test.d.ts.map +1 -0
- package/dist/__tests__/tools/assemble.test.js +203 -0
- package/dist/__tests__/tools/assemble.test.js.map +1 -0
- package/dist/__tests__/tools/export.test.d.ts +2 -0
- package/dist/__tests__/tools/export.test.d.ts.map +1 -0
- package/dist/__tests__/tools/export.test.js +126 -0
- package/dist/__tests__/tools/export.test.js.map +1 -0
- package/dist/__tests__/tools/integration.test.d.ts +2 -0
- package/dist/__tests__/tools/integration.test.d.ts.map +1 -0
- package/dist/__tests__/tools/integration.test.js +707 -0
- package/dist/__tests__/tools/integration.test.js.map +1 -0
- package/dist/__tests__/tools/relink.test.d.ts +2 -0
- package/dist/__tests__/tools/relink.test.d.ts.map +1 -0
- package/dist/__tests__/tools/relink.test.js +107 -0
- package/dist/__tests__/tools/relink.test.js.map +1 -0
- package/dist/__tests__/tools/render-preview.test.d.ts +2 -0
- package/dist/__tests__/tools/render-preview.test.d.ts.map +1 -0
- package/dist/__tests__/tools/render-preview.test.js +99 -0
- package/dist/__tests__/tools/render-preview.test.js.map +1 -0
- package/dist/engine/engine-client.d.ts +29 -0
- package/dist/engine/engine-client.d.ts.map +1 -0
- package/dist/engine/engine-client.js +211 -0
- package/dist/engine/engine-client.js.map +1 -0
- package/dist/engine/engine-manager.d.ts +31 -0
- package/dist/engine/engine-manager.d.ts.map +1 -0
- package/dist/engine/engine-manager.js +144 -0
- package/dist/engine/engine-manager.js.map +1 -0
- package/dist/engine/types.d.ts +6 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +2 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/helpers/errors.d.ts +13 -0
- package/dist/helpers/errors.d.ts.map +1 -0
- package/dist/helpers/errors.js +14 -0
- package/dist/helpers/errors.js.map +1 -0
- package/dist/helpers/media-extensions.d.ts +8 -0
- package/dist/helpers/media-extensions.d.ts.map +1 -0
- package/dist/helpers/media-extensions.js +39 -0
- package/dist/helpers/media-extensions.js.map +1 -0
- package/dist/helpers/undo.d.ts +55 -0
- package/dist/helpers/undo.d.ts.map +1 -0
- package/dist/helpers/undo.js +142 -0
- package/dist/helpers/undo.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +62 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +7 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +59 -0
- package/dist/resources.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +33 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/project-store.d.ts +92 -0
- package/dist/storage/project-store.d.ts.map +1 -0
- package/dist/storage/project-store.js +399 -0
- package/dist/storage/project-store.js.map +1 -0
- package/dist/storage/sqlite-store.d.ts +207 -0
- package/dist/storage/sqlite-store.d.ts.map +1 -0
- package/dist/storage/sqlite-store.js +983 -0
- package/dist/storage/sqlite-store.js.map +1 -0
- package/dist/tools/assemble.d.ts +17 -0
- package/dist/tools/assemble.d.ts.map +1 -0
- package/dist/tools/assemble.js +237 -0
- package/dist/tools/assemble.js.map +1 -0
- package/dist/tools/edit.d.ts +7 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +159 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/export.d.ts +7 -0
- package/dist/tools/export.d.ts.map +1 -0
- package/dist/tools/export.js +108 -0
- package/dist/tools/export.js.map +1 -0
- package/dist/tools/inspect.d.ts +7 -0
- package/dist/tools/inspect.d.ts.map +1 -0
- package/dist/tools/inspect.js +238 -0
- package/dist/tools/inspect.js.map +1 -0
- package/dist/tools/process.d.ts +7 -0
- package/dist/tools/process.d.ts.map +1 -0
- package/dist/tools/process.js +211 -0
- package/dist/tools/process.js.map +1 -0
- package/dist/tools/project.d.ts +7 -0
- package/dist/tools/project.d.ts.map +1 -0
- package/dist/tools/project.js +178 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/search.d.ts +7 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +75 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/utility.d.ts +7 -0
- package/dist/tools/utility.d.ts.map +1 -0
- package/dist/tools/utility.js +108 -0
- package/dist/tools/utility.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FilmWhisper Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-integrity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/data-integrity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Integrity Hardening tests:
|
|
3
|
+
* 1. Stale .tmp.* cleanup
|
|
4
|
+
* 2. WAL integrity check on a valid database
|
|
5
|
+
* 3. Cross-store reconciliation mismatch detection
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { ProjectStore } from "../storage/project-store.js";
|
|
12
|
+
import { SqliteStore } from "../storage/sqlite-store.js";
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Helpers
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
async function makeTmpDir() {
|
|
17
|
+
return fs.mkdtemp(path.join(os.tmpdir(), "fw-integrity-"));
|
|
18
|
+
}
|
|
19
|
+
async function initFwDir(projectPath) {
|
|
20
|
+
await fs.mkdir(path.join(projectPath, ".fw", "assets"), { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// 1. Stale .tmp.* cleanup
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
describe("ProjectStore.cleanupStaleFiles", () => {
|
|
26
|
+
let tmpDir;
|
|
27
|
+
const store = new ProjectStore();
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
tmpDir = await makeTmpDir();
|
|
30
|
+
await initFwDir(tmpDir);
|
|
31
|
+
});
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
it("removes .tmp.{pid} files from .fw/ and nested dirs", async () => {
|
|
36
|
+
const fw = path.join(tmpDir, ".fw");
|
|
37
|
+
const assetsDir = path.join(fw, "assets");
|
|
38
|
+
// Create stale temp files
|
|
39
|
+
const stale1 = path.join(fw, "project.fw.json.tmp.12345");
|
|
40
|
+
const stale2 = path.join(assetsDir, "abc123.fw.json.tmp.99999");
|
|
41
|
+
await fs.writeFile(stale1, "orphan");
|
|
42
|
+
await fs.writeFile(stale2, "orphan");
|
|
43
|
+
// Create a legitimate file that should NOT be deleted
|
|
44
|
+
const legit = path.join(fw, "project.fw.json");
|
|
45
|
+
await fs.writeFile(legit, "{}");
|
|
46
|
+
const deleted = await store.cleanupStaleFiles(tmpDir);
|
|
47
|
+
expect(deleted).toHaveLength(2);
|
|
48
|
+
expect(deleted).toContain(stale1);
|
|
49
|
+
expect(deleted).toContain(stale2);
|
|
50
|
+
// Legitimate file untouched
|
|
51
|
+
await expect(fs.access(legit)).resolves.toBeUndefined();
|
|
52
|
+
// Stale files gone
|
|
53
|
+
await expect(fs.access(stale1)).rejects.toThrow();
|
|
54
|
+
await expect(fs.access(stale2)).rejects.toThrow();
|
|
55
|
+
});
|
|
56
|
+
it("returns empty array when no stale files exist", async () => {
|
|
57
|
+
const fw = path.join(tmpDir, ".fw");
|
|
58
|
+
await fs.writeFile(path.join(fw, "project.fw.json"), "{}");
|
|
59
|
+
const deleted = await store.cleanupStaleFiles(tmpDir);
|
|
60
|
+
expect(deleted).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
it("does not throw when .fw directory does not exist", async () => {
|
|
63
|
+
const emptyDir = await makeTmpDir();
|
|
64
|
+
try {
|
|
65
|
+
const deleted = await store.cleanupStaleFiles(emptyDir);
|
|
66
|
+
expect(deleted).toHaveLength(0);
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
await fs.rm(emptyDir, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// 2. WAL integrity check
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
describe("SqliteStore.checkIntegrity", () => {
|
|
77
|
+
it("returns 'ok' for a freshly created in-memory database", () => {
|
|
78
|
+
const store = new SqliteStore(":memory:");
|
|
79
|
+
try {
|
|
80
|
+
expect(store.checkIntegrity()).toBe("ok");
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
store.close();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
it("returns 'ok' for a freshly created on-disk database", async () => {
|
|
87
|
+
const tmpDir = await makeTmpDir();
|
|
88
|
+
const dbPath = path.join(tmpDir, "state.sqlite");
|
|
89
|
+
try {
|
|
90
|
+
const store = new SqliteStore(dbPath);
|
|
91
|
+
try {
|
|
92
|
+
expect(store.checkIntegrity()).toBe("ok");
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
store.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
it("walCheckpoint runs without error on a healthy database", () => {
|
|
103
|
+
const store = new SqliteStore(":memory:");
|
|
104
|
+
try {
|
|
105
|
+
expect(() => store.walCheckpoint()).not.toThrow();
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
store.close();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// 3. Cross-store reconciliation
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
describe("SqliteStore.getAssetHashes reconciliation", () => {
|
|
116
|
+
it("returns empty set when no clips or reviews exist", () => {
|
|
117
|
+
const store = new SqliteStore(":memory:");
|
|
118
|
+
try {
|
|
119
|
+
expect(store.getAssetHashes().size).toBe(0);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
store.close();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
it("returns hashes referenced by timeline clips", () => {
|
|
126
|
+
const store = new SqliteStore(":memory:");
|
|
127
|
+
try {
|
|
128
|
+
store.addClip({ assetHash: "hash-aaa", inPointMs: 0, outPointMs: 1000 });
|
|
129
|
+
store.addClip({ assetHash: "hash-bbb", inPointMs: 0, outPointMs: 500 });
|
|
130
|
+
const hashes = store.getAssetHashes();
|
|
131
|
+
expect(hashes.has("hash-aaa")).toBe(true);
|
|
132
|
+
expect(hashes.has("hash-bbb")).toBe(true);
|
|
133
|
+
expect(hashes.size).toBe(2);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
store.close();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
it("returns hashes referenced by segment reviews", () => {
|
|
140
|
+
const store = new SqliteStore(":memory:");
|
|
141
|
+
try {
|
|
142
|
+
store.setReview({ assetHash: "hash-ccc", segmentIndex: 0, status: "keep" });
|
|
143
|
+
const hashes = store.getAssetHashes();
|
|
144
|
+
expect(hashes.has("hash-ccc")).toBe(true);
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
store.close();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
it("detects SQLite-only hashes (no corresponding JSON asset file)", () => {
|
|
151
|
+
// Simulate what fw_project_rescan does: compare JSON asset hash set with SQLite
|
|
152
|
+
const store = new SqliteStore(":memory:");
|
|
153
|
+
try {
|
|
154
|
+
store.addClip({ assetHash: "orphan-hash", inPointMs: 0, outPointMs: 1000 });
|
|
155
|
+
const jsonAssetHashes = new Set(["known-hash"]);
|
|
156
|
+
const sqliteHashes = store.getAssetHashes();
|
|
157
|
+
const sqliteOnlyHashes = [];
|
|
158
|
+
for (const hash of sqliteHashes) {
|
|
159
|
+
if (!jsonAssetHashes.has(hash)) {
|
|
160
|
+
sqliteOnlyHashes.push(hash);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
expect(sqliteOnlyHashes).toContain("orphan-hash");
|
|
164
|
+
expect(sqliteOnlyHashes).toHaveLength(1);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
store.close();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=data-integrity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-integrity.test.js","sourceRoot":"","sources":["../../src/__tests__/data-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,KAAK,UAAU,UAAU;IACvB,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,WAAmB;IAC1C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,IAAI,MAAc,CAAC;IACnB,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IAEjC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAC5B,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,2BAA2B,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;QAChE,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrC,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAErC,sDAAsD;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAEtD,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAElC,4BAA4B;QAC5B,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QACxD,mBAAmB;QACnB,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAClD,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;QAE3D,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,QAAQ,GAAG,MAAM,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YACxD,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5C,CAAC;oBAAS,CAAC;gBACT,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACpD,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;YACxE,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;YACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;YACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,gFAAgF;QAChF,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;YAE5E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,CAAC,YAAY,CAAC,CAAC,CAAC;YACxD,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;YAE5C,MAAM,gBAAgB,GAAa,EAAE,CAAC;YACtC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;gBAChC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/B,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;YAED,MAAM,CAAC,gBAAgB,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,CAAC,gBAAgB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipc-contract.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/engine/ipc-contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPC Contract Tests
|
|
3
|
+
*
|
|
4
|
+
* These tests spawn the real Python daemon and verify the JSON-RPC 2.0
|
|
5
|
+
* protocol contract between the TS client and the Python process.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import { EngineClient, EngineClientError } from "../../engine/engine-client.js";
|
|
11
|
+
import { EngineManager } from "../../engine/engine-manager.js";
|
|
12
|
+
const ENGINE_DIR = path.resolve(__dirname, "../../../../processing-engine");
|
|
13
|
+
const VENV_PYTHON = path.join(ENGINE_DIR, ".venv", "bin", "python3");
|
|
14
|
+
const PYTHON_PATH = fs.existsSync(VENV_PYTHON) ? VENV_PYTHON : "python3";
|
|
15
|
+
describe("IPC Contract Tests", () => {
|
|
16
|
+
let client;
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
client = new EngineClient(PYTHON_PATH);
|
|
19
|
+
await client.start(ENGINE_DIR);
|
|
20
|
+
});
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await client.stop();
|
|
23
|
+
});
|
|
24
|
+
it("initialize handshake returns protocol version", async () => {
|
|
25
|
+
const result = await client.request("initialize", { protocol_version: "1.0" }, 5_000);
|
|
26
|
+
expect(result).toHaveProperty("protocol_version", "1.0");
|
|
27
|
+
expect(result).toHaveProperty("capabilities");
|
|
28
|
+
expect(typeof result.capabilities).toBe("object");
|
|
29
|
+
});
|
|
30
|
+
it("ping returns pong with uptime", async () => {
|
|
31
|
+
const result = await client.request("ping", {}, 5_000);
|
|
32
|
+
expect(result).toHaveProperty("status", "pong");
|
|
33
|
+
expect(result).toHaveProperty("uptime_seconds");
|
|
34
|
+
expect(typeof result.uptime_seconds).toBe("number");
|
|
35
|
+
});
|
|
36
|
+
it("unknown method returns -32601 error", async () => {
|
|
37
|
+
try {
|
|
38
|
+
await client.request("nonexistent_method", {}, 5_000);
|
|
39
|
+
expect.fail("Should have thrown");
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
expect(err).toBeInstanceOf(EngineClientError);
|
|
43
|
+
expect(err.code).toBe(-32601);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
it("timeout produces clear error", async () => {
|
|
47
|
+
// Spawn a separate client that we'll stop mid-request to test timeout.
|
|
48
|
+
// We create a second client, start it, then kill its process so it can't
|
|
49
|
+
// respond, and verify the pending request times out.
|
|
50
|
+
const timeoutClient = new EngineClient(PYTHON_PATH);
|
|
51
|
+
await timeoutClient.start(ENGINE_DIR);
|
|
52
|
+
// Verify it's alive
|
|
53
|
+
const pong = await timeoutClient.request("ping", {}, 5_000);
|
|
54
|
+
expect(pong.status).toBe("pong");
|
|
55
|
+
// Kill the underlying process so the next request can never get a response.
|
|
56
|
+
// Access the pid and kill it with SIGSTOP to freeze (not exit) so the
|
|
57
|
+
// client stays "running" but unresponsive.
|
|
58
|
+
const pid = timeoutClient.pid;
|
|
59
|
+
expect(pid).not.toBeNull();
|
|
60
|
+
process.kill(pid, "SIGSTOP");
|
|
61
|
+
try {
|
|
62
|
+
await timeoutClient.request("ping", {}, 200);
|
|
63
|
+
expect.fail("Should have thrown");
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
expect(err).toBeInstanceOf(EngineClientError);
|
|
67
|
+
expect(err.message).toMatch(/timeout/i);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
// SIGCONT then stop cleanly
|
|
71
|
+
try {
|
|
72
|
+
process.kill(pid, "SIGCONT");
|
|
73
|
+
}
|
|
74
|
+
catch { /* process may have exited */ }
|
|
75
|
+
await timeoutClient.stop();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it("transcode returns FILE_NOT_FOUND for missing source", async () => {
|
|
79
|
+
try {
|
|
80
|
+
await client.request("transcode", {
|
|
81
|
+
asset_hash: "test123",
|
|
82
|
+
source_path: "/nonexistent_test.mp4",
|
|
83
|
+
proxy_spec: "720p",
|
|
84
|
+
output_path: "/tmp/fw_test_out.mp4",
|
|
85
|
+
}, 5_000);
|
|
86
|
+
throw new Error("Should have thrown");
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
expect(err).toBeInstanceOf(EngineClientError);
|
|
90
|
+
expect(err.code).toBe(1003); // FILE_NOT_FOUND
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe("EngineManager lifecycle", () => {
|
|
95
|
+
let manager;
|
|
96
|
+
afterAll(async () => {
|
|
97
|
+
if (manager) {
|
|
98
|
+
await manager.shutdown();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
it("auto-starts daemon on first request", async () => {
|
|
102
|
+
manager = new EngineManager(ENGINE_DIR, PYTHON_PATH);
|
|
103
|
+
expect(manager.isRunning).toBe(false);
|
|
104
|
+
const result = await manager.request("ping", {}, 5_000);
|
|
105
|
+
expect(manager.isRunning).toBe(true);
|
|
106
|
+
expect(result.status).toBe("pong");
|
|
107
|
+
});
|
|
108
|
+
it("healthCheck returns true when daemon is running", async () => {
|
|
109
|
+
const healthy = await manager.healthCheck();
|
|
110
|
+
expect(healthy).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it("shutdown stops the daemon", async () => {
|
|
113
|
+
await manager.shutdown();
|
|
114
|
+
expect(manager.isRunning).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it("restarts after shutdown on next request", async () => {
|
|
117
|
+
const result = await manager.request("ping", {}, 5_000);
|
|
118
|
+
expect(manager.isRunning).toBe(true);
|
|
119
|
+
expect(result.status).toBe("pong");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=ipc-contract.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipc-contract.test.js","sourceRoot":"","sources":["../../../src/__tests__/engine/ipc-contract.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fw-project-init.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fw-project-init.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TDD RED → GREEN: fw_project_init MCP tool test
|
|
3
|
+
* Uses InMemoryTransport for process-free testing
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
+
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { createServer } from "../server.js";
|
|
12
|
+
import { ProjectStore } from "../storage/project-store.js";
|
|
13
|
+
describe("fw_project_init tool", () => {
|
|
14
|
+
let client;
|
|
15
|
+
let cleanup;
|
|
16
|
+
let tmpDir;
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "fw-test-"));
|
|
19
|
+
const deps = {
|
|
20
|
+
projectStore: new ProjectStore(),
|
|
21
|
+
projects: new Map(),
|
|
22
|
+
};
|
|
23
|
+
const server = createServer(deps);
|
|
24
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
25
|
+
await server.connect(serverTransport);
|
|
26
|
+
client = new Client({ name: "test-client", version: "1.0.0" });
|
|
27
|
+
await client.connect(clientTransport);
|
|
28
|
+
cleanup = async () => {
|
|
29
|
+
await client.close();
|
|
30
|
+
await server.close();
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await cleanup();
|
|
35
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
it("should be listed in available tools", async () => {
|
|
38
|
+
const { tools } = await client.listTools();
|
|
39
|
+
const toolNames = tools.map((t) => t.name);
|
|
40
|
+
expect(toolNames).toContain("fw_project_init");
|
|
41
|
+
});
|
|
42
|
+
it("should return project_id and asset counts on valid input", async () => {
|
|
43
|
+
const result = await client.callTool({
|
|
44
|
+
name: "fw_project_init",
|
|
45
|
+
arguments: {
|
|
46
|
+
name: "Test Project",
|
|
47
|
+
source_path: tmpDir,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
expect(result.isError).toBeFalsy();
|
|
51
|
+
const content = result.content;
|
|
52
|
+
expect(content).toHaveLength(1);
|
|
53
|
+
expect(content[0].type).toBe("text");
|
|
54
|
+
const data = JSON.parse(content[0].text);
|
|
55
|
+
expect(data).toHaveProperty("project_id");
|
|
56
|
+
expect(data).toHaveProperty("assets_discovered");
|
|
57
|
+
expect(data).toHaveProperty("assets_new");
|
|
58
|
+
expect(data).toHaveProperty("revision");
|
|
59
|
+
expect(typeof data.project_id).toBe("string");
|
|
60
|
+
expect(data.project_id.length).toBeGreaterThan(0);
|
|
61
|
+
});
|
|
62
|
+
it("should accept optional fields", async () => {
|
|
63
|
+
const result = await client.callTool({
|
|
64
|
+
name: "fw_project_init",
|
|
65
|
+
arguments: {
|
|
66
|
+
name: "Full Options Project",
|
|
67
|
+
source_path: tmpDir,
|
|
68
|
+
proxy_spec: "1280x720",
|
|
69
|
+
language: "ko",
|
|
70
|
+
target_duration_seconds: 120,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
expect(result.isError).toBeFalsy();
|
|
74
|
+
const data = JSON.parse(result.content[0].text);
|
|
75
|
+
expect(data.project_id).toBeTruthy();
|
|
76
|
+
});
|
|
77
|
+
it("should fail when name is missing", async () => {
|
|
78
|
+
const result = await client.callTool({
|
|
79
|
+
name: "fw_project_init",
|
|
80
|
+
arguments: {
|
|
81
|
+
source_path: tmpDir,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
expect(result.isError).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it("should fail when source_path is missing", async () => {
|
|
87
|
+
const result = await client.callTool({
|
|
88
|
+
name: "fw_project_init",
|
|
89
|
+
arguments: {
|
|
90
|
+
name: "Test",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
expect(result.isError).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=fw-project-init.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fw-project-init.test.js","sourceRoot":"","sources":["../../src/__tests__/fw-project-init.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-engine.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/mock-engine.ts"],"names":[],"mappings":"AAMA,wBAAgB,uBAAuB,IAAI,OAAO,CAuFjD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function createMockEngineManager() {
|
|
2
|
+
return {
|
|
3
|
+
async request(method, params, _timeout) {
|
|
4
|
+
switch (method) {
|
|
5
|
+
case "editorial_rank": {
|
|
6
|
+
// Echo back the segments that were passed in as ranked clips
|
|
7
|
+
const inputSegments = params.segments;
|
|
8
|
+
return {
|
|
9
|
+
ranked_clips: inputSegments.map((s) => ({
|
|
10
|
+
asset_hash: s.asset_hash,
|
|
11
|
+
segment_index: s.segment_index,
|
|
12
|
+
score: 0.9,
|
|
13
|
+
role: "interview",
|
|
14
|
+
selection_reason: "high story value",
|
|
15
|
+
})),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
case "duration_trim": {
|
|
19
|
+
// Echo back the clips that were passed in, preserving asset_hash/segment_index
|
|
20
|
+
const inputClips = params.clips;
|
|
21
|
+
const trimmedClips = inputClips.map((c) => ({
|
|
22
|
+
asset_hash: c.asset_hash,
|
|
23
|
+
segment_index: c.segment_index,
|
|
24
|
+
in_point_ms: c.start_ms,
|
|
25
|
+
out_point_ms: c.end_ms,
|
|
26
|
+
trimmed: false,
|
|
27
|
+
}));
|
|
28
|
+
const totalDurationMs = inputClips.reduce((sum, c) => sum + (c.end_ms - c.start_ms), 0);
|
|
29
|
+
return {
|
|
30
|
+
trimmed_clips: trimmedClips,
|
|
31
|
+
total_duration_ms: totalDurationMs,
|
|
32
|
+
rationale: trimmedClips.map((_, i) => ({
|
|
33
|
+
clip_index: i,
|
|
34
|
+
role: "interview",
|
|
35
|
+
selection_reason: "included at full length",
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
case "gap_fill": {
|
|
40
|
+
// Echo backbone clips back as-is (no actual gap fill in tests)
|
|
41
|
+
const backbone = params.backbone_clips;
|
|
42
|
+
const filledClips = backbone.map((c) => ({
|
|
43
|
+
asset_hash: c.asset_hash,
|
|
44
|
+
segment_index: c.segment_index,
|
|
45
|
+
in_point_ms: c.start_ms,
|
|
46
|
+
out_point_ms: c.end_ms,
|
|
47
|
+
is_gap_fill: false,
|
|
48
|
+
}));
|
|
49
|
+
return {
|
|
50
|
+
filled_clips: filledClips,
|
|
51
|
+
total_duration_ms: backbone.reduce((sum, c) => sum + (c.end_ms - c.start_ms), 0),
|
|
52
|
+
rationale: filledClips.map((_, i) => ({
|
|
53
|
+
clip_index: i,
|
|
54
|
+
role: "backbone",
|
|
55
|
+
selection_reason: "backbone clip",
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
case "export":
|
|
60
|
+
return { output_path: params.output_path };
|
|
61
|
+
case "assemble":
|
|
62
|
+
return {
|
|
63
|
+
output_path: params.output_path,
|
|
64
|
+
duration_seconds: 30,
|
|
65
|
+
};
|
|
66
|
+
default:
|
|
67
|
+
throw new Error(`Mock: unknown method ${method}`);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
async ensureRunning() {
|
|
71
|
+
// no-op
|
|
72
|
+
},
|
|
73
|
+
get isRunning() {
|
|
74
|
+
return true;
|
|
75
|
+
},
|
|
76
|
+
async shutdown() {
|
|
77
|
+
// no-op
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=mock-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-engine.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/mock-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r1-quality.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/r1-quality.test.ts"],"names":[],"mappings":""}
|