@xopcai/xopc 0.0.293 → 0.0.295
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/browser-ext/dist/assets/{auth-C8gJZEsn.js → auth-B_9ZuGDx.js} +2 -1
- package/dist/browser-ext/dist/background.js +1 -1
- package/dist/browser-ext/dist/sidepanel.html +1 -1
- package/dist/browser-ext/dist/sidepanel.js +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DBoiUaYJ.js → agent-avatar-dicebear-CdaEBfdU.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BBBTq1DQ.js → agent-defaults-settings-panel-CVunPl7p.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DLyyYg6o.js → agents-DessO-kK.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Dt33QQO_.js → app-management-settings-panel-BeucVbqM.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-TbZePT0h.js → appearance-settings-D9QdN7OM.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-D1xQLM8o.js → apps-page-CbawVYBT.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-BlhqSDGJ.js → archive-plugin-fRMLP9dL.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DYN6uTdY.js → attachment-load-DVYW2whr.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-vR2Ox-54.js → automations-page-iS1Wmdlc.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-Bq2HWxpx.js → automations-workspace-ClZiIDCC.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-BwJI5t0J.js → binary-plugins-Bj3KFtIt.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CSVyQoll.js → block-editor-BLs4IKoH.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-BZ3-XyQW.js → browser-automations-page-BntIB4a1.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-BIJNmJgx.js → browser-settings-page-BMd_-Dq1.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-B9gmXaV6.js → capabilities-settings-panel-Cpb0KBZc.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-CVbrTHRw.js → channels-settings-DoRAjGiN.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BraLmomg.js → chat-terminal-dock-DH36RVtb.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-model-settings-DgLCyFBM.js → computer-model-settings-CH8z1kz0.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-BnbrU0bt.js → computer-settings-page-D8oeOhou.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-D1fqQ8a3.js → connectors-page-BpEEyOGa.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-C2z8-QnA.js → date-picker-BEZDMNzZ.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-BsC_4LIh.js → dependency-picker-DC8V8-Zp.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-Dk0QTgXU.js → desktop-pet-CJloB255.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-Dj7bIWMk.js → desktop-pet-settings-B97XYYYn.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-BpJkOK07.js → device-pairing-wizard-Cci5K_bT.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-C-A8q7eR.js → extension-debug-page-BTrK__r7.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BQitxYaL.js → extension-page-DgvpFyGJ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-C1TqQn1x.js → extension-settings-page-DjfuFesA.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-B6zLZO7B.js → gateway-settings-BsbofCP6.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CB9zJjx1.js → heartbeat-settings-BXxtbvha.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-C5CwdYuu.js → home-page-DErKqiEK.js} +1 -1
- package/dist/gateway/static/root/assets/{index-im9qTn_e.js → index-CEkw-fGy.js} +8 -8
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DpmGojOS.js → keyboard-shortcuts-settings-B7E6CgtA.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-BAnnVjV6.js → local-app-workbench-page-CQsTUpog.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-DVWnUNKI.js → local-apps-page-C10gURTO.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-BuuEU9H4.js → logs-page-Dg9Ma1sl.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-DuPqqO2p.js → management-settings-Bx1sNion.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C1mHdgxN.js → media-plugins-BNoJCDvT.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-CqJWQqLq.js → note-detail-panel-Bsiuk-2U.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-Dx4LmFQv.js → notes-page-BvAj-wx0.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-3VB72Q2A.js → page-tabs-B604dteb.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-C3gIPd87.js → preview-open-alternatives-Cp8vT7M-.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-BoXPVmE5.js → proactive-page-afUn4Km7.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-tD7h3hb1.js → project-detail-page-D6DLiVAL.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-COURhAyP.js → projects-page-Cq9hR-e_.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-DW0sfbOM.js → remote-access-hub-rAPyndf3.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Clq1U2Bs.js → runtime-tools-settings-panel-B5EkdFYg.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-D29HClcq.js → schema-form-BGMXczAd.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CN9mE6lf.js → sessions-page-DwLbo0cX.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-XmB-Qb44.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-BLX6yLkO.js → settings-form-section-Dzhqip3r.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-BVrp_njx.js → settings-page-DduNxMxn.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BT8UzHXg.js → setup-status-panel-ClmdhFhE.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DSooDmlI.js → share-preview-page-CpreOM1V.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-BQvk7HB9.js → shares-settings-CHkRyFET.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-DPhl0o2K.js → skill-reload-api-DWVyUbv4.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BTZgqlu5.js → skills-page-ClFiDnDu.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-DUL2MSIF.js → skills-page.utils-BR4q22o3.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-CLQ0kUhx.js → system-settings-panel-BaXq-D5z.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-D-mE7_Y9.js → task-detail-page-CJnZCq6v.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-Ct6uU5aE.js → tunnel-api-Dl9H37se.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-BM4VIISj.js → use-autosave-CTEqUAxS.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-BeF3xmRU.js → user-model-page-DOA5rAgd.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-Di5bEpbR.js → voice-api-key-field-CVCnJKQC.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BvUDSW1e.js → work-discovery-overlay-C8pcp5_j.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-4_qbvODo.js → workflow-run-setup-panel-CvayjHzR.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-Dn15wZnh.js → workflows-page-D7pBtuZt.js} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/device-pairing.js +9 -7
- package/dist/packages/gateway-contract/src/sessions.js +1 -0
- package/dist/src/agent/tool-manuals/computer.d.ts +1 -1
- package/dist/src/agent/tool-manuals/computer.js +1 -1
- package/dist/src/agent/tools/computer-use-tool.js +24 -14
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/computer/errors.d.ts +20 -0
- package/dist/src/computer/errors.js +24 -1
- package/dist/src/computer/model-adapter.d.ts +6 -0
- package/dist/src/computer/model-adapter.js +115 -24
- package/dist/src/computer/runtime.d.ts +26 -0
- package/dist/src/computer/runtime.js +5 -4
- package/dist/src/computer/task-state.d.ts +5 -1
- package/dist/src/computer/task-state.js +26 -11
- package/dist/src/config/schema.js +1 -1
- package/dist/src/gateway/hono/routes/device-push.js +11 -1
- package/dist/src/gateway/hono/routes/notes.js +1 -1
- package/dist/src/notes/types.d.ts +1 -1
- package/dist/src/notifications/device-store.js +1 -1
- package/dist/src/notifications/harmony-push.d.ts +26 -0
- package/dist/src/notifications/harmony-push.js +94 -0
- package/dist/src/notifications/service.d.ts +2 -0
- package/dist/src/notifications/service.js +15 -1
- package/dist/src/notifications/store.d.ts +2 -0
- package/dist/src/notifications/store.js +2 -1
- package/dist/src/notifications/types.d.ts +1 -1
- package/dist/src/storage/sqlite/device-access-repository.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/179_harmonyos_devices.sql +41 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +13 -5
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/package.json +3 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BAkTtjLG.js +0 -1
|
@@ -27,7 +27,7 @@ function normalizePreferences(input) {
|
|
|
27
27
|
function deviceFromRow(row) {
|
|
28
28
|
return {
|
|
29
29
|
id: row.device_id,
|
|
30
|
-
platform: row.platform === "android" ? "android" : "ios",
|
|
30
|
+
platform: row.platform === "harmonyos" ? "harmonyos" : row.platform === "android" ? "android" : "ios",
|
|
31
31
|
pushToken: row.push_token,
|
|
32
32
|
enabled: row.enabled !== 0,
|
|
33
33
|
permissions: row.permissions === "granted" || row.permissions === "denied" ? row.permissions : "unknown",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NotificationTarget } from '@xopcai/gateway-contract';
|
|
2
|
+
type ServiceAccount = {
|
|
3
|
+
project_id: string;
|
|
4
|
+
key_id: string;
|
|
5
|
+
sub_account: string;
|
|
6
|
+
private_key: string;
|
|
7
|
+
};
|
|
8
|
+
type PushCategory = 'WORK' | 'IM' | 'MARKETING';
|
|
9
|
+
export type HarmonyPushConfig = {
|
|
10
|
+
accountFile: string;
|
|
11
|
+
category: PushCategory;
|
|
12
|
+
testMessage: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type HarmonyNotification = {
|
|
15
|
+
pushToken: string;
|
|
16
|
+
eventId: string;
|
|
17
|
+
title: string;
|
|
18
|
+
body?: string;
|
|
19
|
+
gatewayId?: string;
|
|
20
|
+
target?: NotificationTarget;
|
|
21
|
+
};
|
|
22
|
+
export declare function harmonyPushConfig(env?: NodeJS.ProcessEnv): HarmonyPushConfig | undefined;
|
|
23
|
+
export declare function createHarmonyPushJwt(account: ServiceAccount, now?: number): string;
|
|
24
|
+
/** Uses HarmonyOS V3 service-account authentication, not the Android HMS OAuth flow. */
|
|
25
|
+
export declare function sendHarmonyPush(notification: HarmonyNotification, fetchImpl?: typeof fetch, config?: HarmonyPushConfig | undefined): Promise<string>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
import { constants, createHash, createPrivateKey, sign } from "node:crypto";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
//#region src/notifications/harmony-push.ts
|
|
5
|
+
function harmonyPushConfig(env = process.env) {
|
|
6
|
+
const accountFile = env.XOPC_HARMONY_PUSH_SERVICE_ACCOUNT?.trim();
|
|
7
|
+
const category = env.XOPC_HARMONY_PUSH_CATEGORY;
|
|
8
|
+
if (!accountFile || !isAbsolute(accountFile) || ![
|
|
9
|
+
"WORK",
|
|
10
|
+
"IM",
|
|
11
|
+
"MARKETING"
|
|
12
|
+
].includes(category ?? "")) return void 0;
|
|
13
|
+
return {
|
|
14
|
+
accountFile,
|
|
15
|
+
category,
|
|
16
|
+
testMessage: env.XOPC_HARMONY_PUSH_TEST_MESSAGE === "true"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createHarmonyPushJwt(account, now = Date.now()) {
|
|
20
|
+
if (!/^\d+$/.test(account.project_id) || !account.key_id || !account.sub_account || !account.private_key) throw new Error("Invalid Harmony push service account");
|
|
21
|
+
const key = createPrivateKey(account.private_key);
|
|
22
|
+
if (key.asymmetricKeyType !== "rsa" || (key.asymmetricKeyDetails?.modulusLength ?? 0) < 2048) throw new Error("Harmony push requires an RSA service account key of at least 2048 bits");
|
|
23
|
+
const iat = Math.floor(now / 1e3);
|
|
24
|
+
const input = `${Buffer.from(JSON.stringify({
|
|
25
|
+
alg: "PS256",
|
|
26
|
+
typ: "JWT",
|
|
27
|
+
kid: account.key_id
|
|
28
|
+
})).toString("base64url")}.${Buffer.from(JSON.stringify({
|
|
29
|
+
iss: account.sub_account,
|
|
30
|
+
aud: "https://oauth-login.cloud.huawei.com/oauth2/v3/token",
|
|
31
|
+
iat,
|
|
32
|
+
exp: iat + 3600
|
|
33
|
+
})).toString("base64url")}`;
|
|
34
|
+
return `${input}.${sign("sha256", Buffer.from(input), {
|
|
35
|
+
key,
|
|
36
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
37
|
+
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
|
|
38
|
+
}).toString("base64url")}`;
|
|
39
|
+
}
|
|
40
|
+
/** Uses HarmonyOS V3 service-account authentication, not the Android HMS OAuth flow. */
|
|
41
|
+
async function sendHarmonyPush(notification, fetchImpl = fetch, config = harmonyPushConfig()) {
|
|
42
|
+
if (!config) throw new Error("Harmony push is not configured");
|
|
43
|
+
let account;
|
|
44
|
+
let jwt;
|
|
45
|
+
try {
|
|
46
|
+
account = JSON.parse(await readFile(config.accountFile, "utf8"));
|
|
47
|
+
jwt = createHarmonyPushJwt(account);
|
|
48
|
+
} catch {
|
|
49
|
+
throw new Error("Harmony push service account could not be loaded");
|
|
50
|
+
}
|
|
51
|
+
const notifyId = createHash("sha256").update(notification.eventId).digest().readUInt32BE(0) & 2147483647;
|
|
52
|
+
const navigation = notification.gatewayId && notification.target ? Buffer.from(JSON.stringify({
|
|
53
|
+
gatewayId: notification.gatewayId,
|
|
54
|
+
eventId: notification.eventId,
|
|
55
|
+
target: notification.target
|
|
56
|
+
})).toString("base64url") : void 0;
|
|
57
|
+
const response = await fetchImpl(`https://push-api.cloud.huawei.com/v3/${account.project_id}/messages:send`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
redirect: "error",
|
|
60
|
+
signal: AbortSignal.timeout(1e4),
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
Authorization: `Bearer ${jwt}`,
|
|
64
|
+
"push-type": "0"
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
payload: { notification: {
|
|
68
|
+
category: config.category,
|
|
69
|
+
title: notification.title.slice(0, 100),
|
|
70
|
+
body: (notification.body || notification.title).slice(0, 200),
|
|
71
|
+
clickAction: navigation ? {
|
|
72
|
+
actionType: 1,
|
|
73
|
+
uri: `xopc://notification#p=${navigation}`
|
|
74
|
+
} : { actionType: 0 },
|
|
75
|
+
foregroundShow: false,
|
|
76
|
+
notifyId
|
|
77
|
+
} },
|
|
78
|
+
target: { token: [notification.pushToken] },
|
|
79
|
+
pushOptions: {
|
|
80
|
+
testMessage: config.testMessage,
|
|
81
|
+
ttl: 86400
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) throw new Error(`Harmony push request failed (${response.status})`);
|
|
86
|
+
const result = await response.json();
|
|
87
|
+
if (result.code !== "80000000" || typeof result.requestId !== "string" || !result.requestId) {
|
|
88
|
+
const code = typeof result.code === "string" && /^\d{8}$/.test(result.code) ? result.code : "invalid_response";
|
|
89
|
+
throw new Error(`Harmony push rejected (${code})`);
|
|
90
|
+
}
|
|
91
|
+
return result.requestId;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { createHarmonyPushJwt, harmonyPushConfig, sendHarmonyPush };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ProactiveChannelSender } from './proactive-channel.js';
|
|
2
2
|
import { type NotificationPlan } from './planner.js';
|
|
3
|
+
import { sendHarmonyPush } from './harmony-push.js';
|
|
3
4
|
export declare class NotificationService {
|
|
4
5
|
private readonly options;
|
|
5
6
|
private timer;
|
|
@@ -9,6 +10,7 @@ export declare class NotificationService {
|
|
|
9
10
|
publish: (type: string, payload: unknown) => void;
|
|
10
11
|
fetch?: typeof fetch;
|
|
11
12
|
sendChannel?: ProactiveChannelSender;
|
|
13
|
+
sendHarmony?: typeof sendHarmonyPush;
|
|
12
14
|
});
|
|
13
15
|
start(): void;
|
|
14
16
|
stop(): void;
|
|
@@ -3,6 +3,7 @@ import { init_logger } from "../utils/logger.js";
|
|
|
3
3
|
import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
4
4
|
import { localizeNotification } from "../../packages/gateway-contract/src/notifications.js";
|
|
5
5
|
import { init_src } from "../../packages/gateway-contract/src/index.js";
|
|
6
|
+
import { getOrCreateGatewayIdentity } from "../storage/sqlite/gateway-identity-repository.js";
|
|
6
7
|
import { proactivePreferences } from "../proactive/policy/service.js";
|
|
7
8
|
import { flushDueDigests } from "../proactive/inbox/digest.js";
|
|
8
9
|
import { notificationPlanFromGatewayEvent } from "./planner.js";
|
|
@@ -11,6 +12,7 @@ import { createNotificationEvent, deferNotificationDelivery, expireUndeliverable
|
|
|
11
12
|
import { drainChannelNotifications, enqueueChannelNotification } from "./proactive-channel.js";
|
|
12
13
|
import { drainBrowserPush, enqueueBrowserPush } from "./web-push.js";
|
|
13
14
|
import { disableNotificationDeviceForPushToken, listDeliverableNotificationDevices } from "./device-store.js";
|
|
15
|
+
import { sendHarmonyPush } from "./harmony-push.js";
|
|
14
16
|
//#region src/notifications/service.ts
|
|
15
17
|
init_transaction();
|
|
16
18
|
init_src();
|
|
@@ -162,6 +164,18 @@ var NotificationService = class {
|
|
|
162
164
|
const fetchImpl = this.options.fetch ?? fetch;
|
|
163
165
|
const localized = localizeNotification(delivery.event, delivery.locale);
|
|
164
166
|
try {
|
|
167
|
+
if (delivery.platform === "harmonyos") {
|
|
168
|
+
const ticket = await (this.options.sendHarmony ?? sendHarmonyPush)({
|
|
169
|
+
pushToken: delivery.pushToken,
|
|
170
|
+
eventId: delivery.event.id,
|
|
171
|
+
gatewayId: getOrCreateGatewayIdentity().id,
|
|
172
|
+
target: delivery.event.target,
|
|
173
|
+
title: delivery.event.type === "proactive.insight" ? delivery.locale === "zh" ? "有一项工作需要查看" : "A work update is ready" : localized.localizedTitle,
|
|
174
|
+
body: delivery.event.type === "proactive.insight" ? delivery.locale === "zh" ? "打开 xopc 查看详情。" : "Open xopc to review it." : localized.localizedBody
|
|
175
|
+
}, fetchImpl);
|
|
176
|
+
markNotificationDeliveryAccepted(delivery.event.id, delivery.deviceId, ticket, Number.MAX_SAFE_INTEGER);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
165
179
|
const response = await fetchImpl(EXPO_PUSH_URL, {
|
|
166
180
|
method: "POST",
|
|
167
181
|
headers: {
|
|
@@ -200,7 +214,7 @@ var NotificationService = class {
|
|
|
200
214
|
}
|
|
201
215
|
}
|
|
202
216
|
async checkReceipts() {
|
|
203
|
-
const withTickets = listDueNotificationDeliveries("accepted").filter((delivery) => delivery.providerTicketId);
|
|
217
|
+
const withTickets = listDueNotificationDeliveries("accepted").filter((delivery) => delivery.platform !== "harmonyos" && delivery.providerTicketId);
|
|
204
218
|
if (withTickets.length === 0) return;
|
|
205
219
|
const fetchImpl = this.options.fetch ?? fetch;
|
|
206
220
|
try {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type ProductNotification } from '@xopcai/gateway-contract';
|
|
2
|
+
import type { NotificationDevicePlatform } from './types.js';
|
|
2
3
|
export type NotificationDelivery = {
|
|
3
4
|
event: ProductNotification;
|
|
4
5
|
deviceId: string;
|
|
5
6
|
pushToken: string;
|
|
7
|
+
platform: NotificationDevicePlatform;
|
|
6
8
|
locale: 'en' | 'zh';
|
|
7
9
|
attempts: number;
|
|
8
10
|
providerTicketId?: string;
|
|
@@ -36,6 +36,7 @@ function deliveryFromRow(row) {
|
|
|
36
36
|
event: eventFromRow(row),
|
|
37
37
|
deviceId: row.device_id,
|
|
38
38
|
pushToken: row.push_token,
|
|
39
|
+
platform: row.platform,
|
|
39
40
|
locale: row.locale === "zh" ? "zh" : "en",
|
|
40
41
|
attempts: row.attempts,
|
|
41
42
|
...row.provider_ticket_id ? { providerTicketId: row.provider_ticket_id } : {}
|
|
@@ -88,7 +89,7 @@ function acknowledgeNotification(eventId, consumerId, surface) {
|
|
|
88
89
|
}
|
|
89
90
|
const DELIVERY_JOIN = `
|
|
90
91
|
SELECT e.*, d.device_id, d.attempts, d.provider_ticket_id,
|
|
91
|
-
n.push_token, n.locale
|
|
92
|
+
n.push_token, n.locale, n.platform
|
|
92
93
|
FROM notification_deliveries d
|
|
93
94
|
JOIN notification_events e ON e.event_id = d.event_id
|
|
94
95
|
JOIN device_push_endpoints n ON n.device_id = d.device_id
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type NotificationDevicePlatform = 'ios' | 'android';
|
|
1
|
+
export type NotificationDevicePlatform = 'ios' | 'android' | 'harmonyos';
|
|
2
2
|
export type NotificationPermission = 'granted' | 'denied' | 'unknown';
|
|
3
3
|
export type NotificationLanguage = 'en' | 'zh';
|
|
4
4
|
export type NotificationPreferences = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import type { GatewayScope } from '../../gateway/security/gateway-scopes.js';
|
|
3
|
-
export type DevicePlatform = '
|
|
3
|
+
export type DevicePlatform = import('@xopcai/gateway-contract').DevicePlatform;
|
|
4
4
|
export type DevicePublicKeyJwk = crypto.webcrypto.JsonWebKey;
|
|
5
5
|
export type DeviceRecord = {
|
|
6
6
|
id: string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
-- The runner disables foreign keys outside the transaction for this parent-table rebuild.
|
|
2
|
+
-- All existing device identities and dependent credential/session records are retained.
|
|
3
|
+
-- The _new tables exist only during this rebuild; all platforms still share the original table names.
|
|
4
|
+
CREATE TABLE devices_new (
|
|
5
|
+
device_id TEXT PRIMARY KEY,
|
|
6
|
+
display_name TEXT NOT NULL,
|
|
7
|
+
platform TEXT NOT NULL CHECK (platform IN ('ios', 'android', 'harmonyos', 'chrome')),
|
|
8
|
+
extension_id TEXT CHECK (extension_id IS NULL OR length(extension_id) = 32),
|
|
9
|
+
public_key_jwk TEXT NOT NULL,
|
|
10
|
+
scopes_json TEXT NOT NULL,
|
|
11
|
+
created_at INTEGER NOT NULL,
|
|
12
|
+
last_seen_at INTEGER,
|
|
13
|
+
revoked_at INTEGER,
|
|
14
|
+
CHECK ((platform = 'chrome') = (extension_id IS NOT NULL))
|
|
15
|
+
);
|
|
16
|
+
INSERT INTO devices_new
|
|
17
|
+
(device_id, display_name, platform, extension_id, public_key_jwk, scopes_json, created_at, last_seen_at, revoked_at)
|
|
18
|
+
SELECT device_id, display_name, platform, extension_id, public_key_jwk, scopes_json, created_at, last_seen_at, revoked_at
|
|
19
|
+
FROM devices;
|
|
20
|
+
DROP TABLE devices;
|
|
21
|
+
ALTER TABLE devices_new RENAME TO devices;
|
|
22
|
+
|
|
23
|
+
CREATE TABLE device_push_endpoints_new (
|
|
24
|
+
device_id TEXT PRIMARY KEY REFERENCES devices(device_id) ON DELETE CASCADE,
|
|
25
|
+
platform TEXT NOT NULL CHECK (platform IN ('ios', 'android', 'harmonyos')),
|
|
26
|
+
push_token TEXT NOT NULL UNIQUE,
|
|
27
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
28
|
+
permissions TEXT NOT NULL CHECK (permissions IN ('granted', 'denied', 'unknown')),
|
|
29
|
+
preferences_json TEXT NOT NULL DEFAULT '{}',
|
|
30
|
+
locale TEXT NOT NULL CHECK (locale IN ('en', 'zh')),
|
|
31
|
+
app_version TEXT,
|
|
32
|
+
lease_expires_at INTEGER NOT NULL,
|
|
33
|
+
last_seen_at INTEGER NOT NULL,
|
|
34
|
+
created_at INTEGER NOT NULL,
|
|
35
|
+
updated_at INTEGER NOT NULL
|
|
36
|
+
);
|
|
37
|
+
INSERT INTO device_push_endpoints_new
|
|
38
|
+
SELECT device_id, platform, push_token, enabled, permissions, preferences_json, locale,
|
|
39
|
+
app_version, lease_expires_at, last_seen_at, created_at, updated_at FROM device_push_endpoints;
|
|
40
|
+
DROP TABLE device_push_endpoints;
|
|
41
|
+
ALTER TABLE device_push_endpoints_new RENAME TO device_push_endpoints;
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 179;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -35,19 +35,27 @@ function applySingleMigration(db, migration) {
|
|
|
35
35
|
targetVersion: migration.targetVersion,
|
|
36
36
|
file: migration.filename
|
|
37
37
|
}, "Applying SQLite migration");
|
|
38
|
-
|
|
38
|
+
const rebuildDevices = migration.targetVersion === 179;
|
|
39
|
+
const foreignKeysEnabled = Number(db.prepare("PRAGMA foreign_keys").get()?.foreign_keys) === 1;
|
|
40
|
+
if (rebuildDevices) db.exec("PRAGMA foreign_keys = OFF");
|
|
41
|
+
let transactionStarted = false;
|
|
39
42
|
try {
|
|
43
|
+
db.exec("BEGIN IMMEDIATE");
|
|
44
|
+
transactionStarted = true;
|
|
40
45
|
db.exec(migration.sql);
|
|
41
46
|
const summary = migration.targetVersion === 178 ? migrateConversationUuids(db) : void 0;
|
|
47
|
+
if (rebuildDevices && db.prepare("PRAGMA foreign_key_check").all().length > 0) throw new Error("Device platform migration would violate foreign key integrity");
|
|
42
48
|
setSchemaVersion(db, migration.targetVersion);
|
|
43
49
|
db.exec("COMMIT");
|
|
44
50
|
return summary;
|
|
45
51
|
} catch (error) {
|
|
46
52
|
try {
|
|
47
|
-
db.exec("ROLLBACK");
|
|
53
|
+
if (transactionStarted) db.exec("ROLLBACK");
|
|
48
54
|
} catch {}
|
|
49
55
|
const em = error instanceof Error ? error.message : String(error);
|
|
50
56
|
throw new Error(`SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`, { cause: error });
|
|
57
|
+
} finally {
|
|
58
|
+
if (rebuildDevices && foreignKeysEnabled) db.exec("PRAGMA foreign_keys = ON");
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
@@ -55,7 +63,7 @@ function applySingleMigration(db, migration) {
|
|
|
55
63
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
56
64
|
*/
|
|
57
65
|
function applyPendingMigrations(db, options = {}) {
|
|
58
|
-
const targetVersion = options.targetVersion ??
|
|
66
|
+
const targetVersion = options.targetVersion ?? 179;
|
|
59
67
|
let currentVersion = readSchemaVersion(db);
|
|
60
68
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
61
69
|
if (!options.migrationsDir && currentVersion < 165) throw new DatabaseSchemaTooOldError(currentVersion, 165);
|
|
@@ -110,7 +118,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
110
118
|
}
|
|
111
119
|
/** Inspect schema version without mutating the database. */
|
|
112
120
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
113
|
-
const appVersion = options.targetVersion ??
|
|
121
|
+
const appVersion = options.targetVersion ?? 179;
|
|
114
122
|
const dbVersion = readSchemaVersion(db);
|
|
115
123
|
if (dbVersion > appVersion) return {
|
|
116
124
|
dbVersion,
|
|
@@ -149,7 +157,7 @@ var init_runner = __esmMin((() => {
|
|
|
149
157
|
init_errors();
|
|
150
158
|
log = createLogger("Sqlite:Migrations");
|
|
151
159
|
XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
152
|
-
XOPC_DB_SCHEMA_VERSION =
|
|
160
|
+
XOPC_DB_SCHEMA_VERSION = 179;
|
|
153
161
|
MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
154
162
|
}));
|
|
155
163
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.295",
|
|
4
4
|
"description": "Local-first personal AI assistant with reviewable user understanding, durable tasks, workflows, automations, and local or cloud models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -196,6 +196,8 @@
|
|
|
196
196
|
"mobile:typecheck": "pnpm -C apps/mobile-expo run typecheck",
|
|
197
197
|
"mobile:test": "pnpm -C apps/mobile-expo test",
|
|
198
198
|
"mobile:test:stream": "pnpm --filter @xopcai/agent-stream-client test",
|
|
199
|
+
"harmony:verify": "tsx apps/mobile-harmony/scripts/verify.mts",
|
|
200
|
+
"harmony:test:server": "tsx apps/mobile-harmony/scripts/native-test-server.mts",
|
|
199
201
|
"mobile:release:patch": "bash scripts/apps/mobile-expo/release-patch.sh",
|
|
200
202
|
"mobile:configure:ios:github": "bash scripts/apps/mobile-expo/configure-ios-github-secrets.sh",
|
|
201
203
|
"mobile:release:ios:testflight": "bash scripts/apps/mobile-expo/release-ios-testflight.sh",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{xn as e}from"./index-im9qTn_e.js";function t(t){let{children:n,fallback:r}=t,i=r===void 0?null:r;return e()?n:i}export{t};
|