sneakoscope 4.7.1 → 4.7.4
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/README.md +11 -11
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/install-helpers.js +215 -57
- package/dist/commands/codex-lb.js +143 -7
- package/dist/commands/doctor.js +59 -2
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +11 -1
- package/dist/core/codex-app/codex-app-restart.js +65 -0
- package/dist/core/codex-app/sks-menubar.js +445 -0
- package/dist/core/codex-app.js +239 -3
- package/dist/core/codex-control/codex-sdk-config-policy.js +2 -2
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/codex-lb-setup.js +1 -1
- package/dist/core/commands/basic-cli.js +14 -7
- package/dist/core/doctor/doctor-dirty-planner.js +2 -0
- package/dist/core/doctor/doctor-readiness-matrix.js +4 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/imagegen/imagegen-capability.js +2 -2
- package/dist/core/init.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/provider/provider-context.js +1 -1
- package/dist/core/routes.js +2 -2
- package/dist/core/update-check.js +67 -18
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-app-provider-model-ui-check.js +114 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +90 -0
- package/dist/scripts/codex-lb-setup-truthfulness-check.js +6 -1
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +5 -1
- package/dist/scripts/gpt-image-2-real-file-smoke.js +2 -2
- package/dist/scripts/provider-context-config-toml-check.js +2 -2
- package/dist/scripts/sks-menubar-install-check.js +59 -0
- package/dist/scripts/update-default-command-check.js +28 -0
- package/package.json +5 -1
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ensureDir, exists, runProcess, which, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
|
+
const LABEL = 'com.sneakoscope.sks-menubar';
|
|
6
|
+
const MENU_ITEMS = [
|
|
7
|
+
'Use codex-lb',
|
|
8
|
+
'Use ChatGPT OAuth',
|
|
9
|
+
'Set OpenRouter Key and GLM Profiles',
|
|
10
|
+
'Fast Check',
|
|
11
|
+
'SKS Version Check',
|
|
12
|
+
'Update SKS Now',
|
|
13
|
+
'Open Codex Settings',
|
|
14
|
+
'Restart Codex',
|
|
15
|
+
'Quit SKS Menu'
|
|
16
|
+
];
|
|
17
|
+
export async function installSksMenuBar(opts = {}) {
|
|
18
|
+
const apply = opts.apply === true;
|
|
19
|
+
const env = opts.env || process.env;
|
|
20
|
+
const home = path.resolve(opts.home || env.HOME || os.homedir());
|
|
21
|
+
const root = path.resolve(opts.root || process.cwd());
|
|
22
|
+
const installDir = path.join(home, '.codex', 'sks-menubar');
|
|
23
|
+
const appPath = path.join(installDir, 'SKSMenuBar.app');
|
|
24
|
+
const contentsPath = path.join(appPath, 'Contents');
|
|
25
|
+
const macosPath = path.join(contentsPath, 'MacOS');
|
|
26
|
+
const executablePath = path.join(macosPath, 'SKSMenuBar');
|
|
27
|
+
const sourcePath = path.join(installDir, 'SKSMenuBar.swift');
|
|
28
|
+
const infoPlistPath = path.join(contentsPath, 'Info.plist');
|
|
29
|
+
const actionScriptPath = path.join(installDir, 'sks-menubar-action.sh');
|
|
30
|
+
const launchAgentPath = path.join(home, 'Library', 'LaunchAgents', `${LABEL}.plist`);
|
|
31
|
+
const reportPath = path.join(root, '.sneakoscope', 'reports', 'sks-menubar.json');
|
|
32
|
+
const actions = [];
|
|
33
|
+
const warnings = [];
|
|
34
|
+
if (process.platform !== 'darwin') {
|
|
35
|
+
const result = {
|
|
36
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
37
|
+
ok: true,
|
|
38
|
+
apply,
|
|
39
|
+
status: 'unsupported_platform',
|
|
40
|
+
platform: process.platform,
|
|
41
|
+
app_path: null,
|
|
42
|
+
executable_path: null,
|
|
43
|
+
launch_agent_path: null,
|
|
44
|
+
action_script_path: null,
|
|
45
|
+
report_path: apply ? reportPath : null,
|
|
46
|
+
menu_items: MENU_ITEMS,
|
|
47
|
+
actions: [],
|
|
48
|
+
launch: { requested: false, method: 'none', ok: true },
|
|
49
|
+
blockers: [],
|
|
50
|
+
warnings: ['sks_menubar_requires_macos']
|
|
51
|
+
};
|
|
52
|
+
if (apply)
|
|
53
|
+
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
if (!apply) {
|
|
57
|
+
const installed = await exists(executablePath);
|
|
58
|
+
const launchAgent = await exists(launchAgentPath);
|
|
59
|
+
return {
|
|
60
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
61
|
+
ok: true,
|
|
62
|
+
apply,
|
|
63
|
+
status: 'planned',
|
|
64
|
+
platform: process.platform,
|
|
65
|
+
app_path: appPath,
|
|
66
|
+
executable_path: executablePath,
|
|
67
|
+
launch_agent_path: launchAgentPath,
|
|
68
|
+
action_script_path: actionScriptPath,
|
|
69
|
+
report_path: reportPath,
|
|
70
|
+
menu_items: MENU_ITEMS,
|
|
71
|
+
actions: installed ? ['menubar_app_present'] : ['menubar_app_install_available'],
|
|
72
|
+
launch: {
|
|
73
|
+
requested: false,
|
|
74
|
+
method: 'skipped',
|
|
75
|
+
ok: true
|
|
76
|
+
},
|
|
77
|
+
blockers: [],
|
|
78
|
+
warnings: launchAgent ? [] : ['launch_agent_not_installed_yet']
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const swiftc = env.SKS_MENUBAR_SWIFTC || await which('swiftc').catch(() => null) || await fallbackTool('/usr/bin/swiftc');
|
|
82
|
+
const launchctl = env.SKS_MENUBAR_LAUNCHCTL || await which('launchctl').catch(() => null) || await fallbackTool('/bin/launchctl');
|
|
83
|
+
const open = env.SKS_MENUBAR_OPEN || await which('open').catch(() => null) || await fallbackTool('/usr/bin/open');
|
|
84
|
+
if (!swiftc)
|
|
85
|
+
return await blockedResult('swiftc_missing');
|
|
86
|
+
await ensureDir(installDir);
|
|
87
|
+
await ensureDir(macosPath);
|
|
88
|
+
await ensureDir(path.dirname(launchAgentPath));
|
|
89
|
+
const sksEntry = resolveSksEntry(opts.sksEntry);
|
|
90
|
+
await writeTextAtomic(actionScriptPath, actionScriptSource({ nodeBin: process.execPath, sksEntry }));
|
|
91
|
+
await fs.chmod(actionScriptPath, 0o755);
|
|
92
|
+
actions.push(`wrote ${actionScriptPath}`);
|
|
93
|
+
await writeTextAtomic(sourcePath, swiftMenuSource(actionScriptPath));
|
|
94
|
+
actions.push(`wrote ${sourcePath}`);
|
|
95
|
+
await writeTextAtomic(infoPlistPath, infoPlistSource());
|
|
96
|
+
actions.push(`wrote ${infoPlistPath}`);
|
|
97
|
+
const compile = await runProcess(swiftc, ['-framework', 'Cocoa', sourcePath, '-o', executablePath], {
|
|
98
|
+
timeoutMs: 60_000,
|
|
99
|
+
maxOutputBytes: 64 * 1024
|
|
100
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
101
|
+
if (compile.code !== 0) {
|
|
102
|
+
return await blockedResult('swift_compile_failed', String(compile.stderr || compile.stdout || '').trim());
|
|
103
|
+
}
|
|
104
|
+
await fs.chmod(executablePath, 0o755).catch(() => undefined);
|
|
105
|
+
actions.push(`compiled ${executablePath}`);
|
|
106
|
+
await writeTextAtomic(launchAgentPath, launchAgentSource(executablePath, installDir));
|
|
107
|
+
actions.push(`wrote ${launchAgentPath}`);
|
|
108
|
+
const launchRequested = opts.launch !== false && env.SKS_SKIP_SKS_MENUBAR_LAUNCH !== '1';
|
|
109
|
+
const launch = launchRequested && launchctl
|
|
110
|
+
? await launchWithLaunchctl({ launchctl, open, appPath, executablePath, launchAgentPath })
|
|
111
|
+
: {
|
|
112
|
+
requested: launchRequested,
|
|
113
|
+
method: 'skipped',
|
|
114
|
+
ok: !launchRequested,
|
|
115
|
+
error: launchRequested ? 'launchctl_missing' : null
|
|
116
|
+
};
|
|
117
|
+
if (launchRequested && !launchctl)
|
|
118
|
+
warnings.push('launchctl_missing');
|
|
119
|
+
if (launch.method === 'open-fallback')
|
|
120
|
+
warnings.push('launchctl_bootstrap_failed_open_fallback_used');
|
|
121
|
+
const ok = launch.ok === true;
|
|
122
|
+
const result = {
|
|
123
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
124
|
+
ok,
|
|
125
|
+
apply,
|
|
126
|
+
status: ok
|
|
127
|
+
? launch.requested === false || launch.method === 'skipped'
|
|
128
|
+
? 'installed_launch_skipped'
|
|
129
|
+
: launch.method === 'open-fallback'
|
|
130
|
+
? 'installed_open_fallback'
|
|
131
|
+
: 'installed'
|
|
132
|
+
: 'blocked',
|
|
133
|
+
platform: process.platform,
|
|
134
|
+
app_path: appPath,
|
|
135
|
+
executable_path: executablePath,
|
|
136
|
+
launch_agent_path: launchAgentPath,
|
|
137
|
+
action_script_path: actionScriptPath,
|
|
138
|
+
report_path: reportPath,
|
|
139
|
+
menu_items: MENU_ITEMS,
|
|
140
|
+
actions,
|
|
141
|
+
launch,
|
|
142
|
+
blockers: ok ? [] : [launch.error || 'sks_menubar_launch_failed'],
|
|
143
|
+
warnings
|
|
144
|
+
};
|
|
145
|
+
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
146
|
+
return result;
|
|
147
|
+
async function blockedResult(reason, detail) {
|
|
148
|
+
const result = {
|
|
149
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
150
|
+
ok: false,
|
|
151
|
+
apply,
|
|
152
|
+
status: 'blocked',
|
|
153
|
+
platform: process.platform,
|
|
154
|
+
app_path: appPath,
|
|
155
|
+
executable_path: executablePath,
|
|
156
|
+
launch_agent_path: launchAgentPath,
|
|
157
|
+
action_script_path: actionScriptPath,
|
|
158
|
+
report_path: reportPath,
|
|
159
|
+
menu_items: MENU_ITEMS,
|
|
160
|
+
actions,
|
|
161
|
+
launch: { requested: false, method: 'none', ok: false, error: detail || reason },
|
|
162
|
+
blockers: [reason],
|
|
163
|
+
warnings: detail ? [detail] : []
|
|
164
|
+
};
|
|
165
|
+
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function fallbackTool(candidate) {
|
|
170
|
+
return await exists(candidate).then((ok) => ok ? candidate : null).catch(() => null);
|
|
171
|
+
}
|
|
172
|
+
function resolveSksEntry(explicit) {
|
|
173
|
+
if (explicit)
|
|
174
|
+
return path.resolve(explicit);
|
|
175
|
+
const argvEntry = process.argv[1] ? path.resolve(process.argv[1]) : '';
|
|
176
|
+
return argvEntry || path.join(process.cwd(), 'dist', 'bin', 'sks.js');
|
|
177
|
+
}
|
|
178
|
+
function actionScriptSource(input) {
|
|
179
|
+
return `#!/bin/zsh
|
|
180
|
+
set -e
|
|
181
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
|
|
182
|
+
NODE_BIN=${shellQuote(input.nodeBin)}
|
|
183
|
+
SKS_ENTRY=${shellQuote(input.sksEntry)}
|
|
184
|
+
if [ -x "$NODE_BIN" ] && [ -f "$SKS_ENTRY" ]; then
|
|
185
|
+
exec "$NODE_BIN" "$SKS_ENTRY" "$@"
|
|
186
|
+
fi
|
|
187
|
+
if command -v sks >/dev/null 2>&1; then
|
|
188
|
+
exec sks "$@"
|
|
189
|
+
fi
|
|
190
|
+
echo "SKS command not found. Run npm link or install Sneakoscope Codex, then run sks doctor --fix again." >&2
|
|
191
|
+
exit 127
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
194
|
+
function swiftMenuSource(actionScriptPath) {
|
|
195
|
+
return `import Cocoa
|
|
196
|
+
import Foundation
|
|
197
|
+
|
|
198
|
+
let actionScript = ${swiftString(actionScriptPath)}
|
|
199
|
+
|
|
200
|
+
func shellQuote(_ value: String) -> String {
|
|
201
|
+
return "'" + value.replacingOccurrences(of: "'", with: "'\\\\''") + "'"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func runDetached(_ executable: String, _ args: [String] = []) {
|
|
205
|
+
let process = Process()
|
|
206
|
+
process.executableURL = URL(fileURLWithPath: executable)
|
|
207
|
+
process.arguments = args
|
|
208
|
+
try? process.run()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
func runInTerminal(_ command: String) {
|
|
212
|
+
let escaped = command
|
|
213
|
+
.replacingOccurrences(of: "\\\\", with: "\\\\\\\\")
|
|
214
|
+
.replacingOccurrences(of: "\\\"", with: "\\\\\\\"")
|
|
215
|
+
let script = "tell application \\"Terminal\\" to activate\\n" +
|
|
216
|
+
"tell application \\"Terminal\\" to do script \\"\(escaped)\\""
|
|
217
|
+
runDetached("/usr/bin/osascript", ["-e", script])
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
221
|
+
var statusItem: NSStatusItem!
|
|
222
|
+
|
|
223
|
+
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
224
|
+
NSApp.setActivationPolicy(.accessory)
|
|
225
|
+
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
|
226
|
+
if let button = statusItem.button {
|
|
227
|
+
button.title = "SKS"
|
|
228
|
+
button.toolTip = "Sneakoscope Codex settings"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let menu = NSMenu()
|
|
232
|
+
add(menu, "Use codex-lb", #selector(useCodexLb))
|
|
233
|
+
add(menu, "Use ChatGPT OAuth", #selector(useChatGptOAuth))
|
|
234
|
+
menu.addItem(NSMenuItem.separator())
|
|
235
|
+
add(menu, "Set OpenRouter Key and GLM Profiles", #selector(setOpenRouterKey))
|
|
236
|
+
add(menu, "Fast Check", #selector(fastCheck))
|
|
237
|
+
add(menu, "SKS Version Check", #selector(sksVersionCheck))
|
|
238
|
+
add(menu, "Update SKS Now", #selector(updateSksNow))
|
|
239
|
+
menu.addItem(NSMenuItem.separator())
|
|
240
|
+
add(menu, "Open Codex Settings", #selector(openCodexSettings))
|
|
241
|
+
add(menu, "Restart Codex", #selector(restartCodex))
|
|
242
|
+
menu.addItem(NSMenuItem.separator())
|
|
243
|
+
add(menu, "Quit SKS Menu", #selector(quit))
|
|
244
|
+
statusItem.menu = menu
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
func add(_ menu: NSMenu, _ title: String, _ selector: Selector) {
|
|
248
|
+
let item = NSMenuItem(title: title, action: selector, keyEquivalent: "")
|
|
249
|
+
item.target = self
|
|
250
|
+
menu.addItem(item)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func runSks(_ args: [String], tail: String = "echo; echo 'SKS command finished. Close this window when ready.'") {
|
|
254
|
+
let quoted = args.map(shellQuote).joined(separator: " ")
|
|
255
|
+
runInTerminal("\\(shellQuote(actionScript)) \\(quoted); \\(tail)")
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@objc func useCodexLb() {
|
|
259
|
+
runSks(["codex-lb", "use-codex-lb"])
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@objc func useChatGptOAuth() {
|
|
263
|
+
runSks(["codex-lb", "use-oauth"])
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@objc func setOpenRouterKey() {
|
|
267
|
+
let command = "printf 'Paste OpenRouter key, then press Return: '; read -r key; printf '%s\\\\n' \\"$key\\" | \\(shellQuote(actionScript)) codex-app set-openrouter-key --api-key-stdin; \\(shellQuote(actionScript)) codex-app glm-profile install; echo; echo 'OpenRouter/GLM update finished. Restart Codex if the model picker was already open.'"
|
|
268
|
+
runInTerminal(command)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@objc func fastCheck() {
|
|
272
|
+
runSks(["codex-lb", "fast-check"])
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@objc func sksVersionCheck() {
|
|
276
|
+
let sks = shellQuote(actionScript)
|
|
277
|
+
runInTerminal("echo 'SKS version'; \\(sks) --version; echo; echo 'Checking npm latest'; \\(sks) update check; echo; echo 'SKS version check finished. Close this window when ready.'")
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@objc func updateSksNow() {
|
|
281
|
+
runSks(["update"], tail: "echo; echo 'SKS update finished. Close this window when ready.'")
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@objc func openCodexSettings() {
|
|
285
|
+
runDetached("/usr/bin/open", ["codex://settings"])
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@objc func restartCodex() {
|
|
289
|
+
runInTerminal("/usr/bin/osascript -e 'tell application \\"Codex\\" to quit'; sleep 1; /usr/bin/open -a Codex; echo 'Codex restart requested.'")
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@objc func quit() {
|
|
293
|
+
NSApplication.shared.terminate(nil)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let app = NSApplication.shared
|
|
298
|
+
let delegate = AppDelegate()
|
|
299
|
+
app.delegate = delegate
|
|
300
|
+
app.run()
|
|
301
|
+
`;
|
|
302
|
+
}
|
|
303
|
+
function infoPlistSource() {
|
|
304
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
305
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
306
|
+
<plist version="1.0">
|
|
307
|
+
<dict>
|
|
308
|
+
<key>CFBundleExecutable</key>
|
|
309
|
+
<string>SKSMenuBar</string>
|
|
310
|
+
<key>CFBundleIdentifier</key>
|
|
311
|
+
<string>${LABEL}</string>
|
|
312
|
+
<key>CFBundleName</key>
|
|
313
|
+
<string>SKS Menu Bar</string>
|
|
314
|
+
<key>CFBundlePackageType</key>
|
|
315
|
+
<string>APPL</string>
|
|
316
|
+
<key>CFBundleVersion</key>
|
|
317
|
+
<string>1</string>
|
|
318
|
+
<key>LSUIElement</key>
|
|
319
|
+
<true/>
|
|
320
|
+
</dict>
|
|
321
|
+
</plist>
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
function launchAgentSource(executablePath, installDir) {
|
|
325
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
326
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
327
|
+
<plist version="1.0">
|
|
328
|
+
<dict>
|
|
329
|
+
<key>Label</key>
|
|
330
|
+
<string>${LABEL}</string>
|
|
331
|
+
<key>ProgramArguments</key>
|
|
332
|
+
<array>
|
|
333
|
+
<string>${escapeXml(executablePath)}</string>
|
|
334
|
+
</array>
|
|
335
|
+
<key>RunAtLoad</key>
|
|
336
|
+
<true/>
|
|
337
|
+
<key>KeepAlive</key>
|
|
338
|
+
<true/>
|
|
339
|
+
<key>StandardOutPath</key>
|
|
340
|
+
<string>${escapeXml(path.join(installDir, 'menubar.out.log'))}</string>
|
|
341
|
+
<key>StandardErrorPath</key>
|
|
342
|
+
<string>${escapeXml(path.join(installDir, 'menubar.err.log'))}</string>
|
|
343
|
+
</dict>
|
|
344
|
+
</plist>
|
|
345
|
+
`;
|
|
346
|
+
}
|
|
347
|
+
async function launchWithLaunchctl(input) {
|
|
348
|
+
const uid = typeof process.getuid === 'function' ? process.getuid() : null;
|
|
349
|
+
const domain = uid === null ? 'gui' : `gui/${uid}`;
|
|
350
|
+
await runProcess(input.launchctl, ['bootout', `${domain}/${LABEL}`], { timeoutMs: 5_000, maxOutputBytes: 16 * 1024 }).catch(() => undefined);
|
|
351
|
+
await runProcess(input.launchctl, ['bootout', domain, input.launchAgentPath], { timeoutMs: 5_000, maxOutputBytes: 16 * 1024 }).catch(() => undefined);
|
|
352
|
+
await terminateExistingMenuBarProcess(input.executablePath);
|
|
353
|
+
const bootstrap = await runProcess(input.launchctl, ['bootstrap', domain, input.launchAgentPath], {
|
|
354
|
+
timeoutMs: 10_000,
|
|
355
|
+
maxOutputBytes: 32 * 1024
|
|
356
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
357
|
+
if (bootstrap.code === 0) {
|
|
358
|
+
const kickstart = await runProcess(input.launchctl, ['kickstart', '-k', `${domain}/${LABEL}`], {
|
|
359
|
+
timeoutMs: 5_000,
|
|
360
|
+
maxOutputBytes: 32 * 1024
|
|
361
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
362
|
+
if (kickstart.code !== 0) {
|
|
363
|
+
const printed = await waitForLaunchctlRunning(input.launchctl, `${domain}/${LABEL}`);
|
|
364
|
+
if (printed.running) {
|
|
365
|
+
return {
|
|
366
|
+
requested: true,
|
|
367
|
+
method: 'launchctl',
|
|
368
|
+
ok: true,
|
|
369
|
+
bootstrap_code: bootstrap.code,
|
|
370
|
+
kickstart_code: kickstart.code,
|
|
371
|
+
print_code: printed.code,
|
|
372
|
+
error: null
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
requested: true,
|
|
378
|
+
method: 'launchctl',
|
|
379
|
+
ok: kickstart.code === 0,
|
|
380
|
+
bootstrap_code: bootstrap.code,
|
|
381
|
+
kickstart_code: kickstart.code,
|
|
382
|
+
error: kickstart.code === 0 ? null : String(kickstart.stderr || kickstart.stdout || '').trim() || 'launchctl_kickstart_failed'
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
if (input.open) {
|
|
386
|
+
const opened = await runProcess(input.open, [input.appPath], {
|
|
387
|
+
timeoutMs: 10_000,
|
|
388
|
+
maxOutputBytes: 32 * 1024
|
|
389
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
390
|
+
return {
|
|
391
|
+
requested: true,
|
|
392
|
+
method: 'open-fallback',
|
|
393
|
+
ok: opened.code === 0,
|
|
394
|
+
bootstrap_code: bootstrap.code,
|
|
395
|
+
open_code: opened.code,
|
|
396
|
+
error: opened.code === 0
|
|
397
|
+
? null
|
|
398
|
+
: String(opened.stderr || opened.stdout || bootstrap.stderr || bootstrap.stdout || '').trim() || 'sks_menubar_launch_failed'
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
requested: true,
|
|
403
|
+
method: 'launchctl',
|
|
404
|
+
ok: false,
|
|
405
|
+
bootstrap_code: bootstrap.code,
|
|
406
|
+
error: String(bootstrap.stderr || bootstrap.stdout || '').trim() || 'launchctl_bootstrap_failed'
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
async function waitForLaunchctlRunning(launchctl, service) {
|
|
410
|
+
let lastCode = null;
|
|
411
|
+
for (let attempt = 0; attempt < 30; attempt += 1) {
|
|
412
|
+
const printed = await runProcess(launchctl, ['print', service], {
|
|
413
|
+
timeoutMs: 2_000,
|
|
414
|
+
maxOutputBytes: 64 * 1024
|
|
415
|
+
}).catch(() => ({ code: 1, stdout: '', stderr: '' }));
|
|
416
|
+
lastCode = printed.code;
|
|
417
|
+
if (printed.code === 0 && /\bstate = running\b|\bpid = \d+\b/.test(`${printed.stdout || ''}\n${printed.stderr || ''}`)) {
|
|
418
|
+
return { code: printed.code, running: true };
|
|
419
|
+
}
|
|
420
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
421
|
+
}
|
|
422
|
+
return { code: lastCode, running: false };
|
|
423
|
+
}
|
|
424
|
+
async function terminateExistingMenuBarProcess(executablePath) {
|
|
425
|
+
const pkill = await which('pkill').catch(() => null) || await fallbackTool('/usr/bin/pkill');
|
|
426
|
+
if (!pkill)
|
|
427
|
+
return;
|
|
428
|
+
await runProcess(pkill, ['-f', executablePath], { timeoutMs: 5_000, maxOutputBytes: 8 * 1024 }).catch(() => undefined);
|
|
429
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
430
|
+
}
|
|
431
|
+
function swiftString(value) {
|
|
432
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
433
|
+
}
|
|
434
|
+
function shellQuote(value) {
|
|
435
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
436
|
+
}
|
|
437
|
+
function escapeXml(value) {
|
|
438
|
+
return value
|
|
439
|
+
.replace(/&/g, '&')
|
|
440
|
+
.replace(/</g, '<')
|
|
441
|
+
.replace(/>/g, '>')
|
|
442
|
+
.replace(/"/g, '"')
|
|
443
|
+
.replace(/'/g, ''');
|
|
444
|
+
}
|
|
445
|
+
//# sourceMappingURL=sks-menubar.js.map
|