pi-harness-runtime 1.1.340 → 1.1.342

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.
Files changed (116) hide show
  1. package/README.md +118 -0
  2. package/index.ts +58 -0
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/package.json +1 -1
  16. package/packages/code-generation/package.json +1 -1
  17. package/packages/code-review/package.json +1 -1
  18. package/packages/codex-adapter/package.json +1 -1
  19. package/packages/codex-watcher/package.json +1 -1
  20. package/packages/config-capture/package.json +1 -1
  21. package/packages/connection-runtime/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/contracts/package.json +1 -1
  26. package/packages/cookie-sanitizer/package.json +1 -1
  27. package/packages/cost-optimizer/package.json +1 -1
  28. package/packages/dependency-analyzer/package.json +1 -1
  29. package/packages/django-plugin/package.json +1 -1
  30. package/packages/doc-generator/package.json +1 -1
  31. package/packages/durable-review/package.json +1 -1
  32. package/packages/e2e-tester/package.json +1 -1
  33. package/packages/evaluation-engine/package.json +1 -1
  34. package/packages/evaluation-runner/package.json +1 -1
  35. package/packages/event-bus/package.json +1 -1
  36. package/packages/event-sink/package.json +1 -1
  37. package/packages/event-store/package.json +1 -1
  38. package/packages/experience-replay/package.json +1 -1
  39. package/packages/feedback-collector/package.json +1 -1
  40. package/packages/file-copy-helper/package.json +1 -1
  41. package/packages/framework-detector/package.json +1 -1
  42. package/packages/framework-plugin-sdk/package.json +1 -1
  43. package/packages/frappe-plugin/package.json +1 -1
  44. package/packages/generic-web-plugin/package.json +1 -1
  45. package/packages/health-monitor/package.json +1 -1
  46. package/packages/intent-analyzer/package.json +1 -1
  47. package/packages/jev-judge/package.json +1 -1
  48. package/packages/knowledge-graph/package.json +1 -1
  49. package/packages/knowledge-retrieval/package.json +1 -1
  50. package/packages/laravel-plugin/package.json +1 -1
  51. package/packages/learning-engine/package.json +1 -1
  52. package/packages/mcp-adapter/package.json +1 -1
  53. package/packages/memory-engine/package.json +1 -1
  54. package/packages/milestone-manager/package.json +1 -1
  55. package/packages/model-registry/package.json +1 -1
  56. package/packages/nextjs-plugin/package.json +1 -1
  57. package/packages/notification/package.json +1 -1
  58. package/packages/observability/package.json +1 -1
  59. package/packages/okf-indexer/package.json +1 -1
  60. package/packages/paperclip-integration/package.json +1 -1
  61. package/packages/performance-optimizer/package.json +1 -1
  62. package/packages/permission-mode/package.json +1 -1
  63. package/packages/privilege-broker/package.json +1 -1
  64. package/packages/project-analyzer/package.json +1 -1
  65. package/packages/project-bootstrap/package.json +1 -1
  66. package/packages/projection-engine/package.json +1 -1
  67. package/packages/prompt-compiler/package.json +1 -1
  68. package/packages/prompt-versioning/package.json +1 -1
  69. package/packages/provider-adapter-sdk/package.json +1 -1
  70. package/packages/provider-router/package.json +1 -1
  71. package/packages/provider-selector/package.json +1 -1
  72. package/packages/providers/package.json +1 -1
  73. package/packages/qdrant-skills/package.json +1 -1
  74. package/packages/quota-manager/package.json +1 -1
  75. package/packages/rate-limiter/package.json +1 -1
  76. package/packages/react-vite-plugin/package.json +1 -1
  77. package/packages/relay/package.json +1 -1
  78. package/packages/release-manager/package.json +1 -1
  79. package/packages/requirement-compiler/package.json +1 -1
  80. package/packages/runtime/package.json +1 -1
  81. package/packages/scheduler/package.json +1 -1
  82. package/packages/scheduler-adapter/package.json +1 -1
  83. package/packages/session/package.json +1 -1
  84. package/packages/session-api/package.json +1 -1
  85. package/packages/session-export/package.json +1 -1
  86. package/packages/shared-context/package.json +1 -1
  87. package/packages/skill-mcp-client/package.json +1 -1
  88. package/packages/skill-registry/package.json +1 -1
  89. package/packages/skills/package.json +1 -1
  90. package/packages/sprint-planner/package.json +1 -1
  91. package/packages/subscription-engine/package.json +1 -1
  92. package/packages/task-compiler/package.json +1 -1
  93. package/packages/telegram/package.json +1 -1
  94. package/packages/tencentdb-memory/package.json +1 -1
  95. package/packages/tencentdb-sync/package.json +1 -1
  96. package/packages/test-data-generator/package.json +1 -1
  97. package/packages/test-generator/package.json +1 -1
  98. package/packages/todo-bd-sync/package.json +1 -1
  99. package/packages/token-estimation/package.json +1 -1
  100. package/packages/token-optimizer/package.json +1 -1
  101. package/packages/trajectory/package.json +1 -1
  102. package/packages/tui/package.json +1 -1
  103. package/packages/types/package.json +1 -1
  104. package/packages/voice-api/dist/line.d.ts +2 -1
  105. package/packages/voice-api/dist/line.js +9 -6
  106. package/packages/voice-api/dist/transcriber.d.ts +12 -41
  107. package/packages/voice-api/dist/transcriber.js +88 -108
  108. package/packages/voice-api/package.json +1 -1
  109. package/packages/voice-api/src/line.ts +11 -8
  110. package/packages/voice-api/src/transcriber.ts +98 -122
  111. package/packages/webmcp-tester/package.json +1 -1
  112. package/packages/wiki/package.json +1 -1
  113. package/packages/workflow-events/package.json +1 -1
  114. package/packages/workspace-scanner/package.json +1 -1
  115. package/packages/worktree/package.json +1 -1
  116. package/packages/write-review/package.json +1 -1
@@ -1,48 +1,19 @@
1
1
  /**
2
- * Voice Transcriber - Main interface for speech-to-text
3
- */
4
- import type { TranscriptionResult, TranscribeOptions, VoiceApiConfig } from './types.js';
5
- import { WhisperProvider } from './provider.js';
6
- /**
7
- * Voice Transcriber
2
+ * OpenAI Whisper API Transcription
8
3
  *
9
- * Main interface for transcribing voice messages to text.
10
- * Supports multiple providers (OpenAI, Replicate).
4
+ * Transcribes audio using OpenAI's Whisper API.
11
5
  */
6
+ import type { TranscriptionResult } from './types.js';
7
+ export interface TranscriberOptions {
8
+ openaiApiKey: string;
9
+ model?: string;
10
+ }
12
11
  export declare class VoiceTranscriber {
13
- private providers;
14
- private defaultProvider;
15
- constructor(config?: VoiceApiConfig);
16
- /**
17
- * Check if transcriber is configured
18
- */
19
- isConfigured(): boolean;
20
- /**
21
- * Get configured providers
22
- */
23
- getProviders(): string[];
24
- /**
25
- * Get default provider
26
- */
27
- getDefaultProvider(): string | null;
12
+ private apiKey;
13
+ private model;
14
+ constructor(options: TranscriberOptions);
28
15
  /**
29
- * Transcribe audio to text
16
+ * Transcribe audio buffer using Whisper API
30
17
  */
31
- transcribe(options: TranscribeOptions): Promise<TranscriptionResult>;
32
- /**
33
- * Add a provider
34
- */
35
- addProvider(name: string, provider: WhisperProvider): void;
36
- /**
37
- * Remove a provider
38
- */
39
- removeProvider(name: string): boolean;
40
- /**
41
- * Set default provider
42
- */
43
- setDefaultProvider(name: string): boolean;
18
+ transcribe(audioBuffer: Uint8Array): Promise<TranscriptionResult>;
44
19
  }
45
- /**
46
- * Create a VoiceTranscriber from environment variables
47
- */
48
- export declare function createFromEnv(): VoiceTranscriber;
@@ -1,118 +1,98 @@
1
1
  /**
2
- * Voice Transcriber - Main interface for speech-to-text
3
- */
4
- import { createOpenAIProvider, createReplicateProvider } from './provider.js';
5
- /**
6
- * Voice Transcriber
2
+ * OpenAI Whisper API Transcription
7
3
  *
8
- * Main interface for transcribing voice messages to text.
9
- * Supports multiple providers (OpenAI, Replicate).
4
+ * Transcribes audio using OpenAI's Whisper API.
10
5
  */
6
+ import * as https from "https";
11
7
  export class VoiceTranscriber {
12
- providers = new Map();
13
- defaultProvider = '';
14
- constructor(config = {}) {
15
- // Initialize OpenAI provider
16
- if (config.openaiApiKey) {
17
- const provider = createOpenAIProvider(config.openaiApiKey);
18
- this.providers.set('openai', provider);
19
- this.defaultProvider = 'openai';
20
- }
21
- // Initialize Replicate provider
22
- if (config.replicateApiToken) {
23
- const provider = createReplicateProvider(config.replicateApiToken);
24
- this.providers.set('replicate', provider);
25
- if (!this.defaultProvider) {
26
- this.defaultProvider = 'replicate';
27
- }
28
- }
29
- // Set default provider from config
30
- if (config.provider && this.providers.has(config.provider)) {
31
- this.defaultProvider = config.provider;
32
- }
33
- }
34
- /**
35
- * Check if transcriber is configured
36
- */
37
- isConfigured() {
38
- return this.providers.size > 0;
39
- }
40
- /**
41
- * Get configured providers
42
- */
43
- getProviders() {
44
- return Array.from(this.providers.keys());
45
- }
46
- /**
47
- * Get default provider
48
- */
49
- getDefaultProvider() {
50
- return this.defaultProvider || null;
8
+ apiKey;
9
+ model;
10
+ constructor(options) {
11
+ this.apiKey = options.openaiApiKey;
12
+ this.model = options.model || "whisper-1";
51
13
  }
52
14
  /**
53
- * Transcribe audio to text
15
+ * Transcribe audio buffer using Whisper API
54
16
  */
55
- async transcribe(options) {
56
- const providerName = options.provider || this.defaultProvider;
57
- if (!providerName) {
58
- return {
59
- text: '',
60
- timestamp: new Date().toISOString(),
61
- provider: 'openai',
62
- error: 'No provider configured',
63
- };
64
- }
65
- const provider = this.providers.get(providerName);
66
- if (!provider) {
67
- return {
68
- text: '',
69
- timestamp: new Date().toISOString(),
70
- provider: providerName,
71
- error: `Provider ${providerName} not configured`,
17
+ async transcribe(audioBuffer) {
18
+ return new Promise((resolve) => {
19
+ const boundary = "----FormBoundary" + Math.random().toString(36).substring(2);
20
+ // Build multipart form data
21
+ const body = Buffer.concat([
22
+ Buffer.from(`--${boundary}\r\n`),
23
+ Buffer.from(`Content-Disposition: form-data; name="file"; filename="audio.m4a"\r\n`),
24
+ Buffer.from(`Content-Type: audio/x-m4a\r\n\r\n`),
25
+ Buffer.from(audioBuffer),
26
+ Buffer.from(`\r\n--${boundary}\r\n`),
27
+ Buffer.from(`Content-Disposition: form-data; name="model"\r\n\r\n`),
28
+ Buffer.from(`${this.model}\r\n`),
29
+ Buffer.from(`--${boundary}--\r\n`),
30
+ ]);
31
+ const options = {
32
+ hostname: "api.openai.com",
33
+ path: "/v1/audio/transcriptions",
34
+ method: "POST",
35
+ headers: {
36
+ "Authorization": `Bearer ${this.apiKey}`,
37
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
38
+ "Content-Length": body.length.toString(),
39
+ }
72
40
  };
73
- }
74
- return provider.transcribe(options);
41
+ const req = https.request(options, (res) => {
42
+ let data = "";
43
+ res.on("data", (chunk) => data += chunk);
44
+ res.on("end", () => {
45
+ if (res.statusCode !== 200) {
46
+ try {
47
+ const error = JSON.parse(data);
48
+ resolve({
49
+ text: "",
50
+ timestamp: new Date().toISOString(),
51
+ provider: 'openai',
52
+ error: error.error?.message || data
53
+ });
54
+ }
55
+ catch {
56
+ resolve({
57
+ text: "",
58
+ timestamp: new Date().toISOString(),
59
+ provider: 'openai',
60
+ error: data
61
+ });
62
+ }
63
+ return;
64
+ }
65
+ try {
66
+ const result = JSON.parse(data);
67
+ resolve({
68
+ text: result.text || "",
69
+ language: result.language,
70
+ duration: result.duration,
71
+ confidence: result.confidence,
72
+ timestamp: new Date().toISOString(),
73
+ provider: 'openai',
74
+ });
75
+ }
76
+ catch (e) {
77
+ resolve({
78
+ text: "",
79
+ timestamp: new Date().toISOString(),
80
+ provider: 'openai',
81
+ error: String(e)
82
+ });
83
+ }
84
+ });
85
+ });
86
+ req.on("error", (err) => {
87
+ resolve({
88
+ text: "",
89
+ timestamp: new Date().toISOString(),
90
+ provider: 'openai',
91
+ error: String(err)
92
+ });
93
+ });
94
+ req.write(body);
95
+ req.end();
96
+ });
75
97
  }
76
- /**
77
- * Add a provider
78
- */
79
- addProvider(name, provider) {
80
- this.providers.set(name, provider);
81
- if (!this.defaultProvider) {
82
- this.defaultProvider = name;
83
- }
84
- }
85
- /**
86
- * Remove a provider
87
- */
88
- removeProvider(name) {
89
- const deleted = this.providers.delete(name);
90
- if (this.defaultProvider === name) {
91
- this.defaultProvider = this.providers.keys().next().value || '';
92
- }
93
- return deleted;
94
- }
95
- /**
96
- * Set default provider
97
- */
98
- setDefaultProvider(name) {
99
- if (this.providers.has(name)) {
100
- this.defaultProvider = name;
101
- return true;
102
- }
103
- return false;
104
- }
105
- }
106
- /**
107
- * Create a VoiceTranscriber from environment variables
108
- */
109
- export function createFromEnv() {
110
- // SAFETY: globalThis.process is always available in Node.js
111
- const env = globalThis.process;
112
- const envVars = env?.env ?? {};
113
- return new VoiceTranscriber({
114
- openaiApiKey: envVars.OPENAI_API_KEY,
115
- replicateApiToken: envVars.REPLICATE_API_TOKEN,
116
- provider: envVars.VOICE_PROVIDER || 'openai',
117
- });
118
98
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/voice-api",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "type": "module",
5
5
  "description": "Voice-to-text API using Whisper for Telegram and LINE integration",
6
6
  "main": "./dist/index.js",
@@ -4,7 +4,7 @@
4
4
  * Handles voice messages from LINE Messaging API.
5
5
  */
6
6
 
7
- import type { VoiceMessage, TranscriptionResult } from './types.js';
7
+ import type { TranscriptionResult } from './types.js';
8
8
  import { VoiceTranscriber } from './transcriber.js';
9
9
 
10
10
  /**
@@ -38,7 +38,7 @@ export interface LineWebhookBody {
38
38
  /**
39
39
  * LINE Messaging API response
40
40
  */
41
- interface LineMessageContent {
41
+ interface _LineMessageContent {
42
42
  displayName?: string;
43
43
  }
44
44
 
@@ -70,6 +70,7 @@ export class LineVoiceHandler {
70
70
  private channelSecret?: string;
71
71
  private transcriber: VoiceTranscriber;
72
72
  private apiBase: string;
73
+ private dataApiBase: string;
73
74
  private onTranscribed?: (userId: string, text: string, result: TranscriptionResult) => Promise<void>;
74
75
  private onError?: (userId: string, error: Error) => void;
75
76
 
@@ -78,6 +79,7 @@ export class LineVoiceHandler {
78
79
  this.channelSecret = options.channelSecret;
79
80
  this.transcriber = options.transcriber;
80
81
  this.apiBase = options.apiBase || 'https://api.line.me';
82
+ this.dataApiBase = 'https://api-data.line.me'; // Content API is different
81
83
  this.onTranscribed = options.onTranscribed;
82
84
  this.onError = options.onError;
83
85
  }
@@ -85,7 +87,7 @@ export class LineVoiceHandler {
85
87
  /**
86
88
  * Verify LINE webhook signature
87
89
  */
88
- verifySignature(body: string, signature: string): boolean {
90
+ verifySignature(_body: string, _signature: string): boolean {
89
91
  if (!this.channelSecret) return true; // Skip verification if no secret
90
92
 
91
93
  // Note: In production, implement proper HMAC verification
@@ -116,9 +118,7 @@ export class LineVoiceHandler {
116
118
  }
117
119
 
118
120
  // Transcribe
119
- const result = await this.transcriber.transcribe({
120
- audioBuffer: audioContent,
121
- });
121
+ const result = await this.transcriber.transcribe(audioContent);
122
122
 
123
123
  if (result.error) {
124
124
  this.onError?.(userId, new Error(result.error));
@@ -145,8 +145,9 @@ export class LineVoiceHandler {
145
145
  */
146
146
  private async getAudioContent(messageId: string): Promise<Uint8Array | null> {
147
147
  try {
148
+ // Use data API for content retrieval
148
149
  const response = await fetch(
149
- `${this.apiBase}/v2/bot/message/${messageId}/content`,
150
+ `${this.dataApiBase}/v2/bot/message/${messageId}/content`,
150
151
  {
151
152
  headers: {
152
153
  'Authorization': `Bearer ${this.channelAccessToken}`,
@@ -155,12 +156,14 @@ export class LineVoiceHandler {
155
156
  );
156
157
 
157
158
  if (!response.ok) {
159
+ console.error(`LINE API error: ${response.status} ${response.statusText}`);
158
160
  return null;
159
161
  }
160
162
 
161
163
  const arrayBuffer = await response.arrayBuffer();
162
164
  return new Uint8Array(arrayBuffer);
163
- } catch {
165
+ } catch (error) {
166
+ console.error(`Failed to get audio content: ${error}`);
164
167
  return null;
165
168
  }
166
169
  }
@@ -1,136 +1,112 @@
1
1
  /**
2
- * Voice Transcriber - Main interface for speech-to-text
3
- */
4
-
5
- import type { TranscriptionResult, TranscribeOptions, VoiceApiConfig } from './types.js';
6
- import { WhisperProvider, createOpenAIProvider, createReplicateProvider } from './provider.js';
7
-
8
- /**
9
- * Voice Transcriber
2
+ * OpenAI Whisper API Transcription
10
3
  *
11
- * Main interface for transcribing voice messages to text.
12
- * Supports multiple providers (OpenAI, Replicate).
4
+ * Transcribes audio using OpenAI's Whisper API.
13
5
  */
14
- export class VoiceTranscriber {
15
- private providers: Map<string, WhisperProvider> = new Map();
16
- private defaultProvider: string = '';
17
6
 
18
- constructor(config: VoiceApiConfig = {}) {
19
- // Initialize OpenAI provider
20
- if (config.openaiApiKey) {
21
- const provider = createOpenAIProvider(config.openaiApiKey);
22
- this.providers.set('openai', provider);
23
- this.defaultProvider = 'openai';
24
- }
7
+ import type { TranscriptionResult } from './types.js';
8
+ import * as https from "https";
25
9
 
26
- // Initialize Replicate provider
27
- if (config.replicateApiToken) {
28
- const provider = createReplicateProvider(config.replicateApiToken);
29
- this.providers.set('replicate', provider);
30
- if (!this.defaultProvider) {
31
- this.defaultProvider = 'replicate';
32
- }
33
- }
34
-
35
- // Set default provider from config
36
- if (config.provider && this.providers.has(config.provider)) {
37
- this.defaultProvider = config.provider;
38
- }
39
- }
40
-
41
- /**
42
- * Check if transcriber is configured
43
- */
44
- isConfigured(): boolean {
45
- return this.providers.size > 0;
46
- }
10
+ export interface TranscriberOptions {
11
+ openaiApiKey: string;
12
+ model?: string;
13
+ }
47
14
 
48
- /**
49
- * Get configured providers
50
- */
51
- getProviders(): string[] {
52
- return Array.from(this.providers.keys());
53
- }
15
+ export class VoiceTranscriber {
16
+ private apiKey: string;
17
+ private model: string;
54
18
 
55
- /**
56
- * Get default provider
57
- */
58
- getDefaultProvider(): string | null {
59
- return this.defaultProvider || null;
19
+ constructor(options: TranscriberOptions) {
20
+ this.apiKey = options.openaiApiKey;
21
+ this.model = options.model || "whisper-1";
60
22
  }
61
23
 
62
24
  /**
63
- * Transcribe audio to text
25
+ * Transcribe audio buffer using Whisper API
64
26
  */
65
- async transcribe(options: TranscribeOptions): Promise<TranscriptionResult> {
66
- const providerName = options.provider || this.defaultProvider;
67
-
68
- if (!providerName) {
69
- return {
70
- text: '',
71
- timestamp: new Date().toISOString(),
72
- provider: 'openai',
73
- error: 'No provider configured',
27
+ async transcribe(audioBuffer: Uint8Array): Promise<TranscriptionResult> {
28
+ return new Promise((resolve) => {
29
+ const boundary = "----FormBoundary" + Math.random().toString(36).substring(2);
30
+
31
+ // Build multipart form data
32
+ const body = Buffer.concat([
33
+ Buffer.from(`--${boundary}\r\n`),
34
+ Buffer.from(`Content-Disposition: form-data; name="file"; filename="audio.m4a"\r\n`),
35
+ Buffer.from(`Content-Type: audio/x-m4a\r\n\r\n`),
36
+ Buffer.from(audioBuffer),
37
+ Buffer.from(`\r\n--${boundary}\r\n`),
38
+ Buffer.from(`Content-Disposition: form-data; name="model"\r\n\r\n`),
39
+ Buffer.from(`${this.model}\r\n`),
40
+ Buffer.from(`--${boundary}--\r\n`),
41
+ ]);
42
+
43
+ const options = {
44
+ hostname: "api.openai.com",
45
+ path: "/v1/audio/transcriptions",
46
+ method: "POST",
47
+ headers: {
48
+ "Authorization": `Bearer ${this.apiKey}`,
49
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
50
+ "Content-Length": body.length.toString(),
51
+ }
74
52
  };
75
- }
76
-
77
- const provider = this.providers.get(providerName);
78
- if (!provider) {
79
- return {
80
- text: '',
81
- timestamp: new Date().toISOString(),
82
- provider: providerName as 'openai' | 'replicate',
83
- error: `Provider ${providerName} not configured`,
84
- };
85
- }
86
-
87
- return provider.transcribe(options);
88
- }
89
-
90
- /**
91
- * Add a provider
92
- */
93
- addProvider(name: string, provider: WhisperProvider): void {
94
- this.providers.set(name, provider);
95
- if (!this.defaultProvider) {
96
- this.defaultProvider = name;
97
- }
98
- }
99
-
100
- /**
101
- * Remove a provider
102
- */
103
- removeProvider(name: string): boolean {
104
- const deleted = this.providers.delete(name);
105
- if (this.defaultProvider === name) {
106
- this.defaultProvider = this.providers.keys().next().value || '';
107
- }
108
- return deleted;
109
- }
110
-
111
- /**
112
- * Set default provider
113
- */
114
- setDefaultProvider(name: string): boolean {
115
- if (this.providers.has(name)) {
116
- this.defaultProvider = name;
117
- return true;
118
- }
119
- return false;
53
+
54
+ const req = https.request(options, (res) => {
55
+ let data = "";
56
+ res.on("data", (chunk) => data += chunk);
57
+ res.on("end", () => {
58
+ if (res.statusCode !== 200) {
59
+ try {
60
+ const error = JSON.parse(data);
61
+ resolve({
62
+ text: "",
63
+ timestamp: new Date().toISOString(),
64
+ provider: 'openai' as const,
65
+ error: error.error?.message || data
66
+ });
67
+ } catch {
68
+ resolve({
69
+ text: "",
70
+ timestamp: new Date().toISOString(),
71
+ provider: 'openai' as const,
72
+ error: data
73
+ });
74
+ }
75
+ return;
76
+ }
77
+
78
+ try {
79
+ const result = JSON.parse(data);
80
+ resolve({
81
+ text: result.text || "",
82
+ language: result.language,
83
+ duration: result.duration,
84
+ confidence: result.confidence,
85
+ timestamp: new Date().toISOString(),
86
+ provider: 'openai' as const,
87
+ });
88
+ } catch (e) {
89
+ resolve({
90
+ text: "",
91
+ timestamp: new Date().toISOString(),
92
+ provider: 'openai' as const,
93
+ error: String(e)
94
+ });
95
+ }
96
+ });
97
+ });
98
+
99
+ req.on("error", (err) => {
100
+ resolve({
101
+ text: "",
102
+ timestamp: new Date().toISOString(),
103
+ provider: 'openai' as const,
104
+ error: String(err)
105
+ });
106
+ });
107
+
108
+ req.write(body);
109
+ req.end();
110
+ });
120
111
  }
121
112
  }
122
-
123
- /**
124
- * Create a VoiceTranscriber from environment variables
125
- */
126
- export function createFromEnv(): VoiceTranscriber {
127
- // SAFETY: globalThis.process is always available in Node.js
128
- const env = (globalThis as unknown as { process: { env: Record<string, string> } }).process;
129
- const envVars = env?.env ?? {};
130
-
131
- return new VoiceTranscriber({
132
- openaiApiKey: envVars.OPENAI_API_KEY,
133
- replicateApiToken: envVars.REPLICATE_API_TOKEN,
134
- provider: (envVars.VOICE_PROVIDER as 'openai' | 'replicate') || 'openai',
135
- });
136
- }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/webmcp-tester",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/wiki",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "description": "LLM Wiki integration for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/write-review",
3
- "version": "1.1.340",
3
+ "version": "1.1.342",
4
4
  "description": "Two-agent write-review loop with blackboard coordination",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",