@drip-apex/apex-cli 0.20.0 → 0.21.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/README.md +21 -0
- package/dist/cli.js +255 -90
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,6 +184,7 @@ including every subcommand, argument, option, and alias.
|
|
|
184
184
|
- `apex keys` — manage shop API keys.
|
|
185
185
|
- `apex org` — inspect and manage the organization.
|
|
186
186
|
- `apex team` — manage shop-scoped members and invitations.
|
|
187
|
+
- `apex notifications` — choose Ready to launch email recipients and toggle ready messages on an existing Slack destination.
|
|
187
188
|
|
|
188
189
|
### Experiments and delivery
|
|
189
190
|
|
|
@@ -742,3 +743,23 @@ Unavailable values print `–` (empty in CSV); `*` marks a denominator below 50.
|
|
|
742
743
|
`apex tracking manifest [manifest]` includes the shop's form definitions in a schemaVersion 1 manifest.
|
|
743
744
|
|
|
744
745
|
Flag goals: `apex flags create --key banner --name "Banner" --goal <uuid> --goal <uuid> --primary-goal <uuid>`. The same options work on `flags update <id>`; `--goal` replaces the attached set. `flags get <id>` prints goals and marks the primary goal. JSON input can clear the set with `{"goal_ids":[]}`.
|
|
746
|
+
|
|
747
|
+
### Ready to launch notifications
|
|
748
|
+
|
|
749
|
+
Use a user API key with `team:read` / `team:write` and the shop permission `members:manage`.
|
|
750
|
+
Every command accepts `--shop <id>` and `--json`; otherwise the selected shop is used.
|
|
751
|
+
|
|
752
|
+
```sh
|
|
753
|
+
apex notifications ready get --shop <id> --json
|
|
754
|
+
apex notifications ready email set --recipients a@example.com,b@example.com --shop <id>
|
|
755
|
+
apex notifications ready email add --recipients c@example.com --shop <id>
|
|
756
|
+
apex notifications ready email remove --recipients a@example.com --shop <id>
|
|
757
|
+
apex notifications ready email off --shop <id>
|
|
758
|
+
apex notifications ready slack on --shop <id>
|
|
759
|
+
apex notifications ready slack off --shop <id>
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
Recipients must already be current shop members. Email `off` clears the recipient list.
|
|
763
|
+
Slack `off` removes only `experiment.ready_to_launch` from the existing destination's events.
|
|
764
|
+
Connect or resume Slack in Settings > Integrations; selecting the ready event preserves its pause state.
|
|
765
|
+
There is no master switch. If settings change concurrently, retry the command to read the new version.
|
package/dist/cli.js
CHANGED
|
@@ -3074,14 +3074,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3074
3074
|
*/
|
|
3075
3075
|
helpInformation(contextOptions) {
|
|
3076
3076
|
const helper = this.createHelp();
|
|
3077
|
-
const
|
|
3077
|
+
const context2 = this._getOutputContext(contextOptions);
|
|
3078
3078
|
helper.prepareContext({
|
|
3079
|
-
error:
|
|
3080
|
-
helpWidth:
|
|
3081
|
-
outputHasColors:
|
|
3079
|
+
error: context2.error,
|
|
3080
|
+
helpWidth: context2.helpWidth,
|
|
3081
|
+
outputHasColors: context2.hasColors
|
|
3082
3082
|
});
|
|
3083
3083
|
const text2 = helper.formatHelp(this, helper);
|
|
3084
|
-
if (
|
|
3084
|
+
if (context2.hasColors) return text2;
|
|
3085
3085
|
return this._outputConfiguration.stripColor(text2);
|
|
3086
3086
|
}
|
|
3087
3087
|
/**
|
|
@@ -3243,15 +3243,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3243
3243
|
Expecting one of '${allowedValues.join("', '")}'`);
|
|
3244
3244
|
}
|
|
3245
3245
|
const helpEvent = `${position}Help`;
|
|
3246
|
-
this.on(helpEvent, (
|
|
3246
|
+
this.on(helpEvent, (context2) => {
|
|
3247
3247
|
let helpStr;
|
|
3248
3248
|
if (typeof text2 === "function") {
|
|
3249
|
-
helpStr = text2({ error:
|
|
3249
|
+
helpStr = text2({ error: context2.error, command: context2.command });
|
|
3250
3250
|
} else {
|
|
3251
3251
|
helpStr = text2;
|
|
3252
3252
|
}
|
|
3253
3253
|
if (helpStr) {
|
|
3254
|
-
|
|
3254
|
+
context2.write(`${helpStr}
|
|
3255
3255
|
`);
|
|
3256
3256
|
}
|
|
3257
3257
|
});
|
|
@@ -7210,7 +7210,7 @@ function readStoredCredentials(file = import_node_path.default.join(import_node_
|
|
|
7210
7210
|
}
|
|
7211
7211
|
}
|
|
7212
7212
|
function resolveCredentials(env2 = process.env, stored = readStoredCredentials()) {
|
|
7213
|
-
const
|
|
7213
|
+
const context2 = {
|
|
7214
7214
|
orgId: env2.DRIP_ORG || stored?.context?.orgId,
|
|
7215
7215
|
shopId: env2.DRIP_SHOP || stored?.context?.shopId
|
|
7216
7216
|
};
|
|
@@ -7223,7 +7223,7 @@ function resolveCredentials(env2 = process.env, stored = readStoredCredentials()
|
|
|
7223
7223
|
apiKey: environmentApiKey,
|
|
7224
7224
|
baseUrl: environmentBaseUrl ?? "https://app.drip-apex.com",
|
|
7225
7225
|
credentialKind: user ? "user" : "shop",
|
|
7226
|
-
resolvedContext:
|
|
7226
|
+
resolvedContext: context2
|
|
7227
7227
|
};
|
|
7228
7228
|
}
|
|
7229
7229
|
if (stored?.userApiKey) return {
|
|
@@ -7231,9 +7231,9 @@ function resolveCredentials(env2 = process.env, stored = readStoredCredentials()
|
|
|
7231
7231
|
apiKey: stored.userApiKey,
|
|
7232
7232
|
baseUrl: stored.userBaseUrl ?? stored.baseUrl,
|
|
7233
7233
|
credentialKind: "user",
|
|
7234
|
-
resolvedContext:
|
|
7234
|
+
resolvedContext: context2
|
|
7235
7235
|
};
|
|
7236
|
-
if (stored?.apiKey) return { ...stored, credentialKind: "shop", resolvedContext:
|
|
7236
|
+
if (stored?.apiKey) return { ...stored, credentialKind: "shop", resolvedContext: context2 };
|
|
7237
7237
|
return null;
|
|
7238
7238
|
}
|
|
7239
7239
|
|
|
@@ -7423,7 +7423,7 @@ async function checkForUpdate(current) {
|
|
|
7423
7423
|
// package.json
|
|
7424
7424
|
var package_default = {
|
|
7425
7425
|
name: "@drip-apex/apex-cli",
|
|
7426
|
-
version: "0.
|
|
7426
|
+
version: "0.21.0",
|
|
7427
7427
|
license: "MIT",
|
|
7428
7428
|
description: "Apex by DRIP CLI: build, preview and run A/B tests on your online shop from the terminal or an AI coding agent (Claude Code, Codex, Cursor).",
|
|
7429
7429
|
keywords: [
|
|
@@ -7546,8 +7546,8 @@ var envelopeContext = {
|
|
|
7546
7546
|
shop_id: null,
|
|
7547
7547
|
credential: initialEnvironmentApiKey?.startsWith("apx_uk_") ? "user" : "shop"
|
|
7548
7548
|
};
|
|
7549
|
-
function setEnvelopeContext(
|
|
7550
|
-
envelopeContext = { ...envelopeContext, ...
|
|
7549
|
+
function setEnvelopeContext(context2) {
|
|
7550
|
+
envelopeContext = { ...envelopeContext, ...context2 };
|
|
7551
7551
|
}
|
|
7552
7552
|
var jsonMode = false;
|
|
7553
7553
|
var commandName = "apex";
|
|
@@ -7955,14 +7955,14 @@ function argvOption(name) {
|
|
|
7955
7955
|
return void 0;
|
|
7956
7956
|
}
|
|
7957
7957
|
var didWarnStoredContext = false;
|
|
7958
|
-
function warnStoredContextFallback(creds, flags,
|
|
7958
|
+
function warnStoredContextFallback(creds, flags, context2) {
|
|
7959
7959
|
if (didWarnStoredContext) return;
|
|
7960
7960
|
const storedOrg = !flags.orgId && !process.env.DRIP_ORG && Boolean(creds?.context?.orgId);
|
|
7961
7961
|
const storedShop = !flags.shopId && !process.env.DRIP_SHOP && Boolean(creds?.context?.shopId);
|
|
7962
7962
|
if (!storedOrg && !storedShop) return;
|
|
7963
7963
|
const selections = [
|
|
7964
|
-
storedOrg ? `org ${
|
|
7965
|
-
storedShop ? `shop ${
|
|
7964
|
+
storedOrg ? `org ${context2.orgId}` : null,
|
|
7965
|
+
storedShop ? `shop ${context2.shopId}` : null
|
|
7966
7966
|
].filter(Boolean).join(" / ");
|
|
7967
7967
|
console.error(`Using ${selections} from 'apex use' context (pass --org/--shop to override).`);
|
|
7968
7968
|
didWarnStoredContext = true;
|
|
@@ -7996,21 +7996,21 @@ function resolveContext(creds = getCredentials(), flags = { orgId: argvOption("o
|
|
|
7996
7996
|
shopId: flags.shopId || process.env.DRIP_SHOP || void 0
|
|
7997
7997
|
};
|
|
7998
7998
|
}
|
|
7999
|
-
const
|
|
7999
|
+
const context2 = {
|
|
8000
8000
|
orgId: flags.orgId || process.env.DRIP_ORG || creds?.context?.orgId,
|
|
8001
8001
|
shopId: flags.shopId || process.env.DRIP_SHOP || creds?.context?.shopId
|
|
8002
8002
|
};
|
|
8003
|
-
warnStoredContextFallback(creds, flags,
|
|
8004
|
-
return
|
|
8003
|
+
warnStoredContextFallback(creds, flags, context2);
|
|
8004
|
+
return context2;
|
|
8005
8005
|
}
|
|
8006
8006
|
function configureInvocationContext() {
|
|
8007
8007
|
const creds = getCredentials();
|
|
8008
8008
|
const credential = defaultCredentialKind(creds);
|
|
8009
|
-
const
|
|
8009
|
+
const context2 = resolveContext(creds, void 0, credential);
|
|
8010
8010
|
setEnvelopeContext({
|
|
8011
8011
|
credential,
|
|
8012
|
-
org_id:
|
|
8013
|
-
shop_id:
|
|
8012
|
+
org_id: context2.orgId ?? null,
|
|
8013
|
+
shop_id: context2.shopId ?? null
|
|
8014
8014
|
});
|
|
8015
8015
|
}
|
|
8016
8016
|
var DRIP_DIR = import_node_path3.default.join(import_node_os4.default.homedir(), ".drip");
|
|
@@ -8059,15 +8059,15 @@ function requireCredentials(fallbackShopId) {
|
|
|
8059
8059
|
EXIT_CODES.auth
|
|
8060
8060
|
);
|
|
8061
8061
|
}
|
|
8062
|
-
const
|
|
8063
|
-
if (!
|
|
8062
|
+
const context2 = resolveContext(creds, void 0, defaultCredentialKind(creds));
|
|
8063
|
+
if (!context2.shopId) context2.shopId = fallbackShopId?.trim() || void 0;
|
|
8064
8064
|
if (defaultCredentialKind(creds) === "user") {
|
|
8065
8065
|
setEnvelopeContext({
|
|
8066
8066
|
credential: "user",
|
|
8067
|
-
org_id:
|
|
8068
|
-
shop_id:
|
|
8067
|
+
org_id: context2.orgId ?? null,
|
|
8068
|
+
shop_id: context2.shopId ?? null
|
|
8069
8069
|
});
|
|
8070
|
-
if (!
|
|
8070
|
+
if (!context2.shopId && !getEnvironmentApiKey()) {
|
|
8071
8071
|
throw new CliError(
|
|
8072
8072
|
'Shop context is required. Run "apex use", or pass --shop <id>.',
|
|
8073
8073
|
EXIT_CODES.validation,
|
|
@@ -8083,22 +8083,22 @@ function requireCredentials(fallbackShopId) {
|
|
|
8083
8083
|
userApiKey: environmentApiKey,
|
|
8084
8084
|
userBaseUrl: creds.baseUrl,
|
|
8085
8085
|
credentialKind: "user",
|
|
8086
|
-
resolvedContext:
|
|
8086
|
+
resolvedContext: context2
|
|
8087
8087
|
}
|
|
8088
8088
|
) : {
|
|
8089
8089
|
...creds,
|
|
8090
8090
|
apiKey: creds.userApiKey,
|
|
8091
8091
|
baseUrl: creds.userBaseUrl ?? creds.baseUrl,
|
|
8092
8092
|
credentialKind: "user",
|
|
8093
|
-
resolvedContext:
|
|
8093
|
+
resolvedContext: context2
|
|
8094
8094
|
};
|
|
8095
8095
|
}
|
|
8096
8096
|
setEnvelopeContext({
|
|
8097
8097
|
credential: "shop",
|
|
8098
|
-
org_id:
|
|
8099
|
-
shop_id:
|
|
8098
|
+
org_id: context2.orgId ?? null,
|
|
8099
|
+
shop_id: context2.shopId ?? null
|
|
8100
8100
|
});
|
|
8101
|
-
return { ...creds, credentialKind: "shop", resolvedContext:
|
|
8101
|
+
return { ...creds, credentialKind: "shop", resolvedContext: context2 };
|
|
8102
8102
|
}
|
|
8103
8103
|
function resolveCheckShop(explicitShopId, fallbackShopId) {
|
|
8104
8104
|
const explicit = explicitShopId?.trim() || null;
|
|
@@ -8157,11 +8157,11 @@ function requireUserCredentials() {
|
|
|
8157
8157
|
);
|
|
8158
8158
|
}
|
|
8159
8159
|
const resolvedCredentials = creds;
|
|
8160
|
-
const
|
|
8160
|
+
const context2 = resolveContext(creds, void 0, "user");
|
|
8161
8161
|
setEnvelopeContext({
|
|
8162
8162
|
credential: "user",
|
|
8163
|
-
org_id:
|
|
8164
|
-
shop_id:
|
|
8163
|
+
org_id: context2.orgId ?? null,
|
|
8164
|
+
shop_id: context2.shopId ?? null
|
|
8165
8165
|
});
|
|
8166
8166
|
return environmentUserKey ? (
|
|
8167
8167
|
// The user-plane request path reads userApiKey/userBaseUrl, so the
|
|
@@ -8172,14 +8172,14 @@ function requireUserCredentials() {
|
|
|
8172
8172
|
userApiKey: environmentUserKey,
|
|
8173
8173
|
userBaseUrl: resolvedCredentials.baseUrl,
|
|
8174
8174
|
credentialKind: "user",
|
|
8175
|
-
resolvedContext:
|
|
8175
|
+
resolvedContext: context2
|
|
8176
8176
|
}
|
|
8177
8177
|
) : {
|
|
8178
8178
|
...resolvedCredentials,
|
|
8179
8179
|
apiKey: resolvedCredentials.userApiKey,
|
|
8180
8180
|
baseUrl: resolvedCredentials.userBaseUrl ?? resolvedCredentials.baseUrl,
|
|
8181
8181
|
credentialKind: "user",
|
|
8182
|
-
resolvedContext:
|
|
8182
|
+
resolvedContext: context2
|
|
8183
8183
|
};
|
|
8184
8184
|
}
|
|
8185
8185
|
var CONFIG_NAMES = [".drip.config.ts", ".drip.config.js", "drip.config.ts", "drip.config.js"];
|
|
@@ -8257,7 +8257,7 @@ function detectSessionId(env2) {
|
|
|
8257
8257
|
const hostId = env2.CLAUDE_CODE_SESSION_ID || env2.CODEX_THREAD_ID;
|
|
8258
8258
|
return hostId ? `h_${(0, import_node_crypto2.createHash)("sha256").update(hostId).digest("hex").slice(0, 32)}` : void 0;
|
|
8259
8259
|
}
|
|
8260
|
-
function beginInvocation(command,
|
|
8260
|
+
function beginInvocation(command, context2 = {}) {
|
|
8261
8261
|
resolvedPath = telemetryCommandPath(command);
|
|
8262
8262
|
helpPath = void 0;
|
|
8263
8263
|
attempted = void 0;
|
|
@@ -8280,8 +8280,8 @@ function beginInvocation(command, context = {}) {
|
|
|
8280
8280
|
json_mode: shouldUseJson(command),
|
|
8281
8281
|
tty: process.stdout.isTTY === true,
|
|
8282
8282
|
credential_kind: process.env.APEX_API_KEY || process.env.DRIP_API_KEY ? "env" : defaultCredentialKind(getCredentials()),
|
|
8283
|
-
org_id:
|
|
8284
|
-
shop_id:
|
|
8283
|
+
org_id: context2.orgId,
|
|
8284
|
+
shop_id: context2.shopId
|
|
8285
8285
|
};
|
|
8286
8286
|
}
|
|
8287
8287
|
var staticRouteSegments = /* @__PURE__ */ new Set([
|
|
@@ -9555,9 +9555,9 @@ function listOrgMembers(creds) {
|
|
|
9555
9555
|
"/api/v1/org/members"
|
|
9556
9556
|
);
|
|
9557
9557
|
}
|
|
9558
|
-
function inviteOrg(creds,
|
|
9558
|
+
function inviteOrg(creds, email2, org_role, default_shop_role) {
|
|
9559
9559
|
return request(userCreds(creds), "POST", "/api/v1/org/invitations", {
|
|
9560
|
-
email,
|
|
9560
|
+
email: email2,
|
|
9561
9561
|
org_role,
|
|
9562
9562
|
default_shop_role
|
|
9563
9563
|
});
|
|
@@ -9589,9 +9589,9 @@ function listTeamMembers(creds, shop) {
|
|
|
9589
9589
|
`/api/v1/team/members?shop_id=${encodeURIComponent(shop)}`
|
|
9590
9590
|
);
|
|
9591
9591
|
}
|
|
9592
|
-
function inviteTeam(creds,
|
|
9592
|
+
function inviteTeam(creds, email2, shop_id, role, permission_overrides) {
|
|
9593
9593
|
return request(userCreds(creds), "POST", "/api/v1/team/invitations", {
|
|
9594
|
-
email,
|
|
9594
|
+
email: email2,
|
|
9595
9595
|
shop_id,
|
|
9596
9596
|
role,
|
|
9597
9597
|
...permission_overrides ? { permission_overrides } : {}
|
|
@@ -9882,6 +9882,21 @@ function putCommerceDefinition(creds, experiment2, definition2, expectedRevision
|
|
|
9882
9882
|
expectedRevision
|
|
9883
9883
|
});
|
|
9884
9884
|
}
|
|
9885
|
+
function notificationCredentials(creds, shopId) {
|
|
9886
|
+
return { ...creds, resolvedContext: { ...creds.resolvedContext, shopId } };
|
|
9887
|
+
}
|
|
9888
|
+
function getReadyEmail(creds, shopId) {
|
|
9889
|
+
return userRequest(notificationCredentials(creds, shopId), "GET", `/api/v1/notifications/ready/email?shop_id=${encodeURIComponent(shopId)}`);
|
|
9890
|
+
}
|
|
9891
|
+
function setReadyEmail(creds, shopId, body6) {
|
|
9892
|
+
return userRequest(notificationCredentials(creds, shopId), "PUT", `/api/v1/notifications/ready/email?shop_id=${encodeURIComponent(shopId)}`, body6);
|
|
9893
|
+
}
|
|
9894
|
+
function getReadySlack(creds, shopId) {
|
|
9895
|
+
return userRequest(notificationCredentials(creds, shopId), "GET", `/api/v1/notifications/ready/slack?shop_id=${encodeURIComponent(shopId)}`);
|
|
9896
|
+
}
|
|
9897
|
+
function setReadySlack(creds, shopId, body6) {
|
|
9898
|
+
return userRequest(notificationCredentials(creds, shopId), "PUT", `/api/v1/notifications/ready/slack?shop_id=${encodeURIComponent(shopId)}`, body6);
|
|
9899
|
+
}
|
|
9885
9900
|
|
|
9886
9901
|
// src/context.ts
|
|
9887
9902
|
var import_node_readline = __toESM(require("node:readline"));
|
|
@@ -10322,21 +10337,21 @@ var whoamiCommand = new Command("whoami").description("Show your identity, crede
|
|
|
10322
10337
|
return;
|
|
10323
10338
|
}
|
|
10324
10339
|
if (defaultCredentialKind(stored) === "shop") {
|
|
10325
|
-
const
|
|
10340
|
+
const context2 = resolveContext(stored, void 0, "shop");
|
|
10326
10341
|
setEnvelopeContext({
|
|
10327
10342
|
credential: "shop",
|
|
10328
|
-
org_id:
|
|
10329
|
-
shop_id:
|
|
10343
|
+
org_id: context2.orgId ?? null,
|
|
10344
|
+
shop_id: context2.shopId ?? null
|
|
10330
10345
|
});
|
|
10331
10346
|
const data2 = {
|
|
10332
10347
|
logged_in: true,
|
|
10333
10348
|
credential: { kind: "shop" },
|
|
10334
|
-
context: { org_id:
|
|
10349
|
+
context: { org_id: context2.orgId ?? null, shop_id: context2.shopId ?? null }
|
|
10335
10350
|
};
|
|
10336
10351
|
output(this, data2, () => {
|
|
10337
10352
|
console.log(
|
|
10338
10353
|
`Credential: shop key (pre-bound server-side)
|
|
10339
|
-
Declared shop: ${
|
|
10354
|
+
Declared shop: ${context2.shopId ?? "none \u2014 set DRIP_SHOP to assert one"}
|
|
10340
10355
|
For account-wide visibility run "apex login" and "apex whoami" without APEX_API_KEY (or DRIP_API_KEY).`
|
|
10341
10356
|
);
|
|
10342
10357
|
});
|
|
@@ -16181,8 +16196,8 @@ function printFilesystemPlan(plan2) {
|
|
|
16181
16196
|
if ((plan2.blockingIssues?.length ?? 0) > 0) {
|
|
16182
16197
|
console.log(source_default.red("\nBlocking issues"));
|
|
16183
16198
|
(plan2.blockingIssues ?? []).forEach((issue) => {
|
|
16184
|
-
const
|
|
16185
|
-
console.log(` - [${issue.code}] ${issue.message}${
|
|
16199
|
+
const context2 = [issue.path, issue.variation ? `variation: ${issue.variation}` : null].filter(Boolean).join(", ");
|
|
16200
|
+
console.log(` - [${issue.code}] ${issue.message}${context2 ? ` (${context2})` : ""}`);
|
|
16186
16201
|
});
|
|
16187
16202
|
}
|
|
16188
16203
|
if ((plan2.commerceLaunchPreview?.length ?? 0) > 0) {
|
|
@@ -17709,8 +17724,8 @@ var experimentStatus = new Command("status").description(
|
|
|
17709
17724
|
"Versions are unavailable without a pulled folder. Pull the experiment to compare local files."
|
|
17710
17725
|
);
|
|
17711
17726
|
if (creds.credentialKind === "user") {
|
|
17712
|
-
const
|
|
17713
|
-
const owner =
|
|
17727
|
+
const context2 = await getAccountContext(creds);
|
|
17728
|
+
const owner = context2.data?.data?.orgs?.find(
|
|
17714
17729
|
(item) => item.shops?.some((item2) => item2.id === shopId)
|
|
17715
17730
|
);
|
|
17716
17731
|
if (owner) {
|
|
@@ -19096,6 +19111,10 @@ var import_node_path10 = __toESM(require("node:path"));
|
|
|
19096
19111
|
var import_node_url2 = require("node:url");
|
|
19097
19112
|
function toApiBody(exp) {
|
|
19098
19113
|
return {
|
|
19114
|
+
...exp.commerce !== void 0 ? {
|
|
19115
|
+
expectedCommerceRevision: exp.commerce.revision,
|
|
19116
|
+
...exp.commerce.definition !== void 0 ? { definition: exp.commerce.definition } : {}
|
|
19117
|
+
} : {},
|
|
19099
19118
|
name: exp.name,
|
|
19100
19119
|
description: exp.description ?? null,
|
|
19101
19120
|
traffic_allocation: exp.trafficAllocation === void 0 ? 1 : exp.trafficAllocation,
|
|
@@ -19106,7 +19125,8 @@ function toApiBody(exp) {
|
|
|
19106
19125
|
name: v.name ?? v.id ?? "Unnamed",
|
|
19107
19126
|
weight: v.weight,
|
|
19108
19127
|
is_control: v.isControl ?? v.is_control ?? false,
|
|
19109
|
-
mutations: v.mutations
|
|
19128
|
+
...v.mutations !== void 0 ? { mutations: v.mutations } : {},
|
|
19129
|
+
...(v.artifactHash ?? v.artifact_hash) !== void 0 ? { expectedArtifactHash: v.artifactHash ?? v.artifact_hash } : {},
|
|
19110
19130
|
custom_css: v.css ?? v.custom_css ?? null,
|
|
19111
19131
|
custom_js: v.js ?? v.custom_js ?? null,
|
|
19112
19132
|
reset_js: v.resetJs ?? v.reset_js ?? null,
|
|
@@ -19121,7 +19141,7 @@ async function rewriteExperimentAssets(exp, rewrite, baseDir) {
|
|
|
19121
19141
|
variations: await Promise.all(
|
|
19122
19142
|
exp.variations.map(async (variation) => ({
|
|
19123
19143
|
...variation,
|
|
19124
|
-
mutations: await rewrite.rewriteUnknown(variation.mutations
|
|
19144
|
+
...variation.mutations !== void 0 ? { mutations: await rewrite.rewriteUnknown(variation.mutations, baseDir) } : {},
|
|
19125
19145
|
css: variation.css === void 0 ? void 0 : await rewrite.rewrite(variation.css, baseDir),
|
|
19126
19146
|
custom_css: variation.custom_css === void 0 ? void 0 : await rewrite.rewrite(variation.custom_css, baseDir),
|
|
19127
19147
|
js: variation.js === void 0 ? void 0 : await rewrite.rewrite(variation.js, baseDir),
|
|
@@ -19310,7 +19330,9 @@ var pushCommand = new Command("push").description("Push experiment definitions f
|
|
|
19310
19330
|
slug: exp.slug,
|
|
19311
19331
|
status: "failed",
|
|
19312
19332
|
error: res.error,
|
|
19313
|
-
statusCode: res.status
|
|
19333
|
+
statusCode: res.status,
|
|
19334
|
+
code: typeof res.errorDetails?.code === "string" ? res.errorDetails.code : void 0,
|
|
19335
|
+
details: res.errorDetails
|
|
19314
19336
|
});
|
|
19315
19337
|
continue;
|
|
19316
19338
|
}
|
|
@@ -19813,6 +19835,8 @@ function sessionFromWorkspace(input) {
|
|
|
19813
19835
|
variationId: input.variationId,
|
|
19814
19836
|
workspaceId: input.workspace.workspace.id,
|
|
19815
19837
|
workspaceRevisionId: input.workspace.workspace.currentRevisionId,
|
|
19838
|
+
commerceRevision: input.workspace.commerceRevision,
|
|
19839
|
+
artifactHash: input.workspace.artifactHash,
|
|
19816
19840
|
checkedOutAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19817
19841
|
files: Object.fromEntries(
|
|
19818
19842
|
input.workspace.files.map((file) => [
|
|
@@ -19902,6 +19926,7 @@ async function syncChangedFiles(folder, session) {
|
|
|
19902
19926
|
file.path,
|
|
19903
19927
|
{
|
|
19904
19928
|
content: file.content,
|
|
19929
|
+
expected_version: session.files[file.path]?.sha256,
|
|
19905
19930
|
source: "operator",
|
|
19906
19931
|
title: `CLI sync ${file.path}`
|
|
19907
19932
|
}
|
|
@@ -19912,6 +19937,13 @@ async function syncChangedFiles(folder, session) {
|
|
|
19912
19937
|
classifyStatus(res.status),
|
|
19913
19938
|
res.errorDetails
|
|
19914
19939
|
);
|
|
19940
|
+
const saved = res.data;
|
|
19941
|
+
if (saved?.revision?.id) session.workspaceRevisionId = saved.revision.id;
|
|
19942
|
+
session.files[file.path] = {
|
|
19943
|
+
sha256: saved?.file?.sha256 ?? file.sha256,
|
|
19944
|
+
sizeBytes: file.sizeBytes
|
|
19945
|
+
};
|
|
19946
|
+
writeSession(folder, session);
|
|
19915
19947
|
}
|
|
19916
19948
|
return { localFiles, changed };
|
|
19917
19949
|
}
|
|
@@ -19920,12 +19952,15 @@ async function runPublish(folderArg, opts = {}) {
|
|
|
19920
19952
|
const session = readSession(folder);
|
|
19921
19953
|
const { localFiles, changed } = await syncChangedFiles(folder, session);
|
|
19922
19954
|
const creds = requireCredentials();
|
|
19923
|
-
const build = await buildOperatorWorkspace(
|
|
19924
|
-
|
|
19925
|
-
session.
|
|
19926
|
-
session.
|
|
19927
|
-
|
|
19928
|
-
|
|
19955
|
+
const build = await buildOperatorWorkspace(creds, session.experimentId, session.variationId, {
|
|
19956
|
+
...opts.allowGoalWrites ? { allowGoalWrites: true } : {},
|
|
19957
|
+
expectedCommerceRevision: session.commerceRevision,
|
|
19958
|
+
expectedArtifactHash: session.artifactHash,
|
|
19959
|
+
expectedWorkspaceRevision: session.workspaceRevisionId ?? void 0,
|
|
19960
|
+
expectedFileVersions: Object.fromEntries(
|
|
19961
|
+
Object.entries(session.files).filter(([path22]) => path22 !== "brand.md").map(([path22, file]) => [path22, file.sha256])
|
|
19962
|
+
)
|
|
19963
|
+
});
|
|
19929
19964
|
if (!build.ok || !build.data?.ok) {
|
|
19930
19965
|
const issueDetails = formatWorkspaceBlockingIssues(build.data);
|
|
19931
19966
|
throw new CliError(
|
|
@@ -21862,13 +21897,13 @@ async function identifyRuntimeSettingsShop(command, requested) {
|
|
|
21862
21897
|
if (credentials.credentialKind === "user") {
|
|
21863
21898
|
let shopId = requested;
|
|
21864
21899
|
if (!uuidPattern.test(requested)) {
|
|
21865
|
-
const
|
|
21900
|
+
const context2 = await getAccountContext({
|
|
21866
21901
|
...credentials,
|
|
21867
21902
|
resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
|
|
21868
21903
|
});
|
|
21869
|
-
if (!
|
|
21870
|
-
fail(command,
|
|
21871
|
-
const match2 =
|
|
21904
|
+
if (!context2.ok || !context2.data)
|
|
21905
|
+
fail(command, context2.error ?? "Shop discovery failed", classifyStatus(context2.status));
|
|
21906
|
+
const match2 = context2.data.data.orgs.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requested.toLowerCase());
|
|
21872
21907
|
if (!match2)
|
|
21873
21908
|
fail(command, "--shop domain is not available to this user", EXIT_CODES.validation);
|
|
21874
21909
|
shopId = match2.id;
|
|
@@ -22484,17 +22519,17 @@ async function identify(command, requestedShop) {
|
|
|
22484
22519
|
...credentials,
|
|
22485
22520
|
resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
|
|
22486
22521
|
};
|
|
22487
|
-
const
|
|
22488
|
-
if (!
|
|
22522
|
+
const context2 = await getAccountContext(discoveryCredentials);
|
|
22523
|
+
if (!context2.ok) {
|
|
22489
22524
|
fail(
|
|
22490
22525
|
command,
|
|
22491
|
-
`${
|
|
22492
|
-
classifyStatus(
|
|
22493
|
-
|
|
22494
|
-
apiFailureOf(
|
|
22526
|
+
`${context2.status === 401 || context2.status === 403 ? "TRIGGERS_AUTH" : "TRIGGERS_CONFIG_ABSENT"}: ${context2.error ?? "account context unavailable"}`,
|
|
22527
|
+
classifyStatus(context2.status),
|
|
22528
|
+
context2.errorDetails,
|
|
22529
|
+
apiFailureOf(context2)
|
|
22495
22530
|
);
|
|
22496
22531
|
}
|
|
22497
|
-
const match2 =
|
|
22532
|
+
const match2 = context2.data?.data?.orgs?.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requestedShop.toLowerCase());
|
|
22498
22533
|
if (!match2) {
|
|
22499
22534
|
fail(
|
|
22500
22535
|
command,
|
|
@@ -22829,9 +22864,9 @@ async function findLandingDomain(command, host) {
|
|
|
22829
22864
|
return row;
|
|
22830
22865
|
}
|
|
22831
22866
|
async function verifyOnce(command, id) {
|
|
22832
|
-
const
|
|
22833
|
-
if (!
|
|
22834
|
-
return
|
|
22867
|
+
const checked2 = await verifyLandingPageDomain(requireCredentials(), id);
|
|
22868
|
+
if (!checked2.ok || !checked2.data) domainFail(command, checked2, "Failed to verify the domain");
|
|
22869
|
+
return checked2.data.data;
|
|
22835
22870
|
}
|
|
22836
22871
|
var claim = new Command("claim").description("Claim a client hostname for landing pages (CNAME to the Apex edge)").argument("<host>", "hostname the client controls, for example offers.example.com").option("--wait", "check DNS and the certificate every 10 seconds for up to two minutes").action(async function action56(host, opts) {
|
|
22837
22872
|
const result = await createLandingPageDomain(requireCredentials(), {
|
|
@@ -29089,13 +29124,28 @@ var settingsGet = new Command("settings").description("Show platform operator se
|
|
|
29089
29124
|
res.data,
|
|
29090
29125
|
() => printTable(
|
|
29091
29126
|
["SETTING", "VALUE"],
|
|
29092
|
-
Object.entries(res.data.data).map(([k, v]) => [
|
|
29127
|
+
Object.entries(res.data.data).map(([k, v]) => [
|
|
29128
|
+
k,
|
|
29129
|
+
typeof v === "boolean" ? v ? "on" : "off" : String(v)
|
|
29130
|
+
])
|
|
29093
29131
|
)
|
|
29094
29132
|
);
|
|
29095
29133
|
});
|
|
29096
|
-
var set5 = new Command("set").description("Set platform kill switches").option("--guardian <on|off>").option("--srm-check <on|off>").option("--confirm", "Confirm immediate production impact").action(async function(opts) {
|
|
29097
|
-
if (!opts.guardian && !opts.srmCheck
|
|
29098
|
-
|
|
29134
|
+
var set5 = new Command("set").description("Set platform kill switches").option("--guardian <on|off>").option("--srm-check <on|off>").option("--undo-completion-window-days <days>", "Integer 0..365; 0 disables undo").option("--expected-version <version>", "Policy version returned by admin settings").option("--reason <text>", "Audit reason for a policy change").option("--confirm", "Confirm immediate production impact").action(async function(opts) {
|
|
29135
|
+
if (!opts.guardian && !opts.srmCheck && opts.undoCompletionWindowDays === void 0)
|
|
29136
|
+
fail(this, "Provide --guardian, --srm-check or --undo-completion-window-days", 3);
|
|
29137
|
+
const body6 = {};
|
|
29138
|
+
if (opts.undoCompletionWindowDays !== void 0) {
|
|
29139
|
+
const days = Number(opts.undoCompletionWindowDays);
|
|
29140
|
+
if (!/^(0|[1-9]\d*)$/.test(opts.undoCompletionWindowDays) || days > 365)
|
|
29141
|
+
fail(this, "--undo-completion-window-days must be an integer from 0 to 365", 3);
|
|
29142
|
+
if (!opts.expectedVersion || !opts.reason?.trim() || opts.reason.trim().length > 500)
|
|
29143
|
+
fail(this, "Policy changes require --expected-version and --reason (1..500 characters)", 3);
|
|
29144
|
+
body6.experiment_undo_completion_window_days = days;
|
|
29145
|
+
body6.expected_version = opts.expectedVersion;
|
|
29146
|
+
body6.reason = opts.reason.trim();
|
|
29147
|
+
} else if (opts.expectedVersion || opts.reason)
|
|
29148
|
+
fail(this, "--expected-version and --reason require --undo-completion-window-days", 3);
|
|
29099
29149
|
try {
|
|
29100
29150
|
if (opts.guardian) body6.guardian_enabled = switchValue(opts.guardian);
|
|
29101
29151
|
if (opts.srmCheck) body6.srm_check_enabled = switchValue(opts.srmCheck);
|
|
@@ -29105,7 +29155,10 @@ var set5 = new Command("set").description("Set platform kill switches").option("
|
|
|
29105
29155
|
if (!opts.confirm) {
|
|
29106
29156
|
const switches = [
|
|
29107
29157
|
opts.guardian ? `--guardian ${opts.guardian}` : "",
|
|
29108
|
-
opts.srmCheck ? `--srm-check ${opts.srmCheck}` : ""
|
|
29158
|
+
opts.srmCheck ? `--srm-check ${opts.srmCheck}` : "",
|
|
29159
|
+
opts.undoCompletionWindowDays !== void 0 ? `--undo-completion-window-days ${opts.undoCompletionWindowDays}` : "",
|
|
29160
|
+
opts.expectedVersion ? `--expected-version '${opts.expectedVersion.replaceAll("'", `'"'"'`)}'` : "",
|
|
29161
|
+
opts.reason ? `--reason '${opts.reason.replaceAll("'", `'"'"'`)}'` : ""
|
|
29109
29162
|
].filter(Boolean).join(" ");
|
|
29110
29163
|
confirmationRequired(
|
|
29111
29164
|
this,
|
|
@@ -29619,11 +29672,11 @@ function adminClientLinkRequest(opts) {
|
|
|
29619
29672
|
throw new Error("--consultant must be a UUID");
|
|
29620
29673
|
const teammates = (opts.teammate ?? []).map((value2) => {
|
|
29621
29674
|
const split = value2.lastIndexOf(":");
|
|
29622
|
-
const
|
|
29675
|
+
const email2 = value2.slice(0, split).trim().toLowerCase();
|
|
29623
29676
|
const role = value2.slice(split + 1);
|
|
29624
|
-
if (split < 1 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(
|
|
29677
|
+
if (split < 1 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email2) || !["editor", "client_viewer"].includes(role))
|
|
29625
29678
|
throw new Error("--teammate must be email:editor or email:client_viewer");
|
|
29626
|
-
return { email, role };
|
|
29679
|
+
return { email: email2, role };
|
|
29627
29680
|
});
|
|
29628
29681
|
const discountMonths = opts.discountMonths === "forever" ? null : integerFlag("--discount-months", opts.discountMonths, 1);
|
|
29629
29682
|
return {
|
|
@@ -29677,6 +29730,117 @@ var createClient = new Command("create").description("Create a personal managed-
|
|
|
29677
29730
|
var clients = new Command("clients").description("Create managed-client links").addCommand(createClient);
|
|
29678
29731
|
var adminCommand = new Command("admin").description("Operate allowlisted platform controls").addCommand(settingsGet).addCommand(quarantine).addCommand(shops).addCommand(clients).addCommand(customers);
|
|
29679
29732
|
|
|
29733
|
+
// src/commands/notifications.ts
|
|
29734
|
+
var notificationsCommand = new Command("notifications").description(
|
|
29735
|
+
"Manage notification settings"
|
|
29736
|
+
);
|
|
29737
|
+
var ready = notificationsCommand.command("ready").description("Ready to launch email recipients and Slack event toggle");
|
|
29738
|
+
function context(command, explicit) {
|
|
29739
|
+
const creds = requireUserCredentials();
|
|
29740
|
+
const shopId = explicit ?? creds.resolvedContext?.shopId;
|
|
29741
|
+
if (!shopId) fail(command, "Choose a shop with --shop <id> or apex use <org>/<shop>.", 3);
|
|
29742
|
+
return { creds, shopId };
|
|
29743
|
+
}
|
|
29744
|
+
function checked(command, response) {
|
|
29745
|
+
if (!response.ok)
|
|
29746
|
+
fail(
|
|
29747
|
+
command,
|
|
29748
|
+
response.status === 409 ? "Notification settings changed. Retry the command." : response.error ?? "Could not read or save notification settings.",
|
|
29749
|
+
classifyStatus(response.status),
|
|
29750
|
+
response.errorDetails,
|
|
29751
|
+
apiFailureOf(response)
|
|
29752
|
+
);
|
|
29753
|
+
if (!response.data) fail(command, "The API returned no notification settings.", 1);
|
|
29754
|
+
return response.data;
|
|
29755
|
+
}
|
|
29756
|
+
function print(command, data, lines) {
|
|
29757
|
+
output(command, data, () => {
|
|
29758
|
+
for (const line of lines) console.log(line);
|
|
29759
|
+
});
|
|
29760
|
+
}
|
|
29761
|
+
function emailLine(members, ids) {
|
|
29762
|
+
if (ids.length === 0) return "Email: off (no recipients)";
|
|
29763
|
+
const names = ids.map((id) => members.find((member2) => member2.user_id === id)?.email ?? id);
|
|
29764
|
+
return `Email: ${names.length} recipient${names.length === 1 ? "" : "s"}: ${names.join(", ")}`;
|
|
29765
|
+
}
|
|
29766
|
+
function slackLine(settings2) {
|
|
29767
|
+
const destination = settings2.destination;
|
|
29768
|
+
if (!destination) return "Slack: not connected";
|
|
29769
|
+
const paused = !destination.active || destination.disabled_at ? " (delivery paused)" : "";
|
|
29770
|
+
return `Slack: ${destination.ready_to_launch ? "on" : "off"}${paused}`;
|
|
29771
|
+
}
|
|
29772
|
+
ready.command("get").option("--shop <id>").action(async function(options) {
|
|
29773
|
+
const { creds, shopId } = context(this, options.shop);
|
|
29774
|
+
const email2 = checked(this, await getReadyEmail(creds, shopId)).data;
|
|
29775
|
+
assertResponseShop(email2.shop_id, shopId, creds.credentialKind);
|
|
29776
|
+
const slack2 = checked(this, await getReadySlack(creds, shopId)).data;
|
|
29777
|
+
assertResponseShop(slack2.shop_id, shopId, creds.credentialKind);
|
|
29778
|
+
print(this, { email: email2, slack: slack2 }, [
|
|
29779
|
+
emailLine(
|
|
29780
|
+
email2.members,
|
|
29781
|
+
email2.members.filter((member2) => member2.selected).map((member2) => member2.user_id)
|
|
29782
|
+
),
|
|
29783
|
+
slackLine(slack2)
|
|
29784
|
+
]);
|
|
29785
|
+
});
|
|
29786
|
+
var email = ready.command("email").description("Choose current shop members to receive ready emails; off clears recipients");
|
|
29787
|
+
for (const operation of ["set", "add", "remove", "off"]) {
|
|
29788
|
+
const command = email.command(operation).option("--shop <id>");
|
|
29789
|
+
if (operation !== "off")
|
|
29790
|
+
command.requiredOption("--recipients <emails>", "Comma-separated shop member emails");
|
|
29791
|
+
command.action(async function(options) {
|
|
29792
|
+
const { creds, shopId } = context(this, options.shop);
|
|
29793
|
+
const current = checked(this, await getReadyEmail(creds, shopId)).data;
|
|
29794
|
+
assertResponseShop(current.shop_id, shopId, creds.credentialKind);
|
|
29795
|
+
const ids = [];
|
|
29796
|
+
if (operation !== "off") {
|
|
29797
|
+
const emails = options.recipients.split(",").map((value2) => value2.trim().toLowerCase());
|
|
29798
|
+
if (emails.some((value2) => !value2))
|
|
29799
|
+
fail(this, "Provide a comma-separated list of member emails without empty entries.", 3);
|
|
29800
|
+
for (const address of new Set(emails)) {
|
|
29801
|
+
const matches = current.members.filter((member2) => member2.email?.toLowerCase() === address);
|
|
29802
|
+
if (matches.length !== 1)
|
|
29803
|
+
fail(this, `Unknown or ambiguous shop member email: ${address}`, 3);
|
|
29804
|
+
ids.push(matches[0].user_id);
|
|
29805
|
+
}
|
|
29806
|
+
}
|
|
29807
|
+
const selected2 = current.members.filter((member2) => member2.selected).map((member2) => member2.user_id);
|
|
29808
|
+
const recipient_user_ids = operation === "off" ? [] : operation === "set" ? ids : operation === "add" ? [.../* @__PURE__ */ new Set([...selected2, ...ids])] : selected2.filter((id) => !ids.includes(id));
|
|
29809
|
+
const saved = checked(
|
|
29810
|
+
this,
|
|
29811
|
+
await setReadyEmail(creds, shopId, { version: current.version, recipient_user_ids })
|
|
29812
|
+
);
|
|
29813
|
+
assertResponseShop(saved.data.shop_id, shopId, creds.credentialKind);
|
|
29814
|
+
print(this, saved, [emailLine(current.members, saved.data.recipient_user_ids)]);
|
|
29815
|
+
});
|
|
29816
|
+
}
|
|
29817
|
+
var slack = ready.command("slack").description(
|
|
29818
|
+
"Toggle the ready event on an existing Slack destination; other events and pause state stay as configured"
|
|
29819
|
+
);
|
|
29820
|
+
for (const enabled of [true, false]) {
|
|
29821
|
+
slack.command(enabled ? "on" : "off").option("--shop <id>").action(async function(options) {
|
|
29822
|
+
const { creds, shopId } = context(this, options.shop);
|
|
29823
|
+
const current = checked(this, await getReadySlack(creds, shopId)).data;
|
|
29824
|
+
assertResponseShop(current.shop_id, shopId, creds.credentialKind);
|
|
29825
|
+
if (!current.destination || !current.version)
|
|
29826
|
+
fail(this, "Connect a Slack destination in Settings > Integrations first.", 3);
|
|
29827
|
+
const result = checked(
|
|
29828
|
+
this,
|
|
29829
|
+
await setReadySlack(creds, shopId, {
|
|
29830
|
+
version: current.version,
|
|
29831
|
+
ready_to_launch: enabled
|
|
29832
|
+
})
|
|
29833
|
+
);
|
|
29834
|
+
assertResponseShop(result.data.shop_id, shopId, creds.credentialKind);
|
|
29835
|
+
if (enabled && result.data.destination && (!result.data.destination.active || result.data.destination.disabled_at)) {
|
|
29836
|
+
process.stderr.write(
|
|
29837
|
+
"Ready to launch is selected, but Slack delivery is paused. Resume the destination in Settings > Integrations.\n"
|
|
29838
|
+
);
|
|
29839
|
+
}
|
|
29840
|
+
print(this, result, [slackLine(result.data)]);
|
|
29841
|
+
});
|
|
29842
|
+
}
|
|
29843
|
+
|
|
29680
29844
|
// src/commands/membership.ts
|
|
29681
29845
|
init_source();
|
|
29682
29846
|
async function run2(c, p, label) {
|
|
@@ -29751,7 +29915,7 @@ orgCommand.command("invite").argument("<email>").option("--role <role>", "Alias
|
|
|
29751
29915
|
"--default-shop-role <role>",
|
|
29752
29916
|
"Default shop role for every brand in the workspace (workspace defaults are internal roles)"
|
|
29753
29917
|
).choices(["editor"]).default("editor")
|
|
29754
|
-
).action(async function(
|
|
29918
|
+
).action(async function(email2, o) {
|
|
29755
29919
|
if (o.role !== void 0) {
|
|
29756
29920
|
process.stderr.write(
|
|
29757
29921
|
"Warning: --role is deprecated for apex org invite; use --org-role instead. --role will be removed in v0.7.0.\n"
|
|
@@ -29764,7 +29928,7 @@ orgCommand.command("invite").argument("<email>").option("--role <role>", "Alias
|
|
|
29764
29928
|
}
|
|
29765
29929
|
await run2(
|
|
29766
29930
|
this,
|
|
29767
|
-
inviteOrg(requireUserCredentials(),
|
|
29931
|
+
inviteOrg(requireUserCredentials(), email2, o.role ?? o.orgRole, o.defaultShopRole),
|
|
29768
29932
|
"invite member"
|
|
29769
29933
|
);
|
|
29770
29934
|
});
|
|
@@ -29836,13 +30000,13 @@ teamCommand.command("invite").argument("<email>").option("--shop <id>", teamShop
|
|
|
29836
30000
|
).option(
|
|
29837
30001
|
"--design-approver",
|
|
29838
30002
|
"Also let them sign off designs once they accept (a client_viewer with this is a Client approver). Needs store owner or admin rights."
|
|
29839
|
-
).action(async function(
|
|
30003
|
+
).action(async function(email2, o) {
|
|
29840
30004
|
o.shop = teamShop(this, o.shop);
|
|
29841
30005
|
await run2(
|
|
29842
30006
|
this,
|
|
29843
30007
|
inviteTeam(
|
|
29844
30008
|
requireUserCredentials(),
|
|
29845
|
-
|
|
30009
|
+
email2,
|
|
29846
30010
|
o.shop,
|
|
29847
30011
|
o.role,
|
|
29848
30012
|
o.designApprover ? { "approvals:design": true } : void 0
|
|
@@ -31532,6 +31696,7 @@ program2.addCommand(skillsCommand);
|
|
|
31532
31696
|
program2.addCommand(adminCommand, { hidden: true });
|
|
31533
31697
|
program2.addCommand(orgCommand);
|
|
31534
31698
|
program2.addCommand(teamCommand);
|
|
31699
|
+
program2.addCommand(notificationsCommand);
|
|
31535
31700
|
program2.addCommand(upgradeCommand);
|
|
31536
31701
|
addJsonOption(program2);
|
|
31537
31702
|
registerTelemetryProgram(program2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drip-apex/apex-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Apex by DRIP CLI: build, preview and run A/B tests on your online shop from the terminal or an AI coding agent (Claude Code, Codex, Cursor).",
|
|
6
6
|
"keywords": [
|