@clawos-dev/clawd 0.2.71-beta.129.3d783e6 → 0.2.71-beta.131.d934b0e
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 +141 -64
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -27956,10 +27956,62 @@ function forkSession(input) {
|
|
|
27956
27956
|
return { forkedToolSessionId, forkedFilePath };
|
|
27957
27957
|
}
|
|
27958
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
|
+
|
|
27959
27987
|
// src/handlers/session.ts
|
|
27988
|
+
init_protocol();
|
|
27960
27989
|
function buildSessionHandlers(deps) {
|
|
27961
|
-
const { manager, observer, getAdapter: getAdapter2 } = deps;
|
|
27962
|
-
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) => {
|
|
27963
28015
|
const args = SessionCreateArgs.parse(frame);
|
|
27964
28016
|
if (args.ownerPersonaId) {
|
|
27965
28017
|
const persona = deps.personaRegistry.get(args.ownerPersonaId);
|
|
@@ -27967,58 +28019,75 @@ function buildSessionHandlers(deps) {
|
|
|
27967
28019
|
throw new Error(`persona not found: ${args.ownerPersonaId}`);
|
|
27968
28020
|
}
|
|
27969
28021
|
}
|
|
28022
|
+
ensurePersonaAccess(ctx, args.ownerPersonaId, "send");
|
|
27970
28023
|
const { response, broadcast } = manager.create(args);
|
|
27971
28024
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
27972
28025
|
};
|
|
27973
|
-
const list = async () => {
|
|
28026
|
+
const list = async (_frame, _client, ctx) => {
|
|
27974
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
|
+
}
|
|
27975
28035
|
return { response: { type: "session:list", ...response } };
|
|
27976
28036
|
};
|
|
27977
|
-
const get = async (frame) => {
|
|
28037
|
+
const get = async (frame, _client, ctx) => {
|
|
27978
28038
|
const args = SessionIdArgs.parse(frame);
|
|
28039
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
27979
28040
|
const { response } = manager.get(args);
|
|
27980
28041
|
return { response: { type: "session:info", ...response } };
|
|
27981
28042
|
};
|
|
27982
|
-
const update = async (frame) => {
|
|
28043
|
+
const update = async (frame, _client, ctx) => {
|
|
27983
28044
|
const args = SessionUpdateArgs.parse(frame);
|
|
28045
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27984
28046
|
const { response, broadcast } = manager.update(args);
|
|
27985
28047
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
27986
28048
|
};
|
|
27987
|
-
const del = async (frame) => {
|
|
28049
|
+
const del = async (frame, _client, ctx) => {
|
|
27988
28050
|
const args = SessionIdArgs.parse(frame);
|
|
28051
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27989
28052
|
const { broadcast } = manager.delete(args);
|
|
27990
28053
|
return {
|
|
27991
28054
|
response: { type: "session:deleted", sessionId: args.sessionId },
|
|
27992
28055
|
broadcast
|
|
27993
28056
|
};
|
|
27994
28057
|
};
|
|
27995
|
-
const send = async (frame) => {
|
|
28058
|
+
const send = async (frame, _client, ctx) => {
|
|
27996
28059
|
const args = SessionSendArgs.parse(frame);
|
|
28060
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
27997
28061
|
const { broadcast } = manager.send(args);
|
|
27998
28062
|
return { response: { type: "session:send", ok: true }, broadcast };
|
|
27999
28063
|
};
|
|
28000
|
-
const stop = async (frame) => {
|
|
28064
|
+
const stop = async (frame, _client, ctx) => {
|
|
28001
28065
|
const args = SessionIdArgs.parse(frame);
|
|
28066
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28002
28067
|
const { broadcast } = await manager.stop(args);
|
|
28003
28068
|
return { response: { type: "session:stop", ok: true }, broadcast };
|
|
28004
28069
|
};
|
|
28005
|
-
const interrupt = async (frame) => {
|
|
28070
|
+
const interrupt = async (frame, _client, ctx) => {
|
|
28006
28071
|
const args = SessionIdArgs.parse(frame);
|
|
28072
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28007
28073
|
const { broadcast } = await manager.interrupt(args);
|
|
28008
28074
|
return { response: { type: "session:interrupt", ok: true }, broadcast };
|
|
28009
28075
|
};
|
|
28010
|
-
const rewind = async (frame) => {
|
|
28076
|
+
const rewind = async (frame, _client, ctx) => {
|
|
28011
28077
|
const args = SessionRewindArgs.parse(frame);
|
|
28078
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28012
28079
|
const { response, broadcast } = await manager.rewind(args);
|
|
28013
28080
|
return { response: { type: "session:rewind", ...response }, broadcast };
|
|
28014
28081
|
};
|
|
28015
|
-
const rewindDiff = async (frame) => {
|
|
28082
|
+
const rewindDiff = async (frame, _client, ctx) => {
|
|
28016
28083
|
const args = SessionRewindDiffArgs.parse(frame);
|
|
28084
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28017
28085
|
const { response } = await manager.rewindDiff(args);
|
|
28018
28086
|
return { response: { type: "session:rewind-diff", ...response } };
|
|
28019
28087
|
};
|
|
28020
|
-
const rewindableMessageIds = async (frame) => {
|
|
28088
|
+
const rewindableMessageIds = async (frame, _client, ctx) => {
|
|
28021
28089
|
const args = SessionRewindableMessageIdsArgs.parse(frame);
|
|
28090
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28022
28091
|
const { response } = manager.rewindableMessageIds(args);
|
|
28023
28092
|
return {
|
|
28024
28093
|
response: { type: "session:rewindable-message-ids", ...response }
|
|
@@ -28029,19 +28098,22 @@ function buildSessionHandlers(deps) {
|
|
|
28029
28098
|
const result = forkSession(args);
|
|
28030
28099
|
return { response: { type: "session:fork", ...result } };
|
|
28031
28100
|
};
|
|
28032
|
-
const newSession = async (frame) => {
|
|
28101
|
+
const newSession = async (frame, _client, ctx) => {
|
|
28033
28102
|
const args = SessionIdArgs.parse(frame);
|
|
28103
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28034
28104
|
observer.stop(args.sessionId);
|
|
28035
28105
|
const { response, broadcast } = manager.newSession(args);
|
|
28036
28106
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28037
28107
|
};
|
|
28038
|
-
const resume = async (frame) => {
|
|
28108
|
+
const resume = async (frame, _client, ctx) => {
|
|
28039
28109
|
const args = SessionResumeArgs.parse(frame);
|
|
28110
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28040
28111
|
const { response, broadcast } = manager.resume(args);
|
|
28041
28112
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28042
28113
|
};
|
|
28043
|
-
const observe = async (frame) => {
|
|
28114
|
+
const observe = async (frame, _client, ctx) => {
|
|
28044
28115
|
const args = SessionObserveArgs.parse(frame);
|
|
28116
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28045
28117
|
const { response: file } = manager.get({ sessionId: args.sessionId });
|
|
28046
28118
|
const sessionFile = file;
|
|
28047
28119
|
manager.ensureSession(sessionFile);
|
|
@@ -28055,14 +28127,17 @@ function buildSessionHandlers(deps) {
|
|
|
28055
28127
|
});
|
|
28056
28128
|
return { response: { type: "session:observe", ok: true } };
|
|
28057
28129
|
};
|
|
28058
|
-
const events = async (frame) => {
|
|
28130
|
+
const events = async (frame, _client, ctx) => {
|
|
28059
28131
|
const args = SessionEventsArgs.parse(frame);
|
|
28132
|
+
ensureSessionAccess(ctx, args.sessionId, "read");
|
|
28060
28133
|
const { response } = manager.getEvents(args);
|
|
28061
28134
|
return { response: { type: "session:events", ...response } };
|
|
28062
28135
|
};
|
|
28063
|
-
const subscribe = async (frame, client) => {
|
|
28064
|
-
if (typeof frame.sessionId === "string")
|
|
28136
|
+
const subscribe = async (frame, client, ctx) => {
|
|
28137
|
+
if (typeof frame.sessionId === "string") {
|
|
28138
|
+
ensureSessionAccess(ctx, frame.sessionId, "read");
|
|
28065
28139
|
addSubscription(client, frame.sessionId);
|
|
28140
|
+
}
|
|
28066
28141
|
return {
|
|
28067
28142
|
response: { type: "subscribed", sessionId: frame.sessionId }
|
|
28068
28143
|
};
|
|
@@ -28074,8 +28149,9 @@ function buildSessionHandlers(deps) {
|
|
|
28074
28149
|
response: { type: "unsubscribed", sessionId: frame.sessionId }
|
|
28075
28150
|
};
|
|
28076
28151
|
};
|
|
28077
|
-
const pin = async (frame) => {
|
|
28152
|
+
const pin = async (frame, _client, ctx) => {
|
|
28078
28153
|
const args = SessionPinArgs.parse(frame);
|
|
28154
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28079
28155
|
const { response, broadcast } = manager.pin(args);
|
|
28080
28156
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
28081
28157
|
};
|
|
@@ -28084,13 +28160,15 @@ function buildSessionHandlers(deps) {
|
|
|
28084
28160
|
const { response, broadcast } = manager.reorderPins(args);
|
|
28085
28161
|
return { response: { type: "session:reorderPins", ...response }, broadcast };
|
|
28086
28162
|
};
|
|
28087
|
-
const answerQuestion = async (frame) => {
|
|
28163
|
+
const answerQuestion = async (frame, _client, ctx) => {
|
|
28088
28164
|
const args = AnswerQuestionArgs.parse(frame);
|
|
28165
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28089
28166
|
const { response, broadcast } = manager.answerQuestion(args);
|
|
28090
28167
|
return { response: { type: "session:answerQuestion", ...response }, broadcast };
|
|
28091
28168
|
};
|
|
28092
|
-
const cancelQuestion = async (frame) => {
|
|
28169
|
+
const cancelQuestion = async (frame, _client, ctx) => {
|
|
28093
28170
|
const args = CancelQuestionArgs.parse(frame);
|
|
28171
|
+
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
28094
28172
|
const { response, broadcast } = await manager.cancelQuestion(args);
|
|
28095
28173
|
return { response: { type: "session:cancelQuestion", ...response }, broadcast };
|
|
28096
28174
|
};
|
|
@@ -28586,11 +28664,20 @@ function buildWhoamiHandler(deps) {
|
|
|
28586
28664
|
capability = stripSecretHash(cap);
|
|
28587
28665
|
}
|
|
28588
28666
|
const grantedPersonas = [];
|
|
28589
|
-
|
|
28590
|
-
|
|
28591
|
-
const
|
|
28592
|
-
|
|
28593
|
-
|
|
28667
|
+
const hasWildcard = capability.grants.some((g2) => g2.resource.type === "*");
|
|
28668
|
+
if (hasWildcard) {
|
|
28669
|
+
for (const id of deps.personaStore.list()) {
|
|
28670
|
+
const file = deps.personaStore.read(id);
|
|
28671
|
+
if (!file) continue;
|
|
28672
|
+
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
28673
|
+
}
|
|
28674
|
+
} else {
|
|
28675
|
+
for (const g2 of capability.grants) {
|
|
28676
|
+
if (g2.resource.type !== "persona") continue;
|
|
28677
|
+
const file = deps.personaStore.read(g2.resource.id);
|
|
28678
|
+
if (!file) continue;
|
|
28679
|
+
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
28680
|
+
}
|
|
28594
28681
|
}
|
|
28595
28682
|
return {
|
|
28596
28683
|
response: {
|
|
@@ -28878,6 +28965,7 @@ var ADMIN_ANY = {
|
|
|
28878
28965
|
resource: { type: "*" },
|
|
28879
28966
|
action: "admin"
|
|
28880
28967
|
};
|
|
28968
|
+
var CAPABILITY_SCOPED = { kind: "capability-scoped" };
|
|
28881
28969
|
var METHOD_GRANT_MAP = {
|
|
28882
28970
|
// ---- public(meta-only,guest 也能调) ----
|
|
28883
28971
|
"info": { kind: "public" },
|
|
@@ -28900,30 +28988,33 @@ var METHOD_GRANT_MAP = {
|
|
|
28900
28988
|
"remote-persona:add": ADMIN_ANY,
|
|
28901
28989
|
"remote-persona:list": ADMIN_ANY,
|
|
28902
28990
|
"remote-persona:remove": ADMIN_ANY,
|
|
28903
|
-
// ----
|
|
28904
|
-
|
|
28905
|
-
|
|
28906
|
-
"session:
|
|
28907
|
-
"session:
|
|
28908
|
-
"session:
|
|
28909
|
-
"session:
|
|
28910
|
-
"session:
|
|
28911
|
-
"session:
|
|
28912
|
-
"session:
|
|
28913
|
-
"session:
|
|
28914
|
-
"session:
|
|
28915
|
-
"session:
|
|
28916
|
-
"session:
|
|
28917
|
-
"session:
|
|
28918
|
-
"session:
|
|
28919
|
-
"session:
|
|
28920
|
-
"session:
|
|
28921
|
-
"session:
|
|
28922
|
-
"session:
|
|
28991
|
+
// ---- session:* / chat:* 业务方法(v2 Phase 8 两层模型)----
|
|
28992
|
+
// dispatcher 不验资源,handler 内按 ctx + frame.args 反查 ownerPersonaId 调 assertGrant。
|
|
28993
|
+
// owner 自动通过(ctx 自带 '*':'admin' grant 一切 match);guest 在被授权 persona 内可调。
|
|
28994
|
+
"session:create": CAPABILITY_SCOPED,
|
|
28995
|
+
"session:list": CAPABILITY_SCOPED,
|
|
28996
|
+
"session:get": CAPABILITY_SCOPED,
|
|
28997
|
+
"session:update": CAPABILITY_SCOPED,
|
|
28998
|
+
"session:delete": CAPABILITY_SCOPED,
|
|
28999
|
+
"session:send": CAPABILITY_SCOPED,
|
|
29000
|
+
"session:stop": CAPABILITY_SCOPED,
|
|
29001
|
+
"session:interrupt": CAPABILITY_SCOPED,
|
|
29002
|
+
"session:rewind": CAPABILITY_SCOPED,
|
|
29003
|
+
"session:rewind-diff": CAPABILITY_SCOPED,
|
|
29004
|
+
"session:rewindable-message-ids": CAPABILITY_SCOPED,
|
|
29005
|
+
"session:fork": CAPABILITY_SCOPED,
|
|
29006
|
+
"session:new": CAPABILITY_SCOPED,
|
|
29007
|
+
"session:resume": CAPABILITY_SCOPED,
|
|
29008
|
+
"session:observe": CAPABILITY_SCOPED,
|
|
29009
|
+
"session:events": CAPABILITY_SCOPED,
|
|
29010
|
+
"session:subscribe": CAPABILITY_SCOPED,
|
|
29011
|
+
"session:unsubscribe": CAPABILITY_SCOPED,
|
|
29012
|
+
"session:pin": CAPABILITY_SCOPED,
|
|
28923
29013
|
"session:reorderPins": ADMIN_ANY,
|
|
28924
|
-
|
|
28925
|
-
"
|
|
28926
|
-
"session:
|
|
29014
|
+
// owner 全局操作,无 personaId 维度
|
|
29015
|
+
"permission:respond": CAPABILITY_SCOPED,
|
|
29016
|
+
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29017
|
+
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
28927
29018
|
"history:projects": ADMIN_ANY,
|
|
28928
29019
|
"history:list": ADMIN_ANY,
|
|
28929
29020
|
"history:read": ADMIN_ANY,
|
|
@@ -28959,6 +29050,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
28959
29050
|
const rule = METHOD_GRANT_MAP[method];
|
|
28960
29051
|
if (!rule) return { kind: "public" };
|
|
28961
29052
|
if (rule.kind === "public") return { kind: "public" };
|
|
29053
|
+
if (rule.kind === "capability-scoped") return { kind: "public" };
|
|
28962
29054
|
if (rule.kind === "fixed") {
|
|
28963
29055
|
return { kind: "check", resource: rule.resource, action: rule.action };
|
|
28964
29056
|
}
|
|
@@ -28967,21 +29059,6 @@ function computeGrantForFrame(method, frame) {
|
|
|
28967
29059
|
return { kind: "check", resource: picked.resource, action: picked.action };
|
|
28968
29060
|
}
|
|
28969
29061
|
|
|
28970
|
-
// src/permission/capability.ts
|
|
28971
|
-
function matchResource(grant, target) {
|
|
28972
|
-
if (grant.type === "*") return true;
|
|
28973
|
-
if (grant.type !== target.type) return false;
|
|
28974
|
-
return grant.id === target.id;
|
|
28975
|
-
}
|
|
28976
|
-
function assertGrant(grants, resource, action) {
|
|
28977
|
-
for (const g2 of grants) {
|
|
28978
|
-
if (!matchResource(g2.resource, resource)) continue;
|
|
28979
|
-
if (g2.actions.includes(action)) return true;
|
|
28980
|
-
if (g2.actions.includes("admin")) return true;
|
|
28981
|
-
}
|
|
28982
|
-
return false;
|
|
28983
|
-
}
|
|
28984
|
-
|
|
28985
29062
|
// src/index.ts
|
|
28986
29063
|
async function startDaemon(config) {
|
|
28987
29064
|
const logger = createLogger({
|
package/package.json
CHANGED