newo
Version:
NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features account migration, integration management, webhook automation, AKB knowledge base, project attributes, sandbox testing, IDN-based file management, r
373 lines • 15.6 kB
JavaScript
import axios, {} from 'axios';
import { getValidAccessToken, forceReauth } from './auth.js';
import { ENV } from './env.js';
// Per-request retry tracking to avoid shared state issues
const RETRY_SYMBOL = Symbol('retried');
export async function makeClient(verbose = false, token) {
let accessToken = token || await getValidAccessToken();
if (verbose)
console.log('✓ Access token obtained');
const client = axios.create({
baseURL: ENV.NEWO_BASE_URL,
headers: { accept: 'application/json' }
});
client.interceptors.request.use(async (config) => {
config.headers = config.headers || {};
config.headers.Authorization = `Bearer ${accessToken}`;
if (verbose) {
console.log(`→ ${config.method?.toUpperCase()} ${config.url}`);
if (config.data)
console.log(' Data:', JSON.stringify(config.data, null, 2));
if (config.params)
console.log(' Params:', config.params);
}
return config;
});
client.interceptors.response.use((response) => {
if (verbose) {
console.log(`← ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`);
if (response.data && Object.keys(response.data).length < 20) {
console.log(' Response:', JSON.stringify(response.data, null, 2));
}
else if (response.data) {
const itemCount = Array.isArray(response.data) ? response.data.length : Object.keys(response.data).length;
console.log(` Response: [${typeof response.data}] ${Array.isArray(response.data) ? itemCount + ' items' : 'large object'}`);
}
}
return response;
}, async (error) => {
const status = error?.response?.status;
if (verbose) {
console.log(`← ${status} ${error.config?.method?.toUpperCase()} ${error.config?.url} - ${error.message}`);
if (error.response?.data)
console.log(' Error data:', error.response.data);
}
// Use per-request retry tracking to avoid shared state issues
const config = error.config;
if (status === 401 && !config?.[RETRY_SYMBOL]) {
if (config) {
config[RETRY_SYMBOL] = true;
if (verbose)
console.log('🔄 Retrying with fresh token...');
accessToken = await forceReauth();
config.headers = config.headers || {};
config.headers.Authorization = `Bearer ${accessToken}`;
return client.request(config);
}
}
throw error;
});
return client;
}
export async function listProjects(client) {
const response = await client.get('/api/v1/designer/projects');
return response.data;
}
export async function listAgents(client, projectId) {
const response = await client.get('/api/v1/bff/agents/list', {
params: { project_id: projectId }
});
return response.data;
}
export async function getProjectMeta(client, projectId) {
const response = await client.get(`/api/v1/designer/projects/by-id/${projectId}`);
return response.data;
}
export async function listFlowSkills(client, flowId) {
const response = await client.get(`/api/v1/designer/flows/${flowId}/skills`);
return response.data;
}
export async function getSkill(client, skillId) {
const response = await client.get(`/api/v1/designer/skills/${skillId}`);
return response.data;
}
export async function updateSkill(client, skillObject) {
await client.put(`/api/v1/designer/flows/skills/${skillObject.id}`, skillObject);
}
export async function listFlowEvents(client, flowId) {
const response = await client.get(`/api/v1/designer/flows/${flowId}/events`);
return response.data;
}
export async function listFlowStates(client, flowId) {
const response = await client.get(`/api/v1/designer/flows/${flowId}/states`);
return response.data;
}
export async function importAkbArticle(client, articleData) {
const response = await client.post('/api/v1/akb/append-manual', articleData);
return response.data;
}
export async function getCustomerProfile(client) {
const response = await client.get('/api/v1/customer/profile');
return response.data;
}
export async function getCustomerAttributes(client, includeHidden = true) {
const response = await client.get('/api/v1/bff/customer/attributes', {
params: { include_hidden: includeHidden }
});
return response.data;
}
export async function updateCustomerAttribute(client, attribute) {
if (!attribute.id) {
throw new Error(`Attribute ${attribute.idn} is missing ID - cannot update`);
}
await client.put(`/api/v1/customer/attributes/${attribute.id}`, {
idn: attribute.idn,
value: attribute.value,
title: attribute.title,
description: attribute.description,
group: attribute.group,
is_hidden: attribute.is_hidden,
possible_values: attribute.possible_values,
value_type: attribute.value_type
});
}
export async function getProjectAttributes(client, projectId, includeHidden = false) {
const response = await client.get(`/api/v1/bff/projects/${projectId}/attributes`, {
params: {
query: '',
include_hidden: includeHidden
}
});
return response.data;
}
export async function updateProjectAttribute(client, projectId, attribute) {
if (!attribute.id) {
throw new Error(`Project attribute ${attribute.idn} is missing ID - cannot update`);
}
await client.put(`/api/v1/designer/projects/${projectId}/attributes/${attribute.id}`, {
idn: attribute.idn,
value: attribute.value,
title: attribute.title,
description: attribute.description,
group: attribute.group,
is_hidden: attribute.is_hidden,
possible_values: attribute.possible_values,
value_type: attribute.value_type
});
}
export async function createProjectAttribute(client, projectId, attributeData) {
const response = await client.post(`/api/v1/designer/projects/${projectId}/attributes`, attributeData);
return response.data;
}
export async function deleteProjectAttribute(client, projectId, attributeId) {
await client.delete(`/api/v1/designer/projects/${projectId}/attributes/${attributeId}`);
}
// Conversation API Functions
export async function listUserPersonas(client, page = 1, per = 50) {
const response = await client.get('/api/v1/bff/conversations/user-personas', {
params: { page, per }
});
return response.data;
}
export async function getUserPersona(client, personaId) {
const response = await client.get(`/api/v1/bff/conversations/user-personas/${personaId}`);
return response.data;
}
export async function getAccount(client) {
const response = await client.get('/api/v1/account');
return response.data;
}
export async function getChatHistory(client, params) {
const queryParams = {
user_actor_id: params.user_actor_id,
page: params.page || 1,
per: params.per || 50
};
// Only add agent_actor_id if provided
if (params.agent_actor_id) {
queryParams.agent_actor_id = params.agent_actor_id;
}
const response = await client.get('/api/v1/chat/history', {
params: queryParams
});
return response.data;
}
// Entity Creation/Deletion API Functions
export async function createAgent(client, projectId, agentData) {
// Use project-specific v2 endpoint for proper project association
const response = await client.post(`/api/v2/designer/${projectId}/agents`, agentData);
return response.data;
}
export async function deleteAgent(client, agentId) {
await client.delete(`/api/v1/designer/agents/${agentId}`);
}
export async function createFlow(client, agentId, flowData) {
// Use the correct NEWO endpoint pattern for flow creation
const response = await client.post(`/api/v1/designer/${agentId}/flows/empty`, flowData);
// The NEWO flow creation API returns empty response body with 201 status
// The flow is created successfully, but we need to get the ID through agent listing
if (response.status === 201) {
// Flow created successfully, but ID will be retrieved during pull operation
return { id: 'pending-sync' };
}
throw new Error(`Flow creation failed with status: ${response.status}`);
}
export async function deleteFlow(client, flowId) {
await client.delete(`/api/v1/designer/flows/${flowId}`);
}
export async function createSkill(client, flowId, skillData) {
const response = await client.post(`/api/v1/designer/flows/${flowId}/skills`, skillData);
return response.data;
}
export async function deleteSkill(client, skillId) {
await client.delete(`/api/v1/designer/flows/skills/${skillId}`);
}
export async function deleteSkillById(client, skillId) {
console.log(`🗑️ Deleting skill from platform: ${skillId}`);
await client.delete(`/api/v1/designer/flows/skills/${skillId}`);
console.log(`✅ Skill deleted from platform: ${skillId}`);
}
export async function createFlowEvent(client, flowId, eventData) {
const response = await client.post(`/api/v1/designer/flows/${flowId}/events`, eventData);
return response.data;
}
export async function deleteFlowEvent(client, eventId) {
await client.delete(`/api/v1/designer/flows/events/${eventId}`);
}
export async function createFlowState(client, flowId, stateData) {
const response = await client.post(`/api/v1/designer/flows/${flowId}/states`, stateData);
return response.data;
}
export async function createSkillParameter(client, skillId, paramData) {
// Debug the parameter creation request
console.log('Creating parameter for skill:', skillId);
console.log('Parameter data:', JSON.stringify(paramData, null, 2));
try {
const response = await client.post(`/api/v1/designer/flows/skills/${skillId}/parameters`, paramData);
return response.data;
}
catch (error) {
console.error('Parameter creation error details:', error.response?.data);
throw error;
}
}
export async function createCustomerAttribute(client, attributeData) {
const response = await client.post('/api/v1/customer/attributes', attributeData);
return response.data;
}
export async function createProject(client, projectData) {
const response = await client.post('/api/v1/designer/projects', projectData);
return response.data;
}
export async function deleteProject(client, projectId) {
await client.delete(`/api/v1/designer/projects/${projectId}`);
}
export async function createPersona(client, personaData) {
const response = await client.post('/api/v1/customer/personas', personaData);
return response.data;
}
export async function publishFlow(client, flowId, publishData) {
const response = await client.post(`/api/v1/designer/flows/${flowId}/publish`, publishData);
return response.data;
}
// Sandbox Chat API Functions
export async function listIntegrations(client) {
const response = await client.get('/api/v1/integrations');
return response.data;
}
export async function listConnectors(client, integrationId) {
const response = await client.get(`/api/v1/integrations/${integrationId}/connectors`);
return response.data;
}
export async function createSandboxPersona(client, personaData) {
const response = await client.post('/api/v1/customer/personas', personaData);
return response.data;
}
export async function createActor(client, personaId, actorData) {
const response = await client.post(`/api/v1/customer/personas/${personaId}/actors`, actorData);
return response.data;
}
export async function sendChatMessage(client, actorId, messageData) {
await client.post(`/api/v1/chat/user/${actorId}`, messageData);
}
export async function getConversationActs(client, params) {
const queryParams = {
user_persona_id: params.user_persona_id,
user_actor_id: params.user_actor_id,
per: params.per || 100,
page: params.page || 1
};
// Only add agent_persona_id if provided
if (params.agent_persona_id) {
queryParams.agent_persona_id = params.agent_persona_id;
}
const response = await client.get('/api/v1/bff/conversations/acts', {
params: queryParams
});
return response.data;
}
// Script Actions API Functions
export async function getScriptActions(client) {
const response = await client.get('/api/v1/script/actions');
return response.data;
}
// Integration API Functions
export async function getIntegrationSettings(client, integrationId) {
const response = await client.get(`/api/v1/integrations/${integrationId}/settings`);
return response.data;
}
// Connector CRUD API Functions
export async function createConnector(client, integrationId, connectorData) {
const response = await client.post(`/api/v1/integrations/${integrationId}/connectors`, connectorData);
return response.data;
}
export async function updateConnector(client, connectorId, updateData) {
await client.put(`/api/v1/integrations/connectors/${connectorId}`, updateData);
}
export async function deleteConnector(client, connectorId) {
await client.delete(`/api/v1/integrations/connectors/${connectorId}`);
}
// Webhook API Functions
export async function listOutgoingWebhooks(client) {
const response = await client.get('/api/v1/webhooks');
return response.data;
}
export async function listIncomingWebhooks(client) {
const response = await client.get('/api/v1/webhooks/incoming');
return response.data;
}
// AKB (Knowledge Base) API Functions
export async function searchPersonas(client, isLinkedToAgent = true, page = 1, per = 30) {
const response = await client.get('/api/v1/bff/personas/search', {
params: { is_linked_to_agent: isLinkedToAgent, page, per }
});
return response.data;
}
export async function getAkbTopics(client, personaId, page = 1, per = 100, orderBy = 'created_at') {
const response = await client.get('/api/v1/akb/topics', {
params: { persona_id: personaId, page, per, order_by: orderBy }
});
return response.data;
}
// Project update
export async function updateProject(client, projectId, updateData) {
await client.put(`/api/v1/designer/projects/${projectId}`, updateData);
}
// Agent update
export async function updateAgent(client, agentId, updateData) {
await client.put(`/api/v1/designer/agents/${agentId}`, updateData);
}
// Webhook creation
export async function createOutgoingWebhook(client, webhookData) {
const response = await client.post('/api/v1/webhooks', webhookData);
return response.data;
}
export async function createIncomingWebhook(client, webhookData) {
const response = await client.post('/api/v1/webhooks/incoming', webhookData);
return response.data;
}
// Registry API Functions
export async function listRegistries(client) {
const response = await client.get('/api/v1/designer/registries');
return response.data;
}
export async function listRegistryItems(client, registryId) {
const response = await client.get(`/api/v1/designer/registries/${registryId}/items`);
return response.data;
}
export async function addProjectFromRegistry(client, projectData) {
// Uses the same endpoint as createProject, but with registry fields populated
const response = await client.post('/api/v1/designer/projects', projectData);
return response.data;
}
//# sourceMappingURL=api.js.map