dsh-todo-time-log 0.5.2 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +10 -2
  2. package/lib/index.js +42 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  | 中文显示名 | 待办时间日志 |
10
10
  | 短别名 | `todo-time-log`、`ttl`(文档与工具前缀用;dsh 的 Entry / Slot / Tool 标识统一用包名) |
11
11
  | 数据文件 | `<DSH_PROFILE_DIR 或 ~/.dsh>/todo-time-log/todos.json`(可用环境变量或行配置覆盖) |
12
- | HTTP 接口 | `POST /api/dsh-todo-time-log/*` |
12
+ | HTTP 接口 | `POST /api/dsh-todo-time-log/*`(**经宿主认证**:每条请求先过 `connection.requestRejection()`,无登录 cookie 返回 401、跨站/不可信来源返回 403) |
13
13
  | 智能体工具 | `todo_time_log`(action = list / get / create / complete / uncomplete / update / delete / health) |
14
14
  | UI 位置 | 中央面板 `main`(key = `dsh-todo-time-log`)+ 侧边栏图标 `sidebar.panellist`(id 同名,点击打开面板) |
15
15
  | 视图 | 四视图:**表格 / 看板 / 甘特图 / 日历**(甘特条 = 创建 → 完成,未完成延伸到「现在」;四视图内均可完成/编辑/删除) |
@@ -188,7 +188,15 @@ export function apply(ctx, config = {}) {
188
188
 
189
189
  mountOn(ctx, ['webServer'], (scope) => { // 路由:供 UI 调用
190
190
  scope.effect(() => scope.webServer.register({
191
- kind: 'prefix', path: ROUTE_PREFIX, handler: createHttpHandler(store),
191
+ kind: 'prefix', path: ROUTE_PREFIX,
192
+ handler: createHttpHandler(store, {
193
+ // 宿主认证:connection 缺失或抛错一律拒绝,不退回到 Host/Origin 自检
194
+ authorize: (req) => {
195
+ const connection = ctx.get('connection');
196
+ if (!connection) return 403;
197
+ return connection.requestRejection(req) ?? undefined;
198
+ },
199
+ }),
192
200
  }));
193
201
  });
194
202
 
package/lib/index.js CHANGED
@@ -219,6 +219,9 @@ import { createFileIndex } from './files.js';
219
219
  * @property {() => string} [getWorkspaceRoot]
220
220
  * @property {string} [workspaceRoot]
221
221
  * @property {() => Array<{ path: string, title: string, source: string }>} [listWorkspaces]
222
+ * @property {(req: import('node:http').IncomingMessage) => number | undefined | null} [authorize]
223
+ * Host admission check; `apply()` wires `connection.requestRejection()`. When
224
+ * omitted (direct handler tests) every request is admitted.
222
225
  */
223
226
 
224
227
  export const name = 'dsh-todo-time-log';
@@ -1113,6 +1116,15 @@ export function createHttpHandler(store, options = {}) {
1113
1116
  const listWorkspaces = typeof options.listWorkspaces === 'function'
1114
1117
  ? options.listWorkspaces
1115
1118
  : () => [{ path: getWorkspaceRoot(), title: '', source: 'default' }];
1119
+ /**
1120
+ * Host admission check, supplied by `apply()` from the composition's
1121
+ * `connection` service. It returns the HTTP status that rejects the request
1122
+ * (`401`/`403`) or a falsy value when the caller is an authenticated Peer.
1123
+ * Omitting it (tests that drive the handler directly) admits every request.
1124
+ *
1125
+ * @type {((req: import('node:http').IncomingMessage) => number | undefined | null) | null}
1126
+ */
1127
+ const authorize = typeof options.authorize === 'function' ? options.authorize : null;
1116
1128
  const resolveRequestRoot = (requested) => {
1117
1129
  const candidates = listWorkspaces();
1118
1130
  if (typeof requested !== 'string' || requested.trim() === '') {
@@ -1132,6 +1144,27 @@ export function createHttpHandler(store, options = {}) {
1132
1144
  return { root: match.path, candidates, error: null };
1133
1145
  };
1134
1146
  return async function handler(req, res) {
1147
+ // Authenticate before touching any data: the routes below read and write
1148
+ // the todo file and enumerate the workspace and live sessions, so they must
1149
+ // stay behind the host's Host/Origin fence and login-token cookie.
1150
+ if (authorize) {
1151
+ let rejection;
1152
+ try {
1153
+ rejection = authorize(req);
1154
+ } catch {
1155
+ rejection = 403;
1156
+ }
1157
+ if (rejection) {
1158
+ sendJson(res, rejection === 401 ? 401 : 403, {
1159
+ ok: false,
1160
+ error: {
1161
+ code: rejection === 401 ? 'UNAUTHORIZED' : 'FORBIDDEN',
1162
+ message: '请求未经 DSH 宿主认证(connection.requestRejection)',
1163
+ },
1164
+ });
1165
+ return;
1166
+ }
1167
+ }
1135
1168
  let url;
1136
1169
  try {
1137
1170
  url = new URL(req.url ?? '/', 'http://127.0.0.1');
@@ -1394,6 +1427,15 @@ export function apply(ctx, config = {}) {
1394
1427
  fileIndex,
1395
1428
  getWorkspaceRoot,
1396
1429
  listWorkspaces: () => listWorkspaceCandidates(ctx, config),
1430
+ // Host admission: the composition's `connection` service owns the
1431
+ // Host/Origin fence and browser authentication. A missing or failing
1432
+ // service rejects the request instead of falling back to a weaker check.
1433
+ authorize: (req) => {
1434
+ const connection = ctx.get('connection');
1435
+ if (!connection || typeof connection.requestRejection !== 'function') return 403;
1436
+ const rejection = connection.requestRejection(req);
1437
+ return rejection === undefined || rejection === null ? undefined : rejection;
1438
+ },
1397
1439
  }),
1398
1440
  }));
1399
1441
  logger.info(`routes mounted at ${ROUTE_PREFIX}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-todo-time-log",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "查看待办事项,并记录与展示每个待办的创建时间与完成时间(Todo time log for DeepSeek Harness)",
5
5
  "keywords": [
6
6
  "dsh",