@stratusagent/cli 0.9.1 → 0.9.2
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/README.md +78 -990
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +201 -61
- package/dist/index.js.map +1 -1
- package/package.json +22 -21
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type JsonValue, type Session, type StratusEvent } from '@stratusagent/core';
|
|
2
|
-
import { type CredentialProviderName, type RuntimeSelection, type RuntimeConfig, type StratusProviderName } from '@stratusagent/state';
|
|
2
|
+
import { type CredentialProviderName, type RuntimeSelection, type RuntimeConfig, type StratusConfigFile, type StratusProviderName } from '@stratusagent/state';
|
|
3
3
|
import { type ServiceRunner } from './service.ts';
|
|
4
4
|
export { installService, launchdPlist, readServiceCommand, readServiceStatus, servicePlatform, serviceUnitPath, startService, stopService, systemdUnit, uninstallService, SERVICE_LABEL, type ServiceRunner, type ServiceStatus, } from './service.ts';
|
|
5
5
|
export { createLogWriter, currentLogPosition, truncateRedirectLogs, formatLogRecord, parseLogLines, readRecentRecords, tailLog, type LogRecord, } from './logs.ts';
|
|
@@ -207,7 +207,7 @@ export interface ParsedHelpCommand {
|
|
|
207
207
|
command: 'help';
|
|
208
208
|
}
|
|
209
209
|
export type ParsedCommand = ParsedRunCommand | ParsedChatCommand | ParsedDashboardCommand | ParsedSetupCommand | ParsedAgentNewCommand | ParsedAgentsCommand | ParsedSkillAddCommand | ParsedSkillsCommand | ParsedSchedulesCommand | ParsedDoctorCommand | ParsedLogsCommand | ParsedUpdateCommand | ParsedServiceCommand | ParsedServeCommand | ParsedHelpCommand;
|
|
210
|
-
export declare const CLI_VERSION = "0.9.
|
|
210
|
+
export declare const CLI_VERSION = "0.9.2";
|
|
211
211
|
/**
|
|
212
212
|
* The range every package declares in its `engines` field. Two floors, not
|
|
213
213
|
* one: `node:sqlite` was unflagged in 22.13.0 on the LTS line and in 23.4.0
|
|
@@ -229,7 +229,7 @@ export declare const SUPPORTED_NODE_RANGE = ">=22.13 <23 || >=23.4";
|
|
|
229
229
|
* string is a bad reason to refuse to run.
|
|
230
230
|
*/
|
|
231
231
|
export declare const unsupportedNodeMessage: (version: string) => string | undefined;
|
|
232
|
-
declare const HELP_TEXT = "Stratus Agent CLI\n\nUsage:\n stratus setup\n stratus chat\n stratus chat --soul ./examples/souls/ava.md\n stratus run --prompt \"Use the demo tool\"\n stratus run \"Say hello\"\n ANTHROPIC_API_KEY=... stratus run --provider anthropic \"Say hello\"\n stratus run --soul ./examples/souls/ava.md \"Say hello\"\n echo \"Use the echo tool\" | stratus run --stdin\n STRATUS_PROVIDER=openai OPENAI_API_KEY=... stratus run \"Say hello\"\n stratus run --config ./stratus.config.json --provider openai \"Say hello\"\n stratus agents\n stratus agents --gateway http://127.0.0.1:4123\n stratus skill add stratuslabs/skill-code-review\n stratus skill add ./my-skills --skill code-review --agent ava\n stratus skills\n stratus schedules\n stratus schedules cancel <id>\n stratus doctor\n stratus update\n stratus update --check\n stratus service install\n stratus service status\n stratus logs -f\n stratus logs --agent ava -n 200\n stratus dashboard\n stratus dashboard --port 4123 --host 0.0.0.0 --no-open\n\nCommands:\n setup Menu-driven onboarding: pick a provider, sign in (Claude\n subscription or API key), create your agent, connect it to\n Slack, and test it \u2014 settings go to ~/.stratus/config.json,\n sign-ins and channel tokens to ~/.stratus/credentials.json\n (0600). Save & finish offers to install any optional\n package your answers imply (the Slack channel, the control\n API and dashboard) before it starts the daemon\n chat Talk with your agent \u2014 the conversation persists across turns\n and remembered facts accumulate; /exit or Ctrl+C to leave\n run Execute one local Stratus Agent session\n serve Run stratusd, the always-on gateway: durable sessions, the\n whole roster live at once (each agent on its own provider),\n delegation, and a watchdog \u2014 Ctrl+C / SIGTERM drains cleanly\n (--idle-timeout <seconds>, --approvals <headless|remote>,\n --no-events, --no-log-file, --config <path>); everything it\n says is also written to ~/.stratus/logs, which\n \"stratus logs\" reads. With @stratusagent/control-api\n installed it also serves the HTTP + WebSocket control API\n on 127.0.0.1:4123 (--no-api, --api-host, --api-port, or\n the config file's \"api\" block)\n service Keep stratusd running under launchd (macOS) or systemd\n (Linux): install, uninstall, status, start, stop.\n Installing starts it now and at every login\n (--no-login installs without the login trigger,\n --config <path> pins the daemon to one config file)\n logs Read the daemon's log from any terminal: -f to follow,\n -n <count> for backlog, --agent / --session to filter,\n --format json for the raw records\n skill add Install skills from a GitHub repo (owner/repo or URL) or a\n local path into ~/.stratus/skills \u2014 whole directories, one\n per skill; works with skills published for other agents\n (skills.sh-style repos). Installed is not enabled: a soul\n opts in via skills:, or pass --agent <id> to enable now.\n --skill <id> picks from a multi-skill repo (repeatable),\n --force replaces an already-installed id\n skills List installed skills and which agents enable each\n (also: stratus skill list)\n schedules List every schedule the fleet has set \u2014 cadence, prompt,\n pre-authorized destination, next firing \u2014 straight from the\n daemon's database (--format json). \"stratus schedules\n cancel <id>\" stops the next firing and revokes the\n destination that was approved with it\n (also: stratus schedule list / schedule cancel <id>)\n agent new Create an agent identity (generates a human-ish name + avatar theme)\n agents List your agents: who they are, where their souls live, what\n they run on, what they remember (also: stratus agent list).\n --gateway <url> asks a running daemon instead of resolving\n locally, authenticating with ~/.stratus/gateway-token\n (override with --token or STRATUS_GATEWAY_TOKEN)\n doctor Show what a run would use right now \u2014 provider, model, soul \u2014\n and which file or environment variable decided each, then\n flag anything that would surprise you (--format json)\n update The whole upgrade dance, in the order that cannot lose\n data: stop stratusd, upgrade the package from npm, run\n pending state migrations, rewrite the service unit with\n current node/entrypoint paths, restart. --check reports\n what it would do without doing any of it (exits 1 when\n something is actionable). Works offline too \u2014 an\n unreachable npm skips the upgrade but still migrates and\n repairs the unit\n dashboard Open the web dashboard: finds a running daemon (or starts one),\n mints a single-use sign-in link, and opens your browser at it.\n Needs @stratusagent/control-api and @stratusagent/dashboard\n help Show this help message\n\nAgent options:\n --name Agent name (omit to have one generated)\n --instructions The agent's persona/instructions\n --format Output format for agent new: text, json, or soul (a ready-to-edit soul file)\n\nOptions:\n --prompt, -p Prompt to send to the local agent loop\n --stdin Read the prompt from stdin\n --provider Provider to use: anthropic, openai, or demo\n --model Model name for real providers (anthropic default: claude-opus-5, openai default: gpt-4.1-mini)\n --base-url Override the provider API base URL\n --soul Run as the agent defined by a soul file (markdown + frontmatter, see examples/souls)\n --config Config file path (run: load settings from it, setup: write it)\n --format Output format: text or json (default: text)\n --no-events Hide event-by-event progress lines in text mode\n --approvals run/chat: tool approval mode \u2014 always, ask, or never (default: always)\n serve: how the daemon reaches a human \u2014 headless (refuse every\n gated call) or remote (ask in Slack). Default headless, or\n the config file's \"approvals.mode\"\n --max-turns Maximum provider turns per run (default: 8)\n --port dashboard: port for a daemon it starts (default: 4123)\n --host dashboard: host for a daemon it starts (default: 127.0.0.1)\n --no-open Do not open the browser automatically\n --gateway agents: read the roster from a running daemon's control API\n --token Bearer token for --gateway (default: ~/.stratus/gateway-token)\n --no-api serve: do not serve the control API\n --api-host serve: control API interface (default: 127.0.0.1)\n --api-port serve: control API port (default: 4123, 0 for any free port)\n --help, -h Show this help message\n\nConfig file:\n The CLI looks for ./stratus.config.json first, then a path from --config / STRATUS_CONFIG,\n then the global ~/.stratus/config.json written by `stratus setup`.\n A \"soul\" key (or STRATUS_SOUL) points at a soul file so every run uses that agent.\n\nPlugins (tools):\n Capability is optional: install a package, then list it under \"plugins\" in a\n TRUSTED config (--config, STRATUS_CONFIG, or ~/.stratus/config.json), keyed by\n package name. A plugin runs inside this process, so an auto-discovered\n project-local stratus.config.json may not enable one.\n\n \"plugins\": {\n \"@stratusagent/tool-fs\": { \"enabled\": true, \"roots\": [\"~/notes\"],\n \"agents\": { \"ava\": { \"roots\": [\"~/work/ava\"] } } },\n \"@stratusagent/tool-web\": { \"enabled\": true }\n }\n\n Available: @stratusagent/tool-fs (fs.read/list/search/write),\n @stratusagent/tool-shell (shell.run), @stratusagent/tool-web (web.fetch),\n @stratusagent/tool-browser (browser.goto/read/screenshot/act).\n Installing one grants no agent anything \u2014 each soul lists what it may call.\n\nSoul files:\n A soul file is markdown with frontmatter (name, provider, model, tools, skills, credentials)\n followed by the agent's persona in prose. See examples/souls/ava.md.\n \"tools\" takes exact names or a whole toolset: tools: [fs.read, fs.search] or\n tools: [fs.*]. Omitted means every registered tool.\n \"skills\" is the same allowlist shape over installed skills (see stratus\n skills), except omitted means none \u2014 a skill is enabled per agent, never\n by being installed.\n";
|
|
232
|
+
declare const HELP_TEXT = "Stratus Agent CLI\n\nUsage:\n stratus setup\n stratus chat\n stratus chat --soul ./examples/souls/ava.md\n stratus run --prompt \"Use the demo tool\"\n stratus run \"Say hello\"\n ANTHROPIC_API_KEY=... stratus run --provider anthropic \"Say hello\"\n stratus run --soul ./examples/souls/ava.md \"Say hello\"\n echo \"Use the echo tool\" | stratus run --stdin\n STRATUS_PROVIDER=openai OPENAI_API_KEY=... stratus run \"Say hello\"\n stratus run --config ./stratus.config.json --provider openai \"Say hello\"\n stratus agents\n stratus agents --gateway http://127.0.0.1:4123\n stratus skill add stratuslabs/skill-code-review\n stratus skill add ./my-skills --skill code-review --agent ava\n stratus skills\n stratus schedules\n stratus schedules cancel <id>\n stratus doctor\n stratus update\n stratus update --check\n stratus service install\n stratus service status\n stratus logs -f\n stratus logs --agent ava -n 200\n stratus dashboard\n stratus dashboard --port 4123 --host 0.0.0.0 --no-open\n\nCommands:\n setup Menu-driven onboarding: pick a provider, sign in (Claude\n subscription or API key), create your agent, connect it to\n Slack, and test it \u2014 settings go to ~/.stratus/config.json,\n sign-ins and channel tokens to ~/.stratus/credentials.json\n (0600). Save & finish offers to install any optional\n package your answers imply (the Slack channel, the control\n API and dashboard) before it starts the daemon\n chat Talk with your agent \u2014 the conversation persists across turns\n and remembered facts accumulate; /exit or Ctrl+C to leave\n run Execute one local Stratus Agent session\n serve Run stratusd, the always-on gateway: durable sessions, the\n whole roster live at once (each agent on its own provider),\n delegation, and a watchdog \u2014 Ctrl+C / SIGTERM drains cleanly\n (--idle-timeout <seconds>, --approvals <headless|remote>,\n --no-events, --no-log-file, --config <path>); everything it\n says is also written to ~/.stratus/logs, which\n \"stratus logs\" reads. With @stratusagent/control-api\n installed it also serves the HTTP + WebSocket control API\n on 127.0.0.1:4123 (--no-api, --api-host, --api-port, or\n the config file's \"api\" block)\n service Keep stratusd running under launchd (macOS) or systemd\n (Linux): install, uninstall, status, start, stop.\n Installing starts it now and at every login\n (--no-login installs without the login trigger,\n --config <path> pins the daemon to one config file)\n logs Read the daemon's log from any terminal: -f to follow,\n -n <count> for backlog, --agent / --session to filter,\n --format json for the raw records\n skill add Install skills from a GitHub repo (owner/repo or URL) or a\n local path into ~/.stratus/skills \u2014 whole directories, one\n per skill; works with skills published for other agents\n (skills.sh-style repos). Installed is not enabled: a soul\n opts in via skills:, or pass --agent <id> to enable now.\n --skill <id> picks from a multi-skill repo (repeatable),\n --force replaces an already-installed id\n skills List installed skills and which agents enable each\n (also: stratus skill list)\n schedules List every schedule the fleet has set \u2014 cadence, prompt,\n pre-authorized destination, next firing \u2014 straight from the\n daemon's database (--format json). \"stratus schedules\n cancel <id>\" stops the next firing and revokes the\n destination that was approved with it\n (also: stratus schedule list / schedule cancel <id>)\n agent new Create an agent identity (generates a human-ish name + avatar theme)\n agents List your agents: who they are, where their souls live, what\n they run on, what they remember (also: stratus agent list).\n --gateway <url> asks a running daemon instead of resolving\n locally, authenticating with ~/.stratus/gateway-token\n (override with --token or STRATUS_GATEWAY_TOKEN)\n doctor Show what a run would use right now \u2014 provider, model, soul \u2014\n and which file or environment variable decided each, then\n flag anything that would surprise you (--format json)\n update The whole upgrade dance, in the order that cannot lose\n data: stop stratusd, upgrade the package from npm, run\n pending state migrations, rewrite the service unit with\n current node/entrypoint paths, restart. --check reports\n what it would do without doing any of it (exits 1 when\n something is actionable). Works offline too \u2014 an\n unreachable npm skips the upgrade but still migrates and\n repairs the unit\n dashboard Open the web dashboard: finds a running daemon (or starts one),\n mints a single-use sign-in link, and opens your browser at it.\n Needs @stratusagent/control-api and @stratusagent/dashboard\n help Show this help message\n\nAgent options:\n --name Agent name (omit to have one generated)\n --instructions The agent's persona/instructions\n --format Output format for agent new: text, json, or soul (a ready-to-edit soul file)\n\nOptions:\n --prompt, -p Prompt to send to the local agent loop\n --stdin Read the prompt from stdin\n --provider Provider to use: anthropic, openai, codex, or demo\n --model Model name for real providers (anthropic default: claude-opus-5, openai default: gpt-4.1-mini, codex default: gpt-5.5)\n --base-url Override the provider API base URL\n --soul Run as the agent defined by a soul file (markdown + frontmatter, see examples/souls)\n --config Config file path (run: load settings from it, setup: write it)\n --format Output format: text or json (default: text)\n --no-events Hide event-by-event progress lines in text mode\n --approvals run/chat: tool approval mode \u2014 always, ask, or never (default: always)\n serve: how the daemon reaches a human \u2014 headless (refuse every\n gated call) or remote (ask in Slack). Default headless, or\n the config file's \"approvals.mode\"\n --max-turns Maximum provider turns per run (default: 8)\n --port dashboard: port for a daemon it starts (default: 4123)\n --host dashboard: host for a daemon it starts (default: 127.0.0.1)\n --no-open Do not open the browser automatically\n --gateway agents: read the roster from a running daemon's control API\n --token Bearer token for --gateway (default: ~/.stratus/gateway-token)\n --no-api serve: do not serve the control API\n --api-host serve: control API interface (default: 127.0.0.1)\n --api-port serve: control API port (default: 4123, 0 for any free port)\n --help, -h Show this help message\n\nConfig file:\n The CLI looks for ./stratus.config.json first, then a path from --config / STRATUS_CONFIG,\n then the global ~/.stratus/config.json written by `stratus setup`.\n A \"soul\" key (or STRATUS_SOUL) points at a soul file so every run uses that agent.\n\nPlugins (tools):\n Capability is optional: install a package, then list it under \"plugins\" in a\n TRUSTED config (--config, STRATUS_CONFIG, or ~/.stratus/config.json), keyed by\n package name. A plugin runs inside this process, so an auto-discovered\n project-local stratus.config.json may not enable one.\n\n \"plugins\": {\n \"@stratusagent/tool-fs\": { \"enabled\": true, \"roots\": [\"~/notes\"],\n \"agents\": { \"ava\": { \"roots\": [\"~/work/ava\"] } } },\n \"@stratusagent/tool-web\": { \"enabled\": true }\n }\n\n Available: @stratusagent/tool-fs (fs.read/list/search/write),\n @stratusagent/tool-shell (shell.run), @stratusagent/tool-web (web.fetch),\n @stratusagent/tool-browser (browser.goto/read/screenshot/act).\n Installing one grants no agent anything \u2014 each soul lists what it may call.\n\nSoul files:\n A soul file is markdown with frontmatter (name, provider, model, tools, skills, credentials)\n followed by the agent's persona in prose. See examples/souls/ava.md.\n \"tools\" takes exact names or a whole toolset: tools: [fs.read, fs.search] or\n tools: [fs.*]. Omitted means every registered tool.\n \"skills\" is the same allowlist shape over installed skills (see stratus\n skills), except omitted means none \u2014 a skill is enabled per agent, never\n by being installed.\n";
|
|
233
233
|
declare const stringifyValue: (value: JsonValue) => string;
|
|
234
234
|
export declare const parseCommand: (argv: string[], env?: CliEnvironment) => ParsedCommand;
|
|
235
235
|
export declare const formatEvent: (event: StratusEvent) => string | null;
|
|
@@ -336,6 +336,23 @@ export declare const runSkillAdd: (command: ParsedSkillAddCommand, streams: CliS
|
|
|
336
336
|
*/
|
|
337
337
|
export declare const runSchedules: (command: ParsedSchedulesCommand, streams: CliStreams, env?: CliEnvironment) => Promise<number>;
|
|
338
338
|
export declare const runSkills: (streams: CliStreams, env?: CliEnvironment) => Promise<number>;
|
|
339
|
+
/**
|
|
340
|
+
* The provider/model a newly created soul pins. Frontmatter pins what a run
|
|
341
|
+
* from this directory would actually use: env vars outrank the active
|
|
342
|
+
* config file (project-local or explicit first, global otherwise), and no
|
|
343
|
+
* provider anywhere falls back to demo — the exact precedence of stratus
|
|
344
|
+
* run. Demo produces no model at all: the soul keeps following the
|
|
345
|
+
* machine's configuration instead of demanding credentials nothing has
|
|
346
|
+
* signed in for. The active config's model was written for the provider
|
|
347
|
+
* named in that config, so it only travels into the soul when they still
|
|
348
|
+
* match; otherwise the selected provider's own default model stands in.
|
|
349
|
+
*
|
|
350
|
+
* Exported for tests: the interactive path that consumes it needs a TTY.
|
|
351
|
+
*/
|
|
352
|
+
export declare const soulPinForNewAgent: (activeConfig: StratusConfigFile, processEnv: NodeJS.ProcessEnv) => {
|
|
353
|
+
provider: StratusProviderName;
|
|
354
|
+
model?: string;
|
|
355
|
+
};
|
|
339
356
|
export declare const runAgentNew: (command: ParsedAgentNewCommand, streams: CliStreams, env?: CliEnvironment) => Promise<number>;
|
|
340
357
|
/**
|
|
341
358
|
* `stratus dashboard` — open the web UI against a running daemon, starting
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAaL,KAAK,SAAS,EAGd,KAAK,OAAO,EACZ,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -17,8 +17,9 @@ import { createLocalCommandExecutor, } from '@stratusagent/executor-local';
|
|
|
17
17
|
import { loadPlugins } from '@stratusagent/plugins';
|
|
18
18
|
import { createFileCommandWhitelist, createPermissionPolicy, describeCommandScope, } from '@stratusagent/permissions';
|
|
19
19
|
import { DEFAULT_ANTHROPIC_MODEL, redactAnthropicRawTurns, } from '@stratusagent/provider-anthropic';
|
|
20
|
+
import { DEFAULT_CODEX_MODEL } from '@stratusagent/provider-codex';
|
|
20
21
|
import { canonicalDestination, createForgetTool, createRecallTool, createRememberTool, defineAgent, describeCadence, describeSchedule, FORGET_TOOL_NAME, MEMORY_TOOL_NAME, formatSoul, generateAgentName, parseSoul, } from '@stratusagent/agents';
|
|
21
|
-
import { agentsDirPath, claimSoulFile, collectAvailableModels as collectModels, createDemoTool, createFileMemoryStore, createRuntimeProvider, credentialsPath, defaultApiKeyEnvName, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_STRATUS_AGENT, discoverActiveConfig, globalConfigPath, loadChannelCredentials, loadCredentials, installSkillsFromDirectory, loadOperatorSkills, loadRosterSouls, skillsDirPath, listAgentSummaries, loadSoulFile, memoryFilePath, migrateLegacyMemory, parseProviderName, readNonEmptyString, readProcessEnv, resolveConfiguredSoul, logsDirPath, readWorkingDirectory, resolveAgentApprovals, DEFAULT_CONFIG_FILENAME, loadConfigFile, resolveConfigLocation, resolveRuntimeConfig as resolveStateRuntimeConfig, saveChannelCredentials, saveConfigFile, readTrustedConfigBlock, saveCredentials, servedRuntimes, verifyProviderKey, withLegacyDefaultMemories, workspacesDirPath, gatewayInfoPath, gatewayTokenPath, newerStateMessage, pendingStateMigrations, readStateStamp, runStateMigrations, STATE_SCHEMA_VERSION, } from '@stratusagent/state';
|
|
22
|
+
import { agentsDirPath, claimSoulFile, collectAvailableModels as collectModels, createDemoTool, createFileMemoryStore, createRuntimeProvider, credentialsPath, defaultApiKeyEnvName, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_STRATUS_AGENT, discoverActiveConfig, globalConfigPath, loadChannelCredentials, loadCredentials, installSkillsFromDirectory, loadOperatorSkills, loadRosterSouls, skillsDirPath, listAgentSummaries, loadSoulFile, memoryFilePath, migrateLegacyMemory, parseProviderName, readNonEmptyString, readProcessEnv, resolveConfiguredSoul, logsDirPath, readWorkingDirectory, resolveAgentApprovals, DEFAULT_CONFIG_FILENAME, loadConfigFile, resolveConfigLocation, resolveRuntimeConfig as resolveStateRuntimeConfig, saveChannelCredentials, saveConfigFile, readTrustedConfigBlock, saveCredentials, servedRuntimes, CREDENTIAL_PROVIDER_NAMES, verifyProviderKey, withLegacyDefaultMemories, workspacesDirPath, gatewayInfoPath, gatewayTokenPath, newerStateMessage, pendingStateMigrations, readStateStamp, runStateMigrations, STATE_SCHEMA_VERSION, } from '@stratusagent/state';
|
|
22
23
|
import { installService, readServiceCommand, readServiceStatus, servicePlatform, serviceUnitPath, startService, stopService, uninstallService, } from "./service.js";
|
|
23
24
|
export { installService, launchdPlist, readServiceCommand, readServiceStatus, servicePlatform, serviceUnitPath, startService, stopService, systemdUnit, uninstallService, SERVICE_LABEL, } from "./service.js";
|
|
24
25
|
import { createLogWriter, currentLogPosition, truncateRedirectLogs, formatLogRecord, readRecentRecords, tailLog, } from "./logs.js";
|
|
@@ -122,7 +123,7 @@ const defaultPackageInstaller = async (packages) => {
|
|
|
122
123
|
});
|
|
123
124
|
});
|
|
124
125
|
};
|
|
125
|
-
export const CLI_VERSION = '0.9.
|
|
126
|
+
export const CLI_VERSION = '0.9.2';
|
|
126
127
|
const DASHBOARD_TITLE = 'Stratus Agent Dashboard';
|
|
127
128
|
/**
|
|
128
129
|
* The range every package declares in its `engines` field. Two floors, not
|
|
@@ -265,8 +266,8 @@ Agent options:
|
|
|
265
266
|
Options:
|
|
266
267
|
--prompt, -p Prompt to send to the local agent loop
|
|
267
268
|
--stdin Read the prompt from stdin
|
|
268
|
-
--provider Provider to use: anthropic, openai, or demo
|
|
269
|
-
--model Model name for real providers (anthropic default: ${DEFAULT_ANTHROPIC_MODEL}, openai default: gpt-4.1-mini)
|
|
269
|
+
--provider Provider to use: anthropic, openai, codex, or demo
|
|
270
|
+
--model Model name for real providers (anthropic default: ${DEFAULT_ANTHROPIC_MODEL}, openai default: gpt-4.1-mini, codex default: ${DEFAULT_CODEX_MODEL})
|
|
270
271
|
--base-url Override the provider API base URL
|
|
271
272
|
--soul Run as the agent defined by a soul file (markdown + frontmatter, see examples/souls)
|
|
272
273
|
--config Config file path (run: load settings from it, setup: write it)
|
|
@@ -1082,30 +1083,42 @@ export const warnOnCredentialOverride = async (runtime, streams, env = {}) => {
|
|
|
1082
1083
|
if (runtime.provider === 'demo') {
|
|
1083
1084
|
return;
|
|
1084
1085
|
}
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1086
|
+
const credentials = await loadCredentials(env);
|
|
1087
|
+
const processEnv = readProcessEnv(env);
|
|
1089
1088
|
// The resolver records the variable that actually won — guessing it here
|
|
1090
1089
|
// would name the wrong one whenever a custom apiKeyEnv supplied the key,
|
|
1091
1090
|
// sending the reader to unset something that was never the cause.
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
// once the primary is already failing — the worst moment to discover it.
|
|
1095
|
-
// The fallback path consults the provider's own conventional variable.
|
|
1096
|
-
const processEnv = readProcessEnv(env);
|
|
1097
|
-
const fallbackVar = runtime.fallback?.provider === 'anthropic'
|
|
1098
|
-
&& runtime.fallback.apiKey !== undefined
|
|
1099
|
-
&& readNonEmptyString(processEnv.ANTHROPIC_API_KEY) === runtime.fallback.apiKey
|
|
1100
|
-
? 'ANTHROPIC_API_KEY'
|
|
1091
|
+
const primaryOverride = runtime.apiKey !== undefined
|
|
1092
|
+
? { provider: runtime.provider, envVar: runtime.apiKeyEnvVar }
|
|
1101
1093
|
: undefined;
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1094
|
+
// Both subscription sign-ins demote the same way: a Claude setup token
|
|
1095
|
+
// and the codex ChatGPT marker each lose to an environment API key, and
|
|
1096
|
+
// the run otherwise looks identical while billing per token.
|
|
1097
|
+
for (const target of ['anthropic', 'codex']) {
|
|
1098
|
+
if (credentials[target]?.type !== 'oauth_token') {
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
const planName = target === 'anthropic'
|
|
1102
|
+
? 'Claude subscription sign-in'
|
|
1103
|
+
: 'ChatGPT (codex login) sign-in';
|
|
1104
|
+
const primary = primaryOverride?.provider === target ? primaryOverride.envVar : undefined;
|
|
1105
|
+
// A fallback demoted the same way costs exactly as much, and only bites
|
|
1106
|
+
// once the primary is already failing — the worst moment to discover it.
|
|
1107
|
+
// The fallback path consults the provider's own conventional variable.
|
|
1108
|
+
const conventionalVar = defaultApiKeyEnvName(target);
|
|
1109
|
+
const fallbackVar = runtime.fallback?.provider === target
|
|
1110
|
+
&& runtime.fallback.apiKey !== undefined
|
|
1111
|
+
&& readNonEmptyString(processEnv[conventionalVar]) === runtime.fallback.apiKey
|
|
1112
|
+
? conventionalVar
|
|
1113
|
+
: undefined;
|
|
1114
|
+
const culprit = primary ?? fallbackVar;
|
|
1115
|
+
if (!culprit) {
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
writeLine(streams.stderr, `Warning: ${culprit} in your environment outranks the ${planName} saved by \`stratus setup\`, `
|
|
1119
|
+
+ `so ${primary ? 'this run is' : 'a fallback retry would be'} billed per token instead of through your plan. `
|
|
1120
|
+
+ 'Unset it to use the subscription, or run `stratus doctor` to see everything that is being overridden.');
|
|
1105
1121
|
}
|
|
1106
|
-
writeLine(streams.stderr, `Warning: ${culprit} in your environment outranks the Claude subscription sign-in saved by \`stratus setup\`, `
|
|
1107
|
-
+ `so ${primary ? 'this run is' : 'a fallback retry would be'} billed per token instead of through your plan. `
|
|
1108
|
-
+ 'Unset it to use the subscription, or run `stratus doctor` to see everything that is being overridden.');
|
|
1109
1122
|
};
|
|
1110
1123
|
const createApprovalPolicy = (mode, streams, env, ask) => {
|
|
1111
1124
|
if (mode === 'always') {
|
|
@@ -2051,15 +2064,21 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2051
2064
|
header: stratusHeaderLines,
|
|
2052
2065
|
consumeNotices: () => recentNotices.splice(0),
|
|
2053
2066
|
});
|
|
2054
|
-
const defaultModelFor = (provider) => provider === 'openai'
|
|
2067
|
+
const defaultModelFor = (provider) => provider === 'openai'
|
|
2068
|
+
? DEFAULT_OPENAI_MODEL
|
|
2069
|
+
: provider === 'codex'
|
|
2070
|
+
? DEFAULT_CODEX_MODEL
|
|
2071
|
+
: DEFAULT_ANTHROPIC_MODEL;
|
|
2055
2072
|
// Widened to CliProviderName only so setup can ask about the provider it
|
|
2056
2073
|
// currently has selected, `demo` included; the rule itself is the shared one.
|
|
2057
|
-
const defaultKeyEnvFor = (provider) => defaultApiKeyEnvName(provider === '
|
|
2058
|
-
const credentialLabel = (credential) => credential.type === 'oauth_token'
|
|
2074
|
+
const defaultKeyEnvFor = (provider) => defaultApiKeyEnvName(provider === 'demo' ? 'anthropic' : provider);
|
|
2075
|
+
const credentialLabel = (provider, credential) => credential.type === 'oauth_token'
|
|
2076
|
+
? (provider === 'codex' ? 'ChatGPT sign-in' : 'Claude subscription')
|
|
2077
|
+
: 'API key';
|
|
2059
2078
|
const providerSignInStatus = (provider) => {
|
|
2060
2079
|
const credential = state.credentials[provider];
|
|
2061
2080
|
if (credential) {
|
|
2062
|
-
return `signed in (${credentialLabel(credential)})`;
|
|
2081
|
+
return `signed in (${credentialLabel(provider, credential)})`;
|
|
2063
2082
|
}
|
|
2064
2083
|
if (readNonEmptyString(processEnv[defaultKeyEnvFor(provider)])) {
|
|
2065
2084
|
return `using ${defaultKeyEnvFor(provider)} from your environment`;
|
|
@@ -2068,10 +2087,10 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2068
2087
|
};
|
|
2069
2088
|
const providersSummary = () => {
|
|
2070
2089
|
const parts = [];
|
|
2071
|
-
for (const provider of
|
|
2090
|
+
for (const provider of CREDENTIAL_PROVIDER_NAMES) {
|
|
2072
2091
|
const credential = state.credentials[provider];
|
|
2073
2092
|
if (credential) {
|
|
2074
|
-
parts.push(`${provider} (${credentialLabel(credential)})`);
|
|
2093
|
+
parts.push(`${provider} (${credentialLabel(provider, credential)})`);
|
|
2075
2094
|
}
|
|
2076
2095
|
else if (readNonEmptyString(processEnv[defaultKeyEnvFor(provider)])) {
|
|
2077
2096
|
parts.push(`${provider} (env key)`);
|
|
@@ -2099,7 +2118,9 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2099
2118
|
const credential = state.credentials[state.provider];
|
|
2100
2119
|
if (credential) {
|
|
2101
2120
|
return credential.type === 'oauth_token'
|
|
2102
|
-
?
|
|
2121
|
+
? (state.provider === 'codex'
|
|
2122
|
+
? 'using your ChatGPT (codex login) sign-in'
|
|
2123
|
+
: 'signed in with your Claude subscription')
|
|
2103
2124
|
: 'signed in with an API key';
|
|
2104
2125
|
}
|
|
2105
2126
|
const keyEnv = state.apiKeyEnv ?? defaultKeyEnvFor(state.provider);
|
|
@@ -2233,6 +2254,59 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2233
2254
|
writeLine(streams.stdout, `! Could not reach the API to verify (${verdict.detail}). Saved the key anyway — it will be checked on your first run.`);
|
|
2234
2255
|
}
|
|
2235
2256
|
};
|
|
2257
|
+
const signInCodex = async () => {
|
|
2258
|
+
const signedIn = state.credentials.codex !== undefined;
|
|
2259
|
+
const answer = await prompter.select('How should Stratus connect to Codex?', [
|
|
2260
|
+
'ChatGPT subscription — uses this machine\'s `codex login` sign-in, no per-token cost',
|
|
2261
|
+
'OpenAI API key — pay per use (platform.openai.com)',
|
|
2262
|
+
'Skip for now',
|
|
2263
|
+
...(signedIn ? ['Sign out'] : []),
|
|
2264
|
+
]);
|
|
2265
|
+
if (signedIn && answer.kind === 'index' && answer.index === 3) {
|
|
2266
|
+
delete state.credentials.codex;
|
|
2267
|
+
state.credentialsDirty = true;
|
|
2268
|
+
writeLine(streams.stdout, 'Signed out of Codex. (A `codex login` sign-in, if any, stays with codex itself — run `codex logout` to clear it.)');
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
if (answer.kind !== 'index' || answer.index === 2) {
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
if (answer.index === 1) {
|
|
2275
|
+
const key = await prompter.askSecret('Paste your OpenAI API key (Enter to skip; input is hidden): ');
|
|
2276
|
+
if (!key) {
|
|
2277
|
+
writeLine(streams.stdout, 'Skipped — you can sign in any time by re-running this menu.');
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
writeLine(streams.stdout, 'Checking the key against the OpenAI API…');
|
|
2281
|
+
const verdict = await verifyProviderKey('codex', key, undefined, env.fetch ?? globalThis.fetch);
|
|
2282
|
+
if (verdict.status === 'ok') {
|
|
2283
|
+
storeCredential('codex', { type: 'api_key', value: key });
|
|
2284
|
+
writeLine(streams.stdout, '✓ Key verified — Codex runs will bill this OpenAI API key.');
|
|
2285
|
+
}
|
|
2286
|
+
else if (verdict.status === 'rejected') {
|
|
2287
|
+
writeLine(streams.stdout, `✗ OpenAI rejected that key (${verdict.detail}). It was NOT saved — check platform.openai.com and try again from this menu.`);
|
|
2288
|
+
}
|
|
2289
|
+
else {
|
|
2290
|
+
storeCredential('codex', { type: 'api_key', value: key });
|
|
2291
|
+
writeLine(streams.stdout, `! Could not reach the OpenAI API to verify (${verdict.detail}). Saved the key anyway — it will be checked on your first run.`);
|
|
2292
|
+
}
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
// Default: the machine's own ChatGPT sign-in. Codex keeps those tokens
|
|
2296
|
+
// in its own auth store; Stratus records only that this machine uses
|
|
2297
|
+
// it, so nothing secret is written here.
|
|
2298
|
+
writeLine(streams.stdout, 'Your ChatGPT plan covers usage made through Codex.');
|
|
2299
|
+
writeLine(streams.stdout, 'If you have not signed in yet, run this in another terminal on this machine:');
|
|
2300
|
+
writeLine(streams.stdout, ' codex login');
|
|
2301
|
+
writeLine(streams.stdout, '(requires the Codex CLI: npm install -g @openai/codex)');
|
|
2302
|
+
const confirmed = await prompter.select('Use this machine\'s codex sign-in?', ['Yes — codex is (or will be) signed in here', 'Skip for now']);
|
|
2303
|
+
if (confirmed.kind !== 'index' || confirmed.index !== 0) {
|
|
2304
|
+
writeLine(streams.stdout, 'Skipped — you can sign in any time by re-running this menu.');
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
storeCredential('codex', { type: 'oauth_token', value: 'chatgpt' });
|
|
2308
|
+
writeLine(streams.stdout, '✓ Recorded — Codex runs use this machine\'s ChatGPT sign-in. It is verified on your first run.');
|
|
2309
|
+
};
|
|
2236
2310
|
// Changing the default provider invalidates settings that were chosen
|
|
2237
2311
|
// for the old one: the model and apiKeyEnv are cleared (defaults take
|
|
2238
2312
|
// over), while the openai base URL is kept — it belongs to the openai
|
|
@@ -2292,17 +2366,25 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2292
2366
|
const answer = await prompter.select('Providers — sign in to one or more:', [
|
|
2293
2367
|
`Claude (Anthropic) — ${providerSignInStatus('anthropic')}`,
|
|
2294
2368
|
`OpenAI-compatible — ${providerSignInStatus('openai')}`,
|
|
2369
|
+
`Codex (ChatGPT) — ${providerSignInStatus('codex')}`,
|
|
2295
2370
|
'Demo — built-in fake model, offline, no account',
|
|
2296
2371
|
'Back',
|
|
2297
2372
|
]);
|
|
2298
|
-
if (answer.kind !== 'index' || answer.index ===
|
|
2373
|
+
if (answer.kind !== 'index' || answer.index === 4) {
|
|
2299
2374
|
return;
|
|
2300
2375
|
}
|
|
2301
|
-
if (answer.index ===
|
|
2376
|
+
if (answer.index === 3) {
|
|
2302
2377
|
await switchDefaultProvider('demo');
|
|
2303
2378
|
writeLine(streams.stdout, 'Demo selected — no sign-in needed. Mention "echo" or "tool" in a prompt to see tool calls.');
|
|
2304
2379
|
return;
|
|
2305
2380
|
}
|
|
2381
|
+
if (answer.index === 2) {
|
|
2382
|
+
await signInCodex();
|
|
2383
|
+
if (state.credentials.codex) {
|
|
2384
|
+
await maybeSwitchDefault('codex');
|
|
2385
|
+
}
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2306
2388
|
if (answer.index === 1) {
|
|
2307
2389
|
await signInOpenAI();
|
|
2308
2390
|
if (state.credentials.openai) {
|
|
@@ -2349,10 +2431,10 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2349
2431
|
if (typed.includes(':')) {
|
|
2350
2432
|
const [providerPart, ...idParts] = typed.split(':');
|
|
2351
2433
|
const id = idParts.join(':').trim();
|
|
2352
|
-
if ((providerPart === 'anthropic' || providerPart === 'openai') && id) {
|
|
2434
|
+
if ((providerPart === 'anthropic' || providerPart === 'openai' || providerPart === 'codex') && id) {
|
|
2353
2435
|
return { provider: providerPart, id };
|
|
2354
2436
|
}
|
|
2355
|
-
writeLine(streams.stdout, 'Use provider:model, e.g. anthropic:claude-opus-5.');
|
|
2437
|
+
writeLine(streams.stdout, 'Use provider:model, e.g. anthropic:claude-opus-5 or codex:gpt-5.5.');
|
|
2356
2438
|
return undefined;
|
|
2357
2439
|
}
|
|
2358
2440
|
// A typed id that appears in the collected list belongs to that
|
|
@@ -2487,10 +2569,17 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2487
2569
|
? readNonEmptyString(processEnv.STRATUS_API_KEY)
|
|
2488
2570
|
: undefined)
|
|
2489
2571
|
?? readNonEmptyString(processEnv[defaultKeyEnvFor(fallbackProvider)]);
|
|
2490
|
-
const
|
|
2572
|
+
const candidate = envKey ? undefined : state.credentials[fallbackProvider];
|
|
2573
|
+
// A codex fallback consumes no endpoint URL, so a stored key bound to
|
|
2574
|
+
// one cannot serve it — the same skip resolveRuntimeConfig performs.
|
|
2575
|
+
const credential = fallbackProvider === 'codex' && candidate?.type === 'api_key' && candidate.baseUrl !== undefined
|
|
2576
|
+
? undefined
|
|
2577
|
+
: candidate;
|
|
2491
2578
|
const apiKey = envKey ?? (credential?.type === 'api_key' ? credential.value : undefined);
|
|
2492
|
-
|
|
2493
|
-
|
|
2579
|
+
// A codex oauth entry is the subscription marker, not a token to send.
|
|
2580
|
+
const codexSubscription = fallbackProvider === 'codex' && !apiKey && credential?.type === 'oauth_token';
|
|
2581
|
+
const authToken = fallbackProvider !== 'codex' && credential?.type === 'oauth_token' ? credential.value : undefined;
|
|
2582
|
+
if (!apiKey && !authToken && !codexSubscription) {
|
|
2494
2583
|
return undefined;
|
|
2495
2584
|
}
|
|
2496
2585
|
return {
|
|
@@ -2503,13 +2592,17 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2503
2592
|
?? (state.provider === 'openai' ? state.baseUrl : undefined)
|
|
2504
2593
|
?? DEFAULT_OPENAI_BASE_URL,
|
|
2505
2594
|
}
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2595
|
+
// The codex harness owns its endpoints; only anthropic can carry one.
|
|
2596
|
+
: fallbackProvider === 'codex'
|
|
2597
|
+
? {}
|
|
2598
|
+
: (() => {
|
|
2599
|
+
const url = (fallbackProvider === state.provider ? state.baseUrl : undefined)
|
|
2600
|
+
?? (credential?.type === 'api_key' ? credential.baseUrl : undefined);
|
|
2601
|
+
return url ? { baseUrl: url } : {};
|
|
2602
|
+
})()),
|
|
2511
2603
|
...(apiKey ? { apiKey } : {}),
|
|
2512
2604
|
...(authToken ? { authToken } : {}),
|
|
2605
|
+
...(codexSubscription ? { codexSubscription: true } : {}),
|
|
2513
2606
|
};
|
|
2514
2607
|
};
|
|
2515
2608
|
const buildTestRuntime = async () => {
|
|
@@ -2558,6 +2651,32 @@ export const runSetup = async (command, streams, env = {}) => {
|
|
|
2558
2651
|
...(fallback ? { fallback } : {}),
|
|
2559
2652
|
};
|
|
2560
2653
|
}
|
|
2654
|
+
if (state.provider === 'codex') {
|
|
2655
|
+
// The harness owns its endpoints: a key bound to one cannot be
|
|
2656
|
+
// honored on codex — resolveRuntimeConfig refuses this outright, and
|
|
2657
|
+
// the inline test mirrors it rather than quietly unbinding the key.
|
|
2658
|
+
if (boundUrl !== undefined) {
|
|
2659
|
+
writeLine(streams.stdout, `Your saved codex key is bound to ${boundUrl}, and codex does not use a custom base URL. Store the key without one to run on codex.`);
|
|
2660
|
+
return undefined;
|
|
2661
|
+
}
|
|
2662
|
+
const apiKey = envKey ?? (credential?.type === 'api_key' ? credential.value : undefined);
|
|
2663
|
+
// The subscription marker means the machine's own codex sign-in
|
|
2664
|
+
// serves the run — nothing more to resolve here.
|
|
2665
|
+
if (!apiKey && credential?.type !== 'oauth_token') {
|
|
2666
|
+
writeLine(streams.stdout, 'You are not signed in yet — pick option 1 first (or export CODEX_API_KEY).');
|
|
2667
|
+
return undefined;
|
|
2668
|
+
}
|
|
2669
|
+
const fallback = buildTestFallback();
|
|
2670
|
+
return {
|
|
2671
|
+
provider: 'codex',
|
|
2672
|
+
model,
|
|
2673
|
+
...(apiKey ? { apiKey } : {}),
|
|
2674
|
+
...(state.systemPrompt ? { systemPrompt: state.systemPrompt } : {}),
|
|
2675
|
+
...(env.fetch ? { fetch: env.fetch } : {}),
|
|
2676
|
+
...(soul ? { soul } : {}),
|
|
2677
|
+
...(fallback ? { fallback } : {}),
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2561
2680
|
const apiKey = envKey ?? (credential?.type === 'api_key' ? credential.value : undefined);
|
|
2562
2681
|
if (!apiKey) {
|
|
2563
2682
|
writeLine(streams.stdout, 'You are not signed in yet — pick option 1 first (or export OPENAI_API_KEY).');
|
|
@@ -3489,9 +3608,11 @@ export const collectDoctorReport = async (command, warn, env = {}) => {
|
|
|
3489
3608
|
const usedKeyEnvVar = resolved && resolved.provider !== 'demo' ? resolved.apiKeyEnvVar : undefined;
|
|
3490
3609
|
const usesAuthToken = resolved?.provider === 'anthropic' && resolved.authToken !== undefined;
|
|
3491
3610
|
const fallback = resolved && resolved.provider !== 'demo' ? resolved.fallback : undefined;
|
|
3492
|
-
for (const target of
|
|
3611
|
+
for (const target of CREDENTIAL_PROVIDER_NAMES) {
|
|
3493
3612
|
const stored = credentials[target];
|
|
3494
|
-
const label = stored?.type === 'oauth_token'
|
|
3613
|
+
const label = stored?.type === 'oauth_token'
|
|
3614
|
+
? (target === 'codex' ? 'ChatGPT sign-in (codex login)' : 'Claude subscription (Pro/Max)')
|
|
3615
|
+
: 'API key';
|
|
3495
3616
|
const isDefault = resolved?.provider === target;
|
|
3496
3617
|
const isFallback = fallback?.provider === target;
|
|
3497
3618
|
// A provider no resolved run consults is reported, never diagnosed:
|
|
@@ -3505,7 +3626,7 @@ export const collectDoctorReport = async (command, warn, env = {}) => {
|
|
|
3505
3626
|
});
|
|
3506
3627
|
continue;
|
|
3507
3628
|
}
|
|
3508
|
-
const envVar = isDefault ? usedKeyEnvVar : (target
|
|
3629
|
+
const envVar = isDefault ? usedKeyEnvVar : defaultApiKeyEnvName(target);
|
|
3509
3630
|
const viaEnv = isDefault
|
|
3510
3631
|
? usedKeyEnvVar !== undefined
|
|
3511
3632
|
: fallback?.apiKey !== undefined && fallback.apiKey === readNonEmptyString(processEnv[String(envVar)]);
|
|
@@ -4404,6 +4525,37 @@ export const runSkills = async (streams, env = {}) => {
|
|
|
4404
4525
|
}
|
|
4405
4526
|
return 0;
|
|
4406
4527
|
};
|
|
4528
|
+
/**
|
|
4529
|
+
* The provider/model a newly created soul pins. Frontmatter pins what a run
|
|
4530
|
+
* from this directory would actually use: env vars outrank the active
|
|
4531
|
+
* config file (project-local or explicit first, global otherwise), and no
|
|
4532
|
+
* provider anywhere falls back to demo — the exact precedence of stratus
|
|
4533
|
+
* run. Demo produces no model at all: the soul keeps following the
|
|
4534
|
+
* machine's configuration instead of demanding credentials nothing has
|
|
4535
|
+
* signed in for. The active config's model was written for the provider
|
|
4536
|
+
* named in that config, so it only travels into the soul when they still
|
|
4537
|
+
* match; otherwise the selected provider's own default model stands in.
|
|
4538
|
+
*
|
|
4539
|
+
* Exported for tests: the interactive path that consumes it needs a TTY.
|
|
4540
|
+
*/
|
|
4541
|
+
export const soulPinForNewAgent = (activeConfig, processEnv) => {
|
|
4542
|
+
// The map already validates, so the value is a provider name; the cast
|
|
4543
|
+
// only undoes `readNonEmptyString`'s widening to `string`.
|
|
4544
|
+
const envProvider = readNonEmptyString(processEnv.STRATUS_PROVIDER, (value) => parseProviderName(value, 'STRATUS_PROVIDER'));
|
|
4545
|
+
const provider = envProvider ?? activeConfig.provider ?? 'demo';
|
|
4546
|
+
if (provider === 'demo') {
|
|
4547
|
+
return { provider };
|
|
4548
|
+
}
|
|
4549
|
+
const configModelApplies = (activeConfig.provider ?? 'openai') === provider;
|
|
4550
|
+
const model = readNonEmptyString(processEnv.STRATUS_MODEL)
|
|
4551
|
+
?? (configModelApplies ? activeConfig.model : undefined)
|
|
4552
|
+
?? (provider === 'openai'
|
|
4553
|
+
? DEFAULT_OPENAI_MODEL
|
|
4554
|
+
: provider === 'codex'
|
|
4555
|
+
? DEFAULT_CODEX_MODEL
|
|
4556
|
+
: DEFAULT_ANTHROPIC_MODEL);
|
|
4557
|
+
return { provider, model };
|
|
4558
|
+
};
|
|
4407
4559
|
export const runAgentNew = async (command, streams, env = {}) => {
|
|
4408
4560
|
// On a real terminal, creating an agent is the same guided experience as
|
|
4409
4561
|
// setup: a headed screen, a prefilled (editable) name, a personality, and
|
|
@@ -4427,28 +4579,16 @@ export const runAgentNew = async (command, streams, env = {}) => {
|
|
|
4427
4579
|
const name = (await prompter.ask('Choose your name: ', { prefill: suggested })) || suggested;
|
|
4428
4580
|
const instructions = await prompter.ask('Describe their personality (Enter for a starter you can edit later): ', ...(command.instructions ? [{ prefill: command.instructions }] : []));
|
|
4429
4581
|
const persona = instructions || DEFAULT_SOUL_STARTER;
|
|
4430
|
-
// Frontmatter pins what a run from this directory would actually use:
|
|
4431
|
-
// env vars outrank the active config file (project-local or explicit
|
|
4432
|
-
// first, global otherwise), and no provider anywhere falls back to
|
|
4433
|
-
// demo — the exact precedence of stratus run. Demo produces no pin
|
|
4434
|
-
// at all: the soul keeps following the machine's configuration
|
|
4435
|
-
// instead of demanding credentials nothing has signed in for.
|
|
4436
4582
|
const processEnv = readProcessEnv(env);
|
|
4437
4583
|
// Creating an agent must not be blocked by a broken config — it only
|
|
4438
4584
|
// feeds the soul's provider/model hint, so fall back to defaults.
|
|
4439
4585
|
const { location: configLocation, config: activeConfig } = await discoverActiveConfig(env, (message) => {
|
|
4440
4586
|
writeLine(streams.stdout, `Note: ${message}.`);
|
|
4441
4587
|
});
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
// that config; it only travels into the soul when they still match.
|
|
4447
|
-
const configModelApplies = (activeConfig.provider ?? 'openai') === soulProvider;
|
|
4448
|
-
const soulModel = readNonEmptyString(processEnv.STRATUS_MODEL)
|
|
4449
|
-
?? (configModelApplies ? activeConfig.model : undefined)
|
|
4450
|
-
?? (soulProvider === 'openai' ? DEFAULT_OPENAI_MODEL : DEFAULT_ANTHROPIC_MODEL);
|
|
4451
|
-
const soulPin = soulProvider !== 'demo' ? { provider: soulProvider, model: soulModel } : {};
|
|
4588
|
+
const { provider: soulProvider, model: soulModel } = soulPinForNewAgent(activeConfig, processEnv);
|
|
4589
|
+
const soulPin = soulProvider !== 'demo' && soulModel !== undefined
|
|
4590
|
+
? { provider: soulProvider, model: soulModel }
|
|
4591
|
+
: {};
|
|
4452
4592
|
const claimed = await claimSoulFile(env, { name, instructions: persona }, (candidate) => formatSoul({ agent: candidate, ...soulPin }), (message) => writeLine(streams.stdout, message));
|
|
4453
4593
|
const { agent, soulPath } = claimed;
|
|
4454
4594
|
const makeDefault = await prompter.select(`Make ${agent.name} your default agent?`, [
|