newo 3.2.0 ā 3.3.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 +85 -1
- package/dist/api.d.ts +30 -0
- package/dist/api.js +17 -0
- package/dist/cli/commands/create-webhooks.d.ts +3 -0
- package/dist/cli/commands/create-webhooks.js +83 -0
- package/dist/cli/commands/help.js +5 -0
- package/dist/cli/commands/migrate-account.d.ts +3 -0
- package/dist/cli/commands/migrate-account.js +84 -0
- package/dist/cli/commands/verify-migration.d.ts +3 -0
- package/dist/cli/commands/verify-migration.js +68 -0
- package/dist/cli.js +13 -0
- package/dist/sync/migrate.d.ts +32 -0
- package/dist/sync/migrate.js +590 -0
- package/dist/sync.d.ts +1 -0
- package/dist/sync.js +1 -0
- package/dist/types.d.ts +15 -1
- package/package.json +7 -3
- package/src/api.ts +56 -0
- package/src/cli/commands/create-webhooks.ts +100 -0
- package/src/cli/commands/help.ts +5 -0
- package/src/cli/commands/migrate-account.ts +104 -0
- package/src/cli/commands/verify-migration.ts +88 -0
- package/src/cli.ts +16 -0
- package/src/sync/migrate.ts +746 -0
- package/src/sync.ts +1 -0
- package/src/types.ts +15 -1
package/src/api.ts
CHANGED
|
@@ -496,4 +496,60 @@ export async function getAkbTopics(
|
|
|
496
496
|
params: { persona_id: personaId, page, per, order_by: orderBy }
|
|
497
497
|
});
|
|
498
498
|
return response.data;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Project update
|
|
502
|
+
export async function updateProject(
|
|
503
|
+
client: AxiosInstance,
|
|
504
|
+
projectId: string,
|
|
505
|
+
updateData: Partial<{
|
|
506
|
+
title: string;
|
|
507
|
+
description: string;
|
|
508
|
+
is_auto_update_enabled: boolean;
|
|
509
|
+
registry_idn: string;
|
|
510
|
+
registry_item_idn: string | null;
|
|
511
|
+
registry_item_version: string | null;
|
|
512
|
+
}>
|
|
513
|
+
): Promise<void> {
|
|
514
|
+
await client.put(`/api/v1/designer/projects/${projectId}`, updateData);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Agent update
|
|
518
|
+
export async function updateAgent(
|
|
519
|
+
client: AxiosInstance,
|
|
520
|
+
agentId: string,
|
|
521
|
+
updateData: Partial<{
|
|
522
|
+
title: string;
|
|
523
|
+
description: string;
|
|
524
|
+
persona_id: string | null;
|
|
525
|
+
}>
|
|
526
|
+
): Promise<void> {
|
|
527
|
+
await client.put(`/api/v1/designer/agents/${agentId}`, updateData);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Webhook creation
|
|
531
|
+
export async function createOutgoingWebhook(
|
|
532
|
+
client: AxiosInstance,
|
|
533
|
+
webhookData: {
|
|
534
|
+
connector_idn: string;
|
|
535
|
+
event_idn: string;
|
|
536
|
+
url: string;
|
|
537
|
+
method: string;
|
|
538
|
+
headers?: Record<string, string>;
|
|
539
|
+
body_template?: string;
|
|
540
|
+
}
|
|
541
|
+
): Promise<{ id: string }> {
|
|
542
|
+
const response = await client.post('/api/v1/webhooks', webhookData);
|
|
543
|
+
return response.data;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export async function createIncomingWebhook(
|
|
547
|
+
client: AxiosInstance,
|
|
548
|
+
webhookData: {
|
|
549
|
+
connector_idn: string;
|
|
550
|
+
event_idn: string;
|
|
551
|
+
}
|
|
552
|
+
): Promise<{ id: string; url: string }> {
|
|
553
|
+
const response = await client.post('/api/v1/webhooks/incoming', webhookData);
|
|
554
|
+
return response.data;
|
|
499
555
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook creation command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
6
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
7
|
+
import { customerDir } from '../../fsutil.js';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import yaml from 'js-yaml';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import type { MultiCustomerConfig, CliArgs } from '../../types.js';
|
|
12
|
+
|
|
13
|
+
export async function handleCreateWebhooksCommand(
|
|
14
|
+
customerConfig: MultiCustomerConfig,
|
|
15
|
+
args: CliArgs,
|
|
16
|
+
verbose: boolean
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
const { selectedCustomer } = selectSingleCustomer(
|
|
19
|
+
customerConfig,
|
|
20
|
+
args.customer as string | undefined
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (!selectedCustomer) {
|
|
24
|
+
console.error('ā No customer selected');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
29
|
+
const client = await makeClient(verbose, accessToken);
|
|
30
|
+
|
|
31
|
+
console.log(`\nš” Creating webhooks for ${selectedCustomer.idn}...\n`);
|
|
32
|
+
|
|
33
|
+
const custDir = customerDir(selectedCustomer.idn);
|
|
34
|
+
let outgoingCreated = 0;
|
|
35
|
+
let incomingCreated = 0;
|
|
36
|
+
|
|
37
|
+
// Create outgoing webhooks
|
|
38
|
+
const outgoingFile = path.join(custDir, 'integrations/api/connectors/webhook/webhooks/outgoing.yaml');
|
|
39
|
+
if (await fs.pathExists(outgoingFile)) {
|
|
40
|
+
const outgoingData = yaml.load(await fs.readFile(outgoingFile, 'utf8')) as any;
|
|
41
|
+
const webhooks = outgoingData.webhooks || [];
|
|
42
|
+
|
|
43
|
+
console.log(`Found ${webhooks.length} outgoing webhooks in YAML file`);
|
|
44
|
+
|
|
45
|
+
for (const webhook of webhooks) {
|
|
46
|
+
try {
|
|
47
|
+
await client.post('/api/v1/webhooks', {
|
|
48
|
+
idn: webhook.idn,
|
|
49
|
+
description: webhook.description || '',
|
|
50
|
+
connector_idn: webhook.connector_idn,
|
|
51
|
+
url: webhook.url,
|
|
52
|
+
command_idns: webhook.command_idns || []
|
|
53
|
+
});
|
|
54
|
+
outgoingCreated++;
|
|
55
|
+
console.log(` ā
Created outgoing: ${webhook.idn}`);
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
const status = error.response?.status;
|
|
58
|
+
|
|
59
|
+
if (status === 409) {
|
|
60
|
+
console.log(` ā¹ļø Already exists: ${webhook.idn}`);
|
|
61
|
+
} else {
|
|
62
|
+
console.error(` ā Failed: ${webhook.idn} - ${error.response?.data?.reason || error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create incoming webhooks
|
|
69
|
+
const incomingFile = path.join(custDir, 'integrations/api/connectors/webhook/webhooks/incoming.yaml');
|
|
70
|
+
if (await fs.pathExists(incomingFile)) {
|
|
71
|
+
const incomingData = yaml.load(await fs.readFile(incomingFile, 'utf8')) as any;
|
|
72
|
+
const webhooks = incomingData.webhooks || [];
|
|
73
|
+
|
|
74
|
+
console.log(`\nFound ${webhooks.length} incoming webhooks in YAML file`);
|
|
75
|
+
|
|
76
|
+
for (const webhook of webhooks) {
|
|
77
|
+
try {
|
|
78
|
+
await client.post('/api/v1/webhooks/incoming', {
|
|
79
|
+
idn: webhook.idn,
|
|
80
|
+
description: webhook.description || '',
|
|
81
|
+
connector_idn: webhook.connector_idn,
|
|
82
|
+
event_idns: webhook.event_idns || [],
|
|
83
|
+
allowed_ips: webhook.allowed_ips || []
|
|
84
|
+
});
|
|
85
|
+
incomingCreated++;
|
|
86
|
+
console.log(` ā
Created incoming: ${webhook.idn}`);
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
const status = error.response?.status;
|
|
89
|
+
|
|
90
|
+
if (status === 409) {
|
|
91
|
+
console.log(` ā¹ļø Already exists: ${webhook.idn}`);
|
|
92
|
+
} else {
|
|
93
|
+
console.error(` ā Failed: ${webhook.idn} - ${error.response?.data?.reason || error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(`\nā
Created ${outgoingCreated} outgoing and ${incomingCreated} incoming webhooks\n`);
|
|
100
|
+
}
|
package/src/cli/commands/help.ts
CHANGED
|
@@ -49,6 +49,11 @@ Enterprise Features:
|
|
|
49
49
|
newo pull-akb [--customer <idn>] # download AKB articles for all personas with agents ā ./newo_customers/<idn>/akb/
|
|
50
50
|
newo push-akb [--customer <idn>] # upload AKB articles from local YAML files to platform
|
|
51
51
|
|
|
52
|
+
Account Migration (NEW):
|
|
53
|
+
newo migrate-account --source <idn> --dest <idn> [--yes] # migrate complete account from source to destination
|
|
54
|
+
newo verify --source <idn> --dest <idn> # verify migration by comparing entity counts
|
|
55
|
+
newo create-webhooks [--customer <idn>] # create webhooks from YAML files
|
|
56
|
+
|
|
52
57
|
Flags:
|
|
53
58
|
--customer <idn> # specify customer (if not set, uses default or interactive selection)
|
|
54
59
|
--all # include all available data (for conversations: all personas and acts)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account migration command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { migrateAccount } from '../../sync/migrate.js';
|
|
6
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
7
|
+
import { getCustomer } from '../../customer.js';
|
|
8
|
+
import type { MultiCustomerConfig, CliArgs } from '../../types.js';
|
|
9
|
+
|
|
10
|
+
export async function handleMigrateAccountCommand(
|
|
11
|
+
customerConfig: MultiCustomerConfig,
|
|
12
|
+
args: CliArgs,
|
|
13
|
+
verbose: boolean
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
// Get source and destination customer IDNs
|
|
16
|
+
const sourceIdn = args.source as string | undefined;
|
|
17
|
+
const destIdn = args.dest as string | undefined;
|
|
18
|
+
|
|
19
|
+
if (!sourceIdn || !destIdn) {
|
|
20
|
+
console.error('ā Usage: newo migrate-account --source <sourceIdn> --dest <destIdn>');
|
|
21
|
+
console.error('Example: newo migrate-account --source NEWO_bb5lmJjg --dest NEq9OCwSXw');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const sourceCustomer = getCustomer(customerConfig, sourceIdn);
|
|
26
|
+
const destCustomer = getCustomer(customerConfig, destIdn);
|
|
27
|
+
|
|
28
|
+
if (!sourceCustomer) {
|
|
29
|
+
console.error(`ā Source customer ${sourceIdn} not found in configuration`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!destCustomer) {
|
|
34
|
+
console.error(`ā Destination customer ${destIdn} not found in configuration`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log('\nš Account Migration');
|
|
39
|
+
console.log(`Source: ${sourceIdn}`);
|
|
40
|
+
console.log(`Destination: ${destIdn}`);
|
|
41
|
+
console.log('\nā ļø This will copy ALL data from source to destination');
|
|
42
|
+
console.log('ā ļø Source account will NOT be modified (read-only)');
|
|
43
|
+
|
|
44
|
+
// Confirm migration
|
|
45
|
+
if (!args.yes && !args.y) {
|
|
46
|
+
const readline = await import('readline');
|
|
47
|
+
const rl = readline.createInterface({
|
|
48
|
+
input: process.stdin,
|
|
49
|
+
output: process.stdout
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const answer = await new Promise<string>((resolve) => {
|
|
53
|
+
rl.question('\nProceed with migration? (yes/NO): ', resolve);
|
|
54
|
+
});
|
|
55
|
+
rl.close();
|
|
56
|
+
|
|
57
|
+
if (answer.toLowerCase() !== 'yes') {
|
|
58
|
+
console.log('ā Migration cancelled');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Authenticate
|
|
64
|
+
const sourceToken = await getValidAccessToken(sourceCustomer);
|
|
65
|
+
const destToken = await getValidAccessToken(destCustomer);
|
|
66
|
+
|
|
67
|
+
const sourceClient = await makeClient(verbose, sourceToken);
|
|
68
|
+
const destClient = await makeClient(verbose, destToken);
|
|
69
|
+
|
|
70
|
+
// Run migration
|
|
71
|
+
const result = await migrateAccount({
|
|
72
|
+
sourceCustomer,
|
|
73
|
+
destCustomer,
|
|
74
|
+
sourceClient,
|
|
75
|
+
destClient,
|
|
76
|
+
verbose
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Print summary
|
|
80
|
+
console.log('\nš MIGRATION SUMMARY\n');
|
|
81
|
+
console.log(`Projects created: ${result.projectsCreated}`);
|
|
82
|
+
console.log(`Agents created: ${result.agentsCreated}`);
|
|
83
|
+
console.log(`Flows created: ${result.flowsCreated}`);
|
|
84
|
+
console.log(`Skills created: ${result.skillsCreated}`);
|
|
85
|
+
console.log(`Attributes migrated: ${result.attributesMigrated}`);
|
|
86
|
+
console.log(`Personas created: ${result.personasCreated}`);
|
|
87
|
+
console.log(`AKB articles imported: ${result.articlesImported}`);
|
|
88
|
+
console.log(`Connectors created: ${result.connectorsCreated}`);
|
|
89
|
+
console.log(`Webhooks created: ${result.webhooksCreated}`);
|
|
90
|
+
|
|
91
|
+
if (result.errors.length > 0) {
|
|
92
|
+
console.log(`\nā ļø Errors: ${result.errors.length}`);
|
|
93
|
+
result.errors.forEach(err => console.error(` - ${err}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`\n${result.success ? 'ā
Migration completed successfully!' : 'ā Migration completed with errors'}\n`);
|
|
97
|
+
|
|
98
|
+
if (result.success) {
|
|
99
|
+
console.log('š Next steps:');
|
|
100
|
+
console.log(` 1. Push skill content: npx newo push --customer ${destIdn}`);
|
|
101
|
+
console.log(` 2. Verify migration: npx newo verify --source ${sourceIdn} --dest ${destIdn}`);
|
|
102
|
+
console.log(` 3. Test agent: npx newo sandbox "test message" --customer ${destIdn}\n`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration verification command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient, listProjects, listAgents, listFlowSkills } from '../../api.js';
|
|
5
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
6
|
+
import { getCustomer } from '../../customer.js';
|
|
7
|
+
import type { MultiCustomerConfig, CliArgs } from '../../types.js';
|
|
8
|
+
|
|
9
|
+
export async function handleVerifyMigrationCommand(
|
|
10
|
+
customerConfig: MultiCustomerConfig,
|
|
11
|
+
args: CliArgs,
|
|
12
|
+
verbose: boolean
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
const sourceIdn = args.source as string | undefined;
|
|
15
|
+
const destIdn = args.dest as string | undefined;
|
|
16
|
+
|
|
17
|
+
if (!sourceIdn || !destIdn) {
|
|
18
|
+
console.error('ā Usage: newo verify --source <sourceIdn> --dest <destIdn>');
|
|
19
|
+
console.error('Example: newo verify --source NEWO_bb5lmJjg --dest NEq9OCwSXw');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const sourceCustomer = getCustomer(customerConfig, sourceIdn);
|
|
24
|
+
const destCustomer = getCustomer(customerConfig, destIdn);
|
|
25
|
+
|
|
26
|
+
if (!sourceCustomer || !destCustomer) {
|
|
27
|
+
console.error('ā Customer not found in configuration');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('\nš Migration Verification');
|
|
32
|
+
console.log(`Source: ${sourceIdn}`);
|
|
33
|
+
console.log(`Destination: ${destIdn}\n`);
|
|
34
|
+
|
|
35
|
+
const sourceToken = await getValidAccessToken(sourceCustomer);
|
|
36
|
+
const destToken = await getValidAccessToken(destCustomer);
|
|
37
|
+
|
|
38
|
+
const sourceClient = await makeClient(verbose, sourceToken);
|
|
39
|
+
const destClient = await makeClient(verbose, destToken);
|
|
40
|
+
|
|
41
|
+
// Count entities
|
|
42
|
+
const sourceProjects = await listProjects(sourceClient);
|
|
43
|
+
const destProjects = await listProjects(destClient);
|
|
44
|
+
|
|
45
|
+
let srcAgents = 0, srcFlows = 0, srcSkills = 0;
|
|
46
|
+
let dstAgents = 0, dstFlows = 0, dstSkills = 0;
|
|
47
|
+
|
|
48
|
+
for (const proj of sourceProjects) {
|
|
49
|
+
const agents = await listAgents(sourceClient, proj.id);
|
|
50
|
+
srcAgents += agents.length;
|
|
51
|
+
|
|
52
|
+
for (const agent of agents) {
|
|
53
|
+
srcFlows += (agent.flows || []).length;
|
|
54
|
+
|
|
55
|
+
for (const flow of agent.flows || []) {
|
|
56
|
+
const skills = await listFlowSkills(sourceClient, flow.id);
|
|
57
|
+
srcSkills += skills.length;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const proj of destProjects.filter(p => p.idn !== 'test')) {
|
|
63
|
+
const agents = await listAgents(destClient, proj.id);
|
|
64
|
+
dstAgents += agents.length;
|
|
65
|
+
|
|
66
|
+
for (const agent of agents) {
|
|
67
|
+
dstFlows += (agent.flows || []).length;
|
|
68
|
+
|
|
69
|
+
for (const flow of agent.flows || []) {
|
|
70
|
+
const skills = await listFlowSkills(destClient, flow.id);
|
|
71
|
+
dstSkills += skills.length;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('š Entity Counts:\n');
|
|
77
|
+
console.log(`Projects: ${sourceProjects.length} ā ${destProjects.filter(p => p.idn !== 'test').length} ${sourceProjects.length === destProjects.filter(p => p.idn !== 'test').length ? 'ā
' : 'ā'}`);
|
|
78
|
+
console.log(`Agents: ${srcAgents} ā ${dstAgents} ${srcAgents === dstAgents ? 'ā
' : 'ā'}`);
|
|
79
|
+
console.log(`Flows: ${srcFlows} ā ${dstFlows} ${srcFlows === dstFlows ? 'ā
' : 'ā'}`);
|
|
80
|
+
console.log(`Skills: ${srcSkills} ā ${dstSkills} ${srcSkills === dstSkills ? 'ā
' : 'ā'}\n`);
|
|
81
|
+
|
|
82
|
+
if (srcAgents === dstAgents && srcFlows === dstFlows && srcSkills === dstSkills) {
|
|
83
|
+
console.log('ā
All entity counts match - Migration successful!\n');
|
|
84
|
+
} else {
|
|
85
|
+
console.log('ā ļø Entity counts differ - Migration may be incomplete\n');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -35,6 +35,9 @@ import { handleListActionsCommand } from './cli/commands/list-actions.js';
|
|
|
35
35
|
import { handleProfileCommand } from './cli/commands/profile.js';
|
|
36
36
|
import { handlePullAkbCommand } from './cli/commands/pull-akb.js';
|
|
37
37
|
import { handlePushAkbCommand } from './cli/commands/push-akb.js';
|
|
38
|
+
import { handleMigrateAccountCommand } from './cli/commands/migrate-account.js';
|
|
39
|
+
import { handleVerifyMigrationCommand } from './cli/commands/verify-migration.js';
|
|
40
|
+
import { handleCreateWebhooksCommand } from './cli/commands/create-webhooks.js';
|
|
38
41
|
import type { CliArgs, NewoApiError } from './types.js';
|
|
39
42
|
|
|
40
43
|
dotenv.config();
|
|
@@ -188,6 +191,19 @@ async function main(): Promise<void> {
|
|
|
188
191
|
await handlePushAkbCommand(customerConfig, args, verbose);
|
|
189
192
|
break;
|
|
190
193
|
|
|
194
|
+
case 'migrate-account':
|
|
195
|
+
await handleMigrateAccountCommand(customerConfig, args, verbose);
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
case 'verify':
|
|
199
|
+
case 'verify-migration':
|
|
200
|
+
await handleVerifyMigrationCommand(customerConfig, args, verbose);
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
case 'create-webhooks':
|
|
204
|
+
await handleCreateWebhooksCommand(customerConfig, args, verbose);
|
|
205
|
+
break;
|
|
206
|
+
|
|
191
207
|
default:
|
|
192
208
|
console.error('Unknown command:', cmd);
|
|
193
209
|
console.error('Run "newo --help" for usage information');
|