shennian 0.2.27 → 0.2.29

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/README.md CHANGED
@@ -20,10 +20,12 @@
20
20
  | `shennian status` | 查看后台服务状态 |
21
21
  | `shennian logs -n 100` | 查看最近日志 |
22
22
  | `shennian config` | 查看本地配置 |
23
+ | `shennian config set server cn\|global` | 切换国内 / 海外中继节点 |
23
24
  | `shennian agent add/list/remove` | 管理自定义 Agent |
24
25
  | `shennian upgrade` | 升级 CLI |
25
26
 
26
27
  `start`、`stop`、`status` 可选加 `--json`。`logs` 输出日志文本,不支持 `--json`。
28
+ 切换节点后运行 `shennian stop && shennian start` 让后台服务重新连接。
27
29
 
28
30
  ## 开发
29
31
 
@@ -16,9 +16,8 @@ const BUILTIN_COMMANDS = {
16
16
  { command: 'agent', args: [], display: 'agent' },
17
17
  { command: 'cursor', args: ['agent'], display: 'cursor agent' },
18
18
  ],
19
- openclaw: [
20
- { command: 'openclaw', args: [], display: 'openclaw' },
21
- ],
19
+ // OpenClaw support is intentionally disabled.
20
+ openclaw: [],
22
21
  opencode: [
23
22
  { command: 'opencode', args: [], display: 'opencode' },
24
23
  ],
@@ -2,7 +2,7 @@
2
2
  // @test src/__tests__/model-switching.test.ts
3
3
  import { createInterface } from 'node:readline';
4
4
  import { resolveBuiltinCommand, spawnResolvedCommand } from '../command-spec.js';
5
- import { fallbackClaudeAliasModels, fallbackGeminiModels, parseCodexAppServerModels, parseCursorModels, parseOpenCodeModels, parseOpenClawModels, } from './parsers.js';
5
+ import { fallbackClaudeAliasModels, fallbackGeminiModels, parseCodexAppServerModels, parseCursorModels, parseOpenCodeModels, } from './parsers.js';
6
6
  import { runResolvedCommand } from './runner.js';
7
7
  import { DISCOVERY_WORKDIR } from './types.js';
8
8
  function sendAppServerRpc(proc, pending, id, method, params, timeoutMs) {
@@ -90,13 +90,13 @@ async function discoverCursorModels() {
90
90
  const result = await runResolvedCommand(spec, ['--list-models']);
91
91
  return parseCursorModels(result.stdout);
92
92
  }
93
- async function discoverOpenClawModels() {
94
- const spec = resolveBuiltinCommand('openclaw');
95
- if (!spec)
96
- return [];
97
- const result = await runResolvedCommand(spec, ['models', 'list', '--json']);
98
- return parseOpenClawModels(result.stdout);
99
- }
93
+ // OpenClaw support is intentionally disabled.
94
+ // async function discoverOpenClawModels(): Promise<ModelInfo[]> {
95
+ // const spec = resolveBuiltinCommand('openclaw')
96
+ // if (!spec) return []
97
+ // const result = await runResolvedCommand(spec, ['models', 'list', '--json'])
98
+ // return parseOpenClawModels(result.stdout)
99
+ // }
100
100
  async function discoverOpenCodeModels() {
101
101
  const spec = resolveBuiltinCommand('opencode');
102
102
  if (!spec)
@@ -160,7 +160,8 @@ async function discoverBuiltinModels(type, context) {
160
160
  case 'cursor':
161
161
  return discoverCursorModels();
162
162
  case 'openclaw':
163
- return discoverOpenClawModels();
163
+ // OpenClaw support is intentionally disabled.
164
+ return [];
164
165
  case 'opencode':
165
166
  return discoverOpenCodeModels();
166
167
  case 'pi':
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { AgentAdapter, registerAgent } from './adapter.js';
2
+ import { AgentAdapter } from './adapter.js';
3
3
  import { resolveBuiltinCommand, spawnResolvedCommand } from './command-spec.js';
4
4
  /** Best-effort parse of `openclaw agent --json` stdout; shape may vary by version. */
5
5
  function extractResponseText(parsed) {
@@ -270,4 +270,5 @@ export class OpenClawAdapter extends AgentAdapter {
270
270
  });
271
271
  }
272
272
  }
273
- registerAgent('openclaw', () => new OpenClawAdapter());
273
+ // OpenClaw support is intentionally disabled.
274
+ // registerAgent('openclaw', () => new OpenClawAdapter())
@@ -12,6 +12,8 @@ export type DaemonStatus = {
12
12
  shennianDir: string;
13
13
  pidFile: string;
14
14
  logFile: string;
15
+ machineId?: string;
16
+ serverUrl?: string;
15
17
  };
16
18
  export type ServiceLaunchSpec = {
17
19
  command: string;
@@ -6,7 +6,7 @@ import path from 'node:path';
6
6
  import os from 'node:os';
7
7
  import { execSync, spawn } from 'node:child_process';
8
8
  import { fileURLToPath } from 'node:url';
9
- import { getShennianDir, resolveShennianPath } from '../config/index.js';
9
+ import { getShennianDir, loadConfig, resolveShennianPath, saveConfig } from '../config/index.js';
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
  const SHENNIAN_DIR = getShennianDir();
12
12
  const PID_FILE = resolveShennianPath('daemon.pid');
@@ -156,6 +156,7 @@ export function getDaemonStatus(opts = {}) {
156
156
  const pid = readPid();
157
157
  const running = pid !== null && isRunning(pid);
158
158
  const stale = pid !== null && !running;
159
+ const config = loadConfig();
159
160
  if (stale && opts.cleanupStale) {
160
161
  try {
161
162
  fs.unlinkSync(PID_FILE);
@@ -174,11 +175,21 @@ export function getDaemonStatus(opts = {}) {
174
175
  shennianDir: SHENNIAN_DIR,
175
176
  pidFile: PID_FILE,
176
177
  logFile: LOG_FILE,
178
+ ...(config.machineId ? { machineId: config.machineId } : {}),
179
+ ...(config.serverUrl ? { serverUrl: config.serverUrl } : {}),
177
180
  };
178
181
  }
179
182
  function printJson(value) {
180
183
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
181
184
  }
185
+ function persistServerUrlOverride(serverUrl) {
186
+ const normalized = serverUrl?.trim();
187
+ if (!normalized)
188
+ return;
189
+ const config = loadConfig();
190
+ config.serverUrl = normalized;
191
+ saveConfig(config);
192
+ }
182
193
  // ─── Service definitions ─────────────────────────────────────────────────────
183
194
  const LAUNCHD_LABEL = 'com.shennian.agent';
184
195
  const LAUNCHD_PLIST = path.join(os.homedir(), 'Library/LaunchAgents', `${LAUNCHD_LABEL}.plist`);
@@ -639,6 +650,7 @@ async function stopDaemonProcessAndWait(timeoutMs = 5000) {
639
650
  return result;
640
651
  }
641
652
  function enableRemoteAccess(opts = {}) {
653
+ persistServerUrlOverride(opts.api);
642
654
  try {
643
655
  fs.unlinkSync(REMOTE_ACCESS_DISABLED_FILE);
644
656
  }
@@ -726,6 +738,7 @@ async function waitForPidExit(pid, timeoutMs = 5000) {
726
738
  return !isRunning(pid);
727
739
  }
728
740
  async function daemonRestart(opts = {}) {
741
+ persistServerUrlOverride(opts.api);
729
742
  const pid = readPid();
730
743
  if (pid !== null && isRunning(pid)) {
731
744
  try {
@@ -888,6 +901,7 @@ export function registerDaemonCommand(program) {
888
901
  .command('start')
889
902
  .description('Start the background service')
890
903
  .option('--json', 'Print machine-readable status JSON')
904
+ .option('--api <url>', 'Server URL override')
891
905
  .action((opts) => daemonStart(opts));
892
906
  program
893
907
  .command('stop')
@@ -913,6 +927,7 @@ export function registerDaemonCommand(program) {
913
927
  .command('start')
914
928
  .description('Start the background service')
915
929
  .option('--json', 'Print machine-readable status JSON')
930
+ .option('--api <url>', 'Server URL override')
916
931
  .action((opts) => {
917
932
  warnDeprecated('start', opts.json);
918
933
  daemonStart(opts);
@@ -929,6 +944,7 @@ export function registerDaemonCommand(program) {
929
944
  .command('restart')
930
945
  .description('Restart the background service')
931
946
  .option('--json', 'Print machine-readable status JSON')
947
+ .option('--api <url>', 'Server URL override')
932
948
  .action((opts) => {
933
949
  warnDeprecated('stop && shennian start', opts.json);
934
950
  daemonRestart(opts);
package/dist/src/index.js CHANGED
@@ -89,16 +89,20 @@ program
89
89
  catch {
90
90
  // env.json may not exist yet
91
91
  }
92
- // Single-instance guard. systemd-managed starts are authoritative and may
93
- // need to take over from an older detached `shennian start` process.
92
+ // Single-instance guard. Service-manager starts are authoritative and may
93
+ // need to take over from an older detached process after app/daemon upgrades.
94
94
  const pidFile = resolveShennianPath('daemon.pid');
95
95
  try {
96
96
  const oldPid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
97
97
  if (oldPid && oldPid !== process.pid) {
98
98
  try {
99
99
  process.kill(oldPid, 0);
100
- if (process.env.INVOCATION_ID || process.env.JOURNAL_STREAM) {
101
- console.log(`[${new Date().toISOString()}] systemd start taking over from existing daemon (PID ${oldPid})`);
100
+ const serviceManagedStart = Boolean(process.env.INVOCATION_ID ||
101
+ process.env.JOURNAL_STREAM ||
102
+ process.env.SHENNIAN_DESKTOP_CLI_SCRIPT ||
103
+ process.env.SHENNIAN_DESKTOP_CLI_BRIDGE);
104
+ if (serviceManagedStart) {
105
+ console.log(`[${new Date().toISOString()}] managed start taking over from existing daemon (PID ${oldPid})`);
102
106
  process.kill(oldPid, 'SIGTERM');
103
107
  const stopped = await waitForPidExit(oldPid);
104
108
  if (!stopped) {
@@ -5,7 +5,6 @@ import '../agents/claude.js';
5
5
  import '../agents/codex.js';
6
6
  import '../agents/gemini.js';
7
7
  import '../agents/cursor.js';
8
- import '../agents/openclaw.js';
9
8
  import '../agents/opencode.js';
10
9
  import '../agents/pi.js';
11
10
  export declare function resolveSessionWorkDir(input: string): string;
@@ -15,7 +15,8 @@ import '../agents/claude.js';
15
15
  import '../agents/codex.js';
16
16
  import '../agents/gemini.js';
17
17
  import '../agents/cursor.js';
18
- import '../agents/openclaw.js';
18
+ // OpenClaw support is intentionally disabled.
19
+ // import '../agents/openclaw.js'
19
20
  import '../agents/opencode.js';
20
21
  import '../agents/pi.js';
21
22
  import { registerCustomAgent } from '../agents/custom.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.27",
3
+ "version": "0.2.29",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {