@intutic/proxy 1.10.0 → 2.0.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/README.md +16 -2
- package/bin/proxy.js +72 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,21 @@ The proxy processes every request through an ordered plugin chain:
|
|
|
30
30
|
|
|
31
31
|
### Model Selection
|
|
32
32
|
|
|
33
|
-
- **Thompson Sampling bandit**:
|
|
33
|
+
- **Thompson Sampling bandit**: selects a model per task cell from observed
|
|
34
|
+
**availability and latency**.
|
|
35
|
+
|
|
36
|
+
Two corrections to what this said before. It claimed selection was based on
|
|
37
|
+
"historical cost, latency, and quality signals". `RewardSignals`
|
|
38
|
+
(`src/routing/reward.rs:32`) carries `upstream_ok`, `latency_ms`,
|
|
39
|
+
`token_anomaly`, `raw_cost_usd` and `actual_cost_usd` — and the cost pair does
|
|
40
|
+
not move the reward: `cheaper_routed_model_earns_no_bonus` asserts a cheaper
|
|
41
|
+
route earns nothing, deliberately. There is **no quality signal at all**: a
|
|
42
|
+
cheap model returning a confidently wrong answer, quickly, scores a perfect
|
|
43
|
+
1.0.
|
|
44
|
+
|
|
45
|
+
So the router today optimises for *responding*, not for responding *well* or
|
|
46
|
+
*cheaply*. Do not describe it as saving money or preserving quality until
|
|
47
|
+
there is a measurement behind either word.
|
|
34
48
|
|
|
35
49
|
### Network Enforcement
|
|
36
50
|
|
|
@@ -45,7 +59,7 @@ The proxy processes every request through an ordered plugin chain:
|
|
|
45
59
|
- **DLP scanner**: Pattern-based and entropy-based secret detection
|
|
46
60
|
- **Code skeleton extraction**: Uses `tree-sitter` and `syn` for AST-level code analysis
|
|
47
61
|
- **Token counting**: Accurate pre-flight token estimation via `tiktoken-rs`
|
|
48
|
-
- **Telemetry**: OpenTelemetry OTLP export for traces
|
|
62
|
+
- **Telemetry**: OpenTelemetry OTLP export for traces and metrics — a `SpanExporter` and `SdkMeterProvider` ship in `src/main.rs`, with the exported instrument set declared in `src/metrics.rs` (snip compaction histograms, egress counters, policy refusals). Still no logs pipeline (TD-161's resolution covers the metrics half only)
|
|
49
63
|
|
|
50
64
|
## Dependencies
|
|
51
65
|
|
package/bin/proxy.js
CHANGED
|
@@ -18,6 +18,7 @@ import { spawn } from 'child_process';
|
|
|
18
18
|
import path from 'path';
|
|
19
19
|
import fs from 'fs';
|
|
20
20
|
import os from 'os';
|
|
21
|
+
import crypto from 'crypto';
|
|
21
22
|
import { createRequire } from 'module';
|
|
22
23
|
import { fileURLToPath } from 'url';
|
|
23
24
|
|
|
@@ -28,9 +29,15 @@ const { version } = require('../package.json');
|
|
|
28
29
|
const isWindows = process.platform === 'win32';
|
|
29
30
|
const binaryName = isWindows ? 'intutic-proxy.exe' : 'intutic-proxy';
|
|
30
31
|
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Asset names MUST match publish.yml's build-rust-proxy matrix
|
|
34
|
+
* artifact_name — every value returned here has a live release asset
|
|
35
|
+
* verified against it (`gh release view` on v1.6.0 through the current
|
|
36
|
+
* release). `platform`/`arch` are parameters, not read from `process`
|
|
37
|
+
* directly, so proxy.test.mjs can exercise every platform/arch combination
|
|
38
|
+
* without needing to stub global process state.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveAssetName(platform = process.platform, arch = process.arch) {
|
|
34
41
|
if (platform === 'darwin') {
|
|
35
42
|
if (arch === 'arm64') return 'intutic-proxy-darwin-arm64';
|
|
36
43
|
if (arch === 'x64') return 'intutic-proxy-darwin-x64';
|
|
@@ -110,6 +117,57 @@ function pruneLegacyCache() {
|
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
/** `sha256:<hex>` of `buffer` — the same shape publish.yml's checksums.json values use. */
|
|
121
|
+
export function sha256Hex(buffer) {
|
|
122
|
+
return 'sha256:' + crypto.createHash('sha256').update(buffer).digest('hex');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Parses a checksums.json body into a plain name→digest map. Throws on any other shape. */
|
|
126
|
+
export function parseChecksums(text) {
|
|
127
|
+
const data = JSON.parse(text);
|
|
128
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
|
|
129
|
+
throw new Error('checksums.json did not parse to an object');
|
|
130
|
+
}
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Throws unless `buffer`'s digest matches `checksums[assetName]`. A missing entry
|
|
136
|
+
* fails the same as a mismatch — an asset absent from the manifest is exactly as
|
|
137
|
+
* unverifiable as one that fails it, and treating it as "nothing to check" would
|
|
138
|
+
* make the whole guard optional for any release that forgot to list an asset.
|
|
139
|
+
*/
|
|
140
|
+
export function verifyChecksum(buffer, checksums, assetName) {
|
|
141
|
+
const expected = checksums[assetName];
|
|
142
|
+
const actual = sha256Hex(buffer);
|
|
143
|
+
if (!expected) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`checksums.json has no entry for ${assetName} — refusing to install an unverified binary.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (actual !== expected) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Checksum mismatch for ${assetName}:\n` +
|
|
151
|
+
` expected: ${expected}\n` +
|
|
152
|
+
` actual: ${actual}\n` +
|
|
153
|
+
`This could mean a corrupted download or a tampered release asset — refusing to install it. Do not retry blindly.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function fetchChecksums(assetVersion) {
|
|
159
|
+
const url = `https://github.com/intutic/intutic/releases/download/v${assetVersion}/checksums.json`;
|
|
160
|
+
const response = await fetch(url);
|
|
161
|
+
if (!response.ok) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Failed to download checksums.json (HTTP ${response.status} ${response.statusText}).\n` +
|
|
164
|
+
` URL: ${url}\n` +
|
|
165
|
+
`Refusing to install an unverified binary. Build from source instead: cargo build --release --package intutic-proxy`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return parseChecksums(await response.text());
|
|
169
|
+
}
|
|
170
|
+
|
|
113
171
|
async function downloadBinary(destPath) {
|
|
114
172
|
const assetName = resolveAssetName();
|
|
115
173
|
if (!assetName) {
|
|
@@ -132,6 +190,11 @@ async function downloadBinary(destPath) {
|
|
|
132
190
|
}
|
|
133
191
|
|
|
134
192
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
193
|
+
|
|
194
|
+
process.stderr.write(`intutic-proxy: verifying checksum…\n`);
|
|
195
|
+
const checksums = await fetchChecksums(version);
|
|
196
|
+
verifyChecksum(buffer, checksums, assetName);
|
|
197
|
+
|
|
135
198
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
136
199
|
|
|
137
200
|
// Write to a temp path and rename, so a partial download from an interrupted
|
|
@@ -183,4 +246,9 @@ async function main() {
|
|
|
183
246
|
});
|
|
184
247
|
}
|
|
185
248
|
|
|
186
|
-
|
|
249
|
+
// Guarded so a test can `import` this module for its pure functions
|
|
250
|
+
// (sha256Hex, parseChecksums, verifyChecksum) without triggering a real
|
|
251
|
+
// download-and-spawn as a side effect of the import.
|
|
252
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
253
|
+
main();
|
|
254
|
+
}
|