@shadowob/connector 1.1.16 → 1.1.18

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/cli.js CHANGED
@@ -7662,10 +7662,10 @@ var require_main = __commonJS({
7662
7662
  });
7663
7663
 
7664
7664
  // src/cli.ts
7665
- import { spawn, spawnSync as spawnSync2 } from "child_process";
7666
- import { chmodSync as chmodSync2, cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
7667
- import { arch, homedir as homedir2, hostname, platform } from "os";
7668
- import { dirname as dirname2, resolve as resolve2 } from "path";
7665
+ import { spawn, spawnSync as spawnSync3 } from "child_process";
7666
+ import { chmodSync as chmodSync3, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync as writeFileSync3 } from "fs";
7667
+ import { arch, homedir as homedir3, hostname, platform as platform2 } from "os";
7668
+ import { dirname as dirname3, resolve as resolve3 } from "path";
7669
7669
  import { fileURLToPath } from "url";
7670
7670
 
7671
7671
  // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
@@ -8521,20 +8521,20 @@ import { dirname, resolve } from "path";
8521
8521
 
8522
8522
  // src/cc-connect-fork.ts
8523
8523
  var CC_CONNECT_FORK_REPO = "buggyblues/cc-connect";
8524
- var CC_CONNECT_FORK_REF = "63b5d59127b3004bc7002f2d51892b1f2a91ea83";
8524
+ var CC_CONNECT_FORK_REF = "f382563cfebaef36c5d257461dfaf318161fe3ea";
8525
8525
  var CC_CONNECT_FORK_SHORT_REF = CC_CONNECT_FORK_REF.slice(0, 7);
8526
- var CC_CONNECT_FORK_PACKAGE_VERSION = "1.3.3-beta.5";
8526
+ var CC_CONNECT_FORK_PACKAGE_VERSION = "1.3.3-beta.7";
8527
8527
  var CC_CONNECT_FORK_DOCS_URL = `https://github.com/${CC_CONNECT_FORK_REPO}/blob/main/docs/shadowob.md`;
8528
8528
 
8529
8529
  // src/cc-connect-installer.ts
8530
8530
  var NAME = "cc-connect";
8531
8531
  var RELEASE_ARCHIVE_SHA256 = {
8532
- "cc-connect-v1.3.3-beta.5-darwin-amd64.tar.gz": "71677cd565f6ea79e186ffc1b842e4548faa3baeb2e3dd2ced25ab2e95c416a3",
8533
- "cc-connect-v1.3.3-beta.5-darwin-arm64.tar.gz": "c1fc5a3d4cfe6db97a5d864ea844dbfd86345b89cea3d89871330568e6eb43cf",
8534
- "cc-connect-v1.3.3-beta.5-linux-amd64.tar.gz": "812484d19733044c8c5d67d997a921e351e2ae4e9a200ce5ae258ab338400bc1",
8535
- "cc-connect-v1.3.3-beta.5-linux-arm64.tar.gz": "290110a60e905f5e25f6133f52c1b3988ab6aeff1b1199318398cfef06f81e9a",
8536
- "cc-connect-v1.3.3-beta.5-windows-amd64.zip": "a90f8a669a48412fc5896613173b5e9b3dc5fdebdee731b6f6b9d4625a200952",
8537
- "cc-connect-v1.3.3-beta.5-windows-arm64.zip": "3c5ff24769d95c42b95a717949dc6369169c029e1110ab4bdd76d12e0043d283"
8532
+ "cc-connect-v1.3.3-beta.7-darwin-amd64.tar.gz": "e5215b202924eea6d98c8dda79318e7ab5ff663e4b7a4091f316e625b87d763f",
8533
+ "cc-connect-v1.3.3-beta.7-darwin-arm64.tar.gz": "633927197755ae14f8987aaf078b9b20003aa69abfd3f7d99dd657c36812f288",
8534
+ "cc-connect-v1.3.3-beta.7-linux-amd64.tar.gz": "ad290a15ba6de0686e18f54221ab83e093bc22513dd1fb29f51c2ce2d26d003d",
8535
+ "cc-connect-v1.3.3-beta.7-linux-arm64.tar.gz": "cb4473e7c128be83c5965dc7e6ef8c4bafd3b7dce1cb2e8adb72064818b85acb",
8536
+ "cc-connect-v1.3.3-beta.7-windows-amd64.zip": "174b53ca30f3667caef640b403b442521b9b37bf45f7e1bfad3c19d07b84c581",
8537
+ "cc-connect-v1.3.3-beta.7-windows-arm64.zip": "ce32a00d2ec4f8deeefe134ba23d4bd6587651c32772773c81772739fe335713"
8538
8538
  };
8539
8539
  var PLATFORM_MAP = {
8540
8540
  darwin: "darwin",
@@ -8580,14 +8580,14 @@ function runCommand(command, args, options) {
8580
8580
  }
8581
8581
  }
8582
8582
  function platformInfo() {
8583
- const platform2 = PLATFORM_MAP[process.platform];
8583
+ const platform3 = PLATFORM_MAP[process.platform];
8584
8584
  const arch2 = ARCH_MAP[process.arch];
8585
- if (!platform2 || !arch2) {
8585
+ if (!platform3 || !arch2) {
8586
8586
  throw new Error(
8587
8587
  `Unsupported cc-connect platform: ${process.platform}/${process.arch}. Supported: linux/darwin/windows x64/arm64`
8588
8588
  );
8589
8589
  }
8590
- return { platform: platform2, arch: arch2, ext: platform2 === "windows" ? ".zip" : ".tar.gz" };
8590
+ return { platform: platform3, arch: arch2, ext: platform3 === "windows" ? ".zip" : ".tar.gz" };
8591
8591
  }
8592
8592
  function fetchBuffer(url, redirects = 5) {
8593
8593
  return new Promise((resolvePromise, reject) => {
@@ -8650,9 +8650,9 @@ function verifyReleaseChecksum(filename, data) {
8650
8650
  }
8651
8651
  }
8652
8652
  async function installFromRelease(binaryPath, options) {
8653
- const { platform: platform2, arch: arch2, ext } = platformInfo();
8653
+ const { platform: platform3, arch: arch2, ext } = platformInfo();
8654
8654
  const version = `v${CC_CONNECT_FORK_PACKAGE_VERSION}`;
8655
- const filename = `${NAME}-${version}-${platform2}-${arch2}${ext}`;
8655
+ const filename = `${NAME}-${version}-${platform3}-${arch2}${ext}`;
8656
8656
  const url = `https://github.com/${CC_CONNECT_FORK_REPO}/releases/download/${version}/${filename}`;
8657
8657
  const binDir = dirname(binaryPath);
8658
8658
  const archivePath = resolve(binDir, `_release${ext}`);
@@ -8806,6 +8806,19 @@ function ensureTrailingNewline(value) {
8806
8806
  function quoteEnv(value) {
8807
8807
  return /^[A-Za-z0-9_./:@-]+$/.test(value) ? value : JSON.stringify(value);
8808
8808
  }
8809
+ function normalizeModelProvider(provider) {
8810
+ const baseUrl = provider?.baseUrl.trim();
8811
+ const apiKey = provider?.apiKey.trim();
8812
+ const model = provider?.model.trim();
8813
+ if (!baseUrl || !apiKey || !model) return null;
8814
+ return {
8815
+ id: provider?.id?.trim() || "shadow-official",
8816
+ label: provider?.label?.trim() || "Shadow official LLM proxy",
8817
+ baseUrl,
8818
+ apiKey,
8819
+ model
8820
+ };
8821
+ }
8809
8822
  function normalizeJsonRoot(existing, label) {
8810
8823
  if (!existing.trim()) return {};
8811
8824
  const parsed = JSON.parse(existing);
@@ -8833,11 +8846,18 @@ function parseTomlRoot(existing, label) {
8833
8846
  }
8834
8847
  function mergeEnvContent(existing, values) {
8835
8848
  (0, import_dotenv.parse)(existing);
8849
+ const modelProvider = normalizeModelProvider(values.modelProvider);
8836
8850
  const updates = {
8837
8851
  SHADOW_BASE_URL: values.serverUrl,
8838
8852
  SHADOW_TOKEN: values.token,
8839
8853
  ...SHADOW_ENV_VALUES
8840
8854
  };
8855
+ if (modelProvider) {
8856
+ updates.OPENAI_COMPATIBLE_BASE_URL = modelProvider.baseUrl;
8857
+ updates.OPENAI_COMPATIBLE_API_KEY = modelProvider.apiKey;
8858
+ updates.OPENAI_COMPATIBLE_MODEL_ID = modelProvider.model;
8859
+ updates.SHADOW_MODEL_PROVIDER_ID = modelProvider.id ?? "shadow-official";
8860
+ }
8841
8861
  const seen = /* @__PURE__ */ new Set();
8842
8862
  const lines = existing.length > 0 ? existing.split(/\r?\n/) : [];
8843
8863
  const next = [];
@@ -8860,6 +8880,7 @@ function mergeEnvContent(existing, values) {
8860
8880
  }
8861
8881
  function mergeOpenClawConfigContent(existing, values) {
8862
8882
  const root = normalizeJsonRoot(existing, "OpenClaw");
8883
+ const modelProvider = normalizeModelProvider(values.modelProvider);
8863
8884
  const channels = asRecord(root.channels);
8864
8885
  const legacyShadow = asRecord(channels["openclaw-shadowob"]);
8865
8886
  const shadow = asRecord(channels.shadowob);
@@ -8884,10 +8905,27 @@ function mergeOpenClawConfigContent(existing, values) {
8884
8905
  };
8885
8906
  plugins.entries = entries;
8886
8907
  root.plugins = plugins;
8908
+ if (modelProvider) {
8909
+ const models = asRecord(root.models);
8910
+ const providers = asRecord(models.providers);
8911
+ const providerId = modelProvider.id ?? "shadow-official";
8912
+ providers[providerId] = {
8913
+ ...asRecord(providers[providerId]),
8914
+ api: "openai-completions",
8915
+ apiKey: "${env:OPENAI_COMPATIBLE_API_KEY}",
8916
+ baseUrl: modelProvider.baseUrl,
8917
+ request: { allowPrivateNetwork: true },
8918
+ models: [{ id: modelProvider.model, name: modelProvider.model }]
8919
+ };
8920
+ models.mode = models.mode ?? "merge";
8921
+ models.providers = providers;
8922
+ root.models = models;
8923
+ }
8887
8924
  return ensureTrailingNewline(JSON.stringify(root, null, 2));
8888
8925
  }
8889
8926
  function mergeHermesConfigContent(existing, values) {
8890
8927
  const root = parseYamlRoot(existing, "Hermes");
8928
+ const modelProvider = normalizeModelProvider(values.modelProvider);
8891
8929
  const plugins = asRecord(root.plugins);
8892
8930
  plugins.enabled = uniqueStrings(Array.isArray(plugins.enabled) ? plugins.enabled : [], "shadowob");
8893
8931
  root.plugins = plugins;
@@ -8909,6 +8947,15 @@ function mergeHermesConfigContent(existing, values) {
8909
8947
  }
8910
8948
  };
8911
8949
  root.platforms = platforms;
8950
+ if (modelProvider) {
8951
+ const model = asRecord(root.model);
8952
+ root.model = {
8953
+ ...model,
8954
+ default: model.model ?? model.default ?? modelProvider.model,
8955
+ provider: model.provider ?? "custom",
8956
+ base_url: model.base_url ?? modelProvider.baseUrl
8957
+ };
8958
+ }
8912
8959
  return ensureTrailingNewline((0, import_yaml.stringify)(root));
8913
8960
  }
8914
8961
  function asTomlTable(value) {
@@ -8945,6 +8992,27 @@ function mergeCcConnectConfigContent(existing, values) {
8945
8992
  ...agentOptions,
8946
8993
  work_dir: values.workDir
8947
8994
  };
8995
+ const modelProvider = normalizeModelProvider(values.modelProvider);
8996
+ if (modelProvider) {
8997
+ agent.options = {
8998
+ ...asTomlTable(agent.options),
8999
+ provider: modelProvider.id ?? "shadow-official",
9000
+ model: modelProvider.model
9001
+ };
9002
+ const providers = tomlArray(agent.providers);
9003
+ const providerId = modelProvider.id ?? "shadow-official";
9004
+ let provider = providers.find((item) => item.name === providerId);
9005
+ if (!provider) {
9006
+ provider = {};
9007
+ providers.push(provider);
9008
+ }
9009
+ provider.name = providerId;
9010
+ provider.api_key = modelProvider.apiKey;
9011
+ provider.base_url = modelProvider.baseUrl;
9012
+ provider.model = modelProvider.model;
9013
+ provider.models = [{ model: modelProvider.model }];
9014
+ agent.providers = providers;
9015
+ }
8948
9016
  project.agent = agent;
8949
9017
  const platforms = tomlArray(project.platforms);
8950
9018
  let shadowPlatform = platforms.find((item) => item.type === "shadowob");
@@ -9154,9 +9222,35 @@ var normalizeServerUrl = (value) => {
9154
9222
  return trimmed.endsWith("/api") ? trimmed.slice(0, -4) : trimmed.replace(/\/$/, "");
9155
9223
  };
9156
9224
  var tokenOrPlaceholder = (token) => token.trim() || "<BUDDY_TOKEN>";
9225
+ function normalizeModelProvider2(input) {
9226
+ const baseUrl = input.modelProvider?.baseUrl.trim();
9227
+ const apiKey = input.modelProvider?.apiKey.trim();
9228
+ const model = input.modelProvider?.model.trim();
9229
+ if (!baseUrl || !apiKey || !model) return null;
9230
+ return {
9231
+ id: input.modelProvider?.id?.trim() || "shadow-official",
9232
+ label: input.modelProvider?.label?.trim() || "Shadow official LLM proxy",
9233
+ baseUrl,
9234
+ apiKey,
9235
+ model
9236
+ };
9237
+ }
9238
+ function modelProviderEnvLines(provider) {
9239
+ if (!provider) return [];
9240
+ return [
9241
+ `OPENAI_COMPATIBLE_BASE_URL=${shellQuote(provider.baseUrl)}`,
9242
+ `OPENAI_COMPATIBLE_API_KEY=${shellQuote(provider.apiKey)}`,
9243
+ `OPENAI_COMPATIBLE_MODEL_ID=${shellQuote(provider.model)}`,
9244
+ `SHADOW_MODEL_PROVIDER_ID=${shellQuote(provider.id ?? "shadow-official")}`
9245
+ ];
9246
+ }
9247
+ function modelProviderCapabilities(provider, capabilities) {
9248
+ return provider ? [...capabilities, "officialModelProvider"] : capabilities;
9249
+ }
9157
9250
  function buildOpenClawPlan(input) {
9158
9251
  const token = tokenOrPlaceholder(input.token);
9159
9252
  const serverUrl = normalizeServerUrl(input.serverUrl);
9253
+ const modelProvider = normalizeModelProvider2(input);
9160
9254
  const jsonConfig = JSON.stringify(
9161
9255
  {
9162
9256
  channels: {
@@ -9164,7 +9258,21 @@ function buildOpenClawPlan(input) {
9164
9258
  token,
9165
9259
  serverUrl
9166
9260
  }
9167
- }
9261
+ },
9262
+ ...modelProvider ? {
9263
+ models: {
9264
+ mode: "merge",
9265
+ providers: {
9266
+ [modelProvider.id ?? "shadow-official"]: {
9267
+ api: "openai-completions",
9268
+ apiKey: "${env:OPENAI_COMPATIBLE_API_KEY}",
9269
+ baseUrl: modelProvider.baseUrl,
9270
+ request: { allowPrivateNetwork: true },
9271
+ models: [{ id: modelProvider.model, name: modelProvider.model }]
9272
+ }
9273
+ }
9274
+ }
9275
+ } : {}
9168
9276
  },
9169
9277
  null,
9170
9278
  2
@@ -9210,6 +9318,7 @@ function buildOpenClawPlan(input) {
9210
9318
  "",
9211
9319
  `Preferred one-line setup: ${connectCommand}`,
9212
9320
  "The connector installs/configures the Shadow CLI, official Shadow skill files, and the Buddy profile before applying the OpenClaw channel config.",
9321
+ modelProvider ? `It also configures ${modelProvider.label ?? "Shadow official LLM proxy"} as an OpenAI-compatible model provider (${modelProvider.model}).` : "",
9213
9322
  "",
9214
9323
  "Run these steps in order:",
9215
9324
  ...commands.map((item, index) => `${index + 1}. ${item.command}`),
@@ -9217,7 +9326,7 @@ function buildOpenClawPlan(input) {
9217
9326
  "Confirm each step and then verify the gateway is running."
9218
9327
  ].join("\n"),
9219
9328
  docsUrl: "/product/index.html",
9220
- capabilities: [
9329
+ capabilities: modelProviderCapabilities(modelProvider, [
9221
9330
  "channelMessages",
9222
9331
  "dms",
9223
9332
  "threads",
@@ -9238,18 +9347,20 @@ function buildOpenClawPlan(input) {
9238
9347
  "notifications",
9239
9348
  "officialSkills",
9240
9349
  "cronTasks"
9241
- ]
9350
+ ])
9242
9351
  };
9243
9352
  }
9244
9353
  function buildHermesPlan(input) {
9245
9354
  const token = tokenOrPlaceholder(input.token);
9246
9355
  const serverUrl = normalizeServerUrl(input.serverUrl);
9356
+ const modelProvider = normalizeModelProvider2(input);
9247
9357
  const envBlock = [
9248
9358
  `SHADOW_BASE_URL=${shellQuote(serverUrl)}`,
9249
9359
  `SHADOW_TOKEN=${shellQuote(token)}`,
9250
9360
  "SHADOW_ALLOW_ALL_USERS=true",
9251
9361
  "SHADOW_HEARTBEAT_INTERVAL_SECONDS=30",
9252
- `SHADOW_SLASH_COMMANDS_JSON=${shellQuote("[]")}`
9362
+ `SHADOW_SLASH_COMMANDS_JSON=${shellQuote("[]")}`,
9363
+ ...modelProviderEnvLines(modelProvider)
9253
9364
  ].join("\n");
9254
9365
  const yamlConfig = [
9255
9366
  "plugins:",
@@ -9266,7 +9377,14 @@ function buildHermesPlan(input) {
9266
9377
  " rest_only: false",
9267
9378
  " catchup_minutes: 0",
9268
9379
  " download_media: true",
9269
- " slash_commands: []"
9380
+ " slash_commands: []",
9381
+ ...modelProvider ? [
9382
+ "",
9383
+ "model:",
9384
+ ` default: "${modelProvider.model}"`,
9385
+ " provider: custom",
9386
+ ` base_url: "${modelProvider.baseUrl}"`
9387
+ ] : []
9270
9388
  ].join("\n");
9271
9389
  const commands = [
9272
9390
  {
@@ -9310,10 +9428,11 @@ function buildHermesPlan(input) {
9310
9428
  `Buddy token: ${token}`,
9311
9429
  "",
9312
9430
  `Preferred one-line setup: ${connectCommand}`,
9313
- "The connector installs/configures the Shadow CLI, official Shadow skill files, and the Buddy profile before writing Hermes config. The plugin resolves the Buddy agent id and channel policy from Shadow at runtime."
9431
+ "The connector installs/configures the Shadow CLI, official Shadow skill files, and the Buddy profile before writing Hermes config. The plugin resolves the Buddy agent id and channel policy from Shadow at runtime.",
9432
+ modelProvider ? `It also configures Hermes custom model endpoint ${modelProvider.baseUrl} with model ${modelProvider.model}.` : ""
9314
9433
  ].join("\n"),
9315
9434
  docsUrl: "https://hermes-agent.nousresearch.com/docs/user-guide/messaging",
9316
- capabilities: [
9435
+ capabilities: modelProviderCapabilities(modelProvider, [
9317
9436
  "channelMessages",
9318
9437
  "dms",
9319
9438
  "threads",
@@ -9330,7 +9449,7 @@ function buildHermesPlan(input) {
9330
9449
  "shadowCliLogin",
9331
9450
  "notifications",
9332
9451
  "officialSkills"
9333
- ]
9452
+ ])
9334
9453
  };
9335
9454
  }
9336
9455
  function buildCcConnectPlan(input) {
@@ -9339,6 +9458,7 @@ function buildCcConnectPlan(input) {
9339
9458
  const projectName = input.projectName?.trim() || DEFAULT_PROJECT_NAME;
9340
9459
  const workDir = input.workDir?.trim() || DEFAULT_WORK_DIR;
9341
9460
  const agentType = input.agentType?.trim() || DEFAULT_CC_AGENT;
9461
+ const modelProvider = normalizeModelProvider2(input);
9342
9462
  const tomlConfig = [
9343
9463
  'language = "zh"',
9344
9464
  "",
@@ -9350,6 +9470,19 @@ function buildCcConnectPlan(input) {
9350
9470
  "",
9351
9471
  "[projects.agent.options]",
9352
9472
  `work_dir = "${workDir}"`,
9473
+ ...modelProvider ? [
9474
+ `provider = "${modelProvider.id ?? "shadow-official"}"`,
9475
+ `model = "${modelProvider.model}"`,
9476
+ "",
9477
+ "[[projects.agent.providers]]",
9478
+ `name = "${modelProvider.id ?? "shadow-official"}"`,
9479
+ `api_key = "${modelProvider.apiKey}"`,
9480
+ `base_url = "${modelProvider.baseUrl}"`,
9481
+ `model = "${modelProvider.model}"`,
9482
+ "",
9483
+ "[[projects.agent.providers.models]]",
9484
+ `model = "${modelProvider.model}"`
9485
+ ] : [],
9353
9486
  "",
9354
9487
  "[[projects.platforms]]",
9355
9488
  'type = "shadowob"',
@@ -9402,10 +9535,11 @@ function buildCcConnectPlan(input) {
9402
9535
  `Agent type: ${agentType}`,
9403
9536
  "",
9404
9537
  `Preferred one-line setup: ${startCommand}`,
9405
- `Install ${CC_CONNECT_FORK_REPO}@${CC_CONNECT_FORK_SHORT_REF}, install/configure the Shadow CLI and official Shadow skill files, add the TOML platform block, and start cc-connect.`
9538
+ `Install ${CC_CONNECT_FORK_REPO}@${CC_CONNECT_FORK_SHORT_REF}, install/configure the Shadow CLI and official Shadow skill files, add the TOML platform block, and start cc-connect.`,
9539
+ modelProvider ? `Configure ${modelProvider.label ?? "Shadow official LLM proxy"} as provider ${modelProvider.id ?? "shadow-official"} for ${agentType}.` : ""
9406
9540
  ].join("\n"),
9407
9541
  docsUrl: CC_CONNECT_FORK_DOCS_URL,
9408
- capabilities: [
9542
+ capabilities: modelProviderCapabilities(modelProvider, [
9409
9543
  "channelMessages",
9410
9544
  "dms",
9411
9545
  "attachments",
@@ -9420,7 +9554,7 @@ function buildCcConnectPlan(input) {
9420
9554
  "multiAgentBinding",
9421
9555
  "shadowCliLogin",
9422
9556
  "notifications"
9423
- ]
9557
+ ])
9424
9558
  };
9425
9559
  }
9426
9560
  function createConnectorPlan(input) {
@@ -9430,6 +9564,287 @@ function createConnectorPlan(input) {
9430
9564
  throw new Error(`Unsupported connector target: ${String(input.target)}`);
9431
9565
  }
9432
9566
 
9567
+ // src/toolchain.ts
9568
+ import { spawnSync as spawnSync2 } from "child_process";
9569
+ import { createHash as createHash2 } from "crypto";
9570
+ import {
9571
+ chmodSync as chmodSync2,
9572
+ existsSync as existsSync2,
9573
+ mkdirSync as mkdirSync2,
9574
+ readdirSync as readdirSync2,
9575
+ renameSync as renameSync2,
9576
+ rmSync as rmSync2,
9577
+ writeFileSync as writeFileSync2
9578
+ } from "fs";
9579
+ import { get as httpsGet2 } from "https";
9580
+ import { homedir as homedir2, platform } from "os";
9581
+ import { dirname as dirname2, resolve as resolve2 } from "path";
9582
+ var CONNECTOR_MANAGED_NODE_VERSION = process.env.SHADOW_CONNECTOR_NODE_VERSION?.trim() || "22.16.0";
9583
+ var PATH_KEY = process.platform === "win32" ? "Path" : "PATH";
9584
+ var NODE_PLATFORM = {
9585
+ darwin: "darwin",
9586
+ linux: "linux",
9587
+ win32: "win"
9588
+ };
9589
+ var NODE_ARCH = {
9590
+ x64: "x64",
9591
+ arm64: "arm64"
9592
+ };
9593
+ var loginShellPath;
9594
+ var cachedNvmBinDirs;
9595
+ var cachedConnectorPath;
9596
+ function expandHome2(value) {
9597
+ return value.startsWith("~/") ? resolve2(homedir2(), value.slice(2)) : resolve2(value);
9598
+ }
9599
+ function connectorHome() {
9600
+ const override = process.env.SHADOW_CONNECTOR_HOME?.trim();
9601
+ return override ? expandHome2(override) : resolve2(homedir2(), ".shadowob/connector");
9602
+ }
9603
+ function managedNodeRoot() {
9604
+ return resolve2(connectorHome(), "node", `v${CONNECTOR_MANAGED_NODE_VERSION}`);
9605
+ }
9606
+ function managedNodeBinDir() {
9607
+ return process.platform === "win32" ? managedNodeRoot() : resolve2(managedNodeRoot(), "bin");
9608
+ }
9609
+ function nodeGlobalRoot() {
9610
+ return resolve2(connectorHome(), "node-global");
9611
+ }
9612
+ function nodeGlobalBinDir() {
9613
+ return process.platform === "win32" ? nodeGlobalRoot() : resolve2(nodeGlobalRoot(), "bin");
9614
+ }
9615
+ function splitPath(value) {
9616
+ return (value ?? "").split(process.platform === "win32" ? ";" : ":").filter(Boolean);
9617
+ }
9618
+ function dedupePaths(paths) {
9619
+ const seen = /* @__PURE__ */ new Set();
9620
+ const result = [];
9621
+ for (const raw of paths) {
9622
+ const item = raw.trim();
9623
+ if (!item || seen.has(item)) continue;
9624
+ seen.add(item);
9625
+ result.push(item);
9626
+ }
9627
+ return result;
9628
+ }
9629
+ function readLoginShellPath() {
9630
+ if (process.env.SHADOW_CONNECTOR_SKIP_LOGIN_SHELL === "1") return [];
9631
+ if (loginShellPath !== void 0) return splitPath(loginShellPath ?? "");
9632
+ const shells = dedupePaths(
9633
+ [process.env.SHELL, "/bin/zsh", "/bin/bash"].filter(
9634
+ (item) => Boolean(item?.trim())
9635
+ )
9636
+ );
9637
+ for (const shell of shells) {
9638
+ const result = spawnSync2(shell, ["-lc", 'printf %s "$PATH"'], {
9639
+ encoding: "utf8",
9640
+ stdio: ["ignore", "pipe", "ignore"],
9641
+ timeout: 1800
9642
+ });
9643
+ if (result.status === 0 && result.stdout.trim()) {
9644
+ loginShellPath = result.stdout.trim();
9645
+ return splitPath(loginShellPath);
9646
+ }
9647
+ }
9648
+ loginShellPath = null;
9649
+ return [];
9650
+ }
9651
+ function nvmBinDirs() {
9652
+ if (cachedNvmBinDirs) return cachedNvmBinDirs;
9653
+ const roots = dedupePaths(
9654
+ [process.env.NVM_DIR, resolve2(homedir2(), ".nvm")].filter((item) => Boolean(item?.trim())).map(expandHome2)
9655
+ );
9656
+ const bins = [];
9657
+ if (process.env.NVM_BIN?.trim()) bins.push(process.env.NVM_BIN.trim());
9658
+ for (const root of roots) {
9659
+ const versionsDir = resolve2(root, "versions/node");
9660
+ if (!existsSync2(versionsDir)) continue;
9661
+ try {
9662
+ const versions = readdirSync2(versionsDir).filter((entry) => /^v?\d+\.\d+\.\d+/.test(entry)).sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
9663
+ for (const version of versions) bins.push(resolve2(versionsDir, version, "bin"));
9664
+ } catch {
9665
+ }
9666
+ }
9667
+ cachedNvmBinDirs = bins;
9668
+ return bins;
9669
+ }
9670
+ function commonBinDirs() {
9671
+ return [
9672
+ resolve2(homedir2(), ".local/bin"),
9673
+ nodeGlobalBinDir(),
9674
+ managedNodeBinDir(),
9675
+ resolve2(homedir2(), ".npm-global/bin"),
9676
+ resolve2(homedir2(), ".npm/bin"),
9677
+ resolve2(homedir2(), ".volta/bin"),
9678
+ resolve2(homedir2(), ".bun/bin"),
9679
+ resolve2(homedir2(), ".deno/bin"),
9680
+ resolve2(homedir2(), ".cargo/bin"),
9681
+ "/opt/homebrew/bin",
9682
+ "/opt/homebrew/sbin",
9683
+ "/usr/local/bin",
9684
+ "/usr/local/sbin",
9685
+ "/usr/bin",
9686
+ "/bin",
9687
+ "/usr/sbin",
9688
+ "/sbin"
9689
+ ];
9690
+ }
9691
+ function connectorPath(env = process.env) {
9692
+ if (env === process.env && cachedConnectorPath) return cachedConnectorPath;
9693
+ const value = dedupePaths([
9694
+ ...commonBinDirs(),
9695
+ ...nvmBinDirs(),
9696
+ ...readLoginShellPath(),
9697
+ ...splitPath(env.PATH ?? env.Path)
9698
+ ]).join(process.platform === "win32" ? ";" : ":");
9699
+ if (env === process.env) cachedConnectorPath = value;
9700
+ return value;
9701
+ }
9702
+ function connectorProcessEnv(env = process.env) {
9703
+ const next = {
9704
+ ...env,
9705
+ SHADOW_CONNECTOR_HOME: connectorHome(),
9706
+ NPM_CONFIG_PREFIX: nodeGlobalRoot(),
9707
+ npm_config_prefix: nodeGlobalRoot()
9708
+ };
9709
+ next[PATH_KEY] = connectorPath(env);
9710
+ next.PATH = next[PATH_KEY];
9711
+ return next;
9712
+ }
9713
+ function findCommandOnConnectorPath(command, env = process.env) {
9714
+ if (command.includes("/") || process.platform === "win32" && command.includes("\\")) {
9715
+ return existsSync2(command) ? command : null;
9716
+ }
9717
+ const extensions = process.platform === "win32" ? splitPath(env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").map((item) => item.toLowerCase()) : [""];
9718
+ for (const dir of splitPath(connectorPath(env))) {
9719
+ for (const ext of extensions) {
9720
+ const candidate = resolve2(dir, process.platform === "win32" ? `${command}${ext}` : command);
9721
+ if (existsSync2(candidate)) return candidate;
9722
+ }
9723
+ }
9724
+ return null;
9725
+ }
9726
+ function commandExistsOnConnectorPath(command, _args = ["--version"]) {
9727
+ return Boolean(findCommandOnConnectorPath(command));
9728
+ }
9729
+ function nodeAssetInfo() {
9730
+ const nodePlatform = NODE_PLATFORM[process.platform];
9731
+ const nodeArch = NODE_ARCH[process.arch];
9732
+ if (!nodePlatform || !nodeArch) {
9733
+ throw new Error(`Unsupported managed Node platform: ${platform()}/${process.arch}`);
9734
+ }
9735
+ const ext = nodePlatform === "win" ? ".zip" : ".tar.xz";
9736
+ const filename = `node-v${CONNECTOR_MANAGED_NODE_VERSION}-${nodePlatform}-${nodeArch}${ext}`;
9737
+ const baseUrl = `https://nodejs.org/dist/v${CONNECTOR_MANAGED_NODE_VERSION}`;
9738
+ return {
9739
+ filename,
9740
+ url: `${baseUrl}/${filename}`,
9741
+ shasumsUrl: `${baseUrl}/SHASUMS256.txt`,
9742
+ ext
9743
+ };
9744
+ }
9745
+ function fetchBuffer2(url, redirects = 5) {
9746
+ return new Promise((resolvePromise, reject) => {
9747
+ if (redirects <= 0) {
9748
+ reject(new Error(`Too many redirects for ${url}`));
9749
+ return;
9750
+ }
9751
+ const request = httpsGet2(url, { headers: { "User-Agent": "shadowob-connector" } }, (res) => {
9752
+ const location = res.headers.location;
9753
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && location) {
9754
+ res.resume();
9755
+ resolvePromise(fetchBuffer2(new URL(location, url).toString(), redirects - 1));
9756
+ return;
9757
+ }
9758
+ if (res.statusCode !== 200) {
9759
+ res.resume();
9760
+ reject(new Error(`HTTP ${res.statusCode ?? "unknown"} for ${url}`));
9761
+ return;
9762
+ }
9763
+ const chunks = [];
9764
+ res.on("data", (chunk) => chunks.push(chunk));
9765
+ res.on("end", () => resolvePromise(Buffer.concat(chunks)));
9766
+ res.on("error", reject);
9767
+ });
9768
+ request.on("error", reject);
9769
+ });
9770
+ }
9771
+ function verifyNodeArchive(filename, archive, shasums) {
9772
+ const line = shasums.split(/\r?\n/).find(
9773
+ (entry) => entry.trim().endsWith(` ${filename}`) || entry.trim().endsWith(` ${filename}`)
9774
+ );
9775
+ const expected = line?.trim().split(/\s+/)[0];
9776
+ if (!expected) throw new Error(`No Node.js SHA-256 found for ${filename}`);
9777
+ const actual = createHash2("sha256").update(archive).digest("hex");
9778
+ if (actual !== expected) {
9779
+ throw new Error(`Node.js SHA-256 mismatch for ${filename}: expected ${expected}, got ${actual}`);
9780
+ }
9781
+ }
9782
+ function runExtract(command, args) {
9783
+ const result = spawnSync2(command, args, { stdio: "ignore" });
9784
+ if (result.status !== 0) {
9785
+ throw new Error(`Failed to extract managed Node.js archive with ${command}`);
9786
+ }
9787
+ }
9788
+ function extractNodeArchive(archivePath, outputDir, ext) {
9789
+ if (ext === ".tar.xz") {
9790
+ runExtract("tar", ["-xJf", archivePath, "-C", outputDir]);
9791
+ return;
9792
+ }
9793
+ const result = spawnSync2(
9794
+ "powershell",
9795
+ ["-NoProfile", "-Command", `Expand-Archive -Force '${archivePath}' '${outputDir}'`],
9796
+ {
9797
+ stdio: "ignore"
9798
+ }
9799
+ );
9800
+ if (result.status !== 0) runExtract("unzip", ["-q", archivePath, "-d", outputDir]);
9801
+ }
9802
+ async function ensureManagedNodeRuntime(options) {
9803
+ const root = managedNodeRoot();
9804
+ const binDir = managedNodeBinDir();
9805
+ const nodeBinary = resolve2(binDir, process.platform === "win32" ? "node.exe" : "node");
9806
+ const npmBinary = resolve2(binDir, process.platform === "win32" ? "npm.cmd" : "npm");
9807
+ if (existsSync2(nodeBinary) && existsSync2(npmBinary)) return { binDir, root };
9808
+ if (options.dryRun) {
9809
+ options.log?.(`[dry-run] install Node.js v${CONNECTOR_MANAGED_NODE_VERSION} -> ${root}`);
9810
+ return { binDir, root };
9811
+ }
9812
+ const asset = nodeAssetInfo();
9813
+ const parent = dirname2(root);
9814
+ const tmpDir = resolve2(parent, `_node-${process.pid}-${Date.now()}`);
9815
+ const archivePath = resolve2(parent, asset.filename);
9816
+ options.log?.(`[toolchain] Installing managed Node.js v${CONNECTOR_MANAGED_NODE_VERSION}`);
9817
+ mkdirSync2(parent, { recursive: true });
9818
+ const [archive, shasums] = await Promise.all([
9819
+ fetchBuffer2(asset.url),
9820
+ fetchBuffer2(asset.shasumsUrl).then((buffer) => buffer.toString("utf8"))
9821
+ ]);
9822
+ verifyNodeArchive(asset.filename, archive, shasums);
9823
+ rmSync2(tmpDir, { recursive: true, force: true });
9824
+ mkdirSync2(tmpDir, { recursive: true });
9825
+ writeFileSync2(archivePath, archive);
9826
+ try {
9827
+ extractNodeArchive(archivePath, tmpDir, asset.ext);
9828
+ const extracted = readdirSync2(tmpDir, { withFileTypes: true }).find(
9829
+ (entry) => entry.isDirectory()
9830
+ );
9831
+ if (!extracted) throw new Error("Managed Node.js archive did not contain a directory");
9832
+ rmSync2(root, { recursive: true, force: true });
9833
+ renameSync2(resolve2(tmpDir, extracted.name), root);
9834
+ if (process.platform !== "win32") {
9835
+ chmodSync2(nodeBinary, 493);
9836
+ chmodSync2(npmBinary, 493);
9837
+ }
9838
+ } finally {
9839
+ rmSync2(archivePath, { force: true });
9840
+ rmSync2(tmpDir, { recursive: true, force: true });
9841
+ }
9842
+ return { binDir, root };
9843
+ }
9844
+ function shellCommandNeedsNpm(command) {
9845
+ return /(^|[;&|()\s])(?:npm|npx)(?:\.cmd)?(?:\s|$)/.test(command);
9846
+ }
9847
+
9433
9848
  // src/cli.ts
9434
9849
  var TARGETS = /* @__PURE__ */ new Set(["openclaw", "hermes", "cc-connect"]);
9435
9850
  var COMMANDS = /* @__PURE__ */ new Set([
@@ -9486,6 +9901,10 @@ function usage() {
9486
9901
  " --work-dir-map-file <path> Daemon-local JSON map for Buddy/runtime work directories",
9487
9902
  " --project-name <name> cc-connect project name",
9488
9903
  " --agent-type <type> cc-connect agent type, default codex",
9904
+ " --model-provider-base-url <url> OpenAI-compatible model provider base URL",
9905
+ " --model-provider-api-key <key> OpenAI-compatible model provider API key",
9906
+ " --model-provider-model <model> OpenAI-compatible model id",
9907
+ " --model-provider-id <id> Model provider id, default shadow-official",
9489
9908
  " --json Print the full plan as JSON",
9490
9909
  " --force Overwrite target config files when needed",
9491
9910
  " --install Install connector runtime dependencies",
@@ -9530,6 +9949,11 @@ function parseArgs(args) {
9530
9949
  workDirMapFile: readOption(optionArgs, "--work-dir-map-file"),
9531
9950
  projectName: readOption(optionArgs, "--project-name"),
9532
9951
  agentType: readOption(optionArgs, "--agent-type"),
9952
+ modelProviderId: readOption(optionArgs, "--model-provider-id"),
9953
+ modelProviderLabel: readOption(optionArgs, "--model-provider-label"),
9954
+ modelProviderBaseUrl: readOption(optionArgs, "--model-provider-base-url"),
9955
+ modelProviderApiKey: readOption(optionArgs, "--model-provider-api-key"),
9956
+ modelProviderModel: readOption(optionArgs, "--model-provider-model"),
9533
9957
  json: hasFlag(optionArgs, "--json"),
9534
9958
  force: hasFlag(optionArgs, "--force"),
9535
9959
  install,
@@ -9541,7 +9965,11 @@ function parseArgs(args) {
9541
9965
  }
9542
9966
  function printPlan(options) {
9543
9967
  const target = requireTarget(options);
9544
- const plan = createConnectorPlan({ ...options, target });
9968
+ const plan = createConnectorPlan({
9969
+ ...options,
9970
+ target,
9971
+ modelProvider: modelProviderFromOptions(options)
9972
+ });
9545
9973
  if (options.json) {
9546
9974
  console.log(JSON.stringify(plan, null, 2));
9547
9975
  return;
@@ -9557,19 +9985,19 @@ function printPlan(options) {
9557
9985
  console.log(block.content);
9558
9986
  }
9559
9987
  }
9560
- function runShell(command, dryRun) {
9988
+ function runShell(command, dryRun, env = connectorProcessEnv()) {
9561
9989
  if (dryRun) {
9562
9990
  console.log(`[dry-run] ${command}`);
9563
9991
  return;
9564
9992
  }
9565
- const result = spawnSync2(command, { shell: true, stdio: "inherit" });
9993
+ const result = spawnSync3(command, { shell: true, stdio: "inherit", env });
9566
9994
  if (result.status !== 0) {
9567
9995
  throw new Error(`Command failed with exit code ${result.status ?? "unknown"}: ${command}`);
9568
9996
  }
9569
9997
  }
9570
- function runShellQuiet(command, dryRun) {
9998
+ function runShellQuiet(command, dryRun, env = connectorProcessEnv()) {
9571
9999
  if (dryRun) return;
9572
- const result = spawnSync2(command, { shell: true, encoding: "utf8" });
10000
+ const result = spawnSync3(command, { shell: true, encoding: "utf8", env });
9573
10001
  if (result.status !== 0) {
9574
10002
  const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
9575
10003
  throw new Error(
@@ -9577,13 +10005,19 @@ function runShellQuiet(command, dryRun) {
9577
10005
  );
9578
10006
  }
9579
10007
  }
10008
+ async function envForShellCommand(command, dryRun) {
10009
+ if (shellCommandNeedsNpm(command) && !commandExists("npm")) {
10010
+ await ensureManagedNodeRuntime({ dryRun, log: (message) => console.log(message) });
10011
+ }
10012
+ return connectorProcessEnv();
10013
+ }
9580
10014
  function runBinary(binaryPath, args, dryRun) {
9581
10015
  const rendered = [binaryPath, ...args].map((arg) => /^[A-Za-z0-9_./:@=-]+$/.test(arg) ? arg : JSON.stringify(arg)).join(" ");
9582
10016
  if (dryRun) {
9583
10017
  console.log(`[dry-run] ${rendered}`);
9584
10018
  return;
9585
10019
  }
9586
- const result = spawnSync2(binaryPath, args, { stdio: "inherit" });
10020
+ const result = spawnSync3(binaryPath, args, { stdio: "inherit", env: connectorProcessEnv() });
9587
10021
  if (result.status !== 0) {
9588
10022
  throw new Error(`Command failed with exit code ${result.status ?? "unknown"}: ${rendered}`);
9589
10023
  }
@@ -9593,21 +10027,21 @@ function writeFile(path, content, dryRun) {
9593
10027
  console.log(`[dry-run] write ${path}`);
9594
10028
  return;
9595
10029
  }
9596
- mkdirSync2(dirname2(path), { recursive: true });
9597
- writeFileSync2(path, content.endsWith("\n") ? content : `${content}
10030
+ mkdirSync3(dirname3(path), { recursive: true });
10031
+ writeFileSync3(path, content.endsWith("\n") ? content : `${content}
9598
10032
  `);
9599
10033
  }
9600
10034
  function packageRoot() {
9601
- return resolve2(dirname2(fileURLToPath(import.meta.url)), "..");
10035
+ return resolve3(dirname3(fileURLToPath(import.meta.url)), "..");
9602
10036
  }
9603
- function expandHome2(value) {
9604
- return value.startsWith("~/") ? resolve2(homedir2(), value.slice(2)) : resolve2(value);
10037
+ function expandHome3(value) {
10038
+ return value.startsWith("~/") ? resolve3(homedir3(), value.slice(2)) : resolve3(value);
9605
10039
  }
9606
10040
  function readExisting(path) {
9607
- return existsSync2(path) ? readFileSync(path, "utf8") : "";
10041
+ return existsSync3(path) ? readFileSync(path, "utf8") : "";
9608
10042
  }
9609
10043
  function resolveOpenClawConfigPath(options) {
9610
- return expandHome2(
10044
+ return expandHome3(
9611
10045
  options.openclawConfig ?? process.env.OPENCLAW_CONFIG ?? process.env.OPENCLAW_CONFIG_PATH ?? DEFAULT_OPENCLAW_CONFIG
9612
10046
  );
9613
10047
  }
@@ -9623,6 +10057,19 @@ function shellQuote2(value) {
9623
10057
  function tokenForCommand(options) {
9624
10058
  return options.token.trim() || "<BUDDY_TOKEN>";
9625
10059
  }
10060
+ function modelProviderFromOptions(options) {
10061
+ const baseUrl = options.modelProviderBaseUrl?.trim();
10062
+ const apiKey = options.modelProviderApiKey?.trim();
10063
+ const model = options.modelProviderModel?.trim();
10064
+ if (!baseUrl || !apiKey || !model) return void 0;
10065
+ return {
10066
+ id: options.modelProviderId?.trim() || "shadow-official",
10067
+ label: options.modelProviderLabel?.trim() || "Shadow official LLM proxy",
10068
+ baseUrl,
10069
+ apiKey,
10070
+ model
10071
+ };
10072
+ }
9626
10073
  function connectorCommand(command, target, options, extras = []) {
9627
10074
  const parts = ["shadowob-connector", command, "--target", target];
9628
10075
  if (command !== "doctor" && command !== "status") {
@@ -9637,20 +10084,26 @@ function connectorCommand(command, target, options, extras = []) {
9637
10084
  return parts.map(shellQuote2).join(" ");
9638
10085
  }
9639
10086
  function commandExists(command) {
9640
- const result = spawnSync2(command, ["--version"], { stdio: "ignore" });
9641
- return result.status === 0;
10087
+ return commandExistsOnConnectorPath(command);
9642
10088
  }
9643
10089
  function writeExecutable(path, content, dryRun) {
9644
10090
  writeFile(path, content, dryRun);
9645
10091
  if (dryRun) return;
9646
- chmodSync2(path, 493);
10092
+ chmodSync3(path, 493);
9647
10093
  }
9648
10094
  function ensureNpxShim(options) {
9649
- if (commandExists(options.command)) return;
9650
- const localBin = resolve2(homedir2(), ".local/bin");
9651
- const target = resolve2(localBin, options.command);
10095
+ const localBin = resolve3(homedir3(), ".local/bin");
10096
+ const target = resolve3(localBin, options.command);
10097
+ if (commandExists(options.command) && existsSync3(target)) return;
10098
+ const pathPrefix = [localBin, nodeGlobalBinDir(), managedNodeBinDir()].map(shellQuote2).join(":");
9652
10099
  const content = [
9653
10100
  "#!/usr/bin/env sh",
10101
+ `PATH=${pathPrefix}:$PATH`,
10102
+ "export PATH",
10103
+ `if command -v ${options.binaryName} >/dev/null 2>&1; then`,
10104
+ ` resolved="$(command -v ${options.binaryName})"`,
10105
+ ' if [ "$resolved" != "$0" ]; then exec "$resolved" "$@"; fi',
10106
+ "fi",
9654
10107
  `exec npx -y ${options.packageSpec} ${options.binaryName === options.command ? "" : options.binaryName} "$@"`,
9655
10108
  ""
9656
10109
  ].join("\n").replace(' "$@"', ' "$@"');
@@ -9661,11 +10114,26 @@ function ensureNpxShim(options) {
9661
10114
  console.log(`Note: add ${localBin} to PATH so agents can run ${options.command}`);
9662
10115
  }
9663
10116
  }
10117
+ async function installShadowNpmPackages(options) {
10118
+ if (commandExists("shadowob") && commandExists("shadowob-connector")) return;
10119
+ if (!commandExists("npm")) {
10120
+ await ensureManagedNodeRuntime({
10121
+ dryRun: options.dryRun,
10122
+ log: (message) => console.log(message)
10123
+ });
10124
+ }
10125
+ console.log("Applying: Install Shadow CLI packages");
10126
+ runShellQuiet(
10127
+ `npm install -g ${SHADOW_CLI_PACKAGE} ${SHADOW_CONNECTOR_PACKAGE}`,
10128
+ options.dryRun,
10129
+ connectorProcessEnv()
10130
+ );
10131
+ }
9664
10132
  function shadowCliProfileName(options) {
9665
10133
  return options.projectName?.trim() || "shadow-buddy";
9666
10134
  }
9667
10135
  function writeShadowCliProfile(options) {
9668
- const configPath = resolve2(homedir2(), ".shadowob/shadowob.config.json");
10136
+ const configPath = resolve3(homedir3(), ".shadowob/shadowob.config.json");
9669
10137
  const current = (() => {
9670
10138
  try {
9671
10139
  return JSON.parse(readExisting(configPath));
@@ -9690,37 +10158,38 @@ function writeShadowCliProfile(options) {
9690
10158
  }
9691
10159
  function shadowobSkillMarkdown() {
9692
10160
  const candidates = [
9693
- resolve2(packageRoot(), "skills/shadowob/SKILL.md"),
9694
- resolve2(process.cwd(), "skills/shadowob-cli/SKILL.md"),
9695
- resolve2(process.cwd(), "packages/openclaw-shadowob/skills/shadowob/SKILL.md")
10161
+ resolve3(packageRoot(), "skills/shadowob/SKILL.md"),
10162
+ resolve3(process.cwd(), "skills/shadowob-cli/SKILL.md"),
10163
+ resolve3(process.cwd(), "packages/openclaw-shadowob/skills/shadowob/SKILL.md")
9696
10164
  ];
9697
10165
  let currentDir = packageRoot();
9698
10166
  while (true) {
9699
- candidates.push(resolve2(currentDir, "skills/shadowob-cli/SKILL.md"));
9700
- const parentDir = dirname2(currentDir);
10167
+ candidates.push(resolve3(currentDir, "skills/shadowob-cli/SKILL.md"));
10168
+ const parentDir = dirname3(currentDir);
9701
10169
  if (parentDir === currentDir) break;
9702
10170
  currentDir = parentDir;
9703
10171
  }
9704
- const found = candidates.find((candidate) => existsSync2(candidate));
10172
+ const found = candidates.find((candidate) => existsSync3(candidate));
9705
10173
  if (!found) throw new Error("Cannot find bundled Shadow CLI skill");
9706
10174
  return readFileSync(found, "utf8");
9707
10175
  }
9708
10176
  function shadowobSkillTargets(options) {
9709
- const hermesDir = expandHome2(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
10177
+ const hermesDir = expandHome3(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
9710
10178
  return Array.from(
9711
10179
  /* @__PURE__ */ new Set([
9712
- resolve2(homedir2(), ".shadowob/skills/shadowob/SKILL.md"),
9713
- resolve2(homedir2(), ".agents/skills/shadowob/SKILL.md"),
9714
- resolve2(homedir2(), ".codex/skills/shadowob/SKILL.md"),
9715
- resolve2(homedir2(), ".claude/skills/shadowob/SKILL.md"),
9716
- resolve2(homedir2(), ".gemini/skills/shadowob/SKILL.md"),
9717
- resolve2(homedir2(), ".opencode/skills/shadowob/SKILL.md"),
9718
- resolve2(homedir2(), ".openclaw/skills/shadowob/SKILL.md"),
9719
- resolve2(hermesDir, "skills/shadowob/SKILL.md")
10180
+ resolve3(homedir3(), ".shadowob/skills/shadowob/SKILL.md"),
10181
+ resolve3(homedir3(), ".agents/skills/shadowob/SKILL.md"),
10182
+ resolve3(homedir3(), ".codex/skills/shadowob/SKILL.md"),
10183
+ resolve3(homedir3(), ".claude/skills/shadowob/SKILL.md"),
10184
+ resolve3(homedir3(), ".gemini/skills/shadowob/SKILL.md"),
10185
+ resolve3(homedir3(), ".opencode/skills/shadowob/SKILL.md"),
10186
+ resolve3(homedir3(), ".openclaw/skills/shadowob/SKILL.md"),
10187
+ resolve3(hermesDir, "skills/shadowob/SKILL.md")
9720
10188
  ])
9721
10189
  );
9722
10190
  }
9723
- function installShadowCliAndSkills(options) {
10191
+ async function installShadowCliAndSkills(options) {
10192
+ await installShadowNpmPackages(options);
9724
10193
  ensureNpxShim({
9725
10194
  command: "shadowob",
9726
10195
  packageSpec: SHADOW_CLI_PACKAGE,
@@ -9779,8 +10248,8 @@ function diagnoseCommon(options) {
9779
10248
  "Run fix/update to install the ~/.local/bin/shadowob-connector shim."
9780
10249
  )
9781
10250
  ];
9782
- const profilePath = resolve2(homedir2(), ".shadowob/shadowob.config.json");
9783
- if (!existsSync2(profilePath)) {
10251
+ const profilePath = resolve3(homedir3(), ".shadowob/shadowob.config.json");
10252
+ if (!existsSync3(profilePath)) {
9784
10253
  checks.push(
9785
10254
  check(
9786
10255
  "common",
@@ -9805,7 +10274,7 @@ function diagnoseCommon(options) {
9805
10274
  );
9806
10275
  }
9807
10276
  const skillTargets = shadowobSkillTargets(options);
9808
- const installed = skillTargets.filter((target) => existsSync2(target)).length;
10277
+ const installed = skillTargets.filter((target) => existsSync3(target)).length;
9809
10278
  checks.push(
9810
10279
  check(
9811
10280
  "common",
@@ -9828,7 +10297,7 @@ function diagnoseOpenClaw(options) {
9828
10297
  "Install OpenClaw before starting the gateway."
9829
10298
  )
9830
10299
  ];
9831
- if (!existsSync2(configPath)) {
10300
+ if (!existsSync3(configPath)) {
9832
10301
  checks.push(
9833
10302
  check(
9834
10303
  "openclaw",
@@ -9884,10 +10353,10 @@ function diagnoseOpenClaw(options) {
9884
10353
  return checks;
9885
10354
  }
9886
10355
  function diagnoseHermes(options) {
9887
- const hermesDir = expandHome2(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
9888
- const pluginTarget = resolve2(hermesDir, "plugins/shadowob");
9889
- const envPath = resolve2(hermesDir, ".env");
9890
- const configPath = resolve2(hermesDir, "config.yaml");
10356
+ const hermesDir = expandHome3(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
10357
+ const pluginTarget = resolve3(hermesDir, "plugins/shadowob");
10358
+ const envPath = resolve3(hermesDir, ".env");
10359
+ const configPath = resolve3(hermesDir, "config.yaml");
9891
10360
  const checks = [
9892
10361
  check(
9893
10362
  "hermes",
@@ -9898,9 +10367,9 @@ function diagnoseHermes(options) {
9898
10367
  ),
9899
10368
  check(
9900
10369
  "hermes",
9901
- existsSync2(pluginTarget) ? "ok" : "fail",
10370
+ existsSync3(pluginTarget) ? "ok" : "fail",
9902
10371
  "Hermes Shadow plugin",
9903
- existsSync2(pluginTarget) ? `${pluginTarget} exists` : `${pluginTarget} is missing`,
10372
+ existsSync3(pluginTarget) ? `${pluginTarget} exists` : `${pluginTarget} is missing`,
9904
10373
  "Run fix/update."
9905
10374
  )
9906
10375
  ];
@@ -9910,11 +10379,11 @@ function diagnoseHermes(options) {
9910
10379
  "hermes",
9911
10380
  env.includes("SHADOW_TOKEN=") && env.includes("SHADOW_BASE_URL=") ? "ok" : "fail",
9912
10381
  "Hermes environment",
9913
- existsSync2(envPath) ? "SHADOW_TOKEN and SHADOW_BASE_URL are present" : `${envPath} does not exist`,
10382
+ existsSync3(envPath) ? "SHADOW_TOKEN and SHADOW_BASE_URL are present" : `${envPath} does not exist`,
9914
10383
  "Run fix/update with --token and --server-url."
9915
10384
  )
9916
10385
  );
9917
- if (!existsSync2(configPath)) {
10386
+ if (!existsSync3(configPath)) {
9918
10387
  checks.push(
9919
10388
  check("hermes", "fail", "Hermes config", `${configPath} does not exist`, "Run fix/update.")
9920
10389
  );
@@ -9947,7 +10416,7 @@ function diagnoseHermes(options) {
9947
10416
  return checks;
9948
10417
  }
9949
10418
  function diagnoseCcConnect(options) {
9950
- const configPath = resolve2(homedir2(), ".cc-connect/config.toml");
10419
+ const configPath = resolve3(homedir3(), ".cc-connect/config.toml");
9951
10420
  const binary = getCcConnectBinaryStatus();
9952
10421
  const checks = [
9953
10422
  check(
@@ -9958,7 +10427,7 @@ function diagnoseCcConnect(options) {
9958
10427
  "Run fix/update with --install."
9959
10428
  )
9960
10429
  ];
9961
- if (!existsSync2(configPath)) {
10430
+ if (!existsSync3(configPath)) {
9962
10431
  checks.push(
9963
10432
  check(
9964
10433
  "cc-connect",
@@ -10039,7 +10508,7 @@ function printDiagnostics(options, mode) {
10039
10508
  return !checks.some((item) => item.status === "fail");
10040
10509
  }
10041
10510
  function firstExistingPath(paths) {
10042
- return paths.find((path) => existsSync2(path));
10511
+ return paths.find((path) => existsSync3(path));
10043
10512
  }
10044
10513
  function openClawConfigCandidates(options) {
10045
10514
  return Array.from(
@@ -10050,12 +10519,12 @@ function openClawConfigCandidates(options) {
10050
10519
  process.env.OPENCLAW_CONFIG_PATH,
10051
10520
  DEFAULT_OPENCLAW_CONFIG,
10052
10521
  LEGACY_OPENCLAW_CONFIG
10053
- ].filter((value) => !!value?.trim()).map(expandHome2)
10522
+ ].filter((value) => !!value?.trim()).map(expandHome3)
10054
10523
  )
10055
10524
  );
10056
10525
  }
10057
10526
  function ccConnectScanExtras(options) {
10058
- const configPath = resolve2(homedir2(), ".cc-connect/config.toml");
10527
+ const configPath = resolve3(homedir3(), ".cc-connect/config.toml");
10059
10528
  const fallback = [
10060
10529
  "--work-dir",
10061
10530
  options.workDir?.trim() || ".",
@@ -10064,14 +10533,14 @@ function ccConnectScanExtras(options) {
10064
10533
  "--agent-type",
10065
10534
  options.agentType?.trim() || "codex"
10066
10535
  ];
10067
- if (!existsSync2(configPath)) return fallback;
10536
+ if (!existsSync3(configPath)) return fallback;
10068
10537
  try {
10069
10538
  const root = parse(readExisting(configPath));
10070
10539
  const projects = Array.isArray(root.projects) ? root.projects : [];
10071
10540
  const configuredProject = projects.find((project2) => {
10072
10541
  const platformsValue = asObject(project2).platforms;
10073
10542
  const platforms = Array.isArray(platformsValue) ? platformsValue : [];
10074
- return platforms.some((platform2) => asObject(platform2).type === "shadowob");
10543
+ return platforms.some((platform3) => asObject(platform3).type === "shadowob");
10075
10544
  }) ?? projects[0];
10076
10545
  const project = asObject(configuredProject);
10077
10546
  const agent = asObject(project.agent);
@@ -10106,19 +10575,19 @@ function scanOpenClaw(options) {
10106
10575
  };
10107
10576
  }
10108
10577
  function scanHermes(options) {
10109
- const hermesDir = expandHome2(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
10110
- const configPath = resolve2(hermesDir, "config.yaml");
10578
+ const hermesDir = expandHome3(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
10579
+ const configPath = resolve3(hermesDir, "config.yaml");
10111
10580
  const evidence = [];
10112
10581
  if (commandExists("hermes")) evidence.push("hermes command is on PATH");
10113
- if (existsSync2(configPath)) evidence.push(`config found at ${configPath}`);
10114
- if (existsSync2(resolve2(hermesDir, "plugins/shadowob"))) {
10115
- evidence.push(`shadowob plugin found under ${resolve2(hermesDir, "plugins/shadowob")}`);
10582
+ if (existsSync3(configPath)) evidence.push(`config found at ${configPath}`);
10583
+ if (existsSync3(resolve3(hermesDir, "plugins/shadowob"))) {
10584
+ evidence.push(`shadowob plugin found under ${resolve3(hermesDir, "plugins/shadowob")}`);
10116
10585
  }
10117
10586
  return {
10118
10587
  target: "hermes",
10119
10588
  detected: evidence.length > 0,
10120
10589
  evidence,
10121
- configPath: existsSync2(configPath) ? configPath : void 0,
10590
+ configPath: existsSync3(configPath) ? configPath : void 0,
10122
10591
  connectCommand: connectorCommand("connect", "hermes", options),
10123
10592
  updateCommand: connectorCommand("update", "hermes", options),
10124
10593
  doctorCommand: connectorCommand("doctor", "hermes", options),
@@ -10126,18 +10595,18 @@ function scanHermes(options) {
10126
10595
  };
10127
10596
  }
10128
10597
  function scanCcConnect(options) {
10129
- const configPath = resolve2(homedir2(), ".cc-connect/config.toml");
10598
+ const configPath = resolve3(homedir3(), ".cc-connect/config.toml");
10130
10599
  const binary = getCcConnectBinaryStatus();
10131
10600
  const evidence = [];
10132
10601
  if (commandExists("cc-connect")) evidence.push("cc-connect command is on PATH");
10133
10602
  if (binary.usable) evidence.push(`Shadow fork binary found at ${binary.binaryPath}`);
10134
- if (existsSync2(configPath)) evidence.push(`config found at ${configPath}`);
10603
+ if (existsSync3(configPath)) evidence.push(`config found at ${configPath}`);
10135
10604
  const extras = ccConnectScanExtras(options);
10136
10605
  return {
10137
10606
  target: "cc-connect",
10138
10607
  detected: evidence.length > 0,
10139
10608
  evidence,
10140
- configPath: existsSync2(configPath) ? configPath : void 0,
10609
+ configPath: existsSync3(configPath) ? configPath : void 0,
10141
10610
  connectCommand: connectorCommand("connect", "cc-connect", options, extras),
10142
10611
  updateCommand: connectorCommand("update", "cc-connect", options, extras),
10143
10612
  doctorCommand: connectorCommand("doctor", "cc-connect", options),
@@ -10190,7 +10659,7 @@ function readDaemonWorkDirMap(options) {
10190
10659
  return { buddies: {}, runtimes: {}, defaultWorkDir: "" };
10191
10660
  }
10192
10661
  try {
10193
- const filePath = expandHome2(options.workDirMapFile);
10662
+ const filePath = expandHome3(options.workDirMapFile);
10194
10663
  const root = JSON.parse(readFileSync(filePath, "utf8"));
10195
10664
  return {
10196
10665
  buddies: stringRecord(root.buddies),
@@ -10218,14 +10687,14 @@ function resolveDaemonWorkDir(job, options) {
10218
10687
  }
10219
10688
  function packageVersion() {
10220
10689
  try {
10221
- const json = JSON.parse(readFileSync(resolve2(packageRoot(), "package.json"), "utf8"));
10690
+ const json = JSON.parse(readFileSync(resolve3(packageRoot(), "package.json"), "utf8"));
10222
10691
  return json.version ?? "dev";
10223
10692
  } catch {
10224
10693
  return "dev";
10225
10694
  }
10226
10695
  }
10227
10696
  function commandVersionWithArgs(command, args = ["--version"]) {
10228
- const result = spawnSync2(command, args, { encoding: "utf8" });
10697
+ const result = spawnSync3(command, args, { encoding: "utf8", env: connectorProcessEnv() });
10229
10698
  if (result.status !== 0) return { ok: false };
10230
10699
  const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
10231
10700
  return { ok: true, version: output.split(/\r?\n/)[0]?.slice(0, 120) || null };
@@ -10310,7 +10779,7 @@ function printRuntimeScan(options) {
10310
10779
  }
10311
10780
  }
10312
10781
  }
10313
- function installRuntime(options) {
10782
+ async function installRuntime(options) {
10314
10783
  const runtime = connectorRuntimeById(options.runtimeId);
10315
10784
  if (!runtime) {
10316
10785
  throw new Error(
@@ -10323,10 +10792,12 @@ function installRuntime(options) {
10323
10792
  `No install command is available for ${runtime.label}. See ${runtime.install.helpUrl}`
10324
10793
  );
10325
10794
  }
10795
+ const command = commands[0];
10796
+ const env = await envForShellCommand(command, options.dryRun);
10326
10797
  if (options.json) {
10327
- runShellQuiet(commands[0], options.dryRun);
10798
+ runShellQuiet(command, options.dryRun, env);
10328
10799
  } else {
10329
- runShell(commands[0], options.dryRun);
10800
+ runShell(command, options.dryRun, env);
10330
10801
  }
10331
10802
  const detected = detectCatalogRuntime(runtime);
10332
10803
  if (options.json) {
@@ -10379,7 +10850,7 @@ async function heartbeat(options) {
10379
10850
  method: "POST",
10380
10851
  body: JSON.stringify({
10381
10852
  hostname: hostname(),
10382
- os: platform(),
10853
+ os: platform2(),
10383
10854
  arch: arch(),
10384
10855
  daemonVersion: packageVersion(),
10385
10856
  runtimes
@@ -10407,6 +10878,7 @@ function startDetached(binaryPath, args, dryRun) {
10407
10878
  }
10408
10879
  const child = spawn(binaryPath, args, {
10409
10880
  detached: true,
10881
+ env: connectorProcessEnv(),
10410
10882
  stdio: "ignore"
10411
10883
  });
10412
10884
  child.unref();
@@ -10420,7 +10892,7 @@ async function applyDaemonJob(job, baseOptions) {
10420
10892
  const projectName = payload.projectName?.trim() || payload.buddy?.username || "shadow-buddy";
10421
10893
  const workDir = resolveDaemonWorkDir(job, baseOptions);
10422
10894
  if (runtimeId === "openclaw") {
10423
- applyOpenClaw(
10895
+ await applyOpenClaw(
10424
10896
  {
10425
10897
  ...baseOptions,
10426
10898
  target: "openclaw",
@@ -10428,6 +10900,11 @@ async function applyDaemonJob(job, baseOptions) {
10428
10900
  token: payload.token,
10429
10901
  projectName,
10430
10902
  workDir,
10903
+ modelProviderId: payload.modelProvider?.id,
10904
+ modelProviderLabel: payload.modelProvider?.label,
10905
+ modelProviderBaseUrl: payload.modelProvider?.baseUrl,
10906
+ modelProviderApiKey: payload.modelProvider?.apiKey,
10907
+ modelProviderModel: payload.modelProvider?.model,
10431
10908
  install: true
10432
10909
  },
10433
10910
  { restart: true }
@@ -10435,13 +10912,18 @@ async function applyDaemonJob(job, baseOptions) {
10435
10912
  return { runtimeId, target: "openclaw" };
10436
10913
  }
10437
10914
  if (runtimeId === "hermes") {
10438
- applyHermes({
10915
+ await applyHermes({
10439
10916
  ...baseOptions,
10440
10917
  target: "hermes",
10441
10918
  serverUrl: payload.serverUrl,
10442
10919
  token: payload.token,
10443
10920
  projectName,
10444
10921
  workDir,
10922
+ modelProviderId: payload.modelProvider?.id,
10923
+ modelProviderLabel: payload.modelProvider?.label,
10924
+ modelProviderBaseUrl: payload.modelProvider?.baseUrl,
10925
+ modelProviderApiKey: payload.modelProvider?.apiKey,
10926
+ modelProviderModel: payload.modelProvider?.model,
10445
10927
  install: true,
10446
10928
  start: false
10447
10929
  });
@@ -10456,6 +10938,11 @@ async function applyDaemonJob(job, baseOptions) {
10456
10938
  projectName,
10457
10939
  workDir,
10458
10940
  agentType: ccAgentTypeForRuntime(runtimeId),
10941
+ modelProviderId: payload.modelProvider?.id,
10942
+ modelProviderLabel: payload.modelProvider?.label,
10943
+ modelProviderBaseUrl: payload.modelProvider?.baseUrl,
10944
+ modelProviderApiKey: payload.modelProvider?.apiKey,
10945
+ modelProviderModel: payload.modelProvider?.model,
10459
10946
  install: true,
10460
10947
  start: false
10461
10948
  });
@@ -10511,22 +10998,24 @@ async function runDaemon(options) {
10511
10998
  }
10512
10999
  function hermesPluginSource() {
10513
11000
  const candidates = [
10514
- resolve2(packageRoot(), "hermes-shadowob-plugin"),
10515
- resolve2(process.cwd(), "packages/connector/hermes-shadowob-plugin")
11001
+ resolve3(packageRoot(), "hermes-shadowob-plugin"),
11002
+ resolve3(process.cwd(), "packages/connector/hermes-shadowob-plugin")
10516
11003
  ];
10517
- const found = candidates.find((candidate) => existsSync2(candidate));
11004
+ const found = candidates.find((candidate) => existsSync3(candidate));
10518
11005
  if (!found) throw new Error("Cannot find bundled hermes-shadowob-plugin directory");
10519
11006
  return found;
10520
11007
  }
10521
- function applyOpenClaw(options, behavior = { restart: true }) {
11008
+ async function applyOpenClaw(options, behavior = { restart: true }) {
10522
11009
  const target = requireTarget(options);
10523
- const plan = createConnectorPlan({ ...options, target });
11010
+ const modelProvider = modelProviderFromOptions(options);
11011
+ const plan = createConnectorPlan({ ...options, target, modelProvider });
10524
11012
  const configPath = resolveOpenClawConfigPath(options);
10525
- installShadowCliAndSkills(options);
11013
+ await installShadowCliAndSkills(options);
10526
11014
  console.log(`Applying: Merge OpenClaw config ${configPath}`);
10527
11015
  const next = mergeOpenClawConfigContent(readExisting(configPath), {
10528
11016
  token: options.token,
10529
- serverUrl: normalizeServerUrl2(options.serverUrl)
11017
+ serverUrl: normalizeServerUrl2(options.serverUrl),
11018
+ modelProvider
10530
11019
  });
10531
11020
  writeFile(configPath, next, options.dryRun);
10532
11021
  if (options.install) {
@@ -10539,35 +11028,38 @@ function applyOpenClaw(options, behavior = { restart: true }) {
10539
11028
  runShell(restart.command, options.dryRun);
10540
11029
  }
10541
11030
  }
10542
- function applyHermes(options) {
11031
+ async function applyHermes(options) {
10543
11032
  const target = requireTarget(options);
10544
- const plan = createConnectorPlan({ ...options, target });
10545
- const hermesDir = expandHome2(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
10546
- const pluginTarget = resolve2(hermesDir, "plugins/shadowob");
10547
- const envPath = resolve2(hermesDir, ".env");
10548
- const configPath = resolve2(hermesDir, "config.yaml");
11033
+ const modelProvider = modelProviderFromOptions(options);
11034
+ const plan = createConnectorPlan({ ...options, target, modelProvider });
11035
+ const hermesDir = expandHome3(options.hermesHome ?? process.env.HERMES_HOME ?? "~/.hermes");
11036
+ const pluginTarget = resolve3(hermesDir, "plugins/shadowob");
11037
+ const envPath = resolve3(hermesDir, ".env");
11038
+ const configPath = resolve3(hermesDir, "config.yaml");
10549
11039
  const envBlock = plan.configBlocks.find((block) => block.label === "~/.hermes/.env");
10550
11040
  if (!envBlock) throw new Error("Hermes plan is missing config blocks");
10551
- installShadowCliAndSkills(options);
11041
+ await installShadowCliAndSkills(options);
10552
11042
  if (options.dryRun) {
10553
11043
  console.log(`[dry-run] copy ${hermesPluginSource()} -> ${pluginTarget}`);
10554
11044
  } else {
10555
- mkdirSync2(resolve2(hermesDir, "plugins"), { recursive: true });
11045
+ mkdirSync3(resolve3(hermesDir, "plugins"), { recursive: true });
10556
11046
  cpSync(hermesPluginSource(), pluginTarget, { recursive: true, force: true });
10557
11047
  }
10558
11048
  const nextEnv = options.force ? envBlock.content : mergeEnvContent(readExisting(envPath), {
10559
11049
  token: options.token,
10560
- serverUrl: normalizeServerUrl2(options.serverUrl)
11050
+ serverUrl: normalizeServerUrl2(options.serverUrl),
11051
+ modelProvider
10561
11052
  });
10562
11053
  writeFile(envPath, nextEnv, options.dryRun);
10563
11054
  const nextConfig = mergeHermesConfigContent(options.force ? "" : readExisting(configPath), {
10564
11055
  token: options.token,
10565
- serverUrl: normalizeServerUrl2(options.serverUrl)
11056
+ serverUrl: normalizeServerUrl2(options.serverUrl),
11057
+ modelProvider
10566
11058
  });
10567
11059
  writeFile(configPath, nextConfig, options.dryRun);
10568
11060
  if (options.install) {
10569
11061
  runShell(
10570
- `python -m pip install -r "${resolve2(pluginTarget, "requirements.txt")}"`,
11062
+ `python -m pip install -r "${resolve3(pluginTarget, "requirements.txt")}"`,
10571
11063
  options.dryRun
10572
11064
  );
10573
11065
  runShell("hermes plugins enable shadowob", options.dryRun);
@@ -10578,17 +11070,19 @@ function applyHermes(options) {
10578
11070
  }
10579
11071
  async function applyCcConnect(options) {
10580
11072
  const target = requireTarget(options);
10581
- const plan = createConnectorPlan({ ...options, target });
11073
+ const modelProvider = modelProviderFromOptions(options);
11074
+ const plan = createConnectorPlan({ ...options, target, modelProvider });
10582
11075
  const configBlock = plan.configBlocks.find((block) => block.label === "~/.cc-connect/config.toml");
10583
11076
  if (!configBlock) throw new Error("cc-connect plan is missing config block");
10584
- installShadowCliAndSkills(options);
10585
- const configPath = resolve2(homedir2(), ".cc-connect/config.toml");
11077
+ await installShadowCliAndSkills(options);
11078
+ const configPath = resolve3(homedir3(), ".cc-connect/config.toml");
10586
11079
  const nextConfig = options.force ? configBlock.content : mergeCcConnectConfigContent(readExisting(configPath), {
10587
11080
  token: options.token,
10588
11081
  serverUrl: normalizeServerUrl2(options.serverUrl),
10589
11082
  projectName: options.projectName?.trim() || "shadow-buddy",
10590
11083
  workDir: options.workDir?.trim() || ".",
10591
- agentType: options.agentType?.trim() || "codex"
11084
+ agentType: options.agentType?.trim() || "codex",
11085
+ modelProvider
10592
11086
  });
10593
11087
  writeFile(configPath, nextConfig, options.dryRun);
10594
11088
  let binaryPath;
@@ -10607,11 +11101,11 @@ async function applyCcConnect(options) {
10607
11101
  async function connect(options) {
10608
11102
  const target = requireTarget(options);
10609
11103
  if (target === "openclaw") {
10610
- applyOpenClaw(options);
11104
+ await applyOpenClaw(options);
10611
11105
  return;
10612
11106
  }
10613
11107
  if (target === "hermes") {
10614
- applyHermes(options);
11108
+ await applyHermes(options);
10615
11109
  return;
10616
11110
  }
10617
11111
  await applyCcConnect(options);
@@ -10620,11 +11114,11 @@ async function repair(options, mode) {
10620
11114
  const target = requireTarget(options);
10621
11115
  console.log(`Applying: ${mode} ${target} connector`);
10622
11116
  if (target === "openclaw") {
10623
- applyOpenClaw(options, { restart: options.start });
11117
+ await applyOpenClaw(options, { restart: options.start });
10624
11118
  return;
10625
11119
  }
10626
11120
  if (target === "hermes") {
10627
- applyHermes({ ...options, start: options.start });
11121
+ await applyHermes({ ...options, start: options.start });
10628
11122
  return;
10629
11123
  }
10630
11124
  await applyCcConnect({ ...options, start: options.start });
@@ -10645,7 +11139,7 @@ async function main() {
10645
11139
  } else if (options.command === "runtime-scan") {
10646
11140
  printRuntimeScan(options);
10647
11141
  } else if (options.command === "runtime-install") {
10648
- installRuntime(options);
11142
+ await installRuntime(options);
10649
11143
  } else {
10650
11144
  printPlan(options);
10651
11145
  }