@stelfield/cli 0.12.2 → 0.12.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stelfield/cli",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Stelfield 平台命令行工具——让任何能执行 shell 命令的 Agent 用一条命令接入 Stelfield:登录、发单接单、管理能力。",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.js CHANGED
@@ -240,7 +240,7 @@ async function authedRequestWithRefresh(baseUrl, credOpts, httpOpts, makeRequest
240
240
 
241
241
  // ── 命中 401:触发 refresh ────────────────────────────────
242
242
  // 用凭证目录作 key 实现 profile 隔离(不同目录各自独立 refresh)
243
- const credDir = credOpts.dir || process.env.STELFIELD_CONFIG_DIR || (await import('node:os')).homedir() + '/' + BRAND.configDirName;
243
+ const credDir = credOpts.dir || (await import('./profile.js')).configRootFromEnv();
244
244
 
245
245
  const newTokens = await refreshWithDedup(credDir, baseUrl, creds.refresh_token, fetchImpl);
246
246
 
@@ -18,7 +18,7 @@
18
18
  import fs from 'node:fs';
19
19
  import path from 'node:path';
20
20
  import { atomicWriteFile } from './atomic-write.js';
21
- import { resolveCredDir, resolveLegacyCredDir } from './profile.js';
21
+ import { resolveCredDir } from './profile.js';
22
22
 
23
23
  /**
24
24
  * 凭证目录解析(三级优先级)。
@@ -60,9 +60,7 @@ export function loadCredentials(opts = {}) {
60
60
  try {
61
61
  raw = fs.readFileSync(file, 'utf8');
62
62
  } catch {
63
- // Upgrade from the old `linger` command: no login here yet, but the same Agent may still be logged in under the
64
- // old folder. Copy it over once (the old folder is left untouched) so the owner does not have to bind again.
65
- return migrateLegacyCredentials(opts);
63
+ return null; // 文件不存在 / 读不了 → 未登录
66
64
  }
67
65
  try {
68
66
  return JSON.parse(raw);
@@ -71,24 +69,3 @@ export function loadCredentials(opts = {}) {
71
69
  }
72
70
  }
73
71
 
74
- /**
75
- * One-time carry-over of a pre-rename login. Skipped when the caller pins an explicit directory (tests) so unit
76
- * tests never touch the real home folder. Returns the credentials, or null when there is nothing to carry over.
77
- */
78
- function migrateLegacyCredentials(opts = {}) {
79
- if (opts.dir) return null;
80
- try {
81
- const legacyFile = path.join(resolveLegacyCredDir({ profile: opts.profile }), 'credentials');
82
- const creds = JSON.parse(fs.readFileSync(legacyFile, 'utf8'));
83
- if (!creds || !creds.refresh_token) return null;
84
- saveCredentials(creds, opts);
85
- // The poll cursor goes along, so the first poll does not replay events the Agent already handled.
86
- try {
87
- const cur = fs.readFileSync(path.join(path.dirname(legacyFile), 'poll-cursor'), 'utf8');
88
- atomicWriteFile(path.join(path.dirname(credentialsPath(opts)), 'poll-cursor'), cur, 0o600);
89
- } catch { /* no cursor yet */ }
90
- return creds;
91
- } catch {
92
- return null; // nothing to carry over, or unreadable: behave as not logged in
93
- }
94
- }
package/src/index.js CHANGED
@@ -12,7 +12,7 @@ import { parseArgs } from './cli-parse.js';
12
12
  import { buildAskQuestions, buildAnswers } from './collab-input.js';
13
13
  import { RETIRED_COMMANDS } from './retired-commands.js';
14
14
  import { resolveBaseUrl } from './config.js';
15
- import { defaultRootDir, isValidProfileName, profileDir, resolveCredDir } from './profile.js';
15
+ import { configRootFromEnv, defaultRootDir, isValidProfileName, profileDir, resolveCredDir } from './profile.js';
16
16
  import { loadCredentials } from './credentials.js';
17
17
  import { runAuthLogin } from './oauth.js';
18
18
  import { runConnect } from './connect.js';
@@ -656,7 +656,7 @@ export async function run(argv, deps = {}) {
656
656
  );
657
657
  return 2;
658
658
  }
659
- profileDirPath = profileDir(flags.profile, { rootDir: env.STELFIELD_CONFIG_DIR || defaultRootDir() });
659
+ profileDirPath = profileDir(flags.profile, { rootDir: configRootFromEnv(env) });
660
660
  }
661
661
 
662
662
  if (command === 'auth:login') {
package/src/profile.js CHANGED
@@ -13,11 +13,28 @@
13
13
  // profile 名限 [A-Za-z0-9._-]{1,32},防止拼出目录穿越路径。
14
14
 
15
15
  import os from 'node:os';
16
+ import fs from 'node:fs';
16
17
  import path from 'node:path';
17
18
 
18
19
  /** 默认(不传 --profile)凭证根目录。 */
19
20
  export function defaultRootDir() {
20
- return path.join(os.homedir(), '.stelfield');
21
+ // An Agent that logged in under the old `linger` command keeps its login in ~/.linger. Use that folder in place
22
+ // (never a copy: the server's refresh token is single-use, so two copies of one login would log each other out
23
+ // while the old and new command run side by side). Fresh installs use ~/.stelfield.
24
+ const fresh = path.join(os.homedir(), '.stelfield');
25
+ const legacy = path.join(os.homedir(), '.linger');
26
+ if (hasLogin(fresh)) return fresh;
27
+ if (hasLogin(legacy)) return legacy;
28
+ return fresh;
29
+ }
30
+
31
+ function hasLogin(dir) {
32
+ return fs.existsSync(path.join(dir, 'credentials')) || fs.existsSync(path.join(dir, 'profiles'));
33
+ }
34
+
35
+ /** Config root named by the environment: STELFIELD_CONFIG_DIR, else the old LINGER_CONFIG_DIR, else the default. */
36
+ export function configRootFromEnv(env = process.env) {
37
+ return env.STELFIELD_CONFIG_DIR || env.LINGER_CONFIG_DIR || defaultRootDir();
21
38
  }
22
39
 
23
40
  /** profile 名合法性(目录名安全子集 · 挡住 ../ 之类)。 */
@@ -51,22 +68,11 @@ export function profileDir(name, opts = {}) {
51
68
  * 注:--profile 优先级高于 STELFIELD_CONFIG_DIR——显式开关应当压过环境变量,
52
69
  * 否则用户设了环境变量后 --profile 就成了哑参数(排查起来很费解)。
53
70
  */
54
- /**
55
- * Where the same Agent kept its login before the rename (command `linger`, folder ~/.linger, env LINGER_CONFIG_DIR).
56
- * Used only to carry an existing login over once; nothing is ever written there.
57
- */
58
- export function resolveLegacyCredDir({ profile, env = process.env } = {}) {
59
- const root = env.LINGER_CONFIG_DIR || path.join(os.homedir(), '.linger');
60
- if (profile != null && profile !== '') return profileDir(profile, { rootDir: root });
61
- return root;
62
- }
63
-
64
71
  export function resolveCredDir({ dir, profile, env = process.env } = {}) {
65
72
  if (dir) return dir;
66
73
  if (profile != null && profile !== '') {
67
- const root = env.STELFIELD_CONFIG_DIR || defaultRootDir();
74
+ const root = configRootFromEnv(env);
68
75
  return profileDir(profile, { rootDir: root });
69
76
  }
70
- if (env.STELFIELD_CONFIG_DIR) return env.STELFIELD_CONFIG_DIR;
71
- return defaultRootDir();
77
+ return configRootFromEnv(env);
72
78
  }