@oxygen-agent/cli 1.922.14 → 1.936.1

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 (46) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.d.ts +18 -0
  3. package/dist/admin-primary-providers-render.js +371 -0
  4. package/dist/command-manifest.js +6 -0
  5. package/dist/functions-commands.d.ts +6 -0
  6. package/dist/functions-commands.js +56 -0
  7. package/dist/http-client.d.ts +4 -0
  8. package/dist/http-client.js +49 -2
  9. package/dist/index.js +175 -40
  10. package/dist/ugc-commands.d.ts +6 -0
  11. package/dist/ugc-commands.js +748 -0
  12. package/dist/visual-commands.d.ts +6 -0
  13. package/dist/visual-commands.js +57 -0
  14. package/dist/visual-render-wait.d.ts +3 -0
  15. package/dist/visual-render-wait.js +56 -0
  16. package/node_modules/@oxygen/shared/dist/byok-connect.d.ts +43 -0
  17. package/node_modules/@oxygen/shared/dist/byok-connect.js +84 -0
  18. package/node_modules/@oxygen/shared/dist/capability-discovery.js +26 -10
  19. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +2 -0
  20. package/node_modules/@oxygen/shared/dist/feature-gates.js +3 -0
  21. package/node_modules/@oxygen/shared/dist/index.d.ts +4 -0
  22. package/node_modules/@oxygen/shared/dist/index.js +4 -0
  23. package/node_modules/@oxygen/shared/dist/knowledge-constants.d.ts +1 -1
  24. package/node_modules/@oxygen/shared/dist/knowledge-constants.js +3 -2
  25. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +1 -1
  26. package/node_modules/@oxygen/shared/dist/langfuse.js +17 -0
  27. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +6 -0
  28. package/node_modules/@oxygen/shared/dist/object-storage.js +5 -0
  29. package/node_modules/@oxygen/shared/dist/provider-balance-signal.d.ts +113 -0
  30. package/node_modules/@oxygen/shared/dist/provider-balance-signal.js +158 -0
  31. package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +12 -0
  32. package/node_modules/@oxygen/shared/dist/sequence-failures.js +24 -0
  33. package/node_modules/@oxygen/shared/dist/sequences.d.ts +16 -0
  34. package/node_modules/@oxygen/shared/dist/sequences.js +54 -0
  35. package/node_modules/@oxygen/shared/dist/ugc.d.ts +113 -0
  36. package/node_modules/@oxygen/shared/dist/ugc.js +2 -0
  37. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.d.ts +9 -0
  38. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.js +33 -0
  39. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  40. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  41. package/node_modules/@oxygen/shared/dist/visual-render.d.ts +30 -0
  42. package/node_modules/@oxygen/shared/dist/visual-render.js +55 -0
  43. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +43 -0
  44. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +126 -0
  45. package/node_modules/@oxygen/shared/package.json +10 -0
  46. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { createHash } from "node:crypto";
3
+ import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
4
+ import { OxygenError } from "./cli-result.js";
5
+ import { resolveObjectStorageClient } from "./object-storage.js";
6
+ export const WORKSPACE_VISUAL_SOURCE_MAX_BYTES = 1_500_000;
7
+ export const WORKSPACE_VISUAL_BINARY_MAX_BYTES = 50 * 1024 * 1024;
8
+ /** Platform abuse guard shared by every plan, not a storage entitlement. */
9
+ export const WORKSPACE_RETAINED_FILES_MAX_BYTES = 1024 * 1024 * 1024;
10
+ const STORAGE_TIMEOUT_MS = 60_000;
11
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12
+ const ORGANIZATION = /^[A-Za-z0-9_-]{1,128}$/;
13
+ const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
14
+ export function assertWorkspaceFileId(id) {
15
+ if (!UUID.test(id))
16
+ throw new OxygenError("invalid_request", "A valid workspace file ID is required.");
17
+ }
18
+ export function normalizeWorkspaceVisualFileName(name) {
19
+ const result = name.trim();
20
+ if (!result || Buffer.byteLength(result) > 255 || /[\x00-\x1f\x7f/\\]/.test(result) || result === "." || result === "..") {
21
+ throw new OxygenError("invalid_request", "File name must be a filename of at most 255 bytes, without paths or control characters.");
22
+ }
23
+ return result;
24
+ }
25
+ export function buildWorkspaceFileObjectKey(input) {
26
+ assertWorkspaceFileId(input.fileId);
27
+ if (!ORGANIZATION.test(input.organizationId))
28
+ throw new OxygenError("invalid_request", "Invalid workspace identity.");
29
+ const name = normalizeWorkspaceVisualFileName(input.fileName);
30
+ const safeName = name.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^[^A-Za-z0-9]+/, "").slice(0, 200) || "file";
31
+ return `workspace-files/${input.organizationId}/${input.fileId}/${safeName}`;
32
+ }
33
+ export function isWorkspaceFileObjectKeyForOrganization(key, organizationId, fileId) {
34
+ const parts = key.split("/");
35
+ return ORGANIZATION.test(organizationId) && parts.length === 4
36
+ && parts[0] === "workspace-files" && parts[1] === organizationId
37
+ && UUID.test(parts[2] ?? "") && SAFE_NAME.test(parts[3] ?? "")
38
+ && (fileId === undefined || parts[2] === fileId);
39
+ }
40
+ export function assertWorkspaceFileObjectKey(input) {
41
+ if (!isWorkspaceFileObjectKeyForOrganization(input.storageKey, input.organizationId, input.fileId)) {
42
+ throw new OxygenError("forbidden_storage_key", "File storage does not belong to this workspace file.");
43
+ }
44
+ }
45
+ /** Framing check only. Full media validation belongs in the isolated renderer validator. */
46
+ export function assertWorkspaceVisualBinary(bytes, mimeType) {
47
+ if (bytes.byteLength < 1 || bytes.byteLength > WORKSPACE_VISUAL_BINARY_MAX_BYTES) {
48
+ throw new OxygenError("workspace_file_too_large", "Visual output must contain between 1 byte and 50 MiB.");
49
+ }
50
+ const png = [137, 80, 78, 71, 13, 10, 26, 10];
51
+ const valid = mimeType === "image/png"
52
+ ? png.every((byte, index) => bytes[index] === byte)
53
+ : mimeType === "application/pdf" && Buffer.from(bytes.subarray(0, 5)).toString("ascii") === "%PDF-";
54
+ if (!valid)
55
+ throw new OxygenError("workspace_file_invalid_type", "Visual output bytes do not match the declared PNG or PDF format.");
56
+ }
57
+ export function workspaceFileSha256(bytes) {
58
+ return createHash("sha256").update(bytes).digest("hex");
59
+ }
60
+ export async function readWorkspaceFileObject(input) {
61
+ input.signal?.throwIfAborted();
62
+ assertWorkspaceFileObjectKey(input);
63
+ if (!Number.isSafeInteger(input.sizeBytes) || input.sizeBytes < 1 || input.sizeBytes > WORKSPACE_VISUAL_BINARY_MAX_BYTES || !/^[a-f0-9]{64}$/.test(input.sha256)) {
64
+ throw new OxygenError("workspace_file_integrity_failed", "File metadata failed integrity validation.");
65
+ }
66
+ const { client, bucket } = resolveObjectStorageClient();
67
+ const signal = input.signal ? AbortSignal.any([input.signal, AbortSignal.timeout(STORAGE_TIMEOUT_MS)]) : AbortSignal.timeout(STORAGE_TIMEOUT_MS);
68
+ const result = await client.send(new GetObjectCommand({
69
+ Bucket: bucket, Key: input.storageKey, Range: `bytes=0-${input.sizeBytes}`,
70
+ }), { abortSignal: signal });
71
+ const body = result.Body;
72
+ if (!body)
73
+ throw new OxygenError("workspace_file_storage_missing", "Stored file bytes are unavailable.");
74
+ const stream = body;
75
+ const abort = () => stream.destroy?.(new Error("Workspace file read timed out."));
76
+ signal.addEventListener("abort", abort, { once: true });
77
+ try {
78
+ if (typeof stream[Symbol.asyncIterator] !== "function")
79
+ throw new OxygenError("workspace_file_storage_invalid", "Stored file transport is invalid.");
80
+ const chunks = [];
81
+ let size = 0;
82
+ for await (const chunk of stream) {
83
+ signal.throwIfAborted();
84
+ if (!(chunk instanceof Uint8Array))
85
+ throw new OxygenError("workspace_file_storage_invalid", "Stored file transport is invalid.");
86
+ size += chunk.byteLength;
87
+ if (size > input.sizeBytes)
88
+ throw new OxygenError("workspace_file_integrity_failed", "Stored file exceeds its recorded size.");
89
+ chunks.push(Buffer.from(chunk));
90
+ }
91
+ const bytes = Buffer.concat(chunks, size);
92
+ signal.throwIfAborted();
93
+ if (size !== input.sizeBytes || workspaceFileSha256(bytes) !== input.sha256 || result.ContentType?.split(";", 1)[0]?.trim().toLowerCase() !== input.mimeType) {
94
+ throw new OxygenError("workspace_file_integrity_failed", "Stored file failed size, format or digest verification.");
95
+ }
96
+ assertWorkspaceVisualBinary(bytes, input.mimeType);
97
+ return bytes;
98
+ }
99
+ finally {
100
+ signal.removeEventListener("abort", abort);
101
+ stream.destroy?.();
102
+ }
103
+ }
104
+ /** Immutable PUT followed by bounded readback. A matching existing object is a safe retry. */
105
+ export async function putWorkspaceFileObject(input) {
106
+ input.signal?.throwIfAborted();
107
+ assertWorkspaceVisualBinary(input.bytes, input.mimeType);
108
+ const storageKey = buildWorkspaceFileObjectKey(input);
109
+ const bytes = Buffer.from(input.bytes);
110
+ const sha256 = workspaceFileSha256(bytes);
111
+ const { client, bucket } = resolveObjectStorageClient();
112
+ try {
113
+ await client.send(new PutObjectCommand({
114
+ Bucket: bucket, Key: storageKey, Body: bytes, ContentLength: bytes.byteLength,
115
+ ContentType: input.mimeType, ContentDisposition: "attachment", IfNoneMatch: "*",
116
+ }), { abortSignal: input.signal ? AbortSignal.any([input.signal, AbortSignal.timeout(STORAGE_TIMEOUT_MS)]) : AbortSignal.timeout(STORAGE_TIMEOUT_MS) });
117
+ }
118
+ catch (error) {
119
+ // Never overwrite a prior attempt, even after upload-success/DB-failure.
120
+ const status = error?.$metadata?.httpStatusCode;
121
+ if (status !== 412)
122
+ throw error;
123
+ }
124
+ await readWorkspaceFileObject({ ...input, storageKey, sizeBytes: bytes.byteLength, sha256 });
125
+ return { storageKey, sizeBytes: bytes.byteLength, sha256 };
126
+ }
@@ -6,6 +6,11 @@
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
+ "./vercel-sandbox-fetch": {
10
+ "types": "./dist/vercel-sandbox-fetch.d.ts",
11
+ "import": "./dist/vercel-sandbox-fetch.js",
12
+ "default": "./dist/vercel-sandbox-fetch.js"
13
+ },
9
14
  ".": {
10
15
  "types": "./dist/index.d.ts",
11
16
  "import": "./dist/index.js",
@@ -190,6 +195,11 @@
190
195
  "types": "./dist/publishing-limits.d.ts",
191
196
  "import": "./dist/publishing-limits.js",
192
197
  "default": "./dist/publishing-limits.js"
198
+ },
199
+ "./workspace-file-storage": {
200
+ "types": "./dist/workspace-file-storage.d.ts",
201
+ "import": "./dist/workspace-file-storage.js",
202
+ "default": "./dist/workspace-file-storage.js"
193
203
  }
194
204
  },
195
205
  "dependencies": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.922.14",
3
+ "version": "1.936.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",