@wingsbutterfly/dsh-rtk 0.1.0
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 +52 -0
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/README.zh.md +211 -0
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/docs/assets/how-it-works.svg +47 -0
- package/docs/verification.md +279 -0
- package/lib/command.d.ts +41 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +137 -0
- package/lib/command.js.map +1 -0
- package/lib/compact/build.d.ts +15 -0
- package/lib/compact/build.d.ts.map +1 -0
- package/lib/compact/build.js +140 -0
- package/lib/compact/build.js.map +1 -0
- package/lib/compact/detect.d.ts +22 -0
- package/lib/compact/detect.d.ts.map +1 -0
- package/lib/compact/detect.js +54 -0
- package/lib/compact/detect.js.map +1 -0
- package/lib/compact/dsh-result.d.ts +49 -0
- package/lib/compact/dsh-result.d.ts.map +1 -0
- package/lib/compact/dsh-result.js +84 -0
- package/lib/compact/dsh-result.js.map +1 -0
- package/lib/compact/git.d.ts +21 -0
- package/lib/compact/git.d.ts.map +1 -0
- package/lib/compact/git.js +197 -0
- package/lib/compact/git.js.map +1 -0
- package/lib/compact/index.d.ts +39 -0
- package/lib/compact/index.d.ts.map +1 -0
- package/lib/compact/index.js +236 -0
- package/lib/compact/index.js.map +1 -0
- package/lib/compact/linter.d.ts +12 -0
- package/lib/compact/linter.d.ts.map +1 -0
- package/lib/compact/linter.js +118 -0
- package/lib/compact/linter.js.map +1 -0
- package/lib/compact/search.d.ts +16 -0
- package/lib/compact/search.d.ts.map +1 -0
- package/lib/compact/search.js +67 -0
- package/lib/compact/search.js.map +1 -0
- package/lib/compact/source.d.ts +22 -0
- package/lib/compact/source.d.ts.map +1 -0
- package/lib/compact/source.js +224 -0
- package/lib/compact/source.js.map +1 -0
- package/lib/compact/test-output.d.ts +12 -0
- package/lib/compact/test-output.d.ts.map +1 -0
- package/lib/compact/test-output.js +168 -0
- package/lib/compact/test-output.js.map +1 -0
- package/lib/compact/text.d.ts +22 -0
- package/lib/compact/text.d.ts.map +1 -0
- package/lib/compact/text.js +87 -0
- package/lib/compact/text.js.map +1 -0
- package/lib/config.d.ts +243 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +183 -0
- package/lib/config.js.map +1 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +405 -0
- package/lib/index.js.map +1 -0
- package/lib/metrics.d.ts +35 -0
- package/lib/metrics.d.ts.map +1 -0
- package/lib/metrics.js +51 -0
- package/lib/metrics.js.map +1 -0
- package/lib/rtk-executable.d.ts +51 -0
- package/lib/rtk-executable.d.ts.map +1 -0
- package/lib/rtk-executable.js +75 -0
- package/lib/rtk-executable.js.map +1 -0
- package/lib/rtk-rewrite.d.ts +88 -0
- package/lib/rtk-rewrite.d.ts.map +1 -0
- package/lib/rtk-rewrite.js +150 -0
- package/lib/rtk-rewrite.js.map +1 -0
- package/lib/runtime-guard.d.ts +31 -0
- package/lib/runtime-guard.d.ts.map +1 -0
- package/lib/runtime-guard.js +32 -0
- package/lib/runtime-guard.js.map +1 -0
- package/package.json +82 -0
- package/scripts/link-dsh.mjs +135 -0
- package/src/command.ts +174 -0
- package/src/compact/build.ts +154 -0
- package/src/compact/detect.ts +54 -0
- package/src/compact/dsh-result.ts +99 -0
- package/src/compact/git.ts +209 -0
- package/src/compact/index.ts +284 -0
- package/src/compact/linter.ts +126 -0
- package/src/compact/search.ts +73 -0
- package/src/compact/source.ts +244 -0
- package/src/compact/test-output.ts +184 -0
- package/src/compact/text.ts +86 -0
- package/src/config.ts +263 -0
- package/src/index.ts +431 -0
- package/src/metrics.ts +84 -0
- package/src/rtk-executable.ts +117 -0
- package/src/rtk-rewrite.ts +179 -0
- package/src/runtime-guard.ts +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtk-executable.js","sourceRoot":"","sources":["../src/rtk-executable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** One `rtk rewrite` outcome. */
|
|
2
|
+
export interface RtkRewriteResult {
|
|
3
|
+
changed: boolean;
|
|
4
|
+
originalCommand: string;
|
|
5
|
+
rewrittenCommand: string;
|
|
6
|
+
exitCode: number;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Injected runner so tests never spawn a process. */
|
|
10
|
+
export type RtkRunner = (command: string, args: readonly string[], options?: {
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
code: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}>;
|
|
18
|
+
/** Options for {@link resolveRtkRewrite}. */
|
|
19
|
+
export interface RtkRewriteOptions {
|
|
20
|
+
runner: RtkRunner;
|
|
21
|
+
executable: string;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Split leading `KEY=VALUE` assignments off a command line.
|
|
27
|
+
*
|
|
28
|
+
* Shell treats a run of assignments before the command word as that command's
|
|
29
|
+
* environment, so `FOO=1 ls` invokes `ls`, not `FOO=1`. Detection must look at
|
|
30
|
+
* the command word; passing the whole line would classify `FOO=1 git status`
|
|
31
|
+
* as unsupported.
|
|
32
|
+
*
|
|
33
|
+
* @param command - raw command line.
|
|
34
|
+
* @returns the assignment prefix (verbatim, including trailing space) and the command word onward.
|
|
35
|
+
*/
|
|
36
|
+
export declare function splitLeadingEnvAssignments(command: string): {
|
|
37
|
+
envPrefix: string;
|
|
38
|
+
command: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Whether a command (ignoring a leading assignment run) already invokes rtk.
|
|
42
|
+
*
|
|
43
|
+
* Rewriting an rtk command would produce `rtk rtk …`, and the guard also saves
|
|
44
|
+
* a process spawn on every already-optimized call.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isAlreadyRtkCommand(command: string): boolean;
|
|
47
|
+
/** Which shell dialect an assignment prefix must be written in. */
|
|
48
|
+
export type ShellKind = 'posix' | 'powershell';
|
|
49
|
+
/**
|
|
50
|
+
* Prefix a command with an isolated `RTK_DB_PATH` so rtk's usage history lands
|
|
51
|
+
* in a scratch directory instead of the working tree.
|
|
52
|
+
*
|
|
53
|
+
* The two shells need different syntax: `export NAME='value'` is a syntax error
|
|
54
|
+
* in PowerShell, which spells the same thing `$env:NAME = 'value'`. Both the
|
|
55
|
+
* harness's `pwsh` tool and its `bash` tool are rewrite targets, so the caller
|
|
56
|
+
* passes the dialect rather than assuming one.
|
|
57
|
+
*
|
|
58
|
+
* The assignment is skipped when the command already sets it or the ambient
|
|
59
|
+
* environment provides one, so an explicit choice is never overridden.
|
|
60
|
+
*
|
|
61
|
+
* @param command - the rewritten command.
|
|
62
|
+
* @param historyDbPath - absolute path for this deployment's rtk history database.
|
|
63
|
+
* @param ambientValue - value currently in the environment, if any.
|
|
64
|
+
* @param shell - the dialect of the command being prefixed.
|
|
65
|
+
* @returns the command to run, prefixed only when isolation applies.
|
|
66
|
+
*/
|
|
67
|
+
export declare function applyRtkHistoryScope(command: string, historyDbPath: string, ambientValue: string | undefined, shell?: ShellKind): string;
|
|
68
|
+
/**
|
|
69
|
+
* Ask the installed rtk binary how it would rewrite a command.
|
|
70
|
+
*
|
|
71
|
+
* rtk owns the rewrite rules; this plugin deliberately carries no duplicate
|
|
72
|
+
* table. The exit-code contract is rtk's:
|
|
73
|
+
*
|
|
74
|
+
* | code | meaning |
|
|
75
|
+
* |------|---------|
|
|
76
|
+
* | 0, 3 | stdout is the rewritten command |
|
|
77
|
+
* | 1 | no rtk equivalent — run the command unchanged |
|
|
78
|
+
* | 2 | rtk refused the rewrite; stderr explains why |
|
|
79
|
+
*
|
|
80
|
+
* A non-zero code is an ordinary outcome, never a rejection: the caller always
|
|
81
|
+
* has the original command to fall back on.
|
|
82
|
+
*
|
|
83
|
+
* @param command - the raw command the model asked to run.
|
|
84
|
+
* @param options - runner, executable, deadline, and caller cancellation.
|
|
85
|
+
* @returns the decision, including the fallback command when nothing changed.
|
|
86
|
+
*/
|
|
87
|
+
export declare function resolveRtkRewrite(command: string, options: RtkRewriteOptions): Promise<RtkRewriteResult>;
|
|
88
|
+
//# sourceMappingURL=rtk-rewrite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtk-rewrite.d.ts","sourceRoot":"","sources":["../src/rtk-rewrite.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,CACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KACnD,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE9D,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6BlG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG5D;AAID,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,YAAY,CAAA;AAK9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,KAAK,GAAE,SAAmB,GACzB,MAAM,CAcR;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiC9G"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
2
|
+
/**
|
|
3
|
+
* Split leading `KEY=VALUE` assignments off a command line.
|
|
4
|
+
*
|
|
5
|
+
* Shell treats a run of assignments before the command word as that command's
|
|
6
|
+
* environment, so `FOO=1 ls` invokes `ls`, not `FOO=1`. Detection must look at
|
|
7
|
+
* the command word; passing the whole line would classify `FOO=1 git status`
|
|
8
|
+
* as unsupported.
|
|
9
|
+
*
|
|
10
|
+
* @param command - raw command line.
|
|
11
|
+
* @returns the assignment prefix (verbatim, including trailing space) and the command word onward.
|
|
12
|
+
*/
|
|
13
|
+
export function splitLeadingEnvAssignments(command) {
|
|
14
|
+
let index = 0;
|
|
15
|
+
let prefixEnd = 0;
|
|
16
|
+
while (index < command.length) {
|
|
17
|
+
const rest = command.slice(index);
|
|
18
|
+
const match = ENV_ASSIGNMENT.exec(rest);
|
|
19
|
+
if (match === null)
|
|
20
|
+
break;
|
|
21
|
+
let cursor = index + match[0].length;
|
|
22
|
+
if (cursor >= command.length)
|
|
23
|
+
return { envPrefix: '', command };
|
|
24
|
+
const first = command[cursor];
|
|
25
|
+
if (first === '"' || first === "'") {
|
|
26
|
+
cursor += 1;
|
|
27
|
+
while (cursor < command.length && command[cursor] !== first)
|
|
28
|
+
cursor += 1;
|
|
29
|
+
if (cursor >= command.length)
|
|
30
|
+
return { envPrefix: '', command };
|
|
31
|
+
cursor += 1;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
while (cursor < command.length && !/\s/.test(command[cursor]))
|
|
35
|
+
cursor += 1;
|
|
36
|
+
}
|
|
37
|
+
if (cursor < command.length && !/\s/.test(command[cursor]))
|
|
38
|
+
return { envPrefix: '', command };
|
|
39
|
+
while (cursor < command.length && /\s/.test(command[cursor]))
|
|
40
|
+
cursor += 1;
|
|
41
|
+
prefixEnd = cursor;
|
|
42
|
+
index = cursor;
|
|
43
|
+
}
|
|
44
|
+
return { envPrefix: command.slice(0, prefixEnd), command: command.slice(prefixEnd) };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether a command (ignoring a leading assignment run) already invokes rtk.
|
|
48
|
+
*
|
|
49
|
+
* Rewriting an rtk command would produce `rtk rtk …`, and the guard also saves
|
|
50
|
+
* a process spawn on every already-optimized call.
|
|
51
|
+
*/
|
|
52
|
+
export function isAlreadyRtkCommand(command) {
|
|
53
|
+
const effective = splitLeadingEnvAssignments(command.trimStart()).command.trimStart();
|
|
54
|
+
return effective === 'rtk' || effective.startsWith('rtk ');
|
|
55
|
+
}
|
|
56
|
+
const RTK_HISTORY_ENV = 'RTK_DB_PATH';
|
|
57
|
+
/// Matches a PowerShell environment assignment already present at the head.
|
|
58
|
+
const PWSH_ASSIGNMENT = /^\$env:RTK_DB_PATH\s*=/;
|
|
59
|
+
/**
|
|
60
|
+
* Prefix a command with an isolated `RTK_DB_PATH` so rtk's usage history lands
|
|
61
|
+
* in a scratch directory instead of the working tree.
|
|
62
|
+
*
|
|
63
|
+
* The two shells need different syntax: `export NAME='value'` is a syntax error
|
|
64
|
+
* in PowerShell, which spells the same thing `$env:NAME = 'value'`. Both the
|
|
65
|
+
* harness's `pwsh` tool and its `bash` tool are rewrite targets, so the caller
|
|
66
|
+
* passes the dialect rather than assuming one.
|
|
67
|
+
*
|
|
68
|
+
* The assignment is skipped when the command already sets it or the ambient
|
|
69
|
+
* environment provides one, so an explicit choice is never overridden.
|
|
70
|
+
*
|
|
71
|
+
* @param command - the rewritten command.
|
|
72
|
+
* @param historyDbPath - absolute path for this deployment's rtk history database.
|
|
73
|
+
* @param ambientValue - value currently in the environment, if any.
|
|
74
|
+
* @param shell - the dialect of the command being prefixed.
|
|
75
|
+
* @returns the command to run, prefixed only when isolation applies.
|
|
76
|
+
*/
|
|
77
|
+
export function applyRtkHistoryScope(command, historyDbPath, ambientValue, shell = 'posix') {
|
|
78
|
+
if (!command.trim() || !historyDbPath.trim())
|
|
79
|
+
return command;
|
|
80
|
+
if (ambientValue !== undefined && ambientValue.trim())
|
|
81
|
+
return command;
|
|
82
|
+
const trimmed = command.trimStart();
|
|
83
|
+
if (shell === 'powershell') {
|
|
84
|
+
if (PWSH_ASSIGNMENT.test(trimmed))
|
|
85
|
+
return command;
|
|
86
|
+
// PowerShell escapes a literal quote by doubling it.
|
|
87
|
+
return `$env:${RTK_HISTORY_ENV} = '${historyDbPath.replace(/'/g, "''")}'; ${command}`;
|
|
88
|
+
}
|
|
89
|
+
if (splitLeadingEnvAssignments(trimmed).envPrefix.includes(`${RTK_HISTORY_ENV}=`))
|
|
90
|
+
return command;
|
|
91
|
+
return `export ${RTK_HISTORY_ENV}='${historyDbPath.replace(/'/g, "'\\''")}'; ${command}`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Ask the installed rtk binary how it would rewrite a command.
|
|
95
|
+
*
|
|
96
|
+
* rtk owns the rewrite rules; this plugin deliberately carries no duplicate
|
|
97
|
+
* table. The exit-code contract is rtk's:
|
|
98
|
+
*
|
|
99
|
+
* | code | meaning |
|
|
100
|
+
* |------|---------|
|
|
101
|
+
* | 0, 3 | stdout is the rewritten command |
|
|
102
|
+
* | 1 | no rtk equivalent — run the command unchanged |
|
|
103
|
+
* | 2 | rtk refused the rewrite; stderr explains why |
|
|
104
|
+
*
|
|
105
|
+
* A non-zero code is an ordinary outcome, never a rejection: the caller always
|
|
106
|
+
* has the original command to fall back on.
|
|
107
|
+
*
|
|
108
|
+
* @param command - the raw command the model asked to run.
|
|
109
|
+
* @param options - runner, executable, deadline, and caller cancellation.
|
|
110
|
+
* @returns the decision, including the fallback command when nothing changed.
|
|
111
|
+
*/
|
|
112
|
+
export async function resolveRtkRewrite(command, options) {
|
|
113
|
+
const unchanged = (exitCode, error) => ({
|
|
114
|
+
changed: false,
|
|
115
|
+
originalCommand: command,
|
|
116
|
+
rewrittenCommand: command,
|
|
117
|
+
exitCode,
|
|
118
|
+
...(error === undefined ? {} : { error }),
|
|
119
|
+
});
|
|
120
|
+
if (!command || !command.trim())
|
|
121
|
+
return unchanged(1);
|
|
122
|
+
if (isAlreadyRtkCommand(command))
|
|
123
|
+
return unchanged(1);
|
|
124
|
+
let result;
|
|
125
|
+
try {
|
|
126
|
+
const runOptions = {};
|
|
127
|
+
if (options.timeoutMs !== undefined)
|
|
128
|
+
runOptions.timeoutMs = options.timeoutMs;
|
|
129
|
+
if (options.signal !== undefined)
|
|
130
|
+
runOptions.signal = options.signal;
|
|
131
|
+
result = await options.runner(options.executable, ['rewrite', command], runOptions);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
return unchanged(-1, error instanceof Error ? error.message : String(error));
|
|
135
|
+
}
|
|
136
|
+
if (result.code === 1)
|
|
137
|
+
return unchanged(1);
|
|
138
|
+
if (result.code === 2)
|
|
139
|
+
return unchanged(2, result.stderr.trim() || 'rtk refused the rewrite');
|
|
140
|
+
if (result.code === 0 || result.code === 3) {
|
|
141
|
+
const rewritten = result.stdout.trim();
|
|
142
|
+
if (!rewritten)
|
|
143
|
+
return unchanged(result.code, 'rtk returned empty output');
|
|
144
|
+
if (rewritten === command)
|
|
145
|
+
return unchanged(result.code);
|
|
146
|
+
return { changed: true, originalCommand: command, rewrittenCommand: rewritten, exitCode: result.code };
|
|
147
|
+
}
|
|
148
|
+
return unchanged(result.code, `unexpected rtk exit code ${result.code}`);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=rtk-rewrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtk-rewrite.js","sourceRoot":"","sources":["../src/rtk-rewrite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RtkConfig } from './config.js';
|
|
2
|
+
/** What the last probe found. */
|
|
3
|
+
export interface RtkRuntimeStatus {
|
|
4
|
+
rtkAvailable: boolean;
|
|
5
|
+
lastCheckedAt?: number;
|
|
6
|
+
rtkExecutablePath?: string;
|
|
7
|
+
rtkExecutableCommand?: string;
|
|
8
|
+
rtkExecutableResolver?: string;
|
|
9
|
+
rtkExecutableResolutionWarning?: string;
|
|
10
|
+
lastError?: string;
|
|
11
|
+
}
|
|
12
|
+
/** How long a probe result stays authoritative before it is refreshed. */
|
|
13
|
+
export declare const RUNTIME_STATUS_TTL_MS = 30000;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a missing rtk binary must be established before handling a command.
|
|
16
|
+
*
|
|
17
|
+
* Only rewrite mode consults availability: `suggest` mode reports what rtk
|
|
18
|
+
* *would* do and never gates execution on it, and compaction is a pure text
|
|
19
|
+
* transform that needs no binary at all.
|
|
20
|
+
*/
|
|
21
|
+
export declare function shouldRequireRtkAvailability(config: RtkConfig): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether command rewriting must stand down for this call.
|
|
24
|
+
*
|
|
25
|
+
* With `guardWhenRtkMissing` on, a call that cannot prove rtk is available
|
|
26
|
+
* runs unchanged — the user's command is never blocked by an absent optimizer.
|
|
27
|
+
*/
|
|
28
|
+
export declare function shouldSkipRewrite(config: RtkConfig, status: RtkRuntimeStatus): boolean;
|
|
29
|
+
/** Whether the cached probe is stale and must be refreshed before use. */
|
|
30
|
+
export declare function isStatusStale(status: RtkRuntimeStatus, now: number, ttlMs?: number): boolean;
|
|
31
|
+
//# sourceMappingURL=runtime-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-guard.d.ts","sourceRoot":"","sources":["../src/runtime-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,iCAAiC;AACjC,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,8BAA8B,CAAC,EAAE,MAAM,CAAA;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAE3C;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAItF;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,SAAwB,GAAG,OAAO,CAG3G"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** How long a probe result stays authoritative before it is refreshed. */
|
|
2
|
+
export const RUNTIME_STATUS_TTL_MS = 30_000;
|
|
3
|
+
/**
|
|
4
|
+
* Whether a missing rtk binary must be established before handling a command.
|
|
5
|
+
*
|
|
6
|
+
* Only rewrite mode consults availability: `suggest` mode reports what rtk
|
|
7
|
+
* *would* do and never gates execution on it, and compaction is a pure text
|
|
8
|
+
* transform that needs no binary at all.
|
|
9
|
+
*/
|
|
10
|
+
export function shouldRequireRtkAvailability(config) {
|
|
11
|
+
return config.enabled && config.mode === 'rewrite';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Whether command rewriting must stand down for this call.
|
|
15
|
+
*
|
|
16
|
+
* With `guardWhenRtkMissing` on, a call that cannot prove rtk is available
|
|
17
|
+
* runs unchanged — the user's command is never blocked by an absent optimizer.
|
|
18
|
+
*/
|
|
19
|
+
export function shouldSkipRewrite(config, status) {
|
|
20
|
+
if (!config.enabled)
|
|
21
|
+
return true;
|
|
22
|
+
if (!config.guardWhenRtkMissing)
|
|
23
|
+
return false;
|
|
24
|
+
return !status.rtkAvailable;
|
|
25
|
+
}
|
|
26
|
+
/** Whether the cached probe is stale and must be refreshed before use. */
|
|
27
|
+
export function isStatusStale(status, now, ttlMs = RUNTIME_STATUS_TTL_MS) {
|
|
28
|
+
if (status.lastCheckedAt === undefined)
|
|
29
|
+
return true;
|
|
30
|
+
return now - status.lastCheckedAt > ttlMs;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=runtime-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-guard.js","sourceRoot":"","sources":["../src/runtime-guard.ts"],"names":[],"mappings":"AAaA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAAiB;IAC5D,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAA;AACpD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,MAAwB;IAC3E,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,CAAC,MAAM,CAAC,mBAAmB;QAAE,OAAO,KAAK,CAAA;IAC7C,OAAO,CAAC,MAAM,CAAC,YAAY,CAAA;AAC7B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,MAAwB,EAAE,GAAW,EAAE,KAAK,GAAG,qBAAqB;IAChG,IAAI,MAAM,CAAC,aAAa,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACnD,OAAO,GAAG,GAAG,MAAM,CAAC,aAAa,GAAG,KAAK,CAAA;AAC3C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wingsbutterfly/dsh-rtk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "RTK command rewriting and tool-output compaction for the DeepSeek Harness: rewrites bash commands to their rtk equivalents and compacts noisy tool output before it reaches the model.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib",
|
|
17
|
+
"src",
|
|
18
|
+
"docs",
|
|
19
|
+
"scripts/link-dsh.mjs",
|
|
20
|
+
"README.md",
|
|
21
|
+
"README.zh.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"THIRD_PARTY_NOTICES.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "wings1848",
|
|
28
|
+
"homepage": "https://github.com/wings1848/dsh-rtk#readme",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/wings1848/dsh-rtk.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/wings1848/dsh-rtk/issues"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"deepseek",
|
|
38
|
+
"harness",
|
|
39
|
+
"dsh",
|
|
40
|
+
"dsh-plugin",
|
|
41
|
+
"rtk",
|
|
42
|
+
"token-optimization",
|
|
43
|
+
"output-compaction",
|
|
44
|
+
"command-rewrite"
|
|
45
|
+
],
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.18.0"
|
|
48
|
+
},
|
|
49
|
+
"packageManager": "pnpm@12.3.4",
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc -p tsconfig.json",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"link-dsh": "node scripts/link-dsh.mjs",
|
|
54
|
+
"test": "node --run build && node scripts/link-dsh.mjs && node --test test/*.test.ts",
|
|
55
|
+
"check": "node --run typecheck && node --run test",
|
|
56
|
+
"prepack": "node --run build"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
60
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
|
|
65
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
69
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
|
|
74
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
75
|
+
"@types/node": "^22.0.0",
|
|
76
|
+
"typescript": "^5.9.0"
|
|
77
|
+
},
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"registry": "https://registry.npmjs.org/",
|
|
80
|
+
"access": "public"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Point the harness packages this plugin links against at the *running* DSH
|
|
4
|
+
* installation.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: `@deepseek-ai/dsh-*` are peer dependencies, and the harness
|
|
7
|
+
* supplies them at plugin load time from its own install. If npm also materializes
|
|
8
|
+
* private copies under this package's `node_modules`, the plugin would build tool
|
|
9
|
+
* definitions (or read settings) through a *different* instance than the runtime
|
|
10
|
+
* that owns them — a class-identity mismatch that fails confusingly, or silently
|
|
11
|
+
* drifts a release behind (`0.1.5-rc.2` on the registry versus the `0.1.5-rc.1`
|
|
12
|
+
* this harness is actually running).
|
|
13
|
+
*
|
|
14
|
+
* So: symlink the peer packages into `node_modules` from the harness install, and
|
|
15
|
+
* let every other dependency resolve normally. Run by the `test` script, and on demand via `pnpm run link-dsh`.
|
|
16
|
+
*
|
|
17
|
+
* Every peer is located independently. They are siblings under a global bun
|
|
18
|
+
* install, but under pnpm each one lives in its own `node_modules/.pnpm/<pkg>@<ver>`
|
|
19
|
+
* directory — assuming a shared root finds the first package and misses the rest.
|
|
20
|
+
*
|
|
21
|
+
* Usage: node scripts/link-dsh.mjs [--check]
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { existsSync, lstatSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from 'node:fs'
|
|
25
|
+
import { createRequire } from 'node:module'
|
|
26
|
+
import { dirname, join } from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
|
|
29
|
+
/** Harness packages this plugin imports at runtime. */
|
|
30
|
+
const PEER_PACKAGES = [
|
|
31
|
+
'@deepseek-ai/cordis',
|
|
32
|
+
'@deepseek-ai/dsh-tools',
|
|
33
|
+
'@deepseek-ai/dsh-llm',
|
|
34
|
+
'@deepseek-ai/dsh-commands',
|
|
35
|
+
'@deepseek-ai/dsh-settings',
|
|
36
|
+
'@deepseek-ai/dsh-system-prompt',
|
|
37
|
+
'@deepseek-ai/schemastery',
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
41
|
+
const checkOnly = process.argv.includes('--check')
|
|
42
|
+
|
|
43
|
+
/** Candidate roots for a global DSH installation, most specific first. */
|
|
44
|
+
function installRoots() {
|
|
45
|
+
const roots = []
|
|
46
|
+
if (process.env['DSH_INSTALL_ROOT'] !== undefined) roots.push(process.env['DSH_INSTALL_ROOT'])
|
|
47
|
+
if (process.env['BUN_INSTALL'] !== undefined) {
|
|
48
|
+
roots.push(join(process.env['BUN_INSTALL'], 'install', 'global', 'node_modules'))
|
|
49
|
+
}
|
|
50
|
+
roots.push(join(process.env['HOME'] ?? '', '.bun', 'install', 'global', 'node_modules'))
|
|
51
|
+
return roots.filter(root => root !== '')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Locate one peer package's directory.
|
|
56
|
+
*
|
|
57
|
+
* Tries the global install roots first, then falls back to whatever this
|
|
58
|
+
* process can resolve — which covers a profile-local install, and a CI checkout
|
|
59
|
+
* where the only copies are the registry ones pnpm installed. Resolution is per
|
|
60
|
+
* package because pnpm does not place them side by side.
|
|
61
|
+
*
|
|
62
|
+
* @param name - The package name to locate.
|
|
63
|
+
* @returns Its directory, or undefined when nothing provides it.
|
|
64
|
+
*/
|
|
65
|
+
function findPeer(name) {
|
|
66
|
+
for (const root of installRoots()) {
|
|
67
|
+
const candidate = join(root, name)
|
|
68
|
+
if (existsSync(join(candidate, 'package.json'))) return candidate
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const require = createRequire(join(packageRoot, 'package.json'))
|
|
72
|
+
return dirname(require.resolve(`${name}/package.json`))
|
|
73
|
+
} catch {
|
|
74
|
+
return undefined
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let linked = 0
|
|
79
|
+
let alreadyCorrect = 0
|
|
80
|
+
const problems = []
|
|
81
|
+
const sources = new Set()
|
|
82
|
+
|
|
83
|
+
for (const name of PEER_PACKAGES) {
|
|
84
|
+
const target = findPeer(name)
|
|
85
|
+
if (target === undefined) {
|
|
86
|
+
problems.push(`${name}: no installation provides it`)
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
sources.add(dirname(target))
|
|
90
|
+
|
|
91
|
+
const link = join(packageRoot, 'node_modules', name)
|
|
92
|
+
if (existsSync(link) || isSymlink(link)) {
|
|
93
|
+
if (isSymlink(link) && readlinkSync(link) === target) {
|
|
94
|
+
alreadyCorrect += 1
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
if (checkOnly) {
|
|
98
|
+
problems.push(`${name}: node_modules copy is not the harness instance`)
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
rmSync(link, { recursive: true, force: true })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (checkOnly) {
|
|
105
|
+
problems.push(`${name}: not linked`)
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
mkdirSync(dirname(link), { recursive: true })
|
|
110
|
+
symlinkSync(target, link, 'dir')
|
|
111
|
+
linked += 1
|
|
112
|
+
console.log(`link-dsh: ${name} -> ${target}`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (problems.length > 0) {
|
|
116
|
+
for (const problem of problems) console.error(`link-dsh: ${problem}`)
|
|
117
|
+
// Without a harness there is nothing to link to, and the registry copies pnpm
|
|
118
|
+
// installed are a working substitute — which is what CI relies on. Only
|
|
119
|
+
// `--check` treats that as a failure.
|
|
120
|
+
if (checkOnly) process.exit(1)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!checkOnly) {
|
|
124
|
+
const where = sources.size === 1 ? [...sources][0] : `${sources.size} locations`
|
|
125
|
+
console.log(`link-dsh: ${linked} linked, ${alreadyCorrect} already correct (from ${where})`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Whether a path is a symbolic link, broken or not. */
|
|
129
|
+
function isSymlink(path) {
|
|
130
|
+
try {
|
|
131
|
+
return lstatSync(path).isSymbolicLink()
|
|
132
|
+
} catch {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
}
|