scenri 0.8.3 → 0.9.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 +44 -0
- package/README.md +2 -0
- package/dist/chunk-2IGINCPU.js +61 -0
- package/dist/{chunk-WGIZJXNE.js → chunk-CMLJGPYC.js} +25 -3
- package/dist/chunk-ECS5LRHB.js +69 -0
- package/dist/chunk-OJAG3FRX.js +1940 -0
- package/dist/chunk-PAIAXRAC.js +20 -0
- package/dist/chunk-SDVL5OJF.js +255 -0
- package/dist/cli-FYWM4HBN.js +526 -0
- package/dist/index.js +24 -3
- package/dist/install-4W6AARK6.js +4 -0
- package/dist/{launcher-DIZ4MQRF.js → launcher-IVFIYHPN.js} +6 -2
- package/dist/offer-IVTNHIDJ.js +47 -0
- package/dist/paths-6ZXRZUHY.js +3 -0
- package/dist/refresh-FKQABJOC.js +105 -0
- package/dist/serve.js +154 -1945
- package/dist/src-CSR34EBZ.js +3 -0
- package/launcher/Scenri.icns +0 -0
- package/launcher/launch.mjs +162 -0
- package/launcher/scenri.ico +0 -0
- package/launcher/starting.html +81 -0
- package/package.json +2 -1
- package/studio-dist/assets/index-DSSGRUMi.js +104 -0
- package/studio-dist/assets/{index-teI3lJtu.css → index-DfAHZRYM.css} +1 -1
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-DODDNVtQ.js +0 -104
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { realpathSync } from 'fs';
|
|
2
|
+
import { sep, join } from 'path';
|
|
3
|
+
|
|
4
|
+
// src/installKind.ts
|
|
5
|
+
function detectInstallKind(entryPath, home) {
|
|
6
|
+
if (!entryPath.includes(`${sep}dist${sep}`) && !entryPath.endsWith(`${sep}dist`)) return "dev";
|
|
7
|
+
let homeReal = home;
|
|
8
|
+
try {
|
|
9
|
+
homeReal = realpathSync(home);
|
|
10
|
+
} catch {
|
|
11
|
+
}
|
|
12
|
+
if (entryPath.startsWith(join(homeReal, "app", "versions") + sep)) return "managed";
|
|
13
|
+
if (entryPath.includes(`${sep}_npx${sep}`)) return "npx";
|
|
14
|
+
if (entryPath.includes(`${sep}node_modules${sep}`)) return "global";
|
|
15
|
+
return "unknown";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { detectInstallKind };
|
|
19
|
+
//# sourceMappingURL=chunk-PAIAXRAC.js.map
|
|
20
|
+
//# sourceMappingURL=chunk-PAIAXRAC.js.map
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { powershellPath, isSupportedPlatform, desktopDir, launcherDir, LAUNCHER_SCHEMA, writeLauncherRecord, recordedEnv, readLauncherRecord, PS_UTF8 } from './chunk-2IGINCPU.js';
|
|
2
|
+
import { realpathSync, existsSync, rmSync, mkdirSync, writeFileSync, chmodSync, copyFileSync, renameSync, utimesSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
|
|
5
|
+
var MAC_BUNDLE_ID = "co.scenri.desktop-launcher";
|
|
6
|
+
var MAC_MARKER = "scenri-launcher.json";
|
|
7
|
+
var MAC_SCRIPT = `#!/bin/sh
|
|
8
|
+
# Scenri desktop launcher. Written by Scenri; deleting it removes only this icon.
|
|
9
|
+
L="$HOME/.scenri/launcher"
|
|
10
|
+
say() {
|
|
11
|
+
if [ "$SCENRI_NO_DIALOG" = "1" ]; then return 0; fi
|
|
12
|
+
/usr/bin/osascript -e 'on run argv' \\
|
|
13
|
+
-e 'display dialog (item 1 of argv) with title "Scenri" buttons {"OK"} default button 1 with icon stop' \\
|
|
14
|
+
-e 'end run' -- "$1"
|
|
15
|
+
}
|
|
16
|
+
if [ ! -f "$L/launch.mjs" ]; then
|
|
17
|
+
say "Scenri's launcher files are missing. Open Terminal and run: npx scenri desktop"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
# The node that installed the icon, while it still exists: no probing, no delay.
|
|
21
|
+
NODE=""
|
|
22
|
+
if [ -r "$L/node-path" ]; then IFS= read -r NODE < "$L/node-path" || NODE=""; fi
|
|
23
|
+
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
|
|
24
|
+
MAJOR=""
|
|
25
|
+
if [ -r "$L/node-major" ]; then IFS= read -r MAJOR < "$L/node-major" || MAJOR=""; fi
|
|
26
|
+
# Native modules were built for that node's major and another major refuses
|
|
27
|
+
# them: every candidate once demanding the same major, then once taking any.
|
|
28
|
+
candidates() {
|
|
29
|
+
command -v node 2>/dev/null || true
|
|
30
|
+
echo /opt/homebrew/bin/node
|
|
31
|
+
echo /usr/local/bin/node
|
|
32
|
+
echo "$HOME/.volta/bin/node"
|
|
33
|
+
echo "$HOME/.local/share/fnm/aliases/default/bin/node"
|
|
34
|
+
echo "$HOME/Library/Application Support/fnm/aliases/default/bin/node"
|
|
35
|
+
for c in "$HOME"/.nvm/versions/node/*/bin/node; do echo "$c"; done
|
|
36
|
+
}
|
|
37
|
+
pick() {
|
|
38
|
+
want="$1"
|
|
39
|
+
candidates | while IFS= read -r c; do
|
|
40
|
+
[ -n "$c" ] && [ -x "$c" ] || continue
|
|
41
|
+
if [ -n "$want" ]; then
|
|
42
|
+
v="$("$c" -p 'process.versions.node.split(".")[0]' 2>/dev/null)" || continue
|
|
43
|
+
[ "$v" = "$want" ] || continue
|
|
44
|
+
fi
|
|
45
|
+
echo "$c"
|
|
46
|
+
done | tail -n 1
|
|
47
|
+
}
|
|
48
|
+
NODE=""
|
|
49
|
+
if [ -n "$MAJOR" ]; then NODE="$(pick "$MAJOR")"; fi
|
|
50
|
+
if [ -z "$NODE" ]; then NODE="$(pick "")"; fi
|
|
51
|
+
fi
|
|
52
|
+
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
|
|
53
|
+
say "Scenri needs Node.js, and none was found on this Mac. Install it from nodejs.org, then open Terminal and run: npx scenri"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
exec "$NODE" "$L/launch.mjs"
|
|
57
|
+
`;
|
|
58
|
+
function macPlist(opts) {
|
|
59
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
60
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
61
|
+
<plist version="1.0">
|
|
62
|
+
<dict>
|
|
63
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
64
|
+
<string>en</string>
|
|
65
|
+
<key>CFBundleDisplayName</key>
|
|
66
|
+
<string>Scenri</string>
|
|
67
|
+
<key>CFBundleExecutable</key>
|
|
68
|
+
<string>Scenri</string>
|
|
69
|
+
<key>CFBundleIconFile</key>
|
|
70
|
+
<string>Scenri.icns</string>
|
|
71
|
+
<key>CFBundleIdentifier</key>
|
|
72
|
+
<string>${MAC_BUNDLE_ID}</string>
|
|
73
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
74
|
+
<string>6.0</string>
|
|
75
|
+
<key>CFBundleName</key>
|
|
76
|
+
<string>Scenri</string>
|
|
77
|
+
<key>CFBundlePackageType</key>
|
|
78
|
+
<string>APPL</string>
|
|
79
|
+
<key>CFBundleShortVersionString</key>
|
|
80
|
+
<string>${opts.version}</string>
|
|
81
|
+
<key>CFBundleVersion</key>
|
|
82
|
+
<string>${opts.schema}</string>
|
|
83
|
+
<key>LSMinimumSystemVersion</key>
|
|
84
|
+
<string>12.0</string>
|
|
85
|
+
<key>NSHighResolutionCapable</key>
|
|
86
|
+
<true/>
|
|
87
|
+
</dict>
|
|
88
|
+
</plist>
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
var isOurMacBundle = (bundle) => existsSync(join(bundle, "Contents", "Resources", MAC_MARKER));
|
|
92
|
+
function writeMacBundle(opts) {
|
|
93
|
+
const tmp = `${opts.path}.tmp-${process.pid}`;
|
|
94
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
95
|
+
const contents = join(tmp, "Contents");
|
|
96
|
+
mkdirSync(join(contents, "MacOS"), { recursive: true });
|
|
97
|
+
mkdirSync(join(contents, "Resources"), { recursive: true });
|
|
98
|
+
writeFileSync(join(contents, "Info.plist"), macPlist(opts));
|
|
99
|
+
writeFileSync(join(contents, "PkgInfo"), "APPL????");
|
|
100
|
+
const script = join(contents, "MacOS", "Scenri");
|
|
101
|
+
writeFileSync(script, MAC_SCRIPT);
|
|
102
|
+
chmodSync(script, 493);
|
|
103
|
+
copyFileSync(opts.icns, join(contents, "Resources", "Scenri.icns"));
|
|
104
|
+
writeFileSync(
|
|
105
|
+
join(contents, "Resources", MAC_MARKER),
|
|
106
|
+
`${JSON.stringify({ scenri: "desktop-launcher", schema: opts.schema, version: opts.version }, null, 2)}
|
|
107
|
+
`
|
|
108
|
+
);
|
|
109
|
+
rmSync(opts.path, { recursive: true, force: true });
|
|
110
|
+
mkdirSync(dirname(opts.path), { recursive: true });
|
|
111
|
+
renameSync(tmp, opts.path);
|
|
112
|
+
const now = /* @__PURE__ */ new Date();
|
|
113
|
+
utimesSync(opts.path, now, now);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/desktop/windows.ts
|
|
117
|
+
var POWERSHELL = powershellPath(process.env);
|
|
118
|
+
var POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"];
|
|
119
|
+
var CREATE_LNK_COMMAND = "$s = New-Object -ComObject WScript.Shell; $l = $s.CreateShortcut($env:SCENRI_LNK); $l.TargetPath = $env:SCENRI_TARGET; $l.Arguments = $env:SCENRI_ARGS; $l.WorkingDirectory = $env:SCENRI_WORKDIR; $l.IconLocation = $env:SCENRI_ICON; $l.Description = 'Open Scenri'; $l.WindowStyle = 7; $l.Save()";
|
|
120
|
+
var READ_LNK_COMMAND = `${PS_UTF8}$s = New-Object -ComObject WScript.Shell; $l = $s.CreateShortcut($env:SCENRI_LNK); Write-Output $l.TargetPath; Write-Output $l.Arguments`;
|
|
121
|
+
async function readLnk(runImpl, path) {
|
|
122
|
+
const out = await runImpl(POWERSHELL, [...POWERSHELL_ARGS, READ_LNK_COMMAND], { env: { SCENRI_LNK: path } });
|
|
123
|
+
const [target = "", args = ""] = out.split(/\r?\n/);
|
|
124
|
+
return { target: target.trim(), args: args.trim() };
|
|
125
|
+
}
|
|
126
|
+
async function isOurLnk(runImpl, path, bootstrap) {
|
|
127
|
+
try {
|
|
128
|
+
const { args } = await readLnk(runImpl, path);
|
|
129
|
+
return args.toLowerCase().includes(bootstrap.toLowerCase());
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function writeLnk(runImpl, opts) {
|
|
135
|
+
await runImpl(POWERSHELL, [...POWERSHELL_ARGS, CREATE_LNK_COMMAND], {
|
|
136
|
+
env: {
|
|
137
|
+
SCENRI_LNK: opts.path,
|
|
138
|
+
SCENRI_TARGET: opts.target,
|
|
139
|
+
SCENRI_ARGS: opts.args,
|
|
140
|
+
SCENRI_WORKDIR: opts.workdir,
|
|
141
|
+
SCENRI_ICON: opts.icon
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/desktop/install.ts
|
|
147
|
+
var UNSUPPORTED = "Desktop shortcuts are not available on this system yet.";
|
|
148
|
+
var NO_DESKTOP = "Your Desktop folder could not be found.";
|
|
149
|
+
var COLLISION = "Something else named Scenri is already on your desktop. Move or rename it, then try again.";
|
|
150
|
+
var DENIED = "macOS did not allow writing to your Desktop. Allow it under System Settings > Privacy & Security > Files and Folders, then try again.";
|
|
151
|
+
var SUPPORT_FILES = ["launch.mjs", "Scenri.icns", "scenri.ico"];
|
|
152
|
+
async function installDesktop(deps) {
|
|
153
|
+
if (!isSupportedPlatform(deps.platform)) return { ok: false, reason: "unsupported", message: UNSUPPORTED };
|
|
154
|
+
const desktop = await desktopDir(deps);
|
|
155
|
+
if (!desktop) return { ok: false, reason: "no-desktop", message: NO_DESKTOP };
|
|
156
|
+
const darwin = deps.platform === "darwin";
|
|
157
|
+
const support = launcherDir(deps.homedir);
|
|
158
|
+
const bootstrap = join(support, "launch.mjs");
|
|
159
|
+
const kind = darwin ? "macos-app" : "windows-lnk";
|
|
160
|
+
const artifact = join(desktop, darwin ? "Scenri.app" : "Scenri.lnk");
|
|
161
|
+
if (existsSync(artifact)) {
|
|
162
|
+
const ours = darwin ? isOurMacBundle(artifact) : await isOurLnk(deps.runImpl, artifact, bootstrap);
|
|
163
|
+
if (!ours) return { ok: false, reason: "collision", message: COLLISION };
|
|
164
|
+
}
|
|
165
|
+
const nodeMajor = runningNodeMajor();
|
|
166
|
+
const nodePath = stableExecPath(deps.execPath, deps.platform);
|
|
167
|
+
writeSupportFiles({ ...deps, execPath: nodePath, nodeMajor }, support);
|
|
168
|
+
try {
|
|
169
|
+
if (darwin) {
|
|
170
|
+
writeMacBundle({
|
|
171
|
+
path: artifact,
|
|
172
|
+
icns: join(support, "Scenri.icns"),
|
|
173
|
+
version: deps.version,
|
|
174
|
+
schema: LAUNCHER_SCHEMA
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
await writeLnk(deps.runImpl, {
|
|
178
|
+
path: artifact,
|
|
179
|
+
target: nodePath,
|
|
180
|
+
args: `"${bootstrap}"`,
|
|
181
|
+
workdir: support,
|
|
182
|
+
icon: `${join(support, "scenri.ico")},0`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
const code = err.code;
|
|
187
|
+
if (darwin && (code === "EPERM" || code === "EACCES"))
|
|
188
|
+
return { ok: false, reason: "desktop-denied", message: DENIED };
|
|
189
|
+
const detail = err instanceof Error ? err.message.split("\n")[0] : String(err);
|
|
190
|
+
return { ok: false, reason: "failed", message: `Scenri could not write ${artifact} (${detail}).` };
|
|
191
|
+
}
|
|
192
|
+
writeLauncherRecord(deps.homedir, {
|
|
193
|
+
schema: LAUNCHER_SCHEMA,
|
|
194
|
+
createdBy: deps.version,
|
|
195
|
+
home: deps.home,
|
|
196
|
+
nodePath,
|
|
197
|
+
nodeMajor,
|
|
198
|
+
env: recordedEnv(deps.env),
|
|
199
|
+
artifact: { kind, path: artifact }
|
|
200
|
+
});
|
|
201
|
+
return { ok: true, kind, path: artifact };
|
|
202
|
+
}
|
|
203
|
+
var runningNodeMajor = () => Number(process.versions.node.split(".")[0]);
|
|
204
|
+
function stableExecPath(execPath, platform, realpath = realpathSync) {
|
|
205
|
+
if (platform !== "win32") return execPath;
|
|
206
|
+
try {
|
|
207
|
+
return realpath(execPath);
|
|
208
|
+
} catch {
|
|
209
|
+
return execPath;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function writeSupportFiles(deps, support) {
|
|
213
|
+
mkdirSync(support, { recursive: true });
|
|
214
|
+
for (const f of SUPPORT_FILES) copyFileSync(join(deps.assetsDir, f), join(support, f));
|
|
215
|
+
if (deps.platform === "darwin") {
|
|
216
|
+
writeFileSync(join(support, "node-path"), `${deps.execPath}
|
|
217
|
+
`);
|
|
218
|
+
writeFileSync(join(support, "node-major"), `${deps.nodeMajor}
|
|
219
|
+
`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function removeDesktop(deps) {
|
|
223
|
+
const record = readLauncherRecord(deps.homedir);
|
|
224
|
+
const support = launcherDir(deps.homedir);
|
|
225
|
+
let removed = false;
|
|
226
|
+
let message;
|
|
227
|
+
const path = record?.artifact.path;
|
|
228
|
+
if (path && existsSync(path)) {
|
|
229
|
+
const ours = record.artifact.kind === "macos-app" ? isOurMacBundle(path) : await isOurLnk(deps.runImpl, path, join(support, "launch.mjs"));
|
|
230
|
+
if (ours) {
|
|
231
|
+
rmSync(path, { recursive: true, force: true });
|
|
232
|
+
removed = true;
|
|
233
|
+
} else {
|
|
234
|
+
message = `${path} is not the launcher Scenri made, so it was left alone.`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
rmSync(support, { recursive: true, force: true });
|
|
238
|
+
return { removed, path, message };
|
|
239
|
+
}
|
|
240
|
+
async function desktopStatus(deps) {
|
|
241
|
+
const supported = isSupportedPlatform(deps.platform);
|
|
242
|
+
const record = readLauncherRecord(deps.homedir);
|
|
243
|
+
let installed = false;
|
|
244
|
+
let path = null;
|
|
245
|
+
if (supported && record && existsSync(record.artifact.path)) {
|
|
246
|
+
installed = record.artifact.kind === "macos-app" ? isOurMacBundle(record.artifact.path) : true;
|
|
247
|
+
path = installed ? record.artifact.path : null;
|
|
248
|
+
}
|
|
249
|
+
const current = installed && record !== null && record.schema === LAUNCHER_SCHEMA && (existsSync(record.nodePath) || record.nodePath === deps.execPath);
|
|
250
|
+
return { supported, platform: deps.platform, installed, path, current, record };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { MAC_SCRIPT, POWERSHELL_ARGS, UNSUPPORTED, desktopStatus, installDesktop, isOurMacBundle, macPlist, removeDesktop, runningNodeMajor, stableExecPath, writeLnk, writeMacBundle, writeSupportFiles };
|
|
254
|
+
//# sourceMappingURL=chunk-SDVL5OJF.js.map
|
|
255
|
+
//# sourceMappingURL=chunk-SDVL5OJF.js.map
|