@agentled/cli
Version:
CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.
321 lines • 13.6 kB
JavaScript
import { readFileSync } from 'node:fs';
import { AgentledClient } from '../client.js';
import { printOutput, printError } from '../utils/output.js';
import { findWorkspaceDir, refreshWorkspaceFolder, readWorkspaceMeta } from '../utils/workspace-folder.js';
function readJsonValue(opts, label) {
if (!opts.input && !opts.file) {
throw new Error(`Provide ${label} via --input '<json>' or --file <path>`);
}
if (opts.input && opts.file) {
throw new Error(`Provide ${label} with either --input or --file, not both`);
}
const raw = opts.file
? readFileSync(opts.file, 'utf8')
: opts.input;
try {
return JSON.parse(raw);
}
catch (error) {
throw new Error(`Invalid JSON for ${label}: ${error?.message || 'parse failed'}`);
}
}
export function registerWorkspaceCommands(program) {
const workspace = program
.command('workspace')
.alias('ws')
.description('Workspace information');
workspace
.command('inspect')
.description('One-shot workspace orientation: identity, company profile, workflows, KG lists, connected apps, and agents')
.option('--format <fmt>', 'Output format (json or table)', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
// Fan out all six orientation calls in parallel.
const [workspace, companyProfile, workflows, knowledgeLists, connections, agents] = await Promise.allSettled([
client.getWorkspace(),
client.getWorkspaceCompanyProfile(),
client.listWorkflows(),
client.listKnowledgeLists(),
client.listConnections(),
client.listAgents(),
]);
const result = {
workspace: workspace.status === 'fulfilled' ? workspace.value : { error: workspace.reason?.message },
companyProfile: companyProfile.status === 'fulfilled' ? companyProfile.value : { error: companyProfile.reason?.message },
workflows: workflows.status === 'fulfilled' ? workflows.value : { error: workflows.reason?.message },
knowledgeLists: knowledgeLists.status === 'fulfilled' ? knowledgeLists.value : { error: knowledgeLists.reason?.message },
connections: connections.status === 'fulfilled' ? connections.value : { error: connections.reason?.message },
agents: agents.status === 'fulfilled' ? agents.value : { error: agents.reason?.message },
};
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('info')
.description('Get workspace details')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
const result = await client.getWorkspace();
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('resolve')
.description('Resolve an existing workspace without mutation before operator provisioning')
.option('--input <json>', 'Workspace resolve request JSON')
.option('--file <path>', 'Path to workspace resolve request JSON')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const input = readJsonValue(opts, 'workspace resolve request');
const result = await new AgentledClient().resolveWorkspace(input);
printOutput(result, opts.format);
}
catch (e) {
printError(e instanceof Error ? e.message : String(e));
}
});
workspace
.command('create')
.description('Create one setup-only workspace; requires operator:workspace:create scope')
.option('--input <json>', 'Workspace create request JSON')
.option('--file <path>', 'Path to workspace create request JSON')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const input = readJsonValue(opts, 'workspace create request');
const result = await new AgentledClient().createWorkspace(input);
printOutput(result, opts.format);
}
catch (e) {
printError(e instanceof Error ? e.message : String(e));
}
});
workspace
.command('company-profile')
.description('Get editable workspace company profile and company knowledge text')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
const result = await client.getWorkspaceCompanyProfile();
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('update-company-profile')
.description('Update workspace company profile fields')
.option('--input <json>', 'Company profile JSON object')
.option('--file <path>', 'Path to JSON file containing a company profile object')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const parsed = readJsonValue(opts, 'company profile');
const company = parsed?.company && typeof parsed.company === 'object'
? parsed.company
: parsed;
if (!company || typeof company !== 'object' || Array.isArray(company)) {
throw new Error('Company profile input must be a JSON object');
}
const client = new AgentledClient();
const result = await client.updateWorkspaceCompanyProfile(company);
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('pinned-outputs')
.description('List output pages pinned to the workspace home/sidebar')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
const result = await client.listPinnedOutputs();
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('set-output-pin <workflowId> <outputPagePathname>')
.description('Pin or unpin a workflow output page on the workspace home/sidebar')
.option('--pin', 'Pin the output page (default)')
.option('--unpin', 'Unpin the output page')
.option('--label <label>', 'Optional custom sidebar label when pinning')
.option('--icon-name <name>', 'Optional lucide icon name when pinning')
.option('--format <fmt>', 'Output format', 'json')
.action(async (workflowId, outputPagePathname, opts) => {
try {
if (opts.pin && opts.unpin) {
throw new Error('Use either --pin or --unpin, not both');
}
const client = new AgentledClient();
const result = await client.setOutputPagePin({
workflowId,
outputPagePathname,
pinned: opts.unpin ? false : true,
label: opts.label,
iconName: opts.iconName,
});
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('home-recent')
.description('Inspect the Home tabs, their default tab, the Recent tab configuration, and the current workspace revision')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
const result = await client.inspectHomeRecentTab();
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('configure-home-recent')
.description('Configure the display-only Home Recent tab and default Home tab without running workflows')
.requiredOption('--expected-workspace-updated-at <timestamp>', 'Workspace revision returned by workspace home-recent')
.option('--input <json>', 'Full Home Recent config JSON object')
.option('--file <path>', 'Path to a full Home Recent config JSON object')
.option('--reset', 'Reset to the default Home Recent configuration')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const modeCount = [
opts.input !== undefined,
opts.file !== undefined,
opts.reset === true,
].filter(Boolean).length;
if (modeCount !== 1) {
throw new Error('Use exactly one of --input, --file, or --reset');
}
const config = opts.reset
? null
: readJsonValue(opts, 'Home Recent configuration');
if (config !== null && (!config || typeof config !== 'object' || Array.isArray(config))) {
throw new Error('Home Recent configuration must be a JSON object');
}
const client = new AgentledClient();
const result = await client.configureHomeRecentTab({
expectedWorkspaceUpdatedAt: opts.expectedWorkspaceUpdatedAt,
config,
sourceSurface: 'cli',
});
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
// --- Branding ---
workspace
.command('branding')
.description('Get workspace whitelabel branding configuration')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const client = new AgentledClient();
const result = await client.getBranding();
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('update-branding')
.description('Update workspace whitelabel branding')
.option('--display-name <name>', 'Display name')
.option('--logo-url <url>', 'Logo URL')
.option('--tagline <text>', 'Tagline')
.option('--primary-color <hex>', 'Primary color (light mode)')
.option('--primary-color-dark <hex>', 'Primary color (dark mode)')
.option('--favicon-url <url>', 'Favicon URL')
.option('--hide-badge', 'Hide "Powered by" badge')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const branding = {};
if (opts.displayName)
branding.displayName = opts.displayName;
if (opts.logoUrl)
branding.logoUrl = opts.logoUrl;
if (opts.tagline)
branding.tagline = opts.tagline;
if (opts.primaryColor)
branding.primaryColor = opts.primaryColor;
if (opts.primaryColorDark)
branding.primaryColorDark = opts.primaryColorDark;
if (opts.faviconUrl)
branding.faviconUrl = opts.faviconUrl;
if (opts.hideBadge !== undefined)
branding.hideBadge = true;
if (Object.keys(branding).length === 0) {
printError('Provide at least one branding field to update');
return;
}
const client = new AgentledClient();
const result = await client.updateBranding(branding);
printOutput(result, opts.format);
}
catch (e) {
printError(e.message);
}
});
workspace
.command('sync')
.description('Refresh the local workspace folder cache (apps.json + models.json) and bundled docs (SKILL.md, GOTCHAS.md). Run this after a CLI upgrade or when workspace apps change.')
.option('--format <fmt>', 'Output format', 'json')
.action(async (opts) => {
try {
const wsDir = findWorkspaceDir();
if (!wsDir) {
printError('No agentled workspace folder found. Run `agentled init` first.');
return;
}
const client = new AgentledClient();
const [appsList, modelsList] = await Promise.allSettled([
client.listApps(),
client.listModels(),
]);
refreshWorkspaceFolder(wsDir, {
appsList: appsList.status === 'fulfilled' ? appsList.value : undefined,
modelsList: modelsList.status === 'fulfilled' ? modelsList.value : undefined,
});
printOutput({
workspaceDir: wsDir,
meta: readWorkspaceMeta(wsDir),
refreshed: {
apps: appsList.status === 'fulfilled',
models: modelsList.status === 'fulfilled',
docs: true,
},
}, (opts.format ?? 'json'));
}
catch (e) {
printError(e instanceof Error ? e.message : String(e));
}
});
}
//# sourceMappingURL=workspace.js.map