@oxygen-agent/cli 1.1014.136 → 1.1014.618
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/admin-primary-providers-render.js +27 -19
- package/dist/cli-output.d.ts +12 -0
- package/dist/cli-output.js +395 -0
- package/dist/cli-paths.d.ts +1 -0
- package/dist/cli-paths.js +3 -0
- package/dist/cli-profile-context.d.ts +3 -0
- package/dist/cli-profile-context.js +13 -0
- package/dist/command-manifest.d.ts +15 -0
- package/dist/command-manifest.js +23 -1
- package/dist/command-support.d.ts +438 -0
- package/dist/command-support.js +1610 -0
- package/dist/commands/action-column.d.ts +3 -0
- package/dist/commands/action-column.js +43 -0
- package/dist/commands/activation.d.ts +3 -0
- package/dist/commands/activation.js +24 -0
- package/dist/commands/admin.d.ts +3 -0
- package/dist/commands/admin.js +347 -0
- package/dist/commands/ads.d.ts +3 -0
- package/dist/commands/ads.js +62 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +274 -0
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.js +77 -0
- package/dist/commands/approvals.d.ts +3 -0
- package/dist/commands/approvals.js +518 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.js +230 -0
- package/dist/commands/billing.d.ts +3 -0
- package/dist/commands/billing.js +357 -0
- package/dist/commands/blueprints.d.ts +3 -0
- package/dist/commands/blueprints.js +312 -0
- package/dist/commands/budget.d.ts +3 -0
- package/dist/commands/budget.js +55 -0
- package/dist/commands/callables.d.ts +3 -0
- package/dist/commands/callables.js +111 -0
- package/dist/commands/capabilities.d.ts +3 -0
- package/dist/commands/capabilities.js +44 -0
- package/dist/commands/cells.d.ts +3 -0
- package/dist/commands/cells.js +94 -0
- package/dist/commands/columns.d.ts +3 -0
- package/dist/commands/columns.js +1628 -0
- package/dist/commands/commands.d.ts +3 -0
- package/dist/commands/commands.js +45 -0
- package/dist/commands/comments.d.ts +3 -0
- package/dist/commands/comments.js +188 -0
- package/dist/commands/companies.d.ts +3 -0
- package/dist/commands/companies.js +312 -0
- package/dist/commands/connections.d.ts +3 -0
- package/dist/commands/connections.js +39 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.js +170 -0
- package/dist/commands/copilot.d.ts +3 -0
- package/dist/commands/copilot.js +565 -0
- package/dist/commands/crm.d.ts +3 -0
- package/dist/commands/crm.js +1669 -0
- package/dist/commands/custom-integrations.d.ts +3 -0
- package/dist/commands/custom-integrations.js +140 -0
- package/dist/commands/dashboards.d.ts +3 -0
- package/dist/commands/dashboards.js +240 -0
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.js +190 -0
- package/dist/commands/deliverability.d.ts +3 -0
- package/dist/commands/deliverability.js +90 -0
- package/dist/commands/directory.d.ts +3 -0
- package/dist/commands/directory.js +101 -0
- package/dist/commands/domains.d.ts +3 -0
- package/dist/commands/domains.js +604 -0
- package/dist/commands/egress.d.ts +3 -0
- package/dist/commands/egress.js +130 -0
- package/dist/commands/email.d.ts +3 -0
- package/dist/commands/email.js +49 -0
- package/dist/commands/engagement.d.ts +3 -0
- package/dist/commands/engagement.js +189 -0
- package/dist/commands/enrich-column.d.ts +3 -0
- package/dist/commands/enrich-column.js +149 -0
- package/dist/commands/enrichment.d.ts +3 -0
- package/dist/commands/enrichment.js +49 -0
- package/dist/commands/feedback.d.ts +3 -0
- package/dist/commands/feedback.js +96 -0
- package/dist/commands/feeds.d.ts +3 -0
- package/dist/commands/feeds.js +486 -0
- package/dist/commands/find.d.ts +3 -0
- package/dist/commands/find.js +159 -0
- package/dist/commands/followers.d.ts +3 -0
- package/dist/commands/followers.js +39 -0
- package/dist/commands/formulas.d.ts +3 -0
- package/dist/commands/formulas.js +43 -0
- package/dist/commands/home.d.ts +3 -0
- package/dist/commands/home.js +22 -0
- package/dist/commands/inbox.d.ts +3 -0
- package/dist/commands/inbox.js +779 -0
- package/dist/commands/integrations.d.ts +3 -0
- package/dist/commands/integrations.js +290 -0
- package/dist/commands/knowledge.d.ts +3 -0
- package/dist/commands/knowledge.js +874 -0
- package/dist/commands/lead-sourcing.d.ts +3 -0
- package/dist/commands/lead-sourcing.js +52 -0
- package/dist/commands/limits.d.ts +3 -0
- package/dist/commands/limits.js +54 -0
- package/dist/commands/linkedin.d.ts +3 -0
- package/dist/commands/linkedin.js +275 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +347 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.js +12 -0
- package/dist/commands/mailboxes.d.ts +3 -0
- package/dist/commands/mailboxes.js +1290 -0
- package/dist/commands/managed-inboxes.d.ts +3 -0
- package/dist/commands/managed-inboxes.js +361 -0
- package/dist/commands/messages.d.ts +3 -0
- package/dist/commands/messages.js +78 -0
- package/dist/commands/notetaker.d.ts +3 -0
- package/dist/commands/notetaker.js +79 -0
- package/dist/commands/observability.d.ts +3 -0
- package/dist/commands/observability.js +125 -0
- package/dist/commands/orgs.d.ts +3 -0
- package/dist/commands/orgs.js +237 -0
- package/dist/commands/people.d.ts +3 -0
- package/dist/commands/people.js +353 -0
- package/dist/commands/posts.d.ts +3 -0
- package/dist/commands/posts.js +139 -0
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.js +250 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.js +94 -0
- package/dist/commands/prompts.d.ts +3 -0
- package/dist/commands/prompts.js +84 -0
- package/dist/commands/publishing.d.ts +3 -0
- package/dist/commands/publishing.js +1419 -0
- package/dist/commands/recipes.d.ts +3 -0
- package/dist/commands/recipes.js +120 -0
- package/dist/commands/reviews.d.ts +3 -0
- package/dist/commands/reviews.js +75 -0
- package/dist/commands/rows.d.ts +3 -0
- package/dist/commands/rows.js +27 -0
- package/dist/commands/runs.d.ts +3 -0
- package/dist/commands/runs.js +37 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.js +182 -0
- package/dist/commands/senders.d.ts +3 -0
- package/dist/commands/senders.js +393 -0
- package/dist/commands/sequences.d.ts +3 -0
- package/dist/commands/sequences.js +2322 -0
- package/dist/commands/session.d.ts +3 -0
- package/dist/commands/session.js +109 -0
- package/dist/commands/signals.d.ts +3 -0
- package/dist/commands/signals.js +289 -0
- package/dist/commands/signup-leads.d.ts +3 -0
- package/dist/commands/signup-leads.js +46 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.js +50 -0
- package/dist/commands/sourcing.d.ts +3 -0
- package/dist/commands/sourcing.js +27 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/supabase.d.ts +3 -0
- package/dist/commands/supabase.js +128 -0
- package/dist/commands/support.d.ts +3 -0
- package/dist/commands/support.js +651 -0
- package/dist/commands/suppressions.d.ts +3 -0
- package/dist/commands/suppressions.js +577 -0
- package/dist/commands/table-ingestions.d.ts +3 -0
- package/dist/commands/table-ingestions.js +145 -0
- package/dist/commands/table-runs.d.ts +3 -0
- package/dist/commands/table-runs.js +277 -0
- package/dist/commands/tables.d.ts +3 -0
- package/dist/commands/tables.js +3492 -0
- package/dist/commands/tags.d.ts +3 -0
- package/dist/commands/tags.js +147 -0
- package/dist/commands/tools.d.ts +3 -0
- package/dist/commands/tools.js +246 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +106 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.js +27 -0
- package/dist/commands/viewers.d.ts +3 -0
- package/dist/commands/viewers.js +72 -0
- package/dist/commands/voice.d.ts +3 -0
- package/dist/commands/voice.js +351 -0
- package/dist/commands/whatsapp.d.ts +3 -0
- package/dist/commands/whatsapp.js +226 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +110 -0
- package/dist/commands/worker.d.ts +3 -0
- package/dist/commands/worker.js +60 -0
- package/dist/commands/workflows.d.ts +3 -0
- package/dist/commands/workflows.js +1752 -0
- package/dist/commands/workspace.d.ts +3 -0
- package/dist/commands/workspace.js +94 -0
- package/dist/compact-list-flags.d.ts +7 -0
- package/dist/compact-list-flags.js +12 -0
- package/dist/crm-support-flags.js +14 -5
- package/dist/customer-operation-commands.js +8 -6
- package/dist/diagnostic-uploader.d.ts +3 -1
- package/dist/diagnostic-uploader.js +29 -8
- package/dist/help.d.ts +5 -0
- package/dist/help.js +22 -0
- package/dist/index.d.ts +98 -1
- package/dist/index.js +331 -30719
- package/dist/local-module-loader.d.ts +4 -0
- package/dist/local-module-loader.js +156 -0
- package/dist/visual-commands.js +9 -5
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +2 -2
- package/node_modules/@oxygen/shared/dist/agent-session-schedule.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/agent-session-schedule.js +69 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +14 -5
- package/node_modules/@oxygen/shared/dist/agent-sessions.js +14 -3
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +175 -14
- package/node_modules/@oxygen/shared/dist/compact-list-result.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/compact-list-result.js +27 -0
- package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.d.ts +12 -1
- package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +5 -2
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +4 -2
- package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.d.ts +57 -0
- package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.js +23 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +43 -2
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +17 -14
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.d.ts +49 -0
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.js +14 -0
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-sync.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/limits-read.js +60 -7
- package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +9 -1
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +13 -5
- package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/teammates.d.ts +16 -0
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +115 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +21 -3
- package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +31 -1
- package/node_modules/@oxygen/shared/dist/cost-estimate.js +56 -15
- package/node_modules/@oxygen/shared/dist/diagnostic-credentials.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-credentials.js +2 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-lock.js +71 -44
- package/node_modules/@oxygen/shared/dist/diagnostic-spool.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-spool.js +348 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.js +42 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.js +59 -213
- package/node_modules/@oxygen/shared/dist/diagnostics.js +3 -2
- package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +16 -5
- package/node_modules/@oxygen/shared/dist/email-dsn.js +97 -1
- package/node_modules/@oxygen/shared/dist/emailguard-placement-probe.js +5 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +4 -5
- package/node_modules/@oxygen/shared/dist/file-import.js +5 -2
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +30 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +20 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +6 -2
- package/node_modules/@oxygen/shared/dist/knowledge-seed-content.d.ts +17 -7
- package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +81 -17
- package/node_modules/@oxygen/shared/dist/langfuse.js +8 -4
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +6 -3
- package/node_modules/@oxygen/shared/dist/mailbox-import.js +9 -9
- package/node_modules/@oxygen/shared/dist/mcp-server-info.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/mcp-server-info.js +10 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +14 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.js +17 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
- package/node_modules/@oxygen/shared/dist/provider-selection.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/provider-selection.js +3 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/repricing.js +2 -2
- package/node_modules/@oxygen/shared/dist/schedule-label.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/schedule-label.js +12 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +20 -6
- package/node_modules/@oxygen/shared/dist/teammate-groups.d.ts +55 -0
- package/node_modules/@oxygen/shared/dist/teammate-groups.js +74 -0
- package/node_modules/@oxygen/shared/dist/teammate-messages.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/teammate-messages.js +88 -0
- package/node_modules/@oxygen/shared/dist/teammate-notes.d.ts +58 -0
- package/node_modules/@oxygen/shared/dist/teammate-notes.js +73 -0
- package/node_modules/@oxygen/shared/dist/teammate-routines.d.ts +95 -0
- package/node_modules/@oxygen/shared/dist/teammate-routines.js +266 -0
- package/node_modules/@oxygen/shared/dist/teammate-tasks.d.ts +56 -0
- package/node_modules/@oxygen/shared/dist/teammate-tasks.js +114 -0
- package/node_modules/@oxygen/shared/dist/teammates.d.ts +78 -0
- package/node_modules/@oxygen/shared/dist/teammates.js +140 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +120 -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/visual-render.d.ts +9 -1
- package/node_modules/@oxygen/shared/dist/visual-render.js +12 -5
- package/node_modules/@oxygen/shared/dist/warmup-metrics.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/warmup-metrics.js +44 -0
- package/package.json +1 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { addContractFlags } from "../contract-flags.js";
|
|
2
|
+
import { requestOxygen } from "../http-client.js";
|
|
3
|
+
import { addWhatsAppLimitFlags, buildSenderLimitsBody } from "../sender-limits-flags.js";
|
|
4
|
+
import { readOption } from "../util.js";
|
|
5
|
+
import { WHATSAPP_ACCOUNT_FLAGS, whatsappAccountQuery } from "../whatsapp-account-flags.js";
|
|
6
|
+
import { WHATSAPP_ACCOUNTS_READ_CONTRACT } from "@oxygen/shared/contracts/whatsapp-reads";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
export function registerWhatsappFamily(program, handleAsyncAction) {
|
|
9
|
+
program.addCommand(new Command("whatsapp")
|
|
10
|
+
.description("Manage the org's connected WhatsApp accounts for Sequencer: accounts (list), connect, sync, inspect, disconnect, tune daily limits, and warm-send into an existing conversation. Cold outbound runs through a WhatsApp sequence (sequences create --channels whatsapp).")
|
|
11
|
+
.addCommand(addContractFlags(new Command("accounts")
|
|
12
|
+
.description("Read stored WhatsApp account health. Disconnected accounts are hidden by default; --include-disconnected includes retained accounts, --status selects one status. Presence is not send readiness. Use summary and exact account-id/value for long public metadata."), WHATSAPP_ACCOUNTS_READ_CONTRACT, WHATSAPP_ACCOUNT_FLAGS)
|
|
13
|
+
.option("--json", "Print a JSON envelope.")
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
await handleAsyncAction("whatsapp accounts", options, () => {
|
|
16
|
+
const suffix = whatsappAccountQuery(options).toString();
|
|
17
|
+
return requestOxygen(`/api/cli/whatsapp/accounts${suffix ? `?${suffix}` : ""}`);
|
|
18
|
+
});
|
|
19
|
+
}))
|
|
20
|
+
.addCommand(new Command("connect")
|
|
21
|
+
.description("Get a hosted sign-in link to connect a new WhatsApp account by scanning a QR in WhatsApp → Linked Devices (or reconnect with --reconnect). Running this command creates a pending provider authorization. Inspect the current monthly price and first billing timing with `oxygen billing commitments --view connection-prices --json` before connecting. Newly connected accounts start a mandatory warm-up ramp.")
|
|
22
|
+
.option("--reconnect <connection_id>", "Reconnect an existing connection id instead of linking a new account.")
|
|
23
|
+
.option("--pairing-phone-number <phone>", "Offer phone-number pairing instead of QR where supported; use your international number.")
|
|
24
|
+
.option("--json", "Print a JSON envelope.")
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
await handleAsyncAction("whatsapp connect", options, () => {
|
|
27
|
+
const reconnect = readOption(options.reconnect);
|
|
28
|
+
const pairingPhoneNumber = readOption(options.pairingPhoneNumber);
|
|
29
|
+
return requestOxygen("/api/cli/whatsapp/connect", {
|
|
30
|
+
method: "POST",
|
|
31
|
+
body: {
|
|
32
|
+
...(reconnect ? { reconnect_connection_id: reconnect } : {}),
|
|
33
|
+
...(pairingPhoneNumber ? { pairing_phone_number: pairingPhoneNumber } : {}),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}))
|
|
38
|
+
.addCommand(new Command("get")
|
|
39
|
+
.description("Get one WhatsApp account with limits, warm-up ramp state (account age + today's effective send ceiling), daily-reset timezone, and usage. Direct sends are warm-only; opted-in WhatsApp Sequences share these caps. <id> accepts an account id, connection id, or Unipile account id.")
|
|
40
|
+
.argument("<id>", "WhatsApp account id, connection id, or Unipile account id.")
|
|
41
|
+
.option("--json", "Print a JSON envelope.")
|
|
42
|
+
.action(async (id, options) => {
|
|
43
|
+
await handleAsyncAction("whatsapp get", options, () => requestOxygen(`/api/cli/whatsapp/accounts/${encodeURIComponent(id)}`));
|
|
44
|
+
}))
|
|
45
|
+
.addCommand(new Command("sync")
|
|
46
|
+
.description("Refresh WhatsApp account state (status) from Unipile. Pass --connection-id to sync one account, or omit to sync all.")
|
|
47
|
+
.option("--connection-id <id>", "Sync a specific connection id. Defaults to syncing all connected accounts.")
|
|
48
|
+
.option("--json", "Print a JSON envelope.")
|
|
49
|
+
.action(async (options) => {
|
|
50
|
+
await handleAsyncAction("whatsapp sync", options, () => {
|
|
51
|
+
const connectionId = readOption(options.connectionId);
|
|
52
|
+
return requestOxygen("/api/cli/whatsapp/accounts/sync", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
body: { ...(connectionId ? { connection_id: connectionId } : {}) },
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}))
|
|
58
|
+
.addCommand(new Command("disconnect")
|
|
59
|
+
.description("Disconnect a WhatsApp account so it stops sending. Works on an account that is already disconnected. <id> accepts an account id, connection id, or Unipile account id.")
|
|
60
|
+
.argument("<id>", "WhatsApp account id, connection id, or Unipile account id.")
|
|
61
|
+
.option("--json", "Print a JSON envelope.")
|
|
62
|
+
.action(async (id, options) => {
|
|
63
|
+
await handleAsyncAction("whatsapp disconnect", options, () => requestOxygen(`/api/cli/whatsapp/accounts/${encodeURIComponent(id)}`, { method: "DELETE" }));
|
|
64
|
+
}))
|
|
65
|
+
.addCommand(new Command("limits")
|
|
66
|
+
.description("View and adjust per-account WhatsApp daily limits and the daily-reset timezone. The warm-up ramp is a non-bypassable ceiling on direct sends and opted-in Sequence actions.")
|
|
67
|
+
.addCommand(new Command("get")
|
|
68
|
+
.description("Show current WhatsApp limits, the warm-up ramp state, direct-versus-Sequence delivery policy, daily-reset timezone, defaults, and safe maximums. <id> accepts an account id, connection id, or Unipile account id.")
|
|
69
|
+
.argument("<id>", "WhatsApp account id, connection id, or Unipile account id.")
|
|
70
|
+
.option("--json", "Print a JSON envelope.")
|
|
71
|
+
.action(async (id, options) => {
|
|
72
|
+
await handleAsyncAction("whatsapp limits get", options, () => requestOxygen(`/api/cli/whatsapp/accounts/${encodeURIComponent(id)}/limits`));
|
|
73
|
+
}))
|
|
74
|
+
.addCommand(addWhatsAppLimitFlags(new Command("set")
|
|
75
|
+
.description("Adjust per-account WhatsApp daily limits and the daily-reset timezone. Values are clamped to safe maximums; the warm-up ceiling still applies to direct and Sequence traffic. <id> accepts an account id, connection id, or Unipile account id.")
|
|
76
|
+
.argument("<id>", "WhatsApp account id, connection id, or Unipile account id."))
|
|
77
|
+
.option("--working-days <days>", "Allowed weekdays, e.g. mon-fri or 1,3,5.")
|
|
78
|
+
.option("--working-hours-start <HH:MM>", "Local start of the allowed sending window.")
|
|
79
|
+
.option("--working-hours-end <HH:MM>", "Local end of the allowed sending window.")
|
|
80
|
+
.option("--timezone <tz>", "IANA timezone the daily action counters reset in, e.g. America/New_York.")
|
|
81
|
+
.option("--json", "Print a JSON envelope.")
|
|
82
|
+
.action(async (id, options) => {
|
|
83
|
+
await handleAsyncAction("whatsapp limits set", options, () => {
|
|
84
|
+
const body = buildSenderLimitsBody(options);
|
|
85
|
+
return requestOxygen(`/api/cli/whatsapp/accounts/${encodeURIComponent(id)}/limits`, {
|
|
86
|
+
method: "PATCH",
|
|
87
|
+
body,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
})))
|
|
91
|
+
.addCommand(new Command("warm-send")
|
|
92
|
+
.description("Send ONE warm WhatsApp message into an EXISTING conversation (warm-only; a cold phone number with no prior conversation is rejected). Approval-gated: omit --approved for a preview, then re-run with --approved to send. For cold outbound at scale, use a WhatsApp sequence instead.")
|
|
93
|
+
.requiredOption("--account <ref>", "Connected WhatsApp account id, connection id, Unipile account id, or display-name ref that owns the conversation.")
|
|
94
|
+
.requiredOption("--text <text>", "Message text to send.")
|
|
95
|
+
.option("--conversation <id>", "Existing WhatsApp conversation id or Unipile chat id to send into.")
|
|
96
|
+
.option("--approved", "Actually send. Omit for a preview only.")
|
|
97
|
+
.option("--json", "Print a JSON envelope.")
|
|
98
|
+
.action(async (options) => {
|
|
99
|
+
await handleAsyncAction("whatsapp warm-send", options, () => {
|
|
100
|
+
const account = readOption(options.account);
|
|
101
|
+
const text = readOption(options.text);
|
|
102
|
+
if (!account)
|
|
103
|
+
throw new Error("--account is required.");
|
|
104
|
+
if (!text)
|
|
105
|
+
throw new Error("--text is required.");
|
|
106
|
+
const conversation = readOption(options.conversation);
|
|
107
|
+
if (!conversation) {
|
|
108
|
+
throw new Error("--conversation is required — warm-send targets an existing WhatsApp conversation, not a cold phone number.");
|
|
109
|
+
}
|
|
110
|
+
return requestOxygen("/api/cli/whatsapp/warm-send", {
|
|
111
|
+
method: "POST",
|
|
112
|
+
body: {
|
|
113
|
+
account,
|
|
114
|
+
text,
|
|
115
|
+
conversation_id: conversation,
|
|
116
|
+
...(options.approved ? { approved: true } : {}),
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}))
|
|
121
|
+
.addCommand(new Command("history")
|
|
122
|
+
.description("Backfill one WhatsApp conversation's full message history. Poll the same command until status is SYNC_DONE. This is the only history backfill WhatsApp has — `whatsapp sync` refreshes account status, not messages.")
|
|
123
|
+
.argument("<chat>", "Unipile chat id of the conversation to backfill.")
|
|
124
|
+
.option("--account <ref>", "Connected WhatsApp account (account id, connection id, or Unipile account id). Required only when several are connected.")
|
|
125
|
+
.option("--json", "Print a JSON envelope.")
|
|
126
|
+
.action(async (chat, options) => {
|
|
127
|
+
await handleAsyncAction("whatsapp history", options, () => {
|
|
128
|
+
const account = readOption(options.account);
|
|
129
|
+
return requestOxygen(`/api/cli/whatsapp/chats/${encodeURIComponent(chat)}/sync`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: { ...(account ? { account } : {}) },
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}))
|
|
135
|
+
.addCommand(new Command("profile")
|
|
136
|
+
.description("Show or edit the connected number's own WhatsApp profile (display name, About/status, picture).")
|
|
137
|
+
.addCommand(new Command("show")
|
|
138
|
+
.description("Show the connected number's WhatsApp profile.")
|
|
139
|
+
.option("--account <ref>", "Connected WhatsApp account. Required only when several are connected.")
|
|
140
|
+
.option("--json", "Print a JSON envelope.")
|
|
141
|
+
.action(async (options) => {
|
|
142
|
+
await handleAsyncAction("whatsapp profile show", options, () => {
|
|
143
|
+
const account = readOption(options.account);
|
|
144
|
+
const suffix = account ? `?account=${encodeURIComponent(account)}` : "";
|
|
145
|
+
return requestOxygen(`/api/cli/whatsapp/profile${suffix}`);
|
|
146
|
+
});
|
|
147
|
+
}))
|
|
148
|
+
.addCommand(new Command("edit")
|
|
149
|
+
.description("Edit the connected number's WhatsApp profile. Every contact who has the number sees the change, so this is approval-gated: omit --approved for a preview.")
|
|
150
|
+
.option("--display-name <name>", "New WhatsApp display name.")
|
|
151
|
+
.option("--about <text>", "New About/status text.")
|
|
152
|
+
.option("--picture <url>", "New profile picture (file path or URL).")
|
|
153
|
+
.option("--account <ref>", "Connected WhatsApp account. Required only when several are connected.")
|
|
154
|
+
.option("--approved", "Actually apply the edit. Omit for a preview only.")
|
|
155
|
+
.option("--json", "Print a JSON envelope.")
|
|
156
|
+
.action(async (options) => {
|
|
157
|
+
await handleAsyncAction("whatsapp profile edit", options, () => {
|
|
158
|
+
const displayName = readOption(options.displayName);
|
|
159
|
+
const about = readOption(options.about);
|
|
160
|
+
const picture = readOption(options.picture);
|
|
161
|
+
if (!displayName && !about && !picture) {
|
|
162
|
+
throw new Error("Pass at least one of --display-name, --about, or --picture.");
|
|
163
|
+
}
|
|
164
|
+
const account = readOption(options.account);
|
|
165
|
+
return requestOxygen("/api/cli/whatsapp/profile", {
|
|
166
|
+
method: "PATCH",
|
|
167
|
+
body: {
|
|
168
|
+
...(displayName ? { display_name: displayName } : {}),
|
|
169
|
+
...(about ? { about } : {}),
|
|
170
|
+
...(picture ? { picture_url: picture } : {}),
|
|
171
|
+
...(account ? { account } : {}),
|
|
172
|
+
...(options.approved ? { approved: true } : {}),
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
})))
|
|
177
|
+
.addCommand(new Command("chat-action")
|
|
178
|
+
.description("Act on one WhatsApp chat: read/mute/archive/pin state, labels, group membership, or the group invite link.")
|
|
179
|
+
.argument("<chat>", "Unipile chat id.")
|
|
180
|
+
.requiredOption("--action <action>", "setReadStatus | setMuteStatus | setArchiveStatus | setPinnedStatus | setLabel | addParticipant | removeParticipant | getInviteLink.")
|
|
181
|
+
.option("--value <value>", "Required for every action except getInviteLink: true/false for the status actions, a participant id for addParticipant/removeParticipant, or the label text for setLabel.")
|
|
182
|
+
.option("--account <ref>", "Connected WhatsApp account. Required only when several are connected.")
|
|
183
|
+
.option("--approved", "Required for actions other participants can see (addParticipant, removeParticipant, getInviteLink). Omit for a preview.")
|
|
184
|
+
.option("--json", "Print a JSON envelope.")
|
|
185
|
+
.action(async (chat, options) => {
|
|
186
|
+
await handleAsyncAction("whatsapp chat-action", options, () => {
|
|
187
|
+
const action = readOption(options.action);
|
|
188
|
+
if (!action)
|
|
189
|
+
throw new Error("--action is required.");
|
|
190
|
+
const value = readOption(options.value);
|
|
191
|
+
const account = readOption(options.account);
|
|
192
|
+
return requestOxygen(`/api/cli/whatsapp/chats/${encodeURIComponent(chat)}/action`, {
|
|
193
|
+
method: "POST",
|
|
194
|
+
body: {
|
|
195
|
+
action,
|
|
196
|
+
...(value !== undefined && value !== null ? { value } : {}),
|
|
197
|
+
...(account ? { account } : {}),
|
|
198
|
+
...(options.approved ? { approved: true } : {}),
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}))
|
|
203
|
+
.addCommand(new Command("forward")
|
|
204
|
+
.description("Forward an existing WhatsApp message into another chat. This delivers a new message, so it spends send budget and is approval-gated: omit --approved for a preview.")
|
|
205
|
+
.argument("<message>", "Unipile message id to forward.")
|
|
206
|
+
.requiredOption("--to-chat <id>", "Unipile chat id to forward the message into.")
|
|
207
|
+
.option("--account <ref>", "Connected WhatsApp account. Required only when several are connected.")
|
|
208
|
+
.option("--approved", "Actually forward. Omit for a preview only.")
|
|
209
|
+
.option("--json", "Print a JSON envelope.")
|
|
210
|
+
.action(async (message, options) => {
|
|
211
|
+
await handleAsyncAction("whatsapp forward", options, () => {
|
|
212
|
+
const toChat = readOption(options.toChat);
|
|
213
|
+
if (!toChat)
|
|
214
|
+
throw new Error("--to-chat is required.");
|
|
215
|
+
const account = readOption(options.account);
|
|
216
|
+
return requestOxygen(`/api/cli/whatsapp/messages/${encodeURIComponent(message)}/forward`, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
body: {
|
|
219
|
+
to_chat_id: toChat,
|
|
220
|
+
...(account ? { account } : {}),
|
|
221
|
+
...(options.approved ? { approved: true } : {}),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
})));
|
|
226
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { emitCliFailure, writeJson } from "../cli-output.js";
|
|
2
|
+
import { ansi, describeProfileSource, identityFromWhoami, resolveActiveProfileWithSource, storedOrganizationFromOption } from "../command-support.js";
|
|
3
|
+
import { defaultApiUrl, readEnvToken, saveCredentials } from "../credentials.js";
|
|
4
|
+
import { requestOxygen } from "../http-client.js";
|
|
5
|
+
import { success } from "@oxygen/shared/cli-result";
|
|
6
|
+
import { OXYGEN_VERSION } from "@oxygen/shared/version";
|
|
7
|
+
import { stdout as output } from "node:process";
|
|
8
|
+
async function handleWhoamiAction(options) {
|
|
9
|
+
try {
|
|
10
|
+
const identity = await requestOxygen("/api/cli/whoami", {
|
|
11
|
+
enforceMinimumCliVersion: false,
|
|
12
|
+
});
|
|
13
|
+
const context = await resolveActiveProfileWithSource();
|
|
14
|
+
// Compact, credential-free identity shape for health probes and ops
|
|
15
|
+
// sweeps: the full envelope carries apiKey token prefix/suffix metadata,
|
|
16
|
+
// which forced every automated consumer to sanitize before logging
|
|
17
|
+
// (recurring fleet friction since 2026-06-18).
|
|
18
|
+
if (options.summaryJson) {
|
|
19
|
+
writeJson(success("whoami", {
|
|
20
|
+
user: { id: identity.user.id, email: identity.user.email },
|
|
21
|
+
organization: {
|
|
22
|
+
id: identity.organization.id,
|
|
23
|
+
name: identity.organization.name,
|
|
24
|
+
slug: identity.organization.slug,
|
|
25
|
+
},
|
|
26
|
+
auth_type: identity.authType ?? identity.apiKey?.kind ?? null,
|
|
27
|
+
profile: context.resolution.exists ? context.resolution.name : null,
|
|
28
|
+
profile_source: context.source,
|
|
29
|
+
api_url: context.resolution.credentials?.apiUrl ?? null,
|
|
30
|
+
cli_version: OXYGEN_VERSION,
|
|
31
|
+
}));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (context.resolution.exists) {
|
|
35
|
+
const existingCredentials = context.resolution.credentials;
|
|
36
|
+
const cached = existingCredentials?.identity;
|
|
37
|
+
const cachedOrg = existingCredentials?.activeOrganization ?? cached?.organization ?? null;
|
|
38
|
+
const orgChanged = !cachedOrg || cachedOrg.id !== identity.organization.id;
|
|
39
|
+
const userChanged = !cached || cached.user.id !== identity.user.id;
|
|
40
|
+
// Heal only from a request that ran as this profile. An environment token
|
|
41
|
+
// replaces its token, and OXYGEN_ORG or --org picks a workspace for this
|
|
42
|
+
// command only; saving what either resolved repointed the profile, and with
|
|
43
|
+
// it every later unpinned command in every shell that shares it (2026-10-05).
|
|
44
|
+
const pinnedElsewhere = Boolean(process.env.OXYGEN_ORG?.trim()) && orgChanged;
|
|
45
|
+
if (existingCredentials && !readEnvToken() && !pinnedElsewhere && (orgChanged || userChanged)) {
|
|
46
|
+
const refreshed = {
|
|
47
|
+
token: existingCredentials.token,
|
|
48
|
+
apiUrl: existingCredentials.apiUrl,
|
|
49
|
+
activeOrganization: storedOrganizationFromOption(identity.organization),
|
|
50
|
+
identity: identityFromWhoami(identity),
|
|
51
|
+
};
|
|
52
|
+
if (existingCredentials.authKind)
|
|
53
|
+
refreshed.authKind = existingCredentials.authKind;
|
|
54
|
+
await saveCredentials(refreshed, process.env, {
|
|
55
|
+
profile: context.resolution.name,
|
|
56
|
+
activate: false,
|
|
57
|
+
}).catch(() => undefined);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const data = {
|
|
61
|
+
...identity,
|
|
62
|
+
profile: context.resolution.exists ? context.resolution.name : null,
|
|
63
|
+
profile_source: context.source,
|
|
64
|
+
};
|
|
65
|
+
if (options.json) {
|
|
66
|
+
writeJson(success("whoami", data));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
process.stdout.write(formatWhoami(identity, context));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
emitCliFailure("whoami", error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function formatWhoami(identity, context) {
|
|
76
|
+
const styles = ansi(output.isTTY === true && !process.env.NO_COLOR);
|
|
77
|
+
const email = identity.user.email ?? identity.user.id;
|
|
78
|
+
const orgName = identity.organization.name || identity.organization.slug || identity.organization.id;
|
|
79
|
+
const orgSlug = identity.organization.slug ?? null;
|
|
80
|
+
const org = orgSlug && orgSlug !== orgName ? `${orgName} (${orgSlug})` : orgName;
|
|
81
|
+
const sourceLabel = describeProfileSource(context.source);
|
|
82
|
+
const profileName = context.resolution.exists ? context.resolution.name : "(no stored profile)";
|
|
83
|
+
const apiUrl = context.resolution.credentials?.apiUrl ?? defaultApiUrl();
|
|
84
|
+
// Every Oxygen size shares plan_tier "oxygen"; the name says which size.
|
|
85
|
+
const planName = identity.organization.plan_name ?? null;
|
|
86
|
+
const rows = [
|
|
87
|
+
["Account", email],
|
|
88
|
+
["Organization", org],
|
|
89
|
+
...(planName ? [["Plan", planName]] : []),
|
|
90
|
+
["Profile", `${profileName} ${styles.dim(`(${sourceLabel})`)}`],
|
|
91
|
+
["API", apiUrl],
|
|
92
|
+
];
|
|
93
|
+
const labelWidth = Math.max(...rows.map(([label]) => label.length));
|
|
94
|
+
const lines = [
|
|
95
|
+
"",
|
|
96
|
+
...rows.map(([label, value]) => ` ${styles.dim(label.padEnd(labelWidth))} ${value}`),
|
|
97
|
+
"",
|
|
98
|
+
];
|
|
99
|
+
return lines.join("\n");
|
|
100
|
+
}
|
|
101
|
+
export function registerWhoamiFamily(program, handleAsyncAction) {
|
|
102
|
+
program
|
|
103
|
+
.command("whoami")
|
|
104
|
+
.description("Show the current Oxygen CLI identity.")
|
|
105
|
+
.option("--json", "Print a JSON envelope.")
|
|
106
|
+
.option("--summary-json", "Print a compact identity envelope with no credential-shaped fields (safe for health probes and logs).")
|
|
107
|
+
.action(async (options) => {
|
|
108
|
+
await handleWhoamiAction(options);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readPositiveInt } from "../cli-values.js";
|
|
2
|
+
import { requestOxygen } from "../http-client.js";
|
|
3
|
+
import { readOption } from "../util.js";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
export function registerWorkerFamily(program, handleAsyncAction) {
|
|
6
|
+
program
|
|
7
|
+
.command("worker")
|
|
8
|
+
.description("Background worker commands.")
|
|
9
|
+
.addCommand(new Command("queue-stats")
|
|
10
|
+
.description("Show background action and ingestion queue health.")
|
|
11
|
+
.option("--json", "Print a JSON envelope.")
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
await handleAsyncAction("worker queue-stats", options, () => requestOxygen("/api/cli/worker/queue-stats"));
|
|
14
|
+
}))
|
|
15
|
+
.addCommand(new Command("failures")
|
|
16
|
+
.description("List failed background action and ingestion items.")
|
|
17
|
+
.option("--queue <queue>", "all, actions, ingestions, or postgres_queue. Defaults to all. Legacy aliases: postgres_jobs, bullmq, redis, jobs.")
|
|
18
|
+
.option("--limit <n>", "Maximum failed items per queue. Defaults to 25; server cap is 100.")
|
|
19
|
+
.option("--json", "Print a JSON envelope.")
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
await handleAsyncAction("worker failures", options, () => {
|
|
22
|
+
const query = new URLSearchParams();
|
|
23
|
+
if (readOption(options.queue))
|
|
24
|
+
query.set("queue", readOption(options.queue) ?? "");
|
|
25
|
+
const limit = readPositiveInt(options.limit);
|
|
26
|
+
if (limit)
|
|
27
|
+
query.set("limit", String(limit));
|
|
28
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
29
|
+
return requestOxygen(`/api/cli/worker/failures${suffix}`);
|
|
30
|
+
});
|
|
31
|
+
}))
|
|
32
|
+
.addCommand(new Command("repair")
|
|
33
|
+
.description("Repair stale background action, ingestion, and workflow queue state.")
|
|
34
|
+
.option("--json", "Print a JSON envelope.")
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
await handleAsyncAction("worker repair", options, () => requestOxygen("/api/cli/worker/repair", {
|
|
37
|
+
method: "POST",
|
|
38
|
+
body: {},
|
|
39
|
+
}));
|
|
40
|
+
}))
|
|
41
|
+
.addCommand(new Command("run-once")
|
|
42
|
+
.description("Compatibility alias for worker repair; repairs Postgres-backed worker state for the current organization.")
|
|
43
|
+
.option("--claim-limit <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
44
|
+
.option("--concurrency <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
45
|
+
.option("--enrichment-concurrency <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
46
|
+
.option("--lease-seconds <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
47
|
+
.option("--provider-timeout-ms <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
48
|
+
.option("--recipe-timeout-ms <n>", "Compatibility option; ignored by the Postgres worker.")
|
|
49
|
+
.option("--json", "Print a JSON envelope.")
|
|
50
|
+
.action(async (options) => {
|
|
51
|
+
// The legacy tuning options are accepted for backward compatibility but
|
|
52
|
+
// intentionally ignored: the Postgres worker has no BullMQ knobs, and the
|
|
53
|
+
// route runs repairTenantWorkspaceQueues regardless of the request body.
|
|
54
|
+
// Send an empty body so we stop modeling ignored knobs as meaningful fields.
|
|
55
|
+
await handleAsyncAction("worker run-once", options, () => requestOxygen("/api/cli/worker/run-once", {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: {},
|
|
58
|
+
}));
|
|
59
|
+
}));
|
|
60
|
+
}
|