gsd-pi 2.10.2 → 2.10.5
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 +2 -0
- package/dist/cli.js +7 -0
- package/dist/loader.js +1 -0
- package/dist/onboarding.js +104 -59
- package/dist/update-cmd.d.ts +1 -0
- package/dist/update-cmd.js +40 -0
- package/node_modules/@gsd/native/dist/hasher/index.d.ts +32 -0
- package/node_modules/@gsd/native/dist/hasher/index.js +37 -0
- package/node_modules/@gsd/native/dist/native.d.ts +4 -1
- package/node_modules/@gsd/native/dist/native.js +39 -9
- package/node_modules/@gsd/native/dist/xxhash/index.d.ts +14 -0
- package/node_modules/@gsd/native/dist/xxhash/index.js +17 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/agent-session.d.ts +6 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/agent-session.js +58 -9
- package/node_modules/@gsd/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.d.ts +72 -12
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.js +254 -43
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.test.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.test.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.test.js +159 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/model-registry.d.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/model-registry.js +6 -4
- package/node_modules/@gsd/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/settings-manager.d.ts +15 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/settings-manager.js +12 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.d.ts +40 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.js +92 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.test.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.test.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.test.js +156 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash-interceptor.test.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.d.ts +8 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js +18 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/index.js +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/index.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/src/core/agent-session.ts +65 -9
- package/node_modules/@gsd/pi-coding-agent/src/core/auth-storage.test.ts +194 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/auth-storage.ts +283 -53
- package/node_modules/@gsd/pi-coding-agent/src/core/model-registry.ts +6 -4
- package/node_modules/@gsd/pi-coding-agent/src/core/settings-manager.ts +29 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/bash-interceptor.test.ts +198 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/bash-interceptor.ts +115 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/bash.ts +29 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/index.ts +8 -0
- package/node_modules/@gsd/pi-coding-agent/src/index.ts +6 -0
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +1 -1
- package/package.json +8 -2
- package/packages/native/dist/hasher/index.d.ts +32 -0
- package/packages/native/dist/hasher/index.js +37 -0
- package/packages/native/dist/native.d.ts +4 -1
- package/packages/native/dist/native.js +39 -9
- package/packages/native/dist/xxhash/index.d.ts +14 -0
- package/packages/native/dist/xxhash/index.js +17 -0
- package/packages/native/src/native.ts +39 -9
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +58 -9
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +72 -12
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +254 -43
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +159 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +4 -2
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +6 -4
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +12 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.d.ts +40 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js +92 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js +156 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +18 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +2 -2
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +65 -9
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +194 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +283 -53
- package/packages/pi-coding-agent/src/core/model-registry.ts +6 -4
- package/packages/pi-coding-agent/src/core/settings-manager.ts +29 -0
- package/packages/pi-coding-agent/src/core/tools/bash-interceptor.test.ts +198 -0
- package/packages/pi-coding-agent/src/core/tools/bash-interceptor.ts +115 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +29 -0
- package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
- package/packages/pi-coding-agent/src/index.ts +6 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +211 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +101 -0
- package/src/resources/extensions/async-jobs/cancel-job-tool.ts +34 -0
- package/src/resources/extensions/async-jobs/index.ts +133 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +250 -0
- package/src/resources/extensions/gsd/git-service.ts +13 -3
- package/src/resources/extensions/gsd/prompts/system.md +5 -2
- package/src/resources/extensions/gsd/tests/git-service.test.ts +36 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
checkBashInterception,
|
|
5
|
+
compileInterceptor,
|
|
6
|
+
DEFAULT_BASH_INTERCEPTOR_RULES,
|
|
7
|
+
type BashInterceptorRule,
|
|
8
|
+
} from "./bash-interceptor.js";
|
|
9
|
+
|
|
10
|
+
const ALL_TOOLS = ["read", "grep", "find", "edit", "write"];
|
|
11
|
+
const NO_TOOLS: string[] = [];
|
|
12
|
+
|
|
13
|
+
describe("checkBashInterception", () => {
|
|
14
|
+
describe("read rule (cat/head/tail/less/more)", () => {
|
|
15
|
+
it("blocks cat with a file argument", () => {
|
|
16
|
+
const r = checkBashInterception("cat README.md", ALL_TOOLS);
|
|
17
|
+
assert.equal(r.block, true);
|
|
18
|
+
assert.equal(r.suggestedTool, "read");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("blocks head and tail", () => {
|
|
22
|
+
assert.equal(checkBashInterception("head -n 20 file.ts", ALL_TOOLS).block, true);
|
|
23
|
+
assert.equal(checkBashInterception("tail -f app.log", ALL_TOOLS).block, true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("does NOT block cat used as heredoc (cat <<EOF)", () => {
|
|
27
|
+
const r = checkBashInterception("cat <<EOF > file.txt", ALL_TOOLS);
|
|
28
|
+
assert.notEqual(r.suggestedTool, "read");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("does NOT block when read tool is absent", () => {
|
|
32
|
+
assert.equal(checkBashInterception("cat README.md", NO_TOOLS).block, false);
|
|
33
|
+
assert.equal(checkBashInterception("cat README.md", ["grep"]).block, false);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("grep rule", () => {
|
|
38
|
+
it("blocks grep and rg", () => {
|
|
39
|
+
assert.equal(checkBashInterception("grep foo bar.ts", ALL_TOOLS).block, true);
|
|
40
|
+
assert.equal(checkBashInterception("rg -r pattern .", ALL_TOOLS).block, true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("blocks grep with leading whitespace", () => {
|
|
44
|
+
assert.equal(checkBashInterception(" grep -r foo .", ALL_TOOLS).block, true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("does NOT block when grep tool is absent", () => {
|
|
48
|
+
assert.equal(checkBashInterception("grep foo bar", ["read", "edit"]).block, false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("find rule", () => {
|
|
53
|
+
it("blocks find with -name flag", () => {
|
|
54
|
+
assert.equal(checkBashInterception('find . -name "*.ts"', ALL_TOOLS).block, true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("blocks find with -type flag", () => {
|
|
58
|
+
assert.equal(checkBashInterception("find /tmp -maxdepth 1 -type f", ALL_TOOLS).block, true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("does NOT block find without name/type flags", () => {
|
|
62
|
+
assert.equal(checkBashInterception("find /tmp -maxdepth 1", ALL_TOOLS).block, false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("does NOT block when find tool is absent", () => {
|
|
66
|
+
assert.equal(checkBashInterception('find . -name "*.ts"', ["read", "grep"]).block, false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("edit rule (sed/perl/awk)", () => {
|
|
71
|
+
it("blocks sed -i", () => {
|
|
72
|
+
assert.equal(checkBashInterception("sed -i 's/foo/bar/' file.ts", ALL_TOOLS).block, true);
|
|
73
|
+
assert.equal(checkBashInterception("sed --in-place 's/x/y/' f", ALL_TOOLS).block, true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("does NOT block sed without -i (read-only)", () => {
|
|
77
|
+
assert.equal(checkBashInterception("sed 's/foo/bar/' file.ts", ALL_TOOLS).block, false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("blocks perl -pi and perl -p -i", () => {
|
|
81
|
+
assert.equal(checkBashInterception("perl -pi -e 's/foo/bar/' file", ALL_TOOLS).block, true);
|
|
82
|
+
assert.equal(checkBashInterception("perl -p -i -e 's/x/y/' f", ALL_TOOLS).block, true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("blocks awk -i inplace", () => {
|
|
86
|
+
assert.equal(checkBashInterception("awk -i inplace '{print}' file", ALL_TOOLS).block, true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("does NOT block when edit tool is absent", () => {
|
|
90
|
+
assert.equal(checkBashInterception("sed -i 's/a/b/' f", ["read", "grep"]).block, false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("write rule (echo/printf/heredoc redirect)", () => {
|
|
95
|
+
it("blocks echo with > redirect", () => {
|
|
96
|
+
assert.equal(checkBashInterception("echo hello > file.txt", ALL_TOOLS).block, true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("blocks printf with > redirect", () => {
|
|
100
|
+
assert.equal(checkBashInterception('printf "%s" content > out.txt', ALL_TOOLS).block, true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("does NOT block echo without redirect", () => {
|
|
104
|
+
assert.equal(checkBashInterception("echo hello", ALL_TOOLS).block, false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("does NOT block >> append redirect (write tool does not support appending)", () => {
|
|
108
|
+
assert.equal(checkBashInterception("echo hello >> file.txt", ALL_TOOLS).block, false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("does NOT block stderr redirect (2>)", () => {
|
|
112
|
+
assert.equal(checkBashInterception("echo test 2> /dev/null", ALL_TOOLS).block, false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("does NOT block pipe (echo foo | grep bar)", () => {
|
|
116
|
+
assert.equal(checkBashInterception("echo foo | grep bar", ALL_TOOLS).block, false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("does NOT block when write tool is absent", () => {
|
|
120
|
+
assert.equal(checkBashInterception("echo hello > file.txt", ["read", "grep"]).block, false);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("pass-through commands", () => {
|
|
125
|
+
it("passes npm install", () => {
|
|
126
|
+
assert.equal(checkBashInterception("npm install", ALL_TOOLS).block, false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("passes ls > output.txt (not an echo/printf/cat)", () => {
|
|
130
|
+
assert.equal(checkBashInterception("ls > output.txt", ALL_TOOLS).block, false);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("passes tee file.txt", () => {
|
|
134
|
+
assert.equal(checkBashInterception("tee file.txt", ALL_TOOLS).block, false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("passes git log", () => {
|
|
138
|
+
assert.equal(checkBashInterception("git log --oneline", ALL_TOOLS).block, false);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("block message content", () => {
|
|
143
|
+
it("includes the original command in the block message", () => {
|
|
144
|
+
const r = checkBashInterception("cat README.md", ALL_TOOLS);
|
|
145
|
+
assert.ok(r.message?.includes("cat README.md"), "message should contain original command");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("returns block:false with no message when not blocked", () => {
|
|
149
|
+
const r = checkBashInterception("npm install", ALL_TOOLS);
|
|
150
|
+
assert.equal(r.block, false);
|
|
151
|
+
assert.equal(r.message, undefined);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("compileInterceptor", () => {
|
|
157
|
+
it("produces same results as checkBashInterception", () => {
|
|
158
|
+
const interceptor = compileInterceptor(DEFAULT_BASH_INTERCEPTOR_RULES);
|
|
159
|
+
const cases: [string, string[], boolean][] = [
|
|
160
|
+
["cat README.md", ALL_TOOLS, true],
|
|
161
|
+
["npm install", ALL_TOOLS, false],
|
|
162
|
+
["grep foo bar", ALL_TOOLS, true],
|
|
163
|
+
["echo hello >> file", ALL_TOOLS, false],
|
|
164
|
+
["echo test 2> /dev/null", ALL_TOOLS, false],
|
|
165
|
+
];
|
|
166
|
+
for (const [cmd, tools, expected] of cases) {
|
|
167
|
+
assert.equal(
|
|
168
|
+
interceptor.check(cmd, tools).block,
|
|
169
|
+
expected,
|
|
170
|
+
`pre-compiled: "${cmd}" expected block=${expected}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("silently skips rules with invalid regex patterns", () => {
|
|
176
|
+
const rules: BashInterceptorRule[] = [
|
|
177
|
+
{ pattern: "[invalid(", tool: "read", message: "broken" },
|
|
178
|
+
{ pattern: "^\\s*cat\\s+", tool: "read", message: "valid" },
|
|
179
|
+
];
|
|
180
|
+
const interceptor = compileInterceptor(rules);
|
|
181
|
+
assert.equal(interceptor.check("cat file.txt", ["read"]).block, true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("returns block:false when available tools list is empty", () => {
|
|
185
|
+
const interceptor = compileInterceptor(DEFAULT_BASH_INTERCEPTOR_RULES);
|
|
186
|
+
assert.equal(interceptor.check("cat README.md", []).block, false);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("allows custom rule override", () => {
|
|
190
|
+
const customRules: BashInterceptorRule[] = [
|
|
191
|
+
{ pattern: "^\\s*curl\\s+", tool: "fetch", message: "Use fetch tool instead." },
|
|
192
|
+
];
|
|
193
|
+
const interceptor = compileInterceptor(customRules);
|
|
194
|
+
assert.equal(interceptor.check("curl https://example.com", ["fetch"]).block, true);
|
|
195
|
+
// default rules not active
|
|
196
|
+
assert.equal(interceptor.check("cat file.txt", ["read"]).block, false);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command interceptor — blocks shell commands that duplicate dedicated tools.
|
|
3
|
+
*
|
|
4
|
+
* Each rule defines a regex pattern, a suggested replacement tool, and a message.
|
|
5
|
+
* A command is only blocked when the suggested tool exists in the session's active tool list.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface BashInterceptorRule {
|
|
9
|
+
pattern: string;
|
|
10
|
+
flags?: string;
|
|
11
|
+
tool: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
|
|
16
|
+
{
|
|
17
|
+
// cat/head/tail for file viewing — excludes heredoc syntax (cat <<)
|
|
18
|
+
pattern: "^\\s*(cat(?!\\s*<<)|head|tail|less|more)\\s+",
|
|
19
|
+
tool: "read",
|
|
20
|
+
message: "Use the read tool to view file contents instead of shell commands.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+",
|
|
24
|
+
tool: "grep",
|
|
25
|
+
message: "Use the grep tool for searching file contents instead of shell commands.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)",
|
|
29
|
+
tool: "find",
|
|
30
|
+
message: "Use the find tool for locating files by name/type instead of shell commands.",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
pattern: "^\\s*sed\\s+(-i|--in-place)",
|
|
34
|
+
tool: "edit",
|
|
35
|
+
message: "Use the edit tool for in-place file modifications instead of sed.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: "^\\s*perl\\s+.*-[pn]?i",
|
|
39
|
+
tool: "edit",
|
|
40
|
+
message: "Use the edit tool for in-place file modifications instead of perl.",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pattern: "^\\s*awk\\s+.*-i\\s+inplace",
|
|
44
|
+
tool: "edit",
|
|
45
|
+
message: "Use the edit tool for in-place file modifications instead of awk.",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// echo/printf/heredoc writing to a file via > (not >> append, not 2> stderr redirect)
|
|
49
|
+
// Matches a single > not preceded by |, >, or a digit (fd redirect like 2>)
|
|
50
|
+
pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+.*(?<![|>\\d])>(?!>)\\s*\\S",
|
|
51
|
+
tool: "write",
|
|
52
|
+
message: "Use the write tool to create/overwrite files instead of shell redirects.",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export interface InterceptionResult {
|
|
57
|
+
block: boolean;
|
|
58
|
+
message?: string;
|
|
59
|
+
suggestedTool?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CompiledInterceptor {
|
|
63
|
+
check: (command: string, availableTools: string[]) => InterceptionResult;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Compile rules into an interceptor with pre-built regex objects.
|
|
68
|
+
* Silently skips rules with invalid patterns.
|
|
69
|
+
*
|
|
70
|
+
* Pre-compiling at construction time avoids repeated `new RegExp()` calls
|
|
71
|
+
* on every bash command invocation.
|
|
72
|
+
*/
|
|
73
|
+
export function compileInterceptor(rules: BashInterceptorRule[]): CompiledInterceptor {
|
|
74
|
+
const compiled = rules.flatMap((rule) => {
|
|
75
|
+
try {
|
|
76
|
+
return [{ regex: new RegExp(rule.pattern, rule.flags), rule }];
|
|
77
|
+
} catch {
|
|
78
|
+
return []; // skip invalid regex
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
check(command: string, availableTools: string[]): InterceptionResult {
|
|
84
|
+
const trimmed = command.trim();
|
|
85
|
+
for (const { regex, rule } of compiled) {
|
|
86
|
+
if (regex.test(trimmed) && availableTools.includes(rule.tool)) {
|
|
87
|
+
return {
|
|
88
|
+
block: true,
|
|
89
|
+
message: `Blocked: ${rule.message}\n\nOriginal command: ${command}`,
|
|
90
|
+
suggestedTool: rule.tool,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { block: false };
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check whether a bash command should be intercepted.
|
|
101
|
+
*
|
|
102
|
+
* Compiles rules on each call — prefer `compileInterceptor()` for repeated use.
|
|
103
|
+
*
|
|
104
|
+
* @param command - The shell command to check
|
|
105
|
+
* @param availableTools - Tool names present in the current session
|
|
106
|
+
* @param rules - Override the default rule set (optional)
|
|
107
|
+
*/
|
|
108
|
+
export function checkBashInterception(
|
|
109
|
+
command: string,
|
|
110
|
+
availableTools: string[],
|
|
111
|
+
rules?: BashInterceptorRule[],
|
|
112
|
+
): InterceptionResult {
|
|
113
|
+
const effectiveRules = rules ?? DEFAULT_BASH_INTERCEPTOR_RULES;
|
|
114
|
+
return compileInterceptor(effectiveRules).check(command, availableTools);
|
|
115
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { AgentTool } from "@gsd/pi-agent-core";
|
|
|
7
7
|
import { type Static, Type } from "@sinclair/typebox";
|
|
8
8
|
import { spawn } from "child_process";
|
|
9
9
|
import { getShellConfig, getShellEnv, killProcessTree, sanitizeCommand } from "../../utils/shell.js";
|
|
10
|
+
import { type BashInterceptorRule, compileInterceptor, DEFAULT_BASH_INTERCEPTOR_RULES } from "./bash-interceptor.js";
|
|
10
11
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from "./truncate.js";
|
|
11
12
|
import type { ArtifactManager } from "../artifact-manager.js";
|
|
12
13
|
|
|
@@ -191,6 +192,13 @@ export interface BashToolOptions {
|
|
|
191
192
|
spawnHook?: BashSpawnHook;
|
|
192
193
|
/** Session-scoped artifact storage. When provided, spills to artifact files instead of temp files. */
|
|
193
194
|
artifactManager?: ArtifactManager;
|
|
195
|
+
/** Bash interceptor configuration — blocks commands that duplicate dedicated tools */
|
|
196
|
+
interceptor?: {
|
|
197
|
+
enabled: boolean;
|
|
198
|
+
rules?: BashInterceptorRule[];
|
|
199
|
+
};
|
|
200
|
+
/** Tool names available in the session, used by the interceptor to check if replacement tools exist */
|
|
201
|
+
availableToolNames?: string[] | (() => string[]);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
export function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {
|
|
@@ -199,6 +207,12 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
|
|
|
199
207
|
const spawnHook = options?.spawnHook;
|
|
200
208
|
const artifactManager = options?.artifactManager;
|
|
201
209
|
|
|
210
|
+
// Pre-compile interceptor rules once at construction time
|
|
211
|
+
const interceptorInstance =
|
|
212
|
+
options?.interceptor?.enabled
|
|
213
|
+
? compileInterceptor(options.interceptor.rules ?? DEFAULT_BASH_INTERCEPTOR_RULES)
|
|
214
|
+
: null;
|
|
215
|
+
|
|
202
216
|
return {
|
|
203
217
|
name: "bash",
|
|
204
218
|
label: "bash",
|
|
@@ -210,6 +224,21 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
|
|
|
210
224
|
signal?: AbortSignal,
|
|
211
225
|
onUpdate?,
|
|
212
226
|
) => {
|
|
227
|
+
// Check bash interceptor — block commands that duplicate dedicated tools
|
|
228
|
+
if (interceptorInstance) {
|
|
229
|
+
const toolNames =
|
|
230
|
+
typeof options!.availableToolNames === "function"
|
|
231
|
+
? options!.availableToolNames()
|
|
232
|
+
: options!.availableToolNames ?? [];
|
|
233
|
+
const interception = interceptorInstance.check(command, toolNames);
|
|
234
|
+
if (interception.block) {
|
|
235
|
+
return {
|
|
236
|
+
content: [{ type: "text" as const, text: interception.message ?? "Command blocked by interceptor" }],
|
|
237
|
+
details: undefined,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
213
242
|
// Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
|
|
214
243
|
const resolvedCommand = sanitizeCommand(commandPrefix ? `${commandPrefix}\n${command}` : command);
|
|
215
244
|
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
|
@@ -8,6 +8,14 @@ export {
|
|
|
8
8
|
bashTool,
|
|
9
9
|
createBashTool,
|
|
10
10
|
} from "./bash.js";
|
|
11
|
+
export {
|
|
12
|
+
type BashInterceptorRule,
|
|
13
|
+
checkBashInterception,
|
|
14
|
+
type CompiledInterceptor,
|
|
15
|
+
compileInterceptor,
|
|
16
|
+
DEFAULT_BASH_INTERCEPTOR_RULES,
|
|
17
|
+
type InterceptionResult,
|
|
18
|
+
} from "./bash-interceptor.js";
|
|
11
19
|
export {
|
|
12
20
|
createEditTool,
|
|
13
21
|
type EditOperations,
|
|
@@ -202,6 +202,7 @@ export {
|
|
|
202
202
|
export { BlobStore, isBlobRef, parseBlobRef, externalizeImageData, resolveImageData } from "./core/blob-store.js";
|
|
203
203
|
export { ArtifactManager } from "./core/artifact-manager.js";
|
|
204
204
|
export {
|
|
205
|
+
type AsyncSettings,
|
|
205
206
|
type CompactionSettings,
|
|
206
207
|
type ImageSettings,
|
|
207
208
|
type PackageSource,
|
|
@@ -220,6 +221,7 @@ export {
|
|
|
220
221
|
} from "./core/skills.js";
|
|
221
222
|
// Tools
|
|
222
223
|
export {
|
|
224
|
+
type BashInterceptorRule,
|
|
223
225
|
type BashOperations,
|
|
224
226
|
type BashSpawnContext,
|
|
225
227
|
type BashSpawnHook,
|
|
@@ -227,6 +229,10 @@ export {
|
|
|
227
229
|
type BashToolInput,
|
|
228
230
|
type BashToolOptions,
|
|
229
231
|
bashTool,
|
|
232
|
+
checkBashInterception,
|
|
233
|
+
type CompiledInterceptor,
|
|
234
|
+
compileInterceptor,
|
|
235
|
+
DEFAULT_BASH_INTERCEPTOR_RULES,
|
|
230
236
|
codingTools,
|
|
231
237
|
DEFAULT_MAX_BYTES,
|
|
232
238
|
DEFAULT_MAX_LINES,
|
|
@@ -175,7 +175,7 @@ export class InteractiveMode {
|
|
|
175
175
|
private pendingTools = new Map<string, ToolExecutionComponent>();
|
|
176
176
|
|
|
177
177
|
// Tool output expansion state
|
|
178
|
-
private toolOutputExpanded =
|
|
178
|
+
private toolOutputExpanded = false;
|
|
179
179
|
|
|
180
180
|
// Thinking block visibility state
|
|
181
181
|
private hideThinkingBlock = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-pi",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.5",
|
|
4
4
|
"description": "GSD — Get Shit Done coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"pi:install-global": "node scripts/install-pi-global.js",
|
|
55
55
|
"pi:uninstall-global": "node scripts/uninstall-pi-global.js",
|
|
56
56
|
"sync-pkg-version": "node scripts/sync-pkg-version.cjs",
|
|
57
|
-
"
|
|
57
|
+
"sync-platform-versions": "node native/scripts/sync-platform-versions.cjs",
|
|
58
|
+
"prepublishOnly": "npm run sync-pkg-version && npm run sync-platform-versions && npm run build"
|
|
58
59
|
},
|
|
59
60
|
"dependencies": {
|
|
60
61
|
"@clack/prompts": "^1.1.0",
|
|
@@ -81,6 +82,11 @@
|
|
|
81
82
|
"typescript": "^5.4.0"
|
|
82
83
|
},
|
|
83
84
|
"optionalDependencies": {
|
|
85
|
+
"@gsd-build/engine-darwin-arm64": "2.10.5",
|
|
86
|
+
"@gsd-build/engine-darwin-x64": "2.10.5",
|
|
87
|
+
"@gsd-build/engine-linux-x64-gnu": "2.10.5",
|
|
88
|
+
"@gsd-build/engine-linux-arm64-gnu": "2.10.5",
|
|
89
|
+
"@gsd-build/engine-win32-x64-msvc": "2.10.5",
|
|
84
90
|
"fsevents": "~2.3.3"
|
|
85
91
|
},
|
|
86
92
|
"overrides": {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native BLAKE3 content hashing — Rust implementation via napi-rs.
|
|
3
|
+
*
|
|
4
|
+
* Provides ultra-fast content hashing (~GB/s) using BLAKE3 with automatic
|
|
5
|
+
* SIMD acceleration (AVX2, SSE4.1, NEON). All hashes are lowercase hex,
|
|
6
|
+
* 64 characters (256-bit).
|
|
7
|
+
*/
|
|
8
|
+
export interface HashDirectoryOptions {
|
|
9
|
+
/** Glob pattern to filter files (e.g. "**\/*.ts"). Defaults to all files. */
|
|
10
|
+
glob?: string;
|
|
11
|
+
/** Whether to respect .gitignore rules. Defaults to true. */
|
|
12
|
+
gitignore?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** BLAKE3 hash of a UTF-8 string, returned as lowercase 64-char hex. */
|
|
15
|
+
export declare function hashString(text: string): string;
|
|
16
|
+
/** BLAKE3 hash of a file's contents, returned as lowercase 64-char hex. */
|
|
17
|
+
export declare function hashFile(path: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* BLAKE3 hash of multiple files in parallel.
|
|
20
|
+
* Returns a map of path -> hex hash. Silently skips unreadable files.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hashFiles(paths: string[]): Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* BLAKE3 hash all files in a directory, optionally filtered by glob.
|
|
25
|
+
* Returns a map of relative path -> hex hash.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hashDirectory(dirPath: string, options?: HashDirectoryOptions): Record<string, string>;
|
|
28
|
+
/**
|
|
29
|
+
* Given previous hashes (path -> hex), re-hash each file and return
|
|
30
|
+
* paths whose content changed or no longer exist.
|
|
31
|
+
*/
|
|
32
|
+
export declare function didFilesChange(hashes: Record<string, string>): string[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native BLAKE3 content hashing — Rust implementation via napi-rs.
|
|
3
|
+
*
|
|
4
|
+
* Provides ultra-fast content hashing (~GB/s) using BLAKE3 with automatic
|
|
5
|
+
* SIMD acceleration (AVX2, SSE4.1, NEON). All hashes are lowercase hex,
|
|
6
|
+
* 64 characters (256-bit).
|
|
7
|
+
*/
|
|
8
|
+
import { native } from "../native.js";
|
|
9
|
+
/** BLAKE3 hash of a UTF-8 string, returned as lowercase 64-char hex. */
|
|
10
|
+
export function hashString(text) {
|
|
11
|
+
return native.hashString(text);
|
|
12
|
+
}
|
|
13
|
+
/** BLAKE3 hash of a file's contents, returned as lowercase 64-char hex. */
|
|
14
|
+
export function hashFile(path) {
|
|
15
|
+
return native.hashFile(path);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* BLAKE3 hash of multiple files in parallel.
|
|
19
|
+
* Returns a map of path -> hex hash. Silently skips unreadable files.
|
|
20
|
+
*/
|
|
21
|
+
export function hashFiles(paths) {
|
|
22
|
+
return native.hashFiles(paths);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* BLAKE3 hash all files in a directory, optionally filtered by glob.
|
|
26
|
+
* Returns a map of relative path -> hex hash.
|
|
27
|
+
*/
|
|
28
|
+
export function hashDirectory(dirPath, options) {
|
|
29
|
+
return native.hashDirectory(dirPath, options);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Given previous hashes (path -> hex), re-hash each file and return
|
|
33
|
+
* paths whose content changed or no longer exist.
|
|
34
|
+
*/
|
|
35
|
+
export function didFilesChange(hashes) {
|
|
36
|
+
return native.didFilesChange(hashes);
|
|
37
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* Native addon loader.
|
|
3
3
|
*
|
|
4
4
|
* Locates and loads the compiled Rust N-API addon (`.node` file).
|
|
5
|
-
*
|
|
5
|
+
* Resolution order:
|
|
6
|
+
* 1. @gsd-build/engine-{platform} npm optional dependency (production install)
|
|
7
|
+
* 2. native/addon/gsd_engine.{platform}.node (local release build)
|
|
8
|
+
* 3. native/addon/gsd_engine.dev.node (local debug build)
|
|
6
9
|
*/
|
|
7
10
|
export declare const native: {
|
|
8
11
|
search: (content: Buffer | Uint8Array, options: unknown) => unknown;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* Native addon loader.
|
|
3
3
|
*
|
|
4
4
|
* Locates and loads the compiled Rust N-API addon (`.node` file).
|
|
5
|
-
*
|
|
5
|
+
* Resolution order:
|
|
6
|
+
* 1. @gsd-build/engine-{platform} npm optional dependency (production install)
|
|
7
|
+
* 2. native/addon/gsd_engine.{platform}.node (local release build)
|
|
8
|
+
* 3. native/addon/gsd_engine.dev.node (local debug build)
|
|
6
9
|
*/
|
|
7
10
|
import { createRequire } from "node:module";
|
|
8
11
|
import * as path from "node:path";
|
|
@@ -11,24 +14,51 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
11
14
|
const require = createRequire(import.meta.url);
|
|
12
15
|
const addonDir = path.resolve(__dirname, "..", "..", "..", "native", "addon");
|
|
13
16
|
const platformTag = `${process.platform}-${process.arch}`;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/** Map Node.js platform/arch to the npm package suffix */
|
|
18
|
+
const platformPackageMap = {
|
|
19
|
+
"darwin-arm64": "darwin-arm64",
|
|
20
|
+
"darwin-x64": "darwin-x64",
|
|
21
|
+
"linux-x64": "linux-x64-gnu",
|
|
22
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
23
|
+
"win32-x64": "win32-x64-msvc",
|
|
24
|
+
};
|
|
18
25
|
function loadNative() {
|
|
19
26
|
const errors = [];
|
|
20
|
-
|
|
27
|
+
// 1. Try the platform-specific npm optional dependency
|
|
28
|
+
const packageSuffix = platformPackageMap[platformTag];
|
|
29
|
+
if (packageSuffix) {
|
|
21
30
|
try {
|
|
22
|
-
return require(
|
|
31
|
+
return require(`@gsd-build/engine-${packageSuffix}`);
|
|
23
32
|
}
|
|
24
33
|
catch (err) {
|
|
25
34
|
const message = err instanceof Error ? err.message : String(err);
|
|
26
|
-
errors.push(
|
|
35
|
+
errors.push(`@gsd-build/engine-${packageSuffix}: ${message}`);
|
|
27
36
|
}
|
|
28
37
|
}
|
|
38
|
+
// 2. Try local release build (native/addon/gsd_engine.{platform}.node)
|
|
39
|
+
const releasePath = path.join(addonDir, `gsd_engine.${platformTag}.node`);
|
|
40
|
+
try {
|
|
41
|
+
return require(releasePath);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
+
errors.push(`${releasePath}: ${message}`);
|
|
46
|
+
}
|
|
47
|
+
// 3. Try local dev build (native/addon/gsd_engine.dev.node)
|
|
48
|
+
const devPath = path.join(addonDir, "gsd_engine.dev.node");
|
|
49
|
+
try {
|
|
50
|
+
return require(devPath);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
+
errors.push(`${devPath}: ${message}`);
|
|
55
|
+
}
|
|
29
56
|
const details = errors.map((e) => ` - ${e}`).join("\n");
|
|
57
|
+
const supportedPlatforms = Object.keys(platformPackageMap);
|
|
30
58
|
throw new Error(`Failed to load gsd_engine native addon for ${platformTag}.\n\n` +
|
|
31
59
|
`Tried:\n${details}\n\n` +
|
|
32
|
-
`
|
|
60
|
+
`Supported platforms: ${supportedPlatforms.join(", ")}\n` +
|
|
61
|
+
`If your platform is listed, try reinstalling: npm i -g gsd-pi\n` +
|
|
62
|
+
`Otherwise, please open an issue: https://github.com/gsd-build/gsd-2/issues`);
|
|
33
63
|
}
|
|
34
64
|
export const native = loadNative();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native xxHash32 — Rust implementation via napi-rs.
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for the pure-JS xxHash32 in hashline.ts.
|
|
5
|
+
* Hashes the UTF-8 representation of the input string with the given seed.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Compute xxHash32 of a UTF-8 string.
|
|
9
|
+
*
|
|
10
|
+
* @param input The string to hash (encoded as UTF-8 internally).
|
|
11
|
+
* @param seed 32-bit seed value.
|
|
12
|
+
* @returns 32-bit unsigned hash.
|
|
13
|
+
*/
|
|
14
|
+
export declare function xxHash32(input: string, seed: number): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native xxHash32 — Rust implementation via napi-rs.
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for the pure-JS xxHash32 in hashline.ts.
|
|
5
|
+
* Hashes the UTF-8 representation of the input string with the given seed.
|
|
6
|
+
*/
|
|
7
|
+
import { native } from "../native.js";
|
|
8
|
+
/**
|
|
9
|
+
* Compute xxHash32 of a UTF-8 string.
|
|
10
|
+
*
|
|
11
|
+
* @param input The string to hash (encoded as UTF-8 internally).
|
|
12
|
+
* @param seed 32-bit seed value.
|
|
13
|
+
* @returns 32-bit unsigned hash.
|
|
14
|
+
*/
|
|
15
|
+
export function xxHash32(input, seed) {
|
|
16
|
+
return native.xxHash32(input, seed);
|
|
17
|
+
}
|