@hasna/recordings 0.4.0 → 0.5.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/README.md +56 -6
- package/contracts/v1/fixtures.json +1206 -0
- package/dist/cli/index.js +28 -7
- package/dist/contracts/hosted-v1.d.ts +105 -0
- package/dist/contracts/hosted-v1.d.ts.map +1 -0
- package/dist/contracts/hosted-v1.js +41 -0
- package/dist/contracts/stream-v1.d.ts +117 -0
- package/dist/contracts/stream-v1.d.ts.map +1 -0
- package/dist/contracts/stream-v1.js +35 -0
- package/dist/hosted/index.d.ts +58 -0
- package/dist/hosted/index.d.ts.map +1 -0
- package/dist/hosted/index.js +266 -0
- package/dist/hosted/transport.d.ts +36 -0
- package/dist/hosted/transport.d.ts.map +1 -0
- package/dist/hosted-v1-aavn7ktb.js +4114 -0
- package/dist/hosted-v1-gdr9extc.js +84 -0
- package/dist/index.js +27 -6
- package/dist/mcp/index.js +27 -6
- package/dist/server/index.js +27 -6
- package/dist/storage.js +27 -6
- package/docs/hosted-sdk.md +71 -0
- package/docs/wire-contracts.md +24 -0
- package/package.json +27 -6
- package/scripts/ci-linux-suite.ts +23 -13
- package/scripts/macos_artifact.ts +40 -33
- package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
- package/scripts/native/recordings_fs_guard.c +36 -4
- package/scripts/native-core-receipt.py +171 -0
- package/scripts/native_fs_guard.ts +2 -0
- package/scripts/release-suite-gate.ts +227 -150
- package/scripts/resolve_tailscale_cli.sh +24 -3
- package/src/native/Recordings/RecordingsLib/BlockingOperation.swift +29 -0
- package/src/native/Recordings/RecordingsLib/Info.plist +2 -2
- package/src/native/Recordings/RecordingsLib/ProjectStore.swift +4 -4
- package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +228 -60
- package/src/native/Recordings/RecordingsLib/RecordingPasteTarget.swift +114 -0
- package/src/native/Recordings/RecordingsLib/RecordingProvider.swift +13 -3
- package/src/native/Recordings/RecordingsTests/BlockingOperationTests.swift +85 -0
- package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +441 -72
- package/src/native/Recordings/RecordingsTests/PipeClosureFixture.swift +215 -0
- package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +10 -10
- package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +1 -1
- package/src/native/Recordings/RecordingsTests/RecordingFrozenPasteTargetTests.swift +50 -0
- package/src/native/Recordings/RecordingsTests/RecordingPasteTargetTrackerTests.swift +48 -0
- package/src/native/Recordings/RecordingsTests/RecordingProviderTests.swift +115 -2
- package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +93 -21
- package/src/native/Recordings/RecordingsTests/TestHomeDirectory.swift +5 -1
- package/src/native/Recordings/build.sh +2 -1
- package/dist/__tests__/helpers/installer-guard-execution.d.ts +0 -22
- package/dist/__tests__/helpers/installer-guard-execution.d.ts.map +0 -1
- package/dist/__tests__/helpers/installer-preflight.d.ts +0 -22
- package/dist/__tests__/helpers/installer-preflight.d.ts.map +0 -1
- package/dist/__tests__/helpers/native-fs-guard.d.ts +0 -2
- package/dist/__tests__/helpers/native-fs-guard.d.ts.map +0 -1
- package/dist/__tests__/helpers/source-assertions.d.ts +0 -171
- package/dist/__tests__/helpers/source-assertions.d.ts.map +0 -1
- package/dist/__tests__/preload.d.ts +0 -2
- package/dist/__tests__/preload.d.ts.map +0 -1
|
@@ -75,11 +75,16 @@ public protocol RecordingTranscriptionProvider: Sendable {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
public protocol RecordingTranscriptionSession: Sendable {
|
|
78
|
-
/// Called in capture order
|
|
78
|
+
/// Called in capture order with variable-sized admitted PCM packets, off the audio
|
|
79
|
+
/// callback. Empty packets are omitted; all admitted packets precede inputEnded().
|
|
79
80
|
/// Return promptly. Providers must bound any queue while connecting or sending.
|
|
80
|
-
///
|
|
81
|
-
/// a cancelled session must ignore it.
|
|
81
|
+
/// An in-flight append may race cancel; a cancelled session must ignore it.
|
|
82
82
|
func appendPCM(_ data: Data)
|
|
83
|
+
/// Called once after all capture/converter PCM drains, before the fallback WAV is written.
|
|
84
|
+
/// Streaming providers may close input now and cache an early final result. Return promptly;
|
|
85
|
+
/// this does not authorize publishing text or reading audioURL before finish is called.
|
|
86
|
+
/// Must be thread-safe and idempotent, and ignore calls after cancellation.
|
|
87
|
+
func inputEnded()
|
|
83
88
|
/// Called once after capture drains and the fallback WAV is safely written.
|
|
84
89
|
/// File-based providers may ignore appendPCM and recognize this file locally.
|
|
85
90
|
func finish(_ request: RecordingTranscriptionRequest) async throws -> RecordingProviderResult
|
|
@@ -87,6 +92,11 @@ public protocol RecordingTranscriptionSession: Sendable {
|
|
|
87
92
|
func cancel()
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
public extension RecordingTranscriptionSession {
|
|
96
|
+
/// File providers and existing adapters keep their finish-after-WAV behavior.
|
|
97
|
+
func inputEnded() {}
|
|
98
|
+
}
|
|
99
|
+
|
|
90
100
|
public enum RecordingProviderError: Error, LocalizedError, Sendable {
|
|
91
101
|
case invalidIsolation
|
|
92
102
|
case noAudio
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Dispatch
|
|
2
|
+
import Foundation
|
|
3
|
+
import Testing
|
|
4
|
+
@testable import RecordingsLib
|
|
5
|
+
|
|
6
|
+
@Suite("Blocking operation scheduling")
|
|
7
|
+
struct BlockingOperationTests {
|
|
8
|
+
private final class Observation: @unchecked Sendable {
|
|
9
|
+
let entered = DispatchSemaphore(value: 0)
|
|
10
|
+
let release = DispatchSemaphore(value: 0)
|
|
11
|
+
let peer = DispatchSemaphore(value: 0)
|
|
12
|
+
private let lock = NSLock()
|
|
13
|
+
private var result = (entered: false, peerBeforeRelease: false)
|
|
14
|
+
|
|
15
|
+
func record(entered: Bool, peerBeforeRelease: Bool) {
|
|
16
|
+
lock.lock()
|
|
17
|
+
result = (entered, peerBeforeRelease)
|
|
18
|
+
lock.unlock()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func snapshot() -> (entered: Bool, peerBeforeRelease: Bool) {
|
|
22
|
+
lock.lock()
|
|
23
|
+
defer { lock.unlock() }
|
|
24
|
+
return result
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Test("blocking work leaves executor progress available and drains after cancellation", arguments: [false, true], [false, true])
|
|
29
|
+
func peerProgressWhileBlocked(cancel: Bool, delayedSubmission: Bool) async {
|
|
30
|
+
let observation = Observation()
|
|
31
|
+
// A single buffered handoff gives the operation its already-created Task handle
|
|
32
|
+
// without blocking a cooperative worker or capturing an uninitialized variable.
|
|
33
|
+
let handoff = AsyncStream<Task<(Int, Bool), Never>>.makeStream(bufferingPolicy: .bufferingNewest(1))
|
|
34
|
+
let work = Task.detached {
|
|
35
|
+
var handles = handoff.stream.makeAsyncIterator()
|
|
36
|
+
guard let ownTask = await handles.next() else {
|
|
37
|
+
preconditionFailure("Fixture task handle was not supplied")
|
|
38
|
+
}
|
|
39
|
+
// Admission is deliberately slower than the former two-second entry timer.
|
|
40
|
+
// Only progress while the synchronous operation is held is under observation.
|
|
41
|
+
if delayedSubmission { try? await Task.sleep(for: .seconds(3)) }
|
|
42
|
+
let value = await BlockingOperation.run {
|
|
43
|
+
observation.entered.signal()
|
|
44
|
+
// Start the observation inside the operation, after entry. This independent
|
|
45
|
+
// thread always releases the gate, even when the peer misses its deadline.
|
|
46
|
+
Thread.detachNewThread {
|
|
47
|
+
defer { observation.release.signal() }
|
|
48
|
+
let entered = observation.entered.wait(timeout: .now()) == .success
|
|
49
|
+
if cancel { ownTask.cancel() }
|
|
50
|
+
if entered {
|
|
51
|
+
Task.detached { observation.peer.signal() }
|
|
52
|
+
}
|
|
53
|
+
let progressed = entered && observation.peer.wait(timeout: .now() + .seconds(1)) == .success
|
|
54
|
+
observation.record(entered: entered, peerBeforeRelease: progressed)
|
|
55
|
+
}
|
|
56
|
+
observation.release.wait()
|
|
57
|
+
return 7
|
|
58
|
+
}
|
|
59
|
+
return (value, Task.isCancelled)
|
|
60
|
+
}
|
|
61
|
+
handoff.continuation.yield(work)
|
|
62
|
+
handoff.continuation.finish()
|
|
63
|
+
let (value, wasCancelled) = await work.value
|
|
64
|
+
let observed = observation.snapshot()
|
|
65
|
+
#expect(observed.entered)
|
|
66
|
+
#expect(observed.peerBeforeRelease)
|
|
67
|
+
#expect(value == 7)
|
|
68
|
+
#expect(wasCancelled == cancel)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private enum FixtureFailure: Error { case expected }
|
|
72
|
+
|
|
73
|
+
@Test("throwing blocking work preserves its exact error")
|
|
74
|
+
func preservesError() async {
|
|
75
|
+
do {
|
|
76
|
+
let _: Int = try await BlockingOperation.runThrowing(qos: .utility) {
|
|
77
|
+
throw FixtureFailure.expected
|
|
78
|
+
}
|
|
79
|
+
Issue.record("Blocking operation discarded its error")
|
|
80
|
+
} catch FixtureFailure.expected {
|
|
81
|
+
} catch {
|
|
82
|
+
Issue.record("Blocking operation replaced its error: \(error)")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|