@usestatemachines/cli 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/agents/bundled.d.ts +5 -0
- package/dist/agents/bundled.js +6 -0
- package/dist/agents/destinations.d.ts +13 -0
- package/dist/agents/destinations.js +16 -0
- package/dist/api-internal.d.ts +62 -0
- package/dist/api-internal.js +348 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +4 -0
- package/dist/command.d.ts +119 -0
- package/dist/command.js +195 -0
- package/dist/commands/api/command.d.ts +15 -0
- package/dist/commands/api/command.js +38 -0
- package/dist/commands/api/run.d.ts +4 -0
- package/dist/commands/api/run.js +24 -0
- package/dist/commands/apps/command.d.ts +19 -0
- package/dist/commands/apps/command.js +29 -0
- package/dist/commands/apps/list.d.ts +8 -0
- package/dist/commands/apps/list.js +10 -0
- package/dist/commands/connect/command.d.ts +34 -0
- package/dist/commands/connect/command.js +70 -0
- package/dist/commands/connect/run.d.ts +10 -0
- package/dist/commands/connect/run.js +71 -0
- package/dist/commands/doctor/command.d.ts +29 -0
- package/dist/commands/doctor/command.js +36 -0
- package/dist/commands/doctor/run.d.ts +13 -0
- package/dist/commands/doctor/run.js +12 -0
- package/dist/commands/env/command.d.ts +5 -0
- package/dist/commands/env/command.js +19 -0
- package/dist/commands/env/pull.d.ts +6 -0
- package/dist/commands/env/pull.js +6 -0
- package/dist/commands/environments/command.d.ts +121 -0
- package/dist/commands/environments/command.js +170 -0
- package/dist/commands/environments/create.d.ts +9 -0
- package/dist/commands/environments/create.js +42 -0
- package/dist/commands/environments/delete.d.ts +7 -0
- package/dist/commands/environments/delete.js +48 -0
- package/dist/commands/environments/get.d.ts +39 -0
- package/dist/commands/environments/get.js +5 -0
- package/dist/commands/environments/list.d.ts +41 -0
- package/dist/commands/environments/list.js +18 -0
- package/dist/commands/environments/wait.d.ts +39 -0
- package/dist/commands/environments/wait.js +21 -0
- package/dist/commands/init/command.d.ts +68 -0
- package/dist/commands/init/command.js +90 -0
- package/dist/commands/init/run.d.ts +8 -0
- package/dist/commands/init/run.js +422 -0
- package/dist/commands/link/command.d.ts +13 -0
- package/dist/commands/link/command.js +32 -0
- package/dist/commands/link/run.d.ts +12 -0
- package/dist/commands/link/run.js +7 -0
- package/dist/commands/login/command.d.ts +1 -0
- package/dist/commands/login/command.js +43 -0
- package/dist/commands/login/run.d.ts +30 -0
- package/dist/commands/login/run.js +43 -0
- package/dist/commands/login/schema.d.ts +33 -0
- package/dist/commands/login/schema.js +33 -0
- package/dist/commands/logout/command.d.ts +1 -0
- package/dist/commands/logout/command.js +24 -0
- package/dist/commands/logout/run.d.ts +10 -0
- package/dist/commands/logout/run.js +19 -0
- package/dist/commands/logout/schema.d.ts +10 -0
- package/dist/commands/logout/schema.js +10 -0
- package/dist/commands/profiles/command.d.ts +2 -0
- package/dist/commands/profiles/command.js +29 -0
- package/dist/commands/profiles/list.d.ts +20 -0
- package/dist/commands/profiles/list.js +10 -0
- package/dist/commands/profiles/schema.d.ts +24 -0
- package/dist/commands/profiles/schema.js +19 -0
- package/dist/commands/profiles/use.d.ts +7 -0
- package/dist/commands/profiles/use.js +7 -0
- package/dist/commands/requests/command.d.ts +39 -0
- package/dist/commands/requests/command.js +65 -0
- package/dist/commands/requests/get.d.ts +32 -0
- package/dist/commands/requests/get.js +15 -0
- package/dist/commands/requests/list.d.ts +27 -0
- package/dist/commands/requests/list.js +25 -0
- package/dist/commands/snapshots/command.d.ts +48 -0
- package/dist/commands/snapshots/command.js +80 -0
- package/dist/commands/snapshots/create.d.ts +7 -0
- package/dist/commands/snapshots/create.js +26 -0
- package/dist/commands/snapshots/delete.d.ts +34 -0
- package/dist/commands/snapshots/delete.js +8 -0
- package/dist/commands/snapshots/get.d.ts +35 -0
- package/dist/commands/snapshots/get.js +5 -0
- package/dist/commands/snapshots/list.d.ts +37 -0
- package/dist/commands/snapshots/list.js +10 -0
- package/dist/commands/unlink/command.d.ts +1 -0
- package/dist/commands/unlink/command.js +15 -0
- package/dist/commands/unlink/run.d.ts +7 -0
- package/dist/commands/unlink/run.js +8 -0
- package/dist/commands/whoami/command.d.ts +28 -0
- package/dist/commands/whoami/command.js +44 -0
- package/dist/commands/whoami/run.d.ts +5 -0
- package/dist/commands/whoami/run.js +39 -0
- package/dist/commands/workspaces/command.d.ts +33 -0
- package/dist/commands/workspaces/command.js +73 -0
- package/dist/commands/workspaces/list.d.ts +5 -0
- package/dist/commands/workspaces/list.js +42 -0
- package/dist/commands/workspaces/use.d.ts +5 -0
- package/dist/commands/workspaces/use.js +18 -0
- package/dist/config/project.d.ts +21 -0
- package/dist/config/project.js +105 -0
- package/dist/config/schema.d.ts +616 -0
- package/dist/config/schema.js +282 -0
- package/dist/context-types.d.ts +89 -0
- package/dist/context-types.js +56 -0
- package/dist/context.d.ts +13 -0
- package/dist/context.js +290 -0
- package/dist/credentials/sealing.d.ts +10 -0
- package/dist/credentials/sealing.js +72 -0
- package/dist/credentials/store.d.ts +17 -0
- package/dist/credentials/store.js +156 -0
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.js +239 -0
- package/dist/env-file.d.ts +10 -0
- package/dist/env-file.js +254 -0
- package/dist/errors.d.ts +784 -0
- package/dist/errors.js +459 -0
- package/dist/flags.d.ts +22 -0
- package/dist/flags.js +56 -0
- package/dist/generated/internal.d.ts +138 -0
- package/dist/generated/internal.js +2627 -0
- package/dist/generated/retry.d.ts +1 -0
- package/dist/generated/retry.js +11 -0
- package/dist/help.d.ts +408 -0
- package/dist/help.js +220 -0
- package/dist/io-types.d.ts +127 -0
- package/dist/io-types.js +1 -0
- package/dist/io.d.ts +13 -0
- package/dist/io.js +1017 -0
- package/dist/keys.d.ts +50 -0
- package/dist/keys.js +691 -0
- package/dist/link.d.ts +6 -0
- package/dist/link.js +58 -0
- package/dist/login.d.ts +92 -0
- package/dist/login.js +965 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +126 -0
- package/dist/native-helper.d.ts +1 -0
- package/dist/native-helper.js +2 -0
- package/dist/output.d.ts +8 -0
- package/dist/output.js +135 -0
- package/dist/parser.d.ts +28 -0
- package/dist/parser.js +250 -0
- package/dist/program-types.d.ts +67 -0
- package/dist/program-types.js +14 -0
- package/dist/programs.d.ts +2 -0
- package/dist/programs.js +44 -0
- package/dist/project-roots.d.ts +3 -0
- package/dist/project-roots.js +26 -0
- package/dist/prompt.d.ts +3 -0
- package/dist/prompt.js +61 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +44 -0
- package/dist/resources/create.d.ts +9 -0
- package/dist/resources/create.js +55 -0
- package/dist/resources/read.d.ts +15 -0
- package/dist/resources/read.js +69 -0
- package/dist/resources/schema.d.ts +177 -0
- package/dist/resources/schema.js +158 -0
- package/dist/text.d.ts +3 -0
- package/dist/text.js +19 -0
- package/dist/update.d.ts +22 -0
- package/dist/update.js +126 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +52 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ContextNeed, ContextResolvers, Contexts, Presentation } from './context-types.js';
|
|
3
|
+
import { type ExitCode } from './errors.js';
|
|
4
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
5
|
+
readonly [key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
export interface ArgSpec {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly key: string;
|
|
10
|
+
readonly required: boolean;
|
|
11
|
+
readonly many: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type FlagSpec = {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly description: string;
|
|
17
|
+
readonly short?: string;
|
|
18
|
+
} & ({
|
|
19
|
+
readonly type: 'boolean';
|
|
20
|
+
readonly negatedName: string | null;
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: 'string' | 'string[]';
|
|
23
|
+
});
|
|
24
|
+
export interface CommandSyntax {
|
|
25
|
+
readonly name: readonly [string, ...string[]];
|
|
26
|
+
readonly arguments: readonly ArgSpec[];
|
|
27
|
+
readonly flags: readonly FlagSpec[];
|
|
28
|
+
}
|
|
29
|
+
export interface CommandHelp extends CommandSyntax {
|
|
30
|
+
readonly summary: string;
|
|
31
|
+
readonly need: ContextNeed;
|
|
32
|
+
readonly destructive: boolean;
|
|
33
|
+
readonly confirmation: 'before-run' | 'handler';
|
|
34
|
+
readonly streams: boolean;
|
|
35
|
+
readonly acceptsWorkspace: boolean;
|
|
36
|
+
readonly output: 'structured' | 'raw' | 'passthrough';
|
|
37
|
+
readonly outputException: 'dotenv' | null;
|
|
38
|
+
readonly inputSchema: JsonValue;
|
|
39
|
+
readonly resultSchema: JsonValue;
|
|
40
|
+
readonly progressSchema: JsonValue;
|
|
41
|
+
readonly exitCodes: readonly ExitCode[];
|
|
42
|
+
}
|
|
43
|
+
export type CommandEvent<Result, Progress> = {
|
|
44
|
+
readonly kind: 'progress';
|
|
45
|
+
readonly value: Progress;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: 'result';
|
|
48
|
+
readonly value: Result;
|
|
49
|
+
};
|
|
50
|
+
export type CommandExecution<Result, Progress> = Promise<Result> | AsyncIterable<CommandEvent<Result, Progress>>;
|
|
51
|
+
export interface CommandModule<Input, Result, Progress, Need extends ContextNeed> {
|
|
52
|
+
run(input: Input, context: Contexts[Need]): CommandExecution<Result, Progress>;
|
|
53
|
+
text(result: Result, input: Input): string;
|
|
54
|
+
outputFormat?(input: Input): 'text' | 'json' | null;
|
|
55
|
+
literalText?(result: Result, input: Input): string | null;
|
|
56
|
+
progressText?(progress: Progress): string;
|
|
57
|
+
exitCode?(result: Result): ExitCode;
|
|
58
|
+
}
|
|
59
|
+
interface DefinitionBase<Shape extends z.ZodRawShape, Need extends ContextNeed> extends CommandSyntax {
|
|
60
|
+
readonly summary: string;
|
|
61
|
+
readonly need: Need;
|
|
62
|
+
readonly destructive: boolean;
|
|
63
|
+
readonly confirmation?: 'before-run' | 'handler';
|
|
64
|
+
readonly outputException?: 'dotenv';
|
|
65
|
+
readonly workspace?: boolean;
|
|
66
|
+
readonly input: z.ZodObject<Shape>;
|
|
67
|
+
}
|
|
68
|
+
export interface CommandDefinition<Shape extends z.ZodRawShape, Result extends JsonValue, Progress extends JsonValue, Need extends ContextNeed> extends DefinitionBase<Shape, Need> {
|
|
69
|
+
readonly streams: boolean;
|
|
70
|
+
readonly result: z.ZodType<Result>;
|
|
71
|
+
readonly progress: z.ZodType<Progress>;
|
|
72
|
+
load(): Promise<CommandModule<z.output<z.ZodObject<Shape>>, Result, Progress, Need>>;
|
|
73
|
+
}
|
|
74
|
+
export interface RawHttpResponse {
|
|
75
|
+
readonly status: number;
|
|
76
|
+
readonly body: AsyncIterable<Uint8Array>;
|
|
77
|
+
}
|
|
78
|
+
export interface RawCommandDefinition<Shape extends z.ZodRawShape, Need extends ContextNeed> extends DefinitionBase<Shape, Need> {
|
|
79
|
+
load(): Promise<{
|
|
80
|
+
run(input: z.output<z.ZodObject<Shape>>, context: Contexts[Need]): Promise<RawHttpResponse>;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
export interface CommandRuntime {
|
|
84
|
+
readonly contexts: ContextResolvers;
|
|
85
|
+
readonly presentation: Presentation;
|
|
86
|
+
}
|
|
87
|
+
export type Printable = {
|
|
88
|
+
readonly kind: 'progress';
|
|
89
|
+
readonly json: JsonValue;
|
|
90
|
+
readonly always?: boolean;
|
|
91
|
+
text?(): string;
|
|
92
|
+
} | {
|
|
93
|
+
readonly kind: 'result';
|
|
94
|
+
readonly json: JsonValue;
|
|
95
|
+
readonly exitCode: ExitCode;
|
|
96
|
+
readonly format?: 'text' | 'json' | null;
|
|
97
|
+
text(): string;
|
|
98
|
+
} | {
|
|
99
|
+
readonly kind: 'raw';
|
|
100
|
+
readonly response: RawHttpResponse;
|
|
101
|
+
} | {
|
|
102
|
+
readonly kind: 'literal';
|
|
103
|
+
readonly text: string;
|
|
104
|
+
readonly exitCode: ExitCode;
|
|
105
|
+
};
|
|
106
|
+
export interface RegisteredCommand {
|
|
107
|
+
readonly syntax: CommandSyntax;
|
|
108
|
+
readonly help: CommandHelp;
|
|
109
|
+
validate(raw: unknown): void;
|
|
110
|
+
invoke(raw: unknown, runtime: CommandRuntime): AsyncIterable<Printable>;
|
|
111
|
+
}
|
|
112
|
+
export declare function commandHelp(base: Omit<CommandHelp, 'inputSchema' | 'resultSchema' | 'progressSchema'>, schemas: {
|
|
113
|
+
readonly input: () => JsonValue;
|
|
114
|
+
readonly result: () => JsonValue;
|
|
115
|
+
readonly progress: () => JsonValue;
|
|
116
|
+
}): CommandHelp;
|
|
117
|
+
export declare function defineCommand<Shape extends z.ZodRawShape, Result extends JsonValue, Progress extends JsonValue, Need extends ContextNeed>(definition: CommandDefinition<Shape, Result, Progress, Need>): RegisteredCommand;
|
|
118
|
+
export declare function defineRawCommand<Shape extends z.ZodRawShape, Need extends ContextNeed>(definition: RawCommandDefinition<Shape, Need>): RegisteredCommand;
|
|
119
|
+
export {};
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CliError, exitCodeHelp } from './errors.js';
|
|
3
|
+
import { untrustedResult } from './text.js';
|
|
4
|
+
function matchesInputType(schema, type) {
|
|
5
|
+
if (schema === undefined || typeof schema === 'boolean') {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (schema.type === 'null') {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (schema.anyOf) {
|
|
12
|
+
return schema.anyOf.every((part) => matchesInputType(part, type));
|
|
13
|
+
}
|
|
14
|
+
const expected = type === 'string[]' ? 'array' : type;
|
|
15
|
+
const matches = Array.isArray(schema.type)
|
|
16
|
+
? schema.type.includes(expected) &&
|
|
17
|
+
schema.type.every((value) => value === expected || value === 'null')
|
|
18
|
+
: schema.type === expected;
|
|
19
|
+
if (!matches) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return (type !== 'string[]' ||
|
|
23
|
+
(!Array.isArray(schema.items) && matchesInputType(schema.items, 'string')));
|
|
24
|
+
}
|
|
25
|
+
function validateMetadata(definition) {
|
|
26
|
+
const keys = [...definition.arguments, ...definition.flags].map((field) => field.key);
|
|
27
|
+
const names = definition.flags.flatMap((flag) => flag.type === 'boolean' && flag.negatedName !== null
|
|
28
|
+
? [flag.name, flag.negatedName]
|
|
29
|
+
: [flag.name]);
|
|
30
|
+
const schemaKeys = Object.keys(definition.input.shape);
|
|
31
|
+
if (new Set(keys).size !== keys.length ||
|
|
32
|
+
new Set(names).size !== names.length ||
|
|
33
|
+
schemaKeys.length !== keys.length ||
|
|
34
|
+
schemaKeys.some((key) => !keys.includes(key))) {
|
|
35
|
+
throw new Error('Command arguments and flags must describe each input schema field once.');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function inputJsonSchema(definition) {
|
|
39
|
+
const inputSchema = z.toJSONSchema(definition.input, { io: 'input' });
|
|
40
|
+
if (definition.flags.some((flag) => !matchesInputType(inputSchema.properties?.[flag.key], flag.type)) ||
|
|
41
|
+
definition.arguments.some((argument) => !matchesInputType(inputSchema.properties?.[argument.key], argument.many ? 'string[]' : 'string') ||
|
|
42
|
+
argument.required !==
|
|
43
|
+
(inputSchema.required?.includes(argument.key) ?? false))) {
|
|
44
|
+
throw new Error('Command argument and flag types must match their input schema fields.');
|
|
45
|
+
}
|
|
46
|
+
return z.json().parse(inputSchema);
|
|
47
|
+
}
|
|
48
|
+
function once(compute) {
|
|
49
|
+
let cached = null;
|
|
50
|
+
return () => {
|
|
51
|
+
cached ??= { value: compute() };
|
|
52
|
+
return cached.value;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function commandHelp(base, schemas) {
|
|
56
|
+
const input = once(schemas.input);
|
|
57
|
+
const result = once(schemas.result);
|
|
58
|
+
const progress = once(schemas.progress);
|
|
59
|
+
return {
|
|
60
|
+
...base,
|
|
61
|
+
get inputSchema() {
|
|
62
|
+
return input();
|
|
63
|
+
},
|
|
64
|
+
get resultSchema() {
|
|
65
|
+
return result();
|
|
66
|
+
},
|
|
67
|
+
get progressSchema() {
|
|
68
|
+
return progress();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function helpBase(definition) {
|
|
73
|
+
validateMetadata(definition);
|
|
74
|
+
return {
|
|
75
|
+
name: definition.name,
|
|
76
|
+
summary: definition.summary,
|
|
77
|
+
arguments: definition.arguments,
|
|
78
|
+
flags: definition.flags,
|
|
79
|
+
need: definition.need,
|
|
80
|
+
destructive: definition.destructive,
|
|
81
|
+
confirmation: definition.confirmation ?? 'before-run',
|
|
82
|
+
outputException: definition.outputException ?? null,
|
|
83
|
+
acceptsWorkspace: definition.workspace ?? definition.need === 'workspace',
|
|
84
|
+
exitCodes: exitCodeHelp.map((entry) => entry.code),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function parseInput(definition, raw) {
|
|
88
|
+
const parsed = definition.input.safeParse(raw);
|
|
89
|
+
if (!parsed.success) {
|
|
90
|
+
const custom = parsed.error.issues.find((issue) => issue.code === 'custom');
|
|
91
|
+
throw new CliError('cli.invalid_arguments', custom === undefined ? {} : { message: custom.message });
|
|
92
|
+
}
|
|
93
|
+
return parsed.data;
|
|
94
|
+
}
|
|
95
|
+
function validateValue(schema, raw) {
|
|
96
|
+
const parsed = schema.safeParse(raw);
|
|
97
|
+
if (!parsed.success) {
|
|
98
|
+
throw new CliError('cli.invalid_result');
|
|
99
|
+
}
|
|
100
|
+
return parsed.data;
|
|
101
|
+
}
|
|
102
|
+
export function defineCommand(definition) {
|
|
103
|
+
const help = commandHelp({
|
|
104
|
+
...helpBase(definition),
|
|
105
|
+
streams: definition.streams,
|
|
106
|
+
output: 'structured',
|
|
107
|
+
}, {
|
|
108
|
+
input: () => inputJsonSchema(definition),
|
|
109
|
+
result: () => z.json().parse(z.toJSONSchema(definition.result)),
|
|
110
|
+
progress: () => z.json().parse(z.toJSONSchema(definition.progress)),
|
|
111
|
+
});
|
|
112
|
+
const eventSchema = z.strictObject({
|
|
113
|
+
kind: z.enum(['progress', 'result']),
|
|
114
|
+
value: z.unknown(),
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
syntax: definition,
|
|
118
|
+
help,
|
|
119
|
+
validate(raw) {
|
|
120
|
+
parseInput(definition, raw);
|
|
121
|
+
},
|
|
122
|
+
async *invoke(raw, runtime) {
|
|
123
|
+
const input = parseInput(definition, raw);
|
|
124
|
+
const context = await runtime.contexts[definition.need]();
|
|
125
|
+
const handler = await definition.load();
|
|
126
|
+
const execution = handler.run(input, context);
|
|
127
|
+
function resultOutput(result) {
|
|
128
|
+
const literal = handler.literalText?.(result, input);
|
|
129
|
+
const exitCode = handler.exitCode?.(result) ?? 0;
|
|
130
|
+
if (literal !== undefined && literal !== null) {
|
|
131
|
+
if (definition.outputException !== 'dotenv') {
|
|
132
|
+
throw new CliError('cli.invalid_result');
|
|
133
|
+
}
|
|
134
|
+
return { kind: 'literal', text: literal, exitCode };
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
kind: 'result',
|
|
138
|
+
json: result,
|
|
139
|
+
exitCode,
|
|
140
|
+
format: handler.outputFormat?.(input) ?? null,
|
|
141
|
+
text: () => handler.text(validateValue(definition.result, untrustedResult(result)), input),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (!(Symbol.asyncIterator in execution)) {
|
|
145
|
+
const result = validateValue(definition.result, await execution);
|
|
146
|
+
yield resultOutput(result);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let final = null;
|
|
150
|
+
for await (const rawEvent of execution) {
|
|
151
|
+
if (final !== null) {
|
|
152
|
+
throw new CliError('cli.invalid_result');
|
|
153
|
+
}
|
|
154
|
+
const event = validateValue(eventSchema, rawEvent);
|
|
155
|
+
if (event.kind === 'progress') {
|
|
156
|
+
const progress = validateValue(definition.progress, event.value);
|
|
157
|
+
yield {
|
|
158
|
+
kind: 'progress',
|
|
159
|
+
json: progress,
|
|
160
|
+
always: !definition.streams,
|
|
161
|
+
text: () => handler.progressText?.(validateValue(definition.progress, untrustedResult(progress))) ?? JSON.stringify(untrustedResult(progress)),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
final = { value: validateValue(definition.result, event.value) };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (final === null) {
|
|
169
|
+
throw new CliError('cli.invalid_result');
|
|
170
|
+
}
|
|
171
|
+
const result = final.value;
|
|
172
|
+
yield resultOutput(result);
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
export function defineRawCommand(definition) {
|
|
177
|
+
const help = commandHelp({ ...helpBase(definition), streams: false, output: 'raw' }, {
|
|
178
|
+
input: () => inputJsonSchema(definition),
|
|
179
|
+
result: () => null,
|
|
180
|
+
progress: () => null,
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
syntax: definition,
|
|
184
|
+
help,
|
|
185
|
+
validate(raw) {
|
|
186
|
+
parseInput(definition, raw);
|
|
187
|
+
},
|
|
188
|
+
async *invoke(raw, runtime) {
|
|
189
|
+
const input = parseInput(definition, raw);
|
|
190
|
+
const context = await runtime.contexts[definition.need]();
|
|
191
|
+
const handler = await definition.load();
|
|
192
|
+
yield { kind: 'raw', response: await handler.run(input, context) };
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const apiInputSchema: z.ZodObject<{
|
|
3
|
+
method: z.ZodEnum<{
|
|
4
|
+
DELETE: "DELETE";
|
|
5
|
+
GET: "GET";
|
|
6
|
+
HEAD: "HEAD";
|
|
7
|
+
OPTIONS: "OPTIONS";
|
|
8
|
+
PATCH: "PATCH";
|
|
9
|
+
POST: "POST";
|
|
10
|
+
PUT: "PUT";
|
|
11
|
+
}>;
|
|
12
|
+
path: z.ZodString;
|
|
13
|
+
data: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export declare const apiCommand: import("../../command.js").RegisteredCommand;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineRawCommand } from '../../command.js';
|
|
3
|
+
export const apiInputSchema = z
|
|
4
|
+
.strictObject({
|
|
5
|
+
method: z.enum([
|
|
6
|
+
'GET',
|
|
7
|
+
'POST',
|
|
8
|
+
'PUT',
|
|
9
|
+
'PATCH',
|
|
10
|
+
'DELETE',
|
|
11
|
+
'HEAD',
|
|
12
|
+
'OPTIONS',
|
|
13
|
+
]),
|
|
14
|
+
path: z.string().min(1),
|
|
15
|
+
data: z.string().optional(),
|
|
16
|
+
})
|
|
17
|
+
.refine((input) => input.data === undefined ||
|
|
18
|
+
(input.method !== 'GET' && input.method !== 'HEAD'), { message: 'GET and HEAD requests cannot include --data.' });
|
|
19
|
+
export const apiCommand = defineRawCommand({
|
|
20
|
+
name: ['api'],
|
|
21
|
+
summary: 'Send one API request and print its raw body. Exit 3 for 401, and 1 for other responses outside 2xx. Resource IDs are not checked against a linked workspace.',
|
|
22
|
+
need: 'account',
|
|
23
|
+
destructive: false,
|
|
24
|
+
arguments: [
|
|
25
|
+
{ name: 'method', key: 'method', required: true, many: false },
|
|
26
|
+
{ name: 'path', key: 'path', required: true, many: false },
|
|
27
|
+
],
|
|
28
|
+
flags: [
|
|
29
|
+
{
|
|
30
|
+
name: 'data',
|
|
31
|
+
key: 'data',
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'JSON request data, or @file.',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
input: apiInputSchema,
|
|
37
|
+
load: () => import('./run.js'),
|
|
38
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { AccountContext } from '../../context-types.js';
|
|
3
|
+
import type { apiInputSchema } from './command.js';
|
|
4
|
+
export declare function run(input: z.infer<typeof apiInputSchema>, context: AccountContext): Promise<import("../../command.js").RawHttpResponse>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
3
|
+
export async function run(input, context) {
|
|
4
|
+
let data = input.data ?? null;
|
|
5
|
+
if (data?.startsWith('@')) {
|
|
6
|
+
data = await context.io.files.read(resolve(context.io.host.cwd, data.slice(1)), { signal: context.io.signal, deadline: null });
|
|
7
|
+
if (data === null) {
|
|
8
|
+
throw new CliError('cli.invalid_arguments', {
|
|
9
|
+
message: 'The request data file was not found.',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (data !== null) {
|
|
14
|
+
try {
|
|
15
|
+
JSON.parse(data);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new CliError('cli.invalid_arguments', {
|
|
19
|
+
message: 'Request data must be valid JSON.',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return context.internal.raw({ method: input.method, path: input.path, data });
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const listInputSchema: z.ZodObject<{
|
|
3
|
+
limit: z.ZodOptional<z.ZodString>;
|
|
4
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
5
|
+
all: z.ZodDefault<z.ZodBoolean>;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export declare const listResultSchema: z.ZodObject<{
|
|
8
|
+
data: z.ZodArray<z.ZodObject<{
|
|
9
|
+
id: z.ZodString;
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
description: z.ZodString;
|
|
12
|
+
vendor: z.ZodString;
|
|
13
|
+
type: z.ZodLiteral<"replica">;
|
|
14
|
+
logoPath: z.ZodString;
|
|
15
|
+
defaultVersionId: z.ZodNullable<z.ZodString>;
|
|
16
|
+
}, z.core.$strict>>;
|
|
17
|
+
nextCursor: z.ZodNullable<z.ZodString>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export declare const appsListCommand: import("../../command.js").RegisteredCommand;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineCommand } from '../../command.js';
|
|
3
|
+
import { pageFlags, pageInput } from '../../resources/schema.js';
|
|
4
|
+
export const listInputSchema = z.strictObject(pageInput);
|
|
5
|
+
export const listResultSchema = z.strictObject({
|
|
6
|
+
data: z.array(z.strictObject({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
name: z.string(),
|
|
9
|
+
description: z.string(),
|
|
10
|
+
vendor: z.string(),
|
|
11
|
+
type: z.literal('replica'),
|
|
12
|
+
logoPath: z.string(),
|
|
13
|
+
defaultVersionId: z.string().nullable(),
|
|
14
|
+
})),
|
|
15
|
+
nextCursor: z.string().nullable(),
|
|
16
|
+
});
|
|
17
|
+
export const appsListCommand = defineCommand({
|
|
18
|
+
name: ['apps', 'list'],
|
|
19
|
+
summary: 'List apps and their default versions.',
|
|
20
|
+
need: 'account',
|
|
21
|
+
destructive: false,
|
|
22
|
+
streams: false,
|
|
23
|
+
arguments: [],
|
|
24
|
+
flags: pageFlags,
|
|
25
|
+
input: listInputSchema,
|
|
26
|
+
result: listResultSchema,
|
|
27
|
+
progress: z.never(),
|
|
28
|
+
load: () => import('./list.js'),
|
|
29
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { AccountContext } from '../../context-types.js';
|
|
3
|
+
import type { listInputSchema, listResultSchema } from './command.js';
|
|
4
|
+
export declare function run(input: z.infer<typeof listInputSchema>, context: AccountContext): Promise<{
|
|
5
|
+
data: import("@usestatemachines/sdk").App[];
|
|
6
|
+
nextCursor: string | null;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function text(result: z.infer<typeof listResultSchema>): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { pages } from '../../resources/read.js';
|
|
2
|
+
export async function run(input, context) {
|
|
3
|
+
const page = await pages(input, (query) => context.sdk.apps.list(query));
|
|
4
|
+
return { data: page.data, nextCursor: page.nextCursor };
|
|
5
|
+
}
|
|
6
|
+
export function text(result) {
|
|
7
|
+
return result.data.length === 0
|
|
8
|
+
? 'No apps.\n'
|
|
9
|
+
: `${result.data.map((app) => `${app.id}\t${app.name}\t${app.defaultVersionId ?? '-'}`).join('\n')}\n`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const connectInputSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
app: z.ZodString;
|
|
5
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
6
|
+
as: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
env: "env";
|
|
8
|
+
json: "json";
|
|
9
|
+
}>>;
|
|
10
|
+
showSecrets: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const connectResultSchema: z.ZodObject<{
|
|
13
|
+
environmentId: z.ZodString;
|
|
14
|
+
appName: z.ZodString;
|
|
15
|
+
actorId: z.ZodString;
|
|
16
|
+
token: z.ZodString;
|
|
17
|
+
services: z.ZodArray<z.ZodObject<{
|
|
18
|
+
id: z.ZodString;
|
|
19
|
+
url: z.ZodString;
|
|
20
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
21
|
+
servers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
22
|
+
name: z.ZodString;
|
|
23
|
+
url: z.ZodString;
|
|
24
|
+
}, z.core.$strict>>>;
|
|
25
|
+
}, z.core.$strict>>;
|
|
26
|
+
expiresAt: z.ZodString;
|
|
27
|
+
login: z.ZodNullable<z.ZodObject<{
|
|
28
|
+
authentication: z.ZodString;
|
|
29
|
+
url: z.ZodString;
|
|
30
|
+
parameters: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
31
|
+
secrets: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
32
|
+
}, z.core.$strict>>;
|
|
33
|
+
}, z.core.$strict>;
|
|
34
|
+
export declare const connectCommand: import("../../command.js").RegisteredCommand;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineCommand } from '../../command.js';
|
|
3
|
+
import { idArgument } from '../../resources/schema.js';
|
|
4
|
+
export const connectInputSchema = z.strictObject({
|
|
5
|
+
id: z.string().min(1),
|
|
6
|
+
app: z.string().regex(/^[a-z][a-z0-9-]{0,39}$/),
|
|
7
|
+
actor: z.string().min(1).max(255).optional(),
|
|
8
|
+
as: z.enum(['json', 'env']).optional(),
|
|
9
|
+
showSecrets: z.boolean().default(false),
|
|
10
|
+
});
|
|
11
|
+
export const connectResultSchema = z.strictObject({
|
|
12
|
+
environmentId: z.string(),
|
|
13
|
+
appName: z.string(),
|
|
14
|
+
actorId: z.string(),
|
|
15
|
+
token: z.string(),
|
|
16
|
+
services: z.array(z.strictObject({
|
|
17
|
+
id: z.string(),
|
|
18
|
+
url: z.string(),
|
|
19
|
+
headers: z.record(z.string(), z.string()),
|
|
20
|
+
servers: z
|
|
21
|
+
.array(z.strictObject({ name: z.string(), url: z.string() }))
|
|
22
|
+
.optional(),
|
|
23
|
+
})),
|
|
24
|
+
expiresAt: z.string(),
|
|
25
|
+
login: z
|
|
26
|
+
.strictObject({
|
|
27
|
+
authentication: z.string(),
|
|
28
|
+
url: z.string(),
|
|
29
|
+
parameters: z.record(z.string(), z.string()),
|
|
30
|
+
secrets: z.record(z.string(), z.string()),
|
|
31
|
+
})
|
|
32
|
+
.nullable(),
|
|
33
|
+
});
|
|
34
|
+
export const connectCommand = defineCommand({
|
|
35
|
+
name: ['connect'],
|
|
36
|
+
summary: 'Create credentials for an app in an environment.',
|
|
37
|
+
need: 'workspace',
|
|
38
|
+
destructive: false,
|
|
39
|
+
streams: false,
|
|
40
|
+
outputException: 'dotenv',
|
|
41
|
+
arguments: [
|
|
42
|
+
idArgument,
|
|
43
|
+
{ name: 'app', key: 'app', required: true, many: false },
|
|
44
|
+
],
|
|
45
|
+
flags: [
|
|
46
|
+
{
|
|
47
|
+
name: 'actor',
|
|
48
|
+
key: 'actor',
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Connect as this actor.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'as',
|
|
54
|
+
key: 'as',
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Print credentials as json, or as env variables such as STATEMACHINES_<APP>_<SERVICE>_URL and STATEMACHINES_<APP>_ACTOR_TOKEN.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'show-secrets',
|
|
60
|
+
key: 'showSecrets',
|
|
61
|
+
type: 'boolean',
|
|
62
|
+
negatedName: null,
|
|
63
|
+
description: 'Show credentials in text output.',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
input: connectInputSchema,
|
|
67
|
+
result: connectResultSchema,
|
|
68
|
+
progress: z.never(),
|
|
69
|
+
load: () => import('./run.js'),
|
|
70
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { WorkspaceContext } from '../../context-types.js';
|
|
3
|
+
import type { connectInputSchema, connectResultSchema } from './command.js';
|
|
4
|
+
type Input = z.infer<typeof connectInputSchema>;
|
|
5
|
+
type Result = z.infer<typeof connectResultSchema>;
|
|
6
|
+
export declare function run(input: Input, context: WorkspaceContext): Promise<Result>;
|
|
7
|
+
export declare function outputFormat(input: Input): 'text' | 'json' | null;
|
|
8
|
+
export declare function literalText(result: Result, input: Input): string | null;
|
|
9
|
+
export declare function text(result: Result, input: Input): string;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { parseEnv } from 'node:util';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
3
|
+
import { credentialVariables } from '../../generated/internal.js';
|
|
4
|
+
import { environment } from '../../resources/read.js';
|
|
5
|
+
export async function run(input, context) {
|
|
6
|
+
const checked = await environment(context, input.id);
|
|
7
|
+
const credentials = await checked.connect(input.app, {
|
|
8
|
+
...(input.actor === undefined ? {} : { actorId: input.actor }),
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
environmentId: credentials.environmentId,
|
|
12
|
+
appName: credentials.appName,
|
|
13
|
+
actorId: credentials.actorId,
|
|
14
|
+
token: credentials.token,
|
|
15
|
+
services: credentials.services,
|
|
16
|
+
expiresAt: credentials.expiresAt.toISOString(),
|
|
17
|
+
login: credentials.login,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function outputFormat(input) {
|
|
21
|
+
return input.as === 'env' ? 'text' : input.as === 'json' ? 'json' : null;
|
|
22
|
+
}
|
|
23
|
+
function redactedUrl(value, token) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(value);
|
|
26
|
+
if (url.username !== '') {
|
|
27
|
+
url.username = '***';
|
|
28
|
+
}
|
|
29
|
+
if (url.password !== '') {
|
|
30
|
+
url.password = '***';
|
|
31
|
+
}
|
|
32
|
+
for (const name of new Set(url.searchParams.keys())) {
|
|
33
|
+
if (/token|key|auth|secret|password|signature|credential/i.test(name)) {
|
|
34
|
+
url.searchParams.set(name, '***');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const result = url.toString();
|
|
38
|
+
return token === ''
|
|
39
|
+
? result
|
|
40
|
+
: [token, encodeURIComponent(token)].reduce((text, secret) => text.replaceAll(secret, '***'), result);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return '[hidden]';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function dotenvValue(value) {
|
|
47
|
+
const quote = ["'", '`', '"'].find((candidate) => !value.includes(candidate) &&
|
|
48
|
+
parseEnv(`VALUE=${candidate}${value}${candidate}`).VALUE === value);
|
|
49
|
+
if (quote === undefined || value.includes('\0')) {
|
|
50
|
+
throw new CliError('cli.invalid_response', {
|
|
51
|
+
message: 'These credentials cannot be represented as dotenv values. Use --as json.',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return `${quote}${value}${quote}`;
|
|
55
|
+
}
|
|
56
|
+
export function literalText(result, input) {
|
|
57
|
+
if (input.as === 'env') {
|
|
58
|
+
const variables = credentialVariables(result.appName, result.token, result.services);
|
|
59
|
+
if (new Set(variables.map(([name]) => name)).size !== variables.length) {
|
|
60
|
+
throw new CliError('cli.invalid_response', {
|
|
61
|
+
message: 'The credentials have conflicting dotenv names. Use --as json.',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return `${variables.map(([name, value]) => `${name}=${dotenvValue(value)}`).join('\n')}\n`;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
export function text(result, input) {
|
|
69
|
+
const services = result.services.map(({ id, url }) => `${id}: ${input.showSecrets ? url : redactedUrl(url, result.token)}\n`);
|
|
70
|
+
return `${services.join('')}Token: ${input.showSecrets ? result.token : '[hidden]'}\n`;
|
|
71
|
+
}
|