@agentteams/runner 0.0.113 → 0.0.115
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/api-client.d.ts +7 -0
- package/dist/api-client.js +39 -0
- package/dist/api-client.js.map +1 -1
- package/dist/api-client.test.js +23 -0
- package/dist/api-client.test.js.map +1 -1
- package/dist/commands/update.js +1 -7
- package/dist/commands/update.js.map +1 -1
- package/dist/executable.d.ts +19 -2
- package/dist/executable.js +144 -7
- package/dist/executable.js.map +1 -1
- package/dist/executable.test.js +70 -1
- package/dist/executable.test.js.map +1 -1
- package/dist/handlers/trigger-handler.js +3 -0
- package/dist/handlers/trigger-handler.js.map +1 -1
- package/dist/poller.d.ts +5 -1
- package/dist/poller.js +92 -1
- package/dist/poller.js.map +1 -1
- package/dist/poller.test.js +187 -0
- package/dist/poller.test.js.map +1 -1
- package/dist/runners/amp.js +3 -16
- package/dist/runners/amp.js.map +1 -1
- package/dist/runners/antigravity.js +3 -16
- package/dist/runners/antigravity.js.map +1 -1
- package/dist/runners/capabilities.d.ts +7 -1
- package/dist/runners/capabilities.js +21 -9
- package/dist/runners/capabilities.js.map +1 -1
- package/dist/runners/capabilities.test.js +12 -0
- package/dist/runners/capabilities.test.js.map +1 -1
- package/dist/runners/claude-code.js +3 -8
- package/dist/runners/claude-code.js.map +1 -1
- package/dist/runners/claude-code.test.js +10 -0
- package/dist/runners/claude-code.test.js.map +1 -1
- package/dist/runners/codex.js +3 -16
- package/dist/runners/codex.js.map +1 -1
- package/dist/runners/copilot-cli.js +2 -8
- package/dist/runners/copilot-cli.js.map +1 -1
- package/dist/runners/cursor-cli.js +2 -8
- package/dist/runners/cursor-cli.js.map +1 -1
- package/dist/runners/cursor-cli.test.js +14 -0
- package/dist/runners/cursor-cli.test.js.map +1 -1
- package/dist/runners/engine-commands.d.ts +11 -0
- package/dist/runners/engine-commands.js +27 -0
- package/dist/runners/engine-commands.js.map +1 -0
- package/dist/runners/grok-build-identity.d.ts +8 -0
- package/dist/runners/grok-build-identity.js +17 -0
- package/dist/runners/grok-build-identity.js.map +1 -0
- package/dist/runners/grok-build.d.ts +60 -0
- package/dist/runners/grok-build.js +360 -0
- package/dist/runners/grok-build.js.map +1 -0
- package/dist/runners/grok-build.test.d.ts +1 -0
- package/dist/runners/grok-build.test.js +233 -0
- package/dist/runners/grok-build.test.js.map +1 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/index.js.map +1 -1
- package/dist/runners/index.test.js +2 -0
- package/dist/runners/index.test.js.map +1 -1
- package/dist/runners/kimi-cli.js +2 -8
- package/dist/runners/kimi-cli.js.map +1 -1
- package/dist/runners/kimi-cli.test.js +8 -0
- package/dist/runners/kimi-cli.test.js.map +1 -1
- package/dist/runners/kiro-cli.js +2 -8
- package/dist/runners/kiro-cli.js.map +1 -1
- package/dist/runners/kiro-cli.test.js +1 -0
- package/dist/runners/kiro-cli.test.js.map +1 -1
- package/dist/runners/log-merger.d.ts +8 -0
- package/dist/runners/log-merger.js +33 -0
- package/dist/runners/log-merger.js.map +1 -0
- package/dist/runners/log-reporter.d.ts +2 -2
- package/dist/runners/log-reporter.js +2 -29
- package/dist/runners/log-reporter.js.map +1 -1
- package/dist/runners/opencode.js +3 -16
- package/dist/runners/opencode.js.map +1 -1
- package/dist/runners/session-env.d.ts +24 -0
- package/dist/runners/session-env.js +61 -0
- package/dist/runners/session-env.js.map +1 -0
- package/dist/runners/session-env.test.d.ts +1 -0
- package/dist/runners/session-env.test.js +78 -0
- package/dist/runners/session-env.test.js.map +1 -0
- package/dist/runners/stream-json-parser.js +45 -0
- package/dist/runners/stream-json-parser.js.map +1 -1
- package/dist/runners/types.d.ts +2 -0
- package/dist/utils/auto-update.d.ts +17 -2
- package/dist/utils/auto-update.js +165 -52
- package/dist/utils/auto-update.js.map +1 -1
- package/dist/utils/auto-update.test.js +357 -7
- package/dist/utils/auto-update.test.js.map +1 -1
- package/dist/utils/engine-probe.d.ts +15 -0
- package/dist/utils/engine-probe.js +52 -0
- package/dist/utils/engine-probe.js.map +1 -0
- package/dist/utils/engine-probe.test.d.ts +1 -0
- package/dist/utils/engine-probe.test.js +108 -0
- package/dist/utils/engine-probe.test.js.map +1 -0
- package/dist/utils/model-enumerator.d.ts +42 -0
- package/dist/utils/model-enumerator.js +223 -0
- package/dist/utils/model-enumerator.js.map +1 -0
- package/dist/utils/model-enumerator.test.d.ts +1 -0
- package/dist/utils/model-enumerator.test.js +132 -0
- package/dist/utils/model-enumerator.test.js.map +1 -0
- package/dist/utils/npm-global-permission.d.ts +18 -0
- package/dist/utils/npm-global-permission.js +41 -0
- package/dist/utils/npm-global-permission.js.map +1 -0
- package/dist/utils/npm-global-permission.test.d.ts +1 -0
- package/dist/utils/npm-global-permission.test.js +75 -0
- package/dist/utils/npm-global-permission.test.js.map +1 -0
- package/dist/utils/npm-install-error.d.ts +12 -0
- package/dist/utils/npm-install-error.js +19 -0
- package/dist/utils/npm-install-error.js.map +1 -0
- package/dist/utils/npm-install-error.test.d.ts +1 -0
- package/dist/utils/npm-install-error.test.js +28 -0
- package/dist/utils/npm-install-error.test.js.map +1 -0
- package/native/bin/win32-x64/agentrunner-launcher.exe +0 -0
- package/native/bin/win32-x64/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RunnerType } from '@agentteams/core-constants';
|
|
2
|
+
export type EnumeratedModel = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
maxInputTokens?: number;
|
|
6
|
+
};
|
|
7
|
+
export type ModelEnumerationResult = {
|
|
8
|
+
status: 'SUCCESS';
|
|
9
|
+
values: EnumeratedModel[];
|
|
10
|
+
} | {
|
|
11
|
+
status: 'UNSUPPORTED' | 'EMPTY_OUTPUT' | 'FORMAT_MISMATCH' | 'COMMAND_FAILED';
|
|
12
|
+
message?: string;
|
|
13
|
+
};
|
|
14
|
+
export type ModelEnumeratorDependencies = {
|
|
15
|
+
execute: (executablePath: string, args: string[]) => Promise<{
|
|
16
|
+
stdout: string;
|
|
17
|
+
}>;
|
|
18
|
+
platform: () => NodeJS.Platform;
|
|
19
|
+
resolveExecutable: (name: string, preferredNames: string[]) => string;
|
|
20
|
+
};
|
|
21
|
+
export declare const parseKiroModels: (output: string) => ModelEnumerationResult;
|
|
22
|
+
export declare const parseOpenCodeVerboseModels: (output: string) => ModelEnumerationResult;
|
|
23
|
+
type LineModelShape = 'tab' | 'slash';
|
|
24
|
+
export declare const parseLineModels: (output: string, shape: LineModelShape) => ModelEnumerationResult;
|
|
25
|
+
export declare const parseCursorModels: (output: string) => ModelEnumerationResult;
|
|
26
|
+
/**
|
|
27
|
+
* `grok models`는 JSON 플래그가 없고 사람이 읽는 평문만 낸다(2026-08-14 실측, grok 1.0.3):
|
|
28
|
+
*
|
|
29
|
+
* You are logged in with grok.com.
|
|
30
|
+
*
|
|
31
|
+
* Default model: grok-4.6
|
|
32
|
+
*
|
|
33
|
+
* Available models:
|
|
34
|
+
* * grok-4.6 (default)
|
|
35
|
+
*
|
|
36
|
+
* `Available models:` 헤더 뒤의 글머리 줄만 읽는다. 헤더 앞줄에는 로그인 상태와
|
|
37
|
+
* `Default model:` 같은 진단 문구가 섞여 있어, 줄 형태만으로 거르면 그것들이 모델 값으로
|
|
38
|
+
* 새어 들어간다. 값 뒤의 `(default)` 표시는 값의 일부가 아니라 기본 모델 마커라 떼어낸다.
|
|
39
|
+
*/
|
|
40
|
+
export declare const parseGrokModels: (output: string) => ModelEnumerationResult;
|
|
41
|
+
export declare const enumerateModels: (runnerType: RunnerType, dependencies?: Partial<ModelEnumeratorDependencies>) => Promise<ModelEnumerationResult>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { resolveExecutablePathWithPreference } from '../executable.js';
|
|
5
|
+
import { RUNNER_CAPABILITIES } from '../runners/capabilities.js';
|
|
6
|
+
import { getGrokExecutablePreference } from '../runners/grok-build.js';
|
|
7
|
+
import { getKiroExecutablePreference } from '../runners/kiro-cli.js';
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const RESERVED_MODEL_PREFIX = '__fast__:';
|
|
10
|
+
const ENUMERATION_TIMEOUT_MS = 30_000;
|
|
11
|
+
const defaultDependencies = {
|
|
12
|
+
execute: async (executablePath, args) => {
|
|
13
|
+
const { stdout } = await execFileAsync(executablePath, args, {
|
|
14
|
+
encoding: 'utf8',
|
|
15
|
+
windowsHide: true,
|
|
16
|
+
timeout: ENUMERATION_TIMEOUT_MS,
|
|
17
|
+
killSignal: 'SIGKILL',
|
|
18
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
19
|
+
});
|
|
20
|
+
return { stdout: String(stdout) };
|
|
21
|
+
},
|
|
22
|
+
platform,
|
|
23
|
+
resolveExecutable: resolveExecutablePathWithPreference,
|
|
24
|
+
};
|
|
25
|
+
// API의 value/label은 255자를 넘으면 요청을 거절하므로 러너에서 먼저 자른다.
|
|
26
|
+
// 255자를 넘는 value는 모델 식별자로 볼 수 없으니 자르지 않고 버린다.
|
|
27
|
+
const MAX_FIELD_LENGTH = 255;
|
|
28
|
+
const normalizeModels = (models) => {
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
return models.filter((model) => {
|
|
31
|
+
const value = model.value.trim();
|
|
32
|
+
if (value.length === 0 || value.length > MAX_FIELD_LENGTH || value.startsWith(RESERVED_MODEL_PREFIX)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (seen.has(value)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
seen.add(value);
|
|
39
|
+
model.value = value;
|
|
40
|
+
model.label = (model.label.trim() || value).slice(0, MAX_FIELD_LENGTH);
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const parsedResult = (output, models) => {
|
|
45
|
+
if (output.trim().length === 0) {
|
|
46
|
+
return { status: 'EMPTY_OUTPUT' };
|
|
47
|
+
}
|
|
48
|
+
const values = normalizeModels(models);
|
|
49
|
+
return values.length > 0 ? { status: 'SUCCESS', values } : { status: 'FORMAT_MISMATCH' };
|
|
50
|
+
};
|
|
51
|
+
export const parseKiroModels = (output) => {
|
|
52
|
+
if (output.trim().length === 0)
|
|
53
|
+
return { status: 'EMPTY_OUTPUT' };
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(output);
|
|
56
|
+
const models = Array.isArray(parsed.models)
|
|
57
|
+
? parsed.models.flatMap((model) => {
|
|
58
|
+
if (typeof model.model_id !== 'string')
|
|
59
|
+
return [];
|
|
60
|
+
const maxInputTokens = typeof model.context_window_tokens === 'number' &&
|
|
61
|
+
Number.isInteger(model.context_window_tokens) &&
|
|
62
|
+
model.context_window_tokens > 0
|
|
63
|
+
? model.context_window_tokens
|
|
64
|
+
: undefined;
|
|
65
|
+
return [
|
|
66
|
+
{
|
|
67
|
+
value: model.model_id,
|
|
68
|
+
label: typeof model.model_name === 'string' ? model.model_name : model.model_id,
|
|
69
|
+
...(maxInputTokens ? { maxInputTokens } : {}),
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
})
|
|
73
|
+
: [];
|
|
74
|
+
return parsedResult(output, models);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { status: 'FORMAT_MISMATCH' };
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
export const parseOpenCodeVerboseModels = (output) => {
|
|
81
|
+
const lines = output.split(/\r?\n/u);
|
|
82
|
+
const models = [];
|
|
83
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
84
|
+
const value = lines[index]?.trim() ?? '';
|
|
85
|
+
if (!/^[^\s{}]+\/[^\s{}]+$/u.test(value))
|
|
86
|
+
continue;
|
|
87
|
+
const jsonLines = [];
|
|
88
|
+
for (let jsonIndex = index + 1; jsonIndex < lines.length; jsonIndex += 1) {
|
|
89
|
+
const line = lines[jsonIndex] ?? '';
|
|
90
|
+
if (jsonLines.length === 0 && line.trim() !== '{')
|
|
91
|
+
break;
|
|
92
|
+
jsonLines.push(line);
|
|
93
|
+
try {
|
|
94
|
+
const metadata = JSON.parse(jsonLines.join('\n'));
|
|
95
|
+
const context = metadata.limit?.context;
|
|
96
|
+
models.push({
|
|
97
|
+
value,
|
|
98
|
+
label: typeof metadata.name === 'string' ? metadata.name : value,
|
|
99
|
+
...(typeof context === 'number' && Number.isInteger(context) && context > 0
|
|
100
|
+
? { maxInputTokens: context }
|
|
101
|
+
: {}),
|
|
102
|
+
});
|
|
103
|
+
index = jsonIndex;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// pretty-print JSON이 완성될 때까지 다음 줄을 이어 붙인다.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return parsedResult(output, models);
|
|
112
|
+
};
|
|
113
|
+
export const parseLineModels = (output, shape) => parsedResult(output, output
|
|
114
|
+
.split(/\r?\n/u)
|
|
115
|
+
.filter((line) => line.trim().length > 0 && !line.trim().startsWith('Fetching '))
|
|
116
|
+
.flatMap((line) => {
|
|
117
|
+
if (shape === 'slash') {
|
|
118
|
+
const value = line.trim();
|
|
119
|
+
return /^[^\s{}]+\/[^\s{}]+$/u.test(value) ? [{ value, label: value }] : [];
|
|
120
|
+
}
|
|
121
|
+
const separatorIndex = line.indexOf('\t');
|
|
122
|
+
if (separatorIndex < 0)
|
|
123
|
+
return [];
|
|
124
|
+
const value = line.slice(0, separatorIndex).trim();
|
|
125
|
+
const label = line.slice(separatorIndex + 1).trim();
|
|
126
|
+
return /^\S+$/u.test(value) ? [{ value, label: label || value }] : [];
|
|
127
|
+
}));
|
|
128
|
+
export const parseCursorModels = (output) => parsedResult(output, output.split(/\r?\n/u).flatMap((line) => {
|
|
129
|
+
const match = line.trim().match(/^(\S+)\s+-\s+(.+)$/u);
|
|
130
|
+
if (!match || match[1] === 'auto')
|
|
131
|
+
return [];
|
|
132
|
+
return [{ value: match[1] ?? '', label: match[2] ?? match[1] ?? '' }];
|
|
133
|
+
}));
|
|
134
|
+
/**
|
|
135
|
+
* `grok models`는 JSON 플래그가 없고 사람이 읽는 평문만 낸다(2026-08-14 실측, grok 1.0.3):
|
|
136
|
+
*
|
|
137
|
+
* You are logged in with grok.com.
|
|
138
|
+
*
|
|
139
|
+
* Default model: grok-4.6
|
|
140
|
+
*
|
|
141
|
+
* Available models:
|
|
142
|
+
* * grok-4.6 (default)
|
|
143
|
+
*
|
|
144
|
+
* `Available models:` 헤더 뒤의 글머리 줄만 읽는다. 헤더 앞줄에는 로그인 상태와
|
|
145
|
+
* `Default model:` 같은 진단 문구가 섞여 있어, 줄 형태만으로 거르면 그것들이 모델 값으로
|
|
146
|
+
* 새어 들어간다. 값 뒤의 `(default)` 표시는 값의 일부가 아니라 기본 모델 마커라 떼어낸다.
|
|
147
|
+
*/
|
|
148
|
+
export const parseGrokModels = (output) => {
|
|
149
|
+
if (output.trim().length === 0)
|
|
150
|
+
return { status: 'EMPTY_OUTPUT' };
|
|
151
|
+
const lines = output.split(/\r?\n/u);
|
|
152
|
+
const headerIndex = lines.findIndex((line) => /^available models:\s*$/iu.test(line.trim()));
|
|
153
|
+
if (headerIndex < 0)
|
|
154
|
+
return { status: 'FORMAT_MISMATCH' };
|
|
155
|
+
const models = [];
|
|
156
|
+
for (const line of lines.slice(headerIndex + 1)) {
|
|
157
|
+
const trimmed = line.trim();
|
|
158
|
+
if (trimmed.length === 0)
|
|
159
|
+
continue;
|
|
160
|
+
const match = trimmed.match(/^[*\-•]\s+(\S+)(?:\s+\((.+)\))?$/u);
|
|
161
|
+
// 글머리 형태가 아닌 줄이 나오면 목록 구획이 끝난 것으로 본다.
|
|
162
|
+
if (!match)
|
|
163
|
+
break;
|
|
164
|
+
const value = match[1] ?? '';
|
|
165
|
+
if (value.length > 0)
|
|
166
|
+
models.push({ value, label: value });
|
|
167
|
+
}
|
|
168
|
+
return parsedResult(output, models);
|
|
169
|
+
};
|
|
170
|
+
const commandFailure = (error) => ({
|
|
171
|
+
status: 'COMMAND_FAILED',
|
|
172
|
+
message: error instanceof Error ? error.message : String(error),
|
|
173
|
+
});
|
|
174
|
+
export const enumerateModels = async (runnerType, dependencies = {}) => {
|
|
175
|
+
if (!RUNNER_CAPABILITIES[runnerType].modelEnumeration) {
|
|
176
|
+
return { status: 'UNSUPPORTED' };
|
|
177
|
+
}
|
|
178
|
+
const deps = { ...defaultDependencies, ...dependencies };
|
|
179
|
+
const isWindows = deps.platform() === 'win32';
|
|
180
|
+
try {
|
|
181
|
+
switch (runnerType) {
|
|
182
|
+
case 'KIRO_CLI': {
|
|
183
|
+
const executable = deps.resolveExecutable('kiro-cli', getKiroExecutablePreference(isWindows));
|
|
184
|
+
return parseKiroModels((await deps.execute(executable, ['chat', '--list-models', '--format', 'json'])).stdout);
|
|
185
|
+
}
|
|
186
|
+
case 'OPENCODE': {
|
|
187
|
+
const executable = deps.resolveExecutable('opencode', isWindows ? ['opencode.cmd', 'opencode'] : ['opencode']);
|
|
188
|
+
try {
|
|
189
|
+
const verbose = parseOpenCodeVerboseModels((await deps.execute(executable, ['models', '--verbose'])).stdout);
|
|
190
|
+
if (verbose.status === 'SUCCESS')
|
|
191
|
+
return verbose;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// 구버전 또는 일시 오류에서는 안정적인 줄 단위 명령으로 한 번 폴백한다.
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return parseLineModels((await deps.execute(executable, ['models'])).stdout, 'slash');
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
return commandFailure(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
case 'ANTIGRAVITY': {
|
|
204
|
+
const executable = deps.resolveExecutable('agy', isWindows ? ['agy.cmd', 'agy'] : ['agy']);
|
|
205
|
+
return parseLineModels((await deps.execute(executable, ['models'])).stdout, 'tab');
|
|
206
|
+
}
|
|
207
|
+
case 'CURSOR_CLI': {
|
|
208
|
+
const executable = deps.resolveExecutable('agent', isWindows ? ['agent.cmd', 'agent'] : ['agent']);
|
|
209
|
+
return parseCursorModels((await deps.execute(executable, ['models'])).stdout);
|
|
210
|
+
}
|
|
211
|
+
case 'GROK_BUILD': {
|
|
212
|
+
const executable = deps.resolveExecutable('grok', getGrokExecutablePreference(isWindows));
|
|
213
|
+
return parseGrokModels((await deps.execute(executable, ['models'])).stdout);
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
return { status: 'UNSUPPORTED' };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
return commandFailure(error);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=model-enumerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-enumerator.js","sourceRoot":"","sources":["../../src/utils/model-enumerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { describe, test } from 'node:test';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { enumerateModels, parseCursorModels, parseGrokModels, parseKiroModels, parseLineModels, parseOpenCodeVerboseModels, } from './model-enumerator.js';
|
|
6
|
+
const fixture = (name) => readFile(fileURLToPath(new URL(`../runners/fixtures/${name}`, import.meta.url)), 'utf8');
|
|
7
|
+
describe('model enumerator parsers', () => {
|
|
8
|
+
test('parses OpenCode labels and context limits while allowing a missing context', async () => {
|
|
9
|
+
const result = parseOpenCodeVerboseModels(await fixture('opencode-models-verbose.txt'));
|
|
10
|
+
assert.deepEqual(result, {
|
|
11
|
+
status: 'SUCCESS',
|
|
12
|
+
values: [
|
|
13
|
+
{ value: 'opencode/big-pickle', label: 'Big Pickle', maxInputTokens: 200000 },
|
|
14
|
+
{ value: 'opencode/deepseek-v4-flash-free', label: 'DeepSeek V4 Flash Free' },
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
test('parses Kiro model metadata', async () => {
|
|
19
|
+
const result = parseKiroModels(await fixture('kiro-models.json'));
|
|
20
|
+
assert.equal(result.status, 'SUCCESS');
|
|
21
|
+
if (result.status !== 'SUCCESS')
|
|
22
|
+
return;
|
|
23
|
+
assert.deepEqual(result.values[1], {
|
|
24
|
+
value: 'claude-sonnet-4.5',
|
|
25
|
+
label: 'claude-sonnet-4.5',
|
|
26
|
+
maxInputTokens: 200000,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
test('parses Antigravity values without treating status text or labels as ids', async () => {
|
|
30
|
+
assert.deepEqual(parseLineModels(await fixture('antigravity-models.txt'), 'tab'), {
|
|
31
|
+
status: 'SUCCESS',
|
|
32
|
+
values: [
|
|
33
|
+
{ value: 'gemini-3.7-flash-high', label: 'Gemini 3.7 Flash (High)' },
|
|
34
|
+
{ value: 'gemini-3.6-flash-medium', label: 'Gemini 3.6 Flash (Medium)' },
|
|
35
|
+
{ value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 (Thinking)' },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
test('rejects diagnostic lines that carry no model shape', () => {
|
|
40
|
+
// 인증 만료·네트워크 오류로 CLI가 한 단어 진단 메시지를 내면 그대로 승인 대기 목록에 쌓였다.
|
|
41
|
+
assert.deepEqual(parseLineModels('Error\nUnauthorized\nhttps://example.com/login', 'tab'), {
|
|
42
|
+
status: 'FORMAT_MISMATCH',
|
|
43
|
+
});
|
|
44
|
+
assert.deepEqual(parseLineModels('Error\nUnauthorized\nbare-token', 'slash'), { status: 'FORMAT_MISMATCH' });
|
|
45
|
+
assert.deepEqual(parseLineModels('provider/model-a\nError', 'slash'), {
|
|
46
|
+
status: 'SUCCESS',
|
|
47
|
+
values: [{ value: 'provider/model-a', label: 'provider/model-a' }],
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
test('drops values and labels that exceed the API length limit', () => {
|
|
51
|
+
const longValue = 'a'.repeat(256);
|
|
52
|
+
const longLabel = 'b'.repeat(300);
|
|
53
|
+
assert.deepEqual(parseLineModels(`${longValue}\tlabel\nmodel-a\t${longLabel}`, 'tab'), {
|
|
54
|
+
status: 'SUCCESS',
|
|
55
|
+
values: [{ value: 'model-a', label: 'b'.repeat(255) }],
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
test('drops Cursor headers, auto, and help text', async () => {
|
|
59
|
+
assert.deepEqual(parseCursorModels(await fixture('cursor-models.txt')), {
|
|
60
|
+
status: 'SUCCESS',
|
|
61
|
+
values: [
|
|
62
|
+
{ value: 'gpt-5.3-codex-low', label: 'Codex 5.3 Low' },
|
|
63
|
+
{ value: 'composer-2.5', label: 'Composer 2.5' },
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
test('reads only the Grok bullet list under the Available models header', async () => {
|
|
68
|
+
assert.deepEqual(parseGrokModels(await fixture('grok-models.txt')), {
|
|
69
|
+
status: 'SUCCESS',
|
|
70
|
+
values: [{ value: 'grok-4.6', label: 'grok-4.6' }],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// 헤더 앞에는 로그인 상태와 `Default model:` 진단 줄이 있다. 줄 형태만으로 거르면
|
|
74
|
+
// 그 값들이 모델로 새어 들어간다.
|
|
75
|
+
test('does not leak Grok diagnostic lines above the header as models', () => {
|
|
76
|
+
const output = ['You are logged in with grok.com.', '', 'Default model: grok-4.6', ''].join('\n');
|
|
77
|
+
assert.deepEqual(parseGrokModels(output), { status: 'FORMAT_MISMATCH' });
|
|
78
|
+
});
|
|
79
|
+
test('stops the Grok list at the first non-bullet line and strips the default marker', () => {
|
|
80
|
+
const output = ['Available models:', ' * grok-4.6 (default)', ' * grok-mini', '', 'Notes: something else'].join('\n');
|
|
81
|
+
assert.deepEqual(parseGrokModels(output), {
|
|
82
|
+
status: 'SUCCESS',
|
|
83
|
+
values: [
|
|
84
|
+
{ value: 'grok-4.6', label: 'grok-4.6' },
|
|
85
|
+
{ value: 'grok-mini', label: 'grok-mini' },
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
test('distinguishes empty output, format mismatch, and reserved values', () => {
|
|
90
|
+
assert.deepEqual(parseLineModels('', 'tab'), { status: 'EMPTY_OUTPUT' });
|
|
91
|
+
assert.deepEqual(parseCursorModels('Available models\ninvalid'), { status: 'FORMAT_MISMATCH' });
|
|
92
|
+
assert.deepEqual(parseLineModels('__fast__:hidden\tHidden', 'tab'), { status: 'FORMAT_MISMATCH' });
|
|
93
|
+
assert.deepEqual(parseGrokModels(''), { status: 'EMPTY_OUTPUT' });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('enumerateModels', () => {
|
|
97
|
+
const dependencies = (execute) => ({
|
|
98
|
+
execute,
|
|
99
|
+
platform: () => 'darwin',
|
|
100
|
+
resolveExecutable: (name) => `/bin/${name}`,
|
|
101
|
+
});
|
|
102
|
+
test('falls back to line output when OpenCode verbose enumeration fails', async () => {
|
|
103
|
+
const calls = [];
|
|
104
|
+
const result = await enumerateModels('OPENCODE', dependencies(async (_executable, args) => {
|
|
105
|
+
calls.push(args);
|
|
106
|
+
if (args.includes('--verbose'))
|
|
107
|
+
throw new Error('unsupported flag');
|
|
108
|
+
return { stdout: 'provider/model-a\n' };
|
|
109
|
+
}));
|
|
110
|
+
assert.deepEqual(calls, [['models', '--verbose'], ['models']]);
|
|
111
|
+
assert.deepEqual(result, {
|
|
112
|
+
status: 'SUCCESS',
|
|
113
|
+
values: [{ value: 'provider/model-a', label: 'provider/model-a' }],
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
test('classifies command failures instead of throwing', async () => {
|
|
117
|
+
const result = await enumerateModels('KIRO_CLI', dependencies(async () => {
|
|
118
|
+
throw new Error('offline');
|
|
119
|
+
}));
|
|
120
|
+
assert.equal(result.status, 'COMMAND_FAILED');
|
|
121
|
+
});
|
|
122
|
+
test('does not enumerate unsupported runners', async () => {
|
|
123
|
+
let called = false;
|
|
124
|
+
const result = await enumerateModels('CODEX', dependencies(async () => {
|
|
125
|
+
called = true;
|
|
126
|
+
return { stdout: '' };
|
|
127
|
+
}));
|
|
128
|
+
assert.deepEqual(result, { status: 'UNSUPPORTED' });
|
|
129
|
+
assert.equal(called, false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=model-enumerator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-enumerator.test.js","sourceRoot":"","sources":["../../src/utils/model-enumerator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { accessSync } from 'node:fs';
|
|
2
|
+
import { platform as getPlatform } from 'node:os';
|
|
3
|
+
/**
|
|
4
|
+
* 전역 npm 모듈 루트에 쓸 수 있는지 미리 판정한다.
|
|
5
|
+
*
|
|
6
|
+
* 리눅스 기본 설치(`npm prefix -g` = `/usr/local`, `/usr/local/lib/node_modules`가 root 소유)에서
|
|
7
|
+
* `--user` systemd 서비스로 도는 러너는 `npm install -g`가 항상 EACCES로 죽는다. 실행해봐야 실패할
|
|
8
|
+
* 설치를 매시간 반복하지 않도록 설치 직전에 이 판정을 먼저 한다.
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ fail-open: prefix를 못 구하거나 판정 중 예외가 나면 `true`를 돌려준다. 정상 환경의 자동
|
|
11
|
+
* 업데이트를 새로 막는 것이 반복 실패보다 나쁘기 때문이다.
|
|
12
|
+
*/
|
|
13
|
+
export type NpmGlobalPermissionDeps = {
|
|
14
|
+
npmGlobalPrefix?: () => string | null;
|
|
15
|
+
accessSync?: typeof accessSync;
|
|
16
|
+
platform?: typeof getPlatform;
|
|
17
|
+
};
|
|
18
|
+
export declare const canWriteGlobalNpmRoot: (deps?: NpmGlobalPermissionDeps) => boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { accessSync, constants } from 'node:fs';
|
|
2
|
+
import { platform as getPlatform } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { getNpmGlobalBinPath } from '../executable.js';
|
|
5
|
+
/** 전역 모듈 루트 경로. win32는 prefix 바로 아래, POSIX는 `<prefix>/lib/node_modules`. */
|
|
6
|
+
const resolveGlobalModulesRoot = (prefix, os) => os === 'win32' ? join(prefix, 'node_modules') : join(prefix, 'lib', 'node_modules');
|
|
7
|
+
export const canWriteGlobalNpmRoot = (deps = {}) => {
|
|
8
|
+
const resolvePrefix = deps.npmGlobalPrefix ?? (() => getNpmGlobalBinPath());
|
|
9
|
+
const access = deps.accessSync ?? accessSync;
|
|
10
|
+
const os = (deps.platform ?? getPlatform)();
|
|
11
|
+
try {
|
|
12
|
+
const prefix = resolvePrefix();
|
|
13
|
+
if (!prefix) {
|
|
14
|
+
return true; // prefix를 못 구하면 판정 불가 → fail-open
|
|
15
|
+
}
|
|
16
|
+
// 설치 대상 디렉터리가 아직 없을 수 있다(최초 전역 설치). 존재하는 가장 가까운 상위 경로로
|
|
17
|
+
// 올라가 그곳의 쓰기 권한으로 판정한다.
|
|
18
|
+
let candidate = resolveGlobalModulesRoot(prefix, os);
|
|
19
|
+
for (;;) {
|
|
20
|
+
try {
|
|
21
|
+
access(candidate, constants.W_OK);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const code = error.code;
|
|
26
|
+
if (code !== 'ENOENT') {
|
|
27
|
+
return false; // EACCES/EPERM 등 → 쓰기 불가로 확정
|
|
28
|
+
}
|
|
29
|
+
const parent = dirname(candidate);
|
|
30
|
+
if (parent === candidate) {
|
|
31
|
+
return true; // 루트까지 올라가도 못 찾음 → 판정 불가 → fail-open
|
|
32
|
+
}
|
|
33
|
+
candidate = parent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return true; // 예상 못 한 예외 → fail-open
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=npm-global-permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-global-permission.js","sourceRoot":"","sources":["../../src/utils/npm-global-permission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,QAAQ,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAkBvD,4EAA4E;AAC5E,MAAM,wBAAwB,GAAG,CAAC,MAAc,EAAE,EAAmB,EAAU,EAAE,CAC/E,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AAEtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAgC,EAAE,EAAW,EAAE;IACnF,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,IAAI,CAAC,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC;IAC7C,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,EAAE,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC,CAAC,kCAAkC;QACjD,CAAC;QAED,uDAAuD;QACvD,wBAAwB;QACxB,IAAI,SAAS,GAAG,wBAAwB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACrD,SAAS,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;gBAClC,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;gBACnD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACtB,OAAO,KAAK,CAAC,CAAC,6BAA6B;gBAC7C,CAAC;gBAED,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;gBAClC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,OAAO,IAAI,CAAC,CAAC,qCAAqC;gBACpD,CAAC;gBACD,SAAS,GAAG,MAAM,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,wBAAwB;IACvC,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { canWriteGlobalNpmRoot } from './npm-global-permission.js';
|
|
4
|
+
const enoent = () => {
|
|
5
|
+
const error = new Error('ENOENT');
|
|
6
|
+
error.code = 'ENOENT';
|
|
7
|
+
throw error;
|
|
8
|
+
};
|
|
9
|
+
const eacces = () => {
|
|
10
|
+
const error = new Error('EACCES');
|
|
11
|
+
error.code = 'EACCES';
|
|
12
|
+
throw error;
|
|
13
|
+
};
|
|
14
|
+
test('canWriteGlobalNpmRoot checks <prefix>/lib/node_modules on POSIX', () => {
|
|
15
|
+
const checked = [];
|
|
16
|
+
const result = canWriteGlobalNpmRoot({
|
|
17
|
+
npmGlobalPrefix: () => '/usr/local',
|
|
18
|
+
platform: () => 'linux',
|
|
19
|
+
accessSync: ((path) => {
|
|
20
|
+
checked.push(path);
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
assert.equal(result, true);
|
|
24
|
+
assert.deepEqual(checked, ['/usr/local/lib/node_modules']);
|
|
25
|
+
});
|
|
26
|
+
test('canWriteGlobalNpmRoot checks <prefix>\\node_modules on Windows', () => {
|
|
27
|
+
const checked = [];
|
|
28
|
+
canWriteGlobalNpmRoot({
|
|
29
|
+
npmGlobalPrefix: () => 'C:\\Users\\me\\AppData\\Roaming\\npm',
|
|
30
|
+
platform: () => 'win32',
|
|
31
|
+
accessSync: ((path) => {
|
|
32
|
+
checked.push(path);
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
assert.equal(checked.length, 1);
|
|
36
|
+
assert.match(checked[0] ?? '', /node_modules$/);
|
|
37
|
+
});
|
|
38
|
+
test('canWriteGlobalNpmRoot reports false when the global modules root is not writable', () => {
|
|
39
|
+
const result = canWriteGlobalNpmRoot({
|
|
40
|
+
npmGlobalPrefix: () => '/usr/local',
|
|
41
|
+
platform: () => 'linux',
|
|
42
|
+
accessSync: eacces,
|
|
43
|
+
});
|
|
44
|
+
assert.equal(result, false);
|
|
45
|
+
});
|
|
46
|
+
test('canWriteGlobalNpmRoot walks up to the nearest existing directory', () => {
|
|
47
|
+
const checked = [];
|
|
48
|
+
const result = canWriteGlobalNpmRoot({
|
|
49
|
+
npmGlobalPrefix: () => '/usr/local',
|
|
50
|
+
platform: () => 'linux',
|
|
51
|
+
accessSync: ((path) => {
|
|
52
|
+
checked.push(path);
|
|
53
|
+
if (path !== '/usr') {
|
|
54
|
+
enoent();
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
assert.equal(result, true);
|
|
59
|
+
assert.deepEqual(checked, ['/usr/local/lib/node_modules', '/usr/local/lib', '/usr/local', '/usr']);
|
|
60
|
+
});
|
|
61
|
+
test('canWriteGlobalNpmRoot fails open when the prefix cannot be resolved', () => {
|
|
62
|
+
assert.equal(canWriteGlobalNpmRoot({
|
|
63
|
+
npmGlobalPrefix: () => null,
|
|
64
|
+
platform: () => 'linux',
|
|
65
|
+
accessSync: eacces,
|
|
66
|
+
}), true);
|
|
67
|
+
assert.equal(canWriteGlobalNpmRoot({
|
|
68
|
+
npmGlobalPrefix: () => {
|
|
69
|
+
throw new Error('npm prefix -g failed');
|
|
70
|
+
},
|
|
71
|
+
platform: () => 'linux',
|
|
72
|
+
accessSync: eacces,
|
|
73
|
+
}), true);
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=npm-global-permission.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-global-permission.test.js","sourceRoot":"","sources":["../../src/utils/npm-global-permission.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `npm install -g` 실패 원인 분류의 단일 출처(SSOT).
|
|
3
|
+
*
|
|
4
|
+
* 수동 업데이트(`agentrunner update`)와 자동 업데이트가 같은 판정을 쓰도록 여기로 추출했다.
|
|
5
|
+
* 자동 업데이트는 이 분류 결과를 서버에도 보고하므로, 문구가 아니라 `reason` 코드가 계약이다.
|
|
6
|
+
*/
|
|
7
|
+
export type InstallFailureReason = 'PERMISSION_DENIED' | 'UNKNOWN';
|
|
8
|
+
export declare const classifyInstallError: (error: unknown) => InstallFailureReason;
|
|
9
|
+
/** 사용자에게 보여줄 설치 실패 메시지. 권한 실패는 조치 방법을 담은 고정 문구를 쓴다. */
|
|
10
|
+
export declare const describeInstallError: (error: unknown) => string;
|
|
11
|
+
/** 설치 실패를 사용자용 문구로 정규화한 Error로 바꾼다. */
|
|
12
|
+
export declare const normalizeInstallError: (error: unknown) => Error;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const PERMISSION_DENIED_MESSAGE = 'Global npm install requires elevated permissions. Configure a user-level npm prefix or rerun the update with appropriate permissions.';
|
|
2
|
+
export const classifyInstallError = (error) => {
|
|
3
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4
|
+
if (message.includes('EACCES') || message.includes('EPERM') || message.toLowerCase().includes('permission denied')) {
|
|
5
|
+
return 'PERMISSION_DENIED';
|
|
6
|
+
}
|
|
7
|
+
return 'UNKNOWN';
|
|
8
|
+
};
|
|
9
|
+
/** 사용자에게 보여줄 설치 실패 메시지. 권한 실패는 조치 방법을 담은 고정 문구를 쓴다. */
|
|
10
|
+
export const describeInstallError = (error) => {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
if (classifyInstallError(error) === 'PERMISSION_DENIED') {
|
|
13
|
+
return PERMISSION_DENIED_MESSAGE;
|
|
14
|
+
}
|
|
15
|
+
return `Failed to install the latest AgentRunner package: ${message}`;
|
|
16
|
+
};
|
|
17
|
+
/** 설치 실패를 사용자용 문구로 정규화한 Error로 바꾼다. */
|
|
18
|
+
export const normalizeInstallError = (error) => new Error(describeInstallError(error));
|
|
19
|
+
//# sourceMappingURL=npm-install-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-install-error.js","sourceRoot":"","sources":["../../src/utils/npm-install-error.ts"],"names":[],"mappings":"AAQA,MAAM,yBAAyB,GAC7B,uIAAuI,CAAC;AAE1I,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAwB,EAAE;IAC3E,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvE,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACnH,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,uDAAuD;AACvD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAU,EAAE;IAC7D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvE,IAAI,oBAAoB,CAAC,KAAK,CAAC,KAAK,mBAAmB,EAAE,CAAC;QACxD,OAAO,yBAAyB,CAAC;IACnC,CAAC;IAED,OAAO,qDAAqD,OAAO,EAAE,CAAC;AACxE,CAAC,CAAC;AAEF,uCAAuC;AACvC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAS,EAAE,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|