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.
Files changed (40) hide show
  1. package/dist/cli.js +2942 -1786
  2. package/dist/fontLocalizeCli.js +99 -33
  3. package/dist/hyperframe-runtime.js +54 -54
  4. package/dist/hyperframe.manifest.json +1 -1
  5. package/dist/hyperframe.runtime.iife.js +54 -54
  6. package/dist/hyperframes-player.global.js +3 -3
  7. package/dist/registry/localSemantic.js +20 -1
  8. package/dist/skills/hyperframes/SKILL.md +6 -0
  9. package/dist/skills/hyperframes-cli/SKILL.md +14 -1
  10. package/dist/skills/media-use/registry/localSemantic.js +20 -1
  11. package/dist/skills/media-use/scripts/compatibility.test.mjs +2 -0
  12. package/dist/skills/media-use/scripts/lib/cache.mjs +6 -8
  13. package/dist/skills/media-use/scripts/lib/candidates.test.mjs +2 -2
  14. package/dist/skills/media-use/scripts/lib/manifest.test.mjs +2 -0
  15. package/dist/skills/media-use/scripts/lib/media-home.mjs +20 -0
  16. package/dist/skills/media-use/scripts/lib/media-home.test.mjs +61 -0
  17. package/dist/skills/media-use/scripts/lib/misses.mjs +6 -6
  18. package/dist/skills/media-use/scripts/lib/misses.test.mjs +12 -1
  19. package/dist/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
  20. package/dist/skills/media-use/scripts/lib/prefs-store.test.mjs +1 -1
  21. package/dist/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
  22. package/dist/skills/media-use/scripts/lib/recipe-store.test.mjs +1 -1
  23. package/dist/skills/media-use/scripts/lib/stats.test.mjs +1 -1
  24. package/dist/skills/media-use/scripts/lib/telemetry.mjs +2 -1
  25. package/dist/skills/media-use/scripts/lib/telemetry.test.mjs +1 -0
  26. package/dist/skills/registry/localSemantic.js +20 -1
  27. package/dist/studio/assets/hyperframes-player-Bx_Kw4Ey.js +464 -0
  28. package/dist/studio/assets/{index-DcxWQ7Za.js → index-CJP92F4-.js} +1 -1
  29. package/dist/studio/assets/{index-2GgHjQq-.js → index-DBKySIo7.js} +187 -187
  30. package/dist/studio/assets/{index-CsFCRdMo.js → index-ORZXYB0k.js} +1 -1
  31. package/dist/studio/{chunk-5VANA2TG.js → chunk-I7XNAIOF.js} +2 -1
  32. package/dist/studio/{domEditingLayers-IGPLTI5E.js → domEditingLayers-MHFDKAQ7.js} +2 -2
  33. package/dist/studio/index.d.ts +23 -15
  34. package/dist/studio/index.html +1 -1
  35. package/dist/studio/index.js +459 -257
  36. package/dist/studio/index.js.map +1 -1
  37. package/package.json +1 -1
  38. package/dist/studio/assets/hyperframes-player-BSDTJ9EF.js +0 -464
  39. /package/dist/studio/{chunk-5VANA2TG.js.map → chunk-I7XNAIOF.js.map} +0 -0
  40. /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 HOME, not by globalMediaDir() — passing
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(homedir());
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(homedir(), globalRecord);
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 override HOME
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.HOME = home;
15
+ process.env.HYPERFRAMES_MEDIA_HOME = home;
16
16
  return { root, project, home };
17
17
  }
18
18
  function seedManifest(dir, records) {
@@ -34,6 +34,8 @@ import {
34
34
  promote,
35
35
  } from "./cache.mjs";
36
36
 
37
+ process.env.HYPERFRAMES_MEDIA_HOME = mkdtempSync(join(tmpdir(), "mu-media-home-"));
38
+
37
39
  let tmp;
38
40
 
39
41
  function setup() {
@@ -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 { homedir } from "node:os";
3
- import { join } from "node:path";
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(homedir(), ".media", MISSES_FILE);
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 dir = join(homedir(), ".media");
14
- mkdirSync(dir, { recursive: true });
13
+ const path = missesPath();
14
+ mkdirSync(dirname(path), { recursive: true });
15
15
  appendFileSync(
16
- join(dir, MISSES_FILE),
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.HOME = home;
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(homedir(), ".media", PREFS_FILE);
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.HOME = home;
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(homedir(), ".media", "recipes");
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.HOME = home;
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.HOME = home;
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(homedir(), ".media", "anon-id");
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;
@@ -18,6 +18,7 @@ function sandbox() {
18
18
  const home = join(root, "home");
19
19
  mkdirSync(home, { recursive: true });
20
20
  process.env.HOME = home;
21
+ process.env.HYPERFRAMES_MEDIA_HOME = home;
21
22
  return { root, home };
22
23
  }
23
24
 
@@ -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)