@clawos-dev/clawd 0.2.158-beta.332.393cc6b → 0.2.159-beta.333.770f863

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +136 -41
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -40408,6 +40408,28 @@ async function handleRpcRequest(input) {
40408
40408
  // src/transport/http-router.ts
40409
40409
  var RPC_ROUTE_PREFIX = "/api/rpc/";
40410
40410
  var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
40411
+ var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
40412
+ var AUTHED_RPC_CORS_HEADERS = {
40413
+ "Access-Control-Allow-Origin": "*",
40414
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
40415
+ "Access-Control-Allow-Headers": "Authorization, Content-Type",
40416
+ "Access-Control-Max-Age": "86400"
40417
+ };
40418
+ function authedRpcErrorStatus(code) {
40419
+ switch (code) {
40420
+ case "UNAUTHORIZED":
40421
+ case "FORBIDDEN":
40422
+ case "FEISHU_LOGIN_REQUIRED":
40423
+ return 403;
40424
+ case "METHOD_NOT_ALLOWED":
40425
+ case "METHOD_NOT_IMPLEMENTED":
40426
+ return 404;
40427
+ case "INTERNAL":
40428
+ return 500;
40429
+ default:
40430
+ return 400;
40431
+ }
40432
+ }
40411
40433
  function isLoopbackAddress(addr) {
40412
40434
  if (!addr) return false;
40413
40435
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1" || addr.startsWith("127.");
@@ -40502,6 +40524,52 @@ function createHttpRouter(deps) {
40502
40524
  res.end(html);
40503
40525
  return true;
40504
40526
  }
40527
+ if (url.pathname.startsWith(AUTHED_RPC_ROUTE_PREFIX)) {
40528
+ if (req.method === "OPTIONS") {
40529
+ res.writeHead(204, AUTHED_RPC_CORS_HEADERS);
40530
+ res.end();
40531
+ return true;
40532
+ }
40533
+ if (!deps.authedRpc) {
40534
+ sendJson(res, 501, { ok: false, error: "NOT_IMPLEMENTED", message: "HTTP RPC not wired" }, AUTHED_RPC_CORS_HEADERS);
40535
+ return true;
40536
+ }
40537
+ if (req.method !== "POST") {
40538
+ sendJson(res, 405, { ok: false, error: "METHOD_NOT_ALLOWED", message: "POST only" }, AUTHED_RPC_CORS_HEADERS);
40539
+ return true;
40540
+ }
40541
+ const token = parseBearer(req.headers.authorization);
40542
+ if (!token) {
40543
+ sendJson(res, 401, { ok: false, error: "NO_TOKEN", message: "missing bearer token" }, AUTHED_RPC_CORS_HEADERS);
40544
+ return true;
40545
+ }
40546
+ const auth = await deps.authedRpc.authenticate(token);
40547
+ if (!auth.ok) {
40548
+ sendJson(res, 401, { ok: false, error: auth.code, message: "invalid token" }, AUTHED_RPC_CORS_HEADERS);
40549
+ return true;
40550
+ }
40551
+ const method = url.pathname.slice(AUTHED_RPC_ROUTE_PREFIX.length).split("?")[0] ?? "";
40552
+ let body;
40553
+ try {
40554
+ body = await readJsonBody(req, RPC_BODY_MAX_BYTES);
40555
+ } catch (err) {
40556
+ sendJson(res, 400, { ok: false, error: "INVALID_BODY", message: err instanceof Error ? err.message : String(err) }, AUTHED_RPC_CORS_HEADERS);
40557
+ return true;
40558
+ }
40559
+ try {
40560
+ const result = await deps.authedRpc.dispatch(
40561
+ method,
40562
+ typeof body === "object" && body != null ? body : {},
40563
+ auth.context
40564
+ );
40565
+ sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
40566
+ } catch (err) {
40567
+ const e = err;
40568
+ const code = e?.code ?? "INTERNAL";
40569
+ sendJson(res, authedRpcErrorStatus(code), { ok: false, error: code, message: e?.message ?? String(err) }, AUTHED_RPC_CORS_HEADERS);
40570
+ }
40571
+ return true;
40572
+ }
40505
40573
  if (url.pathname.startsWith(RPC_ROUTE_PREFIX)) {
40506
40574
  if (!deps.rpcDispatcher) {
40507
40575
  sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "HTTP RPC adapter not wired" });
@@ -45838,6 +45906,9 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
45838
45906
  }
45839
45907
  };
45840
45908
 
45909
+ // src/handlers/dispatch-rpc.ts
45910
+ init_protocol();
45911
+
45841
45912
  // src/handlers/method-grants.ts
45842
45913
  var ADMIN_ANY = {
45843
45914
  kind: "fixed",
@@ -46013,6 +46084,32 @@ function computeGrantForFrame(method, frame) {
46013
46084
  return { kind: "check", resource: picked.resource, action: picked.action };
46014
46085
  }
46015
46086
 
46087
+ // src/handlers/dispatch-rpc.ts
46088
+ async function dispatchRpc(method, frame, client, ctx, deps) {
46089
+ if (!METHOD_NAMES.includes(method)) {
46090
+ throw new ClawdError(ERROR_CODES.METHOD_NOT_ALLOWED, `unknown method: ${method}`);
46091
+ }
46092
+ const handler = deps.handlers[method];
46093
+ if (!handler) throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, `not implemented: ${method}`);
46094
+ if (!deps.feishuActive() && FEISHU_GATED_METHODS.includes(method)) {
46095
+ throw new ClawdError(
46096
+ ERROR_CODES.FEISHU_LOGIN_REQUIRED,
46097
+ `${method} requires feishu login (People/remote identity)`
46098
+ );
46099
+ }
46100
+ const verdict = computeGrantForFrame(method, frame);
46101
+ if (verdict.kind === "check") {
46102
+ const ok = assertGrant(ctx.grants, verdict.resource, verdict.action);
46103
+ if (!ok) {
46104
+ throw new ClawdError(
46105
+ ERROR_CODES.UNAUTHORIZED,
46106
+ `principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${verdict.action} on ${verdict.resource.type}${"id" in verdict.resource ? ":" + verdict.resource.id : ""}`
46107
+ );
46108
+ }
46109
+ }
46110
+ return handler(frame, client, ctx);
46111
+ }
46112
+
46016
46113
  // src/extension/runtime.ts
46017
46114
  var import_node_child_process15 = require("child_process");
46018
46115
  var import_node_path45 = __toESM(require("path"), 1);
@@ -46419,6 +46516,22 @@ async function startDaemon(config) {
46419
46516
  const contactStore = new ContactStore(config.dataDir);
46420
46517
  contactStore.load();
46421
46518
  const serverKeyStore = new ServerKeyStore(config.dataDir);
46519
+ const buildConnectAuthDeps = () => ({
46520
+ isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
46521
+ ownerPrincipalId,
46522
+ ownerDisplayName,
46523
+ ...feishuActive ? {
46524
+ verifyConnectToken: (token) => {
46525
+ const publicKeyPem = serverKeyStore.read();
46526
+ if (!publicKeyPem) return { ok: false, reason: "BAD_SIGNATURE" };
46527
+ return verifyConnectToken({
46528
+ token,
46529
+ publicKeyPem,
46530
+ expectedDeviceId: authFile.deviceId
46531
+ });
46532
+ }
46533
+ } : {}
46534
+ });
46422
46535
  const capabilityStore = new CapabilityStore(config.dataDir);
46423
46536
  const capabilityRegistry = new CapabilityRegistry(capabilityStore);
46424
46537
  const capabilityManager = new CapabilityManager(capabilityRegistry, {
@@ -46447,25 +46560,7 @@ async function startDaemon(config) {
46447
46560
  // owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
46448
46561
  expectedToken: resolvedAuthToken,
46449
46562
  authenticate: async (t, selfUrl) => {
46450
- const result = await authenticate(t, {
46451
- isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
46452
- ownerPrincipalId,
46453
- ownerDisplayName,
46454
- // 飞书 gate(spec 决策 #9):未激活飞书身份时不接受 guest 入站(不注入验签函数
46455
- // → 一律 BAD_TOKEN)。已激活时用缓存的 server 公钥本地验签;
46456
- // 公钥缓存缺失(登录后拉取失败)→ fail-closed 拒绝 guest。
46457
- ...feishuActive ? {
46458
- verifyConnectToken: (token) => {
46459
- const publicKeyPem = serverKeyStore.read();
46460
- if (!publicKeyPem) return { ok: false, reason: "BAD_SIGNATURE" };
46461
- return verifyConnectToken({
46462
- token,
46463
- publicKeyPem,
46464
- expectedDeviceId: authFile.deviceId
46465
- });
46466
- }
46467
- } : {}
46468
- });
46563
+ const result = await authenticate(t, buildConnectAuthDeps());
46469
46564
  if (result.ok && result.context.principal.kind === "guest") {
46470
46565
  void autoReverseContact({
46471
46566
  store: contactStore,
@@ -47019,6 +47114,23 @@ async function startDaemon(config) {
47019
47114
  return result.response;
47020
47115
  }
47021
47116
  };
47117
+ const authedRpc = {
47118
+ authenticate: (token) => authenticate(token, buildConnectAuthDeps()),
47119
+ dispatch: (method, body, ctx) => {
47120
+ const client = {
47121
+ id: "http-rpc",
47122
+ subscribedSessions: /* @__PURE__ */ new Map(),
47123
+ send() {
47124
+ },
47125
+ close() {
47126
+ }
47127
+ };
47128
+ return dispatchRpc(method, body, client, ctx, {
47129
+ handlers,
47130
+ feishuActive: () => feishuActive
47131
+ });
47132
+ }
47133
+ };
47022
47134
  const viewerAssetLoader = tryLoadViewerAssets(logger);
47023
47135
  const httpRouter = createHttpRouter({
47024
47136
  authResolver,
@@ -47026,6 +47138,7 @@ async function startDaemon(config) {
47026
47138
  logger,
47027
47139
  sessionStore: store,
47028
47140
  rpcDispatcher,
47141
+ authedRpc,
47029
47142
  ...viewerAssetLoader ? { viewerAssetLoader } : {},
47030
47143
  // /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
47031
47144
  // --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
@@ -47149,30 +47262,12 @@ async function startDaemon(config) {
47149
47262
  const wss = wsServer;
47150
47263
  wss.onFrame(async (client, frame) => {
47151
47264
  const type = frame.type;
47152
- if (!METHOD_NAMES.includes(type)) {
47153
- throw new ClawdError(ERROR_CODES.METHOD_NOT_ALLOWED, `unknown method: ${type}`);
47154
- }
47155
47265
  const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
47156
- const handler = handlers[type];
47157
- if (!handler) throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, `not implemented: ${type}`);
47158
- if (!feishuActive && FEISHU_GATED_METHODS.includes(type)) {
47159
- throw new ClawdError(
47160
- ERROR_CODES.FEISHU_LOGIN_REQUIRED,
47161
- `${type} requires feishu login (People/remote identity)`
47162
- );
47163
- }
47164
47266
  const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
47165
- const verdict = computeGrantForFrame(type, frame);
47166
- if (verdict.kind === "check") {
47167
- const ok = assertGrant(ctx.grants, verdict.resource, verdict.action);
47168
- if (!ok) {
47169
- throw new ClawdError(
47170
- ERROR_CODES.UNAUTHORIZED,
47171
- `principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${verdict.action} on ${verdict.resource.type}${"id" in verdict.resource ? ":" + verdict.resource.id : ""}`
47172
- );
47173
- }
47174
- }
47175
- const result = await handler(frame, client, ctx);
47267
+ const result = await dispatchRpc(type, frame, client, ctx, {
47268
+ handlers,
47269
+ feishuActive: () => feishuActive
47270
+ });
47176
47271
  if (requestId && result.response) {
47177
47272
  client.send({ ...result.response, requestId });
47178
47273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.158-beta.332.393cc6b",
3
+ "version": "0.2.159-beta.333.770f863",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",