@qvac/sdk 0.11.0 → 0.12.1

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 (211) hide show
  1. package/NOTICE +1423 -43
  2. package/README.md +2 -0
  3. package/dist/client/api/classify.d.ts +23 -0
  4. package/dist/client/api/classify.js +43 -0
  5. package/dist/client/api/get-model-info.d.ts +1 -1
  6. package/dist/client/api/index.d.ts +4 -0
  7. package/dist/client/api/index.js +4 -0
  8. package/dist/client/api/load-model.js +24 -1
  9. package/dist/client/api/transcribe.d.ts +3 -0
  10. package/dist/client/api/transcribe.js +13 -2
  11. package/dist/client/api/video.d.ts +64 -0
  12. package/dist/client/api/video.js +147 -0
  13. package/dist/client/api/vla-helpers.d.ts +45 -0
  14. package/dist/client/api/vla-helpers.js +130 -0
  15. package/dist/client/api/vla.d.ts +65 -0
  16. package/dist/client/api/vla.js +109 -0
  17. package/dist/client/config-loader/resolve-config.node.d.ts +16 -0
  18. package/dist/client/config-loader/resolve-config.node.js +77 -23
  19. package/dist/client/plugins-factory.d.ts +8 -0
  20. package/dist/client/plugins-factory.js +10 -0
  21. package/dist/client/rpc/bare-client.d.ts +1 -0
  22. package/dist/client/rpc/bare-client.js +66 -66
  23. package/dist/client/rpc/ensure-worker-ready.d.ts +9 -0
  24. package/dist/client/rpc/ensure-worker-ready.js +17 -0
  25. package/dist/client/rpc/expo-rpc-client.d.ts +1 -0
  26. package/dist/client/rpc/expo-rpc-client.js +4 -0
  27. package/dist/client/rpc/node-rpc-client.d.ts +1 -0
  28. package/dist/client/rpc/node-rpc-client.js +97 -5
  29. package/dist/client/rpc/rpc-client.js +115 -8
  30. package/dist/client/rpc/rpc-error.js +14 -1
  31. package/dist/commands/bundle/bare-pack.d.ts +13 -0
  32. package/dist/commands/bundle/bare-pack.js +54 -0
  33. package/dist/commands/bundle/constants.d.ts +7 -0
  34. package/dist/commands/bundle/constants.js +23 -0
  35. package/dist/commands/bundle/entry-gen.d.ts +2 -0
  36. package/dist/commands/bundle/entry-gen.js +48 -0
  37. package/dist/commands/bundle/index.d.ts +20 -0
  38. package/dist/commands/bundle/index.js +141 -0
  39. package/dist/commands/bundle/manifest.d.ts +21 -0
  40. package/dist/commands/bundle/manifest.js +193 -0
  41. package/dist/commands/bundle/plugins.d.ts +9 -0
  42. package/dist/commands/bundle/plugins.js +51 -0
  43. package/dist/commands/index.d.ts +5 -0
  44. package/dist/commands/index.js +2 -0
  45. package/dist/commands/verify/abi.d.ts +52 -0
  46. package/dist/commands/verify/abi.js +125 -0
  47. package/dist/commands/verify/addon-source.d.ts +43 -0
  48. package/dist/commands/verify/addon-source.js +75 -0
  49. package/dist/commands/verify/bundle-source.d.ts +12 -0
  50. package/dist/commands/verify/bundle-source.js +53 -0
  51. package/dist/commands/verify/index.d.ts +59 -0
  52. package/dist/commands/verify/index.js +371 -0
  53. package/dist/commands/verify/node-modules-source.d.ts +11 -0
  54. package/dist/commands/verify/node-modules-source.js +76 -0
  55. package/dist/commands/verify/prebuilds.d.ts +15 -0
  56. package/dist/commands/verify/prebuilds.js +39 -0
  57. package/dist/examples/classification/classify-image.d.ts +2 -0
  58. package/dist/examples/classification/classify-image.js +23 -0
  59. package/dist/examples/diffusion-txt2vid.d.ts +2 -0
  60. package/dist/examples/diffusion-txt2vid.js +81 -0
  61. package/dist/examples/plugins.js +3 -7
  62. package/dist/examples/tools/llamacpp-tools-gemma4.js +2 -5
  63. package/dist/examples/tools/llamacpp-tools-qwen35.js +2 -3
  64. package/dist/examples/transcription/parakeet-ctc-filesystem.js +18 -12
  65. package/dist/examples/transcription/parakeet-microphone-record.js +7 -12
  66. package/dist/examples/transcription/parakeet-microphone-stream.d.ts +2 -0
  67. package/dist/examples/transcription/parakeet-microphone-stream.js +79 -0
  68. package/dist/examples/transcription/parakeet-sortformer-streaming.d.ts +2 -0
  69. package/dist/examples/transcription/parakeet-sortformer-streaming.js +121 -0
  70. package/dist/examples/transcription/parakeet-sortformer.js +58 -55
  71. package/dist/examples/transcription/parakeet-tdt-filesystem.js +18 -15
  72. package/dist/examples/transcription/whispercpp-microphone-conversation.js +6 -1
  73. package/dist/examples/tts/chatterbox.js +7 -16
  74. package/dist/examples/tts/llm-to-tts-streaming.js +3 -10
  75. package/dist/examples/tts/supertonic-multilingual.js +4 -12
  76. package/dist/examples/tts/supertonic.js +5 -12
  77. package/dist/examples/vla-smolvla.d.ts +2 -0
  78. package/dist/examples/vla-smolvla.js +75 -0
  79. package/dist/examples/voice-assistant/voice-assistant.js +3 -10
  80. package/dist/expo/plugins/find-in-ancestor-node-modules.d.ts +14 -0
  81. package/dist/expo/plugins/find-in-ancestor-node-modules.js +24 -0
  82. package/dist/expo/plugins/get-project-root.d.ts +22 -0
  83. package/dist/expo/plugins/get-project-root.js +23 -0
  84. package/dist/expo/plugins/index.d.ts +4 -0
  85. package/dist/expo/plugins/index.js +4 -0
  86. package/dist/expo/plugins/resolve-sdk-package-dir.d.ts +9 -2
  87. package/dist/expo/plugins/resolve-sdk-package-dir.js +38 -11
  88. package/dist/expo/plugins/withDeviceInfo.js +2 -1
  89. package/dist/expo/plugins/withMobileBundle.d.ts +2 -17
  90. package/dist/expo/plugins/withMobileBundle.js +31 -85
  91. package/dist/index.d.ts +5 -3
  92. package/dist/index.js +9 -3
  93. package/dist/models/registry/models.d.ts +3711 -368
  94. package/dist/models/registry/models.js +7066 -3562
  95. package/dist/models/update-models/naming.js +22 -8
  96. package/dist/pear/pre.js +6 -2
  97. package/dist/schemas/cancel.d.ts +5 -0
  98. package/dist/schemas/cancel.js +1 -0
  99. package/dist/schemas/classification.d.ts +53 -0
  100. package/dist/schemas/classification.js +33 -0
  101. package/dist/schemas/common.d.ts +1001 -1739
  102. package/dist/schemas/common.js +6 -1
  103. package/dist/schemas/completion-event.d.ts +3 -0
  104. package/dist/schemas/completion-event.js +1 -0
  105. package/dist/schemas/completion-stream.d.ts +1 -0
  106. package/dist/schemas/download-asset.d.ts +13 -3
  107. package/dist/schemas/engine-addon-map.d.ts +3 -0
  108. package/dist/schemas/engine-addon-map.js +18 -5
  109. package/dist/schemas/get-model-info.d.ts +4 -0
  110. package/dist/schemas/get-model-info.js +2 -0
  111. package/dist/schemas/index.d.ts +3 -0
  112. package/dist/schemas/index.js +3 -0
  113. package/dist/schemas/llamacpp-config.d.ts +12 -2
  114. package/dist/schemas/load-model.d.ts +6152 -10764
  115. package/dist/schemas/load-model.js +75 -7
  116. package/dist/schemas/model-src-utils.d.ts +22 -2
  117. package/dist/schemas/model-types.d.ts +46 -6
  118. package/dist/schemas/model-types.js +28 -5
  119. package/dist/schemas/ocr.d.ts +5 -0
  120. package/dist/schemas/plugin.d.ts +20 -5
  121. package/dist/schemas/plugin.js +21 -4
  122. package/dist/schemas/registry.d.ts +44 -0
  123. package/dist/schemas/registry.js +5 -0
  124. package/dist/schemas/reload-config.d.ts +22 -2
  125. package/dist/schemas/sdcpp-config.d.ts +316 -1
  126. package/dist/schemas/sdcpp-config.js +232 -33
  127. package/dist/schemas/sdk-config.d.ts +654 -1324
  128. package/dist/schemas/sdk-config.js +16 -1
  129. package/dist/schemas/sdk-errors-client.d.ts +7 -0
  130. package/dist/schemas/sdk-errors-client.js +36 -1
  131. package/dist/schemas/sdk-errors-server.d.ts +3 -1
  132. package/dist/schemas/sdk-errors-server.js +20 -5
  133. package/dist/schemas/text-to-speech.d.ts +79 -696
  134. package/dist/schemas/text-to-speech.js +46 -19
  135. package/dist/schemas/transcription-config.d.ts +37 -226
  136. package/dist/schemas/transcription-config.js +76 -15
  137. package/dist/schemas/transcription.d.ts +63 -11
  138. package/dist/schemas/transcription.js +56 -2
  139. package/dist/schemas/translation-config.d.ts +60 -10
  140. package/dist/schemas/vla.d.ts +87 -0
  141. package/dist/schemas/vla.js +108 -0
  142. package/dist/scripts/run-unit-tests.js +8 -2
  143. package/dist/server/bare/ops/transcribe.d.ts +2 -1
  144. package/dist/server/bare/ops/transcribe.js +59 -34
  145. package/dist/server/bare/plugins/ggml-classification/ops/classify.d.ts +6 -0
  146. package/dist/server/bare/plugins/ggml-classification/ops/classify.js +30 -0
  147. package/dist/server/bare/plugins/ggml-classification/plugin.d.ts +32 -0
  148. package/dist/server/bare/plugins/ggml-classification/plugin.js +55 -0
  149. package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.d.ts +3 -0
  150. package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.js +12 -0
  151. package/dist/server/bare/plugins/ggml-vla/ops/vla-run.d.ts +3 -0
  152. package/dist/server/bare/plugins/ggml-vla/ops/vla-run.js +75 -0
  153. package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +54 -0
  154. package/dist/server/bare/plugins/ggml-vla/plugin.js +57 -0
  155. package/dist/server/bare/plugins/index.d.ts +3 -1
  156. package/dist/server/bare/plugins/index.js +3 -1
  157. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +3 -0
  158. package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.d.ts +29 -0
  159. package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.js +65 -0
  160. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +6 -0
  161. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +26 -0
  162. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +25 -0
  163. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +5 -0
  164. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +67 -246
  165. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +66 -97
  166. package/dist/server/bare/plugins/sdcpp-generation/ops/video.d.ts +3 -0
  167. package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +199 -0
  168. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +200 -2
  169. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +61 -8
  170. package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech-stream.js +2 -15
  171. package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech.js +2 -1
  172. package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +75 -0
  173. package/dist/server/bare/plugins/tts-ggml/plugin.js +189 -0
  174. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +29 -4
  175. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +12 -1
  176. package/dist/server/bare/registry/model-config-utils.js +7 -0
  177. package/dist/server/bare/runtime/request-context.d.ts +1 -1
  178. package/dist/server/bare/runtime-lifecycle.js +9 -9
  179. package/dist/server/bare/types/addon-responses.d.ts +1 -0
  180. package/dist/server/env.js +4 -1
  181. package/dist/server/plugins/index.d.ts +1 -1
  182. package/dist/server/plugins/index.js +1 -1
  183. package/dist/server/plugins/registry.d.ts +1 -0
  184. package/dist/server/plugins/registry.js +5 -0
  185. package/dist/server/rpc/handler-registry.js +4 -0
  186. package/dist/server/rpc/handler-selection.d.ts +9 -0
  187. package/dist/server/rpc/handler-selection.js +14 -0
  188. package/dist/server/rpc/handler-utils.js +5 -13
  189. package/dist/server/rpc/handlers/classify.d.ts +3 -0
  190. package/dist/server/rpc/handlers/classify.js +4 -0
  191. package/dist/server/rpc/handlers/index.d.ts +2 -0
  192. package/dist/server/rpc/handlers/index.js +2 -0
  193. package/dist/server/rpc/handlers/load-model/handler.js +4 -1
  194. package/dist/server/rpc/handlers/load-model/resolve.js +9 -0
  195. package/dist/server/rpc/handlers/video-stream.d.ts +3 -0
  196. package/dist/server/rpc/handlers/video-stream.js +4 -0
  197. package/dist/server/rpc/profiling/operation-metrics.js +23 -0
  198. package/dist/server/utils/config-transform.js +1 -1
  199. package/dist/server/utils/tools/parsers/qwen35.js +7 -3
  200. package/dist/server/worker.js +14 -11
  201. package/dist/utils/errors-client.d.ts +23 -0
  202. package/dist/utils/errors-client.js +40 -0
  203. package/dist/utils/errors-server.d.ts +30 -2
  204. package/dist/utils/errors-server.js +51 -3
  205. package/package.json +66 -80
  206. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +0 -490
  207. package/dist/server/bare/plugins/onnx-tts/plugin.js +0 -272
  208. package/dist/server/bare/plugins/onnx-tts/wav-helper.d.ts +0 -14
  209. package/dist/server/bare/plugins/onnx-tts/wav-helper.js +0 -107
  210. /package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech-stream.d.ts +0 -0
  211. /package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech.d.ts +0 -0
@@ -0,0 +1,109 @@
1
+ import { vlaHparamsResponseSchema, vlaRunResponseSchema, } from "../../schemas/index.js";
2
+ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
3
+ import { invokePlugin } from "./invoke-plugin.js";
4
+ const VLA_RUN_HANDLER = "vlaRun";
5
+ const VLA_HPARAMS_HANDLER = "vlaHparams";
6
+ function bytesOf(arr) {
7
+ return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
8
+ }
9
+ function f32FromBase64(b64) {
10
+ const bytes = decodeBase64(b64);
11
+ // base64 round-trip produces a fresh Uint8Array whose buffer starts at 0
12
+ // and isn't shared, so the underlying ArrayBuffer is safe to reinterpret.
13
+ return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
14
+ }
15
+ /**
16
+ * Run SmolVLA inference on a loaded VLA model and return the produced action
17
+ * chunk plus per-stage timings.
18
+ *
19
+ * @param params - Inference inputs.
20
+ * @param params.modelId - Identifier of the loaded VLA model (returned by
21
+ * `loadModel({ modelType: "vla", ... })`).
22
+ * @param params.images - One or more preprocessed camera frames; each is a
23
+ * `Float32Array` of length `3 * imgWidth * imgHeight` in CHW layout, range
24
+ * `[-1, 1]`. Use the addon's `preprocessImage()` (re-exported as
25
+ * `vlaPreprocessImage`) to produce them.
26
+ * @param params.imgWidth - Width of each preprocessed image; must equal
27
+ * `hparams.visionImageSize`.
28
+ * @param params.imgHeight - Height of each preprocessed image; must equal
29
+ * `hparams.visionImageSize`.
30
+ * @param params.state - Robot end-effector / gripper state padded to
31
+ * `hparams.maxStateDim` (use `vlaPadState`).
32
+ * @param params.tokens - Tokenized instruction (`Int32Array` of length
33
+ * `hparams.tokenizerMaxLength`). Tokenize on the consumer side with the
34
+ * SmolVLM2 tokenizer.
35
+ * @param params.mask - Token attention mask (`Uint8Array` matching `tokens`).
36
+ * @param params.noise - Optional seeded noise prior
37
+ * (`Float32Array` of length `hparams.chunkSize * hparams.maxActionDim`).
38
+ * When omitted the addon samples its own prior.
39
+ * @returns A `VlaClientRunResult` with the produced `actions` Float32Array
40
+ * (length `chunkSize * actionDim`), the corresponding `chunkSize` /
41
+ * `actionDim` returned by the addon, and optional per-stage `stats`.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { loadModel, vla, vlaPreprocessImage, vlaPadState, vlaHparams } from "@qvac/sdk";
46
+ *
47
+ * const modelId = await loadModel({ modelSrc: "/path/to/smolvla.gguf", modelType: "vla" });
48
+ * const { hparams } = await vlaHparams({ modelId });
49
+ * const size = hparams.visionImageSize;
50
+ * const front = vlaPreprocessImage(frontPixels, frontW, frontH, { size });
51
+ * const wrist = vlaPreprocessImage(wristPixels, wristW, wristH, { size });
52
+ * const state = vlaPadState(robotState, hparams.maxStateDim);
53
+ * const tokens = new Int32Array(hparams.tokenizerMaxLength);
54
+ * const mask = new Uint8Array(hparams.tokenizerMaxLength);
55
+ * // ...tokenize the instruction into tokens/mask...
56
+ * const { actions } = await vla({
57
+ * modelId, images: [front, wrist], imgWidth: size, imgHeight: size,
58
+ * state, tokens, mask,
59
+ * });
60
+ * ```
61
+ */
62
+ export async function vla(params) {
63
+ const wireRequest = {
64
+ type: "vlaRun",
65
+ modelId: params.modelId,
66
+ images: params.images.map((img) => encodeBase64(bytesOf(img))),
67
+ imgWidth: params.imgWidth,
68
+ imgHeight: params.imgHeight,
69
+ state: encodeBase64(bytesOf(params.state)),
70
+ tokens: encodeBase64(bytesOf(params.tokens)),
71
+ mask: encodeBase64(bytesOf(params.mask)),
72
+ ...(params.noise !== undefined && {
73
+ noise: encodeBase64(bytesOf(params.noise)),
74
+ }),
75
+ };
76
+ const result = await invokePlugin({
77
+ modelId: params.modelId,
78
+ handler: VLA_RUN_HANDLER,
79
+ params: wireRequest,
80
+ });
81
+ const parsed = vlaRunResponseSchema.parse(result);
82
+ return {
83
+ actions: f32FromBase64(parsed.actions),
84
+ actionDim: parsed.actionDim,
85
+ chunkSize: parsed.chunkSize,
86
+ ...(parsed.stats && { stats: parsed.stats }),
87
+ };
88
+ }
89
+ /**
90
+ * Fetch the loaded VLA model's hyperparameters and the active ggml backend
91
+ * name. Useful to size token / state / noise buffers before calling `vla()`.
92
+ *
93
+ * @param params - Identifier of the loaded VLA model.
94
+ * @returns The model's hparams and the human-readable backend name
95
+ * (`"CPU"` / `"Vulkan"` / `"Metal"` / `"OpenCL"` / `null` if the addon
96
+ * has not surfaced one).
97
+ */
98
+ export async function vlaHparams(params) {
99
+ const wireRequest = {
100
+ type: "vlaHparams",
101
+ modelId: params.modelId,
102
+ };
103
+ const result = await invokePlugin({
104
+ modelId: params.modelId,
105
+ handler: VLA_HPARAMS_HANDLER,
106
+ params: wireRequest,
107
+ });
108
+ return vlaHparamsResponseSchema.parse(result);
109
+ }
@@ -1,4 +1,20 @@
1
1
  import { type QvacConfig } from "./config-utils";
2
+ /** Config filenames searched under a project root (bundle/verify/commands). */
3
+ export declare const CONFIG_CANDIDATES: readonly ["qvac.config.ts", "qvac.config.mjs", "qvac.config.js", "qvac.config.json"];
4
+ /** Load and validate a config file at an explicit path (used by RPC init and commands). */
5
+ export declare function loadConfigFromPath(configPath: string): Promise<QvacConfig>;
6
+ /**
7
+ * Resolve a config file path under `projectRoot` (or an explicit relative/absolute path).
8
+ * Used by `bundleSdk` / `verifyBundle` when the caller supplies `projectRoot`.
9
+ */
10
+ export declare function resolveConfigFileInProject(projectRoot: string, explicitPath?: string): Promise<string | null>;
11
+ /**
12
+ * Load `qvac.config.*` for a known project directory (commands tooling API).
13
+ */
14
+ export declare function resolveConfigForProject(projectRoot: string, explicitPath?: string): Promise<{
15
+ configPath: string | null;
16
+ config: QvacConfig;
17
+ }>;
2
18
  /**
3
19
  * Resolution order for Node.js:
4
20
  * 1. QVAC_CONFIG_PATH environment variable
@@ -6,9 +6,16 @@ import { promises as fs } from "fs";
6
6
  import path from "path";
7
7
  import { pathToFileURL } from "url";
8
8
  import { validateConfig, parseJsonConfig, } from "./config-utils.js";
9
- import { ConfigFileParseFailedError } from "../../utils/errors-client.js";
9
+ import { ConfigFileInvalidError, ConfigFileParseFailedError, } from "../../utils/errors-client.js";
10
10
  import { getClientLogger } from "../../logging/index.js";
11
11
  const logger = getClientLogger();
12
+ /** Config filenames searched under a project root (bundle/verify/commands). */
13
+ export const CONFIG_CANDIDATES = [
14
+ "qvac.config.ts",
15
+ "qvac.config.mjs",
16
+ "qvac.config.js",
17
+ "qvac.config.json",
18
+ ];
12
19
  async function findProjectRoot() {
13
20
  try {
14
21
  let currentDir = path.resolve(process.cwd());
@@ -65,20 +72,76 @@ async function loadJsConfig(filePath) {
65
72
  throw new ConfigFileParseFailedError(filePath, error instanceof Error ? error.message : String(error), error);
66
73
  }
67
74
  }
68
- async function findConfigFile(searchDir) {
69
- const configFiles = [
70
- { name: "qvac.config.ts", type: "ts" },
71
- { name: "qvac.config.js", type: "js" },
72
- { name: "qvac.config.json", type: "json" },
73
- ];
74
- for (const { name, type } of configFiles) {
75
+ async function loadTsConfig(filePath) {
76
+ let tsxApiPath;
77
+ try {
78
+ const { createRequire } = await import("node:module");
79
+ const require = createRequire(import.meta.url);
80
+ tsxApiPath = require.resolve("tsx/esm/api");
81
+ }
82
+ catch (error) {
83
+ throw new ConfigFileInvalidError(filePath, "Loading a TypeScript qvac.config.ts requires the optional peer dependency `tsx`. Install it as a devDependency, or use qvac.config.mjs / qvac.config.js / qvac.config.json instead.", error);
84
+ }
85
+ try {
86
+ const tsxModule = (await import(tsxApiPath));
87
+ const mod = await tsxModule.tsImport(filePath, import.meta.url);
88
+ return validateConfig(mod.default ?? mod);
89
+ }
90
+ catch (error) {
91
+ throw new ConfigFileParseFailedError(filePath, error instanceof Error ? error.message : String(error), error);
92
+ }
93
+ }
94
+ /** Load and validate a config file at an explicit path (used by RPC init and commands). */
95
+ export async function loadConfigFromPath(configPath) {
96
+ const ext = path.extname(configPath).toLowerCase();
97
+ if (ext === ".json") {
98
+ return loadJsonConfig(configPath);
99
+ }
100
+ if (ext === ".js" || ext === ".mjs") {
101
+ return loadJsConfig(configPath);
102
+ }
103
+ if (ext === ".ts") {
104
+ return loadTsConfig(configPath);
105
+ }
106
+ throw new ConfigFileInvalidError(configPath, `Unsupported config format: ${ext}. Use .json, .js, .mjs, or .ts`);
107
+ }
108
+ async function findConfigFileInDir(searchDir) {
109
+ for (const name of CONFIG_CANDIDATES) {
75
110
  const filePath = path.resolve(searchDir, name);
76
111
  if (await fileExists(filePath)) {
77
- return { path: filePath, type };
112
+ return filePath;
78
113
  }
79
114
  }
80
115
  return undefined;
81
116
  }
117
+ /**
118
+ * Resolve a config file path under `projectRoot` (or an explicit relative/absolute path).
119
+ * Used by `bundleSdk` / `verifyBundle` when the caller supplies `projectRoot`.
120
+ */
121
+ export async function resolveConfigFileInProject(projectRoot, explicitPath) {
122
+ if (explicitPath) {
123
+ const absPath = path.isAbsolute(explicitPath)
124
+ ? explicitPath
125
+ : path.join(projectRoot, explicitPath);
126
+ if (await fileExists(absPath)) {
127
+ return absPath;
128
+ }
129
+ throw new ConfigFileInvalidError(absPath, "Config file not found at explicit path");
130
+ }
131
+ const found = await findConfigFileInDir(projectRoot);
132
+ return found ?? null;
133
+ }
134
+ /**
135
+ * Load `qvac.config.*` for a known project directory (commands tooling API).
136
+ */
137
+ export async function resolveConfigForProject(projectRoot, explicitPath) {
138
+ const configPath = await resolveConfigFileInProject(projectRoot, explicitPath);
139
+ if (!configPath) {
140
+ return { configPath: null, config: {} };
141
+ }
142
+ const config = await loadConfigFromPath(configPath);
143
+ return { configPath, config };
144
+ }
82
145
  function getResourcesPath() {
83
146
  const { resourcesPath } = process;
84
147
  return typeof resourcesPath === "string" ? resourcesPath : undefined;
@@ -111,14 +174,7 @@ export async function resolveConfig() {
111
174
  if (configPath) {
112
175
  const normalizedPath = path.resolve(configPath);
113
176
  if (await fileExists(normalizedPath)) {
114
- const ext = normalizedPath.endsWith(".json")
115
- ? "json"
116
- : normalizedPath.endsWith(".ts")
117
- ? "ts"
118
- : "js";
119
- const config = ext === "json"
120
- ? await loadJsonConfig(normalizedPath)
121
- : await loadJsConfig(normalizedPath);
177
+ const config = await loadConfigFromPath(normalizedPath);
122
178
  logger.info(`✅ Loaded config from: ${normalizedPath}`);
123
179
  return config;
124
180
  }
@@ -131,12 +187,10 @@ export async function resolveConfig() {
131
187
  }
132
188
  const projectRoot = await findProjectRoot();
133
189
  if (projectRoot) {
134
- const configFile = await findConfigFile(projectRoot);
135
- if (configFile) {
136
- const config = configFile.type === "json"
137
- ? await loadJsonConfig(configFile.path)
138
- : await loadJsConfig(configFile.path);
139
- logger.info(`✅ Loaded config from: ${configFile.path}`);
190
+ const configFilePath = await findConfigFileInDir(projectRoot);
191
+ if (configFilePath) {
192
+ const config = await loadConfigFromPath(configFilePath);
193
+ logger.info(`✅ Loaded config from: ${configFilePath}`);
140
194
  return config;
141
195
  }
142
196
  }
@@ -0,0 +1,8 @@
1
+ import * as hostApi from "./api/index";
2
+ import type { QvacPlugin } from "../schemas/plugin";
3
+ /**
4
+ * Register plugins and return the client host API. Designed for
5
+ * @qvac/bare-sdk consumers assembling explicit plugin subsets.
6
+ */
7
+ export declare function plugins(pluginList: readonly QvacPlugin[]): typeof hostApi;
8
+ //# sourceMappingURL=plugins-factory.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { registerPlugins } from "../server/plugins/index.js";
2
+ import * as hostApi from "./api/index.js";
3
+ /**
4
+ * Register plugins and return the client host API. Designed for
5
+ * @qvac/bare-sdk consumers assembling explicit plugin subsets.
6
+ */
7
+ export function plugins(pluginList) {
8
+ registerPlugins(pluginList);
9
+ return hostApi;
10
+ }
@@ -1,5 +1,6 @@
1
1
  import type { Request, Response } from "../../schemas/index";
2
2
  export declare function send<T extends Request>(request: T): Promise<Response>;
3
+ export declare function getWorkerLifeSignal(): AbortSignal | null;
3
4
  export declare function getRPC(): Promise<{
4
5
  request(): {
5
6
  send(payload: string): void;
@@ -1,59 +1,27 @@
1
- import { normalizeModelType } from "../../schemas/index.js";
1
+ import { normalizeModelType, PROFILING_KEY } from "../../schemas/index.js";
2
2
  import os from "bare-os";
3
- import { handlers } from "../../server/rpc/handlers/index.js";
4
3
  import { registry } from "../../server/rpc/handler-registry.js";
4
+ import { handlerSupportsProgress, selectHandler, } from "../../server/rpc/handler-selection.js";
5
5
  import { createErrorResponse } from "../../schemas/index.js";
6
- import { PearWorkerEntryRequiredError, RPCNoHandlerError, RPCRequestNotSentError, } from "../../utils/errors-client.js";
6
+ import { RPCNoHandlerError, RPCRequestNotSentError, } from "../../utils/errors-client.js";
7
7
  import { initializeConfig } from "../init-hooks.js";
8
8
  import { setSDKConfig } from "../../server/bare/registry/config-registry.js";
9
9
  import { setRuntimeContext } from "../../server/bare/registry/runtime-context-registry.js";
10
10
  import { resolveModelConfig } from "../../server/bare/registry/model-config-registry.js";
11
11
  import { resolveConfig } from "../config-loader/resolve-config.bare.js";
12
- import { getClientLogger } from "../../logging/index.js";
13
- import { getAllPlugins } from "../../server/plugins/index.js";
14
12
  import { initializeWorkerCore, shutdownBareDirectWorker, cleanupForTerminate, } from "../../server/worker-core.js";
15
13
  import { assertLifecycleAllowed } from "../../server/bare/runtime-lifecycle.js";
16
- const logger = getClientLogger();
17
- /**
18
- * Load worker entry to register plugins before any SDK calls.
19
- *
20
- * If plugins are already registered (e.g., by a Pear worker bootstrap),
21
- * skip loading the default worker but still initialize worker core.
22
- *
23
- * NOTE: For Pear apps, the fallback (loading default worker) cannot work.
24
- *
25
- * The default worker is intentionally excluded from `pear stage --compact`
26
- * to enable tree-shaking of unused built-in plugins. When a Pear app tries
27
- * to dynamically import it at runtime, the path resolves to a pear:// URL.
28
- * The default worker imports built-in plugins, which load native addons
29
- * (.bare files). Native addons cannot be loaded from pear:// URLs, causing
30
- * UNSUPPORTED_PROTOCOL errors.
31
- *
32
- * Instead, we detect Pear runtime and throw a clear error directing users
33
- * to use the generated worker entry (qvac/worker.pear.entry.mjs).
34
- *
35
- * The fallback only works in non-Pear environments.
36
- */
37
- async function loadWorkerEntry() {
14
+ import { ensurePluginsRegistered } from "./ensure-worker-ready.js";
15
+ async function ensureWorkerReady() {
38
16
  initializeWorkerCore();
39
- if (getAllPlugins().length > 0) {
40
- logger.info("📦 Plugins already registered, worker core initialized");
41
- return;
42
- }
43
- const { isPear } = await import("which-runtime");
44
- if (isPear) {
45
- throw new PearWorkerEntryRequiredError("qvac/worker.pear.entry.mjs");
46
- }
47
- // Fallback: load default worker (all built-in plugins)
48
- logger.info("📦 Loading default worker (all built-in plugins)");
49
- const workerPath = "../../server/" + "worker.js";
50
- await import(workerPath);
17
+ await ensurePluginsRegistered();
51
18
  }
52
- let workerEntryLoaded = false;
53
- // Get the handler for a request type
54
- function getHandler(type) {
55
- const handler = handlers[type];
56
- return typeof handler === "function" ? handler : undefined;
19
+ function getHandlerEntry(type) {
20
+ const entry = registry[type];
21
+ if (!entry) {
22
+ throw new RPCNoHandlerError(type);
23
+ }
24
+ return entry;
57
25
  }
58
26
  function applyDeviceDefaultsToLoadModel(request) {
59
27
  if (request.type !== "loadModel" || !("modelSrc" in request)) {
@@ -70,16 +38,49 @@ function applyDeviceDefaultsToLoadModel(request) {
70
38
  const configWithDefaults = resolveModelConfig(canonicalType, rawConfig);
71
39
  return { ...request, modelConfig: configWithDefaults };
72
40
  }
73
- function supportsProgressStreaming(request) {
74
- return ("withProgress" in request &&
75
- request.withProgress &&
76
- ["loadModel", "downloadAsset", "rag", "finetune"].includes(request.type));
41
+ function getProfilingMetaFromRequest(request) {
42
+ if (PROFILING_KEY in request) {
43
+ return request[PROFILING_KEY];
44
+ }
45
+ return undefined;
46
+ }
47
+ function createDelegatedOptions(request, progressCallback) {
48
+ const profilingMeta = getProfilingMetaFromRequest(request);
49
+ if (!profilingMeta && !progressCallback) {
50
+ return undefined;
51
+ }
52
+ const options = {};
53
+ if (progressCallback) {
54
+ options.progressCallback = progressCallback;
55
+ }
56
+ if (profilingMeta) {
57
+ options.profilingMeta = profilingMeta;
58
+ }
59
+ return options;
60
+ }
61
+ function executeDirectHandler(request, handler, isDelegated) {
62
+ const directHandler = handler;
63
+ if (isDelegated) {
64
+ return directHandler(request, createDelegatedOptions(request));
65
+ }
66
+ return directHandler(request);
77
67
  }
78
- async function* streamWithProgress(request, handler) {
68
+ function isAsyncGenerator(result) {
69
+ return (typeof result === "object" &&
70
+ result !== null &&
71
+ Symbol.asyncIterator in result);
72
+ }
73
+ async function* streamWithProgress(request, handler, isDelegated) {
79
74
  const queue = [];
80
75
  const errors = [];
81
76
  let done = false;
82
- handler(request, (update) => queue.push(update))
77
+ function progressCallback(update) {
78
+ queue.push(update);
79
+ }
80
+ const directHandler = handler;
81
+ Promise.resolve(directHandler(request, isDelegated
82
+ ? createDelegatedOptions(request, progressCallback)
83
+ : progressCallback))
83
84
  .then((final) => {
84
85
  queue.push(final);
85
86
  done = true;
@@ -103,25 +104,23 @@ async function* streamWithProgress(request, handler) {
103
104
  }
104
105
  export async function send(request) {
105
106
  assertLifecycleAllowed(request);
106
- const handler = getHandler(request.type);
107
- if (!handler)
108
- throw new RPCNoHandlerError(request.type);
109
107
  const processedRequest = applyDeviceDefaultsToLoadModel(request);
110
- return (await handler(processedRequest));
108
+ const entry = getHandlerEntry(processedRequest.type);
109
+ const { handler, isDelegated } = selectHandler(entry, processedRequest);
110
+ return (await executeDirectHandler(processedRequest, handler, isDelegated));
111
111
  }
112
112
  async function* stream(request) {
113
113
  assertLifecycleAllowed(request);
114
- const handler = getHandler(request.type);
115
- if (!handler)
116
- throw new RPCNoHandlerError(request.type);
117
- if (supportsProgressStreaming(request)) {
118
- const processedRequest = applyDeviceDefaultsToLoadModel(request);
119
- yield* streamWithProgress(processedRequest, handler);
114
+ const processedRequest = applyDeviceDefaultsToLoadModel(request);
115
+ const entry = getHandlerEntry(processedRequest.type);
116
+ const { handler, isDelegated } = selectHandler(entry, processedRequest);
117
+ if (handlerSupportsProgress(entry, processedRequest)) {
118
+ yield* streamWithProgress(processedRequest, handler, isDelegated);
120
119
  }
121
120
  else {
122
- const result = handler(request);
121
+ const result = executeDirectHandler(processedRequest, handler, isDelegated);
123
122
  // Check if the handler returns a Promise or AsyncGenerator
124
- if (Symbol.asyncIterator in result) {
123
+ if (isAsyncGenerator(result)) {
125
124
  // It's an AsyncGenerator
126
125
  yield* result;
127
126
  }
@@ -194,11 +193,12 @@ function createMockRPCRequest() {
194
193
  };
195
194
  }
196
195
  let configInitialized = false;
196
+ // No child worker on Bare-direct — `#rpc` interface stub.
197
+ export function getWorkerLifeSignal() {
198
+ return null;
199
+ }
197
200
  export async function getRPC() {
198
- if (!workerEntryLoaded) {
199
- await loadWorkerEntry();
200
- workerEntryLoaded = true;
201
- }
201
+ await ensureWorkerReady();
202
202
  const mockRPC = {
203
203
  request() {
204
204
  return createMockRPCRequest();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Throw if no plugins are registered before an SDK call. On Pear,
3
+ * point at the generated worker entry; otherwise raise a generic
4
+ * registration error. `isPear` is overridable for tests.
5
+ */
6
+ export declare function ensurePluginsRegistered(opts?: {
7
+ isPear?: boolean;
8
+ }): Promise<void>;
9
+ //# sourceMappingURL=ensure-worker-ready.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { getAllPlugins } from "../../server/plugins/index.js";
2
+ import { PearWorkerEntryRequiredError, WorkerPluginsNotRegisteredError, } from "../../utils/errors-client.js";
3
+ const PEAR_WORKER_ENTRY_PATH = "qvac/worker.pear.entry.mjs";
4
+ /**
5
+ * Throw if no plugins are registered before an SDK call. On Pear,
6
+ * point at the generated worker entry; otherwise raise a generic
7
+ * registration error. `isPear` is overridable for tests.
8
+ */
9
+ export async function ensurePluginsRegistered(opts) {
10
+ if (getAllPlugins().length > 0)
11
+ return;
12
+ const isPear = opts?.isPear ?? (await import("which-runtime")).isPear;
13
+ if (isPear) {
14
+ throw new PearWorkerEntryRequiredError(PEAR_WORKER_ENTRY_PATH);
15
+ }
16
+ throw new WorkerPluginsNotRegisteredError();
17
+ }
@@ -1,4 +1,5 @@
1
1
  import RPC from "bare-rpc";
2
+ export declare function getWorkerLifeSignal(): AbortSignal | null;
2
3
  export declare function getRPC(): Promise<RPC>;
3
4
  export declare function close(): Promise<void>;
4
5
  export declare function createDuplexSession(payload: string, commandId: number): Promise<{
@@ -28,6 +28,10 @@ async function getRuntimeContext() {
28
28
  };
29
29
  return cachedRuntimeContext;
30
30
  }
31
+ // No child-process exit on Expo (Worklet lifecycle) — `#rpc` interface stub.
32
+ export function getWorkerLifeSignal() {
33
+ return null;
34
+ }
31
35
  export async function getRPC() {
32
36
  if (rpcInstance) {
33
37
  return rpcInstance;
@@ -1,4 +1,5 @@
1
1
  import RPC from "bare-rpc";
2
+ export declare function getWorkerLifeSignal(): AbortSignal | null;
2
3
  export declare function getRPC(): Promise<RPC>;
3
4
  export declare function createDuplexSession(payload: string, commandId: number): Promise<{
4
5
  requestStream: RPC.OutgoingStream;