@hypit/hypit 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/public/hyperframes.d.ts +19 -2
- package/package.json +3 -1
- package/packages/caption/src/temporalize.ts +11 -1
- package/packages/caption-fine/README.md +4 -4
- package/packages/credential-store-file/README.md +4 -0
- package/packages/credential-store-file/src/store.ts +2 -2
- package/packages/credential-store-os/runtime/windows-credential.ps1 +3 -0
- package/packages/credential-store-os/src/windows.ts +14 -0
- package/packages/credential-store-platform/README.md +2 -0
- package/packages/hyperframes/README.md +7 -0
- package/packages/hyperframes/src/html-project.ts +99 -0
- package/packages/hyperframes/src/index.ts +2 -0
- package/packages/hyperframes/src/project.ts +28 -0
- package/packages/media-execution/src/execute.ts +21 -3
- package/packages/pixverse/README.md +35 -0
- package/packages/pixverse/package.json +21 -0
- package/packages/pixverse/src/activation.ts +11 -0
- package/packages/pixverse/src/index.ts +122 -0
- package/packages/pixverse/src/surface.ts +146 -0
- package/packages/provider-hiapi/README.md +9 -1
- package/packages/provider-hyperframes-local/README.md +19 -7
- package/packages/provider-hyperframes-local/src/capture.ts +18 -8
- package/packages/provider-hyperframes-local/src/index.ts +1 -1
- package/packages/provider-hyperframes-local/src/output.ts +1 -1
- package/packages/provider-hyperframes-local/src/process-tree.ts +8 -2
- package/packages/provider-hyperframes-local/src/provider.ts +29 -2
- package/packages/provider-hyperframes-local/src/render.ts +45 -20
- package/packages/provider-hyperframes-local/src/sampling.ts +3 -2
- package/packages/provider-hypihub/README.md +10 -2
- package/packages/provider-monid/README.md +29 -9
- package/packages/provider-monid/package.json +2 -1
- package/packages/provider-monid/src/mapping.ts +24 -0
- package/packages/provider-monid/src/provider.ts +24 -11
- package/packages/provider-monid/src/routes.ts +25 -3
- package/packages/provider-pollo/README.md +9 -1
- package/packages/provider-tokendance/README.md +9 -1
- package/packages/provider-whisperx-local/README.md +8 -5
- package/packages/render-hyperframes/README.md +9 -0
- package/packages/render-hyperframes/src/index.ts +3 -0
- package/packages/render-hyperframes/src/manifest.ts +7 -2
- package/packages/render-hyperframes/src/product.ts +41 -2
- package/packages/runtime-local/src/process-control.ts +4 -1
- package/packages/runtime-local/src/programs.ts +15 -3
- package/packages/runtime-local/src/supervisor.ts +8 -1
- package/packages/script/README.md +11 -4
- package/packages/script/src/format.ts +11 -24
- package/packages/script/src/manifest.ts +2 -2
- package/packages/script/src/parser.ts +16 -21
- package/packages/script/src/surface.ts +5 -3
- package/packages/script/src/types.ts +1 -1
- package/packages/source/README.md +1 -1
- package/packages/source/src/header.ts +2 -1
- package/packages/studio/README.md +13 -0
- package/packages/studio/src/feedback-server.ts +7 -0
- package/packages/studio/src/mutation-origin.ts +20 -0
- package/packages/studio/src/parameters.ts +3 -7
- package/packages/studio/src/preview/render.ts +7 -1
- package/packages/studio/src/server.ts +39 -0
- package/packages/studio/src/session.ts +7 -3
- package/packages/studio/src/ui/syntax.ts +11 -10
- package/packages/temporal-markup/EDITING.md +1 -1
- package/packages/video-cli/README.md +47 -4
- package/packages/video-cli/package.json +3 -0
- package/packages/video-cli/src/cli.ts +9 -9
- package/packages/video-cli/src/creation.ts +3 -3
- package/packages/video-cli/src/frame-grid.ts +34 -0
- package/packages/video-cli/src/index.ts +4 -0
- package/packages/video-cli/src/media-frames.ts +25 -0
- package/packages/video-cli/src/media.ts +198 -46
- package/packages/video-cli/src/process.ts +13 -3
- package/packages/video-cli/src/secret-input.ts +14 -0
- package/packages/video-cli/src/snapshot.ts +186 -0
- package/packages/wan/README.md +31 -0
- package/packages/wan/package.json +21 -0
- package/packages/wan/src/activation.ts +11 -0
- package/packages/wan/src/index.ts +160 -0
- package/packages/wan/src/surface.ts +147 -0
- package/packages/yt-dlp/src/download.ts +32 -18
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { artifactTypes } from "@hypit/artifact";
|
|
2
|
+
import { generationPort, sealGenerationMediaBinding, sealGenerationRequestDraft } from "@hypit/generation";
|
|
3
|
+
import type { GenerationMediaPort, GenerationPortValue } from "@hypit/generation";
|
|
4
|
+
import {
|
|
5
|
+
createExactModelPrimaryGenerationFragment,
|
|
6
|
+
exactModelMediaInputNames,
|
|
7
|
+
exactModelTextInputName,
|
|
8
|
+
} from "@hypit/model-kit";
|
|
9
|
+
import type { ExactModelEndpoint } from "@hypit/model-kit";
|
|
10
|
+
import type {
|
|
11
|
+
MarkupAttributeValue,
|
|
12
|
+
StructuredElement,
|
|
13
|
+
StructuredSurfaceHandler,
|
|
14
|
+
SurfaceResolvedReference,
|
|
15
|
+
} from "@hypit/markup";
|
|
16
|
+
import type { CanonicalValue, TypeRef } from "@hypit/protocol";
|
|
17
|
+
import { textTypes, verifyText } from "@hypit/text";
|
|
18
|
+
|
|
19
|
+
import { pixverseEndpoints } from "./index.js";
|
|
20
|
+
|
|
21
|
+
function assert(condition: unknown, message: string): asserts condition {
|
|
22
|
+
if (!condition) throw new Error(message);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sameType(left: TypeRef, right: TypeRef): boolean {
|
|
26
|
+
return left.name === right.name && left.module.name === right.module.name && left.module.version === right.module.version;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const attributes = ["id", "prompt", "first-frame", "last-frame", "duration", "quality",
|
|
30
|
+
"aspect-ratio", "generate-audio", "multi-clip", "seed"] as const;
|
|
31
|
+
|
|
32
|
+
function exact(element: StructuredElement): void {
|
|
33
|
+
const unknown = Object.keys(element.attributes).filter((name) => !attributes.includes(name as typeof attributes[number]));
|
|
34
|
+
assert(unknown.length === 0, `${element.name} does not accept ${unknown[0]}`);
|
|
35
|
+
const missing = ["id", "prompt", "duration", "quality"].filter((name) => element.attributes[name] === undefined);
|
|
36
|
+
assert(missing.length === 0, `${element.name} requires ${missing.join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function text(element: StructuredElement, name: string): string {
|
|
40
|
+
const value = element.attributes[name];
|
|
41
|
+
assert(typeof value === "string" && value.trim().length > 0, `${element.name}.${name} must be text`);
|
|
42
|
+
return value.trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** An absent optional attribute leaves its port unstated, which the model reads as its own default. */
|
|
46
|
+
function optionalText(element: StructuredElement, name: string): readonly GenerationPortValue[] | undefined {
|
|
47
|
+
return element.attributes[name] === undefined ? undefined : [text(element, name)];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function optionalFlag(element: StructuredElement, name: string): readonly GenerationPortValue[] | undefined {
|
|
51
|
+
if (element.attributes[name] === undefined) return undefined;
|
|
52
|
+
const value = text(element, name);
|
|
53
|
+
assert(value === "true" || value === "false", `${element.name}.${name} must be true or false`);
|
|
54
|
+
return [value === "true"];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function integer(element: StructuredElement, name: string): readonly GenerationPortValue[] {
|
|
58
|
+
const value = text(element, name);
|
|
59
|
+
assert(/^\d+$/u.test(value), `${element.name}.${name} must be a whole number`);
|
|
60
|
+
return [Number(value)];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ref(
|
|
64
|
+
element: StructuredElement,
|
|
65
|
+
name: string,
|
|
66
|
+
type: TypeRef,
|
|
67
|
+
resolve: (path: string) => SurfaceResolvedReference | undefined,
|
|
68
|
+
): SurfaceResolvedReference {
|
|
69
|
+
const value: MarkupAttributeValue | undefined = element.attributes[name];
|
|
70
|
+
assert(typeof value === "object" && value.kind === "reference", `${element.name}.${name} must be a reference`);
|
|
71
|
+
const result = resolve(value.path);
|
|
72
|
+
assert(result !== undefined && sameType(result.type, type), `${element.name}.${name} has the wrong type`);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function frame(
|
|
77
|
+
element: StructuredElement,
|
|
78
|
+
name: string,
|
|
79
|
+
resolve: (path: string) => SurfaceResolvedReference | undefined,
|
|
80
|
+
): SurfaceResolvedReference | undefined {
|
|
81
|
+
if (element.attributes[name] === undefined) return undefined;
|
|
82
|
+
const image = ref(element, name, artifactTypes.blob, resolve);
|
|
83
|
+
if (image.record !== undefined) {
|
|
84
|
+
assert(image.record.value.kind === "blob" && image.record.value.mediaType.startsWith("image/"),
|
|
85
|
+
`${element.name}.${name} must reference image media`);
|
|
86
|
+
}
|
|
87
|
+
return image;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function decoder(endpoint: ExactModelEndpoint): StructuredSurfaceHandler {
|
|
91
|
+
return ({ element, resolveReference }) => {
|
|
92
|
+
exact(element);
|
|
93
|
+
assert(!element.children.some((item) => item.kind === "element" || item.value.trim()),
|
|
94
|
+
`${element.name} accepts no children`);
|
|
95
|
+
const id = text(element, "id");
|
|
96
|
+
const prompt = ref(element, "prompt", textTypes.text, resolveReference);
|
|
97
|
+
if (prompt.record !== undefined) {
|
|
98
|
+
assert(prompt.record.value.kind === "inline", `${element.name}.prompt must reference Text`);
|
|
99
|
+
verifyText(prompt.record.value.value);
|
|
100
|
+
}
|
|
101
|
+
const frames = ([["firstFrame", "first-frame"], ["lastFrame", "last-frame"]] as const)
|
|
102
|
+
.flatMap(([port, attribute]) => {
|
|
103
|
+
const image = frame(element, attribute, resolveReference);
|
|
104
|
+
return image === undefined ? [] : [{ port, image }];
|
|
105
|
+
});
|
|
106
|
+
const stated: Record<string, readonly GenerationPortValue[] | undefined> = {
|
|
107
|
+
duration: integer(element, "duration"),
|
|
108
|
+
quality: [text(element, "quality")],
|
|
109
|
+
aspectRatio: optionalText(element, "aspect-ratio"),
|
|
110
|
+
generateAudio: optionalFlag(element, "generate-audio"),
|
|
111
|
+
multiClip: optionalFlag(element, "multi-clip"),
|
|
112
|
+
seed: element.attributes["seed"] === undefined ? undefined : integer(element, "seed"),
|
|
113
|
+
};
|
|
114
|
+
const draft = sealGenerationRequestDraft(endpoint.ports, Object.fromEntries(
|
|
115
|
+
Object.entries(stated).filter((entry): entry is [string, readonly GenerationPortValue[]] => entry[1] !== undefined),
|
|
116
|
+
));
|
|
117
|
+
const records: Array<{ id: string; type: TypeRef; value: { kind: "inline"; value: CanonicalValue }; range: StructuredElement["range"] }> = [{
|
|
118
|
+
id: `${id}.draft`, type: endpoint.draftType,
|
|
119
|
+
value: { kind: "inline", value: draft as unknown as CanonicalValue }, range: element.range,
|
|
120
|
+
}];
|
|
121
|
+
const inputs: Record<string, SurfaceResolvedReference["ref"] | { kind: "record"; id: string }> = {
|
|
122
|
+
draft: { kind: "record", id: `${id}.draft` }, [exactModelTextInputName("prompt")]: prompt.ref,
|
|
123
|
+
};
|
|
124
|
+
const media = frames.map(({ port, image }, index) => {
|
|
125
|
+
const name = `media-${String(index + 1).padStart(4, "0")}`;
|
|
126
|
+
const bindingId = `${id}.${name}.binding`;
|
|
127
|
+
const mediaPort = generationPort(endpoint.ports, port);
|
|
128
|
+
assert(mediaPort.value.kind === "media", `PixVerse port ${port} is not media`);
|
|
129
|
+
records.push({
|
|
130
|
+
id: bindingId, type: endpoint.mediaBindings[port]!.type,
|
|
131
|
+
value: { kind: "inline", value: sealGenerationMediaBinding(mediaPort as GenerationMediaPort, { role: "image" }) as unknown as CanonicalValue },
|
|
132
|
+
range: element.range,
|
|
133
|
+
});
|
|
134
|
+
const names = exactModelMediaInputNames(name);
|
|
135
|
+
inputs[names.binding] = { kind: "record", id: bindingId };
|
|
136
|
+
inputs[names.artifact] = image.ref;
|
|
137
|
+
return { name, port } as const;
|
|
138
|
+
});
|
|
139
|
+
const fragment = createExactModelPrimaryGenerationFragment(endpoint, media, [{ name: "prompt", port: "prompt" }]);
|
|
140
|
+
return { records, fragments: [fragment], components: [{
|
|
141
|
+
id, fragment: fragment.id, inputs, outputs: { video: `${id}.video` }, range: element.range,
|
|
142
|
+
}] };
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const decodePixverseVideoSurface = decoder(pixverseEndpoints.video!);
|
|
@@ -54,6 +54,13 @@ Runtime Profile example:
|
|
|
54
54
|
|
|
55
55
|
```json
|
|
56
56
|
{
|
|
57
|
+
"format": "hypit.runtime-local@1",
|
|
58
|
+
"dataRoot": ".hypit/runtimes/local",
|
|
59
|
+
"credentials": {
|
|
60
|
+
"platform": {
|
|
61
|
+
"use": "@hypit/credential-store-platform"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
57
64
|
"endpoints": {
|
|
58
65
|
"hiapi.default": {
|
|
59
66
|
"use": "@hypit/provider-hiapi",
|
|
@@ -64,7 +71,8 @@ Runtime Profile example:
|
|
|
64
71
|
"pollIntervalMs": 10000
|
|
65
72
|
}
|
|
66
73
|
}
|
|
67
|
-
}
|
|
74
|
+
},
|
|
75
|
+
"bindings": {}
|
|
68
76
|
}
|
|
69
77
|
```
|
|
70
78
|
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
# @hypit/provider-hyperframes-local
|
|
2
2
|
|
|
3
|
-
Trusted local Provider for
|
|
3
|
+
Trusted local Provider for `@hypit/render-hyperframes@1#render-visual` and `#render-frames`. It stages the
|
|
4
4
|
Resource dependencies declared by a `HyperframesDocument`, renders a silent MP4 with the
|
|
5
5
|
HyperFrames engine, probes the bytes, and returns a verified `RenderedVisual`. Before capturing a typed
|
|
6
6
|
Surface it decodes the exact bytes and checks declared dimensions, still/frame timing, SDR/sRGB and
|
|
7
7
|
opaque/straight-alpha facts. These checks validate the typed rendering input; they do not create
|
|
8
8
|
content identity or hidden output metadata.
|
|
9
9
|
|
|
10
|
+
`render-frames` uses the same staging, source-frame mapping, readiness and opaque PNG capture as
|
|
11
|
+
video export, returning the PNGs before encoding. It accepts a compiled document or a materialized
|
|
12
|
+
HTML project. For selected frames it merges only their required source-frame windows; continuous
|
|
13
|
+
windows decode sequentially. A complete batch shares one staged project and browser lifetime.
|
|
14
|
+
The existing browser selection, explicit preparation, worker settings and cancellation apply.
|
|
15
|
+
`maxRenderedBytes` bounds the total returned PNG bytes, or the encoded MP4 for a video request.
|
|
16
|
+
`render-frames` has its own capability binding; it does not inherit a `render-visual` binding.
|
|
17
|
+
For immediate CLI invocation, the host calls the handler directly: Build admission reservations do
|
|
18
|
+
not coordinate separate CLI processes. Worker limits still bound browsers inside each invocation.
|
|
19
|
+
|
|
10
20
|
Normalized transparent videos displayed by Media or project components use the ordinary video path.
|
|
11
21
|
The engine decodes them to PNG frames with alpha, then Chrome blends them with lower layers and
|
|
12
22
|
the authored Canvas background. Final frames use fast PNG encoding after Chrome has composited
|
|
@@ -73,8 +83,7 @@ an opaque session and chooses PNG separately at capture. It does not patch engin
|
|
|
73
83
|
The Runtime Adapter declares one managed browser program. Prepare it explicitly:
|
|
74
84
|
|
|
75
85
|
```sh
|
|
76
|
-
hypit programs
|
|
77
|
-
hypit doctor --runtime ./hypit.runtime.json
|
|
86
|
+
hypit programs prepare --runtime ./hypit.runtime.json --endpoint hyperframes.local
|
|
78
87
|
```
|
|
79
88
|
|
|
80
89
|
Use the instance name from the Profile. `runtime up` also prepares it and starts the Runtime Worker.
|
|
@@ -110,17 +119,20 @@ receipt is stored. To install into an empty location, explicitly choose `browser
|
|
|
110
119
|
|
|
111
120
|
`config.chromePath` selects a user-managed Chrome/Chromium executable. It cannot be combined with
|
|
112
121
|
`browserVersion` or `browserDownloadBaseUrl`; invalid combinations fail instead of assigning precedence. Relative paths resolve
|
|
113
|
-
from the Runtime
|
|
122
|
+
from the Runtime data root, as does `browserCacheDirectory`. The Profile's `dataRoot` itself resolves
|
|
123
|
+
relative to the Profile file. This mode never downloads or repairs a browser. Its version remains
|
|
114
124
|
under the user's control, including system-browser auto-updates. `HYPERFRAMES_BROWSER_PATH` and
|
|
115
125
|
`PRODUCER_HEADLESS_SHELL_PATH` do not select browsers in this Provider; configure `chromePath`.
|
|
116
126
|
On platforms without a supported managed download, explicitly select an installed browser.
|
|
117
127
|
|
|
118
128
|
`doctor` displays the selected path and its source, and only inspects it. Build preflight, rendering
|
|
119
|
-
and previews never install a browser. `programs up`
|
|
129
|
+
and previews never install a browser. `programs prepare`, `programs up` and `runtime up` display the selected managed
|
|
120
130
|
version, installation location and download URL before running preparation. The probe runs `--version` and checks
|
|
121
131
|
FFmpeg/FFprobe; it does not promise GPU or page compatibility. Capture receives that same selected
|
|
122
|
-
path as the engine's `chromePath`, including its GPU probe.
|
|
123
|
-
|
|
132
|
+
path as the engine's `chromePath`, including its GPU probe. New Builds read current Endpoint
|
|
133
|
+
configuration and project implementation; active Builds keep their selected configuration. A
|
|
134
|
+
Distribution update or change to the Worker's inherited environment requires an explicit Worker
|
|
135
|
+
restart when active work permits. Ordinary Profile edits do not require restarting every process.
|
|
124
136
|
|
|
125
137
|
The Provider's `hypit.dependencyInstallEnv` disables Puppeteer's browser download while preparing its
|
|
126
138
|
engine/producer npm dependencies. The repository `.puppeteerrc.cjs` does the same for checkout installs.
|
|
@@ -11,7 +11,8 @@ import { CaptureConcurrency } from "./concurrency.js";
|
|
|
11
11
|
import { createOpaqueFrameCapture } from "./opaque-capture.js";
|
|
12
12
|
|
|
13
13
|
export type CaptureInput = {
|
|
14
|
-
readonly document: HyperframesDocument
|
|
14
|
+
readonly document: Pick<HyperframesDocument, "frameRate" | "frameCount" | "canvas">;
|
|
15
|
+
readonly frames?: readonly number[];
|
|
15
16
|
readonly range: MediaFrameRange;
|
|
16
17
|
readonly config: ReturnType<typeof resolveExecutionOptions> & { readonly chromePath: string };
|
|
17
18
|
readonly directory: string;
|
|
@@ -24,14 +25,16 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
24
25
|
onProgress: (event: HyperframesRenderProgress) => void): Promise<string> {
|
|
25
26
|
const { document, range, config, directory: work } = input;
|
|
26
27
|
const signal = controller.signal;
|
|
27
|
-
const frameCount = range.endFrameExclusive - range.startFrame;
|
|
28
|
+
const frameCount = input.frames?.length ?? range.endFrameExclusive - range.startFrame;
|
|
29
|
+
const selected = input.frames === undefined ? undefined : new Set(input.frames);
|
|
30
|
+
const at = (index: number) => input.frames?.[index] ?? range.startFrame + index;
|
|
28
31
|
const fps = { num: document.frameRate.numerator, den: document.frameRate.denominator };
|
|
29
32
|
const limit = renderWorkerLimit(config, frameCount, fps.num / fps.den);
|
|
30
33
|
const concurrency = new CaptureConcurrency(limit, config.workers === "auto");
|
|
31
34
|
// Short contiguous batches retain sequential capture while allowing free browsers
|
|
32
35
|
// to help with expensive passages. This queue exists only inside this render.
|
|
33
36
|
const batchCount = Math.max(limit, Math.ceil(frameCount / Math.max(1, Math.round(fps.num / fps.den))));
|
|
34
|
-
const batches = distributeFrameRange(
|
|
37
|
+
const batches = distributeFrameRange({ startFrame: 0, endFrameExclusive: frameCount }, batchCount);
|
|
35
38
|
let nextBatch = 0;
|
|
36
39
|
const started = performance.now();
|
|
37
40
|
const elapsedMs = () => Math.round(performance.now() - started);
|
|
@@ -81,7 +84,8 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
81
84
|
const sources = new Map<string, { frames: Map<number, string>; width: number; height: number }>();
|
|
82
85
|
let sourceFrames = 0;
|
|
83
86
|
// One source at a time bounds decoder pressure independently of browser concurrency.
|
|
84
|
-
const windows = sourceWindows(slots, range
|
|
87
|
+
const windows = sourceWindows(slots, input.frames === undefined ? [range]
|
|
88
|
+
: input.frames.map(frame => ({ startFrame: frame, endFrameExclusive: frame + 1 })));
|
|
85
89
|
const sourceTotal = windows.reduce((sum, source) => sum + source.windows.reduce((count, window) =>
|
|
86
90
|
count + window.endFrameExclusive - window.startFrame, 0), 0);
|
|
87
91
|
if (sourceTotal > 0) onProgress({ phase: "decoding", completed: 0, total: sourceTotal, elapsedMs: elapsedMs() });
|
|
@@ -119,7 +123,7 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
119
123
|
const frameIndex = sourceFrameAt(slot, frame);
|
|
120
124
|
const framePath = sources.get(slot.src)?.frames.get(frameIndex);
|
|
121
125
|
// Browser initialization may seek outside the requested interval.
|
|
122
|
-
if (frame < range.startFrame || frame >= range.endFrameExclusive) continue;
|
|
126
|
+
if (selected === undefined ? frame < range.startFrame || frame >= range.endFrameExclusive : !selected.has(frame)) continue;
|
|
123
127
|
assert(framePath !== undefined, `HyperFrames has no decoded frame ${frameIndex} for ${slot.id}`);
|
|
124
128
|
payloads.set(slot.id, { framePath, frameIndex });
|
|
125
129
|
}
|
|
@@ -132,7 +136,7 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
132
136
|
await mkdir(outputFrames);
|
|
133
137
|
onProgress?.({ phase: "prepared", workers: concurrency.target, sourceFrames, elapsedMs: elapsedMs() });
|
|
134
138
|
const jobs: Promise<void>[] = [];
|
|
135
|
-
let active = 0, open = 0, initializing = 0, capturedFrames = 0;
|
|
139
|
+
let active = 0, open = 0, initializing = 0, capturedFrames = 0, capturedBytes = 0;
|
|
136
140
|
let startupMs = 0;
|
|
137
141
|
const launch = (): void => {
|
|
138
142
|
const worker = jobs.length;
|
|
@@ -187,13 +191,18 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
187
191
|
if (active > concurrency.target) break;
|
|
188
192
|
const batch = batches[nextBatch++];
|
|
189
193
|
if (batch === undefined) break;
|
|
190
|
-
for (let
|
|
194
|
+
for (let index = batch.startFrame; index < batch.endFrameExclusive; index++) {
|
|
195
|
+
const frame = at(index);
|
|
191
196
|
signal.throwIfAborted();
|
|
192
197
|
const captured = await stage(`worker ${worker} frame ${frame}`, config.frameTimeoutMs, () => captureFrame(frame));
|
|
198
|
+
if (input.frames !== undefined) {
|
|
199
|
+
capturedBytes += captured.buffer.byteLength;
|
|
200
|
+
assert(capturedBytes <= config.maxRenderedBytes, "HyperFrames PNG output exceeds its byte limit");
|
|
201
|
+
}
|
|
193
202
|
timing.seekMs += captured.seekMs;
|
|
194
203
|
timing.prepareMs += captured.prepareMs;
|
|
195
204
|
timing.screenshotMs += captured.screenshotMs;
|
|
196
|
-
await writeFile(join(outputFrames, `${String(
|
|
205
|
+
await writeFile(join(outputFrames, `${String(index).padStart(9, "0")}.png`), captured.buffer);
|
|
197
206
|
completed++;
|
|
198
207
|
capturedFrames++;
|
|
199
208
|
if (performance.now() - lastProgressAt >= 1_000) {
|
|
@@ -239,6 +248,7 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
|
|
|
239
248
|
if (failure !== undefined) throw failure;
|
|
240
249
|
signal.throwIfAborted();
|
|
241
250
|
await Promise.all(closing.values());
|
|
251
|
+
if (input.frames !== undefined) return outputFrames;
|
|
242
252
|
const output = join(work, "visual.mp4");
|
|
243
253
|
onProgress({ phase: "encoding", elapsedMs: elapsedMs() });
|
|
244
254
|
const crf = { draft: 28, standard: 23, high: 18 }[config.quality];
|
|
@@ -6,5 +6,5 @@ export type {
|
|
|
6
6
|
HyperframesWorkers,
|
|
7
7
|
} from "./provider.js";
|
|
8
8
|
|
|
9
|
-
export { renderHyperframesVisual } from "./render.js";
|
|
9
|
+
export { renderHyperframesVisual, renderHyperframesFrames } from "./render.js";
|
|
10
10
|
export type { RenderHyperframesVisualOptions, HyperframesRenderProgress } from "./render.js";
|
|
@@ -38,7 +38,7 @@ function outputFrameCount(stream: ProbeStream): number {
|
|
|
38
38
|
|
|
39
39
|
export async function verifyOutput(args: {
|
|
40
40
|
readonly path: string;
|
|
41
|
-
readonly document: HyperframesDocument
|
|
41
|
+
readonly document: Pick<HyperframesDocument, "frameRate" | "frameCount" | "canvas">;
|
|
42
42
|
readonly ffprobePath: string;
|
|
43
43
|
readonly timeoutMs: number;
|
|
44
44
|
readonly maxOutputBytes: number;
|
|
@@ -33,7 +33,10 @@ export async function killRenderTree(pid: number): Promise<void> {
|
|
|
33
33
|
for (const child of descendants.reverse()) {
|
|
34
34
|
for (const target of [-child, child]) {
|
|
35
35
|
try { process.kill(target, "SIGKILL"); }
|
|
36
|
-
catch (error) {
|
|
36
|
+
catch (error) {
|
|
37
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
38
|
+
if (code !== "ESRCH" && !(target < 0 && code === "EPERM")) throw error;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
const deadline = Date.now() + cleanupMs;
|
|
@@ -76,7 +79,10 @@ export function killRenderDescendantsSync(pid: number): void {
|
|
|
76
79
|
for (const child of descendants.slice(1).reverse()) {
|
|
77
80
|
for (const target of [-child, child]) {
|
|
78
81
|
try { process.kill(target, "SIGKILL"); }
|
|
79
|
-
catch (error) {
|
|
82
|
+
catch (error) {
|
|
83
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
84
|
+
if (code !== "ESRCH" && !(target < 0 && code === "EPERM")) throw error;
|
|
85
|
+
}
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineEndpointPackage } from "@hypit/endpoint-kit";
|
|
2
2
|
import { mediaTypes } from "@hypit/media";
|
|
3
|
-
import { renderHyperframesCapabilities, verifyHyperframesVisualRequest } from "@hypit/render-hyperframes";
|
|
3
|
+
import { renderHyperframesCapabilities, renderHyperframesTypes, verifyHyperframesFramesRequest, hyperframesFramesDomain, verifyHyperframesVisualRequest } from "@hypit/render-hyperframes";
|
|
4
4
|
import { canonicalize } from "@hypit/protocol";
|
|
5
|
-
import { renderHyperframesVisual, resolveExecutionOptions, renderWorkerLimit } from "./render.js";
|
|
5
|
+
import { renderHyperframesVisual, renderHyperframesFrames, resolveExecutionOptions, renderWorkerLimit } from "./render.js";
|
|
6
6
|
import type { HyperframesExecutionOptions } from "./options.js";
|
|
7
7
|
import { renderProgressReporter } from "./progress.js";
|
|
8
8
|
|
|
@@ -67,6 +67,33 @@ export function createLocalHyperframesProvider(config: CreateLocalHyperframesPro
|
|
|
67
67
|
await progress.flush();
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
+
}, {
|
|
71
|
+
lifecycle: "immediate" as const,
|
|
72
|
+
capability: renderHyperframesCapabilities.renderFrames,
|
|
73
|
+
returns: renderHyperframesTypes.frames,
|
|
74
|
+
...(config.browserCapacity === undefined ? {} : {
|
|
75
|
+
resources: [{ id: browsers, limit: config.browserCapacity }],
|
|
76
|
+
unitsForRequest: (request: import("@hypit/endpoint-kit").EndpointRequest) => {
|
|
77
|
+
verifyHyperframesFramesRequest(request.constraints);
|
|
78
|
+
const document = hyperframesFramesDomain(request.constraints);
|
|
79
|
+
return { [browsers]: renderWorkerLimit(reserved, request.constraints.frames.length,
|
|
80
|
+
document.frameRate.numerator / document.frameRate.denominator) };
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
handler: async context => {
|
|
84
|
+
const request = context.need.constraints;
|
|
85
|
+
verifyHyperframesFramesRequest(request);
|
|
86
|
+
const progress = renderProgressReporter(context.reportProgress, request.frames.length);
|
|
87
|
+
try {
|
|
88
|
+
const frames = await renderHyperframesFrames(request, { ...config,
|
|
89
|
+
...(execution.chromePath === undefined ? { browserVersion: execution.browserVersion! } : { chromePath: execution.chromePath }),
|
|
90
|
+
browserCacheDirectory: execution.browserCacheDirectory,
|
|
91
|
+
workers: execution.workers, maxWorkers,
|
|
92
|
+
resources: context.resources, onProgress: progress.onProgress,
|
|
93
|
+
...(context.reportDiagnostic === undefined ? {} : { onDiagnostic: context.reportDiagnostic }) });
|
|
94
|
+
return { value: { kind: "inline", value: canonicalize(frames) } };
|
|
95
|
+
} finally { await progress.flush(); }
|
|
96
|
+
},
|
|
70
97
|
}],
|
|
71
98
|
});
|
|
72
99
|
}
|
|
@@ -3,12 +3,12 @@ import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import type { EndpointInvocationContext } from "@hypit/endpoint-kit";
|
|
6
|
-
import { stageHyperframesProject } from "@hypit/hyperframes/project";
|
|
6
|
+
import { stageHyperframesProject, stageHyperframesHtmlProject } from "@hypit/hyperframes/project";
|
|
7
7
|
import { sealRenderedVisual } from "@hypit/media";
|
|
8
8
|
import type { MediaFrameRange, RenderedVisual } from "@hypit/media";
|
|
9
9
|
import { verifyCompositableSurfaceFile } from "@hypit/media-execution";
|
|
10
|
-
import { verifyHyperframesVisualRequest } from "@hypit/render-hyperframes";
|
|
11
|
-
import type { HyperframesVisualRequest } from "@hypit/render-hyperframes";
|
|
10
|
+
import { verifyHyperframesVisualRequest, verifyHyperframesFramesRequest, hyperframesFramesDomain } from "@hypit/render-hyperframes";
|
|
11
|
+
import type { HyperframesVisualRequest, HyperframesFramesRequest, HyperframesFrames } from "@hypit/render-hyperframes";
|
|
12
12
|
import { isStreamingResourceStore } from "@hypit/runtime";
|
|
13
13
|
import type { HyperframesExecutionOptions } from "./options.js";
|
|
14
14
|
import { assert, positiveInteger } from "./process.js";
|
|
@@ -75,10 +75,22 @@ export async function renderHyperframesVisual(
|
|
|
75
75
|
options: RenderHyperframesVisualOptions,
|
|
76
76
|
): Promise<RenderedVisual> {
|
|
77
77
|
verifyHyperframesVisualRequest(request);
|
|
78
|
+
return capture(request, options) as Promise<RenderedVisual>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function renderHyperframesFrames(request: HyperframesFramesRequest, options: RenderHyperframesVisualOptions): Promise<HyperframesFrames> {
|
|
82
|
+
verifyHyperframesFramesRequest(request);
|
|
83
|
+
return capture(request, options) as Promise<HyperframesFrames>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function capture(request: HyperframesVisualRequest | HyperframesFramesRequest, options: RenderHyperframesVisualOptions): Promise<RenderedVisual | HyperframesFrames> {
|
|
78
87
|
const config = { ...resolveExecutionOptions(options), chromePath: browserExecutablePath(options) };
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
88
|
+
const frames = "frames" in request ? request.frames : undefined;
|
|
89
|
+
const document = "frames" in request ? hyperframesFramesDomain(request) : request.document;
|
|
90
|
+
const range = frames === undefined
|
|
91
|
+
? (request as HyperframesVisualRequest).range ?? { startFrame: 0, endFrameExclusive: document.frameCount }
|
|
92
|
+
: { startFrame: frames[0]!, endFrameExclusive: frames.at(-1)! + 1 };
|
|
93
|
+
const frameCount = frames?.length ?? range.endFrameExclusive - range.startFrame;
|
|
82
94
|
const controller = new AbortController();
|
|
83
95
|
const signal = options.signal === undefined ? controller.signal : AbortSignal.any([controller.signal, options.signal]);
|
|
84
96
|
const started = performance.now();
|
|
@@ -103,23 +115,25 @@ export async function renderHyperframesVisual(
|
|
|
103
115
|
signal.throwIfAborted();
|
|
104
116
|
await requireBrowserExecutable(config.chromePath, config.browserVersion);
|
|
105
117
|
await options.onDiagnostic?.({ level: "info", message:
|
|
106
|
-
`
|
|
118
|
+
`Capture ${frameCount} frames; workers ${config.workers} (limit ${renderWorkerLimit(config, frameCount, document.frameRate.numerator / document.frameRate.denominator)}); opaque fast PNG; GPU ${config.browserGpu}; browser ${config.chromePath}`
|
|
119
|
+
+ (frames === undefined ? `; quality ${config.quality}; encoder ${config.ffmpegPath}` : "; PNG output") });
|
|
107
120
|
options.onProgress?.({ phase: "staging", elapsedMs: 0 });
|
|
108
121
|
work = await mkdtemp(join(tmpdir(), "hypit-hyperframes-local-"));
|
|
109
|
-
|
|
110
|
-
read: async (artifact, readSignal) => {
|
|
122
|
+
const read: import("@hypit/hyperframes/project").HyperframesArtifactReader = async (artifact, readSignal) => {
|
|
111
123
|
const io = { signal: readSignal! };
|
|
112
124
|
const bytes = isStreamingResourceStore(options.resources)
|
|
113
125
|
? await options.resources.open(artifact.resource, io) : await options.resources.get(artifact.resource, io);
|
|
114
126
|
assert(bytes !== undefined, `HyperFrames Artifact ${artifact.resource} is unavailable`);
|
|
115
127
|
return bytes;
|
|
116
|
-
}
|
|
128
|
+
};
|
|
129
|
+
if ("project" in request) await stageHyperframesHtmlProject({ project: request.project, directory: work, signal, read });
|
|
130
|
+
else await stageHyperframesProject({ document: request.document, directory: work, signal, read,
|
|
117
131
|
validateSurface: (surface, path, probeSignal) => verifyCompositableSurfaceFile({ surface, path,
|
|
118
132
|
ffprobePath: config.ffprobePath, processTimeoutMs: config.processTimeoutMs,
|
|
119
133
|
maxProbeOutputBytes: config.maxProcessOutputBytes, signal: probeSignal! }),
|
|
120
134
|
});
|
|
121
135
|
changePhase("decoding source frames");
|
|
122
|
-
await runCaptureProcess({ document, range, config, directory: work,
|
|
136
|
+
await runCaptureProcess({ document, range, ...(frames === undefined ? {} : { frames }), config, directory: work,
|
|
123
137
|
engineModule: import.meta.resolve("@hyperframes/engine"),
|
|
124
138
|
producerModule: import.meta.resolve("@hyperframes/producer"),
|
|
125
139
|
}, signal, (event) => {
|
|
@@ -132,22 +146,33 @@ export async function renderHyperframesVisual(
|
|
|
132
146
|
signal.throwIfAborted();
|
|
133
147
|
changePhase("storing output");
|
|
134
148
|
options.onProgress?.({ phase: "storing", elapsedMs: Math.round(performance.now() - started) });
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
const store = async (output: string, mediaType: string) => {
|
|
150
|
+
if (isStreamingResourceStore(options.resources)) {
|
|
151
|
+
const stream = createReadStream(output, { signal });
|
|
152
|
+
const closed = finished(stream).catch(() => {});
|
|
153
|
+
try { return await options.resources.putStream(stream, mediaType, { signal }); }
|
|
154
|
+
finally { stream.destroy(); await closed; }
|
|
155
|
+
}
|
|
156
|
+
return options.resources.put(await readFile(output, { signal }), mediaType, { signal });
|
|
157
|
+
};
|
|
158
|
+
let result: RenderedVisual | HyperframesFrames;
|
|
159
|
+
if (frames === undefined) {
|
|
160
|
+
result = sealRenderedVisual({ frameRate: document.frameRate, frameCount, canvas: document.canvas,
|
|
161
|
+
artifact: await store(join(work, "visual.mp4"), "video/mp4") });
|
|
142
162
|
} else {
|
|
143
|
-
|
|
163
|
+
const artifacts = [];
|
|
164
|
+
for (let index = 0; index < frames.length; index++) {
|
|
165
|
+
signal.throwIfAborted();
|
|
166
|
+
artifacts.push(await store(join(work, "frames", `${String(index).padStart(9, "0")}.png`), "image/png"));
|
|
167
|
+
}
|
|
168
|
+
result = artifacts;
|
|
144
169
|
}
|
|
145
170
|
signal.throwIfAborted();
|
|
146
171
|
|
|
147
172
|
changePhase("complete");
|
|
148
173
|
await Promise.all(diagnostics);
|
|
149
174
|
options.onProgress?.({ phase: "complete", frames: frameCount, elapsedMs: Math.round(performance.now() - started) });
|
|
150
|
-
return
|
|
175
|
+
return result;
|
|
151
176
|
} catch (error) {
|
|
152
177
|
if (signal.aborted) throw signal.reason;
|
|
153
178
|
controller.abort(error);
|
|
@@ -51,9 +51,10 @@ export function sourceFrameAt(slot: VideoSlot, frame: number): number {
|
|
|
51
51
|
return value;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export function sourceWindows(slots: readonly VideoSlot[],
|
|
54
|
+
export function sourceWindows(slots: readonly VideoSlot[], selection: MediaFrameRange | readonly MediaFrameRange[]) {
|
|
55
|
+
const ranges: readonly MediaFrameRange[] = Array.isArray(selection) ? selection : [selection as MediaFrameRange];
|
|
55
56
|
const sources = new Map<string, { fps: VideoSlot["sourceFps"]; windows: MediaFrameRange[] }>();
|
|
56
|
-
for (const slot of slots) {
|
|
57
|
+
for (const range of ranges) for (const slot of slots) {
|
|
57
58
|
const first = Math.max(range.startFrame, slot.startFrame);
|
|
58
59
|
const last = Math.min(range.endFrameExclusive, slot.endFrameExclusive) - 1;
|
|
59
60
|
if (last < first) continue;
|
|
@@ -84,18 +84,26 @@ Runtime Profile example:
|
|
|
84
84
|
|
|
85
85
|
```json
|
|
86
86
|
{
|
|
87
|
+
"format": "hypit.runtime-local@1",
|
|
88
|
+
"dataRoot": ".hypit/runtimes/local",
|
|
89
|
+
"credentials": {
|
|
90
|
+
"platform": {
|
|
91
|
+
"use": "@hypit/credential-store-platform"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
87
94
|
"endpoints": {
|
|
88
95
|
"hypihub.default": {
|
|
89
96
|
"use": "@hypit/provider-hypihub",
|
|
90
97
|
"pool": "hypihub.default",
|
|
91
98
|
"config": {
|
|
92
99
|
"baseUrl": "https://hypit.ai",
|
|
93
|
-
"apiKey": { "store": "
|
|
100
|
+
"apiKey": { "store": "platform", "key": "hypihub.oauth" },
|
|
94
101
|
"defaultConcurrency": 3,
|
|
95
102
|
"pollIntervalMs": 10000
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
|
-
}
|
|
105
|
+
},
|
|
106
|
+
"bindings": {}
|
|
99
107
|
}
|
|
100
108
|
```
|
|
101
109
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Hypit Runtime Provider for a [Monid](https://monid.ai) workspace. It runs Monid's generation
|
|
4
4
|
endpoints through `POST /v1/run`, polls `GET /v1/runs/{runId}` until the run is terminal, downloads
|
|
5
|
-
|
|
5
|
+
every returned file and stores it in the current Build.
|
|
6
6
|
|
|
7
7
|
| Capability | Monid endpoint |
|
|
8
8
|
| --- | --- |
|
|
@@ -11,16 +11,17 @@ the returned video and stores it in the current Build.
|
|
|
11
11
|
| `@hypit/seedance@1#seedance-2-mini` | `bytedance` `/v1/video/seedance-2.0-mini` |
|
|
12
12
|
| `@hypit/seedance@1#seedance-2.5` | `bytedance` `/v1/video/seedance-2.5` |
|
|
13
13
|
| `@hypit/minimax-h3@1#minimax-h3` | `minimax` `/v1/video/minimax-h3` |
|
|
14
|
+
| `@hypit/wan@1#wan-2.7-image` | `alibaba` `/v1/image/wan2.7-image` |
|
|
15
|
+
| `@hypit/wan@1#wan-2.7-image-pro` | `alibaba` `/v1/image/wan2.7-image-pro` |
|
|
14
16
|
|
|
15
17
|
Monid's catalogue lists further generation endpoints, including the H3 Fast, Max and Max Turbo
|
|
16
|
-
variants
|
|
17
|
-
are published through the authenticated `inspect` operation,
|
|
18
|
-
|
|
18
|
+
variants, Hailuo 2.3 and the Kling, Gemini and Qwen families; their models are not the ones the
|
|
19
|
+
Distribution describes. Input schemas are published through the authenticated `inspect` operation,
|
|
20
|
+
which is where the request bodies below come from.
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
`duration`.
|
|
22
|
+
The video endpoints relay a BytePlus ModelArk request: one `content` array holding the prompt and
|
|
23
|
+
each media input as a typed item with its `role` (`first_frame`, `last_frame`, `reference_image`,
|
|
24
|
+
`reference_video`, `reference_audio`), then `resolution`, `ratio` and `duration`.
|
|
24
25
|
|
|
25
26
|
The Seedance endpoints add `generate_audio`. Monid documents no web search field for them, so
|
|
26
27
|
`web-search="true"` is unsupported, and Seedance 2.5 frame mode (`first-frame` present) requires
|
|
@@ -37,6 +38,17 @@ text-to-video request carrying no `aspect-ratio` is reported unsupported before
|
|
|
37
38
|
resolved, and the other two modes are sent as `adaptive`. H3 returns its video at `content.url`
|
|
38
39
|
rather than the ModelArk `video_url`.
|
|
39
40
|
|
|
41
|
+
The Wan image endpoints take their fields directly: `prompt`, `images` as plain URLs, `size` for the
|
|
42
|
+
band, `n`, `enable_sequential`, `thinking_mode`, `watermark` and `seed`. Both variants accept the
|
|
43
|
+
same fields, and the Pro variant adds the `4K` band. Two service limits are reported before any
|
|
44
|
+
reference is resolved: `4K` renders from a prompt alone, without reference images or an image set,
|
|
45
|
+
and a request renders at most four pictures outside an image set. An image set returns several
|
|
46
|
+
files, so collection downloads each one.
|
|
47
|
+
|
|
48
|
+
The endpoints also document a custom colour palette and per-image bounding boxes. Neither has a
|
|
49
|
+
representation in the model port vocabulary, which carries scalars and media rather than object
|
|
50
|
+
arrays, so the Provider transmits neither.
|
|
51
|
+
|
|
40
52
|
Reference media are uploaded through the workspace file system Monid provides for this purpose
|
|
41
53
|
(`sfs`): `/put` signs an upload for `hypit/<resource>.<ext>`, the bytes are `PUT` to that URL, and
|
|
42
54
|
`/cat` mints a one-day download URL that the generation endpoint fetches. Uploaded files stay in the
|
|
@@ -52,6 +64,13 @@ Runtime Profile example:
|
|
|
52
64
|
|
|
53
65
|
```json
|
|
54
66
|
{
|
|
67
|
+
"format": "hypit.runtime-local@1",
|
|
68
|
+
"dataRoot": ".hypit/runtimes/local",
|
|
69
|
+
"credentials": {
|
|
70
|
+
"platform": {
|
|
71
|
+
"use": "@hypit/credential-store-platform"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
55
74
|
"endpoints": {
|
|
56
75
|
"monid.default": {
|
|
57
76
|
"use": "@hypit/provider-monid",
|
|
@@ -62,7 +81,8 @@ Runtime Profile example:
|
|
|
62
81
|
"pollIntervalMs": 10000
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
|
-
}
|
|
84
|
+
},
|
|
85
|
+
"bindings": {}
|
|
66
86
|
}
|
|
67
87
|
```
|
|
68
88
|
|