@oxygen-agent/cli 1.1014.1600 → 1.1014.1696
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 +4 -1
- package/dist/commands/custom-integrations.js +13 -0
- package/dist/commands/inbox.js +1 -1
- package/dist/commands/integrations.js +5 -1
- package/dist/commands/mailboxes.js +26 -6
- package/dist/commands/managed-inboxes.js +2 -2
- package/dist/commands/senders.js +4 -4
- package/dist/credential-storage/store.d.ts +10 -0
- package/dist/credential-storage/store.js +282 -0
- package/dist/credentials.d.ts +1 -3
- package/dist/credentials.js +3 -275
- package/dist/mailbox-fleet-output.js +11 -2
- package/dist/workspace-placement-commands.js +3 -2
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +63 -9
- package/node_modules/@oxygen/shared/dist/clay-migration-auth-state.d.ts +7 -1
- package/node_modules/@oxygen/shared/dist/clay-migration-auth-state.js +6 -5
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +1 -0
- package/node_modules/@oxygen/shared/dist/contracts/clay-migration.d.ts +39 -1
- package/node_modules/@oxygen/shared/dist/contracts/clay-migration.js +32 -4
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +2 -1
- package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.js +2 -2
- package/node_modules/@oxygen/shared/dist/email-classification-scope.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/email-classification-scope.js +40 -0
- package/node_modules/@oxygen/shared/dist/native-chat.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/native-chat.js +12 -1
- package/node_modules/@oxygen/shared/dist/onboarding-research.d.ts +9 -2
- package/node_modules/@oxygen/shared/dist/onboarding-research.js +9 -2
- package/node_modules/@oxygen/shared/dist/recipes.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/recipes.js +13 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-rollout.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-rollout.js +28 -0
- package/node_modules/@oxygen/shared/dist/tool-call-arguments.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/tool-call-arguments.js +68 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-placement.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/workspace-placement.js +11 -0
- package/package.json +1 -1
package/dist/credentials.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, chmodSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { promisify } from "node:util";
|
|
1
|
+
import { createCredentialStorage } from "./credential-storage/store.js";
|
|
6
2
|
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
7
3
|
import { sameApiOrigin } from "./runtime.js";
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const ACCOUNT_NAME = "default";
|
|
4
|
+
export { resolveConfigDir } from "./credential-storage/store.js";
|
|
5
|
+
const { readSystemCredentialPayload, writeSystemCredentialPayload, deleteSystemCredentialPayload } = createCredentialStorage();
|
|
11
6
|
const DEFAULT_API_URL = "https://oxygen-agent.com";
|
|
12
7
|
const DEFAULT_PROFILE_NAME = "default";
|
|
13
|
-
const WINDOWS_CREDENTIAL_PAYLOAD_ENV = "OXYGEN_CLI_WINDOWS_CREDENTIAL_PAYLOAD";
|
|
14
8
|
function readEnvApiUrl(env) {
|
|
15
9
|
const apiUrl = env.OXYGEN_API_URL?.trim();
|
|
16
10
|
return apiUrl ? normalizeApiUrl(apiUrl) : null;
|
|
@@ -347,38 +341,6 @@ async function readSystemCredentialDocument(env) {
|
|
|
347
341
|
return null;
|
|
348
342
|
return parseCredentialDocument(payload);
|
|
349
343
|
}
|
|
350
|
-
async function readSystemCredentialPayload(env) {
|
|
351
|
-
const store = credentialStore(env);
|
|
352
|
-
if (store === "keychain")
|
|
353
|
-
return readMacOSKeychainPayload(env);
|
|
354
|
-
if (store === "credential-manager")
|
|
355
|
-
return readWindowsCredentialManagerPayload(env);
|
|
356
|
-
return readFileCredentialPayload(env);
|
|
357
|
-
}
|
|
358
|
-
async function writeSystemCredentialPayload(payload, env) {
|
|
359
|
-
const store = credentialStore(env);
|
|
360
|
-
if (store === "keychain") {
|
|
361
|
-
await writeMacOSKeychainPayload(payload, env);
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
if (store === "credential-manager") {
|
|
365
|
-
await writeWindowsCredentialManagerPayload(payload, env);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
writeFileCredentialPayload(payload, env);
|
|
369
|
-
}
|
|
370
|
-
async function deleteSystemCredentialPayload(env) {
|
|
371
|
-
const store = credentialStore(env);
|
|
372
|
-
if (store === "keychain") {
|
|
373
|
-
await deleteMacOSKeychain(env);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
if (store === "credential-manager") {
|
|
377
|
-
await deleteWindowsCredentialManager(env);
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
deleteFileCredentials(env);
|
|
381
|
-
}
|
|
382
344
|
async function writeCredentialDocument(input, env) {
|
|
383
345
|
const activeCredentials = input.profiles[input.activeProfile];
|
|
384
346
|
if (!activeCredentials) {
|
|
@@ -397,240 +359,6 @@ async function writeCredentialDocument(input, env) {
|
|
|
397
359
|
document.identity = activeCredentials.identity;
|
|
398
360
|
await writeSystemCredentialPayload(serializeCredentialDocument(document), env);
|
|
399
361
|
}
|
|
400
|
-
function credentialStore(env) {
|
|
401
|
-
const forced = env.OXYGEN_CREDENTIAL_STORE?.trim().toLowerCase();
|
|
402
|
-
if (forced === "file")
|
|
403
|
-
return "file";
|
|
404
|
-
if (forced === "keychain")
|
|
405
|
-
return "keychain";
|
|
406
|
-
if (forced === "credential-manager")
|
|
407
|
-
return "credential-manager";
|
|
408
|
-
if (process.platform === "darwin")
|
|
409
|
-
return "keychain";
|
|
410
|
-
if (process.platform === "win32")
|
|
411
|
-
return "credential-manager";
|
|
412
|
-
return "file";
|
|
413
|
-
}
|
|
414
|
-
/** The CLI's per-user config directory; `OXYGEN_CONFIG_DIR` overrides it. */
|
|
415
|
-
export function resolveConfigDir(env = process.env) {
|
|
416
|
-
return env.OXYGEN_CONFIG_DIR?.trim()
|
|
417
|
-
|| (process.platform === "win32"
|
|
418
|
-
? join(env.APPDATA || join(homedir(), "AppData", "Roaming"), "oxygen")
|
|
419
|
-
: join(homedir(), ".config", "oxygen"));
|
|
420
|
-
}
|
|
421
|
-
function credentialsPath(env) {
|
|
422
|
-
return join(resolveConfigDir(env), "credentials.json");
|
|
423
|
-
}
|
|
424
|
-
function readFileCredentialPayload(env) {
|
|
425
|
-
const path = credentialsPath(env);
|
|
426
|
-
if (!existsSync(path))
|
|
427
|
-
return null;
|
|
428
|
-
return readFileSync(path, "utf8");
|
|
429
|
-
}
|
|
430
|
-
function writeFileCredentialPayload(payload, env) {
|
|
431
|
-
const path = credentialsPath(env);
|
|
432
|
-
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
433
|
-
const tempPath = `${path}.${process.pid}.tmp`;
|
|
434
|
-
writeFileSync(tempPath, payload, { mode: 0o600 });
|
|
435
|
-
chmodSync(tempPath, 0o600);
|
|
436
|
-
renameSync(tempPath, path);
|
|
437
|
-
chmodSync(path, 0o600);
|
|
438
|
-
}
|
|
439
|
-
function deleteFileCredentials(env) {
|
|
440
|
-
const path = credentialsPath(env);
|
|
441
|
-
rmSync(path, { force: true });
|
|
442
|
-
}
|
|
443
|
-
async function readMacOSKeychainPayload(env) {
|
|
444
|
-
try {
|
|
445
|
-
const { stdout } = await execFileAsync(macOSSecurityCommand(env), [
|
|
446
|
-
"find-generic-password",
|
|
447
|
-
"-a",
|
|
448
|
-
ACCOUNT_NAME,
|
|
449
|
-
"-s",
|
|
450
|
-
SERVICE_NAME,
|
|
451
|
-
"-w",
|
|
452
|
-
], { env });
|
|
453
|
-
return stdout;
|
|
454
|
-
}
|
|
455
|
-
catch {
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
// `security -i` reads commands with a line buffer that traditionally caps at
|
|
460
|
-
// 4096 bytes. Beyond that, the input is silently truncated, which would
|
|
461
|
-
// corrupt the stored credential and break re-auth. Reject early with a clear
|
|
462
|
-
// error so a user with too many profiles gets actionable feedback instead of
|
|
463
|
-
// a partial-write that only surfaces on the next CLI invocation.
|
|
464
|
-
const MACOS_SECURITY_LINE_LIMIT_BYTES = 4096;
|
|
465
|
-
async function writeMacOSKeychainPayload(payload, env) {
|
|
466
|
-
// `security add-generic-password -w PAYLOAD` would put the token in argv
|
|
467
|
-
// (visible to any same-UID `ps`). Use `security -i` instead: the subcommand
|
|
468
|
-
// is written on stdin, so the only argv `ps` ever sees is `security -i`.
|
|
469
|
-
const command = macOSSecurityCommand(env);
|
|
470
|
-
const escaped = payload.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
471
|
-
const line = `add-generic-password -a "${ACCOUNT_NAME}" -s "${SERVICE_NAME}" -w "${escaped}" -U\n`;
|
|
472
|
-
const lineBytes = Buffer.byteLength(line, "utf8");
|
|
473
|
-
if (lineBytes >= MACOS_SECURITY_LINE_LIMIT_BYTES) {
|
|
474
|
-
throw credentialStoreError("write", "macOS Keychain", {
|
|
475
|
-
code: "payload_too_large",
|
|
476
|
-
stderr: `Credential document is ${lineBytes} bytes; \`security -i\` truncates lines at ${MACOS_SECURITY_LINE_LIMIT_BYTES} bytes. Reduce stored profiles (oxygen logout <profile>) and retry.`,
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
await new Promise((resolve, reject) => {
|
|
480
|
-
const child = spawn(command, ["-i"], { env, stdio: ["pipe", "pipe", "pipe"] });
|
|
481
|
-
let stderr = "";
|
|
482
|
-
child.stderr.on("data", (chunk) => {
|
|
483
|
-
stderr += chunk.toString("utf8");
|
|
484
|
-
});
|
|
485
|
-
child.once("error", (error) => {
|
|
486
|
-
reject(credentialStoreError("write", "macOS Keychain", error));
|
|
487
|
-
});
|
|
488
|
-
child.once("close", (code, signal) => {
|
|
489
|
-
const trimmedStderr = stderr.trim();
|
|
490
|
-
if (code === 0 && !trimmedStderr) {
|
|
491
|
-
resolve();
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
reject(credentialStoreError("write", "macOS Keychain", { code, signal, stderr: trimmedStderr }));
|
|
495
|
-
});
|
|
496
|
-
child.stdin.end(line);
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
async function deleteMacOSKeychain(env) {
|
|
500
|
-
await execFileAsync(macOSSecurityCommand(env), [
|
|
501
|
-
"delete-generic-password",
|
|
502
|
-
"-a",
|
|
503
|
-
ACCOUNT_NAME,
|
|
504
|
-
"-s",
|
|
505
|
-
SERVICE_NAME,
|
|
506
|
-
], { env }).catch(() => undefined);
|
|
507
|
-
}
|
|
508
|
-
async function readWindowsCredentialManagerPayload(env) {
|
|
509
|
-
try {
|
|
510
|
-
const { stdout } = await runPowerShell(`${windowsCredentialManagerScript()}
|
|
511
|
-
$ptr = [IntPtr]::Zero
|
|
512
|
-
try {
|
|
513
|
-
if (-not [OxygenCredMan]::CredRead("${SERVICE_NAME}", 1, 0, [ref]$ptr)) { exit 2 }
|
|
514
|
-
$cred = [Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [type][OxygenCredMan+CREDENTIAL])
|
|
515
|
-
$secret = [Runtime.InteropServices.Marshal]::PtrToStringUni($cred.CredentialBlob, [int]($cred.CredentialBlobSize / 2))
|
|
516
|
-
[Console]::Out.Write($secret)
|
|
517
|
-
} finally {
|
|
518
|
-
if ($ptr -ne [IntPtr]::Zero) { [OxygenCredMan]::CredFree($ptr) }
|
|
519
|
-
}
|
|
520
|
-
`, env);
|
|
521
|
-
return stdout;
|
|
522
|
-
}
|
|
523
|
-
catch {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
async function writeWindowsCredentialManagerPayload(value, env) {
|
|
528
|
-
const payload = Buffer.from(value, "utf16le").toString("base64");
|
|
529
|
-
try {
|
|
530
|
-
await runPowerShell(`${windowsCredentialManagerScript()}
|
|
531
|
-
$payload = $env:${WINDOWS_CREDENTIAL_PAYLOAD_ENV}
|
|
532
|
-
if ([string]::IsNullOrWhiteSpace($payload)) { exit 1 }
|
|
533
|
-
$secret = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($payload))
|
|
534
|
-
$bytes = [Text.Encoding]::Unicode.GetBytes($secret)
|
|
535
|
-
$blob = [Runtime.InteropServices.Marshal]::StringToCoTaskMemUni($secret)
|
|
536
|
-
try {
|
|
537
|
-
$cred = New-Object OxygenCredMan+CREDENTIAL
|
|
538
|
-
$cred.Type = 1
|
|
539
|
-
$cred.TargetName = "${SERVICE_NAME}"
|
|
540
|
-
$cred.UserName = "${ACCOUNT_NAME}"
|
|
541
|
-
$cred.CredentialBlob = $blob
|
|
542
|
-
$cred.CredentialBlobSize = $bytes.Length
|
|
543
|
-
$cred.Persist = 2
|
|
544
|
-
if (-not [OxygenCredMan]::CredWrite([ref]$cred, 0)) { exit 1 }
|
|
545
|
-
} finally {
|
|
546
|
-
[Runtime.InteropServices.Marshal]::FreeCoTaskMem($blob)
|
|
547
|
-
}
|
|
548
|
-
`, {
|
|
549
|
-
...env,
|
|
550
|
-
[WINDOWS_CREDENTIAL_PAYLOAD_ENV]: payload,
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
catch (error) {
|
|
554
|
-
throw credentialStoreError("write", "Windows Credential Manager", error);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
async function deleteWindowsCredentialManager(env) {
|
|
558
|
-
await runPowerShell(`${windowsCredentialManagerScript()}
|
|
559
|
-
[void][OxygenCredMan]::CredDelete("${SERVICE_NAME}", 1, 0)
|
|
560
|
-
`, env).catch(() => undefined);
|
|
561
|
-
}
|
|
562
|
-
function runPowerShell(script, env) {
|
|
563
|
-
const encodedCommand = Buffer.from(script, "utf16le").toString("base64");
|
|
564
|
-
return execFileAsync(windowsPowerShellCommand(env), [
|
|
565
|
-
"-NoProfile",
|
|
566
|
-
"-NonInteractive",
|
|
567
|
-
"-ExecutionPolicy",
|
|
568
|
-
"Bypass",
|
|
569
|
-
"-EncodedCommand",
|
|
570
|
-
encodedCommand,
|
|
571
|
-
], { env });
|
|
572
|
-
}
|
|
573
|
-
function credentialStoreError(operation, store, error) {
|
|
574
|
-
const child = error;
|
|
575
|
-
const details = {
|
|
576
|
-
operation,
|
|
577
|
-
store,
|
|
578
|
-
};
|
|
579
|
-
if (typeof child.code === "number") {
|
|
580
|
-
details.exit_code = child.code;
|
|
581
|
-
}
|
|
582
|
-
else if (typeof child.code === "string") {
|
|
583
|
-
details.reason = child.code;
|
|
584
|
-
}
|
|
585
|
-
if (typeof child.signal === "string")
|
|
586
|
-
details.signal = child.signal;
|
|
587
|
-
if (typeof child.stderr === "string" && child.stderr.trim()) {
|
|
588
|
-
details.stderr = sanitizeCredentialErrorText(child.stderr.trim()).slice(0, 4000);
|
|
589
|
-
}
|
|
590
|
-
return new OxygenError("credential_store_failed", `Unable to ${operation} Oxygen CLI credentials in ${store}.`, { details, exitCode: 1 });
|
|
591
|
-
}
|
|
592
|
-
function sanitizeCredentialErrorText(value) {
|
|
593
|
-
return value.replace(/\boxy_(?:live|user)_[A-Za-z0-9_-]+\b/g, "[redacted_token]");
|
|
594
|
-
}
|
|
595
|
-
function macOSSecurityCommand(env) {
|
|
596
|
-
return env.OXYGEN_SECURITY_COMMAND?.trim() || "security";
|
|
597
|
-
}
|
|
598
|
-
function windowsPowerShellCommand(env) {
|
|
599
|
-
return env.OXYGEN_POWERSHELL_COMMAND?.trim() || "powershell.exe";
|
|
600
|
-
}
|
|
601
|
-
function windowsCredentialManagerScript() {
|
|
602
|
-
return `
|
|
603
|
-
Add-Type @"
|
|
604
|
-
using System;
|
|
605
|
-
using System.Runtime.InteropServices;
|
|
606
|
-
public static class OxygenCredMan {
|
|
607
|
-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
608
|
-
public struct CREDENTIAL {
|
|
609
|
-
public UInt32 Flags;
|
|
610
|
-
public UInt32 Type;
|
|
611
|
-
public string TargetName;
|
|
612
|
-
public string Comment;
|
|
613
|
-
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
614
|
-
public UInt32 CredentialBlobSize;
|
|
615
|
-
public IntPtr CredentialBlob;
|
|
616
|
-
public UInt32 Persist;
|
|
617
|
-
public UInt32 AttributeCount;
|
|
618
|
-
public IntPtr Attributes;
|
|
619
|
-
public string TargetAlias;
|
|
620
|
-
public string UserName;
|
|
621
|
-
}
|
|
622
|
-
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
623
|
-
public static extern bool CredWrite([In] ref CREDENTIAL userCredential, UInt32 flags);
|
|
624
|
-
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
625
|
-
public static extern bool CredRead(string target, UInt32 type, UInt32 reservedFlag, out IntPtr credentialPtr);
|
|
626
|
-
[DllImport("advapi32.dll", SetLastError = true)]
|
|
627
|
-
public static extern bool CredDelete(string target, UInt32 type, UInt32 flags);
|
|
628
|
-
[DllImport("advapi32.dll", SetLastError = true)]
|
|
629
|
-
public static extern void CredFree([In] IntPtr cred);
|
|
630
|
-
}
|
|
631
|
-
"@
|
|
632
|
-
`;
|
|
633
|
-
}
|
|
634
362
|
function serializeCredentialDocument(document) {
|
|
635
363
|
return JSON.stringify(document);
|
|
636
364
|
}
|
|
@@ -29,9 +29,9 @@ export function formatMailboxFleetHealth(data) {
|
|
|
29
29
|
`Warm-up stop causes: ${counts(warmup.byCause)}`,
|
|
30
30
|
"Enrollment and next actions: oxygen mailboxes list --json (warmupTruth).",
|
|
31
31
|
"",
|
|
32
|
-
"Domains (worst first):",
|
|
32
|
+
...(Array.isArray(data.domain_context) ? ["Selected inbox domains (whole-domain totals):"] : ["Domains (worst first):"]),
|
|
33
33
|
];
|
|
34
|
-
const domains = Array.isArray(data.domains) ? data.domains.map(record) : [];
|
|
34
|
+
const domains = Array.isArray(data.domain_context) ? data.domain_context.map(record) : Array.isArray(data.domains) ? data.domains.map(record) : [];
|
|
35
35
|
for (const domain of domains)
|
|
36
36
|
lines.push(` ${text(domain.domain)} · ${count(domain.mailboxes)} inboxes · provider rejections ${count(domain.providerRejections7d)} (Google ${count(domain.googleRejections7d)}) · hard bounces ${count(domain.hardBounces7d)} / ${count(domain.coldSends7d)} sequence sends · youngest warm-up day ${count(domain.warmupDayMin)}`);
|
|
37
37
|
if (!domains.length)
|
|
@@ -50,11 +50,20 @@ export function formatMailboxFleetHealth(data) {
|
|
|
50
50
|
}
|
|
51
51
|
const mailboxes = Array.isArray(data.mailboxes) ? data.mailboxes.map(record) : null;
|
|
52
52
|
if (mailboxes) {
|
|
53
|
+
const selection = record(data.mailbox_selection);
|
|
54
|
+
if (Object.keys(selection).length)
|
|
55
|
+
lines.push("", `Selected inbox details: ${count(selection.matched)} of ${count(selection.fleet_total)}; fleet totals remain unfiltered.`);
|
|
53
56
|
lines.push("", `Inbox roster (${mailboxes.length}):`);
|
|
54
57
|
for (const mailbox of mailboxes) {
|
|
55
58
|
const mailboxWarmup = record(mailbox.warmup);
|
|
56
59
|
const warnings = Array.isArray(mailbox.warnings) ? mailbox.warnings.join(", ") : "";
|
|
57
60
|
lines.push(` ${text(mailbox.emailAddress)} · ${text(mailbox.status)} · health ${text(mailbox.healthState)} (${text(mailbox.healthFreshness)}) · stored warm-up rail ${text(mailboxWarmup.state)}${typeof mailboxWarmup.pauseCode === "string" ? ` / ${mailboxWarmup.pauseCode}` : ""}${warnings ? ` · ${warnings}` : ""}`);
|
|
61
|
+
if (data.view === "full") {
|
|
62
|
+
const evidence = record(mailbox.signals);
|
|
63
|
+
lines.push(` Last-known external score: ${count(mailbox.healthScore)} · ${text(mailbox.healthProvider)} · checked ${text(mailbox.healthCheckedAt)}`);
|
|
64
|
+
lines.push(` ${text(evidence.window)} evidence: ${count(evidence.coldSends7d)} sequence sends · ${count(evidence.hardBounces7d)} hard bounces · ${count(evidence.googleRejections7d)} Google rejections (all send sources)`);
|
|
65
|
+
lines.push(` Delivery notifications: ${counts(evidence.dsn7d)} · diagnostic codes: ${counts(evidence.dsnCodes7d)}`);
|
|
66
|
+
}
|
|
58
67
|
}
|
|
59
68
|
}
|
|
60
69
|
else if (data.view === "fleet")
|
|
@@ -11,7 +11,8 @@ export function registerWorkspacePlacementCommands(root, handle) {
|
|
|
11
11
|
.option("--request-id <id>", "Request UUID from preview.").option("--plan <hash>", "Exact preview plan hash.").option("--max-live-sends <n>", "Maximum approved diagnostic emails (1–50).")
|
|
12
12
|
.option("--preview-cursor <id>", "Read the next manifest page using the same input, request ID and rendering time.").option("--preview-limit <n>", "Preview manifest page size (1–20); never use with approval.")
|
|
13
13
|
.option("--preview-probe-id <id>", "Read exact rendered copy for a preview probe; reuse request ID and rendering time.").option("--preview-part <part>", "body_text or body_html.").option("--preview-offset <n>", "Unicode character offset returned by copy preview.").option("--preview-copy-limit <n>", "Copy page size (1–3000 characters).")
|
|
14
|
-
.option("--allow-paused-probe", "Explicitly allow diagnostic sends from paused senders, preserving pauses.")
|
|
14
|
+
.option("--allow-paused-probe", "Explicitly allow diagnostic sends from paused senders, preserving pauses.")
|
|
15
|
+
.option("--allow-daily-limit-override", "Staff only: approve daily sender/receiver quota exceptions for this exact test, one diagnostic per sender. Preserve mailbox settings.").option("--json", "Print JSON.")
|
|
15
16
|
.action(async (options) => handle("deliverability workspace-test run", options, () => {
|
|
16
17
|
const input = readJsonFileValue(resolve(options.input), "--input");
|
|
17
18
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
@@ -20,7 +21,7 @@ export function registerWorkspacePlacementCommands(root, handle) {
|
|
|
20
21
|
...(options.approved ? { approved: true } : {}), ...(options.requestId ? { request_id: options.requestId } : {}),
|
|
21
22
|
...(options.plan ? { plan_hash: options.plan } : {}), ...(options.renderedAt ? { rendered_at: options.renderedAt } : {}), ...(options.trackingOpens ? { tracking_opens: true } : {}), ...(options.trackingClicks ? { tracking_clicks: true } : {}), ...(options.maxLiveSends ? { max_live_sends: Number(options.maxLiveSends) } : {}),
|
|
22
23
|
...(options.receiverEnvironment ? { receiver_environment: options.receiverEnvironment } : {}), ...(options.receiverWorkspace ? { receiver_workspace_id: options.receiverWorkspace } : {}),
|
|
23
|
-
...(options.allowPausedProbe ? { allow_paused_probe: true } : {}), ...(options.receiverScope ? { receiver_scope: options.receiverScope } : {}), ...(options.receiverProviders ? { receiver_providers: String(options.receiverProviders).split(",") } : {}),
|
|
24
|
+
...(options.allowPausedProbe ? { allow_paused_probe: true } : {}), ...(options.allowDailyLimitOverride ? { allow_daily_limit_override: true } : {}), ...(options.receiverScope ? { receiver_scope: options.receiverScope } : {}), ...(options.receiverProviders ? { receiver_providers: String(options.receiverProviders).split(",") } : {}),
|
|
24
25
|
...(options.previewCursor ? { preview_cursor: options.previewCursor } : {}), ...(options.previewLimit !== undefined ? { preview_limit: Number(options.previewLimit) } : {}),
|
|
25
26
|
...(options.previewProbeId ? { preview_probe_id: options.previewProbeId } : {}), ...(options.previewPart ? { preview_part: options.previewPart } : {}), ...(options.previewOffset !== undefined ? { preview_offset: Number(options.previewOffset) } : {}), ...(options.previewCopyLimit !== undefined ? { preview_copy_limit: Number(options.previewCopyLimit) } : {}) } });
|
|
26
27
|
})))
|
|
@@ -26,7 +26,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
26
26
|
posture: "workspace_write",
|
|
27
27
|
gatewayTools: ["oxygen_visual_source_create", "oxygen_visual_render_preview", "oxygen_visual_render_get", "oxygen_visual_image_view"],
|
|
28
28
|
gatewayCommands: ["visuals source-create", "visuals render-preview", "visuals render-get", "visuals download"],
|
|
29
|
-
skills: ["oxygen-knowledge"],
|
|
29
|
+
skills: ["oxygen-linkedin-content", "oxygen-knowledge"],
|
|
30
30
|
endpointSections: ["visuals"],
|
|
31
31
|
intentTerms: ["infographic", "gtm flow image", "render html", "visual design", "carousel pages", "svg", "png", "pdf export", "graphic designer"],
|
|
32
32
|
},
|
|
@@ -114,8 +114,8 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
114
114
|
notFor: "Primitive run diagnosis when no support report is needed; inspect Observability first.",
|
|
115
115
|
execution: "Start or continue a hosted support conversation, optionally file structured metadata, and return its deep link; never send a customer reply automatically.",
|
|
116
116
|
posture: "workspace_write",
|
|
117
|
-
gatewayTools: ["oxygen_feedback", "oxygen_support_ticket_create", "oxygen_support_tickets_list", "oxygen_chat_channel", "oxygen_chat_thread_get", "oxygen_chat_thread_create", "oxygen_chat_thread_reply", "oxygen_chat_file_upload", "oxygen_chat_file_get", "oxygen_chat_thread_read"],
|
|
118
|
-
gatewayCommands: ["feedback", "support chat", "support file", "support list", "chat channel", "chat threads get", "chat threads create", "chat threads reply", "chat files upload", "chat files get", "chat threads read"],
|
|
117
|
+
gatewayTools: ["oxygen_feedback", "oxygen_support_ticket_create", "oxygen_support_tickets_list", "oxygen_chat_threads_list", "oxygen_chat_channel", "oxygen_chat_thread_get", "oxygen_chat_thread_create", "oxygen_chat_thread_reply", "oxygen_chat_file_upload", "oxygen_chat_file_get", "oxygen_chat_thread_read"],
|
|
118
|
+
gatewayCommands: ["feedback", "support chat", "support file", "support list", "chat threads list", "chat channel", "chat threads get", "chat threads create", "chat threads reply", "chat files upload", "chat files get", "chat threads read"],
|
|
119
119
|
skills: ["oxygen-feedback"],
|
|
120
120
|
endpointSections: ["feedback", "support", "chat"],
|
|
121
121
|
intentTerms: ["support", "feedback", "bug report", "feature request", "report issue", "ticket", "complaint", "support chat", "support thread", "chat attachment"],
|
|
@@ -398,12 +398,12 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
398
398
|
posture: "external_write",
|
|
399
399
|
gatewayTools: ["oxygen_publishing_posts_create", "oxygen_publishing_posts_list", "oxygen_publishing_posts_approve", "oxygen_ugc_get"],
|
|
400
400
|
gatewayCommands: ["publishing posts create", "publishing posts list", "publishing posts approve", "ugc programs list", "ugc programs duplicate", "ugc programs archive"],
|
|
401
|
-
skills: ["oxygen-publishing", "oxygen-ugc"],
|
|
401
|
+
skills: ["oxygen-publishing", "oxygen-linkedin-content", "oxygen-ugc"],
|
|
402
402
|
endpointSections: ["publishing", "ugc"],
|
|
403
403
|
// "remove program" / "delete program" landed on route: null in a blind eval
|
|
404
404
|
// (2026-09-10); a program is a UGC noun here, and its lifecycle verbs must
|
|
405
405
|
// resolve to the group that owns `ugc programs archive|delete|duplicate`.
|
|
406
|
-
intentTerms: ["publish", "publishing", "schedule post", "content calendar", "posting calendar", "approve post", "social publishing", "video cover", "video thumbnail", "cover image", "post preview", "ugc", "creator program", "user generated content", "creator voice", "sponsored creator", "ugc program", "archive program", "delete program", "remove program", "duplicate program", "copy program"],
|
|
406
|
+
intentTerms: ["publish", "publishing", "schedule post", "content calendar", "posting calendar", "approve post", "social publishing", "video cover", "video thumbnail", "cover image", "post preview", "ugc", "creator program", "user generated content", "creator voice", "sponsored creator", "ugc program", "archive program", "delete program", "remove program", "duplicate program", "copy program", "write posts", "write a post", "post ideas", "content pillars", "hook", "linkedin content"],
|
|
407
407
|
},
|
|
408
408
|
{
|
|
409
409
|
id: "workflows",
|
|
@@ -486,7 +486,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
486
486
|
posture: "mixed",
|
|
487
487
|
gatewayTools: ["oxygen_integrations_list", "oxygen_integrations_connect", "oxygen_integrations_list_actions"],
|
|
488
488
|
gatewayCommands: ["integrations list", "integrations connect", "custom-integrations list"],
|
|
489
|
-
skills: ["oxygen-tables", "oxygen-gtm"],
|
|
489
|
+
skills: ["oxygen-custom-http", "oxygen-tables", "oxygen-gtm"],
|
|
490
490
|
endpointSections: ["custom-integrations", "integrations"],
|
|
491
491
|
intentTerms: ["integration", "integrations", "connect provider", "oauth", "byok", "bring your own key", "provider key", "provider connection", "custom http", "custom integration", "connection readiness"],
|
|
492
492
|
},
|
|
@@ -591,6 +591,19 @@ export function inferCapabilityRoute(query) {
|
|
|
591
591
|
const normalized = normalizeIntent(query);
|
|
592
592
|
if (!normalized)
|
|
593
593
|
return null;
|
|
594
|
+
if (/\b(custom http|custom integrations?|own (?:rest|http) api)\b/.test(normalized)) {
|
|
595
|
+
const integrations = ROUTE_BY_ID.get("connected-integrations");
|
|
596
|
+
if (integrations) {
|
|
597
|
+
const removal = /\b(remov\w*|delet\w*|disconnect\w*|cleanup|clean up|throwaway|disposable)\b/.test(normalized);
|
|
598
|
+
const tools = removal ? ["oxygen_custom_integrations_remove", "oxygen_custom_integrations_list"] : ["oxygen_custom_integrations_list", "oxygen_custom_integrations_apply", "oxygen_custom_integration_connect"];
|
|
599
|
+
const commands = removal ? ["custom-integrations remove", "custom-integrations list"] : ["custom-integrations list", "custom-integrations apply", "custom-integrations connect"];
|
|
600
|
+
return {
|
|
601
|
+
card: { ...integrations, gatewayTools: tools, gatewayCommands: commands, skills: ["oxygen-custom-http"], execution: removal ? "Preview the exact custom HTTP connection and tools, then approve the returned plan_hash. Caller definitions and history remain, but bound callers stop working; provider-side key revocation is separate." : "Read oxygen-custom-http for the manifest schema and auth types. Apply registers catalog tools without a provider call; connect stores credentials separately. Preview a tool request before approved live execution." },
|
|
602
|
+
recommendedTools: tools,
|
|
603
|
+
recommendedCommands: commands,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
}
|
|
594
607
|
if (/\b(mailbox(?:es)?|inbox(?:es)?)\b/.test(normalized) && /\b(standby|reserve|replacement|replace|burn(?:ed|t)?)\b/.test(normalized)) {
|
|
595
608
|
const campaign = /\b(campaign|sequence|cadence)\b/.test(normalized);
|
|
596
609
|
const card = ROUTE_BY_ID.get(campaign ? "sequences" : "sending-infrastructure") ?? ROUTE_BY_PRIMITIVE.get("sequences");
|
|
@@ -734,12 +747,14 @@ export const OXYGEN_PRODUCT_SKILL_AUDIENCES = Object.freeze({
|
|
|
734
747
|
"oxygen-deliverability": "workspace",
|
|
735
748
|
"oxygen-sequencer": "workspace",
|
|
736
749
|
"oxygen-publishing": "workspace",
|
|
750
|
+
"oxygen-linkedin-content": "workspace",
|
|
737
751
|
"oxygen-signals": "workspace",
|
|
738
752
|
"oxygen-table-tidy": "workspace",
|
|
739
753
|
"oxygen-diagnostics": "workspace",
|
|
740
754
|
"oxygen-feedback": "workspace",
|
|
741
755
|
"oxygen-clay-migration": "workspace",
|
|
742
756
|
"oxygen-ugc": "workspace",
|
|
757
|
+
"oxygen-custom-http": "workspace",
|
|
743
758
|
// Retired (RETIRED_PRODUCT_SKILLS): readable for existing pins, never served.
|
|
744
759
|
"oxygen-playbooks": "workspace",
|
|
745
760
|
"oxygen-visual-design": "workspace",
|
|
@@ -1149,6 +1164,20 @@ function readInspectionRecommendation(query) {
|
|
|
1149
1164
|
if (action)
|
|
1150
1165
|
return null;
|
|
1151
1166
|
const stored = positive.replace(/\bwithout\s+(?:changing|approving|rejecting|publishing|running|executing|refreshing)\b(?:\s+(?:widget\s+)?data\b)?/g, "");
|
|
1167
|
+
// A daily cap is context for an inbox inspection, not a recurring job or a
|
|
1168
|
+
// request to change its volume. Keep explicit writes and other owners intact.
|
|
1169
|
+
const mailboxInspection = stored.replace(/\bdaily (?:(?:campaign|sending|email) )?(?:limits?|caps?)\b/g, "sending limit");
|
|
1170
|
+
if (/\b(?:mailbox(?:es)?|inbox(?:es)?|email accounts?|email senders?)\b/.test(stored)
|
|
1171
|
+
&& /\b(?:health|reputation|rejections?|bounc\w*|deliverability|dsn|ndr)\b/.test(stored)
|
|
1172
|
+
&& !isHostedWorkflowIntent(mailboxInspection)
|
|
1173
|
+
&& !isSecondPartyDecisionIntent(stored)
|
|
1174
|
+
&& !/\b(?:linkedin|whatsapp|sequences?|campaigns?|tables?|columns?|crm|posts?|messages?|conversations?|threads?|dns|spf|dkim|dmarc|verify|verification|create|write|edit|update|change|delete|remove|archive|save|approve|reject|publish|run|running|execute|send|refresh|launch|enable|disable|apply|import|sync|schedule|enroll|add|set|pause|resume|test)\b/.test(mailboxInspection))
|
|
1175
|
+
return {
|
|
1176
|
+
capabilityId: "sending-infrastructure",
|
|
1177
|
+
tools: ["oxygen_mailboxes_health", "oxygen_mailboxes_get", "oxygen_mailboxes_deliverability_evidence"],
|
|
1178
|
+
commands: ["mailboxes health", "mailboxes get", "mailboxes deliverability evidence"],
|
|
1179
|
+
execution: "Read stored reputation, measurement freshness and seven-day rejection/send evidence with oxygen_mailboxes_health. Its mailboxes selector accepts inbox ids or addresses and defaults to full evidence; fleet totals and coverage stay unfiltered. CLI: mailboxes health --mailboxes <id-or-address> --json. Read oxygen_mailboxes_get for today's effective cap; deliverability evidence adds stored placement and domain checks. These reads cost 0 credits and do not sync providers or change settings. External MCP uses oxygen_capabilities_read; hydrate the selected tool's schema before calling.",
|
|
1180
|
+
};
|
|
1152
1181
|
// A trigger diagnosis needs the live definition, not run tailing or a call.
|
|
1153
1182
|
// Keep execution, authoring and run-history requests with their own owners.
|
|
1154
1183
|
if (/\bworkflows?\b/.test(stored)
|
|
@@ -1247,8 +1276,11 @@ function isPartnerBonusReadIntent(query) {
|
|
|
1247
1276
|
/** The card a route reports: Publishing's own, or its UGC view for a creator-program ask. */
|
|
1248
1277
|
function routeCardFor(card, query) {
|
|
1249
1278
|
const readOnly = readInspectionRecommendation(query);
|
|
1250
|
-
if (readOnly?.capabilityId === card.id)
|
|
1251
|
-
|
|
1279
|
+
if (readOnly?.capabilityId === card.id) {
|
|
1280
|
+
const skill = preferredSkillForIntent(card, query);
|
|
1281
|
+
return { ...card, posture: "read_only", execution: readOnly.execution,
|
|
1282
|
+
...(skill ? { skills: [skill, ...card.skills.filter(name => name !== skill)] } : {}) };
|
|
1283
|
+
}
|
|
1252
1284
|
const placementGuide = card.id === "sending-infrastructure" ? placementGuideRecommendation(query) : null;
|
|
1253
1285
|
if (placementGuide)
|
|
1254
1286
|
card = { ...card, posture: "read_only", execution: placementGuide.execution };
|
|
@@ -1361,6 +1393,9 @@ function routeCardFor(card, query) {
|
|
|
1361
1393
|
const removal = card.primitive === "tables" ? tableRowRemovalRecommendation(query) : null;
|
|
1362
1394
|
if (removal)
|
|
1363
1395
|
card = { ...card, posture: "workspace_write", execution: removal.execution };
|
|
1396
|
+
const insert = card.primitive === "tables" ? tableRowInsertRecommendation(query) : null;
|
|
1397
|
+
if (insert)
|
|
1398
|
+
card = { ...card, posture: "workspace_write", execution: insert.execution };
|
|
1364
1399
|
const sent = card.id === "connected-linkedin" ? sentLinkedInInvitationReadRecommendation(query) : null;
|
|
1365
1400
|
if (sent)
|
|
1366
1401
|
card = { ...card, execution: sent.execution };
|
|
@@ -1400,7 +1435,7 @@ function preferredSkillForIntent(card, query) {
|
|
|
1400
1435
|
return "oxygen-enrichment";
|
|
1401
1436
|
if (card.id === "billing-and-budgets" && /\b(explain|why|reduce|increas\w*|burn|spent)\b/.test(query))
|
|
1402
1437
|
return "oxygen-diagnostics";
|
|
1403
|
-
if (card.id === "sending-infrastructure" && /\b(bounc\w*|health|placement|deliverability|spam)\b/.test(query))
|
|
1438
|
+
if (card.id === "sending-infrastructure" && /\b(bounc\w*|health|reputation|rejections?|placement|deliverability|spam)\b/.test(query))
|
|
1404
1439
|
return "oxygen-deliverability";
|
|
1405
1440
|
if (["signals", "linkedin-public-research"].includes(card.id) && /\blinkedin\b/.test(query))
|
|
1406
1441
|
return "oxygen-signals";
|
|
@@ -1865,6 +1900,22 @@ function tableRowRemovalRecommendation(query) {
|
|
|
1865
1900
|
execution: "Delete Table rows with tables rows remove-selection (oxygen_table_rows_remove_selection): the rows a readable filter tree matches (--filter-tree-json / filter_tree, the same tree tables query takes), exact row_ids, or an all_matching selection with search, sort, range and exclusions. Hydrate its schema first. Unreadable filter nodes and filters arrays are refused; unqualified all_matching deletes every row. This is permanent and costs 0 credits; to keep a restorable copy first, tables duplicate (oxygen_tables_duplicate, 0 credits). Read the matching rows before deletion and the survivors afterward; each removed row's values stay readable with rows history (oxygen_rows_history). Deleting rows does not stop campaign enrollment.",
|
|
1866
1901
|
};
|
|
1867
1902
|
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Literal row values go in with the row-insert tool. Without this card "add rows"
|
|
1905
|
+
* ranked create, enrichment and the external Records import first (sweep 3).
|
|
1906
|
+
*/
|
|
1907
|
+
function tableRowInsertRecommendation(query) {
|
|
1908
|
+
if (!/\brows?\b/.test(query) || !/\b(add|adding|insert\w*|append\w*|put|enter|write)\b/.test(query)
|
|
1909
|
+
|| /\b(delete|remove|update|edit|change|set|mark|import\w*|csv|crm|records?|enrich\w*|sync\w*|workflows?|agents?|campaigns?|sequences?|webhooks?|feeds?|functions?|callables?|cells?|dedup\w*|duplicates?)\b/.test(query)
|
|
1910
|
+
|| /\b(?:add|insert|new)\s+(?:an?\s+|the\s+)?(?:\w+\s+)?columns?\b/.test(query)
|
|
1911
|
+
|| isSecondPartyDecisionIntent(query))
|
|
1912
|
+
return null;
|
|
1913
|
+
return {
|
|
1914
|
+
tools: ["oxygen_table_rows_insert", "oxygen_tables_create", "oxygen_tables_query"],
|
|
1915
|
+
commands: ["tables rows insert", "tables create", "tables query"],
|
|
1916
|
+
execution: "Add literal rows with tables rows insert (oxygen_table_rows_insert): table plus rows, an array of {column_key: value} objects. tables create (oxygen_tables_create) makes the columns but takes no rows, so insert after it. Manual rows are a free internal write. Read the rows back with tables query (oxygen_tables_query).",
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1868
1919
|
/** Reads of grid values/state are separate from executing or resolving actions. */
|
|
1869
1920
|
function tableReadRecommendation(query) {
|
|
1870
1921
|
if (isFunctionIntent(query) || isSecondPartyDecisionIntent(query)
|
|
@@ -2917,6 +2968,9 @@ function recommendationsFor(card, query) {
|
|
|
2917
2968
|
const removal = tableRowRemovalRecommendation(query);
|
|
2918
2969
|
if (removal)
|
|
2919
2970
|
return removal;
|
|
2971
|
+
const insert = tableRowInsertRecommendation(query);
|
|
2972
|
+
if (insert)
|
|
2973
|
+
return insert;
|
|
2920
2974
|
const read = tableReadRecommendation(query);
|
|
2921
2975
|
if (read)
|
|
2922
2976
|
return read;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
type FailureState = {
|
|
2
|
+
encryptedState: string;
|
|
3
|
+
expiresAt?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function tagClayMigrationFailureState(error: unknown, encryptedState: string, expiresAt?: string): void;
|
|
2
6
|
/** Consume only state attached in this process by the migration adapter. */
|
|
7
|
+
export declare function consumeClayMigrationFailureState(error: unknown): FailureState | null;
|
|
3
8
|
export declare function readClayMigrationFailureState(error: unknown): string | null;
|
|
9
|
+
export {};
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
// Rotated credentials must survive a failed source read, without becoming part
|
|
2
|
-
// of a customer error, operator log or serialized tool result.
|
|
3
1
|
const failureStates = new WeakMap();
|
|
4
|
-
export function tagClayMigrationFailureState(error, encryptedState) {
|
|
2
|
+
export function tagClayMigrationFailureState(error, encryptedState, expiresAt) {
|
|
5
3
|
if (error !== null && typeof error === "object")
|
|
6
|
-
failureStates.set(error, encryptedState);
|
|
4
|
+
failureStates.set(error, { encryptedState, ...(expiresAt === undefined ? {} : { expiresAt }) });
|
|
7
5
|
}
|
|
8
6
|
/** Consume only state attached in this process by the migration adapter. */
|
|
9
|
-
export function
|
|
7
|
+
export function consumeClayMigrationFailureState(error) {
|
|
10
8
|
if (error === null || typeof error !== "object")
|
|
11
9
|
return null;
|
|
12
10
|
const state = failureStates.get(error) ?? null;
|
|
13
11
|
failureStates.delete(error);
|
|
14
12
|
return state;
|
|
15
13
|
}
|
|
14
|
+
export function readClayMigrationFailureState(error) {
|
|
15
|
+
return consumeClayMigrationFailureState(error)?.encryptedState ?? null;
|
|
16
|
+
}
|
|
@@ -115,6 +115,7 @@ export const BROWSER_OPERATIONS = [
|
|
|
115
115
|
return_to: text("Same-origin path to return to after Checkout, for example /home."),
|
|
116
116
|
source: text("Optional upgrade-entry attribution; omit if unknown."),
|
|
117
117
|
blocked: text("Optional capability that led to the upgrade; omit if unknown."),
|
|
118
|
+
allow_promotion_codes: bool({ description: "Show the promotion-code field instead of the first-month offer." }),
|
|
118
119
|
}, ["plan"]),
|
|
119
120
|
operation("billing.confirm", "/api/cli/billing/confirm", "POST", "billing confirm", "oxygen_billing_confirm", "billing", "Confirm a plan-change preview within 15 minutes; approved accepts payment, term and effective date. Unrestricted members allowed; restricted clients denied.", { token: text(), approved: bool() }, ["token", "approved"], { spends: true }),
|
|
120
121
|
operation("billing.portal", "/api/cli/billing/portal", "POST", "billing portal", "oxygen_billing_portal", "billing", "Create a Stripe billing-portal URL. Unrestricted members allowed; restricted clients denied.", { returnUrl: text() }),
|
|
@@ -3,6 +3,41 @@ import { type ContractIssue, type Infer } from "./schema.js";
|
|
|
3
3
|
export declare const CLAY_MIGRATION_ACTIONS: readonly ["connect", "status", "discover", "read", "transfer", "checkpoint", "report", "disconnect"];
|
|
4
4
|
export declare const CLAY_RESOURCE_TYPES: readonly ["workspace", "folder", "workbook", "table", "audience", "workflow", "function", "claygent", "knowledge", "schedule", "campaign"];
|
|
5
5
|
export declare const CLAY_RESOURCE_DISPOSITIONS: readonly ["pending", "copied", "rebuilt", "needs_decision", "unsupported", "inaccessible", "excluded", "failed"];
|
|
6
|
+
type GlobalAudienceScope = {
|
|
7
|
+
entity_type: "people" | "companies" | "deals";
|
|
8
|
+
global: true;
|
|
9
|
+
archived?: true;
|
|
10
|
+
};
|
|
11
|
+
export declare const CLAY_GLOBAL_AUDIENCE_SCOPES: {
|
|
12
|
+
readonly "global-people": {
|
|
13
|
+
readonly entity_type: "people";
|
|
14
|
+
readonly global: true;
|
|
15
|
+
};
|
|
16
|
+
readonly "global-companies": {
|
|
17
|
+
readonly entity_type: "companies";
|
|
18
|
+
readonly global: true;
|
|
19
|
+
};
|
|
20
|
+
readonly "global-deals": {
|
|
21
|
+
readonly entity_type: "deals";
|
|
22
|
+
readonly global: true;
|
|
23
|
+
};
|
|
24
|
+
readonly "global-archived-people": {
|
|
25
|
+
readonly entity_type: "people";
|
|
26
|
+
readonly global: true;
|
|
27
|
+
readonly archived: true;
|
|
28
|
+
};
|
|
29
|
+
readonly "global-archived-companies": {
|
|
30
|
+
readonly entity_type: "companies";
|
|
31
|
+
readonly global: true;
|
|
32
|
+
readonly archived: true;
|
|
33
|
+
};
|
|
34
|
+
readonly "global-archived-deals": {
|
|
35
|
+
readonly entity_type: "deals";
|
|
36
|
+
readonly global: true;
|
|
37
|
+
readonly archived: true;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export declare function clayGlobalAudienceScope(resourceId: string): GlobalAudienceScope | null;
|
|
6
41
|
export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObject<{
|
|
7
42
|
readonly action: {
|
|
8
43
|
type: "string";
|
|
@@ -17,9 +52,10 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
17
52
|
description?: string;
|
|
18
53
|
};
|
|
19
54
|
readonly resource_id: import("./schema.js").StringProperty;
|
|
55
|
+
readonly row_id: import("./schema.js").StringProperty;
|
|
20
56
|
readonly section: {
|
|
21
57
|
type: "string";
|
|
22
|
-
enum: readonly ("columns" | "fields" | "graph" | "metadata" | "record_ids" | "records" | "rows" | "sequence")[];
|
|
58
|
+
enum: readonly ("columns" | "fields" | "graph" | "metadata" | "record_ids" | "records" | "row" | "rows" | "sequence")[];
|
|
23
59
|
description?: string;
|
|
24
60
|
};
|
|
25
61
|
readonly path: import("./schema.js").StringProperty;
|
|
@@ -35,6 +71,7 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
35
71
|
enum: readonly ("companies" | "deals" | "people")[];
|
|
36
72
|
description?: string;
|
|
37
73
|
};
|
|
74
|
+
readonly archived: import("./schema.js").BooleanProperty;
|
|
38
75
|
readonly record_ids: import("./schema.js").ArrayProperty<{
|
|
39
76
|
type: "integer";
|
|
40
77
|
description?: string;
|
|
@@ -69,3 +106,4 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
69
106
|
export type ClayMigrationInput = Infer<typeof CLAY_MIGRATION_CONTRACT>;
|
|
70
107
|
export declare function clayMigrationInputIssues(args: Readonly<Record<string, unknown>>): ContractIssue[];
|
|
71
108
|
export declare const CLAY_MIGRATION_OPERATION: OperationSpec<typeof CLAY_MIGRATION_CONTRACT>;
|
|
109
|
+
export {};
|