@volter/editor-live 0.5.63 → 0.5.65

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/BUNDLED_NOTICES CHANGED
@@ -3,8 +3,9 @@ Bundled license notices
3
3
  These notices accompany code incorporated into this package. Separate npm dependencies retain their own licenses.
4
4
 
5
5
 
6
- @volter/editor-project@0.5.63
6
+ @volter/editor-project@0.5.65
7
7
  Declared license: Apache-2.0
8
+ Source: the unmodified npm package @volter/editor-project@0.5.65, from https://github.com/volter-ai/editor.git
8
9
 
9
10
  --- packages/editor-project/LICENSE ---
10
11
 
@@ -212,8 +213,9 @@ Declared license: Apache-2.0
212
213
  limitations under the License.
213
214
 
214
215
 
215
- @volter/editor-sdk@0.5.63
216
+ @volter/editor-sdk@0.5.65
216
217
  Declared license: Apache-2.0
218
+ Source: the unmodified npm package @volter/editor-sdk@0.5.65, from https://github.com/volter-ai/editor.git
217
219
 
218
220
  --- packages/editor-sdk/LICENSE ---
219
221
 
package/dist/index.js CHANGED
@@ -168,7 +168,7 @@ var EditorClient = class {
168
168
  commandDispatcher = void 0;
169
169
  if (error?.name === "TimeoutError") {
170
170
  throw new EditorCommandError(
171
- `The editor at ${this.baseUrl} never answered "${type}" within ${Math.round(deadlineMs / 1e3)}s \u2014 past every server-side budget, so the server itself is not answering. Check the terminal running \`volter-editor edit\`.`,
171
+ `The editor at ${this.baseUrl} never answered "${type}" within ${Math.round(deadlineMs / 1e3)}s \u2014 past every server-side budget, so the server itself is not answering. Check the terminal that started this editor session.`,
172
172
  void 0,
173
173
  true
174
174
  );
@@ -180,7 +180,7 @@ var EditorClient = class {
180
180
  continue;
181
181
  }
182
182
  throw new EditorCommandError(
183
- `POST ${url} ("${type}") never reached the editor: ${code}${detail ? ` (${detail})` : ""}.` + (retried ? ` Retried once after ${TRANSPORT_RETRY_DELAY_MS}ms; it failed the same way.` : "") + (options?.retryTransport === true ? "" : " Not retried automatically: this command can change editor state, and a socket that died after the request was written cannot prove the editor did not already run it.") + " A transport failure means the port stopped answering, not that the editor refused \u2014 the dev server restarts on any watched source edit, and it shuts itself down after an idle window. Check the terminal running `volter-editor edit` and confirm the port this client resolved.",
183
+ `POST ${url} ("${type}") never reached the editor: ${code}${detail ? ` (${detail})` : ""}.` + (retried ? ` Retried once after ${TRANSPORT_RETRY_DELAY_MS}ms; it failed the same way.` : "") + (options?.retryTransport === true ? "" : " Not retried automatically: this command can change editor state, and a socket that died after the request was written cannot prove the editor did not already run it.") + " A transport failure means the port stopped answering, not that the editor refused \u2014 the dev server restarts on any watched source edit, and it shuts itself down after an idle window. Check the terminal that started this editor session and confirm the port this client resolved.",
184
184
  code,
185
185
  false
186
186
  );
@@ -1017,7 +1017,7 @@ var EditorClient = class {
1017
1017
  if (error?.name === "TimeoutError") throw error;
1018
1018
  const { code, detail } = describeFetchFailure(error);
1019
1019
  throw new EditorCommandError(
1020
- `${init?.method ?? "GET"} ${url} never reached the editor: ${code}${detail ? ` (${detail})` : ""}. Nothing answered on that port \u2014 check the terminal running \`volter-editor edit\` and confirm the port this client resolved.`,
1020
+ `${init?.method ?? "GET"} ${url} never reached the editor: ${code}${detail ? ` (${detail})` : ""}. Nothing answered on that port \u2014 check the terminal that started this editor session and confirm the port this client resolved.`,
1021
1021
  code,
1022
1022
  false
1023
1023
  );
@@ -1033,7 +1033,7 @@ var EditorClient = class {
1033
1033
  const contentType = res.headers.get("content-type") ?? "";
1034
1034
  if (!contentType.includes("application/json")) {
1035
1035
  throw new Error(
1036
- `The editor at ${this.baseUrl} answered with its page fallback (${contentType || "no content-type"}) rather than JSON, so no editor server handled the request. Check that this URL is a running \`volter-editor edit\` session.`
1036
+ `The editor at ${this.baseUrl} answered with its page fallback (${contentType || "no content-type"}) rather than JSON, so no editor server handled the request. Check that this URL is a running editor session.`
1037
1037
  );
1038
1038
  }
1039
1039
  const body = await res.json();
@@ -1786,8 +1786,8 @@ function lazyChainProxy(resolveRoot, path = []) {
1786
1786
  }
1787
1787
 
1788
1788
  // src/session.ts
1789
- import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
1790
- import { dirname, join as join3, resolve as resolve2 } from "node:path";
1789
+ import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
1790
+ import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
1791
1791
 
1792
1792
  // ../editor-project/src/manifest/locate.ts
1793
1793
  import { existsSync } from "node:fs";
@@ -1812,18 +1812,172 @@ function resolveManifestPath(dir) {
1812
1812
  return join(dir, MANIFEST_FILENAME);
1813
1813
  }
1814
1814
 
1815
+ // ../editor-sdk/src/session/product-locator.ts
1816
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
1817
+ import { createRequire } from "node:module";
1818
+ import { dirname, join as join2 } from "node:path";
1819
+ import { pathToFileURL } from "node:url";
1820
+ var PRODUCT_DECLARATION_KEY = "vgai.product";
1821
+ var PRODUCT_INSTALL_LINES = [" npm install --save-dev @volter/editor"];
1822
+ var PRODUCT_CREATE_LINES = [" npx @volter/editor create <name>"];
1823
+ var PRODUCT_COLOR_THEMES = ["dark", "light"];
1824
+ function readProductManifest(packageDir) {
1825
+ const manifestPath = join2(packageDir, "package.json");
1826
+ let manifest;
1827
+ try {
1828
+ manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
1829
+ } catch {
1830
+ return null;
1831
+ }
1832
+ const declared = manifest.vgai?.product;
1833
+ if (declared === void 0) return null;
1834
+ const entry = declared.entry;
1835
+ if (typeof entry !== "string" || !entry.startsWith("./"))
1836
+ throw new Error(
1837
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its "entry" is ${JSON.stringify(entry)}. It must be a ./-relative module \u2014 the product's ONE source entry, which is also its frame entry (e.g. "./src/index.ts").`
1838
+ );
1839
+ const resolved = join2(packageDir, entry);
1840
+ if (!existsSync2(resolved))
1841
+ throw new Error(
1842
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY}.entry ${JSON.stringify(entry)}, which does not exist (${resolved}).`
1843
+ );
1844
+ const colorTheme = declared.colorTheme;
1845
+ if (typeof colorTheme !== "string" || !PRODUCT_COLOR_THEMES.includes(colorTheme))
1846
+ throw new Error(
1847
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its "colorTheme" is ${JSON.stringify(colorTheme)}. It must be ${PRODUCT_COLOR_THEMES.map(
1848
+ (one) => JSON.stringify(one)
1849
+ ).join(" or ")} \u2014 which way round this product paints, read before any of its code runs so the workbench's first frame is already the product's own background.`
1850
+ );
1851
+ const bin = manifest.bin;
1852
+ const commands = bin !== null && typeof bin === "object" ? Object.keys(bin) : [];
1853
+ if (commands.length !== 1)
1854
+ throw new Error(
1855
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its "bin" names ${commands.length === 0 ? "no command" : `${commands.length} commands`}. A product has exactly one command \u2014 the one every session message tells a person to run.`
1856
+ );
1857
+ const displayName = declared.displayName;
1858
+ if (typeof displayName !== "string" || displayName.trim() === "")
1859
+ throw new Error(
1860
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its "displayName" is ${JSON.stringify(displayName)}. It must be the name a person sees (e.g. "Volter Editor").`
1861
+ );
1862
+ return {
1863
+ name: typeof manifest.name === "string" ? manifest.name : packageDir,
1864
+ version: typeof manifest.version === "string" ? manifest.version : "unknown",
1865
+ command: commands[0],
1866
+ displayName,
1867
+ dir: packageDir,
1868
+ entry,
1869
+ colorTheme,
1870
+ // A private source product may use an explicitly selected matching checkout.
1871
+ // Public packages must always carry a downloadable, checksummed workbench.
1872
+ workbench: manifest.private === true && declared.workbench === void 0 ? null : readProductWorkbench(manifestPath, declared.workbench)
1873
+ };
1874
+ }
1875
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
1876
+ function readProductWorkbench(manifestPath, declared) {
1877
+ const example = ' "vgai": { "product": { "workbench": { "release": "<product>-<fork sha 12>-<platform>", "tarballSha256": "<64 hex>" } } }\n`node scripts/workbench/build-release.mjs --publish --out <release dir>` cuts the release and prints exactly that block.';
1878
+ if (typeof declared !== "object" || declared === null || Array.isArray(declared))
1879
+ throw new Error(
1880
+ `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its "workbench" is ${JSON.stringify(declared)}. A product IS the Code-OSS workbench it was compiled into, so it names the published release those bytes are:
1881
+ ${example}`
1882
+ );
1883
+ const record = declared;
1884
+ const extra = Object.keys(record).filter((key) => key !== "release" && key !== "tarballSha256");
1885
+ if (extra.length > 0)
1886
+ throw new Error(
1887
+ `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench carries ${extra.map((key) => `"${key}"`).join(", ")}, which this declaration has no meaning for. It names the release and the pin, and nothing else:
1888
+ ${example}`
1889
+ );
1890
+ const release = record["release"];
1891
+ if (typeof release !== "string" || release.trim() === "")
1892
+ throw new Error(
1893
+ `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench names no "release" \u2014 the GitHub Release tag its workbench was published under:
1894
+ ${example}`
1895
+ );
1896
+ const tarballSha256 = record["tarballSha256"];
1897
+ if (typeof tarballSha256 !== "string" || !SHA256_HEX.test(tarballSha256))
1898
+ throw new Error(
1899
+ `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench has tarballSha256 ${JSON.stringify(tarballSha256)}, which is not 64 hex characters. It is the PIN \u2014 the hash the downloaded tarball is checked against before a byte of it is extracted, so it is never approximate:
1900
+ ${example}`
1901
+ );
1902
+ return { release, tarballSha256 };
1903
+ }
1904
+ function declaredVgaiPackages(projectRoot) {
1905
+ let manifest;
1906
+ try {
1907
+ manifest = JSON.parse(
1908
+ readFileSync(join2(projectRoot, "package.json"), "utf8")
1909
+ );
1910
+ } catch {
1911
+ return [];
1912
+ }
1913
+ return Object.keys({ ...manifest.dependencies ?? {}, ...manifest.devDependencies ?? {} }).filter((name) => name.startsWith("@volter/")).sort();
1914
+ }
1915
+ function locatePackage(projectRoot, name) {
1916
+ const require2 = createRequire(pathToFileURL(join2(projectRoot, "package.json")));
1917
+ try {
1918
+ return dirname(require2.resolve(`${name}/package.json`));
1919
+ } catch {
1920
+ try {
1921
+ let dir = dirname(require2.resolve(name));
1922
+ for (; ; ) {
1923
+ const candidate = join2(dir, "package.json");
1924
+ try {
1925
+ if (JSON.parse(readFileSync(candidate, "utf8")).name === name)
1926
+ return dir;
1927
+ } catch {
1928
+ }
1929
+ const parent = dirname(dir);
1930
+ if (parent === dir) return null;
1931
+ dir = parent;
1932
+ }
1933
+ } catch {
1934
+ return null;
1935
+ }
1936
+ }
1937
+ }
1938
+ function declaredProducts(projectRoot) {
1939
+ const found = [];
1940
+ for (const name of declaredVgaiPackages(projectRoot)) {
1941
+ const dir = locatePackage(projectRoot, name);
1942
+ if (dir === null) continue;
1943
+ const product = readProductManifest(dir);
1944
+ if (product !== null) found.push(product);
1945
+ }
1946
+ return found;
1947
+ }
1948
+ function resolveProductForProject(projectRoot) {
1949
+ const products = declaredProducts(projectRoot);
1950
+ if (products.length === 1) return products[0];
1951
+ if (products.length === 0)
1952
+ throw new Error(
1953
+ `${projectRoot} declares no editor product, so there is no editor to open it in.
1954
+ The installed product supplies the editor,
1955
+ and which one runs is what the project installed, never a flag.
1956
+ For THIS folder, install the one you are building with:
1957
+ ${PRODUCT_INSTALL_LINES.join("\n")}
1958
+ then run that product's \`edit\` command again. For a NEW project, the product creates it:
1959
+ ${PRODUCT_CREATE_LINES.join("\n")}`
1960
+ );
1961
+ throw new Error(
1962
+ `${projectRoot} declares ${products.length} editor products \u2014 ${products.map((product) => product.name).join(" and ")}.
1963
+ One page runs one product, and nothing here may choose between them. Remove the one you
1964
+ are not building with:
1965
+ ` + products.map((product) => ` npm uninstall ${product.name}`).join("\n")
1966
+ );
1967
+ }
1968
+
1815
1969
  // ../editor-sdk/src/session/registry-format.ts
1816
1970
  import {
1817
1971
  mkdirSync,
1818
1972
  readdirSync,
1819
- readFileSync,
1973
+ readFileSync as readFileSync2,
1820
1974
  realpathSync,
1821
1975
  unlinkSync,
1822
1976
  writeFileSync
1823
1977
  } from "node:fs";
1824
1978
  import { homedir } from "node:os";
1825
- import { join as join2, resolve } from "node:path";
1826
- var EDITOR_SESSIONS_REGISTRY_FILE = join2(homedir(), ".vgai", "editor-sessions.json");
1979
+ import { join as join3, resolve } from "node:path";
1980
+ var EDITOR_SESSIONS_REGISTRY_FILE = join3(homedir(), ".vgai", "editor-sessions.json");
1827
1981
  function isEditorSessionEntry(v) {
1828
1982
  if (typeof v !== "object" || v === null) return false;
1829
1983
  const s = v;
@@ -1854,7 +2008,7 @@ function pidAlive(pid) {
1854
2008
  }
1855
2009
  function readLiveRegisteredSessions() {
1856
2010
  try {
1857
- const raw = JSON.parse(readFileSync(EDITOR_SESSIONS_REGISTRY_FILE, "utf8"));
2011
+ const raw = JSON.parse(readFileSync2(EDITOR_SESSIONS_REGISTRY_FILE, "utf8"));
1858
2012
  return Array.isArray(raw) ? raw.filter(isEditorSessionEntry).map(normalizeEditorSessionEntry).filter((s) => pidAlive(s.pid)) : [];
1859
2013
  } catch {
1860
2014
  return [];
@@ -1933,8 +2087,8 @@ var HttpSessionDiscovery = class {
1933
2087
  function findProjectRootFrom(dir) {
1934
2088
  let cur = resolve2(dir);
1935
2089
  for (; ; ) {
1936
- if (existsSync2(resolveManifestPath(cur))) return cur;
1937
- const parent = dirname(cur);
2090
+ if (existsSync3(resolveManifestPath(cur))) return cur;
2091
+ const parent = dirname2(cur);
1938
2092
  if (parent === cur) return null;
1939
2093
  cur = parent;
1940
2094
  }
@@ -1949,7 +2103,7 @@ function canonicalPath(path) {
1949
2103
  function readProjectSession(projectRoot) {
1950
2104
  try {
1951
2105
  const value = JSON.parse(
1952
- readFileSync2(join3(projectRoot, ".vgai", "session.json"), "utf8")
2106
+ readFileSync3(join4(projectRoot, ".vgai", "session.json"), "utf8")
1953
2107
  );
1954
2108
  if (typeof value !== "object" || value === null) return null;
1955
2109
  const hint = value;
@@ -1981,12 +2135,12 @@ ${manifestError}`
1981
2135
  function noMatchingSessionRefusal(projectRoot, canon, sessions, discoveryFailure) {
1982
2136
  if (discoveryFailure !== null) {
1983
2137
  return new Error(
1984
- `@volter/editor-live: could not READ the editor session registry while looking for ${projectRoot} \u2014 ${discoveryFailure}. This is not the answer "no editor is running": the question went unanswered, so nothing is known about what is live. Retry (a probe can time out while the box is loaded); if it keeps failing, \`vgai sessions\` asks the same question directly.`
2138
+ `@volter/editor-live: could not READ the editor session registry while looking for ${projectRoot} \u2014 ${discoveryFailure}. This is not the answer "no editor is running": the question went unanswered, so nothing is known about what is live. Retry (a probe can time out while the box is loaded); if it keeps failing, ${sessionsCommandFor(projectRoot)} asks the same question directly.`
1985
2139
  );
1986
2140
  }
1987
2141
  if (sessions.length === 0) {
1988
2142
  return new Error(
1989
- `@volter/editor-live: the editor session registry is readable and lists NO live sessions, so none covers ${projectRoot}. @volter/editor-live only attaches to an already-running session \u2014 it never starts one \u2014 so run \`volter-editor edit\` in that project first, then retry.`
2143
+ `@volter/editor-live: the editor session registry is readable and lists NO live sessions, so none covers ${projectRoot}. @volter/editor-live only attaches to an already-running session \u2014 it never starts one \u2014 so run ${editCommandFor(projectRoot)} in that project first, then retry.`
1990
2144
  );
1991
2145
  }
1992
2146
  const listed = sessions.map((s) => ` port ${s.port} \u2192 ${s.project === null ? "(no project)" : s.project}`).join("\n");
@@ -1995,7 +2149,7 @@ function noMatchingSessionRefusal(projectRoot, canon, sessions, discoveryFailure
1995
2149
  looking for (resolved): ${canon}
1996
2150
  live sessions:
1997
2151
  ${listed}
1998
- If one of those is meant to be this project, the two paths differ after resolution \u2014 the usual cause is a git worktree or a symlink, where the session was opened through a different path to the same files. Run \`volter-editor edit\` from THIS path, or use the path the session lists.`
2152
+ If one of those is meant to be this project, the two paths differ after resolution \u2014 the usual cause is a git worktree or a symlink, where the session was opened through a different path to the same files. Run ${editCommandFor(projectRoot)} from THIS path, or use the path the session lists.`
1999
2153
  );
2000
2154
  }
2001
2155
  async function resolveSession(projectDir = process.cwd(), deps = {}) {
@@ -2004,7 +2158,7 @@ async function resolveSession(projectDir = process.cwd(), deps = {}) {
2004
2158
  const projectRoot = findRoot(projectDir);
2005
2159
  if (projectRoot === null) {
2006
2160
  throw new Error(
2007
- `@volter/editor-live: no vgai.project.json found in ${projectDir} or any parent directory \u2014 is this a vgai project?`
2161
+ `@volter/editor-live: no vgai.project.json found in ${projectDir} or any parent directory \u2014 is this a project?`
2008
2162
  );
2009
2163
  }
2010
2164
  const localHint = (deps.readProjectSession ?? readProjectSession)(projectRoot);
@@ -2039,6 +2193,19 @@ async function resolveSession(projectDir = process.cwd(), deps = {}) {
2039
2193
  if (match.manifestError != null) throw manifestRefusal(projectRoot, match.manifestError);
2040
2194
  return { port: match.port, projectRoot };
2041
2195
  }
2196
+ function editCommandFor(projectRoot) {
2197
+ return verbFor(projectRoot, "edit");
2198
+ }
2199
+ function sessionsCommandFor(projectRoot) {
2200
+ return verbFor(projectRoot, "sessions");
2201
+ }
2202
+ function verbFor(projectRoot, verb) {
2203
+ try {
2204
+ return `\`${resolveProductForProject(projectRoot).command} ${verb}\``;
2205
+ } catch {
2206
+ return `its product's \`${verb}\` command`;
2207
+ }
2208
+ }
2042
2209
 
2043
2210
  // src/singleton.ts
2044
2211
  function createLazySession(factory) {