@camstack/types 1.2.391 → 1.2.393
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/deps/archive-extract.d.ts +2 -1
- package/dist/deps/deb-extract.d.ts +34 -0
- package/dist/deps/edgetpu-delegate.d.ts +83 -0
- package/dist/node.d.ts +5 -3
- package/dist/node.js +365 -1
- package/dist/node.mjs +353 -4
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** `tar` = an uncompressed tarball (a `.deb` payload this module already decompressed). */
|
|
2
|
+
export type ArchiveFormat = 'zip' | 'tar.gz' | 'tar.xz' | 'tar';
|
|
2
3
|
export type ExecFile = (file: string, args: readonly string[]) => unknown;
|
|
3
4
|
export interface ExtractDeps {
|
|
4
5
|
readonly platform: NodeJS.Platform;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ArchiveFormat } from './archive-extract.js';
|
|
2
|
+
/** The host-tar step, injectable so a spec can see what it is handed. */
|
|
3
|
+
export type TarExtract = (archivePath: string, format: ArchiveFormat, destDir: string) => void;
|
|
4
|
+
export interface ArMember {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly data: Buffer;
|
|
7
|
+
}
|
|
8
|
+
/** Every member of an `ar` archive, in order. Throws on anything that is not one. */
|
|
9
|
+
export declare function readArMembers(archive: Buffer): readonly ArMember[];
|
|
10
|
+
export type DebPayloadCompression = 'zst' | 'xz' | 'gz' | 'none';
|
|
11
|
+
export interface DebPayload {
|
|
12
|
+
readonly compression: DebPayloadCompression;
|
|
13
|
+
readonly data: Buffer;
|
|
14
|
+
}
|
|
15
|
+
/** The `data.tar.*` member of a `.deb`, with the compression its name declares. */
|
|
16
|
+
export declare function debDataMember(deb: Buffer): DebPayload;
|
|
17
|
+
/**
|
|
18
|
+
* Unpack a `.deb`'s payload into `destDir` (the package's own tree: `usr/lib/…`).
|
|
19
|
+
* zstd and gzip are decompressed in-process; the plain tar and the xz payload
|
|
20
|
+
* are expanded by the host `tar`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractDebPayload(deb: Buffer, destDir: string, workDir: string, extract?: TarExtract): void;
|
|
23
|
+
/**
|
|
24
|
+
* The first entry named exactly `name` under `dir` — a regular file OR a
|
|
25
|
+
* symlink: a library's soname (`libusb-1.0.so.0`) is a link to the versioned
|
|
26
|
+
* file in every package, so a file-only search never finds it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function findEntryByName(dir: string, name: string): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Copy the library `memberName` out of the `.deb` at `debPath` to `targetPath`,
|
|
31
|
+
* dereferencing the soname link to the real file. Throws, naming the member,
|
|
32
|
+
* when the package does not carry it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function installLibraryFromDeb(debPath: string, memberName: string, targetPath: string, workDir: string, extract?: TarExtract): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { IScopedLogger } from '../interfaces/logging.js';
|
|
2
|
+
/** The path override (a library PATH, never a secret). */
|
|
3
|
+
export declare const EDGETPU_LIB_ENV = "CAMSTACK_EDGETPU_LIB";
|
|
4
|
+
/** The pinned feranick release. Changing it is the delegate version change. */
|
|
5
|
+
export declare const EDGETPU_DELEGATE_RELEASE: '16.0TF2.17.1-1';
|
|
6
|
+
/** Where the hub image's installer has always put the delegate. */
|
|
7
|
+
export declare const LEGACY_EDGETPU_LIB = "/data/deps/edgetpu/lib/libedgetpu.so.1";
|
|
8
|
+
/** One file of the delegate set: where it comes from and the name it is staged as. */
|
|
9
|
+
export interface EdgetpuStagedFile {
|
|
10
|
+
readonly url: string;
|
|
11
|
+
/** SHA-256 of the downloaded container, verified before a byte is extracted. */
|
|
12
|
+
readonly sha256: string;
|
|
13
|
+
readonly sizeBytes: number;
|
|
14
|
+
readonly container: 'deb' | 'zip';
|
|
15
|
+
/** deb: the library's file name inside the payload; zip: its path inside the archive. */
|
|
16
|
+
readonly member: string;
|
|
17
|
+
/** The name it is staged as in `<deps>/edgetpu/lib/`. */
|
|
18
|
+
readonly installAs: string;
|
|
19
|
+
}
|
|
20
|
+
export type EdgetpuDelegateArtifact = {
|
|
21
|
+
readonly supported: true;
|
|
22
|
+
/** The delegate first; libusb (Linux) after it. */
|
|
23
|
+
readonly files: readonly EdgetpuStagedFile[];
|
|
24
|
+
} | {
|
|
25
|
+
readonly supported: false;
|
|
26
|
+
readonly reason: string;
|
|
27
|
+
};
|
|
28
|
+
/** The pinned delegate set for a platform and arch, or why there is none. */
|
|
29
|
+
export declare function getEdgetpuDelegateArtifact(platform: NodeJS.Platform, arch: string): EdgetpuDelegateArtifact;
|
|
30
|
+
/** The delegate's file name on a platform (what the Python pool loads). */
|
|
31
|
+
export declare function edgetpuDelegateFileName(platform: NodeJS.Platform): string;
|
|
32
|
+
/** `<dataDir>/deps/edgetpu/lib` — where the deps downloader stages the set. */
|
|
33
|
+
export declare function edgetpuLibDir(dataDir: string): string;
|
|
34
|
+
export type EdgetpuDelegateOrigin = 'override' | 'staged' | 'legacy';
|
|
35
|
+
export interface EdgetpuDelegateChoice {
|
|
36
|
+
readonly path: string;
|
|
37
|
+
readonly origin: EdgetpuDelegateOrigin;
|
|
38
|
+
}
|
|
39
|
+
export interface EdgetpuDelegateLookup {
|
|
40
|
+
readonly platform: NodeJS.Platform;
|
|
41
|
+
/** The node data root (`CAMSTACK_DATA`), or `undefined` when unknown. */
|
|
42
|
+
readonly dataDir: string | undefined;
|
|
43
|
+
/** `CAMSTACK_EDGETPU_LIB`, verbatim. */
|
|
44
|
+
readonly override: string | undefined;
|
|
45
|
+
readonly exists: (path: string) => boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The delegate this node loads, or `null` when none is present. An override
|
|
49
|
+
* that names a missing file is NOT followed past: the operator said where it
|
|
50
|
+
* is, and a silent fallback would load a different build than they chose.
|
|
51
|
+
*/
|
|
52
|
+
export declare function chooseEdgetpuDelegate(lookup: EdgetpuDelegateLookup): EdgetpuDelegateChoice | null;
|
|
53
|
+
/** The node data root the way every node process names it. */
|
|
54
|
+
export declare function nodeDataRootFromEnv(env: NodeJS.ProcessEnv): string | undefined;
|
|
55
|
+
export type EdgetpuDelegateResult = {
|
|
56
|
+
readonly kind: 'ready';
|
|
57
|
+
readonly path: string;
|
|
58
|
+
readonly origin: EdgetpuDelegateOrigin | 'downloaded';
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'unavailable';
|
|
61
|
+
readonly reason: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A requirements file names the native library its packages cannot run
|
|
65
|
+
* without on this line; `installPythonRequirements` stages it after pip.
|
|
66
|
+
* (`ai-edge-litert` imports without the delegate and is useless without it.)
|
|
67
|
+
*/
|
|
68
|
+
export declare const EDGETPU_DELEGATE_DIRECTIVE = "# camstack-native-dep: edgetpu-delegate";
|
|
69
|
+
export declare function declaresEdgetpuDelegate(requirementsText: string): boolean;
|
|
70
|
+
export interface EnsureEdgetpuDelegateOptions {
|
|
71
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
72
|
+
/** The host, for a spec; defaults to this process's. */
|
|
73
|
+
readonly platform?: NodeJS.Platform;
|
|
74
|
+
readonly arch?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The delegate this node loads, staging the pinned set under
|
|
78
|
+
* `<dataDir>/deps/edgetpu/lib/` when nothing is present yet. Never throws for
|
|
79
|
+
* a platform without a build — it answers `unavailable` with the reason; a
|
|
80
|
+
* download or checksum failure throws, naming the file. The line it logs says
|
|
81
|
+
* which delegate (and from where) the pool will load.
|
|
82
|
+
*/
|
|
83
|
+
export declare function ensureEdgetpuDelegate(dataDir: string, logger: IScopedLogger, opts?: EnsureEdgetpuDelegateOptions): Promise<EdgetpuDelegateResult>;
|
package/dist/node.d.ts
CHANGED
|
@@ -3,16 +3,18 @@ export { buildBinaryPath, downloadBinary, ensureBinary, findInPath, getPlatformI
|
|
|
3
3
|
export { ensureFfmpeg } from './deps/ffmpeg-downloader.js';
|
|
4
4
|
export type { OwnerOnlyDeps } from './util/owner-only-file.js';
|
|
5
5
|
export { icaclsArgs, mkdirOwnerOnlySync, OwnerOnlyError, restrictDirToOwnerSync, restrictFileToOwnerSync, writeOwnerOnlyFileSync, } from './util/owner-only-file.js';
|
|
6
|
-
export { FFMPEG_RELEASE, getFfmpegArtifact, getFfmpegDownloadUrl } from './deps/ffmpeg-artifacts.js';
|
|
6
|
+
export { FFMPEG_RELEASE, getFfmpegArtifact, getFfmpegDownloadUrl, } from './deps/ffmpeg-artifacts.js';
|
|
7
7
|
export { probeFfmpegBinary, parseFfmpegCapabilities, type FfmpegBinaryCapabilities, } from './ffmpeg/binary-capabilities.js';
|
|
8
8
|
export { ensurePython, getPythonDownloadUrl, installPythonPackages, installPythonRequirements, PYTHON_VERSION, } from './deps/python-downloader.js';
|
|
9
|
+
export { chooseEdgetpuDelegate, declaresEdgetpuDelegate, EDGETPU_DELEGATE_DIRECTIVE, EDGETPU_DELEGATE_RELEASE, EDGETPU_LIB_ENV, edgetpuDelegateFileName, edgetpuLibDir, ensureEdgetpuDelegate, getEdgetpuDelegateArtifact, LEGACY_EDGETPU_LIB, nodeDataRootFromEnv, type EdgetpuDelegateArtifact, type EdgetpuDelegateChoice, type EdgetpuDelegateLookup, type EdgetpuDelegateOrigin, type EdgetpuDelegateResult, type EdgetpuStagedFile, type EnsureEdgetpuDelegateOptions, } from './deps/edgetpu-delegate.js';
|
|
10
|
+
export { debDataMember, extractDebPayload, installLibraryFromDeb, readArMembers, } from './deps/deb-extract.js';
|
|
9
11
|
export type { Fmp4FragmentChildArgs, Fmp4FragmentChildDeps, } from './ffmpeg/fmp4-fragment-child.js';
|
|
10
12
|
export { Fmp4FragmentChild } from './ffmpeg/fmp4-fragment-child.js';
|
|
11
13
|
export type { Fmp4CloseReason, Fmp4Packet, Fmp4PrebufferOptions, Fmp4PrebufferStats, Fmp4SubscribeInput, Fmp4SubscriberInfo, Fmp4Subscription, } from './ffmpeg/fmp4-fragment-plane.js';
|
|
12
14
|
export { Fmp4FragmentPlane } from './ffmpeg/fmp4-fragment-plane.js';
|
|
13
15
|
export type { FfmpegExit, FfmpegExitClass, FfmpegProcessOptions, FfmpegSpawnFn, SpawnedFfmpegProcess, } from './ffmpeg/process.js';
|
|
14
16
|
export { defaultFfmpegSpawn, FfmpegProcess } from './ffmpeg/process.js';
|
|
15
|
-
export type { FfmpegArgvSpawn, PinnedFfmpegSpawnAnswer } from './ffmpeg/pinned-spawn.js';
|
|
17
|
+
export type { FfmpegArgvSpawn, PinnedFfmpegSpawnAnswer, } from './ffmpeg/pinned-spawn.js';
|
|
16
18
|
export { NO_PINNED_FFMPEG_DETAIL, pinnedFfmpegSpawner, resolvePinnedFfmpegSpawner, } from './ffmpeg/pinned-spawn.js';
|
|
17
19
|
export type { ChildCostClaimHandle, ChildCostClaimInput, ProcStatReader, } from './process/child-cost-registry.js';
|
|
18
20
|
export { ChildCostRegistry, NO_COST_CLAIM, nodeProcStatReader, parseProcCpuSeconds, parseProcRssBytes, readProcessCost, } from './process/child-cost-registry.js';
|
|
@@ -23,6 +25,6 @@ export { containsOrEquals, physicalRootOf } from './storage/physical-root.js';
|
|
|
23
25
|
export { canonicalHash } from './utils/canonical-hash.js';
|
|
24
26
|
export { customizationBlockName } from './composition/customization-name.js';
|
|
25
27
|
export type { VerifyExpiringUrlInput } from './utils/expiring-url-signature.js';
|
|
26
|
-
export { signExpiringUrl, verifyExpiringUrl } from './utils/expiring-url-signature.js';
|
|
28
|
+
export { signExpiringUrl, verifyExpiringUrl, } from './utils/expiring-url-signature.js';
|
|
27
29
|
export type { DeviceExportShape, ExportDelta, ExportTargetEntry, } from './utils/export-reconciler.js';
|
|
28
30
|
export { canonicalDeviceFingerprint, diffExportTargets, resolveExportFingerprint, } from './utils/export-reconciler.js';
|
package/dist/node.js
CHANGED
|
@@ -178,8 +178,9 @@ function extractArchive(archivePath, format, destDir, overrides = {}) {
|
|
|
178
178
|
}
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
|
+
const flags = format === "tar.xz" ? "-xJf" : format === "tar" ? "-xf" : "-xzf";
|
|
181
182
|
deps.exec(tar, [
|
|
182
|
-
|
|
183
|
+
flags,
|
|
183
184
|
archivePath,
|
|
184
185
|
"-C",
|
|
185
186
|
destDir
|
|
@@ -873,6 +874,354 @@ async function installPythonRequirements(pythonPath, requirementsFile, logger) {
|
|
|
873
874
|
} });
|
|
874
875
|
}
|
|
875
876
|
//#endregion
|
|
877
|
+
//#region src/deps/deb-extract.ts
|
|
878
|
+
/**
|
|
879
|
+
* Pull one shared library out of a Debian package without `dpkg-deb`.
|
|
880
|
+
*
|
|
881
|
+
* A `.deb` is an `ar` archive holding `debian-binary`, `control.tar.*` and
|
|
882
|
+
* `data.tar.*`; the payload's compression is the packager's choice. The two
|
|
883
|
+
* debs the Coral delegate needs show both live ones: feranick's libedgetpu
|
|
884
|
+
* ships `data.tar.xz`, Ubuntu's libusb (24.04 and every release since 21.10)
|
|
885
|
+
* ships `data.tar.zst` — which `dpkg-deb` older than 1.21.18 and a stock
|
|
886
|
+
* macOS `tar` without a `zstd` binary cannot open. So the ar container and a
|
|
887
|
+
* zstd/gzip payload are unpacked IN-PROCESS (`node:zlib` has zstd since Node
|
|
888
|
+
* 22.15, which the hub and the desktop's Electron both run); only an xz
|
|
889
|
+
* payload goes through `tar -xJf`, which every POSIX host has.
|
|
890
|
+
*
|
|
891
|
+
* POSIX only: the delegate is never staged on Windows (no build exists).
|
|
892
|
+
*/
|
|
893
|
+
var AR_MAGIC = "!<arch>\n";
|
|
894
|
+
var AR_HEADER_BYTES = 60;
|
|
895
|
+
/** Every member of an `ar` archive, in order. Throws on anything that is not one. */
|
|
896
|
+
function readArMembers(archive) {
|
|
897
|
+
if (archive.subarray(0, 8).toString("latin1") !== AR_MAGIC) throw new Error("not an ar archive (a .deb starts with \"!<arch>\")");
|
|
898
|
+
const members = [];
|
|
899
|
+
let offset = 8;
|
|
900
|
+
while (offset + AR_HEADER_BYTES <= archive.length) {
|
|
901
|
+
const header = archive.subarray(offset, offset + AR_HEADER_BYTES).toString("latin1");
|
|
902
|
+
if (header.slice(58, 60) !== "`\n") throw new Error(`corrupt ar member header at byte ${offset}`);
|
|
903
|
+
const name = header.slice(0, 16).trim().replace(/\/$/, "");
|
|
904
|
+
const size = Number.parseInt(header.slice(48, 58).trim(), 10);
|
|
905
|
+
if (!Number.isInteger(size) || size < 0) throw new Error(`ar member "${name}" has no valid size`);
|
|
906
|
+
const start = offset + AR_HEADER_BYTES;
|
|
907
|
+
if (start + size > archive.length) throw new Error(`ar member "${name}" runs past the end of the archive`);
|
|
908
|
+
members.push({
|
|
909
|
+
name,
|
|
910
|
+
data: archive.subarray(start, start + size)
|
|
911
|
+
});
|
|
912
|
+
offset = start + size + size % 2;
|
|
913
|
+
}
|
|
914
|
+
return members;
|
|
915
|
+
}
|
|
916
|
+
/** The `data.tar.*` member of a `.deb`, with the compression its name declares. */
|
|
917
|
+
function debDataMember(deb) {
|
|
918
|
+
const member = readArMembers(deb).find((m) => m.name.startsWith("data.tar"));
|
|
919
|
+
if (member === void 0) throw new Error(".deb has no data.tar member");
|
|
920
|
+
const suffix = member.name.slice(8);
|
|
921
|
+
const compression = suffix === ".zst" ? "zst" : suffix === ".xz" ? "xz" : suffix === ".gz" ? "gz" : suffix === "" ? "none" : null;
|
|
922
|
+
if (compression === null) throw new Error(`.deb payload ${member.name} uses a compression this extractor does not read`);
|
|
923
|
+
return {
|
|
924
|
+
compression,
|
|
925
|
+
data: member.data
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Unpack a `.deb`'s payload into `destDir` (the package's own tree: `usr/lib/…`).
|
|
930
|
+
* zstd and gzip are decompressed in-process; the plain tar and the xz payload
|
|
931
|
+
* are expanded by the host `tar`.
|
|
932
|
+
*/
|
|
933
|
+
function extractDebPayload(deb, destDir, workDir, extract = extractArchive) {
|
|
934
|
+
const payload = debDataMember(deb);
|
|
935
|
+
(0, node_fs.mkdirSync)(destDir, { recursive: true });
|
|
936
|
+
(0, node_fs.mkdirSync)(workDir, { recursive: true });
|
|
937
|
+
if (payload.compression === "xz") {
|
|
938
|
+
const xzPath = (0, node_path.join)(workDir, "data.tar.xz");
|
|
939
|
+
(0, node_fs.writeFileSync)(xzPath, payload.data);
|
|
940
|
+
extract(xzPath, "tar.xz", destDir);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const tar = payload.compression === "zst" ? (0, node_zlib.zstdDecompressSync)(payload.data) : payload.compression === "gz" ? (0, node_zlib.gunzipSync)(payload.data) : payload.data;
|
|
944
|
+
const tarPath = (0, node_path.join)(workDir, "data.tar");
|
|
945
|
+
(0, node_fs.writeFileSync)(tarPath, tar);
|
|
946
|
+
extract(tarPath, "tar", destDir);
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* The first entry named exactly `name` under `dir` — a regular file OR a
|
|
950
|
+
* symlink: a library's soname (`libusb-1.0.so.0`) is a link to the versioned
|
|
951
|
+
* file in every package, so a file-only search never finds it.
|
|
952
|
+
*/
|
|
953
|
+
function findEntryByName(dir, name) {
|
|
954
|
+
for (const entry of (0, node_fs.readdirSync)(dir, { withFileTypes: true })) {
|
|
955
|
+
const full = (0, node_path.join)(dir, entry.name);
|
|
956
|
+
if ((entry.isFile() || entry.isSymbolicLink()) && entry.name === name) return full;
|
|
957
|
+
if (entry.isDirectory()) {
|
|
958
|
+
const nested = findEntryByName(full, name);
|
|
959
|
+
if (nested !== null) return nested;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Copy the library `memberName` out of the `.deb` at `debPath` to `targetPath`,
|
|
966
|
+
* dereferencing the soname link to the real file. Throws, naming the member,
|
|
967
|
+
* when the package does not carry it.
|
|
968
|
+
*/
|
|
969
|
+
function installLibraryFromDeb(debPath, memberName, targetPath, workDir, extract = extractArchive) {
|
|
970
|
+
const tree = (0, node_path.join)(workDir, "tree");
|
|
971
|
+
try {
|
|
972
|
+
extractDebPayload((0, node_fs.readFileSync)(debPath), tree, workDir, extract);
|
|
973
|
+
const found = findEntryByName(tree, memberName);
|
|
974
|
+
if (found === null) throw new Error(`${memberName} not found inside ${debPath}`);
|
|
975
|
+
(0, node_fs.copyFileSync)(found, targetPath);
|
|
976
|
+
} finally {
|
|
977
|
+
(0, node_fs.rmSync)(workDir, {
|
|
978
|
+
recursive: true,
|
|
979
|
+
force: true
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
//#endregion
|
|
984
|
+
//#region src/deps/edgetpu-delegate.ts
|
|
985
|
+
/**
|
|
986
|
+
* The Coral Edge TPU delegate (libedgetpu) this node loads, and how the deps
|
|
987
|
+
* downloader stages it.
|
|
988
|
+
*
|
|
989
|
+
* The delegate is a native shared library, not a pip package, and its TF
|
|
990
|
+
* version MUST match the interpreter: ai-edge-litert 2.1.6 pairs with
|
|
991
|
+
* feranick's libedgetpu rebuilt against TF 2.17.1 (release
|
|
992
|
+
* `16.0TF2.17.1-1`); Google's stock 16.0 "grouper" build fails at invoke.
|
|
993
|
+
* Every artifact below was fetched and hashed on 2026-10-22 and inspected:
|
|
994
|
+
*
|
|
995
|
+
* - linux x64 / arm64 — the `libedgetpu1-std` deb for Ubuntu 24.04 (the same
|
|
996
|
+
* build `edgetpu-install.sh` installs on the hub image; it needs glibc
|
|
997
|
+
* 2.38), payload `data.tar.xz`. The std delegate links libusb even for a
|
|
998
|
+
* PCIe Coral, so Ubuntu 24.04's `libusb-1.0-0` deb is staged beside it —
|
|
999
|
+
* payload `data.tar.zst`, which is why `deb-extract.ts` reads zstd itself.
|
|
1000
|
+
* - darwin arm64 — the `MacOS_Silicon` zip, `throttled/` (the std clock, as on
|
|
1001
|
+
* Linux). `otool -L` shows no libusb: it is linked in, nothing else needed.
|
|
1002
|
+
* - darwin x64 — REFUSED: ai-edge-litert 2.1.6 publishes no macOS x86_64
|
|
1003
|
+
* wheel, and the Intel delegate links MacPorts' `/opt/local/lib/libusb`.
|
|
1004
|
+
* - win32 — REFUSED: no TF 2.17.1 libedgetpu exists for Windows; Google's old
|
|
1005
|
+
* Windows delegate is built for a TFLite ai-edge-litert 2.1.6 cannot load.
|
|
1006
|
+
*
|
|
1007
|
+
* Resolution order, mirrored by `edgetpu_device.edgetpu_library_path` in the
|
|
1008
|
+
* Python pool: the `CAMSTACK_EDGETPU_LIB` path override → the copy staged
|
|
1009
|
+
* under `<CAMSTACK_DATA>/deps/edgetpu/lib/` → the hub's historical
|
|
1010
|
+
* `/data/deps/edgetpu/lib/libedgetpu.so.1`. On the hub image
|
|
1011
|
+
* `CAMSTACK_DATA=/data`, so the staged path and the historical one are the
|
|
1012
|
+
* same file and nothing moves.
|
|
1013
|
+
*/
|
|
1014
|
+
/** The path override (a library PATH, never a secret). */
|
|
1015
|
+
var EDGETPU_LIB_ENV = "CAMSTACK_EDGETPU_LIB";
|
|
1016
|
+
/** The pinned feranick release. Changing it is the delegate version change. */
|
|
1017
|
+
var EDGETPU_DELEGATE_RELEASE = "16.0TF2.17.1-1";
|
|
1018
|
+
/** Where the hub image's installer has always put the delegate. */
|
|
1019
|
+
var LEGACY_EDGETPU_LIB = "/data/deps/edgetpu/lib/libedgetpu.so.1";
|
|
1020
|
+
var FERANICK = `https://github.com/feranick/libedgetpu/releases/download/${EDGETPU_DELEGATE_RELEASE}`;
|
|
1021
|
+
function linuxSet(deb, debSha, debSize, libusbUrl, libusbSha, libusbSize) {
|
|
1022
|
+
return {
|
|
1023
|
+
supported: true,
|
|
1024
|
+
files: [{
|
|
1025
|
+
url: `${FERANICK}/${deb}`,
|
|
1026
|
+
sha256: debSha,
|
|
1027
|
+
sizeBytes: debSize,
|
|
1028
|
+
container: "deb",
|
|
1029
|
+
member: "libedgetpu.so.1",
|
|
1030
|
+
installAs: "libedgetpu.so.1"
|
|
1031
|
+
}, {
|
|
1032
|
+
url: libusbUrl,
|
|
1033
|
+
sha256: libusbSha,
|
|
1034
|
+
sizeBytes: libusbSize,
|
|
1035
|
+
container: "deb",
|
|
1036
|
+
member: "libusb-1.0.so.0",
|
|
1037
|
+
installAs: "libusb-1.0.so.0"
|
|
1038
|
+
}]
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
/** The pinned delegate set for a platform and arch, or why there is none. */
|
|
1042
|
+
function getEdgetpuDelegateArtifact(platform, arch) {
|
|
1043
|
+
if (platform === "linux" && arch === "x64") return linuxSet("libedgetpu1-std_16.0tf2.17.1-1.ubuntu24.04_amd64.deb", "3cd98ae42d26a0ab601673d2162eb968921f751d3879ef960687637cad1cba96", 389600, "http://archive.ubuntu.com/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.27-1_amd64.deb", "d59e679a82ee2b724c556397e831e879936adbe8a007705872b65a5e1ec887b5", 54008);
|
|
1044
|
+
if (platform === "linux" && arch === "arm64") return linuxSet("libedgetpu1-std_16.0tf2.17.1-1.ubuntu24.04_arm64.deb", "6d53b6f6f9a19a6685a488e0c262aed2b11b6ff8c953f10e0aa14a16122f0d37", 338416, "http://ports.ubuntu.com/ubuntu-ports/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.27-1_arm64.deb", "c8c34ef4385b6be34fe94aabd4de06c7bbd5ae617e80e9306f412f4c979aa5e4", 54082);
|
|
1045
|
+
if (platform === "darwin" && arch === "arm64") return {
|
|
1046
|
+
supported: true,
|
|
1047
|
+
files: [{
|
|
1048
|
+
url: `${FERANICK}/libedgetpu-16.0-tf2.17.1-1_MacOS_Silicon.zip`,
|
|
1049
|
+
sha256: "5d57b4ff80bfe70d1d9306a3a8a83f52b38e8a151c35aecf68d807744eacf1f4",
|
|
1050
|
+
sizeBytes: 760105,
|
|
1051
|
+
container: "zip",
|
|
1052
|
+
member: "libedgetpu-16.0-tf2.17.1-1_MacOS_Silicon/throttled/darwin_arm64/libedgetpu.1.0.dylib",
|
|
1053
|
+
installAs: "libedgetpu.1.dylib"
|
|
1054
|
+
}]
|
|
1055
|
+
};
|
|
1056
|
+
if (platform === "darwin") return {
|
|
1057
|
+
supported: false,
|
|
1058
|
+
reason: "ai-edge-litert 2.1.6 publishes no macOS x86_64 wheel, and the Intel delegate links MacPorts libusb at /opt/local/lib"
|
|
1059
|
+
};
|
|
1060
|
+
if (platform === "win32") return {
|
|
1061
|
+
supported: false,
|
|
1062
|
+
reason: "no libedgetpu built against TF 2.17.1 exists for Windows (feranick ships Linux and macOS only)"
|
|
1063
|
+
};
|
|
1064
|
+
return {
|
|
1065
|
+
supported: false,
|
|
1066
|
+
reason: `no pinned libedgetpu build for ${platform}-${arch}`
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
/** The delegate's file name on a platform (what the Python pool loads). */
|
|
1070
|
+
function edgetpuDelegateFileName(platform) {
|
|
1071
|
+
return platform === "darwin" ? "libedgetpu.1.dylib" : "libedgetpu.so.1";
|
|
1072
|
+
}
|
|
1073
|
+
/** `<dataDir>/deps/edgetpu/lib` — where the deps downloader stages the set. */
|
|
1074
|
+
function edgetpuLibDir(dataDir) {
|
|
1075
|
+
return (0, node_path.join)(dataDir, "deps", "edgetpu", "lib");
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* The delegate this node loads, or `null` when none is present. An override
|
|
1079
|
+
* that names a missing file is NOT followed past: the operator said where it
|
|
1080
|
+
* is, and a silent fallback would load a different build than they chose.
|
|
1081
|
+
*/
|
|
1082
|
+
function chooseEdgetpuDelegate(lookup) {
|
|
1083
|
+
const override = lookup.override?.trim();
|
|
1084
|
+
if (override !== void 0 && override.length > 0) return lookup.exists(override) ? {
|
|
1085
|
+
path: override,
|
|
1086
|
+
origin: "override"
|
|
1087
|
+
} : null;
|
|
1088
|
+
if (lookup.dataDir !== void 0 && lookup.dataDir.length > 0) {
|
|
1089
|
+
const staged = (0, node_path.join)(edgetpuLibDir(lookup.dataDir), edgetpuDelegateFileName(lookup.platform));
|
|
1090
|
+
if (lookup.exists(staged)) return {
|
|
1091
|
+
path: staged,
|
|
1092
|
+
origin: "staged"
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
if (lookup.exists("/data/deps/edgetpu/lib/libedgetpu.so.1")) return {
|
|
1096
|
+
path: LEGACY_EDGETPU_LIB,
|
|
1097
|
+
origin: "legacy"
|
|
1098
|
+
};
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
/** The node data root the way every node process names it. */
|
|
1102
|
+
function nodeDataRootFromEnv(env) {
|
|
1103
|
+
return env["CAMSTACK_DATA"] ?? env["CAMSTACK_DATA_DIR"];
|
|
1104
|
+
}
|
|
1105
|
+
/** Under 1 MB each: held whole, written once, hashed from disk before use. */
|
|
1106
|
+
async function download(url, target) {
|
|
1107
|
+
const response = await fetch(url, { redirect: "follow" });
|
|
1108
|
+
if (!response.ok) throw new Error(`download failed: ${response.status} ${response.statusText} (${url})`);
|
|
1109
|
+
(0, node_fs.writeFileSync)(target, Buffer.from(await response.arrayBuffer()));
|
|
1110
|
+
}
|
|
1111
|
+
async function stageFile(file, libDir, workRoot) {
|
|
1112
|
+
const target = (0, node_path.join)(libDir, file.installAs);
|
|
1113
|
+
if ((0, node_fs.existsSync)(target)) return;
|
|
1114
|
+
const work = (0, node_path.join)(workRoot, file.installAs);
|
|
1115
|
+
(0, node_fs.rmSync)(work, {
|
|
1116
|
+
recursive: true,
|
|
1117
|
+
force: true
|
|
1118
|
+
});
|
|
1119
|
+
(0, node_fs.mkdirSync)(work, { recursive: true });
|
|
1120
|
+
const container = (0, node_path.join)(work, `download.${file.container}`);
|
|
1121
|
+
const partial = `${target}.partial`;
|
|
1122
|
+
try {
|
|
1123
|
+
await download(file.url, container);
|
|
1124
|
+
verifySha256(container, file.sha256, file.installAs, file.url);
|
|
1125
|
+
if (file.container === "deb") installLibraryFromDeb(container, file.member, partial, (0, node_path.join)(work, "deb"));
|
|
1126
|
+
else {
|
|
1127
|
+
const tree = (0, node_path.join)(work, "tree");
|
|
1128
|
+
(0, node_fs.mkdirSync)(tree, { recursive: true });
|
|
1129
|
+
extractArchive(container, "zip", tree);
|
|
1130
|
+
const inner = (0, node_path.join)(tree, file.member);
|
|
1131
|
+
const found = (0, node_fs.existsSync)(inner) ? inner : findEntryByName(tree, file.member.split("/").pop() ?? file.member);
|
|
1132
|
+
if (found === null) throw new Error(`${file.member} not found inside ${file.url}`);
|
|
1133
|
+
(0, node_fs.copyFileSync)(found, partial);
|
|
1134
|
+
}
|
|
1135
|
+
(0, node_fs.renameSync)(partial, target);
|
|
1136
|
+
} finally {
|
|
1137
|
+
(0, node_fs.rmSync)(work, {
|
|
1138
|
+
recursive: true,
|
|
1139
|
+
force: true
|
|
1140
|
+
});
|
|
1141
|
+
(0, node_fs.rmSync)(partial, { force: true });
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* A requirements file names the native library its packages cannot run
|
|
1146
|
+
* without on this line; `installPythonRequirements` stages it after pip.
|
|
1147
|
+
* (`ai-edge-litert` imports without the delegate and is useless without it.)
|
|
1148
|
+
*/
|
|
1149
|
+
var EDGETPU_DELEGATE_DIRECTIVE = "# camstack-native-dep: edgetpu-delegate";
|
|
1150
|
+
function declaresEdgetpuDelegate(requirementsText) {
|
|
1151
|
+
return requirementsText.split(/\r?\n/).some((line) => line.trim() === EDGETPU_DELEGATE_DIRECTIVE);
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* The delegate this node loads, staging the pinned set under
|
|
1155
|
+
* `<dataDir>/deps/edgetpu/lib/` when nothing is present yet. Never throws for
|
|
1156
|
+
* a platform without a build — it answers `unavailable` with the reason; a
|
|
1157
|
+
* download or checksum failure throws, naming the file. The line it logs says
|
|
1158
|
+
* which delegate (and from where) the pool will load.
|
|
1159
|
+
*/
|
|
1160
|
+
async function ensureEdgetpuDelegate(dataDir, logger, opts = {}) {
|
|
1161
|
+
const env = opts.env ?? process.env;
|
|
1162
|
+
const platform = opts.platform ?? process.platform;
|
|
1163
|
+
const arch = opts.arch ?? process.arch;
|
|
1164
|
+
const override = env[EDGETPU_LIB_ENV]?.trim();
|
|
1165
|
+
const present = chooseEdgetpuDelegate({
|
|
1166
|
+
platform,
|
|
1167
|
+
dataDir,
|
|
1168
|
+
override,
|
|
1169
|
+
exists: node_fs.existsSync
|
|
1170
|
+
});
|
|
1171
|
+
if (present !== null) {
|
|
1172
|
+
logger.info("Coral delegate resolved", { meta: {
|
|
1173
|
+
path: present.path,
|
|
1174
|
+
origin: present.origin
|
|
1175
|
+
} });
|
|
1176
|
+
return {
|
|
1177
|
+
kind: "ready",
|
|
1178
|
+
path: present.path,
|
|
1179
|
+
origin: present.origin
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
if (override !== void 0 && override.length > 0) {
|
|
1183
|
+
const reason = `${EDGETPU_LIB_ENV} names a file that does not exist: ${override}`;
|
|
1184
|
+
logger.error("Coral delegate not staged", { meta: { reason } });
|
|
1185
|
+
return {
|
|
1186
|
+
kind: "unavailable",
|
|
1187
|
+
reason
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
const artifact = getEdgetpuDelegateArtifact(platform, arch);
|
|
1191
|
+
if (!artifact.supported) {
|
|
1192
|
+
logger.warn("Coral delegate not staged", { meta: { reason: artifact.reason } });
|
|
1193
|
+
return {
|
|
1194
|
+
kind: "unavailable",
|
|
1195
|
+
reason: artifact.reason
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
const libDir = edgetpuLibDir(dataDir);
|
|
1199
|
+
(0, node_fs.mkdirSync)(libDir, { recursive: true });
|
|
1200
|
+
logger.info("Coral delegate is not on this node yet — downloading the pinned set", { meta: {
|
|
1201
|
+
release: EDGETPU_DELEGATE_RELEASE,
|
|
1202
|
+
files: artifact.files.map((f) => f.installAs).join(",")
|
|
1203
|
+
} });
|
|
1204
|
+
const workRoot = (0, node_path.join)(dataDir, "deps", ".edgetpu-staging");
|
|
1205
|
+
try {
|
|
1206
|
+
for (const file of artifact.files) await stageFile(file, libDir, workRoot);
|
|
1207
|
+
} finally {
|
|
1208
|
+
(0, node_fs.rmSync)(workRoot, {
|
|
1209
|
+
recursive: true,
|
|
1210
|
+
force: true
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
const path = (0, node_path.join)(libDir, edgetpuDelegateFileName(platform));
|
|
1214
|
+
logger.info("Coral delegate resolved", { meta: {
|
|
1215
|
+
path,
|
|
1216
|
+
origin: "downloaded"
|
|
1217
|
+
} });
|
|
1218
|
+
return {
|
|
1219
|
+
kind: "ready",
|
|
1220
|
+
path,
|
|
1221
|
+
origin: "downloaded"
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
//#endregion
|
|
876
1225
|
//#region src/ffmpeg/fmp4-fragment-child.ts
|
|
877
1226
|
var DEFAULT_FIRST_UNIT_TIMEOUT_MS = 12e3;
|
|
878
1227
|
/** Heartbeat cadence — ~2 minutes of 4 s fragments. */
|
|
@@ -2274,11 +2623,15 @@ function resolveExportFingerprint(input) {
|
|
|
2274
2623
|
}
|
|
2275
2624
|
//#endregion
|
|
2276
2625
|
exports.ChildCostRegistry = ChildCostRegistry;
|
|
2626
|
+
exports.EDGETPU_DELEGATE_DIRECTIVE = EDGETPU_DELEGATE_DIRECTIVE;
|
|
2627
|
+
exports.EDGETPU_DELEGATE_RELEASE = EDGETPU_DELEGATE_RELEASE;
|
|
2628
|
+
exports.EDGETPU_LIB_ENV = EDGETPU_LIB_ENV;
|
|
2277
2629
|
exports.FFMPEG_RELEASE = require_composition.FFMPEG_RELEASE;
|
|
2278
2630
|
exports.FfmpegProcess = FfmpegProcess;
|
|
2279
2631
|
exports.FilesystemStorageProvider = FilesystemStorageProvider;
|
|
2280
2632
|
exports.Fmp4FragmentChild = Fmp4FragmentChild;
|
|
2281
2633
|
exports.Fmp4FragmentPlane = Fmp4FragmentPlane;
|
|
2634
|
+
exports.LEGACY_EDGETPU_LIB = LEGACY_EDGETPU_LIB;
|
|
2282
2635
|
exports.NO_COST_CLAIM = NO_COST_CLAIM;
|
|
2283
2636
|
exports.NO_PINNED_FFMPEG_DETAIL = NO_PINNED_FFMPEG_DETAIL;
|
|
2284
2637
|
exports.OwnerOnlyError = OwnerOnlyError;
|
|
@@ -2286,23 +2639,33 @@ exports.PYTHON_VERSION = PYTHON_VERSION;
|
|
|
2286
2639
|
exports.buildBinaryPath = buildBinaryPath;
|
|
2287
2640
|
exports.canonicalDeviceFingerprint = canonicalDeviceFingerprint;
|
|
2288
2641
|
exports.canonicalHash = require_composition.canonicalHash;
|
|
2642
|
+
exports.chooseEdgetpuDelegate = chooseEdgetpuDelegate;
|
|
2289
2643
|
exports.containsOrEquals = containsOrEquals;
|
|
2290
2644
|
exports.customizationBlockName = customizationBlockName;
|
|
2645
|
+
exports.debDataMember = debDataMember;
|
|
2646
|
+
exports.declaresEdgetpuDelegate = declaresEdgetpuDelegate;
|
|
2291
2647
|
exports.defaultFfmpegSpawn = defaultFfmpegSpawn;
|
|
2292
2648
|
exports.diffExportTargets = diffExportTargets;
|
|
2293
2649
|
exports.downloadBinary = downloadBinary;
|
|
2650
|
+
exports.edgetpuDelegateFileName = edgetpuDelegateFileName;
|
|
2651
|
+
exports.edgetpuLibDir = edgetpuLibDir;
|
|
2294
2652
|
exports.ensureBinary = ensureBinary;
|
|
2653
|
+
exports.ensureEdgetpuDelegate = ensureEdgetpuDelegate;
|
|
2295
2654
|
exports.ensureFfmpeg = ensureFfmpeg;
|
|
2296
2655
|
exports.ensurePython = ensurePython;
|
|
2656
|
+
exports.extractDebPayload = extractDebPayload;
|
|
2297
2657
|
exports.findInPath = findInPath;
|
|
2658
|
+
exports.getEdgetpuDelegateArtifact = getEdgetpuDelegateArtifact;
|
|
2298
2659
|
exports.getFfmpegArtifact = require_composition.getFfmpegArtifact;
|
|
2299
2660
|
exports.getFfmpegDownloadUrl = require_composition.getFfmpegDownloadUrl;
|
|
2300
2661
|
exports.getPlatformInfo = getPlatformInfo;
|
|
2301
2662
|
exports.getPythonDownloadUrl = getPythonDownloadUrl;
|
|
2302
2663
|
exports.icaclsArgs = icaclsArgs;
|
|
2664
|
+
exports.installLibraryFromDeb = installLibraryFromDeb;
|
|
2303
2665
|
exports.installPythonPackages = installPythonPackages;
|
|
2304
2666
|
exports.installPythonRequirements = installPythonRequirements;
|
|
2305
2667
|
exports.mkdirOwnerOnlySync = mkdirOwnerOnlySync;
|
|
2668
|
+
exports.nodeDataRootFromEnv = nodeDataRootFromEnv;
|
|
2306
2669
|
exports.nodeProcStatReader = nodeProcStatReader;
|
|
2307
2670
|
exports.parseFfmpegCapabilities = parseFfmpegCapabilities;
|
|
2308
2671
|
exports.parseProcCpuSeconds = parseProcCpuSeconds;
|
|
@@ -2310,6 +2673,7 @@ exports.parseProcRssBytes = parseProcRssBytes;
|
|
|
2310
2673
|
exports.physicalRootOf = physicalRootOf;
|
|
2311
2674
|
exports.pinnedFfmpegSpawner = pinnedFfmpegSpawner;
|
|
2312
2675
|
exports.probeFfmpegBinary = probeFfmpegBinary;
|
|
2676
|
+
exports.readArMembers = readArMembers;
|
|
2313
2677
|
exports.readProcessCost = readProcessCost;
|
|
2314
2678
|
exports.resolveExportFingerprint = resolveExportFingerprint;
|
|
2315
2679
|
exports.resolvePinnedFfmpegSpawner = resolvePinnedFfmpegSpawner;
|
package/dist/node.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import { At as Fmp4BoxSplitter, Ct as FFMPEG_PATH_ENV, Dt as getFfmpegArtifact,
|
|
|
2
2
|
import { t as errMsg } from "./err-msg-BXKzRBwg.mjs";
|
|
3
3
|
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
-
import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { chmodSync, copyFileSync, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import * as path$1 from "node:path";
|
|
7
7
|
import path, { basename, dirname, join, resolve, sep } from "node:path";
|
|
8
8
|
import { pipeline } from "node:stream/promises";
|
|
9
9
|
import { Readable } from "node:stream";
|
|
10
10
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
11
|
-
import { crc32, inflateRawSync } from "node:zlib";
|
|
11
|
+
import { crc32, gunzipSync, inflateRawSync, zstdDecompressSync } from "node:zlib";
|
|
12
12
|
import { userInfo } from "node:os";
|
|
13
13
|
import { readFile } from "node:fs/promises";
|
|
14
14
|
//#region src/deps/zip-extract.ts
|
|
@@ -155,8 +155,9 @@ function extractArchive(archivePath, format, destDir, overrides = {}) {
|
|
|
155
155
|
}
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
|
+
const flags = format === "tar.xz" ? "-xJf" : format === "tar" ? "-xf" : "-xzf";
|
|
158
159
|
deps.exec(tar, [
|
|
159
|
-
|
|
160
|
+
flags,
|
|
160
161
|
archivePath,
|
|
161
162
|
"-C",
|
|
162
163
|
destDir
|
|
@@ -850,6 +851,354 @@ async function installPythonRequirements(pythonPath, requirementsFile, logger) {
|
|
|
850
851
|
} });
|
|
851
852
|
}
|
|
852
853
|
//#endregion
|
|
854
|
+
//#region src/deps/deb-extract.ts
|
|
855
|
+
/**
|
|
856
|
+
* Pull one shared library out of a Debian package without `dpkg-deb`.
|
|
857
|
+
*
|
|
858
|
+
* A `.deb` is an `ar` archive holding `debian-binary`, `control.tar.*` and
|
|
859
|
+
* `data.tar.*`; the payload's compression is the packager's choice. The two
|
|
860
|
+
* debs the Coral delegate needs show both live ones: feranick's libedgetpu
|
|
861
|
+
* ships `data.tar.xz`, Ubuntu's libusb (24.04 and every release since 21.10)
|
|
862
|
+
* ships `data.tar.zst` — which `dpkg-deb` older than 1.21.18 and a stock
|
|
863
|
+
* macOS `tar` without a `zstd` binary cannot open. So the ar container and a
|
|
864
|
+
* zstd/gzip payload are unpacked IN-PROCESS (`node:zlib` has zstd since Node
|
|
865
|
+
* 22.15, which the hub and the desktop's Electron both run); only an xz
|
|
866
|
+
* payload goes through `tar -xJf`, which every POSIX host has.
|
|
867
|
+
*
|
|
868
|
+
* POSIX only: the delegate is never staged on Windows (no build exists).
|
|
869
|
+
*/
|
|
870
|
+
var AR_MAGIC = "!<arch>\n";
|
|
871
|
+
var AR_HEADER_BYTES = 60;
|
|
872
|
+
/** Every member of an `ar` archive, in order. Throws on anything that is not one. */
|
|
873
|
+
function readArMembers(archive) {
|
|
874
|
+
if (archive.subarray(0, 8).toString("latin1") !== AR_MAGIC) throw new Error("not an ar archive (a .deb starts with \"!<arch>\")");
|
|
875
|
+
const members = [];
|
|
876
|
+
let offset = 8;
|
|
877
|
+
while (offset + AR_HEADER_BYTES <= archive.length) {
|
|
878
|
+
const header = archive.subarray(offset, offset + AR_HEADER_BYTES).toString("latin1");
|
|
879
|
+
if (header.slice(58, 60) !== "`\n") throw new Error(`corrupt ar member header at byte ${offset}`);
|
|
880
|
+
const name = header.slice(0, 16).trim().replace(/\/$/, "");
|
|
881
|
+
const size = Number.parseInt(header.slice(48, 58).trim(), 10);
|
|
882
|
+
if (!Number.isInteger(size) || size < 0) throw new Error(`ar member "${name}" has no valid size`);
|
|
883
|
+
const start = offset + AR_HEADER_BYTES;
|
|
884
|
+
if (start + size > archive.length) throw new Error(`ar member "${name}" runs past the end of the archive`);
|
|
885
|
+
members.push({
|
|
886
|
+
name,
|
|
887
|
+
data: archive.subarray(start, start + size)
|
|
888
|
+
});
|
|
889
|
+
offset = start + size + size % 2;
|
|
890
|
+
}
|
|
891
|
+
return members;
|
|
892
|
+
}
|
|
893
|
+
/** The `data.tar.*` member of a `.deb`, with the compression its name declares. */
|
|
894
|
+
function debDataMember(deb) {
|
|
895
|
+
const member = readArMembers(deb).find((m) => m.name.startsWith("data.tar"));
|
|
896
|
+
if (member === void 0) throw new Error(".deb has no data.tar member");
|
|
897
|
+
const suffix = member.name.slice(8);
|
|
898
|
+
const compression = suffix === ".zst" ? "zst" : suffix === ".xz" ? "xz" : suffix === ".gz" ? "gz" : suffix === "" ? "none" : null;
|
|
899
|
+
if (compression === null) throw new Error(`.deb payload ${member.name} uses a compression this extractor does not read`);
|
|
900
|
+
return {
|
|
901
|
+
compression,
|
|
902
|
+
data: member.data
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Unpack a `.deb`'s payload into `destDir` (the package's own tree: `usr/lib/…`).
|
|
907
|
+
* zstd and gzip are decompressed in-process; the plain tar and the xz payload
|
|
908
|
+
* are expanded by the host `tar`.
|
|
909
|
+
*/
|
|
910
|
+
function extractDebPayload(deb, destDir, workDir, extract = extractArchive) {
|
|
911
|
+
const payload = debDataMember(deb);
|
|
912
|
+
mkdirSync(destDir, { recursive: true });
|
|
913
|
+
mkdirSync(workDir, { recursive: true });
|
|
914
|
+
if (payload.compression === "xz") {
|
|
915
|
+
const xzPath = join(workDir, "data.tar.xz");
|
|
916
|
+
writeFileSync(xzPath, payload.data);
|
|
917
|
+
extract(xzPath, "tar.xz", destDir);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const tar = payload.compression === "zst" ? zstdDecompressSync(payload.data) : payload.compression === "gz" ? gunzipSync(payload.data) : payload.data;
|
|
921
|
+
const tarPath = join(workDir, "data.tar");
|
|
922
|
+
writeFileSync(tarPath, tar);
|
|
923
|
+
extract(tarPath, "tar", destDir);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* The first entry named exactly `name` under `dir` — a regular file OR a
|
|
927
|
+
* symlink: a library's soname (`libusb-1.0.so.0`) is a link to the versioned
|
|
928
|
+
* file in every package, so a file-only search never finds it.
|
|
929
|
+
*/
|
|
930
|
+
function findEntryByName(dir, name) {
|
|
931
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
932
|
+
const full = join(dir, entry.name);
|
|
933
|
+
if ((entry.isFile() || entry.isSymbolicLink()) && entry.name === name) return full;
|
|
934
|
+
if (entry.isDirectory()) {
|
|
935
|
+
const nested = findEntryByName(full, name);
|
|
936
|
+
if (nested !== null) return nested;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Copy the library `memberName` out of the `.deb` at `debPath` to `targetPath`,
|
|
943
|
+
* dereferencing the soname link to the real file. Throws, naming the member,
|
|
944
|
+
* when the package does not carry it.
|
|
945
|
+
*/
|
|
946
|
+
function installLibraryFromDeb(debPath, memberName, targetPath, workDir, extract = extractArchive) {
|
|
947
|
+
const tree = join(workDir, "tree");
|
|
948
|
+
try {
|
|
949
|
+
extractDebPayload(readFileSync(debPath), tree, workDir, extract);
|
|
950
|
+
const found = findEntryByName(tree, memberName);
|
|
951
|
+
if (found === null) throw new Error(`${memberName} not found inside ${debPath}`);
|
|
952
|
+
copyFileSync(found, targetPath);
|
|
953
|
+
} finally {
|
|
954
|
+
rmSync(workDir, {
|
|
955
|
+
recursive: true,
|
|
956
|
+
force: true
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region src/deps/edgetpu-delegate.ts
|
|
962
|
+
/**
|
|
963
|
+
* The Coral Edge TPU delegate (libedgetpu) this node loads, and how the deps
|
|
964
|
+
* downloader stages it.
|
|
965
|
+
*
|
|
966
|
+
* The delegate is a native shared library, not a pip package, and its TF
|
|
967
|
+
* version MUST match the interpreter: ai-edge-litert 2.1.6 pairs with
|
|
968
|
+
* feranick's libedgetpu rebuilt against TF 2.17.1 (release
|
|
969
|
+
* `16.0TF2.17.1-1`); Google's stock 16.0 "grouper" build fails at invoke.
|
|
970
|
+
* Every artifact below was fetched and hashed on 2026-10-22 and inspected:
|
|
971
|
+
*
|
|
972
|
+
* - linux x64 / arm64 — the `libedgetpu1-std` deb for Ubuntu 24.04 (the same
|
|
973
|
+
* build `edgetpu-install.sh` installs on the hub image; it needs glibc
|
|
974
|
+
* 2.38), payload `data.tar.xz`. The std delegate links libusb even for a
|
|
975
|
+
* PCIe Coral, so Ubuntu 24.04's `libusb-1.0-0` deb is staged beside it —
|
|
976
|
+
* payload `data.tar.zst`, which is why `deb-extract.ts` reads zstd itself.
|
|
977
|
+
* - darwin arm64 — the `MacOS_Silicon` zip, `throttled/` (the std clock, as on
|
|
978
|
+
* Linux). `otool -L` shows no libusb: it is linked in, nothing else needed.
|
|
979
|
+
* - darwin x64 — REFUSED: ai-edge-litert 2.1.6 publishes no macOS x86_64
|
|
980
|
+
* wheel, and the Intel delegate links MacPorts' `/opt/local/lib/libusb`.
|
|
981
|
+
* - win32 — REFUSED: no TF 2.17.1 libedgetpu exists for Windows; Google's old
|
|
982
|
+
* Windows delegate is built for a TFLite ai-edge-litert 2.1.6 cannot load.
|
|
983
|
+
*
|
|
984
|
+
* Resolution order, mirrored by `edgetpu_device.edgetpu_library_path` in the
|
|
985
|
+
* Python pool: the `CAMSTACK_EDGETPU_LIB` path override → the copy staged
|
|
986
|
+
* under `<CAMSTACK_DATA>/deps/edgetpu/lib/` → the hub's historical
|
|
987
|
+
* `/data/deps/edgetpu/lib/libedgetpu.so.1`. On the hub image
|
|
988
|
+
* `CAMSTACK_DATA=/data`, so the staged path and the historical one are the
|
|
989
|
+
* same file and nothing moves.
|
|
990
|
+
*/
|
|
991
|
+
/** The path override (a library PATH, never a secret). */
|
|
992
|
+
var EDGETPU_LIB_ENV = "CAMSTACK_EDGETPU_LIB";
|
|
993
|
+
/** The pinned feranick release. Changing it is the delegate version change. */
|
|
994
|
+
var EDGETPU_DELEGATE_RELEASE = "16.0TF2.17.1-1";
|
|
995
|
+
/** Where the hub image's installer has always put the delegate. */
|
|
996
|
+
var LEGACY_EDGETPU_LIB = "/data/deps/edgetpu/lib/libedgetpu.so.1";
|
|
997
|
+
var FERANICK = `https://github.com/feranick/libedgetpu/releases/download/${EDGETPU_DELEGATE_RELEASE}`;
|
|
998
|
+
function linuxSet(deb, debSha, debSize, libusbUrl, libusbSha, libusbSize) {
|
|
999
|
+
return {
|
|
1000
|
+
supported: true,
|
|
1001
|
+
files: [{
|
|
1002
|
+
url: `${FERANICK}/${deb}`,
|
|
1003
|
+
sha256: debSha,
|
|
1004
|
+
sizeBytes: debSize,
|
|
1005
|
+
container: "deb",
|
|
1006
|
+
member: "libedgetpu.so.1",
|
|
1007
|
+
installAs: "libedgetpu.so.1"
|
|
1008
|
+
}, {
|
|
1009
|
+
url: libusbUrl,
|
|
1010
|
+
sha256: libusbSha,
|
|
1011
|
+
sizeBytes: libusbSize,
|
|
1012
|
+
container: "deb",
|
|
1013
|
+
member: "libusb-1.0.so.0",
|
|
1014
|
+
installAs: "libusb-1.0.so.0"
|
|
1015
|
+
}]
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
/** The pinned delegate set for a platform and arch, or why there is none. */
|
|
1019
|
+
function getEdgetpuDelegateArtifact(platform, arch) {
|
|
1020
|
+
if (platform === "linux" && arch === "x64") return linuxSet("libedgetpu1-std_16.0tf2.17.1-1.ubuntu24.04_amd64.deb", "3cd98ae42d26a0ab601673d2162eb968921f751d3879ef960687637cad1cba96", 389600, "http://archive.ubuntu.com/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.27-1_amd64.deb", "d59e679a82ee2b724c556397e831e879936adbe8a007705872b65a5e1ec887b5", 54008);
|
|
1021
|
+
if (platform === "linux" && arch === "arm64") return linuxSet("libedgetpu1-std_16.0tf2.17.1-1.ubuntu24.04_arm64.deb", "6d53b6f6f9a19a6685a488e0c262aed2b11b6ff8c953f10e0aa14a16122f0d37", 338416, "http://ports.ubuntu.com/ubuntu-ports/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.27-1_arm64.deb", "c8c34ef4385b6be34fe94aabd4de06c7bbd5ae617e80e9306f412f4c979aa5e4", 54082);
|
|
1022
|
+
if (platform === "darwin" && arch === "arm64") return {
|
|
1023
|
+
supported: true,
|
|
1024
|
+
files: [{
|
|
1025
|
+
url: `${FERANICK}/libedgetpu-16.0-tf2.17.1-1_MacOS_Silicon.zip`,
|
|
1026
|
+
sha256: "5d57b4ff80bfe70d1d9306a3a8a83f52b38e8a151c35aecf68d807744eacf1f4",
|
|
1027
|
+
sizeBytes: 760105,
|
|
1028
|
+
container: "zip",
|
|
1029
|
+
member: "libedgetpu-16.0-tf2.17.1-1_MacOS_Silicon/throttled/darwin_arm64/libedgetpu.1.0.dylib",
|
|
1030
|
+
installAs: "libedgetpu.1.dylib"
|
|
1031
|
+
}]
|
|
1032
|
+
};
|
|
1033
|
+
if (platform === "darwin") return {
|
|
1034
|
+
supported: false,
|
|
1035
|
+
reason: "ai-edge-litert 2.1.6 publishes no macOS x86_64 wheel, and the Intel delegate links MacPorts libusb at /opt/local/lib"
|
|
1036
|
+
};
|
|
1037
|
+
if (platform === "win32") return {
|
|
1038
|
+
supported: false,
|
|
1039
|
+
reason: "no libedgetpu built against TF 2.17.1 exists for Windows (feranick ships Linux and macOS only)"
|
|
1040
|
+
};
|
|
1041
|
+
return {
|
|
1042
|
+
supported: false,
|
|
1043
|
+
reason: `no pinned libedgetpu build for ${platform}-${arch}`
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
/** The delegate's file name on a platform (what the Python pool loads). */
|
|
1047
|
+
function edgetpuDelegateFileName(platform) {
|
|
1048
|
+
return platform === "darwin" ? "libedgetpu.1.dylib" : "libedgetpu.so.1";
|
|
1049
|
+
}
|
|
1050
|
+
/** `<dataDir>/deps/edgetpu/lib` — where the deps downloader stages the set. */
|
|
1051
|
+
function edgetpuLibDir(dataDir) {
|
|
1052
|
+
return join(dataDir, "deps", "edgetpu", "lib");
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* The delegate this node loads, or `null` when none is present. An override
|
|
1056
|
+
* that names a missing file is NOT followed past: the operator said where it
|
|
1057
|
+
* is, and a silent fallback would load a different build than they chose.
|
|
1058
|
+
*/
|
|
1059
|
+
function chooseEdgetpuDelegate(lookup) {
|
|
1060
|
+
const override = lookup.override?.trim();
|
|
1061
|
+
if (override !== void 0 && override.length > 0) return lookup.exists(override) ? {
|
|
1062
|
+
path: override,
|
|
1063
|
+
origin: "override"
|
|
1064
|
+
} : null;
|
|
1065
|
+
if (lookup.dataDir !== void 0 && lookup.dataDir.length > 0) {
|
|
1066
|
+
const staged = join(edgetpuLibDir(lookup.dataDir), edgetpuDelegateFileName(lookup.platform));
|
|
1067
|
+
if (lookup.exists(staged)) return {
|
|
1068
|
+
path: staged,
|
|
1069
|
+
origin: "staged"
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
if (lookup.exists("/data/deps/edgetpu/lib/libedgetpu.so.1")) return {
|
|
1073
|
+
path: LEGACY_EDGETPU_LIB,
|
|
1074
|
+
origin: "legacy"
|
|
1075
|
+
};
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
/** The node data root the way every node process names it. */
|
|
1079
|
+
function nodeDataRootFromEnv(env) {
|
|
1080
|
+
return env["CAMSTACK_DATA"] ?? env["CAMSTACK_DATA_DIR"];
|
|
1081
|
+
}
|
|
1082
|
+
/** Under 1 MB each: held whole, written once, hashed from disk before use. */
|
|
1083
|
+
async function download(url, target) {
|
|
1084
|
+
const response = await fetch(url, { redirect: "follow" });
|
|
1085
|
+
if (!response.ok) throw new Error(`download failed: ${response.status} ${response.statusText} (${url})`);
|
|
1086
|
+
writeFileSync(target, Buffer.from(await response.arrayBuffer()));
|
|
1087
|
+
}
|
|
1088
|
+
async function stageFile(file, libDir, workRoot) {
|
|
1089
|
+
const target = join(libDir, file.installAs);
|
|
1090
|
+
if (existsSync(target)) return;
|
|
1091
|
+
const work = join(workRoot, file.installAs);
|
|
1092
|
+
rmSync(work, {
|
|
1093
|
+
recursive: true,
|
|
1094
|
+
force: true
|
|
1095
|
+
});
|
|
1096
|
+
mkdirSync(work, { recursive: true });
|
|
1097
|
+
const container = join(work, `download.${file.container}`);
|
|
1098
|
+
const partial = `${target}.partial`;
|
|
1099
|
+
try {
|
|
1100
|
+
await download(file.url, container);
|
|
1101
|
+
verifySha256(container, file.sha256, file.installAs, file.url);
|
|
1102
|
+
if (file.container === "deb") installLibraryFromDeb(container, file.member, partial, join(work, "deb"));
|
|
1103
|
+
else {
|
|
1104
|
+
const tree = join(work, "tree");
|
|
1105
|
+
mkdirSync(tree, { recursive: true });
|
|
1106
|
+
extractArchive(container, "zip", tree);
|
|
1107
|
+
const inner = join(tree, file.member);
|
|
1108
|
+
const found = existsSync(inner) ? inner : findEntryByName(tree, file.member.split("/").pop() ?? file.member);
|
|
1109
|
+
if (found === null) throw new Error(`${file.member} not found inside ${file.url}`);
|
|
1110
|
+
copyFileSync(found, partial);
|
|
1111
|
+
}
|
|
1112
|
+
renameSync(partial, target);
|
|
1113
|
+
} finally {
|
|
1114
|
+
rmSync(work, {
|
|
1115
|
+
recursive: true,
|
|
1116
|
+
force: true
|
|
1117
|
+
});
|
|
1118
|
+
rmSync(partial, { force: true });
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* A requirements file names the native library its packages cannot run
|
|
1123
|
+
* without on this line; `installPythonRequirements` stages it after pip.
|
|
1124
|
+
* (`ai-edge-litert` imports without the delegate and is useless without it.)
|
|
1125
|
+
*/
|
|
1126
|
+
var EDGETPU_DELEGATE_DIRECTIVE = "# camstack-native-dep: edgetpu-delegate";
|
|
1127
|
+
function declaresEdgetpuDelegate(requirementsText) {
|
|
1128
|
+
return requirementsText.split(/\r?\n/).some((line) => line.trim() === EDGETPU_DELEGATE_DIRECTIVE);
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* The delegate this node loads, staging the pinned set under
|
|
1132
|
+
* `<dataDir>/deps/edgetpu/lib/` when nothing is present yet. Never throws for
|
|
1133
|
+
* a platform without a build — it answers `unavailable` with the reason; a
|
|
1134
|
+
* download or checksum failure throws, naming the file. The line it logs says
|
|
1135
|
+
* which delegate (and from where) the pool will load.
|
|
1136
|
+
*/
|
|
1137
|
+
async function ensureEdgetpuDelegate(dataDir, logger, opts = {}) {
|
|
1138
|
+
const env = opts.env ?? process.env;
|
|
1139
|
+
const platform = opts.platform ?? process.platform;
|
|
1140
|
+
const arch = opts.arch ?? process.arch;
|
|
1141
|
+
const override = env[EDGETPU_LIB_ENV]?.trim();
|
|
1142
|
+
const present = chooseEdgetpuDelegate({
|
|
1143
|
+
platform,
|
|
1144
|
+
dataDir,
|
|
1145
|
+
override,
|
|
1146
|
+
exists: existsSync
|
|
1147
|
+
});
|
|
1148
|
+
if (present !== null) {
|
|
1149
|
+
logger.info("Coral delegate resolved", { meta: {
|
|
1150
|
+
path: present.path,
|
|
1151
|
+
origin: present.origin
|
|
1152
|
+
} });
|
|
1153
|
+
return {
|
|
1154
|
+
kind: "ready",
|
|
1155
|
+
path: present.path,
|
|
1156
|
+
origin: present.origin
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
if (override !== void 0 && override.length > 0) {
|
|
1160
|
+
const reason = `${EDGETPU_LIB_ENV} names a file that does not exist: ${override}`;
|
|
1161
|
+
logger.error("Coral delegate not staged", { meta: { reason } });
|
|
1162
|
+
return {
|
|
1163
|
+
kind: "unavailable",
|
|
1164
|
+
reason
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
const artifact = getEdgetpuDelegateArtifact(platform, arch);
|
|
1168
|
+
if (!artifact.supported) {
|
|
1169
|
+
logger.warn("Coral delegate not staged", { meta: { reason: artifact.reason } });
|
|
1170
|
+
return {
|
|
1171
|
+
kind: "unavailable",
|
|
1172
|
+
reason: artifact.reason
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
const libDir = edgetpuLibDir(dataDir);
|
|
1176
|
+
mkdirSync(libDir, { recursive: true });
|
|
1177
|
+
logger.info("Coral delegate is not on this node yet — downloading the pinned set", { meta: {
|
|
1178
|
+
release: EDGETPU_DELEGATE_RELEASE,
|
|
1179
|
+
files: artifact.files.map((f) => f.installAs).join(",")
|
|
1180
|
+
} });
|
|
1181
|
+
const workRoot = join(dataDir, "deps", ".edgetpu-staging");
|
|
1182
|
+
try {
|
|
1183
|
+
for (const file of artifact.files) await stageFile(file, libDir, workRoot);
|
|
1184
|
+
} finally {
|
|
1185
|
+
rmSync(workRoot, {
|
|
1186
|
+
recursive: true,
|
|
1187
|
+
force: true
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
const path = join(libDir, edgetpuDelegateFileName(platform));
|
|
1191
|
+
logger.info("Coral delegate resolved", { meta: {
|
|
1192
|
+
path,
|
|
1193
|
+
origin: "downloaded"
|
|
1194
|
+
} });
|
|
1195
|
+
return {
|
|
1196
|
+
kind: "ready",
|
|
1197
|
+
path,
|
|
1198
|
+
origin: "downloaded"
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
//#endregion
|
|
853
1202
|
//#region src/ffmpeg/fmp4-fragment-child.ts
|
|
854
1203
|
var DEFAULT_FIRST_UNIT_TIMEOUT_MS = 12e3;
|
|
855
1204
|
/** Heartbeat cadence — ~2 minutes of 4 s fragments. */
|
|
@@ -2250,4 +2599,4 @@ function resolveExportFingerprint(input) {
|
|
|
2250
2599
|
return input.persisted ?? input.fresh;
|
|
2251
2600
|
}
|
|
2252
2601
|
//#endregion
|
|
2253
|
-
export { ChildCostRegistry, FFMPEG_RELEASE, FfmpegProcess, FilesystemStorageProvider, Fmp4FragmentChild, Fmp4FragmentPlane, NO_COST_CLAIM, NO_PINNED_FFMPEG_DETAIL, OwnerOnlyError, PYTHON_VERSION, buildBinaryPath, canonicalDeviceFingerprint, canonicalHash, containsOrEquals, customizationBlockName, defaultFfmpegSpawn, diffExportTargets, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegArtifact, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, icaclsArgs, installPythonPackages, installPythonRequirements, mkdirOwnerOnlySync, nodeProcStatReader, parseFfmpegCapabilities, parseProcCpuSeconds, parseProcRssBytes, physicalRootOf, pinnedFfmpegSpawner, probeFfmpegBinary, readProcessCost, resolveExportFingerprint, resolvePinnedFfmpegSpawner, restrictDirToOwnerSync, restrictFileToOwnerSync, signExpiringUrl, verifyExpiringUrl, writeFileAtomic, writeOwnerOnlyFileSync };
|
|
2602
|
+
export { ChildCostRegistry, EDGETPU_DELEGATE_DIRECTIVE, EDGETPU_DELEGATE_RELEASE, EDGETPU_LIB_ENV, FFMPEG_RELEASE, FfmpegProcess, FilesystemStorageProvider, Fmp4FragmentChild, Fmp4FragmentPlane, LEGACY_EDGETPU_LIB, NO_COST_CLAIM, NO_PINNED_FFMPEG_DETAIL, OwnerOnlyError, PYTHON_VERSION, buildBinaryPath, canonicalDeviceFingerprint, canonicalHash, chooseEdgetpuDelegate, containsOrEquals, customizationBlockName, debDataMember, declaresEdgetpuDelegate, defaultFfmpegSpawn, diffExportTargets, downloadBinary, edgetpuDelegateFileName, edgetpuLibDir, ensureBinary, ensureEdgetpuDelegate, ensureFfmpeg, ensurePython, extractDebPayload, findInPath, getEdgetpuDelegateArtifact, getFfmpegArtifact, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, icaclsArgs, installLibraryFromDeb, installPythonPackages, installPythonRequirements, mkdirOwnerOnlySync, nodeDataRootFromEnv, nodeProcStatReader, parseFfmpegCapabilities, parseProcCpuSeconds, parseProcRssBytes, physicalRootOf, pinnedFfmpegSpawner, probeFfmpegBinary, readArMembers, readProcessCost, resolveExportFingerprint, resolvePinnedFfmpegSpawner, restrictDirToOwnerSync, restrictFileToOwnerSync, signExpiringUrl, verifyExpiringUrl, writeFileAtomic, writeOwnerOnlyFileSync };
|