@alex-dbs/coderuler 0.0.0-stage → 1.3.1

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.
Files changed (121) hide show
  1. package/LICENSE +175 -0
  2. package/README.md +218 -2
  3. package/dist/adapter/cli/commands/check.d.ts +3 -0
  4. package/dist/adapter/cli/commands/check.js +93 -0
  5. package/dist/adapter/cli/commands/deps.d.ts +3 -0
  6. package/dist/adapter/cli/commands/deps.js +4 -0
  7. package/dist/adapter/cli/commands/inspect.d.ts +3 -0
  8. package/dist/adapter/cli/commands/inspect.js +59 -0
  9. package/dist/adapter/cli/commands/rules.d.ts +10 -0
  10. package/dist/adapter/cli/commands/rules.js +17 -0
  11. package/dist/adapter/cli/commands/skill.d.ts +4 -0
  12. package/dist/adapter/cli/commands/skill.js +7 -0
  13. package/dist/adapter/cli/commands/test.d.ts +3 -0
  14. package/dist/adapter/cli/commands/test.js +78 -0
  15. package/dist/adapter/cli/commands/update.d.ts +3 -0
  16. package/dist/adapter/cli/commands/update.js +7 -0
  17. package/dist/adapter/cli/commands/validate.d.ts +3 -0
  18. package/dist/adapter/cli/commands/validate.js +12 -0
  19. package/dist/adapter/cli/exit.d.ts +7 -0
  20. package/dist/adapter/cli/exit.js +10 -0
  21. package/dist/adapter/cli/inspect-json.d.ts +9 -0
  22. package/dist/adapter/cli/inspect-json.js +160 -0
  23. package/dist/adapter/cli/inspect-usage.d.ts +9 -0
  24. package/dist/adapter/cli/inspect-usage.js +14 -0
  25. package/dist/adapter/cli/program.d.ts +4 -0
  26. package/dist/adapter/cli/program.js +216 -0
  27. package/dist/adapter/cli/progress.d.ts +22 -0
  28. package/dist/adapter/cli/progress.js +79 -0
  29. package/dist/adapter/cli/reports.d.ts +47 -0
  30. package/dist/adapter/cli/reports.js +735 -0
  31. package/dist/adapter/cli/self-update.d.ts +1 -0
  32. package/dist/adapter/cli/self-update.js +89 -0
  33. package/dist/adapter/cli/services.d.ts +101 -0
  34. package/dist/adapter/cli/services.js +1 -0
  35. package/dist/adapter/cli/text.d.ts +5 -0
  36. package/dist/adapter/cli/text.js +36 -0
  37. package/dist/adapter/cli/version.d.ts +1 -0
  38. package/dist/adapter/cli/version.js +3 -0
  39. package/dist/adapter/decision/jev.d.ts +11 -0
  40. package/dist/adapter/decision/jev.js +138 -0
  41. package/dist/adapter/filesystem/dependencies.d.ts +26 -0
  42. package/dist/adapter/filesystem/dependencies.js +577 -0
  43. package/dist/adapter/filesystem/dependency-installation.d.ts +11 -0
  44. package/dist/adapter/filesystem/dependency-installation.js +373 -0
  45. package/dist/adapter/filesystem/project.d.ts +14 -0
  46. package/dist/adapter/filesystem/project.js +387 -0
  47. package/dist/adapter/filesystem/rule-resolution.d.ts +4 -0
  48. package/dist/adapter/filesystem/rule-resolution.js +516 -0
  49. package/dist/adapter/filesystem/rule-test-worker.d.ts +17 -0
  50. package/dist/adapter/filesystem/rule-test-worker.js +47 -0
  51. package/dist/adapter/filesystem/rule-tests.d.ts +3 -0
  52. package/dist/adapter/filesystem/rule-tests.js +264 -0
  53. package/dist/adapter/filesystem/rules.d.ts +13 -0
  54. package/dist/adapter/filesystem/rules.js +242 -0
  55. package/dist/adapter/git/changes.d.ts +1 -0
  56. package/dist/adapter/git/changes.js +54 -0
  57. package/dist/adapter/git/environment.d.ts +1 -0
  58. package/dist/adapter/git/environment.js +10 -0
  59. package/dist/adapter/github/api.d.ts +5 -0
  60. package/dist/adapter/github/api.js +40 -0
  61. package/dist/adapter/github/comments.d.ts +3 -0
  62. package/dist/adapter/github/comments.js +235 -0
  63. package/dist/adapter/github/pull-request.d.ts +16 -0
  64. package/dist/adapter/github/pull-request.js +139 -0
  65. package/dist/adapter/scope/astgrep-languages.d.ts +5 -0
  66. package/dist/adapter/scope/astgrep-languages.js +119 -0
  67. package/dist/adapter/scope/astgrep.d.ts +14 -0
  68. package/dist/adapter/scope/astgrep.js +183 -0
  69. package/dist/adapter/scope/csv.d.ts +4 -0
  70. package/dist/adapter/scope/csv.js +158 -0
  71. package/dist/adapter/scope/file.d.ts +2 -0
  72. package/dist/adapter/scope/file.js +4 -0
  73. package/dist/adapter/scope/json.d.ts +7 -0
  74. package/dist/adapter/scope/json.js +132 -0
  75. package/dist/adapter/scope/lines.d.ts +10 -0
  76. package/dist/adapter/scope/lines.js +42 -0
  77. package/dist/adapter/scope/protobuf-options.d.ts +29 -0
  78. package/dist/adapter/scope/protobuf-options.js +64 -0
  79. package/dist/adapter/scope/protobuf-parser.d.ts +30 -0
  80. package/dist/adapter/scope/protobuf-parser.js +772 -0
  81. package/dist/adapter/scope/protobuf.d.ts +5 -0
  82. package/dist/adapter/scope/protobuf.js +53 -0
  83. package/dist/adapter/scope/structured.d.ts +10 -0
  84. package/dist/adapter/scope/structured.js +84 -0
  85. package/dist/adapter/scope/yaml.d.ts +7 -0
  86. package/dist/adapter/scope/yaml.js +224 -0
  87. package/dist/adapter/skills/cli.d.ts +10 -0
  88. package/dist/adapter/skills/cli.js +77 -0
  89. package/dist/app/concurrency.d.ts +1 -0
  90. package/dist/app/concurrency.js +44 -0
  91. package/dist/app/evaluators.d.ts +14 -0
  92. package/dist/app/evaluators.js +175 -0
  93. package/dist/app/execute.d.ts +2 -0
  94. package/dist/app/execute.js +427 -0
  95. package/dist/app/jsonpath.d.ts +1 -0
  96. package/dist/app/jsonpath.js +8 -0
  97. package/dist/app/ports.d.ts +165 -0
  98. package/dist/app/ports.js +1 -0
  99. package/dist/app/rule-tests.d.ts +20 -0
  100. package/dist/app/rule-tests.js +11 -0
  101. package/dist/app/rule.d.ts +121 -0
  102. package/dist/app/rule.js +24 -0
  103. package/dist/app/selection.d.ts +2 -0
  104. package/dist/app/selection.js +23 -0
  105. package/dist/app/usage.d.ts +2 -0
  106. package/dist/app/usage.js +13 -0
  107. package/dist/app/validate.d.ts +11 -0
  108. package/dist/app/validate.js +415 -0
  109. package/dist/app/violation-comments.d.ts +10 -0
  110. package/dist/app/violation-comments.js +1 -0
  111. package/dist/cli.d.ts +2 -0
  112. package/dist/cli.js +3 -0
  113. package/dist/index.d.ts +22 -0
  114. package/dist/index.js +11 -0
  115. package/dist/infra/runtime.d.ts +3 -0
  116. package/dist/infra/runtime.js +168 -0
  117. package/dist/infra/settings.d.ts +5 -0
  118. package/dist/infra/settings.js +91 -0
  119. package/package.json +86 -4
  120. package/skills/coderuler-new-rule/SKILL.md +141 -0
  121. package/skills/coderuler-new-rule/reference.md +447 -0
@@ -0,0 +1 @@
1
+ export declare function createToolUpdater(cwd: string, env: NodeJS.ProcessEnv): () => Promise<number>;
@@ -0,0 +1,89 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { constants as fsConstants } from 'node:fs';
3
+ import { access, realpath } from 'node:fs/promises';
4
+ import { constants as osConstants } from 'node:os';
5
+ import { basename, delimiter, dirname, join, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ const PACKAGE_NAME = '@alex-dbs/coderuler';
8
+ const PACKAGE_ROOT = fileURLToPath(new URL('../../../', import.meta.url));
9
+ const UPDATE_INSTRUCTIONS = 'Self-update requires the active global npm installation. For project-local installs, run npm install --save-dev @alex-dbs/coderuler@latest. Otherwise use your package manager or reinstall with npm install --global @alex-dbs/coderuler@latest.';
10
+ async function findNpm(env) {
11
+ const path = process.platform === 'win32'
12
+ ? Object.entries(env).find(([name]) => name.toLowerCase() === 'path')?.[1]
13
+ : env.PATH;
14
+ for (const directory of (path ?? '').split(delimiter)) {
15
+ if (directory === '')
16
+ continue;
17
+ const shim = join(directory, process.platform === 'win32' ? 'npm.cmd' : 'npm');
18
+ try {
19
+ await access(shim, process.platform === 'win32' ? fsConstants.F_OK : fsConstants.X_OK);
20
+ const executable = await realpath(shim);
21
+ const cli = process.platform === 'win32'
22
+ ? join(dirname(executable), 'node_modules', 'npm', 'bin', 'npm-cli.js')
23
+ : executable;
24
+ if (basename(cli) !== 'npm-cli.js') {
25
+ throw new Error('The active npm executable is not a supported npm installation; update CodeRuler using your package manager.');
26
+ }
27
+ await access(cli, fsConstants.R_OK);
28
+ return cli;
29
+ }
30
+ catch (error) {
31
+ if (!['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
32
+ throw error;
33
+ }
34
+ }
35
+ throw new Error('npm was not found on PATH; install npm or update CodeRuler using your package manager.');
36
+ }
37
+ function runNpm(executable, args, cwd, env, capture) {
38
+ const { promise, resolve: settle, reject } = Promise.withResolvers();
39
+ const child = spawn(process.execPath, [executable, ...args], {
40
+ cwd,
41
+ env,
42
+ stdio: capture ? ['inherit', 'pipe', 'inherit'] : 'inherit',
43
+ });
44
+ let stdout = '';
45
+ child.stdout?.setEncoding('utf8');
46
+ child.stdout?.on('data', (text) => { stdout += text; });
47
+ child.once('error', reject);
48
+ child.once('close', (code, signal) => {
49
+ settle({ status: code ?? 128 + (signal === null ? 0 : osConstants.signals[signal]), stdout });
50
+ });
51
+ return promise;
52
+ }
53
+ export function createToolUpdater(cwd, env) {
54
+ return async () => {
55
+ const packageRoot = await realpath(PACKAGE_ROOT);
56
+ if (basename(dirname(packageRoot)) !== '@alex-dbs' || basename(dirname(dirname(packageRoot))) !== 'node_modules') {
57
+ throw new Error(UPDATE_INSTRUCTIONS);
58
+ }
59
+ const executable = await findNpm(env);
60
+ const root = await runNpm(executable, ['root', '--global'], cwd, env, true);
61
+ if (root.status !== 0)
62
+ return root.status;
63
+ const globalRoot = root.stdout.trim();
64
+ let activePackage;
65
+ try {
66
+ activePackage = await realpath(join(globalRoot, PACKAGE_NAME));
67
+ }
68
+ catch (error) {
69
+ if (!['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
70
+ throw error;
71
+ throw new Error(UPDATE_INSTRUCTIONS);
72
+ }
73
+ if (activePackage !== packageRoot || join(await realpath(globalRoot), PACKAGE_NAME) !== packageRoot) {
74
+ throw new Error(UPDATE_INSTRUCTIONS);
75
+ }
76
+ const prefix = await runNpm(executable, ['prefix', '--global'], cwd, env, true);
77
+ if (prefix.status !== 0)
78
+ return prefix.status;
79
+ const globalPrefix = prefix.stdout.trim();
80
+ const expectedRoot = process.platform === 'win32'
81
+ ? join(globalPrefix, 'node_modules')
82
+ : join(globalPrefix, 'lib', 'node_modules');
83
+ if (resolve(globalRoot) !== resolve(expectedRoot)) {
84
+ throw new Error('The active npm global prefix does not match this installation; update CodeRuler using your package manager.');
85
+ }
86
+ const result = await runNpm(executable, ['install', '--global', '--prefix', globalPrefix, `${PACKAGE_NAME}@latest`], cwd, env, false);
87
+ return result.status;
88
+ };
89
+ }
@@ -0,0 +1,101 @@
1
+ import type { LoadedRule, Project, RuleReport, RuleSelection, RunContext } from '../../app/ports.js';
2
+ import type { RuleTestResult } from '../../app/rule-tests.js';
3
+ import type { TokenPricing } from '../../app/rule.js';
4
+ import type { ViolationComment } from '../../app/violation-comments.js';
5
+ import type { PullRequest, PullRequestCheckout } from '../github/pull-request.js';
6
+ import type { SkillOptions } from '../skills/cli.js';
7
+ import type { TerminalCapabilities } from './progress.js';
8
+ import type { TextRedactor, TextWriter } from './text.js';
9
+ export interface CliTarget {
10
+ readonly root: string;
11
+ readonly path: string;
12
+ readonly kind: 'file' | 'directory';
13
+ contains(filepath: string): boolean;
14
+ }
15
+ export interface RuleSelectorResolution {
16
+ readonly explicit?: string;
17
+ readonly name?: string;
18
+ }
19
+ export interface CheckOptions {
20
+ readonly rules?: string;
21
+ readonly tags: readonly string[];
22
+ readonly excludeTags: readonly string[];
23
+ readonly json?: boolean;
24
+ readonly jsonl?: boolean;
25
+ readonly outputAll?: boolean;
26
+ readonly debug?: boolean;
27
+ readonly verbose?: boolean;
28
+ readonly diffOnly?: boolean;
29
+ readonly pr?: string;
30
+ readonly autoComment?: boolean;
31
+ readonly base?: string;
32
+ readonly concurrency: number;
33
+ readonly threshold?: number;
34
+ }
35
+ export interface InspectOptions {
36
+ readonly json?: boolean;
37
+ readonly jsonl?: boolean;
38
+ readonly raw?: boolean;
39
+ readonly inputCostPerMillion?: number;
40
+ readonly outputCostPerMillion?: number;
41
+ }
42
+ export interface TestOptions {
43
+ readonly timeout: number;
44
+ readonly concurrency: number;
45
+ readonly ai?: boolean;
46
+ readonly failuresOnly?: boolean;
47
+ }
48
+ export interface DependencyOptions {
49
+ readonly global?: boolean;
50
+ readonly directory?: string;
51
+ }
52
+ export interface CliContext {
53
+ readonly cwd: string;
54
+ readonly version: string;
55
+ readonly redact: TextRedactor;
56
+ readonly write: TextWriter;
57
+ readonly writeError: TextWriter;
58
+ readonly terminal?: TerminalCapabilities;
59
+ }
60
+ export interface RuleServices {
61
+ readonly loadRules: (selection: RuleSelection) => Promise<readonly LoadedRule[]>;
62
+ readonly resolveRuleSelector: (selector: string, invocationCwd: string, allowDirectory?: boolean) => Promise<RuleSelectorResolution>;
63
+ }
64
+ export interface ExecutionServices extends RuleServices {
65
+ readonly createProject: (root: string, options?: {
66
+ readonly excludeFiles?: readonly string[];
67
+ }) => Project;
68
+ readonly resolveTarget: (root: string, target: string) => Promise<CliTarget>;
69
+ readonly runRules: (rules: readonly LoadedRule[], context: RunContext) => Promise<readonly RuleReport[]>;
70
+ readonly decisionResolver: (thresholdOverride?: number, pricingOverrides?: Partial<TokenPricing>) => RunContext['resolveDecision'];
71
+ }
72
+ export interface CheckServices extends ExecutionServices {
73
+ readonly changedFiles: (root: string, base: string) => Promise<ReadonlySet<string>>;
74
+ readonly checkoutPullRequest: (selector: string, baseBranch?: string) => Promise<PullRequestCheckout>;
75
+ readonly commentOnPullRequest: (root: string, comments: readonly ViolationComment[], pullRequest?: PullRequest) => Promise<void>;
76
+ }
77
+ export type InspectServices = ExecutionServices;
78
+ export type ValidateServices = RuleServices;
79
+ export interface TestServices extends RuleServices {
80
+ readonly testRules: (rules: readonly LoadedRule[], onResult: (result: RuleTestResult, lastInRule: boolean) => void, timeoutMs: number, concurrency: number, includeAI: boolean) => Promise<readonly RuleTestResult[]>;
81
+ }
82
+ export interface UpdateServices {
83
+ readonly updateTool: () => Promise<number>;
84
+ }
85
+ export interface SkillServices {
86
+ readonly manageSkill: (operation: 'install' | 'update', options: SkillOptions) => Promise<number>;
87
+ }
88
+ export interface DependencyServices {
89
+ readonly manageDependencies: (operation: 'add' | 'remove' | 'sync', packages: readonly string[], options: DependencyOptions) => Promise<void>;
90
+ }
91
+ export interface CliServices extends CliContext {
92
+ readonly load: {
93
+ readonly check: (context: CliContext) => Promise<CheckServices>;
94
+ readonly inspect: (context: CliContext) => Promise<InspectServices>;
95
+ readonly test: (context: CliContext) => Promise<TestServices>;
96
+ readonly validate: (context: CliContext) => Promise<ValidateServices>;
97
+ readonly update: (context: CliContext) => Promise<UpdateServices>;
98
+ readonly skill: (context: CliContext) => Promise<SkillServices>;
99
+ readonly deps: (context: CliContext) => Promise<DependencyServices>;
100
+ };
101
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export type TextWriter = (text: string) => void;
2
+ export type TextRedactor = (text: string) => string;
3
+ export declare function escapeText(text: string): string;
4
+ export declare function createRedactor(values: readonly (string | undefined)[]): TextRedactor;
5
+ export declare function escapeDiagnostic(text: string): string;
@@ -0,0 +1,36 @@
1
+ const REDACTED = '[REDACTED]';
2
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/g;
3
+ export function escapeText(text) {
4
+ return text.replace(CONTROL_CHARACTERS, character => `\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}`);
5
+ }
6
+ export function createRedactor(values) {
7
+ const needles = new Set();
8
+ for (const value of values) {
9
+ if (value === undefined || value.trim() === '')
10
+ continue;
11
+ for (const candidate of [value, value.trim()]) {
12
+ needles.add(candidate);
13
+ needles.add(escapeText(candidate));
14
+ needles.add(JSON.stringify(candidate).slice(1, -1));
15
+ }
16
+ }
17
+ const replacements = [...needles]
18
+ .filter(needle => needle !== '')
19
+ .sort((left, right) => right.length - left.length)
20
+ .map(needle => [needle, needle.replace(/[^\r\n]+/g, REDACTED)]);
21
+ if (replacements.length === 0)
22
+ return text => text;
23
+ return text => {
24
+ let redacted = text;
25
+ for (const [needle, replacement] of replacements) {
26
+ redacted = redacted.replaceAll(needle, replacement);
27
+ }
28
+ return redacted;
29
+ };
30
+ }
31
+ export function escapeDiagnostic(text) {
32
+ return text
33
+ .split('\n')
34
+ .map(segment => escapeText(segment))
35
+ .join('\n');
36
+ }
@@ -0,0 +1 @@
1
+ export declare const installedVersion: string;
@@ -0,0 +1,3 @@
1
+ import { readFileSync } from 'node:fs';
2
+ const manifest = JSON.parse(readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'));
3
+ export const installedVersion = manifest.version;
@@ -0,0 +1,11 @@
1
+ import type { DecisionBackend, DecisionObserver, DecisionOutcome, DecisionRequest } from '../../app/ports.js';
2
+ export interface JevOptions {
3
+ readonly model: string;
4
+ readonly baseUrl: string;
5
+ readonly apiKey: string;
6
+ }
7
+ export declare class JevAdapter implements DecisionBackend {
8
+ #private;
9
+ constructor({ model, baseUrl, apiKey }: JevOptions);
10
+ decide(request: DecisionRequest, observer?: DecisionObserver): Promise<DecisionOutcome>;
11
+ }
@@ -0,0 +1,138 @@
1
+ const ENDPOINT = 'v1/systemone';
2
+ const QUESTION_ID = 'follows_instructions';
3
+ const TIMEOUT_MS = 30_000;
4
+ const REDACTED = '[REDACTED]';
5
+ const URL_LABEL = 'Decision base URL (TYPESAFE_BASE_URL)';
6
+ function isMapping(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+ function baseUrlOf(baseUrl) {
10
+ let url;
11
+ try {
12
+ url = new URL(baseUrl);
13
+ }
14
+ catch {
15
+ throw new Error(`${URL_LABEL} is not an absolute URL`);
16
+ }
17
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
18
+ throw new Error(`${URL_LABEL} must use http or https`);
19
+ }
20
+ if (baseUrl.includes('?') || baseUrl.includes('#')) {
21
+ throw new Error(`${URL_LABEL} must not carry a query or a fragment`);
22
+ }
23
+ if (url.username !== '' || url.password !== '') {
24
+ throw new Error(`${URL_LABEL} must not embed credentials`);
25
+ }
26
+ return baseUrl;
27
+ }
28
+ function redactionNeedles(apiKey) {
29
+ if (apiKey === '')
30
+ return [];
31
+ const escaped = JSON.stringify(apiKey).slice(1, -1);
32
+ return escaped === apiKey ? [apiKey] : [escaped, apiKey];
33
+ }
34
+ function redact(text, needles) {
35
+ let redacted = text;
36
+ for (const needle of needles)
37
+ redacted = redacted.replaceAll(needle, REDACTED);
38
+ return redacted;
39
+ }
40
+ function requestFailure(error, axios) {
41
+ if (axios.isAxiosError(error)) {
42
+ const status = error.response?.status;
43
+ if (status !== undefined) {
44
+ return new Error(`Decision request failed with HTTP ${status}`);
45
+ }
46
+ if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
47
+ return new Error(`Decision request timed out after ${TIMEOUT_MS} ms`);
48
+ }
49
+ const code = error.code;
50
+ return code === undefined
51
+ ? new Error('Decision request failed: network error')
52
+ : new Error(`Decision request failed: ${code}`);
53
+ }
54
+ return new Error(`Decision request failed: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ function readDecision(data) {
57
+ if (!isMapping(data)) {
58
+ throw new Error('Decision response is not a JSON object');
59
+ }
60
+ const model = data.model;
61
+ if (typeof model !== 'string' || model.trim() === '') {
62
+ throw new Error('Decision response carries no model');
63
+ }
64
+ if (!isMapping(data.answers)) {
65
+ throw new Error('Decision response carries no answers');
66
+ }
67
+ const answer = data.answers[QUESTION_ID];
68
+ if (!isMapping(answer)) {
69
+ throw new Error(`Decision response carries no '${QUESTION_ID}' answer`);
70
+ }
71
+ if (answer.type !== 'noul') {
72
+ throw new Error(`Decision response '${QUESTION_ID}' answer is not a noul answer`);
73
+ }
74
+ const probability = answer.noul;
75
+ if (typeof probability !== 'number' || !Number.isFinite(probability) || probability < 0 || probability > 1) {
76
+ throw new Error(`Decision response '${QUESTION_ID}' answer is not a probability between 0 and 1`);
77
+ }
78
+ const rawUsage = data.usage;
79
+ if (rawUsage === undefined)
80
+ return { probability, model };
81
+ if (!isMapping(rawUsage))
82
+ throw new Error('Decision response usage must be an object');
83
+ const inputTokens = rawUsage.input_tokens;
84
+ const outputTokens = rawUsage.output_tokens;
85
+ if (typeof inputTokens !== 'number' || !Number.isSafeInteger(inputTokens) || inputTokens < 0 ||
86
+ typeof outputTokens !== 'number' || !Number.isSafeInteger(outputTokens) || outputTokens < 0) {
87
+ throw new Error('Decision response usage must carry nonnegative safe integer input_tokens and output_tokens');
88
+ }
89
+ return { probability, model, usage: { inputTokens, outputTokens } };
90
+ }
91
+ export class JevAdapter {
92
+ #clientOptions;
93
+ #client;
94
+ #model;
95
+ #needles;
96
+ constructor({ model, baseUrl, apiKey }) {
97
+ this.#model = model;
98
+ this.#needles = redactionNeedles(apiKey);
99
+ this.#clientOptions = {
100
+ baseURL: baseUrlOf(baseUrl),
101
+ timeout: TIMEOUT_MS,
102
+ maxRedirects: 0,
103
+ headers: { Authorization: `Bearer ${apiKey}` },
104
+ };
105
+ }
106
+ async decide(request, observer) {
107
+ const { default: axios } = await import('axios');
108
+ const client = this.#client ??= axios.create(this.#clientOptions);
109
+ const body = {
110
+ model: this.#model,
111
+ state: request.state,
112
+ questions: {
113
+ [QUESTION_ID]: { type: 'noul', instructions: request.instructions },
114
+ },
115
+ };
116
+ const wire = JSON.stringify(body);
117
+ observer?.({ type: 'request', body: redact(wire, this.#needles) });
118
+ let data;
119
+ try {
120
+ const response = await client.post(ENDPOINT, wire, {
121
+ headers: { 'Content-Type': 'application/json' },
122
+ });
123
+ data = response.data;
124
+ observer?.({ type: 'response', status: response.status, body: redact(JSON.stringify(data) ?? '', this.#needles) });
125
+ }
126
+ catch (error) {
127
+ if (axios.isAxiosError(error) && error.response !== undefined) {
128
+ observer?.({
129
+ type: 'response',
130
+ status: error.response.status,
131
+ body: redact(JSON.stringify(error.response.data) ?? '', this.#needles),
132
+ });
133
+ }
134
+ throw requestFailure(error, axios);
135
+ }
136
+ return readDecision(data);
137
+ }
138
+ }
@@ -0,0 +1,26 @@
1
+ import { Agent } from 'node:https';
2
+ import type { ClientRequest, RequestOptions } from 'node:http';
3
+ export interface PreparedRuleDependencies {
4
+ readonly directory: string;
5
+ readonly directDependencies: readonly string[];
6
+ readonly key: string;
7
+ }
8
+ export interface RuleDependencyManager {
9
+ prepare(directory: string): Promise<PreparedRuleDependencies>;
10
+ change(directory: string, operation: 'add' | 'remove', packages: readonly string[]): Promise<PreparedRuleDependencies & {
11
+ readonly directVersions: Readonly<Record<string, string>>;
12
+ }>;
13
+ sync(directory: string): Promise<PreparedRuleDependencies>;
14
+ }
15
+ type Manifest = Record<string, unknown>;
16
+ export declare function ruleDependencyLockKey(parsed: Manifest | null, directory: string): string;
17
+ export declare function readRuleDependencyLock(directory: string): Promise<string | null>;
18
+ export declare function dependencyDirectory(ruleFile: string): Promise<string>;
19
+ export declare class RegistryHttpsAgent extends Agent {
20
+ addRequest(request: ClientRequest, options: RequestOptions): void;
21
+ }
22
+ export declare function createRuleDependencyManager(options?: {
23
+ readonly cacheRoot?: string;
24
+ readonly onWarning?: (message: string) => void;
25
+ }): RuleDependencyManager;
26
+ export {};