hyperframes 0.8.77 → 0.8.79
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/cli.js +2942 -1786
- package/dist/fontLocalizeCli.js +99 -33
- package/dist/hyperframe-runtime.js +54 -54
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +54 -54
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/registry/localSemantic.js +20 -1
- package/dist/skills/hyperframes/SKILL.md +6 -0
- package/dist/skills/hyperframes-cli/SKILL.md +14 -1
- package/dist/skills/media-use/registry/localSemantic.js +20 -1
- package/dist/skills/media-use/scripts/compatibility.test.mjs +2 -0
- package/dist/skills/media-use/scripts/lib/cache.mjs +6 -8
- package/dist/skills/media-use/scripts/lib/candidates.test.mjs +2 -2
- package/dist/skills/media-use/scripts/lib/manifest.test.mjs +2 -0
- package/dist/skills/media-use/scripts/lib/media-home.mjs +20 -0
- package/dist/skills/media-use/scripts/lib/media-home.test.mjs +61 -0
- package/dist/skills/media-use/scripts/lib/misses.mjs +6 -6
- package/dist/skills/media-use/scripts/lib/misses.test.mjs +12 -1
- package/dist/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
- package/dist/skills/media-use/scripts/lib/prefs-store.test.mjs +1 -1
- package/dist/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
- package/dist/skills/media-use/scripts/lib/recipe-store.test.mjs +1 -1
- package/dist/skills/media-use/scripts/lib/stats.test.mjs +1 -1
- package/dist/skills/media-use/scripts/lib/telemetry.mjs +2 -1
- package/dist/skills/media-use/scripts/lib/telemetry.test.mjs +1 -0
- package/dist/skills/registry/localSemantic.js +20 -1
- package/dist/studio/assets/hyperframes-player-Bx_Kw4Ey.js +464 -0
- package/dist/studio/assets/{index-DcxWQ7Za.js → index-CJP92F4-.js} +1 -1
- package/dist/studio/assets/{index-2GgHjQq-.js → index-DBKySIo7.js} +187 -187
- package/dist/studio/assets/{index-CsFCRdMo.js → index-ORZXYB0k.js} +1 -1
- package/dist/studio/{chunk-5VANA2TG.js → chunk-I7XNAIOF.js} +2 -1
- package/dist/studio/{domEditingLayers-IGPLTI5E.js → domEditingLayers-MHFDKAQ7.js} +2 -2
- package/dist/studio/index.d.ts +23 -15
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +459 -257
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/studio/assets/hyperframes-player-BSDTJ9EF.js +0 -464
- /package/dist/studio/{chunk-5VANA2TG.js.map → chunk-I7XNAIOF.js.map} +0 -0
- /package/dist/studio/{domEditingLayers-IGPLTI5E.js.map → domEditingLayers-MHFDKAQ7.js.map} +0 -0
|
@@ -6,6 +6,8 @@ import { execFileSync } from "node:child_process";
|
|
|
6
6
|
import test from "node:test";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
|
|
9
|
+
process.env.HYPERFRAMES_MEDIA_HOME = mkdtempSync(join(tmpdir(), "media-use-compat-home-"));
|
|
10
|
+
|
|
9
11
|
function run(scriptsDir, script, args) {
|
|
10
12
|
return execFileSync(process.execPath, [join(scriptsDir, script), ...args], {
|
|
11
13
|
encoding: "utf8",
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "node:fs";
|
|
2
2
|
import { join, basename } from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
4
|
import { readManifest, appendRecord, normalizePrompt } from "./manifest.mjs";
|
|
5
|
+
import { globalMediaDir, mediaHome } from "./media-home.mjs";
|
|
6
|
+
|
|
7
|
+
export { globalMediaDir };
|
|
6
8
|
|
|
7
9
|
const SCHEMA_PREFIX = "mu-v1-";
|
|
8
10
|
const KEY_HEX_CHARS = 16;
|
|
9
11
|
const COMPLETE_SENTINEL = ".hf-complete";
|
|
10
12
|
|
|
11
|
-
export function globalMediaDir() {
|
|
12
|
-
return join(homedir(), ".media");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
13
|
export function contentHash(filePath) {
|
|
16
14
|
const bytes = readFileSync(filePath);
|
|
17
15
|
return createHash("sha256").update(bytes).digest("hex");
|
|
@@ -30,11 +28,11 @@ function markComplete(entryDir) {
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
// The manifest helpers append their own ".media" to the dir they get, so the
|
|
33
|
-
// global manifest must be addressed by
|
|
31
|
+
// global manifest must be addressed by mediaHome(), not by globalMediaDir() — passing
|
|
34
32
|
// the latter nested it at ~/.media/.media/manifest.jsonl, invisible to the
|
|
35
33
|
// Studio /api/assets/global route (which reads the documented flat path).
|
|
36
34
|
export function readGlobalManifest() {
|
|
37
|
-
return readManifest(
|
|
35
|
+
return readManifest(mediaHome());
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
// Resolve a content-sha (full or unambiguous prefix) to a reusable global-cache
|
|
@@ -105,7 +103,7 @@ export function cachePut(filePath, record) {
|
|
|
105
103
|
reusable: true,
|
|
106
104
|
cached_path: dest,
|
|
107
105
|
};
|
|
108
|
-
appendRecord(
|
|
106
|
+
appendRecord(mediaHome(), globalRecord);
|
|
109
107
|
return { sha, cached_path: dest };
|
|
110
108
|
}
|
|
111
109
|
|
|
@@ -6,13 +6,13 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { listCandidates, formatCandidates, CANDIDATE_CAP } from "./candidates.mjs";
|
|
7
7
|
import { findGlobalBySha } from "./cache.mjs";
|
|
8
8
|
|
|
9
|
-
// candidates + findGlobalBySha are offline (no heygen), so we can
|
|
9
|
+
// candidates + findGlobalBySha are offline (no heygen), so we can point HYPERFRAMES_MEDIA_HOME
|
|
10
10
|
// to a temp dir and seed a fake global ~/.media manifest deterministically.
|
|
11
11
|
function sandbox() {
|
|
12
12
|
const root = mkdtempSync(join(tmpdir(), "mu-cand-"));
|
|
13
13
|
const project = join(root, "proj");
|
|
14
14
|
const home = join(root, "home");
|
|
15
|
-
process.env.
|
|
15
|
+
process.env.HYPERFRAMES_MEDIA_HOME = home;
|
|
16
16
|
return { root, project, home };
|
|
17
17
|
}
|
|
18
18
|
function seedManifest(dir, records) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
// The one owner of where the person's media library lives. This repo's test runs set
|
|
5
|
+
// HYPERFRAMES_MEDIA_HOME_REQUIRED, so a test that forgot HYPERFRAMES_MEDIA_HOME fails instead
|
|
6
|
+
// of writing fixtures into the media library apps list as the person's own files.
|
|
7
|
+
export function mediaHome() {
|
|
8
|
+
const override = process.env.HYPERFRAMES_MEDIA_HOME;
|
|
9
|
+
if (override) return override;
|
|
10
|
+
if (process.env.HYPERFRAMES_MEDIA_HOME_REQUIRED) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"media-use: a test reached the real ~/.media; set HYPERFRAMES_MEDIA_HOME to a temp dir",
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return homedir();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function globalMediaDir() {
|
|
19
|
+
return join(mediaHome(), ".media");
|
|
20
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
|
|
8
|
+
import { cachePut } from "./cache.mjs";
|
|
9
|
+
import { globalMediaDir } from "./media-home.mjs";
|
|
10
|
+
|
|
11
|
+
const HOME = mkdtempSync(join(tmpdir(), "media-home-"));
|
|
12
|
+
process.env.HYPERFRAMES_MEDIA_HOME = HOME;
|
|
13
|
+
|
|
14
|
+
test("the global cache writes into the media home a test points it at", () => {
|
|
15
|
+
const work = mkdtempSync(join(tmpdir(), "media-home-src-"));
|
|
16
|
+
const file = join(work, "clip.wav");
|
|
17
|
+
writeFileSync(file, "media-home regression bytes");
|
|
18
|
+
cachePut(file, { id: "bgm_001", type: "bgm", provenance: { prompt: "media home probe" } });
|
|
19
|
+
const manifest = join(HOME, ".media", "manifest.jsonl");
|
|
20
|
+
assert.ok(existsSync(manifest), "the temp media home holds the global manifest");
|
|
21
|
+
assert.match(readFileSync(manifest, "utf8"), /media home probe/);
|
|
22
|
+
assert.equal(globalMediaDir(), join(HOME, ".media"));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// A child process that resolves the media dir, with only the given media-home variables set.
|
|
26
|
+
function runProbe(env) {
|
|
27
|
+
const lib = new URL("./media-home.mjs", import.meta.url).href;
|
|
28
|
+
const childEnv = { ...process.env };
|
|
29
|
+
delete childEnv.HYPERFRAMES_MEDIA_HOME;
|
|
30
|
+
delete childEnv.HYPERFRAMES_MEDIA_HOME_REQUIRED;
|
|
31
|
+
delete childEnv.NODE_TEST_CONTEXT;
|
|
32
|
+
const run = spawnSync(
|
|
33
|
+
process.execPath,
|
|
34
|
+
[
|
|
35
|
+
"--input-type=module",
|
|
36
|
+
"-e",
|
|
37
|
+
`const m = await import(${JSON.stringify(lib)}); console.log("dir=" + m.globalMediaDir());`,
|
|
38
|
+
],
|
|
39
|
+
{ encoding: "utf8", env: { ...childEnv, ...env } },
|
|
40
|
+
);
|
|
41
|
+
return { status: run.status, output: `${run.stdout}${run.stderr}` };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test("a repo test run that never points the media home anywhere fails instead of writing", () => {
|
|
45
|
+
const { status, output } = runProbe({ HYPERFRAMES_MEDIA_HOME_REQUIRED: "1" });
|
|
46
|
+
assert.notEqual(status, 0, output);
|
|
47
|
+
assert.match(output, /set HYPERFRAMES_MEDIA_HOME to a temp dir/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Node sets NODE_TEST_CONTEXT for every project's test runs, so another project whose own tests
|
|
51
|
+
// spawn this CLI must get the person's real library, not the guard.
|
|
52
|
+
test("another project's node --test run still resolves the real media home", () => {
|
|
53
|
+
const home = mkdtempSync(join(tmpdir(), "media-home-user-"));
|
|
54
|
+
const { status, output } = runProbe({
|
|
55
|
+
HOME: home,
|
|
56
|
+
USERPROFILE: home,
|
|
57
|
+
NODE_TEST_CONTEXT: "child-v8",
|
|
58
|
+
});
|
|
59
|
+
assert.equal(status, 0, output);
|
|
60
|
+
assert.ok(output.includes(`dir=${join(home, ".media")}`), output);
|
|
61
|
+
});
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { globalMediaDir } from "./media-home.mjs";
|
|
4
4
|
|
|
5
5
|
const MISSES_FILE = "misses.jsonl";
|
|
6
6
|
|
|
7
7
|
function missesPath() {
|
|
8
|
-
return join(
|
|
8
|
+
return join(globalMediaDir(), MISSES_FILE);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function recordMiss({ type, intent, provider_override, local_only }) {
|
|
12
12
|
try {
|
|
13
|
-
const
|
|
14
|
-
mkdirSync(
|
|
13
|
+
const path = missesPath();
|
|
14
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
15
15
|
appendFileSync(
|
|
16
|
-
|
|
16
|
+
path,
|
|
17
17
|
JSON.stringify({
|
|
18
18
|
ts: new Date().toISOString(),
|
|
19
19
|
type,
|
|
@@ -9,7 +9,7 @@ function sandbox() {
|
|
|
9
9
|
const root = mkdtempSync(join(tmpdir(), "mu-misses-"));
|
|
10
10
|
const home = join(root, "home");
|
|
11
11
|
mkdirSync(home, { recursive: true });
|
|
12
|
-
process.env.
|
|
12
|
+
process.env.HYPERFRAMES_MEDIA_HOME = home;
|
|
13
13
|
return { root, home };
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -54,6 +54,17 @@ test("recordMiss swallows filesystem failures", () => {
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
test("recordMiss stays best-effort when the media home cannot be resolved", () => {
|
|
58
|
+
const savedEnv = { ...process.env };
|
|
59
|
+
delete process.env.HYPERFRAMES_MEDIA_HOME;
|
|
60
|
+
process.env.HYPERFRAMES_MEDIA_HOME_REQUIRED = "1";
|
|
61
|
+
try {
|
|
62
|
+
assert.doesNotThrow(() => recordMiss({ type: "sfx", intent: "no home", local_only: true }));
|
|
63
|
+
} finally {
|
|
64
|
+
restoreEnv(savedEnv);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
57
68
|
test("readMisses skips corrupt lines", () => {
|
|
58
69
|
const savedEnv = { ...process.env };
|
|
59
70
|
const { root, home } = sandbox();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { globalMediaDir } from "./media-home.mjs";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Remembered defaults — the lightweight tier of HyperFrames user memory.
|
|
@@ -41,7 +41,7 @@ export function projectPrefsPath(projectDir) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function userPrefsPath() {
|
|
44
|
-
return join(
|
|
44
|
+
return join(globalMediaDir(), PREFS_FILE);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function emptyFile() {
|
|
@@ -19,7 +19,7 @@ function sandbox() {
|
|
|
19
19
|
mkdirSync(home, { recursive: true });
|
|
20
20
|
mkdirSync(projectA, { recursive: true });
|
|
21
21
|
mkdirSync(projectB, { recursive: true });
|
|
22
|
-
process.env.
|
|
22
|
+
process.env.HYPERFRAMES_MEDIA_HOME = home;
|
|
23
23
|
return { root, home, projectA, projectB };
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
rmSync,
|
|
9
9
|
writeFileSync,
|
|
10
10
|
} from "node:fs";
|
|
11
|
-
import { homedir } from "node:os";
|
|
12
11
|
import { basename, join, resolve } from "node:path";
|
|
12
|
+
import { globalMediaDir } from "./media-home.mjs";
|
|
13
13
|
import { appendRecord, mediaDir, nextId } from "./manifest.mjs";
|
|
14
14
|
import { regenerateIndex } from "./index-gen.mjs";
|
|
15
15
|
import { mergedPreferences } from "./prefs-store.mjs";
|
|
@@ -58,7 +58,7 @@ export function projectRecipesDir(projectDir) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function userRecipesDir() {
|
|
61
|
-
return join(
|
|
61
|
+
return join(globalMediaDir(), "recipes");
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export function slugifyRecipeName(name) {
|
|
@@ -83,7 +83,7 @@ function sandbox() {
|
|
|
83
83
|
mkdirSync(project, { recursive: true });
|
|
84
84
|
writeFileSync(join(project, "frame.md"), "# Frame spec\nbackground: #101014\n");
|
|
85
85
|
writeFileSync(join(project, "STORYBOARD.md"), STORYBOARD);
|
|
86
|
-
process.env.
|
|
86
|
+
process.env.HYPERFRAMES_MEDIA_HOME = home;
|
|
87
87
|
return { root, home, project };
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -11,7 +11,7 @@ function sandbox() {
|
|
|
11
11
|
const projectDir = join(root, "project");
|
|
12
12
|
mkdirSync(home, { recursive: true });
|
|
13
13
|
mkdirSync(projectDir, { recursive: true });
|
|
14
|
-
process.env.
|
|
14
|
+
process.env.HYPERFRAMES_MEDIA_HOME = home;
|
|
15
15
|
return { root, home, projectDir };
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -5,6 +5,7 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { globalMediaDir } from "./media-home.mjs";
|
|
8
9
|
|
|
9
10
|
const POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
|
|
10
11
|
const POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -72,7 +73,7 @@ function writeSharedConfig(config) {
|
|
|
72
73
|
// one — otherwise cross-surface continuity would start over on upgrade.
|
|
73
74
|
function legacyMediaAnonId() {
|
|
74
75
|
try {
|
|
75
|
-
const file = join(
|
|
76
|
+
const file = join(globalMediaDir(), "anon-id");
|
|
76
77
|
if (existsSync(file)) {
|
|
77
78
|
const id = readFileSync(file, "utf8").trim();
|
|
78
79
|
if (id) return id;
|
|
@@ -21,13 +21,14 @@ import {
|
|
|
21
21
|
mkdirSync,
|
|
22
22
|
readdirSync,
|
|
23
23
|
readFileSync,
|
|
24
|
+
realpathSync,
|
|
24
25
|
renameSync,
|
|
25
26
|
rmSync,
|
|
26
27
|
writeFileSync
|
|
27
28
|
} from "fs";
|
|
28
29
|
import { createRequire } from "module";
|
|
29
30
|
import { homedir } from "os";
|
|
30
|
-
import { basename, dirname, join } from "path";
|
|
31
|
+
import { basename, dirname, join, sep } from "path";
|
|
31
32
|
|
|
32
33
|
// src/utils/npxCommand.ts
|
|
33
34
|
function buildCmdShimCommand(name, args, platform) {
|
|
@@ -50,6 +51,8 @@ function optionalPackageDir(name, cacheDir = CACHE_DIR) {
|
|
|
50
51
|
return join(cacheDir, `${name.replace("/", "__")}@${OPTIONAL_PACKAGES[name]}`);
|
|
51
52
|
}
|
|
52
53
|
async function loadOptionalPackage(name, feature, deps = defaultDeps) {
|
|
54
|
+
const beside = deps.loadBesideCli(name);
|
|
55
|
+
if (beside !== null) return beside;
|
|
53
56
|
const dir = optionalPackageDir(name, deps.cacheDir);
|
|
54
57
|
const installed = deps.loadInstalled(dir, name);
|
|
55
58
|
if (installed !== null) return installed;
|
|
@@ -81,6 +84,21 @@ function loadInstalled(dir, name) {
|
|
|
81
84
|
if (!isInstalled(dir, name)) return null;
|
|
82
85
|
return createRequire(join(dir, "package.json"))(name);
|
|
83
86
|
}
|
|
87
|
+
function pinnedCopyBesideCli(name, cliUrl) {
|
|
88
|
+
const req = createRequire(cliUrl);
|
|
89
|
+
try {
|
|
90
|
+
const entry = realpathSync(req.resolve(name));
|
|
91
|
+
const copy = (req.resolve.paths(name) ?? []).map((dir) => join(dir, name)).find((dir) => existsSync(dir) && entry.startsWith(realpathSync(dir) + sep));
|
|
92
|
+
if (!copy) return false;
|
|
93
|
+
const manifest = readFileSync(join(copy, "package.json"), "utf-8");
|
|
94
|
+
return JSON.parse(manifest).version === OPTIONAL_PACKAGES[name];
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function loadBesideCli(name, cliUrl = import.meta.url) {
|
|
100
|
+
return pinnedCopyBesideCli(name, cliUrl) ? createRequire(cliUrl)(name) : null;
|
|
101
|
+
}
|
|
84
102
|
function runNpm(args) {
|
|
85
103
|
const npm = buildNpmCommand(args);
|
|
86
104
|
const child = spawn(npm.command, npm.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -149,6 +167,7 @@ async function install(dir, name, version, run = runNpm) {
|
|
|
149
167
|
}
|
|
150
168
|
var defaultDeps = {
|
|
151
169
|
cacheDir: CACHE_DIR,
|
|
170
|
+
loadBesideCli: (name) => loadBesideCli(name),
|
|
152
171
|
loadInstalled,
|
|
153
172
|
install,
|
|
154
173
|
log: (line) => console.error(line)
|