@atel-ai/runtime-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/adapters/claude-code.d.ts +3 -0
  2. package/dist/adapters/claude-code.d.ts.map +1 -0
  3. package/dist/adapters/claude-code.js +65 -0
  4. package/dist/adapters/claude-code.js.map +1 -0
  5. package/dist/adapters/codex.d.ts +3 -0
  6. package/dist/adapters/codex.d.ts.map +1 -0
  7. package/dist/adapters/codex.js +49 -0
  8. package/dist/adapters/codex.js.map +1 -0
  9. package/dist/adapters/gemini-cli.d.ts +3 -0
  10. package/dist/adapters/gemini-cli.d.ts.map +1 -0
  11. package/dist/adapters/gemini-cli.js +43 -0
  12. package/dist/adapters/gemini-cli.js.map +1 -0
  13. package/dist/adapters/hermes.d.ts +3 -0
  14. package/dist/adapters/hermes.d.ts.map +1 -0
  15. package/dist/adapters/hermes.js +58 -0
  16. package/dist/adapters/hermes.js.map +1 -0
  17. package/dist/adapters/openclaw.d.ts +13 -0
  18. package/dist/adapters/openclaw.d.ts.map +1 -0
  19. package/dist/adapters/openclaw.js +36 -0
  20. package/dist/adapters/openclaw.js.map +1 -0
  21. package/dist/adapters/qwen-agent.d.ts +3 -0
  22. package/dist/adapters/qwen-agent.d.ts.map +1 -0
  23. package/dist/adapters/qwen-agent.js +128 -0
  24. package/dist/adapters/qwen-agent.js.map +1 -0
  25. package/dist/adapters/registry.d.ts +20 -0
  26. package/dist/adapters/registry.d.ts.map +1 -0
  27. package/dist/adapters/registry.js +42 -0
  28. package/dist/adapters/registry.js.map +1 -0
  29. package/dist/adapters/types.d.ts +46 -0
  30. package/dist/adapters/types.d.ts.map +1 -0
  31. package/dist/adapters/types.js +2 -0
  32. package/dist/adapters/types.js.map +1 -0
  33. package/dist/adapters/util.d.ts +10 -0
  34. package/dist/adapters/util.d.ts.map +1 -0
  35. package/dist/adapters/util.js +86 -0
  36. package/dist/adapters/util.js.map +1 -0
  37. package/dist/auth.d.ts +42 -0
  38. package/dist/auth.d.ts.map +1 -0
  39. package/dist/auth.js +91 -0
  40. package/dist/auth.js.map +1 -0
  41. package/dist/client.d.ts +55 -0
  42. package/dist/client.d.ts.map +1 -0
  43. package/dist/client.js +139 -0
  44. package/dist/client.js.map +1 -0
  45. package/dist/companion.d.ts +45 -0
  46. package/dist/companion.d.ts.map +1 -0
  47. package/dist/companion.js +224 -0
  48. package/dist/companion.js.map +1 -0
  49. package/dist/config.d.ts +35 -0
  50. package/dist/config.d.ts.map +1 -0
  51. package/dist/config.js +29 -0
  52. package/dist/config.js.map +1 -0
  53. package/dist/deadletter.d.ts +31 -0
  54. package/dist/deadletter.d.ts.map +1 -0
  55. package/dist/deadletter.js +67 -0
  56. package/dist/deadletter.js.map +1 -0
  57. package/dist/envelope.d.ts +21 -0
  58. package/dist/envelope.d.ts.map +1 -0
  59. package/dist/envelope.js +20 -0
  60. package/dist/envelope.js.map +1 -0
  61. package/dist/events/channel-relay.d.ts +35 -0
  62. package/dist/events/channel-relay.d.ts.map +1 -0
  63. package/dist/events/channel-relay.js +97 -0
  64. package/dist/events/channel-relay.js.map +1 -0
  65. package/dist/events/channel-spine.d.ts +41 -0
  66. package/dist/events/channel-spine.d.ts.map +1 -0
  67. package/dist/events/channel-spine.js +139 -0
  68. package/dist/events/channel-spine.js.map +1 -0
  69. package/dist/events/dedup.d.ts +25 -0
  70. package/dist/events/dedup.d.ts.map +1 -0
  71. package/dist/events/dedup.js +56 -0
  72. package/dist/events/dedup.js.map +1 -0
  73. package/dist/events/mapping.d.ts +37 -0
  74. package/dist/events/mapping.d.ts.map +1 -0
  75. package/dist/events/mapping.js +212 -0
  76. package/dist/events/mapping.js.map +1 -0
  77. package/dist/events/roles.d.ts +21 -0
  78. package/dist/events/roles.d.ts.map +1 -0
  79. package/dist/events/roles.js +80 -0
  80. package/dist/events/roles.js.map +1 -0
  81. package/dist/events/types.d.ts +51 -0
  82. package/dist/events/types.d.ts.map +1 -0
  83. package/dist/events/types.js +96 -0
  84. package/dist/events/types.js.map +1 -0
  85. package/dist/identity.d.ts +48 -0
  86. package/dist/identity.d.ts.map +1 -0
  87. package/dist/identity.js +123 -0
  88. package/dist/identity.js.map +1 -0
  89. package/dist/index.d.ts +32 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +40 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/queue.d.ts +12 -0
  94. package/dist/queue.d.ts.map +1 -0
  95. package/dist/queue.js +25 -0
  96. package/dist/queue.js.map +1 -0
  97. package/dist/tracker.d.ts +27 -0
  98. package/dist/tracker.d.ts.map +1 -0
  99. package/dist/tracker.js +80 -0
  100. package/dist/tracker.js.map +1 -0
  101. package/dist/wakers/cli.d.ts +36 -0
  102. package/dist/wakers/cli.d.ts.map +1 -0
  103. package/dist/wakers/cli.js +116 -0
  104. package/dist/wakers/cli.js.map +1 -0
  105. package/dist/wakers/daemon.d.ts +26 -0
  106. package/dist/wakers/daemon.d.ts.map +1 -0
  107. package/dist/wakers/daemon.js +51 -0
  108. package/dist/wakers/daemon.js.map +1 -0
  109. package/dist/wakers/embedded.d.ts +23 -0
  110. package/dist/wakers/embedded.d.ts.map +1 -0
  111. package/dist/wakers/embedded.js +26 -0
  112. package/dist/wakers/embedded.js.map +1 -0
  113. package/dist/wakers/types.d.ts +29 -0
  114. package/dist/wakers/types.d.ts.map +1 -0
  115. package/dist/wakers/types.js +2 -0
  116. package/dist/wakers/types.js.map +1 -0
  117. package/package.json +44 -0
  118. package/src/skill/SKILL.md +80 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * 适配器共用工具:探可执行文件、读写 JSON/TOML 配置(浅合并,保留用户其他配置)。
3
+ */
4
+ import { execFile } from 'node:child_process';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ /** which/where 解析 bin 绝对路径;找不到返回 null。 */
8
+ export function which(bin) {
9
+ const finder = process.platform === 'win32' ? 'where' : 'which';
10
+ return new Promise((resolve) => {
11
+ execFile(finder, [bin], { timeout: 5000 }, (err, stdout) => {
12
+ if (err)
13
+ return resolve(null);
14
+ const first = (stdout || '').split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
15
+ resolve(first || null);
16
+ });
17
+ });
18
+ }
19
+ /** 跑 `bin <args>` 取首行(探版本用);失败返回 null。 */
20
+ export function runText(bin, args, timeoutMs = 8000) {
21
+ return new Promise((resolve) => {
22
+ execFile(bin, args, { timeout: timeoutMs }, (err, stdout, stderr) => {
23
+ if (err && !stdout && !stderr)
24
+ return resolve(null);
25
+ const out = ((stdout || '') + (stderr || '')).trim();
26
+ resolve(out || null);
27
+ });
28
+ });
29
+ }
30
+ export function readJson(file) {
31
+ try {
32
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /** 浅合并写回 JSON,保留文件已有的其他键。 */
39
+ export function mergeJson(file, patch) {
40
+ const existing = readJson(file) || {};
41
+ const merged = deepMerge(existing, patch);
42
+ fs.mkdirSync(path.dirname(file), { recursive: true });
43
+ fs.writeFileSync(file, JSON.stringify(merged, null, 2));
44
+ }
45
+ function deepMerge(a, b) {
46
+ const out = { ...a };
47
+ for (const [k, v] of Object.entries(b)) {
48
+ if (v && typeof v === 'object' && !Array.isArray(v) &&
49
+ out[k] && typeof out[k] === 'object' && !Array.isArray(out[k])) {
50
+ out[k] = deepMerge(out[k], v);
51
+ }
52
+ else {
53
+ out[k] = v;
54
+ }
55
+ }
56
+ return out;
57
+ }
58
+ /** 极简 TOML 段写入:替换/追加 [section] 下的 key=value 块(够 codex mcp_servers 用)。 */
59
+ export function writeTomlSection(file, section, body) {
60
+ let content = '';
61
+ try {
62
+ content = fs.readFileSync(file, 'utf8');
63
+ }
64
+ catch {
65
+ content = '';
66
+ }
67
+ const header = `[${section}]`;
68
+ const block = `${header}\n${body.trim()}\n`;
69
+ // 删除已存在的同名段(到下一个顶层 [ 或文件尾)
70
+ const lines = content.split(/\r?\n/);
71
+ const out = [];
72
+ let skip = false;
73
+ for (const line of lines) {
74
+ const isHeader = /^\[.+\]\s*$/.test(line.trim());
75
+ if (isHeader) {
76
+ skip = line.trim() === header;
77
+ }
78
+ if (!skip)
79
+ out.push(line);
80
+ }
81
+ let result = out.join('\n').replace(/\n{3,}/g, '\n\n').trim();
82
+ result = (result ? result + '\n\n' : '') + block;
83
+ fs.mkdirSync(path.dirname(file), { recursive: true });
84
+ fs.writeFileSync(file, result);
85
+ }
86
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/adapters/util.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * AuthManager —— JWT 生命周期(会真炸的坑,见 6.5 §6-1)。
3
+ *
4
+ * Claude/Codex/Hermes/Gemini 的 JWT 是写死在各自 MCP 配置文件里的,7 天到期即 401;
5
+ * A2A 单 deadline 也能 7 天。companion 持私钥:
6
+ * 到期前自动重签 → 换新 JWT → 重写该 runtime 的 MCP 配置 → 热重载/下次唤醒生效。
7
+ *
8
+ * 这里只管"拿 token + 到点重签 + 回调让适配器落配置";具体怎么写各家 MCP 配置
9
+ * 由适配器的 writeMcpConfig(token) 实现(格式各异)。
10
+ */
11
+ import type { Identity } from './identity.js';
12
+ export interface AuthHooks {
13
+ /** 拿到新 JWT 后写进该 runtime 的 MCP 配置(各家格式不同)。 */
14
+ onToken?: (token: string, expiresAt: number) => void | Promise<void>;
15
+ /** 写完配置后热重载(若 runtime 需要;CLI 类通常下次唤醒自动生效)。 */
16
+ reload?: () => void | Promise<void>;
17
+ /** 日志 */
18
+ log?: (msg: string) => void;
19
+ }
20
+ export declare class AuthManager {
21
+ private readonly identity;
22
+ private readonly spineBaseUrl;
23
+ private readonly hooks;
24
+ private readonly refreshLeadMs;
25
+ private current?;
26
+ private timer?;
27
+ private refreshing;
28
+ constructor(identity: Identity, spineBaseUrl: string, hooks?: AuthHooks, refreshLeadMs?: number);
29
+ /** 当前有效 JWT(可能为空,未 start 或刚失败)。 */
30
+ get token(): string | undefined;
31
+ get expiresAt(): number | undefined;
32
+ /** 首次换 token 并安排自动续期。返回首个 token。 */
33
+ start(): Promise<string>;
34
+ /** 立即重签换新 JWT,落配置,排下一次续期。 */
35
+ refresh(): Promise<string>;
36
+ /** 确保 token 有效(未到期);若已过/将过期则刷新。 */
37
+ ensureFresh(): Promise<string>;
38
+ private schedule;
39
+ stop(): void;
40
+ private log;
41
+ }
42
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS;IACT,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,qBAAa,WAAW;IAMpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa;IARhC,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,KAAK,CAAC,CAAgC;IAC9C,OAAO,CAAC,UAAU,CAAS;gBAGR,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,GAAE,SAAc,EACrB,aAAa,SAAiB;IAGjD,mCAAmC;IACnC,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED,oCAAoC;IAC9B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAK9B,6BAA6B;IACvB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAoBhC,mCAAmC;IAC7B,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAQpC,OAAO,CAAC,QAAQ;IAchB,IAAI,IAAI,IAAI;IAKZ,OAAO,CAAC,GAAG;CAGZ"}
package/dist/auth.js ADDED
@@ -0,0 +1,91 @@
1
+ import { spineIdentityVerify } from './client.js';
2
+ export class AuthManager {
3
+ identity;
4
+ spineBaseUrl;
5
+ hooks;
6
+ refreshLeadMs;
7
+ current;
8
+ timer;
9
+ refreshing = false;
10
+ constructor(identity, spineBaseUrl, hooks = {}, refreshLeadMs = 60 * 60 * 1000) {
11
+ this.identity = identity;
12
+ this.spineBaseUrl = spineBaseUrl;
13
+ this.hooks = hooks;
14
+ this.refreshLeadMs = refreshLeadMs;
15
+ }
16
+ /** 当前有效 JWT(可能为空,未 start 或刚失败)。 */
17
+ get token() {
18
+ return this.current?.token;
19
+ }
20
+ get expiresAt() {
21
+ return this.current?.expiresAt;
22
+ }
23
+ /** 首次换 token 并安排自动续期。返回首个 token。 */
24
+ async start() {
25
+ const token = await this.refresh();
26
+ return token;
27
+ }
28
+ /** 立即重签换新 JWT,落配置,排下一次续期。 */
29
+ async refresh() {
30
+ if (this.refreshing) {
31
+ // 合并并发刷新
32
+ while (this.refreshing)
33
+ await sleep(50);
34
+ if (this.current)
35
+ return this.current.token;
36
+ }
37
+ this.refreshing = true;
38
+ try {
39
+ const res = await spineIdentityVerify(this.spineBaseUrl, this.identity);
40
+ this.current = res;
41
+ this.log(`JWT refreshed, expiresAt=${new Date(res.expiresAt).toISOString()}`);
42
+ if (this.hooks.onToken)
43
+ await this.hooks.onToken(res.token, res.expiresAt);
44
+ if (this.hooks.reload)
45
+ await this.hooks.reload();
46
+ this.schedule();
47
+ return res.token;
48
+ }
49
+ finally {
50
+ this.refreshing = false;
51
+ }
52
+ }
53
+ /** 确保 token 有效(未到期);若已过/将过期则刷新。 */
54
+ async ensureFresh() {
55
+ if (!this.current)
56
+ return this.refresh();
57
+ if (Date.now() >= this.current.expiresAt - this.refreshLeadMs) {
58
+ return this.refresh();
59
+ }
60
+ return this.current.token;
61
+ }
62
+ schedule() {
63
+ if (this.timer)
64
+ clearTimeout(this.timer);
65
+ if (!this.current)
66
+ return;
67
+ const delay = Math.max(60_000, this.current.expiresAt - this.refreshLeadMs - Date.now());
68
+ this.timer = setTimeout(() => {
69
+ this.refresh().catch((e) => this.log(`refresh failed: ${e.message}; retry in 5m`));
70
+ if (!this.current || Date.now() >= this.current.expiresAt - this.refreshLeadMs) {
71
+ // 失败后兜底 5 分钟重试
72
+ this.timer = setTimeout(() => this.refresh().catch(() => { }), 5 * 60 * 1000);
73
+ }
74
+ }, delay);
75
+ if (this.timer.unref)
76
+ this.timer.unref();
77
+ }
78
+ stop() {
79
+ if (this.timer)
80
+ clearTimeout(this.timer);
81
+ this.timer = undefined;
82
+ }
83
+ log(msg) {
84
+ if (this.hooks.log)
85
+ this.hooks.log(`[atel/auth] ${msg}`);
86
+ }
87
+ }
88
+ function sleep(ms) {
89
+ return new Promise((r) => setTimeout(r, ms));
90
+ }
91
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { Identity } from './identity.js';
2
+ export interface HttpResult {
3
+ ok: boolean;
4
+ status: number;
5
+ body: string;
6
+ json: unknown;
7
+ }
8
+ /** 带退避重试的 fetch(沿用 poll-loop fetchWithRetry:409 视为成功不重试)。 */
9
+ export declare function fetchWithRetry(url: string, options: RequestInit, label: string, maxAttempts?: number): Promise<HttpResult>;
10
+ /** POST 一个已签名的 SignedRequest 信封。 */
11
+ export declare function postSigned<T>(baseUrl: string, pathname: string, identity: Identity, payload: T, label: string, extraHeaders?: Record<string, string>): Promise<HttpResult>;
12
+ /** 带 Bearer JWT 的 POST(platform registry 用 JWT,不是签名信封)。 */
13
+ export declare function postBearer<T>(baseUrl: string, pathname: string, jwt: string, payload: T, label: string): Promise<HttpResult>;
14
+ /** POST /registry/v1/remote/register → 注册/更新本 agent(name + capabilities + discoverable)。 */
15
+ export declare function registryRemoteRegister(platformBaseUrl: string, jwt: string, input: {
16
+ name: string;
17
+ description?: string;
18
+ capabilities: string[];
19
+ discoverable: boolean;
20
+ }): Promise<HttpResult>;
21
+ export interface VerifyResult {
22
+ token: string;
23
+ did: string;
24
+ expiresAt: number;
25
+ }
26
+ /** POST /identity/verify → JWT。 */
27
+ export declare function spineIdentityVerify(spineBaseUrl: string, identity: Identity): Promise<VerifyResult>;
28
+ /** POST /wallet/balance → 余额(DID-Sig)。 */
29
+ export declare function spineWalletBalance(spineBaseUrl: string, identity: Identity): Promise<HttpResult>;
30
+ /** POST /events/subscribe → 订阅点号领域事件(通道甲)。 */
31
+ export declare function spineSubscribe(spineBaseUrl: string, identity: Identity, topics: string[], deliveryMode: 'sse' | 'webhook', callbackUrl?: string): Promise<HttpResult>;
32
+ /**
33
+ * POST /events/stream-token → 取一次性 SSE 连接 token(DID-Sig)。
34
+ * 然后 GET /events/stream?token=<tok> 在 30s 内连上(spine stream_token.go)。
35
+ * 比把签名信封塞进 URL 更干净(避免进 nginx 日志/CDN 缓存)。
36
+ */
37
+ export declare function spineStreamToken(spineBaseUrl: string, identity: Identity): Promise<{
38
+ token: string;
39
+ expiresAt?: string;
40
+ }>;
41
+ /** POST /relay/notify → 给某 DID 推消息(用于死信报警给 agent 主人)。 */
42
+ export declare function spineRelayNotify(spineBaseUrl: string, identity: Identity, targetDid: string, message: unknown, kind?: string): Promise<HttpResult>;
43
+ /** JWT 过期时间归一:服务端可能给秒级,这里统一成毫秒 epoch。 */
44
+ export declare function normalizeExpiry(expiresAt: number | undefined): number;
45
+ export interface RelayMessage {
46
+ id: string;
47
+ message: {
48
+ body?: unknown;
49
+ } & Record<string, unknown>;
50
+ }
51
+ /** POST /relay/v1/poll → 取下划线通知(通道乙)。 */
52
+ export declare function relayPoll(platformBaseUrl: string, identity: Identity): Promise<RelayMessage[]>;
53
+ /** POST /relay/v1/ack → 确认已消费(漏签会 401 循环,见 project_sdk_relay_ack_signing)。 */
54
+ export declare function relayAck(platformBaseUrl: string, identity: Identity, ids: string[]): Promise<HttpResult>;
55
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,139 @@
1
+ /**
2
+ * 签名 HTTP 客户端 —— 对 Spine 公开面 + platform relay 发 DID-Sig 请求。
3
+ * 只走公开面(MCP + Spine + relay),不 import platform internal、不连 DB、
4
+ * 不设 X-Internal-Secret(遵 START-HERE 三纪律)。
5
+ */
6
+ import { signEnvelope } from './envelope.js';
7
+ function trimSlash(u) {
8
+ return u.replace(/\/+$/, '');
9
+ }
10
+ /** 带退避重试的 fetch(沿用 poll-loop fetchWithRetry:409 视为成功不重试)。 */
11
+ export async function fetchWithRetry(url, options, label, maxAttempts = 3) {
12
+ let lastErr = null;
13
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
14
+ try {
15
+ const res = await fetch(url, options);
16
+ const body = await res.text().catch(() => '');
17
+ if (res.ok || res.status === 409) {
18
+ return { ok: res.ok, status: res.status, body, json: safeJson(body) };
19
+ }
20
+ if (attempt < maxAttempts - 1) {
21
+ await sleep((attempt + 1) * 2000);
22
+ continue;
23
+ }
24
+ return { ok: false, status: res.status, body, json: safeJson(body) };
25
+ }
26
+ catch (err) {
27
+ lastErr = err;
28
+ if (attempt < maxAttempts - 1) {
29
+ await sleep((attempt + 1) * 2000);
30
+ continue;
31
+ }
32
+ }
33
+ }
34
+ throw lastErr || new Error(`${label}: request failed`);
35
+ }
36
+ function safeJson(s) {
37
+ try {
38
+ return JSON.parse(s);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ function sleep(ms) {
45
+ return new Promise((r) => setTimeout(r, ms));
46
+ }
47
+ /** POST 一个已签名的 SignedRequest 信封。 */
48
+ export async function postSigned(baseUrl, pathname, identity, payload, label, extraHeaders) {
49
+ const env = signEnvelope(identity, payload);
50
+ return fetchWithRetry(`${trimSlash(baseUrl)}${pathname}`, {
51
+ method: 'POST',
52
+ headers: { 'content-type': 'application/json', ...(extraHeaders || {}) },
53
+ body: JSON.stringify(env),
54
+ }, label);
55
+ }
56
+ /** 带 Bearer JWT 的 POST(platform registry 用 JWT,不是签名信封)。 */
57
+ export async function postBearer(baseUrl, pathname, jwt, payload, label) {
58
+ return fetchWithRetry(`${trimSlash(baseUrl)}${pathname}`, {
59
+ method: 'POST',
60
+ headers: { 'content-type': 'application/json', Authorization: `Bearer ${jwt}` },
61
+ body: JSON.stringify(payload),
62
+ }, label);
63
+ }
64
+ /** POST /registry/v1/remote/register → 注册/更新本 agent(name + capabilities + discoverable)。 */
65
+ export async function registryRemoteRegister(platformBaseUrl, jwt, input) {
66
+ return postBearer(platformBaseUrl, '/registry/v1/remote/register', jwt, input, 'registry/remote/register');
67
+ }
68
+ /** POST /identity/verify → JWT。 */
69
+ export async function spineIdentityVerify(spineBaseUrl, identity) {
70
+ const r = await postSigned(spineBaseUrl, '/identity/verify', identity, {}, 'identity/verify');
71
+ if (!r.ok)
72
+ throw new Error(`identity/verify HTTP ${r.status}: ${r.body.slice(0, 200)}`);
73
+ const j = r.json;
74
+ if (!j?.token)
75
+ throw new Error(`identity/verify: no token in response: ${r.body.slice(0, 200)}`);
76
+ return { token: j.token, did: j.did || identity.did, expiresAt: normalizeExpiry(j.expiresAt) };
77
+ }
78
+ /** POST /wallet/balance → 余额(DID-Sig)。 */
79
+ export async function spineWalletBalance(spineBaseUrl, identity) {
80
+ return postSigned(spineBaseUrl, '/wallet/balance', identity, {}, 'wallet/balance');
81
+ }
82
+ /** POST /events/subscribe → 订阅点号领域事件(通道甲)。 */
83
+ export async function spineSubscribe(spineBaseUrl, identity, topics, deliveryMode, callbackUrl) {
84
+ const payload = { topics, deliveryMode };
85
+ if (deliveryMode === 'webhook') {
86
+ if (!callbackUrl)
87
+ throw new Error('webhook deliveryMode requires callbackUrl');
88
+ payload.callbackUrl = callbackUrl;
89
+ }
90
+ return postSigned(spineBaseUrl, '/events/subscribe', identity, payload, 'events/subscribe');
91
+ }
92
+ /**
93
+ * POST /events/stream-token → 取一次性 SSE 连接 token(DID-Sig)。
94
+ * 然后 GET /events/stream?token=<tok> 在 30s 内连上(spine stream_token.go)。
95
+ * 比把签名信封塞进 URL 更干净(避免进 nginx 日志/CDN 缓存)。
96
+ */
97
+ export async function spineStreamToken(spineBaseUrl, identity) {
98
+ const r = await postSigned(spineBaseUrl, '/events/stream-token', identity, {}, 'events/stream-token');
99
+ if (!r.ok)
100
+ throw new Error(`events/stream-token HTTP ${r.status}: ${r.body.slice(0, 200)}`);
101
+ const j = r.json;
102
+ if (!j?.token)
103
+ throw new Error(`events/stream-token: no token: ${r.body.slice(0, 160)}`);
104
+ return { token: j.token, expiresAt: j.expiresAt };
105
+ }
106
+ /** POST /relay/notify → 给某 DID 推消息(用于死信报警给 agent 主人)。 */
107
+ export async function spineRelayNotify(spineBaseUrl, identity, targetDid, message, kind = 'notify') {
108
+ // openapi: payload 必填 targetDid + message(不是 toDid)
109
+ return postSigned(spineBaseUrl, '/relay/notify', identity, { targetDid, message, kind }, 'relay/notify');
110
+ }
111
+ /** JWT 过期时间归一:服务端可能给秒级,这里统一成毫秒 epoch。 */
112
+ export function normalizeExpiry(expiresAt) {
113
+ if (!expiresAt || typeof expiresAt !== 'number') {
114
+ return Date.now() + 7 * 24 * 60 * 60 * 1000; // 缺省按 7 天
115
+ }
116
+ // < 1e12 视为秒级
117
+ return expiresAt < 1e12 ? expiresAt * 1000 : expiresAt;
118
+ }
119
+ /** POST /relay/v1/poll → 取下划线通知(通道乙)。 */
120
+ export async function relayPoll(platformBaseUrl, identity) {
121
+ const nonce = cryptoNonce();
122
+ const r = await postSigned(platformBaseUrl, '/relay/v1/poll', identity, { nonce }, 'relay/poll');
123
+ if (!r.ok)
124
+ throw new Error(`relay/poll HTTP ${r.status}: ${r.body.slice(0, 200)}`);
125
+ const data = r.json;
126
+ return Array.isArray(data?.messages) ? data.messages : [];
127
+ }
128
+ /** POST /relay/v1/ack → 确认已消费(漏签会 401 循环,见 project_sdk_relay_ack_signing)。 */
129
+ export async function relayAck(platformBaseUrl, identity, ids) {
130
+ return postSigned(platformBaseUrl, '/relay/v1/ack', identity, { ids }, 'relay/ack');
131
+ }
132
+ function cryptoNonce() {
133
+ // 16 随机字节 hex,无需强随机(只防同窗口重放)
134
+ const bytes = new Uint8Array(16);
135
+ for (let i = 0; i < bytes.length; i++)
136
+ bytes[i] = Math.floor(Math.random() * 256);
137
+ return Buffer.from(bytes).toString('hex');
138
+ }
139
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { Identity } from './identity.js';
2
+ import type { RuntimeConfig } from './config.js';
3
+ import type { Waker } from './wakers/types.js';
4
+ import { type AuthHooks } from './auth.js';
5
+ export interface CompanionOpts {
6
+ identity: Identity;
7
+ config: RuntimeConfig;
8
+ waker: Waker;
9
+ /** 适配器写 MCP 配置(JWT 续期后调用) */
10
+ authHooks?: AuthHooks;
11
+ log?: (msg: string) => void;
12
+ }
13
+ export interface CompanionStats {
14
+ received: number;
15
+ deduped: number;
16
+ woken: number;
17
+ wakeOk: number;
18
+ wakeFail: number;
19
+ deadLetters: number;
20
+ activeOrders: number;
21
+ }
22
+ export declare class Companion {
23
+ private readonly opts;
24
+ private readonly auth;
25
+ private readonly dedup;
26
+ private readonly roles;
27
+ private readonly tracker;
28
+ private readonly queue;
29
+ private readonly dlq;
30
+ private relay;
31
+ private spine;
32
+ private autonomyTimer?;
33
+ private readonly stats;
34
+ constructor(opts: CompanionOpts);
35
+ start(): Promise<void>;
36
+ stop(): void;
37
+ /** webhook 模式入口:外部 HTTP 服务把回调事件喂进来。 */
38
+ ingestWebhook(raw: unknown): Promise<void>;
39
+ getStats(): CompanionStats;
40
+ private onEvent;
41
+ private executePlan;
42
+ private runAutonomy;
43
+ private log;
44
+ }
45
+ //# sourceMappingURL=companion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companion.d.ts","sourceRoot":"","sources":["../src/companion.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAWxD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,6BAA6B;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAID,qBAAa,SAAS;IAoBR,OAAO,CAAC,QAAQ,CAAC,IAAI;IAnBjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAQpB;gBAE2B,IAAI,EAAE,aAAa;IAa1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC5B,IAAI,IAAI,IAAI;IAOZ,uCAAuC;IACjC,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,QAAQ,IAAI,cAAc;YAKZ,OAAO;YA+BP,WAAW;IA8BzB,OAAO,CAAC,WAAW;IAgDnB,OAAO,CAAC,GAAG;CAGZ"}