@yancyyu/openhermit 1.6.17 → 1.6.19

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.
Files changed (2) hide show
  1. package/bin/hermit.mjs +65 -61
  2. package/package.json +1 -1
package/bin/hermit.mjs CHANGED
@@ -96,8 +96,7 @@ const ccConnectConfigPath =
96
96
  process.env.HERMIT_CC_CONNECT_CONFIG ||
97
97
  process.env.CC_CONNECT_CONFIG ||
98
98
  path.join(hermitHome, 'cc-connect', 'config.toml');
99
- const bootstrapProjectName = 'default';
100
- const legacyBootstrapProjectName = '__openhermit_bootstrap__';
99
+ const starterProjectName = 'my-project';
101
100
 
102
101
  // ---------------------------------------------------------------------------
103
102
  // Update command
@@ -364,36 +363,6 @@ function parseTomlToken(raw, section) {
364
363
  return match?.[1] || '';
365
364
  }
366
365
 
367
- function hasProjectEntries(raw) {
368
- return /^\s*\[\[projects\]\]/m.test(raw);
369
- }
370
-
371
- function buildBootstrapProjectToml() {
372
- return `
373
- # Internal bootstrap project used only so cc-connect can start with an otherwise empty config.
374
- # It is safe to keep this project; users can replace or delete it after creating real teams.
375
- [[projects]]
376
- name = "${bootstrapProjectName}"
377
- disabled_commands = ["*"]
378
-
379
- [projects.agent]
380
- type = "claudecode"
381
-
382
- [projects.agent.options]
383
- work_dir = "${escapeTomlPath(hermitHome)}"
384
- mode = "default"
385
-
386
- [[projects.platforms]]
387
- type = "line"
388
-
389
- [projects.platforms.options]
390
- channel_secret = "openhermit-bootstrap"
391
- channel_token = "openhermit-bootstrap"
392
- port = "0"
393
- callback_path = "/openhermit-bootstrap"
394
- `;
395
- }
396
-
397
366
  function escapeRegExp(value) {
398
367
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
399
368
  }
@@ -415,24 +384,11 @@ function isManagedBootstrapBlock(block) {
415
384
  );
416
385
  }
417
386
 
418
- function migrateManagedBootstrapProject(raw) {
419
- const legacyBlock = findProjectBlock(raw, legacyBootstrapProjectName);
420
- if (!legacyBlock || !isManagedBootstrapBlock(legacyBlock.match[0])) return raw;
421
-
422
- const withoutLegacy = raw.replace(legacyBlock.pattern, '').replace(/\n{3,}/g, '\n\n').trimEnd();
423
- if (findProjectBlock(withoutLegacy, bootstrapProjectName)) {
424
- return `${withoutLegacy}\n`;
425
- }
387
+ function buildStarterConfigToml(managementToken, bridgeToken) {
388
+ return `# cc-connect configuration
389
+ # Docs: https://github.com/chenhg5/cc-connect
426
390
 
427
- return `${withoutLegacy}\n${buildBootstrapProjectToml()}`;
428
- }
429
-
430
- function ensureCcConnectConfig() {
431
- mkdirSync(path.dirname(ccConnectConfigPath), { recursive: true });
432
- if (!existsSync(ccConnectConfigPath)) {
433
- const managementToken = randomToken();
434
- const bridgeToken = randomToken();
435
- const config = `data_dir = "${escapeTomlPath(path.join(hermitHome, 'cc-connect', 'data'))}"
391
+ data_dir = "${escapeTomlPath(path.join(hermitHome, 'cc-connect', 'data'))}"
436
392
  language = "zh"
437
393
 
438
394
  [management]
@@ -450,21 +406,61 @@ path = "/bridge/ws"
450
406
 
451
407
  [log]
452
408
  level = "info"
453
- ${buildBootstrapProjectToml()}`;
454
- writeFileSync(ccConnectConfigPath, config, 'utf-8');
455
- }
456
409
 
457
- let raw = readFileSync(ccConnectConfigPath, 'utf-8');
458
- if (!hasProjectEntries(raw)) {
459
- raw = `${raw.trimEnd()}\n${buildBootstrapProjectToml()}`;
460
- writeFileSync(ccConnectConfigPath, raw, 'utf-8');
461
- } else {
462
- const migrated = migrateManagedBootstrapProject(raw);
463
- if (migrated !== raw) {
464
- raw = migrated;
465
- writeFileSync(ccConnectConfigPath, raw, 'utf-8');
410
+ [[projects]]
411
+ name = "my-project"
412
+
413
+ [projects.agent]
414
+ type = "claudecode" # "claudecode", "codex", "cursor", "gemini", "qoder", "opencode", or "iflow"
415
+
416
+ [projects.agent.options]
417
+ work_dir = "/path/to/your/project"
418
+ mode = "default"
419
+ # model = "claude-sonnet-4-20250514"
420
+
421
+ # --- Choose at least one platform below ---
422
+
423
+ [[projects.platforms]]
424
+ type = "feishu"
425
+
426
+ [projects.platforms.options]
427
+ app_id = "your-feishu-app-id"
428
+ app_secret = "your-feishu-app-secret"
429
+ `;
430
+ }
431
+
432
+ function isStarterProjectConfig(raw) {
433
+ const block = findProjectBlock(raw, starterProjectName);
434
+ if (!block) return false;
435
+ const text = block.match[0];
436
+ return (
437
+ text.includes('name = "my-project"') &&
438
+ text.includes('type = "claudecode"') &&
439
+ text.includes('work_dir = "/path/to/your/project"') &&
440
+ text.includes('app_id = "your-feishu-app-id"') &&
441
+ text.includes('app_secret = "your-feishu-app-secret"')
442
+ );
443
+ }
444
+
445
+ function hasRunnableProjectEntries(raw) {
446
+ const projectPattern = /\[\[projects\]\]\nname\s*=\s*"([^"]+)"[\s\S]*?(?=\n\[\[projects\]\]|\s*$)/g;
447
+ for (const match of raw.matchAll(projectPattern)) {
448
+ if (!isStarterProjectConfig(match[0]) && !isManagedBootstrapBlock(match[0])) {
449
+ return true;
466
450
  }
467
451
  }
452
+ return false;
453
+ }
454
+
455
+ function ensureCcConnectConfig() {
456
+ mkdirSync(path.dirname(ccConnectConfigPath), { recursive: true });
457
+ if (!existsSync(ccConnectConfigPath)) {
458
+ const managementToken = randomToken();
459
+ const bridgeToken = randomToken();
460
+ writeFileSync(ccConnectConfigPath, buildStarterConfigToml(managementToken, bridgeToken), 'utf-8');
461
+ }
462
+
463
+ const raw = readFileSync(ccConnectConfigPath, 'utf-8');
468
464
 
469
465
  return {
470
466
  managementToken:
@@ -475,6 +471,8 @@ ${buildBootstrapProjectToml()}`;
475
471
  process.env.CC_CONNECT_BRIDGE_TOKEN ||
476
472
  process.env.CC_CONNECT_TOKEN ||
477
473
  parseTomlToken(raw, 'bridge'),
474
+ hasRunnableProjects: hasRunnableProjectEntries(raw),
475
+ isStarterConfig: isStarterProjectConfig(raw),
478
476
  };
479
477
  }
480
478
 
@@ -568,6 +566,12 @@ if (!skipCcConnect) {
568
566
  const alreadyRunning = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 1_000);
569
567
  if (alreadyRunning) {
570
568
  console.log(`[openHermit] Runtime service already running: ${ccBaseUrl}`);
569
+ } else if (!ccTokens.hasRunnableProjects) {
570
+ console.log('[openHermit] Runtime config is a starter template; starting control panel only.');
571
+ console.log(`[openHermit] Edit runtime config in the UI before starting the runtime service.`);
572
+ if (ccTokens.isStarterConfig) {
573
+ console.log(`[openHermit] Starter config: ${ccConnectConfigPath}`);
574
+ }
571
575
  } else {
572
576
  console.log('[openHermit] Starting bundled runtime service...');
573
577
  console.log(`[openHermit] Runtime config: ${ccConnectConfigPath}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yancyyu/openhermit",
3
3
  "type": "module",
4
- "version": "1.6.17",
4
+ "version": "1.6.19",
5
5
  "description": "openHermit: team-oriented agent management workbench atop cc-connect.",
6
6
  "license": "AGPL-3.0",
7
7
  "author": {