evolcore 0.0.33 → 0.0.35-test.0
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/CHANGELOG.md +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +25 -8
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +131 -6
- package/dist/core/session/session-title.js +26 -1
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +17 -5
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +13 -16
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +12 -12
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
package/dist/channels/daemon.js
CHANGED
|
@@ -2,6 +2,7 @@ import { appendExpireEntry, recordNextLaunchCleanup, cleanupPreviousByKind, runE
|
|
|
2
2
|
import { ExecutionTimeoutError } from '../utils/error-utils.js';
|
|
3
3
|
import { normalizeExecutionMode } from '../trigger/validation.js';
|
|
4
4
|
import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
|
|
5
|
+
import { assertSystemSessionLaunchable } from '../core/system-session/registry.js';
|
|
5
6
|
const DEFAULT_WATCHDOG_MS = 11 * 60 * 1000;
|
|
6
7
|
export class DaemonChannel {
|
|
7
8
|
sessionManager;
|
|
@@ -253,6 +254,9 @@ export class DaemonChannel {
|
|
|
253
254
|
// 记忆整理会话:把会话原型与整理模式写进 metadata,供渲染层选 manifest / 手册,
|
|
254
255
|
// 供 ipc 侧校验写命令的 --scope。
|
|
255
256
|
const { memoryMode, memoryScope, consolidationBatchDir, consolidationChatDir, consolidationMemoryDir } = trigger.execution;
|
|
257
|
+
// 系统会话:类型 / subtype / runner 校验不过直接拒绝(Gemini 不支持),不降级成普通会话
|
|
258
|
+
if (memoryMode)
|
|
259
|
+
assertSystemSessionLaunchable({ type: 'memory', subtype: memoryMode }, baseagent);
|
|
256
260
|
// 反馈目标与触发方式(consolidation.ts 在 normalize 后补挂的字段):
|
|
257
261
|
// 收尾时据此投递反馈,并判断是否该反馈(定时触发不反馈)。
|
|
258
262
|
const { feedbackOrigin, consolidationTriggeredBy } = trigger.execution;
|
|
@@ -260,7 +264,7 @@ export class DaemonChannel {
|
|
|
260
264
|
channelKey: this.channelKey,
|
|
261
265
|
peerId: `trigger:${trigger.id}`,
|
|
262
266
|
peerName: trigger.name,
|
|
263
|
-
...(memoryMode ? { sessionType: 'memory', memoryMode } : {}),
|
|
267
|
+
...(memoryMode ? { systemSession: { type: 'memory', subtype: memoryMode }, sessionType: 'memory', memoryMode } : {}),
|
|
264
268
|
...(memoryScope ? { memoryScope } : {}),
|
|
265
269
|
...(consolidationBatchDir ? { consolidationBatchDir } : {}),
|
|
266
270
|
...(consolidationChatDir ? { consolidationChatDir } : {}),
|
|
@@ -570,8 +574,6 @@ export class DaemonChannel {
|
|
|
570
574
|
peerId: `bg:${spec.kind}`,
|
|
571
575
|
peerName: spec.kind,
|
|
572
576
|
peerType: 'system',
|
|
573
|
-
bgKind: spec.kind,
|
|
574
|
-
bgInstanceId: spec.instanceId,
|
|
575
577
|
...(spec.metadata ?? {}),
|
|
576
578
|
}, spec.kind, undefined, 'private', spec.baseagent, spec.agentAid, this.channelName, 'system');
|
|
577
579
|
const promise = new Promise((resolve) => {
|
|
@@ -586,6 +588,7 @@ export class DaemonChannel {
|
|
|
586
588
|
terminated: false,
|
|
587
589
|
cleanupPolicy,
|
|
588
590
|
agentAid: spec.agentAid,
|
|
591
|
+
runtimeDir: spec.runtimeDir,
|
|
589
592
|
watchdogTimer: setTimeout(() => {
|
|
590
593
|
this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
|
|
591
594
|
}, this.watchdogMs),
|
package/dist/cli/aun-commands.js
CHANGED
|
@@ -259,14 +259,18 @@ async function runManagedMsg(args, formatJson) {
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
else {
|
|
262
|
-
|
|
262
|
+
// `--thread` 是话题作用域的读取:目标对端由话题推断,数据是本 agent 自己的
|
|
263
|
+
// 消息日志,因此既不受"当前必须是私聊"限制(群会话里也要能查 A↔C 的话题),
|
|
264
|
+
// 也不受"只能查当前对端"限制。不带 --thread 时保持原有边界。
|
|
265
|
+
const threadScoped = parsed.command.kind === 'history' && !!parsed.command.threadId;
|
|
266
|
+
if (runtime && (runtime.channel !== 'aun' || runtime.chatType !== 'private') && !threadScoped) {
|
|
263
267
|
failManagedGroup(formatJson, 'SCOPE_MISMATCH', 'ec msg history/online 只能读取当前 AUN 私聊关系');
|
|
264
268
|
}
|
|
265
269
|
// owner/admin 可以跨对端读历史(每次查询仍只针对一个对端)。这里只是快速
|
|
266
270
|
// 预检:角色未知时放行交给 daemon 判定,daemon 侧 handleMsgOperation 才是
|
|
267
271
|
// 权威关卡,member 由 currentRelationOnly 约束、visitor 直接拒绝。
|
|
268
272
|
const mayReadCrossPeer = parsed.command.kind === 'history'
|
|
269
|
-
&& (runtime?.peerRole === undefined || isManagementRole(runtime.peerRole));
|
|
273
|
+
&& (threadScoped || runtime?.peerRole === undefined || isManagementRole(runtime.peerRole));
|
|
270
274
|
if (!mayReadCrossPeer && runtime?.channelId && parsed.command.target !== runtime.channelId.replace(/^@/, '')) {
|
|
271
275
|
failManagedGroup(formatJson, 'SCOPE_MISMATCH', `只能读取当前 AUN 私聊对端:${runtime.channelId}`);
|
|
272
276
|
}
|
|
@@ -2065,10 +2069,15 @@ Options:
|
|
|
2065
2069
|
// ==================== Group ====================
|
|
2066
2070
|
const GROUP_FROM_AID_FORMAT_EXAMPLE = 'sender.example.invalid';
|
|
2067
2071
|
const GROUP_AID_FORMAT_EXAMPLE = '12345.example.invalid';
|
|
2068
|
-
function
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
+
function printGroupArgsHint(sub) {
|
|
2073
|
+
if (sub === 'send') {
|
|
2074
|
+
console.error('正确格式: ec group send <发送方AID> <真实群AID> <正文或文件参数>');
|
|
2075
|
+
}
|
|
2076
|
+
else {
|
|
2077
|
+
console.error(`正确格式: ec group ${sub} <发送方AID> <真实群AID> [选项]`);
|
|
2078
|
+
}
|
|
2079
|
+
console.error(`仅格式示例(.example.invalid 是保留的虚构域,不是真实 AID): ec group ${sub} ${GROUP_FROM_AID_FORMAT_EXAMPLE} ${GROUP_AID_FORMAT_EXAMPLE}`);
|
|
2080
|
+
console.error('顺序固定为「先发送方 AID,后群 AID」;查询真实群 AID: ec group list <发送方AID>');
|
|
2072
2081
|
}
|
|
2073
2082
|
function printGroupRulesUsage() {
|
|
2074
2083
|
console.log(`用法:
|
|
@@ -2173,7 +2182,8 @@ Options:
|
|
|
2173
2182
|
无值时默认 5m;Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
|
|
2174
2183
|
取消/调整用返回的 trigger_id(trig_…),show/update/cancel 只认 id
|
|
2175
2184
|
--trigger-prompt <text> 自定义复查 Trigger prompt 中的用户段(仅 --check-every)
|
|
2176
|
-
--mention
|
|
2185
|
+
--mention <...> 发送时 @ 某个成员。单引号包住、逗号分隔多个 aid
|
|
2186
|
+
(如 --mention 'alice.agentid.pub,bob.agentid.pub'),可多次传入
|
|
2177
2187
|
--mention-all 发送时 @ 所有人
|
|
2178
2188
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
2179
2189
|
--as <type> 附件类型:image|video|voice|file(仅 --file 模式)
|
|
@@ -2187,7 +2197,7 @@ Options:
|
|
|
2187
2197
|
格式示例(.example.invalid 是保留的虚构域,以下 AID 均不是真实地址):
|
|
2188
2198
|
ec group create sender.example.invalid "Dev Team" --visibility private
|
|
2189
2199
|
ec group send sender.example.invalid 12345.example.invalid "hello team"
|
|
2190
|
-
ec group send sender.example.invalid 12345.example.invalid
|
|
2200
|
+
ec group send sender.example.invalid 12345.example.invalid '@member 看下 PR' --mention 'member.example.invalid'
|
|
2191
2201
|
ec group send sender.example.invalid 12345.example.invalid --file ./arch.png
|
|
2192
2202
|
ec group send sender.example.invalid g-12345.example.invalid "进度?" --thread thread-xxx --check-every 30m
|
|
2193
2203
|
ec group rules sender.example.invalid 12345.example.invalid set ./rules.md
|
|
@@ -2207,13 +2217,28 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2207
2217
|
console.error('❌ 缺少 <from-aid> 参数');
|
|
2208
2218
|
process.exit(1);
|
|
2209
2219
|
}
|
|
2220
|
+
// 第 1 个位置参数必须是「发送方 AID」。群 ID 与私聊 AID 语法相同,isValidAid
|
|
2221
|
+
// 会放行,随后在加载证书时抛 CERT_NOT_FOUND——这条路径既不说明原因也不给出
|
|
2222
|
+
// 正确写法。因此先用明确的群 ID 形态判定,把"参数写反了"当场指出来。
|
|
2223
|
+
// 例外:invites 是多值标志/邀请列表,第 1 个位置参数不是 from;create/list 的
|
|
2224
|
+
// 形态不同,也在下面各自的用法里处理,这里只拦截"以群 ID 打头"这一种错位。
|
|
2210
2225
|
const { isValidAid } = await import('../aun/aid/index.js');
|
|
2226
|
+
if (isExplicitGroupId(from)) {
|
|
2227
|
+
const second = args[2];
|
|
2228
|
+
console.error(`❌ 第 1 个位置参数 <from-aid> 无效:收到了群 ID: ${from}`);
|
|
2229
|
+
if (second && isValidAid(second) && !isExplicitGroupId(second)) {
|
|
2230
|
+
console.error(`检测到参数顺序写反了:第 2 个参数 ${second} 才是发送方 AID。`);
|
|
2231
|
+
console.error(`请改成: ec group ${sub} ${second} ${from}` + (sub === 'send' ? ' <正文>' : ''));
|
|
2232
|
+
}
|
|
2233
|
+
else {
|
|
2234
|
+
console.error('当前命令缺少发送方 AID,后续参数发生了错位。');
|
|
2235
|
+
}
|
|
2236
|
+
printGroupArgsHint(sub);
|
|
2237
|
+
process.exit(1);
|
|
2238
|
+
}
|
|
2211
2239
|
if (!isValidAid(from)) {
|
|
2212
2240
|
console.error(`❌ 第 1 个位置参数 <from-aid> 无效: ${from}`);
|
|
2213
|
-
|
|
2214
|
-
console.error('检测到这里填写的是群 ID;当前命令缺少发送方 AID,后续参数发生了错位。');
|
|
2215
|
-
}
|
|
2216
|
-
printGroupSendAidHint();
|
|
2241
|
+
printGroupArgsHint(sub);
|
|
2217
2242
|
process.exit(1);
|
|
2218
2243
|
}
|
|
2219
2244
|
if ((process.env.EVOLCORE_SESSION_ID || (sub === 'history' && readDelegationCredential())) && sub !== 'send') {
|
|
@@ -2285,7 +2310,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2285
2310
|
const gid = args[2];
|
|
2286
2311
|
if (!gid) {
|
|
2287
2312
|
console.error('❌ 缺少第 2 个位置参数 <group-aid>');
|
|
2288
|
-
|
|
2313
|
+
printGroupArgsHint(sub);
|
|
2289
2314
|
process.exit(1);
|
|
2290
2315
|
}
|
|
2291
2316
|
try {
|
|
@@ -2293,7 +2318,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2293
2318
|
}
|
|
2294
2319
|
catch (error) {
|
|
2295
2320
|
console.error(`❌ 第 2 个位置参数 <group-aid> 不是有效群 AID: ${gid}: ${error.message}`);
|
|
2296
|
-
|
|
2321
|
+
printGroupArgsHint(sub);
|
|
2297
2322
|
process.exit(1);
|
|
2298
2323
|
}
|
|
2299
2324
|
};
|
package/dist/cli/ctl-command.js
CHANGED
|
@@ -25,7 +25,9 @@ const CTL_HELP = `用法: ec ctl <command> [args...]
|
|
|
25
25
|
ec ctl name "新会话名称"
|
|
26
26
|
ec ctl send "任务已完成"
|
|
27
27
|
ec ctl file report.md
|
|
28
|
-
ec ctl queue --full
|
|
28
|
+
ec ctl queue --full
|
|
29
|
+
ec ctl path list
|
|
30
|
+
ec ctl path grant "D:/shared/data" --access read`;
|
|
29
31
|
export async function cmdCtl(args) {
|
|
30
32
|
if (args.length === 0) {
|
|
31
33
|
console.error(CTL_HELP);
|
|
@@ -4,7 +4,7 @@ import os from 'os';
|
|
|
4
4
|
import { spawn, execFile } from 'child_process';
|
|
5
5
|
import { promisify } from 'util';
|
|
6
6
|
import readline from 'readline';
|
|
7
|
-
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath } from '../paths.js';
|
|
7
|
+
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath, asModeOverridePath } from '../paths.js';
|
|
8
8
|
import { loadDefaults, loadAllAgents, loadDaemonConfig, saveDaemonConfig } from '../config-store.js';
|
|
9
9
|
import { resolveEffective } from '../config/config-manager.js';
|
|
10
10
|
import { resolveAnthropicConfig } from '../agents/baseagent.js';
|
|
@@ -25,7 +25,7 @@ import { isNonAgentSessionChannel } from '../core/system-channels.js';
|
|
|
25
25
|
import { printCodeStats } from './code-stats.js';
|
|
26
26
|
import { getProcessStartTime, startTimeMatches } from '../utils/process-introspect.js';
|
|
27
27
|
import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
28
|
-
import { MAIN_CLI_BIN, RUNTIME_HOME_ENV, WEB_CLI_BIN, WEB_PACKAGE_LATEST, WEB_PACKAGE_NAME } from '../product.js';
|
|
28
|
+
import { MAIN_CLI_BIN, RUNTIME_HOME_ENV, TARGET_PACKAGE_ROOT_ENV, WEB_CLI_BIN, WEB_PACKAGE_LATEST, WEB_PACKAGE_NAME, } from '../product.js';
|
|
29
29
|
import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
30
30
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
31
31
|
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
@@ -79,7 +79,7 @@ export async function ensureCodexHookBeforeLifecycle(onRepaired) {
|
|
|
79
79
|
});
|
|
80
80
|
if (!usesCodex)
|
|
81
81
|
return true;
|
|
82
|
-
const packageRoot = getPackageRoot();
|
|
82
|
+
const packageRoot = process.env[TARGET_PACKAGE_ROOT_ENV] || getPackageRoot();
|
|
83
83
|
const result = await ensureCodexManagedHook({
|
|
84
84
|
packageRoot,
|
|
85
85
|
repairDecision: state => confirmCodexHookRebind(state, packageRoot),
|
|
@@ -328,6 +328,11 @@ async function launchPackageLifecycle(source, log, options = {}) {
|
|
|
328
328
|
const result = await platform.launchMonitorAndConfirm(args, env, pid => scanInstances().restartMonitors.some(item => item.alive && item.record.pid === pid), pid => scanInstances().restartMonitors.some(item => item.alive && item.record.pid !== pid));
|
|
329
329
|
if (!result.ok && launch.stageDir)
|
|
330
330
|
platform.cleanupMonitorStage(launch.stageDir);
|
|
331
|
+
if (!result.ok) {
|
|
332
|
+
for (const line of (result.stderrTail ?? '').split(/\r?\n/).filter(Boolean).slice(-8)) {
|
|
333
|
+
console.error(` | ${line}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
331
336
|
return { ok: result.ok, ...(!result.ok ? { reason: result.reason } : {}) };
|
|
332
337
|
}
|
|
333
338
|
function requireCompletedDataMigration(root) {
|
|
@@ -458,7 +463,73 @@ function resolveRunningDuration(pid, uptime) {
|
|
|
458
463
|
const instance = scanInstances().mains.find(entry => entry.alive && entry.record.pid === pid);
|
|
459
464
|
return instance ? formatRunningDuration(Date.now() - instance.record.startedAt) : '未知';
|
|
460
465
|
}
|
|
461
|
-
|
|
466
|
+
/**
|
|
467
|
+
* 渲染"运行模式"那一行。
|
|
468
|
+
*
|
|
469
|
+
* `daemonMode` 有值时用 daemon 实例记录里的模式(`ec status` 看的是正在跑的进程);
|
|
470
|
+
* 没有时按当前进程判断(`ec start` 时就是这一份 CLI 即将拉起的模式)。
|
|
471
|
+
*/
|
|
472
|
+
/**
|
|
473
|
+
* CLI 侧按包路径判断模式。**不能用 isDevMode()**:它首次求值会读走并删除
|
|
474
|
+
* `--as-mode` 覆盖文件,那份文件只该由 daemon 消费。
|
|
475
|
+
*/
|
|
476
|
+
function packageIsDevMode() {
|
|
477
|
+
return !getPackageRoot().includes('node_modules');
|
|
478
|
+
}
|
|
479
|
+
export function describeRunMode(daemonMode, cliIsDevMode = packageIsDevMode()) {
|
|
480
|
+
const effective = daemonMode ?? (cliIsDevMode ? 'dev' : 'install');
|
|
481
|
+
const label = effective === 'install' ? '安装模式' : '开发模式';
|
|
482
|
+
const note = effective === 'install'
|
|
483
|
+
? '(校验随包文件,篡改自动恢复)'
|
|
484
|
+
: '(不校验随包文件,可随意改动)';
|
|
485
|
+
return `运行模式: ${label}${note}`;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* 把本次 `--as-mode` 写进一次性覆盖文件,daemon 启动时读走并删除。
|
|
489
|
+
*
|
|
490
|
+
* 不走环境变量:环境变量会被 `ec restart` 全量继承,模式会在操作者不知情的
|
|
491
|
+
* 情况下被带到新进程。文件是一次性的,daemon 读过即删;若 daemon 最终没起来,
|
|
492
|
+
* 文件还留着,会在下一次启动时被读到——所以启动真实失败时此处清理。
|
|
493
|
+
*/
|
|
494
|
+
function writeAsModeOverride(asMode) {
|
|
495
|
+
const file = asModeOverridePath();
|
|
496
|
+
if (!asMode) {
|
|
497
|
+
// 没带 --as-mode:清掉上次失败启动可能残留的文件,保证这次按包路径判断
|
|
498
|
+
clearAsModeOverride(file);
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
502
|
+
fs.writeFileSync(file, `${asMode}\n`, 'utf-8');
|
|
503
|
+
return file;
|
|
504
|
+
}
|
|
505
|
+
function clearAsModeOverride(file) {
|
|
506
|
+
if (!file)
|
|
507
|
+
return;
|
|
508
|
+
try {
|
|
509
|
+
fs.rmSync(file, { force: true });
|
|
510
|
+
}
|
|
511
|
+
catch { /* ignore */ }
|
|
512
|
+
}
|
|
513
|
+
/** daemon 已在运行时 `--as-mode` 不会生效,明说,别让人以为切过去了。 */
|
|
514
|
+
function warnAsModeNotApplied(asMode) {
|
|
515
|
+
if (!asMode)
|
|
516
|
+
return;
|
|
517
|
+
console.log(` ⚠ --as-mode ${asMode} 未生效:daemon 已在运行,模式只在启动时确定。`);
|
|
518
|
+
console.log(` 需要切换请先 ec stop,再 ec start --as-mode ${asMode}。`);
|
|
519
|
+
}
|
|
520
|
+
function printAsModeNotice(asMode) {
|
|
521
|
+
if (!asMode)
|
|
522
|
+
return;
|
|
523
|
+
console.log('');
|
|
524
|
+
console.log(`🧪 本次运行模式已设为${asMode === 'install' ? '安装模式' : '开发模式'}(ec start --as-mode ${asMode})`);
|
|
525
|
+
if (asMode === 'install') {
|
|
526
|
+
console.log(' 随包发布的 kits/技能文件将按台账校验,带外改动会被自动恢复。');
|
|
527
|
+
console.log(' 开发仓里请勿长期使用:本机 kits 文件会被登记进真实台账,');
|
|
528
|
+
console.log(' 之后再按开发模式修改它们,会在下次以安装模式启动时被回滚。');
|
|
529
|
+
}
|
|
530
|
+
console.log(' 只影响这一次启动;ec restart 不带该参数,会恢复按包路径判断。');
|
|
531
|
+
}
|
|
532
|
+
function printStartupInfo(opts = {}, asMode) {
|
|
462
533
|
const pkgRoot = getPackageRoot();
|
|
463
534
|
const isNpmInstall = pkgRoot.includes('node_modules');
|
|
464
535
|
const cliRunsSource = !import.meta.url.includes('/dist/');
|
|
@@ -502,13 +573,18 @@ function printStartupInfo(opts = {}) {
|
|
|
502
573
|
console.log(`${prefix}${version}${pidPart}${aunPart}`);
|
|
503
574
|
console.log(` 包路径: ${pkgRoot}`);
|
|
504
575
|
console.log(` 安装类型: ${isNpmInstall ? 'npm全局安装' : '开发仓(link)'}`);
|
|
576
|
+
// asMode 只是"本次请求",是否生效要看 daemon 是否真的由这次启动拉起,标清楚别和实际模式混淆
|
|
577
|
+
const modeLine = describeRunMode(opts.runMode ?? asMode, !isNpmInstall);
|
|
578
|
+
console.log(` ${asMode && !opts.runMode ? `${modeLine}(--as-mode 请求)` : modeLine}`);
|
|
505
579
|
console.log(` CLI执行: ${cliRunsSource ? '源码(tsx)' : '编译产物(dist)'}`);
|
|
506
580
|
console.log(` Daemon执行: ${daemonRunsDist ? '编译产物(dist)' : '未知'}`);
|
|
507
581
|
console.log(` 代码时间: ${latestMtime ? formatLocalTime(latestMtime) : '?'}`);
|
|
508
582
|
}
|
|
509
583
|
export async function cmdStart(opts = {}) {
|
|
510
|
-
|
|
511
|
-
|
|
584
|
+
// Measure from process start so the reported time includes CLI bootstrap
|
|
585
|
+
// (tsx compilation in a dev checkout), which is what the operator waits for.
|
|
586
|
+
const cmdStartedAt = Math.min(Date.now(), Math.round(performance.timeOrigin));
|
|
587
|
+
printStartupInfo(undefined, opts.asMode);
|
|
512
588
|
const p = resolvePaths();
|
|
513
589
|
const existingDaemon = await probeDaemon(p.socket);
|
|
514
590
|
if (existingDaemon) {
|
|
@@ -521,6 +597,9 @@ export async function cmdStart(opts = {}) {
|
|
|
521
597
|
return;
|
|
522
598
|
}
|
|
523
599
|
console.log(` EvolCore is already running (PID: ${existingDaemon.pid}, IPC reachable)`);
|
|
600
|
+
const runningRecord = scanInstances().mains.find(m => m.alive && m.record.pid === existingDaemon.pid)?.record;
|
|
601
|
+
console.log(` 当前 daemon ${describeRunMode(runningRecord?.runMode)}`);
|
|
602
|
+
warnAsModeNotApplied(opts.asMode);
|
|
524
603
|
console.log(` 已在运行;已运行时长: ${resolveRunningDuration(existingDaemon.pid, existingDaemon.uptime)};部署/脚本可直接复用当前实例`);
|
|
525
604
|
return;
|
|
526
605
|
}
|
|
@@ -621,6 +700,8 @@ export async function cmdStart(opts = {}) {
|
|
|
621
700
|
console.log(` EvolCore is already running (PID: ${aliveMains.map(m => m.record.pid).join(', ')})`);
|
|
622
701
|
console.log(` 启动于: ${new Date(first.record.startedAtIso).toLocaleString()}`);
|
|
623
702
|
console.log(` 启动方式: ${first.record.launchedBy}`);
|
|
703
|
+
console.log(` 当前 daemon ${describeRunMode(first.record.runMode)}`);
|
|
704
|
+
warnAsModeNotApplied(opts.asMode);
|
|
624
705
|
// 报告 AID 状态
|
|
625
706
|
if (status.aidLastActivity.size > 0) {
|
|
626
707
|
console.log(' AID 状态:');
|
|
@@ -647,7 +728,9 @@ export async function cmdStart(opts = {}) {
|
|
|
647
728
|
// On Windows/macOS the process environment may be unavailable, so an
|
|
648
729
|
// unregistered process from this package is conservatively treated as a
|
|
649
730
|
// blocker when its HOME cannot be identified.
|
|
650
|
-
|
|
731
|
+
// No live main reached this point, so the registry snapshot above is still
|
|
732
|
+
// the authoritative "known PID" set; avoid a second scan.
|
|
733
|
+
const orphans = findOrphanProcesses(status);
|
|
651
734
|
const blockingOrphans = orphans.filter(o => isRuntimeOrphan(o));
|
|
652
735
|
if (blockingOrphans.length > 0) {
|
|
653
736
|
console.error(`❌ 检测到未登记的 EvolCore 进程(PID: ${blockingOrphans.map(o => o.pid).join(', ')}),已停止启动以避免重复连接 AUN。`);
|
|
@@ -658,10 +741,13 @@ export async function cmdStart(opts = {}) {
|
|
|
658
741
|
reportOrphans(orphans);
|
|
659
742
|
if (versionInfo.some(result => result.hasUpdate)) {
|
|
660
743
|
cleanEnv();
|
|
744
|
+
// 这条路由 restart-monitor 拉起 daemon,同样靠覆盖文件把 --as-mode 带过去
|
|
745
|
+
const overrideFile = writeAsModeOverride(opts.asMode);
|
|
661
746
|
const launched = await launchPackageLifecycle('start', console.log, {
|
|
662
747
|
prepared: true, foreground: opts.foreground, env: buildDaemonEnv(p, opts),
|
|
663
748
|
});
|
|
664
749
|
if (!launched.ok) {
|
|
750
|
+
clearAsModeOverride(overrideFile);
|
|
665
751
|
console.error(`❌ 启动事务未能建立:${launched.reason}`);
|
|
666
752
|
process.exitCode = 1;
|
|
667
753
|
}
|
|
@@ -684,6 +770,7 @@ export async function cmdStart(opts = {}) {
|
|
|
684
770
|
const appMain = path.join(getPackageRoot(), 'dist', 'index.js');
|
|
685
771
|
const daemonEnv = buildDaemonEnv(p, opts);
|
|
686
772
|
if (opts.foreground) {
|
|
773
|
+
const overrideFile = writeAsModeOverride(opts.asMode);
|
|
687
774
|
const child = spawn('node', ['--no-warnings=ExperimentalWarning', appMain], {
|
|
688
775
|
detached: false,
|
|
689
776
|
stdio: 'inherit',
|
|
@@ -692,6 +779,7 @@ export async function cmdStart(opts = {}) {
|
|
|
692
779
|
});
|
|
693
780
|
const childPid = child.pid;
|
|
694
781
|
if (!childPid) {
|
|
782
|
+
clearAsModeOverride(overrideFile);
|
|
695
783
|
console.error('❌ Failed to start EvolCore (process creation failed)');
|
|
696
784
|
process.exit(1);
|
|
697
785
|
return;
|
|
@@ -710,16 +798,17 @@ export async function cmdStart(opts = {}) {
|
|
|
710
798
|
console.log('🔧 调试模式已启用(EVOLCORE_DEBUG_MODE=1)');
|
|
711
799
|
console.log(' 在宿主终端以任意 session 身份执行命令:');
|
|
712
800
|
console.log('');
|
|
713
|
-
console.log(
|
|
801
|
+
console.log(` ec msg send '<消息内容>' --as-session <sessionId>`);
|
|
714
802
|
console.log(' ec trigger list --as-session <sessionId>');
|
|
715
803
|
console.log(' ec memory list --as-session <sessionId>');
|
|
716
804
|
console.log('');
|
|
717
805
|
console.log(' 查看可用会话(运行后将在此处列出):');
|
|
718
|
-
console.log(
|
|
806
|
+
console.log(` ec msg send 'test' --as-session # 不带 sessionId 自动列出`);
|
|
719
807
|
console.log('');
|
|
720
808
|
console.log(' 注意:重启 daemon(ec restart)会清除调试模式,');
|
|
721
809
|
console.log(' 需重新执行 ec start --debug 才能恢复。');
|
|
722
810
|
}
|
|
811
|
+
printAsModeNotice(opts.asMode);
|
|
723
812
|
void waitForExistingEcweb(p).then((ecweb) => {
|
|
724
813
|
if (shouldPrintNoSelfAgentHints) {
|
|
725
814
|
printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted: ecweb !== null, skipped });
|
|
@@ -766,6 +855,7 @@ export async function cmdStart(opts = {}) {
|
|
|
766
855
|
const stdoutLog = path.join(p.logs, 'stdout.log');
|
|
767
856
|
const out = fs.openSync(stdoutLog, 'a');
|
|
768
857
|
const err = fs.openSync(stdoutLog, 'a');
|
|
858
|
+
const overrideFile = writeAsModeOverride(opts.asMode);
|
|
769
859
|
const child = spawn('node', ['--no-warnings=ExperimentalWarning', appMain], {
|
|
770
860
|
detached: true,
|
|
771
861
|
stdio: ['ignore', out, err],
|
|
@@ -773,6 +863,12 @@ export async function cmdStart(opts = {}) {
|
|
|
773
863
|
env: daemonEnv,
|
|
774
864
|
});
|
|
775
865
|
const childPid = child.pid;
|
|
866
|
+
// daemon 已在启动时读走该文件;若未能起来(pid 丢失/提前退出),清掉避免影响下次启动
|
|
867
|
+
child.once('error', () => clearAsModeOverride(overrideFile));
|
|
868
|
+
// Track liveness from the spawn handle instead of launching tasklist on
|
|
869
|
+
// every poll; the poll interval below is short enough that it matters.
|
|
870
|
+
let childExited = false;
|
|
871
|
+
child.once('exit', () => { childExited = true; });
|
|
776
872
|
child.unref();
|
|
777
873
|
// 等待 ready signal(最多 30 秒,AUN sidecar 超时 15s + 其他通道连接)
|
|
778
874
|
const startTime = Date.now();
|
|
@@ -823,25 +919,27 @@ export async function cmdStart(opts = {}) {
|
|
|
823
919
|
printCodeStats(getPackageRoot(), p.logs);
|
|
824
920
|
}
|
|
825
921
|
console.log(`⏱ done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
|
|
922
|
+
printAsModeNotice(opts.asMode);
|
|
826
923
|
if (opts.debug) {
|
|
827
924
|
console.log('');
|
|
828
925
|
console.log('🔧 调试模式已启用(EVOLCORE_DEBUG_MODE=1)');
|
|
829
926
|
console.log(' 在宿主终端以任意 session 身份执行命令:');
|
|
830
927
|
console.log('');
|
|
831
|
-
console.log(
|
|
928
|
+
console.log(` ec msg send '<消息内容>' --as-session <sessionId>`);
|
|
832
929
|
console.log(' ec trigger list --as-session <sessionId>');
|
|
833
930
|
console.log(' ec memory list --as-session <sessionId>');
|
|
834
931
|
console.log('');
|
|
835
932
|
console.log(' 查看可用会话(运行后将在此处列出):');
|
|
836
|
-
console.log(
|
|
933
|
+
console.log(` ec msg send 'test' --as-session # 不带 sessionId 自动列出`);
|
|
837
934
|
console.log('');
|
|
838
935
|
console.log(' 注意:重启 daemon(ec restart)会清除调试模式,');
|
|
839
936
|
console.log(' 需重新执行 ec start --debug 才能恢复。');
|
|
840
937
|
}
|
|
841
|
-
// ECWeb lifecycle belongs to the daemon; the CLI only
|
|
842
|
-
// the
|
|
843
|
-
|
|
938
|
+
// ECWeb lifecycle belongs to the daemon; the CLI only needs its health
|
|
939
|
+
// for the no-agent hints. Waiting otherwise just holds the prompt for
|
|
940
|
+
// up to the probe timeout after the daemon is already ready.
|
|
844
941
|
if (shouldPrintNoSelfAgentHints) {
|
|
942
|
+
const ecwebStarted = await waitForExistingEcweb(p) !== null;
|
|
845
943
|
printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted, skipped });
|
|
846
944
|
}
|
|
847
945
|
return;
|
|
@@ -860,7 +958,7 @@ export async function cmdStart(opts = {}) {
|
|
|
860
958
|
return;
|
|
861
959
|
}
|
|
862
960
|
// 进程已退出且无 ready signal
|
|
863
|
-
if (
|
|
961
|
+
if (childExited) {
|
|
864
962
|
// 给进程一点时间写 ready signal(可能刚好在写入中)
|
|
865
963
|
if (Date.now() - startTime > 3000) {
|
|
866
964
|
console.log('❌ Failed to start EvolCore');
|
|
@@ -875,9 +973,9 @@ export async function cmdStart(opts = {}) {
|
|
|
875
973
|
return;
|
|
876
974
|
}
|
|
877
975
|
}
|
|
878
|
-
setTimeout(checkReady,
|
|
976
|
+
setTimeout(checkReady, 200);
|
|
879
977
|
};
|
|
880
|
-
setTimeout(checkReady,
|
|
978
|
+
setTimeout(checkReady, 200);
|
|
881
979
|
}
|
|
882
980
|
function isPidReused(pid, expectedStartedAt) {
|
|
883
981
|
if (expectedStartedAt === undefined)
|
|
@@ -890,7 +988,10 @@ function reportPidReused(pid) {
|
|
|
890
988
|
}
|
|
891
989
|
async function stopPid(pid, gracefulRequested = false, expectedStartedAt) {
|
|
892
990
|
console.log(`🛑 Stopping EvolCore (PID: ${pid})...`);
|
|
893
|
-
|
|
991
|
+
// A graceful request was accepted by the daemon that answered for this exact
|
|
992
|
+
// PID over IPC, so its identity is already proven; the start-time probe (a
|
|
993
|
+
// PowerShell launch on Windows) only guards the kill path.
|
|
994
|
+
if (!gracefulRequested && isPidReused(pid, expectedStartedAt)) {
|
|
894
995
|
reportPidReused(pid);
|
|
895
996
|
return true;
|
|
896
997
|
}
|
|
@@ -939,7 +1040,7 @@ function isRuntimeOrphan(orphan, currentHome = resolveRoot()) {
|
|
|
939
1040
|
export async function cmdStop() {
|
|
940
1041
|
const status = scanInstances();
|
|
941
1042
|
const p = resolvePaths();
|
|
942
|
-
const initialOrphans = findOrphanProcesses();
|
|
1043
|
+
const initialOrphans = findOrphanProcesses(status);
|
|
943
1044
|
const runtimeOrphans = initialOrphans.filter(orphan => isRuntimeOrphan(orphan, p.root));
|
|
944
1045
|
const ping = await probeDaemon(p.socket);
|
|
945
1046
|
if (ping && !hasVisibleDaemonPid(status, ping) && runtimeOrphans.length === 0) {
|
|
@@ -964,7 +1065,8 @@ export async function cmdStop() {
|
|
|
964
1065
|
process.exitCode = 1;
|
|
965
1066
|
return;
|
|
966
1067
|
}
|
|
967
|
-
|
|
1068
|
+
// stopPid already observed every process leave the process table, so the
|
|
1069
|
+
// records can be cleaned immediately (same reasoning as restart-monitor).
|
|
968
1070
|
cleanupInstances();
|
|
969
1071
|
if (aliveMains.length > 1) {
|
|
970
1072
|
console.log(`⚠ 停止了 ${aliveMains.length} 个 main 实例: ${aliveMains.map(m => m.record.pid).join(', ')}`);
|
|
@@ -973,6 +1075,9 @@ export async function cmdStop() {
|
|
|
973
1075
|
export async function cmdRestart(opts = {}) {
|
|
974
1076
|
const cmdStartedAt = Date.now();
|
|
975
1077
|
console.log('🔄 Restarting EvolCore...');
|
|
1078
|
+
// 重启不带 --as-mode,模式按包路径重新判定;清掉可能残留的覆盖文件,并显示出来
|
|
1079
|
+
writeAsModeOverride(undefined);
|
|
1080
|
+
console.log(` ${describeRunMode()}`);
|
|
976
1081
|
if (!await ensureCodexHookBeforeLifecycle())
|
|
977
1082
|
return;
|
|
978
1083
|
const initialStatus = scanInstances();
|
|
@@ -1186,7 +1291,8 @@ export async function cmdStatus() {
|
|
|
1186
1291
|
console.log('');
|
|
1187
1292
|
}
|
|
1188
1293
|
if (pid) {
|
|
1189
|
-
|
|
1294
|
+
const mainRecord = aliveMains.find(m => m.record.pid === pid)?.record;
|
|
1295
|
+
printStartupInfo({ pid, running: true, runMode: mainRecord?.runMode });
|
|
1190
1296
|
if (!ping) {
|
|
1191
1297
|
console.log('⚠ 仅确认 daemon PID 存活,IPC 不可用,不能据此判断服务健康。');
|
|
1192
1298
|
if (ipcFailure)
|
|
@@ -3203,12 +3309,13 @@ export async function cmdDiagnose(args = []) {
|
|
|
3203
3309
|
console.log('[diagnose] - Codex managed hook: 跳过(没有 Agent 使用 Codex)');
|
|
3204
3310
|
}
|
|
3205
3311
|
else {
|
|
3206
|
-
const
|
|
3312
|
+
const packageRoot = process.env[TARGET_PACKAGE_ROOT_ENV] || getPackageRoot();
|
|
3313
|
+
const hook = inspectCodexManagedHook({ packageRoot });
|
|
3207
3314
|
if (hook.status === 'installed') {
|
|
3208
3315
|
console.log(`[diagnose] ✓ Codex managed hook 已安装: ${hook.hookPath}`);
|
|
3209
3316
|
if (runHookLaunchProbe) {
|
|
3210
3317
|
try {
|
|
3211
|
-
const launch = runCodexHookLaunchDiagnostic({ packageRoot
|
|
3318
|
+
const launch = runCodexHookLaunchDiagnostic({ packageRoot });
|
|
3212
3319
|
if (launch.status === 'ok') {
|
|
3213
3320
|
console.log('[diagnose] ✓ Codex managed hook 启动探针报告: ' + launch.output);
|
|
3214
3321
|
for (const result of launch.results) {
|
package/dist/cli/fs-command.js
CHANGED
|
@@ -56,7 +56,8 @@ Options:
|
|
|
56
56
|
--content-type <mime> 上传时指定内容类型
|
|
57
57
|
--public 上传后公开可读(personal storage)
|
|
58
58
|
--visibility <public|private> chmod 可见性
|
|
59
|
-
--allow-roles <roles> chmod
|
|
59
|
+
--allow-roles <roles> chmod 角色约束,单引号包住、逗号分隔,
|
|
60
|
+
如 --allow-roles 'owner,admin'
|
|
60
61
|
-m aid:<aid>:<perms> setfacl 新增/更新 ACL
|
|
61
62
|
-x aid:<aid> setfacl 移除 ACL
|
|
62
63
|
--source <AID>:<path> mount 虚拟卷来源
|