@h-ear/core 0.1.0-dev.202603280856
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-client.d.ts +51 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +218 -0
- package/dist/api-client.js.map +1 -0
- package/dist/chunker.d.ts +41 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +141 -0
- package/dist/chunker.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +29 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +21 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +162 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the H-ear Enterprise API.
|
|
3
|
+
* All classify operations are async: submit -> poll -> return results.
|
|
4
|
+
*/
|
|
5
|
+
import type { ServerConfig } from './config.js';
|
|
6
|
+
import type { ClassifyRequest, ClassifyResult, AsyncAccepted, BatchRequest, BatchAccepted, ClassesResult, HealthResult, UsageResult, JobsResult, JobResult, WebhookRegister, WebhookResult } from './types.js';
|
|
7
|
+
export declare class HearApiError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly requestId?: string | undefined;
|
|
11
|
+
constructor(status: number, code: string, message: string, requestId?: string | undefined);
|
|
12
|
+
}
|
|
13
|
+
/** Progress callback for polling -- allows consumers to log status */
|
|
14
|
+
export type ProgressCallback = (message: string) => void;
|
|
15
|
+
export declare class HearApiClient {
|
|
16
|
+
private config;
|
|
17
|
+
constructor(config: ServerConfig);
|
|
18
|
+
private headers;
|
|
19
|
+
/**
|
|
20
|
+
* Submit audio for classification (always async).
|
|
21
|
+
* Returns 202 with requestId + pollUrl.
|
|
22
|
+
*/
|
|
23
|
+
submitClassify(params: ClassifyRequest): Promise<AsyncAccepted>;
|
|
24
|
+
/**
|
|
25
|
+
* Poll job status until complete or failed.
|
|
26
|
+
* Returns full classification results.
|
|
27
|
+
*/
|
|
28
|
+
pollJob(jobId: string, onProgress?: ProgressCallback): Promise<ClassifyResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Classify audio end-to-end: submit async -> poll -> return results.
|
|
31
|
+
*/
|
|
32
|
+
classify(params: ClassifyRequest, onProgress?: ProgressCallback): Promise<ClassifyResult>;
|
|
33
|
+
classifyBatch(params: BatchRequest): Promise<BatchAccepted>;
|
|
34
|
+
listClasses(params?: {
|
|
35
|
+
taxonomy?: string;
|
|
36
|
+
category?: string;
|
|
37
|
+
limit?: number;
|
|
38
|
+
offset?: number;
|
|
39
|
+
}): Promise<ClassesResult>;
|
|
40
|
+
health(): Promise<HealthResult>;
|
|
41
|
+
usage(): Promise<UsageResult>;
|
|
42
|
+
listJobs(params?: {
|
|
43
|
+
limit?: number;
|
|
44
|
+
offset?: number;
|
|
45
|
+
status?: string;
|
|
46
|
+
}): Promise<JobsResult>;
|
|
47
|
+
getJob(jobId: string): Promise<JobResult>;
|
|
48
|
+
registerWebhook(params: WebhookRegister): Promise<WebhookResult>;
|
|
49
|
+
deregisterWebhook(webhookId: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EACR,eAAe,EAAE,cAAc,EAAE,aAAa,EAC9C,YAAY,EAAE,aAAa,EAC3B,aAAa,EAAE,YAAY,EAC3B,WAAW,EAAE,UAAU,EAAE,SAAS,EAClC,eAAe,EAAE,aAAa,EACjC,MAAM,YAAY,CAAC;AAGpB,qBAAa,YAAa,SAAQ,KAAK;aAEf,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,SAAS,CAAC,EAAE,MAAM;gBAHlB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,SAAS,CAAC,EAAE,MAAM,YAAA;CAKzC;AA4BD,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzD,qBAAa,aAAa;IACV,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC,OAAO,CAAC,OAAO;IAUf;;;OAGG;IACG,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBrE;;;OAGG;IACG,OAAO,CACT,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAyC1B;;OAEG;IACG,QAAQ,CACV,MAAM,EAAE,eAAe,EACvB,UAAU,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC,cAAc,CAAC;IASpB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAe3D,WAAW,CAAC,MAAM,CAAC,EAAE;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,aAAa,CAAC;IAqBpB,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAc/B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAc7B,QAAQ,CAAC,MAAM,CAAC,EAAE;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,UAAU,CAAC;IAkBjB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAczC,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAahE,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAoB5D"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the H-ear Enterprise API.
|
|
3
|
+
* All classify operations are async: submit -> poll -> return results.
|
|
4
|
+
*/
|
|
5
|
+
import { apiUrl } from './config.js';
|
|
6
|
+
import { HEAR_API } from './constants.js';
|
|
7
|
+
export class HearApiError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
code;
|
|
10
|
+
requestId;
|
|
11
|
+
constructor(status, code, message, requestId) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.requestId = requestId;
|
|
16
|
+
this.name = 'HearApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function handleResponse(response) {
|
|
20
|
+
const text = await response.text();
|
|
21
|
+
let body;
|
|
22
|
+
try {
|
|
23
|
+
body = JSON.parse(text);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new HearApiError(response.status, `HTTP_${response.status}`, `Non-JSON response (${response.status}): ${text.substring(0, 200)}`);
|
|
27
|
+
}
|
|
28
|
+
if (!response.ok && response.status !== 202) {
|
|
29
|
+
throw new HearApiError(response.status, body.code || `HTTP_${response.status}`, body.error || response.statusText, body.requestId);
|
|
30
|
+
}
|
|
31
|
+
return body;
|
|
32
|
+
}
|
|
33
|
+
export class HearApiClient {
|
|
34
|
+
config;
|
|
35
|
+
constructor(config) {
|
|
36
|
+
this.config = config;
|
|
37
|
+
}
|
|
38
|
+
headers(auth) {
|
|
39
|
+
const h = { 'Content-Type': 'application/json' };
|
|
40
|
+
if (auth && this.config.apiKey) {
|
|
41
|
+
h['X-NCM-Api-Key'] = this.config.apiKey;
|
|
42
|
+
}
|
|
43
|
+
return h;
|
|
44
|
+
}
|
|
45
|
+
// --- Classify ------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Submit audio for classification (always async).
|
|
48
|
+
* Returns 202 with requestId + pollUrl.
|
|
49
|
+
*/
|
|
50
|
+
async submitClassify(params) {
|
|
51
|
+
const url = apiUrl(this.config, '/v1/classify');
|
|
52
|
+
const asyncParams = {
|
|
53
|
+
...params,
|
|
54
|
+
callbackUrl: HEAR_API.NOOP_CALLBACK_URL,
|
|
55
|
+
};
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: this.headers(true),
|
|
59
|
+
body: JSON.stringify(asyncParams),
|
|
60
|
+
signal: AbortSignal.timeout(30000),
|
|
61
|
+
});
|
|
62
|
+
return handleResponse(response);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Poll job status until complete or failed.
|
|
66
|
+
* Returns full classification results.
|
|
67
|
+
*/
|
|
68
|
+
async pollJob(jobId, onProgress) {
|
|
69
|
+
const startMs = Date.now();
|
|
70
|
+
const pollUrl = apiUrl(this.config, `/v1/jobs/${jobId}`);
|
|
71
|
+
let attempts = 0;
|
|
72
|
+
while (Date.now() - startMs < HEAR_API.POLL_TIMEOUT_MS) {
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, HEAR_API.POLL_INTERVAL_MS));
|
|
74
|
+
attempts++;
|
|
75
|
+
const elapsedSec = ((Date.now() - startMs) / 1000).toFixed(0);
|
|
76
|
+
onProgress?.(`Polling job ${jobId}... (${elapsedSec}s, attempt ${attempts})`);
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(pollUrl, {
|
|
79
|
+
method: 'GET',
|
|
80
|
+
headers: this.headers(true),
|
|
81
|
+
signal: AbortSignal.timeout(10000),
|
|
82
|
+
});
|
|
83
|
+
const result = await handleResponse(response);
|
|
84
|
+
if (result.status === 'completed' || (result.classifications && result.classifications.length > 0)) {
|
|
85
|
+
onProgress?.(`Job ${jobId} completed in ${elapsedSec}s`);
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
if (result.status === 'failed') {
|
|
89
|
+
throw new HearApiError(500, 'JOB_FAILED', `Job ${jobId} failed`, jobId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// 404 means job not yet visible in Cosmos -- keep polling
|
|
94
|
+
if (err instanceof HearApiError && err.status === 404) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new HearApiError(408, 'POLL_TIMEOUT', `Job ${jobId} did not complete within ${HEAR_API.POLL_TIMEOUT_MS / 1000}s`, jobId);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Classify audio end-to-end: submit async -> poll -> return results.
|
|
104
|
+
*/
|
|
105
|
+
async classify(params, onProgress) {
|
|
106
|
+
onProgress?.('Submitting audio for classification...');
|
|
107
|
+
const accepted = await this.submitClassify(params);
|
|
108
|
+
onProgress?.(`Job queued: ${accepted.requestId}`);
|
|
109
|
+
return this.pollJob(accepted.requestId, onProgress);
|
|
110
|
+
}
|
|
111
|
+
// --- Batch ---------------------------------------------------------------
|
|
112
|
+
async classifyBatch(params) {
|
|
113
|
+
const url = apiUrl(this.config, '/v1/classify/batch');
|
|
114
|
+
const response = await fetch(url, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: this.headers(true),
|
|
117
|
+
body: JSON.stringify(params),
|
|
118
|
+
signal: AbortSignal.timeout(30000),
|
|
119
|
+
});
|
|
120
|
+
return handleResponse(response);
|
|
121
|
+
}
|
|
122
|
+
// --- Classes -------------------------------------------------------------
|
|
123
|
+
async listClasses(params) {
|
|
124
|
+
const query = new URLSearchParams();
|
|
125
|
+
if (params?.taxonomy)
|
|
126
|
+
query.set('taxonomy', params.taxonomy);
|
|
127
|
+
if (params?.category)
|
|
128
|
+
query.set('category', params.category);
|
|
129
|
+
if (params?.limit !== undefined)
|
|
130
|
+
query.set('limit', String(params.limit));
|
|
131
|
+
if (params?.offset !== undefined)
|
|
132
|
+
query.set('offset', String(params.offset));
|
|
133
|
+
const qs = query.toString();
|
|
134
|
+
const url = apiUrl(this.config, `/v1/classes${qs ? `?${qs}` : ''}`);
|
|
135
|
+
const response = await fetch(url, {
|
|
136
|
+
method: 'GET',
|
|
137
|
+
headers: { 'Content-Type': 'application/json' },
|
|
138
|
+
signal: AbortSignal.timeout(15000),
|
|
139
|
+
});
|
|
140
|
+
return handleResponse(response);
|
|
141
|
+
}
|
|
142
|
+
// --- Health --------------------------------------------------------------
|
|
143
|
+
async health() {
|
|
144
|
+
const url = apiUrl(this.config, '/v1/health');
|
|
145
|
+
const response = await fetch(url, {
|
|
146
|
+
method: 'GET',
|
|
147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
148
|
+
signal: AbortSignal.timeout(10000),
|
|
149
|
+
});
|
|
150
|
+
return handleResponse(response);
|
|
151
|
+
}
|
|
152
|
+
// --- Usage ---------------------------------------------------------------
|
|
153
|
+
async usage() {
|
|
154
|
+
const url = apiUrl(this.config, '/v1/usage');
|
|
155
|
+
const response = await fetch(url, {
|
|
156
|
+
method: 'GET',
|
|
157
|
+
headers: this.headers(true),
|
|
158
|
+
signal: AbortSignal.timeout(10000),
|
|
159
|
+
});
|
|
160
|
+
return handleResponse(response);
|
|
161
|
+
}
|
|
162
|
+
// --- Jobs ----------------------------------------------------------------
|
|
163
|
+
async listJobs(params) {
|
|
164
|
+
const query = new URLSearchParams();
|
|
165
|
+
if (params?.limit !== undefined)
|
|
166
|
+
query.set('limit', String(params.limit));
|
|
167
|
+
if (params?.offset !== undefined)
|
|
168
|
+
query.set('offset', String(params.offset));
|
|
169
|
+
if (params?.status)
|
|
170
|
+
query.set('status', params.status);
|
|
171
|
+
const qs = query.toString();
|
|
172
|
+
const url = apiUrl(this.config, `/v1/jobs${qs ? `?${qs}` : ''}`);
|
|
173
|
+
const response = await fetch(url, {
|
|
174
|
+
method: 'GET',
|
|
175
|
+
headers: this.headers(true),
|
|
176
|
+
signal: AbortSignal.timeout(15000),
|
|
177
|
+
});
|
|
178
|
+
return handleResponse(response);
|
|
179
|
+
}
|
|
180
|
+
async getJob(jobId) {
|
|
181
|
+
const url = apiUrl(this.config, `/v1/jobs/${jobId}`);
|
|
182
|
+
const response = await fetch(url, {
|
|
183
|
+
method: 'GET',
|
|
184
|
+
headers: this.headers(true),
|
|
185
|
+
signal: AbortSignal.timeout(10000),
|
|
186
|
+
});
|
|
187
|
+
return handleResponse(response);
|
|
188
|
+
}
|
|
189
|
+
// --- Webhooks ------------------------------------------------------------
|
|
190
|
+
async registerWebhook(params) {
|
|
191
|
+
const url = apiUrl(this.config, '/v1/webhooks');
|
|
192
|
+
const response = await fetch(url, {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: this.headers(true),
|
|
195
|
+
body: JSON.stringify(params),
|
|
196
|
+
signal: AbortSignal.timeout(10000),
|
|
197
|
+
});
|
|
198
|
+
return handleResponse(response);
|
|
199
|
+
}
|
|
200
|
+
async deregisterWebhook(webhookId) {
|
|
201
|
+
const url = apiUrl(this.config, `/v1/webhooks/${webhookId}`);
|
|
202
|
+
const response = await fetch(url, {
|
|
203
|
+
method: 'DELETE',
|
|
204
|
+
headers: this.headers(true),
|
|
205
|
+
signal: AbortSignal.timeout(10000),
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const text = await response.text();
|
|
209
|
+
let body = {};
|
|
210
|
+
try {
|
|
211
|
+
body = JSON.parse(text);
|
|
212
|
+
}
|
|
213
|
+
catch { /* non-JSON */ }
|
|
214
|
+
throw new HearApiError(response.status, body.code || `HTTP_${response.status}`, body.error || response.statusText);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio file chunking for large files that exceed the API size limit.
|
|
3
|
+
* Uses ffmpeg to split audio into overlapping chunks, preserving temporal connectivity.
|
|
4
|
+
*
|
|
5
|
+
* Overlap: 30s between chunks so boundary events are not lost.
|
|
6
|
+
*/
|
|
7
|
+
import type { ClassifyResult } from './types.js';
|
|
8
|
+
/** Chunk duration (seconds). Keeps each chunk under API 25MB limit. */
|
|
9
|
+
export declare const CHUNK_DURATION_SEC = 120;
|
|
10
|
+
/** Chunk overlap (seconds). Preserves temporal connectivity at chunk boundaries. */
|
|
11
|
+
export declare const CHUNK_OVERLAP_SEC = 30;
|
|
12
|
+
export interface ChunkInfo {
|
|
13
|
+
path: string;
|
|
14
|
+
startTime: number;
|
|
15
|
+
duration: number;
|
|
16
|
+
index: number;
|
|
17
|
+
}
|
|
18
|
+
/** Check if ffmpeg is available */
|
|
19
|
+
export declare function hasFFmpeg(): boolean;
|
|
20
|
+
/** Get audio duration in seconds using ffprobe */
|
|
21
|
+
export declare function getAudioDuration(filePath: string): number;
|
|
22
|
+
/**
|
|
23
|
+
* Split audio file into overlapping chunks using ffmpeg.
|
|
24
|
+
* Returns array of chunk file paths with their time offsets.
|
|
25
|
+
*/
|
|
26
|
+
export declare function splitAudioFile(filePath: string, totalDuration: number, options?: {
|
|
27
|
+
chunkDurationSec?: number;
|
|
28
|
+
overlapSec?: number;
|
|
29
|
+
}): ChunkInfo[];
|
|
30
|
+
/** Clean up chunk temp files */
|
|
31
|
+
export declare function cleanupChunks(chunks: ChunkInfo[]): void;
|
|
32
|
+
/**
|
|
33
|
+
* Merge classification results from multiple chunks into a unified result.
|
|
34
|
+
* Adjusts timestamps by chunk startTime offset.
|
|
35
|
+
* Deduplicates overlapping events (same className within overlap window).
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeChunkResults(chunkResults: Array<{
|
|
38
|
+
chunk: ChunkInfo;
|
|
39
|
+
result: ClassifyResult;
|
|
40
|
+
}>, originalFileName: string, totalDuration: number): ClassifyResult;
|
|
41
|
+
//# sourceMappingURL=chunker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,mCAAmC;AACnC,wBAAgB,SAAS,IAAI,OAAO,CAOnC;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D,SAAS,EAAE,CA2Cb;AAED,gCAAgC;AAChC,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAOvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC,EACjE,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,GACtB,cAAc,CA2BhB"}
|
package/dist/chunker.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio file chunking for large files that exceed the API size limit.
|
|
3
|
+
* Uses ffmpeg to split audio into overlapping chunks, preserving temporal connectivity.
|
|
4
|
+
*
|
|
5
|
+
* Overlap: 30s between chunks so boundary events are not lost.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync, spawnSync } from 'child_process';
|
|
8
|
+
import { existsSync, mkdirSync, unlinkSync, rmdirSync } from 'fs';
|
|
9
|
+
import { join, basename } from 'path';
|
|
10
|
+
import { tmpdir } from 'os';
|
|
11
|
+
/** Chunk duration (seconds). Keeps each chunk under API 25MB limit. */
|
|
12
|
+
export const CHUNK_DURATION_SEC = 120;
|
|
13
|
+
/** Chunk overlap (seconds). Preserves temporal connectivity at chunk boundaries. */
|
|
14
|
+
export const CHUNK_OVERLAP_SEC = 30;
|
|
15
|
+
/** Check if ffmpeg is available */
|
|
16
|
+
export function hasFFmpeg() {
|
|
17
|
+
try {
|
|
18
|
+
execSync('ffmpeg -version', { stdio: 'pipe', timeout: 5000 });
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Get audio duration in seconds using ffprobe */
|
|
26
|
+
export function getAudioDuration(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
const result = execSync(`ffprobe -v error -show_entries format=duration -of csv=p=0 "${filePath}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 });
|
|
29
|
+
return parseFloat(result.trim());
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Split audio file into overlapping chunks using ffmpeg.
|
|
37
|
+
* Returns array of chunk file paths with their time offsets.
|
|
38
|
+
*/
|
|
39
|
+
export function splitAudioFile(filePath, totalDuration, options) {
|
|
40
|
+
const chunkDuration = options?.chunkDurationSec ?? CHUNK_DURATION_SEC;
|
|
41
|
+
const overlap = options?.overlapSec ?? CHUNK_OVERLAP_SEC;
|
|
42
|
+
const chunkDir = join(tmpdir(), `h-ear-chunks-${Date.now()}`);
|
|
43
|
+
mkdirSync(chunkDir, { recursive: true });
|
|
44
|
+
const chunks = [];
|
|
45
|
+
const name = basename(filePath, '.mp3').replace(/\.[^.]+$/, '');
|
|
46
|
+
let startTime = 0;
|
|
47
|
+
let index = 0;
|
|
48
|
+
while (startTime < totalDuration) {
|
|
49
|
+
const chunkFile = join(chunkDir, `${name}_chunk${index}.mp3`);
|
|
50
|
+
const duration = Math.min(chunkDuration, totalDuration - startTime);
|
|
51
|
+
const result = spawnSync('ffmpeg', [
|
|
52
|
+
'-y',
|
|
53
|
+
'-ss', String(startTime),
|
|
54
|
+
'-i', filePath,
|
|
55
|
+
'-t', String(duration),
|
|
56
|
+
'-acodec', 'libmp3lame',
|
|
57
|
+
'-q:a', '4', // Good quality MP3 (~128kbps)
|
|
58
|
+
chunkFile,
|
|
59
|
+
], {
|
|
60
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
61
|
+
encoding: 'utf-8',
|
|
62
|
+
timeout: 30000,
|
|
63
|
+
});
|
|
64
|
+
if (result.status !== 0 || !existsSync(chunkFile)) {
|
|
65
|
+
process.stderr.write(`[h-ear-core] ffmpeg chunk ${index} failed: ${(result.stderr || '').split('\n').pop()}\n`);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
chunks.push({ path: chunkFile, startTime, duration, index });
|
|
69
|
+
// Advance by (chunkDuration - overlap) to create overlap
|
|
70
|
+
startTime += chunkDuration - overlap;
|
|
71
|
+
index++;
|
|
72
|
+
}
|
|
73
|
+
return chunks;
|
|
74
|
+
}
|
|
75
|
+
/** Clean up chunk temp files */
|
|
76
|
+
export function cleanupChunks(chunks) {
|
|
77
|
+
if (chunks.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
const chunkDir = join(chunks[0].path, '..');
|
|
80
|
+
for (const chunk of chunks) {
|
|
81
|
+
try {
|
|
82
|
+
unlinkSync(chunk.path);
|
|
83
|
+
}
|
|
84
|
+
catch { /* ignore */ }
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
rmdirSync(chunkDir);
|
|
88
|
+
}
|
|
89
|
+
catch { /* ignore */ }
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Merge classification results from multiple chunks into a unified result.
|
|
93
|
+
* Adjusts timestamps by chunk startTime offset.
|
|
94
|
+
* Deduplicates overlapping events (same className within overlap window).
|
|
95
|
+
*/
|
|
96
|
+
export function mergeChunkResults(chunkResults, originalFileName, totalDuration) {
|
|
97
|
+
const allClassifications = [];
|
|
98
|
+
for (const { chunk, result } of chunkResults) {
|
|
99
|
+
if (!result.classifications)
|
|
100
|
+
continue;
|
|
101
|
+
for (const cls of result.classifications) {
|
|
102
|
+
allClassifications.push({
|
|
103
|
+
...cls,
|
|
104
|
+
startTime: (cls.startTime || 0) + chunk.startTime,
|
|
105
|
+
endTime: (cls.endTime || 0) + chunk.startTime,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const deduplicated = deduplicateOverlaps(allClassifications);
|
|
110
|
+
// Sort by startTime
|
|
111
|
+
deduplicated.sort((a, b) => (a.startTime || 0) - (b.startTime || 0));
|
|
112
|
+
return {
|
|
113
|
+
requestId: `merged-${Date.now()}`,
|
|
114
|
+
duration: totalDuration,
|
|
115
|
+
processingTimeMs: 0,
|
|
116
|
+
eventCount: deduplicated.length,
|
|
117
|
+
classifications: deduplicated,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function deduplicateOverlaps(events) {
|
|
121
|
+
if (events.length <= 1)
|
|
122
|
+
return events;
|
|
123
|
+
const sorted = [...events].sort((a, b) => (a.startTime || 0) - (b.startTime || 0));
|
|
124
|
+
const kept = [];
|
|
125
|
+
for (const event of sorted) {
|
|
126
|
+
const duplicate = kept.find(k => k.className === event.className &&
|
|
127
|
+
Math.abs((k.startTime || 0) - (event.startTime || 0)) < CHUNK_OVERLAP_SEC &&
|
|
128
|
+
Math.abs((k.endTime || 0) - (event.endTime || 0)) < CHUNK_OVERLAP_SEC);
|
|
129
|
+
if (duplicate) {
|
|
130
|
+
if ((event.confidence || 0) > (duplicate.confidence || 0)) {
|
|
131
|
+
const idx = kept.indexOf(duplicate);
|
|
132
|
+
kept[idx] = event;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
kept.push(event);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return kept;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=chunker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.js","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config schema and URL helpers for the H-ear API client.
|
|
3
|
+
* CLI arg parsing is left to each consumer (mcp-server, openclaw).
|
|
4
|
+
*/
|
|
5
|
+
import { type HearEnvironment } from './constants.js';
|
|
6
|
+
export interface ServerConfig {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
environment: HearEnvironment;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
apiPath: string;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve config from environment variables only (no CLI args). */
|
|
13
|
+
export declare function resolveConfigFromEnv(): ServerConfig;
|
|
14
|
+
/** Build full API URL for a path like /v1/health */
|
|
15
|
+
export declare function apiUrl(config: ServerConfig, path: string): string;
|
|
16
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,eAAe,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,IAAI,YAAY,CAUnD;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config schema and URL helpers for the H-ear API client.
|
|
3
|
+
* CLI arg parsing is left to each consumer (mcp-server, openclaw).
|
|
4
|
+
*/
|
|
5
|
+
import { ENVIRONMENTS } from './constants.js';
|
|
6
|
+
/** Resolve config from environment variables only (no CLI args). */
|
|
7
|
+
export function resolveConfigFromEnv() {
|
|
8
|
+
const apiKey = process.env.HEAR_API_KEY || '';
|
|
9
|
+
const envStr = process.env.HEAR_ENV || 'prod';
|
|
10
|
+
const environment = (Object.keys(ENVIRONMENTS).includes(envStr) ? envStr : 'prod');
|
|
11
|
+
const envConfig = ENVIRONMENTS[environment];
|
|
12
|
+
const baseUrl = process.env.HEAR_BASE_URL || envConfig.baseUrl;
|
|
13
|
+
const apiPath = envConfig.apiPath;
|
|
14
|
+
return { apiKey, environment, baseUrl, apiPath };
|
|
15
|
+
}
|
|
16
|
+
/** Build full API URL for a path like /v1/health */
|
|
17
|
+
export function apiUrl(config, path) {
|
|
18
|
+
return `${config.baseUrl}${config.apiPath}${path}`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAwB,MAAM,gBAAgB,CAAC;AASpE,oEAAoE;AACpE,MAAM,UAAU,oBAAoB;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC;IAE9C,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAoB,CAAC;IACtG,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS,CAAC,OAAO,CAAC;IAC/D,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAElC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,MAAM,CAAC,MAAoB,EAAE,IAAY;IACrD,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone API constants for the H-ear API client.
|
|
3
|
+
* Duplicated from @ncm/config ENTERPRISE_API to maintain zero monorepo dependencies.
|
|
4
|
+
*/
|
|
5
|
+
export declare const HEAR_API: {
|
|
6
|
+
readonly MAX_FILE_SIZE_BYTES: number;
|
|
7
|
+
readonly MAX_DURATION_SECONDS: 600;
|
|
8
|
+
readonly POLL_INTERVAL_MS: 3000;
|
|
9
|
+
readonly POLL_TIMEOUT_MS: number;
|
|
10
|
+
readonly MAX_BATCH_FILES: 50;
|
|
11
|
+
readonly SUPPORTED_FORMATS: readonly ["MP3", "WAV", "FLAC", "OGG", "M4A"];
|
|
12
|
+
readonly NOOP_CALLBACK_URL: "https://mcp.h-ear.world/noop";
|
|
13
|
+
};
|
|
14
|
+
export declare const ENVIRONMENTS: {
|
|
15
|
+
readonly dev: {
|
|
16
|
+
readonly baseUrl: "https://api-dev.h-ear.world";
|
|
17
|
+
readonly apiPath: "/api";
|
|
18
|
+
};
|
|
19
|
+
readonly staging: {
|
|
20
|
+
readonly baseUrl: "https://api-staging.h-ear.world";
|
|
21
|
+
readonly apiPath: "/api";
|
|
22
|
+
};
|
|
23
|
+
readonly prod: {
|
|
24
|
+
readonly baseUrl: "https://api.h-ear.world";
|
|
25
|
+
readonly apiPath: "/api";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type HearEnvironment = keyof typeof ENVIRONMENTS;
|
|
29
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,QAAQ;;;;;;;;CASX,CAAC;AAGX,eAAO,MAAM,YAAY;;;;;;;;;;;;;CAIf,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,YAAY,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone API constants for the H-ear API client.
|
|
3
|
+
* Duplicated from @ncm/config ENTERPRISE_API to maintain zero monorepo dependencies.
|
|
4
|
+
*/
|
|
5
|
+
export const HEAR_API = {
|
|
6
|
+
MAX_FILE_SIZE_BYTES: 25 * 1024 * 1024, // 25 MB
|
|
7
|
+
MAX_DURATION_SECONDS: 600, // 10 minutes
|
|
8
|
+
POLL_INTERVAL_MS: 3000, // 3 second polling interval
|
|
9
|
+
POLL_TIMEOUT_MS: 5 * 60 * 1000, // 5 minutes max poll wait
|
|
10
|
+
MAX_BATCH_FILES: 50, // max files per batch
|
|
11
|
+
SUPPORTED_FORMATS: ['MP3', 'WAV', 'FLAC', 'OGG', 'M4A'],
|
|
12
|
+
// Noop callback URL -- triggers async 202 response from the API
|
|
13
|
+
NOOP_CALLBACK_URL: 'https://mcp.h-ear.world/noop',
|
|
14
|
+
};
|
|
15
|
+
// Front Door URLs -- async submit returns 202 in ~1s, no timeout concern
|
|
16
|
+
export const ENVIRONMENTS = {
|
|
17
|
+
dev: { baseUrl: 'https://api-dev.h-ear.world', apiPath: '/api' },
|
|
18
|
+
staging: { baseUrl: 'https://api-staging.h-ear.world', apiPath: '/api' },
|
|
19
|
+
prod: { baseUrl: 'https://api.h-ear.world', apiPath: '/api' },
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACpB,mBAAmB,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAW,QAAQ;IACxD,oBAAoB,EAAE,GAAG,EAAwB,aAAa;IAC9D,gBAAgB,EAAE,IAAI,EAA4B,4BAA4B;IAC9E,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAoB,0BAA0B;IAC5E,eAAe,EAAE,EAAE,EAA+B,sBAAsB;IACxE,iBAAiB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAU;IAChE,gEAAgE;IAChE,iBAAiB,EAAE,8BAA8B;CAC3C,CAAC;AAEX,yEAAyE;AACzE,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,GAAG,EAAE,EAAE,OAAO,EAAE,6BAA6B,EAAE,OAAO,EAAE,MAAM,EAAE;IAChE,OAAO,EAAE,EAAE,OAAO,EAAE,iCAAiC,EAAE,OAAO,EAAE,MAAM,EAAE;IACxE,IAAI,EAAE,EAAE,OAAO,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,EAAE;CACvD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @h-ear/core — Shared API client for H-ear World audio classification.
|
|
3
|
+
*/
|
|
4
|
+
export { HearApiClient, HearApiError } from './api-client.js';
|
|
5
|
+
export type { ProgressCallback } from './api-client.js';
|
|
6
|
+
export { resolveConfigFromEnv, apiUrl } from './config.js';
|
|
7
|
+
export type { ServerConfig } from './config.js';
|
|
8
|
+
export { HEAR_API, ENVIRONMENTS } from './constants.js';
|
|
9
|
+
export type { HearEnvironment } from './constants.js';
|
|
10
|
+
export { hasFFmpeg, getAudioDuration, splitAudioFile, cleanupChunks, mergeChunkResults, CHUNK_DURATION_SEC, CHUNK_OVERLAP_SEC, } from './chunker.js';
|
|
11
|
+
export type { ChunkInfo } from './chunker.js';
|
|
12
|
+
export type { ClassifyRequest, ClassifyResult, Classification, NoiseEvent, AsyncAccepted, BatchFile, BatchRequest, BatchAccepted, AudioClass, ClassesResult, HealthResult, UsageResult, JobSummary, JobsResult, JobResult, WebhookRegister, WebhookResult, ApiErrorBody, } from './types.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,OAAO,EACH,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAC3C,aAAa,EAAE,iBAAiB,EAChC,kBAAkB,EAAE,iBAAiB,GACxC,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,YAAY,EACR,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAC1E,SAAS,EAAE,YAAY,EAAE,aAAa,EACtC,UAAU,EAAE,aAAa,EACzB,YAAY,EACZ,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAC9C,eAAe,EAAE,aAAa,EAC9B,YAAY,GACf,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @h-ear/core — Shared API client for H-ear World audio classification.
|
|
3
|
+
*/
|
|
4
|
+
// API Client
|
|
5
|
+
export { HearApiClient, HearApiError } from './api-client.js';
|
|
6
|
+
// Config
|
|
7
|
+
export { resolveConfigFromEnv, apiUrl } from './config.js';
|
|
8
|
+
// Constants
|
|
9
|
+
export { HEAR_API, ENVIRONMENTS } from './constants.js';
|
|
10
|
+
// Chunker
|
|
11
|
+
export { hasFFmpeg, getAudioDuration, splitAudioFile, cleanupChunks, mergeChunkResults, CHUNK_DURATION_SEC, CHUNK_OVERLAP_SEC, } from './chunker.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,aAAa;AACb,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG9D,SAAS;AACT,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG3D,YAAY;AACZ,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGxD,UAAU;AACV,OAAO,EACH,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAC3C,aAAa,EAAE,iBAAiB,EAChC,kBAAkB,EAAE,iBAAiB,GACxC,MAAM,cAAc,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript types for H-ear Enterprise API request/response.
|
|
3
|
+
*/
|
|
4
|
+
export interface ClassifyRequest {
|
|
5
|
+
base64?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
fileName?: string;
|
|
8
|
+
threshold?: number;
|
|
9
|
+
modelId?: string;
|
|
10
|
+
callbackUrl?: string;
|
|
11
|
+
callbackSecret?: string;
|
|
12
|
+
filterMinDurationSeconds?: number;
|
|
13
|
+
latitude?: number;
|
|
14
|
+
longitude?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Classification {
|
|
17
|
+
class?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
confidence: number;
|
|
20
|
+
category?: string;
|
|
21
|
+
startTime?: number;
|
|
22
|
+
endTime?: number;
|
|
23
|
+
tier1?: string;
|
|
24
|
+
tier2?: string;
|
|
25
|
+
tier3?: string;
|
|
26
|
+
sourceId?: string;
|
|
27
|
+
decibelLevel?: number;
|
|
28
|
+
taxonomy?: string;
|
|
29
|
+
modelIndex?: number;
|
|
30
|
+
tierDepth?: number;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface NoiseEvent {
|
|
34
|
+
label: string;
|
|
35
|
+
startTime: number;
|
|
36
|
+
confidence: number;
|
|
37
|
+
}
|
|
38
|
+
export interface ClassifyResult {
|
|
39
|
+
requestId: string;
|
|
40
|
+
duration: number;
|
|
41
|
+
processingTimeMs: number;
|
|
42
|
+
eventCount: number;
|
|
43
|
+
classifications: Classification[];
|
|
44
|
+
noiseEvents?: NoiseEvent[];
|
|
45
|
+
reportUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface AsyncAccepted {
|
|
48
|
+
requestId: string;
|
|
49
|
+
status: 'processing';
|
|
50
|
+
message: string;
|
|
51
|
+
callbackUrl?: string;
|
|
52
|
+
pollUrl: string;
|
|
53
|
+
}
|
|
54
|
+
export interface BatchFile {
|
|
55
|
+
url: string;
|
|
56
|
+
id?: string;
|
|
57
|
+
metadata?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
export interface BatchRequest {
|
|
60
|
+
files: BatchFile[];
|
|
61
|
+
callbackUrl: string;
|
|
62
|
+
callbackSecret?: string;
|
|
63
|
+
threshold?: number;
|
|
64
|
+
modelId?: string;
|
|
65
|
+
filterMinDurationSeconds?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface BatchAccepted {
|
|
68
|
+
batchId: string;
|
|
69
|
+
fileCount: number;
|
|
70
|
+
jobIds: string[];
|
|
71
|
+
status: 'processing';
|
|
72
|
+
estimatedCompletionMinutes: number;
|
|
73
|
+
message: string;
|
|
74
|
+
pollUrls: string[];
|
|
75
|
+
}
|
|
76
|
+
export interface AudioClass {
|
|
77
|
+
index: number;
|
|
78
|
+
name: string;
|
|
79
|
+
category: string;
|
|
80
|
+
tier1?: string;
|
|
81
|
+
tier2?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface ClassesResult {
|
|
84
|
+
taxonomy: string;
|
|
85
|
+
classes: AudioClass[];
|
|
86
|
+
total: number;
|
|
87
|
+
totalFiltered: number;
|
|
88
|
+
totalAvailable: number;
|
|
89
|
+
categories: string[];
|
|
90
|
+
availableTaxonomies: string[];
|
|
91
|
+
pagination?: {
|
|
92
|
+
offset: number;
|
|
93
|
+
limit: number;
|
|
94
|
+
hasMore: boolean;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export interface HealthResult {
|
|
98
|
+
status: string;
|
|
99
|
+
version: string;
|
|
100
|
+
deployedTimestamp: string;
|
|
101
|
+
}
|
|
102
|
+
export interface UsageResult {
|
|
103
|
+
plan: string;
|
|
104
|
+
minutesUsed: number;
|
|
105
|
+
minutesTotal: number;
|
|
106
|
+
callsToday: number;
|
|
107
|
+
callsLimit: number;
|
|
108
|
+
activeKeys: number;
|
|
109
|
+
periodStart: string;
|
|
110
|
+
periodEnd: string;
|
|
111
|
+
}
|
|
112
|
+
export interface JobSummary {
|
|
113
|
+
jobId: string;
|
|
114
|
+
status: string;
|
|
115
|
+
fileName?: string;
|
|
116
|
+
duration?: number;
|
|
117
|
+
eventCount?: number;
|
|
118
|
+
createdAt: string;
|
|
119
|
+
completedAt?: string;
|
|
120
|
+
}
|
|
121
|
+
export interface JobsResult {
|
|
122
|
+
jobs: JobSummary[];
|
|
123
|
+
total: number;
|
|
124
|
+
pagination: {
|
|
125
|
+
offset: number;
|
|
126
|
+
limit: number;
|
|
127
|
+
hasMore: boolean;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export interface JobResult {
|
|
131
|
+
jobId: string;
|
|
132
|
+
status: string;
|
|
133
|
+
fileName?: string;
|
|
134
|
+
duration?: number;
|
|
135
|
+
eventCount?: number;
|
|
136
|
+
createdAt: string;
|
|
137
|
+
completedAt?: string;
|
|
138
|
+
classifications?: Classification[];
|
|
139
|
+
noiseEvents?: NoiseEvent[];
|
|
140
|
+
reportUrl?: string;
|
|
141
|
+
}
|
|
142
|
+
export interface WebhookRegister {
|
|
143
|
+
url: string;
|
|
144
|
+
events: string[];
|
|
145
|
+
secret?: string;
|
|
146
|
+
soundClass?: string;
|
|
147
|
+
}
|
|
148
|
+
export interface WebhookResult {
|
|
149
|
+
webhookId: string;
|
|
150
|
+
url: string;
|
|
151
|
+
events: string[];
|
|
152
|
+
soundClass?: string;
|
|
153
|
+
status: string;
|
|
154
|
+
createdAt: string;
|
|
155
|
+
}
|
|
156
|
+
export interface ApiErrorBody {
|
|
157
|
+
error: string;
|
|
158
|
+
code: string;
|
|
159
|
+
requestId?: string;
|
|
160
|
+
hint?: string;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h-ear/core",
|
|
3
|
+
"version": "0.1.0-dev.202603280856",
|
|
4
|
+
"description": "Shared API client for H-ear World audio classification — used by @h-ear/mcp-server and @h-ear/openclaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./api-client": {
|
|
14
|
+
"import": "./dist/api-client.js",
|
|
15
|
+
"types": "./dist/api-client.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./types": {
|
|
18
|
+
"import": "./dist/types.js",
|
|
19
|
+
"types": "./dist/types.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./chunker": {
|
|
22
|
+
"import": "./dist/chunker.js",
|
|
23
|
+
"types": "./dist/chunker.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./constants": {
|
|
26
|
+
"import": "./dist/constants.js",
|
|
27
|
+
"types": "./dist/constants.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./config": {
|
|
30
|
+
"import": "./dist/config.js",
|
|
31
|
+
"types": "./dist/config.d.ts"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"build:watch": "tsc --watch",
|
|
40
|
+
"clean": "rm -rf dist",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"zod": "^3.23.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^20.11.0",
|
|
48
|
+
"typescript": "^5.9.3"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"README.md"
|
|
56
|
+
],
|
|
57
|
+
"keywords": [
|
|
58
|
+
"h-ear",
|
|
59
|
+
"audio",
|
|
60
|
+
"classification",
|
|
61
|
+
"noise",
|
|
62
|
+
"sound",
|
|
63
|
+
"api-client"
|
|
64
|
+
],
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/noise-control-monitor/ncm-monorepo",
|
|
69
|
+
"directory": "packages/core"
|
|
70
|
+
}
|
|
71
|
+
}
|