@shipeasy/openapi 1.4.0 → 1.6.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.
Files changed (63) hide show
  1. package/dist/index.d.ts +24 -0
  2. package/dist/index.js +1924 -3
  3. package/dist/node-context.d.ts +37 -0
  4. package/dist/node-context.js +56 -0
  5. package/dist/operations/attributes.d.ts +33 -0
  6. package/dist/operations/attributes.js +16 -0
  7. package/dist/operations/cli-adapter.d.ts +35 -0
  8. package/dist/operations/cli-adapter.js +91 -0
  9. package/dist/operations/coerce.d.ts +32 -0
  10. package/dist/operations/coerce.js +53 -0
  11. package/dist/operations/configs.d.ts +28 -0
  12. package/dist/operations/configs.js +119 -0
  13. package/dist/operations/docs.d.ts +37 -0
  14. package/dist/operations/docs.js +39 -0
  15. package/dist/operations/events.d.ts +28 -0
  16. package/dist/operations/events.js +121 -0
  17. package/dist/operations/experiments.d.ts +28 -0
  18. package/dist/operations/experiments.js +280 -0
  19. package/dist/operations/gates.d.ts +28 -0
  20. package/dist/operations/gates.js +132 -0
  21. package/dist/operations/i18n.d.ts +28 -0
  22. package/dist/operations/i18n.js +121 -0
  23. package/dist/operations/index.d.ts +63 -0
  24. package/dist/operations/index.js +1751 -0
  25. package/dist/operations/killswitches.d.ts +28 -0
  26. package/dist/operations/killswitches.js +126 -0
  27. package/dist/operations/mcp-adapter.d.ts +35 -0
  28. package/dist/operations/mcp-adapter.js +94 -0
  29. package/dist/operations/metrics.d.ts +30 -0
  30. package/dist/operations/metrics.js +114 -0
  31. package/dist/operations/ops.d.ts +28 -0
  32. package/dist/operations/ops.js +313 -0
  33. package/dist/operations/projects.d.ts +28 -0
  34. package/dist/operations/projects.js +36 -0
  35. package/dist/operations/sdk-docs.d.ts +28 -0
  36. package/dist/operations/sdk-docs.js +160 -0
  37. package/dist/operations/types.d.ts +138 -0
  38. package/dist/operations/types.js +9 -0
  39. package/dist/operations/universes.d.ts +28 -0
  40. package/dist/operations/universes.js +93 -0
  41. package/dist/operations/util.d.ts +33 -0
  42. package/dist/operations/util.js +9 -0
  43. package/dist/resources/alert-rules.d.ts +3 -1
  44. package/dist/resources/alert-rules.js +11 -2
  45. package/dist/resources/attributes.d.ts +19 -0
  46. package/dist/resources/attributes.js +10 -0
  47. package/dist/resources/events.d.ts +61 -0
  48. package/dist/resources/events.js +42 -0
  49. package/dist/resources/i18n.d.ts +84 -0
  50. package/dist/resources/i18n.js +60 -0
  51. package/dist/resources/index.d.ts +22 -4
  52. package/dist/resources/index.js +184 -3
  53. package/dist/resources/metrics.d.ts +54 -0
  54. package/dist/resources/metrics.js +40 -0
  55. package/dist/resources/ops.d.ts +88 -0
  56. package/dist/resources/ops.js +36 -0
  57. package/dist/resources/projects.d.ts +54 -0
  58. package/dist/resources/projects.js +14 -0
  59. package/dist/schemas/alert-rules.d.ts +23 -1
  60. package/dist/schemas/alert-rules.js +13 -3
  61. package/dist/schemas/index.d.ts +1 -1
  62. package/dist/schemas/index.js +12 -2
  63. package/package.json +10 -1
@@ -0,0 +1,121 @@
1
+ // src/operations/util.ts
2
+ var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
+ var bool = (i, k) => i[k] === void 0 ? void 0 : i[k];
4
+
5
+ // src/operations/events.ts
6
+ var GROUP = ["events"];
7
+ var PROPS_DESC = `Event properties as a JSON array, e.g. '[{"name":"amount","type":"number","required":true}]'. type is one of string|number|boolean. Replaces the full set on update/approve.`;
8
+ function props(i) {
9
+ return i.props === void 0 ? void 0 : i.props;
10
+ }
11
+ var eventOperations = [
12
+ {
13
+ group: GROUP,
14
+ name: "list",
15
+ mutates: false,
16
+ summary: "List catalogued events",
17
+ description: "List every catalogued event (including pending auto-discovered names). Pass `pending` to show only the unapproved queue.",
18
+ params: [{ name: "pending", type: "boolean", description: "Show only pending (unapproved) events." }],
19
+ examples: [
20
+ { run: "shipeasy metrics events list" },
21
+ { note: "Review the auto-discovered queue", run: "shipeasy metrics events list --pending true" }
22
+ ],
23
+ run: async (client, i) => {
24
+ const rows = await client.events.list();
25
+ return bool(i, "pending") ? rows.filter((e) => e.pending === 1) : rows;
26
+ }
27
+ },
28
+ {
29
+ group: GROUP,
30
+ name: "get",
31
+ mutates: false,
32
+ summary: "Show one catalogued event",
33
+ description: "Resolve an event by id, id-prefix, or name and return its full detail.",
34
+ params: [{ name: "event", type: "string", description: "Event id or name.", required: true, positional: true }],
35
+ examples: [{ run: "shipeasy metrics events get checkout_completed" }],
36
+ run: (client, i) => client.events.resolve(i.event)
37
+ },
38
+ {
39
+ group: GROUP,
40
+ name: "create",
41
+ mutates: true,
42
+ summary: "Register a new event",
43
+ description: "Register a new event name and (optionally) its typed properties.",
44
+ params: [
45
+ { name: "name", type: "string", description: "Event name.", required: true, positional: true },
46
+ { name: "folder", type: "string", description: "Folder to file it under." },
47
+ { name: "description", type: "string", description: "Human-readable description." },
48
+ { name: "props", type: "json", description: PROPS_DESC }
49
+ ],
50
+ examples: [
51
+ { run: "shipeasy metrics events create checkout_completed" },
52
+ { note: "With a typed, required property", run: `shipeasy metrics events create purchase --props '[{"name":"amount","type":"number","required":true}]'` }
53
+ ],
54
+ run: (client, i) => client.events.create({
55
+ name: i.name,
56
+ folder: str(i, "folder") ?? null,
57
+ ...i.description ? { description: i.description } : {},
58
+ properties: props(i) ?? []
59
+ })
60
+ },
61
+ {
62
+ group: GROUP,
63
+ name: "update",
64
+ mutates: true,
65
+ summary: "Update an event",
66
+ description: "Update an event's folder, description, or properties (props replace the full set).",
67
+ params: [
68
+ { name: "event", type: "string", description: "Event id.", required: true, positional: true },
69
+ { name: "folder", type: "string", description: "New folder." },
70
+ { name: "description", type: "string", description: "New description." },
71
+ { name: "props", type: "json", description: PROPS_DESC }
72
+ ],
73
+ examples: [{ run: "shipeasy metrics events update evt_3f9a --folder checkout" }],
74
+ run: (client, i) => {
75
+ const patch = {};
76
+ if (i.folder !== void 0) patch.folder = i.folder;
77
+ if (i.description !== void 0) patch.description = i.description;
78
+ const p = props(i);
79
+ if (p) patch.properties = p;
80
+ return client.events.update(i.event, patch);
81
+ }
82
+ },
83
+ {
84
+ group: GROUP,
85
+ name: "approve",
86
+ mutates: true,
87
+ summary: "Approve a pending event",
88
+ description: "Promote a pending (auto-discovered) event so metrics can query it. Optionally declare its folder/props on approval.",
89
+ params: [
90
+ { name: "event", type: "string", description: "Event id.", required: true, positional: true },
91
+ { name: "folder", type: "string", description: "Folder to file it under." },
92
+ { name: "description", type: "string", description: "Description." },
93
+ { name: "props", type: "json", description: PROPS_DESC }
94
+ ],
95
+ examples: [{ note: "Promote an auto-discovered event", run: "shipeasy metrics events approve evt_3f9a" }],
96
+ run: (client, i) => {
97
+ const body = {};
98
+ if (i.folder !== void 0) body.folder = i.folder;
99
+ if (i.description !== void 0) body.description = i.description;
100
+ const p = props(i);
101
+ if (p) body.properties = p;
102
+ return client.events.approve(i.event, body);
103
+ }
104
+ },
105
+ {
106
+ group: GROUP,
107
+ name: "archive",
108
+ mutates: true,
109
+ summary: "Archive an event",
110
+ description: "Archive (remove) an event from the catalog by id.",
111
+ params: [{ name: "event", type: "string", description: "Event id.", required: true, positional: true }],
112
+ examples: [{ run: "shipeasy metrics events archive evt_3f9a" }],
113
+ run: async (client, i) => {
114
+ await client.events.delete(i.event);
115
+ return { ok: true, archived: i.event };
116
+ }
117
+ }
118
+ ];
119
+ export {
120
+ eventOperations
121
+ };
@@ -0,0 +1,28 @@
1
+ import { Operation } from './types.js';
2
+ import '../resources/index.js';
3
+ import 'zod/v4/core';
4
+ import 'zod';
5
+ import '../resources/attributes.js';
6
+ import '../transport.js';
7
+ import '../resources/i18n.js';
8
+ import '../resources/projects.js';
9
+ import '../resources/ops.js';
10
+ import '../resources/events.js';
11
+ import '../resources/metrics.js';
12
+ import '../resources/alert-rules.js';
13
+ import '../schemas/alert-rules.js';
14
+ import '../resources/killswitches.js';
15
+ import '../schemas/killswitches.js';
16
+ import '../schemas/pagination.js';
17
+ import '../resources/universes.js';
18
+ import '../schemas/universes.js';
19
+ import '../resources/configs.js';
20
+ import '../schemas/configs.js';
21
+ import '../resources/experiments.js';
22
+ import '../schemas/experiments.js';
23
+ import '../resources/gates.js';
24
+ import '../schemas/gates.js';
25
+
26
+ declare const experimentOperations: Operation[];
27
+
28
+ export { experimentOperations };
@@ -0,0 +1,280 @@
1
+ // src/operations/util.ts
2
+ var num = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
+ var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
4
+ var bool = (i, k) => i[k] === void 0 ? void 0 : i[k];
5
+
6
+ // src/operations/experiments.ts
7
+ var GROUP = ["release", "experiments"];
8
+ var DEFAULT_GROUPS = [
9
+ { name: "control", weight: 5e3, params: {} },
10
+ { name: "test", weight: 5e3, params: {} }
11
+ ];
12
+ var AGGREGATIONS = [
13
+ "count_users",
14
+ "count_events",
15
+ "retention_7d",
16
+ "retention_30d",
17
+ "sum",
18
+ "avg"
19
+ ];
20
+ function buildGoalMetric(i) {
21
+ const event = str(i, "successEvent");
22
+ if (!event) return void 0;
23
+ const agg = str(i, "successAggregation") ?? "count_users";
24
+ const value = str(i, "successValue");
25
+ switch (agg) {
26
+ case "count_users":
27
+ return { query: `count_users(${event})` };
28
+ case "count_events":
29
+ return { query: `count(${event})` };
30
+ case "retention_7d":
31
+ return { query: `retention_7d(${event})` };
32
+ case "retention_30d":
33
+ return { query: `retention_30d(${event})` };
34
+ case "sum":
35
+ case "avg":
36
+ if (!value)
37
+ throw new Error(
38
+ `successAggregation '${agg}' requires successValue \u2014 the numeric event property to ${agg} (e.g. "amount")`
39
+ );
40
+ return { query: `${agg}(${event}, ${value})` };
41
+ default:
42
+ throw new Error(`Unknown successAggregation '${agg}'`);
43
+ }
44
+ }
45
+ function parseGuardrails(raw) {
46
+ if (!Array.isArray(raw)) return [];
47
+ const out = [];
48
+ for (const entry of raw) {
49
+ if (typeof entry === "string") {
50
+ const query = entry.trim();
51
+ if (query) out.push({ query });
52
+ } else if (entry && typeof entry === "object") {
53
+ const o = entry;
54
+ const query = typeof o.query === "string" ? o.query.trim() : "";
55
+ if (query)
56
+ out.push(typeof o.name === "string" && o.name ? { query, name: o.name } : { query });
57
+ }
58
+ }
59
+ return out;
60
+ }
61
+ function computeVerdict(detail, results) {
62
+ if (results.some((r) => r.srm_detected === 1))
63
+ return { verdict: "invalid_srm", reason: "sample-ratio mismatch detected" };
64
+ if (detail.status !== "running") return { verdict: "not_running", reason: `status=${detail.status}` };
65
+ if (!results.length) return { verdict: "wait", reason: "no data yet" };
66
+ const withP = results.filter((r) => r.p_value !== null);
67
+ if (!withP.length) return { verdict: "wait", reason: "no p-values yet" };
68
+ const best = withP.reduce((a, b) => a.p_value < b.p_value ? a : b);
69
+ const threshold = detail.significance_threshold ?? 0.05;
70
+ if (best.p_value < threshold) return { verdict: "ship" };
71
+ const startedAt = detail.startedAt ?? detail.started_at;
72
+ const daysRunning = startedAt ? (Date.now() - new Date(startedAt).getTime()) / (1e3 * 60 * 60 * 24) : 0;
73
+ return daysRunning < (detail.min_runtime_days ?? 0) ? { verdict: "wait", reason: "min runtime not reached" } : { verdict: "hold" };
74
+ }
75
+ var experimentOperations = [
76
+ {
77
+ group: GROUP,
78
+ name: "list",
79
+ mutates: false,
80
+ summary: "List experiments",
81
+ description: "Return every experiment (status, universe, allocation).",
82
+ params: [],
83
+ examples: [{ run: "shipeasy release experiments list" }],
84
+ run: (client) => client.experiments.listAll()
85
+ },
86
+ {
87
+ group: GROUP,
88
+ name: "create",
89
+ mutates: true,
90
+ summary: "Create an experiment",
91
+ description: "Create an experiment draft with groups, params, optional targeting gate, a success (goal) metric, optional guardrail metrics, and full statistical config. Pass `successEvent` (+ `successAggregation`) to attach the goal metric inline \u2014 required before the experiment can be started. Defaults to a 50/50 control-vs-test split at 100% allocation in the `default` universe. Does NOT start \u2014 call `start`.",
92
+ params: [
93
+ { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
94
+ { name: "universe", type: "string", description: "Universe name. Defaults to 'default'." },
95
+ { name: "allocation", type: "number", description: "Allocation percentage, 0\u2013100. Defaults to 100." },
96
+ { name: "groups", type: "json", description: "Groups as JSON [{name,weight,params}]. weight is basis points." },
97
+ { name: "params", type: "json", description: "Parameter schema {name: type}." },
98
+ { name: "targetingGate", type: "string", description: "Targeting gate name." },
99
+ { name: "bucketBy", type: "string", description: "Attribute to bucket by (defaults to the universe unit)." },
100
+ { name: "salt", type: "string", description: "Override hash salt." },
101
+ { name: "successEvent", type: "string", description: "Event name for the inline goal metric. Attaching one makes the draft startable." },
102
+ { name: "successAggregation", type: "string", description: `Goal-metric reducer. Defaults to count_users.`, enum: [...AGGREGATIONS] },
103
+ { name: "successValue", type: "string", description: "Numeric event property for sum/avg goal metrics." },
104
+ { name: "guardrailMetrics", type: "json", description: "Up to 10 guardrail metrics: JSON array of DSL query strings or {query,name}." },
105
+ { name: "significance", type: "number", description: "Significance threshold (0.0001\u20130.5). Defaults to 0.05." },
106
+ { name: "minRuntimeDays", type: "number", description: "Minimum runtime in days." },
107
+ { name: "minSampleSize", type: "number", description: "Minimum sample size." },
108
+ { name: "sequentialTesting", type: "boolean", description: "Enable sequential testing (always-valid p-values; Premium+)." },
109
+ { name: "description", type: "string", description: "Human-readable description." }
110
+ ],
111
+ examples: [
112
+ { note: "Default 50/50, full traffic", run: "shipeasy release experiments create pricing-page" },
113
+ {
114
+ note: "Attach a goal metric so it's startable",
115
+ run: "shipeasy release experiments create checkout-cta --universe web --success-event checkout_completed"
116
+ }
117
+ ],
118
+ run: (client, i) => {
119
+ const goal_metric = buildGoalMetric(i);
120
+ const guardrail_metrics = parseGuardrails(i.guardrailMetrics);
121
+ return client.experiments.create({
122
+ name: i.name,
123
+ universe: str(i, "universe") ?? "default",
124
+ allocation_pct: Math.round((num(i, "allocation") ?? 100) * 100),
125
+ groups: i.groups ?? DEFAULT_GROUPS,
126
+ params: i.params ?? {},
127
+ targeting_gate: str(i, "targetingGate") ?? null,
128
+ ...str(i, "bucketBy") ? { bucket_by: str(i, "bucketBy") } : {},
129
+ ...str(i, "description") !== void 0 ? { description: str(i, "description") } : {},
130
+ ...i.salt ? { salt: i.salt } : {},
131
+ ...num(i, "significance") !== void 0 ? { significance_threshold: num(i, "significance") } : {},
132
+ ...num(i, "minRuntimeDays") !== void 0 ? { min_runtime_days: num(i, "minRuntimeDays") } : {},
133
+ ...num(i, "minSampleSize") !== void 0 ? { min_sample_size: num(i, "minSampleSize") } : {},
134
+ ...bool(i, "sequentialTesting") !== void 0 ? { sequential_testing: bool(i, "sequentialTesting") } : {},
135
+ ...goal_metric ? { goal_metric } : {},
136
+ ...guardrail_metrics.length ? { guardrail_metrics } : {}
137
+ });
138
+ }
139
+ },
140
+ {
141
+ group: GROUP,
142
+ name: "update",
143
+ mutates: true,
144
+ summary: "Update an experiment",
145
+ description: "Patch allocation, groups, params, targeting gate, or stats thresholds. Pass `targetingGate` = 'null' to clear it.",
146
+ params: [
147
+ { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
148
+ { name: "allocation", type: "number", description: "Allocation percentage, 0\u2013100." },
149
+ { name: "groups", type: "json", description: "Groups as JSON [{name,weight,params}]." },
150
+ { name: "params", type: "json", description: "Parameter schema {name: type}." },
151
+ { name: "targetingGate", type: "string", description: "Targeting gate name ('null' to clear)." },
152
+ { name: "bucketBy", type: "string", description: "Attribute to bucket by (empty string clears)." },
153
+ { name: "successEvent", type: "string", description: "Attach/replace the goal metric from this event." },
154
+ { name: "successAggregation", type: "string", description: "Goal-metric reducer (with successEvent).", enum: [...AGGREGATIONS] },
155
+ { name: "successValue", type: "string", description: "Numeric event property for sum/avg goal metrics." },
156
+ { name: "guardrailMetrics", type: "json", description: "Replace the guardrail set: JSON array of DSL queries or {query,name}." },
157
+ { name: "significance", type: "number", description: "Significance threshold (0.0001\u20130.5)." },
158
+ { name: "minRuntimeDays", type: "number", description: "Minimum runtime in days." },
159
+ { name: "minSampleSize", type: "number", description: "Minimum sample size." },
160
+ { name: "sequentialTesting", type: "boolean", description: "Enable sequential testing (always-valid p-values)." }
161
+ ],
162
+ examples: [
163
+ { note: "Dial allocation to 50%", run: "shipeasy release experiments update pricing-page --allocation 50" },
164
+ { note: "Clear targeting + tighten significance", run: "shipeasy release experiments update checkout-cta --targeting-gate null --significance 0.01" }
165
+ ],
166
+ run: async (client, i) => {
167
+ const e = await client.experiments.resolve(i.name);
168
+ const patch = {};
169
+ const allocation = num(i, "allocation");
170
+ if (allocation !== void 0) patch.allocation_pct = Math.round(allocation * 100);
171
+ if (i.groups !== void 0) patch.groups = i.groups;
172
+ if (i.params !== void 0) patch.params = i.params;
173
+ if (i.targetingGate !== void 0)
174
+ patch.targeting_gate = i.targetingGate === "null" ? null : i.targetingGate;
175
+ if (str(i, "bucketBy") !== void 0) patch.bucket_by = str(i, "bucketBy") || null;
176
+ if (i.significance !== void 0) patch.significance_threshold = i.significance;
177
+ if (i.minRuntimeDays !== void 0) patch.min_runtime_days = i.minRuntimeDays;
178
+ if (i.minSampleSize !== void 0) patch.min_sample_size = i.minSampleSize;
179
+ if (bool(i, "sequentialTesting") !== void 0) patch.sequential_testing = bool(i, "sequentialTesting");
180
+ if (i.guardrailMetrics !== void 0) patch.guardrail_metrics = parseGuardrails(i.guardrailMetrics);
181
+ const goal_metric = buildGoalMetric(i);
182
+ if (goal_metric) patch.goal_metric = goal_metric;
183
+ return client.experiments.update(e.id, patch);
184
+ }
185
+ },
186
+ {
187
+ group: GROUP,
188
+ name: "start",
189
+ mutates: true,
190
+ summary: "Start an experiment",
191
+ description: "Move an experiment from draft to running.",
192
+ params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
193
+ examples: [{ run: "shipeasy release experiments start pricing-page" }],
194
+ run: async (client, i) => {
195
+ const e = await client.experiments.resolve(i.name);
196
+ return client.experiments.start(e.id);
197
+ }
198
+ },
199
+ {
200
+ group: GROUP,
201
+ name: "stop",
202
+ mutates: true,
203
+ summary: "Stop a running experiment",
204
+ description: "Stop a running experiment. Optionally name a `promoteGroup` to record an intent note (actual promotion happens in the dashboard).",
205
+ params: [
206
+ { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
207
+ { name: "promoteGroup", type: "string", description: "Group to note as the winner to promote." }
208
+ ],
209
+ examples: [{ run: "shipeasy release experiments stop pricing-page" }],
210
+ run: async (client, i) => {
211
+ const e = await client.experiments.resolve(i.name);
212
+ const result = await client.experiments.stop(e.id);
213
+ const promote = str(i, "promoteGroup");
214
+ return {
215
+ ...result,
216
+ ...promote ? {
217
+ promote_group_note: `Manual promotion needed: promote group '${promote}' in the dashboard or via PATCH /api/admin/experiments/${e.id}.`
218
+ } : {}
219
+ };
220
+ }
221
+ },
222
+ {
223
+ group: GROUP,
224
+ name: "archive",
225
+ mutates: true,
226
+ summary: "Archive a stopped experiment",
227
+ description: "Archive (soft-delete) a stopped experiment, hiding it from the default list while preserving results.",
228
+ params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
229
+ examples: [{ run: "shipeasy release experiments archive pricing-page" }],
230
+ run: async (client, i) => {
231
+ const e = await client.experiments.resolve(i.name);
232
+ return client.experiments.archive(e.id);
233
+ }
234
+ },
235
+ {
236
+ group: GROUP,
237
+ name: "restore",
238
+ mutates: true,
239
+ summary: "Restore an archived experiment to draft",
240
+ description: "Restore a soft-deleted (archived) experiment back to `draft`. Allowed only if it never started; one that already ran must be cloned instead. Preserves the goal metric.",
241
+ params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
242
+ examples: [{ run: "shipeasy release experiments restore pricing-page" }],
243
+ run: async (client, i) => {
244
+ const e = await client.experiments.resolve(i.name);
245
+ return client.experiments.restore(e.id);
246
+ }
247
+ },
248
+ {
249
+ group: GROUP,
250
+ name: "reanalyze",
251
+ mutates: true,
252
+ summary: "Re-run the analysis pass",
253
+ description: "Trigger a fresh analysis pass for an experiment.",
254
+ params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
255
+ examples: [{ run: "shipeasy release experiments reanalyze pricing-page" }],
256
+ run: async (client, i) => {
257
+ const e = await client.experiments.resolve(i.name);
258
+ return client.experiments.reanalyze(e.id);
259
+ }
260
+ },
261
+ {
262
+ group: GROUP,
263
+ name: "status",
264
+ mutates: false,
265
+ summary: "Show experiment status, verdict, and latest results",
266
+ description: "Return the experiment, its latest per-metric results (enrolment, deltas, p-values), and a ship/hold/wait/not_running/invalid_srm verdict computed from the goal metric vs the significance threshold and minimum runtime.",
267
+ params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
268
+ examples: [{ run: "shipeasy release experiments status pricing-page" }],
269
+ run: async (client, i) => {
270
+ const e = await client.experiments.resolve(i.name);
271
+ const detail = await client.experiments.get(e.id).catch(() => e);
272
+ const results = await client.experiments.results(e.id).catch(() => []);
273
+ const { verdict, reason } = computeVerdict(detail, results);
274
+ return { verdict, ...reason ? { reason } : {}, experiment: detail, results };
275
+ }
276
+ }
277
+ ];
278
+ export {
279
+ experimentOperations
280
+ };
@@ -0,0 +1,28 @@
1
+ import { Operation } from './types.js';
2
+ import '../resources/index.js';
3
+ import 'zod/v4/core';
4
+ import 'zod';
5
+ import '../resources/attributes.js';
6
+ import '../transport.js';
7
+ import '../resources/i18n.js';
8
+ import '../resources/projects.js';
9
+ import '../resources/ops.js';
10
+ import '../resources/events.js';
11
+ import '../resources/metrics.js';
12
+ import '../resources/alert-rules.js';
13
+ import '../schemas/alert-rules.js';
14
+ import '../resources/killswitches.js';
15
+ import '../schemas/killswitches.js';
16
+ import '../schemas/pagination.js';
17
+ import '../resources/universes.js';
18
+ import '../schemas/universes.js';
19
+ import '../resources/configs.js';
20
+ import '../schemas/configs.js';
21
+ import '../resources/experiments.js';
22
+ import '../schemas/experiments.js';
23
+ import '../resources/gates.js';
24
+ import '../schemas/gates.js';
25
+
26
+ declare const gateOperations: Operation[];
27
+
28
+ export { gateOperations };
@@ -0,0 +1,132 @@
1
+ // src/operations/util.ts
2
+ var num = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
+
4
+ // src/operations/gates.ts
5
+ var ROLLOUT_DESC = "Rollout percentage, 0\u2013100 (converted to basis points internally).";
6
+ var RULES_DESC = `Targeting rules as a JSON array, e.g. '[{"attr":"plan","op":"eq","value":"pro"}]'. AND-combined.`;
7
+ var STACK_DESC = "Gatekeeper stack as a JSON array of ordered first-match-wins tiers (condition/rollout steps, rolloutPct in basis points). Takes precedence over flat rules/rollout. Pass to express internal \u222A beta \u222A public in one gate.";
8
+ var gateOperations = [
9
+ {
10
+ group: ["release", "flags"],
11
+ name: "list",
12
+ mutates: false,
13
+ summary: "List all feature gates",
14
+ description: "Return every feature gate in the project (name, enabled, rollout %), ordered by most recently updated.",
15
+ params: [],
16
+ examples: [{ run: "shipeasy release flags list" }],
17
+ run: (client) => client.gates.listAll()
18
+ },
19
+ {
20
+ group: ["release", "flags"],
21
+ name: "create",
22
+ mutates: true,
23
+ summary: "Create a feature gate",
24
+ description: "Create a feature gate. A flat gate is `rules` + a single `rollout` %. For tiered rollouts (e.g. 'US @ 50% \u2192 bots @ 30% \u2192 everyone else @ 10%') pass `stack` \u2014 an ordered list of steps evaluated first-match-wins; flat `rules`/`rollout` are then ignored.",
25
+ params: [
26
+ { name: "name", type: "string", description: "Gate key (lowercase, digits, _/-).", required: true, positional: true },
27
+ { name: "rollout", type: "number", description: ROLLOUT_DESC },
28
+ { name: "rules", type: "json", description: RULES_DESC },
29
+ { name: "salt", type: "string", description: "Override hash salt (immutable after create)." },
30
+ { name: "stack", type: "json", description: STACK_DESC }
31
+ ],
32
+ examples: [
33
+ { note: "Off by default; flip it on later", run: "shipeasy release flags create checkout-v2" },
34
+ {
35
+ note: "Roll out to 25% of pro-plan users",
36
+ run: `shipeasy release flags create new-ui --rollout 25 --rules '[{"attr":"plan","op":"eq","value":"pro"}]'`
37
+ }
38
+ ],
39
+ run: (client, i) => client.gates.create({
40
+ name: i.name,
41
+ rollout_pct: Math.round((num(i, "rollout") ?? 0) * 100),
42
+ // Validated at runtime by gateCreateSchema.parse inside gates.create().
43
+ rules: i.rules ?? [],
44
+ ...i.salt ? { salt: i.salt } : {},
45
+ ...i.stack ? { stack: i.stack } : {}
46
+ })
47
+ },
48
+ {
49
+ group: ["release", "flags"],
50
+ name: "update",
51
+ mutates: true,
52
+ summary: "Update a gate's rollout, rules, stack, or enabled flag",
53
+ description: "Update a feature gate's rollout, rules, gatekeeper stack, or enabled flag. Pass `stack` to replace the tiered rollout wholesale; array fields replace, they do not merge.",
54
+ params: [
55
+ { name: "name", type: "string", description: "Gate name to update.", required: true, positional: true },
56
+ { name: "rollout", type: "number", description: ROLLOUT_DESC },
57
+ { name: "rules", type: "json", description: RULES_DESC + " Replaces the existing rules wholesale." },
58
+ { name: "stack", type: "json", description: STACK_DESC + " Pass null to revert to flat evaluation." },
59
+ { name: "enabled", type: "boolean", description: "Master switch \u2014 false forces the gate off for every caller." }
60
+ ],
61
+ examples: [{ note: "Ramp to 50%", run: "shipeasy release flags update checkout-v2 --rollout 50" }],
62
+ run: async (client, i) => {
63
+ const gate = await client.gates.resolve(i.name);
64
+ const patch = {};
65
+ const rollout = num(i, "rollout");
66
+ if (rollout !== void 0) patch.rollout_pct = Math.round(rollout * 100);
67
+ if (i.rules !== void 0) patch.rules = i.rules;
68
+ if (i.stack !== void 0) patch.stack = i.stack;
69
+ if (i.enabled !== void 0) patch.enabled = i.enabled;
70
+ return client.gates.update(gate.id, patch);
71
+ }
72
+ },
73
+ {
74
+ group: ["release", "flags"],
75
+ name: "enable",
76
+ mutates: true,
77
+ summary: "Enable a feature gate",
78
+ description: "Set `enabled: true`, preserving the current rollout %.",
79
+ params: [{ name: "name", type: "string", description: "Gate name.", required: true, positional: true }],
80
+ examples: [{ run: "shipeasy release flags enable checkout-v2" }],
81
+ run: async (client, i) => {
82
+ const gate = await client.gates.resolve(i.name);
83
+ return client.gates.enable(gate.id);
84
+ }
85
+ },
86
+ {
87
+ group: ["release", "flags"],
88
+ name: "disable",
89
+ mutates: true,
90
+ summary: "Disable a feature gate (kill switch)",
91
+ description: "Set `enabled: false` so the gate evaluates to `false` for every caller regardless of rules/rollout.",
92
+ params: [{ name: "name", type: "string", description: "Gate name.", required: true, positional: true }],
93
+ examples: [{ run: "shipeasy release flags disable checkout-v2" }],
94
+ run: async (client, i) => {
95
+ const gate = await client.gates.resolve(i.name);
96
+ return client.gates.disable(gate.id);
97
+ }
98
+ },
99
+ {
100
+ group: ["release", "flags"],
101
+ name: "rollout",
102
+ mutates: true,
103
+ summary: "Set rollout percentage (0\u2013100)",
104
+ description: "Resolve the gate by name and set its rollout percentage (0\u2013100).",
105
+ params: [
106
+ { name: "name", type: "string", description: "Gate name.", required: true, positional: true },
107
+ { name: "pct", type: "number", description: "Rollout percentage, 0\u2013100.", required: true, positional: true }
108
+ ],
109
+ examples: [
110
+ { note: "Ramp to 50%", run: "shipeasy release flags rollout checkout-v2 50" },
111
+ { note: "Instant kill \u2014 set to 0", run: "shipeasy release flags rollout checkout-v2 0" }
112
+ ],
113
+ run: (client, i) => client.gates.setRollout(i.name, num(i, "pct"))
114
+ },
115
+ {
116
+ group: ["release", "flags"],
117
+ name: "archive",
118
+ mutates: true,
119
+ summary: "Archive a feature gate",
120
+ description: "Archive (soft-delete) a feature gate by name. Refuses if a running experiment references it as a targeting gate.",
121
+ params: [{ name: "name", type: "string", description: "Gate name.", required: true, positional: true }],
122
+ examples: [{ run: "shipeasy release flags archive checkout-v2" }],
123
+ run: async (client, i) => {
124
+ const gate = await client.gates.resolve(i.name);
125
+ await client.gates.delete(gate.id);
126
+ return { ok: true, archived: i.name };
127
+ }
128
+ }
129
+ ];
130
+ export {
131
+ gateOperations
132
+ };
@@ -0,0 +1,28 @@
1
+ import { Operation } from './types.js';
2
+ import '../resources/index.js';
3
+ import 'zod/v4/core';
4
+ import 'zod';
5
+ import '../resources/attributes.js';
6
+ import '../transport.js';
7
+ import '../resources/i18n.js';
8
+ import '../resources/projects.js';
9
+ import '../resources/ops.js';
10
+ import '../resources/events.js';
11
+ import '../resources/metrics.js';
12
+ import '../resources/alert-rules.js';
13
+ import '../schemas/alert-rules.js';
14
+ import '../resources/killswitches.js';
15
+ import '../schemas/killswitches.js';
16
+ import '../schemas/pagination.js';
17
+ import '../resources/universes.js';
18
+ import '../schemas/universes.js';
19
+ import '../resources/configs.js';
20
+ import '../schemas/configs.js';
21
+ import '../resources/experiments.js';
22
+ import '../schemas/experiments.js';
23
+ import '../resources/gates.js';
24
+ import '../schemas/gates.js';
25
+
26
+ declare const i18nOperations: Operation[];
27
+
28
+ export { i18nOperations };