sos-sdk-node-ts 0.1.2 → 0.1.3
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 +15 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/prompt-audio-runtime.d.ts +85 -0
- package/dist/prompt-audio-runtime.js +193 -0
- package/docs/PROMPT_AUDIO_RUNTIME.md +36 -0
- package/docs/SDK_LAYERING.md +14 -6
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ deterministic failures and never commit a local package reference. Staging
|
|
|
9
9
|
begins only after exact registry packages are installed and verified.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Product-neutral Node adapters shared by SOSChain, Vet and UHC trusted runtimes.
|
|
13
13
|
|
|
14
14
|
```text
|
|
15
15
|
gdc-* frozen
|
|
@@ -21,10 +21,10 @@ sos-sdk-core-ts
|
|
|
21
21
|
sos-sdk-node-ts
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
This package owns server-side
|
|
25
|
-
jobs. It does not own
|
|
26
|
-
|
|
27
|
-
SOSChain
|
|
24
|
+
This package owns reusable server-side Node transport, credentials,
|
|
25
|
+
persistence, jobs and prompt-audio runtime behavior. It does not own
|
|
26
|
+
deterministic data contracts, common authorization or sector policy. Vet, UHC
|
|
27
|
+
and SOSChain may consume it without depending on one another.
|
|
28
28
|
|
|
29
29
|
The first adapter, `SosEmergencyDispatchClient`, sends an already-authorized
|
|
30
30
|
emergency request over HTTPS with a stable idempotency key and validates the
|
|
@@ -43,6 +43,16 @@ API. Applications select the protected `contentType`, apply their own upload or
|
|
|
43
43
|
audio-duration limits, and authorize every recipient change before calling the
|
|
44
44
|
shared core. See [docs/SECURE_MESSAGES.md](docs/SECURE_MESSAGES.md).
|
|
45
45
|
|
|
46
|
+
## Immutable prompt audio
|
|
47
|
+
|
|
48
|
+
`sos-sdk-node-ts/prompt-audio-runtime` hashes the canonical descriptor produced
|
|
49
|
+
by `sos-data-utils-ts/prompt-audio`, coalesces concurrent misses, persists the
|
|
50
|
+
audio and prewarms prompts sequentially. It includes a filesystem adapter for
|
|
51
|
+
local execution and an ElevenLabs multilingual-v2 synthesizer. A production
|
|
52
|
+
runtime supplies its approved durable-object-store adapter. Complete live OTPs
|
|
53
|
+
are never cache entries; ICA composes static prefix and digit assets. See
|
|
54
|
+
[docs/PROMPT_AUDIO_RUNTIME.md](docs/PROMPT_AUDIO_RUNTIME.md).
|
|
55
|
+
|
|
46
56
|
```bash
|
|
47
57
|
npm ci
|
|
48
58
|
npm run check
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Product-neutral Node adapters shared by SOSChain, Vet and UHC runtimes. */
|
|
2
2
|
export * from './emergency-dispatch.js';
|
|
3
|
+
export * from './prompt-audio-runtime.js';
|
|
3
4
|
export * from './secure-message-crypto.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Product-neutral Node adapters shared by SOSChain, Vet and UHC runtimes. */
|
|
2
2
|
export * from './emergency-dispatch.js';
|
|
3
|
+
export * from './prompt-audio-runtime.js';
|
|
3
4
|
export * from './secure-message-crypto.js';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export interface PromptAudioObjectStore {
|
|
2
|
+
get(objectKey: string): Promise<Uint8Array | undefined>;
|
|
3
|
+
putIfAbsent(objectKey: string, audio: Uint8Array): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
/** Structural subset of a GCS Bucket; keeps the shared SDK cloud-client-version agnostic. */
|
|
6
|
+
export interface PromptAudioGcsBucket {
|
|
7
|
+
file(name: string): {
|
|
8
|
+
download(): Promise<readonly [Buffer]>;
|
|
9
|
+
save(data: Uint8Array, options: Readonly<{
|
|
10
|
+
resumable: false;
|
|
11
|
+
contentType: string;
|
|
12
|
+
metadata: Readonly<{
|
|
13
|
+
cacheControl: string;
|
|
14
|
+
}>;
|
|
15
|
+
preconditionOpts: Readonly<{
|
|
16
|
+
ifGenerationMatch: number;
|
|
17
|
+
}>;
|
|
18
|
+
}>): Promise<unknown>;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface CachedPromptAudio {
|
|
22
|
+
objectKey: string;
|
|
23
|
+
audio: Uint8Array;
|
|
24
|
+
created: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ElevenLabsPromptAudioRequest {
|
|
27
|
+
voiceId: string;
|
|
28
|
+
text: string;
|
|
29
|
+
modelId?: string;
|
|
30
|
+
outputFormat?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Hash a canonical descriptor produced by `sos-data-utils-ts/prompt-audio`. */
|
|
33
|
+
export declare function buildPromptAudioObjectKey(canonicalDescriptor: string, extension: string): string;
|
|
34
|
+
/** Durable local adapter. Production callers may implement the same interface over object storage. */
|
|
35
|
+
export declare class FileSystemPromptAudioStore implements PromptAudioObjectStore {
|
|
36
|
+
private readonly rootDirectory;
|
|
37
|
+
constructor(rootDirectory: string);
|
|
38
|
+
private resolveObjectPath;
|
|
39
|
+
get(objectKey: string): Promise<Uint8Array | undefined>;
|
|
40
|
+
putIfAbsent(objectKey: string, audio: Uint8Array): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Shared durable GCS adapter for public immutable prompts.
|
|
44
|
+
*
|
|
45
|
+
* The default prefix is intentionally product-neutral: Vet, UHC and Connect
|
|
46
|
+
* ICA resolve the same object whenever their complete canonical descriptors
|
|
47
|
+
* match. `ifGenerationMatch: 0` makes concurrent cross-process writes
|
|
48
|
+
* create-only; an existing object wins and is read back by the cache.
|
|
49
|
+
*/
|
|
50
|
+
export declare class GcsPromptAudioStore implements PromptAudioObjectStore {
|
|
51
|
+
private readonly bucket;
|
|
52
|
+
private readonly prefix;
|
|
53
|
+
constructor(bucket: PromptAudioGcsBucket, options?: Readonly<{
|
|
54
|
+
prefix?: string;
|
|
55
|
+
cacheControl?: string;
|
|
56
|
+
}>);
|
|
57
|
+
private readonly cacheControl;
|
|
58
|
+
private resolveObjectName;
|
|
59
|
+
get(objectKey: string): Promise<Uint8Array | undefined>;
|
|
60
|
+
putIfAbsent(objectKey: string, audio: Uint8Array): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** Runtime cache with one synthesis operation for every in-process immutable miss. */
|
|
63
|
+
export declare class ImmutablePromptAudioCache {
|
|
64
|
+
private readonly store;
|
|
65
|
+
private readonly inFlight;
|
|
66
|
+
constructor(store: PromptAudioObjectStore);
|
|
67
|
+
getOrCreate(input: Readonly<{
|
|
68
|
+
canonicalDescriptor: string;
|
|
69
|
+
extension: string;
|
|
70
|
+
synthesize: () => Promise<Uint8Array>;
|
|
71
|
+
}>): Promise<CachedPromptAudio>;
|
|
72
|
+
private create;
|
|
73
|
+
}
|
|
74
|
+
/** Prewarm deliberately serializes paid synthesis and stops at the first failure. */
|
|
75
|
+
export declare function prewarmPromptAudioSequentially<Input, Output>(inputs: readonly Input[], prepare: (input: Input) => Promise<Output>): Promise<Output[]>;
|
|
76
|
+
/** Minimal ElevenLabs adapter; credentials stay only in the trusted Node runtime. */
|
|
77
|
+
export declare class ElevenLabsPromptAudioSynthesizer {
|
|
78
|
+
private readonly apiKey;
|
|
79
|
+
private readonly fetchImplementation;
|
|
80
|
+
constructor(options: Readonly<{
|
|
81
|
+
apiKey: string;
|
|
82
|
+
fetch?: typeof fetch;
|
|
83
|
+
}>);
|
|
84
|
+
synthesize(request: ElevenLabsPromptAudioRequest): Promise<Uint8Array>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const REQUIRED_TOKEN = /^[A-Za-z0-9._-]+$/u;
|
|
5
|
+
function requireText(value, errorCode) {
|
|
6
|
+
const normalized = String(value ?? '').trim();
|
|
7
|
+
if (!normalized)
|
|
8
|
+
throw new Error(errorCode);
|
|
9
|
+
return normalized;
|
|
10
|
+
}
|
|
11
|
+
/** Hash a canonical descriptor produced by `sos-data-utils-ts/prompt-audio`. */
|
|
12
|
+
export function buildPromptAudioObjectKey(canonicalDescriptor, extension) {
|
|
13
|
+
requireText(canonicalDescriptor, 'prompt_audio_canonical_descriptor_required');
|
|
14
|
+
const normalizedExtension = requireText(extension, 'prompt_audio_extension_required').replace(/^\./u, '');
|
|
15
|
+
if (!REQUIRED_TOKEN.test(normalizedExtension))
|
|
16
|
+
throw new Error('prompt_audio_extension_invalid');
|
|
17
|
+
const digest = createHash('sha256').update(canonicalDescriptor, 'utf8').digest('hex');
|
|
18
|
+
return `prompt-audio/sha256/${digest}.${normalizedExtension}`;
|
|
19
|
+
}
|
|
20
|
+
/** Durable local adapter. Production callers may implement the same interface over object storage. */
|
|
21
|
+
export class FileSystemPromptAudioStore {
|
|
22
|
+
rootDirectory;
|
|
23
|
+
constructor(rootDirectory) {
|
|
24
|
+
this.rootDirectory = rootDirectory;
|
|
25
|
+
if (!path.isAbsolute(rootDirectory))
|
|
26
|
+
throw new Error('prompt_audio_store_root_must_be_absolute');
|
|
27
|
+
}
|
|
28
|
+
resolveObjectPath(objectKey) {
|
|
29
|
+
if (!/^prompt-audio\/sha256\/[a-f0-9]{64}\.[A-Za-z0-9._-]+$/u.test(objectKey)) {
|
|
30
|
+
throw new Error('prompt_audio_object_key_invalid');
|
|
31
|
+
}
|
|
32
|
+
const resolved = path.resolve(this.rootDirectory, objectKey);
|
|
33
|
+
const relative = path.relative(this.rootDirectory, resolved);
|
|
34
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
35
|
+
throw new Error('prompt_audio_object_key_invalid');
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
async get(objectKey) {
|
|
39
|
+
try {
|
|
40
|
+
return new Uint8Array(await readFile(this.resolveObjectPath(objectKey)));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code === 'ENOENT')
|
|
44
|
+
return undefined;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async putIfAbsent(objectKey, audio) {
|
|
49
|
+
if (!(audio instanceof Uint8Array) || audio.byteLength === 0)
|
|
50
|
+
throw new Error('prompt_audio_bytes_required');
|
|
51
|
+
const objectPath = this.resolveObjectPath(objectKey);
|
|
52
|
+
await mkdir(path.dirname(objectPath), { recursive: true });
|
|
53
|
+
try {
|
|
54
|
+
await writeFile(objectPath, audio, { flag: 'wx' });
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error.code !== 'EEXIST')
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Shared durable GCS adapter for public immutable prompts.
|
|
64
|
+
*
|
|
65
|
+
* The default prefix is intentionally product-neutral: Vet, UHC and Connect
|
|
66
|
+
* ICA resolve the same object whenever their complete canonical descriptors
|
|
67
|
+
* match. `ifGenerationMatch: 0` makes concurrent cross-process writes
|
|
68
|
+
* create-only; an existing object wins and is read back by the cache.
|
|
69
|
+
*/
|
|
70
|
+
export class GcsPromptAudioStore {
|
|
71
|
+
bucket;
|
|
72
|
+
prefix;
|
|
73
|
+
constructor(bucket, options = {}) {
|
|
74
|
+
this.bucket = bucket;
|
|
75
|
+
this.prefix = String(options.prefix ?? '').trim().replace(/^\/+|\/+$/gu, '');
|
|
76
|
+
this.cacheControl = String(options.cacheControl ?? 'public, max-age=31536000, immutable').trim();
|
|
77
|
+
if (!this.cacheControl)
|
|
78
|
+
throw new Error('prompt_audio_gcs_cache_control_required');
|
|
79
|
+
}
|
|
80
|
+
cacheControl;
|
|
81
|
+
resolveObjectName(objectKey) {
|
|
82
|
+
if (!/^prompt-audio\/sha256\/[a-f0-9]{64}\.[A-Za-z0-9._-]+$/u.test(objectKey)) {
|
|
83
|
+
throw new Error('prompt_audio_object_key_invalid');
|
|
84
|
+
}
|
|
85
|
+
return this.prefix ? `${this.prefix}/${objectKey}` : objectKey;
|
|
86
|
+
}
|
|
87
|
+
async get(objectKey) {
|
|
88
|
+
try {
|
|
89
|
+
const [bytes] = await this.bucket.file(this.resolveObjectName(objectKey)).download();
|
|
90
|
+
return new Uint8Array(bytes);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const status = error.code;
|
|
94
|
+
if (status === 404 || status === '404')
|
|
95
|
+
return undefined;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async putIfAbsent(objectKey, audio) {
|
|
100
|
+
if (!(audio instanceof Uint8Array) || audio.byteLength === 0)
|
|
101
|
+
throw new Error('prompt_audio_bytes_required');
|
|
102
|
+
try {
|
|
103
|
+
await this.bucket.file(this.resolveObjectName(objectKey)).save(audio, {
|
|
104
|
+
resumable: false,
|
|
105
|
+
contentType: objectKey.endsWith('.mp3') ? 'audio/mpeg' : 'application/octet-stream',
|
|
106
|
+
metadata: { cacheControl: this.cacheControl },
|
|
107
|
+
preconditionOpts: { ifGenerationMatch: 0 },
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const status = error.code;
|
|
112
|
+
if (status !== 409 && status !== 412 && status !== '409' && status !== '412')
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Runtime cache with one synthesis operation for every in-process immutable miss. */
|
|
118
|
+
export class ImmutablePromptAudioCache {
|
|
119
|
+
store;
|
|
120
|
+
inFlight = new Map();
|
|
121
|
+
constructor(store) {
|
|
122
|
+
this.store = store;
|
|
123
|
+
}
|
|
124
|
+
async getOrCreate(input) {
|
|
125
|
+
const objectKey = buildPromptAudioObjectKey(input.canonicalDescriptor, input.extension);
|
|
126
|
+
const stored = await this.store.get(objectKey);
|
|
127
|
+
if (stored)
|
|
128
|
+
return { objectKey, audio: stored, created: false };
|
|
129
|
+
const current = this.inFlight.get(objectKey);
|
|
130
|
+
if (current)
|
|
131
|
+
return current;
|
|
132
|
+
const creation = this.create(objectKey, input.synthesize);
|
|
133
|
+
this.inFlight.set(objectKey, creation);
|
|
134
|
+
try {
|
|
135
|
+
return await creation;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.inFlight.delete(objectKey);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async create(objectKey, synthesize) {
|
|
142
|
+
const audio = await synthesize();
|
|
143
|
+
if (!(audio instanceof Uint8Array) || audio.byteLength === 0)
|
|
144
|
+
throw new Error('prompt_audio_synthesis_empty');
|
|
145
|
+
await this.store.putIfAbsent(objectKey, audio);
|
|
146
|
+
const durableAudio = await this.store.get(objectKey);
|
|
147
|
+
if (!durableAudio)
|
|
148
|
+
throw new Error('prompt_audio_persistence_failed');
|
|
149
|
+
return { objectKey, audio: durableAudio, created: true };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Prewarm deliberately serializes paid synthesis and stops at the first failure. */
|
|
153
|
+
export async function prewarmPromptAudioSequentially(inputs, prepare) {
|
|
154
|
+
const outputs = [];
|
|
155
|
+
for (const input of inputs)
|
|
156
|
+
outputs.push(await prepare(input));
|
|
157
|
+
return outputs;
|
|
158
|
+
}
|
|
159
|
+
/** Minimal ElevenLabs adapter; credentials stay only in the trusted Node runtime. */
|
|
160
|
+
export class ElevenLabsPromptAudioSynthesizer {
|
|
161
|
+
apiKey;
|
|
162
|
+
fetchImplementation;
|
|
163
|
+
constructor(options) {
|
|
164
|
+
this.apiKey = requireText(options.apiKey, 'elevenlabs_api_key_required');
|
|
165
|
+
this.fetchImplementation = options.fetch ?? fetch;
|
|
166
|
+
}
|
|
167
|
+
async synthesize(request) {
|
|
168
|
+
const voiceId = requireText(request.voiceId, 'elevenlabs_voice_id_required');
|
|
169
|
+
const outputFormat = request.outputFormat ?? 'mp3_44100_128';
|
|
170
|
+
if (!REQUIRED_TOKEN.test(voiceId))
|
|
171
|
+
throw new Error('elevenlabs_voice_id_invalid');
|
|
172
|
+
if (!REQUIRED_TOKEN.test(outputFormat))
|
|
173
|
+
throw new Error('elevenlabs_output_format_invalid');
|
|
174
|
+
const response = await this.fetchImplementation(`https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voiceId)}?output_format=${encodeURIComponent(outputFormat)}`, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: {
|
|
177
|
+
accept: 'audio/mpeg',
|
|
178
|
+
'content-type': 'application/json',
|
|
179
|
+
'xi-api-key': this.apiKey,
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify({
|
|
182
|
+
text: requireText(request.text, 'elevenlabs_text_required'),
|
|
183
|
+
model_id: request.modelId ?? 'eleven_multilingual_v2',
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
if (!response.ok)
|
|
187
|
+
throw new Error(`prompt_audio_synthesis_failed:${response.status}`);
|
|
188
|
+
const audio = new Uint8Array(await response.arrayBuffer());
|
|
189
|
+
if (audio.byteLength === 0)
|
|
190
|
+
throw new Error('prompt_audio_synthesis_empty');
|
|
191
|
+
return audio;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Immutable prompt audio in Node runtimes
|
|
2
|
+
|
|
3
|
+
Telephone, portal, chatbot and messaging channels use the same reviewed prompt
|
|
4
|
+
identity from `sos-data-utils-ts/prompt-audio`. The application canonicalizes
|
|
5
|
+
the public-static descriptor, then passes that opaque canonical value to
|
|
6
|
+
`ImmutablePromptAudioCache`.
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
reviewed prompt descriptor
|
|
10
|
+
↓ canonicalize in sos-data-utils-ts
|
|
11
|
+
canonical JSON
|
|
12
|
+
↓ SHA-256 in sos-sdk-node-ts
|
|
13
|
+
immutable object key
|
|
14
|
+
↓
|
|
15
|
+
durable store ── miss only ──> configured synthesizer
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The durable store, rather than process memory, is authoritative across
|
|
19
|
+
restarts. Concurrent in-process misses are coalesced. Startup prewarming is
|
|
20
|
+
sequential so it cannot create a burst of paid requests or exhaust a shared
|
|
21
|
+
developer machine.
|
|
22
|
+
|
|
23
|
+
`FileSystemPromptAudioStore` supports local and demo execution. Production
|
|
24
|
+
applications use `GcsPromptAudioStore` or implement `PromptAudioObjectStore`
|
|
25
|
+
over another approved object store without changing the cache lifecycle.
|
|
26
|
+
One GCS bucket may be shared by Vet, UHC and Connect ICA: the default object
|
|
27
|
+
name has no product prefix and reuse occurs only when the complete canonical
|
|
28
|
+
descriptor matches. Deployments may configure an isolation prefix when policy
|
|
29
|
+
requires separate storage. `ElevenLabsPromptAudioSynthesizer`
|
|
30
|
+
defaults to `eleven_multilingual_v2`; API keys stay in the trusted runtime and
|
|
31
|
+
are neither logged nor included in an error.
|
|
32
|
+
|
|
33
|
+
Activation and verification services must never cache a complete live code.
|
|
34
|
+
They retain its value, expiry and attempt counter, and compose the call from a
|
|
35
|
+
static prefix plus localized digit fragments. Personalized names and clinical
|
|
36
|
+
data are not public-static prompts.
|
package/docs/SDK_LAYERING.md
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Shared Node SDK boundary
|
|
2
2
|
|
|
3
3
|
```mermaid
|
|
4
4
|
flowchart TB
|
|
5
5
|
GDC["gdc-* frozen"] --> DATA["sos-data-utils-ts<br/>deterministic contracts"]
|
|
6
6
|
DATA --> CORE["sos-sdk-core-ts<br/>runtime-neutral authorization"]
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
DATA --> NODE["sos-sdk-node-ts<br/>product-neutral Node adapters"]
|
|
8
|
+
CORE --> NODE
|
|
9
|
+
NODE --> RUNTIME["SOSChain · Vet · UHC trusted Node runtimes"]
|
|
9
10
|
CORE --> VET["vet-sdk-core-ts"]
|
|
10
11
|
CORE --> UHC["uhc-sdk-core-ts"]
|
|
11
12
|
DATA --> VET
|
|
12
13
|
DATA --> UHC
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
`sos-sdk-node-ts` is
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
`sos-sdk-node-ts` is the common Node-runtime layer; it is not the SOSChain
|
|
17
|
+
product SDK. Product-neutral storage, transport, credential, job and audio
|
|
18
|
+
adapters belong here. Sector policy remains in the corresponding Vet, UHC or
|
|
19
|
+
SOSChain package, and no shared package may depend back on a product SDK.
|
|
20
|
+
|
|
21
|
+
For immutable prompt audio, `sos-data-utils-ts/prompt-audio` produces the
|
|
22
|
+
canonical descriptor and localized numeric-code fragments. This package
|
|
23
|
+
hashes that exact descriptor, stores the resulting bytes durably and invokes
|
|
24
|
+
the configured provider only on a miss. Complete activation or verification
|
|
25
|
+
codes remain ICA state and are played by composing reusable digit assets.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sos-sdk-node-ts",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Product-neutral Node adapters shared by SOSChain, Vet and UHC runtimes",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Connecting Solution & Applications Ltd",
|
|
7
7
|
"repository": {
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"types": "./dist/emergency-dispatch.d.ts",
|
|
21
21
|
"default": "./dist/emergency-dispatch.js"
|
|
22
22
|
},
|
|
23
|
+
"./prompt-audio-runtime": {
|
|
24
|
+
"types": "./dist/prompt-audio-runtime.d.ts",
|
|
25
|
+
"default": "./dist/prompt-audio-runtime.js"
|
|
26
|
+
},
|
|
23
27
|
"./secure-message-crypto": {
|
|
24
28
|
"types": "./dist/secure-message-crypto.d.ts",
|
|
25
29
|
"default": "./dist/secure-message-crypto.js"
|
|
@@ -34,7 +38,7 @@
|
|
|
34
38
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
35
39
|
"build": "npm run clean && tsc -p tsconfig.json",
|
|
36
40
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
-
"test": "npm run build && node --test tests/*.test.mjs",
|
|
41
|
+
"test": "npm run build && node --test --test-concurrency=1 tests/*.test.mjs",
|
|
38
42
|
"check": "npm run typecheck && npm test",
|
|
39
43
|
"prepack": "npm run check"
|
|
40
44
|
},
|