sessix-server 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.
- package/dist/approval/ApprovalProxy.d.ts +86 -0
- package/dist/approval/ApprovalProxy.d.ts.map +1 -0
- package/dist/approval/ApprovalProxy.js +363 -0
- package/dist/approval/ApprovalProxy.js.map +1 -0
- package/dist/hooks/HookInstaller.d.ts +55 -0
- package/dist/hooks/HookInstaller.d.ts.map +1 -0
- package/dist/hooks/HookInstaller.js +215 -0
- package/dist/hooks/HookInstaller.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3115 -0
- package/dist/index.js.map +1 -0
- package/dist/mdns/MdnsService.d.ts +36 -0
- package/dist/mdns/MdnsService.d.ts.map +1 -0
- package/dist/mdns/MdnsService.js +66 -0
- package/dist/mdns/MdnsService.js.map +1 -0
- package/dist/notification/ActivityPushChannel.d.ts +54 -0
- package/dist/notification/ActivityPushChannel.d.ts.map +1 -0
- package/dist/notification/ActivityPushChannel.js +235 -0
- package/dist/notification/ActivityPushChannel.js.map +1 -0
- package/dist/notification/ExpoNotificationChannel.d.ts +17 -0
- package/dist/notification/ExpoNotificationChannel.d.ts.map +1 -0
- package/dist/notification/ExpoNotificationChannel.js +57 -0
- package/dist/notification/ExpoNotificationChannel.js.map +1 -0
- package/dist/notification/MacNotificationChannel.d.ts +22 -0
- package/dist/notification/MacNotificationChannel.d.ts.map +1 -0
- package/dist/notification/MacNotificationChannel.js +33 -0
- package/dist/notification/MacNotificationChannel.js.map +1 -0
- package/dist/notification/NotificationService.d.ts +50 -0
- package/dist/notification/NotificationService.d.ts.map +1 -0
- package/dist/notification/NotificationService.js +177 -0
- package/dist/notification/NotificationService.js.map +1 -0
- package/dist/providers/ExecutionProvider.d.ts +60 -0
- package/dist/providers/ExecutionProvider.d.ts.map +1 -0
- package/dist/providers/ExecutionProvider.js +3 -0
- package/dist/providers/ExecutionProvider.js.map +1 -0
- package/dist/providers/ProcessProvider.d.ts +117 -0
- package/dist/providers/ProcessProvider.d.ts.map +1 -0
- package/dist/providers/ProcessProvider.js +507 -0
- package/dist/providers/ProcessProvider.js.map +1 -0
- package/dist/server.d.ts +32 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +3054 -0
- package/dist/server.js.map +1 -0
- package/dist/session/ProjectReader.d.ts +44 -0
- package/dist/session/ProjectReader.d.ts.map +1 -0
- package/dist/session/ProjectReader.js +471 -0
- package/dist/session/ProjectReader.js.map +1 -0
- package/dist/session/SessionFileWatcher.d.ts +35 -0
- package/dist/session/SessionFileWatcher.d.ts.map +1 -0
- package/dist/session/SessionFileWatcher.js +207 -0
- package/dist/session/SessionFileWatcher.js.map +1 -0
- package/dist/session/SessionManager.d.ts +114 -0
- package/dist/session/SessionManager.d.ts.map +1 -0
- package/dist/session/SessionManager.js +356 -0
- package/dist/session/SessionManager.js.map +1 -0
- package/dist/ws/WsBridge.d.ts +55 -0
- package/dist/ws/WsBridge.d.ts.map +1 -0
- package/dist/ws/WsBridge.js +220 -0
- package/dist/ws/WsBridge.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** mDNS 服务配置 */
|
|
2
|
+
interface MdnsServiceOptions {
|
|
3
|
+
/** WebSocket 端口 */
|
|
4
|
+
wsPort: number;
|
|
5
|
+
/** HTTP 审批端口 */
|
|
6
|
+
httpPort: number;
|
|
7
|
+
/** 服务版本 */
|
|
8
|
+
version?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* mDNS 局域网广播服务
|
|
12
|
+
*
|
|
13
|
+
* 使用 bonjour-service 在局域网内广播 Sessix 服务,
|
|
14
|
+
* 让手机端能自动发现 Mac 上运行的 Sessix 服务器。
|
|
15
|
+
*
|
|
16
|
+
* 广播协议:_sessix._tcp
|
|
17
|
+
* TXT 记录包含版本号和 HTTP 端口信息。
|
|
18
|
+
*/
|
|
19
|
+
export declare class MdnsService {
|
|
20
|
+
private bonjour;
|
|
21
|
+
private service;
|
|
22
|
+
private wsPort;
|
|
23
|
+
private httpPort;
|
|
24
|
+
private version;
|
|
25
|
+
constructor(options: MdnsServiceOptions);
|
|
26
|
+
/**
|
|
27
|
+
* 启动 mDNS 广播
|
|
28
|
+
*/
|
|
29
|
+
start(): void;
|
|
30
|
+
/**
|
|
31
|
+
* 停止 mDNS 广播
|
|
32
|
+
*/
|
|
33
|
+
stop(): void;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=MdnsService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MdnsService.d.ts","sourceRoot":"","sources":["../../src/mdns/MdnsService.ts"],"names":[],"mappings":"AAEA,gBAAgB;AAChB,UAAU,kBAAkB;IAC1B,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAQ;gBAEX,OAAO,EAAE,kBAAkB;IAMvC;;OAEG;IACH,KAAK,IAAI,IAAI;IAqBb;;OAEG;IACH,IAAI,IAAI,IAAI;CAeb"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MdnsService = void 0;
|
|
7
|
+
const bonjour_service_1 = __importDefault(require("bonjour-service"));
|
|
8
|
+
/**
|
|
9
|
+
* mDNS 局域网广播服务
|
|
10
|
+
*
|
|
11
|
+
* 使用 bonjour-service 在局域网内广播 Sessix 服务,
|
|
12
|
+
* 让手机端能自动发现 Mac 上运行的 Sessix 服务器。
|
|
13
|
+
*
|
|
14
|
+
* 广播协议:_sessix._tcp
|
|
15
|
+
* TXT 记录包含版本号和 HTTP 端口信息。
|
|
16
|
+
*/
|
|
17
|
+
class MdnsService {
|
|
18
|
+
bonjour = null;
|
|
19
|
+
service = null;
|
|
20
|
+
wsPort;
|
|
21
|
+
httpPort;
|
|
22
|
+
version;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.wsPort = options.wsPort;
|
|
25
|
+
this.httpPort = options.httpPort;
|
|
26
|
+
this.version = options.version ?? '0.1.0';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 启动 mDNS 广播
|
|
30
|
+
*/
|
|
31
|
+
start() {
|
|
32
|
+
if (this.bonjour) {
|
|
33
|
+
console.warn('[MdnsService] 服务已在运行中');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.bonjour = new bonjour_service_1.default();
|
|
37
|
+
this.service = this.bonjour.publish({
|
|
38
|
+
name: 'Sessix',
|
|
39
|
+
type: 'sessix',
|
|
40
|
+
port: this.wsPort,
|
|
41
|
+
txt: {
|
|
42
|
+
version: this.version,
|
|
43
|
+
httpPort: String(this.httpPort),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
console.log(`[MdnsService] mDNS 广播已启动: _sessix._tcp 端口 ${this.wsPort}`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 停止 mDNS 广播
|
|
50
|
+
*/
|
|
51
|
+
stop() {
|
|
52
|
+
if (this.service) {
|
|
53
|
+
this.service.stop?.(() => {
|
|
54
|
+
console.log('[MdnsService] 服务广播已停止');
|
|
55
|
+
});
|
|
56
|
+
this.service = null;
|
|
57
|
+
}
|
|
58
|
+
if (this.bonjour) {
|
|
59
|
+
this.bonjour.destroy();
|
|
60
|
+
this.bonjour = null;
|
|
61
|
+
}
|
|
62
|
+
console.log('[MdnsService] mDNS 服务已关闭');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.MdnsService = MdnsService;
|
|
66
|
+
//# sourceMappingURL=MdnsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MdnsService.js","sourceRoot":"","sources":["../../src/mdns/MdnsService.ts"],"names":[],"mappings":";;;;;;AAAA,sEAAuD;AAYvD;;;;;;;;GAQG;AACH,MAAa,WAAW;IACd,OAAO,GAAwC,IAAI,CAAA;IACnD,OAAO,GAAmB,IAAI,CAAA;IAC9B,MAAM,CAAQ;IACd,QAAQ,CAAQ;IAChB,OAAO,CAAQ;IAEvB,YAAY,OAA2B;QACrC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAA;IAC3C,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;YACrC,OAAM;QACR,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,yBAAO,EAAE,CAAA;QAE5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAClC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,GAAG,EAAE;gBACH,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;aAChC;SACF,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,CAAC,6CAA6C,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACzE,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE;gBACvB,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;YACtC,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACrB,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;YACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACrB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;IACzC,CAAC;CACF;AAvDD,kCAuDC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActivityKit Push 通知渠道
|
|
3
|
+
*
|
|
4
|
+
* 通过 APNs HTTP/2 直接发送 ActivityKit push notification,
|
|
5
|
+
* 用于在 App 后台时更新 Live Activity 内容。
|
|
6
|
+
*
|
|
7
|
+
* 注意:ActivityKit push 使用独立的 push token(不同于普通推送 token),
|
|
8
|
+
* 由 Activity 启动时生成,通过 pushTokenUpdates 获取。
|
|
9
|
+
*/
|
|
10
|
+
export interface ActivityPushConfig {
|
|
11
|
+
/** Apple Developer Team ID */
|
|
12
|
+
teamId: string;
|
|
13
|
+
/** APNs Auth Key ID */
|
|
14
|
+
keyId: string;
|
|
15
|
+
/** APNs Auth Key (.p8) 文件路径 */
|
|
16
|
+
authKeyPath: string;
|
|
17
|
+
/** 是否使用沙箱环境(开发模式) */
|
|
18
|
+
sandbox?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class ActivityPushChannel {
|
|
21
|
+
/** sessionId -> activityPushToken */
|
|
22
|
+
private tokens;
|
|
23
|
+
private teamId;
|
|
24
|
+
private keyId;
|
|
25
|
+
private authKey;
|
|
26
|
+
private apnsHost;
|
|
27
|
+
/** 缓存的 JWT token + 过期时间 */
|
|
28
|
+
private cachedJwt;
|
|
29
|
+
/** 复用的 HTTP/2 长连接 */
|
|
30
|
+
private http2Client;
|
|
31
|
+
constructor(config: ActivityPushConfig);
|
|
32
|
+
/** 获取或新建 HTTP/2 长连接 */
|
|
33
|
+
private getHttp2Client;
|
|
34
|
+
/** 注册 Activity push token */
|
|
35
|
+
addToken(sessionId: string, token: string): void;
|
|
36
|
+
/** 移除 Activity push token */
|
|
37
|
+
removeToken(sessionId: string): void;
|
|
38
|
+
/** 发送 content-state 更新到指定会话的 Live Activity */
|
|
39
|
+
updateActivity(sessionId: string, contentState: Record<string, unknown>): Promise<void>;
|
|
40
|
+
/** 发送带通知的 content-state 更新(审批请求时使用) */
|
|
41
|
+
updateActivityWithAlert(sessionId: string, contentState: Record<string, unknown>, alert: {
|
|
42
|
+
title: string;
|
|
43
|
+
body: string;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
/** 结束指定会话的 Live Activity */
|
|
46
|
+
endActivity(sessionId: string, contentState: Record<string, unknown>): Promise<void>;
|
|
47
|
+
/** 检查是否有指定会话的 token */
|
|
48
|
+
hasToken(sessionId: string): boolean;
|
|
49
|
+
/** 发送 APNs HTTP/2 请求 */
|
|
50
|
+
private sendToAPNs;
|
|
51
|
+
/** 生成或获取缓存的 APNs JWT token */
|
|
52
|
+
private getJWT;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=ActivityPushChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityPushChannel.d.ts","sourceRoot":"","sources":["../../src/notification/ActivityPushChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,kBAAkB;IACjC,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,qBAAa,mBAAmB;IAC9B,qCAAqC;IACrC,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;IAExB,2BAA2B;IAC3B,OAAO,CAAC,SAAS,CAAoD;IAErE,qBAAqB;IACrB,OAAO,CAAC,WAAW,CAAwC;gBAE/C,MAAM,EAAE,kBAAkB;IAUtC,uBAAuB;IACvB,OAAO,CAAC,cAAc;IAgBtB,6BAA6B;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKhD,6BAA6B;IAC7B,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIpC,8CAA8C;IACxC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7F,uCAAuC;IACjC,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GACrC,OAAO,CAAC,IAAI,CAAC;IAqBhB,4BAA4B;IACtB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB1F,uBAAuB;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIpC,wBAAwB;YACV,UAAU;IA0DxB,8BAA8B;IAC9B,OAAO,CAAC,MAAM;CA4Bf"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ActivityKit Push 通知渠道
|
|
4
|
+
*
|
|
5
|
+
* 通过 APNs HTTP/2 直接发送 ActivityKit push notification,
|
|
6
|
+
* 用于在 App 后台时更新 Live Activity 内容。
|
|
7
|
+
*
|
|
8
|
+
* 注意:ActivityKit push 使用独立的 push token(不同于普通推送 token),
|
|
9
|
+
* 由 Activity 启动时生成,通过 pushTokenUpdates 获取。
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.ActivityPushChannel = void 0;
|
|
46
|
+
const http2 = __importStar(require("node:http2"));
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const crypto = __importStar(require("node:crypto"));
|
|
49
|
+
class ActivityPushChannel {
|
|
50
|
+
/** sessionId -> activityPushToken */
|
|
51
|
+
tokens = new Map();
|
|
52
|
+
teamId;
|
|
53
|
+
keyId;
|
|
54
|
+
authKey;
|
|
55
|
+
apnsHost;
|
|
56
|
+
/** 缓存的 JWT token + 过期时间 */
|
|
57
|
+
cachedJwt = null;
|
|
58
|
+
/** 复用的 HTTP/2 长连接 */
|
|
59
|
+
http2Client = null;
|
|
60
|
+
constructor(config) {
|
|
61
|
+
this.teamId = config.teamId;
|
|
62
|
+
this.keyId = config.keyId;
|
|
63
|
+
this.authKey = fs.readFileSync(config.authKeyPath, 'utf-8');
|
|
64
|
+
this.apnsHost = config.sandbox
|
|
65
|
+
? 'api.sandbox.push.apple.com'
|
|
66
|
+
: 'api.push.apple.com';
|
|
67
|
+
console.log(`[ActivityPushChannel] 已初始化 (${config.sandbox ? '沙箱' : '生产'}模式)`);
|
|
68
|
+
}
|
|
69
|
+
/** 获取或新建 HTTP/2 长连接 */
|
|
70
|
+
getHttp2Client() {
|
|
71
|
+
if (this.http2Client && !this.http2Client.destroyed && !this.http2Client.closed) {
|
|
72
|
+
return this.http2Client;
|
|
73
|
+
}
|
|
74
|
+
this.http2Client = http2.connect(`https://${this.apnsHost}`);
|
|
75
|
+
this.http2Client.on('error', (err) => {
|
|
76
|
+
console.warn('[ActivityPushChannel] HTTP/2 连接错误,将在下次请求时重建:', err.message);
|
|
77
|
+
this.http2Client?.destroy();
|
|
78
|
+
this.http2Client = null;
|
|
79
|
+
});
|
|
80
|
+
this.http2Client.on('close', () => {
|
|
81
|
+
this.http2Client = null;
|
|
82
|
+
});
|
|
83
|
+
return this.http2Client;
|
|
84
|
+
}
|
|
85
|
+
/** 注册 Activity push token */
|
|
86
|
+
addToken(sessionId, token) {
|
|
87
|
+
this.tokens.set(sessionId, token);
|
|
88
|
+
console.log(`[ActivityPushChannel] 已注册 token: session=${sessionId}`);
|
|
89
|
+
}
|
|
90
|
+
/** 移除 Activity push token */
|
|
91
|
+
removeToken(sessionId) {
|
|
92
|
+
this.tokens.delete(sessionId);
|
|
93
|
+
}
|
|
94
|
+
/** 发送 content-state 更新到指定会话的 Live Activity */
|
|
95
|
+
async updateActivity(sessionId, contentState) {
|
|
96
|
+
const token = this.tokens.get(sessionId);
|
|
97
|
+
if (!token)
|
|
98
|
+
return;
|
|
99
|
+
const payload = {
|
|
100
|
+
aps: {
|
|
101
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
102
|
+
event: 'update',
|
|
103
|
+
'content-state': contentState,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
await this.sendToAPNs(token, payload);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.warn(`[ActivityPushChannel] 更新失败 session=${sessionId}:`, err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** 发送带通知的 content-state 更新(审批请求时使用) */
|
|
114
|
+
async updateActivityWithAlert(sessionId, contentState, alert) {
|
|
115
|
+
const token = this.tokens.get(sessionId);
|
|
116
|
+
if (!token)
|
|
117
|
+
return;
|
|
118
|
+
const payload = {
|
|
119
|
+
aps: {
|
|
120
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
121
|
+
event: 'update',
|
|
122
|
+
'content-state': contentState,
|
|
123
|
+
alert,
|
|
124
|
+
sound: 'default',
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
await this.sendToAPNs(token, payload);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.warn(`[ActivityPushChannel] 带提醒更新失败 session=${sessionId}:`, err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** 结束指定会话的 Live Activity */
|
|
135
|
+
async endActivity(sessionId, contentState) {
|
|
136
|
+
const token = this.tokens.get(sessionId);
|
|
137
|
+
if (!token)
|
|
138
|
+
return;
|
|
139
|
+
const payload = {
|
|
140
|
+
aps: {
|
|
141
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
142
|
+
event: 'end',
|
|
143
|
+
'content-state': contentState,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
try {
|
|
147
|
+
await this.sendToAPNs(token, payload);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.warn(`[ActivityPushChannel] 结束失败 session=${sessionId}:`, err);
|
|
151
|
+
}
|
|
152
|
+
this.tokens.delete(sessionId);
|
|
153
|
+
}
|
|
154
|
+
/** 检查是否有指定会话的 token */
|
|
155
|
+
hasToken(sessionId) {
|
|
156
|
+
return this.tokens.has(sessionId);
|
|
157
|
+
}
|
|
158
|
+
/** 发送 APNs HTTP/2 请求 */
|
|
159
|
+
async sendToAPNs(deviceToken, payload) {
|
|
160
|
+
const topic = 'com.kachun.sessix.push-type.liveactivity';
|
|
161
|
+
const jwt = this.getJWT();
|
|
162
|
+
const payloadStr = JSON.stringify(payload);
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
let client;
|
|
165
|
+
try {
|
|
166
|
+
client = this.getHttp2Client();
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
return reject(err);
|
|
170
|
+
}
|
|
171
|
+
const req = client.request({
|
|
172
|
+
':method': 'POST',
|
|
173
|
+
':path': `/3/device/${deviceToken}`,
|
|
174
|
+
'authorization': `bearer ${jwt}`,
|
|
175
|
+
'apns-topic': topic,
|
|
176
|
+
'apns-push-type': 'liveactivity',
|
|
177
|
+
'apns-priority': '10',
|
|
178
|
+
'apns-expiration': String(Math.floor(Date.now() / 1000) + 30),
|
|
179
|
+
'content-type': 'application/json',
|
|
180
|
+
'content-length': Buffer.byteLength(payloadStr),
|
|
181
|
+
});
|
|
182
|
+
let statusCode = 0;
|
|
183
|
+
let responseData = '';
|
|
184
|
+
req.on('response', (headers) => {
|
|
185
|
+
statusCode = Number(headers[':status'] ?? 0);
|
|
186
|
+
});
|
|
187
|
+
req.on('data', (chunk) => {
|
|
188
|
+
responseData += chunk;
|
|
189
|
+
});
|
|
190
|
+
req.on('end', () => {
|
|
191
|
+
if (statusCode === 200) {
|
|
192
|
+
resolve();
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// 连接可能已损坏,重置以便下次重建
|
|
196
|
+
if (statusCode === 0) {
|
|
197
|
+
this.http2Client?.destroy();
|
|
198
|
+
this.http2Client = null;
|
|
199
|
+
}
|
|
200
|
+
reject(new Error(`APNs 返回 ${statusCode}: ${responseData}`));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
req.on('error', (err) => {
|
|
204
|
+
reject(err);
|
|
205
|
+
});
|
|
206
|
+
req.write(payloadStr);
|
|
207
|
+
req.end();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/** 生成或获取缓存的 APNs JWT token */
|
|
211
|
+
getJWT() {
|
|
212
|
+
const now = Math.floor(Date.now() / 1000);
|
|
213
|
+
// JWT 有效期 50 分钟(Apple 限制 60 分钟)
|
|
214
|
+
if (this.cachedJwt && this.cachedJwt.expiresAt > now) {
|
|
215
|
+
return this.cachedJwt.token;
|
|
216
|
+
}
|
|
217
|
+
const header = Buffer.from(JSON.stringify({
|
|
218
|
+
alg: 'ES256',
|
|
219
|
+
kid: this.keyId,
|
|
220
|
+
})).toString('base64url');
|
|
221
|
+
const claims = Buffer.from(JSON.stringify({
|
|
222
|
+
iss: this.teamId,
|
|
223
|
+
iat: now,
|
|
224
|
+
})).toString('base64url');
|
|
225
|
+
const signingInput = `${header}.${claims}`;
|
|
226
|
+
const sign = crypto.createSign('SHA256');
|
|
227
|
+
sign.update(signingInput);
|
|
228
|
+
const signature = sign.sign(this.authKey, 'base64url');
|
|
229
|
+
const token = `${signingInput}.${signature}`;
|
|
230
|
+
this.cachedJwt = { token, expiresAt: now + 3000 }; // 50 分钟
|
|
231
|
+
return token;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.ActivityPushChannel = ActivityPushChannel;
|
|
235
|
+
//# sourceMappingURL=ActivityPushChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityPushChannel.js","sourceRoot":"","sources":["../../src/notification/ActivityPushChannel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NotificationChannel, NotificationPayload } from './MacNotificationChannel.js';
|
|
2
|
+
/**
|
|
3
|
+
* Expo Push 通知渠道
|
|
4
|
+
*
|
|
5
|
+
* 通过 Expo Push API 向已注册的设备发送推送通知。
|
|
6
|
+
* 支持多个 token(一台 Mac 可以连多台手机)。
|
|
7
|
+
*
|
|
8
|
+
* 数据流:Mac Server → Expo Push API → APNs → iPhone
|
|
9
|
+
*/
|
|
10
|
+
export declare class ExpoNotificationChannel implements NotificationChannel {
|
|
11
|
+
private tokens;
|
|
12
|
+
isAvailable(): boolean;
|
|
13
|
+
addToken(token: string): void;
|
|
14
|
+
removeToken(token: string): void;
|
|
15
|
+
send(payload: NotificationPayload): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ExpoNotificationChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoNotificationChannel.d.ts","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAI3F;;;;;;;GAOG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,MAAM,CAAyB;IAEvC,WAAW,IAAI,OAAO;IAItB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CA6BxD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExpoNotificationChannel = void 0;
|
|
4
|
+
const EXPO_PUSH_API = 'https://exp.host/--/api/v2/push/send';
|
|
5
|
+
/**
|
|
6
|
+
* Expo Push 通知渠道
|
|
7
|
+
*
|
|
8
|
+
* 通过 Expo Push API 向已注册的设备发送推送通知。
|
|
9
|
+
* 支持多个 token(一台 Mac 可以连多台手机)。
|
|
10
|
+
*
|
|
11
|
+
* 数据流:Mac Server → Expo Push API → APNs → iPhone
|
|
12
|
+
*/
|
|
13
|
+
class ExpoNotificationChannel {
|
|
14
|
+
tokens = new Set();
|
|
15
|
+
isAvailable() {
|
|
16
|
+
return this.tokens.size > 0;
|
|
17
|
+
}
|
|
18
|
+
addToken(token) {
|
|
19
|
+
this.tokens.add(token);
|
|
20
|
+
console.log(`[ExpoNotificationChannel] 已注册 push token,当前设备数: ${this.tokens.size}`);
|
|
21
|
+
}
|
|
22
|
+
removeToken(token) {
|
|
23
|
+
this.tokens.delete(token);
|
|
24
|
+
console.log(`[ExpoNotificationChannel] 已移除 push token,当前设备数: ${this.tokens.size}`);
|
|
25
|
+
}
|
|
26
|
+
async send(payload) {
|
|
27
|
+
if (this.tokens.size === 0)
|
|
28
|
+
return;
|
|
29
|
+
const messages = Array.from(this.tokens).map((to) => ({
|
|
30
|
+
to,
|
|
31
|
+
title: payload.title,
|
|
32
|
+
body: payload.body,
|
|
33
|
+
sound: payload.sound ?? 'default',
|
|
34
|
+
data: payload.data ?? {},
|
|
35
|
+
}));
|
|
36
|
+
try {
|
|
37
|
+
console.log('[ExpoNotificationChannel] 发送推送,tokens:', Array.from(this.tokens));
|
|
38
|
+
const res = await fetch(EXPO_PUSH_API, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
41
|
+
body: JSON.stringify(messages),
|
|
42
|
+
});
|
|
43
|
+
const body = await res.json();
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
console.warn('[ExpoNotificationChannel] Expo Push API 返回错误:', res.status, JSON.stringify(body));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log('[ExpoNotificationChannel] Expo Push API 响应:', JSON.stringify(body));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.warn('[ExpoNotificationChannel] 发送推送失败:', err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.ExpoNotificationChannel = ExpoNotificationChannel;
|
|
57
|
+
//# sourceMappingURL=ExpoNotificationChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoNotificationChannel.js","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":";;;AAEA,MAAM,aAAa,GAAG,sCAAsC,CAAA;AAE5D;;;;;;;GAOG;AACH,MAAa,uBAAuB;IAC1B,MAAM,GAAgB,IAAI,GAAG,EAAE,CAAA;IAEvC,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACtB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA4B;QACrC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,OAAM;QAElC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE;YACF,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;SACzB,CAAC,CAAC,CAAA;QAEH,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,wCAAwC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;YAC9E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE;gBACrC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBAC3E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aAC/B,CAAC,CAAA;YAEF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YACjG,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,6CAA6C,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YAClF,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;CACF;AA9CD,0DA8CC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface NotificationPayload {
|
|
2
|
+
title: string;
|
|
3
|
+
body: string;
|
|
4
|
+
sound?: string;
|
|
5
|
+
/** 附加数据,用于未来通知 Action */
|
|
6
|
+
data?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export interface NotificationChannel {
|
|
9
|
+
send(payload: NotificationPayload): Promise<void>;
|
|
10
|
+
isAvailable(): boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* macOS 本地通知渠道(临时方案)
|
|
14
|
+
*
|
|
15
|
+
* 使用 osascript 发送系统通知。
|
|
16
|
+
* 未来 Mac App 打包后替换为 UserNotifications.framework。
|
|
17
|
+
*/
|
|
18
|
+
export declare class MacNotificationChannel implements NotificationChannel {
|
|
19
|
+
isAvailable(): boolean;
|
|
20
|
+
send(payload: NotificationPayload): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=MacNotificationChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MacNotificationChannel.d.ts","sourceRoot":"","sources":["../../src/notification/MacNotificationChannel.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,WAAW,IAAI,OAAO,CAAA;CACvB;AAED;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,WAAW,IAAI,OAAO;IAItB,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CAkBlD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MacNotificationChannel = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
/**
|
|
6
|
+
* macOS 本地通知渠道(临时方案)
|
|
7
|
+
*
|
|
8
|
+
* 使用 osascript 发送系统通知。
|
|
9
|
+
* 未来 Mac App 打包后替换为 UserNotifications.framework。
|
|
10
|
+
*/
|
|
11
|
+
class MacNotificationChannel {
|
|
12
|
+
isAvailable() {
|
|
13
|
+
return process.platform === 'darwin';
|
|
14
|
+
}
|
|
15
|
+
send(payload) {
|
|
16
|
+
if (!this.isAvailable())
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
const title = payload.title.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
19
|
+
const body = payload.body.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
20
|
+
const sound = payload.sound ?? 'Ping';
|
|
21
|
+
const script = `display notification "${body}" with title "${title}" sound name "${sound}"`;
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
(0, node_child_process_1.execFile)('osascript', ['-e', script], (err) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
console.warn('[MacNotificationChannel] 发送通知失败:', err.message);
|
|
26
|
+
}
|
|
27
|
+
resolve();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.MacNotificationChannel = MacNotificationChannel;
|
|
33
|
+
//# sourceMappingURL=MacNotificationChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MacNotificationChannel.js","sourceRoot":"","sources":["../../src/notification/MacNotificationChannel.ts"],"names":[],"mappings":";;;AAAA,2DAA6C;AAe7C;;;;;GAKG;AACH,MAAa,sBAAsB;IACjC,WAAW;QACT,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAA;IACtC,CAAC;IAED,IAAI,CAAC,OAA4B;QAC/B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAEjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACvE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACrE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAA;QAErC,MAAM,MAAM,GAAG,yBAAyB,IAAI,iBAAiB,KAAK,iBAAiB,KAAK,GAAG,CAAA;QAE3F,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAA,6BAAQ,EAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC5C,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,CAAC,IAAI,CAAC,kCAAkC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;gBAC/D,CAAC;gBACD,OAAO,EAAE,CAAA;YACX,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAvBD,wDAuBC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ApprovalRequest } from '@sessix/shared';
|
|
2
|
+
import type { SessionManager } from '../session/SessionManager.js';
|
|
3
|
+
import type { NotificationChannel } from './MacNotificationChannel.js';
|
|
4
|
+
import type { ExpoNotificationChannel } from './ExpoNotificationChannel.js';
|
|
5
|
+
import type { ActivityPushChannel } from './ActivityPushChannel.js';
|
|
6
|
+
/**
|
|
7
|
+
* 通知服务
|
|
8
|
+
*
|
|
9
|
+
* 订阅 SessionManager 事件,根据事件类型构造通知内容,
|
|
10
|
+
* 并分发到所有已注册的通知渠道(Mac 本地通知、Expo Push 等)。
|
|
11
|
+
* 同时支持通过 ActivityPushChannel 更新 Live Activity。
|
|
12
|
+
*/
|
|
13
|
+
export declare class NotificationService {
|
|
14
|
+
private sessionManager;
|
|
15
|
+
private expoChannel;
|
|
16
|
+
private channelMap;
|
|
17
|
+
private unsubscribe;
|
|
18
|
+
private activityPushChannel;
|
|
19
|
+
/** YOLO 模式状态映射:sessionId -> isYoloMode */
|
|
20
|
+
private yoloModeState;
|
|
21
|
+
constructor(sessionManager: SessionManager, expoChannel?: ExpoNotificationChannel | null);
|
|
22
|
+
/** 添加通知渠道(id 唯一,可用于后续动态开关) */
|
|
23
|
+
addChannel(id: string, channel: NotificationChannel, enabled?: boolean): void;
|
|
24
|
+
/** 运行时切换指定渠道的启用状态 */
|
|
25
|
+
setChannelEnabled(id: string, enabled: boolean): void;
|
|
26
|
+
/** 注册手机 push token(连接建立时由 WsBridge 调用) */
|
|
27
|
+
addPushToken(token: string): void;
|
|
28
|
+
/** 移除手机 push token(断线时或手机主动注销时调用) */
|
|
29
|
+
removePushToken(token: string): void;
|
|
30
|
+
/** 设置 ActivityKit Push 渠道(可选,需要 APNs 认证配置) */
|
|
31
|
+
setActivityPushChannel(channel: ActivityPushChannel): void;
|
|
32
|
+
/** 注册 ActivityKit push token(由手机端启动 Live Activity 后上报) */
|
|
33
|
+
addActivityPushToken(sessionId: string, token: string): void;
|
|
34
|
+
/** 移除 ActivityKit push token */
|
|
35
|
+
removeActivityPushToken(sessionId: string): void;
|
|
36
|
+
/** 更新会话的 YOLO 模式状态 */
|
|
37
|
+
setYoloMode(sessionId: string, enabled: boolean): void;
|
|
38
|
+
/** 直接触发审批通知(由 ApprovalProxy 回调调用) */
|
|
39
|
+
notifyApproval(request: ApprovalRequest): void;
|
|
40
|
+
/** 简单的工具危险等级判断 */
|
|
41
|
+
private getDangerLevel;
|
|
42
|
+
/** 清理资源 */
|
|
43
|
+
destroy(): void;
|
|
44
|
+
private handleEvent;
|
|
45
|
+
private notify;
|
|
46
|
+
private getProjectName;
|
|
47
|
+
/** 获取会话的 YOLO 模式状态 */
|
|
48
|
+
private getYoloMode;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=NotificationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../src/notification/NotificationService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAuB,MAAM,6BAA6B,CAAA;AAC3F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAQ5B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,WAAW;IARrB,OAAO,CAAC,UAAU,CAAwE;IAC1F,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,0CAA0C;IAC1C,OAAO,CAAC,aAAa,CAA6B;gBAGxC,cAAc,EAAE,cAAc,EAC9B,WAAW,GAAE,uBAAuB,GAAG,IAAW;IAQ5D,8BAA8B;IAC9B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,UAAO,GAAG,IAAI;IAI1E,qBAAqB;IACrB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAKrD,0CAA0C;IAC1C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjC,qCAAqC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC,8CAA8C;IAC9C,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAI1D,0DAA0D;IAC1D,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5D,gCAAgC;IAChC,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIhD,sBAAsB;IACtB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAItD,qCAAqC;IACrC,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IA0C9C,kBAAkB;IAClB,OAAO,CAAC,cAAc;IAMtB,WAAW;IACX,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,WAAW;IA2CnB,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,cAAc;IAKtB,sBAAsB;IACtB,OAAO,CAAC,WAAW;CAGpB"}
|