@senguoyun/dsh-arkme 0.1.32 → 0.1.34

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 (62) hide show
  1. package/README.md +24 -0
  2. package/lib/client.js +4247 -4140
  3. package/lib/client.js.map +1 -1
  4. package/lib/extension-profile-restart-helper.js +17 -5
  5. package/lib/index.js +412 -23
  6. package/lib/persistent-extension.js +1 -1
  7. package/lib/{persistent-runtime-CFeIg6aw.js → persistent-runtime-CZQTfyC1.js} +35814 -6704
  8. package/lib/types/arkme-service.d.ts +9 -1
  9. package/lib/types/arkme-service.d.ts.map +1 -1
  10. package/lib/types/client/ArkmeExtensionRecoveryNotice.d.ts +9 -0
  11. package/lib/types/client/ArkmeExtensionRecoveryNotice.d.ts.map +1 -0
  12. package/lib/types/client/ArkmeFooterAction.d.ts +1 -7
  13. package/lib/types/client/ArkmeFooterAction.d.ts.map +1 -1
  14. package/lib/types/client/ArkmeFooterDropdown.d.ts.map +1 -1
  15. package/lib/types/client/ArkmeMarketplace.d.ts +2 -1
  16. package/lib/types/client/ArkmeMarketplace.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeMessageSnapshotDialog.d.ts +17 -0
  18. package/lib/types/client/ArkmeMessageSnapshotDialog.d.ts.map +1 -0
  19. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  20. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  21. package/lib/types/client/ArkmeRichComposerInput.d.ts +3 -1
  22. package/lib/types/client/ArkmeRichComposerInput.d.ts.map +1 -1
  23. package/lib/types/client/ArkmeSettingsSurface.d.ts +4 -19
  24. package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
  25. package/lib/types/client/ArkmeSidebar.d.ts +1 -1
  26. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  27. package/lib/types/client/ArkmeUpdateSurfaces.d.ts +38 -5
  28. package/lib/types/client/ArkmeUpdateSurfaces.d.ts.map +1 -1
  29. package/lib/types/client/api.d.ts +1 -1
  30. package/lib/types/client/api.d.ts.map +1 -1
  31. package/lib/types/client/desktop-quarantine-status.d.ts +3 -0
  32. package/lib/types/client/desktop-quarantine-status.d.ts.map +1 -0
  33. package/lib/types/client/index.d.ts +0 -2
  34. package/lib/types/client/index.d.ts.map +1 -1
  35. package/lib/types/client/record-capture-location.d.ts +8 -0
  36. package/lib/types/client/record-capture-location.d.ts.map +1 -0
  37. package/lib/types/client/update-presentation.d.ts +1 -1
  38. package/lib/types/client/update-presentation.d.ts.map +1 -1
  39. package/lib/types/client/update-ui-controller.d.ts +1 -1
  40. package/lib/types/client/update-ui-controller.d.ts.map +1 -1
  41. package/lib/types/extensions/desktop-quarantine.d.ts +79 -0
  42. package/lib/types/extensions/desktop-quarantine.d.ts.map +1 -0
  43. package/lib/types/extensions/profile-installer.d.ts +5 -0
  44. package/lib/types/extensions/profile-installer.d.ts.map +1 -1
  45. package/lib/types/extensions/profile-restart-helper.d.ts +3 -1
  46. package/lib/types/extensions/profile-restart-helper.d.ts.map +1 -1
  47. package/lib/types/host-api.d.ts +3 -1
  48. package/lib/types/host-api.d.ts.map +1 -1
  49. package/lib/types/index.d.ts +1 -0
  50. package/lib/types/index.d.ts.map +1 -1
  51. package/lib/types/local-database.d.ts +1 -0
  52. package/lib/types/local-database.d.ts.map +1 -1
  53. package/lib/types/services/chat-service.d.ts +14 -1
  54. package/lib/types/services/chat-service.d.ts.map +1 -1
  55. package/lib/types/services/group-ai-polish-service.d.ts +3 -1
  56. package/lib/types/services/group-ai-polish-service.d.ts.map +1 -1
  57. package/lib/types/services/record-service.d.ts +10 -3
  58. package/lib/types/services/record-service.d.ts.map +1 -1
  59. package/lib/types/state-store.d.ts.map +1 -1
  60. package/lib/types/types.d.ts +50 -1
  61. package/lib/types/types.d.ts.map +1 -1
  62. package/package.json +6 -5
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as securePrivateDirectory, c as securePrivateFileSync, i as prepareProfilePackageManager, n as isArkmeProfilePackageName, o as securePrivateDirectorySync, r as localExtensionPnpmArgs, s as securePrivateFile, t as applyArkmeProfileBundlePolicy } from "./profile-bundle-policy-CvojZuTD.js";
2
- import { $ as ARKME_RUNTIME_INSTANCE_ID, A as inspectNativeBundleArtifactV3, D as bundleSha256, E as arkmeSandboxEntryId, F as assertExtensionArtifactSize, G as validateVoiceprintPcm16Wav, I as canonicalExtensionJson, K as ARKME_DEFAULT_SHARE_WEBSITE, L as sha256Hex, M as packLocalNativeBundleDirectoryV3, N as readNativeBundleTarballV3, O as canonicalBundleJson, P as renderArkmeSandboxHostEntry, Q as ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, R as unpackArkmeExtension, S as ARKME_BUNDLE_ARTIFACT_KIND, T as ArkmeBundleArtifactError, U as ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE, W as ArkmeService, Z as ARKME_WORLD_PUBLISH_MAX_IMAGES, _ as arkmeClientContentDigest, at as ArkmePluginError, b as verifyExtensionResolutionSignature, c as persistentExtensionBundleDirectory, d as readPersistentExtensionActivation, et as ARKME_TOOL_FILE_MAX_BYTES, f as refreshPersistentClientWrapper, g as activeProfileExtensionOwnerConflicts, h as renderArkmeBundleClientBundle, i as invokePersistentArkmeExtension, it as isArkmeBotAvatarRef, j as packBundleFiles, k as inspectBundleArtifact, l as persistentExtensionPackageIdentity, m as writePersistentExtensionActivation, nt as arkmeNormalizedFileMimeType, o as persistentArkmeExtensionRuntimeState, ot as ArkmeOutgoingCallError, p as repairPersistentExtensionInstallation, q as ARKME_PROVIDER_CONTRACT_VERSION, r as deactivatePersistentArkmeExtension, rt as arkmePickedFileKind, s as materializePersistentExtensionBundle, t as activatePersistentArkmeExtension, tt as arkmeCanInlineLocalFile, u as quarantinePersistentExtension, v as arkmeClientContentDigestFromSource, w as ARKME_NATIVE_BUNDLE_ARTIFACT_KIND, x as verifyNativeBundleResolutionSignature, y as verifyBundleResolutionSignature, z as ARKME_EXTENSION_FORMAT } from "./persistent-runtime-CFeIg6aw.js";
2
+ import { $ as ARKME_RUNTIME_INSTANCE_ID, A as inspectNativeBundleArtifactV3, D as bundleSha256, E as arkmeSandboxEntryId, F as assertExtensionArtifactSize, G as validateVoiceprintPcm16Wav, I as canonicalExtensionJson, K as ARKME_DEFAULT_SHARE_WEBSITE, L as sha256Hex, M as packLocalNativeBundleDirectoryV3, N as readNativeBundleTarballV3, O as canonicalBundleJson, P as renderArkmeSandboxHostEntry, Q as ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, R as unpackArkmeExtension, S as ARKME_BUNDLE_ARTIFACT_KIND, T as ArkmeBundleArtifactError, U as ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE, W as ArkmeService, Z as ARKME_WORLD_PUBLISH_MAX_IMAGES, _ as arkmeClientContentDigest, at as ArkmePluginError, b as verifyExtensionResolutionSignature, c as persistentExtensionBundleDirectory, d as readPersistentExtensionActivation, et as ARKME_TOOL_FILE_MAX_BYTES, f as refreshPersistentClientWrapper, g as activeProfileExtensionOwnerConflicts, h as renderArkmeBundleClientBundle, i as invokePersistentArkmeExtension, it as isArkmeBotAvatarRef, j as packBundleFiles, k as inspectBundleArtifact, l as persistentExtensionPackageIdentity, m as writePersistentExtensionActivation, nt as arkmeNormalizedFileMimeType, o as persistentArkmeExtensionRuntimeState, ot as ArkmeOutgoingCallError, p as repairPersistentExtensionInstallation, q as ARKME_PROVIDER_CONTRACT_VERSION, r as deactivatePersistentArkmeExtension, rt as arkmePickedFileKind, s as materializePersistentExtensionBundle, t as activatePersistentArkmeExtension, tt as arkmeCanInlineLocalFile, u as quarantinePersistentExtension, v as arkmeClientContentDigestFromSource, w as ARKME_NATIVE_BUNDLE_ARTIFACT_KIND, x as verifyNativeBundleResolutionSignature, y as verifyBundleResolutionSignature, z as ARKME_EXTENSION_FORMAT } from "./persistent-runtime-CZQTfyC1.js";
3
3
  import { _ as parsePluginUpdateManifest, b as validatePluginUpdateServiceOrigin, d as writePluginUpdateLifecycleLog, f as PluginUpdateInstallStateStore, g as existingCachedPluginArtifactPath, h as downloadAndCachePluginArtifact, m as PluginUpdateArtifactError, n as buildTargetInstallArgs, o as reconcilePluginUpdaterProfilePolicy, p as ARKME_PLUGIN_PACKAGE_NAME$1, t as assertTargetArtifactIntegrity, u as detachManagedProfilePluginLink, v as pluginUpdateEndpointUrl, y as validatePluginUpdateArtifactOrigin } from "./plugin-updater-helper-BByw0ZyG.js";
4
4
  import { i as invokeArkmeBundle, n as arkmeBundleActive, r as deactivateArkmeBundle } from "./bundle-runtime-BTzRlc6V.js";
5
5
  import { t as ArkmeExtensionInstallStore } from "./install-store-C1tAM346.js";
@@ -10,7 +10,7 @@ import { dirname, isAbsolute, join, relative, resolve, sep, win32 } from "node:p
10
10
  import { fileURLToPath, pathToFileURL } from "node:url";
11
11
  import Schema from "@deepseek-ai/schemastery";
12
12
  import { execFile, spawn } from "node:child_process";
13
- import { chmod as chmod$1, mkdir, open, readFile, realpath, rename, stat, unlink, writeFile } from "node:fs/promises";
13
+ import { chmod as chmod$1, mkdir, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
14
14
  import { promisify } from "node:util";
15
15
  import { createServer } from "node:http";
16
16
  import { Readable } from "node:stream";
@@ -1517,6 +1517,43 @@ function numberParam(params, key, fallback) {
1517
1517
  const value = params[key];
1518
1518
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
1519
1519
  }
1520
+ function captureContextParam(params) {
1521
+ const raw = params.captureContext;
1522
+ if (raw === void 0) return void 0;
1523
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new ArkmePluginError("capture-context-invalid", "记忆快照参数无效", false, 400);
1524
+ const value = raw;
1525
+ const clientName = stringParam(value, "clientName").trim().slice(0, 120);
1526
+ const networkName = stringParam(value, "networkName").trim().slice(0, 120);
1527
+ const electric = typeof value.electric === "number" && Number.isFinite(value.electric) ? Math.trunc(value.electric) : void 0;
1528
+ const charge = Math.trunc(numberParam(value, "charge", 0));
1529
+ const context = {
1530
+ ...clientName === "" ? {} : { clientName },
1531
+ ...networkName === "" ? {} : { networkName },
1532
+ ...electric === void 0 || electric < 0 || electric > 100 ? {} : { electric },
1533
+ ...charge < 1 || charge > 3 ? {} : { charge }
1534
+ };
1535
+ return Object.keys(context).length === 0 ? void 0 : context;
1536
+ }
1537
+ function recordLocationCaptureParam(params) {
1538
+ const raw = params.location;
1539
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new ArkmePluginError("record-location-invalid", "位置快照参数无效", false, 400);
1540
+ const value = raw;
1541
+ const latitude = numberParam(value, "latitude", NaN);
1542
+ const longitude = numberParam(value, "longitude", NaN);
1543
+ if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90 || !Number.isFinite(longitude) || longitude < -180 || longitude > 180) throw new ArkmePluginError("record-location-invalid", "位置坐标无效", false, 400);
1544
+ const altitudeMeters = numberParam(value, "altitudeMeters", NaN);
1545
+ const speedMetersPerSecond = numberParam(value, "speedMetersPerSecond", NaN);
1546
+ const accuracyMeters = numberParam(value, "accuracyMeters", NaN);
1547
+ const capturedAtMillis = Math.trunc(numberParam(value, "capturedAtMillis", 0));
1548
+ return {
1549
+ latitude,
1550
+ longitude,
1551
+ capturedAtMillis: capturedAtMillis > 0 ? capturedAtMillis : Date.now(),
1552
+ ...Number.isFinite(accuracyMeters) && accuracyMeters >= 0 ? { accuracyMeters } : {},
1553
+ ...Number.isFinite(altitudeMeters) ? { altitudeMeters } : {},
1554
+ ...Number.isFinite(speedMetersPerSecond) && speedMetersPerSecond >= 0 ? { speedMetersPerSecond } : {}
1555
+ };
1556
+ }
1520
1557
  const ARKME_DIRECTORY_SECTIONS = /* @__PURE__ */ new Set([
1521
1558
  "groups",
1522
1559
  "bots",
@@ -1773,6 +1810,8 @@ function botRefsParam(params) {
1773
1810
  function richSendParam(params) {
1774
1811
  const rawAssets = Array.isArray(params.assets) ? params.assets : [];
1775
1812
  const thinkingDurationMillis = Math.max(0, Math.trunc(numberParam(params, "thinkingDurationMillis", 0)));
1813
+ const recordDurationMillis = Math.max(0, Math.trunc(numberParam(params, "recordDurationMillis", 0)));
1814
+ const captureContext = captureContextParam(params);
1776
1815
  const humanMentions = humanMentionsParam(params);
1777
1816
  const botMentions = botMentionsParam(params);
1778
1817
  return {
@@ -1780,6 +1819,8 @@ function richSendParam(params) {
1780
1819
  textContent: stringParam(params, "textContent"),
1781
1820
  displayKind: numberParam(params, "displayKind", 0) === 1 ? 1 : 0,
1782
1821
  ...thinkingDurationMillis === 0 ? {} : { thinkingDurationMillis },
1822
+ ...recordDurationMillis === 0 ? {} : { recordDurationMillis },
1823
+ ...captureContext === void 0 ? {} : { captureContext },
1783
1824
  ...humanMentions.length === 0 ? {} : { humanMentions },
1784
1825
  ...botMentions.length === 0 ? {} : { botMentions },
1785
1826
  assets: rawAssets.flatMap((raw) => {
@@ -1893,11 +1934,13 @@ function createArkmeHostApi(service, options) {
1893
1934
  "extensions.persistent.invoke",
1894
1935
  "extensions.bundle.invoke",
1895
1936
  "extensions.mine.publish",
1937
+ "extensions.quarantine.dismiss",
1938
+ "extensions.quarantine.reenable",
1896
1939
  "remote.renameDesktop"
1897
1940
  ].includes(request.operation) && origin === void 0) throw new ArkmePluginError("origin-required", "扩展变更必须从当前 DSH 页面发起", false, 403);
1898
1941
  writeJson$4(res, 200, {
1899
1942
  ok: true,
1900
- value: await dispatchArkmeHostOperation(service, request.operation, params, options.updateManager, options.extensionManager?.(), options.extensionInstallTasks?.(), options.ownedExtensionInventory?.(), controller.signal, options.remoteHost?.())
1943
+ value: await dispatchArkmeHostOperation(service, request.operation, params, options.updateManager, options.extensionManager?.(), options.extensionInstallTasks?.(), options.ownedExtensionInventory?.(), controller.signal, options.remoteHost?.(), options.desktopQuarantine)
1901
1944
  });
1902
1945
  } catch (error) {
1903
1946
  if (controller.signal.aborted && res.destroyed) return;
@@ -1915,7 +1958,7 @@ function createArkmeHostApi(service, options) {
1915
1958
  }
1916
1959
  };
1917
1960
  }
1918
- async function dispatchArkmeHostOperation(service, operation, params, updateManager, extensionManager, extensionInstallTasks, ownedExtensionInventory, requestSignal, remoteHost) {
1961
+ async function dispatchArkmeHostOperation(service, operation, params, updateManager, extensionManager, extensionInstallTasks, ownedExtensionInventory, requestSignal, remoteHost, desktopQuarantine) {
1919
1962
  switch (operation) {
1920
1963
  case "provider.capabilities": return service.providerCapabilities();
1921
1964
  case "provider.instance": return { instanceId: ARKME_RUNTIME_INSTANCE_ID };
@@ -2238,6 +2281,10 @@ async function dispatchArkmeHostOperation(service, operation, params, updateMana
2238
2281
  case "source.message-copy-link.resolve": return await service.resolveMessageCopyLink(stringParam(params, "sid"), requestSignal === void 0 ? {} : { signal: requestSignal });
2239
2282
  case "source.message-copy-link.extend": return await service.extendMessageCopyLink(stringParam(params, "sid"), numberParam(params, "itemIndex", 0), stringParam(params, "textContent"), stringParam(params, "recordUid"), requestSignal === void 0 ? {} : { signal: requestSignal });
2240
2283
  case "source.shared-recording-detail": return await service.sharedRecordingDetail(stringParam(params, "detailRef"), requestSignal === void 0 ? {} : { signal: requestSignal });
2284
+ case "source.message-snapshot.detail": return await service.messageSnapshotDetail(stringParam(params, "sourceRef"), stringParam(params, "actionRef"), requestSignal === void 0 ? {} : { signal: requestSignal });
2285
+ case "source.message-location.set":
2286
+ await service.saveMessageLocation(stringParam(params, "sourceRef"), stringParam(params, "itemUid"), recordLocationCaptureParam(params), numberParam(params, "recordVersion", 0) || void 0, requestSignal === void 0 ? {} : { signal: requestSignal });
2287
+ return { ok: true };
2241
2288
  case "source.forward-messages": return await service.forwardSourceMessages(stringParam(params, "sourceRef"), messageActionRefsParam(params), {
2242
2289
  ...stringParam(params, "targetSourceRef") === "" ? {} : { targetSourceRef: stringParam(params, "targetSourceRef") },
2243
2290
  ...stringParam(params, "recordUid") === "" ? {} : { recordUid: stringParam(params, "recordUid") },
@@ -2249,9 +2296,12 @@ async function dispatchArkmeHostOperation(service, operation, params, updateMana
2249
2296
  const botRefs = botRefsParam(params);
2250
2297
  const humanMentions = humanMentionsParam(params);
2251
2298
  const botMentions = botMentionsParam(params);
2299
+ const captureContext = captureContextParam(params);
2252
2300
  return await service.sendSourceText(stringParam(params, "sourceRef"), stringParam(params, "textContent"), {
2253
2301
  ...stringParam(params, "recordUid") === "" ? {} : { recordUid: stringParam(params, "recordUid") },
2254
2302
  ...stringParam(params, "relationUid") === "" ? {} : { relationUid: stringParam(params, "relationUid") },
2303
+ ...numberParam(params, "recordDurationMillis", 0) <= 0 ? {} : { recordDurationMillis: Math.max(0, Math.trunc(numberParam(params, "recordDurationMillis", 0))) },
2304
+ ...captureContext === void 0 ? {} : { captureContext },
2255
2305
  ...booleanParam(params, "agentAuthored") ? { agentAuthored: true } : {},
2256
2306
  ...botRefs.length === 0 ? {} : { botRefs },
2257
2307
  ...humanMentions.length === 0 ? {} : { humanMentions },
@@ -2451,6 +2501,10 @@ async function dispatchArkmeHostOperation(service, operation, params, updateMana
2451
2501
  case "extensions.share.resolve": return (await enrichExtensionAuthors(service, [await requireExtensionManager(extensionManager).resolveSharedCatalogDetail(stringParam(params, "shareRef"))]))[0];
2452
2502
  case "extensions.delete": return await requireOwnedExtensionInventory(ownedExtensionInventory).delete({ extensionId: stringParam(params, "extensionId") });
2453
2503
  case "extensions.installed-list": return requireExtensionManager(extensionManager).listInstalled();
2504
+ case "extensions.quarantine.status": return await requireDesktopQuarantine(desktopQuarantine).status();
2505
+ case "extensions.quarantine.dismiss": return await requireDesktopQuarantine(desktopQuarantine).dismiss(stringParam(params, "packageName"));
2506
+ case "extensions.quarantine.reenable": return await requireDesktopQuarantine(desktopQuarantine).reenable(stringParam(params, "packageName"));
2507
+ case "extensions.quarantine.health": return await requireDesktopQuarantine(desktopQuarantine).health(stringParam(params, "packageName"));
2454
2508
  case "extensions.mine.list": return await requireOwnedExtensionInventory(ownedExtensionInventory).list({ ...stringParam(params, "currentSessionId").trim() === "" ? {} : { currentSessionId: stringParam(params, "currentSessionId").trim() } });
2455
2509
  case "extensions.mine.publish": return await requireOwnedExtensionInventory(ownedExtensionInventory).publish({
2456
2510
  ownedRef: stringParam(params, "ownedRef"),
@@ -2531,6 +2585,10 @@ function requireExtensionManager(manager) {
2531
2585
  if (manager === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "当前 DSH 未加载 Dynamic Cordis Runner,市集不可用", false, 503);
2532
2586
  return manager;
2533
2587
  }
2588
+ function requireDesktopQuarantine(quarantine) {
2589
+ if (quarantine === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "桌面扩展恢复状态尚未就绪", false, 503);
2590
+ return quarantine;
2591
+ }
2534
2592
  function requireOwnedExtensionInventory(inventory) {
2535
2593
  if (inventory === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "扩展运行时尚未就绪", true, 503);
2536
2594
  return inventory;
@@ -3743,6 +3801,8 @@ var ArkmeLocalDatabase = class {
3743
3801
  sync_state TEXT NOT NULL CHECK (sync_state IN ('synced', 'pending', 'failed')),
3744
3802
  attempts INTEGER NOT NULL DEFAULT 0,
3745
3803
  last_error TEXT,
3804
+ record_duration_millis INTEGER NOT NULL DEFAULT 0,
3805
+ capture_context_json TEXT,
3746
3806
  created_at_millis INTEGER NOT NULL,
3747
3807
  updated_at_millis INTEGER NOT NULL,
3748
3808
  PRIMARY KEY (user_id, record_uid)
@@ -3796,6 +3856,9 @@ var ArkmeLocalDatabase = class {
3796
3856
  CREATE UNIQUE INDEX IF NOT EXISTS extension_review_outbox_user_record
3797
3857
  ON extension_review_outbox (user_id, record_uid);
3798
3858
  `);
3859
+ const recordColumns = this.database.prepare("PRAGMA table_info(record_cache)").all();
3860
+ if (!recordColumns.some((column) => column.name === "record_duration_millis")) this.database.exec("ALTER TABLE record_cache ADD COLUMN record_duration_millis INTEGER NOT NULL DEFAULT 0");
3861
+ if (!recordColumns.some((column) => column.name === "capture_context_json")) this.database.exec("ALTER TABLE record_cache ADD COLUMN capture_context_json TEXT");
3799
3862
  const metaColumns = this.database.prepare("PRAGMA table_info(cache_meta)").all();
3800
3863
  if (!metaColumns.some((column) => column.name === "pagination_initialized")) this.database.exec("ALTER TABLE cache_meta ADD COLUMN pagination_initialized INTEGER NOT NULL DEFAULT 0");
3801
3864
  if (!metaColumns.some((column) => column.name === "revision")) this.database.exec("ALTER TABLE cache_meta ADD COLUMN revision INTEGER NOT NULL DEFAULT 0");
@@ -3954,18 +4017,25 @@ var ArkmeLocalDatabase = class {
3954
4017
  async listPending(userId) {
3955
4018
  await this.ensureMigrated(userId);
3956
4019
  return this.database.prepare(`
3957
- SELECT record_uid, text_content, created_at_millis, send_at_millis, attempts, last_error
4020
+ SELECT record_uid, text_content, created_at_millis, send_at_millis, attempts, last_error,
4021
+ record_duration_millis, capture_context_json
3958
4022
  FROM record_cache
3959
4023
  WHERE user_id = ? AND sync_state IN ('pending', 'failed')
3960
4024
  ORDER BY created_at_millis ASC, record_uid ASC
3961
- `).all(userId).map((row) => ({
3962
- recordUid: row.record_uid,
3963
- textContent: row.text_content,
3964
- createdAtMillis: row.created_at_millis,
3965
- sendAtMillis: row.send_at_millis,
3966
- attempts: row.attempts,
3967
- ...row.last_error == null || row.last_error === "" ? {} : { lastError: row.last_error }
3968
- }));
4025
+ `).all(userId).map((row) => {
4026
+ const captureContext = this.pendingCaptureContext(row.capture_context_json);
4027
+ const recordDurationMillis = Math.max(0, Math.trunc(row.record_duration_millis ?? 0));
4028
+ return {
4029
+ recordUid: row.record_uid,
4030
+ textContent: row.text_content,
4031
+ createdAtMillis: row.created_at_millis,
4032
+ sendAtMillis: row.send_at_millis,
4033
+ attempts: row.attempts,
4034
+ ...recordDurationMillis === 0 ? {} : { recordDurationMillis },
4035
+ ...captureContext === void 0 ? {} : { captureContext },
4036
+ ...row.last_error == null || row.last_error === "" ? {} : { lastError: row.last_error }
4037
+ };
4038
+ });
3969
4039
  }
3970
4040
  async putPending(userId, pending) {
3971
4041
  await this.ensureMigrated(userId);
@@ -4101,12 +4171,14 @@ var ArkmeLocalDatabase = class {
4101
4171
  }
4102
4172
  insertPending(userId, pending) {
4103
4173
  const now = Date.now();
4174
+ const recordDurationMillis = Math.max(0, Math.trunc(pending.recordDurationMillis ?? 0));
4175
+ const captureContextJson = pending.captureContext === void 0 ? null : JSON.stringify(pending.captureContext);
4104
4176
  this.database.prepare(`
4105
4177
  INSERT INTO record_cache (
4106
4178
  user_id, record_uid, send_at_millis, title, text_content, template_kind,
4107
4179
  status, version, sync_state, attempts, last_error,
4108
- created_at_millis, updated_at_millis
4109
- ) VALUES (?, ?, ?, '', ?, 1, 0, 0, 'pending', ?, ?, ?, ?)
4180
+ record_duration_millis, capture_context_json, created_at_millis, updated_at_millis
4181
+ ) VALUES (?, ?, ?, '', ?, 1, 0, 0, 'pending', ?, ?, ?, ?, ?, ?)
4110
4182
  ON CONFLICT(user_id, record_uid) DO UPDATE SET
4111
4183
  send_at_millis = excluded.send_at_millis,
4112
4184
  text_content = excluded.text_content,
@@ -4114,8 +4186,31 @@ var ArkmeLocalDatabase = class {
4114
4186
  sync_state = 'pending',
4115
4187
  attempts = excluded.attempts,
4116
4188
  last_error = excluded.last_error,
4189
+ record_duration_millis = excluded.record_duration_millis,
4190
+ capture_context_json = excluded.capture_context_json,
4117
4191
  updated_at_millis = excluded.updated_at_millis
4118
- `).run(userId, pending.recordUid, pending.sendAtMillis, pending.textContent, pending.attempts, pending.lastError ?? null, pending.createdAtMillis, now);
4192
+ `).run(userId, pending.recordUid, pending.sendAtMillis, pending.textContent, pending.attempts, pending.lastError ?? null, recordDurationMillis, captureContextJson, pending.createdAtMillis, now);
4193
+ }
4194
+ pendingCaptureContext(raw) {
4195
+ if (raw == null || raw === "") return void 0;
4196
+ try {
4197
+ const value = JSON.parse(raw);
4198
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
4199
+ const source = value;
4200
+ const clientName = typeof source.clientName === "string" ? source.clientName.trim().slice(0, 120) : "";
4201
+ const networkName = typeof source.networkName === "string" ? source.networkName.trim().slice(0, 120) : "";
4202
+ const electric = typeof source.electric === "number" && Number.isFinite(source.electric) ? Math.trunc(source.electric) : void 0;
4203
+ const charge = typeof source.charge === "number" && Number.isFinite(source.charge) ? Math.trunc(source.charge) : 0;
4204
+ const result = {
4205
+ ...clientName === "" ? {} : { clientName },
4206
+ ...networkName === "" ? {} : { networkName },
4207
+ ...electric === void 0 || electric < 0 || electric > 100 ? {} : { electric },
4208
+ ...charge < 1 || charge > 3 ? {} : { charge }
4209
+ };
4210
+ return Object.keys(result).length === 0 ? void 0 : result;
4211
+ } catch {
4212
+ return;
4213
+ }
4119
4214
  }
4120
4215
  upsertSyncedRecord(userId, item) {
4121
4216
  const now = Date.now();
@@ -4638,6 +4733,247 @@ var ArkmeRealtimeEvents = class {
4638
4733
  }
4639
4734
  };
4640
4735
  //#endregion
4736
+ //#region src/extensions/desktop-quarantine.ts
4737
+ const QUARANTINE_DIRECTORY_NAME = "desktop-extension-quarantine";
4738
+ const RECEIPT_FILE_NAME = "receipt.json";
4739
+ const PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
4740
+ const QUARANTINE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
4741
+ const RUNTIME_RELEASE_PATTERN = /^electron-runtime-v1-[a-f0-9]{32}$/;
4742
+ const MAX_FAILURE_SUMMARY_LENGTH = 2e3;
4743
+ const MAX_FAILURE_TAIL_LENGTH = 16384;
4744
+ var ArkmeDesktopExtensionQuarantine = class {
4745
+ options;
4746
+ now;
4747
+ constructor(options) {
4748
+ this.options = options;
4749
+ this.now = options.now ?? (() => Date.now());
4750
+ }
4751
+ async reconcile() {
4752
+ for (const stored of await this.readReceipts("active")) {
4753
+ let changed = false;
4754
+ for (const entry of stored.receipt.entries) {
4755
+ if (entry.resolvedAtMillis !== void 0) continue;
4756
+ const installed = this.installedByPackage(entry.packageName);
4757
+ if (installed !== void 0 && (installed.enabled || installed.active || installed.lastError !== stored.receipt.failureSummary)) this.options.installStore.put({
4758
+ ...installed,
4759
+ enabled: false,
4760
+ active: false,
4761
+ lastError: stored.receipt.failureSummary
4762
+ });
4763
+ if (entry.synchronizedAtMillis === void 0) {
4764
+ entry.synchronizedAtMillis = this.now();
4765
+ changed = true;
4766
+ }
4767
+ }
4768
+ if (changed) await this.writeStoredReceipt(stored);
4769
+ }
4770
+ }
4771
+ async status() {
4772
+ const stored = (await this.readReceipts("active")).sort((left, right) => right.receipt.createdAtMillis - left.receipt.createdAtMillis)[0];
4773
+ if (stored === void 0) return {
4774
+ active: false,
4775
+ entries: []
4776
+ };
4777
+ return {
4778
+ active: true,
4779
+ quarantineId: stored.receipt.quarantineId,
4780
+ mode: stored.receipt.mode,
4781
+ failureSummary: stored.receipt.failureSummary,
4782
+ failureLogTail: stored.receipt.failureLogTail,
4783
+ entries: stored.receipt.entries.filter((entry) => entry.resolvedAtMillis === void 0).map((entry) => ({
4784
+ packageName: entry.packageName,
4785
+ ...this.installedByPackage(entry.packageName) === void 0 ? {} : { extensionId: this.installedByPackage(entry.packageName).extensionId },
4786
+ dismissed: entry.notificationDismissedAtMillis !== void 0,
4787
+ resolved: false
4788
+ }))
4789
+ };
4790
+ }
4791
+ async dismiss(packageName) {
4792
+ const stored = await this.activeReceiptForPackage(packageName);
4793
+ const entry = stored.receipt.entries.find((item) => item.packageName === packageName);
4794
+ if (entry.notificationDismissedAtMillis === void 0) {
4795
+ entry.notificationDismissedAtMillis = this.now();
4796
+ await this.writeStoredReceipt(stored);
4797
+ }
4798
+ }
4799
+ async reenable(packageName) {
4800
+ const stored = await this.activeReceiptForPackage(packageName);
4801
+ const entry = stored.receipt.entries.find((item) => item.packageName === packageName);
4802
+ entry.reenableRequestedAtMillis = this.now();
4803
+ await this.writeStoredReceipt(stored);
4804
+ let profileEnabled = false;
4805
+ try {
4806
+ await this.options.setProfileEnabled(packageName, true);
4807
+ profileEnabled = true;
4808
+ await this.options.requestRestart({
4809
+ packageName,
4810
+ previousProfileIncluded: false
4811
+ });
4812
+ } catch (error) {
4813
+ if (profileEnabled) await this.options.setProfileEnabled(packageName, false).catch(() => void 0);
4814
+ delete entry.reenableRequestedAtMillis;
4815
+ await this.writeStoredReceipt(stored).catch(() => void 0);
4816
+ throw error;
4817
+ }
4818
+ }
4819
+ async resolveActive() {
4820
+ for (const stored of await this.readReceipts("active")) {
4821
+ let changed = false;
4822
+ for (const entry of stored.receipt.entries) {
4823
+ if (entry.resolvedAtMillis !== void 0) continue;
4824
+ if (entry.reenableRequestedAtMillis !== void 0 && this.options.isPackageActive(entry.packageName)) {
4825
+ entry.resolvedAtMillis = this.now();
4826
+ delete entry.reenableRequestedAtMillis;
4827
+ const installed = this.installedByPackage(entry.packageName);
4828
+ if (installed !== void 0) {
4829
+ const { lastError: _lastError, ...retained } = installed;
4830
+ this.options.installStore.put({
4831
+ ...retained,
4832
+ enabled: true,
4833
+ active: true
4834
+ });
4835
+ }
4836
+ changed = true;
4837
+ continue;
4838
+ }
4839
+ if (entry.reenableRequestedAtMillis !== void 0) {
4840
+ await this.options.setProfileEnabled(entry.packageName, false);
4841
+ delete entry.reenableRequestedAtMillis;
4842
+ const installed = this.installedByPackage(entry.packageName);
4843
+ if (installed !== void 0) this.options.installStore.put({
4844
+ ...installed,
4845
+ enabled: false,
4846
+ active: false
4847
+ });
4848
+ changed = true;
4849
+ }
4850
+ }
4851
+ if (stored.receipt.entries.every((entry) => entry.resolvedAtMillis !== void 0)) {
4852
+ stored.receipt.phase = "resolved";
4853
+ changed = true;
4854
+ }
4855
+ if (changed) await this.writeStoredReceipt(stored);
4856
+ }
4857
+ }
4858
+ async health(packageName) {
4859
+ if ((await this.activeReceiptForPackage(packageName)).receipt.entries.find((item) => item.packageName === packageName).reenableRequestedAtMillis === void 0) throw new Error("该扩展尚未请求重新启用");
4860
+ const profileEnabled = await this.profileContains(packageName);
4861
+ const active = this.options.isPackageActive(packageName);
4862
+ if (profileEnabled && active) await this.resolveActive();
4863
+ return {
4864
+ profileEnabled,
4865
+ active
4866
+ };
4867
+ }
4868
+ async activeReceiptForPackage(packageName) {
4869
+ if (!isPackageName(packageName) || isProtectedPackage(packageName)) throw new Error("隔离扩展包名无效");
4870
+ const matches = (await this.readReceipts("active")).filter((stored) => stored.receipt.entries.some((entry) => entry.packageName === packageName && entry.resolvedAtMillis === void 0));
4871
+ matches.sort((left, right) => right.receipt.createdAtMillis - left.receipt.createdAtMillis);
4872
+ if (matches[0] === void 0) throw new Error("该扩展没有活动的启动隔离记录");
4873
+ return matches[0];
4874
+ }
4875
+ installedByPackage(packageName) {
4876
+ return this.options.installStore.list().find((item) => item.profilePackageName === packageName);
4877
+ }
4878
+ async profileContains(packageName) {
4879
+ try {
4880
+ const value = JSON.parse(await readFile(join(this.options.dshHome, "profiles", "web", "package.json"), "utf8"));
4881
+ if (!isObject(value) || !isObject(value.dsh) || !isObject(value.dsh.profile) || !Array.isArray(value.dsh.profile.bundles)) return false;
4882
+ return value.dsh.profile.bundles.some((item) => item === packageName);
4883
+ } catch {
4884
+ return false;
4885
+ }
4886
+ }
4887
+ async readReceipts(phase) {
4888
+ const root = join(this.options.dshHome, "arkme-self", QUARANTINE_DIRECTORY_NAME);
4889
+ const directories = await readdir(root, { withFileTypes: true }).catch((error) => {
4890
+ if (error.code === "ENOENT") return [];
4891
+ throw error;
4892
+ });
4893
+ const receipts = [];
4894
+ for (const directory of directories) {
4895
+ if (!directory.isDirectory() || !QUARANTINE_ID_PATTERN.test(directory.name)) continue;
4896
+ const receiptPath = join(root, directory.name, RECEIPT_FILE_NAME);
4897
+ try {
4898
+ const receipt = parseReceipt(await readFile(receiptPath, "utf8"));
4899
+ if (receipt.quarantineId !== directory.name || receipt.environment !== this.options.environment || phase !== void 0 && receipt.phase !== phase) continue;
4900
+ receipts.push({
4901
+ path: receiptPath,
4902
+ receipt
4903
+ });
4904
+ } catch {}
4905
+ }
4906
+ return receipts;
4907
+ }
4908
+ async writeStoredReceipt(stored) {
4909
+ stored.receipt.updatedAtMillis = this.now();
4910
+ await writeTextAtomically(stored.path, `${JSON.stringify(stored.receipt, void 0, 2)}\n`);
4911
+ }
4912
+ };
4913
+ function parseReceipt(text) {
4914
+ const value = JSON.parse(text);
4915
+ if (!isObject(value) || value.schemaVersion !== 1 || typeof value.quarantineId !== "string" || !QUARANTINE_ID_PATTERN.test(value.quarantineId) || value.environment !== "prod" && value.environment !== "test" || ![
4916
+ "pending",
4917
+ "active",
4918
+ "restored",
4919
+ "resolved"
4920
+ ].includes(String(value.phase)) || value.mode !== "targeted" && value.mode !== "local-safe-mode" || !Number.isSafeInteger(value.createdAtMillis) || !Number.isSafeInteger(value.updatedAtMillis) || value.runtimeReleaseId !== void 0 && (typeof value.runtimeReleaseId !== "string" || !RUNTIME_RELEASE_PATTERN.test(value.runtimeReleaseId)) || typeof value.failureSummary !== "string" || value.failureSummary.trim() === "" || value.failureSummary.length > MAX_FAILURE_SUMMARY_LENGTH || typeof value.failureLogTail !== "string" || value.failureLogTail.length > MAX_FAILURE_TAIL_LENGTH || !Array.isArray(value.entries) || value.entries.length === 0) throw new Error("桌面扩展隔离记录无效");
4921
+ const entries = value.entries.map((raw) => parseEntry(raw));
4922
+ if (new Set(entries.map((entry) => entry.packageName)).size !== entries.length) throw new Error("桌面扩展隔离记录包含重复扩展");
4923
+ return {
4924
+ schemaVersion: 1,
4925
+ quarantineId: value.quarantineId,
4926
+ environment: value.environment,
4927
+ phase: value.phase,
4928
+ mode: value.mode,
4929
+ createdAtMillis: Number(value.createdAtMillis),
4930
+ updatedAtMillis: Number(value.updatedAtMillis),
4931
+ ...typeof value.runtimeReleaseId === "string" ? { runtimeReleaseId: value.runtimeReleaseId } : {},
4932
+ failureSummary: value.failureSummary,
4933
+ failureLogTail: value.failureLogTail,
4934
+ entries
4935
+ };
4936
+ }
4937
+ function parseEntry(value) {
4938
+ if (!isObject(value) || typeof value.packageName !== "string" || !isPackageName(value.packageName) || isProtectedPackage(value.packageName) || typeof value.dependencySpec !== "string" || value.dependencySpec.length === 0 || value.dependencySpec.length > 4096 || !Number.isSafeInteger(value.originalBundleIndex) || Number(value.originalBundleIndex) < 0 || !optionalMillis(value.synchronizedAtMillis) || !optionalMillis(value.notificationDismissedAtMillis) || !optionalMillis(value.reenableRequestedAtMillis) || !optionalMillis(value.resolvedAtMillis)) throw new Error("桌面扩展隔离条目无效");
4939
+ return {
4940
+ packageName: value.packageName,
4941
+ dependencySpec: value.dependencySpec,
4942
+ originalBundleIndex: Number(value.originalBundleIndex),
4943
+ ...value.synchronizedAtMillis === void 0 ? {} : { synchronizedAtMillis: Number(value.synchronizedAtMillis) },
4944
+ ...value.notificationDismissedAtMillis === void 0 ? {} : { notificationDismissedAtMillis: Number(value.notificationDismissedAtMillis) },
4945
+ ...value.reenableRequestedAtMillis === void 0 ? {} : { reenableRequestedAtMillis: Number(value.reenableRequestedAtMillis) },
4946
+ ...value.resolvedAtMillis === void 0 ? {} : { resolvedAtMillis: Number(value.resolvedAtMillis) }
4947
+ };
4948
+ }
4949
+ function optionalMillis(value) {
4950
+ return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 0;
4951
+ }
4952
+ function isObject(value) {
4953
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4954
+ }
4955
+ function isPackageName(value) {
4956
+ return PACKAGE_NAME_PATTERN.test(value);
4957
+ }
4958
+ function isProtectedPackage(packageName) {
4959
+ return packageName === "@senguoyun/dsh-arkme" || packageName.startsWith("@deepseek-ai/");
4960
+ }
4961
+ async function writeTextAtomically(filePath, text) {
4962
+ const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
4963
+ try {
4964
+ const handle = await open(temporaryPath, "wx", 384);
4965
+ try {
4966
+ await handle.writeFile(text, "utf8");
4967
+ await handle.sync();
4968
+ } finally {
4969
+ await handle.close();
4970
+ }
4971
+ await rename(temporaryPath, filePath);
4972
+ } finally {
4973
+ await rm(temporaryPath, { force: true }).catch(() => void 0);
4974
+ }
4975
+ }
4976
+ //#endregion
4641
4977
  //#region src/extensions/install-tasks.ts
4642
4978
  const TASK_RETENTION_MILLIS = 600 * 1e3;
4643
4979
  const MAX_RETAINED_TASKS = 100;
@@ -7915,7 +8251,7 @@ var ArkmeExtensionProfileInstaller = class {
7915
8251
  await this.mutationTail;
7916
8252
  if (this.options.stateDirectory === void 0 || this.options.healthUrl === void 0 || this.options.helperPath === void 0 || this.options.restartArgv === void 0 || this.options.installStoreDirectory === void 0) return;
7917
8253
  const plan = {
7918
- schemaVersion: input.activationChange === true ? 3 : input.targetBundlePath?.endsWith(".tgz") === true || input.previousBundlePath?.endsWith(".tgz") === true || input.previousInstalled?.executionModel !== void 0 ? 2 : 1,
8254
+ schemaVersion: input.desktopQuarantineActivation === true ? 4 : input.activationChange === true ? 3 : input.targetBundlePath?.endsWith(".tgz") === true || input.previousBundlePath?.endsWith(".tgz") === true || input.previousInstalled?.executionModel !== void 0 ? 2 : 1,
7919
8255
  parentPid: process.pid,
7920
8256
  execPath: this.options.execPath,
7921
8257
  dshBinPath: this.options.dshBinPath,
@@ -7935,8 +8271,10 @@ var ArkmeExtensionProfileInstaller = class {
7935
8271
  activationChange: true,
7936
8272
  previousProfileIncluded: input.previousProfileIncluded === true
7937
8273
  } : {},
8274
+ ...input.desktopQuarantineActivation === true ? { desktopQuarantineActivation: true } : {},
7938
8275
  healthUrl: this.options.healthUrl,
7939
- logPath: join(this.options.stateDirectory, "extension-profile-restart.log")
8276
+ logPath: join(this.options.stateDirectory, "extension-profile-restart.log"),
8277
+ ...this.options.runtimeReleaseId === void 0 ? {} : { runtimeReleaseId: this.options.runtimeReleaseId }
7940
8278
  };
7941
8279
  if (this.options.supervisedExitCode !== void 0) {
7942
8280
  if (!Number.isSafeInteger(this.options.supervisedExitCode) || this.options.supervisedExitCode < 1 || this.options.supervisedExitCode > 255) throw new Error("受监督重启退出码无效");
@@ -7985,6 +8323,17 @@ var ArkmeExtensionProfileInstaller = class {
7985
8323
  setTimeout(() => process.kill(process.pid, "SIGTERM"), 800).unref?.();
7986
8324
  }))();
7987
8325
  }
8326
+ async restartDesktopQuarantine(input) {
8327
+ if (!isArkmeProfilePackageName(input.packageName)) throw new Error("扩展 Bundle 包名无效");
8328
+ await this.restart({
8329
+ extensionId: `desktop-quarantine:${input.packageName}`,
8330
+ packageName: input.packageName,
8331
+ expectActive: true,
8332
+ activationChange: true,
8333
+ desktopQuarantineActivation: true,
8334
+ previousProfileIncluded: false
8335
+ });
8336
+ }
7988
8337
  async run(args) {
7989
8338
  if (this.options.run !== void 0) return await this.options.run(args);
7990
8339
  if (!existsSync(this.options.execPath) || !existsSync(this.options.dshBinPath)) throw new Error("当前 DSH 运行方式不支持修改 Profile 插件");
@@ -8756,6 +9105,21 @@ function normalizedLongArticleDraft(value) {
8756
9105
  function longArticleDraftKey(sourceRef, itemUid) {
8757
9106
  return `${sourceRef}\u0000${itemUid ?? ""}`;
8758
9107
  }
9108
+ function normalizedCaptureContext(value) {
9109
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
9110
+ const source = value;
9111
+ const clientName = typeof source.clientName === "string" ? source.clientName.trim().slice(0, 120) : "";
9112
+ const networkName = typeof source.networkName === "string" ? source.networkName.trim().slice(0, 120) : "";
9113
+ const electric = typeof source.electric === "number" && Number.isFinite(source.electric) ? Math.trunc(source.electric) : void 0;
9114
+ const charge = typeof source.charge === "number" && Number.isFinite(source.charge) ? Math.trunc(source.charge) : 0;
9115
+ const result = {
9116
+ ...clientName === "" ? {} : { clientName },
9117
+ ...networkName === "" ? {} : { networkName },
9118
+ ...electric === void 0 || electric < 0 || electric > 100 ? {} : { electric },
9119
+ ...charge < 1 || charge > 3 ? {} : { charge }
9120
+ };
9121
+ return Object.keys(result).length === 0 ? void 0 : result;
9122
+ }
8759
9123
  function normalizedPending(value) {
8760
9124
  if (!Array.isArray(value)) return [];
8761
9125
  const result = [];
@@ -8764,12 +9128,16 @@ function normalizedPending(value) {
8764
9128
  const source = item;
8765
9129
  if (typeof source.recordUid !== "string" || source.recordUid.trim() === "") continue;
8766
9130
  if (typeof source.textContent !== "string" || source.textContent.trim() === "") continue;
9131
+ const recordDurationMillis = typeof source.recordDurationMillis === "number" && Number.isFinite(source.recordDurationMillis) ? Math.max(0, Math.trunc(source.recordDurationMillis)) : 0;
9132
+ const captureContext = normalizedCaptureContext(source.captureContext);
8767
9133
  result.push({
8768
9134
  recordUid: source.recordUid,
8769
9135
  textContent: source.textContent,
8770
9136
  createdAtMillis: typeof source.createdAtMillis === "number" ? source.createdAtMillis : 0,
8771
9137
  sendAtMillis: typeof source.sendAtMillis === "number" ? source.sendAtMillis : 0,
8772
9138
  attempts: typeof source.attempts === "number" ? source.attempts : 0,
9139
+ ...recordDurationMillis === 0 ? {} : { recordDurationMillis },
9140
+ ...captureContext === void 0 ? {} : { captureContext },
8773
9141
  ...typeof source.lastError === "string" && source.lastError !== "" ? { lastError: source.lastError } : {}
8774
9142
  });
8775
9143
  }
@@ -18300,6 +18668,9 @@ function resolveArkmeAppVersion(configuredAppVersion, environment = process.env)
18300
18668
  const injected = environment.ARKME_APP_VERSION?.trim();
18301
18669
  return injected === void 0 || injected === "" ? void 0 : injected;
18302
18670
  }
18671
+ function resolvePluginUpdateEnabled(configured, environment = process.env) {
18672
+ return configured && environment.ARKME_RUNTIME_MANAGED !== "1";
18673
+ }
18303
18674
  function dshRemoteClientId(accessToken) {
18304
18675
  const parts = accessToken.split(".");
18305
18676
  if (parts.length !== 3) return void 0;
@@ -18344,7 +18715,7 @@ function apply(ctx, config) {
18344
18715
  const extensionStore = new ArkmeExtensionInstallStore(extensionDirectory);
18345
18716
  const clientModules = ctx.clientModules;
18346
18717
  const updateManager = new ArkmePluginUpdateManager({
18347
- enabled: config.updateCheckEnabled,
18718
+ enabled: resolvePluginUpdateEnabled(config.updateCheckEnabled),
18348
18719
  channel: config.updateChannel,
18349
18720
  updateServiceBaseUrl: config.updateServiceBaseUrl,
18350
18721
  ...config.updateArtifactBaseUrl.trim() === "" ? {} : { updateArtifactBaseUrl: config.updateArtifactBaseUrl },
@@ -18386,9 +18757,26 @@ function apply(ctx, config) {
18386
18757
  installStoreDirectory: extensionDirectory,
18387
18758
  ...process.env.ARKME_DESKTOP_MANAGED_RESTART === "1" && process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH !== void 0 ? {
18388
18759
  supervisedExitCode: 75,
18389
- supervisedPlanPath: process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH
18760
+ supervisedPlanPath: process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH,
18761
+ ...process.env.ARKME_RUNTIME_RELEASE_ID === void 0 ? {} : { runtimeReleaseId: process.env.ARKME_RUNTIME_RELEASE_ID }
18390
18762
  } : {}
18391
18763
  });
18764
+ const pluginInventory = ctx.get("pluginInventory");
18765
+ const desktopQuarantine = new ArkmeDesktopExtensionQuarantine({
18766
+ dshHome,
18767
+ environment: config.environment,
18768
+ installStore: extensionStore,
18769
+ setProfileEnabled: async (packageName, enabled) => {
18770
+ await extensionProfileInstaller.setEnabled(packageName, enabled);
18771
+ },
18772
+ requestRestart: async ({ packageName }) => {
18773
+ await extensionProfileInstaller.restartDesktopQuarantine({ packageName });
18774
+ },
18775
+ isPackageActive: (packageName) => pluginInventory.list().entries.some((entry) => entry.moduleName === packageName && entry.enabled && entry.fiberPhase === "active")
18776
+ });
18777
+ desktopQuarantine.reconcile().catch((error) => {
18778
+ ctx.logger.warn("Arkme desktop extension quarantine reconciliation failed: %s", error instanceof Error ? error.message : String(error));
18779
+ });
18392
18780
  const ownedExtensionStore = new ArkmeOwnedExtensionStore(extensionDirectory);
18393
18781
  const ownedExtensionRefs = new ArkmeOwnedExtensionRefs();
18394
18782
  const ownedExtensionHostInstanceId = randomUUID();
@@ -18414,7 +18802,7 @@ function apply(ctx, config) {
18414
18802
  profileDirectory: extensionProfileDirectory,
18415
18803
  profileInstaller: extensionProfileInstaller,
18416
18804
  clientApiPath: config.routePath,
18417
- pluginInventory: ctx.get("pluginInventory"),
18805
+ pluginInventory,
18418
18806
  ...dshRuntimeVersion === void 0 ? {} : { dshRuntimeVersion }
18419
18807
  });
18420
18808
  extensionManager = manager;
@@ -18523,7 +18911,8 @@ function apply(ctx, config) {
18523
18911
  extensionManager: () => extensionManager,
18524
18912
  extensionInstallTasks: () => extensionInstallTasks,
18525
18913
  ownedExtensionInventory: () => ownedExtensionInventory,
18526
- remoteHost: () => remoteHost
18914
+ remoteHost: () => remoteHost,
18915
+ desktopQuarantine
18527
18916
  });
18528
18917
  const callAssetHandler = createOutgoingCallAssetHandler({ routePrefix: `${config.routePath}/call` });
18529
18918
  const richMediaOptions = {
@@ -18718,4 +19107,4 @@ function validateConfig(ctx, config) {
18718
19107
  }
18719
19108
  }
18720
19109
  //#endregion
18721
- export { ARKME_PRODUCTION_TRUSTED_SIGNING_KEYS, ARKME_PROVIDER_CONTRACT_VERSION, ARKME_WORLD_PUBLISH_MAX_IMAGES, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, ArkmeOutgoingCallError, ArkmeService, Config, apply, createOpenClawCliAdapter, inject, name, readDshRuntimeVersion, resolveArkmeAppVersion, resolveArkmeConfig };
19110
+ export { ARKME_PRODUCTION_TRUSTED_SIGNING_KEYS, ARKME_PROVIDER_CONTRACT_VERSION, ARKME_WORLD_PUBLISH_MAX_IMAGES, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, ArkmeOutgoingCallError, ArkmeService, Config, apply, createOpenClawCliAdapter, inject, name, readDshRuntimeVersion, resolveArkmeAppVersion, resolveArkmeConfig, resolvePluginUpdateEnabled };
@@ -1,2 +1,2 @@
1
- import { a as persistentArkmeExtensionActive, i as invokePersistentArkmeExtension, n as applyPersistentArkmeHostExtension, o as persistentArkmeExtensionRuntimeState, r as deactivatePersistentArkmeExtension, t as activatePersistentArkmeExtension } from "./persistent-runtime-CFeIg6aw.js";
1
+ import { a as persistentArkmeExtensionActive, i as invokePersistentArkmeExtension, n as applyPersistentArkmeHostExtension, o as persistentArkmeExtensionRuntimeState, r as deactivatePersistentArkmeExtension, t as activatePersistentArkmeExtension } from "./persistent-runtime-CZQTfyC1.js";
2
2
  export { activatePersistentArkmeExtension, applyPersistentArkmeHostExtension, deactivatePersistentArkmeExtension, invokePersistentArkmeExtension, persistentArkmeExtensionActive, persistentArkmeExtensionRuntimeState };