@x-code-cli/core 0.1.7 → 0.1.9
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/dist/agent/file-ingest.d.ts +62 -0
- package/dist/agent/file-ingest.d.ts.map +1 -0
- package/dist/agent/file-ingest.js +390 -0
- package/dist/agent/file-ingest.js.map +1 -0
- package/dist/agent/light-compact.d.ts +13 -0
- package/dist/agent/light-compact.d.ts.map +1 -0
- package/dist/agent/light-compact.js +106 -0
- package/dist/agent/light-compact.js.map +1 -0
- package/dist/agent/loop-guard.d.ts +50 -0
- package/dist/agent/loop-guard.d.ts.map +1 -0
- package/dist/agent/loop-guard.js +107 -0
- package/dist/agent/loop-guard.js.map +1 -0
- package/dist/agent/loop-state.d.ts +11 -0
- package/dist/agent/loop-state.d.ts.map +1 -1
- package/dist/agent/loop-state.js +3 -1
- package/dist/agent/loop-state.js.map +1 -1
- package/dist/agent/loop.d.ts +2 -2
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +73 -8
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/messages.d.ts +5 -2
- package/dist/agent/messages.d.ts.map +1 -1
- package/dist/agent/messages.js.map +1 -1
- package/dist/agent/provider-compat.d.ts +7 -0
- package/dist/agent/provider-compat.d.ts.map +1 -1
- package/dist/agent/provider-compat.js +122 -0
- package/dist/agent/provider-compat.js.map +1 -1
- package/dist/agent/stream-utils.d.ts +7 -0
- package/dist/agent/stream-utils.d.ts.map +1 -1
- package/dist/agent/stream-utils.js.map +1 -1
- package/dist/agent/system-prompt.js +3 -3
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/tool-execution.d.ts.map +1 -1
- package/dist/agent/tool-execution.js +68 -26
- package/dist/agent/tool-execution.js.map +1 -1
- package/dist/agent/tool-result-sanitize.d.ts +8 -0
- package/dist/agent/tool-result-sanitize.d.ts.map +1 -0
- package/dist/agent/tool-result-sanitize.js +77 -0
- package/dist/agent/tool-result-sanitize.js.map +1 -0
- package/dist/agent/vision-fallback.d.ts +22 -0
- package/dist/agent/vision-fallback.d.ts.map +1 -0
- package/dist/agent/vision-fallback.js +127 -0
- package/dist/agent/vision-fallback.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/session-usage.d.ts +24 -0
- package/dist/knowledge/session-usage.d.ts.map +1 -0
- package/dist/knowledge/session-usage.js +86 -0
- package/dist/knowledge/session-usage.js.map +1 -0
- package/dist/providers/cache-control.d.ts +29 -0
- package/dist/providers/cache-control.d.ts.map +1 -0
- package/dist/providers/cache-control.js +93 -0
- package/dist/providers/cache-control.js.map +1 -0
- package/dist/providers/capabilities.d.ts +15 -0
- package/dist/providers/capabilities.d.ts.map +1 -0
- package/dist/providers/capabilities.js +38 -0
- package/dist/providers/capabilities.js.map +1 -0
- package/dist/tools/index.d.ts +31 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -10
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read-file.d.ts +29 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +103 -10
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/shell-provider.d.ts +13 -0
- package/dist/tools/shell-provider.d.ts.map +1 -0
- package/dist/tools/shell-provider.js +74 -0
- package/dist/tools/shell-provider.js.map +1 -0
- package/dist/tools/shell-utils.d.ts +1 -7
- package/dist/tools/shell-utils.d.ts.map +1 -1
- package/dist/tools/shell-utils.js +0 -17
- package/dist/tools/shell-utils.js.map +1 -1
- package/dist/tools/truncate.d.ts +36 -0
- package/dist/tools/truncate.d.ts.map +1 -0
- package/dist/tools/truncate.js +118 -0
- package/dist/tools/truncate.js.map +1 -0
- package/dist/tools/web-search.js +2 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/shell-error.d.ts +12 -0
- package/dist/utils/shell-error.d.ts.map +1 -0
- package/dist/utils/shell-error.js +73 -0
- package/dist/utils/shell-error.js.map +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +104 -10
- package/dist/utils.js.map +1 -1
- package/package.json +21 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-usage.js","sourceRoot":"","sources":["../../src/knowledge/session-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ModelMessage } from 'ai';
|
|
2
|
+
export interface CacheControlArgs {
|
|
3
|
+
/** System prompt string. May be wrapped into a system-role message if the
|
|
4
|
+
* provider needs cache_control attached to it. */
|
|
5
|
+
system: string;
|
|
6
|
+
/** Conversation messages to send. */
|
|
7
|
+
messages: ModelMessage[];
|
|
8
|
+
/** provider:model id used to select the caching strategy. */
|
|
9
|
+
modelId: string;
|
|
10
|
+
/** Stable per-session key. Used by OpenAI's `promptCacheKey` to pin
|
|
11
|
+
* identical prefixes to the same cache shard. */
|
|
12
|
+
sessionId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CacheControlResult {
|
|
15
|
+
/** Possibly-undefined: for Anthropic we fold the system prompt into the
|
|
16
|
+
* messages array to attach cache_control; in that case streamText must be
|
|
17
|
+
* called without a separate `system` param. */
|
|
18
|
+
system?: string;
|
|
19
|
+
messages: ModelMessage[];
|
|
20
|
+
/** Top-level providerOptions to pass through to streamText. */
|
|
21
|
+
providerOptions?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Return the request shape enriched with provider-specific caching hints.
|
|
25
|
+
* The input `messages` array is not mutated — new message objects are
|
|
26
|
+
* returned for any message that needs extra providerOptions.
|
|
27
|
+
*/
|
|
28
|
+
export declare function applyCacheControl(args: CacheControlArgs): CacheControlResult;
|
|
29
|
+
//# sourceMappingURL=cache-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.d.ts","sourceRoot":"","sources":["../../src/providers/cache-control.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAWtC,MAAM,WAAW,gBAAgB;IAC/B;uDACmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd,qCAAqC;IACrC,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;IACf;sDACkD;IAClD,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC;;oDAEgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C;AA6BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CAkC5E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @x-code-cli/core — Per-provider prompt caching
|
|
2
|
+
//
|
|
3
|
+
// Prompt caching is the single biggest lever on per-session cost. All current
|
|
4
|
+
// providers offer it, but the activation protocol differs:
|
|
5
|
+
//
|
|
6
|
+
// Anthropic — set `cacheControl: { type: 'ephemeral' }` on the SYSTEM
|
|
7
|
+
// message and the LAST two non-system messages (three
|
|
8
|
+
// breakpoints total, under the API's limit of four). The
|
|
9
|
+
// content at each breakpoint is cached server-side with a
|
|
10
|
+
// 5-minute TTL; subsequent requests that share the exact
|
|
11
|
+
// prefix hit the cache and only pay for the uncached tail.
|
|
12
|
+
//
|
|
13
|
+
// OpenAI — automatic prefix caching, but setting `promptCacheKey`
|
|
14
|
+
// (routes identical keys to the same cache shard) and `store`
|
|
15
|
+
// (retains the call for later fetching) improves hit rates.
|
|
16
|
+
// We send the sessionId as the key so every turn in a
|
|
17
|
+
// conversation maps to the same shard.
|
|
18
|
+
//
|
|
19
|
+
// OpenAI- — the DeepSeek / Moonshot / Alibaba / Zhipu / xAI / custom
|
|
20
|
+
// compatible providers all offer automatic prefix caching with NO
|
|
21
|
+
// explicit flags required. The only prerequisite is a
|
|
22
|
+
// byte-stable prefix across turns: if the system prompt
|
|
23
|
+
// rebuilds with a fresh timestamp every turn, every request
|
|
24
|
+
// misses the cache. We therefore cache the system prompt
|
|
25
|
+
// once per session in LoopState (see loop-state.ts) and use
|
|
26
|
+
// the same string on every subsequent turn.
|
|
27
|
+
//
|
|
28
|
+
// Google — Gemini uses implicit caching; no per-request flags we can
|
|
29
|
+
// usefully set from the SDK. Left as a no-op.
|
|
30
|
+
import { providerOf } from './capabilities.js';
|
|
31
|
+
/** Max messages we attach an Anthropic cache breakpoint to. Anthropic allows
|
|
32
|
+
* up to 4 `cache_control` blocks per request; we spend one on the system
|
|
33
|
+
* prompt, leaving three for the message tail. Two is the sweet spot from
|
|
34
|
+
* opencode's testing — a third breakpoint costs a cache-write against a
|
|
35
|
+
* region (the just-before-last message) that's about to be evicted anyway. */
|
|
36
|
+
const MESSAGE_CACHE_BREAKPOINTS = 2;
|
|
37
|
+
/** Attach the given providerOptions entry to a message non-destructively. */
|
|
38
|
+
function tagMessage(msg, provider, entry) {
|
|
39
|
+
const existing = msg.providerOptions ?? {};
|
|
40
|
+
return {
|
|
41
|
+
...msg,
|
|
42
|
+
providerOptions: {
|
|
43
|
+
...existing,
|
|
44
|
+
[provider]: { ...(existing[provider] ?? {}), ...entry },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Build a system-role message with Anthropic cache_control attached. */
|
|
49
|
+
function anthropicSystemMessage(system) {
|
|
50
|
+
return {
|
|
51
|
+
role: 'system',
|
|
52
|
+
content: system,
|
|
53
|
+
providerOptions: {
|
|
54
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Return the request shape enriched with provider-specific caching hints.
|
|
60
|
+
* The input `messages` array is not mutated — new message objects are
|
|
61
|
+
* returned for any message that needs extra providerOptions.
|
|
62
|
+
*/
|
|
63
|
+
export function applyCacheControl(args) {
|
|
64
|
+
const provider = providerOf(args.modelId);
|
|
65
|
+
if (provider === 'anthropic') {
|
|
66
|
+
// Fold system into messages so we can attach cache_control to it, then
|
|
67
|
+
// mark the last N non-system messages as additional breakpoints.
|
|
68
|
+
const nonSystemTail = args.messages.slice(-MESSAGE_CACHE_BREAKPOINTS);
|
|
69
|
+
const tailSet = new Set(nonSystemTail);
|
|
70
|
+
const tagged = args.messages.map((m) => tailSet.has(m) ? tagMessage(m, 'anthropic', { cacheControl: { type: 'ephemeral' } }) : m);
|
|
71
|
+
return {
|
|
72
|
+
system: undefined,
|
|
73
|
+
messages: [anthropicSystemMessage(args.system), ...tagged],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (provider === 'openai') {
|
|
77
|
+
// store:false — we don't need the stored-call bookkeeping (retrieval via
|
|
78
|
+
// API), but the promptCacheKey still routes identical prefixes to the
|
|
79
|
+
// same cache shard which is the actual cost win.
|
|
80
|
+
return {
|
|
81
|
+
system: args.system,
|
|
82
|
+
messages: args.messages,
|
|
83
|
+
providerOptions: {
|
|
84
|
+
openai: { promptCacheKey: args.sessionId, store: false },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// OpenAI-compatible & Gemini: no explicit flags, just rely on stable prefix.
|
|
89
|
+
// Callers must ensure buildSystemPrompt is cached in LoopState so the same
|
|
90
|
+
// system string is re-sent every turn.
|
|
91
|
+
return { system: args.system, messages: args.messages };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=cache-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.js","sourceRoot":"","sources":["../../src/providers/cache-control.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,yEAAyE;AACzE,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,4EAA4E;AAC5E,sEAAsE;AACtE,uDAAuD;AACvD,EAAE;AACF,2EAA2E;AAC3E,uEAAuE;AACvE,sEAAsE;AACtE,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,4EAA4E;AAC5E,4DAA4D;AAC5D,EAAE;AACF,4EAA4E;AAC5E,8DAA8D;AAI9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C;;;;+EAI+E;AAC/E,MAAM,yBAAyB,GAAG,CAAC,CAAA;AAyBnC,6EAA6E;AAC7E,SAAS,UAAU,CACjB,GAAiB,EACjB,QAAgB,EAChB,KAA8B;IAE9B,MAAM,QAAQ,GAAI,GAAqE,CAAC,eAAe,IAAI,EAAE,CAAA;IAC7G,OAAO;QACL,GAAG,GAAG;QACN,eAAe,EAAE;YACf,GAAG,QAAQ;YACX,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE;SACxD;KACc,CAAA;AACnB,CAAC;AAED,yEAAyE;AACzE,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,MAAM;QACf,eAAe,EAAE;YACf,SAAS,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;SACnD;KACyB,CAAA;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAsB;IACtD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEzC,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC7B,uEAAuE;QACvE,iEAAiE;QACjE,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,yBAAyB,CAAC,CAAA;QACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAA;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACrC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACzF,CAAA;QACD,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC;SAC3D,CAAA;IACH,CAAC;IAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,yEAAyE;QACzE,sEAAsE;QACtE,iDAAiD;QACjD,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,eAAe,EAAE;gBACf,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE;aACzD;SACF,CAAA;IACH,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,uCAAuC;IACvC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;AACzD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ProviderCapabilities {
|
|
2
|
+
/** Provider can receive inline image parts (base64 or URL) in user messages. */
|
|
3
|
+
image: boolean;
|
|
4
|
+
/** Provider can receive inline PDF file parts. */
|
|
5
|
+
pdf: boolean;
|
|
6
|
+
/** Provider has a dedicated /files upload endpoint (file_id references). */
|
|
7
|
+
filesApi: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Extract `provider` from a `provider:model` id. Returns `unknown` if the
|
|
10
|
+
* separator is missing (defensive — shouldn't happen with resolved ids). */
|
|
11
|
+
export declare function providerOf(modelId: string): string;
|
|
12
|
+
/** Look up capabilities for a model id. Unknown providers default to text-only
|
|
13
|
+
* — safer than assuming vision support. */
|
|
14
|
+
export declare function capabilitiesOf(modelId: string): ProviderCapabilities;
|
|
15
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/providers/capabilities.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAA;IACd,kDAAkD;IAClD,GAAG,EAAE,OAAO,CAAA;IACZ,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAA;CAClB;AAiBD;6EAC6E;AAC7E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;4CAC4C;AAC5C,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAEpE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @x-code-cli/core — Provider multi-modal capability table
|
|
2
|
+
//
|
|
3
|
+
// Declares whether each provider's API can natively accept image / pdf
|
|
4
|
+
// content parts in user messages and tool results. Used by the file-ingest
|
|
5
|
+
// pipeline (to decide inline-vs-OCR) and provider-compat (to strip binary
|
|
6
|
+
// parts before sending to providers that would reject them).
|
|
7
|
+
//
|
|
8
|
+
// Provider-level, not model-level. Some providers (alibaba, zhipu) have
|
|
9
|
+
// separate vision-only model ids — users who pick a text-only Qwen/GLM
|
|
10
|
+
// variant and paste an image will still get API errors. That's a deliberate
|
|
11
|
+
// simplification: model-level capability tracking would require per-id
|
|
12
|
+
// tables that go stale quickly.
|
|
13
|
+
const CAPS = {
|
|
14
|
+
anthropic: { image: true, pdf: true, filesApi: true },
|
|
15
|
+
openai: { image: true, pdf: true, filesApi: true },
|
|
16
|
+
google: { image: true, pdf: true, filesApi: true },
|
|
17
|
+
xai: { image: true, pdf: true, filesApi: true },
|
|
18
|
+
moonshotai: { image: true, pdf: true, filesApi: true },
|
|
19
|
+
alibaba: { image: true, pdf: true, filesApi: true },
|
|
20
|
+
zhipu: { image: true, pdf: true, filesApi: true },
|
|
21
|
+
deepseek: { image: false, pdf: false, filesApi: false },
|
|
22
|
+
// Custom OpenAI-compatible endpoints are conservative-by-default —
|
|
23
|
+
// users who know their endpoint supports vision can override via env
|
|
24
|
+
// (X_CODE_CUSTOM_SUPPORTS_IMAGE=1) if we ever add that.
|
|
25
|
+
custom: { image: false, pdf: false, filesApi: false },
|
|
26
|
+
};
|
|
27
|
+
/** Extract `provider` from a `provider:model` id. Returns `unknown` if the
|
|
28
|
+
* separator is missing (defensive — shouldn't happen with resolved ids). */
|
|
29
|
+
export function providerOf(modelId) {
|
|
30
|
+
const idx = modelId.indexOf(':');
|
|
31
|
+
return idx > 0 ? modelId.slice(0, idx) : 'unknown';
|
|
32
|
+
}
|
|
33
|
+
/** Look up capabilities for a model id. Unknown providers default to text-only
|
|
34
|
+
* — safer than assuming vision support. */
|
|
35
|
+
export function capabilitiesOf(modelId) {
|
|
36
|
+
return CAPS[providerOf(modelId)] ?? { image: false, pdf: false, filesApi: false };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/providers/capabilities.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,uEAAuE;AACvE,2EAA2E;AAC3E,0EAA0E;AAC1E,6DAA6D;AAC7D,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,4EAA4E;AAC5E,uEAAuE;AACvE,gCAAgC;AAWhC,MAAM,IAAI,GAAyC;IACjD,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IACrD,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClD,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClD,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC/C,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IACtD,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IACnD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjD,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE;IACvD,mEAAmE;IACnE,qEAAqE;IACrE,wDAAwD;IACxD,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE;CACtD,CAAA;AAED;6EAC6E;AAC7E,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAChC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AACpD,CAAC;AAED;4CAC4C;AAC5C,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;AACnF,CAAC"}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -14,7 +14,35 @@ export declare const toolRegistry: {
|
|
|
14
14
|
filePath: string;
|
|
15
15
|
offset?: number | undefined;
|
|
16
16
|
limit?: number | undefined;
|
|
17
|
-
}, string
|
|
17
|
+
}, string | {
|
|
18
|
+
type: string;
|
|
19
|
+
value: ({
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
data?: undefined;
|
|
23
|
+
mediaType?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
type: string;
|
|
26
|
+
data: string;
|
|
27
|
+
mediaType: string;
|
|
28
|
+
text?: undefined;
|
|
29
|
+
})[];
|
|
30
|
+
} | {
|
|
31
|
+
type: string;
|
|
32
|
+
value: ({
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
data?: undefined;
|
|
36
|
+
mediaType?: undefined;
|
|
37
|
+
filename?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
type: string;
|
|
40
|
+
data: string;
|
|
41
|
+
mediaType: string;
|
|
42
|
+
filename: string;
|
|
43
|
+
text?: undefined;
|
|
44
|
+
})[];
|
|
45
|
+
}>;
|
|
18
46
|
writeFile: import("ai").Tool<{
|
|
19
47
|
filePath: string;
|
|
20
48
|
content: string;
|
|
@@ -66,8 +94,6 @@ export declare const toolRegistry: {
|
|
|
66
94
|
}, string>;
|
|
67
95
|
};
|
|
68
96
|
export { readFile, writeFile, edit, shell, glob, grep, listDir, webSearch, webFetch, askUser, saveKnowledge, };
|
|
69
|
-
|
|
70
|
-
export
|
|
71
|
-
/** Truncate tool result, keeping head and tail */
|
|
72
|
-
export declare function truncateToolResult(result: string): string;
|
|
97
|
+
export { MAX_TOOL_RESULT_LINES, MAX_TOOL_RESULT_BYTES, MAX_AGGREGATE_TOOL_RESULT_BYTES, truncateToolResult, } from './truncate.js';
|
|
98
|
+
export type { TruncateOptions } from './truncate.js';
|
|
73
99
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYxB,CAAA;AAED,OAAO,EACL,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,OAAO,EACP,aAAa,GACd,CAAA;AAED,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,+BAA+B,EAC/B,kBAAkB,GACnB,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/tools/index.js
CHANGED
|
@@ -24,14 +24,5 @@ export const toolRegistry = {
|
|
|
24
24
|
saveKnowledge,
|
|
25
25
|
};
|
|
26
26
|
export { readFile, writeFile, edit, shell, glob, grep, listDir, webSearch, webFetch, askUser, saveKnowledge, };
|
|
27
|
-
|
|
28
|
-
export const MAX_TOOL_RESULT_CHARS = 30000;
|
|
29
|
-
/** Truncate tool result, keeping head and tail */
|
|
30
|
-
export function truncateToolResult(result) {
|
|
31
|
-
if (result.length <= MAX_TOOL_RESULT_CHARS)
|
|
32
|
-
return result;
|
|
33
|
-
const half = Math.floor(MAX_TOOL_RESULT_CHARS / 2);
|
|
34
|
-
const truncatedChars = result.length - MAX_TOOL_RESULT_CHARS;
|
|
35
|
-
return result.slice(0, half) + `\n\n... [truncated ${truncatedChars} characters] ...\n\n` + result.slice(-half);
|
|
36
|
-
}
|
|
27
|
+
export { MAX_TOOL_RESULT_LINES, MAX_TOOL_RESULT_BYTES, MAX_AGGREGATE_TOOL_RESULT_BYTES, truncateToolResult, } from './truncate.js';
|
|
37
28
|
//# sourceMappingURL=index.js.map
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,SAAS;IACT,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,SAAS;IACT,QAAQ;IACR,OAAO;IACP,aAAa;CACd,CAAA;AAED,OAAO,EACL,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,OAAO,EACP,aAAa,GACd,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,SAAS;IACT,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,SAAS;IACT,QAAQ;IACR,OAAO;IACP,aAAa;CACd,CAAA;AAED,OAAO,EACL,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,OAAO,EACP,aAAa,GACd,CAAA;AAED,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,+BAA+B,EAC/B,kBAAkB,GACnB,MAAM,eAAe,CAAA"}
|
|
@@ -2,5 +2,33 @@ export declare const readFile: import("ai").Tool<{
|
|
|
2
2
|
filePath: string;
|
|
3
3
|
offset?: number | undefined;
|
|
4
4
|
limit?: number | undefined;
|
|
5
|
-
}, string
|
|
5
|
+
}, string | {
|
|
6
|
+
type: string;
|
|
7
|
+
value: ({
|
|
8
|
+
type: string;
|
|
9
|
+
text: string;
|
|
10
|
+
data?: undefined;
|
|
11
|
+
mediaType?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
type: string;
|
|
14
|
+
data: string;
|
|
15
|
+
mediaType: string;
|
|
16
|
+
text?: undefined;
|
|
17
|
+
})[];
|
|
18
|
+
} | {
|
|
19
|
+
type: string;
|
|
20
|
+
value: ({
|
|
21
|
+
type: string;
|
|
22
|
+
text: string;
|
|
23
|
+
data?: undefined;
|
|
24
|
+
mediaType?: undefined;
|
|
25
|
+
filename?: undefined;
|
|
26
|
+
} | {
|
|
27
|
+
type: string;
|
|
28
|
+
data: string;
|
|
29
|
+
mediaType: string;
|
|
30
|
+
filename: string;
|
|
31
|
+
text?: undefined;
|
|
32
|
+
})[];
|
|
33
|
+
}>;
|
|
6
34
|
//# sourceMappingURL=read-file.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAoEA,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6DnB,CAAA"}
|
package/dist/tools/read-file.js
CHANGED
|
@@ -1,25 +1,118 @@
|
|
|
1
1
|
// @x-code-cli/core — readFile tool
|
|
2
|
+
//
|
|
3
|
+
// Text files are returned as numbered-line strings (the format agents have
|
|
4
|
+
// been trained against). Binary files (images, PDFs) are returned as an
|
|
5
|
+
// AI-SDK `content` tool result so providers that accept inline media
|
|
6
|
+
// receive proper `image-data` / `file-data` parts instead of a base64 blob
|
|
7
|
+
// stuffed inside a text string.
|
|
8
|
+
//
|
|
9
|
+
// The tool itself does NOT branch on provider capability — that would
|
|
10
|
+
// couple the tool layer to the currently-active model. Instead, every
|
|
11
|
+
// binary result goes out as content parts and the provider-compat layer
|
|
12
|
+
// strips them (falling back to OCR'd text) before they reach a provider
|
|
13
|
+
// that can't handle them.
|
|
2
14
|
import fs from 'node:fs/promises';
|
|
15
|
+
import path from 'node:path';
|
|
3
16
|
import { tool } from 'ai';
|
|
4
17
|
import { z } from 'zod';
|
|
18
|
+
import { classifyFile } from '../agent/file-ingest.js';
|
|
5
19
|
import { reportProgress } from './progress.js';
|
|
20
|
+
function mediaTypeFor(filePath) {
|
|
21
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
22
|
+
if (ext === '.jpg' || ext === '.jpeg')
|
|
23
|
+
return 'image/jpeg';
|
|
24
|
+
if (ext === '.png')
|
|
25
|
+
return 'image/png';
|
|
26
|
+
if (ext === '.webp')
|
|
27
|
+
return 'image/webp';
|
|
28
|
+
if (ext === '.gif')
|
|
29
|
+
return 'image/gif';
|
|
30
|
+
if (ext === '.bmp')
|
|
31
|
+
return 'image/bmp';
|
|
32
|
+
return 'image/png';
|
|
33
|
+
}
|
|
34
|
+
/** Threshold above which a no-args readFile call returns a partial head plus a
|
|
35
|
+
* hint to re-read specific ranges. Picked empirically: 500 lines of code is
|
|
36
|
+
* a realistic ceiling for "skim the whole thing", and anything bigger is
|
|
37
|
+
* almost always used with grep first. */
|
|
38
|
+
const LARGE_FILE_LINE_THRESHOLD = 500;
|
|
39
|
+
async function readTextResult(filePath, offset, limit) {
|
|
40
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
41
|
+
const lines = content.split('\n');
|
|
42
|
+
const totalLines = lines.length;
|
|
43
|
+
// When the caller passes neither offset nor limit and the file is large,
|
|
44
|
+
// return only the head and tell the model how to request the rest. Without
|
|
45
|
+
// this guard, models happily read 2000-line files "just to see what's in
|
|
46
|
+
// there" and the full content rides along on every subsequent turn. The
|
|
47
|
+
// downstream truncator (tool-result-sanitize) would eventually clip this,
|
|
48
|
+
// but doing it at the tool level preserves intent — the model sees
|
|
49
|
+
// explicitly that the file was large and that it should narrow the range.
|
|
50
|
+
const userSpecifiedRange = offset != null || limit != null;
|
|
51
|
+
if (!userSpecifiedRange && totalLines > LARGE_FILE_LINE_THRESHOLD) {
|
|
52
|
+
const head = lines.slice(0, LARGE_FILE_LINE_THRESHOLD);
|
|
53
|
+
const body = head.map((line, i) => `${i + 1}\t${line}`).join('\n');
|
|
54
|
+
return (body +
|
|
55
|
+
`\n\n[readFile: showing first ${LARGE_FILE_LINE_THRESHOLD}/${totalLines} lines. ` +
|
|
56
|
+
`Call readFile again with offset/limit to view other ranges, or use grep to find specific symbols.]`);
|
|
57
|
+
}
|
|
58
|
+
const start = (offset ?? 1) - 1;
|
|
59
|
+
const end = limit ? start + limit : lines.length;
|
|
60
|
+
const sliced = lines.slice(start, end);
|
|
61
|
+
return sliced.map((line, i) => `${start + i + 1}\t${line}`).join('\n');
|
|
62
|
+
}
|
|
6
63
|
export const readFile = tool({
|
|
7
|
-
description: 'Read the contents of a file at the given path. Returns the
|
|
64
|
+
description: 'Read the contents of a file at the given path. Returns line-numbered text for code/docs, and inline media for images/PDFs so the model can inspect them directly.',
|
|
8
65
|
inputSchema: z.object({
|
|
9
66
|
filePath: z.string().describe('Absolute path to the file'),
|
|
10
|
-
offset: z.number().optional().describe('Start line (1-based)'),
|
|
11
|
-
limit: z.number().optional().describe('Max lines to read'),
|
|
67
|
+
offset: z.number().optional().describe('Start line (1-based, text files only)'),
|
|
68
|
+
limit: z.number().optional().describe('Max lines to read (text files only)'),
|
|
12
69
|
}),
|
|
13
70
|
execute: async ({ filePath, offset, limit }, { toolCallId }) => {
|
|
14
71
|
try {
|
|
15
72
|
reportProgress(toolCallId, `Reading ${filePath}`);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
73
|
+
const kind = await classifyFile(filePath).catch(() => 'text');
|
|
74
|
+
if (kind === 'image') {
|
|
75
|
+
const buffer = await fs.readFile(filePath);
|
|
76
|
+
// Content tool result: the provider-compat sanitizer decides whether
|
|
77
|
+
// this image survives to the model (multimodal) or gets replaced
|
|
78
|
+
// with an OCR text block (DeepSeek etc.). We attach both an
|
|
79
|
+
// `image-data` part (for providers that can see it) and a trailing
|
|
80
|
+
// text part with the file path (so the model always has a textual
|
|
81
|
+
// anchor to reference).
|
|
82
|
+
return {
|
|
83
|
+
type: 'content',
|
|
84
|
+
value: [
|
|
85
|
+
{ type: 'text', text: `Loaded image: ${filePath}` },
|
|
86
|
+
{
|
|
87
|
+
type: 'image-data',
|
|
88
|
+
data: buffer.toString('base64'),
|
|
89
|
+
mediaType: mediaTypeFor(filePath),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (kind === 'pdf') {
|
|
95
|
+
const buffer = await fs.readFile(filePath);
|
|
96
|
+
return {
|
|
97
|
+
type: 'content',
|
|
98
|
+
value: [
|
|
99
|
+
{ type: 'text', text: `Loaded PDF: ${filePath}` },
|
|
100
|
+
{
|
|
101
|
+
type: 'file-data',
|
|
102
|
+
data: buffer.toString('base64'),
|
|
103
|
+
mediaType: 'application/pdf',
|
|
104
|
+
filename: path.basename(filePath),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Text / Office / unknown → read as text.
|
|
110
|
+
// (Office files are handled up-front by buildUserContent when the user
|
|
111
|
+
// attaches them via @path; if a model calls readFile on a .docx anyway,
|
|
112
|
+
// we fall through to a UTF-8 read which returns gibberish — a follow-up
|
|
113
|
+
// could route Office here too, but it's a rare path worth keeping
|
|
114
|
+
// simple for now.)
|
|
115
|
+
return await readTextResult(filePath, offset, limit);
|
|
23
116
|
}
|
|
24
117
|
catch (err) {
|
|
25
118
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,OAAO,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ResultPromise } from 'execa';
|
|
2
|
+
export type ShellType = 'bash' | 'zsh' | 'powershell';
|
|
3
|
+
export interface ShellSpawnOptions {
|
|
4
|
+
timeout: number;
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ShellProvider {
|
|
9
|
+
type: ShellType;
|
|
10
|
+
spawn(command: string, opts: ShellSpawnOptions): ResultPromise;
|
|
11
|
+
}
|
|
12
|
+
export declare function getShellProvider(): ShellProvider;
|
|
13
|
+
//# sourceMappingURL=shell-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-provider.d.ts","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"AAMA,OAAO,EAAS,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAGjD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAA;AAErD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAAA;CAC/D;AA6DD,wBAAgB,gBAAgB,IAAI,aAAa,CAYhD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// @x-code-cli/core — Cross-platform shell provider abstraction.
|
|
2
|
+
//
|
|
3
|
+
// Each shell (bash/zsh, PowerShell) spawns its own child process with its own
|
|
4
|
+
// quoting/encoding quirks. Keeping those quirks behind a provider interface
|
|
5
|
+
// means the tool-execution layer does not need platform branches and does not
|
|
6
|
+
// hand-roll quote escapes for PowerShell.
|
|
7
|
+
import { execa } from 'execa';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
function createPosixProvider(executable, type) {
|
|
10
|
+
return {
|
|
11
|
+
type,
|
|
12
|
+
spawn(command, opts) {
|
|
13
|
+
return execa(executable, ['-c', command], {
|
|
14
|
+
timeout: opts.timeout,
|
|
15
|
+
cwd: opts.cwd,
|
|
16
|
+
reject: false,
|
|
17
|
+
env: { ...(opts.env ?? process.env), PYTHONIOENCODING: 'utf-8' },
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// PowerShell's -EncodedCommand accepts a base64 UTF-16LE payload. The char set
|
|
23
|
+
// is [A-Za-z0-9+/=] which survives any outer quoting layer (cmd.exe, Node's
|
|
24
|
+
// Windows argv-to-string serializer, etc.), so we never need to escape quotes
|
|
25
|
+
// in the user's command.
|
|
26
|
+
function encodePowerShellCommand(psCommand) {
|
|
27
|
+
return Buffer.from(psCommand, 'utf16le').toString('base64');
|
|
28
|
+
}
|
|
29
|
+
function createPowerShellProvider(executable) {
|
|
30
|
+
return {
|
|
31
|
+
type: 'powershell',
|
|
32
|
+
spawn(command, opts) {
|
|
33
|
+
// Prefix/suffix run inside the same -EncodedCommand payload:
|
|
34
|
+
// • OutputEncoding = UTF-8 — PS 5.1 on zh-CN Windows otherwise writes
|
|
35
|
+
// output in GBK (mojibake when we decode as UTF-8). Avoids the
|
|
36
|
+
// `chcp 65001 >nul && ...` cmd.exe wrapper.
|
|
37
|
+
// • ProgressPreference = SilentlyContinue — first-run module loads
|
|
38
|
+
// emit CLIXML progress records on stderr, which would surface as
|
|
39
|
+
// noise in tool output.
|
|
40
|
+
// • trailing `exit` — PowerShell does NOT propagate $LASTEXITCODE
|
|
41
|
+
// to its own process exit code. Without this, `git push` failing
|
|
42
|
+
// with exit 1 or `tsc` failing with exit 2 all come back as exit 0
|
|
43
|
+
// or a generic 1, losing the signal. Prefer $LASTEXITCODE when a
|
|
44
|
+
// native exe ran; fall back to $? for cmdlet-only pipelines.
|
|
45
|
+
const wrapped = [
|
|
46
|
+
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
|
|
47
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
48
|
+
command,
|
|
49
|
+
'$__ec = if ($null -ne $LASTEXITCODE) { $LASTEXITCODE } elseif ($?) { 0 } else { 1 }',
|
|
50
|
+
'exit $__ec',
|
|
51
|
+
].join('\n');
|
|
52
|
+
return execa(executable, ['-NoProfile', '-NonInteractive', '-EncodedCommand', encodePowerShellCommand(wrapped)], {
|
|
53
|
+
timeout: opts.timeout,
|
|
54
|
+
cwd: opts.cwd,
|
|
55
|
+
reject: false,
|
|
56
|
+
env: { ...(opts.env ?? process.env), PYTHONIOENCODING: 'utf-8' },
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function getShellProvider() {
|
|
62
|
+
if (os.platform() === 'win32') {
|
|
63
|
+
// Git Bash / MSYS2 / Cygwin set SHELL to a Unix-style path. Prefer that
|
|
64
|
+
// when present so the Unix tool ecosystem works as expected.
|
|
65
|
+
const shell = process.env.SHELL;
|
|
66
|
+
if (shell && /\b(bash|zsh)$/i.test(shell)) {
|
|
67
|
+
return createPosixProvider(shell, shell.endsWith('zsh') ? 'zsh' : 'bash');
|
|
68
|
+
}
|
|
69
|
+
return createPowerShellProvider('powershell.exe');
|
|
70
|
+
}
|
|
71
|
+
const userShell = process.env.SHELL ?? '/bin/bash';
|
|
72
|
+
return createPosixProvider(userShell, userShell.endsWith('zsh') ? 'zsh' : 'bash');
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=shell-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-provider.js","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,0CAA0C;AAC1C,OAAO,EAAE,KAAK,EAAsB,MAAM,OAAO,CAAA;AACjD,OAAO,EAAE,MAAM,SAAS,CAAA;AAexB,SAAS,mBAAmB,CAAC,UAAkB,EAAE,IAAoB;IACnE,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,OAAO,EAAE,IAAI;YACjB,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;gBACxC,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,OAAO,EAAE;aACjE,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC;AAED,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,yBAAyB;AACzB,SAAS,uBAAuB,CAAC,SAAiB;IAChD,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAC7D,CAAC;AAED,SAAS,wBAAwB,CAAC,UAAkB;IAClD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,KAAK,CAAC,OAAO,EAAE,IAAI;YACjB,6DAA6D;YAC7D,wEAAwE;YACxE,mEAAmE;YACnE,gDAAgD;YAChD,qEAAqE;YACrE,qEAAqE;YACrE,4BAA4B;YAC5B,oEAAoE;YACpE,qEAAqE;YACrE,uEAAuE;YACvE,qEAAqE;YACrE,iEAAiE;YACjE,MAAM,OAAO,GAAG;gBACd,0DAA0D;gBAC1D,0CAA0C;gBAC1C,OAAO;gBACP,qFAAqF;gBACrF,YAAY;aACb,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACZ,OAAO,KAAK,CACV,UAAU,EACV,CAAC,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC,EACtF;gBACE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,OAAO,EAAE;aACjE,CACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,wEAAwE;QACxE,6DAA6D;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAA;QAC/B,IAAI,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1C,OAAO,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC3E,CAAC;QACD,OAAO,wBAAwB,CAAC,gBAAgB,CAAC,CAAA;IACnD,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW,CAAA;IAClD,OAAO,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACnF,CAAC"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
export type ShellType
|
|
2
|
-
export interface ShellConfig {
|
|
3
|
-
executable: string;
|
|
4
|
-
args: string[];
|
|
5
|
-
type: ShellType;
|
|
6
|
-
}
|
|
7
|
-
export declare function getShellConfig(): ShellConfig;
|
|
1
|
+
export type { ShellType } from './shell-provider.js';
|
|
8
2
|
/** Split compound shell commands by pipe/chain operators for permission checking */
|
|
9
3
|
export declare function splitShellCommands(cmd: string): string[];
|
|
10
4
|
/** Check if a sub-command is read-only (safe to auto-allow) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-utils.d.ts","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shell-utils.d.ts","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"AAKA,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0CxD;AAkDD,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGlD"}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
// @x-code-cli/core — Cross-platform shell detection and abstraction
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
export function getShellConfig() {
|
|
4
|
-
if (os.platform() === 'win32') {
|
|
5
|
-
// Git Bash / MSYS2 / Cygwin set SHELL to a Unix-style path (e.g. /usr/bin/bash).
|
|
6
|
-
// Prefer that shell when available so the Unix tool ecosystem works as expected.
|
|
7
|
-
const shell = process.env.SHELL;
|
|
8
|
-
if (shell && /\b(bash|zsh)$/i.test(shell)) {
|
|
9
|
-
const type = shell.endsWith('zsh') ? 'zsh' : 'bash';
|
|
10
|
-
return { executable: shell, args: ['-c'], type };
|
|
11
|
-
}
|
|
12
|
-
return { executable: 'powershell.exe', args: ['-NoProfile', '-Command'], type: 'powershell' };
|
|
13
|
-
}
|
|
14
|
-
const userShell = process.env.SHELL ?? '/bin/bash';
|
|
15
|
-
const type = userShell.endsWith('zsh') ? 'zsh' : 'bash';
|
|
16
|
-
return { executable: userShell, args: ['-c'], type };
|
|
17
|
-
}
|
|
18
1
|
/** Split compound shell commands by pipe/chain operators for permission checking */
|
|
19
2
|
export function splitShellCommands(cmd) {
|
|
20
3
|
// Split by |, &&, ;, || — but not inside quotes
|