@parall/agent-core 1.25.0 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge-workspace.d.ts +1 -1
- package/dist/bridge-workspace.d.ts.map +1 -1
- package/dist/bridge-workspace.js +1 -1
- package/dist/dispatch-adapter.d.ts +2 -0
- package/dist/dispatch-adapter.d.ts.map +1 -1
- package/dist/gateway-base.d.ts +6 -0
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +53 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/platform-config.d.ts +23 -0
- package/dist/platform-config.d.ts.map +1 -0
- package/dist/platform-config.js +105 -0
- package/dist/skills/index.d.ts +14 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +44 -0
- package/dist/skills/parall-platform.d.ts +2 -0
- package/dist/skills/parall-platform.d.ts.map +1 -0
- package/dist/skills/parall-platform.js +161 -0
- package/dist/skills/parall-schedules.d.ts +2 -0
- package/dist/skills/parall-schedules.d.ts.map +1 -0
- package/dist/skills/parall-schedules.js +80 -0
- package/dist/skills/parall-tasks.d.ts +2 -0
- package/dist/skills/parall-tasks.d.ts.map +1 -0
- package/dist/skills/parall-tasks.js +65 -0
- package/dist/skills/parall-wiki.d.ts +2 -0
- package/dist/skills/parall-wiki.d.ts.map +1 -0
- package/dist/skills/parall-wiki.js +131 -0
- package/package.json +2 -2
- package/src/bridge-workspace.ts +1 -1
- package/src/dispatch-adapter.ts +2 -0
- package/src/gateway-base.ts +53 -7
- package/src/index.ts +3 -0
- package/src/platform-config.ts +159 -0
- package/src/skills/index.ts +52 -0
- package/src/skills/parall-platform.ts +161 -0
- package/src/skills/parall-schedules.ts +80 -0
- package/src/skills/parall-tasks.ts +65 -0
- package/src/skills/parall-wiki.ts +131 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const PARALL_SCHEDULES_SKILL = "# Parall Schedules\n\nA **Schedule** is a platform time trigger. At fire time the platform delivers the schedule's `description` to a target \u2014 that's it. How you respond is up to you: send a message, create a task, update a wiki page, or do nothing. Use schedules for recurring reminders (\"standup every weekday 10am\"), delayed prompts (\"in 1 hour, check CI\"), or fire-and-forget cron work.\n\nThree spec types \u2014 pick exactly one:\n\n- `cron` \u2014 5-field expression (min granularity: 1 minute)\n- `interval` \u2014 every N seconds (minimum 60)\n- `one_shot` \u2014 fire once at a specific time\n\n## Creating schedules\n\n```bash\n# Recurring cron (weekdays 10am New York)\nparall schedules create \\\n --name \"Daily standup\" \\\n --description \"Ask the team for their plan today; see prll://wik_xxx for the standup template\" \\\n --target-ids prll://usr_xxx \\\n --cron-expr \"0 10 * * 1-5\" \\\n --timezone America/New_York \\\n --attached-to-uri prll://cht_xxx\n\n# Every 30 minutes\nparall schedules create \\\n --name \"CI watch\" \\\n --description \"Check the deploy status and flag failures\" \\\n --target-ids prll://usr_xxx \\\n --interval-seconds 1800\n\n# One-shot at a future RFC3339 time\nparall schedules create \\\n --name \"Followup\" \\\n --description \"Remind the user about the PR review if still pending\" \\\n --target-ids prll://usr_xxx \\\n --run-at <FUTURE_RFC3339_TIME>\n```\n\n`--target-ids` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). `--attached-to-uri` optionally anchors the schedule to a task / chat / project / wiki page \u2014 when that resource is archived or deleted, the schedule auto-cancels (`cancel_reason=attached_gone`).\n\n## Listing / inspecting\n\n```bash\nparall schedules list --status active,paused\nparall schedules list --attached-to prll://tsk_xxx\nparall schedules list --attendee-id prll://usr_xxx\nparall schedules get prll://sch_xxx\nparall schedules runs prll://sch_xxx # fire history\nparall schedules run prll://srn_xxx # single run incl. fire-time snapshot\n```\n\n## Lifecycle\n\n```bash\nparall schedules update prll://sch_xxx --description \"New prompt\"\nparall schedules pause prll://sch_xxx # reversible\nparall schedules resume prll://sch_xxx # does NOT catch up missed slots\nparall schedules cancel prll://sch_xxx # terminal; row + runs preserved, prll://sch_ ref stays valid\nparall schedules delete prll://sch_xxx # hard-delete; requires status=cancelled AND run_count=0. Once a schedule has fired, it is permanently undeletable (409 SCHEDULE_HAS_RUNS) \u2014 cancel it and leave the audit trail. Delete is for never-fired test/accidental schedules only.\n```\n\n`spec_type` cannot be changed via update \u2014 if you need to switch between cron / interval / one_shot, cancel the old one and create a new schedule.\n\n## Responding to schedule fires\n\nWhen you receive `[Event: schedule.fired]`, the platform has fired a schedule targeting you.\n\nThe runtime (agent-core) has already done the heavy lifting: it fetched the schedule run and inlined the fire-time `description` (a frozen snapshot \u2014 later edits to the schedule don't change past fires) into your prompt, alongside `[Schedule: prll://sch_xxx]` and `[Run: prll://srn_xxx]` headers. You do **not** need to call `schedules run prll://srn_xxx` yourself \u2014 the description is already in the prompt body.\n\nYour job is to interpret the description and act:\n\n1. Read the description and any `prll://` refs it contains\n2. Do whatever the prompt asks (send a message, create a task, update a wiki, etc.) \u2014 there is no canonical response format\n3. Optional: if the fire is genuinely a no-op and you don't want to produce any artifact, use `no-reply` (from parall-platform skill) to stay silent for this turn\n\nDo not treat schedule fires as \"tasks assigned to you\" \u2014 there's no status to transition, no acknowledgment required. If the work warrants a task (multi-step, needs tracking), create one from within the response.\n\n**Fetching the run explicitly** (optional): `schedules run prll://srn_xxx` returns the same snapshot plus delivery records (reverse-lookable via `source_id=srn_xxx`) for audit. If you call it and get 404 (because the schedule was cancelled or its target/attachment changed after the fire), drop the request and continue \u2014 don't retry.\n\nCLI command results are JSON on stdout; mutation commands may emit auxiliary hints on stderr (for example, `Created: prll://sch_xxx`).\n";
|
|
2
|
+
//# sourceMappingURL=parall-schedules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parall-schedules.d.ts","sourceRoot":"","sources":["../../src/skills/parall-schedules.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,ugJA+ElC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const PARALL_SCHEDULES_SKILL = `# Parall Schedules
|
|
2
|
+
|
|
3
|
+
A **Schedule** is a platform time trigger. At fire time the platform delivers the schedule's \`description\` to a target — that's it. How you respond is up to you: send a message, create a task, update a wiki page, or do nothing. Use schedules for recurring reminders ("standup every weekday 10am"), delayed prompts ("in 1 hour, check CI"), or fire-and-forget cron work.
|
|
4
|
+
|
|
5
|
+
Three spec types — pick exactly one:
|
|
6
|
+
|
|
7
|
+
- \`cron\` — 5-field expression (min granularity: 1 minute)
|
|
8
|
+
- \`interval\` — every N seconds (minimum 60)
|
|
9
|
+
- \`one_shot\` — fire once at a specific time
|
|
10
|
+
|
|
11
|
+
## Creating schedules
|
|
12
|
+
|
|
13
|
+
\`\`\`bash
|
|
14
|
+
# Recurring cron (weekdays 10am New York)
|
|
15
|
+
parall schedules create \\
|
|
16
|
+
--name "Daily standup" \\
|
|
17
|
+
--description "Ask the team for their plan today; see prll://wik_xxx for the standup template" \\
|
|
18
|
+
--target-ids prll://usr_xxx \\
|
|
19
|
+
--cron-expr "0 10 * * 1-5" \\
|
|
20
|
+
--timezone America/New_York \\
|
|
21
|
+
--attached-to-uri prll://cht_xxx
|
|
22
|
+
|
|
23
|
+
# Every 30 minutes
|
|
24
|
+
parall schedules create \\
|
|
25
|
+
--name "CI watch" \\
|
|
26
|
+
--description "Check the deploy status and flag failures" \\
|
|
27
|
+
--target-ids prll://usr_xxx \\
|
|
28
|
+
--interval-seconds 1800
|
|
29
|
+
|
|
30
|
+
# One-shot at a future RFC3339 time
|
|
31
|
+
parall schedules create \\
|
|
32
|
+
--name "Followup" \\
|
|
33
|
+
--description "Remind the user about the PR review if still pending" \\
|
|
34
|
+
--target-ids prll://usr_xxx \\
|
|
35
|
+
--run-at <FUTURE_RFC3339_TIME>
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
\`--target-ids\` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). \`--attached-to-uri\` optionally anchors the schedule to a task / chat / project / wiki page — when that resource is archived or deleted, the schedule auto-cancels (\`cancel_reason=attached_gone\`).
|
|
39
|
+
|
|
40
|
+
## Listing / inspecting
|
|
41
|
+
|
|
42
|
+
\`\`\`bash
|
|
43
|
+
parall schedules list --status active,paused
|
|
44
|
+
parall schedules list --attached-to prll://tsk_xxx
|
|
45
|
+
parall schedules list --attendee-id prll://usr_xxx
|
|
46
|
+
parall schedules get prll://sch_xxx
|
|
47
|
+
parall schedules runs prll://sch_xxx # fire history
|
|
48
|
+
parall schedules run prll://srn_xxx # single run incl. fire-time snapshot
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
## Lifecycle
|
|
52
|
+
|
|
53
|
+
\`\`\`bash
|
|
54
|
+
parall schedules update prll://sch_xxx --description "New prompt"
|
|
55
|
+
parall schedules pause prll://sch_xxx # reversible
|
|
56
|
+
parall schedules resume prll://sch_xxx # does NOT catch up missed slots
|
|
57
|
+
parall schedules cancel prll://sch_xxx # terminal; row + runs preserved, prll://sch_ ref stays valid
|
|
58
|
+
parall schedules delete prll://sch_xxx # hard-delete; requires status=cancelled AND run_count=0. Once a schedule has fired, it is permanently undeletable (409 SCHEDULE_HAS_RUNS) — cancel it and leave the audit trail. Delete is for never-fired test/accidental schedules only.
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
\`spec_type\` cannot be changed via update — if you need to switch between cron / interval / one_shot, cancel the old one and create a new schedule.
|
|
62
|
+
|
|
63
|
+
## Responding to schedule fires
|
|
64
|
+
|
|
65
|
+
When you receive \`[Event: schedule.fired]\`, the platform has fired a schedule targeting you.
|
|
66
|
+
|
|
67
|
+
The runtime (agent-core) has already done the heavy lifting: it fetched the schedule run and inlined the fire-time \`description\` (a frozen snapshot — later edits to the schedule don't change past fires) into your prompt, alongside \`[Schedule: prll://sch_xxx]\` and \`[Run: prll://srn_xxx]\` headers. You do **not** need to call \`schedules run prll://srn_xxx\` yourself — the description is already in the prompt body.
|
|
68
|
+
|
|
69
|
+
Your job is to interpret the description and act:
|
|
70
|
+
|
|
71
|
+
1. Read the description and any \`prll://\` refs it contains
|
|
72
|
+
2. Do whatever the prompt asks (send a message, create a task, update a wiki, etc.) — there is no canonical response format
|
|
73
|
+
3. Optional: if the fire is genuinely a no-op and you don't want to produce any artifact, use \`no-reply\` (from parall-platform skill) to stay silent for this turn
|
|
74
|
+
|
|
75
|
+
Do not treat schedule fires as "tasks assigned to you" — there's no status to transition, no acknowledgment required. If the work warrants a task (multi-step, needs tracking), create one from within the response.
|
|
76
|
+
|
|
77
|
+
**Fetching the run explicitly** (optional): \`schedules run prll://srn_xxx\` returns the same snapshot plus delivery records (reverse-lookable via \`source_id=srn_xxx\`) for audit. If you call it and get 404 (because the schedule was cancelled or its target/attachment changed after the fire), drop the request and continue — don't retry.
|
|
78
|
+
|
|
79
|
+
CLI command results are JSON on stdout; mutation commands may emit auxiliary hints on stderr (for example, \`Created: prll://sch_xxx\`).
|
|
80
|
+
`;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const PARALL_TASKS_SKILL = "# Parall Tasks\n\nManage tasks and projects via the Parall CLI. Auth and runtime context are pre-configured.\n\n## Task Commands\n\n```bash\n# List tasks (filterable by status)\nparall tasks list\nparall tasks list --status todo\nparall tasks list --status in_progress\n\n# Create a task\nparall tasks create --title \"Task title\" [--assignee-id prll://usr_xxx] [--project-id prll://prj_xxx]\n\n# Update task status\nparall tasks update prll://tsk_xxx --status in_progress\nparall tasks update prll://tsk_xxx --status done\n\n# Add a comment\nparall tasks comments add prll://tsk_xxx --body \"Progress update...\"\n```\n\n## Project Commands\n\n```bash\nparall projects list\n```\n\n## Watching Tasks\n\nSubscribe to a task to receive notifications when others comment on it.\n\n```bash\n# Watch a task (you'll receive dispatch events for new comments)\nparall tasks watch prll://tsk_xxx\n\n# Unwatch\nparall tasks unwatch prll://tsk_xxx\n\n# List who is watching\nparall tasks watchers prll://tsk_xxx\n```\n\nNote: task creators are automatically watching their tasks.\n\n## Responding to Task Assignments\n\nWhen you receive `[Event: task.assigned]`:\n\n1. Acknowledge with a comment: `tasks comments add prll://tsk_xxx --body \"On it\"`\n2. Update status: `tasks update prll://tsk_xxx --status in_progress`\n3. Do the work\n4. Report results via comment and update status to `done`\n\n## Responding to Task Comments\n\nWhen you receive `[Event: task.comment.created]`, someone commented on a task you are watching. Read the comment body and respond if action is needed:\n\n1. Review the comment content and task context\n2. Reply via comment: `tasks comments add prll://tsk_xxx --body \"Response...\"`\n3. If the comment requests status changes, update accordingly\n\nAll CLI output is JSON.\n";
|
|
2
|
+
//# sourceMappingURL=parall-tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parall-tasks.d.ts","sourceRoot":"","sources":["../../src/skills/parall-tasks.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,0wDAgE9B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export const PARALL_TASKS_SKILL = `# Parall Tasks
|
|
2
|
+
|
|
3
|
+
Manage tasks and projects via the Parall CLI. Auth and runtime context are pre-configured.
|
|
4
|
+
|
|
5
|
+
## Task Commands
|
|
6
|
+
|
|
7
|
+
\`\`\`bash
|
|
8
|
+
# List tasks (filterable by status)
|
|
9
|
+
parall tasks list
|
|
10
|
+
parall tasks list --status todo
|
|
11
|
+
parall tasks list --status in_progress
|
|
12
|
+
|
|
13
|
+
# Create a task
|
|
14
|
+
parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--project-id prll://prj_xxx]
|
|
15
|
+
|
|
16
|
+
# Update task status
|
|
17
|
+
parall tasks update prll://tsk_xxx --status in_progress
|
|
18
|
+
parall tasks update prll://tsk_xxx --status done
|
|
19
|
+
|
|
20
|
+
# Add a comment
|
|
21
|
+
parall tasks comments add prll://tsk_xxx --body "Progress update..."
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
## Project Commands
|
|
25
|
+
|
|
26
|
+
\`\`\`bash
|
|
27
|
+
parall projects list
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
## Watching Tasks
|
|
31
|
+
|
|
32
|
+
Subscribe to a task to receive notifications when others comment on it.
|
|
33
|
+
|
|
34
|
+
\`\`\`bash
|
|
35
|
+
# Watch a task (you'll receive dispatch events for new comments)
|
|
36
|
+
parall tasks watch prll://tsk_xxx
|
|
37
|
+
|
|
38
|
+
# Unwatch
|
|
39
|
+
parall tasks unwatch prll://tsk_xxx
|
|
40
|
+
|
|
41
|
+
# List who is watching
|
|
42
|
+
parall tasks watchers prll://tsk_xxx
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
Note: task creators are automatically watching their tasks.
|
|
46
|
+
|
|
47
|
+
## Responding to Task Assignments
|
|
48
|
+
|
|
49
|
+
When you receive \`[Event: task.assigned]\`:
|
|
50
|
+
|
|
51
|
+
1. Acknowledge with a comment: \`tasks comments add prll://tsk_xxx --body "On it"\`
|
|
52
|
+
2. Update status: \`tasks update prll://tsk_xxx --status in_progress\`
|
|
53
|
+
3. Do the work
|
|
54
|
+
4. Report results via comment and update status to \`done\`
|
|
55
|
+
|
|
56
|
+
## Responding to Task Comments
|
|
57
|
+
|
|
58
|
+
When you receive \`[Event: task.comment.created]\`, someone commented on a task you are watching. Read the comment body and respond if action is needed:
|
|
59
|
+
|
|
60
|
+
1. Review the comment content and task context
|
|
61
|
+
2. Reply via comment: \`tasks comments add prll://tsk_xxx --body "Response..."\`
|
|
62
|
+
3. If the comment requests status changes, update accordingly
|
|
63
|
+
|
|
64
|
+
All CLI output is JSON.
|
|
65
|
+
`;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const PARALL_WIKI_SKILL = "# Parall Wiki\n\nManage organization wikis via the Parall CLI. Auth is pre-configured.\n\n## Browsing (no local state needed)\n\n```bash\nparall wiki list # List all wikis\nparall wiki tree <slug> # List files and directories\nparall wiki tree <slug> --path docs/ # List a subdirectory\n```\n\n## Editing\n\nWiki editing works on a **local workspace** \u2014 a directory on disk where wiki\nfiles are synced. You sync from the server, edit files locally, then propose\na changeset.\n\n### Step 1: Sync\n\n```bash\nparall wiki sync <slug>\n```\n\nThis downloads wiki files to a local directory. The output includes the\n**absolute mount path** for each wiki (e.g. `synced \u2192 /path/to/workspace/kb`).\n\n### Step 2: Find the mount path\n\nThe sync output JSON contains `synced[].path` \u2014 the absolute path where files\nlive. You can also check it anytime with:\n\n```bash\nparall wiki status <slug>\n```\n\nThe output includes `Mount: /absolute/path/to/<slug>`.\n\nUse this path with `read`, `write`, and `edit` tools. For example, if the mount\nis `/data/.openclaw/workspace/kb`, then `README.md` is at\n`/data/.openclaw/workspace/kb/README.md`.\n\n### Step 3: Edit files\n\nUse standard file tools (`read`, `write`, `edit`) on files under the mount path.\n\n### Step 4: Review changes\n\n```bash\nparall wiki diff <slug> # Shows unified diff of all local changes\nparall wiki status <slug> # Shows which files changed with +/- line counts\n```\n\nAlways review before proposing.\n\n### Step 5: Propose changeset\n\n```bash\nparall wiki changeset create <slug> --title \"Description of changes\"\n```\n\nThis uploads your local changes. Unprotected paths auto-merge immediately; the\nCLI prints `auto_merged: true` and refreshes the local manifest. Protected\npaths remain as a changeset for review; follow the returned `next_action`.\n\n## Changeset Management\n\n```bash\nparall wiki changeset list <slug> # List all changesets\nparall wiki changeset show <changesetId> <slug> # Show detail + feedback\nparall wiki changeset diff <changesetId> <slug> # Show changeset diff\n```\n\nIf a changeset is rejected, fix the files locally and re-propose:\n\n```bash\nparall wiki changeset create <slug> --update <changesetId>\n```\n\nThe title is inherited from the original changeset \u2014 no need to repeat it.\n\n## Handling sync conflicts\n\n`wiki sync` runs a three-way merge. When both you and the server changed the\nsame file, sync **does not overwrite your work**. It leaves your file intact\nand drops the upstream version under `.parall-wiki/conflicts/`:\n\n| Marker | Meaning |\n|--------|---------|\n| `.parall-wiki/conflicts/<path>.remote` | Server has different content (concurrent edit, new file collision, or server changed a file you deleted) |\n| `.parall-wiki/conflicts/<path>.remote-deleted` | Server deleted the file; you still have edits |\n\nstderr prints one line per conflict. Recovery:\n\n**Accept upstream** (drop your edit):\n```bash\ncp .parall-wiki/conflicts/<path>.remote <path>\nparall wiki sync\n```\n\n**Keep yours** (or hand-merge, then propose):\n```bash\n# edit <path> to final content\nparall wiki diff <slug>\nparall wiki changeset create <slug> --title \"Reconcile <path>\"\nparall wiki sync # fast-forwards after server merges\n```\n\n**Accept server delete** (`.remote-deleted` only):\n```bash\nrm <path>\nparall wiki sync\n```\n\nSync failures (`download`, `shape-conflict`) set exit code 1 and retry on\nnext sync. Conflicts exit 0 \u2014 they need your decision, not a retry.\n\n## Discarding local changes\n\n```bash\nparall wiki reset <slug> # Restore all files to last synced state\n```\n\n## History\n\n```bash\nparall wiki log <slug> # Recent wiki operations\n```\n\nAll CLI output is JSON. Run `parall wiki --help` for full options.\n";
|
|
2
|
+
//# sourceMappingURL=parall-wiki.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parall-wiki.d.ts","sourceRoot":"","sources":["../../src/skills/parall-wiki.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,gyHAkI7B,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const PARALL_WIKI_SKILL = `# Parall Wiki
|
|
2
|
+
|
|
3
|
+
Manage organization wikis via the Parall CLI. Auth is pre-configured.
|
|
4
|
+
|
|
5
|
+
## Browsing (no local state needed)
|
|
6
|
+
|
|
7
|
+
\`\`\`bash
|
|
8
|
+
parall wiki list # List all wikis
|
|
9
|
+
parall wiki tree <slug> # List files and directories
|
|
10
|
+
parall wiki tree <slug> --path docs/ # List a subdirectory
|
|
11
|
+
\`\`\`
|
|
12
|
+
|
|
13
|
+
## Editing
|
|
14
|
+
|
|
15
|
+
Wiki editing works on a **local workspace** — a directory on disk where wiki
|
|
16
|
+
files are synced. You sync from the server, edit files locally, then propose
|
|
17
|
+
a changeset.
|
|
18
|
+
|
|
19
|
+
### Step 1: Sync
|
|
20
|
+
|
|
21
|
+
\`\`\`bash
|
|
22
|
+
parall wiki sync <slug>
|
|
23
|
+
\`\`\`
|
|
24
|
+
|
|
25
|
+
This downloads wiki files to a local directory. The output includes the
|
|
26
|
+
**absolute mount path** for each wiki (e.g. \`synced → /path/to/workspace/kb\`).
|
|
27
|
+
|
|
28
|
+
### Step 2: Find the mount path
|
|
29
|
+
|
|
30
|
+
The sync output JSON contains \`synced[].path\` — the absolute path where files
|
|
31
|
+
live. You can also check it anytime with:
|
|
32
|
+
|
|
33
|
+
\`\`\`bash
|
|
34
|
+
parall wiki status <slug>
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
The output includes \`Mount: /absolute/path/to/<slug>\`.
|
|
38
|
+
|
|
39
|
+
Use this path with \`read\`, \`write\`, and \`edit\` tools. For example, if the mount
|
|
40
|
+
is \`/data/.openclaw/workspace/kb\`, then \`README.md\` is at
|
|
41
|
+
\`/data/.openclaw/workspace/kb/README.md\`.
|
|
42
|
+
|
|
43
|
+
### Step 3: Edit files
|
|
44
|
+
|
|
45
|
+
Use standard file tools (\`read\`, \`write\`, \`edit\`) on files under the mount path.
|
|
46
|
+
|
|
47
|
+
### Step 4: Review changes
|
|
48
|
+
|
|
49
|
+
\`\`\`bash
|
|
50
|
+
parall wiki diff <slug> # Shows unified diff of all local changes
|
|
51
|
+
parall wiki status <slug> # Shows which files changed with +/- line counts
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
Always review before proposing.
|
|
55
|
+
|
|
56
|
+
### Step 5: Propose changeset
|
|
57
|
+
|
|
58
|
+
\`\`\`bash
|
|
59
|
+
parall wiki changeset create <slug> --title "Description of changes"
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
This uploads your local changes. Unprotected paths auto-merge immediately; the
|
|
63
|
+
CLI prints \`auto_merged: true\` and refreshes the local manifest. Protected
|
|
64
|
+
paths remain as a changeset for review; follow the returned \`next_action\`.
|
|
65
|
+
|
|
66
|
+
## Changeset Management
|
|
67
|
+
|
|
68
|
+
\`\`\`bash
|
|
69
|
+
parall wiki changeset list <slug> # List all changesets
|
|
70
|
+
parall wiki changeset show <changesetId> <slug> # Show detail + feedback
|
|
71
|
+
parall wiki changeset diff <changesetId> <slug> # Show changeset diff
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
If a changeset is rejected, fix the files locally and re-propose:
|
|
75
|
+
|
|
76
|
+
\`\`\`bash
|
|
77
|
+
parall wiki changeset create <slug> --update <changesetId>
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
The title is inherited from the original changeset — no need to repeat it.
|
|
81
|
+
|
|
82
|
+
## Handling sync conflicts
|
|
83
|
+
|
|
84
|
+
\`wiki sync\` runs a three-way merge. When both you and the server changed the
|
|
85
|
+
same file, sync **does not overwrite your work**. It leaves your file intact
|
|
86
|
+
and drops the upstream version under \`.parall-wiki/conflicts/\`:
|
|
87
|
+
|
|
88
|
+
| Marker | Meaning |
|
|
89
|
+
|--------|---------|
|
|
90
|
+
| \`.parall-wiki/conflicts/<path>.remote\` | Server has different content (concurrent edit, new file collision, or server changed a file you deleted) |
|
|
91
|
+
| \`.parall-wiki/conflicts/<path>.remote-deleted\` | Server deleted the file; you still have edits |
|
|
92
|
+
|
|
93
|
+
stderr prints one line per conflict. Recovery:
|
|
94
|
+
|
|
95
|
+
**Accept upstream** (drop your edit):
|
|
96
|
+
\`\`\`bash
|
|
97
|
+
cp .parall-wiki/conflicts/<path>.remote <path>
|
|
98
|
+
parall wiki sync
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
**Keep yours** (or hand-merge, then propose):
|
|
102
|
+
\`\`\`bash
|
|
103
|
+
# edit <path> to final content
|
|
104
|
+
parall wiki diff <slug>
|
|
105
|
+
parall wiki changeset create <slug> --title "Reconcile <path>"
|
|
106
|
+
parall wiki sync # fast-forwards after server merges
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
**Accept server delete** (\`.remote-deleted\` only):
|
|
110
|
+
\`\`\`bash
|
|
111
|
+
rm <path>
|
|
112
|
+
parall wiki sync
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
Sync failures (\`download\`, \`shape-conflict\`) set exit code 1 and retry on
|
|
116
|
+
next sync. Conflicts exit 0 — they need your decision, not a retry.
|
|
117
|
+
|
|
118
|
+
## Discarding local changes
|
|
119
|
+
|
|
120
|
+
\`\`\`bash
|
|
121
|
+
parall wiki reset <slug> # Restore all files to last synced state
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
## History
|
|
125
|
+
|
|
126
|
+
\`\`\`bash
|
|
127
|
+
parall wiki log <slug> # Recent wiki operations
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
All CLI output is JSON. Run \`parall wiki --help\` for full options.
|
|
131
|
+
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/agent-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "Shared agent runtime orchestration helpers for Parall",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"src"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@parall/sdk": "1.
|
|
29
|
+
"@parall/sdk": "1.26.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|
package/src/bridge-workspace.ts
CHANGED
|
@@ -29,7 +29,7 @@ All outbound interactions go through the \`parall\` CLI. Credentials are pre-inj
|
|
|
29
29
|
- \`parall tasks update prll://tsk_xxx --status in_progress\` — task state
|
|
30
30
|
- \`parall no-reply [--reason "..."]\` — explicitly declare this turn silent (audit signal; not required for silence, just clarifies intent)
|
|
31
31
|
|
|
32
|
-
The bridge injects Parall context via environment variables. The static credentials \`PRLL_API_URL\`, \`PRLL_API_KEY\`, and \`PRLL_ORG_ID\` are always set.
|
|
32
|
+
The bridge injects Parall context via environment variables. The static credentials \`PRLL_API_URL\`, \`PRLL_API_KEY\`, and \`PRLL_ORG_ID\` are always set. \`PRLL_CONTEXT_FILE\` points to a per-session JSON file that the gateway updates each dispatch with \`session_id\`, \`chat_id\`, \`trigger_message_id\`, \`no_reply\`, and \`step_id\` (updated per tool call). The CLI reads this file automatically — you do not need to pass \`--chat\` or \`--session\` explicitly when the context file is present.
|
|
33
33
|
|
|
34
34
|
## Attachments
|
|
35
35
|
|
package/src/dispatch-adapter.ts
CHANGED
|
@@ -19,6 +19,8 @@ export type DispatchContext = {
|
|
|
19
19
|
chatId?: string;
|
|
20
20
|
triggerMessageId?: string;
|
|
21
21
|
noReply: boolean;
|
|
22
|
+
contextFilePath?: string;
|
|
23
|
+
/** @deprecated Use contextFilePath. Kept for runtimes that haven't migrated. */
|
|
22
24
|
stepIdFilePath?: string;
|
|
23
25
|
client: ParallClient;
|
|
24
26
|
log?: GatewayLogger;
|
package/src/gateway-base.ts
CHANGED
|
@@ -98,6 +98,8 @@ export type ParallGatewayOptions = {
|
|
|
98
98
|
// abort before forcing WS disconnect. Pod termination grace period should
|
|
99
99
|
// be at least this + a few seconds for the remaining cleanup work.
|
|
100
100
|
shutdownDeadlineMs?: number;
|
|
101
|
+
contextFilePathForSession?: (sessionKey: string) => string | undefined;
|
|
102
|
+
/** @deprecated Use contextFilePathForSession. Kept for runtimes that haven't migrated. */
|
|
101
103
|
stepIdFilePathForSession?: (sessionKey: string) => string | undefined;
|
|
102
104
|
onConfigUpdate?: (data: AgentConfigUpdateData) => Promise<void> | void;
|
|
103
105
|
onSessionReady?: (state: { activeSessionId?: string; ws: ParallWs; runtimeKey: string }) => Promise<void> | void;
|
|
@@ -366,6 +368,7 @@ export class ParallAgentGateway {
|
|
|
366
368
|
chatId: event.type === "message" ? event.targetId : undefined,
|
|
367
369
|
triggerMessageId: event.messageId,
|
|
368
370
|
noReply: event.noReply ?? false,
|
|
371
|
+
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey),
|
|
369
372
|
stepIdFilePath: this.opts.stepIdFilePathForSession?.(sessionKey),
|
|
370
373
|
client: this.opts.client,
|
|
371
374
|
log: this.opts.log,
|
|
@@ -401,7 +404,7 @@ export class ParallAgentGateway {
|
|
|
401
404
|
}
|
|
402
405
|
}
|
|
403
406
|
|
|
404
|
-
private async createRuntimeStep(event: ParallEvent, runtimeEvent: RuntimeEvent, stepIdFilePath?: string) {
|
|
407
|
+
private async createRuntimeStep(event: ParallEvent, runtimeEvent: RuntimeEvent, stepIdFilePath?: string, contextFilePath?: string) {
|
|
405
408
|
if (!this.activeSessionId) return;
|
|
406
409
|
|
|
407
410
|
const target = resolveStepTarget(event);
|
|
@@ -446,7 +449,9 @@ export class ParallAgentGateway {
|
|
|
446
449
|
group_key: runtimeEvent.groupKey,
|
|
447
450
|
runtime_key: runtimeEvent.callId,
|
|
448
451
|
});
|
|
449
|
-
if (
|
|
452
|
+
if (contextFilePath) {
|
|
453
|
+
this.updateContextFileStepId(contextFilePath, step.id);
|
|
454
|
+
} else if (stepIdFilePath) {
|
|
450
455
|
this.writeStepIdFile(stepIdFilePath, step.id);
|
|
451
456
|
}
|
|
452
457
|
break;
|
|
@@ -467,7 +472,9 @@ export class ParallAgentGateway {
|
|
|
467
472
|
},
|
|
468
473
|
group_key: runtimeEvent.groupKey,
|
|
469
474
|
});
|
|
470
|
-
if (
|
|
475
|
+
if (contextFilePath) {
|
|
476
|
+
this.updateContextFileStepId(contextFilePath, null);
|
|
477
|
+
} else if (stepIdFilePath) {
|
|
471
478
|
this.clearStepIdFile(stepIdFilePath);
|
|
472
479
|
}
|
|
473
480
|
break;
|
|
@@ -487,6 +494,27 @@ export class ParallAgentGateway {
|
|
|
487
494
|
}
|
|
488
495
|
}
|
|
489
496
|
|
|
497
|
+
private writeContextFile(filePath: string, ctx: Record<string, unknown>) {
|
|
498
|
+
try {
|
|
499
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
500
|
+
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
501
|
+
} catch (err) {
|
|
502
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write context file ${filePath}: ${String(err)}`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private updateContextFileStepId(filePath: string, stepId: string | null) {
|
|
507
|
+
try {
|
|
508
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
509
|
+
const ctx = JSON.parse(raw);
|
|
510
|
+
ctx.step_id = stepId;
|
|
511
|
+
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
512
|
+
} catch (err) {
|
|
513
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file step_id ${filePath}: ${String(err)}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
490
518
|
private writeStepIdFile(filePath: string, stepId: string) {
|
|
491
519
|
try {
|
|
492
520
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
@@ -496,6 +524,7 @@ export class ParallAgentGateway {
|
|
|
496
524
|
}
|
|
497
525
|
}
|
|
498
526
|
|
|
527
|
+
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
499
528
|
private clearStepIdFile(filePath: string) {
|
|
500
529
|
try {
|
|
501
530
|
fs.writeFileSync(filePath, "", "utf8");
|
|
@@ -536,8 +565,19 @@ export class ParallAgentGateway {
|
|
|
536
565
|
setDispatchNoReply(sessionKey, event.noReply ?? false);
|
|
537
566
|
|
|
538
567
|
const dispatchContext = this.buildDispatchContext(event, sessionKey);
|
|
568
|
+
const contextFilePath = dispatchContext.contextFilePath;
|
|
539
569
|
const stepIdFilePath = dispatchContext.stepIdFilePath;
|
|
540
570
|
|
|
571
|
+
if (contextFilePath) {
|
|
572
|
+
this.writeContextFile(contextFilePath, {
|
|
573
|
+
session_id: dispatchContext.sessionId ?? null,
|
|
574
|
+
chat_id: dispatchContext.chatId ?? null,
|
|
575
|
+
trigger_message_id: dispatchContext.triggerMessageId ?? null,
|
|
576
|
+
no_reply: dispatchContext.noReply,
|
|
577
|
+
step_id: null,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
541
581
|
// sync: no await between the shuttingDown check above and this increment
|
|
542
582
|
// — JS event loop is single-threaded, so shutdown() cannot interleave
|
|
543
583
|
// here and miss our in-flight count.
|
|
@@ -567,19 +607,21 @@ export class ParallAgentGateway {
|
|
|
567
607
|
sessionKey,
|
|
568
608
|
context: dispatchContext,
|
|
569
609
|
})) {
|
|
570
|
-
await this.createRuntimeStep(event, runtimeEvent, stepIdFilePath);
|
|
610
|
+
await this.createRuntimeStep(event, runtimeEvent, stepIdFilePath, contextFilePath);
|
|
571
611
|
}
|
|
572
612
|
} catch (err) {
|
|
573
613
|
await this.createRuntimeStep(event, {
|
|
574
614
|
type: "error",
|
|
575
615
|
message: `Dispatch failed: ${String(err)}`,
|
|
576
|
-
}, stepIdFilePath);
|
|
616
|
+
}, stepIdFilePath, contextFilePath);
|
|
577
617
|
throw err;
|
|
578
618
|
} finally {
|
|
579
619
|
clearSessionMessageId(sessionKey);
|
|
580
620
|
clearDispatchMessageId(sessionKey);
|
|
581
621
|
clearDispatchNoReply(sessionKey);
|
|
582
|
-
if (
|
|
622
|
+
if (contextFilePath) {
|
|
623
|
+
this.updateContextFileStepId(contextFilePath, null);
|
|
624
|
+
} else if (stepIdFilePath) {
|
|
583
625
|
this.clearStepIdFile(stepIdFilePath);
|
|
584
626
|
}
|
|
585
627
|
if (this.activeSessionId) {
|
|
@@ -780,9 +822,13 @@ export class ParallAgentGateway {
|
|
|
780
822
|
return dispatched;
|
|
781
823
|
}
|
|
782
824
|
|
|
783
|
-
case "buffer-main":
|
|
825
|
+
case "buffer-main": {
|
|
826
|
+
if (this.shuttingDown) {
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
784
829
|
this.dispatchState.mainBuffer.push(event);
|
|
785
830
|
return false;
|
|
831
|
+
}
|
|
786
832
|
|
|
787
833
|
case "buffer-fork": {
|
|
788
834
|
const activeFork = this.forkStates.get(event.targetId);
|
package/src/index.ts
CHANGED
|
@@ -6,3 +6,6 @@ export * from "./prompt-fragments.js";
|
|
|
6
6
|
export * from "./bridge-workspace.js";
|
|
7
7
|
export * from "./dispatch-adapter.js";
|
|
8
8
|
export * from "./gateway-base.js";
|
|
9
|
+
export * from "./platform-config.js";
|
|
10
|
+
export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
|
|
11
|
+
export type { SkillMeta } from "./skills/index.js";
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { ParallClient, PlatformConfigResponse } from "@parall/sdk";
|
|
4
|
+
import type { GatewayLogger } from "./dispatch-adapter.js";
|
|
5
|
+
|
|
6
|
+
export interface PlatformDefaults {
|
|
7
|
+
model: string | null;
|
|
8
|
+
thinkingEffort: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PlatformConfigManager {
|
|
12
|
+
fetch(): Promise<PlatformDefaults>;
|
|
13
|
+
current(): PlatformDefaults;
|
|
14
|
+
rawConfig(): Record<string, unknown> | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PlatformManagementProfile {
|
|
18
|
+
machine_id?: string | null;
|
|
19
|
+
model_management?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isPlatformManagedProfile(profile: PlatformManagementProfile | null | undefined): boolean {
|
|
23
|
+
return profile?.machine_id != null || profile?.model_management === "platform";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PlatformModelDef {
|
|
27
|
+
id?: unknown;
|
|
28
|
+
runtime_names?: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface CachedPlatformConfig {
|
|
32
|
+
version: string;
|
|
33
|
+
config: Record<string, unknown>;
|
|
34
|
+
fetchedAt: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const CACHE_FILENAME = "parall-platform-config.json";
|
|
38
|
+
const SUPPORTED_SCHEMA_VERSION = 1;
|
|
39
|
+
|
|
40
|
+
function cachePath(stateDir: string): string {
|
|
41
|
+
return path.join(stateDir, CACHE_FILENAME);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function loadCache(stateDir: string): CachedPlatformConfig | null {
|
|
45
|
+
try {
|
|
46
|
+
const raw = fs.readFileSync(cachePath(stateDir), "utf-8");
|
|
47
|
+
return JSON.parse(raw) as CachedPlatformConfig;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function saveCache(stateDir: string, response: PlatformConfigResponse): void {
|
|
54
|
+
const cached: CachedPlatformConfig = {
|
|
55
|
+
version: response.version,
|
|
56
|
+
config: response.config,
|
|
57
|
+
fetchedAt: new Date().toISOString(),
|
|
58
|
+
};
|
|
59
|
+
const filePath = cachePath(stateDir);
|
|
60
|
+
const tmpPath = `${filePath}.tmp`;
|
|
61
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
62
|
+
fs.writeFileSync(tmpPath, JSON.stringify(cached, null, 2), "utf-8");
|
|
63
|
+
fs.renameSync(tmpPath, filePath);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function runtimeModelName(
|
|
67
|
+
canonicalModel: string,
|
|
68
|
+
runtimeType: string | undefined,
|
|
69
|
+
config: Record<string, unknown>,
|
|
70
|
+
): string | null {
|
|
71
|
+
const runtime = runtimeType?.trim();
|
|
72
|
+
if (!runtime || runtime === "openclaw") return canonicalModel;
|
|
73
|
+
|
|
74
|
+
const models = (config.models ?? {}) as Record<string, unknown>;
|
|
75
|
+
const providers = (models.providers ?? {}) as Record<string, unknown>;
|
|
76
|
+
const parall = (providers.parall ?? {}) as Record<string, unknown>;
|
|
77
|
+
const catalog = Array.isArray(parall.models) ? (parall.models as PlatformModelDef[]) : [];
|
|
78
|
+
const match = catalog.find((model) => model.id === canonicalModel);
|
|
79
|
+
const runtimeNames = (match?.runtime_names ?? {}) as Record<string, unknown>;
|
|
80
|
+
const runtimeName = runtimeNames[runtime];
|
|
81
|
+
return typeof runtimeName === "string" && runtimeName ? runtimeName : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractDefaults(config: Record<string, unknown>, runtimeType?: string): PlatformDefaults {
|
|
85
|
+
const agents = (config.agents ?? {}) as Record<string, unknown>;
|
|
86
|
+
const defaults = (agents.defaults ?? {}) as Record<string, unknown>;
|
|
87
|
+
|
|
88
|
+
let model: string | null = null;
|
|
89
|
+
if (typeof defaults.model === "string" && defaults.model) {
|
|
90
|
+
const canonicalModel = defaults.model.replace(/^parall\//, "");
|
|
91
|
+
model = runtimeModelName(canonicalModel, runtimeType, config);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let thinkingEffort: string | null = null;
|
|
95
|
+
if (typeof defaults.thinking_effort === "string" && defaults.thinking_effort) {
|
|
96
|
+
thinkingEffort = defaults.thinking_effort;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { model, thinkingEffort };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function createPlatformConfigManager(opts: {
|
|
103
|
+
client: ParallClient;
|
|
104
|
+
stateDir: string;
|
|
105
|
+
runtimeType?: string;
|
|
106
|
+
log?: GatewayLogger;
|
|
107
|
+
}): PlatformConfigManager {
|
|
108
|
+
const { client, stateDir, runtimeType, log } = opts;
|
|
109
|
+
let cachedVersion: string | undefined;
|
|
110
|
+
let currentDefaults: PlatformDefaults = { model: null, thinkingEffort: null };
|
|
111
|
+
let currentRawConfig: Record<string, unknown> | null = null;
|
|
112
|
+
|
|
113
|
+
const cached = loadCache(stateDir);
|
|
114
|
+
if (cached) {
|
|
115
|
+
cachedVersion = cached.version;
|
|
116
|
+
currentRawConfig = cached.config;
|
|
117
|
+
currentDefaults = extractDefaults(cached.config, runtimeType);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
async fetch(): Promise<PlatformDefaults> {
|
|
122
|
+
let fresh: PlatformConfigResponse | null = null;
|
|
123
|
+
try {
|
|
124
|
+
fresh = await client.getPlatformConfig(cachedVersion);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (cached) {
|
|
127
|
+
log?.warn(`platform config fetch failed, using cached version ${cachedVersion}: ${String(err)}`);
|
|
128
|
+
return currentDefaults;
|
|
129
|
+
}
|
|
130
|
+
log?.warn(`platform config fetch failed and no cache available: ${String(err)}`);
|
|
131
|
+
return currentDefaults;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (fresh === null) {
|
|
135
|
+
return currentDefaults;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (fresh.schema_version !== undefined && fresh.schema_version > SUPPORTED_SCHEMA_VERSION) {
|
|
139
|
+
log?.warn(`platform config schema_version ${fresh.schema_version} > supported (${SUPPORTED_SCHEMA_VERSION}), keeping current`);
|
|
140
|
+
return currentDefaults;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
log?.info(`platform config updated to version ${fresh.version}`);
|
|
144
|
+
saveCache(stateDir, fresh);
|
|
145
|
+
cachedVersion = fresh.version;
|
|
146
|
+
currentRawConfig = fresh.config;
|
|
147
|
+
currentDefaults = extractDefaults(fresh.config, runtimeType);
|
|
148
|
+
return currentDefaults;
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
current(): PlatformDefaults {
|
|
152
|
+
return currentDefaults;
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
rawConfig(): Record<string, unknown> | null {
|
|
156
|
+
return currentRawConfig;
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|