@ollie-shop/cli 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,49 +21,98 @@ 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: 16, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "login" }) }),
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: 16, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "start" }) }),
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" })
33
48
  ] }),
34
49
  /* @__PURE__ */ jsxs(Box, { children: [
35
- /* @__PURE__ */ jsx(Box, { width: 16, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "help" }) }),
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: "schema" }) }),
55
+ /* @__PURE__ */ jsx(Text, { children: "Introspect resource schemas (JSON Schema)" })
56
+ ] }),
57
+ /* @__PURE__ */ jsxs(Box, { children: [
58
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "init" }) }),
59
+ /* @__PURE__ */ jsx(Text, { children: "Write store/version IDs to ollie.json" })
60
+ ] }),
61
+ /* @__PURE__ */ jsxs(Box, { children: [
62
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "help" }) }),
36
63
  /* @__PURE__ */ jsx(Text, { children: "Show this help message" })
37
64
  ] }),
38
65
  /* @__PURE__ */ jsxs(Box, { children: [
39
- /* @__PURE__ */ jsx(Box, { width: 16, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "version" }) }),
66
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "version" }) }),
40
67
  /* @__PURE__ */ jsx(Text, { children: "Show CLI version" })
41
68
  ] })
42
69
  ] }),
43
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Start Options:" }) }),
44
- /* @__PURE__ */ jsx(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: /* @__PURE__ */ jsxs(Box, { children: [
45
- /* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--stage, -s" }) }),
46
- /* @__PURE__ */ jsxs(Text, { children: [
47
- "Config stage (loads ollie.",
48
- "{stage}",
49
- ".json)"
50
- ] })
51
- ] }) }),
52
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Configuration:" }) }),
53
- /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
54
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ollie.json - Default config (prod)" }),
55
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ollie.dev.json - Dev stage config" }),
56
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
57
- "ollie.",
58
- "{stage}",
59
- ".json - Custom stage config"
70
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Global Flags:" }) }),
71
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: [
72
+ /* @__PURE__ */ jsxs(Box, { children: [
73
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--output json, -o json" }) }),
74
+ /* @__PURE__ */ jsx(Text, { children: "Force JSON output (auto when piped)" })
75
+ ] }),
76
+ /* @__PURE__ */ jsxs(Box, { children: [
77
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--dry-run" }) }),
78
+ /* @__PURE__ */ jsx(Text, { children: "Validate without executing mutations" })
79
+ ] }),
80
+ /* @__PURE__ */ jsxs(Box, { children: [
81
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--fields a,b,c" }) }),
82
+ /* @__PURE__ */ jsx(Text, { children: "Limit output fields" })
83
+ ] }),
84
+ /* @__PURE__ */ jsxs(Box, { children: [
85
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
86
+ "--data ",
87
+ `'{...}'`
88
+ ] }) }),
89
+ /* @__PURE__ */ jsx(Text, { children: "Raw JSON payload for mutations" })
90
+ ] }),
91
+ /* @__PURE__ */ jsxs(Box, { children: [
92
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--stage, -s" }) }),
93
+ /* @__PURE__ */ jsxs(Text, { children: [
94
+ "Config stage (loads ollie.",
95
+ "{stage}",
96
+ ".json)"
97
+ ] })
60
98
  ] })
61
99
  ] }),
62
100
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Examples:" }) }),
63
101
  /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
64
102
  /* @__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 start --stage dev" })
103
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop start --stage dev" }),
104
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop whoami -o json" }),
105
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop schema store.create" }),
106
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: '$ ollieshop store create --name "My Store" --platform vtex --platform-store-id mystore' }),
107
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
108
+ "$ ollieshop store create --data",
109
+ " ",
110
+ `'{"name":"My Store","platform":"vtex","platformStoreId":"mystore"}'`,
111
+ " ",
112
+ "-o json"
113
+ ] }),
114
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop version create --store-id UUID --name v1 --active" }),
115
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop init --store-id UUID --version-id UUID" })
67
116
  ] })
68
117
  ] });
69
118
  }
@@ -227,14 +276,32 @@ async function saveCredentials(token) {
227
276
  };
228
277
  await fs.writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
229
278
  }
279
+ async function getCredentials() {
280
+ try {
281
+ const content = await fs.readFile(CREDENTIALS_PATH, "utf-8");
282
+ return JSON.parse(content);
283
+ } catch {
284
+ return null;
285
+ }
286
+ }
287
+ async function getCurrentUser() {
288
+ const credentials = await getCredentials();
289
+ if (!credentials) return null;
290
+ try {
291
+ const decoded = jwtDecode(credentials.accessToken);
292
+ return decoded.email ? { email: decoded.email } : null;
293
+ } catch {
294
+ return null;
295
+ }
296
+ }
230
297
 
231
298
  // src/commands/login.tsx
232
299
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
233
- function LoginCommand({ args: args2 }) {
300
+ function LoginCommand({ args }) {
234
301
  const { exit } = useApp();
235
302
  const [state, setState] = useState({ status: "idle" });
236
- const portIndex = args2.findIndex((a) => a === "--port" || a === "-p");
237
- const port = portIndex !== -1 ? Number.parseInt(args2[portIndex + 1], 10) : 7777;
303
+ const portIndex = args.findIndex((a) => a === "--port" || a === "-p");
304
+ const port = portIndex !== -1 ? Number.parseInt(args[portIndex + 1], 10) : 7777;
238
305
  useEffect(() => {
239
306
  if (state.status !== "idle") return;
240
307
  setState({ status: "waiting" });
@@ -341,6 +408,17 @@ async function loadConfig(options = {}) {
341
408
  };
342
409
  return OllieConfigSchema.parse(merged);
343
410
  }
411
+ async function saveConfig(config, options = {}) {
412
+ const { cwd = process.cwd(), stage } = options;
413
+ const fileName = getConfigFileName(stage);
414
+ const configPath = path2.join(cwd, fileName);
415
+ const existing = await loadConfigFile(configPath);
416
+ const merged = {
417
+ ...existing,
418
+ ...config
419
+ };
420
+ await fs2.writeFile(configPath, JSON.stringify(merged, null, 2));
421
+ }
344
422
  function resolveStage(cliStage) {
345
423
  return cliStage || process.env.OLLIE_STAGE || void 0;
346
424
  }
@@ -764,11 +842,11 @@ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
764
842
  var STUDIO_BASE_URL = "https://admin.ollie.shop/studio";
765
843
  var MAX_LOGS = 10;
766
844
  var PORT = 4e3;
767
- function parseArg(args2, ...flags) {
768
- const index = args2.findIndex((a) => flags.includes(a));
769
- return index !== -1 ? args2[index + 1] : void 0;
845
+ function parseArg(args, ...flags) {
846
+ const index = args.findIndex((a) => flags.includes(a));
847
+ return index !== -1 ? args[index + 1] : void 0;
770
848
  }
771
- function StartCommand({ args: args2 }) {
849
+ function StartCommand({ args }) {
772
850
  const { exit } = useApp2();
773
851
  const [state, setState] = useState2({ status: "initializing" });
774
852
  const [components, setComponents] = useState2([]);
@@ -778,7 +856,7 @@ function StartCommand({ args: args2 }) {
778
856
  const logIdRef = useRef(0);
779
857
  const ctxRef = useRef(null);
780
858
  const stopRef = useRef(null);
781
- const stage = resolveStage(parseArg(args2, "--stage", "-s"));
859
+ const stage = resolveStage(parseArg(args, "--stage", "-s"));
782
860
  const addLog = useCallback((log) => {
783
861
  setLogs((prev) => {
784
862
  const newLog = {
@@ -790,12 +868,12 @@ function StartCommand({ args: args2 }) {
790
868
  });
791
869
  }, []);
792
870
  const handleRequest = useCallback(
793
- (args3) => {
871
+ (args2) => {
794
872
  addLog({
795
- method: args3.method,
796
- path: args3.path,
797
- status: args3.status,
798
- time: args3.timeInMS
873
+ method: args2.method,
874
+ path: args2.path,
875
+ status: args2.status,
876
+ time: args2.timeInMS
799
877
  });
800
878
  },
801
879
  [addLog]
@@ -1065,12 +1143,12 @@ function Footer() {
1065
1143
 
1066
1144
  // src/cli.tsx
1067
1145
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1068
- function App({ command: command2, args: args2 }) {
1069
- switch (command2) {
1146
+ function App({ command, args }) {
1147
+ switch (command) {
1070
1148
  case "login":
1071
- return /* @__PURE__ */ jsx4(LoginCommand, { args: args2 });
1149
+ return /* @__PURE__ */ jsx4(LoginCommand, { args });
1072
1150
  case "start":
1073
- return /* @__PURE__ */ jsx4(StartCommand, { args: args2 });
1151
+ return /* @__PURE__ */ jsx4(StartCommand, { args });
1074
1152
  case "help":
1075
1153
  case "--help":
1076
1154
  case "-h":
@@ -1080,25 +1158,785 @@ function App({ command: command2, args: args2 }) {
1080
1158
  case "-v":
1081
1159
  return /* @__PURE__ */ jsx4(VersionCommand, {});
1082
1160
  default:
1083
- return /* @__PURE__ */ jsx4(UnknownCommand, { command: command2 });
1161
+ return /* @__PURE__ */ jsx4(UnknownCommand, { command });
1084
1162
  }
1085
1163
  }
1086
1164
  function VersionCommand() {
1087
1165
  return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { children: "ollie v0.1.0" }) });
1088
1166
  }
1089
- function UnknownCommand({ command: command2 }) {
1167
+ function UnknownCommand({ command }) {
1090
1168
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
1091
1169
  /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
1092
1170
  "Unknown command: ",
1093
- command2
1171
+ command
1094
1172
  ] }),
1095
1173
  /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Run `ollie help` for available commands." })
1096
1174
  ] });
1097
1175
  }
1098
1176
 
1177
+ // src/core/schema.ts
1178
+ import { z as z2 } from "zod";
1179
+ import { zodToJsonSchema } from "zod-to-json-schema";
1180
+ var PLATFORM_VENDORS = ["vtex", "shopify", "vnda", "custom"];
1181
+ var storeCreateSchema = z2.object({
1182
+ name: z2.string().min(1).describe("Store display name"),
1183
+ platform: z2.enum(PLATFORM_VENDORS).describe("E-commerce platform vendor"),
1184
+ platformStoreId: z2.string().min(1).describe("Platform-specific store identifier (e.g. account name)"),
1185
+ logo: z2.string().url().optional().describe("Store logo URL"),
1186
+ settings: z2.string().optional().describe("JSON settings string")
1187
+ });
1188
+ var storeListSchema = z2.object({}).describe("No parameters required");
1189
+ var versionCreateSchema = z2.object({
1190
+ storeId: z2.string().uuid().describe("Parent store UUID"),
1191
+ name: z2.string().min(1).describe("Version name (e.g. v1, main)"),
1192
+ active: z2.boolean().default(false).describe("Whether version is active"),
1193
+ default: z2.boolean().default(false).describe("Whether this is the default version"),
1194
+ template: z2.string().nullable().default(null).describe("Template name or null")
1195
+ });
1196
+ var versionListSchema = z2.object({
1197
+ storeId: z2.string().uuid().describe("Store UUID to list versions for")
1198
+ });
1199
+ var componentCreateSchema = z2.object({
1200
+ versionId: z2.string().uuid().describe("Parent version UUID"),
1201
+ name: z2.string().min(1).describe("Component name (e.g. FreeShippingBar)"),
1202
+ slot: z2.string().min(1).describe(
1203
+ "Target slot (e.g. cart_header_full_page, shipping_address_details_form)"
1204
+ ),
1205
+ active: z2.boolean().default(true).describe("Whether component is active"),
1206
+ props: z2.record(z2.unknown()).nullable().default(null).describe("Default component props as JSON object")
1207
+ });
1208
+ var componentListSchema = z2.object({
1209
+ storeId: z2.string().uuid().describe("Store UUID"),
1210
+ versionId: z2.string().uuid().optional().describe("Optional version UUID to filter by")
1211
+ });
1212
+ var functionCreateSchema = z2.object({
1213
+ versionId: z2.string().uuid().describe("Parent version UUID"),
1214
+ name: z2.string().min(1).describe("Function name"),
1215
+ trigger: z2.string().min(1).describe("Function trigger (e.g. beforePayment, afterShipping)"),
1216
+ active: z2.boolean().default(true).describe("Whether function is active")
1217
+ });
1218
+ var functionListSchema = z2.object({
1219
+ versionId: z2.string().uuid().describe("Version UUID to list functions for")
1220
+ });
1221
+ var schemas = {
1222
+ store: {
1223
+ create: storeCreateSchema,
1224
+ list: storeListSchema
1225
+ },
1226
+ version: {
1227
+ create: versionCreateSchema,
1228
+ list: versionListSchema
1229
+ },
1230
+ component: {
1231
+ create: componentCreateSchema,
1232
+ list: componentListSchema
1233
+ },
1234
+ function: {
1235
+ create: functionCreateSchema,
1236
+ list: functionListSchema
1237
+ }
1238
+ };
1239
+ function getSchemaNames() {
1240
+ const names = [];
1241
+ for (const [resource, actions] of Object.entries(schemas)) {
1242
+ names.push(resource);
1243
+ for (const action of Object.keys(actions)) {
1244
+ names.push(`${resource}.${action}`);
1245
+ }
1246
+ }
1247
+ return names;
1248
+ }
1249
+ function getJsonSchema(name) {
1250
+ const parts = name.split(".");
1251
+ const resource = parts[0];
1252
+ const action = parts[1];
1253
+ if (!resource || !schemas[resource]) return null;
1254
+ if (action) {
1255
+ const schema = schemas[resource][action];
1256
+ if (!schema) return null;
1257
+ return zodToJsonSchema(schema, { name: `${resource}.${action}` });
1258
+ }
1259
+ const result = {};
1260
+ for (const [actionName, schema] of Object.entries(schemas[resource])) {
1261
+ result[actionName] = zodToJsonSchema(schema, {
1262
+ name: `${resource}.${actionName}`
1263
+ });
1264
+ }
1265
+ return result;
1266
+ }
1267
+
1268
+ // src/core/component.ts
1269
+ async function createComponent(client, input) {
1270
+ const parsed2 = componentCreateSchema.safeParse(input);
1271
+ if (!parsed2.success) {
1272
+ return {
1273
+ error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
1274
+ };
1275
+ }
1276
+ const { data, error } = await client.from("components").insert({
1277
+ name: parsed2.data.name,
1278
+ slot: parsed2.data.slot,
1279
+ active: parsed2.data.active,
1280
+ version_id: parsed2.data.versionId,
1281
+ props: parsed2.data.props
1282
+ }).select("id").single();
1283
+ if (error) {
1284
+ return { error: { message: error.message } };
1285
+ }
1286
+ return { data: { id: data.id } };
1287
+ }
1288
+ async function listComponents(client, storeId, versionId) {
1289
+ let query = client.from("components").select(
1290
+ "id, name, slot, active, version_id, props, created_at, versions!inner(id, name)"
1291
+ ).eq("versions.store_id", storeId);
1292
+ if (versionId) {
1293
+ query = query.eq("versions.id", versionId);
1294
+ }
1295
+ const { data, error } = await query;
1296
+ if (error) {
1297
+ return { error: { message: error.message } };
1298
+ }
1299
+ return { data: data ?? [] };
1300
+ }
1301
+
1302
+ // src/utils/output.ts
1303
+ function detectOutputFormat(cliOutput) {
1304
+ if (cliOutput === "json" || cliOutput === "j") return "json";
1305
+ if (cliOutput === "pretty") return "pretty";
1306
+ return process.stdout.isTTY ? "pretty" : "json";
1307
+ }
1308
+ function filterFields(data, fields) {
1309
+ if (!fields || fields.length === 0) return data;
1310
+ const result = {};
1311
+ for (const field of fields) {
1312
+ if (field in data) {
1313
+ result[field] = data[field];
1314
+ }
1315
+ }
1316
+ return result;
1317
+ }
1318
+ function outputResult(result, format, fields) {
1319
+ if (format === "json") {
1320
+ let output = result;
1321
+ if (fields && result.data && typeof result.data === "object") {
1322
+ if (Array.isArray(result.data)) {
1323
+ output = {
1324
+ ...result,
1325
+ data: result.data.map(
1326
+ (item) => filterFields(item, fields)
1327
+ )
1328
+ };
1329
+ } else {
1330
+ output = {
1331
+ ...result,
1332
+ data: filterFields(result.data, fields)
1333
+ };
1334
+ }
1335
+ }
1336
+ process.stdout.write(`${JSON.stringify(output)}
1337
+ `);
1338
+ return;
1339
+ }
1340
+ if (result.error) {
1341
+ const errMsg = typeof result.error === "object" && result.error !== null ? result.error.message || JSON.stringify(result.error) : String(result.error);
1342
+ console.error(`\x1B[31mError:\x1B[0m ${errMsg}`);
1343
+ return;
1344
+ }
1345
+ if (Array.isArray(result.data)) {
1346
+ printTable(result.data, fields);
1347
+ } else if (typeof result.data === "object" && result.data !== null) {
1348
+ const filtered = fields ? filterFields(result.data, fields) : result.data;
1349
+ for (const [key, value] of Object.entries(
1350
+ filtered
1351
+ )) {
1352
+ console.log(`\x1B[1m${key}:\x1B[0m ${value}`);
1353
+ }
1354
+ } else {
1355
+ console.log(result.data);
1356
+ }
1357
+ }
1358
+ function printTable(rows, fields) {
1359
+ if (rows.length === 0) {
1360
+ console.log("(no results)");
1361
+ return;
1362
+ }
1363
+ const keys = fields || Object.keys(rows[0]);
1364
+ const widths = {};
1365
+ for (const key of keys) {
1366
+ widths[key] = key.length;
1367
+ for (const row of rows) {
1368
+ const val = String(row[key] ?? "");
1369
+ widths[key] = Math.max(widths[key], val.length);
1370
+ }
1371
+ widths[key] = Math.min(widths[key], 40);
1372
+ }
1373
+ const header = keys.map((k) => k.padEnd(widths[k])).join(" ");
1374
+ console.log(`\x1B[1m${header}\x1B[0m`);
1375
+ console.log(keys.map((k) => "\u2500".repeat(widths[k])).join("\u2500\u2500"));
1376
+ for (const row of rows) {
1377
+ const line = keys.map(
1378
+ (k) => String(row[k] ?? "").padEnd(widths[k]).slice(0, widths[k])
1379
+ ).join(" ");
1380
+ console.log(line);
1381
+ }
1382
+ }
1383
+ function outputDryRun(action, data, format) {
1384
+ if (format === "json") {
1385
+ process.stdout.write(`${JSON.stringify({ dryRun: true, action, data })}
1386
+ `);
1387
+ } else {
1388
+ console.log(`\x1B[33m[dry-run]\x1B[0m Would execute: ${action}`);
1389
+ for (const [key, value] of Object.entries(data)) {
1390
+ console.log(` ${key}: ${JSON.stringify(value)}`);
1391
+ }
1392
+ }
1393
+ }
1394
+
1395
+ // src/utils/parse-args.ts
1396
+ function parseArgs(argv) {
1397
+ const args = argv.slice(2);
1398
+ const command = args[0] || "help";
1399
+ const flags = {};
1400
+ const positional = [];
1401
+ let subcommand;
1402
+ if (args[1] && !args[1].startsWith("-")) {
1403
+ subcommand = args[1];
1404
+ }
1405
+ const startIdx = subcommand ? 2 : 1;
1406
+ for (let i = startIdx; i < args.length; i++) {
1407
+ const arg = args[i];
1408
+ if (arg.startsWith("--")) {
1409
+ const key = arg.slice(2);
1410
+ const next = args[i + 1];
1411
+ if (next && !next.startsWith("-")) {
1412
+ flags[key] = next;
1413
+ i++;
1414
+ } else {
1415
+ flags[key] = true;
1416
+ }
1417
+ } else if (arg.startsWith("-") && arg.length === 2) {
1418
+ const key = arg.slice(1);
1419
+ const next = args[i + 1];
1420
+ if (next && !next.startsWith("-")) {
1421
+ flags[key] = next;
1422
+ i++;
1423
+ } else {
1424
+ flags[key] = true;
1425
+ }
1426
+ } else {
1427
+ positional.push(arg);
1428
+ }
1429
+ }
1430
+ const global = {
1431
+ output: flags.output || flags.o || void 0,
1432
+ dryRun: flags["dry-run"] === true,
1433
+ fields: flags.fields ? String(flags.fields).split(",").map((f) => f.trim()) : void 0,
1434
+ data: flags.data || flags.d || void 0
1435
+ };
1436
+ return { command, subcommand, flags, global, positional };
1437
+ }
1438
+ function getFlag(flags, ...names) {
1439
+ for (const name of names) {
1440
+ const val = flags[name];
1441
+ if (typeof val === "string") return val;
1442
+ }
1443
+ return void 0;
1444
+ }
1445
+ function getBoolFlag(flags, ...names) {
1446
+ for (const name of names) {
1447
+ if (flags[name] === true) return true;
1448
+ }
1449
+ return false;
1450
+ }
1451
+
1452
+ // src/utils/supabase.ts
1453
+ import { createClient } from "@supabase/supabase-js";
1454
+ function requireEnv(name) {
1455
+ const value = process.env[name];
1456
+ if (!value) {
1457
+ throw new Error(
1458
+ `Missing required environment variable: ${name}. Set it in your .env file or shell.`
1459
+ );
1460
+ }
1461
+ return value;
1462
+ }
1463
+ async function getAuthenticatedClient() {
1464
+ const credentials = await getCredentials();
1465
+ if (!credentials) {
1466
+ throw new Error("Not authenticated. Run `ollieshop login` first.");
1467
+ }
1468
+ const supabaseUrl = requireEnv("OLLIE_SUPABASE_URL");
1469
+ const supabaseAnonKey = requireEnv("OLLIE_SUPABASE_ANON_KEY");
1470
+ const client = createClient(supabaseUrl, supabaseAnonKey, {
1471
+ auth: {
1472
+ autoRefreshToken: false,
1473
+ persistSession: false
1474
+ }
1475
+ });
1476
+ await client.auth.setSession({
1477
+ access_token: credentials.accessToken,
1478
+ refresh_token: credentials.refreshToken
1479
+ });
1480
+ return client;
1481
+ }
1482
+ async function getOrganizationId(client) {
1483
+ const { data: org, error } = await client.from("organizations").select("id").order("created_at", { ascending: true }).limit(1).maybeSingle();
1484
+ if (error) {
1485
+ throw new Error(`Failed to get organization: ${error.message}`);
1486
+ }
1487
+ if (!org) {
1488
+ throw new Error(
1489
+ "No organization found for your account. Create one at https://admin.ollie.shop"
1490
+ );
1491
+ }
1492
+ return org.id;
1493
+ }
1494
+
1495
+ // src/utils/validate.ts
1496
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1497
+ var CONTROL_CHAR_REGEX = /[\x00-\x1f\x7f]/;
1498
+ function validateUuid(value, name) {
1499
+ if (!UUID_REGEX.test(value)) {
1500
+ throw new Error(
1501
+ `Invalid ${name}: "${value}". Must be a valid UUID (e.g. 7217542a-d7c6-40d3-a20e-db13b310a781).`
1502
+ );
1503
+ }
1504
+ return value;
1505
+ }
1506
+ function rejectControlChars(value, name) {
1507
+ if (CONTROL_CHAR_REGEX.test(value)) {
1508
+ throw new Error(
1509
+ `Invalid ${name}: contains control characters. Input must be printable text.`
1510
+ );
1511
+ }
1512
+ return value;
1513
+ }
1514
+ function validateEnum(value, allowed, name) {
1515
+ if (!allowed.includes(value)) {
1516
+ throw new Error(
1517
+ `Invalid ${name}: "${value}". Must be one of: ${allowed.join(", ")}`
1518
+ );
1519
+ }
1520
+ return value;
1521
+ }
1522
+ function validateRequired(value, name) {
1523
+ if (!value || value.trim() === "") {
1524
+ throw new Error(`Missing required parameter: --${name}`);
1525
+ }
1526
+ return rejectControlChars(value.trim(), name);
1527
+ }
1528
+
1529
+ // src/commands/component-cmd.ts
1530
+ async function componentCommand(parsed2) {
1531
+ const sub = parsed2.subcommand;
1532
+ if (sub === "create") return componentCreateCommand(parsed2);
1533
+ if (sub === "list" || sub === "ls") return componentListCommand(parsed2);
1534
+ console.error(
1535
+ `Unknown component subcommand: ${sub}. Use: component create | component list`
1536
+ );
1537
+ process.exit(1);
1538
+ }
1539
+ async function componentCreateCommand(parsed2) {
1540
+ const format = detectOutputFormat(parsed2.global.output);
1541
+ try {
1542
+ let input;
1543
+ if (parsed2.global.data) {
1544
+ const raw = JSON.parse(parsed2.global.data);
1545
+ input = {
1546
+ versionId: validateUuid(raw.versionId, "versionId"),
1547
+ name: validateRequired(raw.name, "name"),
1548
+ slot: validateRequired(raw.slot, "slot"),
1549
+ active: raw.active ?? true,
1550
+ props: raw.props ?? null
1551
+ };
1552
+ } else {
1553
+ input = {
1554
+ versionId: validateUuid(
1555
+ validateRequired(getFlag(parsed2.flags, "version-id"), "version-id"),
1556
+ "version-id"
1557
+ ),
1558
+ name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
1559
+ slot: validateRequired(getFlag(parsed2.flags, "slot", "s"), "slot"),
1560
+ active: getBoolFlag(parsed2.flags, "active") || !("active" in parsed2.flags),
1561
+ props: null
1562
+ };
1563
+ }
1564
+ if (parsed2.global.dryRun) {
1565
+ outputDryRun(
1566
+ "component.create",
1567
+ input,
1568
+ format
1569
+ );
1570
+ return;
1571
+ }
1572
+ const client = await getAuthenticatedClient();
1573
+ const result = await createComponent(client, input);
1574
+ outputResult(result, format, parsed2.global.fields);
1575
+ if (result.error) process.exit(1);
1576
+ } catch (err) {
1577
+ outputResult(
1578
+ {
1579
+ error: { message: err instanceof Error ? err.message : String(err) }
1580
+ },
1581
+ format
1582
+ );
1583
+ process.exit(1);
1584
+ }
1585
+ }
1586
+ async function componentListCommand(parsed2) {
1587
+ const format = detectOutputFormat(parsed2.global.output);
1588
+ try {
1589
+ const storeId = validateUuid(
1590
+ validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
1591
+ "store-id"
1592
+ );
1593
+ const versionId = getFlag(parsed2.flags, "version-id");
1594
+ if (versionId) validateUuid(versionId, "version-id");
1595
+ const client = await getAuthenticatedClient();
1596
+ const result = await listComponents(client, storeId, versionId);
1597
+ outputResult(result, format, parsed2.global.fields);
1598
+ if (result.error) process.exit(1);
1599
+ } catch (err) {
1600
+ outputResult(
1601
+ {
1602
+ error: { message: err instanceof Error ? err.message : String(err) }
1603
+ },
1604
+ format
1605
+ );
1606
+ process.exit(1);
1607
+ }
1608
+ }
1609
+
1610
+ // src/commands/init-cmd.ts
1611
+ async function initCommand(parsed2) {
1612
+ const format = detectOutputFormat(parsed2.global.output);
1613
+ try {
1614
+ const storeId = validateUuid(
1615
+ validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
1616
+ "store-id"
1617
+ );
1618
+ const versionIdRaw = getFlag(parsed2.flags, "version-id");
1619
+ const versionId = versionIdRaw ? validateUuid(versionIdRaw, "version-id") : void 0;
1620
+ const stage = getFlag(parsed2.flags, "stage", "s");
1621
+ await saveConfig(
1622
+ {
1623
+ storeId,
1624
+ ...versionId ? { versionId } : {}
1625
+ },
1626
+ { stage }
1627
+ );
1628
+ const fileName = stage && stage !== "prod" ? `ollie.${stage}.json` : "ollie.json";
1629
+ outputResult(
1630
+ {
1631
+ data: {
1632
+ file: fileName,
1633
+ storeId,
1634
+ ...versionId ? { versionId } : {}
1635
+ }
1636
+ },
1637
+ format,
1638
+ parsed2.global.fields
1639
+ );
1640
+ } catch (err) {
1641
+ outputResult(
1642
+ {
1643
+ error: { message: err instanceof Error ? err.message : String(err) }
1644
+ },
1645
+ format
1646
+ );
1647
+ process.exit(1);
1648
+ }
1649
+ }
1650
+
1651
+ // src/commands/schema-cmd.ts
1652
+ async function schemaCommand(parsed2) {
1653
+ const format = detectOutputFormat(parsed2.global.output);
1654
+ const resourceName = parsed2.subcommand || parsed2.positional[0];
1655
+ if (!resourceName) {
1656
+ const names = getSchemaNames();
1657
+ if (format === "json") {
1658
+ process.stdout.write(`${JSON.stringify({ schemas: names })}
1659
+ `);
1660
+ } else {
1661
+ console.log("\x1B[1mAvailable schemas:\x1B[0m");
1662
+ for (const name of names) {
1663
+ const indent = name.includes(".") ? " " : " ";
1664
+ console.log(`${indent}\x1B[32m${name}\x1B[0m`);
1665
+ }
1666
+ console.log("\nUsage: ollieshop schema <resource[.action]>");
1667
+ }
1668
+ return;
1669
+ }
1670
+ const schema = getJsonSchema(resourceName);
1671
+ if (!schema) {
1672
+ console.error(
1673
+ `Unknown schema: ${resourceName}. Run \`ollieshop schema\` to see available schemas.`
1674
+ );
1675
+ process.exit(1);
1676
+ }
1677
+ process.stdout.write(`${JSON.stringify(schema, null, 2)}
1678
+ `);
1679
+ }
1680
+
1681
+ // src/core/store.ts
1682
+ async function createStore(client, input) {
1683
+ const parsed2 = storeCreateSchema.safeParse(input);
1684
+ if (!parsed2.success) {
1685
+ return {
1686
+ error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
1687
+ };
1688
+ }
1689
+ const organizationId = await getOrganizationId(client);
1690
+ const { data, error } = await client.from("stores").insert({
1691
+ name: parsed2.data.name,
1692
+ platform: parsed2.data.platform,
1693
+ platform_store_id: parsed2.data.platformStoreId,
1694
+ organization_id: organizationId,
1695
+ logo: parsed2.data.logo ?? null,
1696
+ settings: parsed2.data.settings ?? null
1697
+ }).select("id").single();
1698
+ if (error) {
1699
+ return { error: { message: error.message } };
1700
+ }
1701
+ return { data: { id: data.id } };
1702
+ }
1703
+ async function listStores(client) {
1704
+ const organizationId = await getOrganizationId(client);
1705
+ const { data, error } = await client.from("stores").select(
1706
+ "id, name, platform, platform_store_id, organization_id, logo, settings, created_at"
1707
+ ).eq("organization_id", organizationId).order("created_at", { ascending: false });
1708
+ if (error) {
1709
+ return { error: { message: error.message } };
1710
+ }
1711
+ return { data };
1712
+ }
1713
+
1714
+ // src/commands/store-cmd.ts
1715
+ async function storeCommand(parsed2) {
1716
+ const sub = parsed2.subcommand;
1717
+ if (sub === "create") return storeCreateCommand(parsed2);
1718
+ if (sub === "list" || sub === "ls") return storeListCommand(parsed2);
1719
+ console.error(
1720
+ `Unknown store subcommand: ${sub}. Use: store create | store list`
1721
+ );
1722
+ process.exit(1);
1723
+ }
1724
+ async function storeCreateCommand(parsed2) {
1725
+ const format = detectOutputFormat(parsed2.global.output);
1726
+ try {
1727
+ let input;
1728
+ if (parsed2.global.data) {
1729
+ const raw = JSON.parse(parsed2.global.data);
1730
+ input = {
1731
+ name: validateRequired(raw.name, "name"),
1732
+ platform: validateEnum(raw.platform, PLATFORM_VENDORS, "platform"),
1733
+ platformStoreId: validateRequired(
1734
+ raw.platformStoreId,
1735
+ "platformStoreId"
1736
+ ),
1737
+ logo: raw.logo,
1738
+ settings: raw.settings
1739
+ };
1740
+ } else {
1741
+ input = {
1742
+ name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
1743
+ platform: validateEnum(
1744
+ validateRequired(getFlag(parsed2.flags, "platform", "p"), "platform"),
1745
+ PLATFORM_VENDORS,
1746
+ "platform"
1747
+ ),
1748
+ platformStoreId: validateRequired(
1749
+ getFlag(parsed2.flags, "platform-store-id"),
1750
+ "platform-store-id"
1751
+ ),
1752
+ logo: getFlag(parsed2.flags, "logo"),
1753
+ settings: getFlag(parsed2.flags, "settings")
1754
+ };
1755
+ }
1756
+ if (parsed2.global.dryRun) {
1757
+ outputDryRun("store.create", input, format);
1758
+ return;
1759
+ }
1760
+ const client = await getAuthenticatedClient();
1761
+ const result = await createStore(client, input);
1762
+ outputResult(result, format, parsed2.global.fields);
1763
+ if (result.error) process.exit(1);
1764
+ } catch (err) {
1765
+ outputResult(
1766
+ {
1767
+ error: { message: err instanceof Error ? err.message : String(err) }
1768
+ },
1769
+ format
1770
+ );
1771
+ process.exit(1);
1772
+ }
1773
+ }
1774
+ async function storeListCommand(parsed2) {
1775
+ const format = detectOutputFormat(parsed2.global.output);
1776
+ try {
1777
+ const client = await getAuthenticatedClient();
1778
+ const result = await listStores(client);
1779
+ outputResult(result, format, parsed2.global.fields);
1780
+ if (result.error) process.exit(1);
1781
+ } catch (err) {
1782
+ outputResult(
1783
+ {
1784
+ error: { message: err instanceof Error ? err.message : String(err) }
1785
+ },
1786
+ format
1787
+ );
1788
+ process.exit(1);
1789
+ }
1790
+ }
1791
+
1792
+ // src/core/version.ts
1793
+ async function createVersion(client, input) {
1794
+ const parsed2 = versionCreateSchema.safeParse(input);
1795
+ if (!parsed2.success) {
1796
+ return {
1797
+ error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
1798
+ };
1799
+ }
1800
+ const { data, error } = await client.from("versions").insert({
1801
+ name: parsed2.data.name,
1802
+ active: parsed2.data.active,
1803
+ default: parsed2.data.default,
1804
+ store_id: parsed2.data.storeId,
1805
+ template: parsed2.data.template
1806
+ }).select("id").single();
1807
+ if (error) {
1808
+ return { error: { message: error.message } };
1809
+ }
1810
+ return { data: { id: data.id } };
1811
+ }
1812
+ async function listVersions(client, storeId) {
1813
+ 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 });
1814
+ if (error) {
1815
+ return { error: { message: error.message } };
1816
+ }
1817
+ return { data };
1818
+ }
1819
+
1820
+ // src/commands/version-cmd.ts
1821
+ async function versionCommand(parsed2) {
1822
+ const sub = parsed2.subcommand;
1823
+ if (sub === "create") return versionCreateCommand(parsed2);
1824
+ if (sub === "list" || sub === "ls") return versionListCommand(parsed2);
1825
+ console.error(
1826
+ `Unknown version subcommand: ${sub}. Use: version create | version list`
1827
+ );
1828
+ process.exit(1);
1829
+ }
1830
+ async function versionCreateCommand(parsed2) {
1831
+ const format = detectOutputFormat(parsed2.global.output);
1832
+ try {
1833
+ let input;
1834
+ if (parsed2.global.data) {
1835
+ const raw = JSON.parse(parsed2.global.data);
1836
+ input = {
1837
+ storeId: validateUuid(raw.storeId, "storeId"),
1838
+ name: validateRequired(raw.name, "name"),
1839
+ active: raw.active ?? false,
1840
+ default: raw.default ?? false,
1841
+ template: raw.template ?? null
1842
+ };
1843
+ } else {
1844
+ input = {
1845
+ storeId: validateUuid(
1846
+ validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
1847
+ "store-id"
1848
+ ),
1849
+ name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
1850
+ active: getBoolFlag(parsed2.flags, "active"),
1851
+ default: getBoolFlag(parsed2.flags, "default"),
1852
+ template: getFlag(parsed2.flags, "template") ?? null
1853
+ };
1854
+ }
1855
+ if (parsed2.global.dryRun) {
1856
+ outputDryRun("version.create", input, format);
1857
+ return;
1858
+ }
1859
+ const client = await getAuthenticatedClient();
1860
+ const result = await createVersion(client, input);
1861
+ outputResult(result, format, parsed2.global.fields);
1862
+ if (result.error) process.exit(1);
1863
+ } catch (err) {
1864
+ outputResult(
1865
+ {
1866
+ error: { message: err instanceof Error ? err.message : String(err) }
1867
+ },
1868
+ format
1869
+ );
1870
+ process.exit(1);
1871
+ }
1872
+ }
1873
+ async function versionListCommand(parsed2) {
1874
+ const format = detectOutputFormat(parsed2.global.output);
1875
+ try {
1876
+ const storeId = validateUuid(
1877
+ validateRequired(getFlag(parsed2.flags, "store-id"), "store-id"),
1878
+ "store-id"
1879
+ );
1880
+ const client = await getAuthenticatedClient();
1881
+ const result = await listVersions(client, storeId);
1882
+ outputResult(result, format, parsed2.global.fields);
1883
+ if (result.error) process.exit(1);
1884
+ } catch (err) {
1885
+ outputResult(
1886
+ {
1887
+ error: { message: err instanceof Error ? err.message : String(err) }
1888
+ },
1889
+ format
1890
+ );
1891
+ process.exit(1);
1892
+ }
1893
+ }
1894
+
1895
+ // src/commands/whoami.ts
1896
+ async function whoamiCommand(parsed2) {
1897
+ const format = detectOutputFormat(parsed2.global.output);
1898
+ const user = await getCurrentUser();
1899
+ if (!user) {
1900
+ outputResult(
1901
+ {
1902
+ error: { message: "Not logged in. Run `ollieshop login`." }
1903
+ },
1904
+ format
1905
+ );
1906
+ process.exit(1);
1907
+ }
1908
+ outputResult(
1909
+ {
1910
+ data: {
1911
+ email: user.email
1912
+ }
1913
+ },
1914
+ format,
1915
+ parsed2.global.fields
1916
+ );
1917
+ }
1918
+
1099
1919
  // src/index.tsx
1100
1920
  import { jsx as jsx5 } from "react/jsx-runtime";
1101
- var args = process.argv.slice(2);
1102
- var command = args[0] || "help";
1103
- var commandArgs = args.slice(1);
1104
- render(/* @__PURE__ */ jsx5(App, { command, args: commandArgs }));
1921
+ var AGENT_COMMANDS = {
1922
+ whoami: whoamiCommand,
1923
+ store: storeCommand,
1924
+ version: versionCommand,
1925
+ component: componentCommand,
1926
+ schema: schemaCommand,
1927
+ init: initCommand
1928
+ };
1929
+ var parsed = parseArgs(process.argv);
1930
+ if (parsed.command in AGENT_COMMANDS) {
1931
+ AGENT_COMMANDS[parsed.command](parsed).catch((err) => {
1932
+ const msg = err instanceof Error ? err.message : String(err);
1933
+ process.stderr.write(`${JSON.stringify({ error: { message: msg } })}
1934
+ `);
1935
+ process.exit(1);
1936
+ });
1937
+ } else {
1938
+ const args = process.argv.slice(2);
1939
+ const command = args[0] || "help";
1940
+ const commandArgs = args.slice(1);
1941
+ render(/* @__PURE__ */ jsx5(App, { command, args: commandArgs }));
1942
+ }