sference-cli 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- {sference_cli-0.1.0 → sference_cli-0.1.1}/PKG-INFO +26 -2
- {sference_cli-0.1.0 → sference_cli-0.1.1}/README.md +24 -0
- {sference_cli-0.1.0 → sference_cli-0.1.1}/pyproject.toml +2 -2
- sference_cli-0.1.1/sference_cli/launch.py +305 -0
- {sference_cli-0.1.0 → sference_cli-0.1.1}/sference_cli/main.py +2 -3
- {sference_cli-0.1.0 → sference_cli-0.1.1}/.gitignore +0 -0
- {sference_cli-0.1.0 → sference_cli-0.1.1}/LICENSE +0 -0
- {sference_cli-0.1.0 → sference_cli-0.1.1}/sference_cli/__init__.py +0 -0
- {sference_cli-0.1.0 → sference_cli-0.1.1}/sference_cli/stream_cache.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sference-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: sference command-line interface
|
|
5
5
|
Project-URL: Homepage, https://sference.com
|
|
6
6
|
Project-URL: Repository, https://github.com/s-ference/sference
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Utilities
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: sference-sdk>=0.1.
|
|
24
|
+
Requires-Dist: sference-sdk>=0.1.1
|
|
25
25
|
Requires-Dist: typer>=0.24.1
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
@@ -109,6 +109,7 @@ sference responses tail --stream-id <stream_id>
|
|
|
109
109
|
| Variable | Purpose |
|
|
110
110
|
|----------|---------|
|
|
111
111
|
| `SFERENCE_API_KEY` | API key (or JWT); overrides `~/.sference/credentials.json` |
|
|
112
|
+
| `SFERENCE_MODEL` | Default catalog model for `sference launch` (overrides built-in default) |
|
|
112
113
|
| `SFERENCE_STREAM_CACHE` | Optional path to the stream resumable-cache file (default `~/.sference/stream_cache.json`) |
|
|
113
114
|
| `SFERENCE_STREAM_CHECKPOINTS` | Optional path for **`responses tail`** event checkpoints (default `~/.sference/stream_checkpoints.json`) |
|
|
114
115
|
|
|
@@ -121,6 +122,29 @@ sference responses tail --stream-id <stream_id>
|
|
|
121
122
|
| `sference auth login` | Store an API key (optional `--api-key`, `--no-browser`) |
|
|
122
123
|
| `sference auth me` | Show current user (`--json` for machine-readable output) |
|
|
123
124
|
|
|
125
|
+
### Launch (hidden from `sference --help`)
|
|
126
|
+
|
|
127
|
+
These commands are intentionally omitted from the top-level CLI help; see this section for usage.
|
|
128
|
+
|
|
129
|
+
| Command | Description |
|
|
130
|
+
|---------|-------------|
|
|
131
|
+
| `sference launch claude` | Launch Claude Code with Sference API routing (`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`) |
|
|
132
|
+
| `sference launch claude --dry-run` | Print env + command without starting Claude Code |
|
|
133
|
+
| `sference launch claude --model moonshotai/Kimi-K2.6` | Override catalog model |
|
|
134
|
+
| `sference launch claude --enable-tool-search` | Set `ENABLE_TOOL_SEARCH=true` on custom hosts |
|
|
135
|
+
| `sference launch claude --resume` | Forward Claude Code flags/args after `sference launch claude` |
|
|
136
|
+
|
|
137
|
+
Requires the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) on `PATH`. Uses `~/.sference/credentials.json` or `SFERENCE_API_KEY`. Default model: `moonshotai/Kimi-K2.6` (override with `--model` or `SFERENCE_MODEL`).
|
|
138
|
+
|
|
139
|
+
| Command | Description |
|
|
140
|
+
|---------|-------------|
|
|
141
|
+
| `sference launch pi` | Launch Pi with Sference API routing (writes `~/.pi/agent/models.json`) |
|
|
142
|
+
| `sference launch pi --dry-run` | Print provider config and command without launching Pi |
|
|
143
|
+
| `sference launch pi --model moonshotai/Kimi-K2.6` | Override catalog model |
|
|
144
|
+
| `sference launch pi -- /path/to/project` | Forward Pi args after `sference launch pi` |
|
|
145
|
+
|
|
146
|
+
Requires the [Pi CLI](https://pi.dev/docs) on `PATH`. Writes a `sference` provider block to `~/.pi/agent/models.json` with `baseUrl`, `apiKey`, and the chosen `model`, then execs `pi --provider sference --model <id>`. Uses `~/.sference/credentials.json` or `SFERENCE_API_KEY`. Default model: `moonshotai/Kimi-K2.6` (override with `--model` or `SFERENCE_MODEL`).
|
|
147
|
+
|
|
124
148
|
### Batch
|
|
125
149
|
|
|
126
150
|
| Command | Description |
|
|
@@ -82,6 +82,7 @@ sference responses tail --stream-id <stream_id>
|
|
|
82
82
|
| Variable | Purpose |
|
|
83
83
|
|----------|---------|
|
|
84
84
|
| `SFERENCE_API_KEY` | API key (or JWT); overrides `~/.sference/credentials.json` |
|
|
85
|
+
| `SFERENCE_MODEL` | Default catalog model for `sference launch` (overrides built-in default) |
|
|
85
86
|
| `SFERENCE_STREAM_CACHE` | Optional path to the stream resumable-cache file (default `~/.sference/stream_cache.json`) |
|
|
86
87
|
| `SFERENCE_STREAM_CHECKPOINTS` | Optional path for **`responses tail`** event checkpoints (default `~/.sference/stream_checkpoints.json`) |
|
|
87
88
|
|
|
@@ -94,6 +95,29 @@ sference responses tail --stream-id <stream_id>
|
|
|
94
95
|
| `sference auth login` | Store an API key (optional `--api-key`, `--no-browser`) |
|
|
95
96
|
| `sference auth me` | Show current user (`--json` for machine-readable output) |
|
|
96
97
|
|
|
98
|
+
### Launch (hidden from `sference --help`)
|
|
99
|
+
|
|
100
|
+
These commands are intentionally omitted from the top-level CLI help; see this section for usage.
|
|
101
|
+
|
|
102
|
+
| Command | Description |
|
|
103
|
+
|---------|-------------|
|
|
104
|
+
| `sference launch claude` | Launch Claude Code with Sference API routing (`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`) |
|
|
105
|
+
| `sference launch claude --dry-run` | Print env + command without starting Claude Code |
|
|
106
|
+
| `sference launch claude --model moonshotai/Kimi-K2.6` | Override catalog model |
|
|
107
|
+
| `sference launch claude --enable-tool-search` | Set `ENABLE_TOOL_SEARCH=true` on custom hosts |
|
|
108
|
+
| `sference launch claude --resume` | Forward Claude Code flags/args after `sference launch claude` |
|
|
109
|
+
|
|
110
|
+
Requires the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) on `PATH`. Uses `~/.sference/credentials.json` or `SFERENCE_API_KEY`. Default model: `moonshotai/Kimi-K2.6` (override with `--model` or `SFERENCE_MODEL`).
|
|
111
|
+
|
|
112
|
+
| Command | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| `sference launch pi` | Launch Pi with Sference API routing (writes `~/.pi/agent/models.json`) |
|
|
115
|
+
| `sference launch pi --dry-run` | Print provider config and command without launching Pi |
|
|
116
|
+
| `sference launch pi --model moonshotai/Kimi-K2.6` | Override catalog model |
|
|
117
|
+
| `sference launch pi -- /path/to/project` | Forward Pi args after `sference launch pi` |
|
|
118
|
+
|
|
119
|
+
Requires the [Pi CLI](https://pi.dev/docs) on `PATH`. Writes a `sference` provider block to `~/.pi/agent/models.json` with `baseUrl`, `apiKey`, and the chosen `model`, then execs `pi --provider sference --model <id>`. Uses `~/.sference/credentials.json` or `SFERENCE_API_KEY`. Default model: `moonshotai/Kimi-K2.6` (override with `--model` or `SFERENCE_MODEL`).
|
|
120
|
+
|
|
97
121
|
### Batch
|
|
98
122
|
|
|
99
123
|
| Command | Description |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sference-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
description = "sference command-line interface"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
25
|
"typer>=0.24.1",
|
|
26
|
-
"sference-sdk>=0.1.
|
|
26
|
+
"sference-sdk>=0.1.1",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.urls]
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""Launch external agent tools against the Sference API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
|
|
15
|
+
DEFAULT_LAUNCH_MODEL = "moonshotai/Kimi-K2.6"
|
|
16
|
+
DEFAULT_API_BASE_URL = "https://api.sference.com"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def resolve_api_base_url(explicit: Optional[str]) -> str:
|
|
20
|
+
if explicit:
|
|
21
|
+
return explicit.rstrip("/")
|
|
22
|
+
env = os.environ.get("SFERENCE_BASE_URL")
|
|
23
|
+
if env:
|
|
24
|
+
return env.strip().rstrip("/")
|
|
25
|
+
return DEFAULT_API_BASE_URL
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def resolve_launch_model(explicit: Optional[str]) -> str:
|
|
29
|
+
"""Default catalog model for ``launch`` subcommands (--model overrides)."""
|
|
30
|
+
if explicit:
|
|
31
|
+
return explicit
|
|
32
|
+
env = os.environ.get("SFERENCE_MODEL")
|
|
33
|
+
if env and env.strip():
|
|
34
|
+
return env.strip()
|
|
35
|
+
return DEFAULT_LAUNCH_MODEL
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def resolve_openai_base_url_for_pi(explicit: Optional[str]) -> str:
|
|
39
|
+
"""OpenAI-compatible base URL with ``/v1`` for Pi ``models.json``."""
|
|
40
|
+
base = resolve_api_base_url(explicit)
|
|
41
|
+
base = base.rstrip("/")
|
|
42
|
+
if base.endswith("/v1"):
|
|
43
|
+
return base
|
|
44
|
+
return f"{base}/v1"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def find_claude_executable() -> str | None:
|
|
48
|
+
return shutil.which("claude")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def find_pi_executable() -> str | None:
|
|
52
|
+
return shutil.which("pi")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def build_claude_env(
|
|
56
|
+
*,
|
|
57
|
+
api_key: str,
|
|
58
|
+
base_url: str,
|
|
59
|
+
model: str,
|
|
60
|
+
enable_tool_search: bool,
|
|
61
|
+
) -> dict[str, str]:
|
|
62
|
+
env = os.environ.copy()
|
|
63
|
+
env["ANTHROPIC_BASE_URL"] = base_url
|
|
64
|
+
env["ANTHROPIC_AUTH_TOKEN"] = api_key
|
|
65
|
+
env["ANTHROPIC_MODEL"] = model
|
|
66
|
+
# Claude Code prefers AUTH_TOKEN; drop API_KEY to avoid precedence confusion.
|
|
67
|
+
env.pop("ANTHROPIC_API_KEY", None)
|
|
68
|
+
if enable_tool_search:
|
|
69
|
+
env["ENABLE_TOOL_SEARCH"] = "true"
|
|
70
|
+
else:
|
|
71
|
+
env.pop("ENABLE_TOOL_SEARCH", None)
|
|
72
|
+
return env
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def launch_claude_code(
|
|
76
|
+
*,
|
|
77
|
+
api_key: str,
|
|
78
|
+
base_url: str,
|
|
79
|
+
model: str,
|
|
80
|
+
enable_tool_search: bool,
|
|
81
|
+
claude_args: list[str],
|
|
82
|
+
dry_run: bool,
|
|
83
|
+
) -> None:
|
|
84
|
+
claude_bin = find_claude_executable()
|
|
85
|
+
if claude_bin is None:
|
|
86
|
+
typer.echo(
|
|
87
|
+
"Claude Code CLI not found on PATH.\n"
|
|
88
|
+
"Install from https://docs.anthropic.com/en/docs/claude-code "
|
|
89
|
+
"and ensure the `claude` command is available.",
|
|
90
|
+
err=True,
|
|
91
|
+
)
|
|
92
|
+
raise typer.Exit(code=1)
|
|
93
|
+
|
|
94
|
+
env = build_claude_env(
|
|
95
|
+
api_key=api_key,
|
|
96
|
+
base_url=base_url,
|
|
97
|
+
model=model,
|
|
98
|
+
enable_tool_search=enable_tool_search,
|
|
99
|
+
)
|
|
100
|
+
cmd = [claude_bin, *claude_args]
|
|
101
|
+
|
|
102
|
+
if dry_run:
|
|
103
|
+
typer.echo(f"ANTHROPIC_BASE_URL={env['ANTHROPIC_BASE_URL']}")
|
|
104
|
+
typer.echo(f"ANTHROPIC_MODEL={env['ANTHROPIC_MODEL']}")
|
|
105
|
+
typer.echo("ANTHROPIC_AUTH_TOKEN=<redacted>")
|
|
106
|
+
if enable_tool_search:
|
|
107
|
+
typer.echo("ENABLE_TOOL_SEARCH=true")
|
|
108
|
+
typer.echo(f"command: {' '.join(cmd)}")
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
if sys.platform == "win32":
|
|
112
|
+
raise SystemExit(subprocess.call(cmd, env=env))
|
|
113
|
+
|
|
114
|
+
os.execvpe(claude_bin, cmd, env)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _pi_models_json_path() -> Path:
|
|
118
|
+
return Path.home() / ".pi" / "agent" / "models.json"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _write_pi_models_json(*, base_url: str, api_key: str, model: str) -> Path:
|
|
122
|
+
"""Write or update ``~/.pi/agent/models.json`` with a Sference provider."""
|
|
123
|
+
path = _pi_models_json_path()
|
|
124
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
|
|
126
|
+
provider = {
|
|
127
|
+
"api": "openai-completions",
|
|
128
|
+
"baseUrl": base_url,
|
|
129
|
+
"apiKey": api_key,
|
|
130
|
+
"models": [{"id": model, "name": model}],
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if path.exists():
|
|
134
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
135
|
+
try:
|
|
136
|
+
data = json.load(fh)
|
|
137
|
+
except json.JSONDecodeError:
|
|
138
|
+
data = {}
|
|
139
|
+
else:
|
|
140
|
+
data = {}
|
|
141
|
+
|
|
142
|
+
data.setdefault("providers", {})
|
|
143
|
+
data["providers"]["sference"] = provider
|
|
144
|
+
|
|
145
|
+
with path.open("w", encoding="utf-8") as fh:
|
|
146
|
+
json.dump(data, fh, indent=2)
|
|
147
|
+
fh.write("\n")
|
|
148
|
+
|
|
149
|
+
return path
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def launch_pi(
|
|
153
|
+
*,
|
|
154
|
+
api_key: str,
|
|
155
|
+
base_url: str,
|
|
156
|
+
model: str,
|
|
157
|
+
pi_args: list[str],
|
|
158
|
+
dry_run: bool,
|
|
159
|
+
) -> None:
|
|
160
|
+
pi_bin = find_pi_executable()
|
|
161
|
+
if pi_bin is None:
|
|
162
|
+
typer.echo(
|
|
163
|
+
"Pi CLI not found on PATH.\n"
|
|
164
|
+
"Install from https://pi.dev/docs and ensure the `pi` command is available.",
|
|
165
|
+
err=True,
|
|
166
|
+
)
|
|
167
|
+
raise typer.Exit(code=1)
|
|
168
|
+
|
|
169
|
+
models_json = _write_pi_models_json(base_url=base_url, api_key=api_key, model=model)
|
|
170
|
+
env = os.environ.copy()
|
|
171
|
+
# Pi reads providers from ~/.pi/agent/models.json; no env vars needed.
|
|
172
|
+
cmd = [pi_bin, "--provider", "sference", "--model", model, *pi_args]
|
|
173
|
+
|
|
174
|
+
if dry_run:
|
|
175
|
+
typer.echo(f"Wrote provider 'sference' to {models_json}")
|
|
176
|
+
typer.echo(f"baseUrl: {base_url}")
|
|
177
|
+
typer.echo(f"model: {model}")
|
|
178
|
+
typer.echo("apiKey: <redacted>")
|
|
179
|
+
typer.echo(f"command: {' '.join(cmd)}")
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
if sys.platform == "win32":
|
|
183
|
+
raise SystemExit(subprocess.call(cmd, env=env))
|
|
184
|
+
|
|
185
|
+
os.execvpe(pi_bin, cmd, env)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def register_launch_commands(app: typer.Typer) -> None:
|
|
189
|
+
launch_app = typer.Typer(help="Launch external tools configured for Sference.", invoke_without_command=True)
|
|
190
|
+
|
|
191
|
+
@launch_app.callback()
|
|
192
|
+
def launch_root(ctx: typer.Context) -> None:
|
|
193
|
+
if ctx.invoked_subcommand is None:
|
|
194
|
+
typer.echo(ctx.get_help())
|
|
195
|
+
raise typer.Exit(code=0)
|
|
196
|
+
|
|
197
|
+
@launch_app.command(
|
|
198
|
+
"claude",
|
|
199
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
200
|
+
help="Launch Claude Code with Sference API routing.",
|
|
201
|
+
)
|
|
202
|
+
def claude(
|
|
203
|
+
ctx: typer.Context,
|
|
204
|
+
model: Optional[str] = typer.Option(
|
|
205
|
+
None,
|
|
206
|
+
"--model",
|
|
207
|
+
"-m",
|
|
208
|
+
help=f"Catalog model id (default: {DEFAULT_LAUNCH_MODEL} or SFERENCE_MODEL).",
|
|
209
|
+
),
|
|
210
|
+
base_url: Optional[str] = typer.Option(
|
|
211
|
+
None,
|
|
212
|
+
"--base-url",
|
|
213
|
+
envvar="SFERENCE_BASE_URL",
|
|
214
|
+
help=f"Sference API base URL (default: {DEFAULT_API_BASE_URL}).",
|
|
215
|
+
),
|
|
216
|
+
enable_tool_search: bool = typer.Option(
|
|
217
|
+
False,
|
|
218
|
+
"--enable-tool-search",
|
|
219
|
+
help="Set ENABLE_TOOL_SEARCH=true for Claude Code on a custom API host.",
|
|
220
|
+
),
|
|
221
|
+
dry_run: bool = typer.Option(
|
|
222
|
+
False,
|
|
223
|
+
"--dry-run",
|
|
224
|
+
help="Print env and command without launching Claude Code.",
|
|
225
|
+
),
|
|
226
|
+
) -> None:
|
|
227
|
+
"""Run ``claude`` with Sference credentials.
|
|
228
|
+
|
|
229
|
+
Uses ``~/.sference/credentials.json`` or ``SFERENCE_API_KEY``. Unknown options
|
|
230
|
+
and trailing args are forwarded to Claude Code, e.g.
|
|
231
|
+
``sference launch claude --resume`` or ``sference launch claude -p "fix the bug"``.
|
|
232
|
+
"""
|
|
233
|
+
from sference_cli.main import _ensure_api_credential, _read_token
|
|
234
|
+
|
|
235
|
+
_ensure_api_credential()
|
|
236
|
+
api_key = _read_token()
|
|
237
|
+
if api_key is None:
|
|
238
|
+
raise typer.Exit(code=1)
|
|
239
|
+
|
|
240
|
+
forwarded = list(ctx.args)
|
|
241
|
+
if forwarded and forwarded[0] == "--":
|
|
242
|
+
forwarded = forwarded[1:]
|
|
243
|
+
|
|
244
|
+
launch_claude_code(
|
|
245
|
+
api_key=api_key,
|
|
246
|
+
base_url=resolve_api_base_url(base_url),
|
|
247
|
+
model=resolve_launch_model(model),
|
|
248
|
+
enable_tool_search=enable_tool_search,
|
|
249
|
+
claude_args=forwarded,
|
|
250
|
+
dry_run=dry_run,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
@launch_app.command(
|
|
254
|
+
"pi",
|
|
255
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
256
|
+
help="Launch Pi with Sference OpenAI-compatible routing.",
|
|
257
|
+
)
|
|
258
|
+
def pi(
|
|
259
|
+
ctx: typer.Context,
|
|
260
|
+
model: Optional[str] = typer.Option(
|
|
261
|
+
None,
|
|
262
|
+
"--model",
|
|
263
|
+
"-m",
|
|
264
|
+
help=f"Catalog model id (default: {DEFAULT_LAUNCH_MODEL} or SFERENCE_MODEL).",
|
|
265
|
+
),
|
|
266
|
+
base_url: Optional[str] = typer.Option(
|
|
267
|
+
None,
|
|
268
|
+
"--base-url",
|
|
269
|
+
envvar="SFERENCE_BASE_URL",
|
|
270
|
+
help=f"Sference API base URL (default: {DEFAULT_API_BASE_URL}).",
|
|
271
|
+
),
|
|
272
|
+
dry_run: bool = typer.Option(
|
|
273
|
+
False,
|
|
274
|
+
"--dry-run",
|
|
275
|
+
help="Print provider config and command without launching Pi.",
|
|
276
|
+
),
|
|
277
|
+
) -> None:
|
|
278
|
+
"""Run ``pi`` with Sference credentials.
|
|
279
|
+
|
|
280
|
+
Writes a ``sference`` provider block to ``~/.pi/agent/models.json`` with
|
|
281
|
+
``baseUrl``, ``apiKey``, and the chosen ``model``, then execs
|
|
282
|
+
``pi --provider sference --model <id>``. Unknown options and trailing args
|
|
283
|
+
are forwarded to Pi, e.g. ``sference launch pi -- /path/to/project``.
|
|
284
|
+
"""
|
|
285
|
+
from sference_cli.main import _ensure_api_credential, _read_token
|
|
286
|
+
|
|
287
|
+
_ensure_api_credential()
|
|
288
|
+
api_key = _read_token()
|
|
289
|
+
if api_key is None:
|
|
290
|
+
raise typer.Exit(code=1)
|
|
291
|
+
|
|
292
|
+
forwarded = list(ctx.args)
|
|
293
|
+
if forwarded and forwarded[0] == "--":
|
|
294
|
+
forwarded = forwarded[1:]
|
|
295
|
+
|
|
296
|
+
launch_pi(
|
|
297
|
+
api_key=api_key,
|
|
298
|
+
base_url=resolve_openai_base_url_for_pi(base_url),
|
|
299
|
+
model=resolve_launch_model(model),
|
|
300
|
+
pi_args=forwarded,
|
|
301
|
+
dry_run=dry_run,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Hidden from `sference --help`; documented for Claude Code / Pi setup only.
|
|
305
|
+
app.add_typer(launch_app, name="launch", hidden=True)
|
|
@@ -19,6 +19,7 @@ from sference_cli import __version__ as _CLI_VERSION
|
|
|
19
19
|
from sference_sdk.checkpoint import clear_checkpoint, load_checkpoint, save_checkpoint
|
|
20
20
|
|
|
21
21
|
from . import stream_cache as stream_cache_mod
|
|
22
|
+
from .launch import register_launch_commands
|
|
22
23
|
|
|
23
24
|
_T = TypeVar("_T")
|
|
24
25
|
|
|
@@ -36,6 +37,7 @@ app.add_typer(batch_app, name="batch")
|
|
|
36
37
|
app.add_typer(stream_app, name="stream")
|
|
37
38
|
app.add_typer(responses_app, name="responses")
|
|
38
39
|
app.add_typer(models_app, name="models")
|
|
40
|
+
register_launch_commands(app)
|
|
39
41
|
|
|
40
42
|
CREDENTIALS_PATH = Path.home() / ".sference" / "credentials.json"
|
|
41
43
|
|
|
@@ -72,11 +74,8 @@ def _read_token() -> str | None:
|
|
|
72
74
|
|
|
73
75
|
|
|
74
76
|
def _client(base_url: Optional[str] = None, *, timeout: float | None = None) -> SferenceClient:
|
|
75
|
-
# If the caller didn't explicitly pass --base-url, allow SFERENCE_BASE_URL
|
|
76
|
-
# to override the default.
|
|
77
77
|
env_base_url = os.environ.get("SFERENCE_BASE_URL")
|
|
78
78
|
if env_base_url and (base_url is None or base_url == "https://api.sference.com"):
|
|
79
|
-
_logger.info(f"Using SFERENCE_BASE_URL: {env_base_url}")
|
|
80
79
|
base_url = env_base_url
|
|
81
80
|
return SferenceClient(base_url=base_url, api_key=_read_token(), timeout=timeout)
|
|
82
81
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|