@oneciel-ai/ciel-runtime 0.2.31 → 0.2.32
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/CHANGELOG.md +11 -0
- package/ciel_runtime_support/providers/zai.py +22 -1
- package/ciel_runtime_support/runtime_constants.py +1 -1
- package/ciel_runtime_support/zai_start_plan_captcha.py +86 -3
- package/docs/CLI-Reference.md +17 -0
- package/docs/Configuration.md +28 -0
- package/docs/journal/2026/08/24/mia-zai-start-plan-auth-ua-wire-verification.okf +75 -0
- package/docs/journal/2026/08/24/zai-start-plan-localhost-oauth-routing.okf +15 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
This file records stable Ciel Runtime releases. Changes are grouped by user-visible
|
|
4
4
|
capability, followed by the complete commit ledger merged into each release.
|
|
5
5
|
|
|
6
|
+
## 0.2.32 — 2026-08-24
|
|
7
|
+
|
|
8
|
+
- Keep Z.AI Start Plan wire identity aligned with the installed ZCode app:
|
|
9
|
+
`User-Agent: ZCode/3.8.1` and the OAuth JWT in `Authorization: Bearer`, without
|
|
10
|
+
adding `X-Api-Key`.
|
|
11
|
+
- Add opt-in remote CAPTCHA callback settings for browserless hosts: bind host,
|
|
12
|
+
fixed/dynamic port, public HTTP(S) origin, and timeout are configurable through
|
|
13
|
+
`ciel-runtimectl provider-options` or environment variables.
|
|
14
|
+
- Preserve loopback-only behavior by default and retain the state-bound,
|
|
15
|
+
one-request CAPTCHA result receiver when remote callback access is enabled.
|
|
16
|
+
|
|
6
17
|
## 0.2.31 — 2026-08-24
|
|
7
18
|
|
|
8
19
|
- Force Z.AI Start Plan OAuth through the state-bound
|
|
@@ -5,6 +5,7 @@ from typing import Any, Mapping
|
|
|
5
5
|
|
|
6
6
|
from ..architecture import (
|
|
7
7
|
ProviderCapabilities,
|
|
8
|
+
ProviderConfigurationPolicy,
|
|
8
9
|
ProviderConfig,
|
|
9
10
|
ProviderContextPolicy,
|
|
10
11
|
ProviderModelCatalogPolicy,
|
|
@@ -13,7 +14,7 @@ from ..architecture import (
|
|
|
13
14
|
ProviderStatusPolicy,
|
|
14
15
|
MessageProtocol,
|
|
15
16
|
)
|
|
16
|
-
from .base import HttpBearerProviderAdapter, provider_configuration
|
|
17
|
+
from .base import HttpBearerProviderAdapter, configuration_policy, provider_configuration
|
|
17
18
|
from .constants import PROVIDER_DEFAULT_BASE_URLS, ZAI_MODEL_FALLBACK_IDS
|
|
18
19
|
|
|
19
20
|
|
|
@@ -354,6 +355,26 @@ class ZaiStartPlanProviderAdapter(ZaiCodingPlanProviderAdapter):
|
|
|
354
355
|
) -> Mapping[str, str]:
|
|
355
356
|
return self.build_headers(config, api_key)
|
|
356
357
|
|
|
358
|
+
def configuration_policy(
|
|
359
|
+
self, config: ProviderConfig
|
|
360
|
+
) -> ProviderConfigurationPolicy:
|
|
361
|
+
del config
|
|
362
|
+
return configuration_policy(
|
|
363
|
+
text_option_aliases={
|
|
364
|
+
"captcha_bind_host": "zai_captcha_bind_host",
|
|
365
|
+
"captcha_port": "zai_captcha_port",
|
|
366
|
+
"captcha_public_base_url": "zai_captcha_public_base_url",
|
|
367
|
+
"captcha_timeout_seconds": "zai_captcha_timeout_seconds",
|
|
368
|
+
"zai_captcha_bind_host": "zai_captcha_bind_host",
|
|
369
|
+
"zai_captcha_port": "zai_captcha_port",
|
|
370
|
+
"zai_captcha_public_base_url": "zai_captcha_public_base_url",
|
|
371
|
+
"zai_captcha_timeout_seconds": "zai_captcha_timeout_seconds",
|
|
372
|
+
},
|
|
373
|
+
strip_trailing_slash_fields=frozenset(
|
|
374
|
+
{"zai_captcha_public_base_url"}
|
|
375
|
+
),
|
|
376
|
+
)
|
|
377
|
+
|
|
357
378
|
def launch_api_key_error(self, config: ProviderConfig) -> str | None:
|
|
358
379
|
if not config.api_keys:
|
|
359
380
|
return self.api_key_launch_error_value
|
|
@@ -79,6 +79,7 @@ class _CaptchaResultReceiver:
|
|
|
79
79
|
timeout_seconds: float
|
|
80
80
|
host: str = "127.0.0.1"
|
|
81
81
|
port: int = 0
|
|
82
|
+
public_base_url: str = ""
|
|
82
83
|
_server: _LoopbackCaptchaServer | None = field(default=None, init=False)
|
|
83
84
|
_thread: threading.Thread | None = field(default=None, init=False)
|
|
84
85
|
_ready: threading.Event = field(default_factory=threading.Event, init=False)
|
|
@@ -90,7 +91,36 @@ class _CaptchaResultReceiver:
|
|
|
90
91
|
if self._server is None:
|
|
91
92
|
raise RuntimeError("Z.AI CAPTCHA receiver was not started.")
|
|
92
93
|
query = urllib.parse.urlencode({"state": self.state})
|
|
93
|
-
|
|
94
|
+
base = self._resolved_public_base_url(self._server.server_port)
|
|
95
|
+
return f"{base}{_CAPTCHA_PATH}?{query}"
|
|
96
|
+
|
|
97
|
+
def _resolved_public_base_url(self, server_port: int) -> str:
|
|
98
|
+
configured = str(self.public_base_url or "").strip().rstrip("/")
|
|
99
|
+
if not configured:
|
|
100
|
+
return f"http://localhost:{server_port}"
|
|
101
|
+
candidate = configured.replace("{port}", str(server_port))
|
|
102
|
+
parsed = urllib.parse.urlsplit(candidate)
|
|
103
|
+
if (
|
|
104
|
+
parsed.scheme not in {"http", "https"}
|
|
105
|
+
or not parsed.hostname
|
|
106
|
+
or parsed.username is not None
|
|
107
|
+
or parsed.password is not None
|
|
108
|
+
or parsed.query
|
|
109
|
+
or parsed.fragment
|
|
110
|
+
or parsed.path not in {"", "/"}
|
|
111
|
+
):
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
"Z.AI CAPTCHA public base URL must be an HTTP(S) origin; "
|
|
114
|
+
"use {port} for the receiver port."
|
|
115
|
+
)
|
|
116
|
+
if "{port}" not in configured and parsed.port is None:
|
|
117
|
+
hostname = parsed.hostname
|
|
118
|
+
if ":" in hostname and not hostname.startswith("["):
|
|
119
|
+
hostname = f"[{hostname}]"
|
|
120
|
+
candidate = urllib.parse.urlunsplit(
|
|
121
|
+
(parsed.scheme, f"{hostname}:{server_port}", "", "", "")
|
|
122
|
+
)
|
|
123
|
+
return candidate.rstrip("/")
|
|
94
124
|
|
|
95
125
|
def __enter__(self) -> "_CaptchaResultReceiver":
|
|
96
126
|
receiver = self
|
|
@@ -314,13 +344,37 @@ class ZaiStartPlanCaptchaBroker:
|
|
|
314
344
|
def headers(self, options: Mapping[str, Any]) -> dict[str, str]:
|
|
315
345
|
app_version = str(options.get("zcode_app_version") or "3.8.1").strip()
|
|
316
346
|
timeout = self._timeout(options)
|
|
347
|
+
bind_host = self._bind_host(options)
|
|
348
|
+
port = self._port(options)
|
|
349
|
+
public_base_url = self._public_base_url(options)
|
|
317
350
|
with self._request_lock:
|
|
318
351
|
config = self.fetch_config(app_version)
|
|
319
352
|
state = self.random_state()
|
|
320
|
-
with self.receiver_factory(
|
|
353
|
+
with self.receiver_factory(
|
|
354
|
+
config,
|
|
355
|
+
state,
|
|
356
|
+
timeout,
|
|
357
|
+
host=bind_host,
|
|
358
|
+
port=port,
|
|
359
|
+
public_base_url=public_base_url,
|
|
360
|
+
) as receiver:
|
|
321
361
|
url = receiver.url
|
|
322
362
|
self.log("INFO", f"zai_start_plan_captcha_waiting url={url}")
|
|
323
|
-
|
|
363
|
+
try:
|
|
364
|
+
opened = self.open_url(url)
|
|
365
|
+
except Exception as exc:
|
|
366
|
+
if not public_base_url:
|
|
367
|
+
raise RuntimeError(
|
|
368
|
+
"Could not open the Z.AI Start Plan verification page: "
|
|
369
|
+
+ url
|
|
370
|
+
) from exc
|
|
371
|
+
opened = False
|
|
372
|
+
self.log(
|
|
373
|
+
"WARN",
|
|
374
|
+
"zai_start_plan_captcha_browser_open_failed "
|
|
375
|
+
f"url={url} error={type(exc).__name__}",
|
|
376
|
+
)
|
|
377
|
+
if not opened and not public_base_url:
|
|
324
378
|
raise RuntimeError(
|
|
325
379
|
"Could not open the Z.AI Start Plan verification page: " + url
|
|
326
380
|
)
|
|
@@ -345,6 +399,35 @@ class ZaiStartPlanCaptchaBroker:
|
|
|
345
399
|
except (TypeError, ValueError):
|
|
346
400
|
return 120.0
|
|
347
401
|
|
|
402
|
+
@staticmethod
|
|
403
|
+
def _bind_host(options: Mapping[str, Any]) -> str:
|
|
404
|
+
return str(
|
|
405
|
+
options.get("zai_captcha_bind_host")
|
|
406
|
+
or os.environ.get("CIEL_RUNTIME_ZAI_CAPTCHA_BIND_HOST")
|
|
407
|
+
or "127.0.0.1"
|
|
408
|
+
).strip()
|
|
409
|
+
|
|
410
|
+
@staticmethod
|
|
411
|
+
def _port(options: Mapping[str, Any]) -> int:
|
|
412
|
+
raw = options.get("zai_captcha_port") or os.environ.get(
|
|
413
|
+
"CIEL_RUNTIME_ZAI_CAPTCHA_PORT", "0"
|
|
414
|
+
)
|
|
415
|
+
try:
|
|
416
|
+
port = int(raw)
|
|
417
|
+
except (TypeError, ValueError) as exc:
|
|
418
|
+
raise RuntimeError("Z.AI CAPTCHA port must be an integer.") from exc
|
|
419
|
+
if port < 0 or port > 65535:
|
|
420
|
+
raise RuntimeError("Z.AI CAPTCHA port must be between 0 and 65535.")
|
|
421
|
+
return port
|
|
422
|
+
|
|
423
|
+
@staticmethod
|
|
424
|
+
def _public_base_url(options: Mapping[str, Any]) -> str:
|
|
425
|
+
return str(
|
|
426
|
+
options.get("zai_captcha_public_base_url")
|
|
427
|
+
or os.environ.get("CIEL_RUNTIME_ZAI_CAPTCHA_PUBLIC_BASE_URL")
|
|
428
|
+
or ""
|
|
429
|
+
).strip()
|
|
430
|
+
|
|
348
431
|
@staticmethod
|
|
349
432
|
def _platform_key() -> str:
|
|
350
433
|
system = {"Windows": "win32", "Darwin": "darwin", "Linux": "linux"}.get(
|
package/docs/CLI-Reference.md
CHANGED
|
@@ -108,6 +108,23 @@ ciel-runtimectl base-url [PROVIDER] [URL]
|
|
|
108
108
|
ciel-runtimectl base-url ollama http://remote-server:11434
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
#### `provider-options` — Z.AI Start Plan remote CAPTCHA
|
|
112
|
+
|
|
113
|
+
원격 Linux 호스트에서 Z.AI Start Plan의 사람 검증 페이지를 운영자 브라우저로
|
|
114
|
+
열어야 할 때는 접근 가능한 인터페이스만 명시적으로 바인딩한다. 기본값은 계속
|
|
115
|
+
`127.0.0.1`이며 외부에 공개되지 않는다.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
ciel-runtimectl provider-options zai-start-plan \
|
|
119
|
+
captcha_bind_host=100.95.132.58 \
|
|
120
|
+
captcha_port=42119 \
|
|
121
|
+
'captcha_public_base_url=http://100.95.132.58:{port}'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`captcha_public_base_url`은 `http://` 또는 `https://` origin만 허용하며,
|
|
125
|
+
`{port}`는 실제 CAPTCHA 수신 포트로 치환된다. URL에는 요청마다 새로 생성되는
|
|
126
|
+
state 값이 추가된다.
|
|
127
|
+
|
|
111
128
|
---
|
|
112
129
|
|
|
113
130
|
### API 키 관리
|
package/docs/Configuration.md
CHANGED
|
@@ -408,6 +408,34 @@ ConPTY 생성이 불가능하거나 `CIEL_RUNTIME_WINDOWS_CONPTY=0`으로 명시
|
|
|
408
408
|
ciel-runtimectl language ko
|
|
409
409
|
```
|
|
410
410
|
|
|
411
|
+
### Z.AI Start Plan remote CAPTCHA callback
|
|
412
|
+
|
|
413
|
+
Z.AI Start Plan은 모델 요청 전에 공식 Aliyun CAPTCHA의 일회성 결과가 필요할 수
|
|
414
|
+
있습니다. 브라우저가 없는 원격 호스트에서는 `provider-options`로 운영자
|
|
415
|
+
브라우저가 접근할 callback origin을 설정할 수 있습니다.
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
ciel-runtimectl provider-options zai-start-plan \
|
|
419
|
+
captcha_bind_host=100.95.132.58 \
|
|
420
|
+
captcha_port=42119 \
|
|
421
|
+
'captcha_public_base_url=http://100.95.132.58:{port}' \
|
|
422
|
+
captcha_timeout_seconds=120
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
- `captcha_bind_host`: 수신 서버가 바인딩할 로컬 인터페이스입니다. 기본값은
|
|
426
|
+
`127.0.0.1`입니다.
|
|
427
|
+
- `captcha_port`: 고정 포트이며 `0`이면 운영체제가 빈 포트를 선택합니다.
|
|
428
|
+
- `captcha_public_base_url`: 운영자 브라우저에 표시할 HTTP(S) origin입니다.
|
|
429
|
+
`{port}` 자리표시자를 사용할 수 있습니다.
|
|
430
|
+
- `captcha_timeout_seconds`: 결과 대기 시간이며 15~600초로 제한됩니다.
|
|
431
|
+
|
|
432
|
+
환경 변수 `CIEL_RUNTIME_ZAI_CAPTCHA_BIND_HOST`,
|
|
433
|
+
`CIEL_RUNTIME_ZAI_CAPTCHA_PORT`,
|
|
434
|
+
`CIEL_RUNTIME_ZAI_CAPTCHA_PUBLIC_BASE_URL`,
|
|
435
|
+
`CIEL_RUNTIME_ZAI_CAPTCHA_TIMEOUT_SECONDS`도 같은 값을 제공합니다. 공개 callback은
|
|
436
|
+
state-bound 일회성 URL을 사용하지만, 신뢰하는 사설 인터페이스에만 바인딩해야
|
|
437
|
+
합니다.
|
|
438
|
+
|
|
411
439
|
---
|
|
412
440
|
|
|
413
441
|
## 관련 문서
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
okf_version: "1.0"
|
|
2
|
+
knowledge:
|
|
3
|
+
task:
|
|
4
|
+
id: mia-zai-start-plan-auth-ua-wire-verification-20260824
|
|
5
|
+
date: "2026-08-24"
|
|
6
|
+
objective: >-
|
|
7
|
+
Verify from evidence whether Mia's Z.AI Start Plan routed requests carry
|
|
8
|
+
the OAuth bearer credential and the exact ZCode CLI user agent, and make
|
|
9
|
+
the required human CAPTCHA reachable from a remote operator browser.
|
|
10
|
+
status: in_progress
|
|
11
|
+
|
|
12
|
+
evidence:
|
|
13
|
+
official_zcode_runtime:
|
|
14
|
+
installed_cli_package: zcode-app-cli@3.8.1-15
|
|
15
|
+
app_version: 3.8.1
|
|
16
|
+
header_builder_observation: >-
|
|
17
|
+
The bundled ZCode runtime constructs User-Agent as ZCode/<appVersion>.
|
|
18
|
+
mia_installed_ciel_runtime:
|
|
19
|
+
version: 0.2.31-nightly.20260824-143754.e0bf793
|
|
20
|
+
provider: zai-start-plan
|
|
21
|
+
model: ciel-runtime-zai-start-plan-glm-5.3-1m
|
|
22
|
+
wire_capture:
|
|
23
|
+
user_agent: ZCode/3.8.1
|
|
24
|
+
authorization_present: true
|
|
25
|
+
authorization_scheme: Bearer
|
|
26
|
+
authorization_token_matches_configured_credential: true
|
|
27
|
+
x_api_key_present: false
|
|
28
|
+
secret_handling: >-
|
|
29
|
+
The credential value was not printed; verification used equality and
|
|
30
|
+
scheme checks only.
|
|
31
|
+
connection_failure:
|
|
32
|
+
reproduced_stage: pre-request CAPTCHA header acquisition
|
|
33
|
+
elapsed_seconds: 0.759
|
|
34
|
+
browser_probe: Python webbrowser.get() could not locate a runnable browser
|
|
35
|
+
request_reached_zai_gateway: false
|
|
36
|
+
conclusion: >-
|
|
37
|
+
The observed Mia failure occurs before the Z.AI model request, so the
|
|
38
|
+
verified Authorization and User-Agent headers are not its failing stage.
|
|
39
|
+
browser_runtime:
|
|
40
|
+
headless_shell_result: interactive CAPTCHA required and timed out
|
|
41
|
+
headed_chrome_xvfb_result: interactive CAPTCHA required and timed out
|
|
42
|
+
screenshot_observation: >-
|
|
43
|
+
The official Aliyun SDK displayed a human slider puzzle; changing UA or
|
|
44
|
+
running headed Chrome did not turn it into a traceless pass.
|
|
45
|
+
|
|
46
|
+
implementation:
|
|
47
|
+
auth_and_ua_behavior_change: none
|
|
48
|
+
remote_captcha_callback:
|
|
49
|
+
default_bind_host: 127.0.0.1
|
|
50
|
+
configurable_fields:
|
|
51
|
+
- zai_captcha_bind_host
|
|
52
|
+
- zai_captcha_port
|
|
53
|
+
- zai_captcha_public_base_url
|
|
54
|
+
- zai_captcha_timeout_seconds
|
|
55
|
+
security: >-
|
|
56
|
+
External binding remains opt-in. The public URL must be an HTTP(S)
|
|
57
|
+
origin, and each request retains its random state-bound result URL.
|
|
58
|
+
regression_coverage:
|
|
59
|
+
file: tests/test_zai_provider.py
|
|
60
|
+
assertion: >-
|
|
61
|
+
A real loopback HTTP request must retain User-Agent ZCode/3.8.1 and the
|
|
62
|
+
OAuth Bearer token and must not add X-Api-Key.
|
|
63
|
+
|
|
64
|
+
verification:
|
|
65
|
+
completed:
|
|
66
|
+
- Mia installed-package wire capture
|
|
67
|
+
- ZCode CLI version comparison
|
|
68
|
+
- focused Z.AI regression tests: 32 passed
|
|
69
|
+
- full regression suite: 2769 passed and 136 skipped
|
|
70
|
+
- remote callback GET 200 and state-bound result POST 204 from operator host
|
|
71
|
+
- operator Chrome traceless verification completed with a 280-byte result
|
|
72
|
+
- verification completion screenshot captured
|
|
73
|
+
pending:
|
|
74
|
+
- real Z.AI model response after CAPTCHA
|
|
75
|
+
- main, nightly, npm, and local deployment verification
|
|
@@ -57,4 +57,18 @@ knowledge:
|
|
|
57
57
|
source_installed_sha256_match: true
|
|
58
58
|
start_plan_credential_status: connected
|
|
59
59
|
release:
|
|
60
|
-
status:
|
|
60
|
+
status: passed
|
|
61
|
+
commit: 47605a5b71a2fc7dd7ead93269f5cf93c56cf66c
|
|
62
|
+
branches:
|
|
63
|
+
main: pushed
|
|
64
|
+
nightly: pushed
|
|
65
|
+
github_actions:
|
|
66
|
+
main_ci: success
|
|
67
|
+
main_npm_publish: success
|
|
68
|
+
nightly_ci: success
|
|
69
|
+
nightly_npm_publish: success
|
|
70
|
+
npm:
|
|
71
|
+
latest: 0.2.31
|
|
72
|
+
nightly: 0.2.31-nightly.20260824-142935.47605a5
|
|
73
|
+
latest_npx_version_verified: true
|
|
74
|
+
nightly_npx_version_verified: true
|
package/package.json
CHANGED