@bolloon/bolloon-agent 0.4.32 → 0.5.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.
- package/dist/agents/chain/explorer.js +52 -0
- package/dist/agents/chain/index.js +2 -0
- package/dist/agents/chain/onchain-trade.js +15 -1
- package/dist/agents/contacts/chain.js +27 -7
- package/dist/agents/execution-supervisor.js +493 -35
- package/dist/agents/external-events.js +35 -6
- package/dist/agents/gateway-group.js +100 -12
- package/dist/agents/goal-criteria.js +10 -3
- package/dist/agents/goal-flywheel/block-executor.js +346 -0
- package/dist/agents/goal-flywheel/continuation-decision.js +592 -0
- package/dist/agents/goal-flywheel/goal-change.js +754 -0
- package/dist/agents/goal-flywheel/index.js +26 -0
- package/dist/agents/goal-flywheel/memory-layers.js +227 -0
- package/dist/agents/goal-flywheel/run-closure.js +809 -0
- package/dist/agents/goal-flywheel/skill-candidate.js +628 -0
- package/dist/agents/goal-flywheel/types.js +329 -0
- package/dist/agents/goal-flywheel/wiring/change.js +288 -0
- package/dist/agents/goal-flywheel/wiring/closure.js +697 -0
- package/dist/agents/goal-flywheel/wiring/continuation.js +432 -0
- package/dist/agents/goal-flywheel/wiring/contract.js +270 -0
- package/dist/agents/goal-flywheel/wiring/index.js +26 -0
- package/dist/agents/goal-flywheel/wiring/monitor.js +254 -0
- package/dist/agents/goal-flywheel/wiring/seams.js +592 -0
- package/dist/agents/goal-flywheel/work-contract.js +478 -0
- package/dist/agents/goal-flywheel/work-monitor.js +488 -0
- package/dist/agents/goal-flywheel-wiring.js +1870 -0
- package/dist/agents/goal-state-reducer.js +406 -0
- package/dist/agents/goal-store.js +35 -1
- package/dist/agents/mobile-flywheel-view.js +144 -0
- package/dist/agents/mobile-task-actions-verify.js +74 -0
- package/dist/agents/mobile-task-actions.js +178 -0
- package/dist/agents/mobile-task-views.js +199 -0
- package/dist/agents/network-pulse.js +525 -27
- package/dist/agents/pi-sdk.js +75 -20
- package/dist/agents/run-store.js +18 -5
- package/dist/agents/skill-readiness.js +52 -10
- package/dist/agents/subagent-manager.js +209 -3
- package/dist/agents/task/task-onchain-runner.js +19 -1
- package/dist/agents/task/task-runner.js +36 -6
- package/dist/agents/task-board.js +702 -0
- package/dist/agents/task-group.js +524 -0
- package/dist/agents/task-public-text.js +89 -0
- package/dist/cli/commands/index.js +3 -0
- package/dist/cli/commands/tasks.js +977 -8
- package/dist/cli/identity-command.js +177 -0
- package/dist/cli/protocol-envelope.js +4 -0
- package/dist/cli/setup-wizard.js +71 -0
- package/dist/cli/update-commands.js +150 -24
- package/dist/cli-entry.js +12 -2
- package/dist/electron-build/agents/chain/explorer.js +59 -0
- package/dist/electron-build/agents/chain/explorer.js.map +1 -0
- package/dist/electron-build/agents/contacts/chain.js +26 -6
- package/dist/electron-build/agents/contacts/chain.js.map +1 -1
- package/dist/electron-build/agents/execution-supervisor.js +492 -34
- package/dist/electron-build/agents/execution-supervisor.js.map +1 -1
- package/dist/electron-build/agents/external-events.js +34 -5
- package/dist/electron-build/agents/external-events.js.map +1 -1
- package/dist/electron-build/agents/goal-criteria.js +10 -3
- package/dist/electron-build/agents/goal-criteria.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel/continuation-decision.js +598 -0
- package/dist/electron-build/agents/goal-flywheel/continuation-decision.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/goal-change.js +773 -0
- package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/memory-layers.js +269 -0
- package/dist/electron-build/agents/goal-flywheel/memory-layers.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/run-closure.js +818 -0
- package/dist/electron-build/agents/goal-flywheel/run-closure.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/skill-candidate.js +642 -0
- package/dist/electron-build/agents/goal-flywheel/skill-candidate.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/types.js +333 -0
- package/dist/electron-build/agents/goal-flywheel/types.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/change.js +294 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/change.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/closure.js +742 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/closure.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js +444 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/contract.js +279 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/contract.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/index.js +111 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/index.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js +264 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +612 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/work-contract.js +488 -0
- package/dist/electron-build/agents/goal-flywheel/work-contract.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/work-monitor.js +501 -0
- package/dist/electron-build/agents/goal-flywheel/work-monitor.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel-wiring.js +1962 -0
- package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -0
- package/dist/electron-build/agents/goal-state-reducer.js +414 -0
- package/dist/electron-build/agents/goal-state-reducer.js.map +1 -0
- package/dist/electron-build/agents/goal-store.js +35 -1
- package/dist/electron-build/agents/goal-store.js.map +1 -1
- package/dist/electron-build/agents/local-signer.js +101 -0
- package/dist/electron-build/agents/local-signer.js.map +1 -0
- package/dist/electron-build/agents/network-pulse.js +531 -28
- package/dist/electron-build/agents/network-pulse.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +74 -19
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/run-store.js +20 -5
- package/dist/electron-build/agents/run-store.js.map +1 -1
- package/dist/electron-build/agents/skill-readiness.js +52 -9
- package/dist/electron-build/agents/skill-readiness.js.map +1 -1
- package/dist/electron-build/agents/task/task-runner.js +35 -5
- package/dist/electron-build/agents/task/task-runner.js.map +1 -1
- package/dist/electron-build/agents/task-board.js +761 -0
- package/dist/electron-build/agents/task-board.js.map +1 -0
- package/dist/electron-build/agents/task-contract.js +382 -0
- package/dist/electron-build/agents/task-contract.js.map +1 -0
- package/dist/electron-build/orbitdb/cid-database.js +83 -17
- package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
- package/dist/electron-build/orbitdb/ipfs-node.js +31 -2
- package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
- package/dist/electron-build/utils/dual-source.js +471 -0
- package/dist/electron-build/utils/dual-source.js.map +1 -0
- package/dist/electron-build/utils/update-manager.js +331 -57
- package/dist/electron-build/utils/update-manager.js.map +1 -1
- package/dist/electron-build/utils/update-state.js +16 -1
- package/dist/electron-build/utils/update-state.js.map +1 -1
- package/dist/electron-build/utils/version-info.js +82 -2
- package/dist/electron-build/utils/version-info.js.map +1 -1
- package/dist/index.js +103 -12
- package/dist/ios/chain-explorer.js +617 -0
- package/dist/ios/explorer.html +163 -0
- package/dist/ios/index.html +96 -0
- package/dist/ios/mobile-agent.js +2 -2
- package/dist/ios/mobile-contacts.js +329 -0
- package/dist/ios/mobile-core.js +1379 -226
- package/dist/ios/mobile-privacy.js +185 -0
- package/dist/ios/mobile.css +19 -0
- package/dist/ios/mobile.html +96 -0
- package/dist/ios/mobile.js +706 -7
- package/dist/ios/routes-chain-index.js +108 -0
- package/dist/ios/routes-contacts.js +420 -0
- package/dist/ios/routes-onchain-trade.js +99 -0
- package/dist/ios/routes-x402-info.js +1 -0
- package/dist/ios/server.js +762 -0
- package/dist/ios/task-frame-server.js +211 -0
- package/dist/orbitdb/cid-database.js +81 -16
- package/dist/orbitdb/ipfs-node.js +31 -2
- package/dist/utils/dual-source.js +426 -0
- package/dist/utils/update-cli.js +16 -21
- package/dist/utils/update-health.js +35 -5
- package/dist/utils/update-manager.js +329 -57
- package/dist/utils/update-state.js +16 -1
- package/dist/utils/version-info.js +75 -1
- package/dist/web/mobile-contacts.js +44 -11
- package/dist/web/mobile-core.js +1197 -513
- package/dist/web/mobile-tasks.js +339 -0
- package/dist/web/mobile.css +19 -0
- package/dist/web/mobile.html +64 -0
- package/dist/web/mobile.js +531 -1
- package/dist/web/routes-mobile-tasks.js +353 -0
- package/dist/web/server.js +163 -4
- package/package.json +3 -1
package/dist/web/mobile.js
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
});
|
|
70
70
|
} catch (e) {}
|
|
71
71
|
|
|
72
|
-
const TITLES = { main: '首页', friends: '好友', network: '网络', me: '我' };
|
|
72
|
+
const TITLES = { main: '首页', friends: '好友', network: '网络', tasks: '任务', me: '我' };
|
|
73
73
|
let currentTab = 'main';
|
|
74
74
|
function switchTab(tab) {
|
|
75
75
|
currentTab = tab;
|
|
@@ -3170,3 +3170,533 @@
|
|
|
3170
3170
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindEvents);
|
|
3171
3171
|
else bindEvents();
|
|
3172
3172
|
})();
|
|
3173
|
+
|
|
3174
|
+
// ============ 任务协作: 入群 · 发公告 · 看飞轮进度 (2026-09-25) ============
|
|
3175
|
+
// 独立 IIFE (与联系方式面板同款): 只用 window.BolloonCore.tasks (编译进 mobile-core.js)。
|
|
3176
|
+
// 分工**不变**: 手机 = 输入 · 展示待发内容 · 设备签名; 桌面 = 落盘 · 执行 · 等待回复 · 证据。
|
|
3177
|
+
// 纪律:
|
|
3178
|
+
// · 动态文本一律 textContent (本文件这一段**不用 innerHTML** 拼任何数据)
|
|
3179
|
+
// · 渲染只取白名单字段 (视图对象里多出来的字段进不了 DOM)
|
|
3180
|
+
// · 双语走 data-zh/data-en (切语言只重写文字节点, 不重建结构)
|
|
3181
|
+
// · 相对时间只更新 .rel-time 文字节点 (不重渲染整页)
|
|
3182
|
+
(function () {
|
|
3183
|
+
const $ = (sel) => document.querySelector(sel);
|
|
3184
|
+
const T = () => (window.BolloonCore && window.BolloonCore.tasks) || null;
|
|
3185
|
+
|
|
3186
|
+
// ── 语言 (data-zh/data-en; 默认中文, 显式切 en 才英文) ──────────────────────
|
|
3187
|
+
function lang() {
|
|
3188
|
+
try { return localStorage.getItem('bolloon_lang') === 'en' ? 'en' : 'zh'; } catch (e) { return 'zh'; }
|
|
3189
|
+
}
|
|
3190
|
+
function bilingual(el, zh, en) {
|
|
3191
|
+
if (!el) return el;
|
|
3192
|
+
el.setAttribute('data-zh', String(zh));
|
|
3193
|
+
el.setAttribute('data-en', String(en));
|
|
3194
|
+
el.textContent = lang() === 'en' ? String(en) : String(zh);
|
|
3195
|
+
return el;
|
|
3196
|
+
}
|
|
3197
|
+
function applyLang(root) {
|
|
3198
|
+
const scope = root || document;
|
|
3199
|
+
scope.querySelectorAll('[data-zh][data-en]').forEach((el) => {
|
|
3200
|
+
el.textContent = lang() === 'en' ? el.getAttribute('data-en') : el.getAttribute('data-zh');
|
|
3201
|
+
});
|
|
3202
|
+
}
|
|
3203
|
+
window.__mobileLang = {
|
|
3204
|
+
get: lang,
|
|
3205
|
+
set(v) {
|
|
3206
|
+
try { localStorage.setItem('bolloon_lang', v === 'en' ? 'en' : 'zh'); } catch (e) {}
|
|
3207
|
+
applyLang();
|
|
3208
|
+
// 列表里的空态/描述是用 lang() 现拼的 → 切完语言重渲染一次, 免得留下上一语言的旧句子
|
|
3209
|
+
try {
|
|
3210
|
+
if (window.__mobileTasksUi && typeof window.__mobileTasksUi.refresh === 'function') window.__mobileTasksUi.refresh();
|
|
3211
|
+
} catch (e) {}
|
|
3212
|
+
},
|
|
3213
|
+
};
|
|
3214
|
+
|
|
3215
|
+
// ── DOM 小工具 (全部 textContent) ─────────────────────────────────────────
|
|
3216
|
+
function mk(tag, cls) { const e = document.createElement(tag); if (cls) e.className = cls; return e; }
|
|
3217
|
+
function line(cls, text) { const e = mk('div', cls || 'sheet-text'); if (text !== undefined && text !== null) e.textContent = String(text); return e; }
|
|
3218
|
+
function row(text, sub) {
|
|
3219
|
+
const e = mk('div', 'list-item');
|
|
3220
|
+
const box = mk('div');
|
|
3221
|
+
box.style.flex = '1';
|
|
3222
|
+
box.appendChild(line('', text));
|
|
3223
|
+
if (sub !== undefined && sub !== null && String(sub) !== '') {
|
|
3224
|
+
const s = line('', sub);
|
|
3225
|
+
s.style.fontSize = '12px';
|
|
3226
|
+
s.style.color = 'var(--text-secondary)';
|
|
3227
|
+
box.appendChild(s);
|
|
3228
|
+
}
|
|
3229
|
+
e.appendChild(box);
|
|
3230
|
+
return e;
|
|
3231
|
+
}
|
|
3232
|
+
/** 相对时间节点: 只改这一个文字节点 (data-at 记住基准) */
|
|
3233
|
+
function relNode(atMs) {
|
|
3234
|
+
const s = mk('span', 'rel-time');
|
|
3235
|
+
s.setAttribute('data-at', String(Number(atMs) || 0));
|
|
3236
|
+
s.textContent = relText(Number(atMs) || 0);
|
|
3237
|
+
return s;
|
|
3238
|
+
}
|
|
3239
|
+
/** 相对时间节点 (输入是**毫秒差**, 例如 deadlineInMs / resumeInMs) —— 每 30s 只改这个文字节点 */
|
|
3240
|
+
function relDeltaNode(deltaMs) {
|
|
3241
|
+
const s = mk('span', 'rel-time');
|
|
3242
|
+
s.setAttribute('data-delta', String(Number(deltaMs) || 0));
|
|
3243
|
+
s.textContent = relDeltaText(Number(deltaMs) || 0);
|
|
3244
|
+
return s;
|
|
3245
|
+
}
|
|
3246
|
+
function relDeltaText(deltaMs) {
|
|
3247
|
+
const t = T();
|
|
3248
|
+
if (!t || !t.relative) return '';
|
|
3249
|
+
return t.relative(Number(deltaMs) || 0, lang()) || '';
|
|
3250
|
+
}
|
|
3251
|
+
function relText(atMs) {
|
|
3252
|
+
const t = T();
|
|
3253
|
+
if (!atMs) return '';
|
|
3254
|
+
if (t && t.relative) return t.relative(Date.now() - Number(atMs), lang()) || '';
|
|
3255
|
+
return '';
|
|
3256
|
+
}
|
|
3257
|
+
function shorten(v, n) { const t = T(); return t && t.shorten ? t.shorten(v, n) : String(v == null ? '' : v).slice(0, n || 12); }
|
|
3258
|
+
/** 最后一道自检: 命中红线就不显示原文 (桌面已经挡过, 这里只兜底) */
|
|
3259
|
+
function safeText(v) {
|
|
3260
|
+
const s = String(v == null ? '' : v);
|
|
3261
|
+
const t = T();
|
|
3262
|
+
if (t && t.scanText) {
|
|
3263
|
+
const hits = t.scanText(s);
|
|
3264
|
+
if (hits && hits.length) return '[' + (lang() === 'en' ? 'redacted:' : '已遮蔽:') + hits[0].rule + ']';
|
|
3265
|
+
}
|
|
3266
|
+
return s;
|
|
3267
|
+
}
|
|
3268
|
+
function setText(sel, text) { const el = $(sel); if (el) el.textContent = text == null ? '' : String(text); }
|
|
3269
|
+
/** 动态状态行: 带上 data-zh/data-en, 这样切语言时它也换 (applyLang 只管文字节点) */
|
|
3270
|
+
function dynText(sel, zh, en) {
|
|
3271
|
+
const el = $(sel);
|
|
3272
|
+
if (!el) return;
|
|
3273
|
+
el.setAttribute('data-zh', String(zh));
|
|
3274
|
+
el.setAttribute('data-en', String(en));
|
|
3275
|
+
el.textContent = lang() === 'en' ? String(en) : String(zh);
|
|
3276
|
+
}
|
|
3277
|
+
function clear(node) { if (node) while (node.firstChild) node.removeChild(node.firstChild); }
|
|
3278
|
+
function openSheet() { const el = $('#task-confirm-sheet'); if (el) el.hidden = false; }
|
|
3279
|
+
function closeSheet() { const el = $('#task-confirm-sheet'); if (el) el.hidden = true; }
|
|
3280
|
+
function toast(msg) {
|
|
3281
|
+
if (window.alert) window.alert(String(msg));
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
// ── 状态 (完整 id 只留在这段 JS 内存里, 不进 DOM 文本) ─────────────────────
|
|
3285
|
+
const state = { groups: [], groupsView: [], board: null, flywheel: null, trail: null, selectedGroupRef: '', pending: null };
|
|
3286
|
+
|
|
3287
|
+
// ── 渲染: 群 ──────────────────────────────────────────────────────────────
|
|
3288
|
+
function renderGroups() {
|
|
3289
|
+
const list = $('#tasks-groups');
|
|
3290
|
+
if (!list) return;
|
|
3291
|
+
clear(list);
|
|
3292
|
+
if (!state.groups.length) {
|
|
3293
|
+
list.appendChild(line('sheet-text', lang() === 'en' ? 'No groups joined yet' : '还没有加入任何群'));
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
state.groups.forEach((g, i) => {
|
|
3297
|
+
const view = state.groupsView[i] || {};
|
|
3298
|
+
const r = row(String(view.name || '') + ' · ' + String(view.idShort || ''), '');
|
|
3299
|
+
const when = relNode(Date.parse(String(view.joinedAt || '')) || 0);
|
|
3300
|
+
const sub = line('', '');
|
|
3301
|
+
sub.style.fontSize = '12px';
|
|
3302
|
+
sub.style.color = 'var(--text-secondary)';
|
|
3303
|
+
sub.appendChild(document.createTextNode(lang() === 'en' ? 'joined ' : '加入于 '));
|
|
3304
|
+
sub.appendChild(when);
|
|
3305
|
+
r.firstChild.appendChild(sub);
|
|
3306
|
+
const b = mk('button', 'sheet-choice');
|
|
3307
|
+
b.style.flex = '0 0 auto';
|
|
3308
|
+
bilingual(b, '退群', 'Leave');
|
|
3309
|
+
b.setAttribute('data-group-leave', String(i));
|
|
3310
|
+
b.addEventListener('click', () => askLeave(i));
|
|
3311
|
+
r.appendChild(b);
|
|
3312
|
+
list.appendChild(r);
|
|
3313
|
+
});
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
// ── 渲染: 公告板 ──────────────────────────────────────────────────────────
|
|
3317
|
+
function renderBoard() {
|
|
3318
|
+
const list = $('#tasks-board');
|
|
3319
|
+
if (!list) return;
|
|
3320
|
+
clear(list);
|
|
3321
|
+
const d = state.board || {};
|
|
3322
|
+
const items = d.items || [];
|
|
3323
|
+
if (!items.length) {
|
|
3324
|
+
list.appendChild(line('sheet-text', lang() === 'en' ? 'Board is empty' : '板上暂时没有公告'));
|
|
3325
|
+
}
|
|
3326
|
+
items.forEach((it, i) => {
|
|
3327
|
+
const r = row(
|
|
3328
|
+
String(it.capability || '') + ' · ' + String(it.idShort || '') + ' · ' + String((it.statusLabel || {})[lang()] || ''),
|
|
3329
|
+
'',
|
|
3330
|
+
);
|
|
3331
|
+
const sub = line('', '');
|
|
3332
|
+
sub.style.fontSize = '12px';
|
|
3333
|
+
sub.style.color = 'var(--text-secondary)';
|
|
3334
|
+
sub.appendChild(document.createTextNode((lang() === 'en' ? 'budget ' : '预算 ') + safeText(it.budgetLabel || '—') + ' · '));
|
|
3335
|
+
sub.appendChild(document.createTextNode(lang() === 'en' ? 'closes ' : '截止 '));
|
|
3336
|
+
sub.appendChild(relDeltaNode(it.deadlineInMs));
|
|
3337
|
+
r.firstChild.appendChild(sub);
|
|
3338
|
+
if (it.claimable) {
|
|
3339
|
+
const b = mk('button', 'sheet-choice');
|
|
3340
|
+
b.style.flex = '0 0 auto';
|
|
3341
|
+
bilingual(b, '发进群', 'Post to group');
|
|
3342
|
+
b.setAttribute('data-announce', String(i));
|
|
3343
|
+
b.addEventListener('click', () => askAnnounceToGroup(i));
|
|
3344
|
+
r.appendChild(b);
|
|
3345
|
+
}
|
|
3346
|
+
list.appendChild(r);
|
|
3347
|
+
});
|
|
3348
|
+
const notes = Array.isArray(d.notes) ? d.notes : [];
|
|
3349
|
+
if (notes.length) list.appendChild(line('sheet-text', notes.join(' · ')));
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
// ── 渲染: 飞轮进度 (只读; 状态来自 goal-flywheel 的五类用户可见状态) ────────
|
|
3353
|
+
function renderFlywheel() {
|
|
3354
|
+
const list = $('#tasks-flywheel');
|
|
3355
|
+
if (!list) return;
|
|
3356
|
+
clear(list);
|
|
3357
|
+
const goals = (state.flywheel && state.flywheel.goals) || [];
|
|
3358
|
+
if (!goals.length) {
|
|
3359
|
+
list.appendChild(line('sheet-text', lang() === 'en' ? 'No goals yet' : '还没有目标'));
|
|
3360
|
+
const hint = state.flywheel && state.flywheel.hint;
|
|
3361
|
+
if (hint) list.appendChild(line('sheet-text', hint));
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
const zh = lang() === 'zh';
|
|
3365
|
+
goals.forEach((g) => {
|
|
3366
|
+
const v = g.view || {};
|
|
3367
|
+
const r = row(String(g.objectiveShort || '') + ' · ' + String((v.stateLabel || {})[lang()] || g.status || ''), '');
|
|
3368
|
+
const box = r.firstChild;
|
|
3369
|
+
const rep = v.report || {};
|
|
3370
|
+
if (rep.nextStep) box.appendChild(line('sheet-text', (zh ? '下一步: ' : 'next step: ') + safeText(rep.nextStep)));
|
|
3371
|
+
if (rep.conclusion) box.appendChild(line('sheet-text', (zh ? '结论: ' : 'conclusion: ') + safeText(rep.conclusion)));
|
|
3372
|
+
if (Array.isArray(rep.remaining) && rep.remaining.length) {
|
|
3373
|
+
box.appendChild(line('sheet-text', (zh ? '还剩: ' : 'remaining: ') + rep.remaining.map(safeText).join(' · ')));
|
|
3374
|
+
}
|
|
3375
|
+
if (Array.isArray(rep.blockReasons) && rep.blockReasons.length) {
|
|
3376
|
+
box.appendChild(line('sheet-text', (zh ? '卡住原因: ' : 'blocked by: ') + rep.blockReasons.map(safeText).join(' · ')));
|
|
3377
|
+
}
|
|
3378
|
+
const sub = line('', '');
|
|
3379
|
+
sub.style.fontSize = '12px';
|
|
3380
|
+
sub.style.color = 'var(--text-secondary)';
|
|
3381
|
+
if (v.resumeInMs !== null && v.resumeInMs !== undefined) {
|
|
3382
|
+
sub.appendChild(document.createTextNode(zh ? '下次醒来 ' : 'wakes '));
|
|
3383
|
+
sub.appendChild(relDeltaNode(v.resumeInMs));
|
|
3384
|
+
}
|
|
3385
|
+
sub.appendChild(document.createTextNode((zh ? ' · 会继续: ' : ' · will continue: ') + (rep.willContinue ? (zh ? '是' : 'yes') : (zh ? '否' : 'no'))));
|
|
3386
|
+
box.appendChild(sub);
|
|
3387
|
+
if (v.requiredAgent) box.appendChild(line('sheet-text', safeText(v.requiredAgent)));
|
|
3388
|
+
if (v.riskLevel) box.appendChild(line('sheet-text', (zh ? '风险: ' : 'risk: ') + safeText(v.riskLevel)));
|
|
3389
|
+
const blocks = Array.isArray(v.blocks) ? v.blocks : [];
|
|
3390
|
+
blocks.forEach((b) => {
|
|
3391
|
+
const label = [(b.kindLabel || {})[lang()], (b.ownerLabel || {})[lang()], (b.actionLabel || {})[lang()]].filter(Boolean).join(' · ');
|
|
3392
|
+
const bl = line('sheet-text', safeText(label) + (b.note ? ' — ' + safeText(b.note) : ''));
|
|
3393
|
+
box.appendChild(bl);
|
|
3394
|
+
const bd = line('', '');
|
|
3395
|
+
bd.style.fontSize = '12px';
|
|
3396
|
+
bd.style.color = 'var(--text-secondary)';
|
|
3397
|
+
bd.appendChild(document.createTextNode(zh ? '卡了 ' : 'blocked for '));
|
|
3398
|
+
bd.appendChild(relDeltaNode(b.blockedForMs));
|
|
3399
|
+
box.appendChild(bd);
|
|
3400
|
+
});
|
|
3401
|
+
list.appendChild(r);
|
|
3402
|
+
});
|
|
3403
|
+
const note = state.flywheel && state.flywheel.note;
|
|
3404
|
+
if (note) list.appendChild(line('sheet-text', note));
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// ── 渲染: 群痕迹 (只读) ───────────────────────────────────────────────────
|
|
3408
|
+
function renderTrail() {
|
|
3409
|
+
const list = $('#tasks-trail');
|
|
3410
|
+
if (!list) return;
|
|
3411
|
+
clear(list);
|
|
3412
|
+
const t = state.trail;
|
|
3413
|
+
if (!t) { list.appendChild(line('sheet-text', lang() === 'en' ? 'Pick a group first' : '先选一个群')); return; }
|
|
3414
|
+
if (!t.entries.length) {
|
|
3415
|
+
list.appendChild(line('sheet-text', lang() === 'en' ? 'No trail entries for this round' : '这一期还没有过程痕迹'));
|
|
3416
|
+
return;
|
|
3417
|
+
}
|
|
3418
|
+
t.entries.forEach((e) => {
|
|
3419
|
+
const label = (e.kindLabel || {})[lang()] || e.kind;
|
|
3420
|
+
const r = row(String(label) + ' · ' + String(e.sender || ''), '');
|
|
3421
|
+
const box = r.firstChild;
|
|
3422
|
+
const sub = line('', '');
|
|
3423
|
+
sub.style.fontSize = '12px';
|
|
3424
|
+
sub.style.color = 'var(--text-secondary)';
|
|
3425
|
+
sub.appendChild(relNode(e.atMs || 0));
|
|
3426
|
+
box.appendChild(sub);
|
|
3427
|
+
(e.facts || []).forEach((f) => box.appendChild(line('sheet-text', safeText(f.k) + '=' + safeText(f.v))));
|
|
3428
|
+
list.appendChild(r);
|
|
3429
|
+
});
|
|
3430
|
+
if (t.privacyHits) list.appendChild(line('sheet-text', lang() === 'en' ? '⚠ some lines hit the privacy rules and were redacted' : '⚠ 有消息命中隐私红线, 已遮蔽'));
|
|
3431
|
+
if (t.ignoredMessages) list.appendChild(line('sheet-text', (lang() === 'en' ? 'ignored messages: ' : '忽略消息: ') + String(t.ignoredMessages)));
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
// ── 确认页 (展示待发内容 → 确认 → 签名) ───────────────────────────────────
|
|
3435
|
+
function renderConfirm() {
|
|
3436
|
+
const t = T();
|
|
3437
|
+
const p = state.pending;
|
|
3438
|
+
if (!t || !p) return;
|
|
3439
|
+
const d = t.describeConfirm(p.kind, p.req) || {};
|
|
3440
|
+
const title = $('#task-confirm-title');
|
|
3441
|
+
if (title) bilingual(title, d.titleZh || '确认这个动作', d.titleEn || 'Confirm this action');
|
|
3442
|
+
const lines = $('#task-confirm-lines');
|
|
3443
|
+
if (lines) {
|
|
3444
|
+
clear(lines);
|
|
3445
|
+
(d.lines || []).forEach((l) => lines.appendChild(line('sheet-text', (lang() === 'en' ? l.kEn : l.k) + ': ' + safeText(l.v))));
|
|
3446
|
+
}
|
|
3447
|
+
setText('#task-confirm-what', lang() === 'en'
|
|
3448
|
+
? 'This will be signed with this device key. The desktop verifies that signature before doing anything.'
|
|
3449
|
+
: '这个动作会用本机设备密钥签名; 桌面验签通过后才会真正执行。');
|
|
3450
|
+
// 群选择 (群消息类动作必须显式选群)
|
|
3451
|
+
const wrap = $('#task-confirm-group-wrap');
|
|
3452
|
+
const box = $('#task-confirm-group');
|
|
3453
|
+
const needsGroup = p.needsGroup === true;
|
|
3454
|
+
if (wrap) wrap.hidden = !needsGroup;
|
|
3455
|
+
if (box && needsGroup) {
|
|
3456
|
+
clear(box);
|
|
3457
|
+
state.groups.forEach((g, i) => {
|
|
3458
|
+
const view = state.groupsView[i] || {};
|
|
3459
|
+
const b = mk('button', 'sheet-choice');
|
|
3460
|
+
bilingual(b, String(view.name || ''), String(view.name || ''));
|
|
3461
|
+
b.setAttribute('data-pick-group', String(i));
|
|
3462
|
+
if (p.req.groupRef === g.id) b.style.borderColor = 'var(--accent)';
|
|
3463
|
+
b.addEventListener('click', () => { p.req.groupRef = g.id; state.selectedGroupRef = g.id; renderConfirm(); refreshPreview(); });
|
|
3464
|
+
box.appendChild(b);
|
|
3465
|
+
});
|
|
3466
|
+
if (!state.groups.length) box.appendChild(line('sheet-text', lang() === 'en' ? 'no groups' : '还没有群'));
|
|
3467
|
+
}
|
|
3468
|
+
// 群消息类: 预览按钮 + 原文区
|
|
3469
|
+
const pv = $('#task-confirm-preview');
|
|
3470
|
+
if (pv) pv.hidden = !p.showPreview;
|
|
3471
|
+
const ct = $('#task-confirm-content-title');
|
|
3472
|
+
const cc = $('#task-confirm-content');
|
|
3473
|
+
if (ct) ct.hidden = !p.previewText;
|
|
3474
|
+
if (cc) { cc.hidden = !p.previewText; cc.textContent = p.previewText || ''; }
|
|
3475
|
+
const yes = $('#task-confirm-yes');
|
|
3476
|
+
if (yes) {
|
|
3477
|
+
const disabled = needsGroup && !p.req.groupRef;
|
|
3478
|
+
yes.disabled = !!disabled;
|
|
3479
|
+
yes.style.opacity = disabled ? '0.5' : '';
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
async function refreshPreview() {
|
|
3484
|
+
const t = T();
|
|
3485
|
+
const p = state.pending;
|
|
3486
|
+
if (!t || !p || !p.showPreview) return;
|
|
3487
|
+
if (!p.req.groupRef) { p.previewText = ''; renderConfirm(); return; }
|
|
3488
|
+
const r = await t.previewGroupMessage(p.req);
|
|
3489
|
+
if (r && r.ok) { p.previewText = (r.data && r.data.text) || ''; setText('#tasks-hint', ''); }
|
|
3490
|
+
else { p.previewText = ''; setText('#tasks-hint', (lang() === 'en' ? 'preview failed: ' : '预览失败: ') + String((r && r.code) || '') + ' ' + String((r && r.error) || '')); }
|
|
3491
|
+
renderConfirm();
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
function askConfirm(kind, req, opts) {
|
|
3495
|
+
state.pending = Object.assign({ kind: kind, req: req, showPreview: false, previewText: '' }, opts || {});
|
|
3496
|
+
renderConfirm();
|
|
3497
|
+
openSheet();
|
|
3498
|
+
if (state.pending.showPreview) refreshPreview();
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
function requireSigning() {
|
|
3502
|
+
const t = T();
|
|
3503
|
+
if (!t) { toast(lang() === 'en' ? 'phone kernel not ready' : '手机内核未就绪 (mobile-core 没加载)'); return null; }
|
|
3504
|
+
if (!t.deviceSigning || !t.deviceSigning()) {
|
|
3505
|
+
toast(lang() === 'en'
|
|
3506
|
+
? 'this WebView cannot do Ed25519 device signing — run it from the desktop instead'
|
|
3507
|
+
: '本机 WebView 不支持 Ed25519 设备签名 —— 这个动作请在桌面端执行 (不降级, 不假装签了)');
|
|
3508
|
+
return null;
|
|
3509
|
+
}
|
|
3510
|
+
return t;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
// ── 四个能力的入口 ────────────────────────────────────────────────────────
|
|
3514
|
+
function askJoin() {
|
|
3515
|
+
const link = (($('#tasks-group-link') || {}).value || '').trim();
|
|
3516
|
+
if (!link) { toast(lang() === 'en' ? 'paste the group invite link first' : '先粘贴群邀请链接'); return; }
|
|
3517
|
+
if (!requireSigning()) return;
|
|
3518
|
+
const req = T().emptyRequest('group_join');
|
|
3519
|
+
req.groupRef = link;
|
|
3520
|
+
askConfirm('group_join', req, {});
|
|
3521
|
+
}
|
|
3522
|
+
function askCreate() {
|
|
3523
|
+
const name = (($('#tasks-group-name') || {}).value || '').trim();
|
|
3524
|
+
if (!name) { toast(lang() === 'en' ? 'give the group a name first' : '先给群起个名字'); return; }
|
|
3525
|
+
if (!requireSigning()) return;
|
|
3526
|
+
const req = T().emptyRequest('group_create');
|
|
3527
|
+
req.groupRef = name;
|
|
3528
|
+
askConfirm('group_create', req, {});
|
|
3529
|
+
}
|
|
3530
|
+
function askLeave(i) {
|
|
3531
|
+
if (!requireSigning()) return;
|
|
3532
|
+
const g = state.groups[i];
|
|
3533
|
+
if (!g) return;
|
|
3534
|
+
const req = T().emptyRequest('group_leave');
|
|
3535
|
+
req.groupRef = g.id;
|
|
3536
|
+
askConfirm('group_leave', req, {});
|
|
3537
|
+
}
|
|
3538
|
+
function askPublish() {
|
|
3539
|
+
if (!requireSigning()) return;
|
|
3540
|
+
const cap = (($('#tasks-pub-capability') || {}).value || '').trim();
|
|
3541
|
+
const ins = (($('#tasks-pub-instruction') || {}).value || '').trim();
|
|
3542
|
+
const bud = (($('#tasks-pub-budget') || {}).value || '').trim();
|
|
3543
|
+
const dl = (($('#tasks-pub-deadline') || {}).value || '').trim();
|
|
3544
|
+
if (!cap || !ins || !bud) { toast(lang() === 'en' ? 'capability / instruction / budget are required' : '能力 / 任务正文 / 预算 都要填'); return; }
|
|
3545
|
+
const req = T().emptyRequest('announce_publish');
|
|
3546
|
+
req.capability = cap; req.instruction = ins; req.budgetHuman = bud; req.currency = 'USDC';
|
|
3547
|
+
if (dl) req.deadline = dl;
|
|
3548
|
+
askConfirm('announce_publish', req, {});
|
|
3549
|
+
}
|
|
3550
|
+
function askAnnounceToGroup(i) {
|
|
3551
|
+
if (!requireSigning()) return;
|
|
3552
|
+
const it = ((state.board || {}).items || [])[i];
|
|
3553
|
+
if (!it) return;
|
|
3554
|
+
const req = T().emptyRequest('announce_to_group');
|
|
3555
|
+
req.announcementId = it.ref || '';
|
|
3556
|
+
req.groupRef = state.selectedGroupRef || (state.groups[0] && state.groups[0].id) || '';
|
|
3557
|
+
if (!req.announcementId) { toast(lang() === 'en' ? 'desktop did not give a usable announcement id' : '桌面没给出可用的公告号, 先刷新') ; return; }
|
|
3558
|
+
askConfirm('announce_to_group', req, { needsGroup: true, showPreview: true });
|
|
3559
|
+
}
|
|
3560
|
+
function askTrail(kind) {
|
|
3561
|
+
if (!requireSigning()) return;
|
|
3562
|
+
const req = T().emptyRequest('trail_post');
|
|
3563
|
+
req.groupRef = state.selectedGroupRef || (state.groups[0] && state.groups[0].id) || '';
|
|
3564
|
+
req.trailKind = kind;
|
|
3565
|
+
if (!req.groupRef) { toast(lang() === 'en' ? 'pick a group first' : '先选一个群'); return; }
|
|
3566
|
+
askConfirm('trail_post', req, { needsGroup: true, showPreview: true });
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
async function doConfirmed() {
|
|
3570
|
+
const t = T();
|
|
3571
|
+
const p = state.pending;
|
|
3572
|
+
if (!t || !p) return;
|
|
3573
|
+
const yes = $('#task-confirm-yes');
|
|
3574
|
+
if (yes) yes.disabled = true;
|
|
3575
|
+
const r = await t.execute(p.req);
|
|
3576
|
+
if (yes) yes.disabled = false;
|
|
3577
|
+
closeSheet();
|
|
3578
|
+
if (r && r.ok) {
|
|
3579
|
+
const text = (r.data && r.data.text) || (lang() === 'en' ? 'done' : '已执行');
|
|
3580
|
+
t.setHint ? t.setHint(safeText(text)) : null;
|
|
3581
|
+
toast(safeText(text));
|
|
3582
|
+
} else {
|
|
3583
|
+
const code = String((r && r.code) || 'unknown');
|
|
3584
|
+
const err = String((r && r.error) || '');
|
|
3585
|
+
t.setHint ? t.setHint((lang() === 'en' ? 'failed: ' : '失败: ') + code) : null;
|
|
3586
|
+
toast((lang() === 'en' ? 'not executed: ' : '没有执行: ') + code + (err ? '\n' + safeText(err) : ''));
|
|
3587
|
+
}
|
|
3588
|
+
state.pending = null;
|
|
3589
|
+
state.selectedGroupRef = '';
|
|
3590
|
+
await refreshAll();
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
// ── 刷新 ──────────────────────────────────────────────────────────────────
|
|
3594
|
+
async function refreshAll() {
|
|
3595
|
+
const t = T();
|
|
3596
|
+
if (!t) {
|
|
3597
|
+
dynText('#tasks-signing', '手机内核未就绪 (mobile-core 没加载)', 'phone kernel not ready');
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
const canSign = !!t.deviceSigning();
|
|
3601
|
+
const signingEl = $('#tasks-signing');
|
|
3602
|
+
if (signingEl) signingEl.setAttribute('data-signing', canSign ? 'yes' : 'no');
|
|
3603
|
+
setText('#tasks-hint', t.hint ? t.hint() : '');
|
|
3604
|
+
dynText('#tasks-signing', canSign
|
|
3605
|
+
? '本机可做设备签名 (Ed25519) —— 高风险动作必须由它签名'
|
|
3606
|
+
: '⚠ 本机 WebView 不支持签名 —— 高风险动作请在桌面端执行',
|
|
3607
|
+
canSign
|
|
3608
|
+
? 'this device can sign (Ed25519) — risky actions need it'
|
|
3609
|
+
: '⚠ this WebView cannot sign — run risky actions on the desktop');
|
|
3610
|
+
|
|
3611
|
+
const g = await t.listGroups();
|
|
3612
|
+
state.groups = (g && g.ok && Array.isArray(g.data)) ? g.data.map((x) => ({ id: x.id, name: x.name })) : [];
|
|
3613
|
+
state.groups.some((x) => x.id === state.selectedGroupRef) || (state.selectedGroupRef = state.groups[0] ? state.groups[0].id : '');
|
|
3614
|
+
state.groupsView = (g && g.ok && Array.isArray(g.data)) ? g.data : [];
|
|
3615
|
+
renderGroups();
|
|
3616
|
+
renderTrailGroupPicker();
|
|
3617
|
+
|
|
3618
|
+
const b = await t.loadBoard();
|
|
3619
|
+
state.board = b && b.ok ? b.data : {};
|
|
3620
|
+
renderBoard();
|
|
3621
|
+
|
|
3622
|
+
const f = await t.loadFlywheel();
|
|
3623
|
+
state.flywheel = f && f.ok ? f.data : {};
|
|
3624
|
+
renderFlywheel();
|
|
3625
|
+
|
|
3626
|
+
if (state.selectedGroupRef) await loadTrailNow();
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
function renderTrailGroupPicker() {
|
|
3630
|
+
const list = $('#tasks-trail-group-picker');
|
|
3631
|
+
if (!list) return;
|
|
3632
|
+
clear(list);
|
|
3633
|
+
if (!state.groups.length) { list.appendChild(line('sheet-text', lang() === 'en' ? 'join a group to see its trail' : '入群后可以看群里这一期的痕迹')); return; }
|
|
3634
|
+
state.groups.forEach((g, i) => {
|
|
3635
|
+
const view = state.groupsView[i] || {};
|
|
3636
|
+
const b = mk('button', 'sheet-choice');
|
|
3637
|
+
bilingual(b, String(view.name || ''), String(view.name || ''));
|
|
3638
|
+
b.setAttribute('data-pick-trail', String(i));
|
|
3639
|
+
if (g.id === state.selectedGroupRef) b.style.borderColor = 'var(--accent)';
|
|
3640
|
+
b.addEventListener('click', async () => {
|
|
3641
|
+
state.selectedGroupRef = g.id;
|
|
3642
|
+
renderTrailGroupPicker();
|
|
3643
|
+
await loadTrailNow();
|
|
3644
|
+
});
|
|
3645
|
+
list.appendChild(b);
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
async function loadTrailNow() {
|
|
3650
|
+
const t = T();
|
|
3651
|
+
if (!t || !state.selectedGroupRef) return;
|
|
3652
|
+
const r = await t.loadTrail({ groupRef: state.selectedGroupRef });
|
|
3653
|
+
state.trail = (r && r.ok) ? r.data : null;
|
|
3654
|
+
if (!r || !r.ok) setText('#tasks-hint', (lang() === 'en' ? 'trail read failed: ' : '读群痕迹失败: ') + String((r && r.code) || '') + ' ' + String((r && r.error) || ''));
|
|
3655
|
+
renderTrail();
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
function bindEvents() {
|
|
3659
|
+
const tab = document.querySelector('.tab[data-tab="tasks"]');
|
|
3660
|
+
if (tab) tab.addEventListener('click', () => { applyLang(); refreshAll(); });
|
|
3661
|
+
const r = $('#tasks-refresh');
|
|
3662
|
+
if (r) r.addEventListener('click', refreshAll);
|
|
3663
|
+
const j = $('#tasks-group-join');
|
|
3664
|
+
if (j) j.addEventListener('click', askJoin);
|
|
3665
|
+
const c = $('#tasks-group-create');
|
|
3666
|
+
if (c) c.addEventListener('click', askCreate);
|
|
3667
|
+
const p = $('#tasks-pub-submit');
|
|
3668
|
+
if (p) p.addEventListener('click', askPublish);
|
|
3669
|
+
const tr = $('#tasks-trail-refresh');
|
|
3670
|
+
if (tr) tr.addEventListener('click', loadTrailNow);
|
|
3671
|
+
const pv = $('#task-confirm-preview');
|
|
3672
|
+
if (pv) pv.addEventListener('click', refreshPreview);
|
|
3673
|
+
const yes = $('#task-confirm-yes');
|
|
3674
|
+
if (yes) yes.addEventListener('click', doConfirmed);
|
|
3675
|
+
const no = $('#task-confirm-no');
|
|
3676
|
+
if (no) no.addEventListener('click', () => { state.pending = null; closeSheet(); });
|
|
3677
|
+
|
|
3678
|
+
// 相对时间: 每 30s 只改 .rel-time 文字节点 (不重渲染, 不动结构)
|
|
3679
|
+
setInterval(() => {
|
|
3680
|
+
document.querySelectorAll('.rel-time[data-at]').forEach((el) => {
|
|
3681
|
+
const next = relText(Number(el.getAttribute('data-at')) || 0);
|
|
3682
|
+
if (next && el.textContent !== next) el.textContent = next;
|
|
3683
|
+
});
|
|
3684
|
+
document.querySelectorAll('.rel-time[data-delta]').forEach((el) => {
|
|
3685
|
+
const next = relDeltaText(Number(el.getAttribute('data-delta')) || 0);
|
|
3686
|
+
if (next && el.textContent !== next) el.textContent = next;
|
|
3687
|
+
});
|
|
3688
|
+
}, 30000);
|
|
3689
|
+
|
|
3690
|
+
applyLang();
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindEvents);
|
|
3694
|
+
else bindEvents();
|
|
3695
|
+
|
|
3696
|
+
// 供验收脚本驱动 (真 headless DOM 用): 只暴露行为入口, 不暴露数据
|
|
3697
|
+
window.__mobileTasksUi = {
|
|
3698
|
+
refresh: refreshAll, askJoin: askJoin, askCreate: askCreate, askPublish: askPublish,
|
|
3699
|
+
askTrail: askTrail, askAnnounceToGroup: askAnnounceToGroup, confirm: doConfirmed,
|
|
3700
|
+
preview: refreshPreview, lang: window.__mobileLang, state: state,
|
|
3701
|
+
};
|
|
3702
|
+
})();
|