@ollie-shop/cli 0.1.3 ā 0.3.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/.turbo/turbo-build.log +2 -11
- package/CHANGELOG.md +17 -5
- package/CLAUDE_CLI.md +265 -0
- package/README.md +704 -8
- package/__tests__/mocks/console.ts +22 -0
- package/__tests__/mocks/core.ts +137 -0
- package/__tests__/mocks/index.ts +4 -0
- package/__tests__/mocks/inquirer.ts +16 -0
- package/__tests__/mocks/progress.ts +19 -0
- package/dist/__tests__/helpers/cli-test-helper.d.ts +89 -0
- package/dist/__tests__/helpers/cli-test-helper.d.ts.map +1 -0
- package/dist/__tests__/helpers/cli-test-helper.js +220 -0
- package/dist/__tests__/mocks/index.d.ts +69 -0
- package/dist/__tests__/mocks/index.d.ts.map +1 -0
- package/dist/__tests__/mocks/index.js +77 -0
- package/dist/actions/component.actions.d.ts +14 -0
- package/dist/actions/component.actions.d.ts.map +1 -0
- package/dist/actions/component.actions.js +273 -0
- package/dist/actions/function.actions.d.ts +15 -0
- package/dist/actions/function.actions.d.ts.map +1 -0
- package/dist/actions/function.actions.js +254 -0
- package/dist/actions/project.actions.d.ts +17 -0
- package/dist/actions/project.actions.d.ts.map +1 -0
- package/dist/actions/project.actions.js +97 -0
- package/dist/actions/version.actions.d.ts +19 -0
- package/dist/actions/version.actions.d.ts.map +1 -0
- package/dist/actions/version.actions.js +216 -0
- package/dist/commands/component.d.ts +3 -0
- package/dist/commands/component.d.ts.map +1 -0
- package/dist/commands/component.js +192 -0
- package/dist/commands/docs.d.ts +3 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +16 -0
- package/dist/commands/function.d.ts +3 -0
- package/dist/commands/function.d.ts.map +1 -0
- package/dist/commands/function.js +243 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +20 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +26 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +175 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +78 -0
- package/dist/commands/store-version.d.ts +3 -0
- package/dist/commands/store-version.d.ts.map +1 -0
- package/dist/commands/store-version.js +241 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +46 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +41 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -226
- package/dist/prompts/component.prompts.d.ts +14 -0
- package/dist/prompts/component.prompts.d.ts.map +1 -0
- package/dist/prompts/component.prompts.js +75 -0
- package/dist/prompts/function.prompts.d.ts +21 -0
- package/dist/prompts/function.prompts.d.ts.map +1 -0
- package/dist/prompts/function.prompts.js +127 -0
- package/dist/schemas/command.schema.d.ts +516 -0
- package/dist/schemas/command.schema.d.ts.map +1 -0
- package/dist/schemas/command.schema.js +267 -0
- package/dist/types/index.d.ts +147 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +18 -0
- package/dist/utils/auth.d.ts +4 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +26 -0
- package/dist/utils/cli-progress-reporter.d.ts +12 -0
- package/dist/utils/cli-progress-reporter.d.ts.map +1 -0
- package/dist/utils/cli-progress-reporter.js +77 -0
- package/dist/utils/command-builder.d.ts +22 -0
- package/dist/utils/command-builder.d.ts.map +1 -0
- package/dist/utils/command-builder.js +268 -0
- package/dist/utils/command-helpers.d.ts +19 -0
- package/dist/utils/command-helpers.d.ts.map +1 -0
- package/dist/utils/command-helpers.js +79 -0
- package/dist/utils/command-parser.d.ts +146 -0
- package/dist/utils/command-parser.d.ts.map +1 -0
- package/dist/utils/command-parser.js +179 -0
- package/dist/utils/command-suggestions.d.ts +35 -0
- package/dist/utils/command-suggestions.d.ts.map +1 -0
- package/dist/utils/command-suggestions.js +152 -0
- package/dist/utils/console.d.ts +44 -0
- package/dist/utils/console.d.ts.map +1 -0
- package/dist/utils/console.js +233 -0
- package/dist/utils/constants.d.ts +8 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +10 -0
- package/dist/utils/context-detector.d.ts +12 -0
- package/dist/utils/context-detector.d.ts.map +1 -0
- package/dist/utils/context-detector.js +155 -0
- package/dist/utils/enhanced-error-handler.d.ts +47 -0
- package/dist/utils/enhanced-error-handler.d.ts.map +1 -0
- package/dist/utils/enhanced-error-handler.js +221 -0
- package/dist/utils/error-handler.d.ts +3 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +55 -0
- package/dist/utils/errors.d.ts +44 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +76 -0
- package/dist/utils/interactive-builder.d.ts +22 -0
- package/dist/utils/interactive-builder.d.ts.map +1 -0
- package/dist/utils/interactive-builder.js +246 -0
- package/dist/utils/rich-progress.d.ts +59 -0
- package/dist/utils/rich-progress.d.ts.map +1 -0
- package/dist/utils/rich-progress.js +234 -0
- package/dist/utils/store.d.ts +11 -0
- package/dist/utils/store.d.ts.map +1 -0
- package/dist/utils/store.js +19 -0
- package/dist/utils/validation-error-formatter.d.ts +25 -0
- package/dist/utils/validation-error-formatter.d.ts.map +1 -0
- package/dist/utils/validation-error-formatter.js +258 -0
- package/dist/utils/validation-helpers.d.ts +60 -0
- package/dist/utils/validation-helpers.d.ts.map +1 -0
- package/dist/utils/validation-helpers.js +152 -0
- package/package.json +44 -9
- package/src/__tests__/helpers/cli-test-helper.ts +281 -0
- package/src/__tests__/mocks/index.ts +142 -0
- package/src/actions/component.actions.ts +334 -0
- package/src/actions/function.actions.ts +313 -0
- package/src/actions/project.actions.ts +126 -0
- package/src/actions/version.actions.ts +233 -0
- package/src/commands/__tests__/component-validation.test.ts +250 -0
- package/src/commands/__tests__/component.test.ts +321 -0
- package/src/commands/__tests__/function-validation.test.ts +220 -0
- package/src/commands/__tests__/function.test.ts +286 -0
- package/src/commands/__tests__/store-version-validation.test.ts +414 -0
- package/src/commands/__tests__/store-version.test.ts +405 -0
- package/src/commands/__tests__/version.test.ts +71 -0
- package/src/commands/component.ts +188 -0
- package/src/commands/docs.ts +24 -0
- package/src/commands/function.ts +252 -0
- package/src/commands/help.ts +18 -0
- package/src/commands/index.ts +21 -7
- package/src/commands/login.ts +19 -79
- package/src/commands/project.ts +107 -0
- package/src/commands/store-version.ts +242 -0
- package/src/commands/version.ts +51 -0
- package/src/commands/whoami.ts +46 -0
- package/src/index.ts +110 -15
- package/src/prompts/component.prompts.ts +94 -0
- package/src/prompts/function.prompts.ts +168 -0
- package/src/schemas/command.schema.ts +354 -0
- package/src/types/index.ts +183 -0
- package/src/utils/__tests__/command-parser.test.ts +159 -0
- package/src/utils/__tests__/command-suggestions.test.ts +185 -0
- package/src/utils/__tests__/console.test.ts +192 -0
- package/src/utils/__tests__/context-detector.test.ts +258 -0
- package/src/utils/__tests__/enhanced-error-handler.test.ts +137 -0
- package/src/utils/__tests__/error-handler.test.ts +107 -0
- package/src/utils/__tests__/rich-progress.test.ts +170 -0
- package/src/utils/__tests__/validation-error-formatter.test.ts +175 -0
- package/src/utils/__tests__/validation-helpers.test.ts +125 -0
- package/src/utils/auth.ts +41 -0
- package/src/utils/cli-progress-reporter.ts +84 -0
- package/src/utils/command-builder.ts +390 -0
- package/src/utils/command-helpers.ts +83 -0
- package/src/utils/command-parser.ts +250 -0
- package/src/utils/command-suggestions.ts +176 -0
- package/src/utils/console.ts +291 -0
- package/src/utils/context-detector.ts +177 -0
- package/src/utils/enhanced-error-handler.ts +264 -0
- package/src/utils/error-handler.ts +60 -0
- package/src/utils/errors.ts +125 -0
- package/src/utils/interactive-builder.ts +271 -0
- package/src/utils/rich-progress.ts +320 -0
- package/src/utils/store.ts +23 -0
- package/src/utils/validation-error-formatter.ts +337 -0
- package/src/utils/validation-helpers.ts +192 -0
- package/tsconfig.json +13 -7
- package/vitest.config.ts +28 -0
- package/vitest.setup.ts +29 -0
- package/tsup.config.ts +0 -15
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { Command } from "@commander-js/extra-typings";
|
|
2
|
+
import * as functionActions from "../actions/function.actions";
|
|
3
|
+
import {
|
|
4
|
+
type FunctionCreateOptions,
|
|
5
|
+
FunctionCreateOptionsSchema,
|
|
6
|
+
type FunctionDeployOptions,
|
|
7
|
+
FunctionDeployOptionsSchema,
|
|
8
|
+
FunctionTestOptionsSchema,
|
|
9
|
+
FunctionValidateOptionsSchema,
|
|
10
|
+
} from "../schemas/command.schema";
|
|
11
|
+
import { buildCommand, buildCommandGroup } from "../utils/command-builder";
|
|
12
|
+
import { COMMON_OPTIONS } from "../utils/command-parser";
|
|
13
|
+
|
|
14
|
+
export function registerFunctionCommands(program: Command): void {
|
|
15
|
+
const cmd = buildCommandGroup(
|
|
16
|
+
program,
|
|
17
|
+
"function",
|
|
18
|
+
"Manage Ollie Shop functions",
|
|
19
|
+
["func"],
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Create function command
|
|
23
|
+
buildCommand(cmd, {
|
|
24
|
+
name: "create",
|
|
25
|
+
description: "Create a new function",
|
|
26
|
+
options: [
|
|
27
|
+
COMMON_OPTIONS.functionName,
|
|
28
|
+
COMMON_OPTIONS.functionInvocation,
|
|
29
|
+
COMMON_OPTIONS.priority,
|
|
30
|
+
{
|
|
31
|
+
flags: "--on-error <strategy>",
|
|
32
|
+
description: "Error handling strategy (throw|skip)",
|
|
33
|
+
defaultValue: "throw",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
flags: "-d, --description <description>",
|
|
37
|
+
description: "Function description",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
flags: "--template <template>",
|
|
41
|
+
description: "Function template to use",
|
|
42
|
+
},
|
|
43
|
+
COMMON_OPTIONS.tests,
|
|
44
|
+
COMMON_OPTIONS.noTests,
|
|
45
|
+
],
|
|
46
|
+
schema: FunctionCreateOptionsSchema,
|
|
47
|
+
examples: [
|
|
48
|
+
{
|
|
49
|
+
description: "Create a function to validate orders",
|
|
50
|
+
command:
|
|
51
|
+
"ollieshop function create --name validate-order --event order --timing before",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
description: "Create a discount function for cart",
|
|
55
|
+
command:
|
|
56
|
+
"ollieshop function create --name apply-discount --event cart --description 'Apply bulk discounts'",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
description: "Create a JavaScript function without tests",
|
|
60
|
+
command:
|
|
61
|
+
"ollieshop function create --name check-inventory --language javascript --no-tests",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
handler: async (options, console) => {
|
|
65
|
+
await functionActions.createFunction(
|
|
66
|
+
options as FunctionCreateOptions,
|
|
67
|
+
console,
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Validate function command
|
|
73
|
+
buildCommand(cmd, {
|
|
74
|
+
name: "validate",
|
|
75
|
+
description: "Validate a function",
|
|
76
|
+
options: [COMMON_OPTIONS.path],
|
|
77
|
+
schema: FunctionValidateOptionsSchema,
|
|
78
|
+
examples: [
|
|
79
|
+
{
|
|
80
|
+
description: "Validate current directory",
|
|
81
|
+
command: "ollieshop function validate",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
description: "Validate specific function",
|
|
85
|
+
command:
|
|
86
|
+
"ollieshop function validate --path ./functions/validate-order",
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
handler: async (options, console) => {
|
|
90
|
+
await functionActions.validateFunction(
|
|
91
|
+
{
|
|
92
|
+
path: options.path || process.cwd(),
|
|
93
|
+
strict: options.strict ?? false,
|
|
94
|
+
fix: options.fix ?? false,
|
|
95
|
+
},
|
|
96
|
+
console,
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Test function command
|
|
102
|
+
buildCommand(cmd, {
|
|
103
|
+
name: "test",
|
|
104
|
+
description: "Test a function locally",
|
|
105
|
+
options: [
|
|
106
|
+
COMMON_OPTIONS.path,
|
|
107
|
+
{
|
|
108
|
+
flags: "-w, --watch",
|
|
109
|
+
description: "Watch for changes",
|
|
110
|
+
defaultValue: false,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
flags: "--payload <json>",
|
|
114
|
+
description: "Test payload (JSON string)",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
flags: "--timeout <ms>",
|
|
118
|
+
description: "Timeout in milliseconds",
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
schema: FunctionTestOptionsSchema,
|
|
122
|
+
examples: [
|
|
123
|
+
{
|
|
124
|
+
description: "Test function in current directory",
|
|
125
|
+
command: "ollieshop function test",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
description: "Test with custom payload",
|
|
129
|
+
command: `ollieshop function test --payload '{"items": [{"id": "123", "quantity": 2}]}'`,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
description: "Test in watch mode",
|
|
133
|
+
command: "ollieshop function test --watch",
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
handler: async (options, console) => {
|
|
137
|
+
await functionActions.testFunction(
|
|
138
|
+
{
|
|
139
|
+
path: options.path || process.cwd(),
|
|
140
|
+
payload: options.payload,
|
|
141
|
+
timeout: options.timeout,
|
|
142
|
+
watch: options.watch ?? false,
|
|
143
|
+
coverage: options.coverage ?? false,
|
|
144
|
+
},
|
|
145
|
+
console,
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Deploy function command
|
|
151
|
+
buildCommand(cmd, {
|
|
152
|
+
name: "deploy",
|
|
153
|
+
description: "Build and deploy a function to cloud",
|
|
154
|
+
options: [
|
|
155
|
+
COMMON_OPTIONS.path,
|
|
156
|
+
{
|
|
157
|
+
flags: "--id <id>",
|
|
158
|
+
description: "Function ID for deployment",
|
|
159
|
+
required: true,
|
|
160
|
+
},
|
|
161
|
+
COMMON_OPTIONS.wait,
|
|
162
|
+
],
|
|
163
|
+
schema: FunctionDeployOptionsSchema,
|
|
164
|
+
examples: [
|
|
165
|
+
{
|
|
166
|
+
description: "Deploy current directory",
|
|
167
|
+
command: "ollieshop function deploy",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
description: "Deploy specific function and wait",
|
|
171
|
+
command: "ollieshop function deploy --path ./my-function --wait",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
description: "Deploy with function ID",
|
|
175
|
+
command: "ollieshop function deploy --id func_123abc",
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
handler: async (options: FunctionDeployOptions, console) => {
|
|
179
|
+
// Map id to functionId if provided
|
|
180
|
+
const deployOptions = {
|
|
181
|
+
path: options.path || process.cwd(),
|
|
182
|
+
functionId: options.id || options.functionId,
|
|
183
|
+
wait: options.wait,
|
|
184
|
+
};
|
|
185
|
+
await functionActions.deployFunction(deployOptions, console);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// List functions command
|
|
190
|
+
buildCommand(cmd, {
|
|
191
|
+
name: "list",
|
|
192
|
+
description: "List all functions",
|
|
193
|
+
aliases: ["ls"],
|
|
194
|
+
examples: [
|
|
195
|
+
{
|
|
196
|
+
description: "List all functions",
|
|
197
|
+
command: "ollieshop function list",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
handler: async (_, console) => {
|
|
201
|
+
await functionActions.listFunctions(console);
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Deploy status command
|
|
206
|
+
cmd
|
|
207
|
+
.command("deploy-status <buildId>")
|
|
208
|
+
.description("Check the status of a function deployment")
|
|
209
|
+
.action(async (buildId) => {
|
|
210
|
+
try {
|
|
211
|
+
const { getBuildStatus } = await import("@ollie-shop/core");
|
|
212
|
+
const { console: cliConsole } = await import("../utils/console.js");
|
|
213
|
+
const spinner = cliConsole.spinner("Checking deployment status...");
|
|
214
|
+
|
|
215
|
+
const build = await getBuildStatus(buildId);
|
|
216
|
+
|
|
217
|
+
if (!build) {
|
|
218
|
+
spinner.fail("Build not found");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
spinner.succeed();
|
|
223
|
+
|
|
224
|
+
cliConsole.info("Deployment Status:");
|
|
225
|
+
cliConsole.log(` Build ID: ${build.id}`);
|
|
226
|
+
cliConsole.log(` Status: ${build.status}`);
|
|
227
|
+
cliConsole.log(
|
|
228
|
+
` Started: ${new Date(build.startTime).toLocaleString()}`,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
if (build.endTime) {
|
|
232
|
+
cliConsole.log(
|
|
233
|
+
` Completed: ${new Date(build.endTime).toLocaleString()}`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (build.deploymentUrl) {
|
|
238
|
+
cliConsole.success(`\nFunction URL: ${build.deploymentUrl}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (build.error) {
|
|
242
|
+
cliConsole.error(`\nError: ${build.error}`);
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const { console: cliConsole } = await import("../utils/console.js");
|
|
246
|
+
cliConsole.error(
|
|
247
|
+
`Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
248
|
+
);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Command } from "@commander-js/extra-typings";
|
|
2
|
+
import { console as cliConsole } from "../utils/console";
|
|
3
|
+
|
|
4
|
+
export function configureHelpCommand(program: Command): void {
|
|
5
|
+
program
|
|
6
|
+
.command("help [command]")
|
|
7
|
+
.description("Display help for a specific command or list all commands")
|
|
8
|
+
.action((cmdName: string | undefined) => {
|
|
9
|
+
if (cmdName) {
|
|
10
|
+
// For specific command help, show the program help with the command
|
|
11
|
+
cliConsole.info(`\nHelp for command: ${cmdName}`);
|
|
12
|
+
cliConsole.info("Use 'ollieshop <command> --help' for detailed help\n");
|
|
13
|
+
} else {
|
|
14
|
+
// Show general help
|
|
15
|
+
program.help();
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
package/src/commands/index.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
1
|
+
import type { Command } from "@commander-js/extra-typings";
|
|
2
|
+
import { registerComponentCommands } from "./component";
|
|
3
|
+
import { configureDocsCommand } from "./docs";
|
|
4
|
+
import { registerFunctionCommands } from "./function";
|
|
5
|
+
import { configureHelpCommand } from "./help";
|
|
2
6
|
import { configureLoginCommand } from "./login";
|
|
7
|
+
import { registerProjectCommands } from "./project";
|
|
8
|
+
import { registerStoreVersionCommands } from "./store-version";
|
|
9
|
+
import { configureVersionCommand } from "./version";
|
|
10
|
+
import { configureWhoamiCommand } from "./whoami";
|
|
3
11
|
|
|
4
|
-
/**
|
|
5
|
-
* Register all CLI commands with the program
|
|
6
|
-
* @param program The commander program instance
|
|
7
|
-
*/
|
|
8
12
|
export function registerCommands(program: Command): void {
|
|
9
|
-
// Register
|
|
13
|
+
// Register simple commands that don't need heavy services
|
|
14
|
+
configureDocsCommand(program);
|
|
15
|
+
configureHelpCommand(program);
|
|
10
16
|
configureLoginCommand(program);
|
|
17
|
+
configureWhoamiCommand(program);
|
|
11
18
|
|
|
12
|
-
//
|
|
19
|
+
// Register new commands
|
|
20
|
+
registerStoreVersionCommands(program);
|
|
21
|
+
configureVersionCommand(program);
|
|
22
|
+
|
|
23
|
+
// Register complex commands with lazy service loading
|
|
24
|
+
registerComponentCommands(program);
|
|
25
|
+
registerFunctionCommands(program);
|
|
26
|
+
registerProjectCommands(program);
|
|
13
27
|
}
|
package/src/commands/login.ts
CHANGED
|
@@ -4,23 +4,13 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import type { Command } from "commander";
|
|
7
|
+
import type { Command } from "@commander-js/extra-typings";
|
|
8
|
+
import { isNodeError } from "../types";
|
|
9
|
+
import { console as cliConsole } from "../utils/console";
|
|
8
10
|
|
|
9
|
-
/**
|
|
10
|
-
* Default port for the local callback server
|
|
11
|
-
*/
|
|
12
11
|
const DEFAULT_CALLBACK_PORT = 7777;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Default Ollie Shop authorization endpoint
|
|
16
|
-
*/
|
|
17
12
|
const AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Configure the login command
|
|
21
|
-
* @param program The commander program instance
|
|
22
|
-
* @returns The configured command
|
|
23
|
-
*/
|
|
24
14
|
export function configureLoginCommand(program: Command): Command {
|
|
25
15
|
return program
|
|
26
16
|
.command("login")
|
|
@@ -31,24 +21,22 @@ export function configureLoginCommand(program: Command): Command {
|
|
|
31
21
|
DEFAULT_CALLBACK_PORT.toString(),
|
|
32
22
|
)
|
|
33
23
|
.option("--auth-url <url>", "Custom authorization URL", AUTH_ENDPOINT)
|
|
34
|
-
.action(async (options) => {
|
|
35
|
-
|
|
24
|
+
.action(async (options: { port: string; authUrl: string }) => {
|
|
25
|
+
cliConsole.info("š Initiating Ollie Shop login flow...");
|
|
36
26
|
|
|
37
27
|
try {
|
|
38
28
|
const token = await startWebAuthFlow(options);
|
|
39
29
|
|
|
40
30
|
if (token) {
|
|
41
31
|
await saveCredentials(token);
|
|
42
|
-
|
|
43
|
-
console.log("ā
Successfully logged in!");
|
|
44
|
-
// Process continues normally after login
|
|
32
|
+
cliConsole.success("ā
Successfully logged in!");
|
|
45
33
|
return;
|
|
46
34
|
}
|
|
47
35
|
|
|
48
|
-
|
|
36
|
+
cliConsole.error("ā Authentication failed. Please try again.");
|
|
49
37
|
process.exit(1);
|
|
50
38
|
} catch (error) {
|
|
51
|
-
|
|
39
|
+
cliConsole.error(
|
|
52
40
|
`ā Login failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
53
41
|
);
|
|
54
42
|
process.exit(1);
|
|
@@ -56,20 +44,12 @@ export function configureLoginCommand(program: Command): Command {
|
|
|
56
44
|
});
|
|
57
45
|
}
|
|
58
46
|
|
|
59
|
-
/**
|
|
60
|
-
* Authentication response type
|
|
61
|
-
*/
|
|
62
47
|
type AuthToken = {
|
|
63
48
|
accessToken: string;
|
|
64
49
|
refreshToken: string;
|
|
65
50
|
expiresAt: string;
|
|
66
51
|
};
|
|
67
52
|
|
|
68
|
-
/**
|
|
69
|
-
* Handle the callback endpoint for web authentication flow
|
|
70
|
-
*
|
|
71
|
-
* This now expects to receive tokens directly from the Next.js app with Supabase auth
|
|
72
|
-
*/
|
|
73
53
|
async function handleAuthCallback(
|
|
74
54
|
req: IncomingMessage,
|
|
75
55
|
res: ServerResponse,
|
|
@@ -77,7 +57,6 @@ async function handleAuthCallback(
|
|
|
77
57
|
resolve: (token: AuthToken | null) => void,
|
|
78
58
|
reject: (err: Error) => void,
|
|
79
59
|
): Promise<void> {
|
|
80
|
-
// TypeScript doesn't know the socket will always have localPort, so we cast
|
|
81
60
|
const socket = req.socket as { localPort?: number };
|
|
82
61
|
const url = new URL(
|
|
83
62
|
req.url || "/",
|
|
@@ -85,7 +64,6 @@ async function handleAuthCallback(
|
|
|
85
64
|
);
|
|
86
65
|
const params = url.searchParams;
|
|
87
66
|
|
|
88
|
-
// Verify state from query parameters to prevent CSRF attacks
|
|
89
67
|
const returnedState = params.get("state");
|
|
90
68
|
if (returnedState !== state) {
|
|
91
69
|
sendErrorResponse(
|
|
@@ -98,7 +76,6 @@ async function handleAuthCallback(
|
|
|
98
76
|
return;
|
|
99
77
|
}
|
|
100
78
|
|
|
101
|
-
// Parse the request body to get the access_token from form data
|
|
102
79
|
let formData = "";
|
|
103
80
|
req.on("data", (chunk) => {
|
|
104
81
|
formData += chunk.toString();
|
|
@@ -110,7 +87,6 @@ async function handleAuthCallback(
|
|
|
110
87
|
});
|
|
111
88
|
});
|
|
112
89
|
|
|
113
|
-
// Parse form data
|
|
114
90
|
const formParams = new URLSearchParams(formData);
|
|
115
91
|
const accessToken = formParams.get("access_token");
|
|
116
92
|
const refreshToken = formParams.get("refresh_token") || "";
|
|
@@ -130,17 +106,13 @@ async function handleAuthCallback(
|
|
|
130
106
|
}
|
|
131
107
|
|
|
132
108
|
try {
|
|
133
|
-
// Create token object
|
|
134
109
|
const token: AuthToken = {
|
|
135
110
|
accessToken,
|
|
136
111
|
refreshToken,
|
|
137
112
|
expiresAt,
|
|
138
113
|
};
|
|
139
114
|
|
|
140
|
-
// Send success response to browser
|
|
141
115
|
sendSuccessResponse(res);
|
|
142
|
-
|
|
143
|
-
// Resolve the promise with the token
|
|
144
116
|
resolve(token);
|
|
145
117
|
} catch (error) {
|
|
146
118
|
const errorMessage =
|
|
@@ -155,9 +127,6 @@ async function handleAuthCallback(
|
|
|
155
127
|
}
|
|
156
128
|
}
|
|
157
129
|
|
|
158
|
-
/**
|
|
159
|
-
* Send HTML error response
|
|
160
|
-
*/
|
|
161
130
|
function sendErrorResponse(
|
|
162
131
|
res: ServerResponse,
|
|
163
132
|
statusCode: number,
|
|
@@ -168,9 +137,6 @@ function sendErrorResponse(
|
|
|
168
137
|
res.end(`<h1>${title}</h1><p>${message}</p>`);
|
|
169
138
|
}
|
|
170
139
|
|
|
171
|
-
/**
|
|
172
|
-
* Send HTML success response
|
|
173
|
-
*/
|
|
174
140
|
function sendSuccessResponse(res: ServerResponse): void {
|
|
175
141
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
176
142
|
res.end(
|
|
@@ -178,9 +144,6 @@ function sendSuccessResponse(res: ServerResponse): void {
|
|
|
178
144
|
);
|
|
179
145
|
}
|
|
180
146
|
|
|
181
|
-
/**
|
|
182
|
-
* Send HTML waiting page response
|
|
183
|
-
*/
|
|
184
147
|
function sendWaitingResponse(res: ServerResponse): void {
|
|
185
148
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
186
149
|
res.end(
|
|
@@ -188,9 +151,6 @@ function sendWaitingResponse(res: ServerResponse): void {
|
|
|
188
151
|
);
|
|
189
152
|
}
|
|
190
153
|
|
|
191
|
-
/**
|
|
192
|
-
* Start the web-based authentication flow
|
|
193
|
-
*/
|
|
194
154
|
async function startWebAuthFlow(options: {
|
|
195
155
|
port: string;
|
|
196
156
|
authUrl: string;
|
|
@@ -200,35 +160,29 @@ async function startWebAuthFlow(options: {
|
|
|
200
160
|
const baseUrl = options.authUrl;
|
|
201
161
|
|
|
202
162
|
return new Promise<AuthToken | null>((resolve, reject) => {
|
|
203
|
-
// Create a local server to receive the callback
|
|
204
163
|
const server = createServer(async (req, res) => {
|
|
205
164
|
try {
|
|
206
|
-
// Parse the URL and query parameters
|
|
207
165
|
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
208
166
|
|
|
209
|
-
// Check if this is a callback from the Next.js app
|
|
210
167
|
if (url.pathname === "/callback") {
|
|
211
168
|
await handleAuthCallback(
|
|
212
169
|
req,
|
|
213
170
|
res,
|
|
214
171
|
state,
|
|
215
172
|
(token) => {
|
|
216
|
-
// Wrap the resolve to ensure server is closed
|
|
217
173
|
server.close(() => {
|
|
218
|
-
|
|
174
|
+
cliConsole.debug("š Local authentication server closed");
|
|
219
175
|
resolve(token);
|
|
220
176
|
});
|
|
221
177
|
},
|
|
222
178
|
(error) => {
|
|
223
|
-
// Wrap the reject to ensure server is closed
|
|
224
179
|
server.close(() => {
|
|
225
|
-
|
|
180
|
+
cliConsole.debug("š Local authentication server closed");
|
|
226
181
|
reject(error);
|
|
227
182
|
});
|
|
228
183
|
},
|
|
229
184
|
);
|
|
230
185
|
} else {
|
|
231
|
-
// Serve a simple page for any other path
|
|
232
186
|
sendWaitingResponse(res);
|
|
233
187
|
}
|
|
234
188
|
} catch (error) {
|
|
@@ -236,35 +190,29 @@ async function startWebAuthFlow(options: {
|
|
|
236
190
|
error instanceof Error ? error.message : "Unknown error";
|
|
237
191
|
sendErrorResponse(res, 500, "Server Error", errorMessage);
|
|
238
192
|
server.close(() => {
|
|
239
|
-
|
|
193
|
+
cliConsole.debug("š Local authentication server closed");
|
|
240
194
|
reject(new Error(errorMessage));
|
|
241
195
|
});
|
|
242
196
|
}
|
|
243
197
|
});
|
|
244
198
|
|
|
245
|
-
// Start the server
|
|
246
199
|
server.listen(port, async () => {
|
|
247
|
-
// Build the URL to your Next.js app with necessary parameters
|
|
248
200
|
const redirectUrl = `http://localhost:${port}/callback`;
|
|
249
|
-
|
|
250
201
|
const authUrl = new URL(baseUrl);
|
|
251
202
|
|
|
252
203
|
authUrl.searchParams.set("flow", "cli");
|
|
253
204
|
authUrl.searchParams.set("state", state);
|
|
254
205
|
authUrl.searchParams.set("redirect_to", redirectUrl);
|
|
255
206
|
|
|
256
|
-
|
|
257
|
-
|
|
207
|
+
cliConsole.info("\nš Please authenticate in your browser...\n");
|
|
208
|
+
cliConsole.info(`Opening: ${authUrl}\n`);
|
|
258
209
|
|
|
259
210
|
const open = (await import("open")).default;
|
|
260
|
-
|
|
261
|
-
// Open the browser with the authorization URL
|
|
262
211
|
open(authUrl.toString());
|
|
263
212
|
});
|
|
264
213
|
|
|
265
|
-
// Handle server errors
|
|
266
214
|
server.on("error", (err: Error) => {
|
|
267
|
-
if ((err
|
|
215
|
+
if (isNodeError(err) && err.code === "EADDRINUSE") {
|
|
268
216
|
reject(
|
|
269
217
|
new Error(
|
|
270
218
|
`Port ${port} is already in use. Please specify a different port using the --port option.`,
|
|
@@ -276,44 +224,37 @@ async function startWebAuthFlow(options: {
|
|
|
276
224
|
server.close();
|
|
277
225
|
});
|
|
278
226
|
|
|
279
|
-
// Set a timeout to prevent hanging indefinitely
|
|
280
227
|
const timeoutId = setTimeout(
|
|
281
228
|
() => {
|
|
282
229
|
server.close(() => {
|
|
283
|
-
|
|
230
|
+
cliConsole.debug(
|
|
231
|
+
"š Local authentication server closed due to timeout",
|
|
232
|
+
);
|
|
284
233
|
reject(new Error("Authentication timed out. Please try again."));
|
|
285
234
|
});
|
|
286
235
|
},
|
|
287
236
|
5 * 60 * 1000,
|
|
288
|
-
);
|
|
237
|
+
);
|
|
289
238
|
|
|
290
|
-
// Clean up the timeout if the server closes for other reasons
|
|
291
239
|
server.on("close", () => {
|
|
292
240
|
clearTimeout(timeoutId);
|
|
293
241
|
});
|
|
294
242
|
});
|
|
295
243
|
}
|
|
296
244
|
|
|
297
|
-
/**
|
|
298
|
-
* Save authentication credentials locally
|
|
299
|
-
*
|
|
300
|
-
* @todo Use a more secure storage method like OS keychain
|
|
301
|
-
*/
|
|
302
245
|
async function saveCredentials(token: {
|
|
303
246
|
accessToken: string;
|
|
304
247
|
refreshToken: string;
|
|
305
248
|
expiresAt: string;
|
|
306
249
|
}) {
|
|
307
|
-
|
|
250
|
+
cliConsole.debug("Saving credentials...");
|
|
308
251
|
|
|
309
252
|
const configDir = path.join(homedir(), ".ollie-shop");
|
|
310
253
|
const credentialsPath = path.join(configDir, "credentials.json");
|
|
311
254
|
|
|
312
|
-
// Create the .ollie-shop directory if it doesn't exist
|
|
313
255
|
try {
|
|
314
256
|
await fs.mkdir(configDir, { recursive: true });
|
|
315
257
|
} catch (error) {
|
|
316
|
-
// Ignore error if directory already exists
|
|
317
258
|
if (
|
|
318
259
|
!(error instanceof Error && "code" in error && error.code === "EEXIST")
|
|
319
260
|
) {
|
|
@@ -322,6 +263,5 @@ async function saveCredentials(token: {
|
|
|
322
263
|
}
|
|
323
264
|
|
|
324
265
|
await fs.writeFile(credentialsPath, JSON.stringify(token, null, 2));
|
|
325
|
-
|
|
326
266
|
return true;
|
|
327
267
|
}
|