@oxygen-agent/cli 1.1014.985 → 1.1014.1087

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 (131) hide show
  1. package/README.md +1 -1
  2. package/dist/command-families/program.d.ts +2 -0
  3. package/dist/command-families/program.js +241 -0
  4. package/dist/command-families/tables/automation.d.ts +22 -0
  5. package/dist/command-families/tables/automation.js +250 -0
  6. package/dist/command-families/tables/bundle-export.d.ts +7 -0
  7. package/dist/command-families/tables/bundle-export.js +198 -0
  8. package/dist/command-families/tables/bundle-format.d.ts +66 -0
  9. package/dist/command-families/tables/bundle-format.js +274 -0
  10. package/dist/command-families/tables/bundle-import.d.ts +20 -0
  11. package/dist/command-families/tables/bundle-import.js +260 -0
  12. package/dist/command-families/tables/display.d.ts +107 -0
  13. package/dist/command-families/tables/display.js +229 -0
  14. package/dist/command-families/tables/import-batches.d.ts +26 -0
  15. package/dist/command-families/tables/import-batches.js +94 -0
  16. package/dist/command-families/tables/import-columns.d.ts +24 -0
  17. package/dist/command-families/tables/import-columns.js +131 -0
  18. package/dist/command-families/tables/import-upload.d.ts +30 -0
  19. package/dist/command-families/tables/import-upload.js +203 -0
  20. package/dist/command-families/tables/import-values.d.ts +10 -0
  21. package/dist/command-families/tables/import-values.js +60 -0
  22. package/dist/command-families/tables/imports.d.ts +19 -0
  23. package/dist/command-families/tables/imports.js +200 -0
  24. package/dist/command-families/tables/options.d.ts +314 -0
  25. package/dist/command-families/tables/options.js +1 -0
  26. package/dist/command-families/tables/register-automation.d.ts +2 -0
  27. package/dist/command-families/tables/register-automation.js +127 -0
  28. package/dist/command-families/tables/register-external-imports.d.ts +2 -0
  29. package/dist/command-families/tables/register-external-imports.js +293 -0
  30. package/dist/command-families/tables/register-files.d.ts +2 -0
  31. package/dist/command-families/tables/register-files.js +72 -0
  32. package/dist/command-families/tables/register-followers.d.ts +2 -0
  33. package/dist/command-families/tables/register-followers.js +73 -0
  34. package/dist/command-families/tables/register-lifecycle.d.ts +2 -0
  35. package/dist/command-families/tables/register-lifecycle.js +114 -0
  36. package/dist/command-families/tables/register-reads.d.ts +2 -0
  37. package/dist/command-families/tables/register-reads.js +187 -0
  38. package/dist/command-families/tables/register-relations.d.ts +2 -0
  39. package/dist/command-families/tables/register-relations.js +244 -0
  40. package/dist/command-families/tables/register-row-actions.d.ts +2 -0
  41. package/dist/command-families/tables/register-row-actions.js +53 -0
  42. package/dist/command-families/tables/register-row-writes.d.ts +2 -0
  43. package/dist/command-families/tables/register-row-writes.js +140 -0
  44. package/dist/command-families/tables/register-views.d.ts +2 -0
  45. package/dist/command-families/tables/register-views.js +83 -0
  46. package/dist/command-families/tables/register-watchers.d.ts +2 -0
  47. package/dist/command-families/tables/register-watchers.js +65 -0
  48. package/dist/command-families/tables/register-webhooks.d.ts +2 -0
  49. package/dist/command-families/tables/register-webhooks.js +126 -0
  50. package/dist/command-families/tables/registration-context.d.ts +8 -0
  51. package/dist/command-families/tables/registration-context.js +1 -0
  52. package/dist/command-families/tables/registration.d.ts +3 -0
  53. package/dist/command-families/tables/registration.js +30 -0
  54. package/dist/command-families/tables/row-selection.d.ts +5 -0
  55. package/dist/command-families/tables/row-selection.js +82 -0
  56. package/dist/command-families/tables/send.d.ts +6 -0
  57. package/dist/command-families/tables/send.js +111 -0
  58. package/dist/command-manifest.js +1 -0
  59. package/dist/commands/copilot.js +8 -0
  60. package/dist/commands/deliverability.js +4 -2
  61. package/dist/commands/sequences.js +30 -2
  62. package/dist/commands/tables.d.ts +1 -3
  63. package/dist/commands/tables.js +1 -3505
  64. package/dist/credentials.js +1 -1
  65. package/dist/customer-operation-commands.js +62 -3
  66. package/dist/help.js +1 -1
  67. package/dist/index.d.ts +1 -99
  68. package/dist/index.js +5 -337
  69. package/dist/knowledge-mirror.js +1 -1
  70. package/dist/skills.js +4 -2
  71. package/dist/update.js +2 -1
  72. package/dist/workspace-placement-commands.d.ts +3 -0
  73. package/dist/workspace-placement-commands.js +32 -0
  74. package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +4 -0
  75. package/node_modules/@oxygen/shared/dist/agent-sessions.js +11 -3
  76. package/node_modules/@oxygen/shared/dist/agents-enabled.d.ts +29 -0
  77. package/node_modules/@oxygen/shared/dist/agents-enabled.js +51 -0
  78. package/node_modules/@oxygen/shared/dist/capability-discovery.js +39 -2
  79. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +1 -1
  80. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +1 -1
  81. package/node_modules/@oxygen/shared/dist/contracts/guided-activation.js +5 -5
  82. package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.d.ts +30 -0
  83. package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.js +53 -0
  84. package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.d.ts +120 -0
  85. package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.js +48 -0
  86. package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.d.ts +267 -0
  87. package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.js +63 -0
  88. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +6 -0
  89. package/node_modules/@oxygen/shared/dist/contracts/operations.js +9 -4
  90. package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +2 -0
  91. package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +3 -0
  92. package/node_modules/@oxygen/shared/dist/contracts/teammates.js +17 -15
  93. package/node_modules/@oxygen/shared/dist/email-tracking-token.d.ts +2 -0
  94. package/node_modules/@oxygen/shared/dist/email-tracking-token.js +1 -0
  95. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
  96. package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
  97. package/node_modules/@oxygen/shared/dist/file-import.d.ts +46 -0
  98. package/node_modules/@oxygen/shared/dist/file-import.js +138 -0
  99. package/node_modules/@oxygen/shared/dist/guided-activation.d.ts +8 -0
  100. package/node_modules/@oxygen/shared/dist/guided-company-files.d.ts +13 -0
  101. package/node_modules/@oxygen/shared/dist/guided-company-files.js +14 -0
  102. package/node_modules/@oxygen/shared/dist/guided-first-sourcing.d.ts +116 -0
  103. package/node_modules/@oxygen/shared/dist/guided-first-sourcing.js +11 -0
  104. package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.d.ts +4 -0
  105. package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.js +42 -0
  106. package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.d.ts +22 -0
  107. package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.js +38 -0
  108. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.d.ts +147 -0
  109. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.d.ts +12 -0
  110. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.js +83 -0
  111. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.js +44 -0
  112. package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.d.ts +7 -0
  113. package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.js +13 -0
  114. package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.d.ts +4 -0
  115. package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.js +34 -0
  116. package/node_modules/@oxygen/shared/dist/guided-trial-billing.d.ts +26 -0
  117. package/node_modules/@oxygen/shared/dist/guided-trial-billing.js +19 -0
  118. package/node_modules/@oxygen/shared/dist/index.d.ts +0 -1
  119. package/node_modules/@oxygen/shared/dist/index.js +0 -6
  120. package/node_modules/@oxygen/shared/dist/table-limits.d.ts +1 -0
  121. package/node_modules/@oxygen/shared/dist/table-limits.js +6 -0
  122. package/node_modules/@oxygen/shared/dist/teammates.d.ts +13 -8
  123. package/node_modules/@oxygen/shared/dist/teammates.js +20 -11
  124. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +78 -0
  125. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  126. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  127. package/node_modules/@oxygen/shared/dist/workspace-placement-view.d.ts +149 -0
  128. package/node_modules/@oxygen/shared/dist/workspace-placement-view.js +101 -0
  129. package/node_modules/@oxygen/shared/dist/workspace-placement.d.ts +77 -0
  130. package/node_modules/@oxygen/shared/dist/workspace-placement.js +94 -0
  131. package/package.json +1 -1
@@ -0,0 +1,200 @@
1
+ import { readRecordString } from "../../cli-values.js";
2
+ import { readRecord } from "../../command-support.js";
3
+ import { requestOxygen } from "../../http-client.js";
4
+ import { shouldStreamFileImport, STREAMED_FILE_IMPORT_MIN_BYTES, scanStreamedImportFile, rowsForStreamedImportTarget, putImportFileStream } from "../../streamed-file-import.js";
5
+ import { readOption } from "../../util.js";
6
+ import { chunk } from "@oxygen/shared/array-utils";
7
+ import { OxygenError } from "@oxygen/shared/cli-result";
8
+ import { normalizeRowsFormat, inferRowsFileFormat, assertImportFormatWithinBufferedLimit, dropBlankImportCells } from "@oxygen/shared/file-import";
9
+ import { statSync, readFileSync } from "node:fs";
10
+ import { basename } from "node:path";
11
+ import { emitQueueWaitStderrNote, enqueueImportFile, formatMebibytes, putImportFile, tryEnqueueStagedFileImport } from "./import-upload.js";
12
+ import { LARGE_IMPORT_BACKGROUND_ROW_THRESHOLD, SAFE_IMPORT_WRITE_BATCH_SIZE, hashImportFile, normalizeImportBatchSize, withImportWaitNextStep } from "./import-values.js";
13
+ import { readRowsFile } from "./bundle-format.js";
14
+ import { prepareImportTarget } from "./import-columns.js";
15
+ import { writeImportBatchWithAutoSplit } from "./import-batches.js";
16
+ /**
17
+ * `--url` hands the fetch to the server: the same durable import the file path
18
+ * enqueues, with the bytes read by OXYGEN behind SSRF, size and content guards,
19
+ * so the CLI never downloads anything and a 0-credit preview is one flag away.
20
+ */
21
+ export async function importRowsFromUrl(table, options) {
22
+ if (options.create && table) {
23
+ throw new OxygenError("invalid_import_target", "Pass either a table argument or --create, not both.", { exitCode: 1 });
24
+ }
25
+ if (!options.preview && !options.create && !table) {
26
+ throw new OxygenError("invalid_import_target", "Pass a table argument or --create <name>, or add --preview to look first.", { exitCode: 1 });
27
+ }
28
+ const result = await requestOxygen("/api/cli/tables/import-from-url", {
29
+ method: "POST",
30
+ timeoutMs: 120_000,
31
+ body: {
32
+ url: options.url,
33
+ mode: options.preview ? "preview" : "import",
34
+ ...(table ? { table } : {}),
35
+ ...(options.create ? { create_name: options.create } : {}),
36
+ ...(readOption(options.project) ? { project: readOption(options.project) } : {}),
37
+ ...(readOption(options.upsertKey) ? { upsert_key: readOption(options.upsertKey) } : {}),
38
+ ...(readOption(options.format) ? { format: readOption(options.format) } : {}),
39
+ ...(readOption(options.sheet) ? { sheet: readOption(options.sheet) } : {}),
40
+ },
41
+ });
42
+ if (options.preview)
43
+ return result;
44
+ emitQueueWaitStderrNote(readRecord(result, "queue_wait"));
45
+ return withImportWaitNextStep({ ...result, background: true, autoBackground: true });
46
+ }
47
+ export async function importRows(table, options) {
48
+ if (options.url && options.skipBlankCells) {
49
+ throw new OxygenError("invalid_request", "--skip-blank-cells works on --file imports. Download the link and import the file.", { exitCode: 1 });
50
+ }
51
+ if (options.url)
52
+ return importRowsFromUrl(table, { ...options, url: options.url });
53
+ if (!options.file) {
54
+ throw new OxygenError("invalid_request", "Pass --file <path> or --url <https-link>.", { exitCode: 1 });
55
+ }
56
+ if (options.preview) {
57
+ throw new OxygenError("invalid_request", "--preview only applies to --url imports.", { exitCode: 1 });
58
+ }
59
+ return importRowsFromFile(table, { ...options, file: options.file });
60
+ }
61
+ export async function importRowsFromFile(table, options) {
62
+ const format = normalizeRowsFormat(options.format, inferRowsFileFormat(options.file));
63
+ // Refuse a JSON or XLSX file too large to hold in memory before reading it,
64
+ // and send a large CSV or JSONL file down the streamed path, which never does.
65
+ const fileBytes = statSync(options.file).size;
66
+ assertImportFormatWithinBufferedLimit(format, fileBytes);
67
+ if (shouldStreamFileImport(format, fileBytes)) {
68
+ return withImportWaitNextStep(await importLargeFileStreamed(table, options, format, fileBytes));
69
+ }
70
+ const parsedRows = await readRowsFile(options.file, format, options.sheet);
71
+ if (parsedRows.length === 0) {
72
+ throw new OxygenError("invalid_rows", "Import file did not contain any rows.", {
73
+ exitCode: 1,
74
+ });
75
+ }
76
+ const batchSize = normalizeImportBatchSize(options.batchSize);
77
+ const effectiveBatchSize = Math.min(batchSize, SAFE_IMPORT_WRITE_BATCH_SIZE);
78
+ const sourceHash = hashImportFile(options.file);
79
+ const shouldUseBackground = options.background
80
+ || (!options.sync && parsedRows.length > LARGE_IMPORT_BACKGROUND_ROW_THRESHOLD);
81
+ const preparedBackgroundTarget = shouldUseBackground && table && !options.create
82
+ ? await prepareImportTarget(table, options, parsedRows)
83
+ : null;
84
+ if (shouldUseBackground) {
85
+ // Prefer the object-storage fast path: upload the raw file once via a
86
+ // presigned URL (no request-body limit) so the worker COPY-loads it.
87
+ // Falls back to the inline multipart import when storage is unconfigured.
88
+ const fileBuffer = readFileSync(options.file);
89
+ const staged = await tryEnqueueStagedFileImport(table, options, format, {
90
+ byteLength: fileBuffer.byteLength,
91
+ read: () => Promise.resolve({ targetRows: parsedRows, rowCount: parsedRows.length, sha256: sourceHash }),
92
+ put: (uploadUrl, contentType) => putImportFile(uploadUrl, fileBuffer, contentType),
93
+ }, {
94
+ autoBackground: !options.background,
95
+ batchSize,
96
+ }, preparedBackgroundTarget);
97
+ if (staged)
98
+ return withImportWaitNextStep(staged);
99
+ return withImportWaitNextStep(await enqueueImportFile(table, options, format, batchSize, {
100
+ autoBackground: !options.background,
101
+ sourceHash,
102
+ }, preparedBackgroundTarget));
103
+ }
104
+ const prepared = await prepareImportTarget(table, options, parsedRows);
105
+ // The foreground writes rows itself: a row without a key leaves that value alone.
106
+ const target = options.skipBlankCells
107
+ ? { ...prepared, rows: prepared.rows.map((row) => dropBlankImportCells(row, prepared.upsertKey)) }
108
+ : prepared;
109
+ let rowCount = 0;
110
+ let insertedCount = 0;
111
+ let updatedCount = 0;
112
+ let warningCount = 0;
113
+ const warnings = [];
114
+ let warningsTruncated = false;
115
+ let lastResult = null;
116
+ let writeRequestCount = 0;
117
+ let requestTooLargeRetries = 0;
118
+ let byteSplits = 0;
119
+ let minimumBatchSizeUsed = null;
120
+ for (const [batchIndex, batch] of chunk(target.rows, effectiveBatchSize).entries()) {
121
+ const result = await writeImportBatchWithAutoSplit({
122
+ tableRef: target.tableRef,
123
+ upsertKey: target.upsertKey,
124
+ rows: batch,
125
+ ...(!target.upsertKey
126
+ ? { requestId: `tables-import:${sourceHash}:${batchIndex}` }
127
+ : {}),
128
+ });
129
+ rowCount += result.rowCount;
130
+ insertedCount += result.insertedCount;
131
+ updatedCount += result.updatedCount;
132
+ warningCount += result.warningCount;
133
+ for (const warning of result.warnings) {
134
+ if (warnings.length < 20)
135
+ warnings.push(warning);
136
+ }
137
+ warningsTruncated = warningsTruncated || result.warningsTruncated || warningCount > warnings.length;
138
+ writeRequestCount += result.writeRequestCount;
139
+ requestTooLargeRetries += result.requestTooLargeRetries;
140
+ byteSplits += result.byteSplits;
141
+ minimumBatchSizeUsed = minimumBatchSizeUsed === null
142
+ ? result.minimumBatchSizeUsed
143
+ : Math.min(minimumBatchSizeUsed, result.minimumBatchSizeUsed);
144
+ lastResult = result.lastResult;
145
+ }
146
+ return {
147
+ table: lastResult?.table ?? null,
148
+ ...(target.createdTable ? { createdTable: target.createdTable } : {}),
149
+ rowCount,
150
+ insertedCount,
151
+ updatedCount,
152
+ ...(warningCount > 0 ? {
153
+ warnings,
154
+ warningCount,
155
+ ...(warningsTruncated ? { warningsTruncated: true } : {}),
156
+ } : {}),
157
+ batchCount: writeRequestCount,
158
+ batchSize: effectiveBatchSize,
159
+ ...(effectiveBatchSize !== batchSize ? { requestedBatchSize: batchSize } : {}),
160
+ // Two separate counters: a byte pre-split cost nothing, a server 413 cost a
161
+ // wasted upload — reporting them as one number would hide which happened.
162
+ ...(requestTooLargeRetries > 0 ? { requestTooLargeRetries } : {}),
163
+ ...(byteSplits > 0 ? { byteSplits } : {}),
164
+ ...(requestTooLargeRetries > 0 || byteSplits > 0 ? { minimumBatchSizeUsed } : {}),
165
+ ...(target.tableWebUrl ? { table_web_url: target.tableWebUrl } : {}),
166
+ ...(readRecordString(lastResult, "web_url") ? { web_url: readRecordString(lastResult, "web_url") } : {}),
167
+ };
168
+ }
169
+ /**
170
+ * A CSV or JSONL file over STREAMED_FILE_IMPORT_MIN_BYTES: never read whole.
171
+ * One streaming pass counts, hashes and collects its columns, the upload streams
172
+ * from disk, and the import always runs in the background. There is no inline
173
+ * fallback: a file this large cannot travel in one API request.
174
+ */
175
+ export async function importLargeFileStreamed(table, options, format, fileBytes) {
176
+ if (options.sync) {
177
+ process.stderr.write(`note: files over ${Math.round(STREAMED_FILE_IMPORT_MIN_BYTES / (1024 * 1024))} MiB always import in the background; --sync is ignored\n`);
178
+ }
179
+ const staged = await tryEnqueueStagedFileImport(table, options, format, {
180
+ byteLength: fileBytes,
181
+ read: async () => {
182
+ process.stderr.write(`note: reading ${basename(options.file)} (${formatMebibytes(fileBytes)}) to count its rows before uploading\n`);
183
+ const scan = await scanStreamedImportFile(options.file, format);
184
+ if (scan.rowCount === 0) {
185
+ throw new OxygenError("invalid_rows", "Import file did not contain any rows.", { exitCode: 1 });
186
+ }
187
+ return { targetRows: rowsForStreamedImportTarget(scan), rowCount: scan.rowCount, sha256: scan.sha256 };
188
+ },
189
+ put: (uploadUrl, contentType) => putImportFileStream({ uploadUrl, path: options.file, byteLength: fileBytes, contentType }),
190
+ }, {
191
+ autoBackground: !options.background,
192
+ batchSize: normalizeImportBatchSize(options.batchSize),
193
+ });
194
+ if (staged)
195
+ return { ...staged, streamed_upload: true };
196
+ throw new OxygenError("object_storage_not_configured", `Files over ${Math.round(STREAMED_FILE_IMPORT_MIN_BYTES / (1024 * 1024))} MiB upload straight to file storage, which this OXYGEN server has not configured.`, {
197
+ details: { file_bytes: fileBytes, streamed_import_min_bytes: STREAMED_FILE_IMPORT_MIN_BYTES },
198
+ exitCode: 1,
199
+ });
200
+ }
@@ -0,0 +1,314 @@
1
+ import { JsonOptions } from "../../cli-output.js";
2
+ export type TablesViewOptions = JsonOptions & {
3
+ name?: string;
4
+ viewType?: string;
5
+ config?: string;
6
+ groupBy?: string;
7
+ default?: boolean;
8
+ position?: string;
9
+ };
10
+ export type TablesHubSpotImportOptions = JsonOptions & {
11
+ list?: string;
12
+ object?: string;
13
+ destination?: string;
14
+ connection?: string;
15
+ sample?: boolean;
16
+ dncList?: string;
17
+ companyDncList?: string;
18
+ name?: string;
19
+ project?: string;
20
+ contactLinkedinProperty?: string;
21
+ contactCompanyDomainProperty?: string;
22
+ contactCompanyLinkedinProperty?: string;
23
+ companyDomainProperty?: string;
24
+ companyLinkedinProperty?: string;
25
+ contactProperties?: string;
26
+ maxRecordsPerList?: string;
27
+ maxCredits?: string;
28
+ once?: boolean;
29
+ cron?: string;
30
+ timezone?: string;
31
+ reviewedFingerprint?: string;
32
+ idempotencyKey?: string;
33
+ live?: boolean;
34
+ approved?: boolean;
35
+ };
36
+ export type TablesPipedriveImportOptions = JsonOptions & {
37
+ filter?: string;
38
+ dncFilter?: string;
39
+ orgDncFilter?: string;
40
+ name?: string;
41
+ project?: string;
42
+ personLinkedinField?: string;
43
+ personTitleField?: string;
44
+ orgDomainField?: string;
45
+ orgLinkedinField?: string;
46
+ personFields?: string;
47
+ maxRecordsPerFilter?: string;
48
+ maxCredits?: string;
49
+ once?: boolean;
50
+ cron?: string;
51
+ timezone?: string;
52
+ reviewedFingerprint?: string;
53
+ idempotencyKey?: string;
54
+ live?: boolean;
55
+ approved?: boolean;
56
+ };
57
+ export type TablesWatcherOptions = JsonOptions & {
58
+ table?: string;
59
+ name?: string;
60
+ project?: string;
61
+ profilesJson?: string;
62
+ maxCredits?: string;
63
+ requestId?: string;
64
+ previewHash?: string;
65
+ approved?: boolean;
66
+ credentialMode?: string;
67
+ };
68
+ export type TablesFollowersOptions = JsonOptions & {
69
+ tableId?: string;
70
+ company?: string;
71
+ records?: string;
72
+ package?: string;
73
+ name?: string;
74
+ project?: string;
75
+ maxCredits?: string;
76
+ requestId?: string;
77
+ credentialMode?: string;
78
+ };
79
+ export type TablesCreateOptions = JsonOptions & {
80
+ columnsJson: string;
81
+ project?: string;
82
+ };
83
+ export type TablesListOptions = JsonOptions & {
84
+ project?: string;
85
+ tag?: string;
86
+ includeArchived?: boolean;
87
+ /** `--limit`: cap the listed window (default 200, max 1000). */
88
+ limit?: string;
89
+ /** `--all`: list every table instead of the newest window. */
90
+ all?: boolean;
91
+ cursor?: string;
92
+ query?: string;
93
+ projection?: "full" | "summary";
94
+ maxChars?: string;
95
+ };
96
+ export type TablesDuplicateOptions = JsonOptions & {
97
+ name?: string;
98
+ schemaOnly?: boolean;
99
+ };
100
+ export type TablesQueryOptions = JsonOptions & Record<string, unknown> & {
101
+ limit?: string;
102
+ cursor?: string;
103
+ offset?: string;
104
+ fields?: string;
105
+ filterJson?: string;
106
+ filterTreeJson?: string;
107
+ search?: string;
108
+ formulaValues?: string;
109
+ sortJson?: string;
110
+ systemFields?: boolean;
111
+ output?: string;
112
+ };
113
+ export type TablesPreviewOptions = JsonOptions & {
114
+ limit?: string;
115
+ fields?: string;
116
+ includeSystemFields?: boolean;
117
+ includeCellStates?: boolean;
118
+ summaryOnly?: boolean;
119
+ };
120
+ export type TablesInsertOptions = JsonOptions & {
121
+ rowsJson: string;
122
+ requestId?: string;
123
+ };
124
+ export type TablesUpdateOptions = JsonOptions & {
125
+ valuesJson: string;
126
+ };
127
+ export type TablesDeleteRowsOptions = JsonOptions & {
128
+ metadataJson?: string;
129
+ format?: string;
130
+ rowIdsJson?: string;
131
+ rowIdsFile?: string;
132
+ rowIdColumn?: string;
133
+ sheet?: string;
134
+ };
135
+ export type TablesDescribeOptions = JsonOptions & {
136
+ includeArchived?: boolean;
137
+ stats?: boolean;
138
+ column?: string;
139
+ };
140
+ export type TablesActivityOptions = JsonOptions & {
141
+ limit?: string;
142
+ };
143
+ export type TablesTidySuggestOptions = JsonOptions & {
144
+ limit?: string;
145
+ categories?: string;
146
+ };
147
+ export type TablesRenameOptions = JsonOptions & {
148
+ name: string;
149
+ };
150
+ export type TablesMoveOptions = JsonOptions & {
151
+ project: string;
152
+ };
153
+ export type TablesUpsertOptions = JsonOptions & {
154
+ key: string;
155
+ rowsJson: string;
156
+ "return"?: string;
157
+ dryRun?: boolean;
158
+ };
159
+ export type TablesSourcesOptions = JsonOptions & {
160
+ group?: string;
161
+ };
162
+ export type TablesImportOptions = JsonOptions & {
163
+ file?: string;
164
+ url?: string;
165
+ preview?: boolean;
166
+ format?: string;
167
+ upsertKey?: string;
168
+ skipBlankCells?: boolean;
169
+ batchSize?: string;
170
+ background?: boolean;
171
+ sync?: boolean;
172
+ maxConcurrency?: string;
173
+ create?: string;
174
+ project?: string;
175
+ sheet?: string;
176
+ };
177
+ export type TablesExportOptions = JsonOptions & {
178
+ format?: string;
179
+ output?: string;
180
+ limit?: string;
181
+ };
182
+ export type TablesExportBundleOptions = JsonOptions & {
183
+ output?: string;
184
+ pageSize?: string;
185
+ };
186
+ export type TablesImportBundleOptions = JsonOptions & {
187
+ file: string;
188
+ name?: string;
189
+ project?: string;
190
+ into?: string;
191
+ key?: string;
192
+ batchSize?: string;
193
+ };
194
+ export type TablesRelateOptions = JsonOptions & {
195
+ to?: string;
196
+ targetTable?: string;
197
+ displayName?: string;
198
+ cardinality?: string;
199
+ inverseSlug?: string;
200
+ inverseDisplayName?: string;
201
+ live?: boolean;
202
+ dryRun?: boolean;
203
+ };
204
+ export type TablesRelationUpdateOptions = JsonOptions & {
205
+ displayName?: string;
206
+ inverseDisplayName?: string;
207
+ cardinality?: string;
208
+ approved?: boolean;
209
+ dryRun?: boolean;
210
+ };
211
+ export type TablesRelationArchiveOptions = JsonOptions & {
212
+ approved?: boolean;
213
+ dryRun?: boolean;
214
+ };
215
+ export type TablesUnlinkOptions = JsonOptions & {
216
+ relation: string;
217
+ targetRowId: string;
218
+ approved?: boolean;
219
+ live?: boolean;
220
+ dryRun?: boolean;
221
+ };
222
+ export type TablesLinkOptions = JsonOptions & {
223
+ to?: string;
224
+ on?: string;
225
+ approved?: boolean;
226
+ createMissing?: boolean;
227
+ maxConcurrency?: string;
228
+ undo?: string;
229
+ relation?: string;
230
+ targetRowId?: string;
231
+ live?: boolean;
232
+ dryRun?: boolean;
233
+ };
234
+ export type TablesWebhookCreateOptions = JsonOptions & {
235
+ name?: string;
236
+ source?: string;
237
+ account?: string;
238
+ events?: string;
239
+ mapping?: string;
240
+ mode?: string;
241
+ status?: string;
242
+ authMode?: string;
243
+ upsertKey?: string;
244
+ itemsPath?: string;
245
+ eventIdPath?: string;
246
+ eventTypePath?: string;
247
+ occurredAtPath?: string;
248
+ autoRunColumns?: string;
249
+ autoRunMaxCredits?: string;
250
+ autoRunMaxConcurrency?: string;
251
+ autoRunForce?: boolean;
252
+ autoRunConnectionId?: string;
253
+ autoRunApproved?: boolean;
254
+ clearAutoRun?: boolean;
255
+ table?: string;
256
+ limit?: string;
257
+ autoRunStatus?: string;
258
+ };
259
+ export type TablesAutoRunSetOptions = JsonOptions & {
260
+ columns?: string;
261
+ sources?: string;
262
+ force?: boolean;
263
+ maxCredits?: string;
264
+ maxConcurrency?: string;
265
+ };
266
+ export type TablesAutoDedupeSetOptions = JsonOptions & {
267
+ on: string;
268
+ normalize?: string;
269
+ keep?: string;
270
+ mergeValues?: string;
271
+ };
272
+ export type TablesScheduleSetOptions = JsonOptions & {
273
+ cron?: string;
274
+ every?: string;
275
+ maxCredits?: string;
276
+ approved?: boolean;
277
+ force?: boolean;
278
+ };
279
+ export type TablesPromoteOptions = JsonOptions & {
280
+ object?: string;
281
+ map: string;
282
+ policy?: string;
283
+ rowId?: string[];
284
+ dryRun?: boolean;
285
+ approved?: boolean;
286
+ maxConcurrency?: string;
287
+ };
288
+ export type TablesDedupeOptions = JsonOptions & {
289
+ on: string;
290
+ normalize?: string;
291
+ keep?: string;
292
+ mergeValues?: string;
293
+ scanLimit?: string;
294
+ apply?: boolean;
295
+ approved?: boolean;
296
+ against?: string;
297
+ againstColumn?: string;
298
+ };
299
+ export type TablesSendOptions = JsonOptions & {
300
+ map?: string[];
301
+ mapping?: string;
302
+ automap?: boolean;
303
+ flatten?: string;
304
+ flattenPath?: string;
305
+ filter?: string;
306
+ mode?: string;
307
+ upsertKey?: string;
308
+ pageSize?: string;
309
+ dryRun?: boolean;
310
+ };
311
+ /** Import options once `--file` is known to be present. */
312
+ export type TablesFileImportOptions = TablesImportOptions & {
313
+ file: string;
314
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { TablesRegistrationContext } from "./registration-context.js";
2
+ export declare function registerAutomation({ tablesCommand, handleAsyncAction }: TablesRegistrationContext): void;
@@ -0,0 +1,127 @@
1
+ import { readPositiveInt } from "../../cli-values.js";
2
+ import { readPositiveNumber, readCsvOption, CADENCE_FLAG_DESCRIPTION } from "../../command-support.js";
3
+ import { requestOxygen } from "../../http-client.js";
4
+ import { readOption } from "../../util.js";
5
+ import { Command } from "commander";
6
+ import { buildTablesAutoDedupeSetBody } from "./automation.js";
7
+ export function registerAutomation({ tablesCommand, handleAsyncAction }) {
8
+ tablesCommand.addCommand(new Command("auto-run")
9
+ .description("Inspect and manage a table's standing auto-run configuration (columns queued automatically when new rows are written).")
10
+ .addCommand(new Command("get")
11
+ .description("Show a table's standing auto-run configuration (columns queued automatically when new rows are written).")
12
+ .argument("<table>", "Table id or slug.")
13
+ .option("--json", "Print a JSON envelope.")
14
+ .action((table, options) => handleAsyncAction("tables auto-run get", options, () => {
15
+ const params = new URLSearchParams({ table });
16
+ return requestOxygen(`/api/cli/tables/auto-run?${params.toString()}`);
17
+ })))
18
+ .addCommand(new Command("set")
19
+ .description("Enable a standing auto-run: automatically queue the given columns for rows written to this table (imports, inserts, upserts), including workflow audience upserts and own-follower capture. A queued cell that already has a value is skipped unless --force, so an upsert that touches an existing, filled row is not charged again. Profile viewers use their separate `viewers enrichment --on` subscription. Default off.")
20
+ .argument("<table>", "Table id or slug.")
21
+ .requiredOption("--columns <csv>", "Comma-separated column keys to auto-run on newly written rows (tool, AI, formula, enrichment, bind, or lookup columns).")
22
+ .option("--sources <csv>", "Which write sources trigger the auto-run: any of import, insert, upsert, send. Defaults to all when omitted.")
23
+ .option("--force", "Re-run the columns even for rows that already have values.")
24
+ .option("--max-credits <n>", "Credit ceiling PER enqueued auto-run batch; rows beyond it are skipped with credit_limit_reached. Defaults to your plan's per-delivery ceiling when omitted.")
25
+ .option("--max-concurrency <n>", "Maximum concurrent row items per auto-run batch.")
26
+ .option("--json", "Print a JSON envelope.")
27
+ .action((table, options) => {
28
+ const columns = readCsvOption(options.columns);
29
+ const sources = readCsvOption(options.sources);
30
+ const maxCredits = readPositiveNumber(options.maxCredits);
31
+ const maxConcurrency = readPositiveInt(options.maxConcurrency);
32
+ const force = Boolean(options.force);
33
+ return handleAsyncAction("tables auto-run set", options, () => requestOxygen("/api/cli/tables/auto-run", {
34
+ method: "POST",
35
+ body: {
36
+ table,
37
+ columns,
38
+ ...(sources.length ? { sources } : {}),
39
+ ...(force ? { force: true } : {}),
40
+ ...(maxCredits !== undefined ? { max_credits: maxCredits } : {}),
41
+ ...(maxConcurrency ? { max_concurrency: maxConcurrency } : {}),
42
+ },
43
+ }));
44
+ }))
45
+ .addCommand(new Command("disable")
46
+ .description("Disable (clear) a table's standing auto-run configuration.")
47
+ .argument("<table>", "Table id or slug.")
48
+ .option("--json", "Print a JSON envelope.")
49
+ .action((table, options) => handleAsyncAction("tables auto-run disable", options, () => requestOxygen("/api/cli/tables/auto-run", {
50
+ method: "DELETE",
51
+ body: { table },
52
+ })))));
53
+ tablesCommand.addCommand(new Command("auto-dedupe")
54
+ .description("Inspect and manage a table's standing auto-dedupe configuration (duplicate rows collapsed automatically after every write; deleted rows are kept in row history).")
55
+ .addCommand(new Command("get")
56
+ .description("Show a table's standing auto-dedupe configuration.")
57
+ .argument("<table>", "Table id or slug.")
58
+ .option("--json", "Print a JSON envelope.")
59
+ .action((table, options) => handleAsyncAction("tables auto-dedupe get", options, () => {
60
+ const params = new URLSearchParams({ table });
61
+ return requestOxygen(`/api/cli/tables/auto-dedupe?${params.toString()}`);
62
+ })))
63
+ .addCommand(new Command("set")
64
+ .description("Enable a standing auto-dedupe: after every write (insert, upsert, import, send, webhook), duplicate groups containing a just-written row are collapsed by the keep policy — losers are DELETED automatically without per-write approval (kept in row history), and any standing auto-run only enriches the survivors. Runs BEFORE auto-run so credits are never spent on rows about to be deleted. Fuzzy-label matching is on-demand only: use `oxygen tables dedupe --normalize fuzzy-label` instead.")
65
+ .argument("<table>", "Table id or slug.")
66
+ .requiredOption("--on <cols>", "Key column(s): one column, or two comma-separated for a composite key (e.g. email or first_name,company).")
67
+ .option("--normalize <modes>", "Match mode per --on column, comma-separated positionally: exact (default), email, domain, or linkedin. Unpaired columns default to exact; fuzzy-label is rejected (on-demand only).")
68
+ .option("--keep <policy>", "Survivor per duplicate group (existing rows included): oldest (default), newest, or most-complete.", "oldest")
69
+ .option("--merge-values <mode>", "Fill the survivor's blank cells from the losers before deleting them: fill-empty.")
70
+ .option("--json", "Print a JSON envelope.")
71
+ .action((table, options) => handleAsyncAction("tables auto-dedupe set", options, () => requestOxygen("/api/cli/tables/auto-dedupe", {
72
+ method: "POST",
73
+ body: buildTablesAutoDedupeSetBody(table, options),
74
+ }))))
75
+ .addCommand(new Command("disable")
76
+ .description("Disable (clear) a table's standing auto-dedupe configuration.")
77
+ .argument("<table>", "Table id or slug.")
78
+ .option("--json", "Print a JSON envelope.")
79
+ .action((table, options) => handleAsyncAction("tables auto-dedupe disable", options, () => requestOxygen("/api/cli/tables/auto-dedupe", {
80
+ method: "DELETE",
81
+ body: { table },
82
+ })))));
83
+ tablesCommand.addCommand(new Command("schedule")
84
+ .description("Scheduled column refresh: a cron workflow re-runs one column on a schedule (only empty/failed cells unless --force). One schedule per column.")
85
+ .addCommand(new Command("set")
86
+ .description("Create or replace a column's scheduled refresh (a cron-triggered hosted workflow; visible via `oxygen workflows list`). Paid columns (tool, AI, enrichment) require --max-credits AND --approved; formula columns are free.")
87
+ .argument("<table>", "Table id or slug.")
88
+ .argument("<column>", "Column key or id (tool, AI, formula, or enrichment kind).")
89
+ .option("--cron <expr>", 'Cron expression in UTC, e.g. "0 9 * * *".')
90
+ .option("--every <sugar>", CADENCE_FLAG_DESCRIPTION)
91
+ .option("--max-credits <n>", "Credit ceiling PER scheduled refresh; required with --approved for paid columns.")
92
+ .option("--approved", "Approve the recurring paid runs; required with --max-credits for paid columns.")
93
+ .option("--force", "Re-run cells that already have values on every tick (full refresh instead of fill-missing).")
94
+ .option("--json", "Print a JSON envelope.")
95
+ .action((table, column, options) => {
96
+ const maxCredits = readPositiveNumber(options.maxCredits);
97
+ return handleAsyncAction("tables schedule set", options, () => requestOxygen("/api/cli/tables/schedule", {
98
+ method: "POST",
99
+ body: {
100
+ table,
101
+ column,
102
+ ...(readOption(options.cron) ? { cron: readOption(options.cron) } : {}),
103
+ ...(readOption(options.every) ? { every: readOption(options.every) } : {}),
104
+ ...(maxCredits !== undefined ? { max_credits: maxCredits } : {}),
105
+ ...(options.approved ? { approved: true } : {}),
106
+ ...(options.force ? { force: true } : {}),
107
+ },
108
+ }));
109
+ }))
110
+ .addCommand(new Command("list")
111
+ .description("List a table's scheduled column refreshes.")
112
+ .argument("<table>", "Table id or slug.")
113
+ .option("--json", "Print a JSON envelope.")
114
+ .action((table, options) => handleAsyncAction("tables schedule list", options, () => {
115
+ const params = new URLSearchParams({ table });
116
+ return requestOxygen(`/api/cli/tables/schedule?${params.toString()}`);
117
+ })))
118
+ .addCommand(new Command("remove")
119
+ .description("Remove a column's scheduled refresh (deletes the backing cron workflow; future scheduled runs stop).")
120
+ .argument("<table>", "Table id or slug.")
121
+ .argument("<column>", "Column key or id.")
122
+ .option("--json", "Print a JSON envelope.")
123
+ .action((table, column, options) => handleAsyncAction("tables schedule remove", options, () => requestOxygen("/api/cli/tables/schedule", {
124
+ method: "DELETE",
125
+ body: { table, column },
126
+ })))));
127
+ }
@@ -0,0 +1,2 @@
1
+ import type { TablesRegistrationContext } from "./registration-context.js";
2
+ export declare function registerExternalImports({ tablesCommand, handleAsyncAction }: TablesRegistrationContext): void;