Blizzard
|
1beef44625
|
ci: 加 -race + golangci-lint(new-issues) + govulncheck/gitleaks 安全扫描
补齐审计指出的「CI 缺 lint/安全扫描/-race」低成本质量门:
- go job:go test → go test -race(4 模块本地已验证 race-clean,ubuntu 自带 gcc)。
- lint job:golangci-lint(standard 集)仅 PR 跑 + only-new-issues —— 新代码必须干净,
存量 ~42 处(多为未检 Close/死代码)单独消化,不拿存量红门。加 .golangci.yml(测试放过 errcheck)。
- security job:govulncheck advisory(stdlib/nats CVE 靠 toolchain/依赖升级,只做可见性不阻断)
+ gitleaks 扫提交防密钥泄漏。
- 顺手清掉本会话新代码的 lint:blob GetBytes 的 defer Close、退款测试未检 CreateOrder。
注:gitleaks-action 个人/公开仓库免费;组织仓库需 GITLEAKS_LICENSE。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:44:38 +08:00 |
|
Blizzard
|
5e5f6e9610
|
feat(billing): 人工退款入口 —— paid 单冲销积分+置 refunded(幂等)
PAYMENT_DESIGN §5 承诺却只留了 OrderRefunded 常量、无入口。补齐全链路:
- store.RefundOrder:订单 CAS(paid→refunded) 为主闸(重复退 changed=false 幂等),
同事务记 adjust 负分录(ref=订单号)+ 回退物化余额。照抄 MarkOrderPaid 双闸范式;
新增 idx_ledger_refund_ref 部分唯一索引(kind='adjust' AND ref<>'')做账本级兜底,
与 grant 索引对称、不与 admin 手工校正(ref 空)冲突。
- 积分若已消费,回退后余额可为负(人工退款预期,账本仍自洽,后续消费被硬拦截)。
- handler AdminRefundOrder + POST /admin/orders/:id/refund(admin 组已挂 Audit 留痕);
真渠道钱款原路退回需 admin 另在商户后台操作,本地仅冲销积分与订单态(不接自动退款 API)。
- admin 订单流加「退款」按钮(仅 paid 单可见,二次确认+填原因)。
- 测试:RefundOrder 冲销+幂等、只退 paid 两个不变量测试(sqlite 真 DB,余额=账本之和)。
gateway build/vet/test 全绿,admin tsc 干净。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:35:31 +08:00 |
|
Blizzard
|
ec9431bfa4
|
fix(report): 报告源/产物落 MinIO,去掉 gateway↔mcp-go 本地盘耦合
多机/容器化第一时间断裂的隐患(ARCHITECTURE_REVIEW §7 #1):此前报告
源(reportStore)与 .docx 产物(reportExport/reportRender)都写 mcp-go
本地 SUNDYNIX_REPORTS_DIR,gateway 再按同一路径 c.File 回流——mcp-go
与 gateway 不共享磁盘即断。
- blob 包从 gateway/internal 提到 sundynix-shared/blob,gateway 与 mcp-go
共用同一 MinIO;新增 PutBytes/GetBytes 走二进制(.docx)。
- mcp-go 注入 blob:report 源/产物优先 Put 到对象存储(键 reports/<id>.{json,docx}),
导出结果返回 minio://<key>;MinIO 未就绪回退本地盘(单机降级,getSource 兼容旧本地报告)。
- gateway ExportReport 识别 minio:// → GetBytes 流式下载,否则 c.File 本地降级。
- 测试:mcp 本地回落往返单测 + shared/blob 真 MinIO 二进制往返(BLOB_TEST_ENDPOINT 门控)。
四模块 build 绿,受影响测试全过。
- 附带:go mod tidy 引入 genproto 拆包冲突,四模块统一钉单体 genproto 新版解决
(注:勿 go work sync,会剪掉 indirect require 回落旧版重现冲突)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:29:31 +08:00 |
|
Blizzard
|
1c7e8e1ea9
|
docs: 修 DEPTH_ROADMAP/EINO 两处过期标记
- 删 T4.E 熔断器接回 failover 重复的过期 [ ] 项(222 行 ✅ 为准);
T2.1 熔断器联动注明已整合。
- EINO Phase D FSM 回写主题名笔误 sundynix.tasks.status → sundynix.status.task
(文档 141 行自己记过这次迁移)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:29:01 +08:00 |
|
Blizzard
|
7ae7f7be67
|
feat(memory): 记忆召回加 Relevance —— Generative Agents 打分补齐第三项 (P1)
审计三真桩之一。记忆召回此前打分只有 Recency+Importance,缺 Relevance(对当前
任务的语义相关性)——注释写"待接 Milvus",但召回时甚至不知道当前问什么。
关键发现:dispatcher 注入点 fetchMemory(ctx,uid,_) 手上已有当前任务文本(b.query),
只是被 `_` 丢弃了。所以不是"接 Milvus"那么重,把 query 一路传下去 + 缓存嵌入即可。
设计(偏离注释的"接 Milvus"——用户偏好量小,不值当上向量库):
- Profile 加 embedding 列(float32 小端打包存 bytea);Upsert 时对 value 向量化缓存
(value 没变不重算,失败留空不阻断)。
- memory 包定义 Embedder 小接口,gateway 注入 rag.Engine(复用同一控制面下发的
embedding 模型),不硬依赖 rag 内部;rag.Engine 加导出 Embed 方法。
- memory_get 工具加可选 query 入参;fetchMemory 停止丢弃 b.query 传下去。
- Get(ctx,uid,query):query 非空且 embedder 就绪 → embed(query) 对每条缓存向量
内存算余弦 → 三项打分 0.25R+0.35I+0.4Rel;否则回落两项(升级前行为)。
- 优雅降级贯穿:无 query/无 embedder/query 嵌入失败/行无向量 → 静默回落,绝不报错。
零 Milvus 依赖、零向量库同步问题、保住"没 embedding 也能跑"。
验证:单测(编解码往返/cosine 截0/三项模式相关性翻转顺序/降级返 nil)+ 端到端
(真 PG:写入即向量化、query=咖啡把低重要度的咖啡记忆翻到运动前面)。migration
加列已 live;embedding 复用 RAG 已验证基建。三模块 build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:47:55 +08:00 |
|
Blizzard
|
a742d118ad
|
test(web): 薄 Web 面加 RTL 组件测试 —— 补最大覆盖缺口 (P1)
完成度审计:web 端几乎零覆盖(仅3个 api 纯逻辑用例、无任何组件测试)——三前端里
最薄。补上 RTL 基建 + 入口流组件测试。
- 加 @testing-library/react+jest-dom+user-event,src/test/setup.ts,
vitest setupFiles 接入。
- AuthPage.test.tsx(5 例,mock api 层):登录/注册态切换(名字字段显隐)、
邮箱密码空时按钮禁用、登录成功回调 onAuthed 且带对参数、登录失败显示后端
错误文案不回调、注册态走 authRegister 带名字。
web 测试 3→8。CI web 矩阵已跑 npm test,自动纳入关卡。
tsc + 三前端(desktop 68/admin 41/web 8)全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:28:57 +08:00 |
|
Blizzard
|
e29bc9a91e
|
feat(admin): 「数据源 & RAG」页做实 —— admin 三 mock 页清零 (P1)
审计 P1「admin 三页纯 mock」最后一页。此前 DatasourcesPage 的 GraphRAG 拓扑图
写死节点、「向量/全文/图谱权重滑块」纯 mock——而且权重概念本身虚构:mcp-go 的
RRF 融合是各路等权的倒排互惠融合(rrfK=60 平滑常数),根本没有"每路占几成"。
- store/datasource_query.go:AllDatasources(全平台 KB + 各库文档数/总字数,
按 (space_id,name) 关联 doc,WithoutTenant);GET /admin/datasources(含
SystemCounts 平台计数)。
- DatasourcesPage 重写:保留真实 Embedding 模型配置(ModelManager) + 诚实的
混合检索管线说明(三路 Milvus/Bleve/Neo4j + RRF 等权融合 k=60,非可调权重) +
平台计数卡片 + 真知识库清单表。删假滑块+假拓扑(~200行 mock)。
诚实边界:RRF 无每路权重,不摆假滑块;检索参数在 mcp-go 代码中。
live:/admin/datasources 返 34用户/25库/53文档,清单带真实文档数字数;
浏览器渲染全对。tsc+41 vitest 全绿。**admin 三 mock 页(Evals/Guardrails/
Datasources)全部做实。**
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:26:16 +08:00 |
|
Blizzard
|
c02ebc7bce
|
feat(admin): 「安全护栏」页做实 —— 接真护栏事件,去 mock (P1)
审计 P1「admin 三页纯 mock」之二。此前 GuardrailsPage 是写死的正则/敏感词
编辑框(改了不生效的假配置)+编造拦截日志。后端 /admin/guardrail-events 早已
现成(T4.B),纯前端做实。
- 命中事件流接真数据(guardrail_event,middleware.Guardrail 命中即落库):
blocked 硬拦/suspect 灰区放行,带原因/信号/路径/来源;计数卡片+原因 Top 分布
(客户端从近100条聚合)+按 kind 筛。
- 诚实处理假配置:护栏规则(Tier1 正则/词库 + Tier2 LLM 分类器)是中间件代码
常量、非运行时可配,故删掉"能改却不生效"的编辑框,换成只读规则说明 + 指出
运行时可配需另建配置存储(参考提示词控制面)。不摆假控件。
live:触发注入越狱输入→422 硬拦+落库(reason「疑似提示词注入」)→页面渲染
真事件流+原因分布。tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:07:32 +08:00 |
|
Blizzard
|
d04d830c37
|
feat(admin): 「自动评测」页做实 —— 接真评测数据,去 mock (P1)
审计 P1「admin 三页纯 mock」之一。此前 EvalsPage 是写死的质量趋势+编造的
错题本+虚构纠偏轨迹。现接 sundynix_eval 真数据(评测经 JetStream eval 流持久
落库,刚升级)。
- store/eval_query.go:EvalTrend(按天 avg 综合分/忠实度+低分计数)、EvalSummaryFor
(ok/warn/poor/corrected 计数+均值)、PoorEvals(错题本,level in poor/warn +
评语+纠偏标记+租户名)。全 WithoutTenant 平台口径;忠实度均值只算 sources>0
(无来源的忠实度恒0会压低失真)。
- GET /admin/evals?days=(RequireAdmin);admin api.ts + EvalsPage 重写:
总览卡片(综合分/合格率/低分占比/纠偏采纳率)+质量&忠实度趋势(纯SVG折线+低分
背景条)+错题本(点行展开评语)。
- 诚实边界:纠偏「前后全文对照」后端未持久化,只存了 Reason/Corrected/各维度分,
故错题本展示评语+「已纠偏」标记,不再编造 before/after。
live:/admin/evals 返 57 次评测 avg=0.88、错题本10条、11天趋势;浏览器渲染
真数据(趋势线07-15真实下探)。go+tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:03:54 +08:00 |
|
Blizzard
|
a1c35852ef
|
chore: 删死代码 —— OpenReport/ReadLocalFile/isDesktop/openReport + admin Soon (P2)
完成度审计确认全链零生产调用:
- app.go OpenReport(仅被死包装引用)、ReadLocalFile(仅测试引用);
desktop.ts openReport 包装、isDesktop 导出(实际用 isMacDesktop)——全删。
绑定重新生成(OpenReport/ReadLocalFile 归零,PrintReportPage/SaveReportAs/
Notify 保留)。删对应的 app_test TestReadLocalFile。
- admin Soon.tsx(规划中占位组件)已成未引用死组件——routes.tsx 9 条路由全
ready:true,import 未用。删组件+import。
openInSystem/filepath/os 仍被 PrintReportPage/download 使用,不孤立。
desktop go test + tsc + 68 vitest、admin tsc + 41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:56:51 +08:00 |
|
Blizzard
|
940330cdb7
|
feat(eval): 评测结果回写升 JetStream 持久 —— 消灭最后一处 core NATS 回写 (P1)
完成度审计 P1 + 记忆 nats-durability 的既定规矩「计费/需落库的回写一律
JetStream+幂等,别fire-and-forget」。此前 eval 是全仓最后一处 core NATS pub-sub
回写:网关离线/慢消费者时评测结果直接丢——而质量趋势/门控都依赖它。
照抄已升级的 status/usage 范式(同为 dispatcher→gateway→PG 回写):
- contract 加 StreamEval/ConsumerEval;bus 加 EnsureEvalStream + ConsumeEval
(durable consumer,AckExplicit,落库失败 Nak 重投自愈),PublishEval 改
js.Publish 同步等 ack。删 core NATS 的 SubscribeEval。
- gateway/dispatcher 两个 wrapper 在 connect 时 EnsureEvalStream;gateway main
的评测订阅从 SubscribeEval(fire-and-forget)换 ConsumeEval(handler 返 error→
Nak),接入优雅停机 drain。
- 幂等前提已满足:SaveEval 按 task_id upsert,at-least-once 重投只覆盖不重复。
验证:e2e 去重测试(TestGatewayQueueDedup)升级到 ConsumeEval,50 条两副本合计
处理50次零重复;live 真端到端:提交任务→跑完→评测经新 eval 流落 PG(level=ok)
一次成功;两服务启动 eval 流 ensure 无报错。go build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:52:40 +08:00 |
|
Blizzard
|
3d15cdb493
|
fix(gateway): 任务落库失败不再吞 —— 关键写失败上浮 5xx (P0-2)
DEPTH_ROADMAP:225 未完项 + 完成度审计 P0-2:launch() 里 SaveTask 是 best-effort,
DB 写失败只 log 却照样 PublishTask + 返 202。结果任务发出去在后端跑了,却不进
运行历史、复盘不了、报告类的用户切页面回来彻底找不回——用户以为成功、实际没落库。
- SaveTask 对「DB 降级(nil)」返 nil、对「DB 活着写失败」返真 error,天然可区分:
前者静默跳过(开发态本就无库),后者上浮为提交失败。落库在 Publish 之前,
失败时还没发布,中止干净、不产生"看不见的执行"。
- 两个调用点(SubmitTask/GenerateReport)已把 launch 错误映射 5xx,无需再改。
范围克制:审计列的其它 best-effort 点保持不动——dispatcher 的异步回写
(UpdateTaskStatus/SaveTaskOutput/SaveTaskTrace)、审计日志、用量累计计数,
都不是"用户等着响应"的路径,没有请求可返 5xx,best-effort 是对的。
唯独 launch 这处是"用户以为提交成功实际没有",才该阻断。
live 验证:改名 task 表模拟 DB 写失败 → 提交返 502+明确文案(不再假 202);
表改回 → 立刻恢复 202。happy path 202+落库不变。go build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:45:08 +08:00 |
|
Blizzard
|
bff6e5c7fd
|
test(gateway): 补上「钱路径」与租户隔离的回归测试 —— P0 最高优先级缺口
完成度审计(见记忆 completion-audit)最尖的一条:涉及钱的路径此前 0 测试,
支付一上线 bug=真实错账;租户数据层隔离也只测了角色门禁没测数据层真隔离。
把本会话 live 手验过的断言固化成回归测试。
测试基建:纯 Go sqlite(glebarez,无 CGO)内存库,迁同款模型+建部分唯一索引+
挂租户作用域回调,复用生产 store 方法测真逻辑。CI ubuntu 无 Postgres 也能跑
(此前 store 测试全是纯逻辑,DB 事务逻辑从没进过关卡)。
钱路径不变量(6):
- GrantCredits 记分录+增余额,余额恒等于 SUM(ledger)
- 兑换码核销一次性(CAS)+原子入账,重复核销余额纹丝不动
- 账本(kind,ref)部分唯一索引:重复 grant 被兜底拦下、usage 不受约束
- MarkOrderPaid CAS 幂等:重复回调 changed=false 不重复入账
- SaveUsageEvent task_id 幂等:同任务重投不重复扣费
- ReconcileOrders 抓出 order_without_ledger(钱到了积分没给)
租户隔离(3):创建自动填 tenant_id、查询按 ctx 租户过滤、跨租户改/删命不中、
WithoutTenant 系统视角全可见。
go build/vet + 全 gateway 测试全绿;sqlite 仅测试引用,不进生产二进制。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:39:36 +08:00 |
|
Blizzard
|
3db2de1ef6
|
feat(billing): 支付 P5.3 —— 掉单补偿定时器 + admin 订单流 + 日终对账
支付线封口。此前 pending 单只在「用户开着账单页轮询」时才查单确认——用户扫完码
关页面,钱付了、积分永不到账。
- 掉单补偿定时器(payment_reconcile.go):gateway 内每分钟扫 pending 微信单,
逐单 reconcileOrder 主动查单落态。把「用户在不在场」从入账链路摘掉。
reconcileOrder 从 BillingOrderStatus 抽出、前端轮询与定时器共用一份幂等
落态逻辑(不重蹈 GenerateReport/SubmitTask 的漂移)。渠道未配置时空转不炸。
- admin 订单流 GET /admin/orders(状态计数+全平台订单,可筛)。
- 日终对账 GET /admin/orders/reconcile:paid 单 ↔ 账本 grant 分录逐单比对,
抓 order_without_ledger(钱到了积分没给,最严重)/ ledger_without_paid_order。
- admin 计费页「充值订单与对账」块:计数卡片+订单流+一键对账。
⚠️ live 抓到并修掉一个真 bug:OrderStats 复用同一个 gorm.DB 链式 Count 三次,
WHERE 累加成 status=A AND status=B → 恒 0(订单流显示 2 单但计数全 0)。
改成每次起新 query builder。—— 又一次只有 live 才暴露的。
验证:go 6 包测试+tsc+41 vitest 全绿;live 造差异单对账正确抓出
order_without_ledger、清账后回零差异;补偿器启动日志+渠道未配置空转不炸;
浏览器验订单流卡片+一键对账绿条。TTL 过期路径需真渠道触发,部署后自然覆盖。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:25:37 +08:00 |
|
Blizzard
|
3767c78ee8
|
chore: gitignore desktop 模块目录裸二进制(go build ./... 遗留)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:13:48 +08:00 |
|
Blizzard
|
53d9f49e96
|
docs: create VOICE_DESIGN.md and update go.work.sum dependencies
|
2026-07-17 22:54:53 +08:00 |
|
Blizzard
|
5d7eca5de3
|
fix(billing): 微信支付钉死「微信支付公钥」验签体系 —— 商户 2025-09 开户没有平台证书
用户拿旧项目代码对出来的真问题:我此前用 WithWechatPayAutoAuthCipher(平台证书
模式,APIv3 密钥自动下载平台证书验签),但 2024 起新注册商户只发「微信支付公钥」
(PUB_KEY_ID_ 开头)、没有平台证书——在该商户号上初始化/回调验签都会挂。
- 改 WithWechatPayPublicKeyAuthCipher(商户私钥+公钥ID+公钥文件);回调验签用
NewSHA256WithRSAPubkeyVerifier;平台证书模式不留双模式赘肉(YAGNI)。
- Config 增 public_key_path/public_key_id(必填,公钥文件同样只存路径);
admin 卡片补两字段;env 兜底加 WECHAT_PUBLIC_KEY(_ID)。
- 顺手修 live 撞出的真 bug:sundynix_setting.value 是 varchar(255),
支付配置 JSON(含加密密钥)一条就超(SQLSTATE 22001)→ 改 text。
live:列类型已迁 text;缺公钥两项报「配置不全,缺: public_key_path,
public_key_id」;GET 回显含新字段。go 6 包测试+tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 11:12:26 +08:00 |
|
Blizzard
|
d1e1e0fc4a
|
feat(billing): 微信支付配置进 DB —— admin 控制面保存即热生效
用户定的形态:配置存数据库、密钥文件放服务器磁盘(库里只存路径)。
env 降级为兜底(DB 优先 → env → 隐藏,与 tokens_per_credit 同一约定)。
- payment 包重构:Config(6 字段)+ Manager(RWMutex 热重载,学 prompt 控制面
改完即生效不重启);未启用原因人话化(未配置/缺哪些字段/初始化失败具体错)。
- APIv3 密钥入库前 AES-GCM 加密(shared/secrets,与模型 API Key 同一把
SUNDYNIX_SECRET_KEY);GET 只回 has_apiv3_key 不回显;PUT 留空=沿用旧密钥
(只写不回显语义,同模型 Key)。
- admin GET/PUT /admin/payment/wechat;业务路径全部改经 Manager.Current()
取快照(BillingPacks/下单/查单/回调)。
- admin 计费页「微信支付配置」卡片:状态徽章(已启用/未启用+原因)+六字段
+保存并热生效。
live:无配置→「未配置」;存假配置→热重载报「私钥加载失败:decode err」;
去掉 appid→「配置不全,缺: appid」;密钥留空沿用(has_apiv3_key 保持 true);
psql 复核库内密文 enc:1: 前缀、不含明文子串。go/tsc/41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:55:30 +08:00 |
|
Blizzard
|
efd185b779
|
feat(billing): 支付 P5.2 —— 微信支付 Native 渠道(扫码充值)
wechatpay-go v0.2.21。凭据全 env 注入(WECHAT_MCHID/MCH_CERT_SERIAL/
MCH_PRIVATE_KEY/APIV3_KEY/APPID/NOTIFY_URL),缺一渠道即隐藏——半配置/假凭据
只打日志不拖垮 gateway(用假私钥实测过降级)。
- internal/payment:Native 下单出 code_url、APIv3 回调验签解密、主动查单,
三者统一收敛为 QueryResult。
- 下单 POST /billing/orders {pack_id}(≥member+审计):金额/积分按在售包服务端
锁定进订单行,不信任客户端;渠道下单失败即作废,不留付不了的 pending。
- 到账两条路汇入同一个 MarkOrderPaid 幂等闸(CAS+唯一索引双闸,同 P5.1):
①公开回调路由(验签是唯一的门;金额与订单不符不入账);②前端轮询的
GET /billing/orders/:id 在 pending 时顺路主动查单——本地/内网收不到公网
回调也能确认到账,回调只是生产更快的通道。pending 超 30 分钟置 expired。
- Web 面:在售包卡片(渠道亮才出现)→扫码弹窗(qrcode 画 code_url,二维码底色
固定纯白——暗色主题下低对比码扫不出来)→2.5s 轮询→到账 toast+刷余额。
验证:go/tsc/vitest 全绿;无凭据+假凭据两种降级 live 四连
(channels 只剩 redeem/下单 400 引导兑换码/回调 503/兑换码闭环不受影响)。
⚠️ 真通道(prepay→扫码→回调/查单→入账)需真实商户号,未 live——用户配好
env 后用小额包实测,建议先配 ¥0.01 测试包走一单。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:39:24 +08:00 |
|
Blizzard
|
929bbf334b
|
feat(admin): 计费页长出「充值渠道」块 —— 兑换码生成/台账 + 积分包定价
P5.1 收尾:此前生成码只有 API。计费页现在从上到下 = 计费规则(积分→token
汇率) → 充值渠道(钱→积分:兑换码 + 微信定价用的积分包) → 用量观测,
两层汇率在同一页可见、各管各的。
- 兑换码:面额/张数/备注生成;**明文码只在生成响应显示一次**(等同现金,
台账 GET /admin/redeem-codes 服务端脱敏只露首尾,丢码重生成、不提供找回
——顺手把接口这个第二明文出口堵了);台账含核销状态。
- 积分包:新增/上下架(微信 P5.2 上线前把定价面备好);admin api.ts 补
packs/redeem-codes 四个函数。
- launch.json 加 admin-console-alt(:5176)——5174 被用户自己的 sundynix-site
占着,不动别人端口。
live:5176 登录→生成 5 张(绿色一次性面板+复制全部)→配「入门包 1000分/¥9.9」
在售可下架→台账脱敏 curl 复核;tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:28:03 +08:00 |
|
Blizzard
|
8e05f4b3fe
|
feat(billing): 支付 P5.1 —— 订单骨架 + 兑换码渠道全闭环
按 PAYMENT_DESIGN.md 施工。零资质渠道先把「订单→入账→对账」的骨架跑真,
微信 Native(P5.2)进来只是多一个 adapter。
- store/payment.go:CreditPack(admin 可配包)/PaymentOrder(支付侧事实源,兑换
也写单,全部充值一个查法)/RedeemCode(SDX-XXXX-XXXX-XXXX,crypto/rand,剔除
易混字符)。三模型都不标 isTenantScoped——订单归计费租户,与活跃租户可能
分叉,插件自动注入会写错归属(RecentRuns 同款教训),显式赋值+显式过滤。
- Redeem 单事务:码 CAS 占用(unused→used 只成功一次,幂等主闸)→建已支付
订单→账本分录+物化余额。credit_ledger 加 (kind,ref) 部分唯一索引兜底
——GrantCredits 此前对 ref 零约束,回调 at-least-once 就是重复入账事故。
- 路由:/billing/packs|orders(查,全员) + /billing/redeem(≥member+审计);
admin /redeem-codes(生成/列表) + /packs(配包)。
- Web 面账单页:兑换码输入(viewer 不摆输入框,真闸在后端)+最近充值订单流。
- 测试:码形态/200 样本无撞码/参数边界;go+tsc+vitest 全绿。
live 13 项:生成→核销余额精确+100→重core 400(主闸)→DB 直插重复 ref 被唯一
索引打回(兜底闸实证)→viewer 403→balance==SUM(ledger) 不变量→审计留痕→
浏览器 UI 兑换 100→200+订单流展示。
已知余项:admin 生成码暂只有 API(admin 页 UI 下一刀);微信 adapter=P5.2。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:18:14 +08:00 |
|
Blizzard
|
039e7a2f06
|
docs(payment): 渠道拍板微信支付 Native + 两层汇率解耦写明
- P5.2 = wechatpay-go(Native 下单 code_url→二维码,APIv3 验签解密);商户号/证书
env 注入,未配置渠道自动隐藏只剩兑换码,半配置状态不许把下单路由搞出 5xx。
- 用户强调积分↔token 要可动态调:第二层 tokens_per_credit 是 P2 期现成的
(admin 计费页,DB 热生效);本期只新增第一层(积分包定价,admin 配包)。
订单锁定下单当时的包价,改包不影响已付订单。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:55:02 +08:00 |
|
Blizzard
|
d5836fd190
|
docs: 支付设计一页纸(P5) —— 预付积分包路线,渠道适配器,双闸幂等入账
订阅制砍掉(等付费用户拉动),充值积分包复用现成 credit_ledger/GrantCredits;
指出必须先补的闸:GrantCredits 对 ref 无唯一约束,支付回调 at-least-once
会重复入账——订单状态机 CAS 主闸 + (kind,ref) 部分唯一索引兜底。
P5.1 用零资质的兑换码渠道先跑通全闭环,真渠道(支付宝/微信/Stripe)等拍板。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:47:41 +08:00 |
|
Blizzard
|
206cd51efd
|
ci: 补 desktop 覆盖 —— go 模块进关卡 + 三个前端跑 vitest
两个此前的盲区:
- sundynix-desktop 的 Go 模块不在 go.work,go job 的四模块循环从来没测过它
——app.go 桥方法(download 残file/另存为)的单测在 CI 一次没跑过。
新开 desktop job 用 macos-latest:免装 gtk/webkit(linux 编 wails 一堆 CGO 头),
且 darwin 才是实际发行目标;go:embed 需要 frontend/dist,先 npm build。
- web job 只跑 tsc:desktop 68 例、admin 41 例、web 3 例 vitest 全都不进关卡。
统一补 npm test。
全部命令本地(macOS,与 runner 同环境)原样跑过:前端构建/go build+vet+test/
三处 vitest 112 例全绿;YAML 解析与 GOWORK 引号校验过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:41:41 +08:00 |
|
Blizzard
|
b4012dbbba
|
fix(desktop): 报告 PDF 导出在壳内改走原生桥 —— WKWebView 拦死 window.open
实机验证抓到的:桌面壳内点 PDF 报「打印窗口被拦截」——Wails v3 的 WKWebView
把 window.open 拦成 null,前端弹打印窗那条路在壳内根本走不通(浏览器预览没事)。
- app.go 加 PrintReportPage(filename, html):打印视图 HTML 落临时文件(文件名
过滤路径字符),openInSystem 交系统默认浏览器打开,页面 onload 自动唤起打印框,
用户直接「存储为 PDF」。CJK 零字体依赖的原有优势不变。
- desktop.ts printReportHtml 改 async:inWails 走原生桥,浏览器维持 window.open;
RunsView 调用点随之 async + 错误透 toast。
- 绑定重新生成(注意要 `wails3 generate bindings -ts`,裸跑默认吐 JS 且要
GOWORK=off,否则 go.work 干扰找不到 Service)。
验证:go test/tsc/68 例 vitest 全绿;壳内被拦是用户实机复现的。
⚠️ 原生桥新路径(临时文件→浏览器→打印框)用户尚未实机点验,重新打的包已就位,
下次跑报告顺手验。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:40:15 +08:00 |
|
Blizzard
|
7c67256f87
|
feat(web): 薄 Web 面 sundynix-web —— 注册/组织/团队/账单自助入口
CI / Go · build + vet + test (pull_request) Failing after 30m3s
CI / Frontend · tsc (sundynix-admin) (pull_request) Failing after 9m42s
CI / Frontend · tsc (sundynix-desktop/frontend) (pull_request) Failing after 16m53s
CI / Frontend · tsc (sundynix-web) (pull_request) Has been cancelled
CI / mcp-py · sandbox guard (pull_request) Has been cancelled
SaaS P3 收口第二刀(设计见 SAAS_DESIGN.md §8)。第三个产品面:desktop=用户
工作产品、admin=平台超管控制塔、web=租户客户的自助柜台——做「装桌面端之前
就要能用」的那些事,三者不合并。
- 骨架抄 admin(HashRouter+路由注册表+me()/sdx:logout 鉴权门+vite/vitest
单文件配置);UI 整目录搬 desktop 的 ui/ 组件+ink/brand 主题 token(亮暗
双主题),品牌观感与桌面端一致;api.ts 抄 desktop 的 auth/tenant/usage 段
(纯 fetch 零 Wails 依赖),成员管理/建组织打新的租户自助接口。
- 页面:登录注册(一页两态)/概览(组织+角色+余额+桌面端下载指引)/团队(名册+
邀请+改角色+移除,写控件按角色显隐、真闸在后端)/组织(列表+自助新建+切换)/
用量与账单(余额 hero+趋势+合计+最近消耗,数据全来自现成 /me/usage;充值
只放说明占位,支付 P5 再接,不做假入口)。
- 工程:vite :5175、launch.json 配置、make webface、ci.yml web 矩阵纳入。
- 测试:tsc 干净;vitest 3 例(fmtCredits 去尾零——测试先行抓出 1.50 毛刺;
ASSIGNABLE_ROLES 不含 owner)。浏览器 live 全流程:甲(owner)登录→概览→
团队邀请乙→乙(member)登录写控件全隐藏、名册只读,组织/用量页真数据渲染。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 17:47:23 +08:00 |
|
Blizzard
|
65340616df
|
feat(gateway): 租户自助管理接口 —— 薄 Web 面增量1
SaaS P3 收口第一刀。成员管理此前只有平台超管口径(/admin/tenants/:id/*),
租户 owner 无法自助管自己的组织;建租户同样只有 admin 能做。
- 成员自助四路由(作用于活跃租户,id 取 ctx 不接受路径任意租户):
GET /tenants/current/members(≥viewer) + POST/PUT/DELETE(≥admin+审计)。
RequireTenantRole 是 RBAC 极简版预留的架子,这次直接挂上,零改动生效。
- handler 挡两个平台超管可以、租户 admin 不该有的动作:邀请为 owner;
借 AddMemberByEmail 的 upsert 语义「重新邀请」owner 实现降权——后者
是真实的越权路径,admin 拿 owner 邮箱重邀成 viewer 就把 owner 拉下马。
- POST /me/tenants 自助建组织:创建者即 owner、建完切入;slug 用雪花 id
生成不让用户填(全局唯一,自助场景撞名只添乱);owner 没挂上按错误返回
而不是 warn+200(孤儿租户没人能管)。
- CORS 支持逗号分隔多 origin(desktop 预览/薄 Web 面/admin 不同源):命中
白名单回显请求 Origin+Vary;单值/通配/生产未配置全拒的旧语义不变。
- 测试:RequireTenantRole 补 ≥admin 门槛用例;cors 四场景单测。
live 验证 13 项全过(门槛/owner 保护/降权攻击挡下/CORS 回显/审计落痕)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 17:33:04 +08:00 |
|
Blizzard
|
d14a10479e
|
fix(desktop): 补回报告页剥离时漏搬的 PDF 导出与完成系统通知
759483b 把报告页剥成纯启动器时,Word/Markdown 导出搬去了运行页,但漏了两个:
printReportHtml(前端打印出 PDF,CJK 零字体依赖)和 notify(完成弹系统通知)
外部调用点归零,成了被孤立的活功能。
- 运行页报告正文面板补 PDF 按钮:previewRef 抓已渲染 DOM 送打印视图,
与剥离前同一条路径。
- 完成通知挪进 attachRun 的 token 流 done 回调并加 label 参数:原先只有
报告会通知,但「跑几分钟、人早切走了」对编排任务一样成立,且 attachRun
是所有运行的唯一汇合点,放这儿不会再漂移。恢复的待审任务也带主题。
验证:tsc 干净、68 个 vitest 全过、两函数调用点恢复非零。
⚠️ 未实机点验(需登录态):PDF 按钮实际点击出打印视图、通知实际弹出,
下次起服务后补验。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 16:28:28 +08:00 |
|
Blizzard
|
027ccc0ccc
|
chore(desktop): 版本号三处对齐 0.1.1,追平已存在的 v0.1.1 tag
git tag 已打到 v0.1.1,但 APP_VERSION/package.json/build/config.yml 全停在
0.1.0 —— 按更新提示的比对逻辑这会错乱。三处统一 0.1.1,并跑
wails3 task common:update:build-assets 重新生成原生元数据(Info.plist 等,
CFBundleShortVersionString 已核实落到 0.1.1,包名/标识符未被冲回模板值)。
生成器顺手拉的 build/ios/ 脚手架已删,不做 iOS。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 16:28:03 +08:00 |
|
Blizzard
|
a779953ed9
|
docs(go.work): 注释里的 wails build 是 v2 说法,改 wails3 package
v3 的 build 只出裸二进制,出 .app 得 package;顺带点明任务链自带前端构建,
不用手动 npm run build。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 15:16:54 +08:00 |
|
Blizzard
|
a5cf2c5f36
|
ci: 构建链补完 wails v2→v3 迁移,CI 触发收敛到 main
代码早就全量迁到 v3(go.mod 只要 wails/v3、main.go import v3/pkg/application),
但"怎么构建"这一层从没跟上,全还是 v2:
- release.yml 装 v2 CLI(wails@v2.12.0)去编 v3 代码,编不过;产物路径也还
指向 v2 的 build/bin/,而 v3 落在 bin/。这条只在打 v* tag 时触发,迁移后
一次都没跑过,所以坏了半个月没人知道 —— 下次发版必炸。
- Makefile 的 wails dev/build 直接 command not found。
- README 叫人装 v2 CLI;RELEASE.md 还在提早已删除的 wails.json。
改动:
- ci.yml 触发从 [main, dev] 收到 [main]。dev 分支已删,开发在 feat/wails3,
留着 dev 是死条件;PR 以 main 为目标仍会跑,合入前有关卡。
- release.yml 全量转 v3:
* CLI 钉死 v3.0.0-alpha2.117,与 go.mod 的 wails/v3 版本一致 —— alpha 阶段
CLI 生成的 bindings 和运行时配套,版本错开会出难查的怪问题。
* mac 走 wails3 task darwin:package:universal。v3 的 build 只出裸二进制
(macOS 上双击不起窗),必须 package 才有 .app;本地实跑验证过,
lipo -archs 确认是真 universal(x86_64 + arm64)。
* Windows 保持 v2 时代行为发裸 .exe。v3 的 package 默认走 NSIS 出 installer,
依赖 runner 上有 makensis,属额外未知数,要升级安装包再单独议。
* 产物路径 build/bin/ → bin/。
* fail_on_unmatched_files 改 true,并按 matrix 只匹配自己那个产物 ——
原来是 false 且两个平台都列全量文件,产物路径写错会安静发一个没附件的
Release。这次的 bug 正是路径漂移,得让它当场炸。
- RELEASE.md 版本号对齐表补上第三处 build/config.yml 的 info.version(此前写着
"必须三处对齐"却只列了两处,第三处是已删的 wails.json),并记下改完必须跑
wails3 task common:update:build-assets 重新生成 Info.plist —— 这坑踩过。
- 清掉 v2 残留:frontend/wailsjs/(v2 绑定,v3 用已跟踪的 bindings/)及其
gitignore 规则、build/bin/ 里 v2 时代的 .app。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 15:03:32 +08:00 |
|
Blizzard
|
759483bf92
|
refactor(desktop,gateway): 用户端去重 —— 一个功能一个入口,一份数据一个查法
用户反馈「UI 太乱」。数出来的:report/kb/memory 各 4 个入口、studio 5 个;
运行历史两套渲染。乱的机制是同一件事写了两遍,改一处另一处必然掉队。
**一份数据一个查法**
- 工作台「最近任务」和运行页「运行历史」是同一批数据,却各查各的:
前者走 store.RecentTasks(),后者走 RecentRuns()。于是我给运行历史加的
topic 字段工作台完全没跟上,还在 mono 显示 report_<hex>;而且 RecentTasks
没有租户过滤,口径也不一致。现在统一走 RecentRuns,RecentTasks 删掉
(它只有那一个调用点)。前端 recent_runs 也改用 RunSummary 同一个类型。
**一个功能一个入口**
- 工作台删掉「入库知识/生成报告/管理记忆」按钮排 —— 跳的目标和上面四张
能力卡片完全重合,同一页给两个入口。
- 「最近任务」现在点击直达运行页对应那条(App 加 goto(view, taskId) →
RunsView focusTaskId),而不是只把页面切过去让用户自己再找一遍。
**报告归 RUN,只做启动器**
- 报告是「执行」不是「构建」,从 BUILD 组移到 RUN。
- 报告页砍掉「执行轨迹」「报告正文」两个面板 —— 和运行页完全重复,而且
那套是组件本地 state,切页面就没。现在它只有主题输入框。
- 报告改走 App 的全局运行态(新增 onRunReport,与 onRun 同构、复用 attachRun):
提交 → 跳「运行 · 观测」→ 实时看轨迹/正文/导出。App 里 onRun 早就写着
「发起即跳运行页,观测统一收敛在此页」——这条方针一直在,只有报告没遵守。
**顺带清死代码**
- 市场(规划中)从导航下架,ViewKey 里的 "market" 和 Boxes 图标一并删。
- PLACEHOLDERS 整块死代码:它列的 home/kb/report/runs 全都早已真实实现,
market 是唯一还可能用到的、下架后那个 Placeholder 分支永远走不到
(tsc 报 "Spread types" 就是因为 PLACEHOLDERS[view] 已成 never)。
连同 views/Placeholder.tsx 一起删。
live 验证(真账号,桌面端实机):报告页输入主题 → 提交 → 自动跳运行页 →
顶部「当前运行 · live」→ 轨迹实时跑 → 报告正文流式出 + 导出按钮在位 →
report_7a0110a10b534f90 落库带 topic「归口测试」,POST /api/v1/reports → 202。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 14:08:16 +08:00 |
|
Blizzard
|
fd99ba6226
|
feat(desktop,gateway): 报告归口运行页 —— 能找回、能复盘、能导出
承接上一个提交(报告终于落库进运行历史),把「所有执行归口运行页」做完整:
只能看不能存,不算归口。
- 运行页的输出面板:报告类运行显示「报告正文」并在右上角出 Word/Markdown
导出。报告页那两个导出按钮依赖它自己一切页面就没的本地状态,所以从历史
里找回来的报告,只能在运行页导。
- 运行历史列表显示报告主题。此前只有 report_<hex>,谁也认不出是哪份报告。
主题从 graph->>'topic' 取(报告的 graph 就是占位 DSL {"topic":"…"},普通
任务 DSL 没有顶层 topic → 空串,不会误伤)。
- isReportRun() 按 task_id 的 report_ 前缀判定 —— 这是既有契约,导出接口
/reports/:id/export 本来就按同一个 id 寻址。+4 单测(含"不能只看是否包含
report"的误判防线)。
- reportFilename 从 ReportView 提到 lib:运行页也要用,不能私藏在一个 view 里。
live 验证(真账号,桌面端实机):运行历史首条显示「Redis 缓存穿透的三种解法」
→ 点开复盘 8 节点 + 全文 → 点 Word → **原生另存为对话框弹出**(文件名预填
主题)→ 保存 → 落盘 5KB,file 认 Microsoft Word 2007+,解 zip 得
word/document.xml,正文 2446 字。
**顺带把 wails3 迁移最后一个盲区验掉了**:v3 把 runtime.SaveFileDialog 重写成
application.Get().Dialog.SaveFile().SetFilename().AddFilter()
.PromptForSingleSelection(),此前从没被点过一次,记忆里只敢写"理应工作"。
现在实测通了。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 13:20:57 +08:00 |
|
Blizzard
|
fd268664c8
|
fix(gateway): 报告生成绕过了落库/计费/RBAC —— 生成完就永远找不回
用户实测:报告页输入主题、点生成、看着没反应,切到别的页面再回来,什么都没了。
真相是报告在后端好好地生成完了(26s),是 UI 把它弄丢了、而且永远找不回。
根因:两条提交路径漂移。SubmitTask(POST /tasks) 这一年陆续长出了预算门控/
计费租户/积分硬拦截/落库/录像五道,而 GenerateReport(POST /reports) 还停在
最初的「发个 NATS」,一道都没有。后果远不止看不到历史:
- 不落库 → 运行历史(读 sundynix_task)永远看不到报告。实测修复前该表
report_% 前缀 0 行。
- 无 token/轨迹录像 → SSE 没有回放能力,切走即永久丢失。
- 无 MetaTenantID → 报告用量记不到租户头上 = 漏账。
- 无预算门控/积分硬拦截 → 余额为 0 也能生成,绕过全部成本护栏。
- 路由漏了 RequireTenantRole → **viewer 只读角色能生成报告烧积分**,
而隔壁 /tasks 的注释白纸黑字写着「viewer 只读拦下」。报告一样烧钱。
修法不是把代码抄一份(那只会再漂一次),而是抽两个共用函数:
preflight() — 预算 → 计费租户 → 积分硬拦截,返回 billingTenant
launch() — 落库 + PublishTask + token/轨迹录像
两条路径都走它们,不可能再各长各的。SubmitTask 行为逐行不变。
live 验证(真账号 blizzardzhang,桌面端实机):生成 report_5dae9155af5cb500
→ sundynix_task 建行且带 owner+tenant_id → 出现在运行历史首条 → 点开
完整复盘 8 节点轨迹 + 报告全文。gin 路由表也确认 /reports 中间件数
12 → 13(与 /tasks 齐平)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 13:13:21 +08:00 |
|
Blizzard
|
bf3b0048fd
|
fix(desktop): 原生下载失败会在用户选定路径上留半截文件
桌面端 Go 侧此前零测试。补 5 个(download/ReadLocalFile/Ping),
逮到一个真 bug:io.Copy 中途断开时,用户选定的路径上会留下一个半截的
.docx —— 带着用户自己起的名字躺在那儿,虽然前端会弹错误,但以后双击打不开,
而用户会以为是导出功能坏了。现在失败一律 os.Remove 不留残file。
同批修的两处(同一段代码,都没测试盖到):
- Close 的错误被 defer 吞掉。写文件时 io.Copy 成功不代表数据落盘,
flush 失败只在 Close 上报——吞掉就是静默截断,且 download 返回 nil(成功)。
- http.Get 用默认 client,没有超时。上游卡住的话「另存为」会永远转,
用户只能强杀 app。改用带 3 分钟超时的 client。
顺带核实过一个可疑点、结论是不用改:download 走裸 http.Get 不带鉴权头,
但报告导出路由 `/reports/:id/export` 是故意公开的(router.go 注释:
"EventSource/下载无法带 Bearer"),所以能通。
对话框本身(application.Get().Dialog)要真窗口,自动化盖不到,仍需手点。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 12:06:21 +08:00 |
|
Blizzard
|
d52af12f3d
|
fix(desktop): 打包元数据还是 wails3 脚手架模板值,app 根本起不来
现象:wails3 package 出来的 .app 双击报「可执行文件不存在」,macOS 直接拒绝启动。
根因是我迁移 v3 时埋的:build/darwin/Info.plist 里 CFBundleExecutable 写着
"wails3ref"、bundle ID 是 "com.example.wails3ref",全是脚手架模板默认值,
而实际二进制叫 sundynix_desktop —— 对不上,macOS 认为 bundle 坏了。
我当时在 build/config.yml 里填了正确的产品信息(com.sundynix.agentix /
sundynix-agentix),但漏了那个文件顶部写明的一步:改了 info 要跑
`wails3 task common:update:build-assets` 重新生成资产。补跑之后不只 macOS,
Linux 的 desktop/nfpm、Windows 的 info.json/nsis/manifest 里的
"My Product"/"My Company"/"wails3ref" 也一并换成了真实产品信息 ——
这些留到打包分发才发现就麻烦了。
Assets.car/icons.icns 一并提交:体积变化大(1.63→2.03MB / 52→45KB)不是噪声,
是真被重新生成了;但逐像素比对确认图案没变(平均差 0.6/255),无回归。
顺带删掉重生成又造出来的 build/ios —— 项目是 desktop-only。这次它只有
plist/storyboard/pbxproj、没有 .go 文件,不会像当初那样让 go build ./... 挂掉,
但也没必要留。已验 go build ./... 通过。
已知待办(非本次引入):app 图标至今是 Wails 默认的 "W" logo,从没做过自定义。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 11:42:21 +08:00 |
|
Blizzard
|
bfbca08d72
|
feat(desktop): 办公室换 AI 房间背景,手绘 SVG 场景全部退役
第三版房间才可用。前两版地板都只占画面 13%,而场景需要 ~60% 的地面
才摆得下工位——但那不怪模型,是我提示词自相矛盾:同时要求"正面平视"和
"下方 60% 是地板",几何上不可能同时成立(平视时地板向地平线收缩)。
改成"俯视 30 度、娃娃屋视角"后一次就对(实测地板 53% / 墙 47%)。
参考项目的 office.png 本来就是俯视娃娃屋 + 正面角色,它早验证过这个组合。
不拉伸图去迁就场景,而是改场景去迁就图:VB 720×510 → 940×512,正好是
房间图的比例(1.835)。拉伸 30% 的话墙上挂钟会变成椭圆,一眼假。
所有坐标按图里量出的百分比换算:FLOOR_Y=242(地平线 47.3%)、
DOOR_X=279(门中心 29.7%,角色从画上那扇真门里走出来)。
量着修的两个碰撞:
- 白板压住门和窗。实测墙上被门 214~343、钟 443~515、窗 612~791、
绿植 863~932 占着,最大空白段仅 214 宽,放不下 340 的白板。选择盖掉
挂钟(静态装饰、零信息量),白板落在门与窗之间的 347~613。
- 协调者的桌子撞上正下方工位的气泡。可行区间只有 (242, 276):
低于 242 会飘到墙上,高于 276 撞气泡。取 268。
字号 ×1.3 补偿:viewBox 从 720 加宽到 940 后,同样单位在真实面板
(~520px)里会缩小 28%,不补的话白板正文只有 7px。验收也是在 520px
下做的,不是在预览页那个宽容的尺寸下。
顺带清掉 4 个死常量(WALL/FLOOR/DESK_TOP/DESK_FRONT)——房间换贴图后没人用了。
资源合计 148 KB(7 立绘 + 1 房间,全 WebP)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 11:17:41 +08:00 |
|
Blizzard
|
8b19c572f4
|
feat(desktop): 办公室换 AI 立绘素材(告别手绘 SVG 小人)
用户三次反馈手绘 SVG 不好看 —— 是对的,我不是插画师,程序化矢量画不出
商用品质。技术栈换来换去(3D/Lottie/Spine)都绕不开这点:卡的是美术。
素材:用户用 Nano Banana 生成,链式参考锁风格(先出主管定基准,其余 5 个
+ 桌子 + 房间都拿它当参考图)。版权归用户账号,干净。
为什么最终没上 Spine(用户原本要求照搬 ai-office-react 的实现):
- Spine 运行时不是 MIT。原文要求"每个用户须自购 Spine Editor 授权",
对要卖的产品等于不可行;走另一条要我们买 $69/$330 按座授权。
- 那套 chibi-stickers 素材是 Spine 官方示例(皮肤名里有 spineboy 和
Spine 团队成员名),只授权用于评估学习。参考项目 README 自己写着
"注意素材版权问题!"。
有了立绘就不需要骨骼动画:走位/时序/状态全是现成的 CSS 关键帧,
Chibi 那个口直接换贴图,零 Pixi 零 Spine 零授权。
抠像(scratchpad/art/key.py):只抠「与画面边缘连通」的绿。
绿衬衫角色实测 (41,184,60) vs 绿幕 (9,240,2) —— 两者都"绿压过红蓝",
按颜色阈值必然把衣服抠出洞(实测躯干 81.3% 被误杀);连通域一刀切干净,
对绿植/绿屏同样安全。含软边 + 去绿边,7 张残留绿毛均为 0 像素。
体积:PNG 540KB → WebP 117KB(-78%,平涂+透明是 WebP 强项)。
桌子按「桌面」锚点而非底边:底边贴地时桌宽 200 会让显示器顶到 y=-136
糊住人脸。按桌面对齐后桌子下半截自然超出脚线 —— 这是对的,桌子比人更
靠近镜头本就该更低。铭牌/副标题都压在挡板内(实测 local -27~+23),
掉出去会骑在下边线上被切半截。状态辉光改画在贴图之下、用径向渐变,
硬边色斑糊在显示器背壳上物理讲不通也像贴纸。
手绘版保留为 sprite 缺省时的回落,未删。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 10:34:05 +08:00 |
|
Blizzard
|
c2811e79a3
|
fix(desktop,dispatcher): 从编排执行的多 agent 图看不到「团队」tab
现象:编排里并排三个 agent(研究/撰写/审查)跑完,运行页没有团队 tab。
两处卡住,不是一处:
1) isMultiAgent 只认 coordinator: 节点。用户自己在图里并排多个 agent 也是
团队,却被整个漏掉。改成:有协调者,或 ≥2 个 agent: 节点。
2) 就算放宽 1,deriveTeam 仍按 kind==="agent" 挑工位——而两条产生 agent 的
路径 kind 并不一致:协调者派发的专家是 kind=agent,图里的 agent 节点是
kind=model。改成按节点名前缀(agent:/tool:)判,这本来就是后端一直遵守的
约定;顺带天然把 retriever:/map:/render: 这些同为 kind=tool 的节点挡在
工位之外(之前它们会混进来当工位)。
连带修一个更要命的:runAgent 把轨迹标签写死成"模型流式推理"、runReactAgent
写死成"ReAct 智能体(自主调工具)",用户在编排里给节点起的名字(研究 Agent /
撰写 Agent / 审查 Agent)整个丢了。后果不止办公室:执行轨迹里三行同名,根本
分不出谁是谁;团队视图只能退回节点 ID,工位显示成 r/w/rev。
改成一律 labelOf(n, 兜底) 由调用方传入,+2 单测钉住。
另:没有协调者时不再凭空画一个"协调者"小人(白板改挂「任务产出」),也不演
递简报那一程(没人可递),✓ 气泡改为收工即冒。
注意:已存的历史轨迹是落库的,仍是旧标签;只有新跑的任务才有节点名。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-15 17:41:21 +08:00 |
|
Blizzard
|
02608bb6a4
|
feat(desktop): 办公室加走位编排(进场 / 敲键盘 / 递简报)
专家被派发 → 从门口走进来 → 在工位敲键盘 → 干完起身走到协调者桌前
递简报(手里真拿一页) → 回工位。协调者的白板仍是思考/综合流。
为什么做这个:重看参考项目(ai-office-react),它的 Spine 动画只有 idle
和 walking 两个——"好玩"来自角色在房间里走动,不是画质。上一版把人钉死
在工位上只让手动,这才是不好玩的根因。
实现:全 CSS 关键帧,无 rAF / 无定时器 / 无状态机。
- 直播(streaming):工位随事件到达而挂载,延迟一律 0,动画即真实进度。
- 复盘:按事件时间戳算出各自的出场/交付时刻,整段压进 PLAY_MS 当一出戏放;
敲击次数由"工作时长 ÷ 敲击周期"换算,所以快的人敲几下、慢的人敲一路。
- 收工时刻强制不早于"进门 + 敲一下",否则压缩比大时人还没进门就来递简报。
- 角色层与桌子层分开画,人才能走到别人桌前而不被自己那张桌子焊死。
- 补了腿:原来是"永远被桌子挡住的半身像",一走出工位就成了悬空上半身。
- reduced-motion 直接就位不演。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-15 17:41:21 +08:00 |
|
Blizzard
|
225fee5519
|
feat(desktop): 团队视图加「卡通办公室」皮肤(手绘 SVG 贴纸风)
多智能体协调演成办公室:协调者在白板前统筹(白板即思考/综合流),
专家在工位敲键盘,完成打✓、失败挂!、派发走虚线。
风格对齐 ai-office-react(粗描边+平涂+大头身),但用手写 SVG 而非
PixiJS+Spine:零外部素材、无 Spine 运行时授权问题、几 KB 进包。
先试过 three.js 真 3D,低模程序化角色出来是玩具味,已弃并卸干净依赖。
数据与「卡片看板」同源(deriveTeam),换皮不换数据,后端零改动。
动效=状态编码(敲键盘/思考流/辉光),守 prefers-reduced-motion。
顺带修一个真 bug:并发时间轴复盘卡在 running 的旧任务时,未收口工位
会一路量到 Date.now(),跨度爆表(实测 32103562.4s)。抽 teamNow():
直播用此刻、复盘用最后一个事件时间戳;两个皮肤共用,+3 单测。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-15 17:41:21 +08:00 |
|
Blizzard
|
0152e7837a
|
feat(desktop): 多智能体「团队」视图(运行·观测新增,轨迹仍默认)
把多智能体协调画成「协调者居中 + 专家工位环绕」,让最难读的一段——并行派发与综合
——变得一眼可读。执行轨迹保持默认视图;「团队」tab 只在多智能体运行时出现,
普通任务不塞多余 tab。
- lib/run.ts 加 isMultiAgent()(据 coordinator: 节点判定) + deriveTeam():
从现有执行事件流派生协调者/工位模型。与 deriveNodes 的区别是保留 start/end 时间戳,
才能画并发时间轴;并解析专家收尾 detail("简报 X → Y")还原简报与产出。
- components/TeamView.tsx:径向布局(工位按角度均分环绕,>6 个降级为网格)、
协调者实时思考流(接 token 流 + 闪烁光标)、并发时间轴(重叠一眼可见)。
动效只标活跃态(派发链路流动/工位脉冲)——是状态编码不是装饰,带 prefers-reduced-motion 守卫。
专家(kind=agent)与工具(kind=tool)图标区分。
- 零后端改动:数据全部来自现有 exec 事件(coordinator:/agent:/tool:)。
补 9 个单测覆盖派生逻辑(简报解析/失败态/时间窗/lead 不混入工位)。
实机经回放路径验证渲染(径向布局/简报/并发条/tab 条件出现)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-15 17:41:21 +08:00 |
|
Blizzard
|
2d5b72930a
|
fix(dispatcher): 中文专家名导致多智能体协调 400 挂死
buildSpecialists 把专家名原样当 OpenAI function-calling 的 tools[].function.name,
而该字段受约束 ^[a-zA-Z0-9_-]+$。中文命名专家(中文产品里最自然的用法,如"条款专家")
会让模型直接 400:
Invalid 'tools[0].function.name': string does not match pattern
整个协调节点挂掉,再沿 failover 链把备用模型也拖垮。
修:toolFuncName() 规范化给模型看的函数名——非法字符→下划线,清空→expert_N,
同批内撞名加序号保证唯一;展示名与执行轨迹(agent:<原名>)仍用原名不变。
模型靠 Desc(spec.Use) 判断何时调用,函数名不承载语义,故退化命名不影响派发质量。
实测(真 deepseek):修前中文名必现 tools[0].function.name 400;修后该 400 归零。
补 TestToolFuncName 覆盖:纯中文/合法ASCII/混合名/撞名唯一性。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-15 17:41:21 +08:00 |
|
Blizzard
|
11bf7d2756
|
fix(desktop): wails3 绑定 .js→.ts + 实机验证通过
wails3 build 重新生成绑定为 TypeScript(比 standalone generate 的 .js 更规范,
tsconfig allowJs 保留以兼容两种)。整体迁移实机验证:
- wails3 build 全过(前端 build → 绑定生成 → go build -tags production 原生编译 → 打包)
- 原生二进制 bin/sundynix_desktop 启动,窗口开、进程稳、日志无 error/panic
- app 前端加载后自动登录(持久态)并打通后端:gateway 收到 /spaces/current、
/me/spaces、/me/tenants、/stats/overview、/runs、/health 一整串鉴权请求全 200
- runtime 版本号不一致(npm alpha.97 vs CLI alpha2.117)未导致问题
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-14 10:55:21 +08:00 |
|
Blizzard
|
1f487c3163
|
feat(desktop): 迁移 Wails v2 → v3(架构/目录/绑定重写)[WIP]
从 wails/v2 v2.12.0 迁到 v3 v3.0.0-alpha2.117,采用 v3 工程规范:
Go 侧:
- main.go:wails.Run(options.App) → application.New(Options{Services,Assets,Mac})
+ app.Window.NewWithOptions(WebviewWindowOptions) + app.Run();窗口选项照搬
(1440×900/min 1100×700/#0b0d12/Mac TitleBarHiddenInset)
- app.go:App 结构体绑定 → v3 Service(application.NewService);去 startup/ctx 注入;
SaveFileDialog 改 v3 链式 API(application.Get().Dialog.SaveFile().SetFilename().
AddFilter().PromptForSingleSelection());5 个原生方法保留
- go.mod wails/v2→v3、go 1.25
构建:
- wails.json → Taskfile.yml + build/(config.yml 填产品信息 + 各平台配置)
- 删移动端脚手架(ios/android),桌面端只留 darwin/windows/linux
- .gitignore 改为只忽略 build/bin(build/ 构建系统需提交)
前端:
- vite.config 接 @wailsio/runtime/plugins/vite 插件 + WAILS_VITE_PORT
- package.json 加 @wailsio/runtime;wails3 generate bindings → frontend/bindings/
- desktop.ts:window.go.main.App → import { App } from bindings;桌面态检测改
window._wails.environment(原生注入);isMacDesktop 用 System.IsMac()
- version.ts:runtime.BrowserOpenURL → @wailsio/runtime Browser.OpenURL;测试同步更新
- tsconfig 加 allowJs(消费 JSDoc 绑定)
验证进度:Go 编译过 + tsc 干净 + 48 前端测试绿 + 前端 vite build 通过。
**未完成:整体 wails3 build/dev 原生窗口实跑 + 原生能力(另存为/打开/外链)联调**——下次续。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 18:00:45 +08:00 |
|
Blizzard
|
8f1aefe13e
|
refactor(desktop): 去黑话 + 收基建灯(商业化语言铺开)
把首屏定的"去黑话+精致克制"语言铺到其余页面:
- 顶栏 5 盏基建灯(Gateway/DB/NATS/Milvus/Neo4j)→ 一枚「系统正常」状态点
(客户不看数据库名;异常时 tooltip 提示项数,顺带缓解窄屏拥挤)
- 知识库:入库阶段标签去实现名(写入向量库/Milvus/Bleve/Neo4j → 建立语义索引/
全文索引/构建知识关系);检索台「混合召回+rerank」→「在库中搜索」,命中分数
0.83→83%,"RAG 未配置"→"该库可能为空";图谱去「Neo4j·力导向」
- 运行:流式中→生成中;"token 在此流式呈现"→"回复逐字呈现";忠实→可信度/引用
- 编排节点「检索(RAG)」→「知识检索」,描述去"混合检索"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 16:57:24 +08:00 |
|
Blizzard
|
2eb38d3eaf
|
feat(desktop): 首屏工作台商业化重做(定调)
把"工程师内部控制台"改成"给客户用的产品门面"(精致深色专业版方向):
- 问候式头部(下午好,X · 工作区上下文)+ 主 CTA「新建编排」抬到显眼位
- 指标去黑话:评测均分/忠实 → 「回答质量 89%」;Token → 「今日用量」
- 能力卡改结果语言(不再暴露 Eino/向量·全文·图谱/Word 等架构黑话)
- 删「服务·网关/总线/PG/Redis/Milvus/Neo4j」健康灯带(客户不看数据库名)
- 空态改引导(还没运行记录 → 从新建编排开始 CTA)
- 图标去彩虹化,单色 + hover 才亮 brand,右上箭头暗示可点
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 16:51:41 +08:00 |
|
Blizzard
|
1efe29aed3
|
refactor(desktop): 清理死 UI / 假功能(P0 完善)
让桌面端只暴露"真的能用"的东西,提升完成度观感、避免误导:
- 删顶栏假"版本选择器"(通用版/法律版/医疗版)——死 defaultValue、无 onChange
- 删「管理」导航项 + 其占位页/命令——内容是 admin 控制台的事(租户/计费/护栏/模型),
违背 desktop/admin 产品边界,留着只是让用户点了扑空
- 「市场·Packs」标记 ready:false(规划中徽标 + 标题标注),不再伪装成已就绪功能;
移除其命令面板项(命令面板只列真实可达目的地)
- Agent 节点模型选项去掉假的"占位 Pool/ollama:qwen/vllm:custom",改由已登记模型
运行时填充(无模型时显示"(无,先创建)"而非可误选的假项);默认 model 置空
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 16:24:56 +08:00 |
|
Blizzard
|
8f766a75a7
|
feat(space): 桌面端空间成员管理界面 —— 补齐"临时组队协作"前端闭环
此前能建项目空间/切换/启用全员空间,但邀请队友进空间只有后端 API、无界面,
协作故事前端断了一半。本次补上成员管理 UI,让 Space 协作端到端可用。
- api.ts: spaceMembers/addSpaceMember/setSpaceMemberRole/removeSpaceMember + SpaceMemberInfo
- SpaceMembers 弹窗: 列成员(名/邮箱/角色 badge); 空间 admin/owner 可邀请(邮箱+角色)/
下拉改角色/移除, owner 受保护(无下拉/移除); 非管理员只读提示。复用 Dialog/Button/Select
- 顶栏加"管理成员"入口(UserCog): 活跃空间为 project/tenant 时显示; App 管弹窗开关,
传 spaceId/spaceName/canManage(空间角色 admin/owner)/selfUserId
实机验证(Wails 桌面 + 全栈后端): demospace(owner)在项目空间邀请 teammate → 成员列表
即时出现(切换器 1人→2人) → 下拉改 teammate 成员→管理员(服务端确认 owner/admin) →
owner 自身受保护无改删入口。tsc + 48 前端测试通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 14:29:28 +08:00 |
|
Blizzard
|
9dab25a5f0
|
feat(space): 全员空间(tenant-kind) —— 整租户共享的"开关",自动纳入全部成员
补齐 Space 三类型的最后一种:tenant-kind「全员空间」= 用户最初想要的"共享工作区开关"。
与 project 空间的本质区别:自动纳入全租户成员(含之后新加入的),而非手动逐个邀请。
后端:
- EnableTenantSpace(幂等):建 kind=tenant 空间 + 把全部 active 租户成员按租户角色纳入
(owner→owner…billing_admin→viewer);tenantSpaceID 查询;spaceRoleFromTenant 角色映射
- AddMemberByEmail 加租户成员时 autoJoinTenantSpace:若已启用全员空间则自动补纳新成员
- SpaceCreate handler:kind=tenant 路由到 EnableTenantSpace,且须租户 admin/owner(inline 403)
桌面端:
- 空间切换器加"启用全员空间"入口(Globe 图标):仅租户 admin/owner 且尚未启用时显示,
带二次确认;启用后该按钮自动隐藏(hasTenantSpace),全员空间进切换器
验证(gateway+docker):member 启用 403 / owner 启用纳入全部现有成员(owner·member·viewer
按租户角色映射) / 新加租户成员自动纳入 / 幂等(再启用返回同一个不重复建) / viewer 在全员空间
入库 403(只读) / owner 建库 200;浏览器实机:启用后全员空间进切换器 + 启用按钮消失
至此 Space 三类型(个人/项目/全员)全部落地。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-13 13:55:29 +08:00 |
|