@ollie-shop/cli 1.0.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +6 -0
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +39 -0
- package/CONTEXT.md +102 -0
- package/README.md +231 -0
- package/dist/index.js +1341 -48
- package/package.json +4 -2
- package/src/commands/business-rule-cmd.ts +161 -0
- package/src/commands/component-cmd.ts +107 -0
- package/src/commands/deploy-cmd.ts +146 -0
- package/src/commands/help.tsx +108 -17
- package/src/commands/init-cmd.ts +53 -0
- package/src/commands/schema-cmd.ts +34 -0
- package/src/commands/status-cmd.ts +68 -0
- package/src/commands/store-cmd.ts +105 -0
- package/src/commands/version-cmd.ts +100 -0
- package/src/commands/whoami.ts +28 -0
- package/src/core/business-rule.ts +128 -0
- package/src/core/component.ts +76 -0
- package/src/core/deploy.ts +171 -0
- package/src/core/schema.ts +136 -0
- package/src/core/store.ts +76 -0
- package/src/core/version.ts +67 -0
- package/src/index.tsx +40 -4
- package/src/utils/output.ts +125 -0
- package/src/utils/parse-args.ts +90 -0
- package/src/utils/supabase.ts +67 -0
- package/src/utils/validate.ts +58 -0
- package/tsup.config.ts +9 -2
package/dist/index.js
CHANGED
|
@@ -21,49 +21,108 @@ function HelpCommand() {
|
|
|
21
21
|
"<command>",
|
|
22
22
|
" [options]"
|
|
23
23
|
] }) }),
|
|
24
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Commands:" }) }),
|
|
24
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Interactive Commands:" }) }),
|
|
25
25
|
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: [
|
|
26
26
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
27
|
-
/* @__PURE__ */ jsx(Box, { width:
|
|
27
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "login" }) }),
|
|
28
28
|
/* @__PURE__ */ jsx(Text, { children: "Authenticate with your Ollie Shop account" })
|
|
29
29
|
] }),
|
|
30
30
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
31
|
-
/* @__PURE__ */ jsx(Box, { width:
|
|
31
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "start" }) }),
|
|
32
32
|
/* @__PURE__ */ jsx(Text, { children: "Start the development server with hot reload" })
|
|
33
|
+
] })
|
|
34
|
+
] }),
|
|
35
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Agent Commands:" }) }),
|
|
36
|
+
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: [
|
|
37
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
38
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "whoami" }) }),
|
|
39
|
+
/* @__PURE__ */ jsx(Text, { children: "Show current user and organization" })
|
|
40
|
+
] }),
|
|
41
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
42
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "store create|list" }) }),
|
|
43
|
+
/* @__PURE__ */ jsx(Text, { children: "Create or list stores" })
|
|
44
|
+
] }),
|
|
45
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
46
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "version create|list" }) }),
|
|
47
|
+
/* @__PURE__ */ jsx(Text, { children: "Create or list versions" })
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
50
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "component create|list" }) }),
|
|
51
|
+
/* @__PURE__ */ jsx(Text, { children: "Create or list components" })
|
|
52
|
+
] }),
|
|
53
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
54
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "deploy" }) }),
|
|
55
|
+
/* @__PURE__ */ jsx(Text, { children: "Bundle and upload a component/function build" })
|
|
33
56
|
] }),
|
|
34
57
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
35
|
-
/* @__PURE__ */ jsx(Box, { width:
|
|
58
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "status" }) }),
|
|
59
|
+
/* @__PURE__ */ jsx(Text, { children: "Check or poll a build status" })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
62
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "schema" }) }),
|
|
63
|
+
/* @__PURE__ */ jsx(Text, { children: "Introspect resource schemas (JSON Schema)" })
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
66
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "init" }) }),
|
|
67
|
+
/* @__PURE__ */ jsx(Text, { children: "Write store/version IDs to ollie.json" })
|
|
68
|
+
] }),
|
|
69
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
70
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "help" }) }),
|
|
36
71
|
/* @__PURE__ */ jsx(Text, { children: "Show this help message" })
|
|
37
72
|
] }),
|
|
38
73
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
39
|
-
/* @__PURE__ */ jsx(Box, { width:
|
|
74
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "version" }) }),
|
|
40
75
|
/* @__PURE__ */ jsx(Text, { children: "Show CLI version" })
|
|
41
76
|
] })
|
|
42
77
|
] }),
|
|
43
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "
|
|
44
|
-
/* @__PURE__ */
|
|
45
|
-
/* @__PURE__ */
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Global Flags:" }) }),
|
|
79
|
+
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: [
|
|
80
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
81
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--output json, -o json" }) }),
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { children: "Force JSON output (auto when piped)" })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
85
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--dry-run" }) }),
|
|
86
|
+
/* @__PURE__ */ jsx(Text, { children: "Validate without executing mutations" })
|
|
87
|
+
] }),
|
|
88
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
89
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--fields a,b,c" }) }),
|
|
90
|
+
/* @__PURE__ */ jsx(Text, { children: "Limit output fields" })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
93
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
94
|
+
"--data ",
|
|
95
|
+
`'{...}'`
|
|
96
|
+
] }) }),
|
|
97
|
+
/* @__PURE__ */ jsx(Text, { children: "Raw JSON payload for mutations" })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
100
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--stage, -s" }) }),
|
|
101
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
102
|
+
"Config stage (loads ollie.",
|
|
103
|
+
"{stage}",
|
|
104
|
+
".json)"
|
|
105
|
+
] })
|
|
60
106
|
] })
|
|
61
107
|
] }),
|
|
62
108
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Examples:" }) }),
|
|
63
109
|
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
|
|
64
110
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop login" }),
|
|
65
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop start" }),
|
|
66
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop
|
|
111
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop start --stage dev" }),
|
|
112
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop whoami -o json" }),
|
|
113
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop schema store.create" }),
|
|
114
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: '$ ollieshop store create --name "My Store" --platform vtex --platform-store-id mystore' }),
|
|
115
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
116
|
+
"$ ollieshop store create --data",
|
|
117
|
+
" ",
|
|
118
|
+
`'{"name":"My Store","platform":"vtex","platformStoreId":"mystore"}'`,
|
|
119
|
+
" ",
|
|
120
|
+
"-o json"
|
|
121
|
+
] }),
|
|
122
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop version create --store-id UUID --name v1 --active" }),
|
|
123
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop init --store-id UUID --version-id UUID" }),
|
|
124
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop deploy --component-id UUID --name FreeShippingBar --wait" }),
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop status --build-id BUILD_ID --wait -o json" })
|
|
67
126
|
] })
|
|
68
127
|
] });
|
|
69
128
|
}
|
|
@@ -227,14 +286,32 @@ async function saveCredentials(token) {
|
|
|
227
286
|
};
|
|
228
287
|
await fs.writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
|
|
229
288
|
}
|
|
289
|
+
async function getCredentials() {
|
|
290
|
+
try {
|
|
291
|
+
const content = await fs.readFile(CREDENTIALS_PATH, "utf-8");
|
|
292
|
+
return JSON.parse(content);
|
|
293
|
+
} catch {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async function getCurrentUser() {
|
|
298
|
+
const credentials = await getCredentials();
|
|
299
|
+
if (!credentials) return null;
|
|
300
|
+
try {
|
|
301
|
+
const decoded = jwtDecode(credentials.accessToken);
|
|
302
|
+
return decoded.email ? { email: decoded.email } : null;
|
|
303
|
+
} catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
230
307
|
|
|
231
308
|
// src/commands/login.tsx
|
|
232
309
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
233
|
-
function LoginCommand({ args
|
|
310
|
+
function LoginCommand({ args }) {
|
|
234
311
|
const { exit } = useApp();
|
|
235
312
|
const [state, setState] = useState({ status: "idle" });
|
|
236
|
-
const portIndex =
|
|
237
|
-
const port = portIndex !== -1 ? Number.parseInt(
|
|
313
|
+
const portIndex = args.findIndex((a) => a === "--port" || a === "-p");
|
|
314
|
+
const port = portIndex !== -1 ? Number.parseInt(args[portIndex + 1], 10) : 7777;
|
|
238
315
|
useEffect(() => {
|
|
239
316
|
if (state.status !== "idle") return;
|
|
240
317
|
setState({ status: "waiting" });
|
|
@@ -341,6 +418,17 @@ async function loadConfig(options = {}) {
|
|
|
341
418
|
};
|
|
342
419
|
return OllieConfigSchema.parse(merged);
|
|
343
420
|
}
|
|
421
|
+
async function saveConfig(config, options = {}) {
|
|
422
|
+
const { cwd = process.cwd(), stage } = options;
|
|
423
|
+
const fileName = getConfigFileName(stage);
|
|
424
|
+
const configPath = path2.join(cwd, fileName);
|
|
425
|
+
const existing = await loadConfigFile(configPath);
|
|
426
|
+
const merged = {
|
|
427
|
+
...existing,
|
|
428
|
+
...config
|
|
429
|
+
};
|
|
430
|
+
await fs2.writeFile(configPath, JSON.stringify(merged, null, 2));
|
|
431
|
+
}
|
|
344
432
|
function resolveStage(cliStage) {
|
|
345
433
|
return cliStage || process.env.OLLIE_STAGE || void 0;
|
|
346
434
|
}
|
|
@@ -764,11 +852,11 @@ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
|
764
852
|
var STUDIO_BASE_URL = "https://admin.ollie.shop/studio";
|
|
765
853
|
var MAX_LOGS = 10;
|
|
766
854
|
var PORT = 4e3;
|
|
767
|
-
function parseArg(
|
|
768
|
-
const index =
|
|
769
|
-
return index !== -1 ?
|
|
855
|
+
function parseArg(args, ...flags) {
|
|
856
|
+
const index = args.findIndex((a) => flags.includes(a));
|
|
857
|
+
return index !== -1 ? args[index + 1] : void 0;
|
|
770
858
|
}
|
|
771
|
-
function StartCommand({ args
|
|
859
|
+
function StartCommand({ args }) {
|
|
772
860
|
const { exit } = useApp2();
|
|
773
861
|
const [state, setState] = useState2({ status: "initializing" });
|
|
774
862
|
const [components, setComponents] = useState2([]);
|
|
@@ -778,7 +866,7 @@ function StartCommand({ args: args2 }) {
|
|
|
778
866
|
const logIdRef = useRef(0);
|
|
779
867
|
const ctxRef = useRef(null);
|
|
780
868
|
const stopRef = useRef(null);
|
|
781
|
-
const stage = resolveStage(parseArg(
|
|
869
|
+
const stage = resolveStage(parseArg(args, "--stage", "-s"));
|
|
782
870
|
const addLog = useCallback((log) => {
|
|
783
871
|
setLogs((prev) => {
|
|
784
872
|
const newLog = {
|
|
@@ -790,12 +878,12 @@ function StartCommand({ args: args2 }) {
|
|
|
790
878
|
});
|
|
791
879
|
}, []);
|
|
792
880
|
const handleRequest = useCallback(
|
|
793
|
-
(
|
|
881
|
+
(args2) => {
|
|
794
882
|
addLog({
|
|
795
|
-
method:
|
|
796
|
-
path:
|
|
797
|
-
status:
|
|
798
|
-
time:
|
|
883
|
+
method: args2.method,
|
|
884
|
+
path: args2.path,
|
|
885
|
+
status: args2.status,
|
|
886
|
+
time: args2.timeInMS
|
|
799
887
|
});
|
|
800
888
|
},
|
|
801
889
|
[addLog]
|
|
@@ -1065,12 +1153,12 @@ function Footer() {
|
|
|
1065
1153
|
|
|
1066
1154
|
// src/cli.tsx
|
|
1067
1155
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1068
|
-
function App({ command
|
|
1069
|
-
switch (
|
|
1156
|
+
function App({ command, args }) {
|
|
1157
|
+
switch (command) {
|
|
1070
1158
|
case "login":
|
|
1071
|
-
return /* @__PURE__ */ jsx4(LoginCommand, { args
|
|
1159
|
+
return /* @__PURE__ */ jsx4(LoginCommand, { args });
|
|
1072
1160
|
case "start":
|
|
1073
|
-
return /* @__PURE__ */ jsx4(StartCommand, { args
|
|
1161
|
+
return /* @__PURE__ */ jsx4(StartCommand, { args });
|
|
1074
1162
|
case "help":
|
|
1075
1163
|
case "--help":
|
|
1076
1164
|
case "-h":
|
|
@@ -1080,25 +1168,1230 @@ function App({ command: command2, args: args2 }) {
|
|
|
1080
1168
|
case "-v":
|
|
1081
1169
|
return /* @__PURE__ */ jsx4(VersionCommand, {});
|
|
1082
1170
|
default:
|
|
1083
|
-
return /* @__PURE__ */ jsx4(UnknownCommand, { command
|
|
1171
|
+
return /* @__PURE__ */ jsx4(UnknownCommand, { command });
|
|
1084
1172
|
}
|
|
1085
1173
|
}
|
|
1086
1174
|
function VersionCommand() {
|
|
1087
1175
|
return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { children: "ollie v0.1.0" }) });
|
|
1088
1176
|
}
|
|
1089
|
-
function UnknownCommand({ command
|
|
1177
|
+
function UnknownCommand({ command }) {
|
|
1090
1178
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
1091
1179
|
/* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
1092
1180
|
"Unknown command: ",
|
|
1093
|
-
|
|
1181
|
+
command
|
|
1094
1182
|
] }),
|
|
1095
1183
|
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Run `ollie help` for available commands." })
|
|
1096
1184
|
] });
|
|
1097
1185
|
}
|
|
1098
1186
|
|
|
1187
|
+
// src/core/business-rule.ts
|
|
1188
|
+
var SELECT_FIELDS = "id, store_id, title, content, previous_content, versions_ids, components_ids, functions_ids, status, code_updated, created_at, updated_at";
|
|
1189
|
+
async function listBusinessRules(client, filters = {}) {
|
|
1190
|
+
let query = client.from("business_rules").select(SELECT_FIELDS).order("created_at", { ascending: false });
|
|
1191
|
+
if (filters.store_id !== void 0) {
|
|
1192
|
+
query = query.eq("store_id", filters.store_id);
|
|
1193
|
+
}
|
|
1194
|
+
if (filters.version_id !== void 0) {
|
|
1195
|
+
query = query.filter(
|
|
1196
|
+
"versions_ids",
|
|
1197
|
+
"cs",
|
|
1198
|
+
JSON.stringify([filters.version_id])
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
if (filters.code_updated !== void 0) {
|
|
1202
|
+
query = query.eq("code_updated", filters.code_updated);
|
|
1203
|
+
}
|
|
1204
|
+
const { data, error } = await query;
|
|
1205
|
+
if (error) {
|
|
1206
|
+
return { error: { message: error.message } };
|
|
1207
|
+
}
|
|
1208
|
+
return { data };
|
|
1209
|
+
}
|
|
1210
|
+
async function getBusinessRule(client, id) {
|
|
1211
|
+
const { data, error } = await client.from("business_rules").select(SELECT_FIELDS).eq("id", id).single();
|
|
1212
|
+
if (error) {
|
|
1213
|
+
return { error: { message: error.message } };
|
|
1214
|
+
}
|
|
1215
|
+
return { data };
|
|
1216
|
+
}
|
|
1217
|
+
async function updateBusinessRule(client, id, input) {
|
|
1218
|
+
const { data: current, error: fetchError } = await client.from("business_rules").select("content").eq("id", id).single();
|
|
1219
|
+
if (fetchError) {
|
|
1220
|
+
return { error: { message: fetchError.message } };
|
|
1221
|
+
}
|
|
1222
|
+
const updatePayload = {
|
|
1223
|
+
previous_content: current.content,
|
|
1224
|
+
content: input.content,
|
|
1225
|
+
code_updated: false
|
|
1226
|
+
};
|
|
1227
|
+
if (input.versions_ids !== void 0) {
|
|
1228
|
+
updatePayload.versions_ids = input.versions_ids;
|
|
1229
|
+
}
|
|
1230
|
+
if (input.components_ids !== void 0) {
|
|
1231
|
+
updatePayload.components_ids = input.components_ids;
|
|
1232
|
+
}
|
|
1233
|
+
if (input.functions_ids !== void 0) {
|
|
1234
|
+
updatePayload.functions_ids = input.functions_ids;
|
|
1235
|
+
}
|
|
1236
|
+
const { data, error } = await client.from("business_rules").update(updatePayload).eq("id", id).select("id").single();
|
|
1237
|
+
if (error) {
|
|
1238
|
+
return { error: { message: error.message } };
|
|
1239
|
+
}
|
|
1240
|
+
return { data: { id: data.id } };
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// src/utils/output.ts
|
|
1244
|
+
function detectOutputFormat(cliOutput) {
|
|
1245
|
+
if (cliOutput === "json" || cliOutput === "j") return "json";
|
|
1246
|
+
if (cliOutput === "pretty") return "pretty";
|
|
1247
|
+
return process.stdout.isTTY ? "pretty" : "json";
|
|
1248
|
+
}
|
|
1249
|
+
function filterFields(data, fields) {
|
|
1250
|
+
if (!fields || fields.length === 0) return data;
|
|
1251
|
+
const result = {};
|
|
1252
|
+
for (const field of fields) {
|
|
1253
|
+
if (field in data) {
|
|
1254
|
+
result[field] = data[field];
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return result;
|
|
1258
|
+
}
|
|
1259
|
+
function outputResult(result, format, fields) {
|
|
1260
|
+
if (format === "json") {
|
|
1261
|
+
let output = result;
|
|
1262
|
+
if (fields && result.data && typeof result.data === "object") {
|
|
1263
|
+
if (Array.isArray(result.data)) {
|
|
1264
|
+
output = {
|
|
1265
|
+
...result,
|
|
1266
|
+
data: result.data.map(
|
|
1267
|
+
(item) => filterFields(item, fields)
|
|
1268
|
+
)
|
|
1269
|
+
};
|
|
1270
|
+
} else {
|
|
1271
|
+
output = {
|
|
1272
|
+
...result,
|
|
1273
|
+
data: filterFields(result.data, fields)
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
process.stdout.write(`${JSON.stringify(output)}
|
|
1278
|
+
`);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (result.error) {
|
|
1282
|
+
const errMsg = typeof result.error === "object" && result.error !== null ? result.error.message || JSON.stringify(result.error) : String(result.error);
|
|
1283
|
+
console.error(`\x1B[31mError:\x1B[0m ${errMsg}`);
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (Array.isArray(result.data)) {
|
|
1287
|
+
printTable(result.data, fields);
|
|
1288
|
+
} else if (typeof result.data === "object" && result.data !== null) {
|
|
1289
|
+
const filtered = fields ? filterFields(result.data, fields) : result.data;
|
|
1290
|
+
for (const [key, value] of Object.entries(
|
|
1291
|
+
filtered
|
|
1292
|
+
)) {
|
|
1293
|
+
console.log(`\x1B[1m${key}:\x1B[0m ${value}`);
|
|
1294
|
+
}
|
|
1295
|
+
} else {
|
|
1296
|
+
console.log(result.data);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
function printTable(rows, fields) {
|
|
1300
|
+
if (rows.length === 0) {
|
|
1301
|
+
console.log("(no results)");
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
const keys = fields || Object.keys(rows[0]);
|
|
1305
|
+
const widths = {};
|
|
1306
|
+
for (const key of keys) {
|
|
1307
|
+
widths[key] = key.length;
|
|
1308
|
+
for (const row of rows) {
|
|
1309
|
+
const val = String(row[key] ?? "");
|
|
1310
|
+
widths[key] = Math.max(widths[key], val.length);
|
|
1311
|
+
}
|
|
1312
|
+
widths[key] = Math.min(widths[key], 40);
|
|
1313
|
+
}
|
|
1314
|
+
const header = keys.map((k) => k.padEnd(widths[k])).join(" ");
|
|
1315
|
+
console.log(`\x1B[1m${header}\x1B[0m`);
|
|
1316
|
+
console.log(keys.map((k) => "\u2500".repeat(widths[k])).join("\u2500\u2500"));
|
|
1317
|
+
for (const row of rows) {
|
|
1318
|
+
const line = keys.map(
|
|
1319
|
+
(k) => String(row[k] ?? "").padEnd(widths[k]).slice(0, widths[k])
|
|
1320
|
+
).join(" ");
|
|
1321
|
+
console.log(line);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
function outputDryRun(action, data, format) {
|
|
1325
|
+
if (format === "json") {
|
|
1326
|
+
process.stdout.write(`${JSON.stringify({ dryRun: true, action, data })}
|
|
1327
|
+
`);
|
|
1328
|
+
} else {
|
|
1329
|
+
console.log(`\x1B[33m[dry-run]\x1B[0m Would execute: ${action}`);
|
|
1330
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1331
|
+
console.log(` ${key}: ${JSON.stringify(value)}`);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/utils/parse-args.ts
|
|
1337
|
+
function parseArgs(argv) {
|
|
1338
|
+
const args = argv.slice(2);
|
|
1339
|
+
const command = args[0] || "help";
|
|
1340
|
+
const flags = {};
|
|
1341
|
+
const positional = [];
|
|
1342
|
+
let subcommand;
|
|
1343
|
+
if (args[1] && !args[1].startsWith("-")) {
|
|
1344
|
+
subcommand = args[1];
|
|
1345
|
+
}
|
|
1346
|
+
const startIdx = subcommand ? 2 : 1;
|
|
1347
|
+
for (let i = startIdx; i < args.length; i++) {
|
|
1348
|
+
const arg = args[i];
|
|
1349
|
+
if (arg.startsWith("--")) {
|
|
1350
|
+
const key = arg.slice(2);
|
|
1351
|
+
const next = args[i + 1];
|
|
1352
|
+
if (next && !next.startsWith("-")) {
|
|
1353
|
+
flags[key] = next;
|
|
1354
|
+
i++;
|
|
1355
|
+
} else {
|
|
1356
|
+
flags[key] = true;
|
|
1357
|
+
}
|
|
1358
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
1359
|
+
const key = arg.slice(1);
|
|
1360
|
+
const next = args[i + 1];
|
|
1361
|
+
if (next && !next.startsWith("-")) {
|
|
1362
|
+
flags[key] = next;
|
|
1363
|
+
i++;
|
|
1364
|
+
} else {
|
|
1365
|
+
flags[key] = true;
|
|
1366
|
+
}
|
|
1367
|
+
} else {
|
|
1368
|
+
positional.push(arg);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
const global = {
|
|
1372
|
+
output: flags.output || flags.o || void 0,
|
|
1373
|
+
dryRun: flags["dry-run"] === true,
|
|
1374
|
+
fields: flags.fields ? String(flags.fields).split(",").map((f) => f.trim()) : void 0,
|
|
1375
|
+
data: flags.data || flags.d || void 0
|
|
1376
|
+
};
|
|
1377
|
+
return { command, subcommand, flags, global, positional };
|
|
1378
|
+
}
|
|
1379
|
+
function getFlag(flags, ...names) {
|
|
1380
|
+
for (const name of names) {
|
|
1381
|
+
const val = flags[name];
|
|
1382
|
+
if (typeof val === "string") return val;
|
|
1383
|
+
}
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
function getBoolFlag(flags, ...names) {
|
|
1387
|
+
for (const name of names) {
|
|
1388
|
+
if (flags[name] === true) return true;
|
|
1389
|
+
}
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// src/utils/supabase.ts
|
|
1394
|
+
import { createClient } from "@supabase/supabase-js";
|
|
1395
|
+
async function getAuthenticatedClient() {
|
|
1396
|
+
const credentials = await getCredentials();
|
|
1397
|
+
if (!credentials) {
|
|
1398
|
+
throw new Error("Not authenticated. Run `ollieshop login` first.");
|
|
1399
|
+
}
|
|
1400
|
+
const supabaseUrl = process.env.OLLIE_SUPABASE_URL || "https://aazahtmqrhjqsyqoqdkm.supabase.co";
|
|
1401
|
+
const supabaseAnonKey = process.env.OLLIE_SUPABASE_ANON_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFhemFodG1xcmhqcXN5cW9xZGttIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDg2MzEwOTcsImV4cCI6MjA2NDIwNzA5N30.VuAbAyjDe0HcL09SZtQ-UmP1o7Z6qwGuOtvfFhnyAcM";
|
|
1402
|
+
const client = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1403
|
+
auth: {
|
|
1404
|
+
autoRefreshToken: false,
|
|
1405
|
+
persistSession: false
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
await client.auth.setSession({
|
|
1409
|
+
access_token: credentials.accessToken,
|
|
1410
|
+
refresh_token: credentials.refreshToken
|
|
1411
|
+
});
|
|
1412
|
+
return client;
|
|
1413
|
+
}
|
|
1414
|
+
function getBuilderUrl() {
|
|
1415
|
+
return process.env.OLLIE_BUILDER_URL || "";
|
|
1416
|
+
}
|
|
1417
|
+
async function getAuthToken() {
|
|
1418
|
+
const credentials = await getCredentials();
|
|
1419
|
+
if (!credentials) {
|
|
1420
|
+
throw new Error("Not authenticated. Run `ollieshop login` first.");
|
|
1421
|
+
}
|
|
1422
|
+
return credentials.accessToken;
|
|
1423
|
+
}
|
|
1424
|
+
async function getOrganizationId(client) {
|
|
1425
|
+
const { data: org, error } = await client.from("organizations").select("id").order("created_at", { ascending: true }).limit(1).maybeSingle();
|
|
1426
|
+
if (error) {
|
|
1427
|
+
throw new Error(`Failed to get organization: ${error.message}`);
|
|
1428
|
+
}
|
|
1429
|
+
if (!org) {
|
|
1430
|
+
throw new Error(
|
|
1431
|
+
"No organization found for your account. Create one at https://admin.ollie.shop"
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
return org.id;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// src/utils/validate.ts
|
|
1438
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1439
|
+
var CONTROL_CHAR_REGEX = /[\x00-\x1f\x7f]/;
|
|
1440
|
+
function validateUuid(value, name) {
|
|
1441
|
+
if (!UUID_REGEX.test(value)) {
|
|
1442
|
+
throw new Error(
|
|
1443
|
+
`Invalid ${name}: "${value}". Must be a valid UUID (e.g. 7217542a-d7c6-40d3-a20e-db13b310a781).`
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
return value;
|
|
1447
|
+
}
|
|
1448
|
+
function rejectControlChars(value, name) {
|
|
1449
|
+
if (CONTROL_CHAR_REGEX.test(value)) {
|
|
1450
|
+
throw new Error(
|
|
1451
|
+
`Invalid ${name}: contains control characters. Input must be printable text.`
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
return value;
|
|
1455
|
+
}
|
|
1456
|
+
function validateEnum(value, allowed, name) {
|
|
1457
|
+
if (!allowed.includes(value)) {
|
|
1458
|
+
throw new Error(
|
|
1459
|
+
`Invalid ${name}: "${value}". Must be one of: ${allowed.join(", ")}`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
return value;
|
|
1463
|
+
}
|
|
1464
|
+
function validateRequired(value, name) {
|
|
1465
|
+
if (!value || value.trim() === "") {
|
|
1466
|
+
throw new Error(`Missing required parameter: --${name}`);
|
|
1467
|
+
}
|
|
1468
|
+
return rejectControlChars(value.trim(), name);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// src/commands/business-rule-cmd.ts
|
|
1472
|
+
async function businessRuleCommand(parsed2) {
|
|
1473
|
+
const sub = parsed2.subcommand;
|
|
1474
|
+
if (sub === "list" || sub === "ls") return businessRuleListCommand(parsed2);
|
|
1475
|
+
if (sub === "get") return businessRuleGetCommand(parsed2);
|
|
1476
|
+
if (sub === "update") return businessRuleUpdateCommand(parsed2);
|
|
1477
|
+
console.error(
|
|
1478
|
+
`Unknown business-rule subcommand: ${sub}. Use: business-rule list | business-rule get | business-rule update`
|
|
1479
|
+
);
|
|
1480
|
+
process.exit(1);
|
|
1481
|
+
}
|
|
1482
|
+
async function businessRuleListCommand(parsed2) {
|
|
1483
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1484
|
+
try {
|
|
1485
|
+
const storeId = getFlag(parsed2.flags, "store-id");
|
|
1486
|
+
const versionId = getFlag(parsed2.flags, "version-id");
|
|
1487
|
+
const codeUpdatedRaw = parsed2.flags["code-updated"];
|
|
1488
|
+
let codeUpdated;
|
|
1489
|
+
if (codeUpdatedRaw === "true" || codeUpdatedRaw === true) {
|
|
1490
|
+
codeUpdated = true;
|
|
1491
|
+
} else if (codeUpdatedRaw === "false") {
|
|
1492
|
+
codeUpdated = false;
|
|
1493
|
+
}
|
|
1494
|
+
if (storeId !== void 0) validateUuid(storeId, "store-id");
|
|
1495
|
+
if (versionId !== void 0) validateUuid(versionId, "version-id");
|
|
1496
|
+
const client = await getAuthenticatedClient();
|
|
1497
|
+
const result = await listBusinessRules(client, {
|
|
1498
|
+
store_id: storeId,
|
|
1499
|
+
version_id: versionId,
|
|
1500
|
+
code_updated: codeUpdated
|
|
1501
|
+
});
|
|
1502
|
+
outputResult(result, format, parsed2.global.fields);
|
|
1503
|
+
if (result.error) process.exit(1);
|
|
1504
|
+
} catch (err) {
|
|
1505
|
+
outputResult(
|
|
1506
|
+
{
|
|
1507
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
1508
|
+
},
|
|
1509
|
+
format
|
|
1510
|
+
);
|
|
1511
|
+
process.exit(1);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async function businessRuleGetCommand(parsed2) {
|
|
1515
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1516
|
+
try {
|
|
1517
|
+
const id = validateUuid(
|
|
1518
|
+
validateRequired(getFlag(parsed2.flags, "id"), "id"),
|
|
1519
|
+
"id"
|
|
1520
|
+
);
|
|
1521
|
+
const client = await getAuthenticatedClient();
|
|
1522
|
+
const result = await getBusinessRule(client, id);
|
|
1523
|
+
outputResult(result, format, parsed2.global.fields);
|
|
1524
|
+
if (result.error) process.exit(1);
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
outputResult(
|
|
1527
|
+
{
|
|
1528
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
1529
|
+
},
|
|
1530
|
+
format
|
|
1531
|
+
);
|
|
1532
|
+
process.exit(1);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
async function businessRuleUpdateCommand(parsed2) {
|
|
1536
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1537
|
+
try {
|
|
1538
|
+
let id;
|
|
1539
|
+
let input;
|
|
1540
|
+
if (parsed2.global.data) {
|
|
1541
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
1542
|
+
id = validateUuid(validateRequired(raw.id, "id"), "id");
|
|
1543
|
+
input = {
|
|
1544
|
+
content: validateRequired(raw.content, "content"),
|
|
1545
|
+
versions_ids: raw.versionsIds ?? void 0,
|
|
1546
|
+
components_ids: raw.componentsIds ?? void 0,
|
|
1547
|
+
functions_ids: raw.functionsIds ?? void 0
|
|
1548
|
+
};
|
|
1549
|
+
} else {
|
|
1550
|
+
id = validateUuid(
|
|
1551
|
+
validateRequired(getFlag(parsed2.flags, "id"), "id"),
|
|
1552
|
+
"id"
|
|
1553
|
+
);
|
|
1554
|
+
const versionsIdsRaw = getFlag(parsed2.flags, "versions-ids");
|
|
1555
|
+
const componentsIdsRaw = getFlag(parsed2.flags, "components-ids");
|
|
1556
|
+
const functionsIdsRaw = getFlag(parsed2.flags, "functions-ids");
|
|
1557
|
+
input = {
|
|
1558
|
+
content: validateRequired(
|
|
1559
|
+
getFlag(parsed2.flags, "content", "c"),
|
|
1560
|
+
"content"
|
|
1561
|
+
),
|
|
1562
|
+
versions_ids: versionsIdsRaw ? JSON.parse(versionsIdsRaw) : void 0,
|
|
1563
|
+
components_ids: componentsIdsRaw ? JSON.parse(componentsIdsRaw) : void 0,
|
|
1564
|
+
functions_ids: functionsIdsRaw ? JSON.parse(functionsIdsRaw) : void 0
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
if (parsed2.global.dryRun) {
|
|
1568
|
+
outputDryRun(
|
|
1569
|
+
"business-rule.update",
|
|
1570
|
+
{ id, ...input },
|
|
1571
|
+
format
|
|
1572
|
+
);
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const client = await getAuthenticatedClient();
|
|
1576
|
+
const result = await updateBusinessRule(client, id, input);
|
|
1577
|
+
outputResult(result, format, parsed2.global.fields);
|
|
1578
|
+
if (result.error) process.exit(1);
|
|
1579
|
+
} catch (err) {
|
|
1580
|
+
outputResult(
|
|
1581
|
+
{
|
|
1582
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
1583
|
+
},
|
|
1584
|
+
format
|
|
1585
|
+
);
|
|
1586
|
+
process.exit(1);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/core/schema.ts
|
|
1591
|
+
import { z as z2 } from "zod";
|
|
1592
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
1593
|
+
var PLATFORM_VENDORS = ["vtex", "shopify", "vnda", "custom"];
|
|
1594
|
+
var storeCreateSchema = z2.object({
|
|
1595
|
+
name: z2.string().min(1).describe("Store display name"),
|
|
1596
|
+
platform: z2.enum(PLATFORM_VENDORS).describe("E-commerce platform vendor"),
|
|
1597
|
+
platformStoreId: z2.string().min(1).describe("Platform-specific store identifier (e.g. account name)"),
|
|
1598
|
+
logo: z2.string().url().optional().describe("Store logo URL"),
|
|
1599
|
+
settings: z2.string().optional().describe("JSON settings string")
|
|
1600
|
+
});
|
|
1601
|
+
var storeListSchema = z2.object({}).describe("No parameters required");
|
|
1602
|
+
var versionCreateSchema = z2.object({
|
|
1603
|
+
storeId: z2.string().uuid().describe("Parent store UUID"),
|
|
1604
|
+
name: z2.string().min(1).describe("Version name (e.g. v1, main)"),
|
|
1605
|
+
active: z2.boolean().default(false).describe("Whether version is active"),
|
|
1606
|
+
default: z2.boolean().default(false).describe("Whether this is the default version"),
|
|
1607
|
+
template: z2.string().nullable().default(null).describe("Template name or null")
|
|
1608
|
+
});
|
|
1609
|
+
var versionListSchema = z2.object({
|
|
1610
|
+
storeId: z2.string().uuid().describe("Store UUID to list versions for")
|
|
1611
|
+
});
|
|
1612
|
+
var componentCreateSchema = z2.object({
|
|
1613
|
+
versionId: z2.string().uuid().describe("Parent version UUID"),
|
|
1614
|
+
name: z2.string().min(1).describe("Component name (e.g. FreeShippingBar)"),
|
|
1615
|
+
slot: z2.string().min(1).describe(
|
|
1616
|
+
"Target slot (e.g. cart_header_full_page, shipping_address_details_form)"
|
|
1617
|
+
),
|
|
1618
|
+
active: z2.boolean().default(true).describe("Whether component is active"),
|
|
1619
|
+
props: z2.record(z2.unknown()).nullable().default(null).describe("Default component props as JSON object")
|
|
1620
|
+
});
|
|
1621
|
+
var componentListSchema = z2.object({
|
|
1622
|
+
storeId: z2.string().uuid().describe("Store UUID"),
|
|
1623
|
+
versionId: z2.string().uuid().optional().describe("Optional version UUID to filter by")
|
|
1624
|
+
});
|
|
1625
|
+
var functionCreateSchema = z2.object({
|
|
1626
|
+
versionId: z2.string().uuid().describe("Parent version UUID"),
|
|
1627
|
+
name: z2.string().min(1).describe("Function name"),
|
|
1628
|
+
trigger: z2.string().min(1).describe("Function trigger (e.g. beforePayment, afterShipping)"),
|
|
1629
|
+
active: z2.boolean().default(true).describe("Whether function is active")
|
|
1630
|
+
});
|
|
1631
|
+
var functionListSchema = z2.object({
|
|
1632
|
+
versionId: z2.string().uuid().describe("Version UUID to list functions for")
|
|
1633
|
+
});
|
|
1634
|
+
var schemas = {
|
|
1635
|
+
store: {
|
|
1636
|
+
create: storeCreateSchema,
|
|
1637
|
+
list: storeListSchema
|
|
1638
|
+
},
|
|
1639
|
+
version: {
|
|
1640
|
+
create: versionCreateSchema,
|
|
1641
|
+
list: versionListSchema
|
|
1642
|
+
},
|
|
1643
|
+
component: {
|
|
1644
|
+
create: componentCreateSchema,
|
|
1645
|
+
list: componentListSchema
|
|
1646
|
+
},
|
|
1647
|
+
function: {
|
|
1648
|
+
create: functionCreateSchema,
|
|
1649
|
+
list: functionListSchema
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
function getSchemaNames() {
|
|
1653
|
+
const names = [];
|
|
1654
|
+
for (const [resource, actions] of Object.entries(schemas)) {
|
|
1655
|
+
names.push(resource);
|
|
1656
|
+
for (const action of Object.keys(actions)) {
|
|
1657
|
+
names.push(`${resource}.${action}`);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return names;
|
|
1661
|
+
}
|
|
1662
|
+
function getJsonSchema(name) {
|
|
1663
|
+
const parts = name.split(".");
|
|
1664
|
+
const resource = parts[0];
|
|
1665
|
+
const action = parts[1];
|
|
1666
|
+
if (!resource || !schemas[resource]) return null;
|
|
1667
|
+
if (action) {
|
|
1668
|
+
const schema = schemas[resource][action];
|
|
1669
|
+
if (!schema) return null;
|
|
1670
|
+
return zodToJsonSchema(schema, { name: `${resource}.${action}` });
|
|
1671
|
+
}
|
|
1672
|
+
const result = {};
|
|
1673
|
+
for (const [actionName, schema] of Object.entries(schemas[resource])) {
|
|
1674
|
+
result[actionName] = zodToJsonSchema(schema, {
|
|
1675
|
+
name: `${resource}.${actionName}`
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
return result;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// src/core/component.ts
|
|
1682
|
+
async function createComponent(client, input) {
|
|
1683
|
+
const parsed2 = componentCreateSchema.safeParse(input);
|
|
1684
|
+
if (!parsed2.success) {
|
|
1685
|
+
return {
|
|
1686
|
+
error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
const { data, error } = await client.from("components").insert({
|
|
1690
|
+
name: parsed2.data.name,
|
|
1691
|
+
slot: parsed2.data.slot,
|
|
1692
|
+
active: parsed2.data.active,
|
|
1693
|
+
version_id: parsed2.data.versionId,
|
|
1694
|
+
props: parsed2.data.props
|
|
1695
|
+
}).select("id").single();
|
|
1696
|
+
if (error) {
|
|
1697
|
+
return { error: { message: error.message } };
|
|
1698
|
+
}
|
|
1699
|
+
return { data: { id: data.id } };
|
|
1700
|
+
}
|
|
1701
|
+
async function listComponents(client, storeId, versionId) {
|
|
1702
|
+
let query = client.from("components").select(
|
|
1703
|
+
"id, name, slot, active, version_id, props, created_at, versions!inner(id, name)"
|
|
1704
|
+
).eq("versions.store_id", storeId);
|
|
1705
|
+
if (versionId) {
|
|
1706
|
+
query = query.eq("versions.id", versionId);
|
|
1707
|
+
}
|
|
1708
|
+
const { data, error } = await query;
|
|
1709
|
+
if (error) {
|
|
1710
|
+
return { error: { message: error.message } };
|
|
1711
|
+
}
|
|
1712
|
+
return { data: data ?? [] };
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// src/commands/component-cmd.ts
|
|
1716
|
+
async function componentCommand(parsed2) {
|
|
1717
|
+
const sub = parsed2.subcommand;
|
|
1718
|
+
if (sub === "create") return componentCreateCommand(parsed2);
|
|
1719
|
+
if (sub === "list" || sub === "ls") return componentListCommand(parsed2);
|
|
1720
|
+
console.error(
|
|
1721
|
+
`Unknown component subcommand: ${sub}. Use: component create | component list`
|
|
1722
|
+
);
|
|
1723
|
+
process.exit(1);
|
|
1724
|
+
}
|
|
1725
|
+
async function componentCreateCommand(parsed2) {
|
|
1726
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1727
|
+
try {
|
|
1728
|
+
let input;
|
|
1729
|
+
if (parsed2.global.data) {
|
|
1730
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
1731
|
+
input = {
|
|
1732
|
+
versionId: validateUuid(raw.versionId, "versionId"),
|
|
1733
|
+
name: validateRequired(raw.name, "name"),
|
|
1734
|
+
slot: validateRequired(raw.slot, "slot"),
|
|
1735
|
+
active: raw.active ?? true,
|
|
1736
|
+
props: raw.props ?? null
|
|
1737
|
+
};
|
|
1738
|
+
} else {
|
|
1739
|
+
input = {
|
|
1740
|
+
versionId: validateUuid(
|
|
1741
|
+
validateRequired(getFlag(parsed2.flags, "version-id"), "version-id"),
|
|
1742
|
+
"version-id"
|
|
1743
|
+
),
|
|
1744
|
+
name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
|
|
1745
|
+
slot: validateRequired(getFlag(parsed2.flags, "slot", "s"), "slot"),
|
|
1746
|
+
active: getBoolFlag(parsed2.flags, "active") || !("active" in parsed2.flags),
|
|
1747
|
+
props: null
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
if (parsed2.global.dryRun) {
|
|
1751
|
+
outputDryRun(
|
|
1752
|
+
"component.create",
|
|
1753
|
+
input,
|
|
1754
|
+
format
|
|
1755
|
+
);
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
const client = await getAuthenticatedClient();
|
|
1759
|
+
const result = await createComponent(client, input);
|
|
1760
|
+
outputResult(result, format, parsed2.global.fields);
|
|
1761
|
+
if (result.error) process.exit(1);
|
|
1762
|
+
} catch (err) {
|
|
1763
|
+
outputResult(
|
|
1764
|
+
{
|
|
1765
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
1766
|
+
},
|
|
1767
|
+
format
|
|
1768
|
+
);
|
|
1769
|
+
process.exit(1);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
async function componentListCommand(parsed2) {
|
|
1773
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1774
|
+
try {
|
|
1775
|
+
const storeId = validateUuid(
|
|
1776
|
+
validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
|
|
1777
|
+
"store-id"
|
|
1778
|
+
);
|
|
1779
|
+
const versionId = getFlag(parsed2.flags, "version-id");
|
|
1780
|
+
if (versionId) validateUuid(versionId, "version-id");
|
|
1781
|
+
const client = await getAuthenticatedClient();
|
|
1782
|
+
const result = await listComponents(client, storeId, versionId);
|
|
1783
|
+
outputResult(result, format, parsed2.global.fields);
|
|
1784
|
+
if (result.error) process.exit(1);
|
|
1785
|
+
} catch (err) {
|
|
1786
|
+
outputResult(
|
|
1787
|
+
{
|
|
1788
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
1789
|
+
},
|
|
1790
|
+
format
|
|
1791
|
+
);
|
|
1792
|
+
process.exit(1);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
// src/core/deploy.ts
|
|
1797
|
+
async function uploadBuild(input) {
|
|
1798
|
+
const builderUrl = getBuilderUrl();
|
|
1799
|
+
const token = await getAuthToken();
|
|
1800
|
+
const formData = new FormData();
|
|
1801
|
+
formData.append(
|
|
1802
|
+
"code",
|
|
1803
|
+
new Blob([input.zipBuffer], { type: "application/zip" }),
|
|
1804
|
+
"code.zip"
|
|
1805
|
+
);
|
|
1806
|
+
formData.append("type", input.type);
|
|
1807
|
+
formData.append("resource_id", input.resourceId);
|
|
1808
|
+
let response;
|
|
1809
|
+
try {
|
|
1810
|
+
response = await fetch(`${builderUrl}/`, {
|
|
1811
|
+
method: "POST",
|
|
1812
|
+
headers: {
|
|
1813
|
+
Authorization: `Bearer ${token}`
|
|
1814
|
+
},
|
|
1815
|
+
body: formData
|
|
1816
|
+
});
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
return {
|
|
1819
|
+
success: false,
|
|
1820
|
+
error: {
|
|
1821
|
+
message: `Cannot reach builder at ${builderUrl}: ${err instanceof Error ? err.message : String(err)}`
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
const body = await response.json();
|
|
1826
|
+
if (!response.ok || !body.success) {
|
|
1827
|
+
const msg = body.error?.message || body.error?.type || `Builder returned ${response.status}`;
|
|
1828
|
+
return { success: false, error: { message: msg } };
|
|
1829
|
+
}
|
|
1830
|
+
return { success: true, data: body.data };
|
|
1831
|
+
}
|
|
1832
|
+
async function fetchBuildStatus(buildId) {
|
|
1833
|
+
const builderUrl = getBuilderUrl();
|
|
1834
|
+
const token = await getAuthToken();
|
|
1835
|
+
let response;
|
|
1836
|
+
try {
|
|
1837
|
+
response = await fetch(`${builderUrl}/${encodeURIComponent(buildId)}`, {
|
|
1838
|
+
method: "GET",
|
|
1839
|
+
headers: {
|
|
1840
|
+
Authorization: `Bearer ${token}`
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
} catch (err) {
|
|
1844
|
+
return {
|
|
1845
|
+
success: false,
|
|
1846
|
+
error: {
|
|
1847
|
+
message: `Cannot reach builder at ${builderUrl}: ${err instanceof Error ? err.message : String(err)}`
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
const body = await response.json();
|
|
1852
|
+
if (!response.ok || !body.success) {
|
|
1853
|
+
const msg = body.error?.message || body.error?.type || `Builder returned ${response.status}`;
|
|
1854
|
+
return { success: false, error: { message: msg } };
|
|
1855
|
+
}
|
|
1856
|
+
return { success: true, data: body.data };
|
|
1857
|
+
}
|
|
1858
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
1859
|
+
"SUCCEEDED",
|
|
1860
|
+
"FAILED",
|
|
1861
|
+
"STOPPED",
|
|
1862
|
+
"TIMED_OUT",
|
|
1863
|
+
"FAULT"
|
|
1864
|
+
]);
|
|
1865
|
+
function isTerminalStatus(status) {
|
|
1866
|
+
return TERMINAL_STATUSES.has(status);
|
|
1867
|
+
}
|
|
1868
|
+
async function pollBuildStatus(buildId, options = {}) {
|
|
1869
|
+
const { intervalMs = 5e3, timeoutMs = 3e5, onPoll } = options;
|
|
1870
|
+
const deadline = Date.now() + timeoutMs;
|
|
1871
|
+
while (Date.now() < deadline) {
|
|
1872
|
+
const result = await fetchBuildStatus(buildId);
|
|
1873
|
+
if (!result.success) {
|
|
1874
|
+
return result;
|
|
1875
|
+
}
|
|
1876
|
+
if (onPoll) {
|
|
1877
|
+
onPoll(result.data);
|
|
1878
|
+
}
|
|
1879
|
+
if (isTerminalStatus(result.data.status)) {
|
|
1880
|
+
return result;
|
|
1881
|
+
}
|
|
1882
|
+
const remaining = deadline - Date.now();
|
|
1883
|
+
if (remaining <= 0) break;
|
|
1884
|
+
await new Promise(
|
|
1885
|
+
(resolve) => setTimeout(resolve, Math.min(intervalMs, remaining))
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
return {
|
|
1889
|
+
success: false,
|
|
1890
|
+
error: { message: `Build ${buildId} timed out after ${timeoutMs / 1e3}s` }
|
|
1891
|
+
};
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
// src/commands/deploy-cmd.ts
|
|
1895
|
+
async function deployCommand(parsed2) {
|
|
1896
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
1897
|
+
try {
|
|
1898
|
+
let resourceId;
|
|
1899
|
+
let componentName;
|
|
1900
|
+
let resourceType;
|
|
1901
|
+
let wait;
|
|
1902
|
+
let timeout;
|
|
1903
|
+
if (parsed2.global.data) {
|
|
1904
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
1905
|
+
resourceId = validateUuid(
|
|
1906
|
+
raw.componentId || raw.functionId || raw.resourceId,
|
|
1907
|
+
"componentId or functionId"
|
|
1908
|
+
);
|
|
1909
|
+
componentName = validateRequired(raw.name, "name");
|
|
1910
|
+
resourceType = raw.type === "function" ? "function" : "component";
|
|
1911
|
+
wait = raw.wait ?? false;
|
|
1912
|
+
timeout = raw.timeout ?? 300;
|
|
1913
|
+
} else {
|
|
1914
|
+
const compId = getFlag(parsed2.flags, "component-id");
|
|
1915
|
+
const funcId = getFlag(parsed2.flags, "function-id");
|
|
1916
|
+
if (compId && funcId) {
|
|
1917
|
+
throw new Error(
|
|
1918
|
+
"Provide either --component-id or --function-id, not both."
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
if (compId) {
|
|
1922
|
+
resourceId = validateUuid(compId, "component-id");
|
|
1923
|
+
resourceType = "component";
|
|
1924
|
+
} else if (funcId) {
|
|
1925
|
+
resourceId = validateUuid(funcId, "function-id");
|
|
1926
|
+
resourceType = "function";
|
|
1927
|
+
} else {
|
|
1928
|
+
throw new Error("Either --component-id or --function-id is required.");
|
|
1929
|
+
}
|
|
1930
|
+
componentName = validateRequired(
|
|
1931
|
+
getFlag(parsed2.flags, "name", "n"),
|
|
1932
|
+
"name"
|
|
1933
|
+
);
|
|
1934
|
+
wait = getBoolFlag(parsed2.flags, "wait");
|
|
1935
|
+
timeout = Number(getFlag(parsed2.flags, "timeout") ?? "300");
|
|
1936
|
+
}
|
|
1937
|
+
const stream = await createComponentBundle({
|
|
1938
|
+
componentName,
|
|
1939
|
+
cwd: process.cwd()
|
|
1940
|
+
});
|
|
1941
|
+
const chunks = [];
|
|
1942
|
+
for await (const chunk of stream) {
|
|
1943
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1944
|
+
}
|
|
1945
|
+
const zipBuffer = Buffer.concat(chunks);
|
|
1946
|
+
if (parsed2.global.dryRun) {
|
|
1947
|
+
outputDryRun(
|
|
1948
|
+
"deploy",
|
|
1949
|
+
{
|
|
1950
|
+
resourceId,
|
|
1951
|
+
resourceType,
|
|
1952
|
+
componentName,
|
|
1953
|
+
bundleSizeBytes: zipBuffer.length,
|
|
1954
|
+
bundleSizeKB: Math.round(zipBuffer.length / 1024),
|
|
1955
|
+
wait,
|
|
1956
|
+
timeout
|
|
1957
|
+
},
|
|
1958
|
+
format
|
|
1959
|
+
);
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const uploadResult = await uploadBuild({
|
|
1963
|
+
resourceId,
|
|
1964
|
+
type: resourceType,
|
|
1965
|
+
zipBuffer
|
|
1966
|
+
});
|
|
1967
|
+
if (!uploadResult.success) {
|
|
1968
|
+
outputResult(uploadResult, format);
|
|
1969
|
+
process.exit(1);
|
|
1970
|
+
}
|
|
1971
|
+
if (wait) {
|
|
1972
|
+
const isJson = format === "json";
|
|
1973
|
+
if (!isJson) {
|
|
1974
|
+
process.stderr.write(
|
|
1975
|
+
`Build ${uploadResult.data.id} started. Polling...
|
|
1976
|
+
`
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
const pollResult = await pollBuildStatus(uploadResult.data.id, {
|
|
1980
|
+
timeoutMs: timeout * 1e3,
|
|
1981
|
+
onPoll: (build) => {
|
|
1982
|
+
if (!isJson) {
|
|
1983
|
+
process.stderr.write(` status: ${build.status}
|
|
1984
|
+
`);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
outputResult(pollResult, format, parsed2.global.fields);
|
|
1989
|
+
if (!pollResult.success) process.exit(1);
|
|
1990
|
+
if (pollResult.success && isTerminalStatus(pollResult.data.status) && pollResult.data.status !== "SUCCEEDED") {
|
|
1991
|
+
process.exit(1);
|
|
1992
|
+
}
|
|
1993
|
+
} else {
|
|
1994
|
+
outputResult(uploadResult, format, parsed2.global.fields);
|
|
1995
|
+
}
|
|
1996
|
+
} catch (err) {
|
|
1997
|
+
outputResult(
|
|
1998
|
+
{
|
|
1999
|
+
success: false,
|
|
2000
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2001
|
+
},
|
|
2002
|
+
format
|
|
2003
|
+
);
|
|
2004
|
+
process.exit(1);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// src/commands/init-cmd.ts
|
|
2009
|
+
async function initCommand(parsed2) {
|
|
2010
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2011
|
+
try {
|
|
2012
|
+
const storeId = validateUuid(
|
|
2013
|
+
validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
|
|
2014
|
+
"store-id"
|
|
2015
|
+
);
|
|
2016
|
+
const versionIdRaw = getFlag(parsed2.flags, "version-id");
|
|
2017
|
+
const versionId = versionIdRaw ? validateUuid(versionIdRaw, "version-id") : void 0;
|
|
2018
|
+
const stage = getFlag(parsed2.flags, "stage", "s");
|
|
2019
|
+
await saveConfig(
|
|
2020
|
+
{
|
|
2021
|
+
storeId,
|
|
2022
|
+
...versionId ? { versionId } : {}
|
|
2023
|
+
},
|
|
2024
|
+
{ stage }
|
|
2025
|
+
);
|
|
2026
|
+
const fileName = stage && stage !== "prod" ? `ollie.${stage}.json` : "ollie.json";
|
|
2027
|
+
outputResult(
|
|
2028
|
+
{
|
|
2029
|
+
data: {
|
|
2030
|
+
file: fileName,
|
|
2031
|
+
storeId,
|
|
2032
|
+
...versionId ? { versionId } : {}
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
format,
|
|
2036
|
+
parsed2.global.fields
|
|
2037
|
+
);
|
|
2038
|
+
} catch (err) {
|
|
2039
|
+
outputResult(
|
|
2040
|
+
{
|
|
2041
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2042
|
+
},
|
|
2043
|
+
format
|
|
2044
|
+
);
|
|
2045
|
+
process.exit(1);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// src/commands/schema-cmd.ts
|
|
2050
|
+
async function schemaCommand(parsed2) {
|
|
2051
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2052
|
+
const resourceName = parsed2.subcommand || parsed2.positional[0];
|
|
2053
|
+
if (!resourceName) {
|
|
2054
|
+
const names = getSchemaNames();
|
|
2055
|
+
if (format === "json") {
|
|
2056
|
+
process.stdout.write(`${JSON.stringify({ schemas: names })}
|
|
2057
|
+
`);
|
|
2058
|
+
} else {
|
|
2059
|
+
console.log("\x1B[1mAvailable schemas:\x1B[0m");
|
|
2060
|
+
for (const name of names) {
|
|
2061
|
+
const indent = name.includes(".") ? " " : " ";
|
|
2062
|
+
console.log(`${indent}\x1B[32m${name}\x1B[0m`);
|
|
2063
|
+
}
|
|
2064
|
+
console.log("\nUsage: ollieshop schema <resource[.action]>");
|
|
2065
|
+
}
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
const schema = getJsonSchema(resourceName);
|
|
2069
|
+
if (!schema) {
|
|
2070
|
+
console.error(
|
|
2071
|
+
`Unknown schema: ${resourceName}. Run \`ollieshop schema\` to see available schemas.`
|
|
2072
|
+
);
|
|
2073
|
+
process.exit(1);
|
|
2074
|
+
}
|
|
2075
|
+
process.stdout.write(`${JSON.stringify(schema, null, 2)}
|
|
2076
|
+
`);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/commands/status-cmd.ts
|
|
2080
|
+
async function statusCommand(parsed2) {
|
|
2081
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2082
|
+
try {
|
|
2083
|
+
let buildId;
|
|
2084
|
+
let wait;
|
|
2085
|
+
let timeout;
|
|
2086
|
+
if (parsed2.global.data) {
|
|
2087
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
2088
|
+
buildId = validateRequired(raw.buildId, "buildId");
|
|
2089
|
+
wait = raw.wait ?? false;
|
|
2090
|
+
timeout = raw.timeout ?? 300;
|
|
2091
|
+
} else {
|
|
2092
|
+
buildId = validateRequired(getFlag(parsed2.flags, "build-id"), "build-id");
|
|
2093
|
+
wait = getBoolFlag(parsed2.flags, "wait");
|
|
2094
|
+
timeout = Number(getFlag(parsed2.flags, "timeout") ?? "300");
|
|
2095
|
+
}
|
|
2096
|
+
if (wait) {
|
|
2097
|
+
const isJson = format === "json";
|
|
2098
|
+
if (!isJson) {
|
|
2099
|
+
process.stderr.write(`Polling build ${buildId}...
|
|
2100
|
+
`);
|
|
2101
|
+
}
|
|
2102
|
+
const result = await pollBuildStatus(buildId, {
|
|
2103
|
+
timeoutMs: timeout * 1e3,
|
|
2104
|
+
onPoll: (build) => {
|
|
2105
|
+
if (!isJson) {
|
|
2106
|
+
process.stderr.write(` status: ${build.status}
|
|
2107
|
+
`);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2112
|
+
if (!result.success) process.exit(1);
|
|
2113
|
+
if (result.success && isTerminalStatus(result.data.status) && result.data.status !== "SUCCEEDED") {
|
|
2114
|
+
process.exit(1);
|
|
2115
|
+
}
|
|
2116
|
+
} else {
|
|
2117
|
+
const result = await fetchBuildStatus(buildId);
|
|
2118
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2119
|
+
if (!result.success) process.exit(1);
|
|
2120
|
+
}
|
|
2121
|
+
} catch (err) {
|
|
2122
|
+
outputResult(
|
|
2123
|
+
{
|
|
2124
|
+
success: false,
|
|
2125
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2126
|
+
},
|
|
2127
|
+
format
|
|
2128
|
+
);
|
|
2129
|
+
process.exit(1);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// src/core/store.ts
|
|
2134
|
+
async function createStore(client, input) {
|
|
2135
|
+
const parsed2 = storeCreateSchema.safeParse(input);
|
|
2136
|
+
if (!parsed2.success) {
|
|
2137
|
+
return {
|
|
2138
|
+
error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
const organizationId = await getOrganizationId(client);
|
|
2142
|
+
const { data, error } = await client.from("stores").insert({
|
|
2143
|
+
name: parsed2.data.name,
|
|
2144
|
+
platform: parsed2.data.platform,
|
|
2145
|
+
platform_store_id: parsed2.data.platformStoreId,
|
|
2146
|
+
organization_id: organizationId,
|
|
2147
|
+
logo: parsed2.data.logo ?? null,
|
|
2148
|
+
settings: parsed2.data.settings ?? null
|
|
2149
|
+
}).select("id").single();
|
|
2150
|
+
if (error) {
|
|
2151
|
+
return { error: { message: error.message } };
|
|
2152
|
+
}
|
|
2153
|
+
return { data: { id: data.id } };
|
|
2154
|
+
}
|
|
2155
|
+
async function listStores(client) {
|
|
2156
|
+
const organizationId = await getOrganizationId(client);
|
|
2157
|
+
const { data, error } = await client.from("stores").select(
|
|
2158
|
+
"id, name, platform, platform_store_id, organization_id, logo, settings, created_at"
|
|
2159
|
+
).eq("organization_id", organizationId).order("created_at", { ascending: false });
|
|
2160
|
+
if (error) {
|
|
2161
|
+
return { error: { message: error.message } };
|
|
2162
|
+
}
|
|
2163
|
+
return { data };
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
// src/commands/store-cmd.ts
|
|
2167
|
+
async function storeCommand(parsed2) {
|
|
2168
|
+
const sub = parsed2.subcommand;
|
|
2169
|
+
if (sub === "create") return storeCreateCommand(parsed2);
|
|
2170
|
+
if (sub === "list" || sub === "ls") return storeListCommand(parsed2);
|
|
2171
|
+
console.error(
|
|
2172
|
+
`Unknown store subcommand: ${sub}. Use: store create | store list`
|
|
2173
|
+
);
|
|
2174
|
+
process.exit(1);
|
|
2175
|
+
}
|
|
2176
|
+
async function storeCreateCommand(parsed2) {
|
|
2177
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2178
|
+
try {
|
|
2179
|
+
let input;
|
|
2180
|
+
if (parsed2.global.data) {
|
|
2181
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
2182
|
+
input = {
|
|
2183
|
+
name: validateRequired(raw.name, "name"),
|
|
2184
|
+
platform: validateEnum(raw.platform, PLATFORM_VENDORS, "platform"),
|
|
2185
|
+
platformStoreId: validateRequired(
|
|
2186
|
+
raw.platformStoreId,
|
|
2187
|
+
"platformStoreId"
|
|
2188
|
+
),
|
|
2189
|
+
logo: raw.logo,
|
|
2190
|
+
settings: raw.settings
|
|
2191
|
+
};
|
|
2192
|
+
} else {
|
|
2193
|
+
input = {
|
|
2194
|
+
name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
|
|
2195
|
+
platform: validateEnum(
|
|
2196
|
+
validateRequired(getFlag(parsed2.flags, "platform", "p"), "platform"),
|
|
2197
|
+
PLATFORM_VENDORS,
|
|
2198
|
+
"platform"
|
|
2199
|
+
),
|
|
2200
|
+
platformStoreId: validateRequired(
|
|
2201
|
+
getFlag(parsed2.flags, "platform-store-id"),
|
|
2202
|
+
"platform-store-id"
|
|
2203
|
+
),
|
|
2204
|
+
logo: getFlag(parsed2.flags, "logo"),
|
|
2205
|
+
settings: getFlag(parsed2.flags, "settings")
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if (parsed2.global.dryRun) {
|
|
2209
|
+
outputDryRun("store.create", input, format);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
const client = await getAuthenticatedClient();
|
|
2213
|
+
const result = await createStore(client, input);
|
|
2214
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2215
|
+
if (result.error) process.exit(1);
|
|
2216
|
+
} catch (err) {
|
|
2217
|
+
outputResult(
|
|
2218
|
+
{
|
|
2219
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2220
|
+
},
|
|
2221
|
+
format
|
|
2222
|
+
);
|
|
2223
|
+
process.exit(1);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
async function storeListCommand(parsed2) {
|
|
2227
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2228
|
+
try {
|
|
2229
|
+
const client = await getAuthenticatedClient();
|
|
2230
|
+
const result = await listStores(client);
|
|
2231
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2232
|
+
if (result.error) process.exit(1);
|
|
2233
|
+
} catch (err) {
|
|
2234
|
+
outputResult(
|
|
2235
|
+
{
|
|
2236
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2237
|
+
},
|
|
2238
|
+
format
|
|
2239
|
+
);
|
|
2240
|
+
process.exit(1);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/core/version.ts
|
|
2245
|
+
async function createVersion(client, input) {
|
|
2246
|
+
const parsed2 = versionCreateSchema.safeParse(input);
|
|
2247
|
+
if (!parsed2.success) {
|
|
2248
|
+
return {
|
|
2249
|
+
error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
const { data, error } = await client.from("versions").insert({
|
|
2253
|
+
name: parsed2.data.name,
|
|
2254
|
+
active: parsed2.data.active,
|
|
2255
|
+
default: parsed2.data.default,
|
|
2256
|
+
store_id: parsed2.data.storeId,
|
|
2257
|
+
template: parsed2.data.template
|
|
2258
|
+
}).select("id").single();
|
|
2259
|
+
if (error) {
|
|
2260
|
+
return { error: { message: error.message } };
|
|
2261
|
+
}
|
|
2262
|
+
return { data: { id: data.id } };
|
|
2263
|
+
}
|
|
2264
|
+
async function listVersions(client, storeId) {
|
|
2265
|
+
const { data, error } = await client.from("versions").select("id, name, active, default, store_id, template, created_at").eq("store_id", storeId).order("created_at", { ascending: false });
|
|
2266
|
+
if (error) {
|
|
2267
|
+
return { error: { message: error.message } };
|
|
2268
|
+
}
|
|
2269
|
+
return { data };
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// src/commands/version-cmd.ts
|
|
2273
|
+
async function versionCommand(parsed2) {
|
|
2274
|
+
const sub = parsed2.subcommand;
|
|
2275
|
+
if (sub === "create") return versionCreateCommand(parsed2);
|
|
2276
|
+
if (sub === "list" || sub === "ls") return versionListCommand(parsed2);
|
|
2277
|
+
console.error(
|
|
2278
|
+
`Unknown version subcommand: ${sub}. Use: version create | version list`
|
|
2279
|
+
);
|
|
2280
|
+
process.exit(1);
|
|
2281
|
+
}
|
|
2282
|
+
async function versionCreateCommand(parsed2) {
|
|
2283
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2284
|
+
try {
|
|
2285
|
+
let input;
|
|
2286
|
+
if (parsed2.global.data) {
|
|
2287
|
+
const raw = JSON.parse(parsed2.global.data);
|
|
2288
|
+
input = {
|
|
2289
|
+
storeId: validateUuid(raw.storeId, "storeId"),
|
|
2290
|
+
name: validateRequired(raw.name, "name"),
|
|
2291
|
+
active: raw.active ?? false,
|
|
2292
|
+
default: raw.default ?? false,
|
|
2293
|
+
template: raw.template ?? null
|
|
2294
|
+
};
|
|
2295
|
+
} else {
|
|
2296
|
+
input = {
|
|
2297
|
+
storeId: validateUuid(
|
|
2298
|
+
validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
|
|
2299
|
+
"store-id"
|
|
2300
|
+
),
|
|
2301
|
+
name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
|
|
2302
|
+
active: getBoolFlag(parsed2.flags, "active"),
|
|
2303
|
+
default: getBoolFlag(parsed2.flags, "default"),
|
|
2304
|
+
template: getFlag(parsed2.flags, "template") ?? null
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
if (parsed2.global.dryRun) {
|
|
2308
|
+
outputDryRun("version.create", input, format);
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
const client = await getAuthenticatedClient();
|
|
2312
|
+
const result = await createVersion(client, input);
|
|
2313
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2314
|
+
if (result.error) process.exit(1);
|
|
2315
|
+
} catch (err) {
|
|
2316
|
+
outputResult(
|
|
2317
|
+
{
|
|
2318
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2319
|
+
},
|
|
2320
|
+
format
|
|
2321
|
+
);
|
|
2322
|
+
process.exit(1);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
async function versionListCommand(parsed2) {
|
|
2326
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2327
|
+
try {
|
|
2328
|
+
const storeId = validateUuid(
|
|
2329
|
+
validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
|
|
2330
|
+
"store-id"
|
|
2331
|
+
);
|
|
2332
|
+
const client = await getAuthenticatedClient();
|
|
2333
|
+
const result = await listVersions(client, storeId);
|
|
2334
|
+
outputResult(result, format, parsed2.global.fields);
|
|
2335
|
+
if (result.error) process.exit(1);
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
outputResult(
|
|
2338
|
+
{
|
|
2339
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2340
|
+
},
|
|
2341
|
+
format
|
|
2342
|
+
);
|
|
2343
|
+
process.exit(1);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/commands/whoami.ts
|
|
2348
|
+
async function whoamiCommand(parsed2) {
|
|
2349
|
+
const format = detectOutputFormat(parsed2.global.output);
|
|
2350
|
+
const user = await getCurrentUser();
|
|
2351
|
+
if (!user) {
|
|
2352
|
+
outputResult(
|
|
2353
|
+
{
|
|
2354
|
+
error: { message: "Not logged in. Run `ollieshop login`." }
|
|
2355
|
+
},
|
|
2356
|
+
format
|
|
2357
|
+
);
|
|
2358
|
+
process.exit(1);
|
|
2359
|
+
}
|
|
2360
|
+
outputResult(
|
|
2361
|
+
{
|
|
2362
|
+
data: {
|
|
2363
|
+
email: user.email
|
|
2364
|
+
}
|
|
2365
|
+
},
|
|
2366
|
+
format,
|
|
2367
|
+
parsed2.global.fields
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
|
|
1099
2371
|
// src/index.tsx
|
|
1100
2372
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1101
|
-
var
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
2373
|
+
var AGENT_COMMANDS = {
|
|
2374
|
+
whoami: whoamiCommand,
|
|
2375
|
+
store: storeCommand,
|
|
2376
|
+
version: versionCommand,
|
|
2377
|
+
component: componentCommand,
|
|
2378
|
+
"business-rule": businessRuleCommand,
|
|
2379
|
+
schema: schemaCommand,
|
|
2380
|
+
init: initCommand,
|
|
2381
|
+
deploy: deployCommand,
|
|
2382
|
+
status: statusCommand
|
|
2383
|
+
};
|
|
2384
|
+
var parsed = parseArgs(process.argv);
|
|
2385
|
+
if (parsed.command in AGENT_COMMANDS) {
|
|
2386
|
+
AGENT_COMMANDS[parsed.command](parsed).catch((err) => {
|
|
2387
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2388
|
+
process.stderr.write(`${JSON.stringify({ error: { message: msg } })}
|
|
2389
|
+
`);
|
|
2390
|
+
process.exit(1);
|
|
2391
|
+
});
|
|
2392
|
+
} else {
|
|
2393
|
+
const args = process.argv.slice(2);
|
|
2394
|
+
const command = args[0] || "help";
|
|
2395
|
+
const commandArgs = args.slice(1);
|
|
2396
|
+
render(/* @__PURE__ */ jsx5(App, { command, args: commandArgs }));
|
|
2397
|
+
}
|