@robono/server 0.24.0 → 0.26.0
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/CHANGELOG.md +8 -0
- package/HISTORY-IMPORT.md +110 -0
- package/HOSTED.md +241 -0
- package/INTEGRATION-ASSISTANT.md +103 -0
- package/README.md +27 -3
- package/SBOM.spdx.json +9 -9
- package/dist/calling-session-handle.d.ts.map +1 -1
- package/dist/calling-session-handle.js +1 -0
- package/dist/calling-session-handle.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/client.js.map +1 -1
- package/dist/generated/custom-messages.d.ts +31 -0
- package/dist/generated/custom-messages.d.ts.map +1 -0
- package/dist/generated/custom-messages.js +84 -0
- package/dist/generated/custom-messages.js.map +1 -0
- package/dist/generated/sdk-versions.d.ts +13 -0
- package/dist/generated/sdk-versions.d.ts.map +1 -0
- package/dist/generated/sdk-versions.js +4 -0
- package/dist/generated/sdk-versions.js.map +1 -0
- package/dist/history-export-cli.d.ts +4 -0
- package/dist/history-export-cli.d.ts.map +1 -0
- package/dist/history-export-cli.js +166 -0
- package/dist/history-export-cli.js.map +1 -0
- package/dist/history-export-files.d.ts +39 -0
- package/dist/history-export-files.d.ts.map +1 -0
- package/dist/history-export-files.js +119 -0
- package/dist/history-export-files.js.map +1 -0
- package/dist/history-export.d.ts +92 -0
- package/dist/history-export.d.ts.map +1 -0
- package/dist/history-export.js +78 -0
- package/dist/history-export.js.map +1 -0
- package/dist/history-import-cli.d.ts +11 -0
- package/dist/history-import-cli.d.ts.map +1 -0
- package/dist/history-import-cli.js +94 -0
- package/dist/history-import-cli.js.map +1 -0
- package/dist/history-import.d.ts +66 -0
- package/dist/history-import.d.ts.map +1 -0
- package/dist/history-import.js +69 -0
- package/dist/history-import.js.map +1 -0
- package/dist/hosted-sessions.d.ts +38 -0
- package/dist/hosted-sessions.d.ts.map +1 -0
- package/dist/hosted-sessions.js +68 -0
- package/dist/hosted-sessions.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-assistant.txt +103 -0
- package/dist/managed-media.d.ts +2 -1
- package/dist/managed-media.d.ts.map +1 -1
- package/dist/managed-media.js +1 -1
- package/dist/managed-media.js.map +1 -1
- package/dist/messaging-notification-providers.d.ts +2 -3
- package/dist/messaging-notification-providers.d.ts.map +1 -1
- package/dist/messaging-notification-providers.js +1 -0
- package/dist/messaging-notification-providers.js.map +1 -1
- package/dist/messaging-push-worker.d.ts.map +1 -1
- package/dist/messaging-push-worker.js +3 -2
- package/dist/messaging-push-worker.js.map +1 -1
- package/dist/messaging.d.ts +5 -0
- package/dist/messaging.d.ts.map +1 -1
- package/dist/messaging.js +11 -2
- package/dist/messaging.js.map +1 -1
- package/dist/messenger-check.d.ts +1 -0
- package/dist/messenger-check.d.ts.map +1 -1
- package/dist/messenger-check.js +7 -5
- package/dist/messenger-check.js.map +1 -1
- package/dist/messenger-connected-templates.d.ts.map +1 -1
- package/dist/messenger-connected-templates.js +9 -6
- package/dist/messenger-connected-templates.js.map +1 -1
- package/dist/messenger-example-cli.d.ts +3 -0
- package/dist/messenger-example-cli.d.ts.map +1 -0
- package/dist/messenger-example-cli.js +47 -0
- package/dist/messenger-example-cli.js.map +1 -0
- package/dist/messenger-example.d.ts +9 -0
- package/dist/messenger-example.d.ts.map +1 -0
- package/dist/messenger-example.js +42 -0
- package/dist/messenger-example.js.map +1 -0
- package/dist/messenger-example.json +1 -0
- package/dist/messenger-framework-templates.d.ts +3 -0
- package/dist/messenger-framework-templates.d.ts.map +1 -0
- package/dist/messenger-framework-templates.js +68 -0
- package/dist/messenger-framework-templates.js.map +1 -0
- package/dist/messenger-hosted-install.d.ts +3 -0
- package/dist/messenger-hosted-install.d.ts.map +1 -0
- package/dist/messenger-hosted-install.js +109 -0
- package/dist/messenger-hosted-install.js.map +1 -0
- package/dist/messenger-hosted-templates.d.ts +6 -0
- package/dist/messenger-hosted-templates.d.ts.map +1 -0
- package/dist/messenger-hosted-templates.js +84 -0
- package/dist/messenger-hosted-templates.js.map +1 -0
- package/dist/messenger-install-cli.js +10 -7
- package/dist/messenger-install-cli.js.map +1 -1
- package/dist/messenger-install.d.ts +24 -1
- package/dist/messenger-install.d.ts.map +1 -1
- package/dist/messenger-install.js +24 -9
- package/dist/messenger-install.js.map +1 -1
- package/dist/messenger-node.d.ts.map +1 -1
- package/dist/messenger-node.js +1 -0
- package/dist/messenger-node.js.map +1 -1
- package/dist/messenger-preview-template.d.ts.map +1 -1
- package/dist/messenger-preview-template.js +1 -0
- package/dist/messenger-preview-template.js.map +1 -1
- package/dist/messenger-upgrade.js +1 -1
- package/dist/messenger-upgrade.js.map +1 -1
- package/package.json +10 -4
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { open, lstat, mkdir, link, unlink } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
+
export async function exportPageFiles(exporter, jobId, folder, records, policy, fetcher = fetch) {
|
|
6
|
+
const files = join(folder, 'attachments');
|
|
7
|
+
await mkdir(files, { mode: 0o700 }).catch(e => { if (e.code !== 'EEXIST')
|
|
8
|
+
throw e; });
|
|
9
|
+
const dir = await lstat(files);
|
|
10
|
+
if (!dir.isDirectory() || dir.isSymbolicLink() || process.platform !== 'win32' && (dir.mode & 0o077) !== 0)
|
|
11
|
+
throw Error('Use a private attachments directory without symbolic links.');
|
|
12
|
+
const output = [];
|
|
13
|
+
let bytes = policy.bytes;
|
|
14
|
+
for (const record of records) {
|
|
15
|
+
if (record.type !== 'message' || !record.message.media) {
|
|
16
|
+
output.push(record);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const media = record.message.media;
|
|
20
|
+
if (typeof media.assetId !== 'string' || !/^robono-r2:[0-9a-f-]{36}$/i.test(media.assetId)) {
|
|
21
|
+
output.push({ ...record, attachment: { status: 'customer_managed' } });
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const grant = await exporter.attachment({ jobId, conversationId: record.message.conversationId, messageId: record.message.id, assetId: media.assetId });
|
|
25
|
+
if (!grant.available) {
|
|
26
|
+
output.push({ ...record, attachment: { status: grant.reason } });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (bytes + grant.media.byteSize > policy.maxBytes)
|
|
30
|
+
throw Error('The attachment export reached its byte limit. Resume with a larger --max-attachment-bytes value, or start a data-only export.');
|
|
31
|
+
const name = media.assetId.slice(10).toLowerCase() + '.bin', destination = join(files, name), temporary = join(files, '.part-' + randomUUID());
|
|
32
|
+
const file = await open(temporary, 'wx', 0o600);
|
|
33
|
+
let size = 0;
|
|
34
|
+
const digest = createHash('sha256');
|
|
35
|
+
try {
|
|
36
|
+
// The URL comes only from Robono's authorized grant, never from message text.
|
|
37
|
+
// No API key or customer's authentication header is sent to storage.
|
|
38
|
+
const response = await fetcher(grant.url, { method: 'GET', redirect: 'error', signal: AbortSignal.timeout(120000) });
|
|
39
|
+
if (!response.ok || !response.body) {
|
|
40
|
+
await response.body?.cancel();
|
|
41
|
+
throw Error('An attachment download was interrupted. Resume to request a fresh download grant.');
|
|
42
|
+
}
|
|
43
|
+
const reader = response.body.getReader();
|
|
44
|
+
try {
|
|
45
|
+
while (true) {
|
|
46
|
+
const part = await reader.read();
|
|
47
|
+
if (part.done)
|
|
48
|
+
break;
|
|
49
|
+
size += part.value.byteLength;
|
|
50
|
+
if (size > grant.media.byteSize)
|
|
51
|
+
throw Error('An attachment exceeded its verified size.');
|
|
52
|
+
digest.update(part.value);
|
|
53
|
+
let offset = 0;
|
|
54
|
+
while (offset < part.value.length) {
|
|
55
|
+
const written = await file.write(part.value, offset, part.value.length - offset);
|
|
56
|
+
if (!written.bytesWritten)
|
|
57
|
+
throw Error('Could not save an attachment.');
|
|
58
|
+
offset += written.bytesWritten;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await reader.cancel().catch(() => { });
|
|
64
|
+
reader.releaseLock();
|
|
65
|
+
}
|
|
66
|
+
if (size !== grant.media.byteSize)
|
|
67
|
+
throw Error('An attachment was incomplete. Resume to download it again.');
|
|
68
|
+
const sha256 = digest.digest('hex');
|
|
69
|
+
await file.sync();
|
|
70
|
+
try {
|
|
71
|
+
await link(temporary, destination);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
if (e.code !== 'EEXIST')
|
|
75
|
+
throw e;
|
|
76
|
+
// A crash may leave a complete file before its page checkpoint. Reuse only
|
|
77
|
+
// an identical regular file; never overwrite somebody else's file or link.
|
|
78
|
+
if ((await lstat(destination)).isSymbolicLink())
|
|
79
|
+
throw Error('An attachment destination cannot be a symbolic link.');
|
|
80
|
+
const old = await open(destination, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
81
|
+
try {
|
|
82
|
+
const info = await old.stat();
|
|
83
|
+
if (!info.isFile() || info.nlink !== 1 || info.size !== size || process.platform !== 'win32' && (info.mode & 0o077) !== 0)
|
|
84
|
+
throw Error('An existing attachment file does not match this export.');
|
|
85
|
+
const hash = createHash('sha256'), buffer = Buffer.alloc(65536);
|
|
86
|
+
while (true) {
|
|
87
|
+
const r = await old.read(buffer, 0, buffer.length, null);
|
|
88
|
+
if (!r.bytesRead)
|
|
89
|
+
break;
|
|
90
|
+
hash.update(buffer.subarray(0, r.bytesRead));
|
|
91
|
+
}
|
|
92
|
+
if (hash.digest('hex') !== sha256)
|
|
93
|
+
throw Error('An existing attachment file was changed. Move it aside before resuming.');
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await old.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
bytes += size;
|
|
100
|
+
output.push({ ...record, attachment: { status: 'saved', path: 'attachments/' + name, byteSize: size, sha256, fileName: grant.media.fileName, mimeType: grant.media.mimeType } });
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
await file.close();
|
|
104
|
+
await unlink(temporary).catch(e => { if (e.code !== 'ENOENT')
|
|
105
|
+
throw e; });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (process.platform !== 'win32') {
|
|
109
|
+
const dir = await open(files, 'r');
|
|
110
|
+
try {
|
|
111
|
+
await dir.sync();
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
await dir.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { records: output, bytes };
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=history-export-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-export-files.js","sourceRoot":"","sources":["../src/history-export-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface HistoryExportCursor {
|
|
2
|
+
conversationIndex: number;
|
|
3
|
+
phase: 'conversation' | 'members' | 'messages';
|
|
4
|
+
after: string;
|
|
5
|
+
}
|
|
6
|
+
export interface HistoryExportSelection {
|
|
7
|
+
jobId: string;
|
|
8
|
+
conversationIds: string[];
|
|
9
|
+
from?: string;
|
|
10
|
+
until?: string;
|
|
11
|
+
}
|
|
12
|
+
export type HistoryExportRecord = {
|
|
13
|
+
type: 'conversation';
|
|
14
|
+
id: string;
|
|
15
|
+
group: boolean;
|
|
16
|
+
supergroup: boolean;
|
|
17
|
+
name: string | null;
|
|
18
|
+
externalGroupId: string | null;
|
|
19
|
+
allowed: boolean;
|
|
20
|
+
revision: string;
|
|
21
|
+
ownerId: string | null;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'member';
|
|
24
|
+
conversationId: string;
|
|
25
|
+
userId: string;
|
|
26
|
+
canSend: boolean;
|
|
27
|
+
features?: Record<string, boolean>;
|
|
28
|
+
joinedAfterId?: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'message';
|
|
31
|
+
message: Record<string, unknown> & {
|
|
32
|
+
id: string;
|
|
33
|
+
conversationId: string;
|
|
34
|
+
senderId: string;
|
|
35
|
+
kind: string;
|
|
36
|
+
};
|
|
37
|
+
pin: {
|
|
38
|
+
pinnedBy: string;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
} | null;
|
|
41
|
+
};
|
|
42
|
+
export type HistoryExportAttachment = {
|
|
43
|
+
available: false;
|
|
44
|
+
reason: 'removed_or_changed' | 'expired_or_unavailable';
|
|
45
|
+
} | {
|
|
46
|
+
available: true;
|
|
47
|
+
url: string;
|
|
48
|
+
media: {
|
|
49
|
+
assetId: string;
|
|
50
|
+
byteSize: number;
|
|
51
|
+
fileName: string;
|
|
52
|
+
mimeType: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export interface HistoryExportStatus {
|
|
56
|
+
jobId: string;
|
|
57
|
+
state: 'ready' | 'complete' | 'cancelled';
|
|
58
|
+
createdAt: string;
|
|
59
|
+
expiresAt: string;
|
|
60
|
+
format: 'robono-history-v1';
|
|
61
|
+
}
|
|
62
|
+
export interface HistoryExportPage extends HistoryExportStatus {
|
|
63
|
+
records: HistoryExportRecord[];
|
|
64
|
+
nextCursor: HistoryExportCursor | null;
|
|
65
|
+
done: boolean;
|
|
66
|
+
}
|
|
67
|
+
export declare function validExportCursor(c: unknown): c is HistoryExportCursor;
|
|
68
|
+
/** Server-only export. Contains private message data; never expose the manage key in a browser. */
|
|
69
|
+
export declare class RobonoHistoryExport {
|
|
70
|
+
private options;
|
|
71
|
+
private endpoint;
|
|
72
|
+
constructor(options: {
|
|
73
|
+
apiKey: string;
|
|
74
|
+
baseUrl?: string;
|
|
75
|
+
fetch?: typeof fetch;
|
|
76
|
+
});
|
|
77
|
+
private request;
|
|
78
|
+
start(input: HistoryExportSelection): Promise<HistoryExportStatus>;
|
|
79
|
+
status(jobId: string): Promise<HistoryExportStatus>;
|
|
80
|
+
cancel(jobId: string): Promise<HistoryExportStatus>;
|
|
81
|
+
page(jobId: string, cursor?: HistoryExportCursor): Promise<HistoryExportPage>;
|
|
82
|
+
/** A fresh grant for a retained managed attachment in this export job. Never returns customer-owned URLs. */
|
|
83
|
+
attachment(input: {
|
|
84
|
+
jobId: string;
|
|
85
|
+
conversationId: string;
|
|
86
|
+
messageId: string;
|
|
87
|
+
assetId: string;
|
|
88
|
+
}): Promise<HistoryExportAttachment>;
|
|
89
|
+
/** Yields one bounded page at a time. Persist nextCursor only after saving that page. */
|
|
90
|
+
pages(jobId: string, cursor?: HistoryExportCursor): AsyncGenerator<HistoryExportPage>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=history-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-export.d.ts","sourceRoot":"","sources":["../src/history-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RobonoError } from './errors.js';
|
|
2
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
3
|
+
export function validExportCursor(c) {
|
|
4
|
+
if (!c || typeof c !== 'object')
|
|
5
|
+
return false;
|
|
6
|
+
const v = c;
|
|
7
|
+
return Number.isInteger(v.conversationIndex) && v.conversationIndex >= 0 && v.conversationIndex < 25 && ['conversation', 'members', 'messages'].includes(v.phase) && typeof v.after === 'string' && v.after.length <= 200 && !/[\u0000-\u001f\u007f]/.test(v.after) && (v.phase !== 'messages' || /^[0-9]{1,19}$/.test(v.after) && BigInt(v.after) <= 9223372036854775807n);
|
|
8
|
+
}
|
|
9
|
+
/** Server-only export. Contains private message data; never expose the manage key in a browser. */
|
|
10
|
+
export class RobonoHistoryExport {
|
|
11
|
+
options;
|
|
12
|
+
endpoint;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
const url = new URL(options.baseUrl ?? 'https://api.robono.com/v1/messaging/export');
|
|
16
|
+
if (url.protocol !== 'https:' || url.username || url.password || !options.apiKey.trim())
|
|
17
|
+
throw Error('Use an HTTPS export endpoint and a server API key.');
|
|
18
|
+
this.endpoint = url.href;
|
|
19
|
+
}
|
|
20
|
+
async request(operation, body) {
|
|
21
|
+
if (!uuid.test(body.jobId))
|
|
22
|
+
throw Error('Use a UUID job ID, and keep it unchanged when retrying.');
|
|
23
|
+
const response = await (this.options.fetch ?? fetch)(this.endpoint, { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(120000), headers: { authorization: 'Bearer ' + this.options.apiKey, 'content-type': 'application/json' }, body: JSON.stringify({ ...body, operation }) });
|
|
24
|
+
const data = await response.json().catch(() => null);
|
|
25
|
+
if (!response.ok)
|
|
26
|
+
throw new RobonoError(data?.error?.message ?? 'History export failed.', { status: response.status, code: data?.error?.code ?? 'history_export_failed', retryable: response.status === 429 || response.status >= 500 });
|
|
27
|
+
if (data?.jobId !== body.jobId || data.format !== 'robono-history-v1' || !['ready', 'complete', 'cancelled'].includes(data.state) || !Number.isFinite(Date.parse(data.createdAt)) || !Number.isFinite(Date.parse(data.expiresAt)))
|
|
28
|
+
throw Error('The export service returned an unexpected job.');
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
start(input) {
|
|
32
|
+
if (!Array.isArray(input.conversationIds) || input.conversationIds.length < 1 || input.conversationIds.length > 25 || input.conversationIds.some(id => typeof id !== 'string' || !uuid.test(id)))
|
|
33
|
+
throw Error('Select 1–25 conversation IDs.');
|
|
34
|
+
for (const date of [input.from, input.until])
|
|
35
|
+
if (date !== undefined && (!Number.isFinite(Date.parse(date)) || Date.parse(date) < 0 || Date.parse(date) > Date.now()))
|
|
36
|
+
throw Error('Use valid past export dates.');
|
|
37
|
+
if (input.from && input.until && Date.parse(input.from) >= Date.parse(input.until))
|
|
38
|
+
throw Error('The start date must precede the end date.');
|
|
39
|
+
return this.request('start', { ...input, conversationIds: [...new Set(input.conversationIds.map(id => id.toLowerCase()))].sort() });
|
|
40
|
+
}
|
|
41
|
+
status(jobId) { return this.request('status', { jobId }); }
|
|
42
|
+
cancel(jobId) { return this.request('cancel', { jobId }); }
|
|
43
|
+
async page(jobId, cursor) {
|
|
44
|
+
if (cursor !== undefined && !validExportCursor(cursor))
|
|
45
|
+
throw Error('Keep the returned export cursor unchanged.');
|
|
46
|
+
const data = await this.request('page', { jobId, ...(cursor ? { cursor } : {}) });
|
|
47
|
+
if (!Array.isArray(data.records) || data.records.length > 200 || typeof data.done !== 'boolean' || (data.done ? data.nextCursor !== null : !validExportCursor(data.nextCursor)) || data.records.some(r => !r || !['conversation', 'member', 'message'].includes(r.type)))
|
|
48
|
+
throw Error('The export service returned an invalid page.');
|
|
49
|
+
if (!data.done && JSON.stringify(data.nextCursor) === JSON.stringify(cursor))
|
|
50
|
+
throw Error('The export cursor did not advance.');
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
/** A fresh grant for a retained managed attachment in this export job. Never returns customer-owned URLs. */
|
|
54
|
+
async attachment(input) {
|
|
55
|
+
if (!uuid.test(input.conversationId) || !/^[1-9][0-9]{0,18}$/.test(input.messageId) || BigInt(input.messageId) > 9223372036854775807n || !input.assetId.startsWith('robono-r2:') || !uuid.test(input.assetId.slice(10)))
|
|
56
|
+
throw Error('Use the attachment identifiers from this export.');
|
|
57
|
+
const data = await this.request('asset', input);
|
|
58
|
+
if (data.available === false && ['removed_or_changed', 'expired_or_unavailable'].includes(data.reason))
|
|
59
|
+
return data;
|
|
60
|
+
if (data.available !== true || data.media?.assetId !== input.assetId || !Number.isSafeInteger(data.media.byteSize) || data.media.byteSize < 1 || data.media.byteSize > 104857600 || typeof data.media.fileName !== 'string' || typeof data.media.mimeType !== 'string')
|
|
61
|
+
throw Error('The export service returned an invalid attachment.');
|
|
62
|
+
const url = new URL(data.url);
|
|
63
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.hash)
|
|
64
|
+
throw Error('The export service returned an invalid transfer.');
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
67
|
+
/** Yields one bounded page at a time. Persist nextCursor only after saving that page. */
|
|
68
|
+
async *pages(jobId, cursor) {
|
|
69
|
+
while (true) {
|
|
70
|
+
const page = await this.page(jobId, cursor);
|
|
71
|
+
yield page;
|
|
72
|
+
if (page.done)
|
|
73
|
+
return;
|
|
74
|
+
cursor = page.nextCursor;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=history-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-export.js","sourceRoot":"","sources":["../src/history-export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAUxC,MAAM,IAAI,GAAC,4EAA4E,CAAC;AACxF,MAAM,UAAU,iBAAiB,CAAC,CAAS;IAC1C,IAAG,CAAC,CAAC,IAAE,OAAO,CAAC,KAAG,QAAQ;QAAC,OAAO,KAAK,CAAC;IAAA,MAAM,CAAC,GAAC,CAAwB,CAAC;IACzE,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAE,CAAC,CAAC,iBAAiB,IAAE,CAAC,IAAE,CAAC,CAAC,iBAAiB,GAAC,EAAE,IAAE,CAAC,cAAc,EAAC,SAAS,EAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAE,OAAO,CAAC,CAAC,KAAK,KAAG,QAAQ,IAAE,CAAC,CAAC,KAAK,CAAC,MAAM,IAAE,GAAG,IAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAE,CAAC,CAAC,CAAC,KAAK,KAAG,UAAU,IAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAE,oBAAoB,CAAC,CAAC;AAC7U,CAAC;AACD,mGAAmG;AACnG,MAAM,OAAO,mBAAmB;IAEX;IADZ,QAAQ,CAAQ;IACxB,YAAoB,OAA2D;QAA3D,YAAO,GAAP,OAAO,CAAoD;QAC9E,MAAM,GAAG,GAAC,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,IAAE,4CAA4C,CAAC,CAAC;QACjF,IAAG,GAAG,CAAC,QAAQ,KAAG,QAAQ,IAAE,GAAG,CAAC,QAAQ,IAAE,GAAG,CAAC,QAAQ,IAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;YAAC,MAAM,KAAK,CAAC,oDAAoD,CAAC,CAAC;QAAA,IAAI,CAAC,QAAQ,GAAC,GAAG,CAAC,IAAI,CAAC;IACzK,CAAC;IACO,KAAK,CAAC,OAAO,CAAC,SAAgB,EAAC,IAAwC;QAC9E,IAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YAAC,MAAM,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACjG,MAAM,QAAQ,GAAC,MAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAE,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAC,EAAC,MAAM,EAAC,MAAM,EAAC,QAAQ,EAAC,OAAO,EAAC,MAAM,EAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,EAAC,aAAa,EAAC,SAAS,GAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAC,cAAc,EAAC,kBAAkB,EAAC,EAAC,IAAI,EAAC,IAAI,CAAC,SAAS,CAAC,EAAC,GAAG,IAAI,EAAC,SAAS,EAAC,CAAC,EAAC,CAAC,CAAC;QACpQ,MAAM,IAAI,GAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAE,EAAE,CAAA,IAAI,CAAC,CAAC;QACjD,IAAG,CAAC,QAAQ,CAAC,EAAE;YAAC,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,IAAE,wBAAwB,EAAC,EAAC,MAAM,EAAC,QAAQ,CAAC,MAAM,EAAC,IAAI,EAAC,IAAI,EAAE,KAAK,EAAE,IAAI,IAAE,uBAAuB,EAAC,SAAS,EAAC,QAAQ,CAAC,MAAM,KAAG,GAAG,IAAE,QAAQ,CAAC,MAAM,IAAE,GAAG,EAAC,CAAC,CAAC;QACrN,IAAG,IAAI,EAAE,KAAK,KAAG,IAAI,CAAC,KAAK,IAAE,IAAI,CAAC,MAAM,KAAG,mBAAmB,IAAE,CAAC,CAAC,OAAO,EAAC,UAAU,EAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAAA,OAAO,IAAI,CAAC;IAC9R,CAAC;IACD,KAAK,CAAC,KAA4B;QACjC,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,IAAE,KAAK,CAAC,eAAe,CAAC,MAAM,GAAC,CAAC,IAAE,KAAK,CAAC,eAAe,CAAC,MAAM,GAAC,EAAE,IAAE,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAA,EAAE,CAAA,OAAO,EAAE,KAAG,QAAQ,IAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC7N,KAAI,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAC,KAAK,CAAC,KAAK,CAAC;YAAC,IAAG,IAAI,KAAG,SAAS,IAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAC,CAAC,IAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBAAC,MAAM,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClM,IAAG,KAAK,CAAC,IAAI,IAAE,KAAK,CAAC,KAAK,IAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;YAAC,MAAM,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACrI,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAC,EAAC,GAAG,KAAK,EAAC,eAAe,EAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAA,EAAE,CAAA,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAC,CAAC,CAAC;IAC9H,CAAC;IACD,MAAM,CAAC,KAAY,IAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAC,EAAC,KAAK,EAAC,CAAC,CAAC,CAAA,CAAC;IAC5D,MAAM,CAAC,KAAY,IAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAC,EAAC,KAAK,EAAC,CAAC,CAAC,CAAA,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,KAAY,EAAC,MAA2B;QAClD,IAAG,MAAM,KAAG,SAAS,IAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;YAAC,MAAM,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC5G,MAAM,IAAI,GAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAC,EAAC,KAAK,EAAC,GAAG,CAAC,MAAM,CAAA,CAAC,CAAA,EAAC,MAAM,EAAC,CAAA,CAAC,CAAA,EAAE,CAAC,EAAC,CAAsB,CAAC;QAC3F,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAC,GAAG,IAAE,OAAO,IAAI,CAAC,IAAI,KAAG,SAAS,IAAE,CAAC,IAAI,CAAC,IAAI,CAAA,CAAC,CAAA,IAAI,CAAC,UAAU,KAAG,IAAI,CAAA,CAAC,CAAA,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,IAAE,CAAC,CAAC,cAAc,EAAC,QAAQ,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC5S,IAAG,CAAC,IAAI,CAAC,IAAI,IAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,KAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;YAAC,MAAM,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC1H,OAAO,IAAI,CAAC;IACb,CAAC;IACD,6GAA6G;IAC7G,KAAK,CAAC,UAAU,CAAC,KAA0E;QAC1F,IAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAC,oBAAoB,IAAE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,IAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC7Q,MAAM,IAAI,GAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAC,KAAK,CAAuC,CAAC;QACnF,IAAG,IAAI,CAAC,SAAS,KAAG,KAAK,IAAE,CAAC,oBAAoB,EAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAC7G,IAAG,IAAI,CAAC,SAAS,KAAG,IAAI,IAAE,IAAI,CAAC,KAAK,EAAE,OAAO,KAAG,KAAK,CAAC,OAAO,IAAE,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAC,CAAC,IAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAC,SAAS,IAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAG,QAAQ,IAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAG,QAAQ;YAAC,MAAM,KAAK,CAAC,oDAAoD,CAAC,CAAC;QAChT,MAAM,GAAG,GAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAA,IAAG,GAAG,CAAC,QAAQ,KAAG,QAAQ,IAAE,GAAG,CAAC,QAAQ,IAAE,GAAG,CAAC,QAAQ,IAAE,GAAG,CAAC,IAAI;YAAC,MAAM,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC7J,OAAO,IAAI,CAAC;IACb,CAAC;IACD,yFAAyF;IACzF,KAAK,CAAC,CAAC,KAAK,CAAC,KAAY,EAAC,MAA2B;QACpD,OAAM,IAAI,EAAC,CAAC;YAAA,MAAM,IAAI,GAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAC,MAAM,CAAC,CAAC;YAAA,MAAM,IAAI,CAAC;YAAA,IAAG,IAAI,CAAC,IAAI;gBAAC,OAAO;YAAA,MAAM,GAAC,IAAI,CAAC,UAAW,CAAC;QAAA,CAAC;IAC9G,CAAC;CACD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type HistoryImportMessage } from './history-import.js';
|
|
3
|
+
/** Private JSON export: never print message bodies or keys. Validation does not contact Robono. */
|
|
4
|
+
export declare function runHistoryImport(args: string[], env?: NodeJS.ProcessEnv, log?: (line: string) => void): Promise<void>;
|
|
5
|
+
export declare function validateHistoryImportFile(value: any): asserts value is {
|
|
6
|
+
jobId: string;
|
|
7
|
+
conversationId: string;
|
|
8
|
+
authors: string[];
|
|
9
|
+
messages: HistoryImportMessage[];
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=history-import-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-import-cli.d.ts","sourceRoot":"","sources":["../src/history-import-cli.ts"],"names":[],"mappings":";AAGA,OAAO,EAAqB,KAAK,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAGlF,mGAAmG;AACnG,wBAAsB,gBAAgB,CAAC,IAAI,EAAC,MAAM,EAAE,EAAC,GAAG,GAAC,MAAM,CAAC,UAAsB,EAAC,GAAG,GAAC,CAAC,IAAI,EAAC,MAAM,KAAG,IAAgB,iBAkBzH;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAC,GAAG,GAAE,OAAO,CAAC,KAAK,IAAI;IAAC,KAAK,EAAC,MAAM,CAAC;IAAA,cAAc,EAAC,MAAM,CAAC;IAAA,OAAO,EAAC,MAAM,EAAE,CAAC;IAAA,QAAQ,EAAC,oBAAoB,EAAE,CAAA;CAAC,CAqB1J"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { RobonoHistoryImport } from './history-import.js';
|
|
5
|
+
import { RobonoError } from './errors.js';
|
|
6
|
+
import { validateCustomMessage, customMessageScreeningText } from './generated/custom-messages.js';
|
|
7
|
+
/** Private JSON export: never print message bodies or keys. Validation does not contact Robono. */
|
|
8
|
+
export async function runHistoryImport(args, env = process.env, log = console.log) {
|
|
9
|
+
const flags = new Set(args.filter(a => a === '--apply' || a === '--help'));
|
|
10
|
+
if (flags.has('--help')) {
|
|
11
|
+
log('robono-history-import --file history.json [--apply]\nWithout --apply, validates locally. To import, set ROBONO_API_KEY on your server. Keep jobId and sourceId unchanged when retrying.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const rest = args.filter(a => !flags.has(a));
|
|
15
|
+
if (rest.length !== 2 || rest[0] !== '--file' || !rest[1])
|
|
16
|
+
throw Error('Use --file history.json, with --apply when ready.');
|
|
17
|
+
const info = await stat(rest[1]);
|
|
18
|
+
if (!info.isFile() || info.size > 33554432)
|
|
19
|
+
throw Error('Use a JSON file no larger than 32 MiB.');
|
|
20
|
+
let input;
|
|
21
|
+
try {
|
|
22
|
+
input = JSON.parse(await readFile(rest[1], 'utf8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw Error('The import file must contain valid JSON.');
|
|
26
|
+
}
|
|
27
|
+
validateHistoryImportFile(input);
|
|
28
|
+
log(`Validated ${input.messages.length} messages from ${input.authors.length} authors.`);
|
|
29
|
+
if (!flags.has('--apply')) {
|
|
30
|
+
log('Nothing sent. Add --apply to import into the authorized empty conversation.');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const apiKey = env.ROBONO_API_KEY;
|
|
34
|
+
if (!apiKey)
|
|
35
|
+
throw Error('Set ROBONO_API_KEY in your server environment. Do not put it in the JSON file.');
|
|
36
|
+
const importer = new RobonoHistoryImport({ apiKey });
|
|
37
|
+
for (let attempt = 0;; attempt++) {
|
|
38
|
+
try {
|
|
39
|
+
const result = await importer.importMessages({ ...input, onProgress: (done, total) => log(`Prepared ${done}/${total}.`) });
|
|
40
|
+
log(`Import ${result.state}: ${result.importedCount ?? input.messages.length} messages.`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (attempt >= 4 || !(error instanceof TypeError || error instanceof RobonoError && error.retryable))
|
|
45
|
+
throw error;
|
|
46
|
+
log('Connection interrupted. Retrying the same import safely.');
|
|
47
|
+
await new Promise(r => setTimeout(r, Math.min(15000, 2000 * 2 ** attempt)));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function validateHistoryImportFile(value) {
|
|
52
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
53
|
+
const id = (v) => typeof v === 'string' && v.length > 0 && v.length <= 200 && v === v.trim() && !/[\u0000-\u001f\u007f]/.test(v);
|
|
54
|
+
if (!value || !uuid.test(value.jobId) || !uuid.test(value.conversationId) || !Array.isArray(value.authors) || !value.authors.length || value.authors.length > 10000 || value.authors.some((a) => !id(a)) || new Set(value.authors).size !== value.authors.length)
|
|
55
|
+
throw Error('Provide UUID jobId and conversationId and a unique authors list.');
|
|
56
|
+
if (!Array.isArray(value.messages) || !value.messages.length || value.messages.length > 10000)
|
|
57
|
+
throw Error('Include 1–10,000 messages.');
|
|
58
|
+
const ids = new Map(), order = new Map();
|
|
59
|
+
for (const [index, message] of value.messages.entries()) {
|
|
60
|
+
const fail = (reason) => { throw Error(`Message ${index + 1}: ${reason}`); };
|
|
61
|
+
if (!message || !id(message.sourceId) || ids.has(message.sourceId) || !value.authors.includes(message.authorId))
|
|
62
|
+
fail('use a unique sourceId and an author from the authors list.');
|
|
63
|
+
const sent = Date.parse(message.sentAt);
|
|
64
|
+
if (typeof message.sentAt !== 'string' || !Number.isFinite(sent) || sent < 0 || sent > Date.now())
|
|
65
|
+
fail('sentAt must be a valid past date.');
|
|
66
|
+
const c = message.content;
|
|
67
|
+
if (!c || !['text', 'image', 'video', 'voice', 'document'].includes(c.kind) || Object.keys(c).some(k => !['kind', 'text', 'media', 'custom'].includes(k)))
|
|
68
|
+
fail('provide supported content only.');
|
|
69
|
+
if (c.kind === 'text' && (typeof c.text !== 'string' || !c.text.trim() || c.text.length > 10000 || c.media))
|
|
70
|
+
fail('text must contain 1–10,000 characters.');
|
|
71
|
+
if (c.custom !== undefined) {
|
|
72
|
+
if (c.kind !== 'text')
|
|
73
|
+
fail('custom messages require a text fallback.');
|
|
74
|
+
try {
|
|
75
|
+
customMessageScreeningText(c.text, validateCustomMessage(c.custom));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
fail('custom message data or fallback is invalid.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (c.kind !== 'text' && (!c.media || typeof c.media.assetId !== 'string' || !c.media.assetId.startsWith('robono-r2:')))
|
|
82
|
+
fail('upload attachments privately through importer.upload first. Public source URLs are not accepted.');
|
|
83
|
+
if (message.replyToSourceId !== undefined && !id(message.replyToSourceId))
|
|
84
|
+
fail('replyToSourceId must be an original source ID.');
|
|
85
|
+
ids.set(message.sourceId, sent);
|
|
86
|
+
order.set(message.sourceId, index);
|
|
87
|
+
}
|
|
88
|
+
for (const [index, message] of value.messages.entries())
|
|
89
|
+
if (message.replyToSourceId !== undefined && (!ids.has(message.replyToSourceId) || ids.get(message.replyToSourceId) > Date.parse(message.sentAt) || message.replyToSourceId === message.sourceId || ids.get(message.replyToSourceId) === Date.parse(message.sentAt) && order.get(message.replyToSourceId) >= index))
|
|
90
|
+
throw Error(`Message ${index + 1}: the original reply target must exist and precede this message.`);
|
|
91
|
+
}
|
|
92
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
93
|
+
runHistoryImport(process.argv.slice(2)).catch(error => { console.error(error instanceof Error ? error.message : 'History import failed.'); process.exitCode = 1; });
|
|
94
|
+
//# sourceMappingURL=history-import-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-import-cli.js","sourceRoot":"","sources":["../src/history-import-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { RobonoManagedMedia } from './managed-media.js';
|
|
2
|
+
import type { MessagingStoredMedia, MessagingUploadFile, MessagingMediaKind } from './messaging-media.js';
|
|
3
|
+
import type { CustomMessageContent } from './generated/custom-messages.js';
|
|
4
|
+
export interface HistoryImportMessage {
|
|
5
|
+
sourceId: string;
|
|
6
|
+
authorId: string;
|
|
7
|
+
sentAt: string;
|
|
8
|
+
content: {
|
|
9
|
+
kind: 'text' | 'voice' | 'image' | 'video' | 'document';
|
|
10
|
+
text?: string;
|
|
11
|
+
media?: MessagingStoredMedia;
|
|
12
|
+
custom?: CustomMessageContent;
|
|
13
|
+
};
|
|
14
|
+
replyToSourceId?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface HistoryImportStatus {
|
|
17
|
+
jobId: string;
|
|
18
|
+
state: 'staging' | 'committing' | 'complete' | 'cancelled';
|
|
19
|
+
expiresAt?: string;
|
|
20
|
+
stagedCount?: number;
|
|
21
|
+
importedCount?: number;
|
|
22
|
+
}
|
|
23
|
+
/** Server-only. A manage-scoped key cannot be exposed in an app or browser. */
|
|
24
|
+
export declare class RobonoHistoryImport {
|
|
25
|
+
private options;
|
|
26
|
+
private endpoint;
|
|
27
|
+
constructor(options: {
|
|
28
|
+
apiKey: string;
|
|
29
|
+
baseUrl?: string;
|
|
30
|
+
mediaUrl?: string;
|
|
31
|
+
fetch?: typeof fetch;
|
|
32
|
+
});
|
|
33
|
+
private request;
|
|
34
|
+
start(input: {
|
|
35
|
+
jobId: string;
|
|
36
|
+
conversationId: string;
|
|
37
|
+
authors: string[];
|
|
38
|
+
count: number;
|
|
39
|
+
}): Promise<HistoryImportStatus>;
|
|
40
|
+
append(jobId: string, messages: Array<HistoryImportMessage & {
|
|
41
|
+
ordinal: number;
|
|
42
|
+
}>): Promise<HistoryImportStatus>;
|
|
43
|
+
status(jobId: string): Promise<HistoryImportStatus>;
|
|
44
|
+
commit(jobId: string): Promise<HistoryImportStatus>;
|
|
45
|
+
cancel(jobId: string): Promise<HistoryImportStatus>;
|
|
46
|
+
/** Upload historic files privately, with normal limits, verification and screening. */
|
|
47
|
+
media(jobId: string): RobonoManagedMedia;
|
|
48
|
+
upload(input: {
|
|
49
|
+
jobId: string;
|
|
50
|
+
conversationId: string;
|
|
51
|
+
authorId: string;
|
|
52
|
+
uploadId: string;
|
|
53
|
+
kind: MessagingMediaKind;
|
|
54
|
+
file: MessagingUploadFile;
|
|
55
|
+
bytes: Uint8Array;
|
|
56
|
+
}): Promise<MessagingStoredMedia>;
|
|
57
|
+
/** Retry this whole operation with the same job/source IDs after interruption. */
|
|
58
|
+
importMessages(input: {
|
|
59
|
+
jobId: string;
|
|
60
|
+
conversationId: string;
|
|
61
|
+
authors: string[];
|
|
62
|
+
messages: HistoryImportMessage[];
|
|
63
|
+
onProgress?: (accepted: number, total: number) => void;
|
|
64
|
+
}): Promise<HistoryImportStatus>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=history-import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-import.d.ts","sourceRoot":"","sources":["../src/history-import.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAC,oBAAoB,EAAC,mBAAmB,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACtG,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,gCAAgC,CAAC;AACzE,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAC,MAAM,CAAC;IAAA,QAAQ,EAAC,MAAM,CAAC;IAAA,MAAM,EAAC,MAAM,CAAC;IAC9C,OAAO,EAAC;QAAC,IAAI,EAAC,MAAM,GAAC,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,UAAU,CAAC;QAAA,IAAI,CAAC,EAAC,MAAM,CAAC;QAAA,KAAK,CAAC,EAAC,oBAAoB,CAAC;QAAA,MAAM,CAAC,EAAC,oBAAoB,CAAA;KAAC,CAAC;IAC/H,eAAe,CAAC,EAAC,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,mBAAmB;IAAE,KAAK,EAAC,MAAM,CAAC;IAAA,KAAK,EAAC,SAAS,GAAC,YAAY,GAAC,UAAU,GAAC,WAAW,CAAC;IAAA,SAAS,CAAC,EAAC,MAAM,CAAC;IAAA,WAAW,CAAC,EAAC,MAAM,CAAC;IAAA,aAAa,CAAC,EAAC,MAAM,CAAA;CAAC;AAEnK,+EAA+E;AAC/E,qBAAa,mBAAmB;IAEnB,OAAO,CAAC,OAAO;IAD3B,OAAO,CAAC,QAAQ,CAAQ;gBACJ,OAAO,EAAC;QAAC,MAAM,EAAC,MAAM,CAAC;QAAA,OAAO,CAAC,EAAC,MAAM,CAAC;QAAA,QAAQ,CAAC,EAAC,MAAM,CAAC;QAAA,KAAK,CAAC,EAAC,OAAO,KAAK,CAAA;KAAC;YAIlF,OAAO;IAOrB,KAAK,CAAC,KAAK,EAAC;QAAC,KAAK,EAAC,MAAM,CAAC;QAAA,cAAc,EAAC,MAAM,CAAC;QAAA,OAAO,EAAC,MAAM,EAAE,CAAC;QAAA,KAAK,EAAC,MAAM,CAAA;KAAC;IAC9E,MAAM,CAAC,KAAK,EAAC,MAAM,EAAC,QAAQ,EAAC,KAAK,CAAC,oBAAoB,GAAC;QAAC,OAAO,EAAC,MAAM,CAAA;KAAC,CAAC;IACzE,MAAM,CAAC,KAAK,EAAC,MAAM;IACnB,MAAM,CAAC,KAAK,EAAC,MAAM;IACnB,MAAM,CAAC,KAAK,EAAC,MAAM;IACnB,uFAAuF;IACvF,KAAK,CAAC,KAAK,EAAC,MAAM;IAIZ,MAAM,CAAC,KAAK,EAAC;QAAC,KAAK,EAAC,MAAM,CAAC;QAAA,cAAc,EAAC,MAAM,CAAC;QAAA,QAAQ,EAAC,MAAM,CAAC;QAAA,QAAQ,EAAC,MAAM,CAAC;QAAA,IAAI,EAAC,kBAAkB,CAAC;QAAA,IAAI,EAAC,mBAAmB,CAAC;QAAA,KAAK,EAAC,UAAU,CAAA;KAAC,GAAE,OAAO,CAAC,oBAAoB,CAAC;IAUxL,kFAAkF;IAC5E,cAAc,CAAC,KAAK,EAAC;QAAC,KAAK,EAAC,MAAM,CAAC;QAAA,cAAc,EAAC,MAAM,CAAC;QAAA,OAAO,EAAC,MAAM,EAAE,CAAC;QAAA,QAAQ,EAAC,oBAAoB,EAAE,CAAC;QAAA,UAAU,CAAC,EAAC,CAAC,QAAQ,EAAC,MAAM,EAAC,KAAK,EAAC,MAAM,KAAG,IAAI,CAAA;KAAC,GAAE,OAAO,CAAC,mBAAmB,CAAC;CAW/L"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { RobonoError } from './errors.js';
|
|
2
|
+
import { RobonoManagedMedia } from './managed-media.js';
|
|
3
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
|
+
/** Server-only. A manage-scoped key cannot be exposed in an app or browser. */
|
|
5
|
+
export class RobonoHistoryImport {
|
|
6
|
+
options;
|
|
7
|
+
endpoint;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
const url = new URL(options.baseUrl ?? 'https://api.robono.com/v1/messaging/import');
|
|
11
|
+
if (url.protocol !== 'https:' || url.username || url.password || !options.apiKey.trim())
|
|
12
|
+
throw Error('Use an HTTPS import endpoint and a server API key.');
|
|
13
|
+
this.endpoint = url.href;
|
|
14
|
+
}
|
|
15
|
+
async request(operation, body) {
|
|
16
|
+
if (!uuid.test(body.jobId))
|
|
17
|
+
throw Error('Use a UUID job ID, and keep it unchanged when retrying.');
|
|
18
|
+
const response = await (this.options.fetch ?? fetch)(this.endpoint, { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(120000), headers: { authorization: 'Bearer ' + this.options.apiKey, 'content-type': 'application/json' }, body: JSON.stringify({ ...body, operation }) });
|
|
19
|
+
const data = await response.json().catch(() => null);
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
throw new RobonoError(data?.error?.message ?? 'History import failed.', { status: response.status, code: data?.error?.code ?? 'history_import_failed', retryable: response.status === 429 || response.status >= 500 });
|
|
22
|
+
if (data?.jobId !== body.jobId)
|
|
23
|
+
throw Error('The import service returned an unexpected job.');
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
start(input) { return this.request('start', input); }
|
|
27
|
+
append(jobId, messages) { return this.request('append', { jobId, messages }); }
|
|
28
|
+
status(jobId) { return this.request('status', { jobId }); }
|
|
29
|
+
commit(jobId) { return this.request('commit', { jobId }); }
|
|
30
|
+
cancel(jobId) { return this.request('cancel', { jobId }); }
|
|
31
|
+
/** Upload historic files privately, with normal limits, verification and screening. */
|
|
32
|
+
media(jobId) {
|
|
33
|
+
if (!uuid.test(jobId))
|
|
34
|
+
throw Error('Use a UUID job ID.');
|
|
35
|
+
return new RobonoManagedMedia({ apiKey: this.options.apiKey, importJobId: jobId, ...(this.options.mediaUrl ? { baseUrl: this.options.mediaUrl } : {}), ...(this.options.fetch ? { fetch: this.options.fetch } : {}) });
|
|
36
|
+
}
|
|
37
|
+
async upload(input) {
|
|
38
|
+
if (input.bytes.byteLength !== input.file.byteSize)
|
|
39
|
+
throw Error('The file size must match the supplied bytes.');
|
|
40
|
+
const media = this.media(input.jobId), context = { actorId: input.authorId, conversationId: input.conversationId, uploadId: input.uploadId };
|
|
41
|
+
const plan = await media.prepare({ ...context, kind: input.kind, file: input.file });
|
|
42
|
+
if ('media' in plan)
|
|
43
|
+
return plan.media;
|
|
44
|
+
const url = new URL(plan.url);
|
|
45
|
+
if (url.protocol !== 'https:' || url.username || url.password)
|
|
46
|
+
throw Error('A private HTTPS upload URL is required.');
|
|
47
|
+
// Never forward the account key to the storage URL.
|
|
48
|
+
const response = await (this.options.fetch ?? fetch)(url, { method: 'PUT', redirect: 'error', headers: plan.headers ?? {}, body: input.bytes, signal: AbortSignal.timeout(120000) });
|
|
49
|
+
if (!response.ok)
|
|
50
|
+
throw new RobonoError('The imported attachment upload failed.', { status: response.status, code: 'import_upload_failed', retryable: response.status === 429 || response.status >= 500 });
|
|
51
|
+
return (await media.complete(context)).media;
|
|
52
|
+
}
|
|
53
|
+
/** Retry this whole operation with the same job/source IDs after interruption. */
|
|
54
|
+
async importMessages(input) {
|
|
55
|
+
if (!input.messages.length || input.messages.length > 10000 || new Set(input.messages.map(m => m.sourceId)).size !== input.messages.length)
|
|
56
|
+
throw Error('Import 1–10,000 messages with unique source IDs.');
|
|
57
|
+
const started = await this.start({ jobId: input.jobId, conversationId: input.conversationId, authors: input.authors, count: input.messages.length });
|
|
58
|
+
if (started.state === 'complete')
|
|
59
|
+
return started;
|
|
60
|
+
if (started.state !== 'staging')
|
|
61
|
+
throw Error('This import is no longer active.');
|
|
62
|
+
for (let offset = 0; offset < input.messages.length; offset += 20) {
|
|
63
|
+
await this.append(input.jobId, input.messages.slice(offset, offset + 20).map((message, index) => ({ ...message, ordinal: offset + index })));
|
|
64
|
+
input.onProgress?.(Math.min(offset + 20, input.messages.length), input.messages.length);
|
|
65
|
+
}
|
|
66
|
+
return this.commit(input.jobId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=history-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-import.js","sourceRoot":"","sources":["../src/history-import.ts"],"names":[],"mappings":"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"}
|