@skyphusion-labs/vivijure-core 0.9.4 → 1.0.0
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
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
# @skyphusion-labs/vivijure-core
|
|
2
2
|
|
|
3
|
-
Shared orchestration
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**Shared orchestration that unifies both Vivijure control panels.** Module registry, film pipeline,
|
|
4
|
+
planner helpers, and the **Platform ICD** that lets one codebase run on Cloudflare Workers
|
|
5
|
+
([`vivijure-cf`](https://github.com/skyphusion-labs/vivijure-cf)) or on a home computer / any cloud
|
|
6
|
+
server ([`vivijure-local`](https://github.com/skyphusion-labs/vivijure-local)). Product site:
|
|
7
|
+
[vivijure.com](https://vivijure.com).
|
|
6
8
|
|
|
7
9
|
| Consumer | Role |
|
|
8
10
|
|----------|------|
|
|
9
|
-
| [`vivijure`](https://github.com/skyphusion-labs/vivijure) | CF-native control plane (D1, R2, service bindings) |
|
|
11
|
+
| [`vivijure-cf`](https://github.com/skyphusion-labs/vivijure-cf) | CF-native control plane (D1, R2, service bindings) |
|
|
10
12
|
| [`vivijure-local`](https://github.com/skyphusion-labs/vivijure-local) | Homelab control plane (SQLite, S3/MinIO, HTTP sidecars) |
|
|
11
13
|
|
|
12
|
-
**Status:**
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
**Status:** Published on npm as `@skyphusion-labs/vivijure-core` and consumed by both hosts.
|
|
15
|
+
`vivijure-cf` has adopted the published package (no duplicate orchestration `src/` remains) and
|
|
16
|
+
`vivijure-local` consumes it via semver.
|
|
17
|
+
|
|
18
|
+
## Where it fits
|
|
19
|
+
|
|
20
|
+
`vivijure-core` is a library, not a runnable app: host repos depend on it and provide the runtime
|
|
21
|
+
(Cloudflare Workers or Node). It has no end-user surface of its own; you consume it, you do not deploy it.
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
flowchart TD
|
|
25
|
+
core[vivijure-core -- THIS REPO<br/>shared orchestration lib:<br/>module registry, film pipeline, Platform ICD]
|
|
26
|
+
cf[vivijure-cf<br/>Cloudflare Workers host]
|
|
27
|
+
local[vivijure-local<br/>Node / SQLite / S3 host]
|
|
28
|
+
studio[Vivijure Studio contract<br/>projects, storyboard, cast, render]
|
|
29
|
+
|
|
30
|
+
core --> cf
|
|
31
|
+
core --> local
|
|
32
|
+
cf --> studio
|
|
33
|
+
local --> studio
|
|
34
|
+
```
|
|
15
35
|
|
|
16
36
|
## Layout
|
|
17
37
|
|
|
@@ -43,8 +63,8 @@ npm install @skyphusion-labs/vivijure-core
|
|
|
43
63
|
```
|
|
44
64
|
~/dev/
|
|
45
65
|
vivijure-core/
|
|
46
|
-
vivijure-local/ # package.json: "^0.9.0"
|
|
47
|
-
vivijure/
|
|
66
|
+
vivijure-local/ # package.json: "^0.9.0" -- override with file:../vivijure-core locally
|
|
67
|
+
vivijure-cf/
|
|
48
68
|
```
|
|
49
69
|
|
|
50
70
|
```bash
|
|
@@ -63,8 +83,8 @@ git push origin vivijure-core-v0.9.0
|
|
|
63
83
|
|
|
64
84
|
Or trigger the **Publish npm package** workflow manually after bumping `package.json` version.
|
|
65
85
|
|
|
66
|
-
**flatliners** (Hetzner test box):
|
|
67
|
-
|
|
86
|
+
**flatliners** (Hetzner test box): **retired 2026-07-16**. Historical notes only:
|
|
87
|
+
[docs/FLATLINERS-DEV.md](docs/FLATLINERS-DEV.md). Active GPU/studio host is **propagandhi**.
|
|
68
88
|
|
|
69
89
|
## Key exports
|
|
70
90
|
|
|
@@ -85,4 +105,4 @@ into ported handlers.
|
|
|
85
105
|
|
|
86
106
|
## License
|
|
87
107
|
|
|
88
|
-
AGPL-3.0-only. Same as Vivijure
|
|
108
|
+
AGPL-3.0-only. Same as the rest of the Vivijure constellation.
|
|
@@ -2,8 +2,16 @@ import type { Env } from "./platform/orchestrator-context.js";
|
|
|
2
2
|
export declare const FILM_ADVANCE_LEASE_TTL_SECONDS = 300;
|
|
3
3
|
export interface FilmAdvanceClaim {
|
|
4
4
|
won: boolean;
|
|
5
|
+
/** The lease EXPIRY (ms epoch): the wall-clock instant this claim's hold self-expires. Present
|
|
6
|
+
* only on a guarded win. Informational; the release key is `token`, not this value. */
|
|
5
7
|
lease?: number;
|
|
8
|
+
/** The per-claim leaseholder identity (a UUID), present only on a guarded win. This -- NOT the
|
|
9
|
+
* expiry timestamp -- is what identifies the holder, because the timestamp is NOT unique: CF's
|
|
10
|
+
* `Date.now()` is coarsened + frozen per-invocation, so two claims racing in the same
|
|
11
|
+
* millisecond compute the identical `lease` value. The token is what `releaseFilmAdvance` keys
|
|
12
|
+
* on, and what makes the claim idempotent under `withD1Retry` (see below). */
|
|
13
|
+
token?: string;
|
|
6
14
|
}
|
|
7
15
|
export declare function claimFilmAdvance(env: Env, filmId: string, now?: number): Promise<FilmAdvanceClaim>;
|
|
8
|
-
export declare function releaseFilmAdvance(env: Env, filmId: string,
|
|
16
|
+
export declare function releaseFilmAdvance(env: Env, filmId: string, token: string): Promise<void>;
|
|
9
17
|
//# sourceMappingURL=film-advance-lease.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"film-advance-lease.d.ts","sourceRoot":"","sources":["../src/film-advance-lease.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oCAAoC,CAAC;AAG9D,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"film-advance-lease.d.ts","sourceRoot":"","sources":["../src/film-advance-lease.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oCAAoC,CAAC;AAG9D,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb;4FACwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;mFAI+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC,gBAAgB,CAAC,CA4B3B;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY/F"}
|
|
@@ -2,18 +2,35 @@ import { withD1Retry } from "./d1-retry.js";
|
|
|
2
2
|
export const FILM_ADVANCE_LEASE_TTL_SECONDS = 300;
|
|
3
3
|
export async function claimFilmAdvance(env, filmId, now = Date.now()) {
|
|
4
4
|
const lease = now + FILM_ADVANCE_LEASE_TTL_SECONDS * 1000;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// #29: a UNIQUE per-claim identity, generated ONCE (outside the retry closure) so every retry of
|
|
6
|
+
// THIS claim re-binds the SAME token. That is what fixes the non-idempotent-retry stall: if the
|
|
7
|
+
// first UPDATE commits (row now holds `token`) but its response is lost -> classified transient ->
|
|
8
|
+
// withD1Retry re-runs the UPDATE, the `advance_lease < now` predicate no longer matches (the lease
|
|
9
|
+
// is now in the future), but `advance_lease_token = ?` DOES match my own committed token, so the
|
|
10
|
+
// retry still returns `won: true`. The true holder no longer mistakes its own commit for a loss and
|
|
11
|
+
// wedges the film for up to a TTL. Crucially the token is UNIQUE (a UUID), not the lease value, so a
|
|
12
|
+
// genuine same-millisecond loser -- which computed a DIFFERENT token -- never matches this clause and
|
|
13
|
+
// correctly loses (value-equality on `lease` alone would let it double-win; #29 wave-5 regression).
|
|
14
|
+
const token = crypto.randomUUID();
|
|
15
|
+
const res = await withD1Retry(() => env.DB.prepare(`UPDATE renders SET advance_lease = ?, advance_lease_token = ?
|
|
16
|
+
WHERE job_id = ? AND (advance_lease IS NULL OR advance_lease < ? OR advance_lease_token = ?)`)
|
|
17
|
+
.bind(lease, token, filmId, now, token)
|
|
8
18
|
.run());
|
|
9
19
|
if ((res.meta?.changes ?? 0) === 1)
|
|
10
|
-
return { won: true, lease };
|
|
20
|
+
return { won: true, lease, token };
|
|
11
21
|
const row = await withD1Retry(() => env.DB.prepare(`SELECT 1 AS one FROM renders WHERE job_id = ?`).bind(filmId).first());
|
|
22
|
+
// A row exists but we didn't take the lease -> a live holder owns it: we lose. No row at all ->
|
|
23
|
+
// fail OPEN (a legacy/untracked advanceable film must not deadlock; #29 part 2 is host-owned --
|
|
24
|
+
// every advanceable film should have a renders row so this path is unreachable in practice).
|
|
12
25
|
return row ? { won: false } : { won: true };
|
|
13
26
|
}
|
|
14
|
-
export async function releaseFilmAdvance(env, filmId,
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
export async function releaseFilmAdvance(env, filmId, token) {
|
|
28
|
+
// Release BY TOKEN, not by lease value: only the holder whose exact UUID is still in the row clears
|
|
29
|
+
// it. A same-ms loser (or a later re-grant to a different driver) carries a different token, so a
|
|
30
|
+
// stale release is a precise no-op and can never free another driver's live lease.
|
|
31
|
+
await withD1Retry(() => env.DB.prepare(`UPDATE renders SET advance_lease = NULL, advance_lease_token = NULL
|
|
32
|
+
WHERE job_id = ? AND advance_lease_token = ?`)
|
|
33
|
+
.bind(filmId, token)
|
|
17
34
|
.run());
|
|
18
35
|
}
|
|
19
36
|
//# sourceMappingURL=film-advance-lease.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"film-advance-lease.js","sourceRoot":"","sources":["../src/film-advance-lease.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"film-advance-lease.js","sourceRoot":"","sources":["../src/film-advance-lease.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAelD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAQ,EACR,MAAc,EACd,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,KAAK,GAAG,GAAG,GAAG,8BAA8B,GAAG,IAAI,CAAC;IAC1D,iGAAiG;IACjG,gGAAgG;IAChG,mGAAmG;IACnG,mGAAmG;IACnG,iGAAiG;IACjG,oGAAoG;IACpG,qGAAqG;IACrG,sGAAsG;IACtG,oGAAoG;IACpG,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,CACjC,GAAG,CAAC,EAAE,CAAC,OAAO,CACZ;kGAC4F,CAC7F;SACE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;SACtC,GAAG,EAAE,CACT,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,CACjC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CACrF,CAAC;IACF,gGAAgG;IAChG,gGAAgG;IAChG,6FAA6F;IAC7F,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAQ,EAAE,MAAc,EAAE,KAAa;IAC9E,oGAAoG;IACpG,kGAAkG;IAClG,mFAAmF;IACnF,MAAM,WAAW,CAAC,GAAG,EAAE,CACrB,GAAG,CAAC,EAAE,CAAC,OAAO,CACZ;oDAC8C,CAC/C;SACE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;SACnB,GAAG,EAAE,CACT,CAAC;AACJ,CAAC"}
|
|
@@ -2099,9 +2099,9 @@ export async function advanceFilmJob(env, filmId) {
|
|
|
2099
2099
|
throw e;
|
|
2100
2100
|
}
|
|
2101
2101
|
finally {
|
|
2102
|
-
if (claim.
|
|
2102
|
+
if (claim.token !== undefined) {
|
|
2103
2103
|
try {
|
|
2104
|
-
await releaseFilmAdvance(env, filmId, claim.
|
|
2104
|
+
await releaseFilmAdvance(env, filmId, claim.token);
|
|
2105
2105
|
}
|
|
2106
2106
|
catch (e) {
|
|
2107
2107
|
console.warn(`film ${filmId}: advance lease release failed (${e.message}); it expires on its own`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyphusion-labs/vivijure-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared Vivijure orchestration: module registry, film pipeline,
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared Vivijure orchestration unifying vivijure-cf and vivijure-local: module registry, film pipeline, Platform ICD.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/skyphusion-labs/vivijure-core",
|
|
@@ -54,8 +54,6 @@
|
|
|
54
54
|
"typecheck": "tsc --noEmit",
|
|
55
55
|
"test": "vitest run",
|
|
56
56
|
"test:coverage": "vitest run --coverage.enabled=true --coverage.provider=v8 --coverage.reporter=cobertura --coverage.reporter=text",
|
|
57
|
-
"parity:vivijure": "bash scripts/parity-with-vivijure.sh",
|
|
58
|
-
"parity:vivijure:strict": "bash scripts/parity-with-vivijure.sh --strict",
|
|
59
57
|
"sync:module-types": "cp ../vivijure/src/modules/types.ts src/modules/types.ts",
|
|
60
58
|
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
61
59
|
},
|