@oxygen-agent/cli 1.310.2 → 1.334.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.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +26 -10
  3. package/dist/help.js +7 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +2077 -134
  6. package/dist/runtime.js +13 -3
  7. package/dist/skills.js +192 -32
  8. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +2 -0
  9. package/node_modules/@oxygen/shared/dist/billing.d.ts +52 -29
  10. package/node_modules/@oxygen/shared/dist/billing.js +77 -55
  11. package/node_modules/@oxygen/shared/dist/index.d.ts +4 -0
  12. package/node_modules/@oxygen/shared/dist/index.js +4 -0
  13. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +84 -0
  14. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +82 -0
  15. package/node_modules/@oxygen/shared/dist/recipes.d.ts +19 -0
  16. package/node_modules/@oxygen/shared/dist/recipes.js +90 -0
  17. package/node_modules/@oxygen/shared/dist/sequences.d.ts +61 -15
  18. package/node_modules/@oxygen/shared/dist/sequences.js +134 -23
  19. package/node_modules/@oxygen/shared/dist/sql-error.d.ts +25 -0
  20. package/node_modules/@oxygen/shared/dist/sql-error.js +46 -0
  21. package/node_modules/@oxygen/shared/dist/version.d.ts +2 -2
  22. package/node_modules/@oxygen/shared/dist/version.js +13 -11
  23. package/node_modules/@oxygen/shared/dist/workflow-mcp-tools.d.ts +4 -0
  24. package/node_modules/@oxygen/shared/dist/workflow-mcp-tools.js +18 -0
  25. package/node_modules/@oxygen/shared/dist/workflow-status-change.d.ts +61 -0
  26. package/node_modules/@oxygen/shared/dist/workflow-status-change.js +124 -0
  27. package/node_modules/@oxygen/shared/dist/workspace-agents.d.ts +65 -0
  28. package/node_modules/@oxygen/shared/dist/workspace-agents.js +67 -0
  29. package/node_modules/@oxygen/workflows/dist/index.js +86 -2
  30. package/package.json +1 -1
package/dist/runtime.js CHANGED
@@ -22,6 +22,16 @@ export function resolveCliBinaryName(env = process.env, argv = process.argv) {
22
22
  const explicit = normalizeBinaryName(env.OXYGEN_CLI_BINARY ?? env.OXYGEN_CLI_NAME);
23
23
  if (explicit)
24
24
  return explicit;
25
+ // The dev install (`npm run install-dev-cli`) also ships a legacy shim literally
26
+ // named `oxygen` under ~/.oxygen-dev/. A basename check alone misreads it as the
27
+ // npm production binary, and every command then scolds the user about "pointing
28
+ // a production binary at a non-production API" — for a setup that is exactly as
29
+ // intended (live blind-user finding, 2026-07-13). The install PATH is the
30
+ // truthful channel signal, so check it before the basename.
31
+ const invokedPath = argv[1] ?? "";
32
+ if (invokedPath.includes("/.oxygen-dev/") || invokedPath.includes("\\.oxygen-dev\\")) {
33
+ return DEV_CLI_BINARY;
34
+ }
25
35
  const invoked = normalizeBinaryName(argv[1]);
26
36
  if (invoked)
27
37
  return invoked;
@@ -33,10 +43,10 @@ export function resolveCliUpdateGuidance(endpoint, env = process.env, argv = pro
33
43
  return {
34
44
  binaryName,
35
45
  channel: "dev",
36
- warningInstruction: "Refresh the dev CLI (`oxygen-dev`) from the dev branch before using operational commands.",
37
- failureInstruction: "Refresh the dev CLI (`oxygen-dev`) from the dev branch before using this command.",
46
+ warningInstruction: "Rebuild `oxygen-dev` from the latest dev branch (`npm run install-dev-cli`) before using operational commands.",
47
+ failureInstruction: "Rebuild `oxygen-dev` from the latest dev branch (`npm run install-dev-cli`) before using this command.",
38
48
  details: {
39
- cli_update_instruction: "Refresh the dev CLI (`oxygen-dev`) from the dev branch.",
49
+ cli_update_instruction: "Rebuild `oxygen-dev` from the latest dev branch (`npm run install-dev-cli`).",
40
50
  },
41
51
  };
42
52
  }
package/dist/skills.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { existsSync, readdirSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { join, resolve } from "node:path";
2
+ import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { homedir, tmpdir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
5
  import { OxygenError, toFailure } from "@oxygen/shared";
6
6
  import { defaultApiUrl, loadCredentials, normalizeApiUrl } from "./credentials.js";
7
7
  import { requestOxygen } from "./http-client.js";
@@ -118,35 +118,71 @@ options, runtime = {}) {
118
118
  const skill = readOption(options.skill) ?? "*";
119
119
  const env = runtime.env ?? process.env;
120
120
  const platform = runtime.platform ?? process.platform;
121
- const args = installerAddArgs(source.install_url, agents, skill);
122
- if (!options.project)
123
- args.push("--global");
124
- if (options.copy)
125
- args.push("--copy");
126
- const invocation = resolveCmdShimInvocation("npx", args, platform, env);
127
121
  const spawn = runtime.spawnSync ?? wrapExecFileSync(runtime.execFileSync) ?? spawnSync;
128
- const result = spawn(invocation.executable, invocation.args, {
129
- encoding: "utf8",
130
- env,
131
- stdio: ["ignore", "pipe", "pipe"],
132
- windowsHide: true,
133
- ...(platform === "win32" ? { windowsVerbatimArguments: true } : {}),
134
- });
135
- const stdout = typeof result.stdout === "string" ? result.stdout : "";
136
- const stderr = typeof result.stderr === "string" ? result.stderr : "";
137
- if (result.error || (typeof result.status === "number" && result.status !== 0)) {
138
- const message = result.error instanceof Error ? result.error.message : null;
139
- throw new OxygenError("skills_install_failed", "Unable to install Oxygen agent skills.", {
140
- details: {
141
- index_url: source.index_url,
142
- authenticated: source.authenticated,
143
- executable: invocation.label,
144
- exit_code: typeof result.status === "number" ? result.status : null,
145
- stdout: stdout.slice(0, 2000),
146
- stderr: (stderr || message || "unknown").slice(0, 2000),
147
- },
148
- exitCode: 1,
149
- });
122
+ // A specific shortcut skill can declare prerequisite skills that its SKILL.md
123
+ // invokes (e.g. oxygen-clay-migration → oxygen-gtm, whose recipes it lives in).
124
+ // Installing it alone would leave those missing, so pull them in too. Best-
125
+ // effort (an unreachable/unparseable index just installs the requested skill),
126
+ // and skipped for "*", which already installs everything.
127
+ const dependencySkills = skill === "*"
128
+ ? []
129
+ : await resolveSkillInstallDependencies(skill, source.index_url, runtime.fetch);
130
+ const targets = [skill, ...dependencySkills];
131
+ // The authenticated access URL is org-bound and secret, so it must never
132
+ // reach the installer's argv — any local process can read a child's
133
+ // arguments. Stage the requested skill content to a private temp dir (the
134
+ // tokenized URLs stay inside this process on the fetches) and hand the
135
+ // installer that local path instead. The public, non-secret index URL is
136
+ // passed straight through as before.
137
+ let stagingDir = null;
138
+ const outputs = [];
139
+ try {
140
+ let installSource = source.install_url;
141
+ if (source.authenticated) {
142
+ stagingDir = mkdtempSync(join(tmpdir(), "oxygen-skills-"));
143
+ await stageAuthenticatedSkills(stagingDir, source, targets, runtime.fetch ?? fetch);
144
+ installSource = stagingDir;
145
+ }
146
+ for (const target of targets) {
147
+ const args = installerAddArgs(installSource, agents, target);
148
+ if (!options.project)
149
+ args.push("--global");
150
+ if (options.copy)
151
+ args.push("--copy");
152
+ const invocation = resolveCmdShimInvocation("npx", args, platform, env);
153
+ const result = spawn(invocation.executable, invocation.args, {
154
+ encoding: "utf8",
155
+ env,
156
+ stdio: ["ignore", "pipe", "pipe"],
157
+ windowsHide: true,
158
+ ...(platform === "win32" ? { windowsVerbatimArguments: true } : {}),
159
+ });
160
+ const stdout = typeof result.stdout === "string" ? result.stdout : "";
161
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
162
+ if (result.error || (typeof result.status === "number" && result.status !== 0)) {
163
+ const message = result.error instanceof Error ? result.error.message : null;
164
+ throw new OxygenError("skills_install_failed", "Unable to install Oxygen agent skills.", {
165
+ details: {
166
+ index_url: source.index_url,
167
+ authenticated: source.authenticated,
168
+ skill: target,
169
+ executable: invocation.label,
170
+ exit_code: typeof result.status === "number" ? result.status : null,
171
+ stdout: stdout.slice(0, 2000),
172
+ stderr: (stderr || message || "unknown").slice(0, 2000),
173
+ },
174
+ exitCode: 1,
175
+ });
176
+ }
177
+ outputs.push(stdout);
178
+ }
179
+ }
180
+ finally {
181
+ if (stagingDir)
182
+ rmSync(stagingDir, { recursive: true, force: true });
183
+ }
184
+ if (dependencySkills.length > 0) {
185
+ process.stderr.write(`note: '${skill}' also installs its prerequisite skill(s): ${dependencySkills.join(", ")}.\n`);
150
186
  }
151
187
  return {
152
188
  indexUrl: source.index_url,
@@ -156,10 +192,129 @@ options, runtime = {}) {
156
192
  ...(source.fallback_reason ? { access_fallback: source.fallback_reason } : {}),
157
193
  agents,
158
194
  skill,
195
+ ...(dependencySkills.length > 0 ? { dependency_skills: dependencySkills } : {}),
159
196
  scope: options.project ? "project" : "global",
160
- output: stdout,
197
+ output: outputs.join(""),
161
198
  };
162
199
  }
200
+ /**
201
+ * Stages the requested skill(s) into `dir` so the installer receives a local
202
+ * filesystem path instead of the org-bound tokenized access URL. Keeping the URL
203
+ * out of the child's argv closes a local-process argv-inspection exposure; the
204
+ * token still travels over the network on these in-process fetches, which is the
205
+ * already-accepted part of the design. Layout matches what the installer walks
206
+ * for a local source: `<dir>/<skill>/<file>` (nested paths preserved).
207
+ *
208
+ * Any fetch/staging failure throws the same `skills_install_failed` error the
209
+ * installer path raises — we never fall back to passing the tokenized URL as a
210
+ * process argument.
211
+ */
212
+ async function stageAuthenticatedSkills(dir, source, targets, fetchImpl) {
213
+ const entries = await fetchStagedSkillEntries(source.index_url, fetchImpl);
214
+ const wanted = targets.includes("*")
215
+ ? entries
216
+ : entries.filter((entry) => targets.includes(entry.name));
217
+ // `<base>/.well-known/skills/index.json` → `<base>/.well-known/skills`; the
218
+ // installer reads a skill's files from `<root>/<name>/<file>`.
219
+ const skillsRoot = source.index_url.replace(/\/index\.json$/i, "");
220
+ for (const entry of wanted) {
221
+ // allSettled, not all: on a failed fetch, Promise.all would reject while
222
+ // sibling fetches are still writing — the caller's finally rmSyncs the temp
223
+ // dir and a late mkdirSync would silently recreate it as orphan litter.
224
+ // Waiting for every settle guarantees no write is in flight when we throw.
225
+ const staged = await Promise.allSettled(entry.files.map(async (file) => {
226
+ const content = await fetchStagedSkillFile(`${skillsRoot}/${entry.name}/${file}`, fetchImpl);
227
+ const destination = join(dir, entry.name, ...file.split("/"));
228
+ mkdirSync(dirname(destination), { recursive: true });
229
+ writeFileSync(destination, content);
230
+ }));
231
+ const failed = staged.find((result) => result.status === "rejected");
232
+ if (failed)
233
+ throw failed.reason;
234
+ }
235
+ }
236
+ async function fetchStagedSkillEntries(indexUrl, fetchImpl) {
237
+ let response;
238
+ try {
239
+ response = await fetchImpl(indexUrl, { headers: { Accept: "application/json" } });
240
+ }
241
+ catch (error) {
242
+ throw stagingFailure("fetch_index", { index_url: indexUrl, message: errorMessage(error) });
243
+ }
244
+ if (!response.ok) {
245
+ throw stagingFailure("fetch_index", { index_url: indexUrl, status: response.status });
246
+ }
247
+ let parsed;
248
+ try {
249
+ parsed = JSON.parse(await response.text());
250
+ }
251
+ catch (error) {
252
+ throw stagingFailure("parse_index", { index_url: indexUrl, message: errorMessage(error) });
253
+ }
254
+ if (!isRecord(parsed) || !Array.isArray(parsed.skills)) {
255
+ throw stagingFailure("parse_index", { index_url: indexUrl, message: "skills array missing" });
256
+ }
257
+ const entries = [];
258
+ for (const entry of parsed.skills) {
259
+ if (!isRecord(entry) || typeof entry.name !== "string" || !entry.name.trim())
260
+ continue;
261
+ const files = Array.isArray(entry.files)
262
+ ? entry.files.filter((file) => typeof file === "string" && file.trim().length > 0)
263
+ : [];
264
+ entries.push({ name: entry.name.trim(), files });
265
+ }
266
+ return entries;
267
+ }
268
+ async function fetchStagedSkillFile(url, fetchImpl) {
269
+ let response;
270
+ try {
271
+ response = await fetchImpl(url);
272
+ }
273
+ catch (error) {
274
+ throw stagingFailure("fetch_file", { url, message: errorMessage(error) });
275
+ }
276
+ if (!response.ok) {
277
+ throw stagingFailure("fetch_file", { url, status: response.status });
278
+ }
279
+ return response.text();
280
+ }
281
+ // Mirror the installer-failure error so a staging failure surfaces identically
282
+ // (code, message, exitCode) to the caller — never as a silent URL fallback.
283
+ function stagingFailure(stage, details) {
284
+ return new OxygenError("skills_install_failed", "Unable to install Oxygen agent skills.", {
285
+ details: { stage, ...details },
286
+ exitCode: 1,
287
+ });
288
+ }
289
+ function errorMessage(error) {
290
+ return error instanceof Error ? error.message : String(error);
291
+ }
292
+ // Best-effort resolution of a skill's declared prerequisite skills from the
293
+ // index. Any failure (unreachable index, parse error, unknown skill) yields no
294
+ // dependencies so a dependency lookup can never block or fail an install.
295
+ async function resolveSkillInstallDependencies(skill, indexUrl, fetchImpl) {
296
+ try {
297
+ const index = await inspectSkillIndex(indexUrl, fetchImpl ?? fetch);
298
+ if (!index.reachable)
299
+ return [];
300
+ const entry = index.skills.find((candidate) => candidate.name === skill);
301
+ if (!entry)
302
+ return [];
303
+ // Drop self-references and duplicates; preserve declared order.
304
+ const seen = new Set([skill]);
305
+ const resolved = [];
306
+ for (const dep of entry.dependencies) {
307
+ if (seen.has(dep))
308
+ continue;
309
+ seen.add(dep);
310
+ resolved.push(dep);
311
+ }
312
+ return resolved;
313
+ }
314
+ catch {
315
+ return [];
316
+ }
317
+ }
163
318
  // Adapt the legacy `execFileSync` runtime hook into a spawnSync-shaped return so
164
319
  // existing test fixtures (update.test.ts) keep working without a coordinated
165
320
  // rewrite. New tests should inject `spawnSync` directly.
@@ -347,6 +502,11 @@ function parseSkillIndex(value) {
347
502
  title: typeof entry.title === "string" ? entry.title : null,
348
503
  description: typeof entry.description === "string" ? entry.description : null,
349
504
  entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : null,
505
+ dependencies: Array.isArray(entry.dependencies)
506
+ ? entry.dependencies
507
+ .filter((dep) => typeof dep === "string" && dep.trim().length > 0)
508
+ .map((dep) => dep.trim())
509
+ : [],
350
510
  };
351
511
  })
352
512
  .filter((entry) => entry !== null);
@@ -4,6 +4,7 @@ export type RecipeLogLevel = "debug" | "info" | "warn" | "error";
4
4
  export type RecipeRuntime = "durable";
5
5
  export type RecipeToolRunOptions = {
6
6
  key?: string;
7
+ optional?: boolean;
7
8
  };
8
9
  export type RecipeToolApi = {
9
10
  run: <T = unknown>(toolId: string, payload?: Record<string, unknown>, options?: RecipeToolRunOptions) => Promise<T>;
@@ -46,6 +47,7 @@ export type RecipeTablesApi = {
46
47
  includeProfile?: boolean;
47
48
  includeDataQuality?: boolean;
48
49
  key?: string;
50
+ optional?: boolean;
49
51
  }) => Promise<T>;
50
52
  };
51
53
  export type RecipeRowsApi = {
@@ -26,86 +26,109 @@ export type PricingPlanDefinition = {
26
26
  };
27
27
  export type PricingPlanMetadata = Record<string, unknown> | null | undefined;
28
28
  export declare const CONTACT_SALES_URL = "https://cal.com/tim-scheuer-mxbib9/45";
29
+ export declare const CREDITS_PER_USD = 1000;
30
+ export declare const TOPUP_USD_CENTS_PER_1K_CREDITS = 125;
31
+ export declare const AUTOMATION_ACTION_CREDITS = 0.01;
32
+ export declare const CREDIT_TOPUP_PACKS: readonly [{
33
+ readonly id: "10";
34
+ readonly usdCents: 1000;
35
+ readonly credits: 8000;
36
+ }, {
37
+ readonly id: "25";
38
+ readonly usdCents: 2500;
39
+ readonly credits: 20000;
40
+ }, {
41
+ readonly id: "100";
42
+ readonly usdCents: 10000;
43
+ readonly credits: 80000;
44
+ }, {
45
+ readonly id: "250";
46
+ readonly usdCents: 25000;
47
+ readonly credits: 200000;
48
+ }];
49
+ export type CreditTopupPack = (typeof CREDIT_TOPUP_PACKS)[number];
50
+ export type CreditTopupPackId = CreditTopupPack["id"];
51
+ export declare function resolveCreditTopupPack(id: string | null | undefined): CreditTopupPack | null;
29
52
  export declare const TRIAL_PERIOD_DAYS = 7;
30
- export declare const TRIAL_CREDIT_GRANT = 1000;
53
+ export declare const TRIAL_CREDIT_GRANT = 20000;
31
54
  export declare const BASE_PRICING_PLANS: {
32
55
  readonly free: {
33
56
  readonly tier: "free";
34
57
  readonly name: "Free";
35
58
  readonly monthlyPriceCents: 0;
36
- readonly monthlyCredits: 500;
37
- readonly weeklyCreditsLimit: 500;
38
- readonly rolloverCap: 500;
39
- readonly monthlyAutomationActions: 10000;
59
+ readonly monthlyCredits: 10000;
60
+ readonly weeklyCreditsLimit: 10000;
61
+ readonly rolloverCap: 10000;
62
+ readonly monthlyAutomationActions: null;
40
63
  readonly automationOverageCentsPerMillion: null;
41
64
  readonly automationOverageEnabledDefault: false;
42
65
  readonly byokEnabled: false;
43
66
  readonly description: "Get $10 in credits on us every month — try OXYGEN with managed email, phone enrichment, and AI credits.";
44
67
  readonly ctaLabel: "Start free";
45
- readonly features: readonly ["$10 in credits every month", "10,000 automation actions / month", "All integrations", "Workflows", "No card required"];
68
+ readonly features: readonly ["$10 in credits every month", "Automation actions at 0.01 credits each", "All integrations", "Workflows", "No card required"];
46
69
  };
47
70
  readonly starter: {
48
71
  readonly tier: "starter";
49
72
  readonly name: "Starter";
50
73
  readonly monthlyPriceCents: 9900;
51
- readonly monthlyCredits: 5000;
52
- readonly weeklyCreditsLimit: 2000;
53
- readonly rolloverCap: 5000;
54
- readonly monthlyAutomationActions: 250000;
55
- readonly automationOverageCentsPerMillion: 1000;
74
+ readonly monthlyCredits: 99000;
75
+ readonly weeklyCreditsLimit: 40000;
76
+ readonly rolloverCap: 99000;
77
+ readonly monthlyAutomationActions: null;
78
+ readonly automationOverageCentsPerMillion: null;
56
79
  readonly automationOverageEnabledDefault: false;
57
80
  readonly byokEnabled: true;
58
81
  readonly description: "For founders running focused managed enrichment and AI columns.";
59
82
  readonly ctaLabel: "Choose Starter";
60
- readonly features: readonly ["All integrations", "250,000 automation actions / month", "Bring your own keys", "Workflows", "Standard support"];
83
+ readonly features: readonly ["99,000 credits every month", "All integrations", "Workflows — automation actions at 0.01 credits each", "Bring your own keys", "Standard support"];
61
84
  };
62
85
  readonly pro: {
63
86
  readonly tier: "pro";
64
87
  readonly name: "Pro";
65
88
  readonly monthlyPriceCents: 24900;
66
- readonly monthlyCredits: 12500;
67
- readonly weeklyCreditsLimit: 5000;
68
- readonly rolloverCap: 12500;
69
- readonly monthlyAutomationActions: 2000000;
70
- readonly automationOverageCentsPerMillion: 1000;
89
+ readonly monthlyCredits: 249000;
90
+ readonly weeklyCreditsLimit: 100000;
91
+ readonly rolloverCap: 249000;
92
+ readonly monthlyAutomationActions: null;
93
+ readonly automationOverageCentsPerMillion: null;
71
94
  readonly automationOverageEnabledDefault: false;
72
95
  readonly byokEnabled: true;
73
96
  readonly description: "For recurring GTM enrichment and AI-column workflows on managed credits.";
74
97
  readonly badge: "Most popular";
75
98
  readonly highlighted: true;
76
99
  readonly ctaLabel: "Choose Pro";
77
- readonly features: readonly ["All integrations", "2,000,000 automation actions / month", "Bring your own keys", "Workflows", "Standard support"];
100
+ readonly features: readonly ["249,000 credits every month", "All integrations", "Workflows — automation actions at 0.01 credits each", "Bring your own keys", "Standard support"];
78
101
  };
79
102
  readonly team: {
80
103
  readonly tier: "team";
81
104
  readonly name: "Team";
82
105
  readonly monthlyPriceCents: 74900;
83
- readonly monthlyCredits: 37500;
84
- readonly weeklyCreditsLimit: 15000;
85
- readonly rolloverCap: 37500;
86
- readonly monthlyAutomationActions: 10000000;
87
- readonly automationOverageCentsPerMillion: 500;
106
+ readonly monthlyCredits: 749000;
107
+ readonly weeklyCreditsLimit: 300000;
108
+ readonly rolloverCap: 749000;
109
+ readonly monthlyAutomationActions: null;
110
+ readonly automationOverageCentsPerMillion: null;
88
111
  readonly automationOverageEnabledDefault: false;
89
112
  readonly byokEnabled: true;
90
113
  readonly description: "For small GTM teams and agencies running shared workflows with their own AI keys.";
91
114
  readonly ctaLabel: "Choose Team";
92
- readonly features: readonly ["All integrations", "10,000,000 automation actions / month", "Bring your own keys", "Workflows", "Premium support"];
115
+ readonly features: readonly ["749,000 credits every month", "All integrations", "Workflows — automation actions at 0.01 credits each", "Bring your own keys", "Premium support"];
93
116
  };
94
117
  readonly scale: {
95
118
  readonly tier: "scale";
96
119
  readonly name: "Scale";
97
120
  readonly monthlyPriceCents: null;
98
- readonly monthlyCredits: 500000;
99
- readonly weeklyCreditsLimit: 500000;
100
- readonly rolloverCap: 500000;
101
- readonly monthlyAutomationActions: 50000000;
121
+ readonly monthlyCredits: 10000000;
122
+ readonly weeklyCreditsLimit: 10000000;
123
+ readonly rolloverCap: 10000000;
124
+ readonly monthlyAutomationActions: null;
102
125
  readonly automationOverageCentsPerMillion: null;
103
126
  readonly automationOverageEnabledDefault: false;
104
127
  readonly byokEnabled: true;
105
128
  readonly description: "For high-volume GTM teams that need custom limits and rollout support.";
106
129
  readonly ctaLabel: "Contact sales";
107
130
  readonly contactHref: "https://cal.com/tim-scheuer-mxbib9/45";
108
- readonly features: readonly ["500,000+ managed credits / month", "50,000,000+ automation actions / month", "Phone number enrichment on managed credits", "Custom provider routing and BYOK policies", "Dedicated onboarding"];
131
+ readonly features: readonly ["10,000,000+ managed credits / month", "Automation actions at 0.01 credits each", "Phone number enrichment on managed credits", "Custom provider routing and BYOK policies", "Dedicated onboarding"];
109
132
  };
110
133
  readonly enterprise: {
111
134
  readonly tier: "enterprise";