agim-cli 1.1.0 → 1.1.2
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/CHANGELOG.md +190 -0
- package/README.md +7 -1
- package/README.zh-CN.md +7 -1
- package/dist/cli-ui/i18n.d.ts +4 -4
- package/dist/cli-ui/i18n.js +4 -4
- package/dist/cli-ui/i18n.js.map +1 -1
- package/dist/cli.js +265 -30
- package/dist/cli.js.map +1 -1
- package/dist/core/a2a.d.ts +36 -0
- package/dist/core/a2a.d.ts.map +1 -0
- package/dist/core/a2a.js +203 -0
- package/dist/core/a2a.js.map +1 -0
- package/dist/core/agent-base.d.ts +5 -2
- package/dist/core/agent-base.d.ts.map +1 -1
- package/dist/core/agent-base.js +68 -24
- package/dist/core/agent-base.js.map +1 -1
- package/dist/core/approval-bus.d.ts +81 -2
- package/dist/core/approval-bus.d.ts.map +1 -1
- package/dist/core/approval-bus.js +273 -36
- package/dist/core/approval-bus.js.map +1 -1
- package/dist/core/approval-router.d.ts.map +1 -1
- package/dist/core/approval-router.js +221 -89
- package/dist/core/approval-router.js.map +1 -1
- package/dist/core/commands/a2a.d.ts +3 -0
- package/dist/core/commands/a2a.d.ts.map +1 -0
- package/dist/core/commands/a2a.js +148 -0
- package/dist/core/commands/a2a.js.map +1 -0
- package/dist/core/commands/approval.d.ts.map +1 -1
- package/dist/core/commands/approval.js +7 -5
- package/dist/core/commands/approval.js.map +1 -1
- package/dist/core/commands/builtin.js +2 -2
- package/dist/core/commands/builtin.js.map +1 -1
- package/dist/core/commands/job.d.ts.map +1 -1
- package/dist/core/commands/job.js +11 -2
- package/dist/core/commands/job.js.map +1 -1
- package/dist/core/commands/outbox.d.ts +3 -0
- package/dist/core/commands/outbox.d.ts.map +1 -0
- package/dist/core/commands/outbox.js +92 -0
- package/dist/core/commands/outbox.js.map +1 -0
- package/dist/core/job-board.d.ts +122 -1
- package/dist/core/job-board.d.ts.map +1 -1
- package/dist/core/job-board.js +404 -21
- package/dist/core/job-board.js.map +1 -1
- package/dist/core/job-recovery.d.ts +48 -0
- package/dist/core/job-recovery.d.ts.map +1 -0
- package/dist/core/job-recovery.js +185 -0
- package/dist/core/job-recovery.js.map +1 -0
- package/dist/core/message-sink.d.ts +63 -0
- package/dist/core/message-sink.d.ts.map +1 -0
- package/dist/core/message-sink.js +296 -0
- package/dist/core/message-sink.js.map +1 -0
- package/dist/core/outbox.d.ts +71 -0
- package/dist/core/outbox.d.ts.map +1 -0
- package/dist/core/outbox.js +301 -0
- package/dist/core/outbox.js.map +1 -0
- package/dist/core/reminders.d.ts.map +1 -1
- package/dist/core/reminders.js +12 -1
- package/dist/core/reminders.js.map +1 -1
- package/dist/core/restart-completion.d.ts.map +1 -1
- package/dist/core/restart-completion.js +18 -1
- package/dist/core/restart-completion.js.map +1 -1
- package/dist/core/router.d.ts +8 -0
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +16 -0
- package/dist/core/router.js.map +1 -1
- package/dist/core/self-protect.d.ts +3 -2
- package/dist/core/self-protect.d.ts.map +1 -1
- package/dist/core/self-protect.js +75 -41
- package/dist/core/self-protect.js.map +1 -1
- package/dist/core/types.d.ts +22 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.js +5 -0
- package/dist/plugins/agents/claude-code/index.js.map +1 -1
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +21 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/mcp-approval-server.js +106 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -1
- package/dist/plugins/agents/codex/index.d.ts.map +1 -1
- package/dist/plugins/agents/codex/index.js +5 -0
- package/dist/plugins/agents/codex/index.js.map +1 -1
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -1
- package/dist/plugins/agents/opencode/opencode-http-adapter.js +46 -15
- package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -1
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -1
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +5 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -1
- package/dist/web/public/settings.html +89 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +44 -16
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -141,6 +141,16 @@
|
|
|
141
141
|
adminConfirmRemove: 'Remove admin {p}:{u}?',
|
|
142
142
|
adminPublicBindWarn: '⚠️ Web is bound publicly — admin editor disabled to prevent random visitors from granting themselves access. Bind to 127.0.0.1 to enable.',
|
|
143
143
|
adminBootstrapHint: 'Bootstrap token exists at {path}. Run `cat` on the server, then send `/setup admin <token>` in IM to self-onboard.',
|
|
144
|
+
|
|
145
|
+
// ── Approval policy (timeout default) ─────────────────────
|
|
146
|
+
approvalPolicyTitle: 'Approval Policy',
|
|
147
|
+
approvalPolicyHint: 'What does Agim do when a tool-use approval times out (no IM reply within the budget)? Default deny is strict; switch to allow when you\'ll be away from IM and want the agent to keep moving.',
|
|
148
|
+
approvalTimeoutDefaultLabel: 'On approval timeout',
|
|
149
|
+
approvalTimeoutDeny: 'Deny (default — strict, human in the loop)',
|
|
150
|
+
approvalTimeoutAllow: 'Allow (user-away mode — agent proceeds silently)',
|
|
151
|
+
approvalPolicySave: 'Save',
|
|
152
|
+
approvalPolicySaved: 'Saved — applies to the next pending approval, no restart needed.',
|
|
153
|
+
approvalPolicyLoadFailed: 'Failed to load approval policy',
|
|
144
154
|
},
|
|
145
155
|
zh: {
|
|
146
156
|
title: 'Agim — 设置',
|
|
@@ -268,6 +278,16 @@
|
|
|
268
278
|
adminConfirmRemove: '移除管理员 {p}:{u}?',
|
|
269
279
|
adminPublicBindWarn: '⚠️ web 控制台目前监听公开地址 — 为防止陌生访问者擅自给自己加 admin,此编辑器已禁用。绑回 127.0.0.1 后启用。',
|
|
270
280
|
adminBootstrapHint: '检测到一次性 token 文件在 {path}。在服务器上 `cat` 该文件取 token,然后在 IM 里发 /setup admin <token> 即可把自己设为 admin。',
|
|
281
|
+
|
|
282
|
+
// ── 审批策略 (超时默认决策) ───────────────────────────────
|
|
283
|
+
approvalPolicyTitle: '审批策略',
|
|
284
|
+
approvalPolicyHint: '当工具调用审批超时(IM 端没在限定时间内回复)时 Agim 该怎么办?默认 deny 严格、人在回路;切到 allow 是"出门模式",让 agent 继续跑、不卡你。',
|
|
285
|
+
approvalTimeoutDefaultLabel: '审批超时时',
|
|
286
|
+
approvalTimeoutDeny: '拒绝(默认 — 严格,人工把关)',
|
|
287
|
+
approvalTimeoutAllow: '放行(出门模式 — agent 静默继续)',
|
|
288
|
+
approvalPolicySave: '保存',
|
|
289
|
+
approvalPolicySaved: '已保存 — 下一个挂起的审批起就生效,无需重启。',
|
|
290
|
+
approvalPolicyLoadFailed: '加载审批策略失败',
|
|
271
291
|
},
|
|
272
292
|
};
|
|
273
293
|
function t(key) { return T[window.__lang][key] || T.en[key] || key; }
|
|
@@ -709,6 +729,7 @@
|
|
|
709
729
|
<h1>${t('h1')}</h1>
|
|
710
730
|
${renderServiceCard()}
|
|
711
731
|
${renderSafetyCard()}
|
|
732
|
+
${renderApprovalPolicyCard()}
|
|
712
733
|
${renderAgentsCard()}
|
|
713
734
|
${renderMessengersCard()}
|
|
714
735
|
${renderAcpCard()}
|
|
@@ -721,6 +742,8 @@
|
|
|
721
742
|
void loadServiceStatus();
|
|
722
743
|
// Admin allowlist list — loads via /api/admin-allowlist.
|
|
723
744
|
void loadAdminList();
|
|
745
|
+
// Approval policy (IMHUB_TIMEOUT_DEFAULT) — loaded via /api/env.
|
|
746
|
+
void loadApprovalPolicy();
|
|
724
747
|
}
|
|
725
748
|
|
|
726
749
|
// ==========================================
|
|
@@ -840,6 +863,69 @@
|
|
|
840
863
|
`;
|
|
841
864
|
}
|
|
842
865
|
|
|
866
|
+
// ==========================================
|
|
867
|
+
// Approval Policy card — IMHUB_TIMEOUT_DEFAULT (deny|allow).
|
|
868
|
+
// Hot-reloads via process.env mutation in handlePutEnv, no restart.
|
|
869
|
+
// ==========================================
|
|
870
|
+
function renderApprovalPolicyCard() {
|
|
871
|
+
return `
|
|
872
|
+
<div class="card">
|
|
873
|
+
<h2>${t('approvalPolicyTitle')}</h2>
|
|
874
|
+
<div class="hint" style="margin-bottom:10px">${t('approvalPolicyHint')}</div>
|
|
875
|
+
<div style="margin-bottom:8px">
|
|
876
|
+
<label for="approval-timeout-default">${t('approvalTimeoutDefaultLabel')}</label>
|
|
877
|
+
<select id="approval-timeout-default">
|
|
878
|
+
<option value="deny">${t('approvalTimeoutDeny')}</option>
|
|
879
|
+
<option value="allow">${t('approvalTimeoutAllow')}</option>
|
|
880
|
+
</select>
|
|
881
|
+
</div>
|
|
882
|
+
<div class="actions">
|
|
883
|
+
<button type="button" class="btn btn-primary" id="approval-policy-save">${t('approvalPolicySave')}</button>
|
|
884
|
+
<span id="approval-policy-status" class="hint" style="margin-left:10px"></span>
|
|
885
|
+
</div>
|
|
886
|
+
</div>
|
|
887
|
+
`;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async function loadApprovalPolicy() {
|
|
891
|
+
const sel = document.getElementById('approval-timeout-default');
|
|
892
|
+
const status = document.getElementById('approval-policy-status');
|
|
893
|
+
if (!sel) return;
|
|
894
|
+
try {
|
|
895
|
+
const res = await authFetch('/api/env?reveal=1');
|
|
896
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
897
|
+
const data = await res.json();
|
|
898
|
+
const v = (data.env && typeof data.env.IMHUB_TIMEOUT_DEFAULT === 'string')
|
|
899
|
+
? data.env.IMHUB_TIMEOUT_DEFAULT.toLowerCase()
|
|
900
|
+
: 'deny';
|
|
901
|
+
sel.value = v === 'allow' ? 'allow' : 'deny';
|
|
902
|
+
} catch (err) {
|
|
903
|
+
if (status) status.textContent = t('approvalPolicyLoadFailed');
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
async function saveApprovalPolicy() {
|
|
908
|
+
const sel = document.getElementById('approval-timeout-default');
|
|
909
|
+
const status = document.getElementById('approval-policy-status');
|
|
910
|
+
if (!sel || !status) return;
|
|
911
|
+
const v = sel.value === 'allow' ? 'allow' : 'deny';
|
|
912
|
+
try {
|
|
913
|
+
// authFetch is a thin fetch wrapper that doesn't check res.ok — a
|
|
914
|
+
// 5xx from the server still resolves the promise. Without this guard
|
|
915
|
+
// the UI would lie ("已保存") on a failed write.
|
|
916
|
+
const res = await authFetch('/api/env', {
|
|
917
|
+
method: 'PUT',
|
|
918
|
+
headers: { 'Content-Type': 'application/json' },
|
|
919
|
+
body: JSON.stringify({ updates: { IMHUB_TIMEOUT_DEFAULT: v } }),
|
|
920
|
+
});
|
|
921
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
922
|
+
status.textContent = t('approvalPolicySaved');
|
|
923
|
+
setTimeout(() => { status.textContent = ''; }, 4000);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
status.textContent = t('saveFailed');
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
843
929
|
// ==========================================
|
|
844
930
|
// Agents card
|
|
845
931
|
// ==========================================
|
|
@@ -1346,6 +1432,9 @@
|
|
|
1346
1432
|
document.getElementById('svc-stop')?.addEventListener('click', () => svcAction('stop'));
|
|
1347
1433
|
document.getElementById('svc-start')?.addEventListener('click', () => svcAction('start'));
|
|
1348
1434
|
|
|
1435
|
+
// Approval Policy card — IMHUB_TIMEOUT_DEFAULT toggle.
|
|
1436
|
+
document.getElementById('approval-policy-save')?.addEventListener('click', saveApprovalPolicy);
|
|
1437
|
+
|
|
1349
1438
|
// Add admin (Safety card → Admin Allowlist).
|
|
1350
1439
|
document.getElementById('admin-add')?.addEventListener('click', async () => {
|
|
1351
1440
|
const platform = (document.getElementById('admin-platform')?.value || '').trim();
|
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAsDA,wBAAgB,iBAAiB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAmoB/C"}
|
package/dist/web/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { WebSocketServer } from 'ws';
|
|
|
9
9
|
import { parseMessage, routeMessage } from '../core/router.js';
|
|
10
10
|
import { sessionManager } from '../core/session.js';
|
|
11
11
|
import { registry } from '../core/registry.js';
|
|
12
|
+
import { sink, resolveMessenger } from '../core/message-sink.js';
|
|
12
13
|
import { generateTraceId, createLogger, logger as rootLogger } from '../core/logger.js';
|
|
13
14
|
import { validateConfig } from '../core/config-schema.js';
|
|
14
15
|
import { consumeToken, peekToken } from '../core/location-token.js';
|
|
@@ -229,8 +230,6 @@ export async function startWebServer(options) {
|
|
|
229
230
|
catch (err) {
|
|
230
231
|
webLog.warn({ event: 'web.loc.memo_failed', err: String(err) }, 'failed to write/update memo');
|
|
231
232
|
}
|
|
232
|
-
const messengerName = ctx.platform === 'wechat' ? 'wechat-ilink' : ctx.platform;
|
|
233
|
-
const messenger = registry.getMessenger(messengerName);
|
|
234
233
|
const idTag = memoId ? `#${memoId}` : '';
|
|
235
234
|
const accTxt = accuracy > 0 ? ` (±${Math.round(accuracy)}m)` : '';
|
|
236
235
|
const headLabel = displayWhat ? `'${displayWhat}'` : '';
|
|
@@ -242,14 +241,18 @@ export async function startWebServer(options) {
|
|
|
242
241
|
'',
|
|
243
242
|
` 🗺 [百度地图](${urls.baidu}) · [高德地图](${urls.amap}) · [Google](${urls.google})`,
|
|
244
243
|
].filter(Boolean).join('\n');
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
244
|
+
// Route via sink — adapter resolution (e.g. 'wechat' → 'wechat-ilink')
|
|
245
|
+
// happens inside resolveMessenger(). priority='normal': location replies
|
|
246
|
+
// are non-urgent, queue is fine.
|
|
247
|
+
sink.deliver({
|
|
248
|
+
platform: ctx.platform,
|
|
249
|
+
channelId: ctx.channelId,
|
|
250
|
+
threadId: ctx.threadId,
|
|
251
|
+
payload: reply,
|
|
252
|
+
kind: 'text',
|
|
253
|
+
}).catch((err) => {
|
|
254
|
+
webLog.warn({ event: 'web.loc.dispatch_failed', threadId: ctx.threadId, err: String(err) });
|
|
255
|
+
});
|
|
253
256
|
sendJson(res, 200, { ok: true, id: memoId, lat, lng });
|
|
254
257
|
return;
|
|
255
258
|
}
|
|
@@ -1187,9 +1190,10 @@ async function handleNotify(req, res) {
|
|
|
1187
1190
|
sendJson(res, 400, { error: 'Missing platform / threadId / (text|card)' });
|
|
1188
1191
|
return;
|
|
1189
1192
|
}
|
|
1190
|
-
//
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
+
// Pre-flight: confirm a messenger exists so we can return a clean 404
|
|
1194
|
+
// synchronously. sink will resolve again at delivery time — but the
|
|
1195
|
+
// synchronous 404 is part of the API contract.
|
|
1196
|
+
const messenger = resolveMessenger(platform);
|
|
1193
1197
|
if (!messenger) {
|
|
1194
1198
|
sendJson(res, 404, { error: `Messenger "${platform}" not registered` });
|
|
1195
1199
|
return;
|
|
@@ -1197,11 +1201,19 @@ async function handleNotify(req, res) {
|
|
|
1197
1201
|
const traceId = generateTraceId();
|
|
1198
1202
|
const log = createLogger({ traceId, platform, component: 'notify' });
|
|
1199
1203
|
log.info({ threadId, hasCard: !!card, textLen: text?.length || 0 }, 'notify in');
|
|
1200
|
-
|
|
1201
|
-
|
|
1204
|
+
// External /api/notify callers don't know the IM channelId, so we pass
|
|
1205
|
+
// 'rest' as a sentinel (consistent with other web-originated rows).
|
|
1206
|
+
if (card) {
|
|
1207
|
+
await sink.deliver({
|
|
1208
|
+
platform, channelId: 'rest', threadId,
|
|
1209
|
+
payload: card, kind: 'card',
|
|
1210
|
+
});
|
|
1202
1211
|
}
|
|
1203
1212
|
else if (text) {
|
|
1204
|
-
await
|
|
1213
|
+
await sink.deliver({
|
|
1214
|
+
platform, channelId: 'rest', threadId,
|
|
1215
|
+
payload: text, kind: 'text',
|
|
1216
|
+
});
|
|
1205
1217
|
}
|
|
1206
1218
|
else {
|
|
1207
1219
|
sendJson(res, 400, { error: 'card requires sendCard support, otherwise text is required' });
|
|
@@ -1544,6 +1556,11 @@ const ENV_EDITABLE_KEYS = [
|
|
|
1544
1556
|
// Safety card toggle — drives the Claude --dangerously-skip-permissions
|
|
1545
1557
|
// branch in plugins/agents/claude-code/index.ts. Not a secret, plain '1'/'0'.
|
|
1546
1558
|
'IMHUB_DANGEROUSLY_SKIP_PERMISSIONS',
|
|
1559
|
+
// Approval Policy card — 'allow' | 'deny' (default deny). Decides what the
|
|
1560
|
+
// approval bus does when a tool-use prompt times out with no human reply.
|
|
1561
|
+
// Hot-reload: handlePutEnv mutates process.env so approval-bus picks up the
|
|
1562
|
+
// new value on the next timer fire, no restart needed.
|
|
1563
|
+
'IMHUB_TIMEOUT_DEFAULT',
|
|
1547
1564
|
];
|
|
1548
1565
|
const SECRET_KEYS = new Set(['IMHUB_SMTP_PASS', 'IMHUB_BAIDU_MAP_AK']);
|
|
1549
1566
|
function maskSecret(v) {
|
|
@@ -1594,6 +1611,17 @@ async function handlePutEnv(req, res) {
|
|
|
1594
1611
|
}
|
|
1595
1612
|
const { updateEnvFile } = await import('../cli-ui/env-file.js');
|
|
1596
1613
|
updateEnvFile(safe);
|
|
1614
|
+
// Hot-reload: mutate process.env so modules that re-read on use (e.g.
|
|
1615
|
+
// approval-bus's isTimeoutDefaultAllow) pick up the change without
|
|
1616
|
+
// waiting for a service restart. Modules that capture at module load
|
|
1617
|
+
// (most SMTP / Baidu callers) still need a restart to see the new value
|
|
1618
|
+
// — that's a per-module decision, not enforced here.
|
|
1619
|
+
for (const [k, v] of Object.entries(safe)) {
|
|
1620
|
+
if (v === null)
|
|
1621
|
+
delete process.env[k];
|
|
1622
|
+
else
|
|
1623
|
+
process.env[k] = v;
|
|
1624
|
+
}
|
|
1597
1625
|
sendJson(res, 200, { ok: true, updated: Object.keys(safe) });
|
|
1598
1626
|
}
|
|
1599
1627
|
catch (err) {
|