@yosai/cli 0.2.1-test.12 → 0.2.1-test.14

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
@@ -58,6 +58,7 @@ yos skill install # 安装 9 个内置 Skill(总入口 + 领域 + 安装
58
58
  - 环境变量(均优先于配置文件,适合 CI/容器场景):`YOS_API_KEY` 访问令牌、`YOS_ENV` 服务环境(prod / test / dev)
59
59
  - 版本检查:命令执行前会检查对应通道最新版(后台进程查询、结果缓存 10 分钟),落后时以 stderr 提示「请先执行 yos update 升级再继续任务」;升级通道自动跟随服务环境(测试环境跟 test 通道)。CLI 不静默安装,升级由用户或 Agent 按提示显式执行
60
60
  - 服务环境(测试/开发用):`yos login --env test` 切到测试环境,`--env dev` 指向本机开发服务(http://localhost:8080)
61
+ - 托管模式(YOS 客户端内置 CLI 专用):`YOS_MANAGED=1` 时安装、升级、登录与 Skill 由客户端统一管理——不做版本检查与升级提示,拒绝 `update` / `install` / `login` / `skill install`,令牌只认客户端注入的 `YOS_API_KEY`(不读配置文件),`doctor` 跳过配置文件与 Skill 检查;客户端应同时以 `YOS_CONFIG_DIR` 指向自身私有目录,与用户自行安装的 CLI 配置隔离
61
62
 
62
63
  ## 故障排查
63
64
 
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { parseArgs } from 'node:util';
2
2
  import { DOMAIN_META } from './domain-commands.js';
3
3
  import { CliError, EXIT, UsageError } from './errors.js';
4
+ import { isBlockedInManaged, isManaged, MANAGED_HINT } from './managed.js';
4
5
  import { print, printErr, renderRows } from './output.js';
5
6
  import { handleUpdateNotice } from './update-notice.js';
6
7
  const registry = [];
@@ -17,7 +18,7 @@ export function renderHelp() {
17
18
  ' 0. yos <领域> --help 按领域浏览能力(kb / docs / retrieve / support / transcript)',
18
19
  ' 1. yos tool list 动态发现全部工具(领域命令未覆盖时)',
19
20
  ' 2. 领域命令未覆盖的场景用 yos tool schema / tool call 兜底',
20
- ' 3. yos skill install 安装内置 Skill,接入本机 AI 助手(Claude Code / Cursor / Qoder 等)',
21
+ ...(isManaged() ? [] : [' 3. yos skill install 安装内置 Skill,接入本机 AI 助手(Claude Code / Cursor / Qoder 等)']),
21
22
  '',
22
23
  '约定:',
23
24
  ' - 输出默认人类可读;--json 输出结构化结果(工具返回原文在 text 字段内)',
@@ -28,6 +29,7 @@ export function renderHelp() {
28
29
  ' YOS_API_KEY 访问令牌',
29
30
  ' YOS_ENV 服务环境(prod / test / dev;缺省生产)',
30
31
  ' YOS_CONFIG_DIR 配置目录(缺省 ~/.config/yos)',
32
+ ' YOS_MANAGED 托管模式(=1 时由 YOS 客户端托管,用户无需设置)',
31
33
  '',
32
34
  '示例:',
33
35
  ' yos doctor',
@@ -39,8 +41,9 @@ export function renderHelp() {
39
41
  ' yos docs download NDxxxx --out ./报告.pdf',
40
42
  '',
41
43
  ];
44
+ const visible = registry.filter((command) => !isBlockedInManaged(command.name));
42
45
  for (const group of GROUP_ORDER) {
43
- const commands = registry.filter((command) => command.group === group);
46
+ const commands = visible.filter((command) => command.group === group);
44
47
  if (commands.length === 0) {
45
48
  continue;
46
49
  }
@@ -48,7 +51,7 @@ export function renderHelp() {
48
51
  lines.push(indentRows(renderRows(commands.map((command) => [command.name, command.description]))));
49
52
  lines.push('');
50
53
  }
51
- const ungrouped = registry.filter((command) => !command.group);
54
+ const ungrouped = visible.filter((command) => !command.group);
52
55
  if (ungrouped.length > 0) {
53
56
  lines.push(indentRows(renderRows(ungrouped.map((command) => [command.name, command.description]))));
54
57
  lines.push('');
@@ -167,6 +170,11 @@ export async function dispatch(argv) {
167
170
  return EXIT.USAGE;
168
171
  }
169
172
  const { command, rest } = matched;
173
+ if (isBlockedInManaged(command.name)) {
174
+ printErr(`错误: 内置 CLI 不支持 yos ${command.name}`);
175
+ printErr(`提示: ${MANAGED_HINT},无需也不能自行执行`);
176
+ return EXIT.USAGE;
177
+ }
170
178
  if (rest.includes('--help') || rest.includes('-h')) {
171
179
  print(renderCommandHelp(command));
172
180
  return EXIT.OK;
@@ -2,6 +2,7 @@ import { register } from '../cli.js';
2
2
  import { loadConfig, maskKey, resolveEnv, resolveKey } from '../config.js';
3
3
  import { DEFAULT_ENV, endpointOfEnv, envLabel } from '../endpoints.js';
4
4
  import { CliError, EXIT } from '../errors.js';
5
+ import { isManaged } from '../managed.js';
5
6
  import { formatIdentity, OpenApisClient } from '../openapis-client.js';
6
7
  import { print, printJson } from '../output.js';
7
8
  import { readPackageMeta } from '../npm-meta.js';
@@ -18,11 +19,10 @@ register({
18
19
  const config = loadConfig();
19
20
  const env = resolveEnv(config);
20
21
  const endpoint = endpointOfEnv(env);
21
- checks.push({
22
- name: '配置文件',
23
- ok: config !== null,
24
- detail: config ? '已找到' : '未找到,请执行 yos login',
25
- });
22
+ const managed = isManaged();
23
+ checks.push(managed
24
+ ? { name: '运行模式', ok: true, detail: '客户端托管(令牌与环境由 YOS 客户端注入)' }
25
+ : { name: '配置文件', ok: config !== null, detail: config ? '已找到' : '未找到,请执行 yos login' });
26
26
  if (env !== DEFAULT_ENV) {
27
27
  checks.push({
28
28
  name: '服务环境',
@@ -54,11 +54,13 @@ register({
54
54
  }
55
55
  }
56
56
  const skillDirs = config?.installedSkillDirs ?? [];
57
- checks.push({
58
- name: 'Skill 安装',
59
- ok: skillDirs.length > 0,
60
- detail: skillDirs.length > 0 ? skillDirs.join(', ') : '尚未安装,执行 yos skill install',
61
- });
57
+ if (!managed) {
58
+ checks.push({
59
+ name: 'Skill 安装',
60
+ ok: skillDirs.length > 0,
61
+ detail: skillDirs.length > 0 ? skillDirs.join(', ') : '尚未安装,执行 yos skill install',
62
+ });
63
+ }
62
64
  const meta = readPackageMeta();
63
65
  const envNote = env === DEFAULT_ENV ? '' : `(环境=${envLabel(env)})`;
64
66
  checks.push({
@@ -66,12 +68,14 @@ register({
66
68
  ok: meta !== null,
67
69
  detail: meta ? `${meta.version}${envNote}` : 'unknown(未找到 package.json)',
68
70
  });
69
- const state = loadVersionState();
70
- checks.push({
71
- name: 'Skill 同步',
72
- ok: meta !== null && state?.version === meta.version && skillDirs.length > 0,
73
- detail: skillSyncDetail(meta, state, skillDirs),
74
- });
71
+ if (!managed) {
72
+ const state = loadVersionState();
73
+ checks.push({
74
+ name: 'Skill 同步',
75
+ ok: meta !== null && state?.version === meta.version && skillDirs.length > 0,
76
+ detail: skillSyncDetail(meta, state, skillDirs),
77
+ });
78
+ }
75
79
  if (flags.json) {
76
80
  printJson({ checks });
77
81
  }
@@ -10,7 +10,7 @@ import { decideSingleFileUpload, findSubtreeChildren, uploadDirectory, walkLocal
10
10
  const CONFLICT_POLICIES = ['skip', 'rename'];
11
11
  register({
12
12
  name: 'kb upload',
13
- description: '上传本地文件或整个目录到知识库(目录递归镜像文件夹,multipart 直传,单文件 ≤20MB)',
13
+ description: '上传本地文件或整个目录到知识库(目录递归镜像文件夹;≤20MB 直传,更大文件自动分片上传)',
14
14
  usage: 'yos kb upload <本地文件或目录> --kb <kb_code> [--dir <parent_code>] [--on-conflict <skip|rename>] [--json]',
15
15
  group: '知识库 kb',
16
16
  risk: 'write',
@@ -82,25 +82,17 @@ async function uploadSingle(deps, filePath, kbCode, dirCode, onConflict, json) {
82
82
  return EXIT.OK;
83
83
  }
84
84
  const finalName = decision.finalName;
85
- try {
86
- const result = await deps.uploadFile(filePath, kbCode, dirCode, finalName);
87
- if (json) {
88
- printJson(result);
89
- }
90
- else if (finalName === name) {
91
- print(`已上传:${result.fileName}(${result.fileSize} 字节)→ 节点 ${result.nodeCode}`);
92
- }
93
- else {
94
- print(`已改名上传:${name} → ${result.fileName}(${result.fileSize} 字节)→ 节点 ${result.nodeCode}`);
95
- }
96
- return EXIT.OK;
85
+ const result = await deps.uploadFile(filePath, kbCode, dirCode, finalName);
86
+ if (json) {
87
+ printJson(result);
97
88
  }
98
- catch (e) {
99
- if (e instanceof BusinessError && e.message.includes('超过 20MB')) {
100
- throw new BusinessError(e.message, '更大文件请先在客户端上传后共享,或拆分为多个文件');
101
- }
102
- throw e;
89
+ else if (finalName === name) {
90
+ print(`已上传:${result.fileName}(${result.fileSize} 字节)→ 节点 ${result.nodeCode}`);
91
+ }
92
+ else {
93
+ print(`已改名上传:${name} → ${result.fileName}(${result.fileSize} 字节)→ 节点 ${result.nodeCode}`);
103
94
  }
95
+ return EXIT.OK;
104
96
  }
105
97
  async function uploadDir(deps, localRoot, kbCode, dirCode, onConflict, json) {
106
98
  const { exitCode, aborted, summary } = await uploadDirectory(deps, { localRoot, kbCode, dirCode, onConflict });
package/dist/config.js CHANGED
@@ -3,6 +3,7 @@ import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { channelOfEnv, DEFAULT_ENV, endpointOfEnv } from './endpoints.js';
5
5
  import { UsageError } from './errors.js';
6
+ import { isManaged } from './managed.js';
6
7
  export function configDir() {
7
8
  return process.env.YOS_CONFIG_DIR ?? join(homedir(), '.config', 'yos');
8
9
  }
@@ -32,6 +33,9 @@ export function resolveKey(config) {
32
33
  if (fromEnv) {
33
34
  return fromEnv;
34
35
  }
36
+ if (isManaged()) {
37
+ throw new UsageError('未注入访问令牌', '内置 CLI 的令牌由 YOS 客户端注入 YOS_API_KEY,请重新登录 YOS 客户端');
38
+ }
35
39
  if (config?.apiKey) {
36
40
  return config.apiKey;
37
41
  }
package/dist/errors.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isManaged } from './managed.js';
1
2
  export const EXIT = {
2
3
  OK: 0,
3
4
  BUSINESS: 1,
@@ -33,11 +34,15 @@ export class NetworkError extends CliError {
33
34
  }
34
35
  export class AuthError extends CliError {
35
36
  constructor(message) {
36
- super(message, EXIT.AUTH, '请执行 `yos login` 重新配置访问令牌');
37
+ super(message, EXIT.AUTH, isManaged()
38
+ ? '内置 CLI 的令牌由 YOS 客户端注入,请重新登录 YOS 客户端'
39
+ : '请执行 `yos login` 重新配置访问令牌');
37
40
  }
38
41
  }
39
42
  export class UpgradeError extends CliError {
40
43
  constructor(message, hint) {
41
- super(message, EXIT.UPGRADE, hint ?? '执行 `yos update` 升级后重试');
44
+ super(message, EXIT.UPGRADE, isManaged()
45
+ ? '请升级 YOS 客户端后重试'
46
+ : hint ?? '执行 `yos update` 升级后重试');
42
47
  }
43
48
  }
@@ -0,0 +1,9 @@
1
+ export const MANAGED_ENV = 'YOS_MANAGED';
2
+ const MANAGED_BLOCKED_COMMANDS = new Set(['update', 'install', 'login', 'skill install']);
3
+ export const MANAGED_HINT = '内置 CLI 的安装、升级、登录与 Skill 由 YOS 客户端统一管理';
4
+ export function isManaged() {
5
+ return process.env[MANAGED_ENV] === '1';
6
+ }
7
+ export function isBlockedInManaged(commandName) {
8
+ return isManaged() && MANAGED_BLOCKED_COMMANDS.has(commandName);
9
+ }
@@ -1,9 +1,11 @@
1
- import { readFile, stat } from 'node:fs/promises';
1
+ import { open, readFile, stat } from 'node:fs/promises';
2
2
  import { basename } from 'node:path';
3
3
  import { envNoteOfEndpoint } from './endpoints.js';
4
4
  import { AuthError, BusinessError, NetworkError, UpgradeError } from './errors.js';
5
5
  import { getCliVersion } from './npm-meta.js';
6
- export const MAX_UPLOAD_SIZE_BYTES = 20 * 1024 * 1024;
6
+ export const DIRECT_UPLOAD_THRESHOLD_BYTES = 20 * 1024 * 1024;
7
+ const FALLBACK_PART_SIZE_BYTES = 5 * 1024 * 1024;
8
+ const MAX_PART_SIZE_BYTES = 20 * 1024 * 1024;
7
9
  export const CLI_VERSION_HEADER = 'X-Yos-Cli-Version';
8
10
  export function formatIdentity(identity) {
9
11
  return identity.userName ? `${identity.userName}(${identity.userCode})` : identity.userCode;
@@ -29,24 +31,29 @@ export class OpenApisClient {
29
31
  return this.request('/tool/call', { name, arguments: args });
30
32
  }
31
33
  async request(path, body) {
32
- let response;
33
- try {
34
- response = await fetch(`${this.base}${path}`, {
35
- method: 'POST',
36
- headers: {
37
- 'Content-Type': 'application/json',
38
- 'Authorization': `Bearer ${this.apiKey}`,
39
- [CLI_VERSION_HEADER]: getCliVersion(),
40
- },
41
- body: JSON.stringify(body ?? {}),
42
- });
43
- }
44
- catch (e) {
45
- throw new NetworkError(`无法连接 YOS 服务${envNoteOfBase(this.base)},原因:${e.message}`);
46
- }
47
- return parseEnvelope(response);
34
+ return postJson(this.base, this.apiKey, path, body);
48
35
  }
49
36
  }
37
+ function authHeaders(apiKey) {
38
+ return {
39
+ 'Authorization': `Bearer ${apiKey}`,
40
+ [CLI_VERSION_HEADER]: getCliVersion(),
41
+ };
42
+ }
43
+ async function postJson(base, apiKey, path, body) {
44
+ let response;
45
+ try {
46
+ response = await fetch(`${base}${path}`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json', ...authHeaders(apiKey) },
49
+ body: JSON.stringify(body ?? {}),
50
+ });
51
+ }
52
+ catch (e) {
53
+ throw new NetworkError(`无法连接 YOS 服务${envNoteOfBase(base)},原因:${e.message}`);
54
+ }
55
+ return parseEnvelope(response);
56
+ }
50
57
  function envNoteOfBase(base) {
51
58
  return envNoteOfEndpoint(base.replace(/\/open-apis$/, ''));
52
59
  }
@@ -110,12 +117,15 @@ export async function uploadKnowledgeFile(endpoint, apiKey, filePath, kbCode, pa
110
117
  catch {
111
118
  throw new BusinessError(`文件不存在或不可读:path=${filePath}`);
112
119
  }
113
- if (fileSize > MAX_UPLOAD_SIZE_BYTES) {
114
- throw new BusinessError(`文件超过 20MB 上限:size=${fileSize},path=${filePath}`);
115
- }
120
+ const uploadName = fileName ?? basename(filePath);
121
+ return fileSize <= DIRECT_UPLOAD_THRESHOLD_BYTES
122
+ ? directUpload(endpoint, apiKey, filePath, kbCode, parentCode, uploadName)
123
+ : partUpload(endpoint, apiKey, filePath, fileSize, kbCode, parentCode, uploadName);
124
+ }
125
+ async function directUpload(endpoint, apiKey, filePath, kbCode, parentCode, uploadName) {
116
126
  const content = await readFile(filePath);
117
127
  const form = new FormData();
118
- form.append('file', new Blob([content]), fileName ?? basename(filePath));
128
+ form.append('file', new Blob([content]), uploadName);
119
129
  form.append('kb_code', kbCode);
120
130
  if (parentCode) {
121
131
  form.append('parent_code', parentCode);
@@ -124,7 +134,7 @@ export async function uploadKnowledgeFile(endpoint, apiKey, filePath, kbCode, pa
124
134
  try {
125
135
  response = await fetch(`${endpoint}/open-apis/knowledge/file_upload`, {
126
136
  method: 'POST',
127
- headers: { 'Authorization': `Bearer ${apiKey}`, [CLI_VERSION_HEADER]: getCliVersion() },
137
+ headers: authHeaders(apiKey),
128
138
  body: form,
129
139
  });
130
140
  }
@@ -133,3 +143,74 @@ export async function uploadKnowledgeFile(endpoint, apiKey, filePath, kbCode, pa
133
143
  }
134
144
  return parseEnvelope(response);
135
145
  }
146
+ async function partUpload(endpoint, apiKey, filePath, fileSize, kbCode, parentCode, uploadName) {
147
+ const handle = await open(filePath, 'r');
148
+ let uploadId;
149
+ try {
150
+ const init = await postJson(`${endpoint}/open-apis`, apiKey, '/knowledge/init_part_upload', { fileName: uploadName, fileSize, kbCode, parentCode });
151
+ if (!init.uploadId) {
152
+ throw new BusinessError('分片上传初始化失败:服务端未返回 uploadId');
153
+ }
154
+ uploadId = init.uploadId;
155
+ const partSize = normalizePartSize(init.partSize);
156
+ const parts = [];
157
+ let position = 0;
158
+ while (position < fileSize) {
159
+ const partNumber = parts.length + 1;
160
+ const length = Math.min(partSize, fileSize - position);
161
+ const buffer = Buffer.allocUnsafe(length);
162
+ const { bytesRead } = await handle.read(buffer, 0, length, position);
163
+ if (bytesRead === 0) {
164
+ throw new BusinessError(`读取本地文件提前结束:path=${filePath},已读=${position},总大小=${fileSize}`);
165
+ }
166
+ const part = await uploadKnowledgePart(endpoint, apiKey, init.uploadId, partNumber, buffer.subarray(0, bytesRead), uploadName);
167
+ if (!part.etag) {
168
+ throw new BusinessError(`分片上传响应缺少 etag:partNumber=${partNumber}`);
169
+ }
170
+ parts.push({ partNumber, etag: part.etag });
171
+ position += bytesRead;
172
+ }
173
+ return await postJson(`${endpoint}/open-apis`, apiKey, '/knowledge/complete_part_upload', { uploadId: init.uploadId, parts, kbCode, parentCode });
174
+ }
175
+ catch (e) {
176
+ if (uploadId) {
177
+ await abortPartUploadQuietly(endpoint, apiKey, uploadId);
178
+ }
179
+ throw e;
180
+ }
181
+ finally {
182
+ await handle.close();
183
+ }
184
+ }
185
+ async function uploadKnowledgePart(endpoint, apiKey, uploadId, partNumber, chunk, uploadName) {
186
+ const form = new FormData();
187
+ form.append('file', new Blob([chunk]), `${uploadName}.part${partNumber}`);
188
+ form.append('upload_id', uploadId);
189
+ form.append('part_number', String(partNumber));
190
+ let response;
191
+ try {
192
+ response = await fetch(`${endpoint}/open-apis/knowledge/upload_part`, {
193
+ method: 'POST',
194
+ headers: authHeaders(apiKey),
195
+ body: form,
196
+ });
197
+ }
198
+ catch (e) {
199
+ throw new NetworkError(`无法连接 YOS 服务${envNoteOfEndpoint(endpoint)},原因:${e.message}`);
200
+ }
201
+ return parseEnvelope(response);
202
+ }
203
+ async function abortPartUploadQuietly(endpoint, apiKey, uploadId) {
204
+ try {
205
+ const response = await fetch(`${endpoint}/open-apis/knowledge/abort_part_upload?upload_id=${encodeURIComponent(uploadId)}`, { method: 'POST', headers: authHeaders(apiKey) });
206
+ await parseEnvelope(response);
207
+ }
208
+ catch {
209
+ }
210
+ }
211
+ function normalizePartSize(raw) {
212
+ if (raw === undefined || !Number.isFinite(raw) || raw <= 0) {
213
+ return FALLBACK_PART_SIZE_BYTES;
214
+ }
215
+ return Math.min(Math.floor(raw), MAX_PART_SIZE_BYTES);
216
+ }
@@ -67,7 +67,7 @@ yos retrieve read 'cortex://kb/KB20261009000001/doc/ND20261009000012:1-60' #
67
67
  # 整理知识库
68
68
  yos docs tree KB20261009000001 # 目录树(资源编码字段是 resourceCode)
69
69
  yos docs mkdir '会议纪要' --kb KB20261009000001 # 返回新节点编码
70
- yos kb upload ./报告.pdf --kb KB20261009000001 # 单文件上传(≤20MB,同名时按 --on-conflict 处理)
70
+ yos kb upload ./报告.pdf --kb KB20261009000001 # 单文件上传(≤20MB 直传,更大自动分片;同名按 --on-conflict 处理)
71
71
  yos docs download ND20261009000012 --out ./归档/ # 取回上传的原文件(仅 docType=file)
72
72
  yos docs count ND20261009000012 # 删除前查影响范围
73
73
 
@@ -148,7 +148,8 @@ yos kb upload ./会议纪要 --kb KB20260921000001 --json # 结
148
148
 
149
149
  硬约束与注意点:
150
150
 
151
- - **单文件 ≤20MB**:`错误: 文件超过 20MB 上限:size=22020096,path=...` + `提示: 更大文件请先在客户端上传后共享,或拆分为多个文件`(exit 1)
151
+ - **单文件大小按扩展名限制**:办公三件套(doc/docx/xls/xlsx/ppt/pptx)与 pdf ≤100MB,纯文本(txt/md/csv/html)与 json ≤10MB,图片(png/jpg/jpeg)≤20MB,其余扩展名 ≤20MB;超限 → `错误: 文件大小超出限制:fileName=xxx.csv,类型=csv,大小=12MB,上限=10MB`(exit 1)
152
+ - **超过 20MB 自动分片**:CLI 自动完成初始化、逐片上传与完成登记,命令参数不变;中途失败会尽力取消上传并保留原始错误
152
153
  - **服务端不做任何同名去重**:落库节点名恒等于本次上传用的文件名,同名副本会真实堆积并放大 `retrieve grep` 的命中数。同名判定与 `rename` 的新名都由 CLI 侧完成,因此绕过 CLI 直传不会有这层保护
153
154
  - 目录上传自动跳过隐藏文件(如 `.DS_Store`),并自动创建缺失的子文件夹(单文件上传不会,需先 `docs mkdir`)
154
155
  - 支持类型(以服务端返回为准):doc/docx/xls/xlsx/ppt/pptx/pdf/md/txt/json/csv/html/png/jpg/jpeg
@@ -108,7 +108,7 @@ yos support upgrade-log --start 2026-10-01 --end 2026-10-10 # 按发布日期
108
108
  ## 5. `support feedback '<内容>'` — 提交反馈(写操作)
109
109
 
110
110
  ```bash
111
- yos support feedback '知识库上传 20MB 以上的 PDF 会失败,希望支持大文件分片'
111
+ yos support feedback '检索结果希望支持按部门筛选'
112
112
  yos support feedback -- '转写结果说话人识别不准,希望提供纠错入口' # 内容以 - 开头时必须加 --
113
113
  ```
114
114
 
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { configDir, loadConfig, resolveChannel } from './config.js';
6
+ import { isManaged } from './managed.js';
6
7
  import { compareVersions, getCliVersion } from './npm-meta.js';
7
8
  import { printErr } from './output.js';
8
9
  export const CACHE_TTL_MS = 10 * 60 * 1000;
@@ -48,6 +49,9 @@ export function evaluateNotice(input) {
48
49
  return 'notify';
49
50
  }
50
51
  export function handleUpdateNotice(commandName) {
52
+ if (isManaged()) {
53
+ return;
54
+ }
51
55
  try {
52
56
  const channel = resolveChannel(loadConfig());
53
57
  if (channel === null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yosai/cli",
3
- "version": "0.2.1-test.12",
3
+ "version": "0.2.1-test.14",
4
4
  "description": "YOS 能力的 Agent 执行入口:工具调用、知识库管理与文件上传",
5
5
  "type": "module",
6
6
  "bin": {