@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,7 @@
1
+ export type Field = {
2
+ name: string;
3
+ kind: string;
4
+ description: string;
5
+ required: boolean;
6
+ };
7
+ export declare function parseField(value: unknown, field: Field): unknown;
@@ -0,0 +1,25 @@
1
+ export function parseField(value, field) {
2
+ if (field.kind === "array" || field.kind === "object") {
3
+ const parsed = JSON.parse(String(value));
4
+ if (field.kind === "array"
5
+ ? !Array.isArray(parsed) ||
6
+ !parsed.every((item) => typeof item === "string")
7
+ : !parsed || typeof parsed !== "object" || Array.isArray(parsed))
8
+ throw new Error(`${field.name} must be a JSON ${field.kind === "array" ? "array of strings" : "object"}.`);
9
+ return parsed;
10
+ }
11
+ if (field.kind === "number") {
12
+ const parsed = Number(value);
13
+ if (!Number.isFinite(parsed) || parsed < 0)
14
+ throw new Error(`${field.name} must be a nonnegative finite number.`);
15
+ return parsed;
16
+ }
17
+ if (field.kind === "boolean") {
18
+ if (value === true || value === "true")
19
+ return true;
20
+ if (value === false || value === "false")
21
+ return false;
22
+ throw new Error(`${field.name} must be true or false.`);
23
+ }
24
+ return value;
25
+ }
@@ -0,0 +1,14 @@
1
+ import type { Command } from "commander";
2
+ /** The CLI owns authentication, transport, output and error presentation. */
3
+ export interface UgcCommandDependencies {
4
+ request: (path: string, options?: {
5
+ method: "POST";
6
+ body: Record<string, unknown>;
7
+ idempotencyKey?: string;
8
+ }) => Promise<unknown>;
9
+ handle: (command: string, options: {
10
+ json?: boolean;
11
+ }, action: () => Promise<unknown>) => Promise<void>;
12
+ }
13
+ /** Register the existing UGC command tree without performing requests. */
14
+ export declare function registerUgcCommands(program: Command, dependencies: UgcCommandDependencies): void;
@@ -0,0 +1,5 @@
1
+ import { registerUgcCommands as registerCommands } from "./commands.js";
2
+ /** Register the existing UGC command tree without performing requests. */
3
+ export function registerUgcCommands(program, dependencies) {
4
+ registerCommands(program, dependencies);
5
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@oxygen/cli-ugc",
3
+ "version": "0.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {}
15
+ }
@@ -435,6 +435,13 @@ function readStringLiteral(expression, start, quote) {
435
435
  expression,
436
436
  });
437
437
  }
438
+ /**
439
+ * Only the escapes the language actually defines are decoded. Everything else
440
+ * is preserved verbatim, backslash included, because a formula string literal
441
+ * is the only way to write a regex pattern: collapsing an unknown escape to the
442
+ * bare character silently turned `"\\s+"` into `s+`, so `regex_replace(x, "\\s+", "_")`
443
+ * matched a literal "s" and the pattern looked broken with nothing to debug.
444
+ */
438
445
  function decodeEscapedCharacter(char) {
439
446
  switch (char) {
440
447
  case "n":
@@ -443,8 +450,14 @@ function decodeEscapedCharacter(char) {
443
450
  return "\r";
444
451
  case "t":
445
452
  return "\t";
453
+ case "\\":
454
+ return "\\";
455
+ case "\"":
456
+ return "\"";
457
+ case "'":
458
+ return "'";
446
459
  default:
447
- return char;
460
+ return `\\${char}`;
448
461
  }
449
462
  }
450
463
  function readNumberLiteral(expression, start) {
@@ -1,6 +1,7 @@
1
1
  import { OxygenError } from "@oxygen/shared/cli-result";
2
2
  import { walkJsonPath } from "@oxygen/shared/json-path";
3
3
  import { normalizeDomain, normalizeEmail, normalizeLinkedinUrl, } from "./value-normalizers.js";
4
+ import { classifyEmailType, cleanJobTitle, normalizeCompanyName, splitDelimitedList, } from "./value-cleaners.js";
4
5
  // Pattern LENGTH is not a safety bound and never was -- catastrophic backtracking
5
6
  // is a function of pattern SHAPE, and `(a+)+$` hangs at 6 characters, so 256 already
6
7
  // permitted an unbounded hang while rejecting harmless long literals. The real bound
@@ -129,7 +130,8 @@ function compileFormulaRegex(pattern, functionName, flags) {
129
130
  return new RegExp(pattern, flags);
130
131
  }
131
132
  catch (error) {
132
- throw formulaExpressionError("Invalid regular-expression pattern.", {
133
+ throw formulaExpressionError("Invalid regular-expression pattern. Patterns use JavaScript RegExp syntax without flags:"
134
+ + " inline modifiers such as (?i) are not supported — apply lower() to the input or spell out both cases.", {
133
135
  function: functionName,
134
136
  pattern,
135
137
  parse_error: error instanceof Error ? error.message : String(error),
@@ -638,6 +640,46 @@ const SPECS = [
638
640
  return asText(args[0]).includes(asText(args[1]));
639
641
  },
640
642
  }),
643
+ spec({
644
+ name: "normalize_company_name",
645
+ category: "string",
646
+ signature: "normalize_company_name(text, mode?)",
647
+ description: "Canonical company name: legal suffixes removed (Inc, Ltd, GmbH, Pty Ltd, sp. z o.o., …, repeatedly), stray punctuation and quotes trimmed, whitespace collapsed, \"&\" and apostrophes kept. Casing is the author’s unless the whole name is upper-case, which is title-cased. mode \"short\" (default \"display\") also drops a tagline after \" - \", \" | \", \":\", \" / \" or \"(\" and keeps at most the first three words, for use in a first sentence. Blank input returns null.",
648
+ minArgs: 1,
649
+ maxArgs: 2,
650
+ examples: [
651
+ { expression: "normalize_company_name(\"Acme, Inc.\")", result: "Acme" },
652
+ { expression: "normalize_company_name(\"ACME CORP.\")", result: "Acme" },
653
+ { expression: "normalize_company_name(\"Northwind Traders LLC - Logistics\", \"short\")", result: "Northwind Traders" },
654
+ ],
655
+ evaluate: (args) => {
656
+ if (isBlankFormulaValue(args[0]))
657
+ return null;
658
+ const mode = isBlankFormulaValue(args[1]) ? "display" : asText(args[1]).trim().toLowerCase();
659
+ if (mode !== "display" && mode !== "short") {
660
+ throw formulaExpressionError("Unsupported company-name mode.", {
661
+ function: "normalize_company_name",
662
+ mode: args[1],
663
+ supported: ["display", "short"],
664
+ });
665
+ }
666
+ return normalizeCompanyName(asText(args[0]), mode);
667
+ },
668
+ }),
669
+ spec({
670
+ name: "clean_job_title",
671
+ category: "string",
672
+ signature: "clean_job_title(text)",
673
+ description: "Strips emoji, symbol runs, hashtags, parentheticals and hiring slogans (\"we’re hiring\", \"open to work\", \"looking for\", \"dm me\", …) from a scraped headline, then keeps only the first clause when it is joined by \" | \", \" • \", \" · \", \" — \", \" – \" or \" :: \". A bare \"-\", \",\", \"/\" or \"&\" never cuts, so \"Account Executive - EMEA\" and \"Director of Importing/Exporting\" survive whole. All-caps titles are title-cased; any other casing is the author’s. Blank or noise-only input returns null.",
674
+ minArgs: 1,
675
+ maxArgs: 1,
676
+ examples: [
677
+ { expression: "clean_job_title(\"VP of Sales 🚀 | We’re hiring!\")", result: "VP of Sales" },
678
+ { expression: "clean_job_title(\"Head of Growth (ex-Google)\")", result: "Head of Growth" },
679
+ { expression: "clean_job_title(\"Sr. Director, Demand Generation — Hiring SDRs!\")", result: "Sr. Director, Demand Generation" },
680
+ ],
681
+ evaluate: (args) => (isBlankFormulaValue(args[0]) ? null : cleanJobTitle(asText(args[0]))),
682
+ }),
641
683
  // --- number ----------------------------------------------------------------
642
684
  spec({
643
685
  name: "number",
@@ -911,6 +953,21 @@ const SPECS = [
911
953
  examples: [{ expression: "normalize_linkedin_url(\"https://linkedin.com/in/Ada/\")", result: "https://linkedin.com/in/ada" }],
912
954
  evaluate: (args) => blankToNull(args[0], (input) => normalizeLinkedinUrl(asText(input))),
913
955
  }),
956
+ spec({
957
+ name: "email_type",
958
+ category: "url_email",
959
+ signature: "email_type(email)",
960
+ description: "Classifies an address as \"disposable\", \"role\", \"personal\" (a consumer mailbox provider) or \"work\", from OXYGEN’s built-in domain and shared-inbox lists, in that precedence. Subdomains count as their parent domain and a +tag is ignored. Blank or syntactically invalid input returns null. When a fixed list is not enough, the paid bounceban.check verifier adds live username/domain typing and syntax validity for catch-all and SEG domains.",
961
+ minArgs: 1,
962
+ maxArgs: 1,
963
+ examples: [
964
+ { expression: "email_type(\"jane@gmail.com\")", result: "personal" },
965
+ { expression: "email_type(\"info@stripe.com\")", result: "role" },
966
+ { expression: "email_type(\"alice@mailinator.com\")", result: "disposable" },
967
+ { expression: "email_type(\"mark@acme.com\")", result: "work" },
968
+ ],
969
+ evaluate: (args) => (isBlankFormulaValue(args[0]) ? null : classifyEmailType(asText(args[0]))),
970
+ }),
914
971
  // --- array -----------------------------------------------------------------
915
972
  spec({
916
973
  name: "first",
@@ -989,6 +1046,19 @@ const SPECS = [
989
1046
  return result;
990
1047
  },
991
1048
  }),
1049
+ spec({
1050
+ name: "to_json_list",
1051
+ category: "array",
1052
+ signature: "to_json_list(text, separator?)",
1053
+ description: "Turns one messy cell into a clean array: every part trimmed, blanks dropped, duplicates removed in first-seen order. Without a separator the delimiter is auto-detected (newline, then \";\", then \"|\", then \",\"; otherwise a single item). An existing array or a JSON-encoded array string is cleaned in place. Blank input returns an empty array. Use split() when you want the raw parts, including blanks and duplicates.",
1054
+ minArgs: 1,
1055
+ maxArgs: 2,
1056
+ examples: [
1057
+ { expression: "to_json_list(\"fintech, payments,saas\")", result: ["fintech", "payments", "saas"] },
1058
+ { expression: "to_json_list(\"growth;plg\")", result: ["growth", "plg"] },
1059
+ ],
1060
+ evaluate: (args) => splitDelimitedList(args[0], isBlankFormulaValue(args[1]) ? null : asText(args[1])),
1061
+ }),
992
1062
  // --- json ------------------------------------------------------------------
993
1063
  spec({
994
1064
  name: "path",
@@ -14,4 +14,5 @@ export { isRecord } from "./coerce.js";
14
14
  export * from "./evaluate.js";
15
15
  export * from "./expression.js";
16
16
  export * from "./formula-functions.js";
17
+ export * from "./value-cleaners.js";
17
18
  export * from "./value-normalizers.js";
@@ -14,4 +14,5 @@ export { isRecord } from "./coerce.js";
14
14
  export * from "./evaluate.js";
15
15
  export * from "./expression.js";
16
16
  export * from "./formula-functions.js";
17
+ export * from "./value-cleaners.js";
17
18
  export * from "./value-normalizers.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Deterministic value cleaners for dirty GTM strings — company names, job
3
+ * titles, delimited lists, and email-address classification.
4
+ *
5
+ * Pure string functions with no I/O, no tenant context, and no dependencies,
6
+ * so the exact semantics that clean a cell in the worker also run in the
7
+ * browser (formula editor previews). Every list below is a fixed, auditable
8
+ * data set: nothing here calls a provider, spends a credit, or guesses with a
9
+ * model. When a deterministic list is not enough, the paid verifier columns
10
+ * are the upgrade — these helpers are the free first pass.
11
+ */
12
+ /** True when every cased letter in the text is upper-case (digits/punctuation ignored). */
13
+ export declare function isAllCaps(value: string): boolean;
14
+ /** Capitalizes the first letter of every word and lowercases the rest (Unicode-aware). */
15
+ export declare function titleCaseWords(value: string): string;
16
+ /**
17
+ * Legal/corporate suffixes, stored as punctuation-free lower-case keys so
18
+ * "L.L.C.", "llc", and "LLC," all reduce to `llc`. Multi-word suffixes are
19
+ * joined ("pty ltd" → `ptyltd`) and matched against the trailing 1–3 tokens.
20
+ */
21
+ export declare const COMPANY_LEGAL_SUFFIX_KEYS: ReadonlySet<string>;
22
+ export type CompanyNameMode = "display" | "short";
23
+ /**
24
+ * Canonical company name: legal suffixes removed, stray punctuation and quotes
25
+ * trimmed, whitespace collapsed. Casing is the author's unless the whole name
26
+ * shouts ("ACME CORP." → "Acme"), because "hubspot" is how that company writes
27
+ * itself. `short` additionally cuts a tagline after a separator and keeps at
28
+ * most three words — the form that fits in an email's first sentence.
29
+ *
30
+ * Blank input returns null.
31
+ */
32
+ export declare function normalizeCompanyName(value: unknown, mode?: CompanyNameMode): string | null;
33
+ /**
34
+ * Recruiting and self-promotion phrases that ride along in a scraped headline.
35
+ * Longest first so "we're hiring" is consumed before the bare "hiring".
36
+ */
37
+ export declare const JOB_TITLE_SLOGANS: readonly string[];
38
+ /**
39
+ * Canonical job title: emoji, hashtags, parentheticals, and hiring slogans
40
+ * removed, then only the first clause of a separator-joined headline kept.
41
+ * All-caps titles are title-cased; every other casing is the author's.
42
+ *
43
+ * Blank input, or input that was nothing but noise, returns null.
44
+ */
45
+ export declare function cleanJobTitle(value: unknown): string | null;
46
+ /**
47
+ * Turns one messy cell into a clean array: trimmed, blank-free, de-duplicated
48
+ * in first-seen order. An existing array or a JSON-encoded array is cleaned in
49
+ * place; a string is split on the given separator, or on the first delimiter
50
+ * actually present (newline, `;`, `|`, `,`) when none is given.
51
+ *
52
+ * Blank input returns an empty array.
53
+ */
54
+ export declare function splitDelimitedList(value: unknown, separator?: string | null): string[];
55
+ /** Consumer mailbox providers — a person, not a company domain. */
56
+ export declare const FREE_MAIL_DOMAINS: ReadonlySet<string>;
57
+ /** Throwaway inbox providers — never worth a send, never worth a credit. */
58
+ export declare const DISPOSABLE_EMAIL_DOMAINS: ReadonlySet<string>;
59
+ /** Shared-inbox local parts — a department, not a person. */
60
+ export declare const ROLE_EMAIL_LOCAL_PARTS: ReadonlySet<string>;
61
+ export type EmailType = "disposable" | "role" | "personal" | "work";
62
+ /**
63
+ * Classifies an email address from OXYGEN's built-in lists, in precedence
64
+ * order: a throwaway domain wins, then a shared-inbox local part, then a
65
+ * consumer mailbox provider, and anything left is a company address.
66
+ *
67
+ * Blank or syntactically invalid input returns null.
68
+ */
69
+ export declare function classifyEmailType(value: unknown): EmailType | null;