@pugi/cli 0.1.0-alpha.10
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/LICENSE +21 -0
- package/README.md +172 -0
- package/bin/run.js +2 -0
- package/dist/commands/jobs.js +245 -0
- package/dist/core/agents/loader.js +104 -0
- package/dist/core/agents/registry.js +69 -0
- package/dist/core/auto-open-browser.js +128 -0
- package/dist/core/bash-classifier.js +1001 -0
- package/dist/core/clipboard.js +70 -0
- package/dist/core/context/builder.js +114 -0
- package/dist/core/context/compaction-events.js +99 -0
- package/dist/core/context/compaction.js +602 -0
- package/dist/core/context/invariants.js +250 -0
- package/dist/core/context/markdown-loader.js +270 -0
- package/dist/core/credentials.js +355 -0
- package/dist/core/engine/adapter-runner.js +8 -0
- package/dist/core/engine/anvil-client.js +156 -0
- package/dist/core/engine/compaction-hook.js +154 -0
- package/dist/core/engine/index.js +12 -0
- package/dist/core/engine/native-pugi.js +369 -0
- package/dist/core/engine/noop.js +27 -0
- package/dist/core/engine/prompts.js +118 -0
- package/dist/core/engine/tool-bridge.js +313 -0
- package/dist/core/file-cache.js +29 -0
- package/dist/core/hooks.js +415 -0
- package/dist/core/index-store.js +260 -0
- package/dist/core/jobs/registry.js +462 -0
- package/dist/core/mcp/client.js +316 -0
- package/dist/core/mcp/registry.js +171 -0
- package/dist/core/mcp/trust.js +91 -0
- package/dist/core/path-security.js +63 -0
- package/dist/core/permission.js +309 -0
- package/dist/core/repl/cap-warning.js +91 -0
- package/dist/core/repl/clipboard-read.js +174 -0
- package/dist/core/repl/history-search.js +175 -0
- package/dist/core/repl/history.js +172 -0
- package/dist/core/repl/kill-ring.js +138 -0
- package/dist/core/repl/session.js +618 -0
- package/dist/core/repl/slash-commands.js +227 -0
- package/dist/core/repl/workspace-context.js +113 -0
- package/dist/core/session.js +258 -0
- package/dist/core/settings.js +59 -0
- package/dist/core/skills/loader.js +454 -0
- package/dist/core/skills/sources.js +480 -0
- package/dist/core/skills/trust.js +172 -0
- package/dist/core/subagents/dispatcher.js +258 -0
- package/dist/core/subagents/index.js +26 -0
- package/dist/core/subagents/spawn.js +86 -0
- package/dist/core/trust.js +109 -0
- package/dist/index.js +8 -0
- package/dist/runtime/cli.js +3405 -0
- package/dist/runtime/commands/agents.js +385 -0
- package/dist/runtime/commands/budget.js +192 -0
- package/dist/runtime/commands/config.js +231 -0
- package/dist/runtime/commands/privacy.js +107 -0
- package/dist/runtime/commands/skills.js +401 -0
- package/dist/runtime/commands/undo.js +329 -0
- package/dist/runtime/update-check.js +294 -0
- package/dist/tools/bash.js +660 -0
- package/dist/tools/file-tools.js +346 -0
- package/dist/tools/registry.js +25 -0
- package/dist/tools/web-fetch.js +535 -0
- package/dist/tui/agent-tree.js +66 -0
- package/dist/tui/conversation-pane.js +45 -0
- package/dist/tui/device-flow.js +142 -0
- package/dist/tui/input-box.js +474 -0
- package/dist/tui/login-picker.js +69 -0
- package/dist/tui/render.js +125 -0
- package/dist/tui/repl-render.js +240 -0
- package/dist/tui/repl-splash-art.js +64 -0
- package/dist/tui/repl-splash.js +111 -0
- package/dist/tui/repl.js +214 -0
- package/dist/tui/slash-palette.js +106 -0
- package/dist/tui/splash-data.js +61 -0
- package/dist/tui/splash.js +31 -0
- package/dist/tui/status-bar.js +71 -0
- package/dist/tui/update-banner.js +8 -0
- package/dist/tui/workspace-context.js +105 -0
- package/package.json +71 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Open `url` in the default browser. Returns `{ opened: true }` on a
|
|
4
|
+
* successful spawn, `{ opened: false }` on any failure. The url is not
|
|
5
|
+
* shell-escaped here; we always pass it as a single argv element to a
|
|
6
|
+
* direct spawn (no shell), so quoting is irrelevant.
|
|
7
|
+
*/
|
|
8
|
+
export async function autoOpenBrowser(url, deps = {}) {
|
|
9
|
+
const platform = deps.platform ?? process.platform;
|
|
10
|
+
const spawnDetached = deps.spawnDetached ?? defaultSpawnDetached;
|
|
11
|
+
// Refuse anything that does not parse as http(s). The device-flow URL
|
|
12
|
+
// is always https — guarding here keeps a hostile server response
|
|
13
|
+
// from convincing us to spawn `open file:///etc/passwd` or worse.
|
|
14
|
+
if (!isSafeHttpUrl(url)) {
|
|
15
|
+
return { opened: false };
|
|
16
|
+
}
|
|
17
|
+
if (platform === 'darwin') {
|
|
18
|
+
return { opened: spawnDetached('open', [url]) };
|
|
19
|
+
}
|
|
20
|
+
if (platform === 'win32') {
|
|
21
|
+
// P1-3 (triple-review 2026-05-24): cmd.exe parses `&` as a command
|
|
22
|
+
// separator BEFORE Node hands argv to the child, regardless of
|
|
23
|
+
// `shell: false`. A device-flow URL like
|
|
24
|
+
// `https://app.pugi.io/devices/authorize?user_code=ABC&trace=xyz`
|
|
25
|
+
// would be truncated at the `&`, opening only the first half. We
|
|
26
|
+
// prefer PowerShell (its argv parser is sane: a single quoted URL
|
|
27
|
+
// round-trips verbatim) and fall back to a double-quoted cmd
|
|
28
|
+
// invocation when PowerShell is missing from PATH.
|
|
29
|
+
if (spawnDetached('powershell', [
|
|
30
|
+
'-NoProfile',
|
|
31
|
+
'-NonInteractive',
|
|
32
|
+
'-Command',
|
|
33
|
+
'Start-Process',
|
|
34
|
+
quoteForPowerShell(url),
|
|
35
|
+
])) {
|
|
36
|
+
return { opened: true };
|
|
37
|
+
}
|
|
38
|
+
// Fallback: `cmd /c start "" "<url>"`. The URL itself is wrapped
|
|
39
|
+
// in double quotes so cmd does not split on `&`; any embedded `"`
|
|
40
|
+
// in the URL is escaped using cmd's caret-quote convention.
|
|
41
|
+
return {
|
|
42
|
+
opened: spawnDetached('cmd', ['/c', 'start', '""', quoteForCmd(url)]),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (platform === 'linux' || platform === 'freebsd' || platform === 'openbsd') {
|
|
46
|
+
// Try xdg-open first (the freedesktop standard), then gio (GNOME),
|
|
47
|
+
// then a couple of well-known browsers as a last resort. Each
|
|
48
|
+
// attempt is a fresh spawn — if the binary is missing the helper
|
|
49
|
+
// returns false and we fall through.
|
|
50
|
+
for (const cmd of LINUX_OPENERS) {
|
|
51
|
+
if (spawnDetached(cmd, [url]))
|
|
52
|
+
return { opened: true };
|
|
53
|
+
}
|
|
54
|
+
return { opened: false };
|
|
55
|
+
}
|
|
56
|
+
// Unknown platform (android, aix, sunos…) — degrade gracefully.
|
|
57
|
+
return { opened: false };
|
|
58
|
+
}
|
|
59
|
+
const LINUX_OPENERS = ['xdg-open', 'gio', 'gnome-open', 'kde-open'];
|
|
60
|
+
function isSafeHttpUrl(candidate) {
|
|
61
|
+
try {
|
|
62
|
+
const url = new URL(candidate);
|
|
63
|
+
return url.protocol === 'https:' || url.protocol === 'http:';
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* P1-3 (triple-review 2026-05-24): wrap a URL for PowerShell's
|
|
71
|
+
* `Start-Process` invocation. PowerShell's single-quote string literal
|
|
72
|
+
* does NOT process escape sequences; the only metachar inside is the
|
|
73
|
+
* single quote itself (escaped by doubling). URLs do not contain
|
|
74
|
+
* single quotes in practice, but the helper handles them defensively
|
|
75
|
+
* so future input shapes do not break.
|
|
76
|
+
*/
|
|
77
|
+
function quoteForPowerShell(url) {
|
|
78
|
+
return `'${url.replace(/'/g, "''")}'`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* P1-3 (triple-review 2026-05-24): wrap a URL for cmd.exe's
|
|
82
|
+
* `start ""` invocation. Double quotes pin the URL as a single token
|
|
83
|
+
* so cmd does NOT split on `&`, `|`, `^`, `<`, `>`. Embedded `"` (rare
|
|
84
|
+
* in URLs but defensible) is escaped via cmd's caret-quote convention:
|
|
85
|
+
* `^"`.
|
|
86
|
+
*/
|
|
87
|
+
function quoteForCmd(url) {
|
|
88
|
+
return `"${url.replace(/"/g, '^"')}"`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Real spawn implementation. Detaches the child so the Pugi CLI can
|
|
92
|
+
* exit (or keep polling) without inheriting the browser's lifecycle.
|
|
93
|
+
* Returns `true` only when the spawn produced a pid; any error event
|
|
94
|
+
* (ENOENT for a missing binary, EACCES for a sandboxed permission
|
|
95
|
+
* denial) flips the result to `false`.
|
|
96
|
+
*/
|
|
97
|
+
function defaultSpawnDetached(cmd, args) {
|
|
98
|
+
try {
|
|
99
|
+
const options = {
|
|
100
|
+
detached: true,
|
|
101
|
+
stdio: 'ignore',
|
|
102
|
+
shell: false,
|
|
103
|
+
};
|
|
104
|
+
const child = spawn(cmd, args.slice(), options);
|
|
105
|
+
// P3 polish (triple-review 2026-05-24): swallow the async `error`
|
|
106
|
+
// event (ENOENT for a missing binary, EACCES for a sandboxed
|
|
107
|
+
// permission denial). The old `errored` flag was always false at
|
|
108
|
+
// the synchronous `return !errored` point (the `error` event is
|
|
109
|
+
// delivered later in the next tick), so the flag was dead. The
|
|
110
|
+
// listener is still required: without it, Node escalates the
|
|
111
|
+
// event to an unhandled-error and crashes the host process when
|
|
112
|
+
// the binary is missing.
|
|
113
|
+
child.on('error', () => undefined);
|
|
114
|
+
if (typeof child.pid !== 'number')
|
|
115
|
+
return false;
|
|
116
|
+
// unref so the parent event loop is not held open by the browser
|
|
117
|
+
// handle. The browser process continues independently.
|
|
118
|
+
child.unref();
|
|
119
|
+
// Callers treat `true` as best-effort: the fallback "Browser
|
|
120
|
+
// didn't open?" hint is always rendered, so a silent spawn
|
|
121
|
+
// failure still leaves the user a usable path.
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=auto-open-browser.js.map
|