agentlife 2.6.28 → 2.6.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +912 -352
- package/package.json +16 -6
- package/web-build/6e23e5428398b92da386.wasm +0 -0
- package/web-build/auth-callback.html +40 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_action_plan.xml +10 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_add.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_alarm.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_amazon.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_apple.xml +12 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_clockwise.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_counter_clockwise.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_down_up.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_right.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_square_out.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrows_out.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_audible.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_auto_send.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_back.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_barcode.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell_ringing.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_binoculars.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_brain.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bulb.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_blank.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_check.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_dots.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_down.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_up.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_down.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_right.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_up.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle_text.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_teardrop.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_check_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_child.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_counter_clockwise.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_close.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_comment.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_community.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_copy_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_credit_card.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_decision_framework.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_diff.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_dots_three.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_edit.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_experiment.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_facebook.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_faders.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_dotted.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_text.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_filter.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_flask.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_floppy_disk.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_open.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_git_merge.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google.xml +35 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google_play.xml +22 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_habit_tracker.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_1.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_2.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_3.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_4.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_hide_pass.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_info_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_instagram.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_keyboard.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lightning.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_linkedin.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lock.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_logo.xml +12 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_map_pin_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_media.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_menu.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_microphone_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_more_vert.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paint_bucket.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paper_plane_right.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paperclip.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_parent.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_pause_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_play_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_plus_circle.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin_slash.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_question.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_record.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_rocket.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_scales.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_search.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_settings.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_share_network.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_show_pass.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sign_out.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_smiley.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparkles.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparks.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_speaker_high.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_spotify.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_star.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_circle.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_swap.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tag_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_down.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_up.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tiktok.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_complete.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_translate.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_trash_simple.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tree_structure.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_twitch.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_upload.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_user_circle.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_warning.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_web.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_wrench.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x_circle.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube_music.xml +9 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/tenazitas_logo.xml +27 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Caveat-Medium.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Medium.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Regular.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-SemiBold.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Medium.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Regular.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Italic.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Regular.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-SemiBold.ttf +0 -0
- package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/values/strings.commonMain.cvr +2787 -0
- package/web-build/f722462c344efeb18c2f.wasm +0 -0
- package/web-build/index.html +23 -0
- package/web-build/webApp.js +3 -0
- package/web-build/webApp.js.LICENSE.txt +35 -0
- package/web-build/webApp.js.map +1 -0
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
4
|
// index.ts
|
|
5
|
-
import { homedir as
|
|
6
|
-
import * as
|
|
7
|
-
import { existsSync as
|
|
5
|
+
import { homedir as homedir17 } from "node:os";
|
|
6
|
+
import * as path20 from "node:path";
|
|
7
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
8
8
|
|
|
9
9
|
// db.ts
|
|
10
10
|
import * as fsSync from "node:fs";
|
|
@@ -988,7 +988,7 @@ If you produce a followup whose residue (digits and parameter values stripped) m
|
|
|
988
988
|
|
|
989
989
|
### Guided mode — input surfaces
|
|
990
990
|
|
|
991
|
-
When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons
|
|
991
|
+
When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons.
|
|
992
992
|
|
|
993
993
|
**Flow:**
|
|
994
994
|
1. User clicks a button on a widget → \`[action:*]\`.
|
|
@@ -1581,6 +1581,82 @@ ${userSection}`;
|
|
|
1581
1581
|
}
|
|
1582
1582
|
|
|
1583
1583
|
// provisioning.ts
|
|
1584
|
+
function providerFromModel(model) {
|
|
1585
|
+
const slash = model.indexOf("/");
|
|
1586
|
+
if (slash <= 0)
|
|
1587
|
+
return null;
|
|
1588
|
+
return model.slice(0, slash).trim() || null;
|
|
1589
|
+
}
|
|
1590
|
+
function configuredProvidersFromConfig(cfg) {
|
|
1591
|
+
const profiles = cfg?.auth?.profiles;
|
|
1592
|
+
if (!profiles || typeof profiles !== "object")
|
|
1593
|
+
return [];
|
|
1594
|
+
const out = [];
|
|
1595
|
+
for (const entry of Object.values(profiles)) {
|
|
1596
|
+
const provider = entry?.provider;
|
|
1597
|
+
if (typeof provider === "string" && provider.trim() && !out.includes(provider.trim())) {
|
|
1598
|
+
out.push(provider.trim());
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return out;
|
|
1602
|
+
}
|
|
1603
|
+
function hasGatewayDefaultModel(cfg) {
|
|
1604
|
+
const modelCfg = cfg?.agents?.defaults?.model;
|
|
1605
|
+
if (!modelCfg || typeof modelCfg !== "object")
|
|
1606
|
+
return false;
|
|
1607
|
+
if (typeof modelCfg.primary === "string" && modelCfg.primary.trim())
|
|
1608
|
+
return true;
|
|
1609
|
+
return Array.isArray(modelCfg.fallbacks) && modelCfg.fallbacks.some((m) => typeof m === "string" && m.trim());
|
|
1610
|
+
}
|
|
1611
|
+
function parseJsonArrayFromStdout(stdout) {
|
|
1612
|
+
const raw = stdout.trim();
|
|
1613
|
+
const jsonStart = raw.indexOf("[");
|
|
1614
|
+
if (jsonStart < 0)
|
|
1615
|
+
return [];
|
|
1616
|
+
try {
|
|
1617
|
+
const parsed = JSON.parse(raw.slice(jsonStart));
|
|
1618
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
1619
|
+
} catch {
|
|
1620
|
+
return [];
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
async function resolveProvisionedAgentModel(cfg, currentList, runtime, log) {
|
|
1624
|
+
if (hasGatewayDefaultModel(cfg))
|
|
1625
|
+
return null;
|
|
1626
|
+
const configuredProviders = configuredProvidersFromConfig(cfg);
|
|
1627
|
+
if (configuredProviders.length === 0)
|
|
1628
|
+
return null;
|
|
1629
|
+
const configured = new Set(configuredProviders);
|
|
1630
|
+
const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
|
|
1631
|
+
for (const agent of currentList) {
|
|
1632
|
+
if (!agent || provisionedIds.has(agent.id))
|
|
1633
|
+
continue;
|
|
1634
|
+
const model = typeof agent.model === "string" ? agent.model.trim() : "";
|
|
1635
|
+
const provider = model ? providerFromModel(model) : null;
|
|
1636
|
+
if (model && provider && configured.has(provider)) {
|
|
1637
|
+
return model;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
const run = runtime.system?.runCommandWithTimeout;
|
|
1641
|
+
if (!run)
|
|
1642
|
+
return null;
|
|
1643
|
+
try {
|
|
1644
|
+
const result = await run(["openclaw", "infer", "model", "providers", "--json"], { timeoutMs: 60000 });
|
|
1645
|
+
if ((result?.code ?? 0) !== 0)
|
|
1646
|
+
return null;
|
|
1647
|
+
const catalog = parseJsonArrayFromStdout(result?.stdout ?? "");
|
|
1648
|
+
for (const provider of configuredProviders) {
|
|
1649
|
+
const entry = catalog.find((p) => p?.provider === provider);
|
|
1650
|
+
const defaults = Array.isArray(entry?.defaults) ? entry.defaults : [];
|
|
1651
|
+
const firstDefault = defaults.find((m) => typeof m === "string" && m.trim());
|
|
1652
|
+
if (firstDefault)
|
|
1653
|
+
return `${provider}/${firstDefault.trim()}`;
|
|
1654
|
+
}
|
|
1655
|
+
} catch (err) {
|
|
1656
|
+
log(`[agentlife] provisioned agent model discovery skipped: ${err?.message ?? err}`);
|
|
1657
|
+
}
|
|
1658
|
+
return null;
|
|
1659
|
+
}
|
|
1584
1660
|
var PROVISIONED_AGENTS = [
|
|
1585
1661
|
{
|
|
1586
1662
|
id: "agentlife",
|
|
@@ -1671,6 +1747,7 @@ function scheduleEnrichment(state, runtime, targets, log) {
|
|
|
1671
1747
|
async function provisionAgents(state, cfg, runtime, log) {
|
|
1672
1748
|
const home = os.homedir();
|
|
1673
1749
|
const currentList = [...cfg.agents?.list ?? []];
|
|
1750
|
+
const provisionedModel = await resolveProvisionedAgentModel(cfg, currentList, runtime, log);
|
|
1674
1751
|
let configChanged = false;
|
|
1675
1752
|
const newlyProvisioned = [];
|
|
1676
1753
|
for (const agent of PROVISIONED_AGENTS) {
|
|
@@ -1710,12 +1787,15 @@ async function provisionAgents(state, cfg, runtime, log) {
|
|
|
1710
1787
|
name: agent.name,
|
|
1711
1788
|
workspace: workspaceDir
|
|
1712
1789
|
};
|
|
1713
|
-
|
|
1790
|
+
const userOwnsDefault = currentList.some((a) => a?.default === true);
|
|
1791
|
+
if (agent.isDefault && !userOwnsDefault)
|
|
1714
1792
|
entry.default = true;
|
|
1715
1793
|
if (agent.subagents)
|
|
1716
1794
|
entry.subagents = agent.subagents;
|
|
1717
1795
|
if (agent.tools)
|
|
1718
1796
|
entry.tools = agent.tools;
|
|
1797
|
+
if (provisionedModel)
|
|
1798
|
+
entry.model = provisionedModel;
|
|
1719
1799
|
if (agent.isDefault) {
|
|
1720
1800
|
currentList.unshift(entry);
|
|
1721
1801
|
} else {
|
|
@@ -1741,16 +1821,32 @@ async function provisionAgents(state, cfg, runtime, log) {
|
|
|
1741
1821
|
configChanged = true;
|
|
1742
1822
|
log(`[agentlife] added subagents for ${agent.id}`);
|
|
1743
1823
|
}
|
|
1824
|
+
if (provisionedModel && !existing.model) {
|
|
1825
|
+
existing.model = provisionedModel;
|
|
1826
|
+
configChanged = true;
|
|
1827
|
+
log(`[agentlife] set model for ${agent.id}: ${provisionedModel}`);
|
|
1828
|
+
}
|
|
1829
|
+
if (agent.isDefault && existing.default === true) {
|
|
1830
|
+
const userOwnsDefault = currentList.some((a) => a?.id !== agent.id && a?.default === true);
|
|
1831
|
+
if (userOwnsDefault) {
|
|
1832
|
+
delete existing.default;
|
|
1833
|
+
configChanged = true;
|
|
1834
|
+
log(`[agentlife] surrendered default to user agent (was ${agent.id})`);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1744
1837
|
}
|
|
1745
1838
|
}
|
|
1746
1839
|
}
|
|
1747
1840
|
const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
|
|
1841
|
+
const backfilledSubagentIds = [];
|
|
1748
1842
|
for (const agent of currentList) {
|
|
1749
1843
|
if (provisionedIds.has(agent.id))
|
|
1750
1844
|
continue;
|
|
1751
1845
|
if (!agent.subagents) {
|
|
1752
1846
|
agent.subagents = { allowAgents: ["*"] };
|
|
1753
1847
|
configChanged = true;
|
|
1848
|
+
if (agent.id)
|
|
1849
|
+
backfilledSubagentIds.push(agent.id);
|
|
1754
1850
|
log(`[agentlife] backfilled subagents for ${agent.id}`);
|
|
1755
1851
|
}
|
|
1756
1852
|
}
|
|
@@ -1763,6 +1859,18 @@ async function provisionAgents(state, cfg, runtime, log) {
|
|
|
1763
1859
|
const configPath = path4.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
1764
1860
|
const rawCfgForVisibility = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
1765
1861
|
const backupPath = path4.join(os.homedir(), ".openclaw", "agentlife", "config-backup.json");
|
|
1862
|
+
if (backfilledSubagentIds.length > 0) {
|
|
1863
|
+
try {
|
|
1864
|
+
let backup = {};
|
|
1865
|
+
try {
|
|
1866
|
+
backup = JSON.parse(readFileSync2(backupPath, "utf-8"));
|
|
1867
|
+
} catch {}
|
|
1868
|
+
const existing = Array.isArray(backup.subagentsBackfilledIds) ? backup.subagentsBackfilledIds : [];
|
|
1869
|
+
backup.subagentsBackfilledIds = [...new Set([...existing, ...backfilledSubagentIds])];
|
|
1870
|
+
writeFileSync(backupPath, JSON.stringify(backup, null, 2) + `
|
|
1871
|
+
`, "utf-8");
|
|
1872
|
+
} catch {}
|
|
1873
|
+
}
|
|
1766
1874
|
let visibilityWritten = false;
|
|
1767
1875
|
const currentVisibility = rawCfgForVisibility?.tools?.sessions?.visibility;
|
|
1768
1876
|
if (currentVisibility !== "all") {
|
|
@@ -2085,7 +2193,6 @@ var KNOWN_BODY_KEYWORDS = new Set([
|
|
|
2085
2193
|
"button",
|
|
2086
2194
|
"image",
|
|
2087
2195
|
"icon",
|
|
2088
|
-
"textfield",
|
|
2089
2196
|
"checkbox",
|
|
2090
2197
|
"sparkline",
|
|
2091
2198
|
"progress",
|
|
@@ -2117,7 +2224,6 @@ var INPUT_SURFACE_ACTIONS = new Set([
|
|
|
2117
2224
|
var TEXT_LEVELS = new Set(["h1", "h2", "h3", "h4", "h5", "body", "caption"]);
|
|
2118
2225
|
var TEXT_HINTS = new Set(["icon", "avatar", "smallfeature", "mediumfeature", "largefeature", "header"]);
|
|
2119
2226
|
var IMG_FITS = new Set(["contain", "cover", "fill", "none", "scale-down"]);
|
|
2120
|
-
var TF_TYPES = new Set(["email", "shorttext", "number", "phone", "longtext", "date", "obscured"]);
|
|
2121
2227
|
var ALIGN_VALUES = new Set(["start", "center", "end", "stretch"]);
|
|
2122
2228
|
var DISTRIBUTE_VALUES = new Set(["start", "center", "end", "spacebetween", "spacearound", "spaceevenly"]);
|
|
2123
2229
|
var TREND_VALUES = new Set(["up", "down", "flat"]);
|
|
@@ -2172,14 +2278,6 @@ var COMPONENT_CATALOG = {
|
|
|
2172
2278
|
}
|
|
2173
2279
|
},
|
|
2174
2280
|
icon: { positionalTokens: new Set, positionalFree: true, attrs: {} },
|
|
2175
|
-
textfield: {
|
|
2176
|
-
positionalTokens: new Set,
|
|
2177
|
-
positionalFree: true,
|
|
2178
|
-
attrs: {
|
|
2179
|
-
type: { kind: "enum", values: TF_TYPES },
|
|
2180
|
-
placeholder: { kind: "free" }
|
|
2181
|
-
}
|
|
2182
|
-
},
|
|
2183
2281
|
checkbox: {
|
|
2184
2282
|
positionalTokens: new Set(["checked"]),
|
|
2185
2283
|
positionalFree: true,
|
|
@@ -3194,17 +3292,21 @@ function pollFollowups(state) {
|
|
|
3194
3292
|
}
|
|
3195
3293
|
sweepCounter++;
|
|
3196
3294
|
if (sweepCounter % 5 === 0 && state.surfaceIndex && state.followupQueue && state.fileSurfaceStorage) {
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3295
|
+
try {
|
|
3296
|
+
sweepExpiredSurfaces({
|
|
3297
|
+
index: state.surfaceIndex,
|
|
3298
|
+
queue: state.followupQueue,
|
|
3299
|
+
storage: state.fileSurfaceStorage
|
|
3300
|
+
}).then((r) => {
|
|
3301
|
+
for (const sid of r.purged)
|
|
3302
|
+
broadcastDelete(sid);
|
|
3303
|
+
if (r.purged.length > 0) {
|
|
3304
|
+
console.log("[agentlife:sweep] purged %d expired surfaces", r.purged.length);
|
|
3305
|
+
}
|
|
3306
|
+
}).catch((e) => console.warn("[agentlife:sweep] error: %s", e?.message));
|
|
3307
|
+
} catch (e) {
|
|
3308
|
+
console.warn("[agentlife] sweep invocation failed synchronously: %s", e?.message);
|
|
3309
|
+
}
|
|
3208
3310
|
}
|
|
3209
3311
|
}
|
|
3210
3312
|
|
|
@@ -3473,9 +3575,9 @@ Each turn:
|
|
|
3473
3575
|
2. Not enough → \`agentlife_push\` the SAME surfaceId \`welcome-input\` with:
|
|
3474
3576
|
- h3 question generated from what the user just said
|
|
3475
3577
|
- 2–4 multichoice buttons \`action=choice\`, options derived from the user's own words (NEVER hardcoded domain lists)
|
|
3476
|
-
- \`textfield placeholder="Something else…"\` escape hatch
|
|
3477
3578
|
- updated \`context: {"phase":"welcome","turn":N+1,"answers":[...]}\`
|
|
3478
3579
|
- goal + followup unchanged
|
|
3580
|
+
The input bar's text field is always-on, so the user can also type free text instead of picking a button — handle either response.
|
|
3479
3581
|
Respond \`done\`.
|
|
3480
3582
|
3. Enough → create the generalist and hand off the dashboard:
|
|
3481
3583
|
- Workspace under \`$HOME/.openclaw/workspace-{id}\` (id = slugified short name or "me")
|
|
@@ -3505,7 +3607,6 @@ function renderWelcomeWidget(state, log) {
|
|
|
3505
3607
|
` column`,
|
|
3506
3608
|
` text "What do you want to track or improve?" h3`,
|
|
3507
3609
|
` text "One line. Anything — meals, money, a project, a habit." body`,
|
|
3508
|
-
` textfield placeholder="Type anything…"`,
|
|
3509
3610
|
`goal: Onboard the user to their first agent via a guided interview`,
|
|
3510
3611
|
`followup: +30m "If unanswered, replace the question with a gentler prompt."`,
|
|
3511
3612
|
`context: {"phase":"welcome","turn":1,"answers":[]}`
|
|
@@ -3533,7 +3634,7 @@ function deleteWelcomeWidget(state, log) {
|
|
|
3533
3634
|
}
|
|
3534
3635
|
|
|
3535
3636
|
// services/surfaces-init.ts
|
|
3536
|
-
import * as
|
|
3637
|
+
import * as path6 from "node:path";
|
|
3537
3638
|
|
|
3538
3639
|
// storage/reconciler.ts
|
|
3539
3640
|
var defaultLogger = {
|
|
@@ -3781,23 +3882,45 @@ function dispatchRehydrate(state, runtime, log = console.log) {
|
|
|
3781
3882
|
log(`[agentlife:rehydrate] dispatched rehydrate to ${orphaned.length} orphaned agent(s): ${orphaned.join(", ")}`);
|
|
3782
3883
|
}
|
|
3783
3884
|
|
|
3885
|
+
// runtime-mode.ts
|
|
3886
|
+
import * as path5 from "node:path";
|
|
3887
|
+
function isPluginInstallOrInspectProcess() {
|
|
3888
|
+
const argv = process.argv.join(" ");
|
|
3889
|
+
const entrypoint = path5.basename(process.argv[1] || "");
|
|
3890
|
+
return entrypoint.includes("openclaw-plugins") || argv.includes("plugins") && argv.includes("install") || argv.includes("plugins") && argv.includes("inspect");
|
|
3891
|
+
}
|
|
3892
|
+
function shouldStartRuntimeServices() {
|
|
3893
|
+
return !isPluginInstallOrInspectProcess();
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3784
3896
|
// services/surfaces-init.ts
|
|
3785
3897
|
function registerSurfacesService(api, state) {
|
|
3786
3898
|
api.registerService({
|
|
3787
3899
|
id: "agentlife-surfaces",
|
|
3788
3900
|
start: async (ctx) => {
|
|
3789
|
-
|
|
3901
|
+
if (!shouldStartRuntimeServices())
|
|
3902
|
+
return;
|
|
3903
|
+
const agentlifeDir = path6.join(ctx.stateDir, "agentlife");
|
|
3904
|
+
const nextHistoryDbPath = path6.join(agentlifeDir, "agentlife.db");
|
|
3905
|
+
const previousHistoryDbPath = state.historyDbPath;
|
|
3906
|
+
if (state.historyDb && previousHistoryDbPath && previousHistoryDbPath !== nextHistoryDbPath) {
|
|
3907
|
+
try {
|
|
3908
|
+
state.historyDb.close();
|
|
3909
|
+
} catch {}
|
|
3910
|
+
state.historyDb = null;
|
|
3911
|
+
state.surfaceIndex = null;
|
|
3912
|
+
state.followupQueue = null;
|
|
3913
|
+
}
|
|
3790
3914
|
state.agentlifeStateDir = agentlifeDir;
|
|
3791
|
-
state.registryFilePath =
|
|
3792
|
-
state.dbBaseDir =
|
|
3793
|
-
state.historyDbPath =
|
|
3915
|
+
state.registryFilePath = path6.join(agentlifeDir, "agent-registry.json");
|
|
3916
|
+
state.dbBaseDir = path6.join(agentlifeDir, "db");
|
|
3917
|
+
state.historyDbPath = nextHistoryDbPath;
|
|
3794
3918
|
const db = getOrCreateHistoryDb(state);
|
|
3795
3919
|
if (!state.surfaceIndex)
|
|
3796
3920
|
state.surfaceIndex = new SurfaceIndex(db);
|
|
3797
3921
|
if (!state.followupQueue)
|
|
3798
3922
|
state.followupQueue = new FollowupQueue(db);
|
|
3799
|
-
|
|
3800
|
-
state.fileSurfaceStorage = new FileSurfaceStorage;
|
|
3923
|
+
state.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: ctx.stateDir });
|
|
3801
3924
|
await loadRegistryFromDisk(state);
|
|
3802
3925
|
try {
|
|
3803
3926
|
const knownAgents = collectKnownAgents(api.runtime);
|
|
@@ -3847,11 +3970,11 @@ function collectKnownAgents(runtime) {
|
|
|
3847
3970
|
|
|
3848
3971
|
// services/config-optimizer.ts
|
|
3849
3972
|
import * as fs5 from "node:fs/promises";
|
|
3850
|
-
import * as
|
|
3973
|
+
import * as path7 from "node:path";
|
|
3851
3974
|
import * as os2 from "node:os";
|
|
3852
3975
|
var TARGET_MAX_PING_PONG = 1;
|
|
3853
3976
|
var TARGET_MAX_CONCURRENT = 10;
|
|
3854
|
-
var CONFIG_PATH =
|
|
3977
|
+
var CONFIG_PATH = process.env.OPENCLAW_CONFIG_PATH || path7.join(os2.homedir(), ".openclaw", "openclaw.json");
|
|
3855
3978
|
async function readRawConfigFromDisk() {
|
|
3856
3979
|
const raw = await fs5.readFile(CONFIG_PATH, "utf-8");
|
|
3857
3980
|
return JSON.parse(raw);
|
|
@@ -3879,7 +4002,6 @@ async function restoreConfigFromBackup(api, backupPath) {
|
|
|
3879
4002
|
delete fileCfg.agents?.defaults?.maxConcurrent;
|
|
3880
4003
|
}
|
|
3881
4004
|
await writeRawConfigToDisk(fileCfg);
|
|
3882
|
-
await fs5.unlink(backupPath);
|
|
3883
4005
|
console.log("[agentlife] restored config: maxPingPongTurns=%s, maxConcurrent=%s", backup.maxPingPongTurns ?? "default", backup.maxConcurrent ?? "default");
|
|
3884
4006
|
return `config restored`;
|
|
3885
4007
|
}
|
|
@@ -3887,8 +4009,10 @@ function registerConfigOptimizer(api, _state) {
|
|
|
3887
4009
|
api.registerService({
|
|
3888
4010
|
id: "agentlife-config-optimizer",
|
|
3889
4011
|
start: async (ctx) => {
|
|
3890
|
-
|
|
3891
|
-
|
|
4012
|
+
if (!shouldStartRuntimeServices())
|
|
4013
|
+
return;
|
|
4014
|
+
const configBackupDir = path7.join(ctx.stateDir, "agentlife");
|
|
4015
|
+
const configBackupPath = path7.join(configBackupDir, "config-backup.json");
|
|
3892
4016
|
const cfg = api.runtime.config.loadConfig();
|
|
3893
4017
|
const currentPPT = cfg.session?.agentToAgent?.maxPingPongTurns;
|
|
3894
4018
|
const currentMC = cfg.agents?.defaults?.maxConcurrent;
|
|
@@ -3899,10 +4023,16 @@ function registerConfigOptimizer(api, _state) {
|
|
|
3899
4023
|
return;
|
|
3900
4024
|
}
|
|
3901
4025
|
await fs5.mkdir(configBackupDir, { recursive: true });
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
}
|
|
4026
|
+
let existing = {};
|
|
4027
|
+
try {
|
|
4028
|
+
existing = JSON.parse(await fs5.readFile(configBackupPath, "utf-8"));
|
|
4029
|
+
} catch {}
|
|
4030
|
+
if (!("maxPingPongTurns" in existing))
|
|
4031
|
+
existing.maxPingPongTurns = currentPPT ?? null;
|
|
4032
|
+
if (!("maxConcurrent" in existing))
|
|
4033
|
+
existing.maxConcurrent = currentMC ?? null;
|
|
4034
|
+
await fs5.writeFile(configBackupPath, JSON.stringify(existing, null, 2) + `
|
|
4035
|
+
`);
|
|
3906
4036
|
const fileCfg = await readRawConfigFromDisk();
|
|
3907
4037
|
if (needsPPT) {
|
|
3908
4038
|
fileCfg.session ??= {};
|
|
@@ -3924,13 +4054,196 @@ function registerConfigOptimizer(api, _state) {
|
|
|
3924
4054
|
},
|
|
3925
4055
|
stop: async (ctx) => {
|
|
3926
4056
|
try {
|
|
3927
|
-
const backupPath =
|
|
4057
|
+
const backupPath = path7.join(ctx.stateDir, "agentlife", "config-backup.json");
|
|
3928
4058
|
await restoreConfigFromBackup(api, backupPath);
|
|
3929
4059
|
} catch {}
|
|
3930
4060
|
}
|
|
3931
4061
|
});
|
|
3932
4062
|
}
|
|
3933
4063
|
|
|
4064
|
+
// services/install-bootstrap.ts
|
|
4065
|
+
import * as fs6 from "node:fs";
|
|
4066
|
+
import * as path8 from "node:path";
|
|
4067
|
+
import * as os3 from "node:os";
|
|
4068
|
+
import { upsertAuthProfile } from "openclaw/plugin-sdk/provider-auth";
|
|
4069
|
+
function resolvePaths(home) {
|
|
4070
|
+
const openclawDir = home ? path8.join(home, ".openclaw") : process.env.OPENCLAW_STATE_DIR || path8.join(os3.homedir(), ".openclaw");
|
|
4071
|
+
const configPath = home ? path8.join(openclawDir, "openclaw.json") : process.env.OPENCLAW_CONFIG_PATH || path8.join(openclawDir, "openclaw.json");
|
|
4072
|
+
const agentlifeDir = path8.join(openclawDir, "agentlife");
|
|
4073
|
+
return {
|
|
4074
|
+
configPath,
|
|
4075
|
+
gatewayTokenFile: path8.join(openclawDir, "gateway-token.txt"),
|
|
4076
|
+
agentlifeDir,
|
|
4077
|
+
configBackupPath: path8.join(agentlifeDir, "config-backup.json"),
|
|
4078
|
+
pairRequestMarker: path8.join(agentlifeDir, "pair-requested")
|
|
4079
|
+
};
|
|
4080
|
+
}
|
|
4081
|
+
function readJsonSafe(p) {
|
|
4082
|
+
try {
|
|
4083
|
+
return JSON.parse(fs6.readFileSync(p, "utf-8"));
|
|
4084
|
+
} catch {
|
|
4085
|
+
return null;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
function writeJson(p, obj) {
|
|
4089
|
+
fs6.writeFileSync(p, JSON.stringify(obj, null, 2) + `
|
|
4090
|
+
`, "utf-8");
|
|
4091
|
+
}
|
|
4092
|
+
function normalizeLegacyCredential(profile) {
|
|
4093
|
+
if (!profile || typeof profile !== "object")
|
|
4094
|
+
return null;
|
|
4095
|
+
if (typeof profile.provider !== "string" || !profile.provider.trim())
|
|
4096
|
+
return null;
|
|
4097
|
+
const credential = { ...profile };
|
|
4098
|
+
if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
|
|
4099
|
+
credential.key = credential.apiKey;
|
|
4100
|
+
delete credential.apiKey;
|
|
4101
|
+
}
|
|
4102
|
+
return credential;
|
|
4103
|
+
}
|
|
4104
|
+
function migrateLegacyAuthProfiles(openclawDir) {
|
|
4105
|
+
const agentsRoot = path8.join(openclawDir, "agents");
|
|
4106
|
+
let entries;
|
|
4107
|
+
try {
|
|
4108
|
+
entries = fs6.readdirSync(agentsRoot, { withFileTypes: true });
|
|
4109
|
+
} catch {
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
let migrated = 0;
|
|
4113
|
+
for (const entry of entries) {
|
|
4114
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
4115
|
+
continue;
|
|
4116
|
+
const agentDir = path8.join(agentsRoot, entry.name, "agent");
|
|
4117
|
+
const legacyPath = path8.join(agentDir, "auth-profiles.json");
|
|
4118
|
+
const legacy = readJsonSafe(legacyPath);
|
|
4119
|
+
const profiles = legacy?.profiles;
|
|
4120
|
+
if (!profiles || typeof profiles !== "object")
|
|
4121
|
+
continue;
|
|
4122
|
+
for (const [profileId, profile] of Object.entries(profiles)) {
|
|
4123
|
+
const credential = normalizeLegacyCredential(profile);
|
|
4124
|
+
if (!credential)
|
|
4125
|
+
continue;
|
|
4126
|
+
try {
|
|
4127
|
+
upsertAuthProfile({ profileId, credential, agentDir });
|
|
4128
|
+
migrated += 1;
|
|
4129
|
+
} catch (err) {
|
|
4130
|
+
console.warn(`[agentlife] install-bootstrap: auth profile migration skipped for ${entry.name}/${profileId}: ${err?.message ?? err}`);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
if (migrated > 0) {
|
|
4135
|
+
console.log(`[agentlife] install-bootstrap: migrated ${migrated} legacy auth profile(s) into OpenClaw auth store`);
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
function runInstallBootstrap(home) {
|
|
4139
|
+
const P = resolvePaths(home);
|
|
4140
|
+
try {
|
|
4141
|
+
fs6.mkdirSync(P.agentlifeDir, { recursive: true, mode: 448 });
|
|
4142
|
+
} catch (err) {
|
|
4143
|
+
console.warn(`[agentlife] install-bootstrap: cannot create ${P.agentlifeDir}: ${err?.message ?? err}`);
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
try {
|
|
4147
|
+
fs6.writeFileSync(P.pairRequestMarker, String(Date.now()), { mode: 384 });
|
|
4148
|
+
} catch (err) {
|
|
4149
|
+
console.warn(`[agentlife] install-bootstrap: cannot write pair-requested marker: ${err?.message ?? err}`);
|
|
4150
|
+
}
|
|
4151
|
+
migrateLegacyAuthProfiles(path8.dirname(P.gatewayTokenFile));
|
|
4152
|
+
if (!fs6.existsSync(P.configPath)) {
|
|
4153
|
+
console.log("[agentlife] install-bootstrap: gateway config missing — deferring config writes");
|
|
4154
|
+
return;
|
|
4155
|
+
}
|
|
4156
|
+
const cfg = readJsonSafe(P.configPath);
|
|
4157
|
+
if (!cfg || typeof cfg !== "object") {
|
|
4158
|
+
console.warn(`[agentlife] install-bootstrap: cannot parse ${P.configPath} — skipping config writes`);
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
const existingBackup = readJsonSafe(P.configBackupPath) ?? {};
|
|
4162
|
+
const originals = [
|
|
4163
|
+
["sessionsVisibility", cfg?.tools?.sessions?.visibility ?? null],
|
|
4164
|
+
["agentToAgentEnabled", cfg?.tools?.agentToAgent?.enabled ?? null],
|
|
4165
|
+
["gatewayBind", cfg?.gateway?.bind ?? null],
|
|
4166
|
+
["hooksAllowConversationAccess", cfg?.plugins?.entries?.agentlife?.hooks?.allowConversationAccess ?? null],
|
|
4167
|
+
["maxPingPongTurns", cfg?.session?.agentToAgent?.maxPingPongTurns ?? null],
|
|
4168
|
+
["maxConcurrent", cfg?.agents?.defaults?.maxConcurrent ?? null]
|
|
4169
|
+
];
|
|
4170
|
+
let backupChanged = false;
|
|
4171
|
+
for (const [k, orig] of originals) {
|
|
4172
|
+
if (!(k in existingBackup)) {
|
|
4173
|
+
existingBackup[k] = orig;
|
|
4174
|
+
backupChanged = true;
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
if (backupChanged) {
|
|
4178
|
+
try {
|
|
4179
|
+
writeJson(P.configBackupPath, existingBackup);
|
|
4180
|
+
} catch (err) {
|
|
4181
|
+
console.warn(`[agentlife] install-bootstrap: cannot write backup: ${err?.message ?? err}`);
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
let changed = false;
|
|
4185
|
+
cfg.gateway ??= {};
|
|
4186
|
+
if (cfg.gateway.bind !== "lan") {
|
|
4187
|
+
cfg.gateway.bind = "lan";
|
|
4188
|
+
changed = true;
|
|
4189
|
+
}
|
|
4190
|
+
cfg.gateway.auth ??= {};
|
|
4191
|
+
if (!cfg.gateway.auth.token && fs6.existsSync(P.gatewayTokenFile)) {
|
|
4192
|
+
try {
|
|
4193
|
+
const tok = fs6.readFileSync(P.gatewayTokenFile, "utf-8").trim();
|
|
4194
|
+
if (tok) {
|
|
4195
|
+
cfg.gateway.auth.token = tok;
|
|
4196
|
+
changed = true;
|
|
4197
|
+
console.log(`[agentlife] install-bootstrap: imported gateway token from ${P.gatewayTokenFile}`);
|
|
4198
|
+
}
|
|
4199
|
+
} catch (err) {
|
|
4200
|
+
console.warn(`[agentlife] install-bootstrap: cannot read ${P.gatewayTokenFile}: ${err?.message ?? err}`);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
cfg.tools ??= {};
|
|
4204
|
+
cfg.tools.sessions ??= {};
|
|
4205
|
+
if (cfg.tools.sessions.visibility !== "all") {
|
|
4206
|
+
cfg.tools.sessions.visibility = "all";
|
|
4207
|
+
changed = true;
|
|
4208
|
+
}
|
|
4209
|
+
cfg.tools.agentToAgent ??= {};
|
|
4210
|
+
if (cfg.tools.agentToAgent.enabled !== true) {
|
|
4211
|
+
cfg.tools.agentToAgent.enabled = true;
|
|
4212
|
+
changed = true;
|
|
4213
|
+
}
|
|
4214
|
+
cfg.plugins ??= {};
|
|
4215
|
+
cfg.plugins.entries ??= {};
|
|
4216
|
+
cfg.plugins.entries.agentlife ??= {};
|
|
4217
|
+
cfg.plugins.entries.agentlife.hooks ??= {};
|
|
4218
|
+
if (cfg.plugins.entries.agentlife.hooks.allowConversationAccess !== true) {
|
|
4219
|
+
cfg.plugins.entries.agentlife.hooks.allowConversationAccess = true;
|
|
4220
|
+
changed = true;
|
|
4221
|
+
}
|
|
4222
|
+
cfg.session ??= {};
|
|
4223
|
+
cfg.session.agentToAgent ??= {};
|
|
4224
|
+
if (cfg.session.agentToAgent.maxPingPongTurns !== 1) {
|
|
4225
|
+
cfg.session.agentToAgent.maxPingPongTurns = 1;
|
|
4226
|
+
changed = true;
|
|
4227
|
+
}
|
|
4228
|
+
cfg.agents ??= {};
|
|
4229
|
+
cfg.agents.defaults ??= {};
|
|
4230
|
+
if (!cfg.agents.defaults.maxConcurrent || cfg.agents.defaults.maxConcurrent < 10) {
|
|
4231
|
+
cfg.agents.defaults.maxConcurrent = 10;
|
|
4232
|
+
changed = true;
|
|
4233
|
+
}
|
|
4234
|
+
if (!changed) {
|
|
4235
|
+
console.log("[agentlife] install-bootstrap: config already normalized");
|
|
4236
|
+
return;
|
|
4237
|
+
}
|
|
4238
|
+
try {
|
|
4239
|
+
writeJson(P.configPath, cfg);
|
|
4240
|
+
console.log("[agentlife] install-bootstrap: gateway config normalized (bind=lan, sessions.visibility=all, agentToAgent.enabled=true, hooks.allowConversationAccess=true, maxPingPongTurns=1, maxConcurrent=10)");
|
|
4241
|
+
console.log("[agentlife] install-bootstrap: changes take effect on next `openclaw daemon restart`");
|
|
4242
|
+
} catch (err) {
|
|
4243
|
+
console.warn(`[agentlife] install-bootstrap: cannot write ${P.configPath}: ${err?.message ?? err}`);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
|
|
3934
4247
|
// services/session-reconcile.ts
|
|
3935
4248
|
function modelMatches(configured, sessionProvider, sessionModel) {
|
|
3936
4249
|
if (!sessionModel)
|
|
@@ -4093,7 +4406,7 @@ async function reconcileInternalSessions(state, createdInternalSessions) {
|
|
|
4093
4406
|
}
|
|
4094
4407
|
|
|
4095
4408
|
// services/supervisor-cleanup.ts
|
|
4096
|
-
import * as
|
|
4409
|
+
import * as fs7 from "node:fs";
|
|
4097
4410
|
var STALE_SUPERVISOR_SURFACE = "supervisor-daily";
|
|
4098
4411
|
var STALE_SUPERVISOR_DIRECT_SESSION = "agent:supervisor:agentlife:direct:operator";
|
|
4099
4412
|
async function cleanupSupervisorLegacyState(state) {
|
|
@@ -4106,7 +4419,7 @@ async function cleanupSupervisorLegacyState(state) {
|
|
|
4106
4419
|
removeFollowup(state, STALE_SUPERVISOR_SURFACE);
|
|
4107
4420
|
if (view2 && !view2.isTransient && view2.path) {
|
|
4108
4421
|
try {
|
|
4109
|
-
|
|
4422
|
+
fs7.unlinkSync(view2.path);
|
|
4110
4423
|
} catch {}
|
|
4111
4424
|
}
|
|
4112
4425
|
state.surfaceIndex?.delete(STALE_SUPERVISOR_SURFACE);
|
|
@@ -4288,6 +4601,8 @@ function startObservabilityService(state) {
|
|
|
4288
4601
|
|
|
4289
4602
|
// hooks/activity-hooks.ts
|
|
4290
4603
|
import * as fsSync2 from "node:fs";
|
|
4604
|
+
import * as nodePath from "node:path";
|
|
4605
|
+
import * as nodeOs from "node:os";
|
|
4291
4606
|
|
|
4292
4607
|
// usage.ts
|
|
4293
4608
|
var MODEL_COSTS = {
|
|
@@ -4936,6 +5251,62 @@ ${parts.join(`
|
|
|
4936
5251
|
}
|
|
4937
5252
|
var QUALITY_CHECK_MAX_WAIT_MS = 5000;
|
|
4938
5253
|
var TERMINAL_MARKERS = new Set(["NO_REPLY", "ANNOUNCE_SKIP", "REPLY_SKIP", "HEARTBEAT_OK"]);
|
|
5254
|
+
function resolveOpenClawRoot() {
|
|
5255
|
+
const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
5256
|
+
return fromEnv ? nodePath.resolve(fromEnv) : nodePath.join(nodeOs.homedir(), ".openclaw");
|
|
5257
|
+
}
|
|
5258
|
+
function findSessionModelError(agentId, sessionKey) {
|
|
5259
|
+
const sessionsDir = nodePath.join(resolveOpenClawRoot(), "agents", agentId, "sessions");
|
|
5260
|
+
const indexPath = nodePath.join(sessionsDir, "sessions.json");
|
|
5261
|
+
let sessionFile = null;
|
|
5262
|
+
try {
|
|
5263
|
+
const index = JSON.parse(fsSync2.readFileSync(indexPath, "utf-8"));
|
|
5264
|
+
const entry = index?.[sessionKey];
|
|
5265
|
+
if (typeof entry?.sessionFile === "string") {
|
|
5266
|
+
sessionFile = entry.sessionFile;
|
|
5267
|
+
} else if (typeof entry?.sessionId === "string") {
|
|
5268
|
+
sessionFile = nodePath.join(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
5269
|
+
}
|
|
5270
|
+
} catch {
|
|
5271
|
+
return null;
|
|
5272
|
+
}
|
|
5273
|
+
if (!sessionFile)
|
|
5274
|
+
return null;
|
|
5275
|
+
let provider;
|
|
5276
|
+
let model;
|
|
5277
|
+
let lines;
|
|
5278
|
+
try {
|
|
5279
|
+
lines = fsSync2.readFileSync(sessionFile, "utf-8").split(`
|
|
5280
|
+
`).filter(Boolean);
|
|
5281
|
+
} catch {
|
|
5282
|
+
return null;
|
|
5283
|
+
}
|
|
5284
|
+
for (const line of lines) {
|
|
5285
|
+
try {
|
|
5286
|
+
const entry = JSON.parse(line);
|
|
5287
|
+
if (entry?.type === "model_change") {
|
|
5288
|
+
provider = typeof entry.provider === "string" ? entry.provider : provider;
|
|
5289
|
+
model = typeof entry.modelId === "string" ? entry.modelId : model;
|
|
5290
|
+
}
|
|
5291
|
+
} catch {}
|
|
5292
|
+
}
|
|
5293
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
5294
|
+
try {
|
|
5295
|
+
const entry = JSON.parse(lines[i]);
|
|
5296
|
+
const msg = entry?.message;
|
|
5297
|
+
if (entry?.type !== "message" || msg?.role !== "assistant")
|
|
5298
|
+
continue;
|
|
5299
|
+
if (typeof msg.provider === "string")
|
|
5300
|
+
provider = msg.provider;
|
|
5301
|
+
if (typeof msg.model === "string")
|
|
5302
|
+
model = msg.model;
|
|
5303
|
+
if (typeof msg.errorMessage === "string" && msg.errorMessage.trim()) {
|
|
5304
|
+
return { error: msg.errorMessage, provider, model };
|
|
5305
|
+
}
|
|
5306
|
+
} catch {}
|
|
5307
|
+
}
|
|
5308
|
+
return null;
|
|
5309
|
+
}
|
|
4939
5310
|
function processPendingQualityChecks(state) {
|
|
4940
5311
|
if (state.disabled || !state.historyDb)
|
|
4941
5312
|
return 0;
|
|
@@ -4977,6 +5348,21 @@ function processPendingQualityChecks(state) {
|
|
|
4977
5348
|
const pushed = db.prepare("SELECT COUNT(*) as c FROM surface_events WHERE event IN ('created','updated') AND createdAt > ? AND (agentId = ? OR agentId IS NULL)").get(row.runStart, row.agentId);
|
|
4978
5349
|
if ((pushed?.c ?? 0) > 0)
|
|
4979
5350
|
continue;
|
|
5351
|
+
const modelError = findSessionModelError(row.agentId, row.sessionKey);
|
|
5352
|
+
if (modelError) {
|
|
5353
|
+
recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
|
|
5354
|
+
runId: row.runId,
|
|
5355
|
+
data: JSON.stringify({
|
|
5356
|
+
issue: "model_error_no_widget",
|
|
5357
|
+
trigger: row.trigger,
|
|
5358
|
+
error: modelError.error,
|
|
5359
|
+
provider: modelError.provider,
|
|
5360
|
+
model: modelError.model
|
|
5361
|
+
})
|
|
5362
|
+
});
|
|
5363
|
+
console.warn("[agentlife:quality] %s model error before widget push: %s", row.agentId, modelError.error);
|
|
5364
|
+
continue;
|
|
5365
|
+
}
|
|
4980
5366
|
recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
|
|
4981
5367
|
runId: row.runId,
|
|
4982
5368
|
data: JSON.stringify({ issue: "no_widget_pushed", trigger: row.trigger })
|
|
@@ -5075,7 +5461,7 @@ function rescueOrphanedSurfaces(state, agentId, error) {
|
|
|
5075
5461
|
}
|
|
5076
5462
|
|
|
5077
5463
|
// services/escalation.ts
|
|
5078
|
-
import * as
|
|
5464
|
+
import * as fs8 from "node:fs";
|
|
5079
5465
|
var PATHOLOGY2 = process.env.PATHOLOGY_TEST_MODE === "1";
|
|
5080
5466
|
var ESCALATION_REVIEW_THRESHOLD_MS = PATHOLOGY2 ? 2 * 60 * 1000 : 48 * 60 * 60 * 1000;
|
|
5081
5467
|
var TREND_WINDOW_DAYS = 7;
|
|
@@ -5141,7 +5527,7 @@ function clearEscalationSurface(state, agentId) {
|
|
|
5141
5527
|
const view2 = getSurface(state, surfaceId);
|
|
5142
5528
|
if (view2 && !view2.isTransient && view2.path) {
|
|
5143
5529
|
try {
|
|
5144
|
-
|
|
5530
|
+
fs8.unlinkSync(view2.path);
|
|
5145
5531
|
} catch {}
|
|
5146
5532
|
}
|
|
5147
5533
|
state.surfaceIndex.delete(surfaceId);
|
|
@@ -5385,31 +5771,32 @@ function startDailySweepService(state) {
|
|
|
5385
5771
|
import * as crypto4 from "node:crypto";
|
|
5386
5772
|
import * as fsSync3 from "node:fs";
|
|
5387
5773
|
import { createRequire as createRequire3 } from "node:module";
|
|
5388
|
-
import * as
|
|
5389
|
-
import * as
|
|
5774
|
+
import * as os7 from "node:os";
|
|
5775
|
+
import * as path12 from "node:path";
|
|
5390
5776
|
|
|
5391
5777
|
// gateway/web-app.ts
|
|
5392
5778
|
import * as crypto3 from "node:crypto";
|
|
5393
|
-
import * as
|
|
5394
|
-
import * as
|
|
5395
|
-
import * as
|
|
5779
|
+
import * as os6 from "node:os";
|
|
5780
|
+
import * as path11 from "node:path";
|
|
5781
|
+
import * as fs11 from "node:fs";
|
|
5396
5782
|
|
|
5397
5783
|
// services/pairing-access-token.ts
|
|
5398
5784
|
import * as crypto from "node:crypto";
|
|
5399
|
-
import * as
|
|
5400
|
-
import * as
|
|
5401
|
-
import * as
|
|
5785
|
+
import * as fs9 from "node:fs";
|
|
5786
|
+
import * as os4 from "node:os";
|
|
5787
|
+
import * as path9 from "node:path";
|
|
5402
5788
|
var cachedToken = null;
|
|
5403
5789
|
function pairingAccessPath() {
|
|
5404
|
-
|
|
5790
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path9.join(os4.homedir(), ".openclaw");
|
|
5791
|
+
return path9.join(stateDir, "agentlife", "pairing-access.json");
|
|
5405
5792
|
}
|
|
5406
5793
|
function loadOrCreatePairingAccessToken() {
|
|
5407
5794
|
if (cachedToken)
|
|
5408
5795
|
return cachedToken;
|
|
5409
5796
|
const filePath = pairingAccessPath();
|
|
5410
5797
|
try {
|
|
5411
|
-
if (
|
|
5412
|
-
const raw =
|
|
5798
|
+
if (fs9.existsSync(filePath)) {
|
|
5799
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
5413
5800
|
const obj = JSON.parse(raw);
|
|
5414
5801
|
const token2 = typeof obj?.token === "string" ? obj.token : null;
|
|
5415
5802
|
if (token2 && token2.length >= 32) {
|
|
@@ -5418,12 +5805,12 @@ function loadOrCreatePairingAccessToken() {
|
|
|
5418
5805
|
}
|
|
5419
5806
|
}
|
|
5420
5807
|
const token = crypto.randomBytes(32).toString("base64url");
|
|
5421
|
-
const dir =
|
|
5422
|
-
if (!
|
|
5423
|
-
|
|
5424
|
-
|
|
5808
|
+
const dir = path9.dirname(filePath);
|
|
5809
|
+
if (!fs9.existsSync(dir))
|
|
5810
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5811
|
+
fs9.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
|
|
5425
5812
|
try {
|
|
5426
|
-
|
|
5813
|
+
fs9.chmodSync(filePath, 384);
|
|
5427
5814
|
} catch {}
|
|
5428
5815
|
cachedToken = token;
|
|
5429
5816
|
return token;
|
|
@@ -5436,25 +5823,28 @@ function loadOrCreatePairingAccessToken() {
|
|
|
5436
5823
|
// services/cloudflared-supervisor.ts
|
|
5437
5824
|
import { spawn, spawnSync } from "node:child_process";
|
|
5438
5825
|
import * as crypto2 from "node:crypto";
|
|
5439
|
-
import * as
|
|
5440
|
-
import * as
|
|
5441
|
-
import * as
|
|
5442
|
-
var
|
|
5443
|
-
var
|
|
5444
|
-
var
|
|
5445
|
-
var
|
|
5446
|
-
var
|
|
5447
|
-
var
|
|
5448
|
-
var
|
|
5449
|
-
var
|
|
5450
|
-
var
|
|
5826
|
+
import * as fs10 from "node:fs";
|
|
5827
|
+
import * as os5 from "node:os";
|
|
5828
|
+
import * as path10 from "node:path";
|
|
5829
|
+
var DEFAULT_OPENCLAW_STATE_DIR = path10.join(os5.homedir(), ".openclaw");
|
|
5830
|
+
var OPENCLAW_STATE_DIR = process.env.OPENCLAW_STATE_DIR || DEFAULT_OPENCLAW_STATE_DIR;
|
|
5831
|
+
var IS_PROFILE_STATE_DIR = OPENCLAW_STATE_DIR !== DEFAULT_OPENCLAW_STATE_DIR;
|
|
5832
|
+
var AGENTLIFE_DIR = path10.join(OPENCLAW_STATE_DIR, "agentlife");
|
|
5833
|
+
var TUNNEL_FILE = path10.join(AGENTLIFE_DIR, "tunnel.json");
|
|
5834
|
+
var BIN_DIR = path10.join(AGENTLIFE_DIR, "bin");
|
|
5835
|
+
var PAIR_REQUEST_MARKER = path10.join(AGENTLIFE_DIR, "pair-requested");
|
|
5836
|
+
var IDENTITY_DIR = IS_PROFILE_STATE_DIR ? path10.join(OPENCLAW_STATE_DIR, ".agentlife") : path10.join(os5.homedir(), ".agentlife");
|
|
5837
|
+
var DEVICE_FILE = path10.join(IDENTITY_DIR, "device.json");
|
|
5838
|
+
var LEGACY_DEVICE_FILE = path10.join(AGENTLIFE_DIR, "device.json");
|
|
5839
|
+
var CLOUDFLARED_BIN = os5.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
5840
|
+
var CLOUDFLARED_PATH = path10.join(BIN_DIR, CLOUDFLARED_BIN);
|
|
5451
5841
|
var API_BASE = process.env.AGENTLIFE_API_BASE || "https://api.agentlife.app";
|
|
5452
5842
|
var RESTART_DELAY_MS = 5000;
|
|
5453
5843
|
var STABLE_RUNTIME_MS = 60000;
|
|
5454
5844
|
function createInitialState() {
|
|
5455
5845
|
let resolver;
|
|
5456
|
-
const promise = new Promise((
|
|
5457
|
-
resolver =
|
|
5846
|
+
const promise = new Promise((resolve2) => {
|
|
5847
|
+
resolver = resolve2;
|
|
5458
5848
|
});
|
|
5459
5849
|
return {
|
|
5460
5850
|
tunnelInfo: null,
|
|
@@ -5475,7 +5865,7 @@ function getTunnelInfo() {
|
|
|
5475
5865
|
function awaitTunnelReady(timeoutMs) {
|
|
5476
5866
|
return Promise.race([
|
|
5477
5867
|
state.readyPromise,
|
|
5478
|
-
new Promise((
|
|
5868
|
+
new Promise((resolve2) => setTimeout(() => resolve2(null), timeoutMs))
|
|
5479
5869
|
]);
|
|
5480
5870
|
}
|
|
5481
5871
|
function startCloudflaredSupervisor() {
|
|
@@ -5567,20 +5957,20 @@ async function doBootstrap() {
|
|
|
5567
5957
|
return tunnelInfo;
|
|
5568
5958
|
}
|
|
5569
5959
|
function isPairRequested() {
|
|
5570
|
-
return
|
|
5960
|
+
return fs10.existsSync(PAIR_REQUEST_MARKER);
|
|
5571
5961
|
}
|
|
5572
5962
|
function requestPair() {
|
|
5573
5963
|
try {
|
|
5574
|
-
if (!
|
|
5575
|
-
|
|
5576
|
-
|
|
5964
|
+
if (!fs10.existsSync(AGENTLIFE_DIR))
|
|
5965
|
+
fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
|
|
5966
|
+
fs10.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
|
|
5577
5967
|
} catch (err) {
|
|
5578
5968
|
console.warn(`[cloudflared-supervisor] failed to write pair-request marker: ${err?.message ?? err}`);
|
|
5579
5969
|
}
|
|
5580
5970
|
}
|
|
5581
5971
|
function clearPairRequest() {
|
|
5582
5972
|
try {
|
|
5583
|
-
|
|
5973
|
+
fs10.unlinkSync(PAIR_REQUEST_MARKER);
|
|
5584
5974
|
} catch {}
|
|
5585
5975
|
}
|
|
5586
5976
|
function parseRetryAfter(value) {
|
|
@@ -5604,18 +5994,18 @@ function scheduleProvisionRetry(delayMs) {
|
|
|
5604
5994
|
}, delayMs);
|
|
5605
5995
|
}
|
|
5606
5996
|
function ensureDirs() {
|
|
5607
|
-
if (!
|
|
5608
|
-
|
|
5609
|
-
if (!
|
|
5610
|
-
|
|
5611
|
-
if (!
|
|
5612
|
-
|
|
5997
|
+
if (!fs10.existsSync(AGENTLIFE_DIR))
|
|
5998
|
+
fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
|
|
5999
|
+
if (!fs10.existsSync(BIN_DIR))
|
|
6000
|
+
fs10.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
|
|
6001
|
+
if (!fs10.existsSync(IDENTITY_DIR))
|
|
6002
|
+
fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
5613
6003
|
}
|
|
5614
6004
|
function readIdentityFile(filePath) {
|
|
5615
|
-
if (!
|
|
6005
|
+
if (!fs10.existsSync(filePath))
|
|
5616
6006
|
return null;
|
|
5617
6007
|
try {
|
|
5618
|
-
const raw =
|
|
6008
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
5619
6009
|
const parsed = JSON.parse(raw);
|
|
5620
6010
|
if (typeof parsed.deviceId === "string" && typeof parsed.deviceSecret === "string") {
|
|
5621
6011
|
return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
|
|
@@ -5633,14 +6023,14 @@ function loadDeviceIdentity() {
|
|
|
5633
6023
|
if (!legacy)
|
|
5634
6024
|
return null;
|
|
5635
6025
|
try {
|
|
5636
|
-
if (!
|
|
5637
|
-
|
|
5638
|
-
|
|
6026
|
+
if (!fs10.existsSync(IDENTITY_DIR))
|
|
6027
|
+
fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
6028
|
+
fs10.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
|
|
5639
6029
|
try {
|
|
5640
|
-
|
|
6030
|
+
fs10.chmodSync(DEVICE_FILE, 384);
|
|
5641
6031
|
} catch {}
|
|
5642
6032
|
try {
|
|
5643
|
-
|
|
6033
|
+
fs10.unlinkSync(LEGACY_DEVICE_FILE);
|
|
5644
6034
|
} catch {}
|
|
5645
6035
|
console.log(`[cloudflared-supervisor] migrated device.json to ${DEVICE_FILE}`);
|
|
5646
6036
|
} catch (err) {
|
|
@@ -5652,25 +6042,25 @@ function loadOrCreateDeviceIdentity() {
|
|
|
5652
6042
|
const existing = loadDeviceIdentity();
|
|
5653
6043
|
if (existing)
|
|
5654
6044
|
return existing;
|
|
5655
|
-
if (!
|
|
5656
|
-
|
|
6045
|
+
if (!fs10.existsSync(IDENTITY_DIR))
|
|
6046
|
+
fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
5657
6047
|
const identity = {
|
|
5658
6048
|
deviceId: crypto2.randomUUID(),
|
|
5659
6049
|
deviceSecret: crypto2.randomBytes(32).toString("base64url")
|
|
5660
6050
|
};
|
|
5661
|
-
|
|
6051
|
+
fs10.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
|
|
5662
6052
|
try {
|
|
5663
|
-
|
|
6053
|
+
fs10.chmodSync(DEVICE_FILE, 384);
|
|
5664
6054
|
} catch {}
|
|
5665
6055
|
console.log(`[cloudflared-supervisor] generated new device identity (deviceId=${identity.deviceId})`);
|
|
5666
6056
|
return identity;
|
|
5667
6057
|
}
|
|
5668
6058
|
var AGENTLIFE_API_BASE = API_BASE;
|
|
5669
6059
|
function loadCachedTunnel() {
|
|
5670
|
-
if (!
|
|
6060
|
+
if (!fs10.existsSync(TUNNEL_FILE))
|
|
5671
6061
|
return null;
|
|
5672
6062
|
try {
|
|
5673
|
-
const raw =
|
|
6063
|
+
const raw = fs10.readFileSync(TUNNEL_FILE, "utf-8");
|
|
5674
6064
|
const parsed = JSON.parse(raw);
|
|
5675
6065
|
if (typeof parsed.subdomain === "string" && typeof parsed.hostname === "string" && typeof parsed.tunnelUrl === "string" && typeof parsed.tunnelToken === "string" && typeof parsed.provisionedAt === "number") {
|
|
5676
6066
|
return parsed;
|
|
@@ -5679,9 +6069,9 @@ function loadCachedTunnel() {
|
|
|
5679
6069
|
return null;
|
|
5680
6070
|
}
|
|
5681
6071
|
function persistTunnel(info) {
|
|
5682
|
-
|
|
6072
|
+
fs10.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
|
|
5683
6073
|
try {
|
|
5684
|
-
|
|
6074
|
+
fs10.chmodSync(TUNNEL_FILE, 384);
|
|
5685
6075
|
} catch {}
|
|
5686
6076
|
}
|
|
5687
6077
|
async function provisionTunnel(identity) {
|
|
@@ -5734,14 +6124,14 @@ async function provisionTunnel(identity) {
|
|
|
5734
6124
|
}
|
|
5735
6125
|
}
|
|
5736
6126
|
function ensureCloudflaredBinary() {
|
|
5737
|
-
if (
|
|
6127
|
+
if (fs10.existsSync(CLOUDFLARED_PATH)) {
|
|
5738
6128
|
try {
|
|
5739
|
-
|
|
6129
|
+
fs10.accessSync(CLOUDFLARED_PATH, fs10.constants.X_OK);
|
|
5740
6130
|
return CLOUDFLARED_PATH;
|
|
5741
6131
|
} catch {}
|
|
5742
6132
|
}
|
|
5743
|
-
const platform2 =
|
|
5744
|
-
const arch2 =
|
|
6133
|
+
const platform2 = os5.platform();
|
|
6134
|
+
const arch2 = os5.arch();
|
|
5745
6135
|
const release = detectCloudflaredRelease(platform2, arch2);
|
|
5746
6136
|
if (!release) {
|
|
5747
6137
|
console.warn(`[cloudflared-supervisor] unsupported platform: ${platform2}/${arch2}`);
|
|
@@ -5754,28 +6144,28 @@ function ensureCloudflaredBinary() {
|
|
|
5754
6144
|
if (release.kind === "tgz") {
|
|
5755
6145
|
const extracted = extractTgzCloudflared(release.tempPath, BIN_DIR);
|
|
5756
6146
|
try {
|
|
5757
|
-
|
|
6147
|
+
fs10.unlinkSync(release.tempPath);
|
|
5758
6148
|
} catch {}
|
|
5759
6149
|
if (!extracted)
|
|
5760
6150
|
return null;
|
|
5761
6151
|
} else {
|
|
5762
6152
|
try {
|
|
5763
|
-
|
|
6153
|
+
fs10.renameSync(release.tempPath, CLOUDFLARED_PATH);
|
|
5764
6154
|
} catch (err) {
|
|
5765
6155
|
console.warn(`[cloudflared-supervisor] rename failed: ${err?.message}`);
|
|
5766
6156
|
return null;
|
|
5767
6157
|
}
|
|
5768
6158
|
}
|
|
5769
6159
|
try {
|
|
5770
|
-
|
|
6160
|
+
fs10.chmodSync(CLOUDFLARED_PATH, 493);
|
|
5771
6161
|
} catch {}
|
|
5772
|
-
return
|
|
6162
|
+
return fs10.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
|
|
5773
6163
|
}
|
|
5774
6164
|
function detectCloudflaredRelease(platform2, arch2) {
|
|
5775
6165
|
const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
|
|
5776
6166
|
if (platform2 === "darwin") {
|
|
5777
6167
|
const asset = arch2 === "arm64" ? "cloudflared-darwin-arm64.tgz" : "cloudflared-darwin-amd64.tgz";
|
|
5778
|
-
return { url: `${base}/${asset}`, kind: "tgz", tempPath:
|
|
6168
|
+
return { url: `${base}/${asset}`, kind: "tgz", tempPath: path10.join(BIN_DIR, asset) };
|
|
5779
6169
|
}
|
|
5780
6170
|
if (platform2 === "linux") {
|
|
5781
6171
|
const asset = arch2 === "arm64" ? "cloudflared-linux-arm64" : arch2 === "arm" ? "cloudflared-linux-arm" : arch2 === "x64" ? "cloudflared-linux-amd64" : null;
|
|
@@ -5799,11 +6189,11 @@ function downloadWithCurl(url, dest) {
|
|
|
5799
6189
|
if (result.status !== 0) {
|
|
5800
6190
|
console.warn(`[cloudflared-supervisor] curl failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
|
|
5801
6191
|
try {
|
|
5802
|
-
|
|
6192
|
+
fs10.unlinkSync(dest);
|
|
5803
6193
|
} catch {}
|
|
5804
6194
|
return false;
|
|
5805
6195
|
}
|
|
5806
|
-
return
|
|
6196
|
+
return fs10.existsSync(dest) && fs10.statSync(dest).size > 0;
|
|
5807
6197
|
}
|
|
5808
6198
|
function extractTgzCloudflared(tgzPath, destDir) {
|
|
5809
6199
|
const result = spawnSync("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "pipe" });
|
|
@@ -5811,10 +6201,10 @@ function extractTgzCloudflared(tgzPath, destDir) {
|
|
|
5811
6201
|
console.warn(`[cloudflared-supervisor] tar extract failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
|
|
5812
6202
|
return false;
|
|
5813
6203
|
}
|
|
5814
|
-
return
|
|
6204
|
+
return fs10.existsSync(CLOUDFLARED_PATH);
|
|
5815
6205
|
}
|
|
5816
6206
|
function killOrphanedCloudflareds(binPath) {
|
|
5817
|
-
if (
|
|
6207
|
+
if (os5.platform() === "win32")
|
|
5818
6208
|
return;
|
|
5819
6209
|
const found = spawnSync("pgrep", ["-f", binPath], { stdio: "pipe" });
|
|
5820
6210
|
if (found.status !== 0)
|
|
@@ -5884,13 +6274,16 @@ var MIME_TYPES = {
|
|
|
5884
6274
|
".txt": "text/plain",
|
|
5885
6275
|
".map": "application/json"
|
|
5886
6276
|
};
|
|
6277
|
+
function resolveStateDir() {
|
|
6278
|
+
return process.env.OPENCLAW_STATE_DIR || path11.join(os6.homedir(), ".openclaw");
|
|
6279
|
+
}
|
|
5887
6280
|
function mintBootstrapToken() {
|
|
5888
6281
|
const bootstrapToken = crypto3.randomBytes(32).toString("base64url");
|
|
5889
|
-
const devicesDir =
|
|
5890
|
-
const bootstrapPath =
|
|
5891
|
-
if (!
|
|
5892
|
-
|
|
5893
|
-
const registry =
|
|
6282
|
+
const devicesDir = path11.join(resolveStateDir(), "devices");
|
|
6283
|
+
const bootstrapPath = path11.join(devicesDir, "bootstrap.json");
|
|
6284
|
+
if (!fs11.existsSync(devicesDir))
|
|
6285
|
+
fs11.mkdirSync(devicesDir, { recursive: true });
|
|
6286
|
+
const registry = fs11.existsSync(bootstrapPath) ? JSON.parse(fs11.readFileSync(bootstrapPath, "utf-8")) : {};
|
|
5894
6287
|
registry[bootstrapToken] = {
|
|
5895
6288
|
token: bootstrapToken,
|
|
5896
6289
|
profile: {
|
|
@@ -5899,15 +6292,15 @@ function mintBootstrapToken() {
|
|
|
5899
6292
|
},
|
|
5900
6293
|
issuedAtMs: Date.now()
|
|
5901
6294
|
};
|
|
5902
|
-
|
|
6295
|
+
fs11.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
|
|
5903
6296
|
return bootstrapToken;
|
|
5904
6297
|
}
|
|
5905
6298
|
function registerWebApp(api) {
|
|
5906
|
-
const pluginRoot =
|
|
5907
|
-
const appRoot =
|
|
5908
|
-
const hasWebBuild =
|
|
5909
|
-
const indexPath = hasWebBuild ?
|
|
5910
|
-
const hasIndex = !!(indexPath &&
|
|
6299
|
+
const pluginRoot = path11.resolve(path11.dirname(api.source), "..");
|
|
6300
|
+
const appRoot = path11.join(pluginRoot, "web-build");
|
|
6301
|
+
const hasWebBuild = fs11.existsSync(appRoot);
|
|
6302
|
+
const indexPath = hasWebBuild ? path11.join(appRoot, "index.html") : null;
|
|
6303
|
+
const hasIndex = !!(indexPath && fs11.existsSync(indexPath));
|
|
5911
6304
|
if (!hasWebBuild) {
|
|
5912
6305
|
api.logger.info("[agentlife] web-build/ not found — /agentlife/pair will still register; static dashboard disabled");
|
|
5913
6306
|
} else if (!hasIndex) {
|
|
@@ -5915,11 +6308,13 @@ function registerWebApp(api) {
|
|
|
5915
6308
|
}
|
|
5916
6309
|
let gatewayToken = "";
|
|
5917
6310
|
try {
|
|
5918
|
-
const configPath =
|
|
5919
|
-
const raw = JSON.parse(
|
|
6311
|
+
const configPath = process.env.OPENCLAW_CONFIG_PATH || path11.join(resolveStateDir(), "openclaw.json");
|
|
6312
|
+
const raw = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
|
|
5920
6313
|
gatewayToken = raw?.gateway?.auth?.token || "";
|
|
5921
6314
|
} catch {}
|
|
5922
|
-
|
|
6315
|
+
if (shouldStartRuntimeServices()) {
|
|
6316
|
+
loadOrCreatePairingAccessToken();
|
|
6317
|
+
}
|
|
5923
6318
|
const approveLatest = () => {
|
|
5924
6319
|
try {
|
|
5925
6320
|
const { execSync } = __require("node:child_process");
|
|
@@ -6034,16 +6429,16 @@ function registerWebApp(api) {
|
|
|
6034
6429
|
return true;
|
|
6035
6430
|
}
|
|
6036
6431
|
const relative = urlPath.replace(/^\/agentlife\/?/, "") || "index.html";
|
|
6037
|
-
const filePath =
|
|
6432
|
+
const filePath = path11.resolve(appRoot, relative);
|
|
6038
6433
|
if (!filePath.startsWith(appRoot)) {
|
|
6039
6434
|
res.writeHead(403);
|
|
6040
6435
|
res.end();
|
|
6041
6436
|
return true;
|
|
6042
6437
|
}
|
|
6043
|
-
const target =
|
|
6044
|
-
const ext =
|
|
6438
|
+
const target = fs11.existsSync(filePath) && fs11.statSync(filePath).isFile() ? filePath : indexPath;
|
|
6439
|
+
const ext = path11.extname(target).toLowerCase();
|
|
6045
6440
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
6046
|
-
let content =
|
|
6441
|
+
let content = fs11.readFileSync(target);
|
|
6047
6442
|
if (target === indexPath) {
|
|
6048
6443
|
setTimeout(approveLatest, 2000);
|
|
6049
6444
|
setTimeout(approveLatest, 5000);
|
|
@@ -6079,62 +6474,29 @@ function registerWebApp(api) {
|
|
|
6079
6474
|
|
|
6080
6475
|
// services/pairing.ts
|
|
6081
6476
|
var TUNNEL_READY_TIMEOUT_MS = 90000;
|
|
6477
|
+
function resolveStateDir2(ctx) {
|
|
6478
|
+
return ctx?.stateDir || process.env.OPENCLAW_STATE_DIR || path12.join(os7.homedir(), ".openclaw");
|
|
6479
|
+
}
|
|
6082
6480
|
function registerPairingServices(api) {
|
|
6083
6481
|
api.registerService({
|
|
6084
6482
|
id: "agentlife-pairing-qr",
|
|
6085
|
-
start:
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
const token = cfg.gateway?.auth?.token;
|
|
6090
|
-
if (!token)
|
|
6091
|
-
return;
|
|
6092
|
-
const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
|
|
6093
|
-
if (!supervisorTunnel?.tunnelUrl) {
|
|
6094
|
-
console.warn(`[agentlife] Cloudflared tunnel did not come up within ${TUNNEL_READY_TIMEOUT_MS / 1000}s — ` + `pairing QR will not be emitted. Check cloudflared-supervisor logs in the gateway output.`);
|
|
6095
|
-
return;
|
|
6096
|
-
}
|
|
6097
|
-
const url = supervisorTunnel.tunnelUrl;
|
|
6098
|
-
const bootstrapToken = mintBootstrapToken();
|
|
6099
|
-
const payload = JSON.stringify({ url, bootstrapToken });
|
|
6100
|
-
const setupCode = Buffer.from(payload).toString("base64url");
|
|
6101
|
-
let qrTerminal = null;
|
|
6102
|
-
try {
|
|
6103
|
-
const realPath = fsSync3.realpathSync(process.argv[1] || "");
|
|
6104
|
-
const mainRequire = createRequire3(realPath);
|
|
6105
|
-
qrTerminal = mainRequire("qrcode-terminal");
|
|
6106
|
-
} catch {}
|
|
6107
|
-
console.log(`
|
|
6108
|
-
\x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
|
|
6109
|
-
`);
|
|
6110
|
-
if (qrTerminal?.generate) {
|
|
6111
|
-
qrTerminal.generate(setupCode, { small: true }, (qr) => {
|
|
6112
|
-
console.log(qr);
|
|
6113
|
-
console.log(`
|
|
6114
|
-
Setup code: ${setupCode}
|
|
6115
|
-
`);
|
|
6116
|
-
});
|
|
6117
|
-
} else {
|
|
6118
|
-
console.log(`Setup code: ${setupCode}`);
|
|
6119
|
-
}
|
|
6120
|
-
console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
|
|
6121
|
-
console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
|
|
6122
|
-
console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
|
|
6123
|
-
console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
|
|
6124
|
-
console.log(`
|
|
6125
|
-
Scan the QR from the mobile app to connect.
|
|
6126
|
-
`);
|
|
6127
|
-
} catch (err) {
|
|
6483
|
+
start: (ctx) => {
|
|
6484
|
+
if (!shouldStartRuntimeServices())
|
|
6485
|
+
return;
|
|
6486
|
+
emitPairingQr(api, ctx).catch((err) => {
|
|
6128
6487
|
console.warn("[agentlife] QR generation skipped:", err?.message);
|
|
6129
|
-
}
|
|
6488
|
+
});
|
|
6130
6489
|
}
|
|
6131
6490
|
});
|
|
6132
6491
|
api.registerService({
|
|
6133
6492
|
id: "agentlife-auto-pair",
|
|
6134
6493
|
start: async (ctx) => {
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
const
|
|
6494
|
+
if (!shouldStartRuntimeServices())
|
|
6495
|
+
return;
|
|
6496
|
+
const stateDir = resolveStateDir2(ctx);
|
|
6497
|
+
const devicesDir = path12.join(stateDir, "devices");
|
|
6498
|
+
const pendingPath = path12.join(devicesDir, "pending.json");
|
|
6499
|
+
const pairedPath = path12.join(devicesDir, "paired.json");
|
|
6138
6500
|
const pollInterval = 3000;
|
|
6139
6501
|
const withAdmin = (arr) => {
|
|
6140
6502
|
const base = Array.isArray(arr) ? arr.filter((s) => typeof s === "string") : [];
|
|
@@ -6229,6 +6591,52 @@ Setup code: ${setupCode}
|
|
|
6229
6591
|
}
|
|
6230
6592
|
});
|
|
6231
6593
|
}
|
|
6594
|
+
async function emitPairingQr(api, _ctx) {
|
|
6595
|
+
try {
|
|
6596
|
+
const cfg = api.runtime.config.loadConfig();
|
|
6597
|
+
const port = cfg.gateway?.port ?? 18789;
|
|
6598
|
+
const token = cfg.gateway?.auth?.token;
|
|
6599
|
+
if (!token)
|
|
6600
|
+
return;
|
|
6601
|
+
const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
|
|
6602
|
+
if (!supervisorTunnel?.tunnelUrl) {
|
|
6603
|
+
console.warn(`[agentlife] Cloudflared tunnel did not come up within ${TUNNEL_READY_TIMEOUT_MS / 1000}s — ` + `pairing QR will not be emitted. Check cloudflared-supervisor logs in the gateway output.`);
|
|
6604
|
+
return;
|
|
6605
|
+
}
|
|
6606
|
+
const url = supervisorTunnel.tunnelUrl;
|
|
6607
|
+
const bootstrapToken = mintBootstrapToken();
|
|
6608
|
+
const payload = JSON.stringify({ url, bootstrapToken });
|
|
6609
|
+
const setupCode = Buffer.from(payload).toString("base64url");
|
|
6610
|
+
let qrTerminal = null;
|
|
6611
|
+
try {
|
|
6612
|
+
const realPath = fsSync3.realpathSync(process.argv[1] || "");
|
|
6613
|
+
const mainRequire = createRequire3(realPath);
|
|
6614
|
+
qrTerminal = mainRequire("qrcode-terminal");
|
|
6615
|
+
} catch {}
|
|
6616
|
+
console.log(`
|
|
6617
|
+
\x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
|
|
6618
|
+
`);
|
|
6619
|
+
if (qrTerminal?.generate) {
|
|
6620
|
+
qrTerminal.generate(setupCode, { small: true }, (qr) => {
|
|
6621
|
+
console.log(qr);
|
|
6622
|
+
console.log(`
|
|
6623
|
+
Setup code: ${setupCode}
|
|
6624
|
+
`);
|
|
6625
|
+
});
|
|
6626
|
+
} else {
|
|
6627
|
+
console.log(`Setup code: ${setupCode}`);
|
|
6628
|
+
}
|
|
6629
|
+
console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
|
|
6630
|
+
console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
|
|
6631
|
+
console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
|
|
6632
|
+
console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
|
|
6633
|
+
console.log(`
|
|
6634
|
+
Scan the QR from the mobile app to connect.
|
|
6635
|
+
`);
|
|
6636
|
+
} catch (err) {
|
|
6637
|
+
console.warn("[agentlife] QR generation skipped:", err?.message);
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6232
6640
|
|
|
6233
6641
|
// hooks/bootstrap.ts
|
|
6234
6642
|
var SKIP_GUIDANCE_AGENTS = new Set(["agentlife"]);
|
|
@@ -6388,15 +6796,15 @@ function registerBootstrapHookImpl(api, state2) {
|
|
|
6388
6796
|
}
|
|
6389
6797
|
|
|
6390
6798
|
// notifications.ts
|
|
6391
|
-
import * as
|
|
6392
|
-
import * as
|
|
6799
|
+
import * as fs12 from "node:fs";
|
|
6800
|
+
import * as path13 from "node:path";
|
|
6393
6801
|
var config;
|
|
6394
6802
|
function loadConfig() {
|
|
6395
6803
|
if (config !== undefined)
|
|
6396
6804
|
return config;
|
|
6397
6805
|
try {
|
|
6398
|
-
const configPath =
|
|
6399
|
-
const raw =
|
|
6806
|
+
const configPath = path13.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
|
|
6807
|
+
const raw = fs12.readFileSync(configPath, "utf-8");
|
|
6400
6808
|
const parsed = JSON.parse(raw);
|
|
6401
6809
|
if (parsed.serverUrl && parsed.apiKey) {
|
|
6402
6810
|
config = { serverUrl: parsed.serverUrl, apiKey: parsed.apiKey };
|
|
@@ -6966,9 +7374,101 @@ function registerPromptStateHook(api, state2) {
|
|
|
6966
7374
|
}
|
|
6967
7375
|
|
|
6968
7376
|
// gateway/agents.ts
|
|
6969
|
-
import * as
|
|
6970
|
-
import * as
|
|
6971
|
-
import * as
|
|
7377
|
+
import * as fs13 from "node:fs";
|
|
7378
|
+
import * as os8 from "node:os";
|
|
7379
|
+
import * as path14 from "node:path";
|
|
7380
|
+
import { ensureAuthProfileStore, upsertAuthProfile as upsertAuthProfile2 } from "openclaw/plugin-sdk/provider-auth";
|
|
7381
|
+
function resolveOpenClawRoot2() {
|
|
7382
|
+
const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
7383
|
+
return fromEnv ? path14.resolve(fromEnv) : path14.join(os8.homedir(), ".openclaw");
|
|
7384
|
+
}
|
|
7385
|
+
function providerFromModel2(model) {
|
|
7386
|
+
const slash = model.indexOf("/");
|
|
7387
|
+
if (slash <= 0)
|
|
7388
|
+
return null;
|
|
7389
|
+
return model.slice(0, slash).trim() || null;
|
|
7390
|
+
}
|
|
7391
|
+
function authenticatedProvidersFromConfig(cfg) {
|
|
7392
|
+
const out = new Set;
|
|
7393
|
+
const profiles = cfg?.auth?.profiles;
|
|
7394
|
+
if (!profiles || typeof profiles !== "object")
|
|
7395
|
+
return out;
|
|
7396
|
+
for (const entry of Object.values(profiles)) {
|
|
7397
|
+
const provider = entry?.provider;
|
|
7398
|
+
if (typeof provider === "string" && provider.trim()) {
|
|
7399
|
+
out.add(provider.trim());
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
return out;
|
|
7403
|
+
}
|
|
7404
|
+
function findSiblingAuthProfile(provider, targetAgentId) {
|
|
7405
|
+
const agentsRoot = path14.join(resolveOpenClawRoot2(), "agents");
|
|
7406
|
+
let entries;
|
|
7407
|
+
try {
|
|
7408
|
+
entries = fs13.readdirSync(agentsRoot, { withFileTypes: true });
|
|
7409
|
+
} catch {
|
|
7410
|
+
return null;
|
|
7411
|
+
}
|
|
7412
|
+
for (const entry of entries) {
|
|
7413
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
7414
|
+
continue;
|
|
7415
|
+
if (entry.name === targetAgentId)
|
|
7416
|
+
continue;
|
|
7417
|
+
const agentDir = path14.join(agentsRoot, entry.name, "agent");
|
|
7418
|
+
try {
|
|
7419
|
+
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false, syncExternalCli: false });
|
|
7420
|
+
for (const [profileId, profile] of Object.entries(store?.profiles ?? {})) {
|
|
7421
|
+
if (profile?.provider !== provider)
|
|
7422
|
+
continue;
|
|
7423
|
+
const type = profile?.type;
|
|
7424
|
+
const mode = profile?.mode;
|
|
7425
|
+
if (type === "oauth")
|
|
7426
|
+
continue;
|
|
7427
|
+
if (mode === "oauth")
|
|
7428
|
+
continue;
|
|
7429
|
+
return { profileId, profile };
|
|
7430
|
+
}
|
|
7431
|
+
} catch {}
|
|
7432
|
+
const file = path14.join(agentDir, "auth-profiles.json");
|
|
7433
|
+
let data;
|
|
7434
|
+
try {
|
|
7435
|
+
data = JSON.parse(fs13.readFileSync(file, "utf-8"));
|
|
7436
|
+
} catch {
|
|
7437
|
+
continue;
|
|
7438
|
+
}
|
|
7439
|
+
const profiles = data?.profiles;
|
|
7440
|
+
if (!profiles || typeof profiles !== "object")
|
|
7441
|
+
continue;
|
|
7442
|
+
for (const [profileId, profile] of Object.entries(profiles)) {
|
|
7443
|
+
if (profile?.provider !== provider)
|
|
7444
|
+
continue;
|
|
7445
|
+
const type = profile?.type;
|
|
7446
|
+
const mode = profile?.mode;
|
|
7447
|
+
if (type === "oauth")
|
|
7448
|
+
continue;
|
|
7449
|
+
if (mode === "oauth")
|
|
7450
|
+
continue;
|
|
7451
|
+
return {
|
|
7452
|
+
profileId,
|
|
7453
|
+
profile
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
}
|
|
7457
|
+
return null;
|
|
7458
|
+
}
|
|
7459
|
+
function writeAgentAuthProfile(agentId, provider, match) {
|
|
7460
|
+
if (!match)
|
|
7461
|
+
return false;
|
|
7462
|
+
const agentDir = path14.join(resolveOpenClawRoot2(), "agents", agentId, "agent");
|
|
7463
|
+
fs13.mkdirSync(agentDir, { recursive: true });
|
|
7464
|
+
const credential = { ...match.profile };
|
|
7465
|
+
if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
|
|
7466
|
+
credential.key = credential.apiKey;
|
|
7467
|
+
delete credential.apiKey;
|
|
7468
|
+
}
|
|
7469
|
+
upsertAuthProfile2({ profileId: match.profileId, credential, agentDir });
|
|
7470
|
+
return true;
|
|
7471
|
+
}
|
|
6972
7472
|
function registerAgentGateway(api, state2) {
|
|
6973
7473
|
api.registerGatewayMethod("agentlife.createAgent", async ({ params, respond }) => {
|
|
6974
7474
|
const id = typeof params?.id === "string" ? params.id.trim() : "";
|
|
@@ -6980,14 +7480,26 @@ function registerAgentGateway(api, state2) {
|
|
|
6980
7480
|
const subagents = params?.subagents && typeof params.subagents === "object" ? params.subagents : undefined;
|
|
6981
7481
|
const identity = params?.identity && typeof params.identity === "object" ? params.identity : undefined;
|
|
6982
7482
|
if (!id) {
|
|
6983
|
-
respond(false, {
|
|
7483
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agent id" });
|
|
6984
7484
|
return;
|
|
6985
7485
|
}
|
|
6986
7486
|
if (!workspace) {
|
|
6987
|
-
respond(false, {
|
|
7487
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing workspace path" });
|
|
6988
7488
|
return;
|
|
6989
7489
|
}
|
|
6990
7490
|
const cfg = api.runtime.config.loadConfig();
|
|
7491
|
+
const provider = model ? providerFromModel2(model) : null;
|
|
7492
|
+
const providerInConfig = provider ? authenticatedProvidersFromConfig(cfg).has(provider) : false;
|
|
7493
|
+
const siblingAuth = provider ? findSiblingAuthProfile(provider, id) : null;
|
|
7494
|
+
if (provider && !providerInConfig && !siblingAuth) {
|
|
7495
|
+
respond(false, undefined, {
|
|
7496
|
+
code: "missing_provider_auth",
|
|
7497
|
+
message: `missing provider credentials for ${provider}; configure ${provider} before creating an agent with ${model}`,
|
|
7498
|
+
provider,
|
|
7499
|
+
model
|
|
7500
|
+
});
|
|
7501
|
+
return;
|
|
7502
|
+
}
|
|
6991
7503
|
const currentList = cfg.agents?.list ?? [];
|
|
6992
7504
|
const existing = currentList.find((a) => a.id === id);
|
|
6993
7505
|
if (!existing) {
|
|
@@ -7034,6 +7546,10 @@ function registerAgentGateway(api, state2) {
|
|
|
7034
7546
|
console.log("[agentlife] builder patched agent config: %s", id);
|
|
7035
7547
|
}
|
|
7036
7548
|
}
|
|
7549
|
+
const authCopied = provider ? writeAgentAuthProfile(id, provider, siblingAuth) : false;
|
|
7550
|
+
if (authCopied) {
|
|
7551
|
+
console.log("[agentlife] copied %s auth profile into agent: %s", provider, id);
|
|
7552
|
+
}
|
|
7037
7553
|
const hasGoodDescription = description && isUsableDescription(description, id, name);
|
|
7038
7554
|
state2.agentRegistry.set(id, {
|
|
7039
7555
|
name: name || id,
|
|
@@ -7063,17 +7579,17 @@ function registerAgentGateway(api, state2) {
|
|
|
7063
7579
|
console.warn("[agentlife] deleteWelcomeWidget failed for %s: %s", id, e?.message);
|
|
7064
7580
|
}
|
|
7065
7581
|
}
|
|
7066
|
-
respond(true, { status, id, name, model, workspace, description, ...configFields.length ? { configFields } : {} });
|
|
7067
|
-
});
|
|
7582
|
+
respond(true, { status, id, name, model, workspace, description, ...authCopied ? { authCopied: true } : {}, ...configFields.length ? { configFields } : {} });
|
|
7583
|
+
}, { scope: "operator.admin" });
|
|
7068
7584
|
api.registerGatewayMethod("agentlife.deleteAgent", async ({ params, respond }) => {
|
|
7069
7585
|
const id = typeof params?.id === "string" ? params.id.trim() : "";
|
|
7070
7586
|
if (!id) {
|
|
7071
|
-
respond(false, {
|
|
7587
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agent id" });
|
|
7072
7588
|
return;
|
|
7073
7589
|
}
|
|
7074
7590
|
const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
|
|
7075
7591
|
if (provisionedIds.has(id)) {
|
|
7076
|
-
respond(false, {
|
|
7592
|
+
respond(false, undefined, { code: "FAILED", message: "cannot delete provisioned agent" });
|
|
7077
7593
|
return;
|
|
7078
7594
|
}
|
|
7079
7595
|
const cleanup = {
|
|
@@ -7156,10 +7672,10 @@ function registerAgentGateway(api, state2) {
|
|
|
7156
7672
|
state2.agentDbs.delete(id);
|
|
7157
7673
|
}
|
|
7158
7674
|
if (state2.dbBaseDir) {
|
|
7159
|
-
const dbPath =
|
|
7675
|
+
const dbPath = path14.join(state2.dbBaseDir, `${id}.db`);
|
|
7160
7676
|
try {
|
|
7161
|
-
if (
|
|
7162
|
-
|
|
7677
|
+
if (fs13.existsSync(dbPath)) {
|
|
7678
|
+
fs13.unlinkSync(dbPath);
|
|
7163
7679
|
cleanup.agentDbDeleted = true;
|
|
7164
7680
|
}
|
|
7165
7681
|
} catch (e) {
|
|
@@ -7171,24 +7687,24 @@ function registerAgentGateway(api, state2) {
|
|
|
7171
7687
|
const existingEntry = currentList.find((a) => a.id === id);
|
|
7172
7688
|
const filtered = currentList.filter((a) => a.id !== id);
|
|
7173
7689
|
const removedFromConfig = filtered.length < currentList.length;
|
|
7174
|
-
const openclawHome =
|
|
7175
|
-
const stateDir =
|
|
7176
|
-
if (stateDir.startsWith(
|
|
7690
|
+
const openclawHome = path14.join(os8.homedir(), ".openclaw");
|
|
7691
|
+
const stateDir = path14.join(openclawHome, "agents", id);
|
|
7692
|
+
if (stateDir.startsWith(path14.join(openclawHome, "agents") + path14.sep)) {
|
|
7177
7693
|
try {
|
|
7178
|
-
if (
|
|
7179
|
-
|
|
7694
|
+
if (fs13.existsSync(stateDir)) {
|
|
7695
|
+
fs13.rmSync(stateDir, { recursive: true, force: true });
|
|
7180
7696
|
cleanup.stateDeleted = true;
|
|
7181
7697
|
}
|
|
7182
7698
|
} catch (e) {
|
|
7183
7699
|
console.warn("[agentlife] deleteAgent: failed to delete state dir:", e?.message);
|
|
7184
7700
|
}
|
|
7185
7701
|
}
|
|
7186
|
-
const defaultWorkspace =
|
|
7187
|
-
const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ?
|
|
7188
|
-
if (workspaceToDelete.startsWith(openclawHome +
|
|
7702
|
+
const defaultWorkspace = path14.join(openclawHome, `workspace-${id}`);
|
|
7703
|
+
const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path14.resolve(existingEntry.workspace) : defaultWorkspace;
|
|
7704
|
+
if (workspaceToDelete.startsWith(openclawHome + path14.sep) && workspaceToDelete !== openclawHome) {
|
|
7189
7705
|
try {
|
|
7190
|
-
if (
|
|
7191
|
-
|
|
7706
|
+
if (fs13.existsSync(workspaceToDelete)) {
|
|
7707
|
+
fs13.rmSync(workspaceToDelete, { recursive: true, force: true });
|
|
7192
7708
|
cleanup.workspaceDeleted = true;
|
|
7193
7709
|
}
|
|
7194
7710
|
} catch (e) {
|
|
@@ -7211,7 +7727,7 @@ function registerAgentGateway(api, state2) {
|
|
|
7211
7727
|
}
|
|
7212
7728
|
console.log("[agentlife] deleted agent: %s (config=%s, registry=%s, surfaces=%d, sessions=%d, cron=%d, historyRows=%d, agentDb=%s, state=%s, workspace=%s)", id, removedFromConfig, removedFromRegistry, cleanup.surfacesCleared, cleanup.sessions, cleanup.cronJobs, cleanup.historyRows, cleanup.agentDbDeleted, cleanup.stateDeleted, cleanup.workspaceDeleted);
|
|
7213
7729
|
respond(true, { status: "deleted", id, removedFromConfig, removedFromRegistry, cleanup });
|
|
7214
|
-
});
|
|
7730
|
+
}, { scope: "operator.admin" });
|
|
7215
7731
|
api.registerGatewayMethod("agentlife.agents", ({ respond }) => {
|
|
7216
7732
|
const agents = {};
|
|
7217
7733
|
for (const [id, entry] of state2.agentRegistry.entries()) {
|
|
@@ -7228,11 +7744,11 @@ function registerDbGateway(api, state2) {
|
|
|
7228
7744
|
const sql = typeof params?.sql === "string" ? params.sql.trim() : "";
|
|
7229
7745
|
const sqlParams = Array.isArray(params?.params) ? params.params : [];
|
|
7230
7746
|
if (!agentId)
|
|
7231
|
-
return respond(false, {
|
|
7747
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agentId" });
|
|
7232
7748
|
if (!sql)
|
|
7233
|
-
return respond(false, {
|
|
7749
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing sql" });
|
|
7234
7750
|
if (/^\s*SELECT\b/i.test(sql))
|
|
7235
|
-
return respond(false, {
|
|
7751
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "use agentlife.db.query for SELECT" });
|
|
7236
7752
|
try {
|
|
7237
7753
|
const db = getOrCreateAgentDb(state2, agentId);
|
|
7238
7754
|
if (sqlParams.length > 0) {
|
|
@@ -7243,19 +7759,19 @@ function registerDbGateway(api, state2) {
|
|
|
7243
7759
|
respond(true, { changes: 0, lastInsertRowid: 0 });
|
|
7244
7760
|
}
|
|
7245
7761
|
} catch (err) {
|
|
7246
|
-
respond(false, {
|
|
7762
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "db error" });
|
|
7247
7763
|
}
|
|
7248
|
-
});
|
|
7764
|
+
}, { scope: "operator.admin" });
|
|
7249
7765
|
api.registerGatewayMethod("agentlife.db.query", ({ params, respond }) => {
|
|
7250
7766
|
const agentId = typeof params?.agentId === "string" ? params.agentId.trim() : "";
|
|
7251
7767
|
const sql = typeof params?.sql === "string" ? params.sql.trim() : "";
|
|
7252
7768
|
const sqlParams = Array.isArray(params?.params) ? params.params : [];
|
|
7253
7769
|
if (!agentId)
|
|
7254
|
-
return respond(false, {
|
|
7770
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agentId" });
|
|
7255
7771
|
if (!sql)
|
|
7256
|
-
return respond(false, {
|
|
7772
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing sql" });
|
|
7257
7773
|
if (!/^\s*SELECT\b/i.test(sql))
|
|
7258
|
-
return respond(false, {
|
|
7774
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "use agentlife.db.exec for non-SELECT" });
|
|
7259
7775
|
try {
|
|
7260
7776
|
const db = getOrCreateAgentDb(state2, agentId);
|
|
7261
7777
|
const rows = db.prepare(sql).all(...sqlParams);
|
|
@@ -7265,9 +7781,9 @@ function registerDbGateway(api, state2) {
|
|
|
7265
7781
|
const columns = result.length > 0 ? Object.keys(result[0]) : [];
|
|
7266
7782
|
respond(true, { rows: result, columns, count: result.length, truncated });
|
|
7267
7783
|
} catch (err) {
|
|
7268
|
-
respond(false, {
|
|
7784
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "db error" });
|
|
7269
7785
|
}
|
|
7270
|
-
});
|
|
7786
|
+
}, { scope: "operator.admin" });
|
|
7271
7787
|
}
|
|
7272
7788
|
|
|
7273
7789
|
// gateway/history.ts
|
|
@@ -7308,7 +7824,7 @@ function registerHistoryGateway(api, state2) {
|
|
|
7308
7824
|
const rows = db.prepare(`SELECT * FROM surface_events ${where} ORDER BY createdAt DESC LIMIT ? OFFSET ?`).all(...bind);
|
|
7309
7825
|
respond(true, { events: rows, count: rows.length });
|
|
7310
7826
|
} catch (err) {
|
|
7311
|
-
respond(false, {
|
|
7827
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "history error" });
|
|
7312
7828
|
}
|
|
7313
7829
|
}, { scope: "operator.read" });
|
|
7314
7830
|
api.registerGatewayMethod("agentlife.history.widgets", ({ params, respond }) => {
|
|
@@ -7349,7 +7865,7 @@ function registerHistoryGateway(api, state2) {
|
|
|
7349
7865
|
`).all(...bind, limit, offset);
|
|
7350
7866
|
respond(true, { widgets: rows, count: rows.length, hasMore: rows.length >= limit });
|
|
7351
7867
|
} catch (err) {
|
|
7352
|
-
respond(false, {
|
|
7868
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "history.widgets error" });
|
|
7353
7869
|
}
|
|
7354
7870
|
}, { scope: "operator.read" });
|
|
7355
7871
|
api.registerGatewayMethod("agentlife.trace", ({ params, respond }) => {
|
|
@@ -7406,7 +7922,7 @@ function registerHistoryGateway(api, state2) {
|
|
|
7406
7922
|
}));
|
|
7407
7923
|
respond(true, { entries, hasMore });
|
|
7408
7924
|
} catch (err) {
|
|
7409
|
-
respond(false, {
|
|
7925
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "trace query failed" });
|
|
7410
7926
|
}
|
|
7411
7927
|
}, { scope: "operator.read" });
|
|
7412
7928
|
}
|
|
@@ -7479,13 +7995,13 @@ function registerUsageGateway(api, state2) {
|
|
|
7479
7995
|
bySession: bySessionRows
|
|
7480
7996
|
});
|
|
7481
7997
|
} catch (err) {
|
|
7482
|
-
respond(false, {
|
|
7998
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "usage query error" });
|
|
7483
7999
|
}
|
|
7484
8000
|
}, { scope: "operator.read" });
|
|
7485
8001
|
api.registerGatewayMethod("agentlife.surfaceUsage", ({ params, respond }) => {
|
|
7486
8002
|
const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
|
|
7487
8003
|
if (!surfaceId)
|
|
7488
|
-
return respond(false, {
|
|
8004
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
|
|
7489
8005
|
try {
|
|
7490
8006
|
const db = getOrCreateHistoryDb(state2);
|
|
7491
8007
|
const rows = db.prepare(`SELECT * FROM surface_usage WHERE surfaceId = ? ORDER BY createdAt DESC`).all(surfaceId);
|
|
@@ -7501,13 +8017,13 @@ function registerUsageGateway(api, state2) {
|
|
|
7501
8017
|
}, { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 0, estimatedCost: 0, llmCalls: 0 });
|
|
7502
8018
|
respond(true, { surfaceId, entries: rows, totals });
|
|
7503
8019
|
} catch (err) {
|
|
7504
|
-
respond(false, {
|
|
8020
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "surface usage error" });
|
|
7505
8021
|
}
|
|
7506
8022
|
}, { scope: "operator.read" });
|
|
7507
8023
|
}
|
|
7508
8024
|
|
|
7509
8025
|
// gateway/surfaces-gateway.ts
|
|
7510
|
-
import * as
|
|
8026
|
+
import * as fs14 from "node:fs";
|
|
7511
8027
|
var guidedDismissSent = new Set;
|
|
7512
8028
|
var warnedMissingOrigin = new Set;
|
|
7513
8029
|
function registerSurfacesGateway(api, state2) {
|
|
@@ -7550,7 +8066,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7550
8066
|
api.registerGatewayMethod("agentlife.dismiss", async ({ params, respond }) => {
|
|
7551
8067
|
const surfaceId = params?.surfaceId;
|
|
7552
8068
|
if (!surfaceId || typeof surfaceId !== "string") {
|
|
7553
|
-
respond(false, {
|
|
8069
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
|
|
7554
8070
|
return;
|
|
7555
8071
|
}
|
|
7556
8072
|
const view2 = getSurface(state2, surfaceId);
|
|
@@ -7596,7 +8112,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7596
8112
|
guidedDismissSent.delete(surfaceId);
|
|
7597
8113
|
if (!view2.isTransient && view2.path) {
|
|
7598
8114
|
try {
|
|
7599
|
-
|
|
8115
|
+
fs14.unlinkSync(view2.path);
|
|
7600
8116
|
} catch (err) {
|
|
7601
8117
|
if (err?.code !== "ENOENT")
|
|
7602
8118
|
console.warn("[agentlife] dismiss: file unlink failed for %s: %s", surfaceId, err?.message);
|
|
@@ -7621,7 +8137,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7621
8137
|
const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
|
|
7622
8138
|
const event = typeof params?.event === "string" ? params.event.trim() : "";
|
|
7623
8139
|
if (!surfaceId || !event) {
|
|
7624
|
-
respond(false, {
|
|
8140
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId or event" });
|
|
7625
8141
|
return;
|
|
7626
8142
|
}
|
|
7627
8143
|
const agentId = getAgentId(state2, surfaceId) ?? undefined;
|
|
@@ -7633,7 +8149,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7633
8149
|
const message = typeof params?.message === "string" ? params.message : "";
|
|
7634
8150
|
const sessionKey = typeof params?.sessionKey === "string" ? params.sessionKey : "";
|
|
7635
8151
|
if (!message) {
|
|
7636
|
-
respond(false, {
|
|
8152
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing message" });
|
|
7637
8153
|
return;
|
|
7638
8154
|
}
|
|
7639
8155
|
broadcastInput(message, sessionKey);
|
|
@@ -7642,7 +8158,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7642
8158
|
api.registerGatewayMethod("agentlife.chain.get", ({ params, respond }) => {
|
|
7643
8159
|
const root = typeof params?.root === "string" ? params.root.trim() : "";
|
|
7644
8160
|
if (!root) {
|
|
7645
|
-
respond(false, {
|
|
8161
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing root" });
|
|
7646
8162
|
return;
|
|
7647
8163
|
}
|
|
7648
8164
|
if (!state2.surfaceIndex) {
|
|
@@ -7666,7 +8182,7 @@ function registerSurfacesGateway(api, state2) {
|
|
|
7666
8182
|
api.registerGatewayMethod("agentlife.action", ({ params, respond }) => {
|
|
7667
8183
|
const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
|
|
7668
8184
|
if (!surfaceId) {
|
|
7669
|
-
respond(false, {
|
|
8185
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
|
|
7670
8186
|
return;
|
|
7671
8187
|
}
|
|
7672
8188
|
const view2 = getSurface(state2, surfaceId);
|
|
@@ -7757,7 +8273,7 @@ function registerAutomationsGateway(api, state2) {
|
|
|
7757
8273
|
const name = typeof params?.name === "string" ? params.name.trim() : "";
|
|
7758
8274
|
const automationPath = typeof params?.path === "string" ? params.path.trim() : "";
|
|
7759
8275
|
if (!type || !name || !automationPath) {
|
|
7760
|
-
respond(false, {
|
|
8276
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing required fields: type, name, path" });
|
|
7761
8277
|
return;
|
|
7762
8278
|
}
|
|
7763
8279
|
const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
|
|
@@ -7765,7 +8281,7 @@ function registerAutomationsGateway(api, state2) {
|
|
|
7765
8281
|
const id = registerAutomation(state2, { surfaceId, agentId, type, name, path: automationPath });
|
|
7766
8282
|
respond(true, { id, surfaceId, agentId, type, name, path: automationPath, status: "running" });
|
|
7767
8283
|
} catch (err) {
|
|
7768
|
-
respond(false, {
|
|
8284
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "register failed" });
|
|
7769
8285
|
}
|
|
7770
8286
|
}, { scope: "operator.write" });
|
|
7771
8287
|
api.registerGatewayMethod("agentlife.automations.list", ({ params, respond }) => {
|
|
@@ -7806,7 +8322,7 @@ function registerAutomationsGateway(api, state2) {
|
|
|
7806
8322
|
}))
|
|
7807
8323
|
});
|
|
7808
8324
|
} catch (err) {
|
|
7809
|
-
respond(false, {
|
|
8325
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "list failed" });
|
|
7810
8326
|
}
|
|
7811
8327
|
}, { scope: "operator.read" });
|
|
7812
8328
|
api.registerGatewayMethod("agentlife.automations.update", ({ params, respond }) => {
|
|
@@ -7814,44 +8330,44 @@ function registerAutomationsGateway(api, state2) {
|
|
|
7814
8330
|
const id = typeof params?.id === "number" ? params.id : parseInt(params?.id, 10);
|
|
7815
8331
|
const status = typeof params?.status === "string" ? params.status.trim() : "";
|
|
7816
8332
|
if (!id || !status || !["running", "stopped", "error"].includes(status)) {
|
|
7817
|
-
respond(false, {
|
|
8333
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing id or invalid status (running|stopped|error)" });
|
|
7818
8334
|
return;
|
|
7819
8335
|
}
|
|
7820
8336
|
const db = getOrCreateHistoryDb(state2);
|
|
7821
8337
|
db.prepare("UPDATE automations SET status = ?, updatedAt = ? WHERE id = ?").run(status, Date.now(), id);
|
|
7822
8338
|
respond(true, { id, status });
|
|
7823
8339
|
} catch (err) {
|
|
7824
|
-
respond(false, {
|
|
8340
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "update failed" });
|
|
7825
8341
|
}
|
|
7826
8342
|
}, { scope: "operator.write" });
|
|
7827
8343
|
api.registerGatewayMethod("agentlife.automations.remove", ({ params, respond }) => {
|
|
7828
8344
|
try {
|
|
7829
8345
|
const id = typeof params?.id === "number" ? params.id : parseInt(params?.id, 10);
|
|
7830
8346
|
if (!id) {
|
|
7831
|
-
respond(false, {
|
|
8347
|
+
respond(false, undefined, { code: "INVALID_REQUEST", message: "missing id" });
|
|
7832
8348
|
return;
|
|
7833
8349
|
}
|
|
7834
8350
|
const db = getOrCreateHistoryDb(state2);
|
|
7835
8351
|
db.prepare("UPDATE automations SET status = 'removed', updatedAt = ? WHERE id = ?").run(Date.now(), id);
|
|
7836
8352
|
respond(true, { id, status: "removed" });
|
|
7837
8353
|
} catch (err) {
|
|
7838
|
-
respond(false, {
|
|
8354
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "remove failed" });
|
|
7839
8355
|
}
|
|
7840
8356
|
}, { scope: "operator.write" });
|
|
7841
8357
|
}
|
|
7842
8358
|
|
|
7843
8359
|
// gateway/admin.ts
|
|
7844
|
-
import * as
|
|
8360
|
+
import * as fs15 from "node:fs/promises";
|
|
7845
8361
|
import * as fsSync4 from "node:fs";
|
|
7846
|
-
import * as
|
|
7847
|
-
import * as
|
|
8362
|
+
import * as os9 from "node:os";
|
|
8363
|
+
import * as path15 from "node:path";
|
|
7848
8364
|
import { fileURLToPath } from "node:url";
|
|
7849
8365
|
var PLUGIN_VERSION = (() => {
|
|
7850
8366
|
try {
|
|
7851
|
-
const moduleDir =
|
|
8367
|
+
const moduleDir = path15.dirname(fileURLToPath(import.meta.url));
|
|
7852
8368
|
const candidates = [
|
|
7853
|
-
|
|
7854
|
-
|
|
8369
|
+
path15.join(moduleDir, "..", "package.json"),
|
|
8370
|
+
path15.join(moduleDir, "..", "..", "package.json")
|
|
7855
8371
|
];
|
|
7856
8372
|
for (const p of candidates) {
|
|
7857
8373
|
if (fsSync4.existsSync(p)) {
|
|
@@ -7900,7 +8416,7 @@ async function fetchNpmLatestVersion(force = false) {
|
|
|
7900
8416
|
return inflightFetch;
|
|
7901
8417
|
}
|
|
7902
8418
|
function pluginConfigPath() {
|
|
7903
|
-
return
|
|
8419
|
+
return path15.join(os9.homedir(), ".openclaw", "agentlife", "plugin-config.json");
|
|
7904
8420
|
}
|
|
7905
8421
|
function readPluginConfig() {
|
|
7906
8422
|
try {
|
|
@@ -7910,7 +8426,7 @@ function readPluginConfig() {
|
|
|
7910
8426
|
}
|
|
7911
8427
|
}
|
|
7912
8428
|
function writePluginConfig(config2) {
|
|
7913
|
-
const dir =
|
|
8429
|
+
const dir = path15.dirname(pluginConfigPath());
|
|
7914
8430
|
fsSync4.mkdirSync(dir, { recursive: true });
|
|
7915
8431
|
fsSync4.writeFileSync(pluginConfigPath(), JSON.stringify(config2, null, 2));
|
|
7916
8432
|
}
|
|
@@ -7968,7 +8484,7 @@ function registerAdminGateway(api, state2) {
|
|
|
7968
8484
|
cleanup: { pending: cleanupPending, failed: cleanupFailed, exhausted: cleanupExhausted }
|
|
7969
8485
|
});
|
|
7970
8486
|
} catch (err) {
|
|
7971
|
-
respond(false, {
|
|
8487
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "quality metrics unavailable" });
|
|
7972
8488
|
}
|
|
7973
8489
|
}, { scope: "operator.read" });
|
|
7974
8490
|
api.registerGatewayMethod("agentlife.health.list", ({ respond }) => {
|
|
@@ -7989,7 +8505,7 @@ function registerAdminGateway(api, state2) {
|
|
|
7989
8505
|
const rows = runDailyCycle(db, day);
|
|
7990
8506
|
respond(true, { day, rows });
|
|
7991
8507
|
} catch (err) {
|
|
7992
|
-
respond(false, {
|
|
8508
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "observability runCycle failed" });
|
|
7993
8509
|
}
|
|
7994
8510
|
}, { scope: "operator.write" });
|
|
7995
8511
|
api.registerGatewayMethod("agentlife.sweep.run", ({ respond }) => {
|
|
@@ -7997,7 +8513,7 @@ function registerAdminGateway(api, state2) {
|
|
|
7997
8513
|
const result = runDailySweep(state2);
|
|
7998
8514
|
respond(true, result);
|
|
7999
8515
|
} catch (err) {
|
|
8000
|
-
respond(false, {
|
|
8516
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "daily sweep failed" });
|
|
8001
8517
|
}
|
|
8002
8518
|
}, { scope: "operator.write" });
|
|
8003
8519
|
api.registerGatewayMethod("agentlife.bootstrap", ({ params, respond }) => {
|
|
@@ -8038,7 +8554,7 @@ function registerAdminGateway(api, state2) {
|
|
|
8038
8554
|
api.registerGatewayMethod("agentlife.uninstall", async ({ respond }) => {
|
|
8039
8555
|
try {
|
|
8040
8556
|
const cleaned = [];
|
|
8041
|
-
const baseDir = state2.agentlifeStateDir ??
|
|
8557
|
+
const baseDir = state2.agentlifeStateDir ?? path15.join(os9.homedir(), ".openclaw", "agentlife");
|
|
8042
8558
|
const identity = loadDeviceIdentity();
|
|
8043
8559
|
if (!identity) {
|
|
8044
8560
|
cleaned.push("server deprovision skipped (no device identity)");
|
|
@@ -8103,7 +8619,7 @@ function registerAdminGateway(api, state2) {
|
|
|
8103
8619
|
} catch {
|
|
8104
8620
|
cleaned.push("agent config cleanup skipped");
|
|
8105
8621
|
}
|
|
8106
|
-
const backupPath =
|
|
8622
|
+
const backupPath = path15.join(baseDir, "config-backup.json");
|
|
8107
8623
|
try {
|
|
8108
8624
|
cleaned.push(await restoreConfigFromBackup(api, backupPath));
|
|
8109
8625
|
} catch {
|
|
@@ -8125,35 +8641,34 @@ function registerAdminGateway(api, state2) {
|
|
|
8125
8641
|
dbPath,
|
|
8126
8642
|
dbPath ? `${dbPath}-wal` : null,
|
|
8127
8643
|
dbPath ? `${dbPath}-shm` : null,
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
path13.join(baseDir, "canvas-node-identity.json")
|
|
8644
|
+
path15.join(baseDir, "notification-config.json"),
|
|
8645
|
+
path15.join(baseDir, "canvas-node-identity.json")
|
|
8131
8646
|
].filter(Boolean);
|
|
8132
8647
|
for (const fp of stateFiles) {
|
|
8133
8648
|
try {
|
|
8134
|
-
await
|
|
8135
|
-
cleaned.push(`deleted ${
|
|
8649
|
+
await fs15.unlink(fp);
|
|
8650
|
+
cleaned.push(`deleted ${path15.basename(fp)}`);
|
|
8136
8651
|
} catch {}
|
|
8137
8652
|
}
|
|
8138
8653
|
if (state2.dbBaseDir) {
|
|
8139
8654
|
try {
|
|
8140
|
-
await
|
|
8655
|
+
await fs15.rm(state2.dbBaseDir, { recursive: true, force: true });
|
|
8141
8656
|
cleaned.push("deleted agent databases");
|
|
8142
8657
|
} catch {}
|
|
8143
8658
|
}
|
|
8144
8659
|
for (const agent of PROVISIONED_AGENTS) {
|
|
8145
8660
|
if (agent.existingAgent)
|
|
8146
8661
|
continue;
|
|
8147
|
-
const wsDir = agent.workspaceDir ??
|
|
8662
|
+
const wsDir = agent.workspaceDir ?? path15.join(os9.homedir(), ".openclaw", `workspace-${agent.id}`);
|
|
8148
8663
|
try {
|
|
8149
|
-
await
|
|
8664
|
+
await fs15.rm(wsDir, { recursive: true, force: true });
|
|
8150
8665
|
cleaned.push(`deleted workspace ${agent.id}`);
|
|
8151
8666
|
} catch {}
|
|
8152
8667
|
}
|
|
8153
8668
|
try {
|
|
8154
|
-
const remaining = await
|
|
8669
|
+
const remaining = await fs15.readdir(baseDir);
|
|
8155
8670
|
if (remaining.length === 0) {
|
|
8156
|
-
await
|
|
8671
|
+
await fs15.rmdir(baseDir);
|
|
8157
8672
|
cleaned.push("deleted agentlife state directory");
|
|
8158
8673
|
}
|
|
8159
8674
|
} catch {}
|
|
@@ -8164,7 +8679,7 @@ function registerAdminGateway(api, state2) {
|
|
|
8164
8679
|
});
|
|
8165
8680
|
} catch (err) {
|
|
8166
8681
|
console.error("[agentlife] uninstall failed:", err);
|
|
8167
|
-
respond(false, {
|
|
8682
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "unknown error during uninstall" });
|
|
8168
8683
|
}
|
|
8169
8684
|
});
|
|
8170
8685
|
api.registerGatewayMethod("agentlife.config.get", ({ respond }) => {
|
|
@@ -8193,11 +8708,11 @@ function registerAdminGateway(api, state2) {
|
|
|
8193
8708
|
|
|
8194
8709
|
// gateway/evolutions.ts
|
|
8195
8710
|
import * as fsSync5 from "node:fs";
|
|
8196
|
-
import * as
|
|
8197
|
-
import * as
|
|
8711
|
+
import * as os10 from "node:os";
|
|
8712
|
+
import * as path16 from "node:path";
|
|
8198
8713
|
var ALLOWED_FILENAMES = new Set(["AGENTS.md", "USER.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]);
|
|
8199
8714
|
function workspaceFilePath(agentId, filename) {
|
|
8200
|
-
return
|
|
8715
|
+
return path16.join(os10.homedir(), ".openclaw", `workspace-${agentId}`, filename);
|
|
8201
8716
|
}
|
|
8202
8717
|
function hasRegressionFlag(db, agentId, filename, snapshotAt) {
|
|
8203
8718
|
const horizon = snapshotAt + 7 * 24 * 60 * 60 * 1000;
|
|
@@ -8572,39 +9087,43 @@ function parseOffset(offset) {
|
|
|
8572
9087
|
// gateway/providers.ts
|
|
8573
9088
|
import {
|
|
8574
9089
|
upsertApiKeyProfile,
|
|
8575
|
-
upsertAuthProfile,
|
|
9090
|
+
upsertAuthProfile as upsertAuthProfile3,
|
|
8576
9091
|
buildApiKeyCredential,
|
|
8577
9092
|
applyAuthProfileConfig
|
|
8578
9093
|
} from "openclaw/plugin-sdk/provider-auth-api-key";
|
|
8579
9094
|
|
|
8580
9095
|
// gateway/config-utils.ts
|
|
8581
|
-
import * as
|
|
8582
|
-
import * as
|
|
8583
|
-
import * as
|
|
9096
|
+
import * as fs16 from "node:fs";
|
|
9097
|
+
import * as os11 from "node:os";
|
|
9098
|
+
import * as path17 from "node:path";
|
|
8584
9099
|
function configPath() {
|
|
8585
|
-
return
|
|
9100
|
+
return path17.join(os11.homedir(), ".openclaw", "openclaw.json");
|
|
8586
9101
|
}
|
|
8587
9102
|
function readConfig() {
|
|
8588
9103
|
try {
|
|
8589
|
-
return JSON.parse(
|
|
9104
|
+
return JSON.parse(fs16.readFileSync(configPath(), "utf-8"));
|
|
8590
9105
|
} catch {
|
|
8591
9106
|
return {};
|
|
8592
9107
|
}
|
|
8593
9108
|
}
|
|
8594
9109
|
function writeConfig(cfg) {
|
|
8595
|
-
|
|
9110
|
+
fs16.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
|
|
8596
9111
|
`, "utf-8");
|
|
8597
9112
|
}
|
|
8598
9113
|
|
|
8599
9114
|
// gateway/providers.ts
|
|
8600
|
-
import * as
|
|
8601
|
-
import * as
|
|
8602
|
-
import * as
|
|
9115
|
+
import * as fs17 from "node:fs";
|
|
9116
|
+
import * as os12 from "node:os";
|
|
9117
|
+
import * as path18 from "node:path";
|
|
9118
|
+
function resolveOpenClawRoot3() {
|
|
9119
|
+
const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
9120
|
+
return fromEnv ? path18.resolve(fromEnv) : path18.join(os12.homedir(), ".openclaw");
|
|
9121
|
+
}
|
|
8603
9122
|
function siblingAgentDirs() {
|
|
8604
|
-
const root =
|
|
9123
|
+
const root = path18.join(resolveOpenClawRoot3(), "agents");
|
|
8605
9124
|
let entries;
|
|
8606
9125
|
try {
|
|
8607
|
-
entries =
|
|
9126
|
+
entries = fs17.readdirSync(root, { withFileTypes: true });
|
|
8608
9127
|
} catch {
|
|
8609
9128
|
return [];
|
|
8610
9129
|
}
|
|
@@ -8612,9 +9131,9 @@ function siblingAgentDirs() {
|
|
|
8612
9131
|
for (const entry of entries) {
|
|
8613
9132
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
8614
9133
|
continue;
|
|
8615
|
-
const dir =
|
|
9134
|
+
const dir = path18.join(root, entry.name, "agent");
|
|
8616
9135
|
try {
|
|
8617
|
-
if (
|
|
9136
|
+
if (fs17.statSync(dir).isDirectory())
|
|
8618
9137
|
out.push(dir);
|
|
8619
9138
|
} catch {}
|
|
8620
9139
|
}
|
|
@@ -8684,20 +9203,20 @@ async function ensureModels(run) {
|
|
|
8684
9203
|
}
|
|
8685
9204
|
function readOAuthExpiries() {
|
|
8686
9205
|
const result = new Map;
|
|
8687
|
-
const
|
|
8688
|
-
const
|
|
8689
|
-
const
|
|
8690
|
-
const agentsDir =
|
|
9206
|
+
const fs18 = __require("node:fs");
|
|
9207
|
+
const path19 = __require("node:path");
|
|
9208
|
+
const os13 = __require("node:os");
|
|
9209
|
+
const agentsDir = path19.join(os13.homedir(), ".openclaw", "agents");
|
|
8691
9210
|
let entries;
|
|
8692
9211
|
try {
|
|
8693
|
-
entries =
|
|
9212
|
+
entries = fs18.readdirSync(agentsDir);
|
|
8694
9213
|
} catch {
|
|
8695
9214
|
return result;
|
|
8696
9215
|
}
|
|
8697
9216
|
for (const name of entries) {
|
|
8698
|
-
const filePath =
|
|
9217
|
+
const filePath = path19.join(agentsDir, name, "agent", "auth-profiles.json");
|
|
8699
9218
|
try {
|
|
8700
|
-
const raw =
|
|
9219
|
+
const raw = fs18.readFileSync(filePath, "utf-8");
|
|
8701
9220
|
const data = JSON.parse(raw);
|
|
8702
9221
|
const profiles = data?.profiles ?? {};
|
|
8703
9222
|
for (const entry of Object.values(profiles)) {
|
|
@@ -8814,20 +9333,20 @@ function registerProvidersGateway(api, state2) {
|
|
|
8814
9333
|
}));
|
|
8815
9334
|
respond(true, { providers });
|
|
8816
9335
|
} catch (err) {
|
|
8817
|
-
respond(false, {
|
|
9336
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8818
9337
|
}
|
|
8819
9338
|
}, { scope: "operator.read" });
|
|
8820
9339
|
api.registerGatewayMethod("agentlife.providers.set", async ({ params, respond }) => {
|
|
8821
9340
|
const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
|
|
8822
9341
|
const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
|
|
8823
9342
|
if (!provider)
|
|
8824
|
-
return respond(false, {
|
|
9343
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
|
|
8825
9344
|
if (!apiKey)
|
|
8826
|
-
return respond(false, {
|
|
9345
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "apiKey is empty" });
|
|
8827
9346
|
try {
|
|
8828
9347
|
const catalog = await ensureCatalog(run ?? null);
|
|
8829
9348
|
if (catalog.length > 0 && !catalog.some((e) => e.provider === provider)) {
|
|
8830
|
-
return respond(false, {
|
|
9349
|
+
return respond(false, undefined, { code: "NOT_FOUND", message: `unknown provider: ${provider}` });
|
|
8831
9350
|
}
|
|
8832
9351
|
} catch {}
|
|
8833
9352
|
try {
|
|
@@ -8835,7 +9354,7 @@ function registerProvidersGateway(api, state2) {
|
|
|
8835
9354
|
const credential = buildApiKeyCredential(provider, apiKey);
|
|
8836
9355
|
for (const agentDir of siblingAgentDirs()) {
|
|
8837
9356
|
try {
|
|
8838
|
-
|
|
9357
|
+
upsertAuthProfile3({ profileId, credential, agentDir });
|
|
8839
9358
|
} catch {}
|
|
8840
9359
|
}
|
|
8841
9360
|
const cfg = readConfig();
|
|
@@ -8848,13 +9367,13 @@ function registerProvidersGateway(api, state2) {
|
|
|
8848
9367
|
writeConfig(nextCfg);
|
|
8849
9368
|
respond(true, { provider, configured: true, profileId });
|
|
8850
9369
|
} catch (err) {
|
|
8851
|
-
respond(false, {
|
|
9370
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8852
9371
|
}
|
|
8853
9372
|
}, { scope: "operator.write" });
|
|
8854
9373
|
api.registerGatewayMethod("agentlife.providers.delete", ({ params, respond }) => {
|
|
8855
9374
|
const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
|
|
8856
9375
|
if (!provider)
|
|
8857
|
-
return respond(false, {
|
|
9376
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
|
|
8858
9377
|
try {
|
|
8859
9378
|
const cfg = readConfig();
|
|
8860
9379
|
const profiles = cfg?.auth?.profiles;
|
|
@@ -8869,9 +9388,9 @@ function registerProvidersGateway(api, state2) {
|
|
|
8869
9388
|
}
|
|
8870
9389
|
writeConfig(cfg);
|
|
8871
9390
|
for (const agentDir of siblingAgentDirs()) {
|
|
8872
|
-
const file =
|
|
9391
|
+
const file = path18.join(agentDir, "auth-profiles.json");
|
|
8873
9392
|
try {
|
|
8874
|
-
const raw =
|
|
9393
|
+
const raw = fs17.readFileSync(file, "utf-8");
|
|
8875
9394
|
const data = JSON.parse(raw);
|
|
8876
9395
|
const agentProfiles = data?.profiles;
|
|
8877
9396
|
if (!agentProfiles || typeof agentProfiles !== "object")
|
|
@@ -8888,19 +9407,19 @@ function registerProvidersGateway(api, state2) {
|
|
|
8888
9407
|
changed = true;
|
|
8889
9408
|
}
|
|
8890
9409
|
if (changed)
|
|
8891
|
-
|
|
9410
|
+
fs17.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
|
|
8892
9411
|
} catch {}
|
|
8893
9412
|
}
|
|
8894
9413
|
respond(true, { provider, configured: false });
|
|
8895
9414
|
} catch (err) {
|
|
8896
|
-
respond(false, {
|
|
9415
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8897
9416
|
}
|
|
8898
9417
|
}, { scope: "operator.write" });
|
|
8899
9418
|
api.registerGatewayMethod("agentlife.providers.test", async ({ params, respond }) => {
|
|
8900
9419
|
const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
|
|
8901
9420
|
const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
|
|
8902
9421
|
if (!provider)
|
|
8903
|
-
return respond(false, {
|
|
9422
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
|
|
8904
9423
|
if (!apiKey)
|
|
8905
9424
|
return respond(true, { ok: false, error: "apiKey required — type a key before testing" });
|
|
8906
9425
|
try {
|
|
@@ -8926,7 +9445,7 @@ function registerProvidersGateway(api, state2) {
|
|
|
8926
9445
|
});
|
|
8927
9446
|
respond(true, { models: deduped });
|
|
8928
9447
|
} catch (err) {
|
|
8929
|
-
respond(false, {
|
|
9448
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8930
9449
|
}
|
|
8931
9450
|
}, { scope: "operator.read" });
|
|
8932
9451
|
api.registerGatewayMethod("agentlife.providers.health", ({ respond }) => {
|
|
@@ -8956,44 +9475,44 @@ function registerProvidersGateway(api, state2) {
|
|
|
8956
9475
|
}
|
|
8957
9476
|
respond(true, { probes });
|
|
8958
9477
|
} catch (err) {
|
|
8959
|
-
respond(false, {
|
|
9478
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8960
9479
|
}
|
|
8961
9480
|
}, { scope: "operator.read" });
|
|
8962
9481
|
api.registerGatewayMethod("agentlife.providers.loginCodex", async ({ respond }) => {
|
|
8963
9482
|
if (!run)
|
|
8964
|
-
return respond(false, {
|
|
9483
|
+
return respond(false, undefined, { code: "UNAVAILABLE", message: "runCommand unavailable" });
|
|
8965
9484
|
try {
|
|
8966
9485
|
const result = await run(["openclaw", "capability", "model", "auth", "login", "--provider", "openai-codex"], { timeoutMs: 180000 });
|
|
8967
9486
|
const combined = `${result?.stdout ?? ""}
|
|
8968
9487
|
${result?.stderr ?? ""}`;
|
|
8969
9488
|
const urlMatch = combined.match(/https:\/\/\S+/);
|
|
8970
9489
|
if ((result?.code ?? 0) !== 0 && !urlMatch) {
|
|
8971
|
-
return respond(false, {
|
|
9490
|
+
return respond(false, undefined, { code: "FAILED", message: (result?.stderr ?? "").trim() || "codex login failed" });
|
|
8972
9491
|
}
|
|
8973
9492
|
respond(true, { url: urlMatch?.[0] ?? null, completed: (result?.code ?? 0) === 0 });
|
|
8974
9493
|
} catch (err) {
|
|
8975
|
-
respond(false, {
|
|
9494
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
8976
9495
|
}
|
|
8977
9496
|
}, { scope: "operator.write" });
|
|
8978
9497
|
}
|
|
8979
9498
|
|
|
8980
9499
|
// gateway/models-config.ts
|
|
8981
|
-
import * as
|
|
8982
|
-
import * as
|
|
8983
|
-
import * as
|
|
9500
|
+
import * as fs18 from "node:fs";
|
|
9501
|
+
import * as os13 from "node:os";
|
|
9502
|
+
import * as path19 from "node:path";
|
|
8984
9503
|
function pluginConfigPath2() {
|
|
8985
|
-
return
|
|
9504
|
+
return path19.join(os13.homedir(), ".openclaw", "agentlife", "plugin-config.json");
|
|
8986
9505
|
}
|
|
8987
9506
|
function readPluginConfig2() {
|
|
8988
9507
|
try {
|
|
8989
|
-
return JSON.parse(
|
|
9508
|
+
return JSON.parse(fs18.readFileSync(pluginConfigPath2(), "utf-8"));
|
|
8990
9509
|
} catch {
|
|
8991
9510
|
return {};
|
|
8992
9511
|
}
|
|
8993
9512
|
}
|
|
8994
9513
|
function writePluginConfig2(cfg) {
|
|
8995
|
-
|
|
8996
|
-
|
|
9514
|
+
fs18.mkdirSync(path19.dirname(pluginConfigPath2()), { recursive: true });
|
|
9515
|
+
fs18.writeFileSync(pluginConfigPath2(), JSON.stringify(cfg, null, 2) + `
|
|
8997
9516
|
`, "utf-8");
|
|
8998
9517
|
}
|
|
8999
9518
|
function registerModelsConfigGateway(api) {
|
|
@@ -9008,13 +9527,13 @@ function registerModelsConfigGateway(api) {
|
|
|
9008
9527
|
fallbacks: Array.isArray(m.fallbacks) ? m.fallbacks.filter((x) => typeof x === "string") : []
|
|
9009
9528
|
});
|
|
9010
9529
|
} catch (err) {
|
|
9011
|
-
respond(false, {
|
|
9530
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9012
9531
|
}
|
|
9013
9532
|
}, { scope: "operator.read" });
|
|
9014
9533
|
api.registerGatewayMethod("agentlife.models.defaults.setPrimary", ({ params, respond }) => {
|
|
9015
9534
|
const model = typeof params?.model === "string" ? params.model.trim() : "";
|
|
9016
9535
|
if (!model)
|
|
9017
|
-
return respond(false, {
|
|
9536
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "model is required" });
|
|
9018
9537
|
try {
|
|
9019
9538
|
const cfg = readConfig();
|
|
9020
9539
|
cfg.agents = cfg.agents ?? {};
|
|
@@ -9024,26 +9543,26 @@ function registerModelsConfigGateway(api) {
|
|
|
9024
9543
|
writeConfig(cfg);
|
|
9025
9544
|
respond(true, { ok: true });
|
|
9026
9545
|
} catch (err) {
|
|
9027
|
-
respond(false, {
|
|
9546
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9028
9547
|
}
|
|
9029
9548
|
}, { scope: "operator.write" });
|
|
9030
9549
|
api.registerGatewayMethod("agentlife.models.defaults.setInternal", ({ params, respond }) => {
|
|
9031
9550
|
const model = typeof params?.model === "string" ? params.model.trim() : "";
|
|
9032
9551
|
if (!model)
|
|
9033
|
-
return respond(false, {
|
|
9552
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "model is required" });
|
|
9034
9553
|
try {
|
|
9035
9554
|
const cfg = readPluginConfig2();
|
|
9036
9555
|
cfg.internalModel = model;
|
|
9037
9556
|
writePluginConfig2(cfg);
|
|
9038
9557
|
respond(true, { ok: true });
|
|
9039
9558
|
} catch (err) {
|
|
9040
|
-
respond(false, {
|
|
9559
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9041
9560
|
}
|
|
9042
9561
|
}, { scope: "operator.write" });
|
|
9043
9562
|
api.registerGatewayMethod("agentlife.models.fallbacks.set", ({ params, respond }) => {
|
|
9044
9563
|
const models = Array.isArray(params?.models) ? params.models.filter((x) => typeof x === "string") : null;
|
|
9045
9564
|
if (!models)
|
|
9046
|
-
return respond(false, {
|
|
9565
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "models must be an array of strings" });
|
|
9047
9566
|
try {
|
|
9048
9567
|
const cfg = readConfig();
|
|
9049
9568
|
cfg.agents = cfg.agents ?? {};
|
|
@@ -9053,7 +9572,7 @@ function registerModelsConfigGateway(api) {
|
|
|
9053
9572
|
writeConfig(cfg);
|
|
9054
9573
|
respond(true, { ok: true });
|
|
9055
9574
|
} catch (err) {
|
|
9056
|
-
respond(false, {
|
|
9575
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9057
9576
|
}
|
|
9058
9577
|
}, { scope: "operator.write" });
|
|
9059
9578
|
api.registerGatewayMethod("agentlife.agents.list", ({ respond }) => {
|
|
@@ -9067,23 +9586,23 @@ function registerModelsConfigGateway(api) {
|
|
|
9067
9586
|
})).filter((a) => a.id !== null && a.name !== null);
|
|
9068
9587
|
respond(true, { agents });
|
|
9069
9588
|
} catch (err) {
|
|
9070
|
-
respond(false, {
|
|
9589
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9071
9590
|
}
|
|
9072
9591
|
}, { scope: "operator.read" });
|
|
9073
9592
|
api.registerGatewayMethod("agentlife.agents.setModel", ({ params, respond }) => {
|
|
9074
9593
|
const agentId = typeof params?.agentId === "string" ? params.agentId.trim() : "";
|
|
9075
9594
|
if (!agentId)
|
|
9076
|
-
return respond(false, {
|
|
9595
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "agentId is required" });
|
|
9077
9596
|
const model = params?.model;
|
|
9078
9597
|
if (model !== null && typeof model !== "string") {
|
|
9079
|
-
return respond(false, {
|
|
9598
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "model must be string or null" });
|
|
9080
9599
|
}
|
|
9081
9600
|
try {
|
|
9082
9601
|
const cfg = readConfig();
|
|
9083
9602
|
const list = Array.isArray(cfg?.agents?.list) ? cfg.agents.list : [];
|
|
9084
9603
|
const idx = list.findIndex((a) => a?.id === agentId);
|
|
9085
9604
|
if (idx < 0)
|
|
9086
|
-
return respond(false, {
|
|
9605
|
+
return respond(false, undefined, { code: "NOT_FOUND", message: `agent not found: ${agentId}` });
|
|
9087
9606
|
if (model === null) {
|
|
9088
9607
|
delete list[idx].model;
|
|
9089
9608
|
} else {
|
|
@@ -9092,19 +9611,19 @@ function registerModelsConfigGateway(api) {
|
|
|
9092
9611
|
writeConfig(cfg);
|
|
9093
9612
|
respond(true, { ok: true });
|
|
9094
9613
|
} catch (err) {
|
|
9095
|
-
respond(false, {
|
|
9614
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9096
9615
|
}
|
|
9097
9616
|
}, { scope: "operator.write" });
|
|
9098
9617
|
api.registerGatewayMethod("agentlife.agents.setModel.bulk", ({ params, respond }) => {
|
|
9099
9618
|
const updates = Array.isArray(params?.updates) ? params.updates : null;
|
|
9100
9619
|
if (!updates)
|
|
9101
|
-
return respond(false, {
|
|
9620
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "updates must be an array" });
|
|
9102
9621
|
for (const u of updates) {
|
|
9103
9622
|
if (!u || typeof u.agentId !== "string" || !u.agentId.trim()) {
|
|
9104
|
-
return respond(false, {
|
|
9623
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "each update needs agentId (string)" });
|
|
9105
9624
|
}
|
|
9106
9625
|
if (u.model !== null && typeof u.model !== "string") {
|
|
9107
|
-
return respond(false, {
|
|
9626
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "each update model must be string or null" });
|
|
9108
9627
|
}
|
|
9109
9628
|
}
|
|
9110
9629
|
try {
|
|
@@ -9125,7 +9644,7 @@ function registerModelsConfigGateway(api) {
|
|
|
9125
9644
|
writeConfig(cfg);
|
|
9126
9645
|
respond(true, { ok: true, applied });
|
|
9127
9646
|
} catch (err) {
|
|
9128
|
-
respond(false, {
|
|
9647
|
+
respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
|
|
9129
9648
|
}
|
|
9130
9649
|
}, { scope: "operator.write" });
|
|
9131
9650
|
}
|
|
@@ -9137,9 +9656,12 @@ var stopObservability = null;
|
|
|
9137
9656
|
var stopDailySweep = null;
|
|
9138
9657
|
var stopQualityCheckPoller = null;
|
|
9139
9658
|
var stopCloudflared = null;
|
|
9659
|
+
function resolveOpenClawRoot4() {
|
|
9660
|
+
return process.env.OPENCLAW_STATE_DIR || path20.join(homedir17(), ".openclaw");
|
|
9661
|
+
}
|
|
9140
9662
|
function resolveInternalModel(api) {
|
|
9141
9663
|
try {
|
|
9142
|
-
const pluginCfgPath =
|
|
9664
|
+
const pluginCfgPath = path20.join(resolveOpenClawRoot4(), "agentlife", "plugin-config.json");
|
|
9143
9665
|
try {
|
|
9144
9666
|
const raw = __require("node:fs").readFileSync(pluginCfgPath, "utf-8");
|
|
9145
9667
|
const pluginCfg = JSON.parse(raw);
|
|
@@ -9165,7 +9687,24 @@ function _getState() {
|
|
|
9165
9687
|
throw new Error("plugin not registered");
|
|
9166
9688
|
return currentState;
|
|
9167
9689
|
}
|
|
9690
|
+
async function startProvisioningServices(api, state2) {
|
|
9691
|
+
const cfg = api.runtime.config.loadConfig();
|
|
9692
|
+
await provisionAgents(state2, cfg, api.runtime, console.log);
|
|
9693
|
+
await reconcileInternalSessions(state2, createdInternalSessions);
|
|
9694
|
+
await cleanupSupervisorLegacyState(state2);
|
|
9695
|
+
stopObservability = startObservabilityService(state2);
|
|
9696
|
+
stopDailySweep = startDailySweepService(state2);
|
|
9697
|
+
stopQualityCheckPoller = startQualityCheckPoller(state2);
|
|
9698
|
+
renderAllAgentWidgets(state2, api.runtime, console.log);
|
|
9699
|
+
}
|
|
9168
9700
|
function register(api) {
|
|
9701
|
+
if (!shouldStartRuntimeServices()) {
|
|
9702
|
+
console.log("[agentlife] install/inspect mode — runtime registration skipped");
|
|
9703
|
+
return;
|
|
9704
|
+
}
|
|
9705
|
+
if (shouldStartRuntimeServices()) {
|
|
9706
|
+
runInstallBootstrap();
|
|
9707
|
+
}
|
|
9169
9708
|
if (currentState) {
|
|
9170
9709
|
registerWidgetPushTool(api, currentState);
|
|
9171
9710
|
api.registerChannel({ plugin: createAgentlifeChannel(currentState.channelState) });
|
|
@@ -9175,7 +9714,8 @@ function register(api) {
|
|
|
9175
9714
|
return;
|
|
9176
9715
|
}
|
|
9177
9716
|
registered = true;
|
|
9178
|
-
const
|
|
9717
|
+
const fallbackRoot = resolveOpenClawRoot4();
|
|
9718
|
+
const fallbackDir = path20.join(fallbackRoot, "agentlife");
|
|
9179
9719
|
const state2 = {
|
|
9180
9720
|
fileSurfaceStorage: null,
|
|
9181
9721
|
surfaceIndex: null,
|
|
@@ -9187,9 +9727,9 @@ function register(api) {
|
|
|
9187
9727
|
agentDbs: new Map,
|
|
9188
9728
|
historyDb: null,
|
|
9189
9729
|
agentlifeStateDir: fallbackDir,
|
|
9190
|
-
registryFilePath:
|
|
9191
|
-
dbBaseDir:
|
|
9192
|
-
historyDbPath:
|
|
9730
|
+
registryFilePath: path20.join(fallbackDir, "agent-registry.json"),
|
|
9731
|
+
dbBaseDir: path20.join(fallbackDir, "db"),
|
|
9732
|
+
historyDbPath: path20.join(fallbackDir, "agentlife.db"),
|
|
9193
9733
|
runCommand: api.runtime.system?.runCommandWithTimeout ?? null,
|
|
9194
9734
|
enqueueSystemEvent: null,
|
|
9195
9735
|
requestHeartbeatNow: null,
|
|
@@ -9201,12 +9741,12 @@ function register(api) {
|
|
|
9201
9741
|
currentState = state2;
|
|
9202
9742
|
api.registerChannel({ plugin: createAgentlifeChannel(state2.channelState) });
|
|
9203
9743
|
console.log("[agentlife] channel registered, session format: %s", buildAgentSessionKey("example"));
|
|
9204
|
-
if (
|
|
9744
|
+
if (shouldStartRuntimeServices() && existsSync8(fallbackDir)) {
|
|
9205
9745
|
try {
|
|
9206
9746
|
const db = getOrCreateHistoryDb(state2);
|
|
9207
9747
|
state2.surfaceIndex = new SurfaceIndex(db);
|
|
9208
9748
|
state2.followupQueue = new FollowupQueue(db);
|
|
9209
|
-
state2.fileSurfaceStorage = new FileSurfaceStorage;
|
|
9749
|
+
state2.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: fallbackRoot });
|
|
9210
9750
|
} catch (e) {
|
|
9211
9751
|
console.warn("[agentlife] eager surface storage init failed:", e?.message);
|
|
9212
9752
|
}
|
|
@@ -9216,20 +9756,32 @@ function register(api) {
|
|
|
9216
9756
|
registerSurfacesService(api, state2);
|
|
9217
9757
|
api.registerService({
|
|
9218
9758
|
id: "agentlife-provisioning",
|
|
9219
|
-
start:
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
stopDailySweep = startDailySweepService(state2);
|
|
9226
|
-
stopQualityCheckPoller = startQualityCheckPoller(state2);
|
|
9227
|
-
renderAllAgentWidgets(state2, api.runtime, console.log);
|
|
9759
|
+
start: () => {
|
|
9760
|
+
if (!shouldStartRuntimeServices())
|
|
9761
|
+
return;
|
|
9762
|
+
startProvisioningServices(api, state2).catch((err) => {
|
|
9763
|
+
console.warn("[agentlife] provisioning service failed:", err?.message ?? err);
|
|
9764
|
+
});
|
|
9228
9765
|
}
|
|
9229
9766
|
});
|
|
9230
9767
|
registerConfigOptimizer(api, state2);
|
|
9231
9768
|
registerPairingServices(api);
|
|
9232
|
-
|
|
9769
|
+
api.registerService({
|
|
9770
|
+
id: "agentlife-cloudflared-supervisor",
|
|
9771
|
+
start: () => {
|
|
9772
|
+
if (!shouldStartRuntimeServices())
|
|
9773
|
+
return;
|
|
9774
|
+
if (!stopCloudflared) {
|
|
9775
|
+
stopCloudflared = startCloudflaredSupervisor();
|
|
9776
|
+
}
|
|
9777
|
+
},
|
|
9778
|
+
stop: () => {
|
|
9779
|
+
if (stopCloudflared) {
|
|
9780
|
+
stopCloudflared();
|
|
9781
|
+
stopCloudflared = null;
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
});
|
|
9233
9785
|
api.registerService({
|
|
9234
9786
|
id: "agentlife-shutdown",
|
|
9235
9787
|
start: () => {},
|
|
@@ -9254,7 +9806,15 @@ function register(api) {
|
|
|
9254
9806
|
closeAllDbs(state2);
|
|
9255
9807
|
}
|
|
9256
9808
|
});
|
|
9257
|
-
|
|
9809
|
+
api.registerService({
|
|
9810
|
+
id: "agentlife-followups",
|
|
9811
|
+
start: () => {
|
|
9812
|
+
if (!shouldStartRuntimeServices())
|
|
9813
|
+
return;
|
|
9814
|
+
initFollowupSystem(api, state2);
|
|
9815
|
+
},
|
|
9816
|
+
stop: () => stopFollowupSystem(state2)
|
|
9817
|
+
});
|
|
9258
9818
|
registerPromptStateHook(api, state2);
|
|
9259
9819
|
registerWidgetPushTool(api, state2);
|
|
9260
9820
|
registerActivityHooks(api, state2);
|
|
@@ -9270,17 +9830,17 @@ function register(api) {
|
|
|
9270
9830
|
registerProvidersGateway(api, state2);
|
|
9271
9831
|
registerModelsConfigGateway(api);
|
|
9272
9832
|
registerWebApp(api);
|
|
9273
|
-
const notifyConfigPath =
|
|
9833
|
+
const notifyConfigPath = path20.join(fallbackDir, "notification-config.json");
|
|
9274
9834
|
api.registerGatewayMethod("agentlife.notifications.register", ({ params, respond }) => {
|
|
9275
9835
|
const serverUrl = typeof params?.serverUrl === "string" ? params.serverUrl.trim() : "";
|
|
9276
9836
|
const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
|
|
9277
9837
|
if (!serverUrl || !apiKey) {
|
|
9278
|
-
return respond(false, {
|
|
9838
|
+
return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing serverUrl or apiKey" });
|
|
9279
9839
|
}
|
|
9280
9840
|
try {
|
|
9281
|
-
const { writeFileSync:
|
|
9282
|
-
|
|
9283
|
-
|
|
9841
|
+
const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync10 } = __require("node:fs");
|
|
9842
|
+
mkdirSync10(path20.dirname(notifyConfigPath), { recursive: true });
|
|
9843
|
+
writeFileSync12(notifyConfigPath, JSON.stringify({ serverUrl, apiKey }));
|
|
9284
9844
|
} catch {}
|
|
9285
9845
|
respond(true, { registered: true });
|
|
9286
9846
|
}, { scope: "operator.write" });
|