dsh-email 0.4.0 → 0.4.1

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 +1 -1
  2. package/lib/index.js +24 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -138,7 +138,7 @@ dsh plugin --profile web add dsh-email
138
138
  ```sh
139
139
  pnpm install
140
140
  pnpm run build # tsc → lib/
141
- pnpm test # 构建 + node --test(配置/解析/注册与审批门,37 个用例,无需真实邮箱)
141
+ pnpm test # 构建 + node --test(配置/解析/注册与审批门,38 个用例,无需真实邮箱)
142
142
  ```
143
143
 
144
144
  ## 协议
package/lib/index.js CHANGED
@@ -164,20 +164,6 @@ function renderFolders(value) {
164
164
  function renderAttachment(value) {
165
165
  return oneText('账号 ' + value.account + ' 已下载附件 "' + value.filename + '"(' + value.contentType + ',' + value.size + ' 字节)到:\n' + value.path + '\n可用 read 工具读取该文件。');
166
166
  }
167
- /**
168
- * Best-effort read of the session's effective approval policy. Duck-typed on
169
- * the permission-presets seam; any drift or absence yields undefined and the
170
- * gate falls back to the plain ask flow.
171
- */
172
- function effectiveApprovalPolicy(ctx, exec) {
173
- try {
174
- const preset = ctx.get?.('permissionPresets')?.current?.(exec?.agent?.session?.events);
175
- return typeof preset?.approval === 'string' ? preset.approval : undefined;
176
- }
177
- catch {
178
- return undefined;
179
- }
180
- }
181
167
  function fingerprintSettings(settings) {
182
168
  return JSON.stringify({
183
169
  accounts: [...settings.accounts.entries()].map(([name, account]) => [name, account]),
@@ -372,16 +358,34 @@ export function apply(ctx, config = {}) {
372
358
  const args = (exec.args ?? {});
373
359
  const attachCount = Array.isArray(args.attachments) ? args.attachments.length : 0;
374
360
  const reason = '发送邮件给 ' + args.to + ',主题「' + args.subject + '」' + (attachCount > 0 ? ',附件 ' + attachCount + ' 个' : '');
375
- // Full Access (approval policy 'never') rejects asks before any answerer
376
- // runs, so surface the actionable explanation instead of a misleading
377
- // "the user rejected" failure.
378
- if (effectiveApprovalPolicy(ctx, exec) === 'never') {
361
+ // Gate-owned approval: we run the approval round-trip ourselves so the
362
+ // denial reason is always honest and actionable including the Full
363
+ // Access case where the harness policy answers 'rejected' without ever
364
+ // showing a dialog.
365
+ const approval = ctx.get('approval');
366
+ if (approval === undefined) {
379
367
  return {
380
368
  kind: 'deny',
381
- reason: 'email_send 需要确认,但当前会话处于 Full Access(审批策略 never),不会弹出确认框。两条路:① 把访问模式切回 Read Only 或 Write 再发;② 在设置页关闭「发信前确认」(sendApproval: false),自行承担风险。',
369
+ reason: 'email_send 需要确认,但当前环境没有审批通道(如 headless)。如确定安全,可在配置中设置 sendApproval: false 后直接发送。',
382
370
  };
383
371
  }
384
- return { kind: 'ask', reason };
372
+ const outcome = await approval.request({
373
+ agent: exec.agent,
374
+ toolName: exec.name,
375
+ callId: exec.callId,
376
+ reason,
377
+ signal: exec.signal,
378
+ });
379
+ if (outcome === 'allowed-once')
380
+ return next();
381
+ if (outcome === 'cancelled')
382
+ return { kind: 'deny', reason: '发信确认被取消,邮件未发送。' };
383
+ if (outcome === 'unavailable')
384
+ return { kind: 'deny', reason: '发信确认不可用(没有可用的审批界面),邮件未发送。' };
385
+ return {
386
+ kind: 'deny',
387
+ reason: '发信未获批准:要么你拒绝了,要么当前会话处于 Full Access(审批策略 never,不会弹框)。若在 Full Access:切到 Read Only / Write 再发,或关闭 sendApproval(自行承担风险)。',
388
+ };
385
389
  }, { prepend: true });
386
390
  }
387
391
  export { PROVIDER_NAMES, EMAIL_PASSWORD_ENV } from './config.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-email",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "IMAP/SMTP email tools for DeepSeek Harness: list, read, search and send mail, with QQ/163/126/Sina/Aliyun/Gmail/Outlook/iCloud presets.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",