@oxygen-agent/cli 1.1014.135 → 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,347 @@
|
|
|
1
|
+
import { isTruthyEnv } from "../auto-update.js";
|
|
2
|
+
import { createBrowserLoginSession, createRelayLoginSession, openBrowser } from "../browser-login.js";
|
|
3
|
+
import { emitCliFailure, writeJson } from "../cli-output.js";
|
|
4
|
+
import { buildPostLoginHint, formatLoginSuccessForResolved, persistOrganizationSelection, pickProfileForLogin, readEnvProfileName, resolveLoginIdentity, runKnowledgeMirrorSyncAll, storedOrganizationFromOption } from "../command-support.js";
|
|
5
|
+
import { defaultApiUrl, normalizeApiUrl, saveCredentials } from "../credentials.js";
|
|
6
|
+
import { ensureFreshCliForApiUrl, requestOxygen } from "../http-client.js";
|
|
7
|
+
import { runAutomaticSkillsInstall } from "../skills.js";
|
|
8
|
+
import { readErrorMessage, readOption } from "../util.js";
|
|
9
|
+
import { OxygenError, success } from "@oxygen/shared/cli-result";
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
11
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
12
|
+
import { createInterface } from "node:readline/promises";
|
|
13
|
+
const BROWSER_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
14
|
+
const BROWSER_LOGIN_FALLBACK_HINT_MS = 15 * 1000;
|
|
15
|
+
const OXYGEN_SPINNER_INTERVAL_MS = 90;
|
|
16
|
+
const OXYGEN_SPINNER_FRAMES = [
|
|
17
|
+
"[Oxygen ]",
|
|
18
|
+
"[ Oxygen ]",
|
|
19
|
+
"[ Oxygen]",
|
|
20
|
+
"[ nOxyge ]",
|
|
21
|
+
"[ enOxyg ]",
|
|
22
|
+
"[ genOxy ]",
|
|
23
|
+
"[ ygenOx ]",
|
|
24
|
+
"[ xygenO ]",
|
|
25
|
+
];
|
|
26
|
+
async function handleLoginAction(options) {
|
|
27
|
+
try {
|
|
28
|
+
const data = await login(options);
|
|
29
|
+
if (options.json) {
|
|
30
|
+
writeJson(success("login", data));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
emitCliFailure("login", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Select an organization server-side, then persist it. Used by `login --org`. */
|
|
38
|
+
async function selectOrganizationForProfile(input) {
|
|
39
|
+
const data = await requestOxygen("/api/cli/orgs/select", {
|
|
40
|
+
method: "POST",
|
|
41
|
+
body: { organization: input.organization },
|
|
42
|
+
credentials: input.credentials,
|
|
43
|
+
});
|
|
44
|
+
const selectionPersisted = await persistOrganizationSelection({
|
|
45
|
+
profile: input.profile,
|
|
46
|
+
credentials: input.credentials,
|
|
47
|
+
organization: data.currentOrganization,
|
|
48
|
+
});
|
|
49
|
+
return { data, selectionPersisted };
|
|
50
|
+
}
|
|
51
|
+
async function login(options) {
|
|
52
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
53
|
+
await ensureFreshCliForApiUrl(apiUrl);
|
|
54
|
+
const token = readOption(options.token) ?? await promptForToken({
|
|
55
|
+
apiUrl,
|
|
56
|
+
browser: options.browser !== false,
|
|
57
|
+
json: Boolean(options.json),
|
|
58
|
+
});
|
|
59
|
+
const loginIdentity = await resolveLoginIdentity(token, apiUrl);
|
|
60
|
+
const credentials = loginIdentity.credentials;
|
|
61
|
+
const explicitProfile = readOption(options.profile) ?? readEnvProfileName();
|
|
62
|
+
let chosenProfile;
|
|
63
|
+
let renamed = false;
|
|
64
|
+
if (explicitProfile) {
|
|
65
|
+
chosenProfile = explicitProfile;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const picked = await pickProfileForLogin(loginIdentity);
|
|
69
|
+
chosenProfile = picked.name;
|
|
70
|
+
renamed = picked.renamed;
|
|
71
|
+
}
|
|
72
|
+
const profile = await saveCredentials(credentials, process.env, {
|
|
73
|
+
profile: chosenProfile,
|
|
74
|
+
allowHostChange: options.force === true,
|
|
75
|
+
});
|
|
76
|
+
// `--org` collapses the two-step every agency user was doing by hand (`login`,
|
|
77
|
+
// then `orgs use`). It is skipped when the credential already landed in the
|
|
78
|
+
// requested workspace, so a workspace-bound key naming its own workspace
|
|
79
|
+
// succeeds instead of being refused for asking to stay where it is.
|
|
80
|
+
const requestedOrg = readOption(options.org) ?? process.env.OXYGEN_ORG?.trim() ?? null;
|
|
81
|
+
let activeOrganization = loginIdentity.activeOrganization;
|
|
82
|
+
let organizations = loginIdentity.organizations;
|
|
83
|
+
if (requestedOrg && !matchesStoredOrganization(activeOrganization, requestedOrg)) {
|
|
84
|
+
const selected = await selectOrganizationForProfile({
|
|
85
|
+
organization: requestedOrg,
|
|
86
|
+
profile,
|
|
87
|
+
credentials,
|
|
88
|
+
});
|
|
89
|
+
activeOrganization = storedOrganizationFromOption(selected.data.currentOrganization);
|
|
90
|
+
organizations = organizations.map((organization) => ({
|
|
91
|
+
...organization,
|
|
92
|
+
selected: organization.id === selected.data.currentOrganization.id,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
const skillsInstall = await runAutomaticSkillsInstall({ apiUrl: credentials.apiUrl, credentials });
|
|
96
|
+
// Opt-in eager mirror of every reachable workspace, off by default so login
|
|
97
|
+
// stays fast and non-surprising. Best-effort: a sync failure never fails login.
|
|
98
|
+
if (process.env.OXYGEN_KNOWLEDGE_AUTOSYNC === "1") {
|
|
99
|
+
await runKnowledgeMirrorSyncAll({ ifStale: true }).catch(() => undefined);
|
|
100
|
+
}
|
|
101
|
+
const resolved = {
|
|
102
|
+
...loginIdentity,
|
|
103
|
+
activeOrganization,
|
|
104
|
+
organizations,
|
|
105
|
+
...(requestedOrg ? { selectionRequired: false } : {}),
|
|
106
|
+
};
|
|
107
|
+
if (!options.json) {
|
|
108
|
+
process.stdout.write(formatLoginSuccessForResolved(resolved, profile, { renamed, skillsInstall }));
|
|
109
|
+
const hint = await buildPostLoginHint(profile);
|
|
110
|
+
if (hint)
|
|
111
|
+
process.stdout.write(hint);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
logged_in: true,
|
|
115
|
+
profile,
|
|
116
|
+
api_url: credentials.apiUrl,
|
|
117
|
+
user: resolved.user,
|
|
118
|
+
organization: resolved.activeOrganization,
|
|
119
|
+
organizations: resolved.organizations,
|
|
120
|
+
selection_required: resolved.selectionRequired,
|
|
121
|
+
skills_install: skillsInstall,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// `--org` accepts whatever `orgs list` prints, so the match has to accept the
|
|
125
|
+
// same handles the server does — minus clerkOrgId, which stored profiles do not
|
|
126
|
+
// carry.
|
|
127
|
+
function matchesStoredOrganization(organization, ref) {
|
|
128
|
+
if (!organization)
|
|
129
|
+
return false;
|
|
130
|
+
const normalized = ref.trim();
|
|
131
|
+
if (!normalized)
|
|
132
|
+
return false;
|
|
133
|
+
return organization.id === normalized || organization.slug === normalized;
|
|
134
|
+
}
|
|
135
|
+
async function promptForToken(options) {
|
|
136
|
+
const fallbackLoginUrl = createCliLoginUrl(options.apiUrl);
|
|
137
|
+
const settingsUrl = cliSettingsUrl(options.apiUrl);
|
|
138
|
+
if (options.json) {
|
|
139
|
+
throw new OxygenError("missing_token", `Pass a CLI API token with --token when using --json. Create one at ${settingsUrl}.`, {
|
|
140
|
+
details: { login_url: fallbackLoginUrl },
|
|
141
|
+
exitCode: 1,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// The browser sign-in needs no terminal, so an agent's tool shell (Claude
|
|
145
|
+
// Code, Codex, Cursor), which has no TTY, can still run the setup prompt's
|
|
146
|
+
// `oxygen login` while the person approves in their browser. Only the pasted
|
|
147
|
+
// token fallback reads stdin. CI can never approve, so it still fails at once.
|
|
148
|
+
const interactive = process.stdin.isTTY === true;
|
|
149
|
+
if (!interactive && (!options.browser || isTruthyEnv(process.env.CI))) {
|
|
150
|
+
throw new OxygenError("missing_token", `Pass a CLI API token with --token, or run \`oxygen login\` in an interactive terminal. Create a token at ${settingsUrl}.`, {
|
|
151
|
+
details: { login_url: fallbackLoginUrl },
|
|
152
|
+
exitCode: 1,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const browserSession = options.browser ? await createLoginSession(options.apiUrl) : null;
|
|
156
|
+
if (browserSession) {
|
|
157
|
+
const opened = openBrowser(browserSession.verificationUrl);
|
|
158
|
+
if (opened) {
|
|
159
|
+
output.write("Opened Oxygen in your browser.\n");
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
output.write("Open this URL in your browser:\n");
|
|
163
|
+
output.write(`${browserSession.verificationUrl}\n`);
|
|
164
|
+
}
|
|
165
|
+
if ("confirmationCode" in browserSession) {
|
|
166
|
+
output.write(`Confirmation code: ${browserSession.confirmationCode}\n`);
|
|
167
|
+
output.write("Approve the login in your browser — it must show this exact code.\n");
|
|
168
|
+
}
|
|
169
|
+
let spinner = startOxygenSpinner("Waiting for browser authorization");
|
|
170
|
+
// One-shot fallback hint: after 15s of silence, tell the user how to finish
|
|
171
|
+
// manually (blocked browsers and remote terminals used to look like a hang).
|
|
172
|
+
const hintTimer = setTimeout(() => {
|
|
173
|
+
spinner.stop();
|
|
174
|
+
output.write(`Still waiting? To finish manually: open ${settingsUrl}, create a session, and paste the \`oxygen login --token <token>\` command it shows.\n`);
|
|
175
|
+
spinner = startOxygenSpinner("Waiting for browser authorization");
|
|
176
|
+
}, BROWSER_LOGIN_FALLBACK_HINT_MS);
|
|
177
|
+
hintTimer.unref?.();
|
|
178
|
+
try {
|
|
179
|
+
const token = (await waitForBrowserToken(browserSession)).trim();
|
|
180
|
+
if (token) {
|
|
181
|
+
spinner.succeed("Browser authorization received");
|
|
182
|
+
return token;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
spinner.fail(`Browser login unavailable: ${readErrorMessage(error)}`);
|
|
187
|
+
if (interactive)
|
|
188
|
+
output.write(`Paste a CLI API token instead (create one at ${settingsUrl}).\n`);
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
clearTimeout(hintTimer);
|
|
192
|
+
spinner.stop();
|
|
193
|
+
await browserSession.close();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (!interactive) {
|
|
197
|
+
throw new OxygenError("missing_token", `The browser sign-in did not complete. Run \`oxygen login\` again and approve it in your browser, or pass a CLI API token with --token. Create a token at ${settingsUrl}.`, {
|
|
198
|
+
details: { login_url: fallbackLoginUrl },
|
|
199
|
+
exitCode: 1,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return promptForManualToken();
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Relay login first (server-side approve + claim — works in every browser and
|
|
206
|
+
* for remote/SSH terminals); legacy loopback only when the relay request cannot
|
|
207
|
+
* even be created (older server without the endpoint, network policy). Any
|
|
208
|
+
* create failure falls back — an old server answers with an HTML 404 that
|
|
209
|
+
* surfaces as invalid_response, not not_found.
|
|
210
|
+
*/
|
|
211
|
+
async function createLoginSession(apiUrl) {
|
|
212
|
+
try {
|
|
213
|
+
return await createRelayLoginSession(apiUrl);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Fall through to the legacy loopback handoff.
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
return await createBrowserLoginSession(apiUrl);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
output.write(`Browser login unavailable: ${readErrorMessage(error)}\n`);
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function promptForManualToken() {
|
|
227
|
+
const restoreEcho = muteTokenEcho();
|
|
228
|
+
const rl = createInterface({ input, output });
|
|
229
|
+
try {
|
|
230
|
+
const token = (await rl.question("Oxygen API token: ")).trim();
|
|
231
|
+
output.write("\n");
|
|
232
|
+
if (!token) {
|
|
233
|
+
throw new OxygenError("missing_token", "A CLI API token is required.", {
|
|
234
|
+
exitCode: 1,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return token;
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
rl.close();
|
|
241
|
+
restoreEcho();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async function waitForBrowserToken(session) {
|
|
245
|
+
let timer;
|
|
246
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
247
|
+
timer = setTimeout(() => {
|
|
248
|
+
reject(new OxygenError("browser_login_timeout", "Browser authorization timed out.", {
|
|
249
|
+
exitCode: 1,
|
|
250
|
+
}));
|
|
251
|
+
}, BROWSER_LOGIN_TIMEOUT_MS);
|
|
252
|
+
});
|
|
253
|
+
try {
|
|
254
|
+
return await Promise.race([session.waitForToken(), timeout]);
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
if (timer)
|
|
258
|
+
clearTimeout(timer);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function createCliLoginUrl(apiUrl) {
|
|
262
|
+
const url = new URL("/auth/sign-in", apiUrl);
|
|
263
|
+
url.searchParams.set("redirect_url", "/settings/cli");
|
|
264
|
+
url.searchParams.set("source", "oxygen_cli");
|
|
265
|
+
return url.toString();
|
|
266
|
+
}
|
|
267
|
+
function cliSettingsUrl(apiUrl) {
|
|
268
|
+
return new URL("/settings/cli", apiUrl).toString();
|
|
269
|
+
}
|
|
270
|
+
function startOxygenSpinner(message) {
|
|
271
|
+
if (!output.isTTY) {
|
|
272
|
+
output.write(`${message}...\n`);
|
|
273
|
+
return {
|
|
274
|
+
succeed: (successMessage) => output.write(`${successMessage}.\n`),
|
|
275
|
+
fail: (failureMessage) => output.write(`${failureMessage}.\n`),
|
|
276
|
+
stop: () => undefined,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
let frameIndex = 0;
|
|
280
|
+
let active = true;
|
|
281
|
+
let lastLineLength = 0;
|
|
282
|
+
const clearLine = () => {
|
|
283
|
+
output.write(`\r${" ".repeat(lastLineLength)}\r`);
|
|
284
|
+
lastLineLength = 0;
|
|
285
|
+
};
|
|
286
|
+
const render = () => {
|
|
287
|
+
const frame = OXYGEN_SPINNER_FRAMES[frameIndex % OXYGEN_SPINNER_FRAMES.length];
|
|
288
|
+
frameIndex += 1;
|
|
289
|
+
const line = `${frame} ${message}...`;
|
|
290
|
+
lastLineLength = Math.max(lastLineLength, line.length);
|
|
291
|
+
output.write(`\r${line}${" ".repeat(Math.max(0, lastLineLength - line.length))}`);
|
|
292
|
+
};
|
|
293
|
+
render();
|
|
294
|
+
const interval = setInterval(render, OXYGEN_SPINNER_INTERVAL_MS);
|
|
295
|
+
const finish = (status, finalMessage) => {
|
|
296
|
+
if (!active)
|
|
297
|
+
return;
|
|
298
|
+
active = false;
|
|
299
|
+
clearInterval(interval);
|
|
300
|
+
clearLine();
|
|
301
|
+
output.write(`[OXYGEN ${status}] ${finalMessage}.\n`);
|
|
302
|
+
};
|
|
303
|
+
return {
|
|
304
|
+
succeed: (successMessage) => finish("OK", successMessage),
|
|
305
|
+
fail: (failureMessage) => finish("!!", failureMessage),
|
|
306
|
+
stop: () => {
|
|
307
|
+
if (!active)
|
|
308
|
+
return;
|
|
309
|
+
active = false;
|
|
310
|
+
clearInterval(interval);
|
|
311
|
+
clearLine();
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function muteTokenEcho() {
|
|
316
|
+
if (!input.isTTY || process.platform === "win32")
|
|
317
|
+
return () => undefined;
|
|
318
|
+
try {
|
|
319
|
+
execFileSync("stty", ["-echo"], { stdio: ["inherit", "ignore", "ignore"] });
|
|
320
|
+
return () => {
|
|
321
|
+
try {
|
|
322
|
+
execFileSync("stty", ["echo"], { stdio: ["inherit", "ignore", "ignore"] });
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
// Ignore restore failures; the shell normally restores echo on process exit.
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return () => undefined;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
export function registerLoginFamily(program, handleAsyncAction) {
|
|
334
|
+
program
|
|
335
|
+
.command("login")
|
|
336
|
+
.description("Connect this terminal to Oxygen. Opens the browser to approve this terminal (matching confirmation code shown in both); paste a token with --token instead if you prefer.")
|
|
337
|
+
.option("--token <token>", "CLI API token created in the Oxygen dashboard.")
|
|
338
|
+
.option("--api-url <url>", "Oxygen API URL. Defaults to OXYGEN_API_URL or https://oxygen-agent.com.")
|
|
339
|
+
.option("--profile <name>", "Store credentials under a named CLI profile and make it active.")
|
|
340
|
+
.option("--org <organization>", "Workspace to land in after logging in (id or slug). Needs a user key, which works across every workspace you belong to; a workspace-bound key can only land in its own.")
|
|
341
|
+
.option("--force", "Repoint an existing profile at a different API host. Refused without this flag.")
|
|
342
|
+
.option("--no-browser", "Skip the browser handoff and paste the token manually.")
|
|
343
|
+
.option("--json", "Print a JSON envelope.")
|
|
344
|
+
.action(async (options) => {
|
|
345
|
+
await handleLoginAction(options);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { handleLogoutAction } from "../command-support.js";
|
|
2
|
+
export function registerLogoutFamily(program, handleAsyncAction) {
|
|
3
|
+
program
|
|
4
|
+
.command("logout")
|
|
5
|
+
.description("Remove the active stored CLI API token.")
|
|
6
|
+
.option("--profile <name>", "Remove a named CLI profile instead of the active profile.")
|
|
7
|
+
.option("--all", "Remove all stored CLI profiles.")
|
|
8
|
+
.option("--json", "Print a JSON envelope.")
|
|
9
|
+
.action(async (options) => {
|
|
10
|
+
await handleLogoutAction(options);
|
|
11
|
+
});
|
|
12
|
+
}
|