editmamei 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/bin/editmamei-core-darwin-arm64 +0 -0
- package/dist/bin/editmamei-core-darwin-x64 +0 -0
- package/dist/bin/editmamei-core-win-x64.exe +0 -0
- package/dist/cli/help.js +1 -1
- package/dist/core/server.js +110 -13
- package/dist/core/tool-groups.js +1 -0
- package/dist/core/tool-tiers.js +1 -0
- package/dist/install-channel.js +52 -2
- package/dist/kernel/module-lifecycle.js +44 -17
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/telemetry/activity.js +149 -0
- package/dist/telemetry/client.js +115 -5
- package/dist/telemetry/events.js +43 -1
- package/dist/tools/document-tools.js +23 -1
- package/dist/update/check.js +24 -10
- package/dist/utils/session-log.js +3 -3
- package/dist/utils/xmp-crs.js +682 -0
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/telemetry/client.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { release } from 'node:os';
|
|
1
2
|
import { Logger } from '../utils/logger.js';
|
|
2
3
|
import { EDITION } from '../edition.js';
|
|
3
4
|
import { VERSION } from '../version.js';
|
|
4
5
|
import { resolveInstallChannel } from '../install-channel.js';
|
|
5
|
-
import {
|
|
6
|
+
import { READ_ONLY_TOOLS, KEPT_WORK_TOOLS, nodeMajor, archToken, osMajor, boundMajor, } from './activity.js';
|
|
7
|
+
import { buildClientConnected, buildDiagnosticEvent, buildModuleStatus, buildSessionStart, buildSessionSummary, buildUsageEvent, dayBucket, normalizeDayBucket, isContentSafe, PS_VERSION_UNKNOWN, } from './events.js';
|
|
6
8
|
import { sanitizeMessage, sanitizeSnippet, sanitizeStderrTail } from './sanitize.js';
|
|
7
9
|
import { httpTransport, resolveEndpoint } from './transport.js';
|
|
8
10
|
import { appendOutboxSync, clearOutbox, clearSessionState, readOutbox, readSessionState, writeSessionStateSync, } from './outbox.js';
|
|
@@ -10,6 +12,8 @@ const MAX_BATCH_SIZE = 100;
|
|
|
10
12
|
const MAX_QUEUE_SIZE = 500;
|
|
11
13
|
const DEFAULT_FLUSH_INTERVAL_MS = 5 * 60_000;
|
|
12
14
|
const SESSION_PERSIST_THROTTLE_MS = 10_000;
|
|
15
|
+
const MAX_SESSION_DURATION_S = 604_800;
|
|
16
|
+
const MAX_INSTALL_ASSET_COUNT = 100_000;
|
|
13
17
|
function isTestEnv() {
|
|
14
18
|
return process.env.VITEST !== undefined || process.env.NODE_ENV === 'test';
|
|
15
19
|
}
|
|
@@ -25,12 +29,23 @@ export class TelemetryClient {
|
|
|
25
29
|
active;
|
|
26
30
|
outboxOpts;
|
|
27
31
|
getModuleStatus;
|
|
32
|
+
resolvedModuleStatus = null;
|
|
28
33
|
queue = [];
|
|
29
34
|
timer = null;
|
|
30
35
|
shutdownPromise = null;
|
|
31
36
|
toolCallCount = 0;
|
|
32
37
|
distinctTools = new Set();
|
|
33
38
|
anyFailures = false;
|
|
39
|
+
firstCallAtMs = null;
|
|
40
|
+
lastCallAtMs = null;
|
|
41
|
+
retryCount = 0;
|
|
42
|
+
lastCallSuccess = null;
|
|
43
|
+
editsOk = 0;
|
|
44
|
+
keptWork = 0;
|
|
45
|
+
droppedEvents = 0;
|
|
46
|
+
behindLatest = null;
|
|
47
|
+
moduleUpdate = 'none';
|
|
48
|
+
installAssets = {};
|
|
34
49
|
lastSessionPersistMs = 0;
|
|
35
50
|
startDayBucket = null;
|
|
36
51
|
constructor(opts) {
|
|
@@ -61,24 +76,95 @@ export class TelemetryClient {
|
|
|
61
76
|
this.timer = setInterval(() => void this.flush(), this.flushIntervalMs);
|
|
62
77
|
this.timer.unref?.();
|
|
63
78
|
if (this.settings.telemetry.usage) {
|
|
64
|
-
|
|
65
|
-
const
|
|
79
|
+
const hostNodeMajor = boundMajor(nodeMajor(), 999);
|
|
80
|
+
const hostOsMajor = boundMajor(osMajor(process.platform, release()), 999);
|
|
81
|
+
this.enqueue(buildSessionStart(this.dims, this.now(), {
|
|
82
|
+
...(hostNodeMajor !== null ? { node_major: hostNodeMajor } : {}),
|
|
83
|
+
arch: archToken(),
|
|
84
|
+
...(hostOsMajor !== null ? { os_major: hostOsMajor } : {}),
|
|
85
|
+
}));
|
|
86
|
+
const moduleStatus = this.moduleStatus();
|
|
66
87
|
if (moduleStatus)
|
|
67
88
|
this.enqueue(buildModuleStatus(this.dims, moduleStatus, this.now()));
|
|
68
89
|
void this.flush();
|
|
69
90
|
}
|
|
70
91
|
}
|
|
92
|
+
recordClientConnected(info) {
|
|
93
|
+
if (!this.active || !this.settings.telemetry.usage)
|
|
94
|
+
return;
|
|
95
|
+
this.enqueue(buildClientConnected(this.dims, info, this.now()));
|
|
96
|
+
}
|
|
71
97
|
recordCall(call) {
|
|
72
98
|
if (!this.active || !this.settings.telemetry.usage)
|
|
73
99
|
return;
|
|
74
100
|
this.ensureStartDayBucket();
|
|
101
|
+
const nowDate = this.now();
|
|
102
|
+
const nowMs = nowDate.getTime();
|
|
75
103
|
this.toolCallCount += 1;
|
|
76
104
|
this.distinctTools.add(call.tool);
|
|
77
105
|
if (!call.success)
|
|
78
106
|
this.anyFailures = true;
|
|
79
|
-
this.
|
|
107
|
+
this.lastCallSuccess = call.success;
|
|
108
|
+
if (call.retry)
|
|
109
|
+
this.retryCount += 1;
|
|
110
|
+
if (call.success) {
|
|
111
|
+
if (!READ_ONLY_TOOLS.has(call.tool))
|
|
112
|
+
this.editsOk += 1;
|
|
113
|
+
if (KEPT_WORK_TOOLS.has(call.tool))
|
|
114
|
+
this.keptWork += 1;
|
|
115
|
+
}
|
|
116
|
+
if (this.firstCallAtMs === null)
|
|
117
|
+
this.firstCallAtMs = nowMs;
|
|
118
|
+
this.lastCallAtMs = nowMs;
|
|
119
|
+
this.enqueue(buildUsageEvent(this.dims, call, nowDate));
|
|
80
120
|
this.persistSessionStateThrottled();
|
|
81
121
|
}
|
|
122
|
+
setBehindLatest(value) {
|
|
123
|
+
this.behindLatest = value;
|
|
124
|
+
}
|
|
125
|
+
setModuleUpdate(outcome) {
|
|
126
|
+
this.moduleUpdate = outcome;
|
|
127
|
+
}
|
|
128
|
+
setInstallAssets(assets) {
|
|
129
|
+
const templatesSaved = assets.templates_saved !== undefined ? clampCount(assets.templates_saved) : null;
|
|
130
|
+
const actionSets = assets.action_sets !== undefined ? clampCount(assets.action_sets) : null;
|
|
131
|
+
this.installAssets = {
|
|
132
|
+
...this.installAssets,
|
|
133
|
+
...(templatesSaved !== null ? { templates_saved: templatesSaved } : {}),
|
|
134
|
+
...(actionSets !== null ? { action_sets: actionSets } : {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
moduleStatus() {
|
|
138
|
+
if (this.resolvedModuleStatus === null) {
|
|
139
|
+
this.resolvedModuleStatus = { value: this.getModuleStatus() };
|
|
140
|
+
}
|
|
141
|
+
return this.resolvedModuleStatus.value;
|
|
142
|
+
}
|
|
143
|
+
durationS() {
|
|
144
|
+
if (this.firstCallAtMs === null || this.lastCallAtMs === null)
|
|
145
|
+
return undefined;
|
|
146
|
+
return Math.min(Math.max(0, Math.floor((this.lastCallAtMs - this.firstCallAtMs) / 1000)), MAX_SESSION_DURATION_S);
|
|
147
|
+
}
|
|
148
|
+
summaryFields() {
|
|
149
|
+
const duration = this.durationS();
|
|
150
|
+
const moduleStatus = this.moduleStatus();
|
|
151
|
+
return {
|
|
152
|
+
...(duration !== undefined ? { duration_s: duration } : {}),
|
|
153
|
+
retry_count: this.retryCount,
|
|
154
|
+
...(this.lastCallSuccess !== null ? { ended_after_failure: !this.lastCallSuccess } : {}),
|
|
155
|
+
edits_ok: this.editsOk,
|
|
156
|
+
kept_work: this.keptWork,
|
|
157
|
+
...(this.behindLatest !== null ? { behind_latest: this.behindLatest } : {}),
|
|
158
|
+
dropped_events: this.droppedEvents,
|
|
159
|
+
...(moduleStatus !== null ? { module_update: this.moduleUpdate } : {}),
|
|
160
|
+
...(this.installAssets.templates_saved !== undefined
|
|
161
|
+
? { templates_saved: this.installAssets.templates_saved }
|
|
162
|
+
: {}),
|
|
163
|
+
...(this.installAssets.action_sets !== undefined
|
|
164
|
+
? { action_sets: this.installAssets.action_sets }
|
|
165
|
+
: {}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
82
168
|
psVersion() {
|
|
83
169
|
const v = this.dims.getPsVersion();
|
|
84
170
|
return v && v.length > 0 ? v : PS_VERSION_UNKNOWN;
|
|
@@ -105,6 +191,7 @@ export class TelemetryClient {
|
|
|
105
191
|
tool_call_count: this.toolCallCount,
|
|
106
192
|
distinct_tools: this.distinctTools.size,
|
|
107
193
|
any_failures: this.anyFailures,
|
|
194
|
+
...this.summaryFields(),
|
|
108
195
|
};
|
|
109
196
|
writeSessionStateSync(state, this.outboxOpts);
|
|
110
197
|
}
|
|
@@ -123,6 +210,9 @@ export class TelemetryClient {
|
|
|
123
210
|
error_message: sanitizeMessage(diag.error_message),
|
|
124
211
|
...(diag.snippet ? { snippet: sanitizeSnippet(diag.snippet) } : {}),
|
|
125
212
|
...(diag.stderr_tail ? { stderr_tail: sanitizeStderrTail(diag.stderr_tail) } : {}),
|
|
213
|
+
...(diag.doc_depth !== undefined ? { doc_depth: diag.doc_depth } : {}),
|
|
214
|
+
...(diag.doc_mode !== undefined ? { doc_mode: diag.doc_mode } : {}),
|
|
215
|
+
...(diag.ps_locale !== undefined ? { ps_locale: diag.ps_locale } : {}),
|
|
126
216
|
}, this.now()));
|
|
127
217
|
}
|
|
128
218
|
restampPsVersion(events) {
|
|
@@ -141,7 +231,9 @@ export class TelemetryClient {
|
|
|
141
231
|
enqueue(event) {
|
|
142
232
|
this.queue.push(event);
|
|
143
233
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
144
|
-
|
|
234
|
+
const excess = this.queue.length - MAX_QUEUE_SIZE;
|
|
235
|
+
this.queue.splice(0, excess);
|
|
236
|
+
this.droppedEvents += excess;
|
|
145
237
|
}
|
|
146
238
|
if (this.queue.length >= this.maxBatchSize)
|
|
147
239
|
void this.flush();
|
|
@@ -177,6 +269,7 @@ export class TelemetryClient {
|
|
|
177
269
|
tool_call_count: this.toolCallCount,
|
|
178
270
|
distinct_tools: this.distinctTools.size,
|
|
179
271
|
any_failures: this.anyFailures,
|
|
272
|
+
...this.summaryFields(),
|
|
180
273
|
}, this.ensureStartDayBucket(), this.now()));
|
|
181
274
|
}
|
|
182
275
|
if (this.queue.length > 0) {
|
|
@@ -228,6 +321,8 @@ export class TelemetryClient {
|
|
|
228
321
|
}
|
|
229
322
|
}
|
|
230
323
|
function summaryFromState(s) {
|
|
324
|
+
const templatesSaved = s.templates_saved !== undefined ? clampCount(s.templates_saved) : null;
|
|
325
|
+
const actionSets = s.action_sets !== undefined ? clampCount(s.action_sets) : null;
|
|
231
326
|
return {
|
|
232
327
|
v: 2,
|
|
233
328
|
type: 'session_summary',
|
|
@@ -240,8 +335,23 @@ function summaryFromState(s) {
|
|
|
240
335
|
tool_call_count: s.tool_call_count,
|
|
241
336
|
distinct_tools: s.distinct_tools,
|
|
242
337
|
any_failures: s.any_failures,
|
|
338
|
+
...(s.duration_s !== undefined ? { duration_s: s.duration_s } : {}),
|
|
339
|
+
...(s.retry_count !== undefined ? { retry_count: s.retry_count } : {}),
|
|
340
|
+
...(s.ended_after_failure !== undefined ? { ended_after_failure: s.ended_after_failure } : {}),
|
|
341
|
+
...(s.edits_ok !== undefined ? { edits_ok: s.edits_ok } : {}),
|
|
342
|
+
...(s.kept_work !== undefined ? { kept_work: s.kept_work } : {}),
|
|
343
|
+
...(s.behind_latest !== undefined ? { behind_latest: s.behind_latest } : {}),
|
|
344
|
+
...(s.dropped_events !== undefined ? { dropped_events: s.dropped_events } : {}),
|
|
345
|
+
...(s.module_update !== undefined ? { module_update: s.module_update } : {}),
|
|
346
|
+
...(templatesSaved !== null ? { templates_saved: templatesSaved } : {}),
|
|
347
|
+
...(actionSets !== null ? { action_sets: actionSets } : {}),
|
|
243
348
|
};
|
|
244
349
|
}
|
|
245
350
|
function errMsg(err) {
|
|
246
351
|
return err instanceof Error ? err.message : String(err);
|
|
247
352
|
}
|
|
353
|
+
function clampCount(n) {
|
|
354
|
+
if (!Number.isFinite(n))
|
|
355
|
+
return null;
|
|
356
|
+
return Math.min(Math.max(0, Math.trunc(n)), MAX_INSTALL_ASSET_COUNT);
|
|
357
|
+
}
|
package/dist/telemetry/events.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { mapClientName, parseMajor, boundMajor } from './activity.js';
|
|
1
2
|
export const TELEMETRY_SCHEMA_VERSION = 2;
|
|
2
3
|
export const PS_VERSION_UNKNOWN = 'unknown';
|
|
4
|
+
export const MAX_RESULT_BYTES = 16_777_216;
|
|
5
|
+
const PS_LOCALE_RE = /^[a-z]{2,3}_[A-Z]{2}$/;
|
|
3
6
|
export function dayBucket(now) {
|
|
4
7
|
return now.toISOString().slice(0, 10);
|
|
5
8
|
}
|
|
@@ -31,6 +34,9 @@ export function buildUsageEvent(dims, call, now) {
|
|
|
31
34
|
success: call.success,
|
|
32
35
|
error_class: call.error_class === null ? null : normalizeErrorClass(call.error_class),
|
|
33
36
|
duration_ms: call.duration_ms,
|
|
37
|
+
...(call.result_bytes !== undefined
|
|
38
|
+
? { result_bytes: Math.min(call.result_bytes, MAX_RESULT_BYTES) }
|
|
39
|
+
: {}),
|
|
34
40
|
};
|
|
35
41
|
}
|
|
36
42
|
export function buildSessionSummary(dims, summary, tsBucket, now) {
|
|
@@ -46,9 +52,21 @@ export function buildSessionSummary(dims, summary, tsBucket, now) {
|
|
|
46
52
|
tool_call_count: summary.tool_call_count,
|
|
47
53
|
distinct_tools: summary.distinct_tools,
|
|
48
54
|
any_failures: summary.any_failures,
|
|
55
|
+
...(summary.duration_s !== undefined ? { duration_s: summary.duration_s } : {}),
|
|
56
|
+
...(summary.retry_count !== undefined ? { retry_count: summary.retry_count } : {}),
|
|
57
|
+
...(summary.ended_after_failure !== undefined
|
|
58
|
+
? { ended_after_failure: summary.ended_after_failure }
|
|
59
|
+
: {}),
|
|
60
|
+
...(summary.edits_ok !== undefined ? { edits_ok: summary.edits_ok } : {}),
|
|
61
|
+
...(summary.kept_work !== undefined ? { kept_work: summary.kept_work } : {}),
|
|
62
|
+
...(summary.behind_latest !== undefined ? { behind_latest: summary.behind_latest } : {}),
|
|
63
|
+
...(summary.dropped_events !== undefined ? { dropped_events: summary.dropped_events } : {}),
|
|
64
|
+
...(summary.module_update !== undefined ? { module_update: summary.module_update } : {}),
|
|
65
|
+
...(summary.templates_saved !== undefined ? { templates_saved: summary.templates_saved } : {}),
|
|
66
|
+
...(summary.action_sets !== undefined ? { action_sets: summary.action_sets } : {}),
|
|
49
67
|
};
|
|
50
68
|
}
|
|
51
|
-
export function buildSessionStart(dims, now) {
|
|
69
|
+
export function buildSessionStart(dims, now, facts = {}) {
|
|
52
70
|
return {
|
|
53
71
|
v: TELEMETRY_SCHEMA_VERSION,
|
|
54
72
|
type: 'session_start',
|
|
@@ -59,6 +77,25 @@ export function buildSessionStart(dims, now) {
|
|
|
59
77
|
platform: dims.platform,
|
|
60
78
|
ps_version: psVersionOf(dims),
|
|
61
79
|
channel: dims.channel,
|
|
80
|
+
...(facts.node_major !== undefined ? { node_major: facts.node_major } : {}),
|
|
81
|
+
...(facts.arch !== undefined ? { arch: facts.arch } : {}),
|
|
82
|
+
...(facts.os_major !== undefined ? { os_major: facts.os_major } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function buildClientConnected(dims, info, now) {
|
|
86
|
+
return {
|
|
87
|
+
v: TELEMETRY_SCHEMA_VERSION,
|
|
88
|
+
type: 'client_connected',
|
|
89
|
+
install_id: dims.install_id,
|
|
90
|
+
ts_bucket: dayBucket(now),
|
|
91
|
+
editmamei_version: dims.editmamei_version,
|
|
92
|
+
edition: dims.edition,
|
|
93
|
+
platform: dims.platform,
|
|
94
|
+
client: mapClientName(info.clientName),
|
|
95
|
+
client_major: boundMajor(parseMajor(info.clientVersion), 9999),
|
|
96
|
+
cap_sampling: info.capSampling,
|
|
97
|
+
cap_elicitation: info.capElicitation,
|
|
98
|
+
cap_roots: info.capRoots,
|
|
62
99
|
};
|
|
63
100
|
}
|
|
64
101
|
export function buildModuleStatus(dims, status, now) {
|
|
@@ -90,6 +127,11 @@ export function buildDiagnosticEvent(dims, diag, now) {
|
|
|
90
127
|
error_message: diag.error_message,
|
|
91
128
|
...(diag.snippet ? { snippet: diag.snippet } : {}),
|
|
92
129
|
...(diag.stderr_tail ? { stderr_tail: diag.stderr_tail } : {}),
|
|
130
|
+
...(diag.doc_depth !== undefined ? { doc_depth: diag.doc_depth } : {}),
|
|
131
|
+
...(diag.doc_mode !== undefined ? { doc_mode: diag.doc_mode } : {}),
|
|
132
|
+
...(diag.ps_locale !== undefined && PS_LOCALE_RE.test(diag.ps_locale)
|
|
133
|
+
? { ps_locale: diag.ps_locale }
|
|
134
|
+
: {}),
|
|
93
135
|
};
|
|
94
136
|
}
|
|
95
137
|
export function looksLikeAbsolutePath(value) {
|
|
@@ -83,6 +83,11 @@ const openDocumentSchema = {
|
|
|
83
83
|
description: 'Suppress all PS dialogs during open (raw/HEIC use last-used ACR settings). Default true for pipeline use.',
|
|
84
84
|
default: true,
|
|
85
85
|
},
|
|
86
|
+
bit_depth: {
|
|
87
|
+
type: 'number',
|
|
88
|
+
enum: [8, 16],
|
|
89
|
+
description: 'Open-time bits per channel, RAW sources only. Set this here rather than converting later: ps_convert_image_mode FLATTENS the document, so depth cannot be changed once an edit stack exists. Prefer 16 for anything with heavy gradients (skies, skin, long tonal moves). Ignored for non-raw files, and the returned bits_per_channel always reports what was actually opened.',
|
|
90
|
+
},
|
|
86
91
|
},
|
|
87
92
|
required: ['file_path'],
|
|
88
93
|
};
|
|
@@ -294,6 +299,10 @@ export function createDocumentTools(connection, snippetClient) {
|
|
|
294
299
|
},
|
|
295
300
|
already_open: { type: 'boolean' },
|
|
296
301
|
file_path: { type: 'string' },
|
|
302
|
+
bit_depth_warning: {
|
|
303
|
+
type: 'string',
|
|
304
|
+
description: 'Present only when a requested bit_depth was not what the document actually opened at — because the file is not a raw source, or Photoshop declined the requested depth. Absent means bits_per_channel is what you asked for.',
|
|
305
|
+
},
|
|
297
306
|
context: { type: 'object' },
|
|
298
307
|
},
|
|
299
308
|
required: ['success'],
|
|
@@ -524,8 +533,21 @@ async function openDocumentPipeline(connection, snippetClient, rawArgs) {
|
|
|
524
533
|
const args = validateArgs(openDocumentSchema, rawArgs);
|
|
525
534
|
filePath = args.file_path;
|
|
526
535
|
const suppressDialogs = args.suppress_dialogs;
|
|
527
|
-
const
|
|
536
|
+
const bitDepth = args.bit_depth;
|
|
537
|
+
const script = await snippetClient.build('openDocumentPipeline', {
|
|
538
|
+
filePath,
|
|
539
|
+
suppressDialogs,
|
|
540
|
+
rawBits: bitDepth ?? 0,
|
|
541
|
+
});
|
|
528
542
|
const result = await runScript(connection, script, OPEN_DOCUMENT_TIMEOUT_MS);
|
|
543
|
+
const payload = result;
|
|
544
|
+
if (bitDepth !== undefined && typeof payload.bits_per_channel === 'number') {
|
|
545
|
+
if (payload.bits_per_channel !== bitDepth) {
|
|
546
|
+
payload.bit_depth_warning = payload.is_raw_source
|
|
547
|
+
? `Requested ${bitDepth}-bit but Photoshop opened ${payload.bits_per_channel}-bit.`
|
|
548
|
+
: `bit_depth applies to RAW sources only; this file opened at ${payload.bits_per_channel}-bit.`;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
529
551
|
return {
|
|
530
552
|
content: [
|
|
531
553
|
{
|
package/dist/update/check.js
CHANGED
|
@@ -101,7 +101,15 @@ export function updateMessage(channel, latest) {
|
|
|
101
101
|
return `Download editmamei.mcpb (v${latest}) from ${RELEASES_URL} and reinstall the Claude Desktop extension.`;
|
|
102
102
|
case 'dev':
|
|
103
103
|
return `You're on a local dev build — pull the latest source and rebuild.`;
|
|
104
|
-
case '
|
|
104
|
+
case 'npx':
|
|
105
|
+
return `The next \`npx -y editmamei\` picks up v${latest} automatically — just restart your AI client.`;
|
|
106
|
+
case 'source':
|
|
107
|
+
return `You're running Editmamei from a source checkout — pull the latest changes and rebuild.`;
|
|
108
|
+
case 'npm_local':
|
|
109
|
+
return `Editmamei is installed locally in a project — update the \`editmamei\` dependency in that project (e.g. \`npm install editmamei@latest\`) and restart your MCP client.`;
|
|
110
|
+
case 'unknown':
|
|
111
|
+
return `Editmamei v${latest} is available. Update it the way you installed it, then restart your MCP client.`;
|
|
112
|
+
case 'npm_global':
|
|
105
113
|
default:
|
|
106
114
|
return `Run: npm install -g editmamei@latest (then restart your MCP client).`;
|
|
107
115
|
}
|
|
@@ -110,7 +118,7 @@ export function shouldCheckForUpdate(updateCheckEnabled, env = process.env) {
|
|
|
110
118
|
const inTest = env.VITEST !== undefined || env.NODE_ENV === 'test';
|
|
111
119
|
return updateCheckEnabled === true && !inTest;
|
|
112
120
|
}
|
|
113
|
-
export async function
|
|
121
|
+
export async function checkForUpdateWithStatus(opts = {}) {
|
|
114
122
|
try {
|
|
115
123
|
const env = opts.env ?? process.env;
|
|
116
124
|
const current = opts.current ?? VERSION;
|
|
@@ -118,19 +126,25 @@ export async function checkForUpdate(opts = {}) {
|
|
|
118
126
|
const fetchLatest = opts.fetchLatest ?? httpFetchLatest();
|
|
119
127
|
const manifest = await fetchLatest(resolveUpdateCheckUrl(env), opts.timeoutMs ?? 4000);
|
|
120
128
|
if (!manifest || !parseSemver(manifest.version))
|
|
121
|
-
return null;
|
|
129
|
+
return { status: 'failed', info: null };
|
|
122
130
|
const latest = manifest.version;
|
|
123
131
|
if (!isNewer(latest, current))
|
|
124
|
-
return null;
|
|
132
|
+
return { status: 'current', info: null };
|
|
125
133
|
return {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
status: 'newer',
|
|
135
|
+
info: {
|
|
136
|
+
current,
|
|
137
|
+
latest,
|
|
138
|
+
channel,
|
|
139
|
+
how_to_update: updateMessage(channel, latest),
|
|
140
|
+
fixed_tools: fixedToolsSince(manifest.fixesByVersion, current, latest),
|
|
141
|
+
},
|
|
131
142
|
};
|
|
132
143
|
}
|
|
133
144
|
catch {
|
|
134
|
-
return null;
|
|
145
|
+
return { status: 'failed', info: null };
|
|
135
146
|
}
|
|
136
147
|
}
|
|
148
|
+
export async function checkForUpdate(opts = {}) {
|
|
149
|
+
return (await checkForUpdateWithStatus(opts)).info;
|
|
150
|
+
}
|
|
@@ -267,7 +267,7 @@ export class SessionLog {
|
|
|
267
267
|
this.metaEmitted = true;
|
|
268
268
|
await this.writeLine(JSON.stringify(this.buildMetaEntry()));
|
|
269
269
|
}
|
|
270
|
-
async append(entry, result) {
|
|
270
|
+
async append(entry, result, resultBytes) {
|
|
271
271
|
if (!this.metaEmitted) {
|
|
272
272
|
await this.emitMeta();
|
|
273
273
|
}
|
|
@@ -277,7 +277,7 @@ export class SessionLog {
|
|
|
277
277
|
const callKey = JSON.stringify({ tool: entry.tool, args: sanitizedArgs });
|
|
278
278
|
const retrySignal = callKey === this.lastCallKey;
|
|
279
279
|
this.lastCallKey = callKey;
|
|
280
|
-
const
|
|
280
|
+
const finalResultBytes = resultBytes !== undefined ? resultBytes : computeResultBytes(result);
|
|
281
281
|
let hoisted = {};
|
|
282
282
|
try {
|
|
283
283
|
hoisted = hoistContextScalars(result);
|
|
@@ -302,7 +302,7 @@ export class SessionLog {
|
|
|
302
302
|
edition: EDITION,
|
|
303
303
|
platform: process.platform,
|
|
304
304
|
ps_version: this.psVersion,
|
|
305
|
-
result_bytes:
|
|
305
|
+
result_bytes: finalResultBytes,
|
|
306
306
|
...hoisted,
|
|
307
307
|
...(errorClass !== null ? { error_class: errorClass } : {}),
|
|
308
308
|
retry_signal: retrySignal,
|