editmamei 0.22.2 → 0.22.4

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.
Binary file
Binary file
@@ -27,6 +27,7 @@ import { listTemplates } from '../utils/template-storage.js';
27
27
  import { Kernel } from '../kernel/kernel.js';
28
28
  import { HOST_MIN_ABI } from '../kernel/host-api.js';
29
29
  import { ceModule } from '../modules/ce/index.js';
30
+ import { hostDetectionRuntime } from '../detection/runtime.js';
30
31
  export function classifyModuleOutcome(inputs) {
31
32
  if (inputs.proModuleLoaded && inputs.skipReason === null)
32
33
  return 'loaded';
@@ -208,6 +209,7 @@ export class EditmameiServer {
208
209
  registry: this.toolRegistry,
209
210
  connection: this.session.getConnection(),
210
211
  snippet: this.snippetClient,
212
+ detection: hostDetectionRuntime(),
211
213
  resolveModuleSnippet: (manifest) => manifest.goCoreSnippets && manifest.goCoreSnippets.length > 0 && this.proModule
212
214
  ? new GoSnippetClient({ binaryPath: join(this.proModule.binDir, coreBinaryName()) })
213
215
  : null,
@@ -328,6 +330,37 @@ export class EditmameiServer {
328
330
  `${err instanceof Error ? err.message : String(err)}`);
329
331
  }
330
332
  }
333
+ async ensureEntitledModuleFresh(delivery = {}) {
334
+ if (this.moduleSkipReason !== null)
335
+ return;
336
+ if (!isProEntitled())
337
+ return;
338
+ const underTest = process.env.VITEST !== undefined || process.env.NODE_ENV === 'test';
339
+ if (underTest && !delivery.fetchImpl)
340
+ return;
341
+ const license = readLicense();
342
+ if (!license)
343
+ return;
344
+ try {
345
+ const prov = await provisionModules(license.key, {
346
+ config: delivery.config,
347
+ fetchImpl: delivery.fetchImpl,
348
+ signingKeys: delivery.signingKeys,
349
+ sleep: delivery.sleep,
350
+ });
351
+ for (const m of prov.installed) {
352
+ this.logger.info(`Pro module updated to v${m.version} — restart to load.`);
353
+ }
354
+ for (const e of prov.errors) {
355
+ this.logger.warn(`Pro-module freshness check could not provision the ${e.sku} module ` +
356
+ `(staying on the installed version): ${e.message}`);
357
+ }
358
+ }
359
+ catch (err) {
360
+ this.logger.warn(`Background Pro-module freshness check failed (staying on the installed version): ` +
361
+ `${err instanceof Error ? err.message : String(err)}`);
362
+ }
363
+ }
331
364
  assertToolsClassified() {
332
365
  for (const tool of this.toolRegistry.list()) {
333
366
  tierOf(tool.name);
@@ -483,6 +516,7 @@ export class EditmameiServer {
483
516
  await this.server.connect(transport);
484
517
  void this.session.initialize().catch(() => undefined);
485
518
  void this.reprovisionIfModuleSkipped();
519
+ void this.ensureEntitledModuleFresh();
486
520
  this.telemetry.start();
487
521
  void this.telemetry.flushOutboxOnStartup();
488
522
  this.server.onclose = () => {
@@ -2,35 +2,60 @@ import { createRequire } from 'node:module';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import { decode } from 'jpeg-js';
5
- import { readFileSync } from 'node:fs';
6
- import * as ort from 'onnxruntime-web';
5
+ import { existsSync, readFileSync } from 'node:fs';
6
+ import * as staticOrt from 'onnxruntime-web';
7
+ export let ort = staticOrt;
8
+ let injectedLoadModel = null;
9
+ let injectedModelDirs = null;
10
+ export function useHostRuntime(host, proModelsDir) {
11
+ injectedLoadModel = host.loadModel;
12
+ ort = host.ort;
13
+ injectedModelDirs = [proModelsDir, host.ceModelsDir];
14
+ }
15
+ export function hostDetectionRuntime() {
16
+ return { loadModel: hostLoadModel, ort: staticOrt, ceModelsDir: hostModelsDir() };
17
+ }
7
18
  let configured = false;
8
19
  function configureOrt() {
9
20
  if (configured)
10
21
  return;
11
22
  const require = createRequire(import.meta.url);
12
23
  const ortDist = dirname(require.resolve('onnxruntime-web'));
13
- ort.env.wasm.wasmPaths = pathToFileURL(join(ortDist, '/')).href;
14
- ort.env.wasm.numThreads = 1;
15
- ort.env.logLevel = 'error';
24
+ staticOrt.env.wasm.wasmPaths = pathToFileURL(join(ortDist, '/')).href;
25
+ staticOrt.env.wasm.numThreads = 1;
26
+ staticOrt.env.logLevel = 'error';
16
27
  configured = true;
17
28
  }
18
29
  const sessions = new Map();
19
- export function loadModel(absPath) {
30
+ function hostLoadModel(absPath) {
20
31
  configureOrt();
21
32
  let p = sessions.get(absPath);
22
33
  if (!p) {
23
- p = ort.InferenceSession.create(absPath, { logSeverityLevel: 3 });
34
+ p = staticOrt.InferenceSession.create(absPath, { logSeverityLevel: 3 });
24
35
  sessions.set(absPath, p);
25
36
  }
26
37
  return p;
27
38
  }
28
- export function resolveModelPath(filename) {
39
+ export function loadModel(absPath) {
40
+ return injectedLoadModel ? injectedLoadModel(absPath) : hostLoadModel(absPath);
41
+ }
42
+ function hostModelsDir() {
29
43
  const override = process.env.EDITMAMEI_MODELS_DIR;
30
44
  if (override)
31
- return join(override, filename);
45
+ return override;
32
46
  const here = dirname(fileURLToPath(import.meta.url));
33
- return join(here, '..', 'models', filename);
47
+ return join(here, '..', 'models');
48
+ }
49
+ export function resolveModelPath(filename) {
50
+ if (injectedModelDirs) {
51
+ for (const dir of injectedModelDirs) {
52
+ const candidate = join(dir, filename);
53
+ if (existsSync(candidate))
54
+ return candidate;
55
+ }
56
+ return join(injectedModelDirs[injectedModelDirs.length - 1], filename);
57
+ }
58
+ return join(hostModelsDir(), filename);
34
59
  }
35
60
  export function decodeJpeg(path) {
36
61
  let img;
@@ -43,4 +68,3 @@ export function decodeJpeg(path) {
43
68
  }
44
69
  return { width: img.width, height: img.height, data: img.data };
45
70
  }
46
- export { ort };
@@ -1,2 +1,2 @@
1
- export const KERNEL_ABI = 1;
1
+ export const KERNEL_ABI = 2;
2
2
  export const HOST_MIN_ABI = 1;
@@ -1,12 +1,13 @@
1
1
  import { CompositeSnippetClient } from '../api/snippet-client.js';
2
2
  import { Logger } from '../utils/logger.js';
3
3
  import { runScript } from '../utils/run-script.js';
4
- import { KERNEL_ABI } from './host-api.js';
4
+ import { KERNEL_ABI, } from './host-api.js';
5
5
  export class Kernel {
6
6
  registry;
7
7
  connection;
8
8
  snippet;
9
9
  resolveModuleSnippet;
10
+ detection;
10
11
  sessionId;
11
12
  logger;
12
13
  invokeDepth = 0;
@@ -16,6 +17,7 @@ export class Kernel {
16
17
  this.connection = deps.connection;
17
18
  this.snippet = deps.snippet;
18
19
  this.resolveModuleSnippet = deps.resolveModuleSnippet;
20
+ this.detection = deps.detection;
19
21
  this.sessionId = deps.sessionId;
20
22
  this.logger = deps.logger;
21
23
  }
@@ -35,6 +37,7 @@ export class Kernel {
35
37
  connection: this.connection,
36
38
  executeScript: (innerBody, timeoutMs) => runScript(this.connection, innerBody, timeoutMs),
37
39
  snippet: this.snippetFor(manifest),
40
+ detection: this.detection,
38
41
  session: { id: this.sessionId },
39
42
  logger: new Logger(`module:${manifest.id}`),
40
43
  };
@@ -4,8 +4,8 @@ import { readLicense, writeLicense, clearLicense, } from './store.js';
4
4
  import { PolarLicenseClient, PolarLicenseError } from './polar-client.js';
5
5
  import { Logger } from '../utils/logger.js';
6
6
  const logger = new Logger('License');
7
- export const GRACE_MS = 30 * 24 * 60 * 60 * 1000;
8
- export const REFRESH_AFTER_MS = 7 * 24 * 60 * 60 * 1000;
7
+ export const GRACE_MS = 7 * 24 * 60 * 60 * 1000;
8
+ export const REFRESH_AFTER_MS = 24 * 60 * 60 * 1000;
9
9
  export const EXPIRED_REFRESH_TIMEOUT_MS = 5_000;
10
10
  export const CLOCK_SKEW_TOLERANCE_MS = 24 * 60 * 60 * 1000;
11
11
  export function evaluateEntitlement(rec, now) {
@@ -1,10 +1,14 @@
1
1
  import { refreshIfStale } from './entitlement.js';
2
- export function createPingLicenseRefresher(refresh = refreshIfStale) {
3
- let fired = false;
2
+ export const PING_REFRESH_MIN_INTERVAL_MS = 15 * 60_000;
3
+ export function createPingLicenseRefresher(refresh = refreshIfStale, opts = {}) {
4
+ const now = opts.now ?? Date.now;
5
+ const minIntervalMs = opts.minIntervalMs ?? PING_REFRESH_MIN_INTERVAL_MS;
6
+ let lastAttemptAt = Number.NEGATIVE_INFINITY;
4
7
  return () => {
5
- if (fired)
8
+ const t = now();
9
+ if (t - lastAttemptAt < minIntervalMs)
6
10
  return;
7
- fired = true;
11
+ lastAttemptAt = t;
8
12
  try {
9
13
  void refresh().catch(() => { });
10
14
  }
Binary file
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.22.2';
1
+ export const VERSION = '0.22.4';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editmamei",
3
- "version": "0.22.2",
3
+ "version": "0.22.4",
4
4
  "description": "Editmamei — Unlock Photoshop with natural-language photo editing (Community Edition)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",