@drip-apex/apex-cli 0.6.2 → 0.6.3
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/AGENTS.md +28 -0
- package/README.md +32 -0
- package/dist/cli.js +302 -91
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +28 -0
package/AGENTS.md
CHANGED
|
@@ -70,3 +70,31 @@ the first writes: `apex experiments create`, `apex goals create`,
|
|
|
70
70
|
- Edit experiment files, variation code, metrics, and targeting only through supported paths.
|
|
71
71
|
- Do not bypass the variation artifact persistence path with direct API writes.
|
|
72
72
|
- Keep generated evidence outside `qa.json`; attach it through QA commands.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Project code
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
apex project-code get --shop example.com --json
|
|
79
|
+
apex project-code diff --file project-code.json --shop example.com --json
|
|
80
|
+
apex project-code set --file project-code.json --shop example.com --backup previous-code.json --json
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The input is a partial projectCode object, for example `{ "css": "body { color: red; }" }`.
|
|
84
|
+
Supported fields are `enabled` (boolean) and `css`, `helperJs`, and `js` (strings or null).
|
|
85
|
+
Each code field permits at most 20,000 characters. Omitted fields remain unchanged.
|
|
86
|
+
Null or blank code clears that field. Unknown fields and empty objects are rejected.
|
|
87
|
+
|
|
88
|
+
GET returns complete code, shop identity, and an opaque version. DIFF returns effective
|
|
89
|
+
before/after values and changed fields without writing. SET creates a new backup file
|
|
90
|
+
with complete previous code, identity, capture time, and the exact version before PATCH.
|
|
91
|
+
It never replaces an existing backup. Backup failures prevent the write. An effective
|
|
92
|
+
no-op returns `unchanged`, without a PATCH or backup file.
|
|
93
|
+
|
|
94
|
+
The backed-up version becomes expectedVersion unchanged. A conflict returns exit 5,
|
|
95
|
+
keeps the backup, and does not retry. Read the latest state and preview it before a new
|
|
96
|
+
attempt with a new backup path. Authentication uses exit 2; invalid input uses exit 3.
|
|
97
|
+
A successful write returns `saved`, its diff, backup path, current version, writtenVersion,
|
|
98
|
+
and precompile result. A failed precompile is a saved-write warning. All warnings remain
|
|
99
|
+
in JSON and are also written to stderr. User keys resolve domains or pin UUID context;
|
|
100
|
+
shop keys must match the requested identity before any backup or write.
|
package/README.md
CHANGED
|
@@ -109,6 +109,10 @@ including every subcommand, argument, option, and alias.
|
|
|
109
109
|
### Experiments and delivery
|
|
110
110
|
|
|
111
111
|
- `apex consent` — manage consent gating.
|
|
112
|
+
- `apex project-code` — inspect and safely change shop project code.
|
|
113
|
+
- `apex project-code get` — read complete code and its opaque version.
|
|
114
|
+
- `apex project-code diff` — preview a partial JSON change without writing.
|
|
115
|
+
- `apex project-code set` — create a backup before a version-checked write.
|
|
112
116
|
- `apex triggers` — manage data-layer triggers and temporary setup capture.
|
|
113
117
|
- `apex triggers get --shop <domain-or-id>` — read the identified shop's runtime triggers.
|
|
114
118
|
- `apex triggers set` — replace the shop's runtime triggers from a JSON file.
|
|
@@ -228,3 +232,31 @@ Read the CLI reference on the docs site:
|
|
|
228
232
|
- https://docs.drip-apex.com/cli/commands-output
|
|
229
233
|
|
|
230
234
|
Maintainers: see the [CLI release runbook](../../docs/cli-releases.md).
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
## Project code
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
apex project-code get --shop example.com --json
|
|
241
|
+
apex project-code diff --file project-code.json --shop example.com --json
|
|
242
|
+
apex project-code set --file project-code.json --shop example.com --backup previous-code.json --json
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The input is a partial projectCode object, for example `{ "css": "body { color: red; }" }`.
|
|
246
|
+
Supported fields are `enabled` (boolean) and `css`, `helperJs`, and `js` (strings or null).
|
|
247
|
+
Each code field permits at most 20,000 characters. Omitted fields remain unchanged.
|
|
248
|
+
Null or blank code clears that field. Unknown fields and empty objects are rejected.
|
|
249
|
+
|
|
250
|
+
GET returns complete code, shop identity, and an opaque version. DIFF returns effective
|
|
251
|
+
before/after values and changed fields without writing. SET creates a new backup file
|
|
252
|
+
with complete previous code, identity, capture time, and the exact version before PATCH.
|
|
253
|
+
It never replaces an existing backup. Backup failures prevent the write. An effective
|
|
254
|
+
no-op returns `unchanged`, without a PATCH or backup file.
|
|
255
|
+
|
|
256
|
+
The backed-up version becomes expectedVersion unchanged. A conflict returns exit 5,
|
|
257
|
+
keeps the backup, and does not retry. Read the latest state and preview it before a new
|
|
258
|
+
attempt with a new backup path. Authentication uses exit 2; invalid input uses exit 3.
|
|
259
|
+
A successful write returns `saved`, its diff, backup path, current version, writtenVersion,
|
|
260
|
+
and precompile result. A failed precompile is a saved-write warning. All warnings remain
|
|
261
|
+
in JSON and are also written to stderr. User keys resolve domains or pin UUID context;
|
|
262
|
+
shop keys must match the requested identity before any backup or write.
|
package/dist/cli.js
CHANGED
|
@@ -6988,14 +6988,14 @@ var init_open = __esm({
|
|
|
6988
6988
|
}
|
|
6989
6989
|
const subprocess = import_node_child_process5.default.spawn(command, cliArguments, childProcessOptions);
|
|
6990
6990
|
if (options.wait) {
|
|
6991
|
-
return new Promise((
|
|
6991
|
+
return new Promise((resolve2, reject) => {
|
|
6992
6992
|
subprocess.once("error", reject);
|
|
6993
6993
|
subprocess.once("close", (exitCode) => {
|
|
6994
6994
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
6995
6995
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
6996
6996
|
return;
|
|
6997
6997
|
}
|
|
6998
|
-
|
|
6998
|
+
resolve2(subprocess);
|
|
6999
6999
|
});
|
|
7000
7000
|
});
|
|
7001
7001
|
}
|
|
@@ -7112,7 +7112,7 @@ var import_node_os3 = __toESM(require("node:os"));
|
|
|
7112
7112
|
// package.json
|
|
7113
7113
|
var package_default = {
|
|
7114
7114
|
name: "@drip-apex/apex-cli",
|
|
7115
|
-
version: "0.6.
|
|
7115
|
+
version: "0.6.3",
|
|
7116
7116
|
license: "MIT",
|
|
7117
7117
|
description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
|
|
7118
7118
|
private: false,
|
|
@@ -8369,9 +8369,9 @@ function resolveSelection(tree, input) {
|
|
|
8369
8369
|
function ask(question) {
|
|
8370
8370
|
const rl = import_node_readline.default.createInterface({ input: process.stdin, output: process.stderr });
|
|
8371
8371
|
return new Promise(
|
|
8372
|
-
(
|
|
8372
|
+
(resolve2) => rl.question(question, (answer) => {
|
|
8373
8373
|
rl.close();
|
|
8374
|
-
|
|
8374
|
+
resolve2(answer.trim());
|
|
8375
8375
|
})
|
|
8376
8376
|
);
|
|
8377
8377
|
}
|
|
@@ -8449,7 +8449,7 @@ function prompt(question, hide = false) {
|
|
|
8449
8449
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
8450
8450
|
throw new Error("Interactive input is unavailable in non-TTY mode.");
|
|
8451
8451
|
}
|
|
8452
|
-
return new Promise((
|
|
8452
|
+
return new Promise((resolve2) => {
|
|
8453
8453
|
const rl = import_node_readline2.default.createInterface({
|
|
8454
8454
|
input: process.stdin,
|
|
8455
8455
|
output: process.stderr
|
|
@@ -8469,13 +8469,13 @@ function prompt(question, hide = false) {
|
|
|
8469
8469
|
stdout.write = originalWrite;
|
|
8470
8470
|
console.error();
|
|
8471
8471
|
rl.close();
|
|
8472
|
-
|
|
8472
|
+
resolve2(answer.trim());
|
|
8473
8473
|
});
|
|
8474
8474
|
masking = true;
|
|
8475
8475
|
} else {
|
|
8476
8476
|
rl.question(question, (answer) => {
|
|
8477
8477
|
rl.close();
|
|
8478
|
-
|
|
8478
|
+
resolve2(answer.trim());
|
|
8479
8479
|
});
|
|
8480
8480
|
}
|
|
8481
8481
|
});
|
|
@@ -8501,7 +8501,7 @@ function parseFormBody(body5) {
|
|
|
8501
8501
|
async function waitForBrowserLogin(baseUrl, userMode = false) {
|
|
8502
8502
|
const state = import_node_crypto2.default.randomBytes(24).toString("base64url");
|
|
8503
8503
|
const clientName = import_node_os5.default.hostname() || "Local machine";
|
|
8504
|
-
return new Promise((
|
|
8504
|
+
return new Promise((resolve2, reject) => {
|
|
8505
8505
|
let settled = false;
|
|
8506
8506
|
let timeout = null;
|
|
8507
8507
|
const finish = (err, apiKey) => {
|
|
@@ -8513,7 +8513,7 @@ async function waitForBrowserLogin(baseUrl, userMode = false) {
|
|
|
8513
8513
|
} catch {
|
|
8514
8514
|
}
|
|
8515
8515
|
if (err) reject(err);
|
|
8516
|
-
else
|
|
8516
|
+
else resolve2(apiKey ?? "");
|
|
8517
8517
|
};
|
|
8518
8518
|
const server = import_node_http.default.createServer((req, res) => {
|
|
8519
8519
|
if (req.method === "OPTIONS") {
|
|
@@ -9414,8 +9414,8 @@ async function confirmApply(message) {
|
|
|
9414
9414
|
}
|
|
9415
9415
|
const readline3 = await import("node:readline");
|
|
9416
9416
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
9417
|
-
const answer = await new Promise((
|
|
9418
|
-
rl.question(source_default.yellow(`${message} [y/N] `),
|
|
9417
|
+
const answer = await new Promise((resolve2) => {
|
|
9418
|
+
rl.question(source_default.yellow(`${message} [y/N] `), resolve2);
|
|
9419
9419
|
});
|
|
9420
9420
|
rl.close();
|
|
9421
9421
|
return answer.toLowerCase() === "y";
|
|
@@ -9828,8 +9828,8 @@ var del = new Command("delete").alias("rm").description("Delete an experiment").
|
|
|
9828
9828
|
input: process.stdin,
|
|
9829
9829
|
output: process.stderr
|
|
9830
9830
|
});
|
|
9831
|
-
const answer = await new Promise((
|
|
9832
|
-
rl.question(source_default.yellow(`Delete experiment "${exp.name}" (${exp.id})? [y/N] `),
|
|
9831
|
+
const answer = await new Promise((resolve2) => {
|
|
9832
|
+
rl.question(source_default.yellow(`Delete experiment "${exp.name}" (${exp.id})? [y/N] `), resolve2);
|
|
9833
9833
|
});
|
|
9834
9834
|
rl.close();
|
|
9835
9835
|
if (answer.toLowerCase() !== "y") {
|
|
@@ -10357,7 +10357,7 @@ var screenshots = new Command("screenshots").description(
|
|
|
10357
10357
|
if (screenshotNow() >= deadline)
|
|
10358
10358
|
fail(this, `Screenshot job ${job.job_id} timed out after ${timeoutSeconds}s.`);
|
|
10359
10359
|
await new Promise(
|
|
10360
|
-
(
|
|
10360
|
+
(resolve2) => setTimeout(resolve2, Math.min(delayMs, Math.max(0, deadline - screenshotNow())))
|
|
10361
10361
|
);
|
|
10362
10362
|
if (screenshotNow() >= deadline)
|
|
10363
10363
|
fail(this, `Screenshot job ${job.job_id} timed out after ${timeoutSeconds}s.`);
|
|
@@ -10938,11 +10938,11 @@ function cleanupResetJs(experiment) {
|
|
|
10938
10938
|
}
|
|
10939
10939
|
function runAbHarness(args, cwd) {
|
|
10940
10940
|
const bin = process.env.AB_HARNESS_BIN || "ab-harness";
|
|
10941
|
-
return new Promise((
|
|
10941
|
+
return new Promise((resolve2, reject) => {
|
|
10942
10942
|
const child = (0, import_node_child_process6.spawn)(bin, args, { cwd, stdio: "inherit" });
|
|
10943
10943
|
child.on("error", reject);
|
|
10944
10944
|
child.on("exit", (code) => {
|
|
10945
|
-
if (code === 0)
|
|
10945
|
+
if (code === 0) resolve2();
|
|
10946
10946
|
else reject(new Error(`${bin} ${args.join(" ")} exited with ${code}`));
|
|
10947
10947
|
});
|
|
10948
10948
|
});
|
|
@@ -12416,7 +12416,7 @@ var suggest = new Command("suggest").description("Suggest goals and funnels from
|
|
|
12416
12416
|
const readline3 = await import("node:readline");
|
|
12417
12417
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
12418
12418
|
const answer = await new Promise(
|
|
12419
|
-
(
|
|
12419
|
+
(resolve2) => rl.question(source_default.yellow("Create this goal? [y/N] "), resolve2)
|
|
12420
12420
|
);
|
|
12421
12421
|
rl.close();
|
|
12422
12422
|
if (answer.toLowerCase() !== "y") continue;
|
|
@@ -12778,9 +12778,219 @@ var off = new Command("off").description("Disable consent gating").action(async
|
|
|
12778
12778
|
});
|
|
12779
12779
|
var consentCommand = new Command("consent").description("Manage consent gating").addCommand(get2).addCommand(set).addCommand(off);
|
|
12780
12780
|
|
|
12781
|
+
// src/commands/project-code.ts
|
|
12782
|
+
var import_node_fs15 = require("node:fs");
|
|
12783
|
+
var import_node_path13 = require("node:path");
|
|
12784
|
+
|
|
12785
|
+
// src/commands/runtime-settings-shop.ts
|
|
12786
|
+
var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
12787
|
+
async function identifyRuntimeSettingsShop(command, requested) {
|
|
12788
|
+
let credentials = requireCredentials();
|
|
12789
|
+
if (credentials.credentialKind === "user") {
|
|
12790
|
+
let shopId = requested;
|
|
12791
|
+
if (!uuidPattern.test(requested)) {
|
|
12792
|
+
const context = await getAccountContext({
|
|
12793
|
+
...credentials,
|
|
12794
|
+
resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
|
|
12795
|
+
});
|
|
12796
|
+
if (!context.ok || !context.data)
|
|
12797
|
+
fail(command, context.error ?? "Shop discovery failed", classifyStatus(context.status));
|
|
12798
|
+
const match2 = context.data.data.orgs.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requested.toLowerCase());
|
|
12799
|
+
if (!match2)
|
|
12800
|
+
fail(command, "--shop domain is not available to this user", EXIT_CODES.validation);
|
|
12801
|
+
shopId = match2.id;
|
|
12802
|
+
}
|
|
12803
|
+
credentials = { ...credentials, resolvedContext: { ...credentials.resolvedContext, shopId } };
|
|
12804
|
+
}
|
|
12805
|
+
const response = await getInstallDomains(credentials);
|
|
12806
|
+
if (!response.ok || !response.data)
|
|
12807
|
+
fail(command, response.error ?? "Shop identity unavailable", classifyStatus(response.status));
|
|
12808
|
+
const { shop_id, domain } = response.data;
|
|
12809
|
+
if (credentials.credentialKind === "user" && shop_id.toLowerCase() !== credentials.resolvedContext?.shopId?.toLowerCase()) {
|
|
12810
|
+
fail(
|
|
12811
|
+
command,
|
|
12812
|
+
"Shop identity does not match the requested user-key context",
|
|
12813
|
+
EXIT_CODES.validation
|
|
12814
|
+
);
|
|
12815
|
+
}
|
|
12816
|
+
if (requested.toLowerCase() !== shop_id.toLowerCase() && requested.toLowerCase() !== domain.toLowerCase()) {
|
|
12817
|
+
fail(command, "--shop does not match the authenticated shop", EXIT_CODES.validation);
|
|
12818
|
+
}
|
|
12819
|
+
setEnvelopeContext({ shop_id, credential: credentials.credentialKind ?? "shop" });
|
|
12820
|
+
return { shop_id, domain, credentials };
|
|
12821
|
+
}
|
|
12822
|
+
|
|
12823
|
+
// src/commands/project-code.ts
|
|
12824
|
+
var fields = ["enabled", "css", "helperJs", "js"];
|
|
12825
|
+
function readPatch(command, file) {
|
|
12826
|
+
let input;
|
|
12827
|
+
try {
|
|
12828
|
+
input = JSON.parse((0, import_node_fs15.readFileSync)(file, "utf8"));
|
|
12829
|
+
} catch (error) {
|
|
12830
|
+
fail(command, `Cannot read project code JSON: ${String(error)}`, EXIT_CODES.validation);
|
|
12831
|
+
}
|
|
12832
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).length === 0) {
|
|
12833
|
+
fail(command, "Project code must be a nonempty partial object", EXIT_CODES.validation);
|
|
12834
|
+
}
|
|
12835
|
+
for (const [key, value] of Object.entries(input)) {
|
|
12836
|
+
if (!fields.includes(key))
|
|
12837
|
+
fail(command, `Unknown project code field: ${key}`, EXIT_CODES.validation);
|
|
12838
|
+
if (key === "enabled" ? typeof value !== "boolean" : value !== null && (typeof value !== "string" || value.length > 2e4)) {
|
|
12839
|
+
fail(
|
|
12840
|
+
command,
|
|
12841
|
+
`${key} must be ${key === "enabled" ? "a boolean" : "null or a string of at most 20,000 characters"}`,
|
|
12842
|
+
EXIT_CODES.validation
|
|
12843
|
+
);
|
|
12844
|
+
}
|
|
12845
|
+
}
|
|
12846
|
+
return input;
|
|
12847
|
+
}
|
|
12848
|
+
async function readCurrent(command, shop) {
|
|
12849
|
+
const response = await getRuntimeSettings(shop.credentials);
|
|
12850
|
+
if (!response.ok || !response.data)
|
|
12851
|
+
fail(
|
|
12852
|
+
command,
|
|
12853
|
+
response.error ?? "Runtime settings read failed",
|
|
12854
|
+
classifyStatus(response.status)
|
|
12855
|
+
);
|
|
12856
|
+
const data = response.data;
|
|
12857
|
+
if (data.shop_id !== shop.shop_id)
|
|
12858
|
+
fail(command, "Runtime settings returned a different shop", EXIT_CODES.validation);
|
|
12859
|
+
const projectCode = data.runtime_settings?.projectCode;
|
|
12860
|
+
if (!projectCode || typeof projectCode.enabled !== "boolean" || [projectCode.css, projectCode.helperJs, projectCode.js].some(
|
|
12861
|
+
(code) => typeof code !== "string"
|
|
12862
|
+
) || !(data.version === null || typeof data.version === "string" && data.version.length > 0)) {
|
|
12863
|
+
fail(
|
|
12864
|
+
command,
|
|
12865
|
+
"Runtime settings must include complete project code and an opaque version",
|
|
12866
|
+
EXIT_CODES.general
|
|
12867
|
+
);
|
|
12868
|
+
}
|
|
12869
|
+
return { projectCode, version: data.version };
|
|
12870
|
+
}
|
|
12871
|
+
function effectiveDiff(before, patch) {
|
|
12872
|
+
const after = { ...before };
|
|
12873
|
+
if (patch.enabled !== void 0) after.enabled = patch.enabled;
|
|
12874
|
+
for (const key of ["css", "helperJs", "js"]) {
|
|
12875
|
+
if (patch[key] !== void 0) after[key] = patch[key]?.trim() || "";
|
|
12876
|
+
}
|
|
12877
|
+
const changedFields = fields.filter((field) => before[field] !== after[field]);
|
|
12878
|
+
const changes = Object.fromEntries(
|
|
12879
|
+
changedFields.map((field) => [field, { before: before[field], after: after[field] }])
|
|
12880
|
+
);
|
|
12881
|
+
return { changedFields, changes, before, after };
|
|
12882
|
+
}
|
|
12883
|
+
var projectCodeCommand = new Command("project-code").description(
|
|
12884
|
+
"Inspect, preview, and back up shop project code"
|
|
12885
|
+
);
|
|
12886
|
+
projectCodeCommand.addCommand(
|
|
12887
|
+
new Command("get").description("Read complete project code and its opaque version").requiredOption("--shop <domain-or-id>", "Shop domain or UUID").action(async (options, command) => {
|
|
12888
|
+
const { credentials: _credentials, ...identity } = await identifyRuntimeSettingsShop(
|
|
12889
|
+
command,
|
|
12890
|
+
options.shop
|
|
12891
|
+
);
|
|
12892
|
+
const current = await readCurrent(command, { ...identity, credentials: _credentials });
|
|
12893
|
+
output(
|
|
12894
|
+
command,
|
|
12895
|
+
{ ...identity, ...current },
|
|
12896
|
+
() => process.stdout.write(`${JSON.stringify({ ...identity, ...current }, null, 2)}
|
|
12897
|
+
`)
|
|
12898
|
+
);
|
|
12899
|
+
})
|
|
12900
|
+
);
|
|
12901
|
+
for (const mode of ["diff", "set"]) {
|
|
12902
|
+
const command = new Command(mode).description(
|
|
12903
|
+
mode === "diff" ? "Preview an effective partial project code change without writing" : "Back up prior code and apply a version-checked partial change"
|
|
12904
|
+
).requiredOption("--file <path>", "Partial projectCode JSON file").requiredOption("--shop <domain-or-id>", "Shop domain or UUID");
|
|
12905
|
+
if (mode === "set")
|
|
12906
|
+
command.requiredOption(
|
|
12907
|
+
"--backup <path>",
|
|
12908
|
+
"New backup file; an existing file is never replaced"
|
|
12909
|
+
);
|
|
12910
|
+
command.action(async (options, actionCommand) => {
|
|
12911
|
+
const patch = readPatch(actionCommand, options.file);
|
|
12912
|
+
const shop = await identifyRuntimeSettingsShop(actionCommand, options.shop);
|
|
12913
|
+
const current = await readCurrent(actionCommand, shop);
|
|
12914
|
+
const { credentials: _credentials, ...identity } = shop;
|
|
12915
|
+
const diff = effectiveDiff(current.projectCode, patch);
|
|
12916
|
+
if (mode === "diff" || diff.changedFields.length === 0) {
|
|
12917
|
+
const result2 = {
|
|
12918
|
+
...identity,
|
|
12919
|
+
status: diff.changedFields.length ? "changed" : "unchanged",
|
|
12920
|
+
version: current.version,
|
|
12921
|
+
diff
|
|
12922
|
+
};
|
|
12923
|
+
output(
|
|
12924
|
+
actionCommand,
|
|
12925
|
+
result2,
|
|
12926
|
+
() => process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
12927
|
+
`)
|
|
12928
|
+
);
|
|
12929
|
+
return;
|
|
12930
|
+
}
|
|
12931
|
+
const backupPath = (0, import_node_path13.resolve)(options.backup);
|
|
12932
|
+
try {
|
|
12933
|
+
(0, import_node_fs15.writeFileSync)(
|
|
12934
|
+
backupPath,
|
|
12935
|
+
`${JSON.stringify({ ...identity, ...current, capturedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
12936
|
+
`,
|
|
12937
|
+
{ flag: "wx", mode: 384 }
|
|
12938
|
+
);
|
|
12939
|
+
} catch (error) {
|
|
12940
|
+
fail(
|
|
12941
|
+
actionCommand,
|
|
12942
|
+
`Backup failed; project code was not sent: ${String(error)}`,
|
|
12943
|
+
EXIT_CODES.general
|
|
12944
|
+
);
|
|
12945
|
+
}
|
|
12946
|
+
const response = await updateRuntimeSettings(shop.credentials, {
|
|
12947
|
+
projectCode: patch,
|
|
12948
|
+
expectedVersion: current.version
|
|
12949
|
+
});
|
|
12950
|
+
if (!response.ok || !response.data) {
|
|
12951
|
+
fail(
|
|
12952
|
+
actionCommand,
|
|
12953
|
+
response.error ?? "Project code update failed",
|
|
12954
|
+
classifyStatus(response.status),
|
|
12955
|
+
{ ...response.errorDetails, backupPath }
|
|
12956
|
+
);
|
|
12957
|
+
}
|
|
12958
|
+
const data = response.data;
|
|
12959
|
+
const warnings = [...data.warnings ?? []];
|
|
12960
|
+
if (data.precompile?.status === "warning" || data.precompile?.status === "error") {
|
|
12961
|
+
warnings.push(
|
|
12962
|
+
`Project code saved; publication ${data.precompile.status}: ${data.precompile.summary}`
|
|
12963
|
+
);
|
|
12964
|
+
}
|
|
12965
|
+
if (data.shop_id !== identity.shop_id)
|
|
12966
|
+
warnings.push(
|
|
12967
|
+
"The write response has a different shop identity; inspect the saved state before another write."
|
|
12968
|
+
);
|
|
12969
|
+
for (const warning of warnings) process.stderr.write(`WARNING: ${warning}
|
|
12970
|
+
`);
|
|
12971
|
+
const result = {
|
|
12972
|
+
...identity,
|
|
12973
|
+
status: "saved",
|
|
12974
|
+
diff,
|
|
12975
|
+
backupPath,
|
|
12976
|
+
version: data.version,
|
|
12977
|
+
writtenVersion: data.writtenVersion,
|
|
12978
|
+
precompile: data.precompile,
|
|
12979
|
+
warnings
|
|
12980
|
+
};
|
|
12981
|
+
output(
|
|
12982
|
+
actionCommand,
|
|
12983
|
+
result,
|
|
12984
|
+
() => process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
12985
|
+
`)
|
|
12986
|
+
);
|
|
12987
|
+
});
|
|
12988
|
+
projectCodeCommand.addCommand(command);
|
|
12989
|
+
}
|
|
12990
|
+
|
|
12781
12991
|
// src/commands/triggers.ts
|
|
12782
|
-
var
|
|
12783
|
-
var
|
|
12992
|
+
var import_node_fs16 = __toESM(require("node:fs"));
|
|
12993
|
+
var import_node_path14 = __toESM(require("node:path"));
|
|
12784
12994
|
|
|
12785
12995
|
// src/trigger-proposal.ts
|
|
12786
12996
|
function runtimePath(path24) {
|
|
@@ -12868,8 +13078,8 @@ function score2(path24, patterns) {
|
|
|
12868
13078
|
0
|
|
12869
13079
|
);
|
|
12870
13080
|
}
|
|
12871
|
-
function best(
|
|
12872
|
-
const ranked =
|
|
13081
|
+
function best(fields2, patterns, numeric2 = false) {
|
|
13082
|
+
const ranked = fields2.filter(
|
|
12873
13083
|
(field) => !numeric2 || field.numericType === true || field.values.some((value) => typeof value === "number") || field.magnitude !== void 0
|
|
12874
13084
|
).map((field) => ({ field, score: score2(field.path.toLowerCase(), patterns) })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score || (b.field.magnitude ?? 0) - (a.field.magnitude ?? 0));
|
|
12875
13085
|
if (!ranked.length) return [];
|
|
@@ -12894,15 +13104,15 @@ function proposeTriggers(report) {
|
|
|
12894
13104
|
continue;
|
|
12895
13105
|
const descriptors = candidate.fields ?? candidate.paths;
|
|
12896
13106
|
const payload = candidate.properties ?? candidate.payload ?? candidate;
|
|
12897
|
-
const
|
|
13107
|
+
const fields2 = descriptors !== void 0 ? fieldsFrom(descriptors, "", true) : fieldsFrom(payload, "", isDescriptorArray(payload));
|
|
12898
13108
|
const revenues = best(
|
|
12899
|
-
|
|
13109
|
+
fields2,
|
|
12900
13110
|
[/revenue/, /total.*amount|amount.*total/, /grand.*total|total/, /value|amount/],
|
|
12901
13111
|
true
|
|
12902
13112
|
);
|
|
12903
|
-
const orderIds = best(
|
|
12904
|
-
const currencies = best(
|
|
12905
|
-
const items = best(
|
|
13113
|
+
const orderIds = best(fields2, [/order.*id|id.*order/, /transaction.*id/, /(^|\.)id$/]);
|
|
13114
|
+
const currencies = best(fields2, [/currency/, /(^|\.)cur(rency)?$/]);
|
|
13115
|
+
const items = best(fields2, [/(^|\.)((line_?)?items|products)$/]);
|
|
12906
13116
|
if (!revenues.length || !orderIds.length || !currencies.length) continue;
|
|
12907
13117
|
for (const revenue of revenues)
|
|
12908
13118
|
for (const orderId of orderIds)
|
|
@@ -13197,10 +13407,10 @@ function writeWarnings(result) {
|
|
|
13197
13407
|
`);
|
|
13198
13408
|
}
|
|
13199
13409
|
}
|
|
13200
|
-
var
|
|
13410
|
+
var uuidPattern2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
13201
13411
|
async function identify(command, requestedShop) {
|
|
13202
13412
|
let credentials = requireCredentials();
|
|
13203
|
-
if (credentials.credentialKind === "user" && !
|
|
13413
|
+
if (credentials.credentialKind === "user" && !uuidPattern2.test(requestedShop)) {
|
|
13204
13414
|
const discoveryCredentials = {
|
|
13205
13415
|
...credentials,
|
|
13206
13416
|
resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
|
|
@@ -13273,7 +13483,7 @@ var get3 = new Command("get").description("Get data layer triggers").requiredOpt
|
|
|
13273
13483
|
var set2 = new Command("set").requiredOption("--file <json>", "JSON trigger array").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").action(async function(opts) {
|
|
13274
13484
|
let triggers;
|
|
13275
13485
|
try {
|
|
13276
|
-
triggers = strictTriggers(JSON.parse(
|
|
13486
|
+
triggers = strictTriggers(JSON.parse(import_node_fs16.default.readFileSync(import_node_path14.default.resolve(opts.file), "utf8")));
|
|
13277
13487
|
} catch (error) {
|
|
13278
13488
|
fail(
|
|
13279
13489
|
this,
|
|
@@ -13300,7 +13510,7 @@ var propose = new Command("propose").requiredOption("--from-file <u2-report.json
|
|
|
13300
13510
|
let proposals;
|
|
13301
13511
|
let report;
|
|
13302
13512
|
try {
|
|
13303
|
-
report = JSON.parse(
|
|
13513
|
+
report = JSON.parse(import_node_fs16.default.readFileSync(import_node_path14.default.resolve(opts.fromFile), "utf8"));
|
|
13304
13514
|
proposals = proposeTriggers(report);
|
|
13305
13515
|
} catch (error) {
|
|
13306
13516
|
fail(this, error instanceof Error ? error.message : String(error), 2);
|
|
@@ -14932,7 +15142,7 @@ function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
|
|
|
14932
15142
|
`Response from ${hostname} uses unsupported content encoding "${encoding}"`
|
|
14933
15143
|
)
|
|
14934
15144
|
);
|
|
14935
|
-
return new Promise((
|
|
15145
|
+
return new Promise((resolve2, reject) => {
|
|
14936
15146
|
const stream = decoderFor(encoding);
|
|
14937
15147
|
const chunks = [];
|
|
14938
15148
|
let bytes = 0;
|
|
@@ -14952,7 +15162,7 @@ function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
|
|
|
14952
15162
|
}
|
|
14953
15163
|
chunks.push(chunk.subarray(0, limits.maxBytes - bytes));
|
|
14954
15164
|
stream.destroy();
|
|
14955
|
-
settle(() =>
|
|
15165
|
+
settle(() => resolve2({ body: Buffer.concat(chunks), truncated: true }));
|
|
14956
15166
|
return;
|
|
14957
15167
|
}
|
|
14958
15168
|
chunks.push(chunk);
|
|
@@ -14962,7 +15172,7 @@ function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
|
|
|
14962
15172
|
"error",
|
|
14963
15173
|
(error) => settle(() => {
|
|
14964
15174
|
if (limits.onExceed === "truncate" && inputTruncated && chunks.length > 0)
|
|
14965
|
-
|
|
15175
|
+
resolve2({ body: Buffer.concat(chunks), truncated: true });
|
|
14966
15176
|
else
|
|
14967
15177
|
reject(
|
|
14968
15178
|
new BodyDecodeError(
|
|
@@ -14973,7 +15183,7 @@ function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
|
|
|
14973
15183
|
);
|
|
14974
15184
|
stream.on(
|
|
14975
15185
|
"end",
|
|
14976
|
-
() => settle(() =>
|
|
15186
|
+
() => settle(() => resolve2({ body: Buffer.concat(chunks), truncated: false }))
|
|
14977
15187
|
);
|
|
14978
15188
|
stream.end(raw);
|
|
14979
15189
|
});
|
|
@@ -15000,7 +15210,7 @@ function responseFromIncoming(res, body5, decoded) {
|
|
|
15000
15210
|
});
|
|
15001
15211
|
}
|
|
15002
15212
|
function pinnedFetch(url, addresses, limits) {
|
|
15003
|
-
return new Promise((
|
|
15213
|
+
return new Promise((resolve2, reject) => {
|
|
15004
15214
|
const send3 = url.protocol === "https:" ? import_node_https.request : import_node_http2.request;
|
|
15005
15215
|
const pinnedLookup = (_hostname, options, callback) => {
|
|
15006
15216
|
if (options?.all)
|
|
@@ -15047,7 +15257,7 @@ function pinnedFetch(url, addresses, limits) {
|
|
|
15047
15257
|
chunks.push(chunk.subarray(0, limits.maxBytes - bytes));
|
|
15048
15258
|
req.destroy();
|
|
15049
15259
|
settle(
|
|
15050
|
-
() => finalizeIncoming(url, res, Buffer.concat(chunks), true, limits).then(
|
|
15260
|
+
() => finalizeIncoming(url, res, Buffer.concat(chunks), true, limits).then(resolve2, reject)
|
|
15051
15261
|
);
|
|
15052
15262
|
return;
|
|
15053
15263
|
}
|
|
@@ -15062,7 +15272,7 @@ function pinnedFetch(url, addresses, limits) {
|
|
|
15062
15272
|
reject(new Error(`No HTTP status from ${url.hostname}`));
|
|
15063
15273
|
return;
|
|
15064
15274
|
}
|
|
15065
|
-
finalizeIncoming(url, res, Buffer.concat(chunks), false, limits).then(
|
|
15275
|
+
finalizeIncoming(url, res, Buffer.concat(chunks), false, limits).then(resolve2, reject);
|
|
15066
15276
|
})
|
|
15067
15277
|
);
|
|
15068
15278
|
}
|
|
@@ -15494,8 +15704,8 @@ var installCommand = new Command("install").description("Check Apex storefront i
|
|
|
15494
15704
|
|
|
15495
15705
|
// src/commands/flags.ts
|
|
15496
15706
|
init_source();
|
|
15497
|
-
var
|
|
15498
|
-
var
|
|
15707
|
+
var import_node_fs17 = __toESM(require("node:fs"));
|
|
15708
|
+
var import_node_path15 = __toESM(require("node:path"));
|
|
15499
15709
|
|
|
15500
15710
|
// src/edge-status.ts
|
|
15501
15711
|
function getEdgeDeliveryStatus(deliveryType, expectedWorstCaseSeconds = 360) {
|
|
@@ -15537,7 +15747,7 @@ function body(command, value, opts) {
|
|
|
15537
15747
|
let parsed = {};
|
|
15538
15748
|
if (value) {
|
|
15539
15749
|
const source = value.startsWith("@") ? value.slice(1) : value;
|
|
15540
|
-
const text = value.startsWith("@") ||
|
|
15750
|
+
const text = value.startsWith("@") || import_node_fs17.default.existsSync(source) ? import_node_fs17.default.readFileSync(import_node_path15.default.resolve(process.cwd(), source), "utf8") : value;
|
|
15541
15751
|
try {
|
|
15542
15752
|
parsed = JSON.parse(text);
|
|
15543
15753
|
} catch (e) {
|
|
@@ -15670,8 +15880,8 @@ Examples:
|
|
|
15670
15880
|
);
|
|
15671
15881
|
|
|
15672
15882
|
// src/commands/backlog.ts
|
|
15673
|
-
var
|
|
15674
|
-
var
|
|
15883
|
+
var import_node_fs18 = __toESM(require("node:fs"));
|
|
15884
|
+
var import_node_path16 = __toESM(require("node:path"));
|
|
15675
15885
|
init_source();
|
|
15676
15886
|
var kind2 = "backlog";
|
|
15677
15887
|
var singular = "backlog item";
|
|
@@ -15679,7 +15889,7 @@ function parse(command, value) {
|
|
|
15679
15889
|
if (!value) return {};
|
|
15680
15890
|
try {
|
|
15681
15891
|
const source = value.startsWith("@") ? value.slice(1) : value;
|
|
15682
|
-
const text = value.startsWith("@") ||
|
|
15892
|
+
const text = value.startsWith("@") || import_node_fs18.default.existsSync(source) ? import_node_fs18.default.readFileSync(import_node_path16.default.resolve(process.cwd(), source), "utf8") : value;
|
|
15683
15893
|
const body5 = JSON.parse(text);
|
|
15684
15894
|
if (!body5 || typeof body5 !== "object" || Array.isArray(body5))
|
|
15685
15895
|
throw new Error("body must be an object");
|
|
@@ -15779,7 +15989,7 @@ var remove2 = new Command("delete").description("Delete a backlog item").argumen
|
|
|
15779
15989
|
const readline3 = await import("node:readline");
|
|
15780
15990
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
15781
15991
|
const answer = await new Promise(
|
|
15782
|
-
(
|
|
15992
|
+
(resolve2) => rl.question(source_default.yellow(`Delete backlog item ${id}? [y/N] `), resolve2)
|
|
15783
15993
|
);
|
|
15784
15994
|
rl.close();
|
|
15785
15995
|
if (answer.toLowerCase() !== "y") {
|
|
@@ -15795,8 +16005,8 @@ var remove2 = new Command("delete").description("Delete a backlog item").argumen
|
|
|
15795
16005
|
var backlogCommand = new Command("backlog").description("Manage backlog").addCommand(list7).addCommand(get6).addCommand(write2("create")).addCommand(write2("update")).addCommand(archive).addCommand(restore).addCommand(remove2);
|
|
15796
16006
|
|
|
15797
16007
|
// src/commands/signals.ts
|
|
15798
|
-
var
|
|
15799
|
-
var
|
|
16008
|
+
var import_node_fs19 = __toESM(require("node:fs"));
|
|
16009
|
+
var import_node_path17 = __toESM(require("node:path"));
|
|
15800
16010
|
init_source();
|
|
15801
16011
|
var kind3 = "signals";
|
|
15802
16012
|
var singular2 = "signal";
|
|
@@ -15804,7 +16014,7 @@ function parse2(command, value) {
|
|
|
15804
16014
|
if (!value) return {};
|
|
15805
16015
|
try {
|
|
15806
16016
|
const source = value.startsWith("@") ? value.slice(1) : value;
|
|
15807
|
-
const text = value.startsWith("@") ||
|
|
16017
|
+
const text = value.startsWith("@") || import_node_fs19.default.existsSync(source) ? import_node_fs19.default.readFileSync(import_node_path17.default.resolve(process.cwd(), source), "utf8") : value;
|
|
15808
16018
|
const body5 = JSON.parse(text);
|
|
15809
16019
|
if (!body5 || typeof body5 !== "object" || Array.isArray(body5))
|
|
15810
16020
|
throw new Error("body must be an object");
|
|
@@ -15886,14 +16096,14 @@ var signalsCommand = new Command("signals").description("Manage signals").addCom
|
|
|
15886
16096
|
|
|
15887
16097
|
// src/commands/personalizations.ts
|
|
15888
16098
|
init_source();
|
|
15889
|
-
var
|
|
15890
|
-
var
|
|
16099
|
+
var import_node_fs20 = __toESM(require("node:fs"));
|
|
16100
|
+
var import_node_path18 = __toESM(require("node:path"));
|
|
15891
16101
|
var kind4 = "personalizations";
|
|
15892
16102
|
function body2(command, value, opts) {
|
|
15893
16103
|
let parsed = {};
|
|
15894
16104
|
if (value) {
|
|
15895
16105
|
const source = value.startsWith("@") ? value.slice(1) : value;
|
|
15896
|
-
const text = value.startsWith("@") ||
|
|
16106
|
+
const text = value.startsWith("@") || import_node_fs20.default.existsSync(source) ? import_node_fs20.default.readFileSync(import_node_path18.default.resolve(process.cwd(), source), "utf8") : value;
|
|
15897
16107
|
try {
|
|
15898
16108
|
parsed = JSON.parse(text);
|
|
15899
16109
|
} catch (e) {
|
|
@@ -15975,13 +16185,13 @@ var personalizationsCommand = new Command("personalizations").description("Manag
|
|
|
15975
16185
|
|
|
15976
16186
|
// src/commands/targeting-resources.ts
|
|
15977
16187
|
init_source();
|
|
15978
|
-
var
|
|
15979
|
-
var
|
|
16188
|
+
var import_node_fs21 = __toESM(require("node:fs"));
|
|
16189
|
+
var import_node_path19 = __toESM(require("node:path"));
|
|
15980
16190
|
function body3(command, value) {
|
|
15981
16191
|
if (!value) return {};
|
|
15982
16192
|
const source = value.startsWith("@") ? value.slice(1) : value;
|
|
15983
16193
|
try {
|
|
15984
|
-
const text = value.startsWith("@") ||
|
|
16194
|
+
const text = value.startsWith("@") || import_node_fs21.default.existsSync(source) ? import_node_fs21.default.readFileSync(import_node_path19.default.resolve(process.cwd(), source), "utf8") : value;
|
|
15985
16195
|
return JSON.parse(text);
|
|
15986
16196
|
} catch (error) {
|
|
15987
16197
|
fail(
|
|
@@ -16101,8 +16311,8 @@ function targetingResourceCommand(kind5) {
|
|
|
16101
16311
|
input: process.stdin,
|
|
16102
16312
|
output: process.stderr
|
|
16103
16313
|
});
|
|
16104
|
-
const answer = await new Promise((
|
|
16105
|
-
rl.question(source_default.yellow(`Delete exclusion group "${id}"? [y/N] `),
|
|
16314
|
+
const answer = await new Promise((resolve2) => {
|
|
16315
|
+
rl.question(source_default.yellow(`Delete exclusion group "${id}"? [y/N] `), resolve2);
|
|
16106
16316
|
});
|
|
16107
16317
|
rl.close();
|
|
16108
16318
|
if (answer.toLowerCase() !== "y") {
|
|
@@ -16232,8 +16442,8 @@ var targetingCommand = new Command("targeting").description("Test experiment tar
|
|
|
16232
16442
|
|
|
16233
16443
|
// src/commands/sos.ts
|
|
16234
16444
|
init_source();
|
|
16235
|
-
var
|
|
16236
|
-
var
|
|
16445
|
+
var import_node_fs22 = __toESM(require("node:fs"));
|
|
16446
|
+
var import_node_path20 = __toESM(require("node:path"));
|
|
16237
16447
|
var SOS_TYPES = /* @__PURE__ */ new Set([
|
|
16238
16448
|
"full_handover",
|
|
16239
16449
|
"design",
|
|
@@ -16246,7 +16456,7 @@ var SOS_TYPES = /* @__PURE__ */ new Set([
|
|
|
16246
16456
|
function readText(value) {
|
|
16247
16457
|
if (!value) return void 0;
|
|
16248
16458
|
if (!value.startsWith("@")) return value;
|
|
16249
|
-
return
|
|
16459
|
+
return import_node_fs22.default.readFileSync(import_node_path20.default.resolve(process.cwd(), value.slice(1)), "utf8");
|
|
16250
16460
|
}
|
|
16251
16461
|
function printTicket(ticket, reused) {
|
|
16252
16462
|
console.log();
|
|
@@ -16380,13 +16590,13 @@ var markRead = new Command("mark-read").option("--channel <key>", "Mark one chan
|
|
|
16380
16590
|
var inboxCommand = new Command("inbox").description("Read and send inbox messages").addCommand(list11).addCommand(send2).addCommand(markRead);
|
|
16381
16591
|
|
|
16382
16592
|
// src/commands/shops.ts
|
|
16383
|
-
var
|
|
16384
|
-
var
|
|
16593
|
+
var import_node_fs23 = __toESM(require("node:fs"));
|
|
16594
|
+
var import_node_path21 = __toESM(require("node:path"));
|
|
16385
16595
|
function body4(command, raw) {
|
|
16386
16596
|
if (!raw) return {};
|
|
16387
16597
|
try {
|
|
16388
16598
|
const source = raw.startsWith("@") ? raw.slice(1) : raw;
|
|
16389
|
-
const text = raw.startsWith("@") ||
|
|
16599
|
+
const text = raw.startsWith("@") || import_node_fs23.default.existsSync(source) ? import_node_fs23.default.readFileSync(import_node_path21.default.resolve(source), "utf8") : raw;
|
|
16390
16600
|
const value = JSON.parse(text);
|
|
16391
16601
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
16392
16602
|
throw new Error("body must be an object");
|
|
@@ -17199,16 +17409,16 @@ var discoverCommand = new Command("discover").description("Read back the trackin
|
|
|
17199
17409
|
});
|
|
17200
17410
|
|
|
17201
17411
|
// src/commands/skills.ts
|
|
17202
|
-
var
|
|
17203
|
-
var
|
|
17412
|
+
var import_node_fs24 = __toESM(require("node:fs"));
|
|
17413
|
+
var import_node_path22 = __toESM(require("node:path"));
|
|
17204
17414
|
function packageRoot() {
|
|
17205
17415
|
const candidates = [
|
|
17206
|
-
|
|
17207
|
-
|
|
17208
|
-
|
|
17416
|
+
import_node_path22.default.resolve(__dirname, ".."),
|
|
17417
|
+
import_node_path22.default.resolve(import_node_path22.default.dirname(process.argv[1] ?? process.cwd()), ".."),
|
|
17418
|
+
import_node_path22.default.resolve(process.cwd(), "packages/cli")
|
|
17209
17419
|
];
|
|
17210
17420
|
for (const candidate of candidates) {
|
|
17211
|
-
if (
|
|
17421
|
+
if (import_node_fs24.default.existsSync(import_node_path22.default.join(candidate, "AGENTS.md"))) return candidate;
|
|
17212
17422
|
}
|
|
17213
17423
|
throw new Error(
|
|
17214
17424
|
`Apex CLI package files not found. Looked for AGENTS.md in: ${candidates.join(", ")}`
|
|
@@ -17217,19 +17427,19 @@ function packageRoot() {
|
|
|
17217
17427
|
function skillPaths() {
|
|
17218
17428
|
const root = packageRoot();
|
|
17219
17429
|
return {
|
|
17220
|
-
agents:
|
|
17221
|
-
skill:
|
|
17430
|
+
agents: import_node_path22.default.join(root, "AGENTS.md"),
|
|
17431
|
+
skill: import_node_path22.default.join(root, "skills", "apex-cli", "SKILL.md")
|
|
17222
17432
|
};
|
|
17223
17433
|
}
|
|
17224
17434
|
function installSkill(paths, targetRoot) {
|
|
17225
|
-
const missing = [paths.skill, paths.agents].filter((filePath) => !
|
|
17435
|
+
const missing = [paths.skill, paths.agents].filter((filePath) => !import_node_fs24.default.existsSync(filePath));
|
|
17226
17436
|
if (missing.length > 0) {
|
|
17227
17437
|
throw new Error(`Apex CLI package files not found. Missing: ${missing.join(", ")}`);
|
|
17228
17438
|
}
|
|
17229
|
-
const target =
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17439
|
+
const target = import_node_path22.default.resolve(targetRoot, "apex-cli");
|
|
17440
|
+
import_node_fs24.default.mkdirSync(target, { recursive: true });
|
|
17441
|
+
import_node_fs24.default.copyFileSync(paths.skill, import_node_path22.default.join(target, "SKILL.md"));
|
|
17442
|
+
import_node_fs24.default.copyFileSync(paths.agents, import_node_path22.default.join(target, "AGENTS.md"));
|
|
17233
17443
|
return target;
|
|
17234
17444
|
}
|
|
17235
17445
|
var skillsCommand = new Command("skills").description("Print or install the Apex CLI agent skill files").option("--install", "Copy the Apex CLI skill into ./.claude/skills/apex-cli").option("--target <dir>", "Install target skills directory", ".claude/skills").option("--json", "Print JSON").action(function action64(opts) {
|
|
@@ -17329,8 +17539,8 @@ async function confirmArchive(command, shopId, force) {
|
|
|
17329
17539
|
}
|
|
17330
17540
|
const readline3 = await import("node:readline");
|
|
17331
17541
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
17332
|
-
const answer = await new Promise((
|
|
17333
|
-
rl.question("Archive this shop? Apex delivery will be disabled. [y/N] ",
|
|
17542
|
+
const answer = await new Promise((resolve2) => {
|
|
17543
|
+
rl.question("Archive this shop? Apex delivery will be disabled. [y/N] ", resolve2);
|
|
17334
17544
|
});
|
|
17335
17545
|
rl.close();
|
|
17336
17546
|
if (answer.toLowerCase() !== "y") {
|
|
@@ -17384,7 +17594,7 @@ async function confirm(c, force, label, retry) {
|
|
|
17384
17594
|
const readline3 = await import("node:readline");
|
|
17385
17595
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
17386
17596
|
const answer = await new Promise(
|
|
17387
|
-
(
|
|
17597
|
+
(resolve2) => rl.question(source_default.yellow(`${label}? [y/N] `), resolve2)
|
|
17388
17598
|
);
|
|
17389
17599
|
rl.close();
|
|
17390
17600
|
if (answer.toLowerCase() !== "y") {
|
|
@@ -17975,10 +18185,10 @@ var webhooksCommand = new Command("webhooks").description("Manage shop webhooks"
|
|
|
17975
18185
|
|
|
17976
18186
|
// src/commands/simulate.ts
|
|
17977
18187
|
init_source();
|
|
17978
|
-
var
|
|
17979
|
-
var
|
|
18188
|
+
var import_node_fs25 = __toESM(require("node:fs"));
|
|
18189
|
+
var import_node_path23 = __toESM(require("node:path"));
|
|
17980
18190
|
function readJson2(file) {
|
|
17981
|
-
const text = file === "-" ?
|
|
18191
|
+
const text = file === "-" ? import_node_fs25.default.readFileSync(0, "utf8") : import_node_fs25.default.readFileSync(import_node_path23.default.resolve(file), "utf8");
|
|
17982
18192
|
try {
|
|
17983
18193
|
return JSON.parse(text);
|
|
17984
18194
|
} catch (error) {
|
|
@@ -18100,8 +18310,8 @@ var simulateCommand = new Command("simulate").description("Simulate data-layer t
|
|
|
18100
18310
|
});
|
|
18101
18311
|
|
|
18102
18312
|
// src/commands/config-resources.ts
|
|
18103
|
-
var
|
|
18104
|
-
var
|
|
18313
|
+
var import_node_fs26 = __toESM(require("node:fs"));
|
|
18314
|
+
var import_node_path24 = __toESM(require("node:path"));
|
|
18105
18315
|
function show(resource) {
|
|
18106
18316
|
process.stdout.write(`${JSON.stringify(resource, null, 2)}
|
|
18107
18317
|
`);
|
|
@@ -18127,8 +18337,8 @@ async function confirmAssetDelete(id) {
|
|
|
18127
18337
|
input: process.stdin,
|
|
18128
18338
|
output: process.stderr
|
|
18129
18339
|
});
|
|
18130
|
-
const answer = await new Promise((
|
|
18131
|
-
rl.question(`Delete asset "${id}"? This cannot be undone. [y/N] `,
|
|
18340
|
+
const answer = await new Promise((resolve2) => {
|
|
18341
|
+
rl.question(`Delete asset "${id}"? This cannot be undone. [y/N] `, resolve2);
|
|
18132
18342
|
});
|
|
18133
18343
|
rl.close();
|
|
18134
18344
|
return answer.trim().toLowerCase() === "y";
|
|
@@ -18174,12 +18384,12 @@ function readFamily(family, label) {
|
|
|
18174
18384
|
}
|
|
18175
18385
|
var assetReads = readFamily("assets", "assets");
|
|
18176
18386
|
var upload = new Command("upload").description("Upload an asset").argument("<file>").action(async function(file) {
|
|
18177
|
-
const absolute =
|
|
18178
|
-
if (!
|
|
18179
|
-
const fileStat =
|
|
18387
|
+
const absolute = import_node_path24.default.resolve(process.cwd(), file);
|
|
18388
|
+
if (!import_node_fs26.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
|
|
18389
|
+
const fileStat = import_node_fs26.default.statSync(absolute);
|
|
18180
18390
|
if (!fileStat.isFile()) fail(this, `File not found: ${file}`, 3);
|
|
18181
18391
|
if (fileStat.size > MAX_ASSET_SIZE_BYTES) fail(this, `${file} is larger than 10 MB`, 3);
|
|
18182
|
-
const mimeType = ASSET_MIME_TYPES[
|
|
18392
|
+
const mimeType = ASSET_MIME_TYPES[import_node_path24.default.extname(absolute).toLowerCase()];
|
|
18183
18393
|
if (!mimeType)
|
|
18184
18394
|
fail(
|
|
18185
18395
|
this,
|
|
@@ -18188,8 +18398,8 @@ var upload = new Command("upload").description("Upload an asset").argument("<fil
|
|
|
18188
18398
|
);
|
|
18189
18399
|
const r = await uploadAssets(requireCredentials(), [
|
|
18190
18400
|
{
|
|
18191
|
-
name:
|
|
18192
|
-
bytes:
|
|
18401
|
+
name: import_node_path24.default.basename(absolute),
|
|
18402
|
+
bytes: import_node_fs26.default.readFileSync(absolute),
|
|
18193
18403
|
type: mimeType
|
|
18194
18404
|
}
|
|
18195
18405
|
]);
|
|
@@ -18279,6 +18489,7 @@ program2.addCommand(funnelsCommand);
|
|
|
18279
18489
|
program2.addCommand(environmentsCommand);
|
|
18280
18490
|
program2.addCommand(consentCommand);
|
|
18281
18491
|
program2.addCommand(triggersCommand);
|
|
18492
|
+
program2.addCommand(projectCodeCommand);
|
|
18282
18493
|
program2.addCommand(domainsCommand);
|
|
18283
18494
|
program2.addCommand(handoverCommand);
|
|
18284
18495
|
program2.addCommand(analyticsCommand);
|
package/package.json
CHANGED
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -63,3 +63,31 @@ Platform administrators can archive a mistakenly created shop with
|
|
|
63
63
|
`apex admin shops restore <shopId>`. Archive refuses a shop with running tests;
|
|
64
64
|
stop those tests rather than retrying or overriding the refusal. Restore leaves
|
|
65
65
|
delivery controlled by the runtime kill switch.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Project code
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
apex project-code get --shop example.com --json
|
|
72
|
+
apex project-code diff --file project-code.json --shop example.com --json
|
|
73
|
+
apex project-code set --file project-code.json --shop example.com --backup previous-code.json --json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The input is a partial projectCode object, for example `{ "css": "body { color: red; }" }`.
|
|
77
|
+
Supported fields are `enabled` (boolean) and `css`, `helperJs`, and `js` (strings or null).
|
|
78
|
+
Each code field permits at most 20,000 characters. Omitted fields remain unchanged.
|
|
79
|
+
Null or blank code clears that field. Unknown fields and empty objects are rejected.
|
|
80
|
+
|
|
81
|
+
GET returns complete code, shop identity, and an opaque version. DIFF returns effective
|
|
82
|
+
before/after values and changed fields without writing. SET creates a new backup file
|
|
83
|
+
with complete previous code, identity, capture time, and the exact version before PATCH.
|
|
84
|
+
It never replaces an existing backup. Backup failures prevent the write. An effective
|
|
85
|
+
no-op returns `unchanged`, without a PATCH or backup file.
|
|
86
|
+
|
|
87
|
+
The backed-up version becomes expectedVersion unchanged. A conflict returns exit 5,
|
|
88
|
+
keeps the backup, and does not retry. Read the latest state and preview it before a new
|
|
89
|
+
attempt with a new backup path. Authentication uses exit 2; invalid input uses exit 3.
|
|
90
|
+
A successful write returns `saved`, its diff, backup path, current version, writtenVersion,
|
|
91
|
+
and precompile result. A failed precompile is a saved-write warning. All warnings remain
|
|
92
|
+
in JSON and are also written to stderr. User keys resolve domains or pin UUID context;
|
|
93
|
+
shop keys must match the requested identity before any backup or write.
|