@senguoyun/dsh-arkme 0.1.32 → 0.1.33

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 (44) hide show
  1. package/README.md +24 -0
  2. package/lib/client.js +514 -1546
  3. package/lib/client.js.map +1 -1
  4. package/lib/extension-profile-restart-helper.js +17 -5
  5. package/lib/index.js +296 -11
  6. package/lib/persistent-extension.js +1 -1
  7. package/lib/{persistent-runtime-CFeIg6aw.js → persistent-runtime-DUqdJv8K.js} +28508 -1
  8. package/lib/types/client/ArkmeExtensionRecoveryNotice.d.ts +9 -0
  9. package/lib/types/client/ArkmeExtensionRecoveryNotice.d.ts.map +1 -0
  10. package/lib/types/client/ArkmeFooterAction.d.ts +1 -7
  11. package/lib/types/client/ArkmeFooterAction.d.ts.map +1 -1
  12. package/lib/types/client/ArkmeFooterDropdown.d.ts.map +1 -1
  13. package/lib/types/client/ArkmeMarketplace.d.ts +2 -1
  14. package/lib/types/client/ArkmeMarketplace.d.ts.map +1 -1
  15. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  16. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeSettingsSurface.d.ts +4 -19
  18. package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  20. package/lib/types/client/ArkmeUpdateSurfaces.d.ts +38 -5
  21. package/lib/types/client/ArkmeUpdateSurfaces.d.ts.map +1 -1
  22. package/lib/types/client/api.d.ts +1 -1
  23. package/lib/types/client/api.d.ts.map +1 -1
  24. package/lib/types/client/desktop-quarantine-status.d.ts +3 -0
  25. package/lib/types/client/desktop-quarantine-status.d.ts.map +1 -0
  26. package/lib/types/client/index.d.ts +0 -2
  27. package/lib/types/client/index.d.ts.map +1 -1
  28. package/lib/types/client/update-presentation.d.ts +1 -1
  29. package/lib/types/client/update-presentation.d.ts.map +1 -1
  30. package/lib/types/client/update-ui-controller.d.ts +1 -1
  31. package/lib/types/client/update-ui-controller.d.ts.map +1 -1
  32. package/lib/types/extensions/desktop-quarantine.d.ts +79 -0
  33. package/lib/types/extensions/desktop-quarantine.d.ts.map +1 -0
  34. package/lib/types/extensions/profile-installer.d.ts +5 -0
  35. package/lib/types/extensions/profile-installer.d.ts.map +1 -1
  36. package/lib/types/extensions/profile-restart-helper.d.ts +3 -1
  37. package/lib/types/extensions/profile-restart-helper.d.ts.map +1 -1
  38. package/lib/types/host-api.d.ts +3 -1
  39. package/lib/types/host-api.d.ts.map +1 -1
  40. package/lib/types/index.d.ts +1 -0
  41. package/lib/types/index.d.ts.map +1 -1
  42. package/lib/types/types.d.ts +1 -1
  43. package/lib/types/types.d.ts.map +1 -1
  44. package/package.json +6 -5
@@ -35,15 +35,18 @@ function parseExtensionProfileRestartPlan(value) {
35
35
  installStoreDirectory: stringValue(source.installStoreDirectory),
36
36
  previousInstalled: source.previousInstalled !== null && typeof source.previousInstalled === "object" ? source.previousInstalled : void 0,
37
37
  activationChange: source.activationChange === true ? true : void 0,
38
+ desktopQuarantineActivation: source.desktopQuarantineActivation === true ? true : void 0,
38
39
  previousProfileIncluded: typeof source.previousProfileIncluded === "boolean" ? source.previousProfileIncluded : void 0,
39
40
  healthUrl: stringValue(source.healthUrl),
40
- logPath: stringValue(source.logPath)
41
+ logPath: stringValue(source.logPath),
42
+ runtimeReleaseId: stringValue(source.runtimeReleaseId)
41
43
  };
42
44
  if (![
43
45
  1,
44
46
  2,
45
- 3
46
- ].includes(plan.schemaVersion) || typeof plan.parentPid !== "number" || !Number.isSafeInteger(plan.parentPid) || plan.parentPid <= 0 || plan.execPath === void 0 || plan.dshBinPath === void 0 || plan.restartArgv.length === 0 || plan.dshHome === void 0 || plan.profileName === void 0 || plan.packageName === void 0 || plan.extensionId === void 0 || typeof plan.expectActive !== "boolean" || plan.installStoreDirectory === void 0 || plan.healthUrl === void 0 || plan.logPath === void 0 || plan.schemaVersion === 3 && (plan.activationChange !== true || plan.previousInstalled === void 0 || plan.previousProfileIncluded === void 0) || plan.schemaVersion !== 3 && plan.activationChange === true || (plan.schemaVersion === 1 ? !/^@arkme-local\/ext-[a-f0-9]{16}$/.test(plan.packageName) : !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(plan.packageName))) throw new Error("extension restart plan is incomplete");
47
+ 3,
48
+ 4
49
+ ].includes(plan.schemaVersion) || typeof plan.parentPid !== "number" || !Number.isSafeInteger(plan.parentPid) || plan.parentPid <= 0 || plan.execPath === void 0 || plan.dshBinPath === void 0 || plan.restartArgv.length === 0 || plan.dshHome === void 0 || plan.profileName === void 0 || plan.packageName === void 0 || plan.extensionId === void 0 || typeof plan.expectActive !== "boolean" || plan.installStoreDirectory === void 0 || plan.healthUrl === void 0 || plan.logPath === void 0 || plan.runtimeReleaseId !== void 0 && !/^electron-runtime-v1-[a-f0-9]{32}$/.test(plan.runtimeReleaseId) || plan.schemaVersion === 3 && (plan.activationChange !== true || plan.desktopQuarantineActivation === true || plan.previousInstalled === void 0 || plan.previousProfileIncluded === void 0) || plan.schemaVersion === 4 && (plan.activationChange !== true || plan.desktopQuarantineActivation !== true || plan.previousInstalled !== void 0 || plan.previousProfileIncluded !== false || plan.expectActive !== true || plan.extensionId !== `desktop-quarantine:${plan.packageName}`) || ![3, 4].includes(plan.schemaVersion) && plan.activationChange === true || plan.schemaVersion !== 4 && plan.desktopQuarantineActivation === true || (plan.schemaVersion === 1 ? !/^@arkme-local\/ext-[a-f0-9]{16}$/.test(plan.packageName) : !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(plan.packageName))) throw new Error("extension restart plan is incomplete");
47
50
  const health = new URL(plan.healthUrl);
48
51
  if (health.protocol !== "http:" || !["127.0.0.1", "localhost"].includes(health.hostname)) throw new Error("extension restart health URL must be loopback HTTP");
49
52
  return {
@@ -88,12 +91,21 @@ async function healthy(plan) {
88
91
  "Content-Type": "application/json",
89
92
  Origin: new URL(plan.healthUrl).origin
90
93
  },
91
- body: JSON.stringify({ operation: "extensions.installed-list" }),
94
+ body: JSON.stringify(plan.desktopQuarantineActivation === true ? {
95
+ operation: "extensions.quarantine.health",
96
+ params: { packageName: plan.packageName }
97
+ } : { operation: "extensions.installed-list" }),
92
98
  signal: AbortSignal.timeout(2e3)
93
99
  });
94
100
  if (response.ok) {
95
101
  const body = await response.json();
96
- const installed = body.value?.find((item) => item.extensionId === plan.extensionId);
102
+ if (plan.desktopQuarantineActivation === true) {
103
+ const status = body.value;
104
+ if (body.ok === true && status?.profileEnabled === true && status.active === true) return true;
105
+ await new Promise((resolve) => setTimeout(resolve, 500));
106
+ continue;
107
+ }
108
+ const installed = Array.isArray(body.value) ? body.value.find((item) => item.extensionId === plan.extensionId) : void 0;
97
109
  if (body.ok === true && (plan.activationChange === true ? plan.expectActive ? installed?.enabled === true && installed.active === true : installed?.enabled === false && installed.active === false : plan.expectActive ? installed?.active === true : installed === void 0)) return true;
98
110
  }
99
111
  } catch {}
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-DUqdJv8K.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";
@@ -1893,11 +1893,13 @@ function createArkmeHostApi(service, options) {
1893
1893
  "extensions.persistent.invoke",
1894
1894
  "extensions.bundle.invoke",
1895
1895
  "extensions.mine.publish",
1896
+ "extensions.quarantine.dismiss",
1897
+ "extensions.quarantine.reenable",
1896
1898
  "remote.renameDesktop"
1897
1899
  ].includes(request.operation) && origin === void 0) throw new ArkmePluginError("origin-required", "扩展变更必须从当前 DSH 页面发起", false, 403);
1898
1900
  writeJson$4(res, 200, {
1899
1901
  ok: true,
1900
- value: await dispatchArkmeHostOperation(service, request.operation, params, options.updateManager, options.extensionManager?.(), options.extensionInstallTasks?.(), options.ownedExtensionInventory?.(), controller.signal, options.remoteHost?.())
1902
+ value: await dispatchArkmeHostOperation(service, request.operation, params, options.updateManager, options.extensionManager?.(), options.extensionInstallTasks?.(), options.ownedExtensionInventory?.(), controller.signal, options.remoteHost?.(), options.desktopQuarantine)
1901
1903
  });
1902
1904
  } catch (error) {
1903
1905
  if (controller.signal.aborted && res.destroyed) return;
@@ -1915,7 +1917,7 @@ function createArkmeHostApi(service, options) {
1915
1917
  }
1916
1918
  };
1917
1919
  }
1918
- async function dispatchArkmeHostOperation(service, operation, params, updateManager, extensionManager, extensionInstallTasks, ownedExtensionInventory, requestSignal, remoteHost) {
1920
+ async function dispatchArkmeHostOperation(service, operation, params, updateManager, extensionManager, extensionInstallTasks, ownedExtensionInventory, requestSignal, remoteHost, desktopQuarantine) {
1919
1921
  switch (operation) {
1920
1922
  case "provider.capabilities": return service.providerCapabilities();
1921
1923
  case "provider.instance": return { instanceId: ARKME_RUNTIME_INSTANCE_ID };
@@ -2451,6 +2453,10 @@ async function dispatchArkmeHostOperation(service, operation, params, updateMana
2451
2453
  case "extensions.share.resolve": return (await enrichExtensionAuthors(service, [await requireExtensionManager(extensionManager).resolveSharedCatalogDetail(stringParam(params, "shareRef"))]))[0];
2452
2454
  case "extensions.delete": return await requireOwnedExtensionInventory(ownedExtensionInventory).delete({ extensionId: stringParam(params, "extensionId") });
2453
2455
  case "extensions.installed-list": return requireExtensionManager(extensionManager).listInstalled();
2456
+ case "extensions.quarantine.status": return await requireDesktopQuarantine(desktopQuarantine).status();
2457
+ case "extensions.quarantine.dismiss": return await requireDesktopQuarantine(desktopQuarantine).dismiss(stringParam(params, "packageName"));
2458
+ case "extensions.quarantine.reenable": return await requireDesktopQuarantine(desktopQuarantine).reenable(stringParam(params, "packageName"));
2459
+ case "extensions.quarantine.health": return await requireDesktopQuarantine(desktopQuarantine).health(stringParam(params, "packageName"));
2454
2460
  case "extensions.mine.list": return await requireOwnedExtensionInventory(ownedExtensionInventory).list({ ...stringParam(params, "currentSessionId").trim() === "" ? {} : { currentSessionId: stringParam(params, "currentSessionId").trim() } });
2455
2461
  case "extensions.mine.publish": return await requireOwnedExtensionInventory(ownedExtensionInventory).publish({
2456
2462
  ownedRef: stringParam(params, "ownedRef"),
@@ -2531,6 +2537,10 @@ function requireExtensionManager(manager) {
2531
2537
  if (manager === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "当前 DSH 未加载 Dynamic Cordis Runner,市集不可用", false, 503);
2532
2538
  return manager;
2533
2539
  }
2540
+ function requireDesktopQuarantine(quarantine) {
2541
+ if (quarantine === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "桌面扩展恢复状态尚未就绪", false, 503);
2542
+ return quarantine;
2543
+ }
2534
2544
  function requireOwnedExtensionInventory(inventory) {
2535
2545
  if (inventory === void 0) throw new ArkmePluginError("extension-runtime-unavailable", "扩展运行时尚未就绪", true, 503);
2536
2546
  return inventory;
@@ -4638,6 +4648,247 @@ var ArkmeRealtimeEvents = class {
4638
4648
  }
4639
4649
  };
4640
4650
  //#endregion
4651
+ //#region src/extensions/desktop-quarantine.ts
4652
+ const QUARANTINE_DIRECTORY_NAME = "desktop-extension-quarantine";
4653
+ const RECEIPT_FILE_NAME = "receipt.json";
4654
+ const PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
4655
+ 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}$/;
4656
+ const RUNTIME_RELEASE_PATTERN = /^electron-runtime-v1-[a-f0-9]{32}$/;
4657
+ const MAX_FAILURE_SUMMARY_LENGTH = 2e3;
4658
+ const MAX_FAILURE_TAIL_LENGTH = 16384;
4659
+ var ArkmeDesktopExtensionQuarantine = class {
4660
+ options;
4661
+ now;
4662
+ constructor(options) {
4663
+ this.options = options;
4664
+ this.now = options.now ?? (() => Date.now());
4665
+ }
4666
+ async reconcile() {
4667
+ for (const stored of await this.readReceipts("active")) {
4668
+ let changed = false;
4669
+ for (const entry of stored.receipt.entries) {
4670
+ if (entry.resolvedAtMillis !== void 0) continue;
4671
+ const installed = this.installedByPackage(entry.packageName);
4672
+ if (installed !== void 0 && (installed.enabled || installed.active || installed.lastError !== stored.receipt.failureSummary)) this.options.installStore.put({
4673
+ ...installed,
4674
+ enabled: false,
4675
+ active: false,
4676
+ lastError: stored.receipt.failureSummary
4677
+ });
4678
+ if (entry.synchronizedAtMillis === void 0) {
4679
+ entry.synchronizedAtMillis = this.now();
4680
+ changed = true;
4681
+ }
4682
+ }
4683
+ if (changed) await this.writeStoredReceipt(stored);
4684
+ }
4685
+ }
4686
+ async status() {
4687
+ const stored = (await this.readReceipts("active")).sort((left, right) => right.receipt.createdAtMillis - left.receipt.createdAtMillis)[0];
4688
+ if (stored === void 0) return {
4689
+ active: false,
4690
+ entries: []
4691
+ };
4692
+ return {
4693
+ active: true,
4694
+ quarantineId: stored.receipt.quarantineId,
4695
+ mode: stored.receipt.mode,
4696
+ failureSummary: stored.receipt.failureSummary,
4697
+ failureLogTail: stored.receipt.failureLogTail,
4698
+ entries: stored.receipt.entries.filter((entry) => entry.resolvedAtMillis === void 0).map((entry) => ({
4699
+ packageName: entry.packageName,
4700
+ ...this.installedByPackage(entry.packageName) === void 0 ? {} : { extensionId: this.installedByPackage(entry.packageName).extensionId },
4701
+ dismissed: entry.notificationDismissedAtMillis !== void 0,
4702
+ resolved: false
4703
+ }))
4704
+ };
4705
+ }
4706
+ async dismiss(packageName) {
4707
+ const stored = await this.activeReceiptForPackage(packageName);
4708
+ const entry = stored.receipt.entries.find((item) => item.packageName === packageName);
4709
+ if (entry.notificationDismissedAtMillis === void 0) {
4710
+ entry.notificationDismissedAtMillis = this.now();
4711
+ await this.writeStoredReceipt(stored);
4712
+ }
4713
+ }
4714
+ async reenable(packageName) {
4715
+ const stored = await this.activeReceiptForPackage(packageName);
4716
+ const entry = stored.receipt.entries.find((item) => item.packageName === packageName);
4717
+ entry.reenableRequestedAtMillis = this.now();
4718
+ await this.writeStoredReceipt(stored);
4719
+ let profileEnabled = false;
4720
+ try {
4721
+ await this.options.setProfileEnabled(packageName, true);
4722
+ profileEnabled = true;
4723
+ await this.options.requestRestart({
4724
+ packageName,
4725
+ previousProfileIncluded: false
4726
+ });
4727
+ } catch (error) {
4728
+ if (profileEnabled) await this.options.setProfileEnabled(packageName, false).catch(() => void 0);
4729
+ delete entry.reenableRequestedAtMillis;
4730
+ await this.writeStoredReceipt(stored).catch(() => void 0);
4731
+ throw error;
4732
+ }
4733
+ }
4734
+ async resolveActive() {
4735
+ for (const stored of await this.readReceipts("active")) {
4736
+ let changed = false;
4737
+ for (const entry of stored.receipt.entries) {
4738
+ if (entry.resolvedAtMillis !== void 0) continue;
4739
+ if (entry.reenableRequestedAtMillis !== void 0 && this.options.isPackageActive(entry.packageName)) {
4740
+ entry.resolvedAtMillis = this.now();
4741
+ delete entry.reenableRequestedAtMillis;
4742
+ const installed = this.installedByPackage(entry.packageName);
4743
+ if (installed !== void 0) {
4744
+ const { lastError: _lastError, ...retained } = installed;
4745
+ this.options.installStore.put({
4746
+ ...retained,
4747
+ enabled: true,
4748
+ active: true
4749
+ });
4750
+ }
4751
+ changed = true;
4752
+ continue;
4753
+ }
4754
+ if (entry.reenableRequestedAtMillis !== void 0) {
4755
+ await this.options.setProfileEnabled(entry.packageName, false);
4756
+ delete entry.reenableRequestedAtMillis;
4757
+ const installed = this.installedByPackage(entry.packageName);
4758
+ if (installed !== void 0) this.options.installStore.put({
4759
+ ...installed,
4760
+ enabled: false,
4761
+ active: false
4762
+ });
4763
+ changed = true;
4764
+ }
4765
+ }
4766
+ if (stored.receipt.entries.every((entry) => entry.resolvedAtMillis !== void 0)) {
4767
+ stored.receipt.phase = "resolved";
4768
+ changed = true;
4769
+ }
4770
+ if (changed) await this.writeStoredReceipt(stored);
4771
+ }
4772
+ }
4773
+ async health(packageName) {
4774
+ if ((await this.activeReceiptForPackage(packageName)).receipt.entries.find((item) => item.packageName === packageName).reenableRequestedAtMillis === void 0) throw new Error("该扩展尚未请求重新启用");
4775
+ const profileEnabled = await this.profileContains(packageName);
4776
+ const active = this.options.isPackageActive(packageName);
4777
+ if (profileEnabled && active) await this.resolveActive();
4778
+ return {
4779
+ profileEnabled,
4780
+ active
4781
+ };
4782
+ }
4783
+ async activeReceiptForPackage(packageName) {
4784
+ if (!isPackageName(packageName) || isProtectedPackage(packageName)) throw new Error("隔离扩展包名无效");
4785
+ const matches = (await this.readReceipts("active")).filter((stored) => stored.receipt.entries.some((entry) => entry.packageName === packageName && entry.resolvedAtMillis === void 0));
4786
+ matches.sort((left, right) => right.receipt.createdAtMillis - left.receipt.createdAtMillis);
4787
+ if (matches[0] === void 0) throw new Error("该扩展没有活动的启动隔离记录");
4788
+ return matches[0];
4789
+ }
4790
+ installedByPackage(packageName) {
4791
+ return this.options.installStore.list().find((item) => item.profilePackageName === packageName);
4792
+ }
4793
+ async profileContains(packageName) {
4794
+ try {
4795
+ const value = JSON.parse(await readFile(join(this.options.dshHome, "profiles", "web", "package.json"), "utf8"));
4796
+ if (!isObject(value) || !isObject(value.dsh) || !isObject(value.dsh.profile) || !Array.isArray(value.dsh.profile.bundles)) return false;
4797
+ return value.dsh.profile.bundles.some((item) => item === packageName);
4798
+ } catch {
4799
+ return false;
4800
+ }
4801
+ }
4802
+ async readReceipts(phase) {
4803
+ const root = join(this.options.dshHome, "arkme-self", QUARANTINE_DIRECTORY_NAME);
4804
+ const directories = await readdir(root, { withFileTypes: true }).catch((error) => {
4805
+ if (error.code === "ENOENT") return [];
4806
+ throw error;
4807
+ });
4808
+ const receipts = [];
4809
+ for (const directory of directories) {
4810
+ if (!directory.isDirectory() || !QUARANTINE_ID_PATTERN.test(directory.name)) continue;
4811
+ const receiptPath = join(root, directory.name, RECEIPT_FILE_NAME);
4812
+ try {
4813
+ const receipt = parseReceipt(await readFile(receiptPath, "utf8"));
4814
+ if (receipt.quarantineId !== directory.name || receipt.environment !== this.options.environment || phase !== void 0 && receipt.phase !== phase) continue;
4815
+ receipts.push({
4816
+ path: receiptPath,
4817
+ receipt
4818
+ });
4819
+ } catch {}
4820
+ }
4821
+ return receipts;
4822
+ }
4823
+ async writeStoredReceipt(stored) {
4824
+ stored.receipt.updatedAtMillis = this.now();
4825
+ await writeTextAtomically(stored.path, `${JSON.stringify(stored.receipt, void 0, 2)}\n`);
4826
+ }
4827
+ };
4828
+ function parseReceipt(text) {
4829
+ const value = JSON.parse(text);
4830
+ if (!isObject(value) || value.schemaVersion !== 1 || typeof value.quarantineId !== "string" || !QUARANTINE_ID_PATTERN.test(value.quarantineId) || value.environment !== "prod" && value.environment !== "test" || ![
4831
+ "pending",
4832
+ "active",
4833
+ "restored",
4834
+ "resolved"
4835
+ ].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("桌面扩展隔离记录无效");
4836
+ const entries = value.entries.map((raw) => parseEntry(raw));
4837
+ if (new Set(entries.map((entry) => entry.packageName)).size !== entries.length) throw new Error("桌面扩展隔离记录包含重复扩展");
4838
+ return {
4839
+ schemaVersion: 1,
4840
+ quarantineId: value.quarantineId,
4841
+ environment: value.environment,
4842
+ phase: value.phase,
4843
+ mode: value.mode,
4844
+ createdAtMillis: Number(value.createdAtMillis),
4845
+ updatedAtMillis: Number(value.updatedAtMillis),
4846
+ ...typeof value.runtimeReleaseId === "string" ? { runtimeReleaseId: value.runtimeReleaseId } : {},
4847
+ failureSummary: value.failureSummary,
4848
+ failureLogTail: value.failureLogTail,
4849
+ entries
4850
+ };
4851
+ }
4852
+ function parseEntry(value) {
4853
+ 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("桌面扩展隔离条目无效");
4854
+ return {
4855
+ packageName: value.packageName,
4856
+ dependencySpec: value.dependencySpec,
4857
+ originalBundleIndex: Number(value.originalBundleIndex),
4858
+ ...value.synchronizedAtMillis === void 0 ? {} : { synchronizedAtMillis: Number(value.synchronizedAtMillis) },
4859
+ ...value.notificationDismissedAtMillis === void 0 ? {} : { notificationDismissedAtMillis: Number(value.notificationDismissedAtMillis) },
4860
+ ...value.reenableRequestedAtMillis === void 0 ? {} : { reenableRequestedAtMillis: Number(value.reenableRequestedAtMillis) },
4861
+ ...value.resolvedAtMillis === void 0 ? {} : { resolvedAtMillis: Number(value.resolvedAtMillis) }
4862
+ };
4863
+ }
4864
+ function optionalMillis(value) {
4865
+ return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 0;
4866
+ }
4867
+ function isObject(value) {
4868
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4869
+ }
4870
+ function isPackageName(value) {
4871
+ return PACKAGE_NAME_PATTERN.test(value);
4872
+ }
4873
+ function isProtectedPackage(packageName) {
4874
+ return packageName === "@senguoyun/dsh-arkme" || packageName.startsWith("@deepseek-ai/");
4875
+ }
4876
+ async function writeTextAtomically(filePath, text) {
4877
+ const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
4878
+ try {
4879
+ const handle = await open(temporaryPath, "wx", 384);
4880
+ try {
4881
+ await handle.writeFile(text, "utf8");
4882
+ await handle.sync();
4883
+ } finally {
4884
+ await handle.close();
4885
+ }
4886
+ await rename(temporaryPath, filePath);
4887
+ } finally {
4888
+ await rm(temporaryPath, { force: true }).catch(() => void 0);
4889
+ }
4890
+ }
4891
+ //#endregion
4641
4892
  //#region src/extensions/install-tasks.ts
4642
4893
  const TASK_RETENTION_MILLIS = 600 * 1e3;
4643
4894
  const MAX_RETAINED_TASKS = 100;
@@ -7915,7 +8166,7 @@ var ArkmeExtensionProfileInstaller = class {
7915
8166
  await this.mutationTail;
7916
8167
  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
8168
  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,
8169
+ 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
8170
  parentPid: process.pid,
7920
8171
  execPath: this.options.execPath,
7921
8172
  dshBinPath: this.options.dshBinPath,
@@ -7935,8 +8186,10 @@ var ArkmeExtensionProfileInstaller = class {
7935
8186
  activationChange: true,
7936
8187
  previousProfileIncluded: input.previousProfileIncluded === true
7937
8188
  } : {},
8189
+ ...input.desktopQuarantineActivation === true ? { desktopQuarantineActivation: true } : {},
7938
8190
  healthUrl: this.options.healthUrl,
7939
- logPath: join(this.options.stateDirectory, "extension-profile-restart.log")
8191
+ logPath: join(this.options.stateDirectory, "extension-profile-restart.log"),
8192
+ ...this.options.runtimeReleaseId === void 0 ? {} : { runtimeReleaseId: this.options.runtimeReleaseId }
7940
8193
  };
7941
8194
  if (this.options.supervisedExitCode !== void 0) {
7942
8195
  if (!Number.isSafeInteger(this.options.supervisedExitCode) || this.options.supervisedExitCode < 1 || this.options.supervisedExitCode > 255) throw new Error("受监督重启退出码无效");
@@ -7985,6 +8238,17 @@ var ArkmeExtensionProfileInstaller = class {
7985
8238
  setTimeout(() => process.kill(process.pid, "SIGTERM"), 800).unref?.();
7986
8239
  }))();
7987
8240
  }
8241
+ async restartDesktopQuarantine(input) {
8242
+ if (!isArkmeProfilePackageName(input.packageName)) throw new Error("扩展 Bundle 包名无效");
8243
+ await this.restart({
8244
+ extensionId: `desktop-quarantine:${input.packageName}`,
8245
+ packageName: input.packageName,
8246
+ expectActive: true,
8247
+ activationChange: true,
8248
+ desktopQuarantineActivation: true,
8249
+ previousProfileIncluded: false
8250
+ });
8251
+ }
7988
8252
  async run(args) {
7989
8253
  if (this.options.run !== void 0) return await this.options.run(args);
7990
8254
  if (!existsSync(this.options.execPath) || !existsSync(this.options.dshBinPath)) throw new Error("当前 DSH 运行方式不支持修改 Profile 插件");
@@ -18300,6 +18564,9 @@ function resolveArkmeAppVersion(configuredAppVersion, environment = process.env)
18300
18564
  const injected = environment.ARKME_APP_VERSION?.trim();
18301
18565
  return injected === void 0 || injected === "" ? void 0 : injected;
18302
18566
  }
18567
+ function resolvePluginUpdateEnabled(configured, environment = process.env) {
18568
+ return configured && environment.ARKME_RUNTIME_MANAGED !== "1";
18569
+ }
18303
18570
  function dshRemoteClientId(accessToken) {
18304
18571
  const parts = accessToken.split(".");
18305
18572
  if (parts.length !== 3) return void 0;
@@ -18344,7 +18611,7 @@ function apply(ctx, config) {
18344
18611
  const extensionStore = new ArkmeExtensionInstallStore(extensionDirectory);
18345
18612
  const clientModules = ctx.clientModules;
18346
18613
  const updateManager = new ArkmePluginUpdateManager({
18347
- enabled: config.updateCheckEnabled,
18614
+ enabled: resolvePluginUpdateEnabled(config.updateCheckEnabled),
18348
18615
  channel: config.updateChannel,
18349
18616
  updateServiceBaseUrl: config.updateServiceBaseUrl,
18350
18617
  ...config.updateArtifactBaseUrl.trim() === "" ? {} : { updateArtifactBaseUrl: config.updateArtifactBaseUrl },
@@ -18386,9 +18653,26 @@ function apply(ctx, config) {
18386
18653
  installStoreDirectory: extensionDirectory,
18387
18654
  ...process.env.ARKME_DESKTOP_MANAGED_RESTART === "1" && process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH !== void 0 ? {
18388
18655
  supervisedExitCode: 75,
18389
- supervisedPlanPath: process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH
18656
+ supervisedPlanPath: process.env.ARKME_DESKTOP_MANAGED_RESTART_PLAN_PATH,
18657
+ ...process.env.ARKME_RUNTIME_RELEASE_ID === void 0 ? {} : { runtimeReleaseId: process.env.ARKME_RUNTIME_RELEASE_ID }
18390
18658
  } : {}
18391
18659
  });
18660
+ const pluginInventory = ctx.get("pluginInventory");
18661
+ const desktopQuarantine = new ArkmeDesktopExtensionQuarantine({
18662
+ dshHome,
18663
+ environment: config.environment,
18664
+ installStore: extensionStore,
18665
+ setProfileEnabled: async (packageName, enabled) => {
18666
+ await extensionProfileInstaller.setEnabled(packageName, enabled);
18667
+ },
18668
+ requestRestart: async ({ packageName }) => {
18669
+ await extensionProfileInstaller.restartDesktopQuarantine({ packageName });
18670
+ },
18671
+ isPackageActive: (packageName) => pluginInventory.list().entries.some((entry) => entry.moduleName === packageName && entry.enabled && entry.fiberPhase === "active")
18672
+ });
18673
+ desktopQuarantine.reconcile().catch((error) => {
18674
+ ctx.logger.warn("Arkme desktop extension quarantine reconciliation failed: %s", error instanceof Error ? error.message : String(error));
18675
+ });
18392
18676
  const ownedExtensionStore = new ArkmeOwnedExtensionStore(extensionDirectory);
18393
18677
  const ownedExtensionRefs = new ArkmeOwnedExtensionRefs();
18394
18678
  const ownedExtensionHostInstanceId = randomUUID();
@@ -18414,7 +18698,7 @@ function apply(ctx, config) {
18414
18698
  profileDirectory: extensionProfileDirectory,
18415
18699
  profileInstaller: extensionProfileInstaller,
18416
18700
  clientApiPath: config.routePath,
18417
- pluginInventory: ctx.get("pluginInventory"),
18701
+ pluginInventory,
18418
18702
  ...dshRuntimeVersion === void 0 ? {} : { dshRuntimeVersion }
18419
18703
  });
18420
18704
  extensionManager = manager;
@@ -18523,7 +18807,8 @@ function apply(ctx, config) {
18523
18807
  extensionManager: () => extensionManager,
18524
18808
  extensionInstallTasks: () => extensionInstallTasks,
18525
18809
  ownedExtensionInventory: () => ownedExtensionInventory,
18526
- remoteHost: () => remoteHost
18810
+ remoteHost: () => remoteHost,
18811
+ desktopQuarantine
18527
18812
  });
18528
18813
  const callAssetHandler = createOutgoingCallAssetHandler({ routePrefix: `${config.routePath}/call` });
18529
18814
  const richMediaOptions = {
@@ -18718,4 +19003,4 @@ function validateConfig(ctx, config) {
18718
19003
  }
18719
19004
  }
18720
19005
  //#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 };
19006
+ 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-DUqdJv8K.js";
2
2
  export { activatePersistentArkmeExtension, applyPersistentArkmeHostExtension, deactivatePersistentArkmeExtension, invokePersistentArkmeExtension, persistentArkmeExtensionActive, persistentArkmeExtensionRuntimeState };