naisys
Version:
NAISYS - Autonomous AI agent runner with built-in context management and cost tracking
171 lines • 7.12 kB
JavaScript
import { buildDefaultAgentConfig, builtInLlmModels, CODEX_REFRESH_TOKEN_VAR, LlmApiType, } from "@naisys/common";
import { askQuestion, cwdWithTilde, loadAgentConfigs, } from "@naisys/common-node";
import fs from "fs";
import yaml from "js-yaml";
import os from "os";
import path from "path";
const CODEX_SUBSCRIPTION_PROVIDER_NAME = "OpenAI Codex Subscription";
export function getNaisysWizardConfig(hubClient, integratedHub, options = {}) {
if (hubClient) {
return {
title: "NAISYS Setup (Hub Client)",
sections: [
{
type: "fields",
comment: "Generate one per host in the Supervisor (Hosts → host → Access Key). The key alone identifies this NAISYS instance.",
fields: [{ key: "HOST_ACCESS_KEY", label: "Host Access Key" }],
},
{
type: "fields",
comment: "Local configuration",
fields: [
{
key: "NAISYS_FOLDER",
label: "NAISYS Data Folder",
defaultValue: cwdWithTilde(),
},
],
},
],
};
}
return {
title: "NAISYS Setup",
sections: [
{
type: "fields",
comment: "Agent home files and NAISYS specific databases will be stored here",
fields: [
{
key: "NAISYS_FOLDER",
label: "NAISYS Data Folder",
defaultValue: cwdWithTilde(),
},
{
key: "NAISYS_HOSTNAME",
label: "Hostname",
defaultValue: os.hostname(),
},
],
},
{
type: "providers",
comment: "Leave API keys blank if not using the service. OpenAI Codex Subscription is connected later through Supervisor Variables.",
label: "AI Providers",
options: [
{
name: "OpenAI",
fields: [{ key: "OPENAI_API_KEY", label: "OpenAI API Key" }],
},
{
name: CODEX_SUBSCRIPTION_PROVIDER_NAME,
fields: [],
onSelected: options.onCodexSubscriptionSelected,
},
{
name: "Google",
fields: [
{ key: "GOOGLE_API_KEY", label: "Google API Key" },
{
key: "GOOGLE_SEARCH_ENGINE_ID",
label: "Google Search Engine ID",
},
],
},
{
name: "Anthropic",
fields: [{ key: "ANTHROPIC_API_KEY", label: "Anthropic API Key" }],
},
{
name: "XAI",
fields: [{ key: "XAI_API_KEY", label: "XAI API Key" }],
},
{
name: "OpenRouter",
fields: [
{ key: "OPENROUTER_API_KEY", label: "OpenRouter API Key" },
],
},
],
},
{
type: "fields",
comment: "Spend limits apply to all agents using this .env file",
fields: [
{ key: "SPEND_LIMIT_DOLLARS", label: "Spend Limit (dollars)" },
{ key: "SPEND_LIMIT_HOURS", label: "Spend Limit Period (hours)" },
],
},
...(integratedHub
? [
{
type: "fields",
comment: "Integrated hub/supervisor server (--integrated-hub mode)",
fields: [
{ key: "SERVER_PORT", label: "Server Port" },
{
key: "ALLOW_PASSWORD_LOGIN",
label: "Allow Supervisor Password Sign-in",
},
],
},
]
: []),
],
};
}
/** Available models ordered by preference (excludes none/mock) */
const availableModels = builtInLlmModels.filter((m) => m.apiKeyVar &&
m.apiType !== LlmApiType.None &&
m.apiType !== LlmApiType.Mock);
const preferredCodexModel = availableModels.find((m) => m.apiType === LlmApiType.OpenAIOAuth);
function hasConfiguredCredentials(model) {
return Boolean(process.env[model.apiKeyVar] ||
(model.apiType === LlmApiType.OpenAIOAuth &&
process.env[CODEX_REFRESH_TOKEN_VAR]));
}
export function printCodexSubscriptionSetupInstructions() {
console.log("\n OpenAI Codex Subscription selected. After Supervisor opens, go to Variables and click OpenAI Codex OAuth Setup.");
console.log(" Finish that flow to connect your subscription and save the required variables.\n");
}
/**
* If no agent path was provided and no agent configs exist in cwd,
* offer to create a default assistant.yaml. Prefer a configured provider, but
* use OpenAI Codex when the subscription provider was selected so first-time
* users can complete subscription setup from Supervisor's Variables page after
* startup.
*/
export async function ensureAgentConfig(agentPath, options = {}) {
if (agentPath)
return;
// Check if there are any non-admin agents that would load from cwd
try {
const users = loadAgentConfigs("");
const hasNonAdmin = Array.from(users.values()).some((u) => u.username !== "admin");
if (hasNonAdmin)
return;
}
catch {
// No configs found, fall through to offer creation
}
const configuredModel = availableModels.find(hasConfiguredCredentials);
const model = (options.useCodexSubscription ? preferredCodexModel : undefined) ??
configuredModel;
if (!model) {
console.log("\n No agent configs found and no AI provider configured. Run with --setup to configure one.");
return;
}
if (!process.stdin.isTTY)
return;
const answer = await askQuestion(`\n No agent config found. Create a default assistant using ${model.label}? (Y/n) `);
if (answer && !answer.toLowerCase().startsWith("y")) {
return;
}
const config = buildDefaultAgentConfig("andy");
config.shellModel = model.key;
config.supervisorApiHints = true;
const filePath = path.resolve("assistant.yaml");
fs.writeFileSync(filePath, yaml.dump(config));
console.log(` Created ${filePath} using ${model.key}\n`);
}
//# sourceMappingURL=naisysSetup.js.map