@ollie-shop/cli 1.1.0 → 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 +3 -0
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +16 -0
- package/CONTEXT.md +19 -1
- package/README.md +39 -7
- package/dist/index.js +579 -124
- package/package.json +1 -1
- package/src/commands/business-rule-cmd.ts +161 -0
- package/src/commands/deploy-cmd.ts +146 -0
- package/src/commands/help.tsx +18 -0
- package/src/commands/status-cmd.ts +68 -0
- package/src/core/business-rule.ts +128 -0
- package/src/core/deploy.ts +171 -0
- package/src/index.tsx +6 -0
- package/src/utils/supabase.ts +19 -11
- package/tsup.config.ts +9 -2
package/dist/index.js
CHANGED
|
@@ -50,6 +50,14 @@ function HelpCommand() {
|
|
|
50
50
|
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "component create|list" }) }),
|
|
51
51
|
/* @__PURE__ */ jsx(Text, { children: "Create or list components" })
|
|
52
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" })
|
|
56
|
+
] }),
|
|
57
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
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
|
+
] }),
|
|
53
61
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
54
62
|
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "schema" }) }),
|
|
55
63
|
/* @__PURE__ */ jsx(Text, { children: "Introspect resource schemas (JSON Schema)" })
|
|
@@ -112,7 +120,9 @@ function HelpCommand() {
|
|
|
112
120
|
"-o json"
|
|
113
121
|
] }),
|
|
114
122
|
/* @__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" })
|
|
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" })
|
|
116
126
|
] })
|
|
117
127
|
] });
|
|
118
128
|
}
|
|
@@ -1174,129 +1184,60 @@ function UnknownCommand({ command }) {
|
|
|
1174
1184
|
] });
|
|
1175
1185
|
}
|
|
1176
1186
|
|
|
1177
|
-
// src/core/
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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
|
-
}
|
|
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
|
+
);
|
|
1246
1200
|
}
|
|
1247
|
-
|
|
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}` });
|
|
1201
|
+
if (filters.code_updated !== void 0) {
|
|
1202
|
+
query = query.eq("code_updated", filters.code_updated);
|
|
1258
1203
|
}
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
name: `${resource}.${actionName}`
|
|
1263
|
-
});
|
|
1204
|
+
const { data, error } = await query;
|
|
1205
|
+
if (error) {
|
|
1206
|
+
return { error: { message: error.message } };
|
|
1264
1207
|
}
|
|
1265
|
-
return
|
|
1208
|
+
return { data };
|
|
1266
1209
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
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();
|
|
1210
|
+
async function getBusinessRule(client, id) {
|
|
1211
|
+
const { data, error } = await client.from("business_rules").select(SELECT_FIELDS).eq("id", id).single();
|
|
1283
1212
|
if (error) {
|
|
1284
1213
|
return { error: { message: error.message } };
|
|
1285
1214
|
}
|
|
1286
|
-
return { data
|
|
1215
|
+
return { data };
|
|
1287
1216
|
}
|
|
1288
|
-
async function
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
if (versionId) {
|
|
1293
|
-
query = query.eq("versions.id", versionId);
|
|
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 } };
|
|
1294
1221
|
}
|
|
1295
|
-
const
|
|
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();
|
|
1296
1237
|
if (error) {
|
|
1297
1238
|
return { error: { message: error.message } };
|
|
1298
1239
|
}
|
|
1299
|
-
return { data:
|
|
1240
|
+
return { data: { id: data.id } };
|
|
1300
1241
|
}
|
|
1301
1242
|
|
|
1302
1243
|
// src/utils/output.ts
|
|
@@ -1451,22 +1392,13 @@ function getBoolFlag(flags, ...names) {
|
|
|
1451
1392
|
|
|
1452
1393
|
// src/utils/supabase.ts
|
|
1453
1394
|
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
1395
|
async function getAuthenticatedClient() {
|
|
1464
1396
|
const credentials = await getCredentials();
|
|
1465
1397
|
if (!credentials) {
|
|
1466
1398
|
throw new Error("Not authenticated. Run `ollieshop login` first.");
|
|
1467
1399
|
}
|
|
1468
|
-
const supabaseUrl =
|
|
1469
|
-
const supabaseAnonKey =
|
|
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";
|
|
1470
1402
|
const client = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1471
1403
|
auth: {
|
|
1472
1404
|
autoRefreshToken: false,
|
|
@@ -1479,6 +1411,16 @@ async function getAuthenticatedClient() {
|
|
|
1479
1411
|
});
|
|
1480
1412
|
return client;
|
|
1481
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
|
+
}
|
|
1482
1424
|
async function getOrganizationId(client) {
|
|
1483
1425
|
const { data: org, error } = await client.from("organizations").select("id").order("created_at", { ascending: true }).limit(1).maybeSingle();
|
|
1484
1426
|
if (error) {
|
|
@@ -1526,6 +1468,250 @@ function validateRequired(value, name) {
|
|
|
1526
1468
|
return rejectControlChars(value.trim(), name);
|
|
1527
1469
|
}
|
|
1528
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
|
+
|
|
1529
1715
|
// src/commands/component-cmd.ts
|
|
1530
1716
|
async function componentCommand(parsed2) {
|
|
1531
1717
|
const sub = parsed2.subcommand;
|
|
@@ -1607,6 +1793,218 @@ async function componentListCommand(parsed2) {
|
|
|
1607
1793
|
}
|
|
1608
1794
|
}
|
|
1609
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
|
+
|
|
1610
2008
|
// src/commands/init-cmd.ts
|
|
1611
2009
|
async function initCommand(parsed2) {
|
|
1612
2010
|
const format = detectOutputFormat(parsed2.global.output);
|
|
@@ -1678,6 +2076,60 @@ async function schemaCommand(parsed2) {
|
|
|
1678
2076
|
`);
|
|
1679
2077
|
}
|
|
1680
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
|
+
|
|
1681
2133
|
// src/core/store.ts
|
|
1682
2134
|
async function createStore(client, input) {
|
|
1683
2135
|
const parsed2 = storeCreateSchema.safeParse(input);
|
|
@@ -1923,8 +2375,11 @@ var AGENT_COMMANDS = {
|
|
|
1923
2375
|
store: storeCommand,
|
|
1924
2376
|
version: versionCommand,
|
|
1925
2377
|
component: componentCommand,
|
|
2378
|
+
"business-rule": businessRuleCommand,
|
|
1926
2379
|
schema: schemaCommand,
|
|
1927
|
-
init: initCommand
|
|
2380
|
+
init: initCommand,
|
|
2381
|
+
deploy: deployCommand,
|
|
2382
|
+
status: statusCommand
|
|
1928
2383
|
};
|
|
1929
2384
|
var parsed = parseArgs(process.argv);
|
|
1930
2385
|
if (parsed.command in AGENT_COMMANDS) {
|