direxio-deployer 0.1.2 → 0.1.4
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/AGENTS.md +1 -1
- package/README.md +12 -10
- package/README_zh.md +12 -10
- package/SKILL.md +41 -19
- package/bin/direxio-deployer.mjs +6 -6
- package/package.json +2 -2
- package/references/agent-targets.md +27 -27
- package/references/deployment-workflow.md +20 -4
- package/references/runtime-wiring.md +2 -2
- package/references/state-machine.md +3 -3
- package/references/token-refresh.md +1 -1
- package/references/windows-deployment-notes.md +1 -1
- package/scripts/cloud-init/init-tokens.sh +10 -5
- package/scripts/lib/aws.sh +3 -0
- package/scripts/lib/state.sh +40 -0
- package/scripts/mcp-tools-list.mjs +59 -123
- package/scripts/orchestrate.sh +25 -5
- package/scripts/phases/s1_preflight.sh +37 -4
- package/scripts/phases/s3_provision.sh +1 -1
- package/scripts/phases/s6_wire_local.sh +56 -17
package/AGENTS.md
CHANGED
|
@@ -42,7 +42,7 @@ If a change writes a path into `state.json`, `credentials.json`, `env`, `cc-conn
|
|
|
42
42
|
## Direxio Connect Wiring
|
|
43
43
|
|
|
44
44
|
- S5/S6 must fail closed when `agent_room_id` is missing or uses a legacy pseudo id such as `!agent:<domain>`.
|
|
45
|
-
- S6 must create a Matrix session through `agent.matrix_session.create` and require `@agent:<server>` for the bridge. Returning `@owner:<server>` is a server-side compatibility failure.
|
|
45
|
+
- S6 must create a Matrix session through `agent.matrix_session.create` using `agent_token`, not owner `access_token`, and require `@agent:<server>` for the bridge. Returning `@owner:<server>` is a server-side compatibility failure.
|
|
46
46
|
- The generated cc-connect config must contain one Matrix platform and must restrict sync/replies to the real `agent_room_id`.
|
|
47
47
|
- The generated agent config must preserve the selected connect agent type and optional agent-specific TOML. Some providers require more than `cmd`; for example `reasonix` needs `serve_url`, `tmux` needs `session`, and generic `acp` may need command/args.
|
|
48
48
|
- `DIREXIO_AGENT_INSTALL=auto` may install/start `direxio-connect`; `recommend` must only write files and print commands.
|
package/README.md
CHANGED
|
@@ -19,48 +19,50 @@
|
|
|
19
19
|
|
|
20
20
|
## Skill Installation And Updates
|
|
21
21
|
|
|
22
|
-
Install the deployer skill from npm, then place it into the current agent runtime's skill directory.
|
|
22
|
+
Install the deployer skill from npm, then place it into the current agent runtime's skill directory. The default install is global for the selected agent runtime. Use a project-local install only when you explicitly want the skill copied into a specific repository or workspace.
|
|
23
23
|
|
|
24
24
|
The GitHub repository keeps tests for maintainers and CI, but the published npm package and installed skill copy exclude `tests/` to keep user installs small.
|
|
25
25
|
|
|
26
|
-
If you want Codex to install and deploy in one instruction, do not say "install skills <GitHub URL>". That triggers GitHub skill installation
|
|
26
|
+
If you want Codex to install and deploy in one instruction, do not say "install skills <GitHub URL>". That triggers GitHub skill installation instead of the npm-managed installer. Use a short instruction that tells the agent to read this README's npm install rule:
|
|
27
27
|
|
|
28
28
|
```text
|
|
29
|
-
|
|
29
|
+
Follow the direxio-deployer README npm install rule, then deploy Direxio with domain __DOMAIN__.
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
After reading this instruction, the agent should run the npm install commands below; it should not switch to a GitHub skill installer.
|
|
33
|
+
|
|
32
34
|
POSIX shells:
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
npm install -g direxio-deployer@latest
|
|
36
|
-
direxio-deployer skill install --agent codex
|
|
38
|
+
direxio-deployer skill install --agent codex
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
Windows PowerShell:
|
|
40
42
|
|
|
41
43
|
```powershell
|
|
42
44
|
npm install -g direxio-deployer@latest
|
|
43
|
-
direxio-deployer skill install --agent codex
|
|
45
|
+
direxio-deployer skill install --agent codex
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
Update the installed skill with the same host runtime:
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
51
|
npm install -g direxio-deployer@latest
|
|
50
|
-
direxio-deployer skill update --agent codex
|
|
52
|
+
direxio-deployer skill update --agent codex
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
Use the matching agent name for your runtime: `codex`, `claudecode`, `gemini`, `cursor`, `copilot`, `openclaw`, `hermes`, `opencode`, `qoder`, `reasonix`, or another target listed in `references/agent-targets.md`.
|
|
55
|
+
Use the matching agent name for your runtime: `codex`, `claudecode`, `gemini`, `cursor`, `copilot`, `openclaw`, `hermes`, `opencode`, `qoder`, `reasonix`, or another target listed in `references/agent-targets.md`. Add `--scope project --project <path>` only when you intentionally want a repository-local skill install:
|
|
54
56
|
|
|
55
57
|
```bash
|
|
56
|
-
direxio-deployer skill install --agent codex --scope
|
|
58
|
+
direxio-deployer skill install --agent codex --scope project --project .
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
The installer writes `.direxio-skill-install.json` into the target directory and refuses to overwrite unmanaged existing content unless `--force` is provided. To pin a version, install that package version first:
|
|
60
62
|
|
|
61
63
|
```bash
|
|
62
|
-
npm install -g direxio-deployer@0.1.
|
|
63
|
-
direxio-deployer skill update --agent codex
|
|
64
|
+
npm install -g direxio-deployer@0.1.4
|
|
65
|
+
direxio-deployer skill update --agent codex
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
The CLI is implemented in Node and uses native paths for the host it runs on. On Windows it writes Windows-compatible paths; on Linux, macOS, Git Bash, or WSL it writes paths for that runtime.
|
package/README_zh.md
CHANGED
|
@@ -17,48 +17,50 @@
|
|
|
17
17
|
|
|
18
18
|
## Skill 安装和更新
|
|
19
19
|
|
|
20
|
-
通过 npm 安装 deployer skill,再把它写入当前智能体运行时的 skill
|
|
20
|
+
通过 npm 安装 deployer skill,再把它写入当前智能体运行时的 skill 目录。默认安装到所选智能体运行时的全局 skill 目录;只有在明确希望跟随某个仓库或 workspace 时,才指定 project-local 安装。
|
|
21
21
|
|
|
22
22
|
GitHub 仓库保留测试用于维护和 CI,但发布到 npm 的包以及安装到智能体 skill 目录的副本不包含 `tests/`,以减小用户安装体积。
|
|
23
23
|
|
|
24
|
-
如果你想让 Codex 一句话安装并开始部署,不要说“安装 skills <GitHub 链接>”。那会触发 GitHub skill
|
|
24
|
+
如果你想让 Codex 一句话安装并开始部署,不要说“安装 skills <GitHub 链接>”。那会触发 GitHub skill 安装器,而不是 npm 管理的安装器。推荐只说短句,让 agent 先读本 README 中的 npm 安装规则:
|
|
25
25
|
|
|
26
26
|
```text
|
|
27
|
-
|
|
27
|
+
请按 direxio-deployer README 的 npm 安装规则安装 skill,然后部署 Direxio,域名 __DOMAIN__。
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Agent 读到这句后应执行下方 npm 安装命令;不要改用 GitHub skill installer。
|
|
31
|
+
|
|
30
32
|
POSIX shell:
|
|
31
33
|
|
|
32
34
|
```bash
|
|
33
35
|
npm install -g direxio-deployer@latest
|
|
34
|
-
direxio-deployer skill install --agent codex
|
|
36
|
+
direxio-deployer skill install --agent codex
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
Windows PowerShell:
|
|
38
40
|
|
|
39
41
|
```powershell
|
|
40
42
|
npm install -g direxio-deployer@latest
|
|
41
|
-
direxio-deployer skill install --agent codex
|
|
43
|
+
direxio-deployer skill install --agent codex
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
在同一个宿主运行时中更新已安装 skill:
|
|
45
47
|
|
|
46
48
|
```bash
|
|
47
49
|
npm install -g direxio-deployer@latest
|
|
48
|
-
direxio-deployer skill update --agent codex
|
|
50
|
+
direxio-deployer skill update --agent codex
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
根据当前运行时替换 agent 名称:`codex`、`claudecode`、`gemini`、`cursor`、`copilot`、`openclaw`、`hermes`、`opencode`、`qoder`、`reasonix`,或使用 `references/agent-targets.md`
|
|
53
|
+
根据当前运行时替换 agent 名称:`codex`、`claudecode`、`gemini`、`cursor`、`copilot`、`openclaw`、`hermes`、`opencode`、`qoder`、`reasonix`,或使用 `references/agent-targets.md` 中列出的其他目标。只有明确想安装到某个项目目录时才使用 `--scope project --project <path>`:
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
direxio-deployer skill install --agent codex --scope
|
|
56
|
+
direxio-deployer skill install --agent codex --scope project --project .
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
安装器会在目标目录写入 `.direxio-skill-install.json`,并拒绝覆盖没有该 manifest 的既有目录,除非显式传入 `--force`。如需固定版本,先安装指定 npm 版本:
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
|
-
npm install -g direxio-deployer@0.1.
|
|
61
|
-
direxio-deployer skill update --agent codex
|
|
62
|
+
npm install -g direxio-deployer@0.1.4
|
|
63
|
+
direxio-deployer skill update --agent codex
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
这个 CLI 由 Node 实现,并使用当前宿主的原生路径。Windows 下写入 Windows 路径;Linux、macOS、Git Bash 或 WSL 下写入对应运行时能读取的路径。
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: direxio-deployer
|
|
3
|
-
description: Deploy, resume, verify, destroy, and locally wire a production P2P-IM Matrix server on AWS for any connent/connect-supported local agent runtime. Use when installing or updating this skill itself; install the versioned npm package `direxio-deployer` and use its CLI to place the skill in the runtime-specific
|
|
3
|
+
description: Deploy, resume, verify, destroy, and locally wire a production P2P-IM Matrix server on AWS for any connent/connect-supported local agent runtime. Use when installing or updating this skill itself; install the versioned npm package `direxio-deployer` and use its CLI to place the skill in the runtime-specific global path from references/agent-targets.md unless the user explicitly asks for a project-local installation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Direxio Deployer
|
|
@@ -24,13 +24,17 @@ against the versioned npm package:
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npm install -g direxio-deployer@latest
|
|
27
|
-
direxio-deployer skill refresh --agent <runtime>
|
|
27
|
+
direxio-deployer skill refresh --agent <runtime>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Use the current runtime name for `<runtime>` when known, such as `codex`,
|
|
31
|
-
`claudecode`, `gemini`, `cursor`, `openclaw`, or `hermes`.
|
|
32
|
-
when
|
|
33
|
-
|
|
31
|
+
`claudecode`, `gemini`, `cursor`, `openclaw`, or `hermes`. Use project scope
|
|
32
|
+
only when the user explicitly asks for a repository-local install or provides
|
|
33
|
+
a project root for that purpose:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
direxio-deployer skill refresh --agent <runtime> --scope project --project <project-root>
|
|
37
|
+
```
|
|
34
38
|
|
|
35
39
|
`direxio-deployer skill refresh` checks the latest npm version, updates the
|
|
36
40
|
global CLI when npm reports a newer package, and refreshes the managed skill
|
|
@@ -261,6 +265,19 @@ Step-by-step onboarding flow:
|
|
|
261
265
|
credits only apply when the account, plan, region, and service usage are
|
|
262
266
|
eligible. Recommend setting an AWS Budget or billing alert before leaving
|
|
263
267
|
the node running.
|
|
268
|
+
- **Before asking for the final deployment confirmation**, run the S1
|
|
269
|
+
preflight or equivalent AWS CLI checks for the selected region so hard
|
|
270
|
+
capacity blockers are known up front. In particular, check EC2-VPC Elastic IP quota because each deployment needs one fixed public IP:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
aws service-quotas get-service-quota --service-code ec2 --quota-code L-0263D0A3 --query 'Quota.Value' --output text
|
|
274
|
+
aws ec2 describe-addresses --query 'length(Addresses[?Domain==`vpc`])' --output text
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
If allocated Elastic IPs are already greater than or equal to quota, stop
|
|
278
|
+
before confirmation and tell the user to release an unused Elastic IP,
|
|
279
|
+
request quota, or choose another region. Do not wait until S3 allocation
|
|
280
|
+
fails on the live deployment path.
|
|
264
281
|
- If the user asks what is billed, mention EC2/server, fixed public IPv4 or
|
|
265
282
|
Elastic IP, storage, DNS, network traffic, and call relay traffic.
|
|
266
283
|
|
|
@@ -300,24 +317,25 @@ When the user asks to install or update this skill itself, or asks to wire Direx
|
|
|
300
317
|
If the user says "install skills" and includes the GitHub repository URL
|
|
301
318
|
`YingSuiAI/direxio-deployer`, do not use a generic GitHub skill installer for
|
|
302
319
|
normal deployment use. First install the npm package and then run
|
|
303
|
-
`direxio-deployer skill install --agent <runtime
|
|
304
|
-
<project-root>`. Use a Git clone only when the user explicitly asks for
|
|
320
|
+
`direxio-deployer skill install --agent <runtime>`. Use a Git clone only when the user explicitly asks for
|
|
305
321
|
deployer development or local patching.
|
|
306
322
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
If a project target exists, install or update this skill with the versioned npm CLI at the runtime-specific project-local path from `references/agent-targets.md`:
|
|
323
|
+
Install or update this skill with the versioned npm CLI at the runtime-specific
|
|
324
|
+
global path from `references/agent-targets.md`:
|
|
310
325
|
|
|
311
326
|
```bash
|
|
312
327
|
npm install -g direxio-deployer@latest
|
|
313
|
-
direxio-deployer skill install --agent <runtime>
|
|
314
|
-
direxio-deployer skill update --agent <runtime>
|
|
328
|
+
direxio-deployer skill install --agent <runtime>
|
|
329
|
+
direxio-deployer skill update --agent <runtime>
|
|
315
330
|
```
|
|
316
331
|
|
|
317
|
-
The installer writes `.direxio-skill-install.json` into the target directory
|
|
332
|
+
The installer writes `.direxio-skill-install.json` into the target directory
|
|
333
|
+
and refuses to overwrite unmanaged existing content unless the operator
|
|
334
|
+
explicitly uses `--force`. Use project-local runtime skill directories only
|
|
335
|
+
when the user explicitly asks to install into a repository or workspace:
|
|
318
336
|
|
|
319
337
|
```bash
|
|
320
|
-
direxio-deployer skill install --agent <runtime> --scope
|
|
338
|
+
direxio-deployer skill install --agent <runtime> --scope project --project <project-root>
|
|
321
339
|
```
|
|
322
340
|
|
|
323
341
|
Use a Git clone only for development or local patching of this deployer, not as the normal end-user installation path.
|
|
@@ -355,7 +373,7 @@ The local MCP tool surface is `direxio-mcp`, installed from `direxio-mcp@latest`
|
|
|
355
373
|
|
|
356
374
|
`DIREXIO_AGENT_PLATFORM` describes the host runtime following the skill, while `DIREXIO_CC_CONNECT_AGENT` describes the local agent backend that `direxio-connect` should launch. Host runtimes such as Hermes or OpenClaw are not native cc-connect backend types; S6 maps them to the generic ACP backend by default and records `cc_connect_agent=acp`. Override `DIREXIO_CC_CONNECT_AGENT` only when the operator intentionally wants a different local backend.
|
|
357
375
|
|
|
358
|
-
`DIREXIO_AGENT_INSTALL` may be `skip`, `recommend`, or `auto`. Only `auto` attempts to run `npm install -g direxio-connent@latest` and `direxio-connect daemon install --config ~/.direxio/nodes/<service_id>/cc-connect/config.toml --service-name <service_id> --force`; the default `recommend` records and prints the command without mutating local daemon state. An automatic install is reported as installed only when `direxio-connect daemon status --service-name <service_id>` returns `Status: Running` and recent daemon logs do not show ACP session initialization failure; otherwise S6 records `agent_install_status=install_failed`.
|
|
376
|
+
`DIREXIO_AGENT_INSTALL` may be `skip`, `recommend`, or `auto`. Only `auto` attempts to run `npm install -g direxio-connent@latest` and `direxio-connect daemon install --config ~/.direxio/nodes/<service_id>/cc-connect/config.toml --service-name <service_id> --force`; the default `recommend` records and prints the command without mutating local daemon state. An automatic install is reported as installed only when `direxio-connect daemon status --service-name <service_id>` returns `Status: Running` and recent daemon logs do not show ACP session initialization failure; otherwise S6 records `agent_install_status=install_failed`. S6 calls `agent.matrix_session.create` with `agent_token` and retries transient HTTP 000/404/408/409/425/429/5xx responses before failing, because the Matrix action can become reachable after `/healthz`; defaults are 12 attempts with exponential backoff capped by `DIREXIO_MATRIX_SESSION_RETRY_MAX_INTERVAL`.
|
|
359
377
|
|
|
360
378
|
Voice input is supported through `direxio-connect` speech-to-text. When `DIREXIO_SPEECH_API_KEY` or a provider-specific key such as `DIREXIO_SPEECH_QWEN_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, `DASHSCOPE_API_KEY`, `GEMINI_API_KEY`, or `GOOGLE_API_KEY` is present, S6 writes `[speech] enabled = true` into the generated config. Without an STT key, do not claim voice input is enabled.
|
|
361
379
|
|
|
@@ -398,9 +416,12 @@ DOMAIN=<DOMAIN> bash scripts/orchestrate.sh verify mcp_tools
|
|
|
398
416
|
DOMAIN=<DOMAIN> bash scripts/orchestrate.sh verify mcp_smoke
|
|
399
417
|
```
|
|
400
418
|
|
|
401
|
-
Use `verify runtime` as the normal aggregate check. It runs
|
|
402
|
-
|
|
403
|
-
|
|
419
|
+
Use `verify runtime` as the normal aggregate check. It runs MCP doctor, MCP
|
|
420
|
+
`tools/list`, and read-only backend smoke. It also runs the service-scoped
|
|
421
|
+
connect daemon check when the daemon was expected to be installed; when S6
|
|
422
|
+
recorded `agent_install_status=recommend` or `skip`, the aggregate marks
|
|
423
|
+
`runtime_checks.connect_daemon.status=manual_pending` and does not fail the
|
|
424
|
+
summary for that explicit operator action. The individual commands are useful
|
|
404
425
|
when diagnosing one layer. These commands write `runtime_checks.connect_daemon`,
|
|
405
426
|
`runtime_checks.mcp_doctor`, `runtime_checks.mcp_tools`, and
|
|
406
427
|
`runtime_checks.mcp_smoke` into `state.json` and the operation report.
|
|
@@ -503,7 +524,8 @@ NS nameservers before authoritative DNS can resolve. Never use temporary
|
|
|
503
524
|
**Credential freshness:** The synced `password` and owner `access_token`
|
|
504
525
|
are one-time/volatile values. User login or token exchange can reset them
|
|
505
526
|
on the server. Before reporting the eight-digit app initialization code or using an owner
|
|
506
|
-
`access_token` for API calls,
|
|
527
|
+
`access_token` for owner API calls, or using `agent_token` for
|
|
528
|
+
`agent.matrix_session.create`, rerun the credential sync path or pull the
|
|
507
529
|
latest `/opt/p2p/bootstrap.json` from the server; do not reuse values from
|
|
508
530
|
old chat output, old `state.json`, or stale local `credentials.json`.
|
|
509
531
|
**Runtime detection note:** S6 checks active-process signals before stale
|
package/bin/direxio-deployer.mjs
CHANGED
|
@@ -94,14 +94,14 @@ function main() {
|
|
|
94
94
|
|
|
95
95
|
function usage(exitCode) {
|
|
96
96
|
const output = `Usage:
|
|
97
|
-
direxio-deployer skill install --agent <runtime> [--scope project
|
|
98
|
-
direxio-deployer skill update --agent <runtime> [--scope project
|
|
99
|
-
direxio-deployer skill refresh --agent <runtime> [--scope project
|
|
97
|
+
direxio-deployer skill install --agent <runtime> [--scope global|project] [--project <path>]
|
|
98
|
+
direxio-deployer skill update --agent <runtime> [--scope global|project] [--project <path>]
|
|
99
|
+
direxio-deployer skill refresh --agent <runtime> [--scope global|project] [--project <path>]
|
|
100
100
|
|
|
101
101
|
Options:
|
|
102
102
|
--agent <runtime> Target agent runtime. Default: codex
|
|
103
|
-
--scope <scope>
|
|
104
|
-
--project <path> Project root for project installs. Default: current directory
|
|
103
|
+
--scope <scope> global or project. Default: global
|
|
104
|
+
--project <path> Project root for explicit project installs. Default: current directory
|
|
105
105
|
--target <path> Explicit install target override
|
|
106
106
|
--home <path> Home directory override for global installs
|
|
107
107
|
--dry-run Resolve and print without writing
|
|
@@ -115,7 +115,7 @@ Options:
|
|
|
115
115
|
function parseArgs(args) {
|
|
116
116
|
const options = {
|
|
117
117
|
agent: "codex",
|
|
118
|
-
scope: "
|
|
118
|
+
scope: "global",
|
|
119
119
|
project: process.cwd(),
|
|
120
120
|
home: homedir(),
|
|
121
121
|
target: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "direxio-deployer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Versioned Direxio deployer agent skill and portable deployment orchestration tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"scripts/"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"test": "bash tests/npm_skill_distribution_test.sh && bash tests/skill_structure_test.sh && bash tests/s6_wire_local_test.sh && bash tests/render_userdata_remote_nodes_test.sh"
|
|
21
|
+
"test": "bash tests/npm_skill_distribution_test.sh && bash tests/skill_structure_test.sh && bash tests/private_file_permissions_test.sh && bash tests/eip_preflight_test.sh && bash tests/mcp_tools_runtime_check_test.sh && bash tests/runtime_summary_check_test.sh && bash tests/s6_wire_local_test.sh && bash tests/render_userdata_remote_nodes_test.sh"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18"
|
|
@@ -4,52 +4,52 @@ Use this file when installing or updating this skill and when reviewing S6 local
|
|
|
4
4
|
|
|
5
5
|
## Npm Skill Installation
|
|
6
6
|
|
|
7
|
-
Prefer
|
|
7
|
+
Prefer the npm-managed global install for normal users. Install the versioned package, then let the CLI copy the skill bundle into the selected runtime's host-level skill directory:
|
|
8
8
|
|
|
9
|
-
Do not use a generic "install skills <GitHub URL>" instruction for normal users. That can invoke a host's GitHub skill installer
|
|
9
|
+
Do not use a generic "install skills <GitHub URL>" instruction for normal users. That can invoke a host's GitHub skill installer instead of the npm-managed installer. A short user prompt should point the agent back to this npm install rule:
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
|
|
12
|
+
Follow the direxio-deployer README npm install rule, then deploy Direxio with domain __DOMAIN__.
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
POSIX shells:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm install -g direxio-deployer@latest
|
|
19
|
-
direxio-deployer skill install --agent codex
|
|
20
|
-
direxio-deployer skill update --agent codex
|
|
19
|
+
direxio-deployer skill install --agent codex
|
|
20
|
+
direxio-deployer skill update --agent codex
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Windows PowerShell:
|
|
24
24
|
|
|
25
25
|
```powershell
|
|
26
26
|
npm install -g direxio-deployer@latest
|
|
27
|
-
direxio-deployer skill install --agent codex
|
|
28
|
-
direxio-deployer skill update --agent codex
|
|
27
|
+
direxio-deployer skill install --agent codex
|
|
28
|
+
direxio-deployer skill update --agent codex
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Use `--scope
|
|
31
|
+
Use `--scope project --project PROJECT_ROOT` only when the user explicitly asks for a repository-local install. Use a Git clone only for deployer development or local patching, not as the normal end-user installation path. The npm installer writes `.direxio-skill-install.json` and refuses to overwrite unmanaged existing target directories unless `--force` is passed.
|
|
32
32
|
|
|
33
|
-
| Runtime |
|
|
33
|
+
| Runtime | Default global skill target | Explicit project-local skill target |
|
|
34
34
|
| --- | --- | --- |
|
|
35
|
-
| Codex |
|
|
36
|
-
| Claude Code |
|
|
37
|
-
| Gemini |
|
|
38
|
-
| Cursor |
|
|
39
|
-
| GitHub Copilot |
|
|
40
|
-
| OpenClaw |
|
|
41
|
-
| Hermes |
|
|
42
|
-
| ACP-compatible |
|
|
43
|
-
| Antigravity |
|
|
44
|
-
| Devin |
|
|
45
|
-
| iFlow |
|
|
46
|
-
| Kimi |
|
|
47
|
-
| OpenCode |
|
|
48
|
-
| Pi |
|
|
49
|
-
| Qoder |
|
|
50
|
-
| Reasonix |
|
|
51
|
-
| tmux |
|
|
52
|
-
| Generic or unknown |
|
|
35
|
+
| Codex | `${CODEX_HOME:-$HOME/.codex}/skills/direxio-deployer` | `PROJECT_ROOT/.codex/skills/direxio-deployer` |
|
|
36
|
+
| Claude Code | `${CLAUDE_HOME:-$HOME/.claude}/skills/direxio-deployer` | `PROJECT_ROOT/.claude/skills/direxio-deployer` |
|
|
37
|
+
| Gemini | `${GEMINI_HOME:-$HOME/.gemini}/skills/direxio-deployer` | `PROJECT_ROOT/.gemini/skills/direxio-deployer` |
|
|
38
|
+
| Cursor | `${CURSOR_HOME:-$HOME/.cursor}/skills/direxio-deployer` | `PROJECT_ROOT/.cursor/skills/direxio-deployer` |
|
|
39
|
+
| GitHub Copilot | `$HOME/.github/copilot/skills/direxio-deployer` | `PROJECT_ROOT/.github/copilot/skills/direxio-deployer` |
|
|
40
|
+
| OpenClaw | `${OPENCLAW_HOME:-$HOME/.openclaw}/skills/direxio-deployer` | `PROJECT_ROOT/.openclaw/skills/direxio-deployer` |
|
|
41
|
+
| Hermes | `${HERMES_HOME:-$HOME/.hermes}/skills/direxio-deployer` | `PROJECT_ROOT/.hermes/skills/direxio-deployer` |
|
|
42
|
+
| ACP-compatible | `$HOME/.agents/skills/direxio-deployer` | `PROJECT_ROOT/.agents/skills/direxio-deployer` |
|
|
43
|
+
| Antigravity | `${ANTIGRAVITY_HOME:-$HOME/.antigravity}/skills/direxio-deployer` | `PROJECT_ROOT/.antigravity/skills/direxio-deployer` |
|
|
44
|
+
| Devin | `${DEVIN_HOME:-$HOME/.devin}/skills/direxio-deployer` | `PROJECT_ROOT/.devin/skills/direxio-deployer` |
|
|
45
|
+
| iFlow | `${IFLOW_HOME:-$HOME/.iflow}/skills/direxio-deployer` | `PROJECT_ROOT/.iflow/skills/direxio-deployer` |
|
|
46
|
+
| Kimi | `${KIMI_HOME:-$HOME/.kimi}/skills/direxio-deployer` | `PROJECT_ROOT/.kimi/skills/direxio-deployer` |
|
|
47
|
+
| OpenCode | `${OPENCODE_HOME:-$HOME/.opencode}/skills/direxio-deployer` | `PROJECT_ROOT/.opencode/skills/direxio-deployer` |
|
|
48
|
+
| Pi | `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/skills/direxio-deployer` | `PROJECT_ROOT/.pi/agent/skills/direxio-deployer` |
|
|
49
|
+
| Qoder | `${QODER_HOME:-$HOME/.qoder}/skills/direxio-deployer` | `PROJECT_ROOT/.qoder/skills/direxio-deployer` |
|
|
50
|
+
| Reasonix | `${REASONIX_HOME:-$HOME/.reasonix}/skills/direxio-deployer` | `PROJECT_ROOT/.reasonix/skills/direxio-deployer` |
|
|
51
|
+
| tmux | `$HOME/.agent/skills/direxio-deployer` | `PROJECT_ROOT/.agent/skills/direxio-deployer` |
|
|
52
|
+
| Generic or unknown | `$HOME/.agent/skills/direxio-deployer` | `PROJECT_ROOT/.agent/skills/direxio-deployer` |
|
|
53
53
|
|
|
54
54
|
## Direxio Connect Target
|
|
55
55
|
|
|
@@ -4,11 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
1. Confirm `DOMAIN`, `DOMAIN_MODE`, and `CONFIRM_DOMAIN_BINDING=1`.
|
|
6
6
|
2. Confirm AWS region, credentials, billing, instance type, and costs.
|
|
7
|
-
3.
|
|
8
|
-
|
|
7
|
+
3. Before asking for final deployment confirmation, check regional hard blockers
|
|
8
|
+
that can be read without creating resources: default VPC, EC2 vCPU quota,
|
|
9
|
+
Elastic IP quota/current allocation, and Ubuntu AMI availability. The S1
|
|
10
|
+
preflight performs these checks; for a manual EIP check, compare:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
aws service-quotas get-service-quota --service-code ec2 --quota-code L-0263D0A3 --query 'Quota.Value' --output text
|
|
14
|
+
aws ec2 describe-addresses --query 'length(Addresses[?Domain==`vpc`])' --output text
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If the selected region has no available Elastic IP capacity, stop before
|
|
18
|
+
confirmation and ask the user to release an unused Elastic IP, request a
|
|
19
|
+
higher EC2-VPC Elastic IP quota, or choose another region.
|
|
20
|
+
4. Check dependencies with the OS-specific commands in `tooling.md`.
|
|
21
|
+
5. Check current DNS provider. Prefer `DOMAIN_MODE=route53` when the user
|
|
9
22
|
confirms AWS may manage the hosted zone and A record. Use `DOMAIN_MODE=user`
|
|
10
23
|
only as a fallback when no DNS provider automation is available.
|
|
11
|
-
|
|
24
|
+
6. Check state:
|
|
12
25
|
|
|
13
26
|
```bash
|
|
14
27
|
bash scripts/orchestrate.sh status
|
|
@@ -145,7 +158,10 @@ When the user or runtime evidence confirms a manual product gate, write it back
|
|
|
145
158
|
to state before regenerating the report. Connect daemon status is a
|
|
146
159
|
service-scoped local bridge check, MCP doctor is a non-polluting runtime check,
|
|
147
160
|
MCP tools is stdio `tools/list` discovery, and MCP smoke is a read-only backend
|
|
148
|
-
call.
|
|
161
|
+
call. In the default `DIREXIO_AGENT_INSTALL=recommend` path, `verify runtime`
|
|
162
|
+
records `connect_daemon=manual_pending` instead of failing the aggregate,
|
|
163
|
+
because daemon installation is an explicit operator action. These checks are
|
|
164
|
+
not the full runtime product gate:
|
|
149
165
|
|
|
150
166
|
```bash
|
|
151
167
|
DOMAIN=__DOMAIN__ bash scripts/orchestrate.sh verify runtime
|
|
@@ -74,7 +74,7 @@ DIREXIO_CREDENTIALS_FILE=~/.direxio/nodes/<service_id>/credentials.json direxio-
|
|
|
74
74
|
|
|
75
75
|
## cc-connect Matrix Bridge
|
|
76
76
|
|
|
77
|
-
S6 calls `agent.matrix_session.create` with the owner
|
|
77
|
+
S6 calls `agent.matrix_session.create` with the backend `agent_token`, not the owner `access_token`. Current message-server builds must return a Matrix session for `@agent:<server>`, not for `@owner:<server>`. S6 retries transient HTTP 000/404/408/409/425/429/5xx responses before failing, because the Matrix action can become reachable after `/healthz`; defaults are 12 attempts with exponential backoff capped by `DIREXIO_MATRIX_SESSION_RETRY_MAX_INTERVAL`. The resulting session is stored at:
|
|
78
78
|
|
|
79
79
|
```text
|
|
80
80
|
~/.direxio/nodes/<service_id>/cc-connect/matrix-session.json
|
|
@@ -137,7 +137,7 @@ Defaults:
|
|
|
137
137
|
- `DIREXIO_CC_CONNECT_AGENT_OPTIONS_TOML` appends agent-specific options under `[projects.agent.options]`; use it for agents with required non-command options such as `reasonix` (`serve_url`) or `tmux` (`session`).
|
|
138
138
|
- OpenClaw Gateway ACP auto-detects the Gateway from `~/.openclaw/openclaw.json` when `DIREXIO_OPENCLAW_ACP_URL` and `DIREXIO_OPENCLAW_ACP_TOKEN_FILE` are unset. It uses `DIREXIO_OPENCLAW_ACP_SESSION` when provided, otherwise `agent:main:main`. To force explicit Gateway settings, complete OpenClaw pairing first and set all three real values: `DIREXIO_OPENCLAW_ACP_URL`, `DIREXIO_OPENCLAW_ACP_TOKEN_FILE`, and `DIREXIO_OPENCLAW_ACP_SESSION`.
|
|
139
139
|
- `DIREXIO_OPENCLAW_ACP_ARGS_TOML` replaces the generated OpenClaw ACP args array, for example `["acp", "--url", "wss://gateway.example.test:18789", "--token-file", "$HOME/.openclaw/gateway.token", "--session", "agent:main:main"]`. `DIREXIO_HERMES_ACP_ARGS_TOML` supplies the child Hermes args; S6 prefixes `["hermes-acp-adapter", "--", "<hermes-command>"]` automatically.
|
|
140
|
-
- `DIREXIO_AGENT_INSTALL=recommend` prints and records the command only.
|
|
140
|
+
- `DIREXIO_AGENT_INSTALL=recommend` prints and records the command only. `verify runtime` records the daemon check as `manual_pending` in this mode and still verifies MCP doctor/tools/smoke.
|
|
141
141
|
- `DIREXIO_AGENT_INSTALL=auto` runs `npm install -g direxio-connent@latest` and then installs the `direxio-connect` daemon with the generated config and `--service-name <service_id>`. It is recorded as installed only when `direxio-connect daemon status --service-name <service_id>` reports `Status: Running` and recent daemon logs do not show ACP session initialization failure; otherwise S6 records `agent_install_status=install_failed`.
|
|
142
142
|
- `DIREXIO_AGENT_INSTALL_MODE=recommended` maps every supported local runtime to `cc-connect`.
|
|
143
143
|
- Speech defaults to `DIREXIO_SPEECH_PROVIDER=openai` and `DIREXIO_SPEECH_LANGUAGE=zh`. Provider-specific keys are also accepted: `DIREXIO_SPEECH_OPENAI_API_KEY` or `OPENAI_API_KEY`, `DIREXIO_SPEECH_GROQ_API_KEY` or `GROQ_API_KEY`, `DIREXIO_SPEECH_QWEN_API_KEY` or `DASHSCOPE_API_KEY`, and `DIREXIO_SPEECH_GEMINI_API_KEY`, `GEMINI_API_KEY`, or `GOOGLE_API_KEY`. Set `DIREXIO_SPEECH_ENABLED=false` to suppress speech config generation even when a key exists.
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
## 阶段
|
|
6
6
|
|
|
7
7
|
- **S0_PREREQ_AWS**: 校验 AWS CLI、凭据和账号身份。
|
|
8
|
-
- **S1_PREFLIGHT**: 校验 region、默认 VPC、vCPU 配额、Ubuntu amd64 AMI。
|
|
8
|
+
- **S1_PREFLIGHT**: 校验 region、默认 VPC、vCPU 配额、Elastic IP 可用配额、Ubuntu amd64 AMI。
|
|
9
9
|
- **S2_DOMAIN**: 确认正式长期域名和 Matrix `server_name` 不可逆绑定。
|
|
10
10
|
- **S3_PROVISION**: 创建 EC2、密钥对、安全组、Elastic IP,按 DNS 模式处理 Route53 hosted zone/A 记录或等待外部 DNS,渲染 cloud-init。默认镜像 `MESSAGE_SERVER_IMAGE=direxio/message-server:latest`。
|
|
11
11
|
- **S4_BOOTSTRAP_STACK**: 等 cloud-init 安装 Docker 并启动 `postgres:18 + message-server + caddy + coturn`,轮询 `https://<domain>/healthz`。
|
|
12
|
-
- **S5_INIT_TOKENS**: SSH 读取云端 `init-tokens.sh` 生成的 `/opt/p2p/bootstrap.json`,归一化 `password`、`access_token`、`agent_token`、真实 `agent_room_id`。云端脚本会先调用 `portal.bootstrap
|
|
13
|
-
- **S6_WIRE_LOCAL**:
|
|
12
|
+
- **S5_INIT_TOKENS**: SSH 读取云端 `init-tokens.sh` 生成的 `/opt/p2p/bootstrap.json`,归一化 `password`、`access_token`、`agent_token`、真实 `agent_room_id`。云端脚本会先调用 `portal.bootstrap`,用 `agent_token` 创建 `@agent:<server>` Matrix session,再用 owner Matrix token 创建房间并邀请/加入 agent,最后回写真正的 agent room。`password`、owner `access_token` 和 `agent_token` 按一次性/易失凭据处理;需要登录或用 token 调接口前,必须重新从服务器拉取最新 `/opt/p2p/bootstrap.json`,不要复用旧输出。
|
|
13
|
+
- **S6_WIRE_LOCAL**: 写本地凭据、用 `agent_token` 创建 `@agent:<server>` Matrix session、写 `cc-connect/config.toml`,写 MCP 配置片段,并按策略安装或推荐 `direxio-connect`。
|
|
14
14
|
- **S7_VERIFY_E2E**: 验证 `/_p2p`、Matrix versions、well-known、owner.json+CORS、TURN。
|
|
15
15
|
|
|
16
16
|
## 云端 compose
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
每次重部署或清空数据卷后,`password`、owner `access_token`、`agent_token` 和 cc-connect Matrix session 都会变化。状态机 S6 会自动回填;手动恢复时按这里检查。
|
|
4
4
|
|
|
5
|
-
从服务端同步过来的 `password` 和 owner `access_token` 必须按一次性/易失凭据处理。`password` 是后端字段名,对用户展示时必须叫八位 App 初始化码。用户完成初始化或 token exchange 后,服务端可能立刻重置这些值;任何需要再次获取初始化码,或需要用 `access_token` 调
|
|
5
|
+
从服务端同步过来的 `password` 和 owner `access_token` 必须按一次性/易失凭据处理。`password` 是后端字段名,对用户展示时必须叫八位 App 初始化码。用户完成初始化或 token exchange 后,服务端可能立刻重置这些值;任何需要再次获取初始化码,或需要用 `access_token` 调 owner 身份 API/Matrix Client API,或需要用 `agent_token` 调 `agent.matrix_session.create` 的操作,都必须先重新从服务器拉取最新 `/opt/p2p/bootstrap.json`,再更新本地 `credentials.json`。不要复用聊天记录、旧 `state.json`、旧 `credentials.json` 或历史部署输出里的 password/access token。
|
|
6
6
|
|
|
7
7
|
现有节点执行 `scripts/update.sh` 或 `scripts/reset-app-data.sh` 后,本地旧证据也必须作废。脚本会清掉旧 `password`、`access_token`、`agent_token`、`agent_room_id`、`user_confirmations` 和 `runtime_checks`,把 `agent_install_status` 标成 `refresh_pending`,并只在 `WorkDir` 匹配当前 service 时停止对应的本地 bridge(stops only the matching service-scoped direxio-connect daemon),再把 S4-S7 标回 pending。这样旧的用户确认、MCP discovery、Agent runtime probe 或旧 bridge 安装状态不会被误用到更新/重置后的节点。`status` 会显示 `Local refresh:`,提醒 update/reset 已经清掉旧 credentials、user confirmations、runtime checks 和 bridge install proof;下一步必须 rerun the deployment workflow to refresh S4-S7, local credentials, MCP snippets, and runtime checks。后续必须续跑 `scripts/orchestrate.sh`,让 S5/S6/S7 和 `verify runtime` 重新写入当前证据。
|
|
8
8
|
|
|
@@ -109,7 +109,7 @@ Use the Git Bash `$HOME` path for files generated by the deployer. If running `d
|
|
|
109
109
|
|
|
110
110
|
## EC2 SSH Key Paths
|
|
111
111
|
|
|
112
|
-
SSH key files are written with Windows-compatible paths such as `C:/Users/.../.direxio/deploy/p2p-*.pem`. The SSH command printed in the delivery summary works in Git Bash. If using PowerShell or cmd, convert forward slashes to backslashes.
|
|
112
|
+
SSH key files are written with Windows-compatible paths such as `C:/Users/.../.direxio/deploy/p2p-*.pem`. The deployer removes broad Windows ACL entries such as `Users`, `Authenticated Users`, `Everyone`, and Codex sandbox groups where possible, while keeping the current Windows user, `SYSTEM`, and `Administrators` on the file so OpenSSH does not reject the private key as too open. The SSH command printed in the delivery summary works in Git Bash. If using PowerShell or cmd, convert forward slashes to backslashes.
|
|
113
113
|
|
|
114
114
|
## Verifying Deployment
|
|
115
115
|
|
|
@@ -149,7 +149,7 @@ PY
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
ensure_agent_room() {
|
|
152
|
-
local owner_token agent_user session room_resp join_resp
|
|
152
|
+
local owner_token agent_auth_token agent_user session room_resp join_resp matrix_agent_token room_id room_path
|
|
153
153
|
if copy_bootstrap_file && bootstrap_has_real_agent_room "$BOOTSTRAP_FILE"; then
|
|
154
154
|
log "agent_room_id is already present."
|
|
155
155
|
return 0
|
|
@@ -160,15 +160,20 @@ ensure_agent_room() {
|
|
|
160
160
|
log "FATAL: access_token is missing; cannot create agent room"
|
|
161
161
|
return 1
|
|
162
162
|
fi
|
|
163
|
+
agent_auth_token=$(json_string agent_token "$BOOTSTRAP_FILE")
|
|
164
|
+
if [ -z "$agent_auth_token" ]; then
|
|
165
|
+
log "FATAL: agent_token is missing; cannot create agent Matrix session"
|
|
166
|
+
return 1
|
|
167
|
+
fi
|
|
163
168
|
agent_user="@agent:${DOMAIN}"
|
|
164
169
|
session=$(mktemp)
|
|
165
|
-
if ! container_post_json "/_p2p/command" '{"action":"agent.matrix_session.create","params":{"device_id":"DIREXIO_DEPLOY_BOOTSTRAP"}}' "$
|
|
170
|
+
if ! container_post_json "/_p2p/command" '{"action":"agent.matrix_session.create","params":{"device_id":"DIREXIO_DEPLOY_BOOTSTRAP"}}' "$agent_auth_token" > "$session" 2>/dev/null; then
|
|
166
171
|
log "FATAL: agent.matrix_session.create failed: $(head -c 160 "$session" 2>/dev/null)"
|
|
167
172
|
rm -f "$session"
|
|
168
173
|
return 1
|
|
169
174
|
fi
|
|
170
|
-
|
|
171
|
-
if [ -z "$
|
|
175
|
+
matrix_agent_token=$(json_string access_token "$session")
|
|
176
|
+
if [ -z "$matrix_agent_token" ]; then
|
|
172
177
|
log "FATAL: agent.matrix_session.create did not return access_token: $(head -c 160 "$session" 2>/dev/null)"
|
|
173
178
|
rm -f "$session"
|
|
174
179
|
return 1
|
|
@@ -190,7 +195,7 @@ ensure_agent_room() {
|
|
|
190
195
|
|
|
191
196
|
room_path=$(matrix_room_path "$room_id")
|
|
192
197
|
join_resp=$(mktemp)
|
|
193
|
-
if ! container_post_json "/_matrix/client/v3/rooms/${room_path}/join" '{}' "$
|
|
198
|
+
if ! container_post_json "/_matrix/client/v3/rooms/${room_path}/join" '{}' "$matrix_agent_token" > "$join_resp" 2>/dev/null; then
|
|
194
199
|
log "FATAL: agent join failed for ${room_id}: $(head -c 160 "$join_resp" 2>/dev/null)"
|
|
195
200
|
rm -f "$join_resp"
|
|
196
201
|
return 1
|
package/scripts/lib/aws.sh
CHANGED
|
@@ -53,6 +53,9 @@ aws_redact_arn() {
|
|
|
53
53
|
# EC2 vCPU quota code: Running On-Demand Standard instances.
|
|
54
54
|
EC2_STD_QUOTA_CODE="L-1216C47A"
|
|
55
55
|
|
|
56
|
+
# EC2-VPC Elastic IP addresses per region.
|
|
57
|
+
EC2_VPC_EIP_QUOTA_CODE="L-0263D0A3"
|
|
58
|
+
|
|
56
59
|
# Dynamically resolve the latest Ubuntu 22.04 amd64 AMI; never hard-code AMI IDs.
|
|
57
60
|
aws_lookup_ubuntu_ami() {
|
|
58
61
|
local ami
|
package/scripts/lib/state.sh
CHANGED
|
@@ -46,6 +46,46 @@ is_yes() {
|
|
|
46
46
|
esac
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
restrict_private_file() {
|
|
50
|
+
local file=$1 uname_s win_file user user_domain
|
|
51
|
+
chmod 600 "$file" 2>/dev/null || true
|
|
52
|
+
uname_s=$(uname -s 2>/dev/null || printf unknown)
|
|
53
|
+
case "$uname_s" in
|
|
54
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
55
|
+
command -v icacls >/dev/null 2>&1 || return 0
|
|
56
|
+
win_file=$file
|
|
57
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
58
|
+
win_file=$(cygpath -w "$file")
|
|
59
|
+
fi
|
|
60
|
+
user=$(_windows_current_user)
|
|
61
|
+
user_domain=${USERDOMAIN:-}
|
|
62
|
+
MSYS2_ARG_CONV_EXCL='*' icacls "$win_file" /inheritance:r >/dev/null 2>&1 || true
|
|
63
|
+
MSYS2_ARG_CONV_EXCL='*' icacls "$win_file" /remove:g \
|
|
64
|
+
"Users" "Authenticated Users" "Everyone" "CodexSandboxUsers" \
|
|
65
|
+
"${user_domain}\\CodexSandboxUsers" >/dev/null 2>&1 || true
|
|
66
|
+
MSYS2_ARG_CONV_EXCL='*' icacls "$win_file" /grant:r "NT AUTHORITY\\SYSTEM:F" "BUILTIN\\Administrators:F" >/dev/null 2>&1 || true
|
|
67
|
+
[ -n "$user" ] && MSYS2_ARG_CONV_EXCL='*' icacls "$win_file" /grant:r "$user:F" >/dev/null 2>&1 || true
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_windows_current_user() {
|
|
73
|
+
if [ -n "${USERDOMAIN:-}" ] && [ -n "${USERNAME:-}" ]; then
|
|
74
|
+
printf '%s\\%s\n' "$USERDOMAIN" "$USERNAME"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
if command -v powershell.exe >/dev/null 2>&1; then
|
|
78
|
+
powershell.exe -NoProfile -NonInteractive -Command '[System.Security.Principal.WindowsIdentity]::GetCurrent().Name' 2>/dev/null \
|
|
79
|
+
| tr -d '\r' | tail -n 1
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
if command -v whoami.exe >/dev/null 2>&1; then
|
|
83
|
+
whoami.exe 2>/dev/null | tr -d '\r' | tail -n 1
|
|
84
|
+
return 0
|
|
85
|
+
fi
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
88
|
+
|
|
49
89
|
# Initialize state.json for a new deployment.
|
|
50
90
|
state_init() {
|
|
51
91
|
mkdir -p "$P2P_WORKDIR"
|
|
@@ -1,156 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
3
6
|
|
|
4
7
|
const command = process.argv[2] || "direxio-mcp";
|
|
5
8
|
const timeoutMs = Number.parseInt(process.env.DIREXIO_MCP_TOOLS_TIMEOUT_MS || "8000", 10);
|
|
6
9
|
|
|
7
|
-
const child = spawn(command, {
|
|
8
|
-
env: process.env,
|
|
9
|
-
shell: true,
|
|
10
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let stdout = Buffer.alloc(0);
|
|
14
|
-
let stderr = "";
|
|
15
|
-
let completed = false;
|
|
16
|
-
const responses = new Map();
|
|
17
|
-
|
|
18
10
|
const timer = setTimeout(() => {
|
|
19
11
|
finishWithError(`timed out waiting for MCP tools/list after ${timeoutMs}ms`);
|
|
20
12
|
}, timeoutMs);
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
child.on("exit", (code) => {
|
|
45
|
-
if (!completed && code !== 0) {
|
|
46
|
-
finishWithError(`MCP server exited with code ${code}${stderr ? `: ${stderr.trim()}` : ""}`);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
send({
|
|
51
|
-
jsonrpc: "2.0",
|
|
52
|
-
id: 1,
|
|
53
|
-
method: "initialize",
|
|
54
|
-
params: {
|
|
55
|
-
protocolVersion: "2024-11-05",
|
|
56
|
-
capabilities: {},
|
|
57
|
-
clientInfo: { name: "direxio-deployer", version: "0.0.0" }
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
send({ jsonrpc: "2.0", method: "notifications/initialized", params: {} });
|
|
61
|
-
send({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
|
|
62
|
-
|
|
63
|
-
function send(message) {
|
|
64
|
-
const body = JSON.stringify(message);
|
|
65
|
-
child.stdin.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
|
|
14
|
+
try {
|
|
15
|
+
const packageRoot = resolveDirexioMcpPackageRoot(command);
|
|
16
|
+
const sdkRoot = path.join(packageRoot, "node_modules", "@modelcontextprotocol", "sdk", "dist", "esm");
|
|
17
|
+
const { Client } = await import(pathToFileURL(path.join(sdkRoot, "client", "index.js")).href);
|
|
18
|
+
const { StdioClientTransport } = await import(pathToFileURL(path.join(sdkRoot, "client", "stdio.js")).href);
|
|
19
|
+
|
|
20
|
+
const transport = new StdioClientTransport({
|
|
21
|
+
command: process.execPath,
|
|
22
|
+
args: [path.join(packageRoot, "dist", "index.js")],
|
|
23
|
+
env: process.env
|
|
24
|
+
});
|
|
25
|
+
const client = new Client({ name: "direxio-deployer", version: "0.0.0" }, { capabilities: {} });
|
|
26
|
+
await client.connect(transport);
|
|
27
|
+
const response = await client.listTools();
|
|
28
|
+
await client.close();
|
|
29
|
+
|
|
30
|
+
const names = (Array.isArray(response?.tools) ? response.tools : [])
|
|
31
|
+
.map((tool) => tool?.name)
|
|
32
|
+
.filter((name) => typeof name === "string" && name.length > 0);
|
|
33
|
+
finish({ tools: names, tool_count: names.length });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
finishWithError(error instanceof Error ? error.message : String(error));
|
|
66
36
|
}
|
|
67
37
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (!Number.isSafeInteger(contentLength) || contentLength < 0) {
|
|
80
|
-
finishWithError("MCP response frame is missing a valid Content-Length header");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const messageEnd = header.bodyStart + contentLength;
|
|
84
|
-
if (stdout.length < messageEnd) return;
|
|
85
|
-
const body = stdout.subarray(header.bodyStart, messageEnd).toString("utf8");
|
|
86
|
-
stdout = stdout.subarray(messageEnd);
|
|
87
|
-
handleMessage(body);
|
|
88
|
-
if (completed) return;
|
|
89
|
-
continue;
|
|
38
|
+
function resolveDirexioMcpPackageRoot(commandName) {
|
|
39
|
+
const executable = resolveExecutable(commandName);
|
|
40
|
+
const basedir = path.dirname(realpathSync(executable));
|
|
41
|
+
const candidates = [
|
|
42
|
+
path.join(basedir, "node_modules", "direxio-mcp"),
|
|
43
|
+
path.join(basedir, "..", "node_modules", "direxio-mcp"),
|
|
44
|
+
path.join(basedir, "..")
|
|
45
|
+
];
|
|
46
|
+
for (const candidate of candidates) {
|
|
47
|
+
if (existsSync(path.join(candidate, "package.json")) && existsSync(path.join(candidate, "dist", "index.js"))) {
|
|
48
|
+
return realpathSync(candidate);
|
|
90
49
|
}
|
|
91
|
-
|
|
92
|
-
const lineEnd = stdout.indexOf("\n");
|
|
93
|
-
if (lineEnd < 0) return;
|
|
94
|
-
const line = stdout.subarray(0, lineEnd).toString("utf8").replace(/\r$/, "");
|
|
95
|
-
stdout = stdout.subarray(lineEnd + 1);
|
|
96
|
-
if (line.length === 0) continue;
|
|
97
|
-
handleMessage(line);
|
|
98
|
-
if (completed) return;
|
|
99
50
|
}
|
|
51
|
+
throw new Error(`unable to locate direxio-mcp package root from ${executable}`);
|
|
100
52
|
}
|
|
101
53
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
function readHeader(buffer) {
|
|
107
|
-
let marker = "\r\n\r\n";
|
|
108
|
-
let headerEnd = buffer.indexOf(marker);
|
|
109
|
-
if (headerEnd < 0) {
|
|
110
|
-
marker = "\n\n";
|
|
111
|
-
headerEnd = buffer.indexOf(marker);
|
|
54
|
+
function resolveExecutable(commandName) {
|
|
55
|
+
const nativeCommand = nativePath(commandName);
|
|
56
|
+
if (path.isAbsolute(nativeCommand) && existsSync(nativeCommand)) {
|
|
57
|
+
return nativeCommand;
|
|
112
58
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function parseContentLength(headerText) {
|
|
121
|
-
for (const line of headerText.split(/\r?\n/)) {
|
|
122
|
-
const match = /^content-length:\s*(\d+)\s*$/i.exec(line);
|
|
123
|
-
if (match) return Number.parseInt(match[1], 10);
|
|
59
|
+
const lookup = process.platform === "win32"
|
|
60
|
+
? spawnSync("where.exe", [nativeCommand], { encoding: "utf8" })
|
|
61
|
+
: spawnSync("sh", ["-lc", `command -v ${shellQuote(nativeCommand)}`], { encoding: "utf8" });
|
|
62
|
+
if (lookup.status !== 0 || !lookup.stdout.trim()) {
|
|
63
|
+
throw new Error(`unable to find ${commandName} on PATH`);
|
|
124
64
|
}
|
|
125
|
-
return
|
|
65
|
+
return lookup.stdout.trim().split(/\r?\n/)[0];
|
|
126
66
|
}
|
|
127
67
|
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
message = JSON.parse(raw);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
finishWithError(`invalid MCP JSON response: ${error.message}`);
|
|
134
|
-
return;
|
|
68
|
+
function nativePath(value) {
|
|
69
|
+
if (process.platform !== "win32" || !String(value).startsWith("/")) {
|
|
70
|
+
return value;
|
|
135
71
|
}
|
|
136
|
-
|
|
137
|
-
|
|
72
|
+
const converted = spawnSync("cygpath", ["-w", value], { encoding: "utf8" });
|
|
73
|
+
if (converted.status === 0 && converted.stdout.trim()) {
|
|
74
|
+
return converted.stdout.trim();
|
|
138
75
|
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function shellQuote(value) {
|
|
80
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
139
81
|
}
|
|
140
82
|
|
|
141
83
|
function finish(value) {
|
|
142
|
-
if (completed) return;
|
|
143
|
-
completed = true;
|
|
144
84
|
clearTimeout(timer);
|
|
145
85
|
console.log(JSON.stringify(value));
|
|
146
|
-
child.kill();
|
|
147
86
|
}
|
|
148
87
|
|
|
149
88
|
function finishWithError(message) {
|
|
150
|
-
if (completed) return;
|
|
151
|
-
completed = true;
|
|
152
89
|
clearTimeout(timer);
|
|
153
90
|
console.error(message);
|
|
154
|
-
child.kill();
|
|
155
91
|
process.exitCode = 1;
|
|
156
92
|
}
|
package/scripts/orchestrate.sh
CHANGED
|
@@ -122,7 +122,7 @@ cmd_status_inventory() {
|
|
|
122
122
|
phase_user_meaning() {
|
|
123
123
|
case "$1" in
|
|
124
124
|
S0_PREREQ_AWS) echo "AWS credentials, CLI tooling, or account identity are not ready." ;;
|
|
125
|
-
S1_PREFLIGHT) echo "AWS region, default VPC, quota, or Ubuntu AMI checks are not ready." ;;
|
|
125
|
+
S1_PREFLIGHT) echo "AWS region, default VPC, EC2/EIP quota, or Ubuntu AMI checks are not ready." ;;
|
|
126
126
|
S2_DOMAIN) echo "The long-lived domain, DNS authority, or irreversible Matrix server_name binding is not confirmed." ;;
|
|
127
127
|
S3_PROVISION) echo "AWS infrastructure provisioning, fixed public IP, security group, or DNS record setup is not complete." ;;
|
|
128
128
|
S4_BOOTSTRAP_STACK) echo "The EC2 instance exists, but cloud-init, Docker, Caddy/TLS, or message-server has not reached healthy state." ;;
|
|
@@ -212,7 +212,7 @@ status_next_action() {
|
|
|
212
212
|
|
|
213
213
|
case "$1" in
|
|
214
214
|
S0_PREREQ_AWS) echo "configure AWS CLI credentials for the selected deployment identity and rerun status" ;;
|
|
215
|
-
S1_PREFLIGHT) echo "fix AWS region, default VPC, EC2 quota, or AMI availability before creating resources" ;;
|
|
215
|
+
S1_PREFLIGHT) echo "fix AWS region, default VPC, EC2/EIP quota, or AMI availability before creating resources" ;;
|
|
216
216
|
S2_DOMAIN) echo "confirm the long-lived domain, DNS authority, and irreversible Matrix server_name binding" ;;
|
|
217
217
|
S3_PROVISION) echo "inspect EC2 provisioning, Elastic IP allocation, security group creation, and DNS record setup" ;;
|
|
218
218
|
S4_BOOTSTRAP_STACK) echo "inspect cloud-init, Docker, Caddy/TLS, and message-server logs over SSH" ;;
|
|
@@ -966,15 +966,35 @@ runtime_check_status() {
|
|
|
966
966
|
json_get "$STATE_JSON" "runtime_checks.$check.status" "not_run"
|
|
967
967
|
}
|
|
968
968
|
|
|
969
|
+
runtime_status_counts_as_failure() {
|
|
970
|
+
local status=$1
|
|
971
|
+
case "$status" in
|
|
972
|
+
passed|manual_pending|skipped) return 1 ;;
|
|
973
|
+
*) return 0 ;;
|
|
974
|
+
esac
|
|
975
|
+
}
|
|
976
|
+
|
|
969
977
|
cmd_verify_runtime() {
|
|
970
978
|
[ -f "$STATE_JSON" ] || {
|
|
971
979
|
warn "state.json not found: $STATE_JSON"
|
|
972
980
|
return 1
|
|
973
981
|
}
|
|
974
982
|
|
|
975
|
-
local rc=0 failed_count=0 connect_status doctor_status tools_status smoke_status status
|
|
983
|
+
local rc=0 failed_count=0 connect_status doctor_status tools_status smoke_status status install_status install_policy service_name
|
|
976
984
|
|
|
977
|
-
|
|
985
|
+
install_status=$(json_get "$STATE_JSON" agent_install_status)
|
|
986
|
+
install_policy=$(json_get "$STATE_JSON" agent_install_policy)
|
|
987
|
+
service_name=$(json_get "$STATE_JSON" agent_service_id)
|
|
988
|
+
[ -n "$service_name" ] || service_name=$(json_get "$STATE_JSON" domain)
|
|
989
|
+
if [ "$install_status" = "recommend" ] || { [ "$install_status" = "skip" ] && [ "${install_policy:-skip}" = "skip" ]; }; then
|
|
990
|
+
state_set_object runtime_checks.connect_daemon \
|
|
991
|
+
status=manual_pending \
|
|
992
|
+
"ts=$(_now)" \
|
|
993
|
+
"evidence=direxio-connect daemon install is an explicit operator action for policy=$install_status" \
|
|
994
|
+
"service_name=${service_name:-cc-connect}"
|
|
995
|
+
else
|
|
996
|
+
cmd_verify_connect_daemon >/dev/null || rc=1
|
|
997
|
+
fi
|
|
978
998
|
cmd_verify_mcp_doctor >/dev/null || rc=1
|
|
979
999
|
cmd_verify_mcp_tools >/dev/null || rc=1
|
|
980
1000
|
cmd_verify_mcp_smoke >/dev/null || rc=1
|
|
@@ -985,7 +1005,7 @@ cmd_verify_runtime() {
|
|
|
985
1005
|
smoke_status=$(runtime_check_status mcp_smoke)
|
|
986
1006
|
|
|
987
1007
|
for status in "$connect_status" "$doctor_status" "$tools_status" "$smoke_status"; do
|
|
988
|
-
|
|
1008
|
+
runtime_status_counts_as_failure "$status" && failed_count=$((failed_count + 1))
|
|
989
1009
|
done
|
|
990
1010
|
|
|
991
1011
|
if [ "$failed_count" -eq 0 ]; then
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# S1 PREFLIGHT - default VPC, EC2 vCPU quota, and AMI checks.
|
|
2
|
+
# S1 PREFLIGHT - default VPC, EC2 vCPU quota, Elastic IP, and AMI checks.
|
|
3
3
|
#
|
|
4
|
-
# New AWS accounts often start with low or
|
|
5
|
-
#
|
|
4
|
+
# New AWS accounts often start with low or exhausted EC2/EIP quota. Report the
|
|
5
|
+
# blocker before S3 creates resources.
|
|
6
6
|
|
|
7
7
|
run_phase() {
|
|
8
8
|
aws_env_prep
|
|
@@ -37,7 +37,10 @@ run_phase() {
|
|
|
37
37
|
|| { phase_set S1_PREFLIGHT failed "quota polling interrupted"; return 1; }
|
|
38
38
|
fi
|
|
39
39
|
|
|
40
|
-
# 3)
|
|
40
|
+
# 3) Elastic IP quota and current regional usage. Unknown quota is warned but not blocked.
|
|
41
|
+
_check_eip_capacity || return $?
|
|
42
|
+
|
|
43
|
+
# 4) AMI (amd64/x86).
|
|
41
44
|
local ami
|
|
42
45
|
ami=$(aws_lookup_ubuntu_ami)
|
|
43
46
|
if [ "$ami" = "None" ] || [ -z "$ami" ]; then
|
|
@@ -70,3 +73,33 @@ _quota_ge_2() {
|
|
|
70
73
|
_is_unknown_quota "$q" && return 1
|
|
71
74
|
_num_ge "$q" 2
|
|
72
75
|
}
|
|
76
|
+
|
|
77
|
+
_check_eip_capacity() {
|
|
78
|
+
local quota allocated available
|
|
79
|
+
quota=$(aws service-quotas get-service-quota --service-code ec2 --quota-code "$EC2_VPC_EIP_QUOTA_CODE" \
|
|
80
|
+
--query 'Quota.Value' --output text 2>/dev/null || echo "unknown")
|
|
81
|
+
quota=${quota:-unknown}
|
|
82
|
+
allocated=$(aws ec2 describe-addresses \
|
|
83
|
+
--query 'length(Addresses[?Domain==`vpc`])' --output text 2>/dev/null || echo "unknown")
|
|
84
|
+
allocated=${allocated:-unknown}
|
|
85
|
+
|
|
86
|
+
res_set eip_quota "$quota"
|
|
87
|
+
res_set eip_allocated "$allocated"
|
|
88
|
+
|
|
89
|
+
if _is_unknown_quota "$quota" || _is_unknown_quota "$allocated"; then
|
|
90
|
+
warn "Could not read Elastic IP quota or current allocation; continuing. If allocate-address fails, check regional EIP quota."
|
|
91
|
+
return 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
available=$(awk -v q="$quota" -v a="$allocated" 'BEGIN { v=int(q+0)-int(a+0); if (v < 0) v=0; print v }')
|
|
95
|
+
res_set eip_available "$available"
|
|
96
|
+
log "Elastic IP quota = $quota, allocated = $allocated, available = $available (need 1)"
|
|
97
|
+
|
|
98
|
+
if ! _num_ge "$available" 1; then
|
|
99
|
+
phase_set S1_PREFLIGHT waiting_user "Elastic IP quota exhausted: allocated=$allocated quota=$quota"
|
|
100
|
+
warn "This region has no available Elastic IP quota: allocated=$allocated quota=$quota."
|
|
101
|
+
warn "Release an unused Elastic IP, request a higher EC2-VPC Elastic IP quota, or choose another AWS region, then rerun."
|
|
102
|
+
return 2
|
|
103
|
+
fi
|
|
104
|
+
return 0
|
|
105
|
+
}
|
|
@@ -57,7 +57,7 @@ run_phase() {
|
|
|
57
57
|
if [ -z "$(res_get key_name)" ]; then
|
|
58
58
|
log "Creating key pair $name ..."
|
|
59
59
|
aws ec2 create-key-pair --key-name "$name" --query KeyMaterial --output text > "$keyfile"
|
|
60
|
-
|
|
60
|
+
restrict_private_file "$keyfile"
|
|
61
61
|
res_set key_name "$name"; res_set key_file "$keyfile"
|
|
62
62
|
else
|
|
63
63
|
log "Key pair already exists; skipping."; keyfile=$(res_get key_file)
|
|
@@ -885,25 +885,64 @@ EOF
|
|
|
885
885
|
}
|
|
886
886
|
|
|
887
887
|
_create_cc_connect_matrix_session() {
|
|
888
|
-
local asurl=$1
|
|
888
|
+
local asurl=$1 agent_auth_token=$2 device_id=$3 out=$4 body code http_body
|
|
889
|
+
local max_attempts interval max_interval attempt preview sleep_for
|
|
889
890
|
body=$(json_build matrix-session-create "$device_id")
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
891
|
+
max_attempts=${DIREXIO_MATRIX_SESSION_CREATE_MAX:-12}
|
|
892
|
+
interval=${DIREXIO_MATRIX_SESSION_RETRY_INTERVAL:-2}
|
|
893
|
+
max_interval=${DIREXIO_MATRIX_SESSION_RETRY_MAX_INTERVAL:-10}
|
|
894
|
+
attempt=1
|
|
895
|
+
sleep_for=$interval
|
|
896
|
+
while [ "$attempt" -le "$max_attempts" ]; do
|
|
897
|
+
http_body=$(mktemp)
|
|
898
|
+
code=$(curl -sk \
|
|
899
|
+
--connect-timeout "${DIREXIO_MATRIX_SESSION_CURL_CONNECT_TIMEOUT:-10}" \
|
|
900
|
+
--max-time "${DIREXIO_MATRIX_SESSION_CURL_MAX_TIME:-20}" \
|
|
901
|
+
-o "$http_body" -w '%{http_code}' -X POST "$asurl/_p2p/command" \
|
|
902
|
+
-H 'Content-Type: application/json' \
|
|
903
|
+
-H "Authorization: Bearer $agent_auth_token" \
|
|
904
|
+
-d "$body" 2>/dev/null || true)
|
|
905
|
+
if [ "$code" = "200" ]; then
|
|
906
|
+
if ! json_assert "$http_body" matrix-session >/dev/null; then
|
|
907
|
+
warn "agent.matrix_session.create response is missing Matrix session fields: $(head -c 200 "$http_body" 2>/dev/null)"
|
|
908
|
+
rm -f "$http_body"
|
|
909
|
+
return 1
|
|
910
|
+
fi
|
|
911
|
+
mv "$http_body" "$out"
|
|
912
|
+
chmod 600 "$out" 2>/dev/null || true
|
|
913
|
+
return 0
|
|
914
|
+
fi
|
|
915
|
+
preview=$(head -c 200 "$http_body" 2>/dev/null || true)
|
|
902
916
|
rm -f "$http_body"
|
|
917
|
+
case "${code:-000}" in
|
|
918
|
+
000|404|408|409|425|429|5*)
|
|
919
|
+
if [ "$attempt" -lt "$max_attempts" ]; then
|
|
920
|
+
warn "agent.matrix_session.create returned HTTP ${code:-000} on attempt $attempt/$max_attempts; retrying in ${sleep_for}s."
|
|
921
|
+
sleep "$sleep_for"
|
|
922
|
+
if _is_non_negative_integer "$sleep_for" && _is_non_negative_integer "$max_interval"; then
|
|
923
|
+
sleep_for=$((sleep_for * 2))
|
|
924
|
+
[ "$sleep_for" -gt "$max_interval" ] && sleep_for=$max_interval
|
|
925
|
+
fi
|
|
926
|
+
attempt=$((attempt + 1))
|
|
927
|
+
continue
|
|
928
|
+
fi
|
|
929
|
+
;;
|
|
930
|
+
401)
|
|
931
|
+
warn "agent.matrix_session.create rejected agent_token. Refresh bootstrap credentials or deploy a message-server build that allows agent_token for this action."
|
|
932
|
+
;;
|
|
933
|
+
*) ;;
|
|
934
|
+
esac
|
|
935
|
+
warn "agent.matrix_session.create returned HTTP ${code:-000}: $preview"
|
|
903
936
|
return 1
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
937
|
+
done
|
|
938
|
+
return 1
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
_is_non_negative_integer() {
|
|
942
|
+
case "$1" in
|
|
943
|
+
''|*[!0-9]*) return 1 ;;
|
|
944
|
+
*) return 0 ;;
|
|
945
|
+
esac
|
|
907
946
|
}
|
|
908
947
|
|
|
909
948
|
_write_cc_connect_config() {
|
|
@@ -1334,7 +1373,7 @@ run_phase() {
|
|
|
1334
1373
|
|
|
1335
1374
|
mkdir -p "$workspace"
|
|
1336
1375
|
mkdir -p "$cc_runtime_dir"
|
|
1337
|
-
if ! _create_cc_connect_matrix_session "$asurl" "$
|
|
1376
|
+
if ! _create_cc_connect_matrix_session "$asurl" "$token" "DIREXIO_CC_CONNECT_${node_id}" "$cc_session"; then
|
|
1338
1377
|
phase_set S6_WIRE_LOCAL failed "agent Matrix session creation failed"
|
|
1339
1378
|
fail "failed to create cc-connect Matrix session via agent.matrix_session.create."
|
|
1340
1379
|
fi
|