@oxygen-agent/cli 1.691.5 → 1.692.5
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/command-manifest.js +24 -14
- package/dist/index.js +30 -1
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +48 -0
- package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/mailbox-import.js +36 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/command-manifest.js
CHANGED
|
@@ -46,6 +46,13 @@ const MUTATING_VERBS = new Set([
|
|
|
46
46
|
"unbind", "unsubscribe", "update", "upload", "upsert", "use", "warm-send", "warmup",
|
|
47
47
|
"withdraw", "write",
|
|
48
48
|
]);
|
|
49
|
+
// `--approved` usually identifies a paid or externally mutating execution
|
|
50
|
+
// mode, but a small number of destructive local operations are explicitly
|
|
51
|
+
// zero-credit. Keep those exceptions exact so discovery never invents spend.
|
|
52
|
+
const ZERO_CREDIT_APPROVAL_COMMANDS = new Set(["mailboxes delete"]);
|
|
53
|
+
// These commands preview when the execution approval is omitted even though
|
|
54
|
+
// their gate is named `--approved` rather than `--live`.
|
|
55
|
+
const APPROVAL_PREVIEW_COMMANDS = new Set(["mailboxes delete"]);
|
|
49
56
|
export function buildCommandManifest(program, binaryName) {
|
|
50
57
|
const commands = [];
|
|
51
58
|
for (const child of program.commands) {
|
|
@@ -172,8 +179,9 @@ function toManifestEntry(command, path) {
|
|
|
172
179
|
const flagStrings = options.map((option) => option.flags);
|
|
173
180
|
const leafVerb = path[path.length - 1] ?? "";
|
|
174
181
|
const verbPrefix = leafVerb.split("-")[0] ?? leafVerb;
|
|
182
|
+
const commandName = path.join(" ");
|
|
175
183
|
return {
|
|
176
|
-
name:
|
|
184
|
+
name: commandName,
|
|
177
185
|
group: path[0] ?? "",
|
|
178
186
|
description: command.description(),
|
|
179
187
|
arguments: command.registeredArguments.map((argument) => ({
|
|
@@ -186,20 +194,22 @@ function toManifestEntry(command, path) {
|
|
|
186
194
|
description: option.description,
|
|
187
195
|
required: option.mandatory,
|
|
188
196
|
})),
|
|
189
|
-
spends_credits:
|
|
190
|
-
flags.includes("--
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(
|
|
197
|
+
spends_credits: !ZERO_CREDIT_APPROVAL_COMMANDS.has(commandName) &&
|
|
198
|
+
(flagStrings.some((flags) => flags.includes("--approved") ||
|
|
199
|
+
flags.includes("--max-credits") ||
|
|
200
|
+
// Copilot's explicit session inference-budget approval flag.
|
|
201
|
+
flags.includes("--budget-credits")) ||
|
|
202
|
+
// `copilot send` bills managed inference (5x actual model cost) inside the
|
|
203
|
+
// session budget approved at `copilot start`, and `copilot approve`
|
|
204
|
+
// dispatches the gated paid/external capability and requeues the paused
|
|
205
|
+
// turn (waking the worker for more 5x-billed inference) — no per-command
|
|
206
|
+
// cap flag exists for either, so the flag heuristic alone would misreport
|
|
207
|
+
// them as free.
|
|
208
|
+
(path[0] === "copilot" &&
|
|
209
|
+
(leafVerb === "send" || leafVerb === "approve"))),
|
|
201
210
|
mutates: MUTATING_VERBS.has(leafVerb) || MUTATING_VERBS.has(verbPrefix),
|
|
202
|
-
preview_by_default: flagStrings.some((flags) => flags.includes("--live"))
|
|
211
|
+
preview_by_default: flagStrings.some((flags) => flags.includes("--live")) ||
|
|
212
|
+
APPROVAL_PREVIEW_COMMANDS.has(commandName),
|
|
203
213
|
json_supported: flagStrings.some((flags) => flags.includes("--json")),
|
|
204
214
|
hidden: isHiddenCommand(command),
|
|
205
215
|
};
|
package/dist/index.js
CHANGED
|
@@ -12704,6 +12704,35 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
12704
12704
|
.option("--json", "Print a JSON envelope.")
|
|
12705
12705
|
.action(async (mailbox, options) => {
|
|
12706
12706
|
await handleAsyncAction("mailboxes get", options, () => requestOxygen(`/api/cli/mailboxes/${encodeURIComponent(mailbox)}`));
|
|
12707
|
+
}))
|
|
12708
|
+
.addCommand(new Command("delete")
|
|
12709
|
+
.description("Preview or approve removal of exact mailboxes from Oxygen at 0 credits. Deletion immediately removes them from sending but never deletes the underlying Google Workspace or Microsoft 365 accounts. It preserves conversation/message/delivery history and stops the 1,000-credit mailbox commitment for future renewals; the current period is not refunded. Listed active/paused sequences keep their status but lose these senders and are not automatically paused. Managed mailboxes and live warmup/monitoring add-ons fail closed with their exact address-scoped teardown steps.")
|
|
12710
|
+
.requiredOption("--mailboxes <list>", "Comma-separated mailbox ids or addresses (maximum 500).")
|
|
12711
|
+
.option("--approved", "Execute the fresh preview. Requires --plan-hash and --confirmation.")
|
|
12712
|
+
.option("--plan-hash <hash>", "Fresh preview plan_hash.")
|
|
12713
|
+
.option("--confirmation <phrase>", "Exact confirmation_phrase returned by the fresh preview.")
|
|
12714
|
+
.option("--json", "Print a JSON envelope.")
|
|
12715
|
+
.action(async (options) => {
|
|
12716
|
+
await handleAsyncAction("mailboxes delete", options, () => {
|
|
12717
|
+
const mailboxes = readCsvOption(options.mailboxes);
|
|
12718
|
+
if (mailboxes.length === 0) {
|
|
12719
|
+
throw new Error("--mailboxes must contain at least one mailbox id or address.");
|
|
12720
|
+
}
|
|
12721
|
+
const planHash = readOption(options.planHash);
|
|
12722
|
+
const confirmation = readOption(options.confirmation);
|
|
12723
|
+
if (options.approved === true && (!planHash || !confirmation)) {
|
|
12724
|
+
throw new Error("--approved requires --plan-hash and --confirmation from a fresh deletion preview.");
|
|
12725
|
+
}
|
|
12726
|
+
return requestOxygen("/api/cli/mailboxes/delete", {
|
|
12727
|
+
method: "POST",
|
|
12728
|
+
body: {
|
|
12729
|
+
mailboxes,
|
|
12730
|
+
...(options.approved === true ? { approved: true } : {}),
|
|
12731
|
+
...(planHash ? { plan_hash: planHash } : {}),
|
|
12732
|
+
...(confirmation ? { confirmation } : {}),
|
|
12733
|
+
},
|
|
12734
|
+
});
|
|
12735
|
+
});
|
|
12707
12736
|
}))
|
|
12708
12737
|
.addCommand(new Command("health")
|
|
12709
12738
|
.description("Fleet email-health: the sending pool rolled up by external deliverability reputation (healthy/degraded/critical/unknown), per-mailbox scores, connected health providers, and DIRECTIONAL recommendations. Pure read — 0 credits; never pauses a mailbox.")
|
|
@@ -12746,7 +12775,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
12746
12775
|
"Credential file contract:",
|
|
12747
12776
|
' JSON: {"mailboxes":[{"email_address":"ada@send-acme.com","provider":"google","app_password":"<Google mailbox app password>"}]}',
|
|
12748
12777
|
" Only Google app passwords enter the encrypted seven-day transfer vault. Microsoft rows remain identity-only. Generic SMTP passwords and OAuth/MFA/delegation secrets are rejected.",
|
|
12749
|
-
" Validation: add --validate-only to parse the complete real file and return safe aggregate counts without authentication, a network request, or a workspace write. Any parse, shape, provider, platform, tenant, secret-policy, or duplicate-conflict error rejects the entire file before the first mailbox write and names mailboxes[index]; validation-only never writes. A later import infrastructure failure may interrupt the upsert; re-run the same file because import is idempotent by address.",
|
|
12778
|
+
" Validation: add --validate-only to parse the complete real file and return safe aggregate counts plus the provider-specific exact-account OAuth review plan without authentication, a network request, or a workspace write. The plan groups only supplied addresses into reviews of at most 10; it never discovers a domain, and the later online preview may skip existing grants. Any parse, shape, provider, platform, tenant, secret-policy, or duplicate-conflict error rejects the entire file before the first mailbox write and names mailboxes[index]; validation-only never writes. A later import infrastructure failure may interrupt the upsert; re-run the same file because import is idempotent by address.",
|
|
12750
12779
|
" Docs: https://oxygen-agent.com/docs/providers/mailbox-compatibility",
|
|
12751
12780
|
" Skill: oxygen-email-infra (`oxygen skills install --skill oxygen-email-infra`).",
|
|
12752
12781
|
"",
|
|
@@ -512,6 +512,12 @@ function normalizeIntent(query) {
|
|
|
512
512
|
return query.toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
513
513
|
}
|
|
514
514
|
function explicitCapabilityIntent(query) {
|
|
515
|
+
if (isMailboxDeleteIntent(query)) {
|
|
516
|
+
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
517
|
+
}
|
|
518
|
+
if (isMailboxOnboardingIntent(query)) {
|
|
519
|
+
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
520
|
+
}
|
|
515
521
|
if (isHostedWorkflowIntent(query))
|
|
516
522
|
return ROUTE_BY_PRIMITIVE.get("workflows") ?? null;
|
|
517
523
|
if (isOwnedPostCommentIntent(query))
|
|
@@ -603,6 +609,36 @@ function highestScoringRoute(query) {
|
|
|
603
609
|
return best?.card ?? null;
|
|
604
610
|
}
|
|
605
611
|
function recommendationsFor(card, query) {
|
|
612
|
+
if (card.id === "sending-infrastructure" && isMailboxDeleteIntent(query)) {
|
|
613
|
+
return {
|
|
614
|
+
tools: [
|
|
615
|
+
"oxygen_mailboxes_delete",
|
|
616
|
+
"oxygen_mailboxes_compatibility",
|
|
617
|
+
"oxygen_mailboxes_list",
|
|
618
|
+
],
|
|
619
|
+
commands: [
|
|
620
|
+
"mailboxes delete",
|
|
621
|
+
"mailboxes compatibility",
|
|
622
|
+
"mailboxes list",
|
|
623
|
+
],
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
if (card.id === "sending-infrastructure" && isMailboxOnboardingIntent(query)) {
|
|
627
|
+
return {
|
|
628
|
+
tools: [
|
|
629
|
+
"oxygen_mailboxes_compatibility",
|
|
630
|
+
"oxygen_mailboxes_import",
|
|
631
|
+
"oxygen_mailboxes_connect_oauth",
|
|
632
|
+
"oxygen_mailboxes_oauth_health",
|
|
633
|
+
],
|
|
634
|
+
commands: [
|
|
635
|
+
"mailboxes compatibility",
|
|
636
|
+
"mailboxes import",
|
|
637
|
+
"mailboxes connect-oauth",
|
|
638
|
+
"mailboxes oauth-health",
|
|
639
|
+
],
|
|
640
|
+
};
|
|
641
|
+
}
|
|
606
642
|
if (card.primitive === "posts" && isOwnedPostCommentIntent(query)) {
|
|
607
643
|
return {
|
|
608
644
|
tools: ["oxygen_publishing_comments", "oxygen_publishing_analytics"],
|
|
@@ -782,6 +818,18 @@ function recommendationsFor(card, query) {
|
|
|
782
818
|
}
|
|
783
819
|
return { tools: [...card.gatewayTools], commands: [...card.gatewayCommands] };
|
|
784
820
|
}
|
|
821
|
+
function isMailboxOnboardingIntent(query) {
|
|
822
|
+
const operation = /\b(import|migrat\w*|onboard|register|upload|bring|connect existing|add existing)\b/.test(query);
|
|
823
|
+
const mailboxScope = /\b(mailbox(?:es)?|inbox(?:es)?|sender accounts?|email accounts?|sending pool)\b/.test(query);
|
|
824
|
+
return operation && mailboxScope;
|
|
825
|
+
}
|
|
826
|
+
function isMailboxDeleteIntent(query) {
|
|
827
|
+
const mailboxScope = /\b(mailbox(?:es)?|inbox(?:es)?|sender accounts?|email accounts?)\b/.test(query);
|
|
828
|
+
const removal = /\b(delete|remove)\b/.test(query)
|
|
829
|
+
|| /\bdisconnect\b.{0,24}\b(mailbox(?:es)?|inbox(?:es)?|sender accounts?|email accounts?)\b/.test(query);
|
|
830
|
+
const scopedDetach = /\b(remove|disconnect)\b.{0,48}\b(mailbox(?:es)?|inbox(?:es)?|sender accounts?|email accounts?)\b.{0,24}\b(?:from|in|on)\b.{0,24}\b(sequence|campaign|cadence|sender profile)\b/.test(query);
|
|
831
|
+
return mailboxScope && removal && !scopedDetach;
|
|
832
|
+
}
|
|
785
833
|
function isNetNewLinkedInInitiation(query) {
|
|
786
834
|
const mentionsLinkedIn = /\blinkedin\b|\bdm\b/.test(query);
|
|
787
835
|
const startsConversation = /\b(send|message|dm|contact|reach out|initiate|start)\b/.test(query)
|
|
@@ -21,6 +21,7 @@ export type MailboxImportValidationSummary = {
|
|
|
21
21
|
infrastructure_platforms: Record<string, number>;
|
|
22
22
|
credential_rows: number;
|
|
23
23
|
identity_only_rows: number;
|
|
24
|
+
oauth_review_plan: MailboxOAuthReviewPlan;
|
|
24
25
|
limits: {
|
|
25
26
|
max_rows: number;
|
|
26
27
|
max_file_bytes: number;
|
|
@@ -31,8 +32,26 @@ export type MailboxImportValidationSummary = {
|
|
|
31
32
|
credits_used: 0;
|
|
32
33
|
next_action: string;
|
|
33
34
|
};
|
|
35
|
+
export type MailboxOAuthProviderReviewPlan = {
|
|
36
|
+
mailboxes: number;
|
|
37
|
+
batches: number;
|
|
38
|
+
batch_sizes: number[];
|
|
39
|
+
};
|
|
40
|
+
export type MailboxOAuthReviewPlan = {
|
|
41
|
+
authorization: "exact_account_oauth";
|
|
42
|
+
scope: "validated_addresses";
|
|
43
|
+
directory_discovery: false;
|
|
44
|
+
existing_grants_checked: false;
|
|
45
|
+
batch_size: number;
|
|
46
|
+
total_batches: number;
|
|
47
|
+
providers: {
|
|
48
|
+
google: MailboxOAuthProviderReviewPlan;
|
|
49
|
+
microsoft: MailboxOAuthProviderReviewPlan;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
34
52
|
export declare const MAILBOX_IMPORT_FILE_MAX_BYTES: number;
|
|
35
53
|
export declare const MAILBOX_IMPORT_ROW_LIMIT = 500;
|
|
54
|
+
export declare const MAILBOX_OAUTH_REVIEW_BATCH_SIZE = 10;
|
|
36
55
|
/**
|
|
37
56
|
* Infer the bounded mailbox-import format without node:path so browser and CLI
|
|
38
57
|
* preflight use the same extension contract.
|
|
@@ -55,4 +74,11 @@ export declare function summarizeMailboxImportValidation(mailboxes: readonly Nor
|
|
|
55
74
|
source: MailboxImportValidationSource;
|
|
56
75
|
sourceProvider: string | null;
|
|
57
76
|
}): MailboxImportValidationSummary;
|
|
77
|
+
/**
|
|
78
|
+
* Build the deterministic, provider-specific review queue for exact-account
|
|
79
|
+
* OAuth. This is safe during local file validation: it uses only normalized
|
|
80
|
+
* provider counts, performs no directory discovery, and does not claim to know
|
|
81
|
+
* whether a workspace already holds a usable grant for an address.
|
|
82
|
+
*/
|
|
83
|
+
export declare function planMailboxOAuthReviews(mailboxes: readonly Pick<NormalizedMailboxImportRow, "provider">[]): MailboxOAuthReviewPlan;
|
|
58
84
|
export declare function normalizeMailboxImportVendor(raw: string | null | undefined, from: string | null | undefined): string | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OxygenError } from "./cli-result.js";
|
|
2
2
|
export const MAILBOX_IMPORT_FILE_MAX_BYTES = 5 * 1024 * 1024;
|
|
3
3
|
export const MAILBOX_IMPORT_ROW_LIMIT = 500;
|
|
4
|
+
export const MAILBOX_OAUTH_REVIEW_BATCH_SIZE = 10;
|
|
4
5
|
const MAILBOX_EMAIL_HEADERS = new Set([
|
|
5
6
|
"address",
|
|
6
7
|
"email",
|
|
@@ -194,6 +195,7 @@ export function summarizeMailboxImportValidation(mailboxes, input) {
|
|
|
194
195
|
infrastructure_platforms: infrastructurePlatforms,
|
|
195
196
|
credential_rows: credentialRows,
|
|
196
197
|
identity_only_rows: mailboxes.length - credentialRows,
|
|
198
|
+
oauth_review_plan: planMailboxOAuthReviews(mailboxes),
|
|
197
199
|
limits: {
|
|
198
200
|
max_rows: MAILBOX_IMPORT_ROW_LIMIT,
|
|
199
201
|
max_file_bytes: MAILBOX_IMPORT_FILE_MAX_BYTES,
|
|
@@ -205,6 +207,40 @@ export function summarizeMailboxImportValidation(mailboxes, input) {
|
|
|
205
207
|
next_action: "Submit this reviewed file to register the mailboxes.",
|
|
206
208
|
};
|
|
207
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Build the deterministic, provider-specific review queue for exact-account
|
|
212
|
+
* OAuth. This is safe during local file validation: it uses only normalized
|
|
213
|
+
* provider counts, performs no directory discovery, and does not claim to know
|
|
214
|
+
* whether a workspace already holds a usable grant for an address.
|
|
215
|
+
*/
|
|
216
|
+
export function planMailboxOAuthReviews(mailboxes) {
|
|
217
|
+
const counts = { google: 0, microsoft: 0 };
|
|
218
|
+
for (const mailbox of mailboxes)
|
|
219
|
+
counts[mailbox.provider] += 1;
|
|
220
|
+
const google = planProviderOAuthReviews(counts.google);
|
|
221
|
+
const microsoft = planProviderOAuthReviews(counts.microsoft);
|
|
222
|
+
return {
|
|
223
|
+
authorization: "exact_account_oauth",
|
|
224
|
+
scope: "validated_addresses",
|
|
225
|
+
directory_discovery: false,
|
|
226
|
+
existing_grants_checked: false,
|
|
227
|
+
batch_size: MAILBOX_OAUTH_REVIEW_BATCH_SIZE,
|
|
228
|
+
total_batches: google.batches + microsoft.batches,
|
|
229
|
+
providers: { google, microsoft },
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function planProviderOAuthReviews(mailboxes) {
|
|
233
|
+
const fullBatches = Math.floor(mailboxes / MAILBOX_OAUTH_REVIEW_BATCH_SIZE);
|
|
234
|
+
const remainder = mailboxes % MAILBOX_OAUTH_REVIEW_BATCH_SIZE;
|
|
235
|
+
const batchSizes = Array.from({ length: fullBatches }, () => MAILBOX_OAUTH_REVIEW_BATCH_SIZE);
|
|
236
|
+
if (remainder > 0)
|
|
237
|
+
batchSizes.push(remainder);
|
|
238
|
+
return {
|
|
239
|
+
mailboxes,
|
|
240
|
+
batches: batchSizes.length,
|
|
241
|
+
batch_sizes: batchSizes,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
208
244
|
export function normalizeMailboxImportVendor(raw, from) {
|
|
209
245
|
if (from === "hypertide") {
|
|
210
246
|
if (raw && raw.trim().toLowerCase() !== "hypertide") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.692.5";
|
|
2
2
|
// The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
|
|
3
3
|
// operational route. Raising it hard-rejects every older CLI from the entire
|
|
4
4
|
// product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
|