@oxygen-agent/cli 1.948.1 → 1.987.20

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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.js +9 -1
  3. package/dist/cli-values.d.ts +14 -0
  4. package/dist/cli-values.js +26 -0
  5. package/dist/command-manifest.js +6 -0
  6. package/dist/functions-commands.js +33 -9
  7. package/dist/help.d.ts +21 -0
  8. package/dist/help.js +94 -0
  9. package/dist/index.js +1493 -297
  10. package/dist/knowledge-repository-commands.d.ts +6 -0
  11. package/dist/knowledge-repository-commands.js +198 -0
  12. package/dist/skills.js +20 -0
  13. package/dist/ugc-commands.d.ts +3 -6
  14. package/dist/ugc-commands.js +2 -1086
  15. package/node_modules/@oxygen/cli-ugc/dist/commands.d.ts +3 -0
  16. package/node_modules/@oxygen/cli-ugc/dist/commands.js +1178 -0
  17. package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +7 -0
  18. package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +25 -0
  19. package/node_modules/@oxygen/cli-ugc/dist/index.d.ts +14 -0
  20. package/node_modules/@oxygen/cli-ugc/dist/index.js +5 -0
  21. package/node_modules/@oxygen/cli-ugc/package.json +15 -0
  22. package/node_modules/@oxygen/formula/dist/expression.js +14 -1
  23. package/node_modules/@oxygen/formula/dist/formula-functions.js +71 -1
  24. package/node_modules/@oxygen/formula/dist/index.d.ts +1 -0
  25. package/node_modules/@oxygen/formula/dist/index.js +1 -0
  26. package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +69 -0
  27. package/node_modules/@oxygen/formula/dist/value-cleaners.js +374 -0
  28. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +2 -0
  29. package/node_modules/@oxygen/shared/dist/billing.d.ts +27 -27
  30. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +8 -0
  31. package/node_modules/@oxygen/shared/dist/capability-discovery.js +127 -18
  32. package/node_modules/@oxygen/shared/dist/column-output-fields.js +12 -4
  33. package/node_modules/@oxygen/shared/dist/copilot-errors.js +3 -0
  34. package/node_modules/@oxygen/shared/dist/copilot-journeys.d.ts +19 -1
  35. package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.d.ts +19 -0
  36. package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.js +26 -0
  37. package/node_modules/@oxygen/shared/dist/copilot-journeys.js +8 -41
  38. package/node_modules/@oxygen/shared/dist/copilot-playbooks.d.ts +18 -0
  39. package/node_modules/@oxygen/shared/dist/copilot-playbooks.js +43 -0
  40. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +15 -0
  41. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +31 -0
  42. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +41 -0
  43. package/node_modules/@oxygen/shared/dist/copilot-skills.js +6 -0
  44. package/node_modules/@oxygen/shared/dist/inbox-avatar-url.d.ts +28 -0
  45. package/node_modules/@oxygen/shared/dist/inbox-avatar-url.js +57 -0
  46. package/node_modules/@oxygen/shared/dist/index.d.ts +5 -0
  47. package/node_modules/@oxygen/shared/dist/index.js +5 -0
  48. package/node_modules/@oxygen/shared/dist/knowledge-bases.d.ts +74 -0
  49. package/node_modules/@oxygen/shared/dist/knowledge-bases.js +456 -0
  50. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +32 -38
  51. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +38 -41
  52. package/node_modules/@oxygen/shared/dist/knowledge-repository.d.ts +22 -0
  53. package/node_modules/@oxygen/shared/dist/knowledge-repository.js +121 -0
  54. package/node_modules/@oxygen/shared/dist/knowledge-vault-markdown.d.ts +20 -0
  55. package/node_modules/@oxygen/shared/dist/knowledge-vault-markdown.js +155 -0
  56. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +12 -1
  57. package/node_modules/@oxygen/shared/dist/langfuse.js +48 -8
  58. package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +10 -0
  59. package/node_modules/@oxygen/shared/dist/mailbox-import.js +53 -0
  60. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +8 -0
  61. package/node_modules/@oxygen/shared/dist/plan-limits.js +8 -0
  62. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
  63. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
  64. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +24 -0
  65. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +24 -0
  66. package/node_modules/@oxygen/shared/dist/recipes.d.ts +6 -0
  67. package/node_modules/@oxygen/shared/dist/recipes.js +23 -0
  68. package/node_modules/@oxygen/shared/dist/research-output-contract.d.ts +33 -1
  69. package/node_modules/@oxygen/shared/dist/research-output-contract.js +64 -2
  70. package/node_modules/@oxygen/shared/dist/sequence-crm-events.d.ts +1 -1
  71. package/node_modules/@oxygen/shared/dist/sequence-hubspot-sync.d.ts +1 -1
  72. package/node_modules/@oxygen/shared/dist/sequences.d.ts +152 -2
  73. package/node_modules/@oxygen/shared/dist/sequences.js +304 -4
  74. package/node_modules/@oxygen/shared/dist/ugc-amplification-identity.d.ts +2 -0
  75. package/node_modules/@oxygen/shared/dist/ugc-amplification-identity.js +24 -0
  76. package/node_modules/@oxygen/shared/dist/ugc.d.ts +8 -0
  77. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +8 -1
  78. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  79. package/node_modules/@oxygen/shared/dist/version.js +3 -1
  80. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +6 -2
  81. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +15 -4
  82. package/node_modules/@oxygen/shared/package.json +25 -0
  83. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +15 -15
  84. package/package.json +6 -2
@@ -0,0 +1,6 @@
1
+ import { Command } from "commander";
2
+ type Handle = (command: string, options: {
3
+ json?: boolean;
4
+ }, action: () => Promise<unknown>) => Promise<void>;
5
+ export declare function registerKnowledgeRepositoryCommands(program: Command, handle: Handle): void;
6
+ export {};
@@ -0,0 +1,198 @@
1
+ import { readFile, writeFile, stat } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
3
+ import { resolve } from "node:path";
4
+ import { requestOxygen } from "./http-client.js";
5
+ const post = (path, body) => {
6
+ if (Buffer.byteLength(JSON.stringify(body)) > 2_000_000)
7
+ throw new Error("This inline request exceeds 2 MB. Add larger attachments through the connected GitHub repository.");
8
+ return requestOxygen(`/api/cli/knowledge/${path}`, { method: "POST", body });
9
+ };
10
+ export function registerKnowledgeRepositoryCommands(program, handle) {
11
+ const knowledge = program.commands.find((c) => c.name() === "knowledge");
12
+ if (!knowledge)
13
+ throw new Error("Knowledge commands must be registered first.");
14
+ const repos = knowledge
15
+ .command("repositories")
16
+ .description("Import an existing GitHub or Obsidian repository into Knowledge, or keep it automatically in sync. Start with integrations connect github; web setup: /knowledge/repositories; docs: /docs/knowledge/knowledge-graph.");
17
+ repos
18
+ .command("list")
19
+ .description("Read connected repositories, import progress, sync freshness, pending changes, and errors. 0 credits.")
20
+ .option("--json", "Print a JSON envelope.")
21
+ .action((o) => handle("knowledge repositories list", o, () => post("repositories/list", {})));
22
+ for (const operation of ["preview", "connect"]) {
23
+ const cmd = repos
24
+ .command(operation)
25
+ .description(operation === "preview"
26
+ ? "Preview branch/folder contents, exclusions and the future sync policy. Does not activate sync. 0 credits."
27
+ : "Approve the exact preview and start importing. two_way authorizes future automatic GitHub writes to the selected branch. 0 Oxygen credits.")
28
+ .requiredOption("--connection-id <id>", "GitHub connection ID from integrations list.")
29
+ .requiredOption("--repository-id <id>", "Repository ID from integrations github repositories.")
30
+ .requiredOption("--branch <name>", "Connected branch; unmerged feature branches are separate.")
31
+ .option("--root-path <path>", "Repository subfolder; omit for the entire repository.", "")
32
+ .option("--mode <mode>", "two_way or import (one-time copy).", "two_way")
33
+ .option("--json", "Print a JSON envelope.");
34
+ if (operation === "connect")
35
+ cmd
36
+ .requiredOption("--preview-token <token>", "Exact token returned by repositories preview.")
37
+ .option("--approved", "Approve the displayed import and future sync policy.");
38
+ cmd.action((o) => handle(`knowledge repositories ${operation}`, o, () => post(`repositories/${operation}`, {
39
+ connectionId: o.connectionId,
40
+ repositoryId: Number(o.repositoryId),
41
+ branch: o.branch,
42
+ rootPath: o.rootPath,
43
+ mode: o.mode,
44
+ ...(operation === "connect"
45
+ ? { previewToken: o.previewToken, approved: o.approved === true }
46
+ : {}),
47
+ })));
48
+ }
49
+ for (const action of ["sync", "pause", "resume", "disconnect"]) {
50
+ const description = {
51
+ sync: "Request an immediate sync attempt. Saved changes remain safe if GitHub is unavailable.",
52
+ pause: "Pause automatic sync; keep all imported files and history.",
53
+ resume: "Resume the previously approved sync policy.",
54
+ disconnect: "Disconnect this repository; keep its files and history in Knowledge.",
55
+ }[action];
56
+ repos
57
+ .command(action)
58
+ .description(description)
59
+ .requiredOption("--id <id>", "Connected repository mount ID.")
60
+ .option("--json", "Print a JSON envelope.")
61
+ .action((o) => handle(`knowledge repositories ${action}`, o, () => post("repositories/action", { id: o.id, action })));
62
+ }
63
+ repos
64
+ .command("conflicts")
65
+ .description("Read conflicts; add --path to inspect preserved base, Oxygen, and GitHub versions.")
66
+ .requiredOption("--mount-id <id>", "Connected repository mount ID.")
67
+ .option("--path <path>", "Exact file path.")
68
+ .option("--json", "Print a JSON envelope.")
69
+ .action((o) => handle("knowledge repositories conflicts", o, () => post("repositories/conflicts", {
70
+ mountId: o.mountId,
71
+ ...(o.path ? { path: o.path } : {}),
72
+ })));
73
+ repos
74
+ .command("resolve")
75
+ .description("Apply a reviewed conflict decision. Canonical copy remains proposal-gated; GitHub publishes under the saved sync policy.")
76
+ .requiredOption("--mount-id <id>", "Connected repository mount ID.")
77
+ .requiredOption("--path <path>", "Exact file path.")
78
+ .requiredOption("--local-revision <revision|null>", "Reviewed Oxygen revision or null.")
79
+ .requiredOption("--remote-sha <sha|null>", "Reviewed GitHub blob SHA or null.")
80
+ .requiredOption("--decision <choice>", "oxygen, github, or merged.")
81
+ .option("--file <path>", "Complete edited UTF-8 file for merged.")
82
+ .option("--approved", "Approve this exact decision.")
83
+ .option("--json", "Print a JSON envelope.")
84
+ .action((o) => handle("knowledge repositories resolve", o, async () => post("repositories/resolve", {
85
+ mountId: o.mountId,
86
+ path: o.path,
87
+ expectedLocalRevision: o.localRevision === "null" ? null : Number(o.localRevision),
88
+ expectedRemoteSha: o.remoteSha === "null" ? null : o.remoteSha,
89
+ decision: o.decision,
90
+ ...(o.file ? { text: await readFile(String(o.file), "utf8") } : {}),
91
+ approved: o.approved === true,
92
+ })));
93
+ const files = knowledge
94
+ .command("files")
95
+ .description("Original repository paths and immutable file revisions, including attachments.");
96
+ files
97
+ .command("list")
98
+ .requiredOption("--mount-id <id>", "Connected repository ID.")
99
+ .option("--cursor <cursor>", "Continue the previous page.")
100
+ .option("--limit <number>", "Maximum files, up to 500.", "100")
101
+ .option("--json", "Print a JSON envelope.")
102
+ .action((o) => handle("knowledge files list", o, () => post("files/list", {
103
+ mountId: o.mountId,
104
+ limit: Number(o.limit),
105
+ ...(o.cursor ? { cursor: o.cursor } : {}),
106
+ })));
107
+ files
108
+ .command("get")
109
+ .description("Read original file content, properties and download link at an optional immutable revision.")
110
+ .requiredOption("--id <id>", "File ID from files list.")
111
+ .option("--revision <number>", "Specific saved file revision.")
112
+ .option("--json", "Print a JSON envelope.")
113
+ .action((o) => handle("knowledge files get", o, () => post("files/get", {
114
+ id: o.id,
115
+ ...(o.revision ? { revision: Number(o.revision) } : {}),
116
+ })));
117
+ files
118
+ .command("upsert")
119
+ .description("Create or replace an original file, or update frontmatter properties. Canonical copy still requires a proposal.")
120
+ .requiredOption("--mount-id <id>", "Connected repository ID.")
121
+ .option("--id <id>", "Existing file ID.")
122
+ .option("--path <path>", "Exact repository-relative path; required for new files.")
123
+ .requiredOption("--expected-revision <number>", "Reviewed file revision; 0 creates a new file.")
124
+ .option("--file <path>", "Complete local file, including any frontmatter or binary bytes.")
125
+ .option("--properties-json <json>", "Frontmatter properties to change; mutually exclusive with --file.")
126
+ .option("--json", "Print a JSON envelope.")
127
+ .action((o) => handle("knowledge files upsert", o, async () => {
128
+ if (Boolean(o.file) === Boolean(o.propertiesJson))
129
+ throw new Error("Provide exactly one of --file or --properties-json.");
130
+ if (o.file && (await stat(String(o.file))).size > 1_500_000)
131
+ throw new Error("Inline file uploads must fit a 2 MB JSON request (including base64). Add larger attachments through the connected GitHub repository.");
132
+ const patch = o.propertiesJson
133
+ ? JSON.parse(String(o.propertiesJson))
134
+ : undefined;
135
+ if (patch !== undefined &&
136
+ (!patch || typeof patch !== "object" || Array.isArray(patch)))
137
+ throw new Error("--properties-json must contain an object.");
138
+ return post("files/upsert", {
139
+ mountId: o.mountId,
140
+ ...(o.id ? { id: o.id } : {}),
141
+ ...(o.path ? { path: o.path } : {}),
142
+ expectedFileRevision: Number(o.expectedRevision),
143
+ ...(o.file
144
+ ? {
145
+ bytesBase64: (await readFile(String(o.file))).toString("base64"),
146
+ }
147
+ : { propertyPatch: patch }),
148
+ });
149
+ }));
150
+ files
151
+ .command("archive")
152
+ .description("Archive a file with revision checking. A connected two-way repository publishes the deletion; history remains.")
153
+ .requiredOption("--id <id>", "File ID.")
154
+ .requiredOption("--expected-revision <number>", "Reviewed file revision.")
155
+ .option("--json", "Print a JSON envelope.")
156
+ .action((o) => handle("knowledge files archive", o, () => post("files/archive", {
157
+ id: o.id,
158
+ expectedFileRevision: Number(o.expectedRevision),
159
+ })));
160
+ files
161
+ .command("download")
162
+ .description("Download exact original bytes with digest verification; refuses to overwrite an existing local file.")
163
+ .requiredOption("--id <id>", "File ID.")
164
+ .requiredOption("--out <path>", "New local destination filename.")
165
+ .option("--revision <number>", "Specific immutable revision.")
166
+ .option("--json", "Print a JSON envelope.")
167
+ .action((o) => handle("knowledge files download", o, async () => {
168
+ const query = new URLSearchParams({
169
+ id: String(o.id),
170
+ ...(o.revision ? { revision: String(o.revision) } : {}),
171
+ });
172
+ const data = await requestOxygen(`/api/cli/knowledge/files/get?${query}`);
173
+ const bytes = await requestOxygen(`/api/cli/knowledge/files/download?${query}`, { binaryMaxBytes: Math.max(1, data.file.sizeBytes) });
174
+ if (bytes.length !== data.file.sizeBytes ||
175
+ createHash("sha256").update(bytes).digest("hex") !== data.file.sha256)
176
+ throw new Error("Downloaded bytes do not match the saved file revision.");
177
+ await writeFile(String(o.out), bytes, { flag: "wx", mode: 0o600 });
178
+ return { path: resolve(String(o.out)), ...data.file };
179
+ }));
180
+ const integrations = program.commands.find((c) => c.name() === "integrations");
181
+ if (integrations) {
182
+ const github = integrations
183
+ .command("github")
184
+ .description("Choose repositories authorized through the GitHub App. Connect first with integrations connect github. The Oxygen GitHub App requests Contents read and write plus Metadata read on the repositories you select, and push events; it requests no account or organization permissions. integrations list reports GitHub availability for this deployment.");
185
+ github
186
+ .command("repositories")
187
+ .requiredOption("--connection-id <id>", "GitHub connection ID.")
188
+ .option("--json", "Print a JSON envelope.")
189
+ .action((o) => handle("integrations github repositories", o, () => requestOxygen(`/api/cli/integrations/github/repositories?connection_id=${encodeURIComponent(String(o.connectionId))}`)));
190
+ github
191
+ .command("branches")
192
+ .requiredOption("--connection-id <id>", "GitHub connection ID.")
193
+ .requiredOption("--repository-id <id>", "Immutable GitHub repository ID.")
194
+ .option("--page <number>", "Page to read.", "1")
195
+ .option("--json", "Print a JSON envelope.")
196
+ .action((o) => handle("integrations github branches", o, () => requestOxygen(`/api/cli/integrations/github/branches?${new URLSearchParams({ connection_id: String(o.connectionId), repository_id: String(o.repositoryId), page: String(o.page) })}`)));
197
+ }
198
+ }
package/dist/skills.js CHANGED
@@ -66,6 +66,17 @@ export async function resolveSkillsInstallSource(options) {
66
66
  return publicSource(`access_unavailable:${code}`);
67
67
  }
68
68
  }
69
+ function requireSkillContentAccess(index, source) {
70
+ if (!index.authentication_required)
71
+ return;
72
+ throw new OxygenError("skills_authentication_required", "Sign in to download Oxygen agent instructions.", {
73
+ details: {
74
+ next_step: "Run `oxygen login`, then retry `oxygen skills install` or `oxygen skills get`.",
75
+ ...(source.fallback_reason ? { access_fallback: source.fallback_reason } : {}),
76
+ },
77
+ exitCode: 1,
78
+ });
79
+ }
69
80
  export async function listAgentSkills(options, runtime = {}) {
70
81
  const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
71
82
  const source = await resolveSkillsInstallSource({ apiUrl, ...runtime });
@@ -137,6 +148,7 @@ export async function getAgentSkill(skillName, options, runtime = {}) {
137
148
  exitCode: 1,
138
149
  });
139
150
  }
151
+ requireSkillContentAccess(index, source);
140
152
  const normalized = skillName.trim();
141
153
  const skill = index.skills.find((entry) => entry.name === normalized);
142
154
  if (!skill) {
@@ -203,6 +215,12 @@ options, runtime = {}) {
203
215
  ...(runtime.credentials !== undefined ? { credentials: runtime.credentials } : {}),
204
216
  ...(runtime.fetch ? { fetch: runtime.fetch } : {}),
205
217
  });
218
+ if (!source.authenticated) {
219
+ // Public discovery remains usable. New servers explicitly mark that their
220
+ // instruction bodies require a signed workspace link; never invoke the
221
+ // installer against that discovery-only catalog.
222
+ requireSkillContentAccess(await inspectSkillIndex(source.index_url, runtime.fetch), source);
223
+ }
206
224
  const agents = readWords(options.agents ?? DEFAULT_SKILL_AGENTS);
207
225
  const skill = readOption(options.skill) ?? "*";
208
226
  const env = runtime.env ?? process.env;
@@ -599,6 +617,7 @@ async function inspectSkillIndex(indexUrl, fetchImpl = fetch) {
599
617
  }
600
618
  return {
601
619
  reachable: true,
620
+ authentication_required: index.authentication_required === true,
602
621
  status: response.status,
603
622
  version: index.version,
604
623
  base_url: index.base_url,
@@ -667,6 +686,7 @@ function parseSkillIndex(value) {
667
686
  })
668
687
  .filter((entry) => entry !== null);
669
688
  return {
689
+ authentication_required: value.authentication_required === true,
670
690
  version: typeof value.version === "string" ? value.version : null,
671
691
  base_url: typeof value.base_url === "string" ? value.base_url : null,
672
692
  skills,
@@ -1,6 +1,3 @@
1
- import { Command } from "commander";
2
- type Handle = (command: string, options: {
3
- json?: boolean;
4
- }, action: () => Promise<unknown>) => Promise<void>;
5
- export declare function registerUgcCommands(program: Command, handle: Handle): void;
6
- export {};
1
+ import type { Command } from "commander";
2
+ import { type UgcCommandDependencies } from "@oxygen/cli-ugc";
3
+ export declare function registerUgcCommands(program: Command, handle: UgcCommandDependencies["handle"]): void;