@oh-my-pi/pi-coding-agent 16.1.18 → 16.1.20
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 +24 -0
- package/dist/cli.js +15452 -15142
- package/dist/types/cli/gallery-cli.d.ts +6 -0
- package/dist/types/commands/gallery.d.ts +1 -1
- package/dist/types/config/service-tier.d.ts +34 -0
- package/dist/types/config/settings-schema.d.ts +36 -33
- package/dist/types/edit/hashline/filesystem.d.ts +1 -18
- package/dist/types/extensibility/plugins/legacy-pi-bundled-keys.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +4 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +12 -0
- package/dist/types/internal-urls/skill-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +3 -0
- package/dist/types/modes/components/custom-editor.d.ts +0 -2
- package/dist/types/modes/controllers/input-controller.d.ts +0 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/task/executor.d.ts +9 -1
- package/dist/types/task/parallel.d.ts +17 -1
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/path-utils.d.ts +3 -0
- package/package.json +13 -13
- package/scripts/build-binary.ts +20 -0
- package/scripts/generate-legacy-pi-bundled-registry.ts +404 -0
- package/src/cli/gallery-cli.ts +31 -2
- package/src/cli/gallery-fixtures/agentic.ts +13 -4
- package/src/commands/gallery.ts +11 -3
- package/src/config/service-tier.ts +87 -0
- package/src/config/settings-schema.ts +48 -23
- package/src/edit/hashline/filesystem.ts +14 -0
- package/src/eval/agent-bridge.ts +2 -0
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +987 -0
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +3330 -18
- package/src/extensibility/plugins/legacy-pi-compat.ts +29 -14
- package/src/internal-urls/local-protocol.ts +116 -9
- package/src/internal-urls/skill-protocol.ts +3 -3
- package/src/internal-urls/types.ts +3 -0
- package/src/main.ts +1 -0
- package/src/mcp/transports/stdio.ts +5 -0
- package/src/modes/components/custom-editor.test.ts +7 -5
- package/src/modes/components/custom-editor.ts +6 -16
- package/src/modes/controllers/input-controller.ts +143 -137
- package/src/sdk.ts +1 -0
- package/src/session/agent-session.ts +72 -15
- package/src/session/messages.ts +70 -47
- package/src/session/session-history-format.ts +21 -4
- package/src/task/executor.ts +79 -2
- package/src/task/index.ts +11 -6
- package/src/task/parallel.ts +59 -7
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/find.ts +1 -0
- package/src/tools/index.ts +3 -1
- package/src/tools/irc.ts +16 -2
- package/src/tools/path-utils.ts +4 -0
- package/src/tools/read.ts +43 -17
- package/src/tools/search.ts +4 -0
- package/src/utils/shell-snapshot-fn-env.sh +60 -0
- package/src/utils/shell-snapshot.ts +77 -20
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Helpers inlined into `generateSnapshotScript` (shell-snapshot.ts).
|
|
2
|
+
#
|
|
3
|
+
# Activation idioms like `mise activate` install a shell function whose body
|
|
4
|
+
# expands a sidecar env var (e.g. `mise() { command "$__MISE_EXE" "$@"; }`).
|
|
5
|
+
# The function survives `declare -f`/`typeset -f` capture, but the helper var
|
|
6
|
+
# is set on the rc-sourced shell and lost when only PATH gets re-exported.
|
|
7
|
+
# The replay shell then calls `command "" …` and dies with
|
|
8
|
+
# `command: command not found:` (issue #3470).
|
|
9
|
+
#
|
|
10
|
+
# `__omp_emit_referenced_exports` reads captured function bodies from stdin,
|
|
11
|
+
# extracts every `$VAR` / `${VAR…}` reference, and emits a single
|
|
12
|
+
# `export VAR='value'` line to stdout for each referenced var that
|
|
13
|
+
# (a) is currently set in this shell and (b) is not a shell-internal name.
|
|
14
|
+
# The script bodies are scanned, not interpreted — over-inclusion (refs inside
|
|
15
|
+
# comments / heredocs) is harmless because we only emit names that are set.
|
|
16
|
+
#
|
|
17
|
+
# Pure POSIX so the same helper works under bash and zsh.
|
|
18
|
+
|
|
19
|
+
# Wrap $1 in single quotes, escaping every embedded `'` as `'\''`.
|
|
20
|
+
__omp_sq_quote() {
|
|
21
|
+
__omp_qbuf=$1
|
|
22
|
+
__omp_qout=
|
|
23
|
+
__omp_sq=\'
|
|
24
|
+
while case "$__omp_qbuf" in *$__omp_sq*) true ;; *) false ;; esac; do
|
|
25
|
+
__omp_qout=$__omp_qout${__omp_qbuf%%$__omp_sq*}"'\\''"
|
|
26
|
+
__omp_qbuf=${__omp_qbuf#*$__omp_sq}
|
|
27
|
+
done
|
|
28
|
+
__omp_qout=$__omp_qout$__omp_qbuf
|
|
29
|
+
printf "'%s'" "$__omp_qout"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Emit `export NAME='value'` for $1 unless the name is a shell-internal we
|
|
33
|
+
# must never overwrite, a likely secret (token / key / password / credential
|
|
34
|
+
# patterns — kept conservative, since `__MISE_EXE` and `FOO_DIR` style helper
|
|
35
|
+
# vars never carry secrets), or the var is unset. POSIX `case` patterns are
|
|
36
|
+
# byte-exact so we list common uppercase variants; lowercase secret vars are
|
|
37
|
+
# rare and out of scope.
|
|
38
|
+
__omp_emit_export_for() {
|
|
39
|
+
case "$1" in
|
|
40
|
+
_|PATH|HOME|USER|LOGNAME|PWD|OLDPWD|SHELL|SHLVL|TERM|TERMINFO|TERMCAP|IFS|TMPDIR|TMOUT|LANG|RANDOM|LINENO|SECONDS|FUNCNAME|HISTFILE|HISTSIZE|HISTFILESIZE|HISTCMD|PS1|PS2|PS3|PS4|UID|EUID|GROUPS|HOSTNAME|HOSTTYPE|OSTYPE|MACHTYPE|PIPESTATUS|BASH|ZSH|argv|PROMPT|RPROMPT|RPS1|RPS2|status|pipestatus|COLUMNS|LINES|COLORTERM|FUNCNEST) return ;;
|
|
41
|
+
LC_*|BASH_*|ZSH_*) return ;;
|
|
42
|
+
# Common secret-name patterns — never materialise these into the
|
|
43
|
+
# snapshot file even though it's now created 0600 (defence in depth
|
|
44
|
+
# against the file ending up in a backup, tarball, or NFS share).
|
|
45
|
+
*TOKEN*|*SECRET*|*PASSWORD*|*PASSWD*|*API_KEY*|*PRIVATE_KEY*|*ACCESS_KEY*|*CREDENTIAL*|*SESSION_KEY*) return ;;
|
|
46
|
+
esac
|
|
47
|
+
eval "[ \"\${$1+x}\" = x ]" 2>/dev/null || return
|
|
48
|
+
eval "__omp_xv=\"\${$1}\"" 2>/dev/null || return
|
|
49
|
+
printf 'export %s=%s\n' "$1" "$(__omp_sq_quote "$__omp_xv")"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Read function bodies on stdin, emit dedup'd export lines on stdout.
|
|
53
|
+
__omp_emit_referenced_exports() {
|
|
54
|
+
grep -oE '\$\{?[A-Za-z_][A-Za-z0-9_]*' \
|
|
55
|
+
| sed -E 's/^\$\{?//' \
|
|
56
|
+
| sort -u \
|
|
57
|
+
| while IFS= read -r __omp_name; do
|
|
58
|
+
__omp_emit_export_for "$__omp_name"
|
|
59
|
+
done
|
|
60
|
+
}
|
|
@@ -9,6 +9,7 @@ import * as fs from "node:fs";
|
|
|
9
9
|
import * as os from "node:os";
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { logger, postmortem } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import fnEnvHelper from "./shell-snapshot-fn-env.sh" with { type: "text" };
|
|
12
13
|
|
|
13
14
|
const cachedSnapshotPaths = new Map<string, string>();
|
|
14
15
|
const SNAPSHOT_TIMEOUT_MS = 2_000;
|
|
@@ -83,9 +84,13 @@ function sanitizeSnapshotEnv(env: Record<string, string | undefined>): Record<st
|
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
86
|
* Get the user's shell config file path.
|
|
87
|
+
*
|
|
88
|
+
* Honours `env.HOME` when present so a caller can target a sandboxed/test
|
|
89
|
+
* home, falling back to `os.homedir()` (which Bun resolves once at startup
|
|
90
|
+
* and caches — `process.env.HOME` overrides don't affect it).
|
|
86
91
|
*/
|
|
87
|
-
function getShellConfigFile(shell: string): string {
|
|
88
|
-
const home = os.homedir();
|
|
92
|
+
function getShellConfigFile(shell: string, env: Record<string, string | undefined>): string {
|
|
93
|
+
const home = env.HOME || os.homedir();
|
|
89
94
|
if (shell.includes("zsh")) return path.join(home, ".zshrc");
|
|
90
95
|
if (shell.includes("bash")) return path.join(home, ".bashrc");
|
|
91
96
|
return path.join(home, ".profile");
|
|
@@ -105,26 +110,22 @@ function generateSnapshotScript(shell: string, snapshotPath: string, rcFile: str
|
|
|
105
110
|
// Escape the snapshot path for shell
|
|
106
111
|
const escapedPath = snapshotPath.replace(/'/g, "'\\''");
|
|
107
112
|
|
|
108
|
-
// Function extraction differs between bash and zsh
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
// Function extraction differs between bash and zsh. Each form prints function
|
|
114
|
+
// bodies on stdout so we can both persist them AND scan their bodies for
|
|
115
|
+
// referenced env vars (issue #3470).
|
|
116
|
+
const functionExtractor = isZsh
|
|
117
|
+
? `# Force autoload all functions first
|
|
113
118
|
typeset -f > /dev/null 2>&1
|
|
114
119
|
# Get user function names - filter system/private ones
|
|
115
120
|
typeset +f 2>/dev/null | grep -vE '^(_|__)' | grep -vE '${commonToolsRegex}' | while read func; do
|
|
116
|
-
typeset -f "$func"
|
|
117
|
-
done
|
|
118
|
-
|
|
119
|
-
: `
|
|
120
|
-
echo "# Functions" >> "$SNAPSHOT_FILE"
|
|
121
|
-
# Force autoload all functions first
|
|
121
|
+
typeset -f "$func" 2>/dev/null
|
|
122
|
+
done`
|
|
123
|
+
: `# Force autoload all functions first
|
|
122
124
|
declare -f > /dev/null 2>&1
|
|
123
125
|
# Get user function names - filter system/private ones
|
|
124
126
|
declare -F 2>/dev/null | cut -d' ' -f3 | grep -vE '^(_|__)' | grep -vE '${commonToolsRegex}' | while read func; do
|
|
125
|
-
declare -f "$func"
|
|
126
|
-
done
|
|
127
|
-
`;
|
|
127
|
+
declare -f "$func" 2>/dev/null
|
|
128
|
+
done`;
|
|
128
129
|
|
|
129
130
|
// Shell options extraction
|
|
130
131
|
const optionsScript = isZsh
|
|
@@ -142,16 +143,45 @@ echo "shopt -s expand_aliases" >> "$SNAPSHOT_FILE"
|
|
|
142
143
|
return `
|
|
143
144
|
SNAPSHOT_FILE='${escapedPath}'
|
|
144
145
|
|
|
146
|
+
# Snapshot may inline env-var values referenced by captured functions (#3470).
|
|
147
|
+
# Defence in depth: (a) JS caller pre-creates the file at 0600 so the shell's
|
|
148
|
+
# \`>|\`/\`>>\` redirections truncate/append without changing the inode mode,
|
|
149
|
+
# (b) we \`umask 077\` before AND after sourcing the rc so any other file the
|
|
150
|
+
# script creates is private even when the rc resets umask to 022, (c) JS
|
|
151
|
+
# caller chmods file + dir after the script exits.
|
|
152
|
+
umask 077
|
|
153
|
+
|
|
145
154
|
# Source user's rc file if it exists
|
|
146
155
|
${hasRcFile ? `source "${rcFile}" < /dev/null 2>/dev/null` : "# No user config file to source"}
|
|
147
156
|
|
|
157
|
+
# Re-tighten umask in case the rc reset it (common \`.bashrc\`/\`.zshrc\` set
|
|
158
|
+
# \`umask 022\` for the interactive shell).
|
|
159
|
+
umask 077
|
|
160
|
+
|
|
148
161
|
# Create/clear the snapshot file
|
|
149
162
|
echo "# Shell snapshot - generated by omp agent" >| "$SNAPSHOT_FILE"
|
|
150
163
|
|
|
151
164
|
# Unalias everything first to avoid conflicts when sourced
|
|
152
165
|
echo "unalias -a 2>/dev/null || true" >> "$SNAPSHOT_FILE"
|
|
153
166
|
|
|
154
|
-
|
|
167
|
+
# Capture function definitions into a variable so we can both persist them
|
|
168
|
+
# and scan their bodies for env-var references (issue #3470).
|
|
169
|
+
__omp_funcs=$(
|
|
170
|
+
${functionExtractor}
|
|
171
|
+
)
|
|
172
|
+
echo "# Functions" >> "$SNAPSHOT_FILE"
|
|
173
|
+
printf '%s\\n' "$__omp_funcs" >> "$SNAPSHOT_FILE"
|
|
174
|
+
|
|
175
|
+
# Re-export uppercase identifiers referenced by snapshotted functions whose
|
|
176
|
+
# value is set in the rc-sourced shell. Without this, activation idioms like
|
|
177
|
+
# mise's \`mise()\` -> \`command "$__MISE_EXE" "$@"\` blow up because the helper
|
|
178
|
+
# var is lost (issue #3470). Helper definitions are POSIX-shell so the same
|
|
179
|
+
# block works for both bash and zsh.
|
|
180
|
+
echo "# Captured function environment" >> "$SNAPSHOT_FILE"
|
|
181
|
+
${fnEnvHelper}
|
|
182
|
+
printf '%s\\n' "$__omp_funcs" | __omp_emit_referenced_exports >> "$SNAPSHOT_FILE"
|
|
183
|
+
unset -f __omp_sq_quote __omp_emit_export_for __omp_emit_referenced_exports 2>/dev/null
|
|
184
|
+
unset __omp_funcs __omp_qbuf __omp_qout __omp_sq __omp_xv __omp_name 2>/dev/null
|
|
155
185
|
|
|
156
186
|
${optionsScript}
|
|
157
187
|
|
|
@@ -198,16 +228,35 @@ export async function getOrCreateSnapshot(
|
|
|
198
228
|
return null;
|
|
199
229
|
}
|
|
200
230
|
|
|
201
|
-
const rcFile = getShellConfigFile(shell);
|
|
231
|
+
const rcFile = getShellConfigFile(shell, env);
|
|
202
232
|
|
|
203
|
-
// Create snapshot directory
|
|
233
|
+
// Create snapshot directory with owner-only perms — the script may inline
|
|
234
|
+
// env vars referenced by captured functions (#3470) and `os.tmpdir()` is
|
|
235
|
+
// shared on Linux. `mode: 0o700` applies to a fresh mkdir; an existing dir
|
|
236
|
+
// keeps its mode, so chmod it defensively. Ignore EPERM (dir owned by
|
|
237
|
+
// another user on a shared box).
|
|
204
238
|
const snapshotDir = path.join(os.tmpdir(), "omp-shell-snapshots");
|
|
205
|
-
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
239
|
+
fs.mkdirSync(snapshotDir, { recursive: true, mode: 0o700 });
|
|
240
|
+
try {
|
|
241
|
+
fs.chmodSync(snapshotDir, 0o700);
|
|
242
|
+
} catch {
|
|
243
|
+
// best-effort
|
|
244
|
+
}
|
|
206
245
|
|
|
207
246
|
// Generate unique snapshot path
|
|
208
247
|
const shellName = shell.includes("zsh") ? "zsh" : shell.includes("bash") ? "bash" : "sh";
|
|
209
248
|
const snapshotPath = path.join(snapshotDir, `snapshot-${shellName}-${crypto.randomUUID()}.sh`);
|
|
210
249
|
|
|
250
|
+
// Pre-create the snapshot file at 0600 so the shell's `>|` (truncate) and
|
|
251
|
+
// `>>` (append) redirections inside `generateSnapshotScript` operate on an
|
|
252
|
+
// existing inode and preserve the private mode, regardless of the umask
|
|
253
|
+
// state inside the spawned shell. Without this, a `.bashrc` that sets
|
|
254
|
+
// `umask 022` (the typical interactive default) before the script's first
|
|
255
|
+
// redirection would create the file world-readable; the JS-side post-spawn
|
|
256
|
+
// chmod would tighten it, but only after the shell finished writing every
|
|
257
|
+
// captured env value to disk.
|
|
258
|
+
fs.writeFileSync(snapshotPath, "", { mode: 0o600 });
|
|
259
|
+
|
|
211
260
|
// Generate and execute snapshot script
|
|
212
261
|
const script = generateSnapshotScript(shell, snapshotPath, rcFile);
|
|
213
262
|
|
|
@@ -230,6 +279,14 @@ export async function getOrCreateSnapshot(
|
|
|
230
279
|
|
|
231
280
|
await child.exited;
|
|
232
281
|
if (child.exitCode === 0 && fs.existsSync(snapshotPath)) {
|
|
282
|
+
// Defence-in-depth: the script's `umask 077` already locks the file at
|
|
283
|
+
// first write, but chmod again in case the umask didn't take (exotic
|
|
284
|
+
// shells) or a postmortem-restored file ended up looser.
|
|
285
|
+
try {
|
|
286
|
+
fs.chmodSync(snapshotPath, 0o600);
|
|
287
|
+
} catch {
|
|
288
|
+
// best-effort
|
|
289
|
+
}
|
|
233
290
|
scrubSnapshotInPlace(snapshotPath);
|
|
234
291
|
cachedSnapshotPaths.set(cacheKey, snapshotPath);
|
|
235
292
|
return snapshotPath;
|