@oxygen-agent/cli 1.837.5 → 1.837.10
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/index.js +2 -2
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +33 -1
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +16 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -10552,7 +10552,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
10552
10552
|
}));
|
|
10553
10553
|
}))
|
|
10554
10554
|
.addCommand(new Command("set-photo")
|
|
10555
|
-
.description("Set the sender's profile picture
|
|
10555
|
+
.description("Set the reusable sender identity's profile picture for FUTURE managed-inbox orders made with --sender <id>. Pick exactly one source: --file (upload your own image; Oxygen hosts it permanently), --url (a public https image), --from-linkedin (re-mirror the photo from the attached LinkedIn account), or --clear. Hosting matters: an inbox vendor fetches the photo HOURS after an order, so an expiring LinkedIn CDN URL can ship the mailbox faceless. This updates Oxygen's sender profile only: it does not change already-provisioned mailbox photos or call the inbox provider. For a one-off typed-name order using profile_picture_url in --mailboxes JSON, use `oxygen managed-inboxes upload-avatar <path>` instead. Free — no credits.")
|
|
10556
10556
|
.argument("<id>", "Sender profile id (from `oxygen senders profiles list`).")
|
|
10557
10557
|
.option("--file <path>", "Upload a PNG, JPEG, or WebP from disk (max 8MB) and use it. Oxygen hosts the image permanently, so an inbox vendor can still fetch it at provisioning time.")
|
|
10558
10558
|
.option("--url <url>", "Use an image already published at a public https URL. If the link expires (e.g. media.licdn.com with e=<epoch>) the photo is kept for Oxygen's own UI but marked non-durable and NEVER sent to an inbox vendor.")
|
|
@@ -13456,7 +13456,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13456
13456
|
});
|
|
13457
13457
|
}))
|
|
13458
13458
|
.addCommand(new Command("upload-avatar")
|
|
13459
|
-
.description("Host a mailbox profile picture and print the URL to pass as profile_picture_url. The inbox vendor FETCHES
|
|
13459
|
+
.description("Host a one-off mailbox profile picture and print the URL to pass as profile_picture_url in --mailboxes JSON. For a reusable sender identity used by FUTURE --sender <id> orders, prefer `oxygen senders profiles set-photo <id> --file <path>` instead. The inbox vendor FETCHES the hosted URL when it provisions the mailbox, often long after the order, so it must be public and permanent. Uploads a PNG, JPEG, or WebP (max 8MB), then normalizes it to a metadata-free 400x400 PNG at a matching .png URL. This only hosts the image: it does not update a sender, place an order, call the inbox provider, or charge credits.")
|
|
13460
13460
|
.argument("<path>", "Path to a PNG, JPEG, or WebP image.")
|
|
13461
13461
|
.option("--json", "Print a JSON envelope.")
|
|
13462
13462
|
.action(async (path, options) => {
|
|
@@ -420,7 +420,26 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
420
420
|
gatewayCommands: ["mailboxes list", "domains list", "mailboxes health"],
|
|
421
421
|
skills: ["oxygen-email-infra", "oxygen-deliverability", "oxygen-sequencer"],
|
|
422
422
|
endpointSections: ["deliverability", "domains", "egress", "mailboxes", "managed-inboxes"],
|
|
423
|
-
intentTerms: [
|
|
423
|
+
intentTerms: [
|
|
424
|
+
"mailbox",
|
|
425
|
+
"mailboxes",
|
|
426
|
+
"sending domain",
|
|
427
|
+
"dns",
|
|
428
|
+
"warmup",
|
|
429
|
+
"deliverability",
|
|
430
|
+
"placement",
|
|
431
|
+
"egress ip",
|
|
432
|
+
"email infrastructure",
|
|
433
|
+
"managed inbox",
|
|
434
|
+
"avatar",
|
|
435
|
+
"profile picture",
|
|
436
|
+
"profile photo",
|
|
437
|
+
"headshot",
|
|
438
|
+
"hosted picture",
|
|
439
|
+
"hosted image",
|
|
440
|
+
"mailbox picture",
|
|
441
|
+
"mailbox photo",
|
|
442
|
+
],
|
|
424
443
|
},
|
|
425
444
|
{
|
|
426
445
|
id: "connected-linkedin",
|
|
@@ -520,6 +539,9 @@ function normalizeIntent(query) {
|
|
|
520
539
|
return query.toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
521
540
|
}
|
|
522
541
|
function explicitCapabilityIntent(query) {
|
|
542
|
+
if (isInboxAvatarIntent(query)) {
|
|
543
|
+
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
544
|
+
}
|
|
523
545
|
if (isMailboxDeleteIntent(query)) {
|
|
524
546
|
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
525
547
|
}
|
|
@@ -660,6 +682,13 @@ function recommendationsFor(card, query) {
|
|
|
660
682
|
],
|
|
661
683
|
};
|
|
662
684
|
}
|
|
685
|
+
if (card.id === "sending-infrastructure"
|
|
686
|
+
&& isInboxAvatarIntent(query)) {
|
|
687
|
+
return {
|
|
688
|
+
tools: [],
|
|
689
|
+
commands: ["managed-inboxes upload-avatar", "senders profiles set-photo"],
|
|
690
|
+
};
|
|
691
|
+
}
|
|
663
692
|
if (card.primitive === "posts" && isOwnedPostCommentIntent(query)) {
|
|
664
693
|
return {
|
|
665
694
|
tools: ["oxygen_publishing_comments", "oxygen_publishing_analytics"],
|
|
@@ -839,6 +868,9 @@ function recommendationsFor(card, query) {
|
|
|
839
868
|
}
|
|
840
869
|
return { tools: [...card.gatewayTools], commands: [...card.gatewayCommands] };
|
|
841
870
|
}
|
|
871
|
+
function isInboxAvatarIntent(query) {
|
|
872
|
+
return /\b(avatar|profile (?:picture|photo)|headshot|hosted (?:picture|image)|mailbox (?:picture|photo))\b/.test(query);
|
|
873
|
+
}
|
|
842
874
|
function isMutualLinkedInConnectionsIntent(query) {
|
|
843
875
|
const linkedInContext = /\blinkedin\b|\bprofiles?\b/.test(query);
|
|
844
876
|
const mutualContext = /\b(mutual|shared|in common)\b.{0,48}\b(connections?|relations?)\b/.test(query)
|
|
@@ -144,6 +144,22 @@ export declare function putInboxAvatarObject(input: {
|
|
|
144
144
|
storageKey: string;
|
|
145
145
|
contentLength: number;
|
|
146
146
|
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Replace one staged avatar with provider-compatible bytes while preserving its
|
|
149
|
+
* public URL. The upload presign exposes that URL before confirmation, so moving
|
|
150
|
+
* the object to a new key during normalization would break an existing API
|
|
151
|
+
* promise. The org-bound key check is load-bearing: this is a bare S3 overwrite
|
|
152
|
+
* in a bucket shared by every tenant and several non-avatar namespaces.
|
|
153
|
+
*/
|
|
154
|
+
export declare function replaceInboxAvatarObject(input: {
|
|
155
|
+
organizationId: string;
|
|
156
|
+
storageKey: string;
|
|
157
|
+
body: Uint8Array;
|
|
158
|
+
contentType: string;
|
|
159
|
+
}): Promise<{
|
|
160
|
+
storageKey: string;
|
|
161
|
+
contentLength: number;
|
|
162
|
+
}>;
|
|
147
163
|
/**
|
|
148
164
|
* Read an avatar back, bounded. `maxBytes` is a hard stop rather than a hint:
|
|
149
165
|
* this is called from an unauthenticated route, so an object that somehow grew
|
|
@@ -334,6 +334,27 @@ export async function putInboxAvatarObject(input) {
|
|
|
334
334
|
}));
|
|
335
335
|
return { storageKey, contentLength: input.body.byteLength };
|
|
336
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Replace one staged avatar with provider-compatible bytes while preserving its
|
|
339
|
+
* public URL. The upload presign exposes that URL before confirmation, so moving
|
|
340
|
+
* the object to a new key during normalization would break an existing API
|
|
341
|
+
* promise. The org-bound key check is load-bearing: this is a bare S3 overwrite
|
|
342
|
+
* in a bucket shared by every tenant and several non-avatar namespaces.
|
|
343
|
+
*/
|
|
344
|
+
export async function replaceInboxAvatarObject(input) {
|
|
345
|
+
if (!isInboxAvatarObjectKeyForOrganization(input.storageKey, input.organizationId)) {
|
|
346
|
+
throw new OxygenError("invalid_object_key", "Refusing to replace an avatar that belongs to another organization.", { exitCode: 1 });
|
|
347
|
+
}
|
|
348
|
+
const { client, config } = resolveClient();
|
|
349
|
+
await client.send(new PutObjectCommand({
|
|
350
|
+
Bucket: config.bucket,
|
|
351
|
+
Key: input.storageKey,
|
|
352
|
+
Body: input.body,
|
|
353
|
+
ContentLength: input.body.byteLength,
|
|
354
|
+
ContentType: input.contentType,
|
|
355
|
+
}));
|
|
356
|
+
return { storageKey: input.storageKey, contentLength: input.body.byteLength };
|
|
357
|
+
}
|
|
337
358
|
/**
|
|
338
359
|
* Read an avatar back, bounded. `maxBytes` is a hard stop rather than a hint:
|
|
339
360
|
* this is called from an unauthenticated route, so an object that somehow grew
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.837.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.837.10";
|
|
2
2
|
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
|
|
3
3
|
export declare const MANAGED_INBOX_MINIMUM_CLI_VERSION = "1.326.2";
|
|
4
4
|
export declare const SUPPORT_AGENT_REPLY_MINIMUM_CLI_VERSION = "1.747.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.837.
|
|
1
|
+
export const OXYGEN_VERSION = "1.837.10";
|
|
2
2
|
// The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
|
|
3
3
|
// operational route. Raising it hard-rejects every older CLI from the entire
|
|
4
4
|
// product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
|