orquesta-cli 0.2.88 → 0.2.90

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/dist/cli.js CHANGED
@@ -120,6 +120,7 @@ program
120
120
  .option('-p, --print [prompt]', 'Execute a prompt and exit (non-interactive mode). Omit the value to read the prompt from stdin — orquesta-agent uses this on Windows to dodge the cmd.exe command-line length limit.')
121
121
  .option('--dangerously-skip-permissions', 'Skip all permission prompts (auto-approve)')
122
122
  .option('--append-system-prompt <prompt>', 'Append text to the system prompt (parity with claude CLI; used by orquesta-agent sessions)')
123
+ .option('--append-system-prompt-file <path>', 'Read the appended system prompt from a file (orquesta-agent uses this to keep a large ~38KB system prompt out of argv — Windows CreateProcess caps the command line at 32767 chars)')
123
124
  .option('--verbose', 'Enable verbose logging')
124
125
  .option('--debug', 'Enable debug logging')
125
126
  .option('--llm-log', 'Enable LLM logging')
@@ -138,6 +139,14 @@ program
138
139
  .option('--update', 'Update orquesta-cli to the latest published version and exit')
139
140
  .option('-c, --continue', 'Resume the most recent conversation session')
140
141
  .action(async (options) => {
142
+ if (options.appendSystemPromptFile) {
143
+ try {
144
+ const { readFileSync } = await import('fs');
145
+ setAppendedSystemPrompt(readFileSync(options.appendSystemPromptFile, 'utf-8'));
146
+ }
147
+ catch {
148
+ }
149
+ }
141
150
  if (options.appendSystemPrompt) {
142
151
  setAppendedSystemPrompt(options.appendSystemPrompt);
143
152
  }
@@ -96,7 +96,9 @@ export declare class LLMClient {
96
96
  healthy: boolean;
97
97
  latency?: number;
98
98
  error?: string;
99
+ switchedToBatuta?: boolean;
99
100
  }>;
101
+ private static probeEndpoint;
100
102
  static testConnection(baseUrl: string, apiKey: string, model: string): Promise<{
101
103
  success: boolean;
102
104
  latency?: number;
@@ -6,6 +6,22 @@ import { LLMError, TokenLimitError, RateLimitError, ContextLengthError, } from '
6
6
  import { logger, isLLMLogEnabled } from '../../utils/logger.js';
7
7
  import { usageTracker } from '../usage-tracker.js';
8
8
  import { getForcedTier, getBatutaSessionId, setLastBatutaRoute } from '../routing-state.js';
9
+ function safeStringify(value, space) {
10
+ const seen = new WeakSet();
11
+ try {
12
+ return JSON.stringify(value, (_key, val) => {
13
+ if (typeof val === 'object' && val !== null) {
14
+ if (seen.has(val))
15
+ return '[Circular]';
16
+ seen.add(val);
17
+ }
18
+ return val;
19
+ }, space);
20
+ }
21
+ catch {
22
+ return String(value);
23
+ }
24
+ }
9
25
  function extractTopLevelJsonObjects(s) {
10
26
  const objects = [];
11
27
  let depth = 0;
@@ -972,11 +988,11 @@ export class LLMClient {
972
988
  errorMessage,
973
989
  errorType,
974
990
  errorCode,
975
- responseBody: JSON.stringify(data, null, 2),
991
+ responseBody: safeStringify(data, 2),
976
992
  requestMethod: requestContext?.method,
977
993
  requestUrl: requestContext?.url,
978
994
  requestBody: requestContext?.body
979
- ? JSON.stringify(requestContext.body, null, 2).substring(0, 5000)
995
+ ? safeStringify(requestContext.body, 2).substring(0, 5000)
980
996
  : undefined,
981
997
  responseHeaders: axiosError.response.headers,
982
998
  });
@@ -1033,6 +1049,16 @@ export class LLMClient {
1033
1049
  },
1034
1050
  });
1035
1051
  }
1052
+ if (status === 402) {
1053
+ logger.error('Batuta not available (402)', { errorMessage });
1054
+ return new APIError(errorMessage || 'Payment required', 402, this.baseUrl, {
1055
+ isRecoverable: false,
1056
+ cause: axiosError,
1057
+ userMessage: `Batuta isn't available for this account (402): ${errorMessage || 'add-on required'}.\n` +
1058
+ `Enable the Batuta add-on for your organization in the Orquesta dashboard, or point the CLI at a local LLM provider.`,
1059
+ details: { endpoint: this.baseUrl, fullError: data },
1060
+ });
1061
+ }
1036
1062
  if (status === 401) {
1037
1063
  logger.error('Authentication Failed', {
1038
1064
  endpoint: this.baseUrl,
@@ -1276,6 +1302,22 @@ export class LLMClient {
1276
1302
  if (!endpoint || !model) {
1277
1303
  return { healthy: false, error: 'No endpoint/model configured' };
1278
1304
  }
1305
+ const first = await LLMClient.probeEndpoint(endpoint, model.id);
1306
+ if (first.healthy)
1307
+ return first;
1308
+ const batuta = configManager.getAllEndpoints().find((e) => e.id === 'batuta-proxy' || e.provider === 'batuta');
1309
+ if (batuta && batuta.apiKey && batuta.id !== endpoint.id) {
1310
+ const m = batuta.models.find((x) => x.enabled) || batuta.models[0];
1311
+ if (m) {
1312
+ await configManager.setCurrentEndpoint(batuta.id);
1313
+ await configManager.setCurrentModel(m.id);
1314
+ const second = await LLMClient.probeEndpoint(batuta, m.id);
1315
+ return { ...second, switchedToBatuta: true };
1316
+ }
1317
+ }
1318
+ return first;
1319
+ }
1320
+ static async probeEndpoint(endpoint, modelId) {
1279
1321
  const startTime = Date.now();
1280
1322
  try {
1281
1323
  const axiosInstance = axios.create({
@@ -1287,7 +1329,7 @@ export class LLMClient {
1287
1329
  timeout: 10000,
1288
1330
  });
1289
1331
  const response = await axiosInstance.post('/chat/completions', {
1290
- model: model.id,
1332
+ model: modelId,
1291
1333
  messages: [{ role: 'user', content: 'ping' }],
1292
1334
  max_tokens: 1,
1293
1335
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.88",
3
+ "version": "0.2.90",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",