@oxygen-agent/cli 1.353.2 → 1.365.3
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/command-manifest.js +12 -1
- package/dist/help.js +6 -5
- package/dist/index.js +321 -145
- package/node_modules/@oxygen/shared/dist/billing.d.ts +32 -10
- package/node_modules/@oxygen/shared/dist/billing.js +79 -11
- package/node_modules/@oxygen/shared/dist/copilot-journeys.d.ts +12 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.js +36 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +89 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +182 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/index.js +3 -0
- package/node_modules/@oxygen/shared/dist/linkedin-mentions.d.ts +37 -0
- package/node_modules/@oxygen/shared/dist/linkedin-mentions.js +157 -0
- package/node_modules/@oxygen/shared/dist/tags.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/tags.js +1 -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
|
@@ -8,16 +8,19 @@ export * from "./cli-envelope.js";
|
|
|
8
8
|
export * from "./cli-login-code.js";
|
|
9
9
|
export * from "./cli-result.js";
|
|
10
10
|
export * from "./column-types.js";
|
|
11
|
+
export * from "./copilot-journeys.js";
|
|
11
12
|
export * from "./credit-guidance.js";
|
|
12
13
|
export * from "./directory.js";
|
|
13
14
|
export * from "./email-tracking-token.js";
|
|
14
15
|
export * from "./email-unsubscribe-token.js";
|
|
15
16
|
export * from "./error-redaction.js";
|
|
17
|
+
export * from "./hosted-ai.js";
|
|
16
18
|
export * from "./identifiers.js";
|
|
17
19
|
export * from "./knowledge-constants.js";
|
|
18
20
|
export * from "./knowledge-links.js";
|
|
19
21
|
export * from "./knowledge-markdown.js";
|
|
20
22
|
export * from "./knowledge-seed-content.js";
|
|
23
|
+
export * from "./linkedin-mentions.js";
|
|
21
24
|
export * from "./linkedin-post-url.js";
|
|
22
25
|
export * from "./linkedin-url.js";
|
|
23
26
|
export * from "./linkedin-sequences.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile LinkedIn post text + stored mention state into Unipile's post
|
|
3
|
+
* contract: the outgoing text carries `{{i}}` placeholders and `mentions[i]`
|
|
4
|
+
* is `{ name, profile_id }`. Anything else (raw `@handle` text next to a
|
|
5
|
+
* mentions array, Oxygen mention-preview snapshots, unresolved handles) is
|
|
6
|
+
* NOT a tagged mention on LinkedIn — the provider either posts it literally
|
|
7
|
+
* or, worse, name-matches entries into the wrong span of text.
|
|
8
|
+
*
|
|
9
|
+
* Accepted inputs per entry (both arrays are optional):
|
|
10
|
+
* - Unipile-shaped: `{ name, profile_id }` (already the wire contract).
|
|
11
|
+
* - Oxygen preview-shaped (publishing mentions resolve output): uses
|
|
12
|
+
* `displayName`/`providerId` (or snake_case), and is only eligible when
|
|
13
|
+
* `status === "resolved"` and `semantics` is `"mention"` (links and plain
|
|
14
|
+
* references stay as typed).
|
|
15
|
+
*
|
|
16
|
+
* Placement: an entry becomes a tagged mention only when its span can be
|
|
17
|
+
* anchored in the text — an existing `{{i}}` placeholder, the entry's `raw`
|
|
18
|
+
* occurrence, or `@name`/`@identifier`. Entries that cannot be anchored are
|
|
19
|
+
* dropped from the outgoing array so the provider can never guess a span.
|
|
20
|
+
*/
|
|
21
|
+
export type UnipileLinkedInMention = {
|
|
22
|
+
name: string;
|
|
23
|
+
profile_id: string;
|
|
24
|
+
};
|
|
25
|
+
export type CompiledLinkedInMentions = {
|
|
26
|
+
text: string;
|
|
27
|
+
mentions: UnipileLinkedInMention[];
|
|
28
|
+
dropped: Array<{
|
|
29
|
+
name: string | null;
|
|
30
|
+
reason: "unresolved" | "not_in_text" | "invalid";
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
export declare function compileLinkedInMentions(input: {
|
|
34
|
+
text: string;
|
|
35
|
+
mentions?: unknown;
|
|
36
|
+
mentionPreviews?: unknown;
|
|
37
|
+
}): CompiledLinkedInMentions;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile LinkedIn post text + stored mention state into Unipile's post
|
|
3
|
+
* contract: the outgoing text carries `{{i}}` placeholders and `mentions[i]`
|
|
4
|
+
* is `{ name, profile_id }`. Anything else (raw `@handle` text next to a
|
|
5
|
+
* mentions array, Oxygen mention-preview snapshots, unresolved handles) is
|
|
6
|
+
* NOT a tagged mention on LinkedIn — the provider either posts it literally
|
|
7
|
+
* or, worse, name-matches entries into the wrong span of text.
|
|
8
|
+
*
|
|
9
|
+
* Accepted inputs per entry (both arrays are optional):
|
|
10
|
+
* - Unipile-shaped: `{ name, profile_id }` (already the wire contract).
|
|
11
|
+
* - Oxygen preview-shaped (publishing mentions resolve output): uses
|
|
12
|
+
* `displayName`/`providerId` (or snake_case), and is only eligible when
|
|
13
|
+
* `status === "resolved"` and `semantics` is `"mention"` (links and plain
|
|
14
|
+
* references stay as typed).
|
|
15
|
+
*
|
|
16
|
+
* Placement: an entry becomes a tagged mention only when its span can be
|
|
17
|
+
* anchored in the text — an existing `{{i}}` placeholder, the entry's `raw`
|
|
18
|
+
* occurrence, or `@name`/`@identifier`. Entries that cannot be anchored are
|
|
19
|
+
* dropped from the outgoing array so the provider can never guess a span.
|
|
20
|
+
*/
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function readString(record, ...keys) {
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const value = record[key];
|
|
27
|
+
if (typeof value === "string" && value.trim())
|
|
28
|
+
return value.trim();
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const PLACEHOLDER_RE = /\{\{(\d+)\}\}/g;
|
|
33
|
+
function normalizeEntry(value) {
|
|
34
|
+
if (!isRecord(value))
|
|
35
|
+
return "invalid";
|
|
36
|
+
const status = readString(value, "status");
|
|
37
|
+
const semantics = readString(value, "semantics");
|
|
38
|
+
// Preview-shaped entries: only resolved, mention-semantics entries qualify.
|
|
39
|
+
// Links (full LinkedIn URLs) and plain references stay as typed text.
|
|
40
|
+
if (semantics === "link" || semantics === "plain")
|
|
41
|
+
return "skip";
|
|
42
|
+
if (status && status !== "resolved")
|
|
43
|
+
return "unresolved";
|
|
44
|
+
const name = readString(value, "name", "displayName", "display_name");
|
|
45
|
+
const profileId = readString(value, "profile_id", "providerId", "provider_id");
|
|
46
|
+
if (!name || !profileId)
|
|
47
|
+
return status || semantics ? "unresolved" : "invalid";
|
|
48
|
+
return {
|
|
49
|
+
name,
|
|
50
|
+
profileId,
|
|
51
|
+
raw: readString(value, "raw"),
|
|
52
|
+
identifier: readString(value, "identifier", "publicIdentifier", "public_identifier"),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* True when the text already uses `{{i}}` placeholders that all resolve to a
|
|
57
|
+
* valid `{ name, profile_id }` entry — the caller pre-compiled the contract,
|
|
58
|
+
* so text and mentions pass through unchanged.
|
|
59
|
+
*/
|
|
60
|
+
function isPrecompiled(text, entries) {
|
|
61
|
+
const indexes = [...text.matchAll(PLACEHOLDER_RE)].map((match) => Number(match[1]));
|
|
62
|
+
if (indexes.length === 0)
|
|
63
|
+
return false;
|
|
64
|
+
return indexes.every((index) => {
|
|
65
|
+
const normalized = normalizeEntry(entries[index]);
|
|
66
|
+
return typeof normalized === "object";
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function compileLinkedInMentions(input) {
|
|
70
|
+
const explicit = Array.isArray(input.mentions) ? input.mentions : [];
|
|
71
|
+
const previews = Array.isArray(input.mentionPreviews) ? input.mentionPreviews : [];
|
|
72
|
+
if (isPrecompiled(input.text, explicit)) {
|
|
73
|
+
// Compact the array and renumber placeholders together: a non-object
|
|
74
|
+
// entry at an unreferenced index below a referenced one would otherwise
|
|
75
|
+
// shift later entries down while `{{i}}` numbers stay fixed, pointing a
|
|
76
|
+
// placeholder at the wrong (or missing) identity.
|
|
77
|
+
const mentions = [];
|
|
78
|
+
const remap = new Map();
|
|
79
|
+
explicit.forEach((value, oldIndex) => {
|
|
80
|
+
const entry = normalizeEntry(value);
|
|
81
|
+
if (typeof entry !== "object")
|
|
82
|
+
return;
|
|
83
|
+
remap.set(oldIndex, mentions.length);
|
|
84
|
+
mentions.push({ name: entry.name, profile_id: entry.profileId });
|
|
85
|
+
});
|
|
86
|
+
const text = input.text.replace(PLACEHOLDER_RE, (whole, digits) => {
|
|
87
|
+
const next = remap.get(Number(digits));
|
|
88
|
+
return next === undefined ? whole : `{{${next}}}`;
|
|
89
|
+
});
|
|
90
|
+
return { text, mentions, dropped: [] };
|
|
91
|
+
}
|
|
92
|
+
const dropped = [];
|
|
93
|
+
const placed = [];
|
|
94
|
+
let text = input.text;
|
|
95
|
+
const seenProfileIds = new Set();
|
|
96
|
+
for (const value of [...explicit, ...previews]) {
|
|
97
|
+
const entry = normalizeEntry(value);
|
|
98
|
+
if (entry === "skip")
|
|
99
|
+
continue;
|
|
100
|
+
if (entry === "invalid") {
|
|
101
|
+
dropped.push({ name: isRecord(value) ? readString(value, "name", "displayName", "display_name") : null, reason: "invalid" });
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (entry === "unresolved") {
|
|
105
|
+
dropped.push({ name: isRecord(value) ? readString(value, "name", "displayName", "display_name", "raw") : null, reason: "unresolved" });
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (seenProfileIds.has(entry.profileId))
|
|
109
|
+
continue;
|
|
110
|
+
const anchor = findAnchor(text, entry);
|
|
111
|
+
if (!anchor) {
|
|
112
|
+
dropped.push({ name: entry.name, reason: "not_in_text" });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const index = placed.length;
|
|
116
|
+
text = text.slice(0, anchor.start) + `{{${index}}}` + text.slice(anchor.end);
|
|
117
|
+
placed.push({ name: entry.name, profile_id: entry.profileId });
|
|
118
|
+
seenProfileIds.add(entry.profileId);
|
|
119
|
+
}
|
|
120
|
+
return { text, mentions: placed, dropped };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Find the span the mention replaces. Candidates are tried most-specific
|
|
124
|
+
* first: the exact raw capture from resolve time (`@handle`, URN, …), then
|
|
125
|
+
* `@identifier`, then `@name`. Bare names are never matched — replacing an
|
|
126
|
+
* un-prefixed word is how mentions end up covering the wrong text.
|
|
127
|
+
*/
|
|
128
|
+
function findAnchor(text, entry) {
|
|
129
|
+
const candidates = [
|
|
130
|
+
entry.raw,
|
|
131
|
+
entry.identifier ? `@${entry.identifier}` : null,
|
|
132
|
+
`@${entry.name}`,
|
|
133
|
+
].filter((candidate) => Boolean(candidate && candidate.trim()));
|
|
134
|
+
const lowerText = text.toLowerCase();
|
|
135
|
+
for (const candidate of candidates) {
|
|
136
|
+
const lowerCandidate = candidate.toLowerCase();
|
|
137
|
+
// Reject matches that continue into more handle characters so `@acme`
|
|
138
|
+
// can never anchor inside `@acme-labs` — a partial span is exactly the
|
|
139
|
+
// wrong-mention rendering this compiler exists to prevent. A dot only
|
|
140
|
+
// continues the handle when an identifier character follows it
|
|
141
|
+
// (`@acme.io`); a sentence period (`@n8n. Next…`) is a boundary.
|
|
142
|
+
let from = 0;
|
|
143
|
+
while (from <= lowerText.length - lowerCandidate.length) {
|
|
144
|
+
const start = lowerText.indexOf(lowerCandidate, from);
|
|
145
|
+
if (start < 0)
|
|
146
|
+
break;
|
|
147
|
+
const end = start + candidate.length;
|
|
148
|
+
const next = text[end];
|
|
149
|
+
const continues = next !== undefined && (/[a-z0-9_-]/i.test(next)
|
|
150
|
+
|| (next === "." && text[end + 1] !== undefined && /[a-z0-9_-]/i.test(text[end + 1])));
|
|
151
|
+
if (!continues)
|
|
152
|
+
return { start, end };
|
|
153
|
+
from = start + 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Primitive kinds that participate in the workspace tag union today. */
|
|
2
|
-
export declare const TAG_KINDS: readonly ["knowledge_page", "sequence", "table", "workflow", "recipe"];
|
|
2
|
+
export declare const TAG_KINDS: readonly ["knowledge_page", "publishing_post", "sequence", "table", "workflow", "recipe"];
|
|
3
3
|
export type TagKind = (typeof TAG_KINDS)[number];
|
|
4
4
|
export declare function isTagKind(value: unknown): value is TagKind;
|
|
5
5
|
/** Hard cap per tagged item — matches the wiki's long-standing limit. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.365.3";
|
|
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:
|