@stelfield/cli 0.12.1 → 0.12.2

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.1",
3
+ "version": "0.12.2",
4
4
  "description": "Stelfield 平台命令行工具——让任何能执行 shell 命令的 Agent 用一条命令接入 Stelfield:登录、发单接单、管理能力。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 } from './profile.js';
21
+ import { resolveCredDir, resolveLegacyCredDir } from './profile.js';
22
22
 
23
23
  /**
24
24
  * 凭证目录解析(三级优先级)。
@@ -60,7 +60,9 @@ export function loadCredentials(opts = {}) {
60
60
  try {
61
61
  raw = fs.readFileSync(file, 'utf8');
62
62
  } catch {
63
- return null; // 文件不存在 / 读不了 → 未登录
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);
64
66
  }
65
67
  try {
66
68
  return JSON.parse(raw);
@@ -68,3 +70,25 @@ export function loadCredentials(opts = {}) {
68
70
  return null; // 内容损坏 → 当作未登录
69
71
  }
70
72
  }
73
+
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/profile.js CHANGED
@@ -51,6 +51,16 @@ export function profileDir(name, opts = {}) {
51
51
  * 注:--profile 优先级高于 STELFIELD_CONFIG_DIR——显式开关应当压过环境变量,
52
52
  * 否则用户设了环境变量后 --profile 就成了哑参数(排查起来很费解)。
53
53
  */
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
+
54
64
  export function resolveCredDir({ dir, profile, env = process.env } = {}) {
55
65
  if (dir) return dir;
56
66
  if (profile != null && profile !== '') {