@theokit/sdk-tools 0.26.1 → 0.26.3
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 +40 -0
- package/README.md +1 -1
- package/dist/index.cjs +41 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -103
- package/dist/index.d.ts +103 -103
- package/dist/index.js +41 -22
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8790f70: Refuse a `workspace:` range before it can reach npm.
|
|
8
|
+
|
|
9
|
+
Five of this repo's twelve publishable packages declare internal dependencies as `workspace:^`, which
|
|
10
|
+
is correct on disk and becomes an unrecoverable defect if the publish goes out through a tool that
|
|
11
|
+
does not rewrite it: `pnpm` resolves the protocol while packing, `npm` ships the manifest verbatim.
|
|
12
|
+
A version published that way fails to install for everyone and cannot be corrected — only
|
|
13
|
+
deprecated.
|
|
14
|
+
|
|
15
|
+
Every publishable package now runs the guard in `prepublishOnly`, so it fires whichever way the
|
|
16
|
+
publish is invoked, and `pnpm release` runs it once across the repo before `changeset publish`.
|
|
17
|
+
|
|
18
|
+
Note for anyone reading a published manifest: the `prepublishOnly` entry points at a path inside
|
|
19
|
+
this repository. It never runs for a consumer — the hook only fires when the package itself is
|
|
20
|
+
published — and guarding the entry point that a hand-run `npm publish` actually uses was worth the
|
|
21
|
+
cosmetic wart of shipping the line.
|
|
22
|
+
|
|
23
|
+
## 0.26.2
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- a713fc7: `interactive_shell` no longer flattens a session-cap error into
|
|
28
|
+
`interactive_unavailable`.
|
|
29
|
+
|
|
30
|
+
`toErrorJson` matched `InteractiveUnavailableError` first, so `MaxSessionsError` — which extends it —
|
|
31
|
+
took that branch and lost `max` and `liveSessionIds`. Those are the only actionable fields in the
|
|
32
|
+
error: without them the model cannot tell a missing backend from a session cap it could clear by
|
|
33
|
+
reusing one of the open sessions, which is exactly what `@theokit/sdk-pty`'s docblock says those
|
|
34
|
+
fields exist for.
|
|
35
|
+
|
|
36
|
+
The check is structural rather than `instanceof`, because the class lives in `@theokit/sdk-pty` and
|
|
37
|
+
this package does not depend on it — and the tool takes an injected backend, so any provider
|
|
38
|
+
reporting the same two fields gets the same treatment.
|
|
39
|
+
|
|
40
|
+
Measured from a consumer that had forked this tool's entire schema and handler to recover the fields,
|
|
41
|
+
since there is no error seam to override.
|
|
42
|
+
|
|
3
43
|
## 0.20.1
|
|
4
44
|
|
|
5
45
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Built-in tools for `@theokit/sdk` agents. File system, git, subprocess, search-t
|
|
|
4
4
|
|
|
5
5
|
Extracted from `@theokit/sdk@1.7.0` as part of the SDK 2.0 package split.
|
|
6
6
|
|
|
7
|
-
See the [**Theo Harness Capability Map**](../../
|
|
7
|
+
See the [**Theo Harness Capability Map**](../../wiki/reference/harness-capability-map.md) for every tool + guard primitive (`buildRepoMap`, `isBlockedIp`, `screenedFetch`, `catastrophicShellReason`, ...) with import paths and examples.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
package/dist/index.cjs
CHANGED
|
@@ -793,12 +793,12 @@ function checkPathScope(path, projectRoot) {
|
|
|
793
793
|
throw err;
|
|
794
794
|
}
|
|
795
795
|
}
|
|
796
|
-
var
|
|
797
|
-
function
|
|
796
|
+
var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([".env", ".git", "node_modules", ".theo"]);
|
|
797
|
+
function isForbiddenAtAnyDepth(path) {
|
|
798
798
|
const segs = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
799
799
|
return segs.some((s) => {
|
|
800
800
|
if (s === ".env.example") return false;
|
|
801
|
-
return
|
|
801
|
+
return SENSITIVE_SEGMENTS.has(s) || /^\.env\./.test(s);
|
|
802
802
|
});
|
|
803
803
|
}
|
|
804
804
|
|
|
@@ -866,7 +866,7 @@ function createGitStatusTool(opts) {
|
|
|
866
866
|
}
|
|
867
867
|
const scopeCheck = checkPathScope(path$1, projectRoot);
|
|
868
868
|
if (scopeCheck !== null) return scopeCheck;
|
|
869
|
-
const args =
|
|
869
|
+
const args = buildArgs(path$1, opts.includeBranch !== false);
|
|
870
870
|
if (opts.sandbox !== void 0) {
|
|
871
871
|
return statusViaSandbox(opts.sandbox, ctx, args, timeoutMs);
|
|
872
872
|
}
|
|
@@ -875,9 +875,9 @@ function createGitStatusTool(opts) {
|
|
|
875
875
|
}
|
|
876
876
|
});
|
|
877
877
|
}
|
|
878
|
-
function
|
|
878
|
+
function buildArgs(path, withBranch) {
|
|
879
879
|
const args = ["status", "--porcelain=v1"];
|
|
880
|
-
if (
|
|
880
|
+
if (withBranch) args.push("-b");
|
|
881
881
|
if (path !== void 0 && path !== "") args.push("--", path);
|
|
882
882
|
return args;
|
|
883
883
|
}
|
|
@@ -1004,7 +1004,26 @@ function globToRegex(pattern) {
|
|
|
1004
1004
|
}
|
|
1005
1005
|
return new RegExp(`^${regexStr}$`);
|
|
1006
1006
|
}
|
|
1007
|
+
function capFields(err) {
|
|
1008
|
+
const e = err;
|
|
1009
|
+
if (typeof e.max !== "number") return void 0;
|
|
1010
|
+
if (!Array.isArray(e.liveSessionIds)) return void 0;
|
|
1011
|
+
if (!e.liveSessionIds.every((id) => typeof id === "string")) return void 0;
|
|
1012
|
+
return { max: e.max, liveSessionIds: e.liveSessionIds };
|
|
1013
|
+
}
|
|
1007
1014
|
function toErrorJson(err) {
|
|
1015
|
+
if (typeof err === "object" && err !== null) {
|
|
1016
|
+
const cap = capFields(err);
|
|
1017
|
+
if (cap !== void 0) {
|
|
1018
|
+
return JSON.stringify({
|
|
1019
|
+
ok: false,
|
|
1020
|
+
error: "interactive_session_limit",
|
|
1021
|
+
max: cap.max,
|
|
1022
|
+
live_session_ids: [...cap.liveSessionIds],
|
|
1023
|
+
message: err.message
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1008
1027
|
if (err instanceof interactive.InteractiveUnavailableError) {
|
|
1009
1028
|
return JSON.stringify({ ok: false, error: "interactive_unavailable" });
|
|
1010
1029
|
}
|
|
@@ -1567,10 +1586,10 @@ function createListDirTool(opts) {
|
|
|
1567
1586
|
}),
|
|
1568
1587
|
handler: async ({ path }, ctx) => {
|
|
1569
1588
|
const relative3 = path === "" || path === "." ? "." : path;
|
|
1570
|
-
const
|
|
1571
|
-
if (
|
|
1572
|
-
if (
|
|
1573
|
-
return listViaLocalFs(
|
|
1589
|
+
const verdict = decideScope(relative3, path, opts.allowAbsolute === true);
|
|
1590
|
+
if (verdict.error !== void 0) return verdict.error;
|
|
1591
|
+
if (verdict.absoluteRoot !== void 0) {
|
|
1592
|
+
return listViaLocalFs(verdict.absoluteRoot, ".", path, max);
|
|
1574
1593
|
}
|
|
1575
1594
|
if (filesystem$1) {
|
|
1576
1595
|
const backend = await filesystem.resolveFilesystem(filesystem$1, ctx ?? {});
|
|
@@ -1580,15 +1599,15 @@ function createListDirTool(opts) {
|
|
|
1580
1599
|
}
|
|
1581
1600
|
});
|
|
1582
1601
|
}
|
|
1583
|
-
function
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1602
|
+
function decideScope(relative3, original, allowAbsolute) {
|
|
1603
|
+
const refuse = (error) => ({
|
|
1604
|
+
error: JSON.stringify({ ok: false, error, path: original })
|
|
1586
1605
|
});
|
|
1587
|
-
if (relative3 !== "." && pathSafety.isForbiddenPath(relative3)) return
|
|
1606
|
+
if (relative3 !== "." && pathSafety.isForbiddenPath(relative3)) return refuse("forbidden_path");
|
|
1588
1607
|
if (!path.isAbsolute(relative3)) return {};
|
|
1589
|
-
if (!allowAbsolute) return
|
|
1590
|
-
if (
|
|
1591
|
-
return {
|
|
1608
|
+
if (!allowAbsolute) return refuse("path_traversal");
|
|
1609
|
+
if (isForbiddenAtAnyDepth(relative3)) return refuse("forbidden_path");
|
|
1610
|
+
return { absoluteRoot: relative3 };
|
|
1592
1611
|
}
|
|
1593
1612
|
async function listViaLocalFs(projectRoot, relative3, originalPath, max) {
|
|
1594
1613
|
const boundary = resolveDirBoundary(relative3, projectRoot, originalPath);
|
|
@@ -1750,10 +1769,10 @@ function createPlanModeTool(options) {
|
|
|
1750
1769
|
}
|
|
1751
1770
|
|
|
1752
1771
|
// src/question.ts
|
|
1753
|
-
function
|
|
1772
|
+
function askerFromContext(context) {
|
|
1754
1773
|
if (typeof context !== "object" || context === null) return void 0;
|
|
1755
|
-
const
|
|
1756
|
-
return typeof
|
|
1774
|
+
const candidate = context.askUser;
|
|
1775
|
+
return typeof candidate === "function" ? candidate : void 0;
|
|
1757
1776
|
}
|
|
1758
1777
|
function createQuestionTool(opts) {
|
|
1759
1778
|
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
@@ -1768,7 +1787,7 @@ function createQuestionTool(opts) {
|
|
|
1768
1787
|
required: ["question"]
|
|
1769
1788
|
},
|
|
1770
1789
|
handler: async (input, ctx) => {
|
|
1771
|
-
const askUser =
|
|
1790
|
+
const askUser = askerFromContext(ctx?.context) ?? opts.askUser;
|
|
1772
1791
|
if (askUser === void 0) {
|
|
1773
1792
|
return JSON.stringify({
|
|
1774
1793
|
ok: false,
|
|
@@ -1805,7 +1824,7 @@ function forbiddenReadError(path$1, allowAbsolute) {
|
|
|
1805
1824
|
if (pathSafety.isForbiddenPath(path$1)) {
|
|
1806
1825
|
return JSON.stringify({ ok: false, error: "forbidden_path", path: path$1 });
|
|
1807
1826
|
}
|
|
1808
|
-
if (allowAbsolute && path.isAbsolute(path$1) &&
|
|
1827
|
+
if (allowAbsolute && path.isAbsolute(path$1) && isForbiddenAtAnyDepth(path$1)) {
|
|
1809
1828
|
return JSON.stringify({ ok: false, error: "forbidden_path", path: path$1 });
|
|
1810
1829
|
}
|
|
1811
1830
|
return null;
|