symbio 0.7.0 → 0.7.1
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/CHANGELOG.md +9 -0
- package/bin/symbio.js +19 -2
- package/dist/server/images.js +13 -1
- package/dist/server/self-update.js +22 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ versions follow [Semantic Versioning](https://semver.org/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.7.1]
|
|
9
|
+
|
|
10
|
+
- On a Node.js older than 22, `sym` says what to install instead of Symbio crashing at start
|
|
11
|
+
(npm installs it anyway and only warns).
|
|
12
|
+
- Automatic updates and `sym update` don't install a version that needs a newer Node.js than the
|
|
13
|
+
machine has.
|
|
14
|
+
- The image library loads when an agent first views an image, so a problem with it affects only
|
|
15
|
+
`view_image`, not Symbio's start.
|
|
16
|
+
|
|
8
17
|
## [0.7.0]
|
|
9
18
|
|
|
10
19
|
- Updates: `sym update` installs the latest version and restarts Symbio once the team is idle
|
package/bin/symbio.js
CHANGED
|
@@ -19,6 +19,18 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
19
19
|
|
|
20
20
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
21
21
|
const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
22
|
+
|
|
23
|
+
// npm only warns when Node is older than package.json's "engines"; without this, Symbio would
|
|
24
|
+
// crash on start with a syntax error from a dependency (seen with Node 18).
|
|
25
|
+
const MIN_NODE = Number(/\d+/.exec(pkg.engines?.node ?? "22")?.[0] ?? 22);
|
|
26
|
+
if (Number(process.versions.node.split(".")[0]) < MIN_NODE && !["-v", "--version", "version", "-h", "--help", "help"].includes(process.argv[2])) {
|
|
27
|
+
console.error(
|
|
28
|
+
`Symbio ${pkg.version} needs Node.js ${MIN_NODE} or newer, and this is Node.js ${process.versions.node}.\n` +
|
|
29
|
+
`Install the current LTS (https://nodejs.org, or on Windows: winget install OpenJS.NodeJS.LTS), open a new terminal,\n` +
|
|
30
|
+
`then reinstall Symbio on it: npm install -g symbio`,
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
22
34
|
const PORT = Number(process.env.SYMBIO_PORT ?? 4310);
|
|
23
35
|
const HOME = process.env.SYMBIO_HOME ?? path.join(os.homedir(), ".symbio");
|
|
24
36
|
const LOG = path.join(HOME, "logs", "symbio.log");
|
|
@@ -208,15 +220,20 @@ async function update() {
|
|
|
208
220
|
console.log(`This Symbio runs from a development folder (${install.root}). Update it there: git pull, npm install, npm run build.`);
|
|
209
221
|
return;
|
|
210
222
|
}
|
|
211
|
-
let
|
|
223
|
+
let release;
|
|
212
224
|
try {
|
|
213
|
-
|
|
225
|
+
release = await self.latestRelease();
|
|
214
226
|
} catch (err) {
|
|
215
227
|
console.error(`Couldn't check for updates: ${err.message}`);
|
|
216
228
|
process.exit(1);
|
|
217
229
|
}
|
|
230
|
+
const latest = release.version;
|
|
218
231
|
if (self.compareVersions(latest, pkg.version) <= 0) return console.log(`Symbio ${pkg.version} is up to date.`);
|
|
219
232
|
console.log(`Symbio ${latest} is available (you have ${pkg.version}).`);
|
|
233
|
+
if (!self.nodeSatisfies(release.node)) {
|
|
234
|
+
console.error(`It needs Node.js ${release.node}, and this is Node.js ${process.versions.node}. Update Node.js first (https://nodejs.org), then run "sym update".`);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
220
237
|
const resultFile = path.join(HOME, "update-result.json");
|
|
221
238
|
const before = existsSync(resultFile) ? readFileSync(resultFile, "utf8") : "";
|
|
222
239
|
|
package/dist/server/images.js
CHANGED
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
// big to send at all. A crop shows one region at full resolution when details matter.
|
|
4
4
|
import { existsSync, promises as fs } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import sharp from "sharp";
|
|
7
6
|
import { IMAGE_EXTENSIONS, isSecretPath } from "./guard.js";
|
|
8
7
|
export const DEFAULT_IMAGE_SIZE = 1024;
|
|
9
8
|
const MAX_IMAGE_SIZE = 2048;
|
|
10
9
|
const MAX_BYTES = 1_500_000; // what we send back, after scaling
|
|
10
|
+
/**
|
|
11
|
+
* The image library is native and loaded on first use, so a problem with it (a platform without
|
|
12
|
+
* prebuilt binaries, an old Node) costs only view_image, not Symbio's startup.
|
|
13
|
+
*/
|
|
14
|
+
async function loadSharp() {
|
|
15
|
+
try {
|
|
16
|
+
return (await import("sharp")).default;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
throw new Error(`Images can't be viewed on this machine: the image library didn't load (${err.message.split("\n")[0]}).`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
11
22
|
export async function viewImage(folder, input) {
|
|
12
23
|
const given = String(input.path ?? "").trim();
|
|
13
24
|
if (!given)
|
|
@@ -21,6 +32,7 @@ export async function viewImage(folder, input) {
|
|
|
21
32
|
if (!existsSync(file))
|
|
22
33
|
throw new Error(`No such file: ${file}`);
|
|
23
34
|
const size = Math.min(MAX_IMAGE_SIZE, Math.max(64, Math.round(Number(input.max_size) || DEFAULT_IMAGE_SIZE)));
|
|
35
|
+
const sharp = await loadSharp();
|
|
24
36
|
let image = sharp(await fs.readFile(file), { animated: false }).rotate(); // apply EXIF orientation
|
|
25
37
|
const meta = await image.metadata();
|
|
26
38
|
const width = meta.autoOrient?.width ?? meta.width ?? 0;
|
|
@@ -45,15 +45,26 @@ export function compareVersions(a, b) {
|
|
|
45
45
|
}
|
|
46
46
|
// Overridable for tests (a local registry and package file); never set in normal use.
|
|
47
47
|
const REGISTRY = (process.env.SYMBIO_UPDATE_REGISTRY ?? "https://registry.npmjs.org").replace(/\/+$/, "");
|
|
48
|
-
/** The version npm installs as `symbio@latest
|
|
49
|
-
export async function
|
|
48
|
+
/** The version npm installs as `symbio@latest`, and the Node.js it needs. */
|
|
49
|
+
export async function latestRelease() {
|
|
50
50
|
const res = await fetch(`${REGISTRY}/symbio/latest`, { signal: AbortSignal.timeout(15_000), headers: { accept: "application/json" } });
|
|
51
51
|
if (!res.ok)
|
|
52
52
|
throw new Error(`npm registry answered ${res.status}`);
|
|
53
53
|
const data = (await res.json());
|
|
54
54
|
if (!data.version)
|
|
55
55
|
throw new Error("npm registry gave no version");
|
|
56
|
-
return data.version;
|
|
56
|
+
return { version: data.version, node: data.engines?.node ?? null };
|
|
57
|
+
}
|
|
58
|
+
export async function latestVersion() {
|
|
59
|
+
return (await latestRelease()).version;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether this Node.js meets a release's requirement (">=22", ">=20.9.0"; anything fancier is
|
|
63
|
+
* assumed met). npm only warns about it, and the new version would then fail to start.
|
|
64
|
+
*/
|
|
65
|
+
export function nodeSatisfies(range, version = process.versions.node) {
|
|
66
|
+
const m = range && /^\s*>=\s*v?(\d+(?:\.\d+){0,2})\s*$/.exec(range);
|
|
67
|
+
return !m || compareVersions(version, m[1].split(".").concat(["0", "0"]).slice(0, 3).join(".")) >= 0;
|
|
57
68
|
}
|
|
58
69
|
/** Changelog entries newer than `current`, from the latest version's CHANGELOG.md (best effort). */
|
|
59
70
|
export async function changesSince(current, latest) {
|
|
@@ -88,6 +99,8 @@ const FAILED_PAUSE_MS = 24 * 60 * 60_000; // after a failed update, don't retry
|
|
|
88
99
|
export class SelfUpdate {
|
|
89
100
|
host;
|
|
90
101
|
latest = null;
|
|
102
|
+
/** The Node.js the latest version needs (its package.json "engines"). */
|
|
103
|
+
latestNode = null;
|
|
91
104
|
checkedAt = null;
|
|
92
105
|
error = null;
|
|
93
106
|
notes = [];
|
|
@@ -158,7 +171,9 @@ export class SelfUpdate {
|
|
|
158
171
|
if (this.mode === "off" && this.checkedAt)
|
|
159
172
|
return this.state();
|
|
160
173
|
try {
|
|
161
|
-
|
|
174
|
+
const release = await latestRelease();
|
|
175
|
+
this.latest = release.version;
|
|
176
|
+
this.latestNode = release.node;
|
|
162
177
|
this.error = null;
|
|
163
178
|
this.notes = compareVersions(this.latest, this.host.version) > 0 ? await changesSince(this.host.version, this.latest) : [];
|
|
164
179
|
}
|
|
@@ -172,6 +187,9 @@ export class SelfUpdate {
|
|
|
172
187
|
blocker(forced) {
|
|
173
188
|
if (this.install.kind === "dev")
|
|
174
189
|
return "This Symbio runs from a development folder; update it with git pull, npm install and npm run build.";
|
|
190
|
+
if (!nodeSatisfies(this.latestNode)) {
|
|
191
|
+
return `Symbio ${this.latest} needs Node.js ${this.latestNode}, and this machine has ${process.versions.node}: update Node.js first (https://nodejs.org), then update Symbio.`;
|
|
192
|
+
}
|
|
175
193
|
if (this.applying)
|
|
176
194
|
return "Already updating.";
|
|
177
195
|
if (!forced && this.mode === "notify")
|