berget 2.2.7 → 2.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +6 -6
- package/.github/workflows/test.yml +1 -1
- package/.prettierrc +5 -3
- package/dist/index.js +24 -25
- package/dist/package.json +7 -3
- package/dist/src/agents/app.js +8 -8
- package/dist/src/agents/backend.js +3 -3
- package/dist/src/agents/devops.js +8 -8
- package/dist/src/agents/frontend.js +3 -3
- package/dist/src/agents/fullstack.js +3 -3
- package/dist/src/agents/index.js +18 -18
- package/dist/src/agents/quality.js +8 -8
- package/dist/src/agents/security.js +8 -8
- package/dist/src/client.js +115 -127
- package/dist/src/commands/api-keys.js +181 -202
- package/dist/src/commands/auth.js +16 -25
- package/dist/src/commands/autocomplete.js +8 -8
- package/dist/src/commands/billing.js +10 -19
- package/dist/src/commands/chat.js +139 -170
- package/dist/src/commands/clusters.js +21 -30
- package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
- package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
- package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
- package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
- package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
- package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
- package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
- package/dist/src/commands/code/auth-sync.js +215 -228
- package/dist/src/commands/code/errors.js +15 -12
- package/dist/src/commands/code/setup.js +390 -425
- package/dist/src/commands/code.js +279 -294
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +16 -25
- package/dist/src/commands/users.js +9 -18
- package/dist/src/constants/command-structure.js +138 -138
- package/dist/src/services/api-key-service.js +132 -152
- package/dist/src/services/auth-service.js +81 -95
- package/dist/src/services/browser-auth.js +121 -131
- package/dist/src/services/chat-service.js +369 -386
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +9 -21
- package/dist/src/services/flux-service.js +13 -25
- package/dist/src/services/helm-service.js +9 -21
- package/dist/src/services/kubectl-service.js +15 -29
- package/dist/src/utils/config-checker.js +8 -8
- package/dist/src/utils/config-loader.js +109 -109
- package/dist/src/utils/default-api-key.js +129 -139
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +62 -62
- package/dist/src/utils/logger.js +74 -67
- package/dist/src/utils/markdown-renderer.js +28 -28
- package/dist/src/utils/opencode-validator.js +67 -69
- package/dist/src/utils/token-manager.js +67 -65
- package/dist/tests/commands/chat.test.js +30 -39
- package/dist/tests/commands/code.test.js +186 -195
- package/dist/tests/utils/config-loader.test.js +107 -107
- package/dist/tests/utils/env-manager.test.js +81 -90
- package/dist/tests/utils/opencode-validator.test.js +42 -41
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +65 -30
- package/index.ts +30 -31
- package/package.json +7 -3
- package/src/agents/app.ts +9 -9
- package/src/agents/backend.ts +4 -4
- package/src/agents/devops.ts +9 -9
- package/src/agents/frontend.ts +4 -4
- package/src/agents/fullstack.ts +4 -4
- package/src/agents/index.ts +27 -25
- package/src/agents/quality.ts +9 -9
- package/src/agents/security.ts +9 -9
- package/src/agents/types.ts +10 -10
- package/src/client.ts +85 -77
- package/src/commands/api-keys.ts +180 -185
- package/src/commands/auth.ts +15 -14
- package/src/commands/autocomplete.ts +10 -10
- package/src/commands/billing.ts +13 -12
- package/src/commands/chat.ts +145 -142
- package/src/commands/clusters.ts +20 -19
- package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
- package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
- package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
- package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
- package/src/commands/code/__tests__/fake-file-store.ts +15 -15
- package/src/commands/code/__tests__/fake-prompter.ts +86 -85
- package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
- package/src/commands/code/adapters/clack-prompter.ts +32 -30
- package/src/commands/code/adapters/fs-file-store.ts +18 -17
- package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
- package/src/commands/code/auth-sync.ts +210 -210
- package/src/commands/code/errors.ts +11 -11
- package/src/commands/code/ports/auth-services.ts +7 -7
- package/src/commands/code/ports/command-runner.ts +2 -2
- package/src/commands/code/ports/file-store.ts +3 -3
- package/src/commands/code/ports/prompter.ts +13 -13
- package/src/commands/code/setup.ts +408 -406
- package/src/commands/code.ts +288 -287
- package/src/commands/index.ts +11 -10
- package/src/commands/models.ts +19 -18
- package/src/commands/users.ts +11 -10
- package/src/constants/command-structure.ts +159 -159
- package/src/services/api-key-service.ts +85 -85
- package/src/services/auth-service.ts +55 -54
- package/src/services/browser-auth.ts +62 -62
- package/src/services/chat-service.ts +170 -171
- package/src/services/cluster-service.ts +28 -28
- package/src/services/collaborator-service.ts +6 -6
- package/src/services/flux-service.ts +17 -17
- package/src/services/helm-service.ts +11 -11
- package/src/services/kubectl-service.ts +12 -12
- package/src/types/api.d.ts +1933 -1933
- package/src/types/json.d.ts +1 -1
- package/src/utils/config-checker.ts +7 -7
- package/src/utils/config-loader.ts +130 -129
- package/src/utils/default-api-key.ts +81 -80
- package/src/utils/env-manager.ts +37 -37
- package/src/utils/error-handler.ts +64 -64
- package/src/utils/logger.ts +72 -66
- package/src/utils/markdown-renderer.ts +28 -28
- package/src/utils/opencode-validator.ts +72 -71
- package/src/utils/token-manager.ts +69 -68
- package/tests/commands/chat.test.ts +32 -31
- package/tests/commands/code.test.ts +182 -181
- package/tests/utils/config-loader.test.ts +111 -110
- package/tests/utils/env-manager.test.ts +83 -79
- package/tests/utils/opencode-validator.test.ts +43 -42
- package/tsconfig.json +2 -1
- package/vitest.config.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiKeyServicePort } from
|
|
1
|
+
import type { ApiKeyServicePort } from '../ports/auth-services';
|
|
2
2
|
|
|
3
3
|
export class FakeApiKeyService implements ApiKeyServicePort {
|
|
4
4
|
private readonly _key: string;
|
|
@@ -7,7 +7,7 @@ export class FakeApiKeyService implements ApiKeyServicePort {
|
|
|
7
7
|
this._key = key;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
async create(_options: {
|
|
10
|
+
async create(_options: { description?: string; name: string }): Promise<{ key: string }> {
|
|
11
11
|
return { key: this._key };
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import type { AuthServicePort } from
|
|
2
|
-
|
|
3
|
-
function base64urlEncode(data: string): string {
|
|
4
|
-
return Buffer.from(data).toString("base64url");
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function makeJwt(payload: Record<string, unknown>): string {
|
|
8
|
-
const header = base64urlEncode(JSON.stringify({ alg: "none", typ: "JWT" }));
|
|
9
|
-
const body = base64urlEncode(JSON.stringify(payload));
|
|
10
|
-
return `${header}.${body}.signature`;
|
|
11
|
-
}
|
|
1
|
+
import type { AuthServicePort } from '../ports/auth-services';
|
|
12
2
|
|
|
13
3
|
export class FakeAuthService implements AuthServicePort {
|
|
14
4
|
loginCallCount = 0;
|
|
@@ -17,7 +7,7 @@ export class FakeAuthService implements AuthServicePort {
|
|
|
17
7
|
constructor(
|
|
18
8
|
private readonly _shouldSucceed: boolean,
|
|
19
9
|
private readonly _hasSeat: boolean = true,
|
|
20
|
-
private readonly _validToken: boolean = true
|
|
10
|
+
private readonly _validToken: boolean = true,
|
|
21
11
|
) {}
|
|
22
12
|
|
|
23
13
|
async login(): Promise<boolean> {
|
|
@@ -25,26 +15,36 @@ export class FakeAuthService implements AuthServicePort {
|
|
|
25
15
|
return this._shouldSucceed;
|
|
26
16
|
}
|
|
27
17
|
|
|
28
|
-
loginInteractive(): ReturnType<AuthServicePort[
|
|
18
|
+
loginInteractive(): ReturnType<AuthServicePort['loginInteractive']> {
|
|
29
19
|
this.loginInteractiveCallCount++;
|
|
30
20
|
if (!this._shouldSucceed) {
|
|
31
|
-
return Promise.resolve({
|
|
21
|
+
return Promise.resolve({ error: 'Login failed', success: false });
|
|
32
22
|
}
|
|
33
23
|
|
|
34
24
|
const farFuture = Math.floor(Date.now() / 1000) + 3600 * 24 * 365; // 1 year from now in seconds
|
|
35
25
|
|
|
36
26
|
const accessToken = this._validToken
|
|
37
27
|
? makeJwt({
|
|
38
|
-
realm_access: { roles: this._hasSeat ? ["berget_code_seat"] : ["default-roles-berget"] },
|
|
39
28
|
exp: farFuture,
|
|
29
|
+
realm_access: { roles: this._hasSeat ? ['berget_code_seat'] : ['default-roles-berget'] },
|
|
40
30
|
})
|
|
41
|
-
:
|
|
31
|
+
: 'invalid.token.here';
|
|
42
32
|
|
|
43
33
|
return Promise.resolve({
|
|
44
|
-
success: true,
|
|
45
34
|
accessToken,
|
|
46
|
-
refreshToken: "refresh",
|
|
47
35
|
expiresIn: 3600,
|
|
36
|
+
refreshToken: 'refresh',
|
|
37
|
+
success: true,
|
|
48
38
|
});
|
|
49
39
|
}
|
|
50
40
|
}
|
|
41
|
+
|
|
42
|
+
function base64urlEncode(data: string): string {
|
|
43
|
+
return Buffer.from(data).toString('base64url');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function makeJwt(payload: Record<string, unknown>): string {
|
|
47
|
+
const header = base64urlEncode(JSON.stringify({ alg: 'none', typ: 'JWT' }));
|
|
48
|
+
const body = base64urlEncode(JSON.stringify(payload));
|
|
49
|
+
return `${header}.${body}.signature`;
|
|
50
|
+
}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import type { CommandRunner } from
|
|
1
|
+
import type { CommandRunner } from '../ports/command-runner';
|
|
2
2
|
|
|
3
3
|
type Handler = {
|
|
4
|
-
match: (command: string,
|
|
5
|
-
response:
|
|
4
|
+
match: (command: string, arguments_: readonly string[]) => boolean;
|
|
5
|
+
response: ((command: string, arguments_: readonly string[]) => Error | string) | Error | string;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export class FakeCommandRunner implements CommandRunner {
|
|
9
|
+
get calls() {
|
|
10
|
+
return this._calls;
|
|
11
|
+
}
|
|
12
|
+
private _calls: Array<{ args: string[]; command: string; options?: { cwd?: string } }> = [];
|
|
13
|
+
|
|
9
14
|
private handlers: Handler[] = [];
|
|
10
|
-
private _calls: Array<{ command: string; args: string[]; options?: { cwd?: string } }> = [];
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
checkInstalled(binary: string): Promise<boolean> {
|
|
17
|
+
this._calls.push({ args: [], command: `check:${binary}` });
|
|
18
|
+
return Promise.resolve(this.handlers.some((h) => h.match(binary, ['--version'])) || false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handle(match: RegExp | string, response: Error | string): this {
|
|
13
22
|
this.handlers.push({
|
|
14
|
-
match: (cmd,
|
|
15
|
-
const full = `${cmd} ${
|
|
16
|
-
if (typeof match ===
|
|
23
|
+
match: (cmd, arguments_) => {
|
|
24
|
+
const full = `${cmd} ${arguments_.join(' ')}`;
|
|
25
|
+
if (typeof match === 'string') return full.startsWith(match);
|
|
17
26
|
return match.test(full);
|
|
18
27
|
},
|
|
19
28
|
response,
|
|
@@ -21,24 +30,21 @@ export class FakeCommandRunner implements CommandRunner {
|
|
|
21
30
|
return this;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
if (!handler) throw new Error(`Unexpected command: ${command} ${args.join(" ")}`);
|
|
33
|
+
async run(
|
|
34
|
+
command: string,
|
|
35
|
+
arguments_: readonly string[],
|
|
36
|
+
options?: { cwd?: string },
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
this._calls.push({ args: [...arguments_], command, options });
|
|
39
|
+
const handler = this.handlers.find((h) => h.match(command, arguments_));
|
|
40
|
+
if (!handler) throw new Error(`Unexpected command: ${command} ${arguments_.join(' ')}`);
|
|
33
41
|
|
|
34
42
|
const result =
|
|
35
|
-
typeof handler.response ===
|
|
43
|
+
typeof handler.response === 'function'
|
|
44
|
+
? handler.response(command, arguments_)
|
|
45
|
+
: handler.response;
|
|
36
46
|
|
|
37
47
|
if (result instanceof Error) throw result;
|
|
38
48
|
return result;
|
|
39
49
|
}
|
|
40
|
-
|
|
41
|
-
get calls() {
|
|
42
|
-
return this._calls;
|
|
43
|
-
}
|
|
44
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileStore } from
|
|
1
|
+
import type { FileStore } from '../ports/file-store';
|
|
2
2
|
|
|
3
3
|
export interface FileEntry {
|
|
4
4
|
content: string;
|
|
@@ -6,39 +6,39 @@ export interface FileEntry {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export class FakeFileStore implements FileStore {
|
|
9
|
-
private
|
|
9
|
+
private _chmodCalls: Array<{ mode: number; path: string }> = [];
|
|
10
10
|
private dirs: Set<string> = new Set();
|
|
11
|
-
private
|
|
11
|
+
private files: Map<string, string> = new Map();
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
this.
|
|
13
|
+
async chmod(path: string, mode: number): Promise<void> {
|
|
14
|
+
this._chmodCalls.push({ mode, path });
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
async exists(path: string): Promise<boolean> {
|
|
18
18
|
return this.files.has(path) || this.dirs.has(path);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
return this.
|
|
21
|
+
getChmodCalls(): Array<{ mode: number; path: string }> {
|
|
22
|
+
return this._chmodCalls;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
this.files
|
|
25
|
+
getWrittenFiles(): Map<string, string> {
|
|
26
|
+
return new Map(this.files);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async mkdir(path: string): Promise<void> {
|
|
30
30
|
this.dirs.add(path);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
async
|
|
34
|
-
this.
|
|
33
|
+
async readFile(path: string): Promise<null | string> {
|
|
34
|
+
return this.files.get(path) ?? null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
seed(path: string, content: string): void {
|
|
38
|
+
this.files.set(path, content);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
async writeFile(path: string, content: string): Promise<void> {
|
|
42
|
+
this.files.set(path, content);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -1,121 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Prompter, Spinner } from "../ports/prompter";
|
|
1
|
+
import type { Prompter, Spinner } from '../ports/prompter';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import { CancelledError } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const CANCEL = Symbol('cancel');
|
|
5
6
|
|
|
6
7
|
type PromptEntry =
|
|
7
|
-
| { kind:
|
|
8
|
-
| { kind:
|
|
9
|
-
| { kind:
|
|
10
|
-
| { kind:
|
|
11
|
-
|
|
12
|
-
export const select = <T>(value:
|
|
13
|
-
kind:
|
|
14
|
-
match: typeof match ===
|
|
15
|
-
response: typeof value ===
|
|
8
|
+
| { kind: 'confirm'; match?: RegExp; response: boolean | symbol }
|
|
9
|
+
| { kind: 'multiselect'; match?: RegExp; response: (string | symbol)[] }
|
|
10
|
+
| { kind: 'select'; match?: RegExp; response: string | symbol }
|
|
11
|
+
| { kind: 'text'; match?: RegExp; response: string | symbol };
|
|
12
|
+
|
|
13
|
+
export const select = <T>(value: symbol | T, match?: RegExp | string): PromptEntry => ({
|
|
14
|
+
kind: 'select',
|
|
15
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
16
|
+
response: typeof value === 'symbol' ? value : String(value),
|
|
16
17
|
});
|
|
17
18
|
|
|
18
|
-
export const text = (value: string | symbol, match?:
|
|
19
|
-
kind:
|
|
20
|
-
match: typeof match ===
|
|
19
|
+
export const text = (value: string | symbol, match?: RegExp | string): PromptEntry => ({
|
|
20
|
+
kind: 'text',
|
|
21
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
21
22
|
response: value,
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
export const confirm = (value: boolean | symbol, match?:
|
|
25
|
-
kind:
|
|
26
|
-
match: typeof match ===
|
|
25
|
+
export const confirm = (value: boolean | symbol, match?: RegExp | string): PromptEntry => ({
|
|
26
|
+
kind: 'confirm',
|
|
27
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
27
28
|
response: value,
|
|
28
29
|
});
|
|
29
30
|
|
|
30
|
-
export const multiselect = <T>(values: T[]
|
|
31
|
-
kind:
|
|
32
|
-
match: typeof match ===
|
|
33
|
-
response:
|
|
34
|
-
values === CANCEL ? [CANCEL] : ((values as T[]).map(v => String(v)) as (string | symbol)[]),
|
|
31
|
+
export const multiselect = <T>(values: symbol | T[], match?: RegExp | string): PromptEntry => ({
|
|
32
|
+
kind: 'multiselect',
|
|
33
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
34
|
+
response: values === CANCEL ? [CANCEL] : ((values as T[]).map(String) as (string | symbol)[]),
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
export class FakePrompter implements Prompter {
|
|
38
|
-
|
|
38
|
+
get calls() {
|
|
39
|
+
return this._calls;
|
|
40
|
+
}
|
|
41
|
+
private _calls: Array<{ args: unknown; method: string }> = [];
|
|
42
|
+
|
|
39
43
|
private _cursor = 0;
|
|
40
44
|
|
|
41
45
|
constructor(private readonly _script: PromptEntry[]) {}
|
|
42
|
-
|
|
46
|
+
assertExhausted() {
|
|
47
|
+
if (this._cursor !== this._script.length) {
|
|
48
|
+
throw new Error(`Script not exhausted: ${this._script.length - this._cursor} entries left`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async confirm(options: { message: string }): Promise<boolean> {
|
|
52
|
+
this._calls.push({ args: options, method: 'confirm' });
|
|
53
|
+
const entry = this._script[this._cursor++];
|
|
54
|
+
if (!entry)
|
|
55
|
+
throw new Error(`No script entry for confirm #${this._cursor} (${options.message})`);
|
|
56
|
+
if (entry.kind !== 'confirm')
|
|
57
|
+
throw new Error(`Expected confirm, got ${entry.kind} for ${options.message}`);
|
|
58
|
+
if (entry.match && !entry.match.test(options.message))
|
|
59
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
60
|
+
if (entry.response === CANCEL) throw new CancelledError();
|
|
61
|
+
return entry.response as boolean;
|
|
62
|
+
}
|
|
43
63
|
intro(message: string): void {
|
|
44
|
-
this._calls.push({
|
|
64
|
+
this._calls.push({ args: { message }, method: 'intro' });
|
|
45
65
|
}
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
|
|
67
|
+
async multiselect<T>(options: { message: string }): Promise<T[]> {
|
|
68
|
+
this._calls.push({ args: options, method: 'multiselect' });
|
|
69
|
+
const entry = this._script[this._cursor++];
|
|
70
|
+
if (!entry)
|
|
71
|
+
throw new Error(`No script entry for multiselect #${this._cursor} (${options.message})`);
|
|
72
|
+
if (entry.kind !== 'multiselect')
|
|
73
|
+
throw new Error(`Expected multiselect, got ${entry.kind} for ${options.message}`);
|
|
74
|
+
if (entry.match && !entry.match.test(options.message))
|
|
75
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
76
|
+
if (entry.response.includes(CANCEL)) throw new CancelledError();
|
|
77
|
+
return entry.response as T[];
|
|
48
78
|
}
|
|
79
|
+
|
|
49
80
|
note(message: string, title?: string): void {
|
|
50
|
-
this._calls.push({
|
|
81
|
+
this._calls.push({ args: { message, title }, method: 'note' });
|
|
51
82
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this._calls.push({ method: "spinner.start", args: { message: msg } });
|
|
56
|
-
},
|
|
57
|
-
stop: (msg: string) => {
|
|
58
|
-
this._calls.push({ method: "spinner.stop", args: { message: msg } });
|
|
59
|
-
},
|
|
60
|
-
};
|
|
83
|
+
|
|
84
|
+
outro(message: string): void {
|
|
85
|
+
this._calls.push({ args: { message }, method: 'outro' });
|
|
61
86
|
}
|
|
62
87
|
|
|
63
|
-
async select<T>(
|
|
64
|
-
this._calls.push({
|
|
88
|
+
async select<T>(options: { message: string }): Promise<T> {
|
|
89
|
+
this._calls.push({ args: options, method: 'select' });
|
|
65
90
|
const entry = this._script[this._cursor++];
|
|
66
|
-
if (!entry) throw new Error(`No script entry for select #${this._cursor} (${
|
|
67
|
-
if (entry.kind !==
|
|
68
|
-
throw new Error(`Expected select, got ${entry.kind} for ${
|
|
69
|
-
if (entry.match && !entry.match.test(
|
|
70
|
-
throw new Error(`Message mismatch: got "${
|
|
91
|
+
if (!entry) throw new Error(`No script entry for select #${this._cursor} (${options.message})`);
|
|
92
|
+
if (entry.kind !== 'select')
|
|
93
|
+
throw new Error(`Expected select, got ${entry.kind} for ${options.message}`);
|
|
94
|
+
if (entry.match && !entry.match.test(options.message))
|
|
95
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
71
96
|
if (entry.response === CANCEL) throw new CancelledError();
|
|
72
97
|
return entry.response as T;
|
|
73
98
|
}
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return entry.response as boolean;
|
|
100
|
+
spinner(): Spinner {
|
|
101
|
+
return {
|
|
102
|
+
start: (message: string) => {
|
|
103
|
+
this._calls.push({ args: { message: message }, method: 'spinner.start' });
|
|
104
|
+
},
|
|
105
|
+
stop: (message: string) => {
|
|
106
|
+
this._calls.push({ args: { message: message }, method: 'spinner.stop' });
|
|
107
|
+
},
|
|
108
|
+
};
|
|
85
109
|
}
|
|
86
110
|
|
|
87
|
-
async text(
|
|
88
|
-
this._calls.push({
|
|
111
|
+
async text(options: { message: string }): Promise<string> {
|
|
112
|
+
this._calls.push({ args: options, method: 'text' });
|
|
89
113
|
const entry = this._script[this._cursor++];
|
|
90
|
-
if (!entry) throw new Error(`No script entry for text #${this._cursor} (${
|
|
91
|
-
if (entry.kind !==
|
|
92
|
-
throw new Error(`Expected text, got ${entry.kind} for ${
|
|
93
|
-
if (entry.match && !entry.match.test(
|
|
94
|
-
throw new Error(`Message mismatch: got "${
|
|
114
|
+
if (!entry) throw new Error(`No script entry for text #${this._cursor} (${options.message})`);
|
|
115
|
+
if (entry.kind !== 'text')
|
|
116
|
+
throw new Error(`Expected text, got ${entry.kind} for ${options.message}`);
|
|
117
|
+
if (entry.match && !entry.match.test(options.message))
|
|
118
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
95
119
|
if (entry.response === CANCEL) throw new CancelledError();
|
|
96
120
|
return entry.response as string;
|
|
97
121
|
}
|
|
98
|
-
|
|
99
|
-
async multiselect<T>(opts: { message: string }): Promise<T[]> {
|
|
100
|
-
this._calls.push({ method: "multiselect", args: opts });
|
|
101
|
-
const entry = this._script[this._cursor++];
|
|
102
|
-
if (!entry)
|
|
103
|
-
throw new Error(`No script entry for multiselect #${this._cursor} (${opts.message})`);
|
|
104
|
-
if (entry.kind !== "multiselect")
|
|
105
|
-
throw new Error(`Expected multiselect, got ${entry.kind} for ${opts.message}`);
|
|
106
|
-
if (entry.match && !entry.match.test(opts.message))
|
|
107
|
-
throw new Error(`Message mismatch: got "${opts.message}"`);
|
|
108
|
-
if (entry.response.includes(CANCEL)) throw new CancelledError();
|
|
109
|
-
return entry.response as T[];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get calls() {
|
|
113
|
-
return this._calls;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
assertExhausted() {
|
|
117
|
-
if (this._cursor !== this._script.length) {
|
|
118
|
-
throw new Error(`Script not exhausted: ${this._script.length - this._cursor} entries left`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
122
|
}
|