@oxygen-agent/cli 1.184.3 → 1.209.23
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 +1 -1
- package/dist/http-client.js +6 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +614 -18
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +28 -6
- package/node_modules/@oxygen/shared/dist/billing.js +41 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/index.js +1 -0
- package/node_modules/@oxygen/shared/dist/networks.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/networks.js +25 -0
- package/node_modules/@oxygen/shared/dist/search-vocab.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/sequences.js +9 -1
- package/node_modules/@oxygen/shared/dist/signup-lead-webhook.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/sql-error.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +2 -5
- package/node_modules/@oxygen/workflows/dist/index.d.ts +1 -0
- package/node_modules/@oxygen/workflows/dist/index.js +39 -5
- package/oxygen.js +2 -0
- package/package.json +2 -2
|
@@ -78,6 +78,46 @@ export type RecipeContextProfileApi = {
|
|
|
78
78
|
export type RecipeContextApi = {
|
|
79
79
|
profile: RecipeContextProfileApi;
|
|
80
80
|
};
|
|
81
|
+
export type RecipeCrmIdentity = {
|
|
82
|
+
key: string;
|
|
83
|
+
value: unknown;
|
|
84
|
+
};
|
|
85
|
+
export type RecipeCrmActivityLink = {
|
|
86
|
+
object: string;
|
|
87
|
+
row_id: string;
|
|
88
|
+
role?: string;
|
|
89
|
+
};
|
|
90
|
+
export type RecipeCrmActivityInput = {
|
|
91
|
+
type: string;
|
|
92
|
+
links: RecipeCrmActivityLink[];
|
|
93
|
+
summary?: string | null;
|
|
94
|
+
body?: string | null;
|
|
95
|
+
channel?: string | null;
|
|
96
|
+
direction?: "inbound" | "outbound" | "internal" | "system" | null;
|
|
97
|
+
occurred_at?: string | null;
|
|
98
|
+
source_provider?: string | null;
|
|
99
|
+
provider_event_id?: string | null;
|
|
100
|
+
metadata?: Record<string, unknown>;
|
|
101
|
+
};
|
|
102
|
+
export type RecipeCrmExternalRef = {
|
|
103
|
+
provider: string;
|
|
104
|
+
object: string;
|
|
105
|
+
external_id: string;
|
|
106
|
+
pulled_at?: string | null;
|
|
107
|
+
provider_event_id?: string | null;
|
|
108
|
+
confidence?: number | null;
|
|
109
|
+
};
|
|
110
|
+
export type RecipeCrmApi = {
|
|
111
|
+
assert: <T = unknown>(object: string, identity: RecipeCrmIdentity, values: Record<string, unknown> | undefined, options: {
|
|
112
|
+
key: string;
|
|
113
|
+
}) => Promise<T>;
|
|
114
|
+
assertFromProvider: <T = unknown>(object: string, identity: RecipeCrmIdentity, values: Record<string, unknown> | undefined, external: RecipeCrmExternalRef, options: {
|
|
115
|
+
key: string;
|
|
116
|
+
}) => Promise<T>;
|
|
117
|
+
logActivity: <T = unknown>(input: RecipeCrmActivityInput, options: {
|
|
118
|
+
key: string;
|
|
119
|
+
}) => Promise<T>;
|
|
120
|
+
};
|
|
81
121
|
export type RecipeApprovalApi = {
|
|
82
122
|
require: <T = {
|
|
83
123
|
approved: boolean;
|
|
@@ -97,6 +137,7 @@ export type RecipeContext = {
|
|
|
97
137
|
rows: RecipeRowsApi;
|
|
98
138
|
columns: RecipeColumnsApi;
|
|
99
139
|
context: RecipeContextApi;
|
|
140
|
+
crm: RecipeCrmApi;
|
|
100
141
|
approvals: RecipeApprovalApi;
|
|
101
142
|
log: (level: RecipeLogLevel, message: string, payload?: Record<string, unknown>) => void;
|
|
102
143
|
step: <T = unknown>(key: string, options: RecipeStepOptions<T>) => Promise<T>;
|
|
@@ -13,6 +13,9 @@ export type PricingPlanDefinition = {
|
|
|
13
13
|
monthlyCredits: number | null;
|
|
14
14
|
weeklyCreditsLimit: number | null;
|
|
15
15
|
rolloverCap: number | null;
|
|
16
|
+
monthlyAutomationActions: number | null;
|
|
17
|
+
automationOverageCentsPerMillion: number | null;
|
|
18
|
+
automationOverageEnabledDefault: boolean;
|
|
16
19
|
byokEnabled: boolean;
|
|
17
20
|
description: string;
|
|
18
21
|
badge?: string;
|
|
@@ -31,10 +34,13 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
31
34
|
readonly monthlyCredits: 500;
|
|
32
35
|
readonly weeklyCreditsLimit: 500;
|
|
33
36
|
readonly rolloverCap: 500;
|
|
37
|
+
readonly monthlyAutomationActions: 10000;
|
|
38
|
+
readonly automationOverageCentsPerMillion: null;
|
|
39
|
+
readonly automationOverageEnabledDefault: false;
|
|
34
40
|
readonly byokEnabled: false;
|
|
35
41
|
readonly description: "Get $10 in credits on us every month — try OXYGEN with managed email, phone enrichment, and AI credits.";
|
|
36
42
|
readonly ctaLabel: "Start free";
|
|
37
|
-
readonly features: readonly ["$10 in credits every month", "All integrations", "Workflows", "No card required"];
|
|
43
|
+
readonly features: readonly ["$10 in credits every month", "10,000 automation actions / month", "All integrations", "Workflows", "No card required"];
|
|
38
44
|
};
|
|
39
45
|
readonly starter: {
|
|
40
46
|
readonly tier: "starter";
|
|
@@ -43,10 +49,13 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
43
49
|
readonly monthlyCredits: 5000;
|
|
44
50
|
readonly weeklyCreditsLimit: 2000;
|
|
45
51
|
readonly rolloverCap: 5000;
|
|
52
|
+
readonly monthlyAutomationActions: 250000;
|
|
53
|
+
readonly automationOverageCentsPerMillion: 1000;
|
|
54
|
+
readonly automationOverageEnabledDefault: false;
|
|
46
55
|
readonly byokEnabled: true;
|
|
47
56
|
readonly description: "For founders running focused managed enrichment and AI columns.";
|
|
48
57
|
readonly ctaLabel: "Choose Starter";
|
|
49
|
-
readonly features: readonly ["All integrations", "Bring your own keys", "Workflows", "Standard support"];
|
|
58
|
+
readonly features: readonly ["All integrations", "250,000 automation actions / month", "Bring your own keys", "Workflows", "Standard support"];
|
|
50
59
|
};
|
|
51
60
|
readonly pro: {
|
|
52
61
|
readonly tier: "pro";
|
|
@@ -55,12 +64,15 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
55
64
|
readonly monthlyCredits: 12500;
|
|
56
65
|
readonly weeklyCreditsLimit: 5000;
|
|
57
66
|
readonly rolloverCap: 12500;
|
|
67
|
+
readonly monthlyAutomationActions: 2000000;
|
|
68
|
+
readonly automationOverageCentsPerMillion: 1000;
|
|
69
|
+
readonly automationOverageEnabledDefault: false;
|
|
58
70
|
readonly byokEnabled: true;
|
|
59
71
|
readonly description: "For recurring GTM enrichment and AI-column workflows on managed credits.";
|
|
60
72
|
readonly badge: "Most popular";
|
|
61
73
|
readonly highlighted: true;
|
|
62
74
|
readonly ctaLabel: "Choose Pro";
|
|
63
|
-
readonly features: readonly ["All integrations", "Bring your own keys", "Workflows", "Standard support"];
|
|
75
|
+
readonly features: readonly ["All integrations", "2,000,000 automation actions / month", "Bring your own keys", "Workflows", "Standard support"];
|
|
64
76
|
};
|
|
65
77
|
readonly team: {
|
|
66
78
|
readonly tier: "team";
|
|
@@ -69,10 +81,13 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
69
81
|
readonly monthlyCredits: 37500;
|
|
70
82
|
readonly weeklyCreditsLimit: 15000;
|
|
71
83
|
readonly rolloverCap: 37500;
|
|
84
|
+
readonly monthlyAutomationActions: 10000000;
|
|
85
|
+
readonly automationOverageCentsPerMillion: 500;
|
|
86
|
+
readonly automationOverageEnabledDefault: false;
|
|
72
87
|
readonly byokEnabled: true;
|
|
73
88
|
readonly description: "For small GTM teams and agencies running shared workflows with their own AI keys.";
|
|
74
89
|
readonly ctaLabel: "Choose Team";
|
|
75
|
-
readonly features: readonly ["All integrations", "Bring your own keys", "Workflows", "Premium support"];
|
|
90
|
+
readonly features: readonly ["All integrations", "10,000,000 automation actions / month", "Bring your own keys", "Workflows", "Premium support"];
|
|
76
91
|
};
|
|
77
92
|
readonly scale: {
|
|
78
93
|
readonly tier: "scale";
|
|
@@ -81,11 +96,14 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
81
96
|
readonly monthlyCredits: 500000;
|
|
82
97
|
readonly weeklyCreditsLimit: 500000;
|
|
83
98
|
readonly rolloverCap: 500000;
|
|
99
|
+
readonly monthlyAutomationActions: 50000000;
|
|
100
|
+
readonly automationOverageCentsPerMillion: null;
|
|
101
|
+
readonly automationOverageEnabledDefault: false;
|
|
84
102
|
readonly byokEnabled: true;
|
|
85
103
|
readonly description: "For high-volume GTM teams that need custom limits and rollout support.";
|
|
86
104
|
readonly ctaLabel: "Contact sales";
|
|
87
105
|
readonly contactHref: "https://cal.com/tim-scheuer-mxbib9/45";
|
|
88
|
-
readonly features: readonly ["500,000+ managed credits / month", "Phone number enrichment on managed credits", "Custom provider routing and BYOK policies", "Dedicated onboarding"];
|
|
106
|
+
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"];
|
|
89
107
|
};
|
|
90
108
|
readonly enterprise: {
|
|
91
109
|
readonly tier: "enterprise";
|
|
@@ -94,11 +112,14 @@ export declare const BASE_PRICING_PLANS: {
|
|
|
94
112
|
readonly monthlyCredits: null;
|
|
95
113
|
readonly weeklyCreditsLimit: null;
|
|
96
114
|
readonly rolloverCap: null;
|
|
115
|
+
readonly monthlyAutomationActions: null;
|
|
116
|
+
readonly automationOverageCentsPerMillion: null;
|
|
117
|
+
readonly automationOverageEnabledDefault: false;
|
|
97
118
|
readonly byokEnabled: true;
|
|
98
119
|
readonly description: "Custom procurement, invoicing, limits, and rollout support.";
|
|
99
120
|
readonly ctaLabel: "Talk to us";
|
|
100
121
|
readonly contactHref: "https://cal.com/tim-scheuer-mxbib9/45";
|
|
101
|
-
readonly features: readonly ["Custom monthly usage pool", "Phone number enrichment", "Custom AI-key and provider routing policies", "SSO, invoicing, and procurement support"];
|
|
122
|
+
readonly features: readonly ["Custom monthly usage pool", "Custom automation action allowance", "Phone number enrichment", "Custom AI-key and provider routing policies", "SSO, invoicing, and procurement support"];
|
|
102
123
|
};
|
|
103
124
|
};
|
|
104
125
|
export declare const PUBLIC_PLAN_ORDER: readonly PlanTier[];
|
|
@@ -114,6 +135,7 @@ export declare function isSelfServePlanTier(value: string): value is SelfServePl
|
|
|
114
135
|
export declare function isBillingCurrency(value: string): value is BillingCurrency;
|
|
115
136
|
export declare function normalizeBillingCurrency(value: string | null | undefined): BillingCurrency;
|
|
116
137
|
export declare function getCurrentFreeTierCycleKey(date?: Date): string;
|
|
138
|
+
export declare function getCurrentBillingCycleKey(date?: Date): string;
|
|
117
139
|
export declare function evaluateWeeklyQuota(usedCredits: number, requestedCredits: number, weeklyCreditsLimit: number): {
|
|
118
140
|
usedCredits: number;
|
|
119
141
|
requestedCredits: number;
|
|
@@ -10,11 +10,15 @@ export const BASE_PRICING_PLANS = {
|
|
|
10
10
|
monthlyCredits: 500,
|
|
11
11
|
weeklyCreditsLimit: 500,
|
|
12
12
|
rolloverCap: 500,
|
|
13
|
+
monthlyAutomationActions: 10_000,
|
|
14
|
+
automationOverageCentsPerMillion: null,
|
|
15
|
+
automationOverageEnabledDefault: false,
|
|
13
16
|
byokEnabled: false,
|
|
14
17
|
description: "Get $10 in credits on us every month — try OXYGEN with managed email, phone enrichment, and AI credits.",
|
|
15
18
|
ctaLabel: "Start free",
|
|
16
19
|
features: [
|
|
17
20
|
"$10 in credits every month",
|
|
21
|
+
"10,000 automation actions / month",
|
|
18
22
|
"All integrations",
|
|
19
23
|
"Workflows",
|
|
20
24
|
"No card required",
|
|
@@ -27,11 +31,15 @@ export const BASE_PRICING_PLANS = {
|
|
|
27
31
|
monthlyCredits: 5_000,
|
|
28
32
|
weeklyCreditsLimit: 2_000,
|
|
29
33
|
rolloverCap: 5_000,
|
|
34
|
+
monthlyAutomationActions: 250_000,
|
|
35
|
+
automationOverageCentsPerMillion: 1_000,
|
|
36
|
+
automationOverageEnabledDefault: false,
|
|
30
37
|
byokEnabled: true,
|
|
31
38
|
description: "For founders running focused managed enrichment and AI columns.",
|
|
32
39
|
ctaLabel: "Choose Starter",
|
|
33
40
|
features: [
|
|
34
41
|
"All integrations",
|
|
42
|
+
"250,000 automation actions / month",
|
|
35
43
|
"Bring your own keys",
|
|
36
44
|
"Workflows",
|
|
37
45
|
"Standard support",
|
|
@@ -44,6 +52,9 @@ export const BASE_PRICING_PLANS = {
|
|
|
44
52
|
monthlyCredits: 12_500,
|
|
45
53
|
weeklyCreditsLimit: 5_000,
|
|
46
54
|
rolloverCap: 12_500,
|
|
55
|
+
monthlyAutomationActions: 2_000_000,
|
|
56
|
+
automationOverageCentsPerMillion: 1_000,
|
|
57
|
+
automationOverageEnabledDefault: false,
|
|
47
58
|
byokEnabled: true,
|
|
48
59
|
description: "For recurring GTM enrichment and AI-column workflows on managed credits.",
|
|
49
60
|
badge: "Most popular",
|
|
@@ -51,6 +62,7 @@ export const BASE_PRICING_PLANS = {
|
|
|
51
62
|
ctaLabel: "Choose Pro",
|
|
52
63
|
features: [
|
|
53
64
|
"All integrations",
|
|
65
|
+
"2,000,000 automation actions / month",
|
|
54
66
|
"Bring your own keys",
|
|
55
67
|
"Workflows",
|
|
56
68
|
"Standard support",
|
|
@@ -63,11 +75,15 @@ export const BASE_PRICING_PLANS = {
|
|
|
63
75
|
monthlyCredits: 37_500,
|
|
64
76
|
weeklyCreditsLimit: 15_000,
|
|
65
77
|
rolloverCap: 37_500,
|
|
78
|
+
monthlyAutomationActions: 10_000_000,
|
|
79
|
+
automationOverageCentsPerMillion: 500,
|
|
80
|
+
automationOverageEnabledDefault: false,
|
|
66
81
|
byokEnabled: true,
|
|
67
82
|
description: "For small GTM teams and agencies running shared workflows with their own AI keys.",
|
|
68
83
|
ctaLabel: "Choose Team",
|
|
69
84
|
features: [
|
|
70
85
|
"All integrations",
|
|
86
|
+
"10,000,000 automation actions / month",
|
|
71
87
|
"Bring your own keys",
|
|
72
88
|
"Workflows",
|
|
73
89
|
"Premium support",
|
|
@@ -80,12 +96,16 @@ export const BASE_PRICING_PLANS = {
|
|
|
80
96
|
monthlyCredits: 500_000,
|
|
81
97
|
weeklyCreditsLimit: 500_000,
|
|
82
98
|
rolloverCap: 500_000,
|
|
99
|
+
monthlyAutomationActions: 50_000_000,
|
|
100
|
+
automationOverageCentsPerMillion: null,
|
|
101
|
+
automationOverageEnabledDefault: false,
|
|
83
102
|
byokEnabled: true,
|
|
84
103
|
description: "For high-volume GTM teams that need custom limits and rollout support.",
|
|
85
104
|
ctaLabel: "Contact sales",
|
|
86
105
|
contactHref: CONTACT_SALES_URL,
|
|
87
106
|
features: [
|
|
88
107
|
"500,000+ managed credits / month",
|
|
108
|
+
"50,000,000+ automation actions / month",
|
|
89
109
|
"Phone number enrichment on managed credits",
|
|
90
110
|
"Custom provider routing and BYOK policies",
|
|
91
111
|
"Dedicated onboarding",
|
|
@@ -98,12 +118,16 @@ export const BASE_PRICING_PLANS = {
|
|
|
98
118
|
monthlyCredits: null,
|
|
99
119
|
weeklyCreditsLimit: null,
|
|
100
120
|
rolloverCap: null,
|
|
121
|
+
monthlyAutomationActions: null,
|
|
122
|
+
automationOverageCentsPerMillion: null,
|
|
123
|
+
automationOverageEnabledDefault: false,
|
|
101
124
|
byokEnabled: true,
|
|
102
125
|
description: "Custom procurement, invoicing, limits, and rollout support.",
|
|
103
126
|
ctaLabel: "Talk to us",
|
|
104
127
|
contactHref: CONTACT_SALES_URL,
|
|
105
128
|
features: [
|
|
106
129
|
"Custom monthly usage pool",
|
|
130
|
+
"Custom automation action allowance",
|
|
107
131
|
"Phone number enrichment",
|
|
108
132
|
"Custom AI-key and provider routing policies",
|
|
109
133
|
"SSO, invoicing, and procurement support",
|
|
@@ -229,6 +253,11 @@ plan, metadata) {
|
|
|
229
253
|
const monthlyCredits = readCreditLimit(config, "monthlyCredits", "monthly_credits", "monthly_credit_limit");
|
|
230
254
|
const weeklyCreditsLimit = readCreditLimit(config, "weeklyCreditsLimit", "weekly_credits_limit", "weekly_credit_limit");
|
|
231
255
|
const rolloverCap = readCreditLimit(config, "rolloverCap", "rollover_cap", "rollover_credits");
|
|
256
|
+
const monthlyAutomationActions = readCreditLimit(config, "monthlyAutomationActions", "monthly_automation_actions", "automation_actions", "automation_action_limit");
|
|
257
|
+
const automationOverageCentsPerMillion = readCreditLimit(config, "automationOverageCentsPerMillion", "automation_overage_cents_per_million", "automation_action_overage_cents_per_million");
|
|
258
|
+
const automationOverageEnabledDefault = readBoolean(config, "automationOverageEnabledDefault")
|
|
259
|
+
?? readBoolean(config, "automation_overage_enabled_default")
|
|
260
|
+
?? readBoolean(config, "automation_overage_enabled");
|
|
232
261
|
const byokEnabled = readBoolean(config, "byokEnabled")
|
|
233
262
|
?? readBoolean(config, "byok_enabled");
|
|
234
263
|
return {
|
|
@@ -237,6 +266,9 @@ plan, metadata) {
|
|
|
237
266
|
monthlyCredits: monthlyCredits ?? plan.monthlyCredits,
|
|
238
267
|
weeklyCreditsLimit: weeklyCreditsLimit ?? plan.weeklyCreditsLimit,
|
|
239
268
|
rolloverCap: rolloverCap ?? plan.rolloverCap,
|
|
269
|
+
monthlyAutomationActions: monthlyAutomationActions ?? plan.monthlyAutomationActions,
|
|
270
|
+
automationOverageCentsPerMillion: automationOverageCentsPerMillion ?? plan.automationOverageCentsPerMillion,
|
|
271
|
+
automationOverageEnabledDefault: automationOverageEnabledDefault ?? plan.automationOverageEnabledDefault,
|
|
240
272
|
byokEnabled: byokEnabled ?? plan.byokEnabled,
|
|
241
273
|
description: description ?? plan.description,
|
|
242
274
|
};
|
|
@@ -284,6 +316,15 @@ export function getCurrentFreeTierCycleKey(date = new Date()) {
|
|
|
284
316
|
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
285
317
|
return `free:${year}-${month}`;
|
|
286
318
|
}
|
|
319
|
+
// Calendar-month cycle key (UTC `YYYY-MM`) for off-Stripe custom plans. Stripe
|
|
320
|
+
// subscriptions advance their billing period via webhooks; manual/invoiced
|
|
321
|
+
// plans never do, so we renew their monthly credit grant by calendar month
|
|
322
|
+
// instead of by Stripe period boundary.
|
|
323
|
+
export function getCurrentBillingCycleKey(date = new Date()) {
|
|
324
|
+
const year = date.getUTCFullYear();
|
|
325
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
326
|
+
return `${year}-${month}`;
|
|
327
|
+
}
|
|
287
328
|
export function evaluateWeeklyQuota(usedCredits, requestedCredits, weeklyCreditsLimit) {
|
|
288
329
|
const projectedCredits = usedCredits + requestedCredits;
|
|
289
330
|
return {
|
|
@@ -8,6 +8,7 @@ export * from "./cli-result.js";
|
|
|
8
8
|
export * from "./column-types.js";
|
|
9
9
|
export * from "./credit-guidance.js";
|
|
10
10
|
export * from "./linkedin-sequences.js";
|
|
11
|
+
export * from "./networks.js";
|
|
11
12
|
export * from "./sequences.js";
|
|
12
13
|
export * from "./log.js";
|
|
13
14
|
export * from "./provider-request-outcomes.js";
|
|
@@ -8,6 +8,7 @@ export * from "./cli-result.js";
|
|
|
8
8
|
export * from "./column-types.js";
|
|
9
9
|
export * from "./credit-guidance.js";
|
|
10
10
|
export * from "./linkedin-sequences.js";
|
|
11
|
+
export * from "./networks.js";
|
|
11
12
|
export * from "./sequences.js";
|
|
12
13
|
export * from "./log.js";
|
|
13
14
|
export * from "./provider-request-outcomes.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sequencer networks — the channel discriminator for the Unipile-backed sender
|
|
3
|
+
* pool, unibox, and per-account daily quota ledger (schema `ox_sequencer`).
|
|
4
|
+
*
|
|
5
|
+
* The sequencer primitives (sender_accounts, conversations, messages,
|
|
6
|
+
* sender_action_counts) are channel-agnostic; `network` tells one Unipile
|
|
7
|
+
* network from another. LinkedIn is the original network and the default for
|
|
8
|
+
* every row/call site that predates the discriminator, so a missing/unknown
|
|
9
|
+
* value always resolves to `"linkedin"` (zero behaviour change). WhatsApp is the
|
|
10
|
+
* second network and is warm-only (no cold sequencer dispatch).
|
|
11
|
+
*
|
|
12
|
+
* Email is intentionally NOT a sequencer network: it has its own mailbox and
|
|
13
|
+
* conversation tables (`email_mailboxes`, `email_conversations`, …).
|
|
14
|
+
*/
|
|
15
|
+
export declare const SEQUENCER_NETWORKS: readonly ["linkedin", "whatsapp"];
|
|
16
|
+
export type SequencerNetwork = (typeof SEQUENCER_NETWORKS)[number];
|
|
17
|
+
/** The network assumed for any row/call site that predates the discriminator. */
|
|
18
|
+
export declare const DEFAULT_SEQUENCER_NETWORK: SequencerNetwork;
|
|
19
|
+
export declare function isSequencerNetwork(value: unknown): value is SequencerNetwork;
|
|
20
|
+
/** Coerce an unknown/null DB value to a network, defaulting to LinkedIn. */
|
|
21
|
+
export declare function coerceSequencerNetwork(value: unknown): SequencerNetwork;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sequencer networks — the channel discriminator for the Unipile-backed sender
|
|
3
|
+
* pool, unibox, and per-account daily quota ledger (schema `ox_sequencer`).
|
|
4
|
+
*
|
|
5
|
+
* The sequencer primitives (sender_accounts, conversations, messages,
|
|
6
|
+
* sender_action_counts) are channel-agnostic; `network` tells one Unipile
|
|
7
|
+
* network from another. LinkedIn is the original network and the default for
|
|
8
|
+
* every row/call site that predates the discriminator, so a missing/unknown
|
|
9
|
+
* value always resolves to `"linkedin"` (zero behaviour change). WhatsApp is the
|
|
10
|
+
* second network and is warm-only (no cold sequencer dispatch).
|
|
11
|
+
*
|
|
12
|
+
* Email is intentionally NOT a sequencer network: it has its own mailbox and
|
|
13
|
+
* conversation tables (`email_mailboxes`, `email_conversations`, …).
|
|
14
|
+
*/
|
|
15
|
+
export const SEQUENCER_NETWORKS = ["linkedin", "whatsapp"];
|
|
16
|
+
/** The network assumed for any row/call site that predates the discriminator. */
|
|
17
|
+
export const DEFAULT_SEQUENCER_NETWORK = "linkedin";
|
|
18
|
+
export function isSequencerNetwork(value) {
|
|
19
|
+
return (typeof value === "string" &&
|
|
20
|
+
SEQUENCER_NETWORKS.includes(value));
|
|
21
|
+
}
|
|
22
|
+
/** Coerce an unknown/null DB value to a network, defaulting to LinkedIn. */
|
|
23
|
+
export function coerceSequencerNetwork(value) {
|
|
24
|
+
return isSequencerNetwork(value) ? value : DEFAULT_SEQUENCER_NETWORK;
|
|
25
|
+
}
|
|
@@ -2,7 +2,7 @@ export declare function normalizeText(value: string): string;
|
|
|
2
2
|
export declare function escapeRegExp(value: string): string;
|
|
3
3
|
export declare function extractUrls(value: string): string[];
|
|
4
4
|
export declare function extractDomains(value: string): string[];
|
|
5
|
-
export declare function parseMoney(amount: string, unit
|
|
5
|
+
export declare function parseMoney(amount: string, unit?: string): number;
|
|
6
6
|
export declare function regionDisplayName(code: string): string | null;
|
|
7
7
|
export declare const COUNTRY_NAME_TO_ISO: Map<string, string>;
|
|
8
8
|
export declare const COUNTRY_ALIAS_TO_ISO: Record<string, string>;
|
|
@@ -315,7 +315,7 @@ export declare function selectStepVariant(step: SequenceStep, key: string): {
|
|
|
315
315
|
* "recipient" mode reads the lead's tz from `recipient_timezone_column`, falling
|
|
316
316
|
* back to the window's fixed `timezone` when the column is absent/blank.
|
|
317
317
|
*/
|
|
318
|
-
export declare function resolveSendWindowTimezone(window: SequenceSendWindow, rowValues
|
|
318
|
+
export declare function resolveSendWindowTimezone(window: SequenceSendWindow, rowValues?: Record<string, unknown> | null): string;
|
|
319
319
|
/**
|
|
320
320
|
* Is `now` inside the send window, evaluated in `timezone` (already resolved for
|
|
321
321
|
* recipient mode via resolveSendWindowTimezone)? Pure; Intl only
|
|
@@ -628,7 +628,7 @@ export function resolveSendWindowTimezone(window, rowValues) {
|
|
|
628
628
|
* start>end wraps past midnight.
|
|
629
629
|
*/
|
|
630
630
|
export function isWithinSendWindow(window, now, timezone) {
|
|
631
|
-
const tz = timezone
|
|
631
|
+
const tz = timezone?.trim() ? timezone.trim() : window.timezone;
|
|
632
632
|
const parts = tzParts(now, tz) ?? tzParts(now, window.timezone) ?? tzParts(now, "UTC");
|
|
633
633
|
if (!parts)
|
|
634
634
|
return true; // unresolvable tz → fail open (caps still apply)
|
|
@@ -714,6 +714,14 @@ function normalizeSendWindow(raw, path, issues) {
|
|
|
714
714
|
issues.push({ path: `${path}.timezone`, message: "timezone (IANA, e.g. America/New_York) is required." });
|
|
715
715
|
const start = normalizeHhmm(raw.start, `${path}.start`, issues);
|
|
716
716
|
const end = normalizeHhmm(raw.end, `${path}.end`, issues);
|
|
717
|
+
// A zero-width window (start === end) is never open — isWithinSendWindow
|
|
718
|
+
// returns false for every instant — so a step carrying it defers
|
|
719
|
+
// (outside_send_window) on every dispatch and the enrollment silently stalls
|
|
720
|
+
// forever (nothing sends, so the send cap never advances it). Reject it at
|
|
721
|
+
// write time instead of letting the misconfiguration strand the sequence.
|
|
722
|
+
if (start !== undefined && end !== undefined && hhmmToMinutes(start) === hhmmToMinutes(end)) {
|
|
723
|
+
issues.push({ path: `${path}.end`, message: "must differ from start — a zero-width send_window never sends." });
|
|
724
|
+
}
|
|
717
725
|
const days = normalizeWeekdays(raw.days, `${path}.days`, issues);
|
|
718
726
|
const mode = raw.timezone_mode === "recipient" ? "recipient" : "fixed";
|
|
719
727
|
const column = mode === "recipient"
|
|
@@ -14,7 +14,7 @@ export declare function isOxygenHostname(hostname: string): boolean;
|
|
|
14
14
|
* internal secrets; external hosts with Oxygen-looking paths are "external" and
|
|
15
15
|
* must never be trusted with the internal webhook secret.
|
|
16
16
|
*/
|
|
17
|
-
export declare function classifySignupLeadWebhookTarget(value
|
|
17
|
+
export declare function classifySignupLeadWebhookTarget(value?: string | null): SignupLeadWebhookTarget;
|
|
18
18
|
/** HMAC-SHA256 hex digest of the request body keyed by the webhook secret. */
|
|
19
19
|
export declare function signSignupLeadWebhookBody(secret: string, body: string): string;
|
|
20
20
|
export type BuildSignupLeadWebhookHeadersInput = {
|
|
@@ -44,6 +44,6 @@ export declare function redactSqlParameters(text: string): string;
|
|
|
44
44
|
* exhausted. Reuses the same SQLSTATE classifier as the telemetry helpers so the
|
|
45
45
|
* 40001/40P01 knowledge lives in one place. Never throws.
|
|
46
46
|
*/
|
|
47
|
-
export declare function isRetryableConcurrencyError(error
|
|
47
|
+
export declare function isRetryableConcurrencyError(error?: unknown): boolean;
|
|
48
48
|
/** Telemetry-attribute shape (dotted keys) for span error attribution. */
|
|
49
49
|
export declare function sqlErrorTelemetryAttributes(error: unknown): Record<string, unknown>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.209.23";
|
|
2
2
|
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.209.23";
|
|
2
2
|
// Bump this only when deployed CLI/API contracts require a newer CLI.
|
|
3
3
|
// 1.181.0: paid table action runs and background columns run require
|
|
4
4
|
// approved=true in addition to max_credits; older CLIs cannot send the flag.
|
|
@@ -5,10 +5,7 @@ export const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS = [
|
|
|
5
5
|
"auto_paused_at",
|
|
6
6
|
"auto_pause_reason",
|
|
7
7
|
];
|
|
8
|
+
const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEY_SET = new Set(WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS);
|
|
8
9
|
export function clearWorkflowTriggerAutoPauseMetadata(metadata) {
|
|
9
|
-
|
|
10
|
-
for (const key of WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS) {
|
|
11
|
-
delete next[key];
|
|
12
|
-
}
|
|
13
|
-
return next;
|
|
10
|
+
return Object.fromEntries(Object.entries(metadata).filter(([key]) => !WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEY_SET.has(key)));
|
|
14
11
|
}
|
|
@@ -828,6 +828,7 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
|
|
|
828
828
|
export declare function computeWorkflowPlanHash(input: Record<string, unknown>): string;
|
|
829
829
|
export declare const WORKFLOW_MAX_CREDITS_EXCEEDED_ERROR_CODE = "max_credits_exceeded";
|
|
830
830
|
export declare function readWorkflowRunMaxCredits(metadata: Record<string, unknown> | null | undefined): number | null;
|
|
831
|
+
export declare function readWorkflowRunPreApprovedLabels(metadata: Record<string, unknown> | null | undefined): string[];
|
|
831
832
|
export declare function readManagedToolRunCredits(output: unknown): number;
|
|
832
833
|
export type WorkflowSpendCapDecision = {
|
|
833
834
|
allowed: boolean;
|
|
@@ -112,12 +112,24 @@ export function nextCronRunAfter(input) {
|
|
|
112
112
|
// If the calendar date itself can't match, no minute that day matches —
|
|
113
113
|
// jump to the next day instead of scanning all 1440 minutes. This keeps
|
|
114
114
|
// sparse schedules (e.g. "0 9 29 2 *") and unsatisfiable ones fast over the
|
|
115
|
-
// multi-year window. The
|
|
116
|
-
//
|
|
117
|
-
// earlier minutes).
|
|
115
|
+
// multi-year window. The jump targets ~00:00 of the next day, so a
|
|
116
|
+
// subsequently matching date is scanned from its start (no missed minutes).
|
|
118
117
|
if (!matchesCronDate(schedule, parts)) {
|
|
119
118
|
const minutesToNextDay = Math.max(1, 24 * 60 - (parts.hour * 60 + parts.minute));
|
|
120
|
-
|
|
119
|
+
let next = new Date(candidate.getTime() + minutesToNextDay * 60_000);
|
|
120
|
+
// minutesToNextDay is a LOCAL-clock count added to a UTC instant. On a
|
|
121
|
+
// spring-forward day the local day is < 24h, so the jump overshoots the
|
|
122
|
+
// next local midnight and lands at ~01:00 — skipping an early-of-day match
|
|
123
|
+
// (e.g. "0 0 …"), which can push the next run a full year out. If the jump
|
|
124
|
+
// crossed into the next local day but past midnight, pull it back to that
|
|
125
|
+
// midnight. (A fall-back day is > 24h: the jump undershoots and stays on
|
|
126
|
+
// the same date, which the next iteration advances — left as-is.)
|
|
127
|
+
const landed = zonedDateParts(next, timezone);
|
|
128
|
+
const intoNextLocalDay = landed.hour * 60 + landed.minute;
|
|
129
|
+
if (landed.dayOfMonth !== parts.dayOfMonth && intoNextLocalDay > 0) {
|
|
130
|
+
next = new Date(next.getTime() - intoNextLocalDay * 60_000);
|
|
131
|
+
}
|
|
132
|
+
candidate = next;
|
|
121
133
|
}
|
|
122
134
|
else {
|
|
123
135
|
candidate = new Date(candidate.getTime() + 60_000);
|
|
@@ -1424,7 +1436,7 @@ function isValidTimeZone(timezone) {
|
|
|
1424
1436
|
try {
|
|
1425
1437
|
// Throws RangeError for an unknown IANA zone; same mechanism the scheduler
|
|
1426
1438
|
// uses, so lint accepts exactly the zones the runtime can resolve.
|
|
1427
|
-
new Intl.DateTimeFormat("en-US", { timeZone: timezone });
|
|
1439
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format(0);
|
|
1428
1440
|
return true;
|
|
1429
1441
|
}
|
|
1430
1442
|
catch {
|
|
@@ -1557,6 +1569,28 @@ export function readWorkflowRunMaxCredits(metadata) {
|
|
|
1557
1569
|
return null;
|
|
1558
1570
|
return parsed;
|
|
1559
1571
|
}
|
|
1572
|
+
// Approval labels the run's enqueue route already collected SYNCHRONOUS human
|
|
1573
|
+
// approval for (a CLI/MCP `approved=true` gate before the run was created), and
|
|
1574
|
+
// recorded in run metadata as `preapproved_labels`. A live ctx.approvals.require()
|
|
1575
|
+
// call whose `label` is in this set is honored immediately by both recipe
|
|
1576
|
+
// executors instead of parking on a durable decision row (local) or failing
|
|
1577
|
+
// closed with approval_requires_local_executor (sandbox). Every other label
|
|
1578
|
+
// still goes through the full durable gate, and the max_credits spend cap is
|
|
1579
|
+
// enforced separately per paid tool step, so a pre-approved label never widens
|
|
1580
|
+
// spend. Only trusted server code (the enqueue route) sets this — it is read
|
|
1581
|
+
// from run metadata, never from user-suppliable workflow input.
|
|
1582
|
+
export function readWorkflowRunPreApprovedLabels(metadata) {
|
|
1583
|
+
if (!isRecord(metadata))
|
|
1584
|
+
return [];
|
|
1585
|
+
const raw = metadata.preapproved_labels ?? metadata.preApprovedLabels;
|
|
1586
|
+
if (!Array.isArray(raw))
|
|
1587
|
+
return [];
|
|
1588
|
+
const labels = raw
|
|
1589
|
+
.filter((entry) => typeof entry === "string")
|
|
1590
|
+
.map((entry) => entry.trim())
|
|
1591
|
+
.filter((entry) => entry.length > 0);
|
|
1592
|
+
return Array.from(new Set(labels));
|
|
1593
|
+
}
|
|
1560
1594
|
// Managed tool runs attach `meta.billing` with the credits the run charged
|
|
1561
1595
|
// (`managed_credit_estimate` is reserved and captured in full). BYOK and
|
|
1562
1596
|
// user-connection runs consume no OXYGEN credits, so they never count
|
package/oxygen.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxygen-agent/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.209.23",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"directory": "packages/cli"
|
|
13
13
|
},
|
|
14
14
|
"bin": {
|
|
15
|
-
"oxygen": "./
|
|
15
|
+
"oxygen": "./oxygen.js"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|