@s-gw/s-gw 0.1.0

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.
Files changed (123) hide show
  1. package/.codex-plugin/plugin.json +35 -0
  2. package/.mcp.json +16 -0
  3. package/LICENSE +201 -0
  4. package/NOTICE +7 -0
  5. package/README.md +197 -0
  6. package/TRADEMARKS.md +9 -0
  7. package/assets/icons/aws-ec2.png +0 -0
  8. package/assets/icons/lucide/bot.svg +8 -0
  9. package/assets/icons/lucide/monitor.svg +5 -0
  10. package/assets/icons/lucide/server.svg +6 -0
  11. package/assets/icons/lucide/terminal.svg +4 -0
  12. package/assets/icons/s-gw-128.png +0 -0
  13. package/assets/icons/s-gw-16.png +0 -0
  14. package/assets/icons/s-gw-180.png +0 -0
  15. package/assets/icons/s-gw-192.png +0 -0
  16. package/assets/icons/s-gw-32.png +0 -0
  17. package/assets/icons/s-gw-64.png +0 -0
  18. package/assets/icons/s-gw-menu-bar-template.png +0 -0
  19. package/dist/agent-context.d.ts +17 -0
  20. package/dist/agent-context.js +207 -0
  21. package/dist/agents.d.ts +64 -0
  22. package/dist/agents.js +763 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +1385 -0
  25. package/dist/command-suggest.d.ts +3 -0
  26. package/dist/command-suggest.js +131 -0
  27. package/dist/console-server.d.ts +16 -0
  28. package/dist/console-server.js +978 -0
  29. package/dist/console-ui/assets/codex-DYTPdPxi.png +0 -0
  30. package/dist/console-ui/assets/cursor-CBrUTJD-.png +0 -0
  31. package/dist/console-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  32. package/dist/console-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  33. package/dist/console-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  34. package/dist/console-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  35. package/dist/console-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  36. package/dist/console-ui/assets/hermes-B8hNbJPm.png +0 -0
  37. package/dist/console-ui/assets/index-BxUf0Sye.js +96 -0
  38. package/dist/console-ui/assets/index-CmTiBR_w.css +2 -0
  39. package/dist/console-ui/assets/omnigent-Cxa4p2Mq.png +0 -0
  40. package/dist/console-ui/assets/openclaw-C5wL4ZVW.png +0 -0
  41. package/dist/console-ui/assets/opencode-D_wFATSC.png +0 -0
  42. package/dist/console-ui/assets/openhands-DnrlGgev.svg +9 -0
  43. package/dist/console-ui/assets/s-gw-64-ByMUGQ3K.png +0 -0
  44. package/dist/console-ui/assets/vscode-Bdtr9eyf.png +0 -0
  45. package/dist/console-ui/assets/zeptoclaw-DztQW8Sw.png +0 -0
  46. package/dist/console-ui/index.html +13 -0
  47. package/dist/crypto.d.ts +6 -0
  48. package/dist/crypto.js +53 -0
  49. package/dist/executor.d.ts +7 -0
  50. package/dist/executor.js +297 -0
  51. package/dist/gateway.d.ts +31 -0
  52. package/dist/gateway.js +114 -0
  53. package/dist/guard.d.ts +61 -0
  54. package/dist/guard.js +247 -0
  55. package/dist/install.d.ts +146 -0
  56. package/dist/install.js +629 -0
  57. package/dist/mcp-server.d.ts +2 -0
  58. package/dist/mcp-server.js +119 -0
  59. package/dist/native/s-gw-core +0 -0
  60. package/dist/native/s-gw-keychain-helper +0 -0
  61. package/dist/onepassword.d.ts +48 -0
  62. package/dist/onepassword.js +412 -0
  63. package/dist/paths.d.ts +4 -0
  64. package/dist/paths.js +22 -0
  65. package/dist/s-gw Menu Bar.app/Contents/Info.plist +28 -0
  66. package/dist/s-gw Menu Bar.app/Contents/MacOS/s-gw-menu-bar-helper +0 -0
  67. package/dist/s-gw Menu Bar.app/Contents/Resources/AppIcon.icns +0 -0
  68. package/dist/s-gw Menu Bar.app/Contents/Resources/AwsEc2.png +0 -0
  69. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-bot.svg +8 -0
  70. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-monitor.svg +5 -0
  71. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-server.svg +6 -0
  72. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-terminal.svg +4 -0
  73. package/dist/s-gw Menu Bar.app/Contents/Resources/MenuBarTemplate.png +0 -0
  74. package/dist/s-gw Menu Bar.app/Contents/_CodeSignature/CodeResources +194 -0
  75. package/dist/s-gw.app/Contents/Info.plist +28 -0
  76. package/dist/s-gw.app/Contents/MacOS/s-gw +0 -0
  77. package/dist/s-gw.app/Contents/Resources/AppIcon.icns +0 -0
  78. package/dist/s-gw.app/Contents/Resources/MenuBarTemplate.png +0 -0
  79. package/dist/s-gw.app/Contents/_CodeSignature/CodeResources +139 -0
  80. package/dist/scanner.d.ts +9 -0
  81. package/dist/scanner.js +437 -0
  82. package/dist/ssh.d.ts +31 -0
  83. package/dist/ssh.js +286 -0
  84. package/dist/store.d.ts +131 -0
  85. package/dist/store.js +1611 -0
  86. package/dist/types.d.ts +196 -0
  87. package/dist/types.js +2 -0
  88. package/dist/unlock.d.ts +29 -0
  89. package/dist/unlock.js +274 -0
  90. package/dist/windows/VERSION.txt +1 -0
  91. package/dist/windows/s-gw-client.cmd +4 -0
  92. package/dist/windows/s-gw-client.ps1 +106 -0
  93. package/dist/windows/s-gw-credential.cmd +4 -0
  94. package/dist/windows/s-gw-credential.ps1 +167 -0
  95. package/dist/windows/s-gw-helper.cmd +4 -0
  96. package/dist/windows/s-gw-helper.ps1 +180 -0
  97. package/docs/README.md +23 -0
  98. package/docs/agents.md +160 -0
  99. package/docs/architecture.md +72 -0
  100. package/docs/deployment.md +447 -0
  101. package/docs/detection.md +44 -0
  102. package/docs/images/s-gw-overview.png +0 -0
  103. package/docs/integrations.md +195 -0
  104. package/docs/keychain.md +39 -0
  105. package/docs/onepassword.md +84 -0
  106. package/docs/quickstart.md +104 -0
  107. package/docs/threat-model.md +100 -0
  108. package/docs/ui/THIRD_PARTY_NOTICES.md +111 -0
  109. package/docs/ui/apple-touch-icon.png +0 -0
  110. package/docs/ui/favicon-32.png +0 -0
  111. package/docs/ui/local-console.html +4477 -0
  112. package/docs/ui/vendor/d3-sankey/d3-array.LICENSE.txt +27 -0
  113. package/docs/ui/vendor/d3-sankey/d3-array.min.js +2 -0
  114. package/docs/ui/vendor/d3-sankey/d3-path.LICENSE.txt +27 -0
  115. package/docs/ui/vendor/d3-sankey/d3-path.min.js +2 -0
  116. package/docs/ui/vendor/d3-sankey/d3-sankey.LICENSE.txt +27 -0
  117. package/docs/ui/vendor/d3-sankey/d3-sankey.min.js +2 -0
  118. package/docs/ui/vendor/d3-sankey/d3-shape.LICENSE.txt +27 -0
  119. package/docs/ui/vendor/d3-sankey/d3-shape.min.js +2 -0
  120. package/docs/ui/vendor/sankeymatic/LICENSE.txt +17 -0
  121. package/docs/ui/vendor/sankeymatic/sankey.js +897 -0
  122. package/package.json +117 -0
  123. package/skills/s-gw/SKILL.md +19 -0
@@ -0,0 +1,629 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { mkdir, rm, writeFile } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { getSgwHome, getStorePath } from "./paths.js";
8
+ import { unlockStatus } from "./unlock.js";
9
+ export const consoleLabel = "com.s-gw.sgw.console";
10
+ export const menuBarLabel = "com.s-gw.sgw.menubar";
11
+ export function getPackageLayout() {
12
+ const here = path.dirname(fileURLToPath(import.meta.url));
13
+ const packageRoot = path.basename(here) === "dist" ? path.dirname(here) : path.dirname(here);
14
+ return {
15
+ packageRoot,
16
+ cliPath: path.join(packageRoot, "dist", "cli.js"),
17
+ mcpPath: path.join(packageRoot, "dist", "mcp-server.js"),
18
+ keychainHelperPath: path.join(packageRoot, "dist", "native", "s-gw-keychain-helper"),
19
+ macAppPath: path.join(packageRoot, "dist", "s-gw.app"),
20
+ macAppBinaryPath: path.join(packageRoot, "dist", "s-gw.app", "Contents", "MacOS", "s-gw"),
21
+ menuBarAppPath: path.join(packageRoot, "dist", "s-gw Menu Bar.app"),
22
+ menuBarBinaryPath: path.join(packageRoot, "dist", "s-gw Menu Bar.app", "Contents", "MacOS", "s-gw-menu-bar-helper"),
23
+ windowsClientScriptPath: path.join(packageRoot, "dist", "windows", "s-gw-client.ps1"),
24
+ windowsClientLauncherPath: path.join(packageRoot, "dist", "windows", "s-gw-client.cmd"),
25
+ windowsHelperScriptPath: path.join(packageRoot, "dist", "windows", "s-gw-helper.ps1"),
26
+ windowsHelperLauncherPath: path.join(packageRoot, "dist", "windows", "s-gw-helper.cmd"),
27
+ windowsCredentialHelperPath: path.join(packageRoot, "dist", "windows", "s-gw-credential.ps1")
28
+ };
29
+ }
30
+ export function packageHealth(port = 8718) {
31
+ const layout = getPackageLayout();
32
+ const unlock = unlockStatus();
33
+ const cli = pathStatus(layout.cliPath);
34
+ const mcp = pathStatus(layout.mcpPath);
35
+ // "Ready" means a fresh user could actually store and redeem a secret: the CLI/MCP
36
+ // entry points exist and there is some unlock source. Without this, `status` looked
37
+ // healthy (every path exists) even when the encrypted ledger could not be unlocked.
38
+ const unlockConfigured = unlock.activeSource !== "none";
39
+ const ready = unlockConfigured && cli.exists && mcp.exists;
40
+ return {
41
+ packageRoot: layout.packageRoot,
42
+ ready,
43
+ readiness: buildReadiness({ unlockConfigured, cli: cli.exists, mcp: mcp.exists }),
44
+ cliPath: cli,
45
+ mcpPath: mcp,
46
+ keychainHelperPath: pathStatus(layout.keychainHelperPath),
47
+ macAppPath: pathStatus(layout.macAppPath),
48
+ macAppBinaryPath: pathStatus(layout.macAppBinaryPath),
49
+ menuBarAppPath: pathStatus(layout.menuBarAppPath),
50
+ menuBarBinaryPath: pathStatus(layout.menuBarBinaryPath),
51
+ windowsClientScriptPath: pathStatus(layout.windowsClientScriptPath),
52
+ windowsClientLauncherPath: pathStatus(layout.windowsClientLauncherPath),
53
+ windowsHelperScriptPath: pathStatus(layout.windowsHelperScriptPath),
54
+ windowsHelperLauncherPath: pathStatus(layout.windowsHelperLauncherPath),
55
+ windowsCredentialHelperPath: pathStatus(layout.windowsCredentialHelperPath),
56
+ storePath: getStorePath(),
57
+ consoleUrl: consoleUrl(port),
58
+ unlock,
59
+ launchAgents: {
60
+ console: launchAgentStatus("console"),
61
+ menuBar: launchAgentStatus("menubar")
62
+ }
63
+ };
64
+ }
65
+ // Console/native surfaces share this so the "not ready" wording stays identical
66
+ // everywhere. The console process is, by definition, running from a built package,
67
+ // so it only needs to report unlock readiness.
68
+ export function readinessForUnlock(unlockConfigured) {
69
+ return buildReadiness({ unlockConfigured, cli: true, mcp: true });
70
+ }
71
+ function buildReadiness(checks) {
72
+ const blockers = [];
73
+ if (!checks.cli || !checks.mcp) {
74
+ blockers.push("Build artifacts are missing. Run `npm run build` (or reinstall the package).");
75
+ }
76
+ if (!checks.unlockConfigured) {
77
+ blockers.push("No local unlock material. Run `s-gw setup`, or `s-gw unlock keychain set --value-stdin`, or set SGW_MASTER_PASSPHRASE.");
78
+ }
79
+ const ok = blockers.length === 0;
80
+ return {
81
+ ok,
82
+ summary: ok ? "s-gw is ready to store and redeem secrets." : "s-gw is not ready yet.",
83
+ blockers
84
+ };
85
+ }
86
+ export async function installConsoleLaunchAgent(options = {}) {
87
+ requireMac("launchd service install");
88
+ const port = options.port || 8718;
89
+ const plistPath = launchAgentPath(consoleLabel);
90
+ const logs = await ensureLogDir();
91
+ await writeFile(plistPath, buildConsoleLaunchAgentPlist(port, logs), { mode: 0o644 });
92
+ if (options.start) {
93
+ startLaunchAgent(consoleLabel, plistPath);
94
+ }
95
+ return launchAgentStatus("console");
96
+ }
97
+ export async function uninstallConsoleLaunchAgent() {
98
+ requireMac("launchd service uninstall");
99
+ stopLaunchAgent(consoleLabel);
100
+ await rm(launchAgentPath(consoleLabel), { force: true });
101
+ return launchAgentStatus("console");
102
+ }
103
+ export async function installMenuBarLaunchAgent(options = {}) {
104
+ requireMac("menu-bar install");
105
+ assertMenuBarExists();
106
+ const plistPath = launchAgentPath(menuBarLabel);
107
+ const logs = await ensureLogDir();
108
+ await writeFile(plistPath, buildMenuBarLaunchAgentPlist(options, logs), { mode: 0o644 });
109
+ if (options.start) {
110
+ startLaunchAgent(menuBarLabel, plistPath);
111
+ }
112
+ return launchAgentStatus("menubar");
113
+ }
114
+ export async function uninstallMenuBarLaunchAgent() {
115
+ requireMac("menu-bar uninstall");
116
+ stopLaunchAgent(menuBarLabel);
117
+ await rm(launchAgentPath(menuBarLabel), { force: true });
118
+ return launchAgentStatus("menubar");
119
+ }
120
+ export function startInstalledLaunchAgent(kind) {
121
+ requireMac("launch-agent start");
122
+ const label = kind === "console" ? consoleLabel : menuBarLabel;
123
+ const plistPath = launchAgentPath(label);
124
+ if (!existsSync(plistPath)) {
125
+ throw new Error(`LaunchAgent is not installed: ${plistPath}`);
126
+ }
127
+ startLaunchAgent(label, plistPath);
128
+ return launchAgentStatus(kind);
129
+ }
130
+ export function stopInstalledLaunchAgent(kind) {
131
+ requireMac("launch-agent stop");
132
+ stopLaunchAgent(kind === "console" ? consoleLabel : menuBarLabel);
133
+ return launchAgentStatus(kind);
134
+ }
135
+ export function launchAgentStatus(kind) {
136
+ const label = kind === "console" ? consoleLabel : menuBarLabel;
137
+ const plistPath = launchAgentPath(label);
138
+ return {
139
+ label,
140
+ plistPath,
141
+ installed: existsSync(plistPath),
142
+ loaded: process.platform === "darwin" ? isLaunchAgentLoaded(label) : false
143
+ };
144
+ }
145
+ export function openMenuBarHelper(options = {}) {
146
+ requireMac("menu-bar open");
147
+ assertMenuBarExists();
148
+ const layout = getPackageLayout();
149
+ const url = options.consoleUrl || consoleUrl(options.port || 8718);
150
+ const env = menuBarEnvironment(url, options.countMode);
151
+ const args = [];
152
+ for (const [key, value] of Object.entries(env)) {
153
+ args.push("--env", `${key}=${value}`);
154
+ }
155
+ args.push(layout.menuBarAppPath, "--args");
156
+ if (options.show) {
157
+ args.push("--show-on-launch");
158
+ }
159
+ if (options.notify !== false) {
160
+ args.push("--notify-on-launch");
161
+ }
162
+ else {
163
+ args.push("--no-notify");
164
+ }
165
+ const result = spawnSync("open", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
166
+ if (result.status !== 0) {
167
+ throw new Error(result.stderr.trim() || "Failed to open s-gw menu-bar helper.");
168
+ }
169
+ return { appPath: layout.menuBarAppPath, consoleUrl: url };
170
+ }
171
+ export function openMacApp(options = {}) {
172
+ requireMac("mac app open");
173
+ assertMacAppExists();
174
+ const layout = getPackageLayout();
175
+ const url = options.consoleUrl || consoleUrl(options.port || 8718);
176
+ const existing = existingMacAppProcess(layout);
177
+ if (existing) {
178
+ focusMacAppProcess(existing, layout.macAppPath);
179
+ return {
180
+ appPath: layout.macAppPath,
181
+ consoleUrl: url,
182
+ reusedExisting: true,
183
+ process: existing
184
+ };
185
+ }
186
+ const env = menuBarEnvironment(url);
187
+ const args = [];
188
+ for (const [key, value] of Object.entries(env)) {
189
+ args.push("--env", `${key}=${value}`);
190
+ }
191
+ args.push(layout.macAppPath);
192
+ const result = spawnSync("open", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
193
+ if (result.status !== 0) {
194
+ throw new Error(result.stderr.trim() || "Failed to open s-gw macOS app.");
195
+ }
196
+ return { appPath: layout.macAppPath, consoleUrl: url, reusedExisting: false };
197
+ }
198
+ export function openWindowsClient(options = {}) {
199
+ requireWindows("Windows client open");
200
+ assertWindowsClientExists();
201
+ const layout = getPackageLayout();
202
+ const url = options.consoleUrl || consoleUrl(options.port || 8718);
203
+ const result = spawnSync("powershell.exe", [
204
+ "-NoProfile",
205
+ "-ExecutionPolicy",
206
+ "Bypass",
207
+ "-File",
208
+ layout.windowsClientScriptPath,
209
+ "-Port",
210
+ String(options.port || 8718),
211
+ "-ConsoleUrl",
212
+ url
213
+ ], {
214
+ encoding: "utf8",
215
+ env: windowsEnvironment(url),
216
+ stdio: ["ignore", "pipe", "pipe"]
217
+ });
218
+ if (result.status !== 0) {
219
+ throw new Error(result.stderr.trim() || result.stdout.trim() || "Failed to open s-gw Windows client.");
220
+ }
221
+ return {
222
+ scriptPath: layout.windowsClientScriptPath,
223
+ launcherPath: layout.windowsClientLauncherPath,
224
+ consoleUrl: url
225
+ };
226
+ }
227
+ export function openWindowsHelper(options = {}) {
228
+ requireWindows("Windows helper open");
229
+ assertWindowsHelperExists();
230
+ const layout = getPackageLayout();
231
+ const url = options.consoleUrl || consoleUrl(options.port || 8718);
232
+ const child = spawn("powershell.exe", [
233
+ "-NoProfile",
234
+ "-ExecutionPolicy",
235
+ "Bypass",
236
+ "-File",
237
+ layout.windowsHelperScriptPath,
238
+ "-Port",
239
+ String(options.port || 8718),
240
+ "-ConsoleUrl",
241
+ url
242
+ ], {
243
+ detached: true,
244
+ env: windowsEnvironment(url),
245
+ stdio: "ignore"
246
+ });
247
+ child.unref();
248
+ return {
249
+ scriptPath: layout.windowsHelperScriptPath,
250
+ launcherPath: layout.windowsHelperLauncherPath,
251
+ consoleUrl: url,
252
+ pid: child.pid
253
+ };
254
+ }
255
+ export function macAppProcessRecordPath() {
256
+ return path.join(os.homedir(), "Library", "Application Support", "s-gw", "s-gw-app.process.json");
257
+ }
258
+ function existingMacAppProcess(layout) {
259
+ const record = readMacAppProcessRecord();
260
+ if (record?.alive) {
261
+ return record;
262
+ }
263
+ return findRunningMacAppProcess(layout);
264
+ }
265
+ function readMacAppProcessRecord() {
266
+ const recordPath = macAppProcessRecordPath();
267
+ if (!existsSync(recordPath)) {
268
+ return undefined;
269
+ }
270
+ try {
271
+ const parsed = JSON.parse(readFileSync(recordPath, "utf8"));
272
+ const pid = Number(parsed.pid);
273
+ if (!Number.isInteger(pid) || pid <= 0) {
274
+ return undefined;
275
+ }
276
+ return {
277
+ pid,
278
+ source: "record",
279
+ alive: isPidAlive(pid),
280
+ recordPath,
281
+ bundleIdentifier: stringValue(parsed.bundleIdentifier),
282
+ bundlePath: stringValue(parsed.bundlePath),
283
+ executablePath: stringValue(parsed.executablePath),
284
+ startedAt: stringValue(parsed.startedAt),
285
+ updatedAt: stringValue(parsed.updatedAt)
286
+ };
287
+ }
288
+ catch {
289
+ return undefined;
290
+ }
291
+ }
292
+ function findRunningMacAppProcess(layout) {
293
+ const result = spawnSync("pgrep", ["-x", "s-gw"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
294
+ if (result.status !== 0) {
295
+ return undefined;
296
+ }
297
+ const pids = result.stdout
298
+ .split(/\s+/)
299
+ .map((item) => Number(item.trim()))
300
+ .filter((pid) => Number.isInteger(pid) && pid > 0 && isPidAlive(pid));
301
+ if (pids.length === 0) {
302
+ return undefined;
303
+ }
304
+ const candidates = pids.map((pid) => ({
305
+ pid,
306
+ command: commandForPid(pid)
307
+ }));
308
+ const selected = candidates.find((item) => commandMatchesPath(item.command, layout.macAppBinaryPath)) || candidates[0];
309
+ return {
310
+ pid: selected.pid,
311
+ source: "process-list",
312
+ alive: true,
313
+ bundleIdentifier: "com.s-gw.sgw.app",
314
+ bundlePath: layout.macAppPath,
315
+ executablePath: layout.macAppBinaryPath,
316
+ command: selected.command,
317
+ otherPids: candidates
318
+ .filter((item) => item.pid !== selected.pid)
319
+ .map((item) => item.pid)
320
+ };
321
+ }
322
+ function focusMacAppProcess(app, appPath) {
323
+ if (postOpenMainWindowNotification()) {
324
+ return;
325
+ }
326
+ if (app.bundleIdentifier) {
327
+ const byBundle = spawnSync("open", ["-b", app.bundleIdentifier], {
328
+ encoding: "utf8",
329
+ stdio: ["ignore", "pipe", "pipe"]
330
+ });
331
+ if (byBundle.status === 0) {
332
+ return;
333
+ }
334
+ }
335
+ const byPath = spawnSync("open", [appPath], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
336
+ if (byPath.status !== 0) {
337
+ throw new Error(byPath.stderr.trim() || "Failed to focus running s-gw macOS app.");
338
+ }
339
+ }
340
+ function postOpenMainWindowNotification() {
341
+ const script = [
342
+ "ObjC.import('Foundation')",
343
+ "$.NSDistributedNotificationCenter.defaultCenter.postNotificationNameObjectUserInfoDeliverImmediately(",
344
+ " 'com.s-gw.sgw.openMainWindow', null, null, true",
345
+ ")"
346
+ ].join("\n");
347
+ const result = spawnSync("osascript", ["-l", "JavaScript", "-e", script], {
348
+ encoding: "utf8",
349
+ stdio: ["ignore", "pipe", "pipe"]
350
+ });
351
+ return result.status === 0;
352
+ }
353
+ function commandForPid(pid) {
354
+ const result = spawnSync("ps", ["-p", String(pid), "-o", "command="], {
355
+ encoding: "utf8",
356
+ stdio: ["ignore", "pipe", "pipe"]
357
+ });
358
+ if (result.status !== 0) {
359
+ return undefined;
360
+ }
361
+ return result.stdout.trim() || undefined;
362
+ }
363
+ function commandMatchesPath(command, targetPath) {
364
+ return command === targetPath || command?.startsWith(`${targetPath} `) === true;
365
+ }
366
+ function isPidAlive(pid) {
367
+ try {
368
+ process.kill(pid, 0);
369
+ return true;
370
+ }
371
+ catch (error) {
372
+ return error.code === "EPERM";
373
+ }
374
+ }
375
+ function stringValue(value) {
376
+ return typeof value === "string" && value.trim() ? value : undefined;
377
+ }
378
+ export function buildConsoleLaunchAgentPlist(port, logsDir) {
379
+ const layout = getPackageLayout();
380
+ return buildLaunchAgentPlist({
381
+ label: consoleLabel,
382
+ programArguments: [
383
+ process.execPath,
384
+ layout.cliPath,
385
+ "console",
386
+ "--host",
387
+ "127.0.0.1",
388
+ "--port",
389
+ String(port),
390
+ "--no-open"
391
+ ],
392
+ environment: launchdBaseEnvironment(),
393
+ runAtLoad: true,
394
+ keepAlive: true,
395
+ stdoutPath: path.join(logsDir, "console.log"),
396
+ stderrPath: path.join(logsDir, "console.err.log")
397
+ });
398
+ }
399
+ export function buildMenuBarLaunchAgentPlist(options, logsDir) {
400
+ const layout = getPackageLayout();
401
+ const args = [layout.menuBarBinaryPath];
402
+ if (options.notify !== false) {
403
+ args.push("--notify-on-launch");
404
+ }
405
+ else {
406
+ args.push("--no-notify");
407
+ }
408
+ return buildLaunchAgentPlist({
409
+ label: menuBarLabel,
410
+ programArguments: args,
411
+ environment: menuBarEnvironment(options.consoleUrl || consoleUrl(options.port || 8718), options.countMode),
412
+ runAtLoad: true,
413
+ keepAlive: false,
414
+ stdoutPath: path.join(logsDir, "menubar.log"),
415
+ stderrPath: path.join(logsDir, "menubar.err.log"),
416
+ limitToAqua: true
417
+ });
418
+ }
419
+ function buildLaunchAgentPlist(definition) {
420
+ const envPairs = Object.entries(definition.environment)
421
+ .sort(([a], [b]) => a.localeCompare(b))
422
+ .map(([key, value]) => ` <key>${xmlEscape(key)}</key>\n <string>${xmlEscape(value)}</string>`)
423
+ .join("\n");
424
+ const programArgs = definition.programArguments
425
+ .map((item) => ` <string>${xmlEscape(item)}</string>`)
426
+ .join("\n");
427
+ const aqua = definition.limitToAqua
428
+ ? " <key>LimitLoadToSessionType</key>\n <string>Aqua</string>\n"
429
+ : "";
430
+ return `<?xml version="1.0" encoding="UTF-8"?>
431
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
432
+ <plist version="1.0">
433
+ <dict>
434
+ <key>Label</key>
435
+ <string>${xmlEscape(definition.label)}</string>
436
+ <key>ProgramArguments</key>
437
+ <array>
438
+ ${programArgs}
439
+ </array>
440
+ <key>EnvironmentVariables</key>
441
+ <dict>
442
+ ${envPairs}
443
+ </dict>
444
+ <key>RunAtLoad</key>
445
+ <${definition.runAtLoad ? "true" : "false"}/>
446
+ <key>KeepAlive</key>
447
+ <${definition.keepAlive ? "true" : "false"}/>
448
+ ${aqua} <key>StandardOutPath</key>
449
+ <string>${xmlEscape(definition.stdoutPath)}</string>
450
+ <key>StandardErrorPath</key>
451
+ <string>${xmlEscape(definition.stderrPath)}</string>
452
+ </dict>
453
+ </plist>
454
+ `;
455
+ }
456
+ function startLaunchAgent(label, plistPath) {
457
+ stopLaunchAgent(label);
458
+ runLaunchctl(["bootstrap", launchdDomain(), plistPath]);
459
+ runLaunchctl(["kickstart", "-k", `${launchdDomain()}/${label}`]);
460
+ }
461
+ function stopLaunchAgent(label) {
462
+ const plistPath = launchAgentPath(label);
463
+ if (existsSync(plistPath)) {
464
+ spawnSync("launchctl", ["bootout", launchdDomain(), plistPath], {
465
+ encoding: "utf8",
466
+ stdio: ["ignore", "pipe", "pipe"]
467
+ });
468
+ }
469
+ spawnSync("launchctl", ["bootout", `${launchdDomain()}/${label}`], {
470
+ encoding: "utf8",
471
+ stdio: ["ignore", "pipe", "pipe"]
472
+ });
473
+ waitForLaunchAgentToUnload(label);
474
+ }
475
+ function runLaunchctl(args) {
476
+ const result = spawnSync("launchctl", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
477
+ if (result.status !== 0) {
478
+ throw new Error(result.stderr.trim() || `launchctl ${args.join(" ")} failed.`);
479
+ }
480
+ }
481
+ function isLaunchAgentLoaded(label) {
482
+ const result = spawnSync("launchctl", ["print", `${launchdDomain()}/${label}`], {
483
+ encoding: "utf8",
484
+ stdio: ["ignore", "pipe", "pipe"]
485
+ });
486
+ return result.status === 0;
487
+ }
488
+ function waitForLaunchAgentToUnload(label) {
489
+ const flag = new Int32Array(new SharedArrayBuffer(4));
490
+ for (let i = 0; i < 20; i += 1) {
491
+ if (!isLaunchAgentLoaded(label)) {
492
+ return;
493
+ }
494
+ Atomics.wait(flag, 0, 0, 50);
495
+ }
496
+ }
497
+ function launchdDomain() {
498
+ return `gui/${process.getuid?.() ?? os.userInfo().uid}`;
499
+ }
500
+ function launchAgentPath(label) {
501
+ return path.join(os.homedir(), "Library", "LaunchAgents", `${label}.plist`);
502
+ }
503
+ async function ensureLogDir() {
504
+ const logs = path.join(getSgwHome(), "logs");
505
+ await mkdir(logs, { recursive: true, mode: 0o700 });
506
+ await mkdir(path.dirname(launchAgentPath(consoleLabel)), { recursive: true });
507
+ return logs;
508
+ }
509
+ function launchdBaseEnvironment() {
510
+ const env = {
511
+ PATH: process.env.PATH || "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin",
512
+ SGW_NODE_PATH: process.execPath
513
+ };
514
+ copyEnv(env, "SGW_HOME");
515
+ copyEnv(env, "SGW_KEYCHAIN_SERVICE");
516
+ copyEnv(env, "SGW_KEYCHAIN_ACCOUNT");
517
+ copyEnv(env, "SGW_KEYCHAIN_HELPER");
518
+ return env;
519
+ }
520
+ export function normalizeMenuBarCountMode(value) {
521
+ if (!value) {
522
+ return undefined;
523
+ }
524
+ switch (value.trim().toLowerCase()) {
525
+ case "pending":
526
+ case "approval":
527
+ case "approvals":
528
+ case "authorization":
529
+ case "authorizations":
530
+ case "auth":
531
+ return "pending";
532
+ case "credential":
533
+ case "credentials":
534
+ case "secret":
535
+ case "secrets":
536
+ case "handle":
537
+ case "handles":
538
+ return "credentials";
539
+ case "none":
540
+ case "off":
541
+ case "hide":
542
+ case "hidden":
543
+ return "none";
544
+ default:
545
+ throw new Error("--count must be pending, credentials, or none.");
546
+ }
547
+ }
548
+ function menuBarEnvironment(url, countMode) {
549
+ const layout = getPackageLayout();
550
+ const env = {
551
+ ...launchdBaseEnvironment(),
552
+ SGW_REPO_ROOT: layout.packageRoot,
553
+ SGW_CLI_PATH: layout.cliPath,
554
+ SGW_CONSOLE_URL: url,
555
+ SGW_APP_PATH: layout.macAppPath
556
+ };
557
+ if (countMode) {
558
+ env.SGW_MENU_BAR_COUNT_MODE = countMode;
559
+ }
560
+ return env;
561
+ }
562
+ function windowsEnvironment(url) {
563
+ const layout = getPackageLayout();
564
+ return {
565
+ ...process.env,
566
+ SGW_NODE_PATH: process.execPath,
567
+ SGW_CLI_PATH: layout.cliPath,
568
+ SGW_CONSOLE_URL: url,
569
+ SGW_APP_PATH: layout.windowsClientLauncherPath
570
+ };
571
+ }
572
+ function copyEnv(target, key) {
573
+ const value = process.env[key];
574
+ if (value) {
575
+ target[key] = value;
576
+ }
577
+ }
578
+ function consoleUrl(port) {
579
+ return `http://127.0.0.1:${port}/`;
580
+ }
581
+ function pathStatus(filePath) {
582
+ return {
583
+ path: filePath,
584
+ exists: existsSync(filePath)
585
+ };
586
+ }
587
+ function assertMenuBarExists() {
588
+ const layout = getPackageLayout();
589
+ if (!existsSync(layout.menuBarAppPath) || !existsSync(layout.menuBarBinaryPath)) {
590
+ throw new Error(`Menu-bar helper is missing. Expected app bundle at ${layout.menuBarAppPath}`);
591
+ }
592
+ }
593
+ function assertMacAppExists() {
594
+ const layout = getPackageLayout();
595
+ if (!existsSync(layout.macAppPath) || !existsSync(layout.macAppBinaryPath)) {
596
+ throw new Error(`macOS app is missing. Expected app bundle at ${layout.macAppPath}`);
597
+ }
598
+ }
599
+ function assertWindowsClientExists() {
600
+ const layout = getPackageLayout();
601
+ if (!existsSync(layout.windowsClientScriptPath)) {
602
+ throw new Error(`Windows client is missing. Expected script at ${layout.windowsClientScriptPath}`);
603
+ }
604
+ }
605
+ function assertWindowsHelperExists() {
606
+ const layout = getPackageLayout();
607
+ if (!existsSync(layout.windowsHelperScriptPath)) {
608
+ throw new Error(`Windows helper is missing. Expected script at ${layout.windowsHelperScriptPath}`);
609
+ }
610
+ }
611
+ function requireMac(action) {
612
+ if (process.platform !== "darwin") {
613
+ throw new Error(`${action} is only available on macOS.`);
614
+ }
615
+ }
616
+ function requireWindows(action) {
617
+ if (process.platform !== "win32") {
618
+ throw new Error(`${action} is only available on Windows.`);
619
+ }
620
+ }
621
+ function xmlEscape(value) {
622
+ return value
623
+ .replaceAll("&", "&amp;")
624
+ .replaceAll("<", "&lt;")
625
+ .replaceAll(">", "&gt;")
626
+ .replaceAll('"', "&quot;")
627
+ .replaceAll("'", "&apos;");
628
+ }
629
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};