@clawos-dev/clawd 0.2.71-beta.128.2805736 → 0.2.71-beta.130.ea9dc82
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/dist/cli.cjs +133 -59
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -5196,6 +5196,10 @@ var init_remote_persona = __esm({
|
|
|
5196
5196
|
alias: external_exports.string().min(1),
|
|
5197
5197
|
remoteUrl: external_exports.string().min(1),
|
|
5198
5198
|
capabilityToken: external_exports.string().min(1),
|
|
5199
|
+
// v2 Phase 7: 远端 daemon 给我颁发的 capability id。preview 时 whoami 返回的
|
|
5200
|
+
// capability.id 直接存进来。旧 v1 持久化文件没此字段 → schema parse fail →
|
|
5201
|
+
// RemotePersonaStore.list 静默跳过 (alpha 阶段可接受, 老板重新 add 即可)。
|
|
5202
|
+
myCapabilityId: external_exports.string().min(1),
|
|
5199
5203
|
remotePersonaId: external_exports.string().min(1),
|
|
5200
5204
|
remoteDisplayName: external_exports.string(),
|
|
5201
5205
|
ownerDisplayName: external_exports.string().optional(),
|
|
@@ -5207,6 +5211,8 @@ var init_remote_persona = __esm({
|
|
|
5207
5211
|
alias: external_exports.string().min(1),
|
|
5208
5212
|
remoteUrl: external_exports.string().min(1),
|
|
5209
5213
|
capabilityToken: external_exports.string().min(1),
|
|
5214
|
+
// v2 Phase 7: UI 调 preview 后从 whoami response 取 capability.id 传入
|
|
5215
|
+
myCapabilityId: external_exports.string().min(1),
|
|
5210
5216
|
remotePersonaId: external_exports.string().min(1),
|
|
5211
5217
|
remoteDisplayName: external_exports.string(),
|
|
5212
5218
|
ownerDisplayName: external_exports.string().optional()
|
|
@@ -27950,10 +27956,62 @@ function forkSession(input) {
|
|
|
27950
27956
|
return { forkedToolSessionId, forkedFilePath };
|
|
27951
27957
|
}
|
|
27952
27958
|
|
|
27959
|
+
// src/permission/capability.ts
|
|
27960
|
+
function matchResource(grant, target) {
|
|
27961
|
+
if (grant.type === "*") return true;
|
|
27962
|
+
if (grant.type !== target.type) return false;
|
|
27963
|
+
return grant.id === target.id;
|
|
27964
|
+
}
|
|
27965
|
+
function assertGrant(grants, resource, action) {
|
|
27966
|
+
for (const g2 of grants) {
|
|
27967
|
+
if (!matchResource(g2.resource, resource)) continue;
|
|
27968
|
+
if (g2.actions.includes(action)) return true;
|
|
27969
|
+
if (g2.actions.includes("admin")) return true;
|
|
27970
|
+
}
|
|
27971
|
+
return false;
|
|
27972
|
+
}
|
|
27973
|
+
|
|
27974
|
+
// src/permission/session-access.ts
|
|
27975
|
+
function canAccessSession(ctx, sessionId, action, deps) {
|
|
27976
|
+
if (ctx.principal.kind === "owner") return true;
|
|
27977
|
+
const file = deps.readSession(sessionId);
|
|
27978
|
+
if (!file) return true;
|
|
27979
|
+
return canAccessPersona(ctx.grants, file.ownerPersonaId, action);
|
|
27980
|
+
}
|
|
27981
|
+
function canAccessPersona(grants, personaId, action) {
|
|
27982
|
+
if (!personaId) return false;
|
|
27983
|
+
const resource = { type: "persona", id: personaId };
|
|
27984
|
+
return assertGrant(grants, resource, action);
|
|
27985
|
+
}
|
|
27986
|
+
|
|
27953
27987
|
// src/handlers/session.ts
|
|
27988
|
+
init_protocol();
|
|
27954
27989
|
function buildSessionHandlers(deps) {
|
|
27955
|
-
const { manager, observer, getAdapter: getAdapter2 } = deps;
|
|
27956
|
-
const
|
|
27990
|
+
const { manager, observer, getAdapter: getAdapter2, store } = deps;
|
|
27991
|
+
const ensureSessionAccess = (ctx, sessionId, action) => {
|
|
27992
|
+
if (!ctx) return;
|
|
27993
|
+
const ok = canAccessSession(ctx, sessionId, action, {
|
|
27994
|
+
readSession: (sid) => store.read(sid)
|
|
27995
|
+
});
|
|
27996
|
+
if (!ok) {
|
|
27997
|
+
throw new ClawdError(
|
|
27998
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
27999
|
+
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${action} session ${sessionId}`
|
|
28000
|
+
);
|
|
28001
|
+
}
|
|
28002
|
+
};
|
|
28003
|
+
const ensurePersonaAccess = (ctx, personaId, action) => {
|
|
28004
|
+
if (!ctx) return;
|
|
28005
|
+
if (ctx.principal.kind === "owner") return;
|
|
28006
|
+
const ok = canAccessPersona(ctx.grants, personaId, action);
|
|
28007
|
+
if (!ok) {
|
|
28008
|
+
throw new ClawdError(
|
|
28009
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
28010
|
+
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${action} on persona:${personaId ?? "<none>"}`
|
|
28011
|
+
);
|
|
28012
|
+
}
|
|
28013
|
+
};
|
|
28014
|
+
const create = async (frame, _client, ctx) => {
|
|
27957
28015
|
const args = SessionCreateArgs.parse(frame);
|
|
27958
28016
|
if (args.ownerPersonaId) {
|
|
27959
28017
|
const persona = deps.personaRegistry.get(args.ownerPersonaId);
|
|
@@ -27961,58 +28019,75 @@ function buildSessionHandlers(deps) {
|
|
|
27961
28019
|
throw new Error(`persona not found: ${args.ownerPersonaId}`);
|
|
27962
28020
|
}
|
|
27963
28021
|
}
|
|
28022
|
+
ensurePersonaAccess(ctx, args.ownerPersonaId, "send");
|
|
27964
28023
|
const { response, broadcast } = manager.create(args);
|
|
27965
28024
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
27966
28025
|
};
|
|
27967
|
-
const list = async () => {
|
|
28026
|
+
const list = async (_frame, _client, ctx) => {
|
|
27968
28027
|
const { response } = manager.list();
|
|
28028
|
+
if (ctx && ctx.principal.kind === "guest") {
|
|
28029
|
+
const sessions = response.sessions ?? [];
|
|
28030
|
+
const filtered = sessions.filter(
|
|
28031
|
+
(s) => canAccessPersona(ctx.grants, s.ownerPersonaId, "read")
|
|
28032
|
+
);
|
|
28033
|
+
return { response: { type: "session:list", sessions: filtered } };
|
|
28034
|
+
}
|
|
27969
28035
|
return { response: { type: "session:list", ...response } };
|
|
27970
28036
|
};
|
|
27971
|
-
const get = async (frame) => {
|
|
28037
|
+
const get = async (frame, _client, ctx) => {
|
|
27972
28038
|
const args = SessionIdArgs.parse(frame);
|
|
28039
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
27973
28040
|
const { response } = manager.get(args);
|
|
27974
28041
|
return { response: { type: "session:info", ...response } };
|
|
27975
28042
|
};
|
|
27976
|
-
const update = async (frame) => {
|
|
28043
|
+
const update = async (frame, _client, ctx) => {
|
|
27977
28044
|
const args = SessionUpdateArgs.parse(frame);
|
|
28045
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27978
28046
|
const { response, broadcast } = manager.update(args);
|
|
27979
28047
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
27980
28048
|
};
|
|
27981
|
-
const del = async (frame) => {
|
|
28049
|
+
const del = async (frame, _client, ctx) => {
|
|
27982
28050
|
const args = SessionIdArgs.parse(frame);
|
|
28051
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27983
28052
|
const { broadcast } = manager.delete(args);
|
|
27984
28053
|
return {
|
|
27985
28054
|
response: { type: "session:deleted", sessionId: args.sessionId },
|
|
27986
28055
|
broadcast
|
|
27987
28056
|
};
|
|
27988
28057
|
};
|
|
27989
|
-
const send = async (frame) => {
|
|
28058
|
+
const send = async (frame, _client, ctx) => {
|
|
27990
28059
|
const args = SessionSendArgs.parse(frame);
|
|
28060
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27991
28061
|
const { broadcast } = manager.send(args);
|
|
27992
28062
|
return { response: { type: "session:send", ok: true }, broadcast };
|
|
27993
28063
|
};
|
|
27994
|
-
const stop = async (frame) => {
|
|
28064
|
+
const stop = async (frame, _client, ctx) => {
|
|
27995
28065
|
const args = SessionIdArgs.parse(frame);
|
|
28066
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27996
28067
|
const { broadcast } = await manager.stop(args);
|
|
27997
28068
|
return { response: { type: "session:stop", ok: true }, broadcast };
|
|
27998
28069
|
};
|
|
27999
|
-
const interrupt = async (frame) => {
|
|
28070
|
+
const interrupt = async (frame, _client, ctx) => {
|
|
28000
28071
|
const args = SessionIdArgs.parse(frame);
|
|
28072
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28001
28073
|
const { broadcast } = await manager.interrupt(args);
|
|
28002
28074
|
return { response: { type: "session:interrupt", ok: true }, broadcast };
|
|
28003
28075
|
};
|
|
28004
|
-
const rewind = async (frame) => {
|
|
28076
|
+
const rewind = async (frame, _client, ctx) => {
|
|
28005
28077
|
const args = SessionRewindArgs.parse(frame);
|
|
28078
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28006
28079
|
const { response, broadcast } = await manager.rewind(args);
|
|
28007
28080
|
return { response: { type: "session:rewind", ...response }, broadcast };
|
|
28008
28081
|
};
|
|
28009
|
-
const rewindDiff = async (frame) => {
|
|
28082
|
+
const rewindDiff = async (frame, _client, ctx) => {
|
|
28010
28083
|
const args = SessionRewindDiffArgs.parse(frame);
|
|
28084
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28011
28085
|
const { response } = await manager.rewindDiff(args);
|
|
28012
28086
|
return { response: { type: "session:rewind-diff", ...response } };
|
|
28013
28087
|
};
|
|
28014
|
-
const rewindableMessageIds = async (frame) => {
|
|
28088
|
+
const rewindableMessageIds = async (frame, _client, ctx) => {
|
|
28015
28089
|
const args = SessionRewindableMessageIdsArgs.parse(frame);
|
|
28090
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28016
28091
|
const { response } = manager.rewindableMessageIds(args);
|
|
28017
28092
|
return {
|
|
28018
28093
|
response: { type: "session:rewindable-message-ids", ...response }
|
|
@@ -28023,19 +28098,22 @@ function buildSessionHandlers(deps) {
|
|
|
28023
28098
|
const result = forkSession(args);
|
|
28024
28099
|
return { response: { type: "session:fork", ...result } };
|
|
28025
28100
|
};
|
|
28026
|
-
const newSession = async (frame) => {
|
|
28101
|
+
const newSession = async (frame, _client, ctx) => {
|
|
28027
28102
|
const args = SessionIdArgs.parse(frame);
|
|
28103
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28028
28104
|
observer.stop(args.sessionId);
|
|
28029
28105
|
const { response, broadcast } = manager.newSession(args);
|
|
28030
28106
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28031
28107
|
};
|
|
28032
|
-
const resume = async (frame) => {
|
|
28108
|
+
const resume = async (frame, _client, ctx) => {
|
|
28033
28109
|
const args = SessionResumeArgs.parse(frame);
|
|
28110
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28034
28111
|
const { response, broadcast } = manager.resume(args);
|
|
28035
28112
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28036
28113
|
};
|
|
28037
|
-
const observe = async (frame) => {
|
|
28114
|
+
const observe = async (frame, _client, ctx) => {
|
|
28038
28115
|
const args = SessionObserveArgs.parse(frame);
|
|
28116
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28039
28117
|
const { response: file } = manager.get({ sessionId: args.sessionId });
|
|
28040
28118
|
const sessionFile = file;
|
|
28041
28119
|
manager.ensureSession(sessionFile);
|
|
@@ -28049,14 +28127,17 @@ function buildSessionHandlers(deps) {
|
|
|
28049
28127
|
});
|
|
28050
28128
|
return { response: { type: "session:observe", ok: true } };
|
|
28051
28129
|
};
|
|
28052
|
-
const events = async (frame) => {
|
|
28130
|
+
const events = async (frame, _client, ctx) => {
|
|
28053
28131
|
const args = SessionEventsArgs.parse(frame);
|
|
28132
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28054
28133
|
const { response } = manager.getEvents(args);
|
|
28055
28134
|
return { response: { type: "session:events", ...response } };
|
|
28056
28135
|
};
|
|
28057
|
-
const subscribe = async (frame, client) => {
|
|
28058
|
-
if (typeof frame.sessionId === "string")
|
|
28136
|
+
const subscribe = async (frame, client, ctx) => {
|
|
28137
|
+
if (typeof frame.sessionId === "string") {
|
|
28138
|
+
ensureSessionAccess(ctx, frame.sessionId, "read");
|
|
28059
28139
|
addSubscription(client, frame.sessionId);
|
|
28140
|
+
}
|
|
28060
28141
|
return {
|
|
28061
28142
|
response: { type: "subscribed", sessionId: frame.sessionId }
|
|
28062
28143
|
};
|
|
@@ -28068,8 +28149,9 @@ function buildSessionHandlers(deps) {
|
|
|
28068
28149
|
response: { type: "unsubscribed", sessionId: frame.sessionId }
|
|
28069
28150
|
};
|
|
28070
28151
|
};
|
|
28071
|
-
const pin = async (frame) => {
|
|
28152
|
+
const pin = async (frame, _client, ctx) => {
|
|
28072
28153
|
const args = SessionPinArgs.parse(frame);
|
|
28154
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28073
28155
|
const { response, broadcast } = manager.pin(args);
|
|
28074
28156
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28075
28157
|
};
|
|
@@ -28078,13 +28160,15 @@ function buildSessionHandlers(deps) {
|
|
|
28078
28160
|
const { response, broadcast } = manager.reorderPins(args);
|
|
28079
28161
|
return { response: { type: "session:reorderPins", ...response }, broadcast };
|
|
28080
28162
|
};
|
|
28081
|
-
const answerQuestion = async (frame) => {
|
|
28163
|
+
const answerQuestion = async (frame, _client, ctx) => {
|
|
28082
28164
|
const args = AnswerQuestionArgs.parse(frame);
|
|
28165
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28083
28166
|
const { response, broadcast } = manager.answerQuestion(args);
|
|
28084
28167
|
return { response: { type: "session:answerQuestion", ...response }, broadcast };
|
|
28085
28168
|
};
|
|
28086
|
-
const cancelQuestion = async (frame) => {
|
|
28169
|
+
const cancelQuestion = async (frame, _client, ctx) => {
|
|
28087
28170
|
const args = CancelQuestionArgs.parse(frame);
|
|
28171
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28088
28172
|
const { response, broadcast } = await manager.cancelQuestion(args);
|
|
28089
28173
|
return { response: { type: "session:cancelQuestion", ...response }, broadcast };
|
|
28090
28174
|
};
|
|
@@ -28872,6 +28956,7 @@ var ADMIN_ANY = {
|
|
|
28872
28956
|
resource: { type: "*" },
|
|
28873
28957
|
action: "admin"
|
|
28874
28958
|
};
|
|
28959
|
+
var CAPABILITY_SCOPED = { kind: "capability-scoped" };
|
|
28875
28960
|
var METHOD_GRANT_MAP = {
|
|
28876
28961
|
// ---- public(meta-only,guest 也能调) ----
|
|
28877
28962
|
"info": { kind: "public" },
|
|
@@ -28894,30 +28979,33 @@ var METHOD_GRANT_MAP = {
|
|
|
28894
28979
|
"remote-persona:add": ADMIN_ANY,
|
|
28895
28980
|
"remote-persona:list": ADMIN_ANY,
|
|
28896
28981
|
"remote-persona:remove": ADMIN_ANY,
|
|
28897
|
-
// ----
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
"session:
|
|
28901
|
-
"session:
|
|
28902
|
-
"session:
|
|
28903
|
-
"session:
|
|
28904
|
-
"session:
|
|
28905
|
-
"session:
|
|
28906
|
-
"session:
|
|
28907
|
-
"session:
|
|
28908
|
-
"session:
|
|
28909
|
-
"session:
|
|
28910
|
-
"session:
|
|
28911
|
-
"session:
|
|
28912
|
-
"session:
|
|
28913
|
-
"session:
|
|
28914
|
-
"session:
|
|
28915
|
-
"session:
|
|
28916
|
-
"session:
|
|
28982
|
+
// ---- session:* / chat:* 业务方法(v2 Phase 8 两层模型)----
|
|
28983
|
+
// dispatcher 不验资源,handler 内按 ctx + frame.args 反查 ownerPersonaId 调 assertGrant。
|
|
28984
|
+
// owner 自动通过(ctx 自带 '*':'admin' grant 一切 match);guest 在被授权 persona 内可调。
|
|
28985
|
+
"session:create": CAPABILITY_SCOPED,
|
|
28986
|
+
"session:list": CAPABILITY_SCOPED,
|
|
28987
|
+
"session:get": CAPABILITY_SCOPED,
|
|
28988
|
+
"session:update": CAPABILITY_SCOPED,
|
|
28989
|
+
"session:delete": CAPABILITY_SCOPED,
|
|
28990
|
+
"session:send": CAPABILITY_SCOPED,
|
|
28991
|
+
"session:stop": CAPABILITY_SCOPED,
|
|
28992
|
+
"session:interrupt": CAPABILITY_SCOPED,
|
|
28993
|
+
"session:rewind": CAPABILITY_SCOPED,
|
|
28994
|
+
"session:rewind-diff": CAPABILITY_SCOPED,
|
|
28995
|
+
"session:rewindable-message-ids": CAPABILITY_SCOPED,
|
|
28996
|
+
"session:fork": CAPABILITY_SCOPED,
|
|
28997
|
+
"session:new": CAPABILITY_SCOPED,
|
|
28998
|
+
"session:resume": CAPABILITY_SCOPED,
|
|
28999
|
+
"session:observe": CAPABILITY_SCOPED,
|
|
29000
|
+
"session:events": CAPABILITY_SCOPED,
|
|
29001
|
+
"session:subscribe": CAPABILITY_SCOPED,
|
|
29002
|
+
"session:unsubscribe": CAPABILITY_SCOPED,
|
|
29003
|
+
"session:pin": CAPABILITY_SCOPED,
|
|
28917
29004
|
"session:reorderPins": ADMIN_ANY,
|
|
28918
|
-
|
|
28919
|
-
"
|
|
28920
|
-
"session:
|
|
29005
|
+
// owner 全局操作,无 personaId 维度
|
|
29006
|
+
"permission:respond": CAPABILITY_SCOPED,
|
|
29007
|
+
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29008
|
+
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
28921
29009
|
"history:projects": ADMIN_ANY,
|
|
28922
29010
|
"history:list": ADMIN_ANY,
|
|
28923
29011
|
"history:read": ADMIN_ANY,
|
|
@@ -28953,6 +29041,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
28953
29041
|
const rule = METHOD_GRANT_MAP[method];
|
|
28954
29042
|
if (!rule) return { kind: "public" };
|
|
28955
29043
|
if (rule.kind === "public") return { kind: "public" };
|
|
29044
|
+
if (rule.kind === "capability-scoped") return { kind: "public" };
|
|
28956
29045
|
if (rule.kind === "fixed") {
|
|
28957
29046
|
return { kind: "check", resource: rule.resource, action: rule.action };
|
|
28958
29047
|
}
|
|
@@ -28961,21 +29050,6 @@ function computeGrantForFrame(method, frame) {
|
|
|
28961
29050
|
return { kind: "check", resource: picked.resource, action: picked.action };
|
|
28962
29051
|
}
|
|
28963
29052
|
|
|
28964
|
-
// src/permission/capability.ts
|
|
28965
|
-
function matchResource(grant, target) {
|
|
28966
|
-
if (grant.type === "*") return true;
|
|
28967
|
-
if (grant.type !== target.type) return false;
|
|
28968
|
-
return grant.id === target.id;
|
|
28969
|
-
}
|
|
28970
|
-
function assertGrant(grants, resource, action) {
|
|
28971
|
-
for (const g2 of grants) {
|
|
28972
|
-
if (!matchResource(g2.resource, resource)) continue;
|
|
28973
|
-
if (g2.actions.includes(action)) return true;
|
|
28974
|
-
if (g2.actions.includes("admin")) return true;
|
|
28975
|
-
}
|
|
28976
|
-
return false;
|
|
28977
|
-
}
|
|
28978
|
-
|
|
28979
29053
|
// src/index.ts
|
|
28980
29054
|
async function startDaemon(config) {
|
|
28981
29055
|
const logger = createLogger({
|
package/package.json
CHANGED