spi-agent-gui 0.1.0 → 0.1.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 +6 -0
- package/dist/spi.cjs +84 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Original female-spy silhouette icon for the application window, favicon and platform packages.
|
|
6
|
+
- Owned Linux desktop entry for default personal installs, with icon refresh on update/rollback and conservative uninstall.
|
|
7
|
+
- Preserve unknown/modified desktop entries and refuse symlink directories; validate desktop Exec escaping.
|
|
8
|
+
|
|
3
9
|
## 0.1.0
|
|
4
10
|
|
|
5
11
|
- Initial Pi launcher package and verified native desktop archive installer.
|
package/dist/spi.cjs
CHANGED
|
@@ -11,7 +11,7 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
11
11
|
var define_SPI_RELEASE_default;
|
|
12
12
|
var init_define_SPI_RELEASE = __esm({
|
|
13
13
|
"<define:__SPI_RELEASE__>"() {
|
|
14
|
-
define_SPI_RELEASE_default = { version: "0.1.
|
|
14
|
+
define_SPI_RELEASE_default = { version: "0.1.1", repository: "DiamondGo/spi", packageName: "spi-agent-gui" };
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
17
|
|
|
@@ -4608,12 +4608,81 @@ var require_yauzl = __commonJS({
|
|
|
4608
4608
|
}
|
|
4609
4609
|
});
|
|
4610
4610
|
|
|
4611
|
+
// packages/pi-spi/lib/desktop.cjs
|
|
4612
|
+
var require_desktop = __commonJS({
|
|
4613
|
+
"packages/pi-spi/lib/desktop.cjs"(exports2, module2) {
|
|
4614
|
+
init_define_SPI_RELEASE();
|
|
4615
|
+
var fs2 = require("node:fs");
|
|
4616
|
+
var path2 = require("node:path");
|
|
4617
|
+
var crypto2 = require("node:crypto");
|
|
4618
|
+
var digest = (file) => crypto2.createHash("sha256").update(fs2.readFileSync(file)).digest("hex");
|
|
4619
|
+
function exists(file) {
|
|
4620
|
+
try {
|
|
4621
|
+
fs2.lstatSync(file);
|
|
4622
|
+
return true;
|
|
4623
|
+
} catch (error) {
|
|
4624
|
+
if (error.code === "ENOENT") return false;
|
|
4625
|
+
throw error;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
function safeParents(directory) {
|
|
4629
|
+
for (let dir = directory; ; dir = path2.dirname(dir)) {
|
|
4630
|
+
if (exists(dir) && (!fs2.lstatSync(dir).isDirectory() || fs2.lstatSync(dir).isSymbolicLink())) throw Error("Refusing symlink desktop directory");
|
|
4631
|
+
if (dir === path2.dirname(dir)) break;
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
function assertDesktopOwned2(record) {
|
|
4635
|
+
if (!record) return;
|
|
4636
|
+
if (!path2.isAbsolute(record.path) || path2.basename(record.path) !== "spi.desktop") throw Error("Invalid desktop entry record");
|
|
4637
|
+
safeParents(path2.dirname(record.path));
|
|
4638
|
+
if (exists(record.path) && (fs2.lstatSync(record.path).isSymbolicLink() || !fs2.lstatSync(record.path).isFile() || digest(record.path) !== record.sha256)) throw Error("Refusing modified desktop entry");
|
|
4639
|
+
}
|
|
4640
|
+
function removeDesktopEntry2(record) {
|
|
4641
|
+
assertDesktopOwned2(record);
|
|
4642
|
+
if (record) fs2.rmSync(record.path, { force: true });
|
|
4643
|
+
}
|
|
4644
|
+
var value = (s) => s.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
|
|
4645
|
+
function desktopText(command, icon) {
|
|
4646
|
+
if (/[\x00-\x1f\x7f]/.test(command + icon)) throw Error("Unsupported control character in desktop path");
|
|
4647
|
+
const argument = '"' + command.replace(/[\\"`$]/g, "\\$&").replaceAll("%", "%%") + '"';
|
|
4648
|
+
return "[Desktop Entry]\nType=Application\nName=sPi\nComment=Pi Coding Agent GUI\nExec=" + value(argument) + "\nIcon=" + value(icon) + "\nTerminal=false\nCategories=Development;\nStartupWMClass=spi\nStartupNotify=true\n";
|
|
4649
|
+
}
|
|
4650
|
+
function syncDesktopEntry2({ enabled, dataHome, root, command, entryPath, previous }) {
|
|
4651
|
+
if (!enabled) return previous;
|
|
4652
|
+
const icon = path2.join(path2.dirname(entryPath), "resources", "icon.png");
|
|
4653
|
+
if (!exists(icon)) {
|
|
4654
|
+
if (previous) removeDesktopEntry2(previous);
|
|
4655
|
+
return void 0;
|
|
4656
|
+
}
|
|
4657
|
+
if (!path2.isAbsolute(dataHome)) throw Error("XDG data home must be absolute");
|
|
4658
|
+
const directory = path2.join(dataHome, "applications"), file = path2.join(directory, "spi.desktop");
|
|
4659
|
+
safeParents(directory);
|
|
4660
|
+
if (exists(file)) {
|
|
4661
|
+
if (!previous || previous.path !== file) throw Error("Refusing unowned desktop entry");
|
|
4662
|
+
assertDesktopOwned2(previous);
|
|
4663
|
+
}
|
|
4664
|
+
if (previous && previous.path !== file) throw Error("Desktop location changed; existing entry was preserved");
|
|
4665
|
+
if (fs2.lstatSync(icon).isSymbolicLink() || !fs2.lstatSync(icon).isFile() || !fs2.realpathSync(icon).startsWith(fs2.realpathSync(root) + path2.sep)) throw Error("Invalid application icon");
|
|
4666
|
+
const text = desktopText(command, icon), temp = file + "." + crypto2.randomUUID() + ".tmp";
|
|
4667
|
+
fs2.mkdirSync(directory, { recursive: true });
|
|
4668
|
+
try {
|
|
4669
|
+
fs2.writeFileSync(temp, text, { mode: 420, flag: "wx" });
|
|
4670
|
+
fs2.renameSync(temp, file);
|
|
4671
|
+
} finally {
|
|
4672
|
+
fs2.rmSync(temp, { force: true });
|
|
4673
|
+
}
|
|
4674
|
+
return { path: file, sha256: digest(file) };
|
|
4675
|
+
}
|
|
4676
|
+
module2.exports = { syncDesktopEntry: syncDesktopEntry2, assertDesktopOwned: assertDesktopOwned2, removeDesktopEntry: removeDesktopEntry2, desktopText };
|
|
4677
|
+
}
|
|
4678
|
+
});
|
|
4679
|
+
|
|
4611
4680
|
// package.json
|
|
4612
4681
|
var require_package = __commonJS({
|
|
4613
4682
|
"package.json"(exports2, module2) {
|
|
4614
4683
|
module2.exports = {
|
|
4615
4684
|
name: "spi",
|
|
4616
|
-
version: "0.1.
|
|
4685
|
+
version: "0.1.1",
|
|
4617
4686
|
description: "sPi - Simple Pi Agent GUI",
|
|
4618
4687
|
private: true,
|
|
4619
4688
|
license: "UNLICENSED",
|
|
@@ -4626,6 +4695,8 @@ var require_package = __commonJS({
|
|
|
4626
4695
|
dev: "electron-vite dev",
|
|
4627
4696
|
postinstall: "electron-builder install-app-deps",
|
|
4628
4697
|
build: "electron-vite build",
|
|
4698
|
+
"build:icons": "node scripts/icons.cjs",
|
|
4699
|
+
"test:icons": "node scripts/icons.test.cjs",
|
|
4629
4700
|
start: "electron-vite preview",
|
|
4630
4701
|
dist: "node scripts/build-portable.cjs",
|
|
4631
4702
|
"dist:linux": "node scripts/build-portable.cjs",
|
|
@@ -4657,6 +4728,7 @@ var require_package = __commonJS({
|
|
|
4657
4728
|
},
|
|
4658
4729
|
devDependencies: {
|
|
4659
4730
|
"@electron/asar": "^3.4.1",
|
|
4731
|
+
"@resvg/resvg-js": "2.6.2",
|
|
4660
4732
|
"@types/react": "^18.3.12",
|
|
4661
4733
|
"@types/react-dom": "^18.3.1",
|
|
4662
4734
|
"@vitejs/plugin-react": "^4.3.4",
|
|
@@ -4695,6 +4767,7 @@ var { spawn } = require("node:child_process");
|
|
|
4695
4767
|
var { pipeline } = require("node:stream/promises");
|
|
4696
4768
|
var tar = require_index_min();
|
|
4697
4769
|
var yauzl = require_yauzl();
|
|
4770
|
+
var { syncDesktopEntry, assertDesktopOwned, removeDesktopEntry } = require_desktop();
|
|
4698
4771
|
var BUILD = typeof define_SPI_RELEASE_default === "undefined" ? { version: require_package().version, repository: require_release_config().repository, packageName: require_release_config().npmPackage } : define_SPI_RELEASE_default;
|
|
4699
4772
|
var OWNER = "sPi-installer-v1";
|
|
4700
4773
|
var MAX_ARCHIVE = 700 * 1024 * 1024;
|
|
@@ -5009,7 +5082,13 @@ function register(root, options2 = {}) {
|
|
|
5009
5082
|
fs.renameSync(temp, dest);
|
|
5010
5083
|
const shims = (oldState.shims || []).filter((s) => s.path !== dest);
|
|
5011
5084
|
shims.push({ path: dest, sha256: hash(dest) });
|
|
5012
|
-
|
|
5085
|
+
let desktop = oldState.desktop;
|
|
5086
|
+
if (process.platform === "linux" && root === path.resolve(defaultRoot())) try {
|
|
5087
|
+
desktop = syncDesktopEntry({ enabled: true, dataHome: process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local/share"), root, command: dest, entryPath: cur.entryPath, previous: desktop });
|
|
5088
|
+
} catch (error) {
|
|
5089
|
+
console.warn("[sPi] Desktop entry unchanged: " + error.message);
|
|
5090
|
+
}
|
|
5091
|
+
atomicJSON(path.join(root, "install-state.json"), { owner: OWNER, shims, desktop, launcher: { sha256: hash(cli) } });
|
|
5013
5092
|
console.log("sPi command: " + dest);
|
|
5014
5093
|
if (!(process.env.PATH || "").split(path.delimiter).some((d) => path.resolve(d) === dir)) console.log("Add this directory to your PATH: " + dir + " (shell configuration was not changed)");
|
|
5015
5094
|
}
|
|
@@ -5157,9 +5236,11 @@ async function uninstall(options2) {
|
|
|
5157
5236
|
if (fs.existsSync(launcher) && fs.lstatSync(launcher).isSymbolicLink()) fail("Refusing symlink launcher");
|
|
5158
5237
|
const cli = path.join(launcher, "spi.cjs");
|
|
5159
5238
|
if (fs.existsSync(cli) && (!state.launcher || hash(cli) !== state.launcher.sha256 || fs.lstatSync(cli).isSymbolicLink())) fail("Refusing modified launcher");
|
|
5239
|
+
assertDesktopOwned(state.desktop);
|
|
5160
5240
|
for (const s of state.shims || []) if (fs.existsSync(s.path)) {
|
|
5161
5241
|
if (fs.lstatSync(s.path).isSymbolicLink() || hash(s.path) !== s.sha256) fail("Refusing to remove a modified command");
|
|
5162
5242
|
}
|
|
5243
|
+
removeDesktopEntry(state.desktop);
|
|
5163
5244
|
for (const s of state.shims || []) fs.rmSync(s.path, { force: true });
|
|
5164
5245
|
for (const dir of owned) fs.rmSync(dir, { recursive: true, force: true });
|
|
5165
5246
|
fs.rmSync(path.join(launcher, "spi.cjs"), { force: true });
|