@phystack/screen-phyos 4.5.64-dev → 5.0.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/bin/index.js CHANGED
@@ -1,23 +1,27 @@
1
1
  #!/usr/bin/env node
2
- const { spawnSync } = require('child_process');
3
- const path = require('path');
2
+ const { spawnSync } = require("child_process");
3
+ const path = require("path");
4
4
 
5
- const BINARY = 'physcreen-manager';
5
+ const BINARY = "physcreen-manager";
6
6
  const key = `${process.platform}-${process.arch}`;
7
7
  const pkg = `@phystack/${BINARY}-${key}`;
8
8
 
9
9
  let binPath;
10
10
  try {
11
- binPath = path.join(path.dirname(require.resolve(`${pkg}/package.json`)), 'bin', BINARY);
11
+ binPath = path.join(
12
+ path.dirname(require.resolve(`${pkg}/package.json`)),
13
+ "bin",
14
+ BINARY,
15
+ );
12
16
  } catch {
13
17
  console.error(
14
18
  `Unsupported or missing platform package: ${pkg}\n` +
15
- `Platform: ${key}\n\n` +
16
- `Install manually: npm install ${pkg}\n` +
17
- `Or download: npx ${pkg}`
19
+ `Platform: ${key}\n\n` +
20
+ `Install manually: npm install ${pkg}\n` +
21
+ `Or download: bunx ${pkg}`,
18
22
  );
19
23
  process.exit(1);
20
24
  }
21
25
 
22
- const result = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
26
+ const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
23
27
  process.exit(result.status ?? 1);
package/dist/index.js ADDED
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ require("./version.js");
7
+ require("./proxy-settings.js");
8
+ const hub_client_1 = require("@phystack/hub-client");
9
+ const phy_logger_1 = require("@phystack/phy-logger");
10
+ const child_process_1 = __importDefault(require("child_process"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const screenshot_js_1 = require("./screenshot.js");
13
+ const sway_js_1 = require("./sway.js");
14
+ const udev_js_1 = __importDefault(require("./udev.js"));
15
+ const browser_js_1 = __importDefault(require("./browser.js"));
16
+ const proxy_js_1 = __importDefault(require("./proxy.js"));
17
+ const twin_types_1 = require("./types/twin.types");
18
+ const logger = new phy_logger_1.PhyLogger({
19
+ logToFile: false,
20
+ logToConsole: true,
21
+ includeTrace: true,
22
+ namespace: 'screen-phyos',
23
+ });
24
+ (async () => {
25
+ const { PHYSCREEN_DEBUG = "0" } = process.env;
26
+ if (parseInt(PHYSCREEN_DEBUG, 10) === 1) {
27
+ logger.info('Starting in PHYSCREEN_DEBUG mode');
28
+ return;
29
+ }
30
+ proxy_js_1.default.start({
31
+ port: 1234,
32
+ dport: 3000,
33
+ });
34
+ const NO_SETTINGS_URL = 'file:///var/physcreen/index.html#no-settings';
35
+ const client = await (0, hub_client_1.connectPhyClient)();
36
+ const deviceInstance = await client.getDeviceInstance();
37
+ const screenInstance = await client.getInstance();
38
+ const { properties } = screenInstance;
39
+ logger.info('Device Instance retrieved', {
40
+ spaceId: deviceInstance.twin.properties.desired.spaceId,
41
+ env: deviceInstance.twin.properties.desired.env,
42
+ displayName: deviceInstance.twin.properties.desired.displayName,
43
+ deviceSerial: deviceInstance.twin.properties.desired.deviceSerial,
44
+ accessKey: deviceInstance.twin.properties.desired.accessKey
45
+ });
46
+ logger.info('Screen Instance retrieved', {
47
+ screenId: screenInstance.id,
48
+ desiredProperties: screenInstance.properties.desired
49
+ });
50
+ if (properties.desired && properties.desired.url) {
51
+ const cachedUrl = properties.desired.url || NO_SETTINGS_URL;
52
+ logger.info('Opening cached URL', { url: cachedUrl });
53
+ await browser_js_1.default.open(cachedUrl);
54
+ }
55
+ const HEARTBEAT_PATH = '/tmp/physcreen-heartbeat';
56
+ setInterval(() => {
57
+ fs_1.default.writeFileSync(HEARTBEAT_PATH, new Date().toISOString());
58
+ }, 1000);
59
+ const emitReportedProperties = async (data, status) => {
60
+ const payload = {
61
+ twinId: screenInstance.id,
62
+ data,
63
+ ...(status && { status }),
64
+ };
65
+ logger.info('Reporting properties', { payload });
66
+ await client.setScreenInstanceReportedProperties(payload);
67
+ };
68
+ let crashes = 0;
69
+ await emitReportedProperties({ crash: { count: crashes } }, twin_types_1.TwinStatusEnum.Online);
70
+ let lastHeartbeat = 0;
71
+ async function onSupervisorUrlUpdated() {
72
+ const url = properties.desired.url;
73
+ if (url === activeUrl)
74
+ return;
75
+ activeUrl = url || NO_SETTINGS_URL;
76
+ logger.info('URL changed', { newUrl: activeUrl });
77
+ await browser_js_1.default.open(activeUrl);
78
+ }
79
+ const delay = (n) => new Promise((resolve) => setTimeout(resolve, n));
80
+ let activeUrl = properties.desired.url || NO_SETTINGS_URL;
81
+ logger.info('Opening initial URL', { url: activeUrl });
82
+ await browser_js_1.default.open(activeUrl);
83
+ if (!properties.desired.screenResolution)
84
+ properties.desired.screenResolution = 'default';
85
+ if (!properties.desired.screenOrientation)
86
+ properties.desired.screenOrientation = 'landscape';
87
+ if (!properties.desired.scale)
88
+ properties.desired.scale = '1.0';
89
+ logger.info('Desired settings retrieved', { desiredSettings: properties.desired });
90
+ logger.info('Setting up config file for wayland composer');
91
+ await (0, sway_js_1.setupSwayConfig)({
92
+ orientation: properties.desired.screenOrientation,
93
+ resolution: properties.desired.screenResolution,
94
+ scale: properties.desired.scale,
95
+ headless: !(0, sway_js_1.isDrmAvailable)(),
96
+ });
97
+ const checkHeartbeats = async () => {
98
+ while (true) {
99
+ await delay(60 * 1000);
100
+ if (!lastHeartbeat)
101
+ continue;
102
+ const now = new Date().getTime();
103
+ if (now - lastHeartbeat <= 60000)
104
+ continue;
105
+ logger.error('Heartbeat not registered for 60 seconds, restarting app');
106
+ lastHeartbeat = 0;
107
+ await browser_js_1.default.restart();
108
+ }
109
+ };
110
+ let prevUrl = '';
111
+ const takeScreenshots = async () => {
112
+ let whiteColorCount = 0;
113
+ let blackColorCount = 0;
114
+ await delay(20 * 1000);
115
+ while (true) {
116
+ const { restartIfAllWhite = true, restartIfAllBlack = true } = properties.desired;
117
+ let data = null;
118
+ try {
119
+ data = await (0, screenshot_js_1.takeScreenshot)();
120
+ }
121
+ catch (e) {
122
+ logger.error('Error taking screenshot', { error: e.toString() });
123
+ (0, sway_js_1.restartSway)();
124
+ continue;
125
+ }
126
+ if (!data) {
127
+ logger.error('Screenshot data is not a buffer');
128
+ throw new Error('Screenshot: not a buffer');
129
+ }
130
+ try {
131
+ const [allWhite, allBlack] = await (0, screenshot_js_1.analyzeScreenshot)(data);
132
+ const resized = await (0, screenshot_js_1.resizeScreenshot)(data);
133
+ let url = `https://gridhealth.blob.core.windows.net/screens/${deviceInstance.twin.deviceId}.jpg`;
134
+ try {
135
+ logger.info('Starting new screenshot upload');
136
+ const token = await (0, screenshot_js_1.getScreenshotToken)({
137
+ browserId: deviceInstance.twin.deviceId,
138
+ accessKey: deviceInstance.twin.properties.desired.accessKey
139
+ });
140
+ logger.debug('Screenshot token retrieved', { token });
141
+ if (!token)
142
+ throw new Error('Screenshot: cannot upload, invalid token');
143
+ url = await (0, screenshot_js_1.uploadScreenshot)(resized, token);
144
+ }
145
+ catch (e) {
146
+ logger.error('Error during screenshot upload', { error: e.toString() });
147
+ throw new Error('Failed to upload screenshot');
148
+ }
149
+ try {
150
+ if (url !== prevUrl) {
151
+ prevUrl = url;
152
+ await emitReportedProperties({ screenshot: url });
153
+ }
154
+ }
155
+ catch (e) {
156
+ logger.error('Error during screenshot telemetry', { error: e.toString() });
157
+ throw new Error('Failed to screenshot telemetry');
158
+ }
159
+ whiteColorCount = allWhite && restartIfAllWhite ? whiteColorCount + 1 : 0;
160
+ blackColorCount = allBlack && restartIfAllBlack ? blackColorCount + 1 : 0;
161
+ if (whiteColorCount)
162
+ logger.warn(`Screenshot: all white (${whiteColorCount} try)`);
163
+ if (blackColorCount)
164
+ logger.warn(`Screenshot: all black (${blackColorCount} try)`);
165
+ if (whiteColorCount > 2) {
166
+ logger.error('Screen detected as all white, restarting browser');
167
+ await emitReportedProperties({ crash: { reason: 'screen_all_white' } });
168
+ browser_js_1.default.restart();
169
+ whiteColorCount = 0;
170
+ }
171
+ if (blackColorCount > 2) {
172
+ logger.error('Screen detected as all black, restarting browser');
173
+ await emitReportedProperties({ crash: { reason: 'screen_all_black' } });
174
+ browser_js_1.default.restart();
175
+ blackColorCount = 0;
176
+ }
177
+ await delay(whiteColorCount + blackColorCount ? 5 * 1000 : 60 * 1000);
178
+ }
179
+ catch (e) {
180
+ logger.error('Error processing screenshot', { error: e.toString() });
181
+ await delay(5 * 1000);
182
+ }
183
+ }
184
+ };
185
+ const isTouchConnected = (info) => info.driver === 'hid-multitouch' && info.action === 'bind';
186
+ const isMouseLikeConnected = (info) => info.action === 'add' &&
187
+ info.subsystem === 'input' &&
188
+ /^\/dev\/input\/mouse[0-9]/.test(info.devname);
189
+ udev_js_1.default.monitor(async (info) => {
190
+ try {
191
+ if (!info)
192
+ return;
193
+ if (info.subsystem === 'bdi')
194
+ return;
195
+ if (info.action === 'add' &&
196
+ info.subsystem === 'graphics' &&
197
+ info.devname === '/dev/fb0') {
198
+ const inputs = await (0, sway_js_1.swayMsg)(`-t get_outputs`);
199
+ if (inputs && inputs.every((input) => /^HEADLESS-/.test(input.name))) {
200
+ logger.info('Udev: Screen is connected for the 1st time, restarting module');
201
+ browser_js_1.default.restart();
202
+ return;
203
+ }
204
+ }
205
+ if (info.action === 'change' &&
206
+ info.subsystem === 'drm' &&
207
+ info.devname === '/dev/dri/card0') {
208
+ const inputs = await (0, sway_js_1.swayMsg)(`-t get_outputs`);
209
+ if (inputs && inputs.every((input) => /^HEADLESS-/.test(input.name))) {
210
+ logger.info('Udev: Screen is connected, restarting module');
211
+ browser_js_1.default.restart();
212
+ }
213
+ }
214
+ }
215
+ catch (e) {
216
+ logger.error('Cannot process udev event', { error: e });
217
+ }
218
+ });
219
+ checkHeartbeats();
220
+ takeScreenshots();
221
+ process.on('SIGTERM', async () => {
222
+ logger.info('Received SIGTERM, exiting');
223
+ try {
224
+ await emitReportedProperties({}, twin_types_1.TwinStatusEnum.Exited);
225
+ fs_1.default.unlinkSync(HEARTBEAT_PATH);
226
+ await new Promise((resolve) => child_process_1.default.exec('killall -9 sway', resolve));
227
+ await delay(500);
228
+ }
229
+ catch (e) {
230
+ logger.error('Error during shutdown', { error: e.toString() });
231
+ }
232
+ process.exit(1);
233
+ });
234
+ })();
235
+ //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,31 +1,33 @@
1
1
  {
2
2
  "name": "@phystack/screen-phyos",
3
- "version": "4.5.64-dev",
4
- "description": "PhyOS Screen",
5
- "license": "MIT",
3
+ "version": "5.0.1",
4
+ "description": "PhyOS screen runtime",
5
+ "main": "dist/index.js",
6
+ "license": "UNLICENSED",
6
7
  "publishConfig": {
7
8
  "access": "public"
8
9
  },
9
- "engines": {
10
- "node": ">=20.0.0"
11
- },
12
10
  "bin": {
13
11
  "physcreen-manager": "./bin/index.js"
14
12
  },
15
13
  "files": [
16
14
  "bin/**/*"
17
15
  ],
16
+ "engines": {
17
+ "node": ">=20.0.0"
18
+ },
18
19
  "scripts": {
19
20
  "build": "tsc",
20
- "build:binary": "VERSION=$(node -p \"require('./package.json').version\") && npx bun build --compile --minify --define \"__PKG_VERSION__=\\\"$VERSION\\\"\" ./src/index.ts --outfile physcreen-manager",
21
- "lint": "eslint --ext .js,.ts src",
21
+ "build:binary": "VERSION=$(bun -e \"console.log(require('./package.json').version)\") && bun build --compile --minify --define \"__PKG_VERSION__=\\\"$VERSION\\\"\" ./src/index.ts --outfile physcreen-manager",
22
+ "dev": "bun --watch src/index.ts",
23
+ "lint": "tsc --noEmit",
24
+ "test:ci": "echo 'no tests' && exit 0",
22
25
  "lint-fix": "tsc --noEmit && eslint --ext .js,.ts src --fix && prettier --write ."
23
26
  },
24
27
  "optionalDependencies": {
25
- "@phystack/physcreen-manager-darwin-arm64": "4.5.64-dev",
26
- "@phystack/physcreen-manager-darwin-x64": "4.5.64-dev",
27
- "@phystack/physcreen-manager-linux-arm64": "4.5.64-dev",
28
- "@phystack/physcreen-manager-linux-x64": "4.5.64-dev"
29
- },
30
- "gitHead": "e29cb3adee4f709ac3f5f2b3de62fe7f14a7a7b3"
28
+ "@phystack/physcreen-manager-darwin-arm64": "5.0.1",
29
+ "@phystack/physcreen-manager-darwin-x64": "5.0.1",
30
+ "@phystack/physcreen-manager-linux-arm64": "5.0.1",
31
+ "@phystack/physcreen-manager-linux-x64": "5.0.1"
32
+ }
31
33
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 PhyStack.com
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/README.md DELETED
@@ -1,62 +0,0 @@
1
- # physcreen-manager
2
-
3
- PhyOS screen runtime for managing display instances. Compiled as a standalone Bun binary.
4
-
5
- ## Installation
6
-
7
- ### Via npm (recommended)
8
-
9
- ```bash
10
- npm install -g @phystack/screen-phyos
11
- ```
12
-
13
- npm automatically downloads the correct binary for your platform.
14
-
15
- ### Direct binary download (no npm required)
16
-
17
- ```bash
18
- VERSION=4.5.62-dev # replace with desired version
19
-
20
- # Linux ARM64
21
- sudo curl -sL "https://registry.npmjs.org/@phystack/physcreen-manager-linux-arm64/-/physcreen-manager-linux-arm64-${VERSION}.tgz" | sudo tar xz --strip-components=2 -C /usr/bin package/bin/physcreen-manager
22
-
23
- # Linux x86_64
24
- sudo curl -sL "https://registry.npmjs.org/@phystack/physcreen-manager-linux-x64/-/physcreen-manager-linux-x64-${VERSION}.tgz" | sudo tar xz --strip-components=2 -C /usr/bin package/bin/physcreen-manager
25
-
26
- # macOS Apple Silicon
27
- curl -sL "https://registry.npmjs.org/@phystack/physcreen-manager-darwin-arm64/-/physcreen-manager-darwin-arm64-${VERSION}.tgz" | tar xz --strip-components=2 -C /usr/local/bin package/bin/physcreen-manager
28
-
29
- # macOS Intel
30
- curl -sL "https://registry.npmjs.org/@phystack/physcreen-manager-darwin-x64/-/physcreen-manager-darwin-x64-${VERSION}.tgz" | tar xz --strip-components=2 -C /usr/local/bin package/bin/physcreen-manager
31
- ```
32
-
33
- To check available versions:
34
-
35
- ```bash
36
- npm view @phystack/physcreen-manager-linux-arm64 versions --json
37
- ```
38
-
39
- ## Supported platforms
40
-
41
- | Platform | Architecture | npm package |
42
- |----------|-------------|-------------|
43
- | Linux | ARM64 | `@phystack/physcreen-manager-linux-arm64` |
44
- | Linux | x86_64 | `@phystack/physcreen-manager-linux-x64` |
45
- | macOS | ARM64 | `@phystack/physcreen-manager-darwin-arm64` |
46
- | macOS | x86_64 | `@phystack/physcreen-manager-darwin-x64` |
47
-
48
- ## Building from source
49
-
50
- ```bash
51
- yarn build:binary # compile for current platform
52
- ```
53
-
54
- Or use the central build script for cross-compilation:
55
-
56
- ```bash
57
- # from repo root
58
- yarn build:binaries # current platform
59
- yarn build:binaries:cross # all platforms
60
- ```
61
-
62
- Output: `dist-binaries/<platform>/physcreen-manager`