pk-annotator 0.6.2 → 0.7.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.
Files changed (24) hide show
  1. package/README.ko.md +9 -1
  2. package/README.md +9 -1
  3. package/dist/overlay.mjs +48 -4
  4. package/dist/{pka-mcp-server-DJ7A2u68.mjs → pka-mcp-server-DeBVJQYE.mjs} +233 -18
  5. package/dist/pka-mcp.mjs +1 -1
  6. package/dist/{pka-overlay-agent-art-BKBtrWqF.mjs → pka-overlay-agent-art-n6aH_1kF.mjs} +1 -1
  7. package/dist/{pka-overlay-agent-theme-CfCyg5Gv.mjs → pka-overlay-agent-theme-DfiOu97H.mjs} +13 -1
  8. package/dist/{pka-overlay-app-CsIxnRqJ.mjs → pka-overlay-app-D-PF-HmH.mjs} +69 -48
  9. package/dist/pka-overlay-channel-C6qfFolf.mjs +113 -0
  10. package/dist/pka-overlay-composer-CK1sTHAv.mjs +244 -0
  11. package/dist/{pka-overlay-copy-CvKjfQl1.mjs → pka-overlay-copy-BTMmwtSZ.mjs} +1 -1
  12. package/dist/{pka-overlay-gifenc-BGK0llcr.mjs → pka-overlay-gifenc-DqOpaD2I.mjs} +1 -1
  13. package/dist/{pka-overlay-perf-panel-gQYSZ0gA.mjs → pka-overlay-perf-panel-xd8kgxtm.mjs} +2 -2
  14. package/dist/{pka-overlay-send-BZyT4xuW.mjs → pka-overlay-send-UrgGuLhh.mjs} +6 -6
  15. package/dist/{pka-overlay-snapdom-djDPjHu_.mjs → pka-overlay-snapdom-BArbLl8i.mjs} +1 -1
  16. package/dist/{pka-overlay-thread-store-DOxLXsAv.mjs → pka-overlay-thread-store-X3FM2ev_.mjs} +1 -1
  17. package/dist/pka-overlay-untracked-fetch-Dj1et-Fv.mjs +1 -0
  18. package/dist/pka.mjs +2 -1
  19. package/dist/{recording-CpEDddkZ.mjs → recording-82fajWWq.mjs} +41 -14
  20. package/dist/vite.mjs +255 -3
  21. package/package.json +1 -1
  22. package/dist/pka-overlay-channel-D5Ghnvgb.mjs +0 -113
  23. package/dist/pka-overlay-composer-CT2x7vgj.mjs +0 -244
  24. package/dist/pka-overlay-untracked-fetch-BCn3eft5.mjs +0 -1
package/dist/vite.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { A as createStore, B as Id, C as agentsDir, D as MissingAnnotationError, H as State, I as resolveInside, L as AnnotationDraft, M as isErrno, N as listIds, O as PkaError, R as ErrorGroup, T as AgentPresence, U as ThreadEntry, V as RelativePath, b as upsertErrorGroups, d as checkCaptureFiles, f as create, g as loadAnnotationUpdates, k as checkStoreSize, r as RecordingManifestDraft, t as RecordingErrors, w as liveAgents, z as ID_PATTERN } from "./recording-CpEDddkZ.mjs";
1
+ import { A as createStore, B as Id, C as agentsDir, D as MissingAnnotationError, H as State, I as resolveInside, L as AnnotationDraft, M as isErrno, N as listIds, O as PkaError, R as ErrorGroup, T as AgentPresence, U as ThreadEntry, V as RelativePath, b as upsertErrorGroups, d as checkCaptureFiles, f as create, g as loadAnnotationUpdates, k as checkStoreSize, r as RecordingManifestDraft, t as RecordingErrors, w as liveAgents, z as ID_PATTERN } from "./recording-82fajWWq.mjs";
2
+ import { createRequire } from "node:module";
2
3
  import { execFile } from "node:child_process";
3
4
  import { randomBytes } from "node:crypto";
4
- import { constants, watch } from "node:fs";
5
+ import { constants, readFileSync, watch } from "node:fs";
5
6
  import { lstat, mkdir, open, readFile, readdir, realpath, rm, stat } from "node:fs/promises";
6
7
  import path, { posix } from "node:path";
7
8
  import { fileURLToPath } from "node:url";
@@ -27,7 +28,10 @@ const CHANNEL = {
27
28
  presence: "pka:presence",
28
29
  agent: "pka:agent",
29
30
  setup: "pka:setup",
30
- setupInfo: "pka:setup-info"
31
+ setupInfo: "pka:setup-info",
32
+ update: "pka:update",
33
+ installUpdate: "pka:install-update",
34
+ updateResult: "pka:update-result"
31
35
  };
32
36
  const MAX_CHUNK_BYTES = 524288;
33
37
  const CreateMessage = z.strictObject({
@@ -52,6 +56,11 @@ const SymbolicateMessage = z.strictObject({
52
56
  });
53
57
  const PresenceMessage = z.strictObject({});
54
58
  const SetupMessage = z.strictObject({ requestId: Id });
59
+ const Version = z.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/);
60
+ const InstallUpdateMessage = z.strictObject({
61
+ requestId: Id,
62
+ version: Version
63
+ });
55
64
  z.strictObject({
56
65
  requestId: Id,
57
66
  id: Id,
@@ -97,6 +106,18 @@ z.strictObject({
97
106
  }).nullable(),
98
107
  cause: z.enum(["presence", "change"])
99
108
  });
109
+ z.strictObject({
110
+ current: Version,
111
+ latest: Version
112
+ });
113
+ z.discriminatedUnion("outcome", [z.strictObject({
114
+ requestId: Id,
115
+ outcome: z.enum(["restart", "restart-manually"])
116
+ }), z.strictObject({
117
+ requestId: Id,
118
+ outcome: z.literal("failed"),
119
+ message: z.string()
120
+ })]);
100
121
  //#endregion
101
122
  //#region src/vite/source.ts
102
123
  const ATTRIBUTE = "data-pka-src";
@@ -226,6 +247,213 @@ async function symbolicate(environment, workspaceRoot, stack) {
226
247
  };
227
248
  }
228
249
  //#endregion
250
+ //#region src/vite/update.ts
251
+ const execFileAsync$1 = promisify(execFile);
252
+ const REGISTRY_LATEST = "https://registry.npmjs.org/pk-annotator/latest";
253
+ const CHECK_TTL_MS = 36e5;
254
+ const INSTALL_TIMEOUT_MS = 3e5;
255
+ const SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
256
+ const REGISTRY_SPEC = /^([~^]?)\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
257
+ const LOCKFILES = [
258
+ ["pnpm-lock.yaml", "pnpm"],
259
+ ["bun.lock", "bun"],
260
+ ["bun.lockb", "bun"],
261
+ ["yarn.lock", "yarn"],
262
+ ["package-lock.json", "npm"]
263
+ ];
264
+ const Manifest = z.object({
265
+ dependencies: z.record(z.string(), z.string()).optional(),
266
+ devDependencies: z.record(z.string(), z.string()).optional(),
267
+ packageManager: z.string().optional()
268
+ });
269
+ const Published = z.object({ version: z.string().regex(SEMVER) });
270
+ /** This module's own package.json; a self-reference resolves through its real path. */
271
+ const OWN_MANIFEST = createRequire(import.meta.url).resolve("pk-annotator/package.json");
272
+ const RUNNING = Published.parse(JSON.parse(readFileSync(OWN_MANIFEST, "utf8"))).version;
273
+ function parseVersion(version) {
274
+ const match = SEMVER.exec(version);
275
+ if (match === null) throw new Error(`Not a semantic version: ${version}`);
276
+ return {
277
+ core: [
278
+ Number(match[1]),
279
+ Number(match[2]),
280
+ Number(match[3])
281
+ ],
282
+ pre: match[4]
283
+ };
284
+ }
285
+ /** Whether `candidate` is a later release than `current`; a prerelease never outranks its release. */
286
+ function isNewer(candidate, current) {
287
+ const next = parseVersion(candidate);
288
+ const now = parseVersion(current);
289
+ for (const [index, part] of next.core.entries()) {
290
+ const other = now.core[index];
291
+ if (part !== other) return part > other;
292
+ }
293
+ return next.pre === void 0 && now.pre !== void 0;
294
+ }
295
+ function installCommand(install, version) {
296
+ const dev = {
297
+ pnpm: "--save-dev",
298
+ npm: "--save-dev",
299
+ yarn: "--dev",
300
+ bun: "--dev"
301
+ }[install.manager];
302
+ const exact = {
303
+ pnpm: "--save-exact",
304
+ npm: "--save-exact",
305
+ yarn: "--exact",
306
+ bun: "--exact"
307
+ }[install.manager];
308
+ return [
309
+ install.manager,
310
+ install.manager === "npm" ? "install" : "add",
311
+ ...install.dev ? [dev] : [],
312
+ ...install.exact ? [exact] : [],
313
+ `pk-annotator@${version}`
314
+ ];
315
+ }
316
+ async function readManifest(dir) {
317
+ const file = path.join(dir, "package.json");
318
+ const text = await readFile(file, "utf8").catch((cause) => {
319
+ if (isErrno(cause, "ENOENT")) return void 0;
320
+ throw cause;
321
+ });
322
+ if (text === void 0) return void 0;
323
+ const parsed = Manifest.safeParse(JSON.parse(text));
324
+ if (!parsed.success) throw new Error(`${file} is not a valid package.json: ${parsed.error.message}`);
325
+ return parsed.data;
326
+ }
327
+ async function exists(file) {
328
+ return stat(file).then(() => true, (cause) => {
329
+ if (isErrno(cause, "ENOENT")) return false;
330
+ throw cause;
331
+ });
332
+ }
333
+ /**
334
+ * The manifest that declares pk-annotator, from the Vite root up to the workspace root, and
335
+ * the workspace's package manager. Null when the dependency is not a registry version, for
336
+ * example this repository's fixture or a workspace link.
337
+ */
338
+ async function findInstall(viteRoot, workspaceRoot) {
339
+ if (!OWN_MANIFEST.split(path.sep).includes("node_modules")) return null;
340
+ let declared;
341
+ for (let dir = path.resolve(viteRoot);; dir = path.dirname(dir)) {
342
+ const manifest = await readManifest(dir);
343
+ const dev = manifest?.devDependencies?.["pk-annotator"];
344
+ const spec = dev ?? manifest?.dependencies?.["pk-annotator"];
345
+ if (spec !== void 0) {
346
+ const match = REGISTRY_SPEC.exec(spec);
347
+ if (match === null) return null;
348
+ declared = {
349
+ dir,
350
+ dev: dev !== void 0,
351
+ exact: match[1] === ""
352
+ };
353
+ break;
354
+ }
355
+ if (dir === path.resolve(workspaceRoot) || dir === path.dirname(dir)) return null;
356
+ }
357
+ const field = (await readManifest(workspaceRoot))?.packageManager?.split("@")[0];
358
+ if (field === "pnpm" || field === "npm" || field === "yarn" || field === "bun") return {
359
+ ...declared,
360
+ manager: field
361
+ };
362
+ for (const [lockfile, manager] of LOCKFILES) if (await exists(path.join(workspaceRoot, lockfile))) return {
363
+ ...declared,
364
+ manager
365
+ };
366
+ return null;
367
+ }
368
+ /** The real path of the pk-annotator manifest that `dir` resolves, read fresh from disk. */
369
+ async function resolveInstalled(dir) {
370
+ for (let from = dir;; from = path.dirname(from)) {
371
+ const found = await realpath(path.join(from, "node_modules/pk-annotator/package.json")).catch((cause) => {
372
+ if (isErrno(cause, "ENOENT")) return void 0;
373
+ throw cause;
374
+ });
375
+ if (found !== void 0) return found;
376
+ if (from === path.dirname(from)) throw new Error(`${dir} resolves no pk-annotator`);
377
+ }
378
+ }
379
+ /**
380
+ * Checks npm for a newer pk-annotator when a page mounts, at most once an hour, and installs
381
+ * it on request with the workspace's package manager. Nothing runs between those events.
382
+ */
383
+ function createUpdater(viteRoot, workspaceRoot, warn) {
384
+ let install;
385
+ let checked;
386
+ let installing = false;
387
+ let staleWarned = false;
388
+ async function latest() {
389
+ if (checked === void 0 || Date.now() - checked.at > CHECK_TTL_MS) {
390
+ const request = fetch(REGISTRY_LATEST, { signal: AbortSignal.timeout(1e4) }).then(async (response) => {
391
+ if (!response.ok) throw new Error(`${REGISTRY_LATEST} answered ${response.status}`);
392
+ return Published.parse(await response.json()).version;
393
+ }).catch((cause) => {
394
+ warn(`update check failed: ${cause instanceof Error ? cause.message : String(cause)}`);
395
+ return null;
396
+ });
397
+ checked = {
398
+ at: Date.now(),
399
+ latest: request
400
+ };
401
+ }
402
+ return checked.latest;
403
+ }
404
+ return {
405
+ /** The update to offer, or null when the installed version is current or not managed by version. */
406
+ async check() {
407
+ install ??= findInstall(viteRoot, workspaceRoot);
408
+ if (await install === null) return null;
409
+ const onDisk = Published.parse(JSON.parse(await readFile(OWN_MANIFEST, "utf8"))).version;
410
+ if (onDisk !== RUNNING) {
411
+ if (!staleWarned) {
412
+ staleWarned = true;
413
+ warn(`${onDisk} is installed but ${RUNNING} is running; restart the dev server`);
414
+ }
415
+ return null;
416
+ }
417
+ const version = await latest();
418
+ return version !== null && isNewer(version, RUNNING) ? {
419
+ current: RUNNING,
420
+ latest: version
421
+ } : null;
422
+ },
423
+ /**
424
+ * Installs `version`, which must be the release the last check offered. "restart" means a
425
+ * dev server restart loads it; "restart-manually" means the package was replaced in place,
426
+ * where Node keeps the old plugin module until the process restarts.
427
+ */
428
+ async install(version) {
429
+ const target = await install;
430
+ if (target === void 0 || target === null) throw new Error("This workspace does not install pk-annotator from the npm registry");
431
+ if (checked === void 0 || await checked.latest !== version) throw new Error(`${version} is not the release last offered`);
432
+ if (installing) throw new Error("An update is already installing");
433
+ installing = true;
434
+ try {
435
+ const [command, ...args] = installCommand(target, version);
436
+ try {
437
+ await execFileAsync$1(command, args, {
438
+ cwd: target.dir,
439
+ timeout: INSTALL_TIMEOUT_MS
440
+ });
441
+ } catch (cause) {
442
+ const stderr = cause instanceof Error && "stderr" in cause ? String(cause.stderr).trim() : "";
443
+ const detail = stderr === "" ? String(cause) : stderr.slice(-600);
444
+ throw new Error(`${[command, ...args].join(" ")} failed in ${target.dir}: ${detail}`, { cause });
445
+ }
446
+ const resolved = await resolveInstalled(target.dir);
447
+ const installed = Published.parse(JSON.parse(await readFile(resolved, "utf8"))).version;
448
+ if (installed !== version) throw new Error(`Installed ${version}, but ${target.dir} resolves pk-annotator ${installed}`);
449
+ return resolved === OWN_MANIFEST ? "restart-manually" : "restart";
450
+ } finally {
451
+ installing = false;
452
+ }
453
+ }
454
+ };
455
+ }
456
+ //#endregion
229
457
  //#region src/vite/index.ts
230
458
  const AnnotatorOptions = z.strictObject({
231
459
  bodies: z.array(z.string().startsWith("/")).optional(),
@@ -544,6 +772,27 @@ async function serve(server, maxStoreBytes, storeRoot) {
544
772
  };
545
773
  client.send(CHANNEL.setupInfo, reply);
546
774
  });
775
+ const updater = createUpdater(server.config.root, workspaceRoot, warn);
776
+ listen(CHANNEL.installUpdate, InstallUpdateMessage, async (message, client) => {
777
+ let result;
778
+ try {
779
+ const outcome = await updater.install(message.version);
780
+ result = {
781
+ requestId: message.requestId,
782
+ outcome
783
+ };
784
+ } catch (cause) {
785
+ error(`update to ${message.version} failed: ${describeError(cause)}`);
786
+ result = {
787
+ requestId: message.requestId,
788
+ outcome: "failed",
789
+ message: describeError(cause)
790
+ };
791
+ }
792
+ client.send(CHANNEL.updateResult, result);
793
+ if (result.outcome === "restart-manually") warn(`updated to ${message.version}; restart the dev server to load it`);
794
+ if (result.outcome === "restart") await server.restart();
795
+ });
547
796
  listen(CHANNEL.sync, SyncMessage, async (message, client) => {
548
797
  const synced = { annotations: [] };
549
798
  for (const id of message.ids) try {
@@ -592,6 +841,9 @@ async function serve(server, maxStoreBytes, storeRoot) {
592
841
  agentQueue = agentQueue.then(async () => pushAgent(await readAgent())).catch((cause) => error(`reading agent presence failed: ${describeError(cause)}`));
593
842
  }
594
843
  listen(CHANNEL.presence, PresenceMessage, async (_message, client) => {
844
+ updater.check().then((offer) => {
845
+ if (offer !== null) client.send(CHANNEL.update, offer);
846
+ }, (cause) => error(`update check failed: ${describeError(cause)}`));
595
847
  const answered = agentQueue.then(async () => {
596
848
  const agent = await readAgent();
597
849
  pushAgent(agent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pk-annotator",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Dev-only browser annotation overlay for Vite apps that sends picked elements, captures, and prompts to Claude Code and Codex through an MCP server.",
5
5
  "keywords": [
6
6
  "annotation",