jbai-cli 1.9.6 → 2.1.1

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.
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Shell completions for jbai-cli.
3
+ *
4
+ * `jbai completions` — print zsh completion script to stdout
5
+ * `jbai completions --install` — append source line to ~/.zshrc
6
+ * `jbai completions --bash` — print bash completion script to stdout
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const config = require('./config');
13
+
14
+ // Read bin entries from package.json for the canonical list of commands.
15
+ const pkg = require('../package.json');
16
+ const ALL_BINS = Object.keys(pkg.bin).sort();
17
+
18
+ // jbai subcommands
19
+ const JBAI_SUBCOMMANDS = [
20
+ 'menu', 'token', 'test', 'env', 'models', 'install', 'doctor',
21
+ 'status', 'proxy', 'help', 'version', 'completions',
22
+ ];
23
+
24
+ const JBAI_TOKEN_SUBCOMMANDS = ['set', 'refresh'];
25
+ const JBAI_ENV_VALUES = ['staging', 'production'];
26
+ const JBAI_MODELS_VALUES = ['all', 'claude', 'codex', 'gemini', 'opencode', 'goose', 'continue'];
27
+ const JBAI_INSTALL_VALUES = ['all', 'claude', 'codex', 'gemini', 'opencode', 'goose', 'continue'];
28
+ const SUPER_FLAGS = ['--super', '--yolo', '-s'];
29
+ const COMMON_FLAGS = ['--model', '--models', '--help'];
30
+
31
+ // Collect all model names for --model completion
32
+ const ALL_MODELS = [
33
+ ...config.MODELS.claude.available,
34
+ ...config.MODELS.openai.available,
35
+ ...config.MODELS.codex.available,
36
+ ...config.MODELS.gemini.available,
37
+ ...config.MODELS.grazie.available,
38
+ ];
39
+ // Dedupe
40
+ const UNIQUE_MODELS = [...new Set(ALL_MODELS)].sort();
41
+
42
+ function generateZsh() {
43
+ // Group shortcuts by tool for descriptive completions
44
+ const shortcutDescriptions = {};
45
+ for (const bin of ALL_BINS) {
46
+ if (bin === 'jbai' || bin === 'jbai-proxy') continue;
47
+ // e.g. jbai-codex-rockhopper -> "Codex + Rockhopper Alpha"
48
+ shortcutDescriptions[bin] = bin;
49
+ }
50
+
51
+ return `#compdef jbai jbai-claude jbai-codex jbai-gemini jbai-opencode jbai-goose jbai-continue jbai-council jbai-proxy jbai-claude-opus jbai-claude-sonnet jbai-codex-5.2 jbai-codex-5.3 jbai-codex-5.4 jbai-codex-rockhopper jbai-gemini-3.1 jbai-gemini-supernova jbai-opencode-rockhopper jbai-opencode-grok jbai-opencode-deepseek
52
+ # ─── jbai-cli zsh completions (auto-generated) ───
53
+
54
+ # Complete model names after --model flag
55
+ _jbai_models() {
56
+ local models=(
57
+ ${UNIQUE_MODELS.map(m => ` '${m}'`).join('\n')}
58
+ )
59
+ _describe 'model' models
60
+ }
61
+
62
+ # Main jbai command completions
63
+ _jbai() {
64
+ local -a subcommands=(
65
+ 'menu:Open interactive control panel'
66
+ 'token:Show or manage authentication token'
67
+ 'test:Test API endpoints'
68
+ 'env:Switch environment (staging/production)'
69
+ 'models:List available models'
70
+ 'install:Install AI tools'
71
+ 'doctor:Check installed tools'
72
+ 'status:Check installed tools'
73
+ 'proxy:Manage local proxy'
74
+ 'help:Show help'
75
+ 'version:Show version'
76
+ 'completions:Shell completions'
77
+ )
78
+
79
+ if (( CURRENT == 2 )); then
80
+ _describe 'command' subcommands
81
+ return
82
+ fi
83
+
84
+ case "\${words[2]}" in
85
+ token)
86
+ if (( CURRENT == 3 )); then
87
+ local -a token_cmds=('set:Set token interactively' 'refresh:Refresh token via API')
88
+ _describe 'token command' token_cmds
89
+ fi
90
+ ;;
91
+ env)
92
+ if (( CURRENT == 3 )); then
93
+ local -a envs=('staging' 'production')
94
+ _describe 'environment' envs
95
+ fi
96
+ ;;
97
+ models)
98
+ if (( CURRENT == 3 )); then
99
+ local -a tools=(${JBAI_MODELS_VALUES.map(v => `'${v}'`).join(' ')})
100
+ _describe 'tool filter' tools
101
+ fi
102
+ ;;
103
+ install)
104
+ if (( CURRENT == 3 )); then
105
+ local -a tools=(${JBAI_INSTALL_VALUES.map(v => `'${v}'`).join(' ')})
106
+ _describe 'tool' tools
107
+ fi
108
+ ;;
109
+ completions)
110
+ if (( CURRENT == 3 )); then
111
+ local -a opts=('--install:Add to ~/.zshrc' '--bash:Print bash completions')
112
+ _describe 'option' opts
113
+ fi
114
+ ;;
115
+ esac
116
+ }
117
+
118
+ # Tool wrapper completions (jbai-claude, jbai-codex, etc.)
119
+ _jbai_tool_wrapper() {
120
+ _arguments -s \\
121
+ '--model[Model to use]:model:_jbai_models' \\
122
+ '-m[Model to use]:model:_jbai_models' \\
123
+ '--super[Enable super/auto mode]' \\
124
+ '--yolo[Enable super/auto mode]' \\
125
+ '-s[Enable super/auto mode]' \\
126
+ '--models[List available models]' \\
127
+ '--list-models[List available models]' \\
128
+ '--help[Show help]' \\
129
+ 'models:List available models' \\
130
+ '*:pass-through:'
131
+ }
132
+
133
+ # Register completions
134
+ compdef _jbai jbai
135
+ compdef _jbai_tool_wrapper jbai-claude jbai-codex jbai-gemini jbai-opencode jbai-goose jbai-continue
136
+ compdef _jbai_tool_wrapper jbai-claude-opus jbai-claude-sonnet
137
+ compdef _jbai_tool_wrapper jbai-codex-5.2 jbai-codex-5.3 jbai-codex-5.4 jbai-codex-rockhopper
138
+ compdef _jbai_tool_wrapper jbai-gemini-3.1 jbai-gemini-supernova
139
+ compdef _jbai_tool_wrapper jbai-opencode-rockhopper jbai-opencode-grok jbai-opencode-deepseek
140
+ `;
141
+ }
142
+
143
+ function generateBash() {
144
+ return `# ─── jbai-cli bash completions (auto-generated) ───
145
+
146
+ _jbai_completions() {
147
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
148
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
149
+
150
+ # jbai subcommands
151
+ if [[ "\${COMP_WORDS[0]}" == "jbai" && \${COMP_CWORD} -eq 1 ]]; then
152
+ COMPREPLY=( $(compgen -W "${JBAI_SUBCOMMANDS.join(' ')}" -- "$cur") )
153
+ return
154
+ fi
155
+
156
+ # jbai token <sub>
157
+ if [[ "\${COMP_WORDS[1]}" == "token" && \${COMP_CWORD} -eq 2 ]]; then
158
+ COMPREPLY=( $(compgen -W "${JBAI_TOKEN_SUBCOMMANDS.join(' ')}" -- "$cur") )
159
+ return
160
+ fi
161
+
162
+ # jbai env <value>
163
+ if [[ "\${COMP_WORDS[1]}" == "env" && \${COMP_CWORD} -eq 2 ]]; then
164
+ COMPREPLY=( $(compgen -W "${JBAI_ENV_VALUES.join(' ')}" -- "$cur") )
165
+ return
166
+ fi
167
+
168
+ # jbai models <value>
169
+ if [[ "\${COMP_WORDS[1]}" == "models" && \${COMP_CWORD} -eq 2 ]]; then
170
+ COMPREPLY=( $(compgen -W "${JBAI_MODELS_VALUES.join(' ')}" -- "$cur") )
171
+ return
172
+ fi
173
+
174
+ # jbai install <value>
175
+ if [[ "\${COMP_WORDS[1]}" == "install" && \${COMP_CWORD} -eq 2 ]]; then
176
+ COMPREPLY=( $(compgen -W "${JBAI_INSTALL_VALUES.join(' ')}" -- "$cur") )
177
+ return
178
+ fi
179
+
180
+ # --model completion
181
+ if [[ "$prev" == "--model" || "$prev" == "-m" ]]; then
182
+ COMPREPLY=( $(compgen -W "${UNIQUE_MODELS.join(' ')}" -- "$cur") )
183
+ return
184
+ fi
185
+
186
+ # Generic flags for tool wrappers
187
+ COMPREPLY=( $(compgen -W "--model --super --yolo --models --help models" -- "$cur") )
188
+ }
189
+
190
+ _jbai_tool_completions() {
191
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
192
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
193
+
194
+ if [[ "$prev" == "--model" || "$prev" == "-m" ]]; then
195
+ COMPREPLY=( $(compgen -W "${UNIQUE_MODELS.join(' ')}" -- "$cur") )
196
+ return
197
+ fi
198
+
199
+ COMPREPLY=( $(compgen -W "--model --super --yolo --models --help models" -- "$cur") )
200
+ }
201
+
202
+ complete -F _jbai_completions jbai
203
+ complete -F _jbai_tool_completions jbai-claude jbai-codex jbai-gemini jbai-opencode jbai-goose jbai-continue
204
+ complete -F _jbai_tool_completions jbai-claude-opus jbai-claude-sonnet
205
+ complete -F _jbai_tool_completions jbai-codex-5.2 jbai-codex-5.3 jbai-codex-5.4 jbai-codex-rockhopper
206
+ complete -F _jbai_tool_completions jbai-gemini-3.1 jbai-gemini-supernova
207
+ complete -F _jbai_tool_completions jbai-opencode-rockhopper jbai-opencode-grok jbai-opencode-deepseek
208
+ `;
209
+ }
210
+
211
+ function getCompletionFilePath() {
212
+ return path.join(config.CONFIG_DIR, 'completions.zsh');
213
+ }
214
+
215
+ function installZsh() {
216
+ const completionFile = getCompletionFilePath();
217
+ const zshrc = path.join(os.homedir(), '.zshrc');
218
+
219
+ // Write completion file
220
+ config.ensureConfigDir();
221
+ fs.writeFileSync(completionFile, generateZsh(), { mode: 0o644 });
222
+ console.log(`Written: ${completionFile}`);
223
+
224
+ // Check if already sourced in .zshrc
225
+ const sourceLine = `[ -f "${completionFile}" ] && source "${completionFile}"`;
226
+
227
+ if (fs.existsSync(zshrc)) {
228
+ const content = fs.readFileSync(zshrc, 'utf-8');
229
+ if (content.includes('jbai') && content.includes('completions.zsh')) {
230
+ console.log('Already in ~/.zshrc — updating completion file only');
231
+ return;
232
+ }
233
+ }
234
+
235
+ fs.appendFileSync(zshrc, `\n# jbai-cli completions\n${sourceLine}\n`);
236
+ console.log('Added to ~/.zshrc');
237
+ console.log('\nRun: source ~/.zshrc (or open a new terminal)');
238
+ }
239
+
240
+ function run(args) {
241
+ if (args.includes('--install')) {
242
+ installZsh();
243
+ return;
244
+ }
245
+
246
+ if (args.includes('--bash')) {
247
+ process.stdout.write(generateBash());
248
+ return;
249
+ }
250
+
251
+ // Default: print zsh completions to stdout
252
+ process.stdout.write(generateZsh());
253
+ }
254
+
255
+ module.exports = { run, generateZsh, generateBash, installZsh, getCompletionFilePath };
package/lib/config.js CHANGED
@@ -31,118 +31,50 @@ const ENDPOINTS = {
31
31
  // Run 'node bin/test-models.js' to verify model availability
32
32
  const MODELS = {
33
33
  claude: {
34
- default: 'claude-sonnet-4-5-20250929',
34
+ default: 'claude-sonnet-4-6',
35
35
  available: [
36
- // Claude 4.6 series (latest)
37
36
  'claude-opus-4-6',
38
- // Claude 4.5 series
39
- 'claude-opus-4-5-20251101',
40
- 'claude-sonnet-4-5-20250929',
41
- 'claude-haiku-4-5-20251001',
42
- // Claude 4.x series
43
- 'claude-opus-4-1-20250805',
44
- 'claude-sonnet-4-20250514',
45
- // Claude 3.x series
46
- 'claude-3-7-sonnet-20250219',
47
- 'claude-3-5-haiku-20241022'
37
+ 'claude-sonnet-4-6',
48
38
  ]
49
39
  },
50
40
  openai: {
51
41
  // Chat/Completions models (used by OpenCode)
52
- // Keep in sync with the OpenAI proxy's advertised list.
53
- default: 'gpt-5.2-2025-12-11',
42
+ // NOTE: codex-only models (gpt-5.x-codex) do NOT work on chat/completions
43
+ default: 'gpt-5.4',
54
44
  available: [
55
- // GPT-5.x series (latest) - require date-versioned names
56
- 'gpt-5.3-codex',
57
- 'gpt-5.2-2025-12-11',
45
+ 'gpt-5.4',
58
46
  'gpt-5.2',
59
- 'gpt-5.1-2025-11-13',
60
- 'gpt-5-2025-08-07',
61
- 'gpt-5-mini-2025-08-07',
62
- 'gpt-5-nano-2025-08-07',
63
- // GPT-4.1 series
64
- 'gpt-4.1-2025-04-14',
65
- 'gpt-4.1-mini-2025-04-14',
66
- 'gpt-4.1-nano-2025-04-14',
67
- // GPT-4o/4-turbo
68
- 'gpt-4o-2024-11-20',
69
- 'gpt-4o-mini-2024-07-18',
70
- 'gpt-4-turbo-2024-04-09',
71
- 'gpt-4-0613',
72
- // O-series (reasoning) - use max_completion_tokens instead of max_tokens
73
- 'o4-mini-2025-04-16',
74
47
  'o3-2025-04-16',
75
- 'o3-mini-2025-01-31',
76
- 'o1-2024-12-17',
77
- // Legacy
78
- 'gpt-3.5-turbo-0125'
79
48
  ]
80
49
  },
81
50
  // Codex CLI uses OpenAI models via the "responses" API (wire_api = "responses")
82
- // Includes chat-capable models PLUS codex-only models (responses API only)
83
51
  codex: {
84
- default: 'gpt-5.3-codex',
52
+ default: 'gpt-5.4',
85
53
  available: [
86
- // Codex-specific models (responses API only, NOT available via chat/completions)
54
+ 'gpt-5.4',
87
55
  'gpt-5.3-codex',
88
- 'gpt-5.3-codex-api-preview',
89
- // GPT-5.x chat models (also work via responses API)
90
- 'gpt-5.2-2025-12-11',
91
- 'gpt-5.2',
92
- 'gpt-5.1-2025-11-13',
93
- 'gpt-5-2025-08-07',
94
56
  'gpt-5.2-codex',
95
- 'gpt-5.2-pro-2025-12-11',
96
- 'gpt-5.1-codex-max',
97
- 'gpt-5.1-codex',
98
- 'gpt-5.1-codex-mini',
99
- 'gpt-5-codex',
100
- // O-series (also work via responses API)
101
- 'o4-mini-2025-04-16',
102
- 'o3-2025-04-16'
57
+ 'o3-2025-04-16',
103
58
  ]
104
59
  },
105
60
  gemini: {
106
- default: 'gemini-2.5-flash',
61
+ default: 'gemini-2.5-pro',
107
62
  available: [
108
- // Gemini 3.x (preview)
63
+ 'gemini-3.1-pro-preview',
109
64
  'gemini-3-pro-preview',
110
- 'gemini-3-flash-preview',
111
- // Gemini 2.5
112
65
  'gemini-2.5-pro',
113
66
  'gemini-2.5-flash',
114
- 'gemini-2.5-flash-lite',
115
- // Gemini 2.0
116
- 'gemini-2.0-flash-001',
117
- 'gemini-2.0-flash-lite-001'
118
67
  ]
119
68
  },
120
69
  // Grazie native Chat API models — accessible via /grazie-openai/v1 translation layer.
121
70
  // Full list is dynamic (fetched from /user/v5/llm/profiles), this is a static fallback.
71
+ // Only coding-capable models with tool use support.
122
72
  grazie: {
123
- default: 'google-gemini-3-0-flash',
73
+ default: 'xai-grok-4',
124
74
  available: [
125
- // Google
126
- 'google-gemini-3-1-pro',
127
- 'google-gemini-3-0-flash',
128
- 'google-chat-gemini-pro-2.5',
129
- 'google-gemini-2.5-flash',
130
- // DeepSeek
131
- 'deepseek-r1',
132
- 'deepseek-chat-v3',
133
- // Mistral
134
- 'mistral-large',
135
- 'mistral-small',
136
- // xAI / Grok
137
75
  'xai-grok-4',
138
- 'xai-grok-4-1-fast',
139
- 'xai-grok-3',
140
- // Qwen
141
- 'qwen-max',
142
- 'qwen-plus',
143
- // Meta (via OpenRouter)
144
- 'openrouter-meta-llama-4-maverick',
145
- 'openrouter-meta-llama-4-scout',
76
+ 'xai-grok-code-fast-1',
77
+ 'deepseek-r1',
146
78
  ]
147
79
  }
148
80
  };
@@ -150,6 +82,10 @@ const MODELS = {
150
82
  // Model aliases: some CLI tools send short names that Grazie doesn't recognise yet.
151
83
  // Map them to the Grazie-accepted equivalents so the proxy can rewrite on the fly.
152
84
  const MODEL_ALIASES = {
85
+ // Temporary compatibility mapping: staging currently exposes GPT-5.4 as spark preview.
86
+ // Keep user-facing defaults on gpt-5.4 and rewrite at the proxy edge.
87
+ 'gpt-5.4': 'gpt-5.3-codex-spark-preview',
88
+ 'openai-gpt-5-4': 'gpt-5.3-codex-spark-preview',
153
89
  };
154
90
 
155
91
  // All models for tools that support multiple providers (OpenCode, Codex)
package/lib/model-list.js CHANGED
@@ -56,7 +56,7 @@ function getGroupsForTool(tool) {
56
56
  defaultModel: config.MODELS.openai.default,
57
57
  },
58
58
  {
59
- title: 'Grazie Native Chat (Google, DeepSeek, Mistral, xAI, Qwen, etc.)',
59
+ title: 'Grazie Native (xAI, DeepSeek)',
60
60
  models: config.MODELS.grazie.available,
61
61
  defaultModel: config.MODELS.grazie.default,
62
62
  },
@@ -85,7 +85,7 @@ function getGroupsForTool(tool) {
85
85
  defaultModel: config.MODELS.gemini.default,
86
86
  },
87
87
  {
88
- title: 'Grazie Native Chat (Google, DeepSeek, Mistral, xAI, Qwen, etc.)',
88
+ title: 'Grazie Native (xAI, DeepSeek)',
89
89
  models: config.MODELS.grazie.available,
90
90
  defaultModel: config.MODELS.grazie.default,
91
91
  },
@@ -1,30 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
3
  const config = require('./config');
6
4
 
7
- // Fix node-pty spawn-helper permissions (macOS/Linux)
8
- // The prebuilt binary sometimes loses execute permissions during npm install
9
- try {
10
- const platform = process.platform === 'darwin' ? 'darwin' : process.platform;
11
- const arch = process.arch;
12
- const spawnHelperPath = path.join(
13
- __dirname,
14
- '..',
15
- 'node_modules',
16
- 'node-pty',
17
- 'prebuilds',
18
- `${platform}-${arch}`,
19
- 'spawn-helper'
20
- );
21
- if (fs.existsSync(spawnHelperPath)) {
22
- fs.chmodSync(spawnHelperPath, 0o755);
23
- }
24
- } catch {
25
- // Ignore errors - this is a best-effort fix
26
- }
27
-
28
5
  console.log(`
29
6
  ╔══════════════════════════════════════════════════════════════╗
30
7
  ║ jbai-cli installed! ║
@@ -46,5 +23,8 @@ Next steps:
46
23
  $ jbai-goose # Goose (Block)
47
24
  $ jbai-continue # Continue CLI
48
25
 
26
+ 4. Enable tab completions:
27
+ $ jbai completions --install
28
+
49
29
  Run 'jbai help' for more options.
50
30
  `);
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared logic for per-model shortcut scripts.
3
+ *
4
+ * Each shortcut (e.g. jbai-codex-rockhopper) is a thin wrapper that calls
5
+ * the base tool wrapper (jbai-codex, jbai-claude, …) with a fixed model
6
+ * and super mode enabled by default.
7
+ *
8
+ * Usage in a shortcut script:
9
+ * require('../lib/shortcut').run({
10
+ * tool: 'codex', // base wrapper to delegate to
11
+ * model: 'rockhopper-alpha',
12
+ * label: 'Rockhopper Alpha',
13
+ * });
14
+ */
15
+
16
+ const { spawn } = require('child_process');
17
+ const path = require('path');
18
+
19
+ function run({ tool, model, label }) {
20
+ const args = process.argv.slice(2);
21
+
22
+ // Already force super mode + the pinned model
23
+ const extraArgs = ['--super', '--model', model];
24
+
25
+ // If user already passed --model, skip our default
26
+ if (args.includes('--model') || args.includes('-m')) {
27
+ extraArgs.splice(extraArgs.indexOf('--model'), 2);
28
+ }
29
+
30
+ const wrapperScript = path.join(__dirname, '..', 'bin', `jbai-${tool}.js`);
31
+ const finalArgs = [wrapperScript, ...extraArgs, ...args];
32
+
33
+ console.log(`⚡ ${label || model} (super mode)`);
34
+
35
+ const child = spawn(process.execPath, finalArgs, {
36
+ stdio: 'inherit',
37
+ env: process.env,
38
+ });
39
+
40
+ child.on('exit', (code) => process.exit(code || 0));
41
+ child.on('error', (err) => {
42
+ console.error(`Error: ${err.message}`);
43
+ process.exit(1);
44
+ });
45
+ }
46
+
47
+ module.exports = { run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jbai-cli",
3
- "version": "1.9.6",
3
+ "version": "2.1.1",
4
4
  "description": "CLI wrappers to use AI coding tools (Claude Code, Codex, Gemini CLI, OpenCode, Goose, Continue) with JetBrains AI Platform",
5
5
  "keywords": [
6
6
  "jetbrains",
@@ -30,9 +30,20 @@
30
30
  "jbai": "bin/jbai.js",
31
31
  "jbai-proxy": "bin/jbai-proxy.js",
32
32
  "jbai-claude": "bin/jbai-claude.js",
33
+ "jbai-claude-opus": "bin/jbai-claude-opus.js",
34
+ "jbai-claude-sonnet": "bin/jbai-claude-sonnet.js",
33
35
  "jbai-codex": "bin/jbai-codex.js",
36
+ "jbai-codex-5.2": "bin/jbai-codex-5.2.js",
37
+ "jbai-codex-5.3": "bin/jbai-codex-5.3.js",
38
+ "jbai-codex-5.4": "bin/jbai-codex-5.4.js",
39
+ "jbai-codex-rockhopper": "bin/jbai-codex-rockhopper.js",
34
40
  "jbai-gemini": "bin/jbai-gemini.js",
41
+ "jbai-gemini-3.1": "bin/jbai-gemini-3.1.js",
42
+ "jbai-gemini-supernova": "bin/jbai-gemini-supernova.js",
35
43
  "jbai-opencode": "bin/jbai-opencode.js",
44
+ "jbai-opencode-rockhopper": "bin/jbai-opencode-rockhopper.js",
45
+ "jbai-opencode-grok": "bin/jbai-opencode-grok.js",
46
+ "jbai-opencode-deepseek": "bin/jbai-opencode-deepseek.js",
36
47
  "jbai-goose": "bin/jbai-goose.js",
37
48
  "jbai-continue": "bin/jbai-continue.js",
38
49
  "jbai-council": "bin/jbai-council.js"
@@ -45,9 +56,7 @@
45
56
  "engines": {
46
57
  "node": ">=18.0.0"
47
58
  },
48
- "dependencies": {
49
- "node-pty": "^1.1.0"
50
- },
59
+ "dependencies": {},
51
60
  "scripts": {
52
61
  "postinstall": "node lib/postinstall.js",
53
62
  "test": "node bin/jbai.js test"
package/lib/handoff.js DELETED
@@ -1,152 +0,0 @@
1
- const { execSync } = require('child_process');
2
- const config = require('./config');
3
-
4
- function getGitOutput(command, cwd = process.cwd()) {
5
- try {
6
- return execSync(command, { stdio: ['ignore', 'pipe', 'ignore'], cwd }).toString().trim();
7
- } catch {
8
- return '';
9
- }
10
- }
11
-
12
- function getGitRepoUrl(cwd = process.cwd()) {
13
- return getGitOutput('git remote get-url origin', cwd);
14
- }
15
-
16
- function getGitRef(cwd = process.cwd()) {
17
- const branch = getGitOutput('git rev-parse --abbrev-ref HEAD', cwd);
18
- if (branch && branch !== 'HEAD') {
19
- return branch;
20
- }
21
- return getGitOutput('git rev-parse HEAD', cwd);
22
- }
23
-
24
- function openUrl(url) {
25
- const escaped = url.replace(/"/g, '\\"');
26
- try {
27
- if (process.platform === 'darwin') {
28
- execSync(`open "${escaped}"`, { stdio: 'ignore' });
29
- return true;
30
- }
31
- if (process.platform === 'win32') {
32
- execSync(`start "" "${escaped}"`, { stdio: 'ignore' });
33
- return true;
34
- }
35
- execSync(`xdg-open "${escaped}"`, { stdio: 'ignore' });
36
- return true;
37
- } catch {
38
- return false;
39
- }
40
- }
41
-
42
- function normalizeGrazieEnvironment(env) {
43
- if (!env) {
44
- return config.getEnvironment() === 'production' ? 'PRODUCTION' : 'STAGING';
45
- }
46
- return env.toString().toUpperCase();
47
- }
48
-
49
- function getDefaultModel() {
50
- return config.MODELS.claude.default;
51
- }
52
-
53
- function parseBool(value, fallback) {
54
- if (value === undefined || value === null) {
55
- return fallback;
56
- }
57
- const normalized = value.toString().toLowerCase();
58
- if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
59
- if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
60
- return fallback;
61
- }
62
-
63
- async function createHandoff({
64
- task,
65
- repoUrl,
66
- ref,
67
- branchName,
68
- grazieToken,
69
- grazieEnvironment,
70
- grazieModel,
71
- gitToken,
72
- facadeToken,
73
- orcaUrl,
74
- source,
75
- autoStart,
76
- shouldOpen,
77
- cwd,
78
- }) {
79
- const finalTask = task && task.trim()
80
- ? task.trim()
81
- : 'Continue the current task from the CLI session.';
82
-
83
- const finalRepoUrl = repoUrl && repoUrl.trim() ? repoUrl.trim() : getGitRepoUrl(cwd);
84
- if (!finalRepoUrl) {
85
- throw new Error('Could not determine git repo. Use --repo or set JBAI_HANDOFF_REPO.');
86
- }
87
-
88
- const finalGrazieToken = grazieToken || config.getToken();
89
- if (!finalGrazieToken) {
90
- throw new Error('No Grazie token found. Run: jbai token set');
91
- }
92
- if (config.isTokenExpired(finalGrazieToken)) {
93
- throw new Error('Grazie token expired. Run: jbai token refresh');
94
- }
95
-
96
- const finalRef = ref || getGitRef(cwd);
97
- const finalOrcaUrl = (orcaUrl || process.env.ORCA_LAB_URL || 'http://localhost:3000')
98
- .replace(/\/$/, '');
99
- const finalFacadeToken = facadeToken || process.env.FACADE_JWT_TOKEN || '';
100
- const finalGitToken = gitToken || process.env.GITHUB_TOKEN || process.env.GH_TOKEN || '';
101
- const finalGrazieEnv = normalizeGrazieEnvironment(
102
- grazieEnvironment || process.env.JBAI_HANDOFF_ENV
103
- );
104
- const finalModel = grazieModel || process.env.JBAI_HANDOFF_MODEL || getDefaultModel();
105
- const finalAutoStart = parseBool(autoStart ?? process.env.JBAI_HANDOFF_AUTO_START, true);
106
- const openBrowser = parseBool(shouldOpen ?? process.env.JBAI_HANDOFF_OPEN, true);
107
-
108
- const payload = {
109
- task: finalTask,
110
- repoUrl: finalRepoUrl,
111
- ref: finalRef || undefined,
112
- branchName: branchName || process.env.JBAI_HANDOFF_BRANCH || undefined,
113
- gitToken: finalGitToken || undefined,
114
- grazieToken: finalGrazieToken,
115
- grazieEnvironment: finalGrazieEnv,
116
- grazieModel: finalModel,
117
- source: source || 'jbai-cli',
118
- autoStart: finalAutoStart,
119
- };
120
-
121
- const headers = {
122
- 'Content-Type': 'application/json',
123
- ...(finalFacadeToken ? { Authorization: `Bearer ${finalFacadeToken}` } : {}),
124
- };
125
-
126
- const response = await fetch(`${finalOrcaUrl}/api/handoff`, {
127
- method: 'POST',
128
- headers,
129
- body: JSON.stringify(payload),
130
- });
131
-
132
- if (!response.ok) {
133
- const errorText = await response.text().catch(() => '');
134
- const detail = errorText ? ` ${errorText}` : '';
135
- throw new Error(`Orca Lab handoff failed (${response.status}).${detail}`);
136
- }
137
-
138
- const result = await response.json();
139
-
140
- if (openBrowser && result.environmentUrl) {
141
- openUrl(result.environmentUrl);
142
- }
143
-
144
- return result;
145
- }
146
-
147
- module.exports = {
148
- createHandoff,
149
- getGitRepoUrl,
150
- getGitRef,
151
- openUrl,
152
- };