@wenrwa/marketplace-cli 0.4.4 → 0.4.6

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 (116) hide show
  1. package/dist/__tests__/bridge-activity-parser.test.d.ts +1 -0
  2. package/dist/__tests__/bridge-activity-parser.test.js +0 -0
  3. package/dist/__tests__/bridge-activity-tap.test.d.ts +1 -0
  4. package/dist/__tests__/bridge-activity-tap.test.js +178 -0
  5. package/dist/__tests__/bridge-api-client.test.d.ts +1 -0
  6. package/dist/__tests__/bridge-api-client.test.js +69 -0
  7. package/dist/__tests__/bridge-browser-driver.test.d.ts +1 -0
  8. package/dist/__tests__/bridge-browser-driver.test.js +379 -0
  9. package/dist/__tests__/bridge-browser-frames.test.d.ts +1 -0
  10. package/dist/__tests__/bridge-browser-frames.test.js +166 -0
  11. package/dist/__tests__/bridge-browser-idle.test.d.ts +1 -0
  12. package/dist/__tests__/bridge-browser-idle.test.js +259 -0
  13. package/dist/__tests__/bridge-browser-relay.test.d.ts +1 -0
  14. package/dist/__tests__/bridge-browser-relay.test.js +670 -0
  15. package/dist/__tests__/bridge-bundle-installer.test.d.ts +1 -0
  16. package/dist/__tests__/bridge-bundle-installer.test.js +126 -0
  17. package/dist/__tests__/bridge-cdp-client.test.d.ts +1 -0
  18. package/dist/__tests__/bridge-cdp-client.test.js +133 -0
  19. package/dist/__tests__/bridge-chromium-launcher.test.d.ts +1 -0
  20. package/dist/__tests__/bridge-chromium-launcher.test.js +202 -0
  21. package/dist/__tests__/bridge-command-browser-options.test.d.ts +1 -0
  22. package/dist/__tests__/bridge-command-browser-options.test.js +37 -0
  23. package/dist/__tests__/bridge-config-mcp.test.js +4 -0
  24. package/dist/__tests__/bridge-context-manifest-frame.test.d.ts +1 -0
  25. package/dist/__tests__/bridge-context-manifest-frame.test.js +125 -0
  26. package/dist/__tests__/bridge-context-writer.test.d.ts +1 -0
  27. package/dist/__tests__/bridge-context-writer.test.js +161 -0
  28. package/dist/__tests__/bridge-device-registration.test.d.ts +1 -0
  29. package/dist/__tests__/bridge-device-registration.test.js +33 -0
  30. package/dist/__tests__/bridge-folder-picker-runner.test.d.ts +1 -0
  31. package/dist/__tests__/bridge-folder-picker-runner.test.js +37 -0
  32. package/dist/__tests__/bridge-folder-picker.test.d.ts +1 -0
  33. package/dist/__tests__/bridge-folder-picker.test.js +66 -0
  34. package/dist/__tests__/bridge-folder-registry.test.d.ts +1 -0
  35. package/dist/__tests__/bridge-folder-registry.test.js +107 -0
  36. package/dist/__tests__/bridge-frame-pacer.test.d.ts +1 -0
  37. package/dist/__tests__/bridge-frame-pacer.test.js +102 -0
  38. package/dist/__tests__/bridge-install-bundle-frame.test.d.ts +1 -0
  39. package/dist/__tests__/bridge-install-bundle-frame.test.js +107 -0
  40. package/dist/__tests__/bridge-local-add-core.test.d.ts +1 -0
  41. package/dist/__tests__/bridge-local-add-core.test.js +48 -0
  42. package/dist/__tests__/bridge-local-path-validation-fs.test.d.ts +1 -0
  43. package/dist/__tests__/bridge-local-path-validation-fs.test.js +88 -0
  44. package/dist/__tests__/bridge-local-path-validation.test.d.ts +1 -0
  45. package/dist/__tests__/bridge-local-path-validation.test.js +45 -0
  46. package/dist/__tests__/bridge-login-status.test.d.ts +1 -0
  47. package/dist/__tests__/bridge-login-status.test.js +60 -0
  48. package/dist/__tests__/bridge-relay-client.test.d.ts +1 -0
  49. package/dist/__tests__/bridge-relay-client.test.js +283 -0
  50. package/dist/__tests__/bridge-relay-mode.test.d.ts +1 -0
  51. package/dist/__tests__/bridge-relay-mode.test.js +33 -0
  52. package/dist/__tests__/bridge-relay-pucc14.test.d.ts +1 -0
  53. package/dist/__tests__/bridge-relay-pucc14.test.js +230 -0
  54. package/dist/__tests__/bridge-server-local-endpoints.test.d.ts +1 -0
  55. package/dist/__tests__/bridge-server-local-endpoints.test.js +197 -0
  56. package/dist/__tests__/bridge-server.test.js +22 -0
  57. package/dist/__tests__/bridge-workspace-multirepo.test.d.ts +1 -0
  58. package/dist/__tests__/bridge-workspace-multirepo.test.js +317 -0
  59. package/dist/__tests__/devices.test.d.ts +1 -0
  60. package/dist/__tests__/devices.test.js +66 -0
  61. package/dist/__tests__/local.test.d.ts +1 -0
  62. package/dist/__tests__/local.test.js +147 -0
  63. package/dist/bridge/activityParser.d.ts +53 -0
  64. package/dist/bridge/activityParser.js +202 -0
  65. package/dist/bridge/apiClient.d.ts +56 -0
  66. package/dist/bridge/apiClient.js +50 -0
  67. package/dist/bridge/browserDriver.d.ts +98 -0
  68. package/dist/bridge/browserDriver.js +265 -0
  69. package/dist/bridge/browserFrames.d.ts +182 -0
  70. package/dist/bridge/browserFrames.js +179 -0
  71. package/dist/bridge/browserIdle.d.ts +75 -0
  72. package/dist/bridge/browserIdle.js +120 -0
  73. package/dist/bridge/bundleInstaller.d.ts +31 -0
  74. package/dist/bridge/bundleInstaller.js +127 -0
  75. package/dist/bridge/cdpClient.d.ts +47 -0
  76. package/dist/bridge/cdpClient.js +102 -0
  77. package/dist/bridge/chromiumLauncher.d.ts +45 -0
  78. package/dist/bridge/chromiumLauncher.js +113 -0
  79. package/dist/bridge/config.d.ts +3 -0
  80. package/dist/bridge/contextWriter.d.ts +31 -0
  81. package/dist/bridge/contextWriter.js +139 -0
  82. package/dist/bridge/deviceRegistration.d.ts +28 -0
  83. package/dist/bridge/deviceRegistration.js +19 -0
  84. package/dist/bridge/folderPicker.d.ts +34 -0
  85. package/dist/bridge/folderPicker.js +36 -0
  86. package/dist/bridge/folderPickerRunner.d.ts +3 -0
  87. package/dist/bridge/folderPickerRunner.js +25 -0
  88. package/dist/bridge/folderRegistry.d.ts +19 -0
  89. package/dist/bridge/folderRegistry.js +100 -0
  90. package/dist/bridge/framePacer.d.ts +61 -0
  91. package/dist/bridge/framePacer.js +79 -0
  92. package/dist/bridge/localAdd.d.ts +28 -0
  93. package/dist/bridge/localAdd.js +67 -0
  94. package/dist/bridge/localPathValidation.d.ts +20 -0
  95. package/dist/bridge/localPathValidation.js +58 -0
  96. package/dist/bridge/localPathValidationFs.d.ts +2 -0
  97. package/dist/bridge/localPathValidationFs.js +98 -0
  98. package/dist/bridge/loginStatus.d.ts +38 -0
  99. package/dist/bridge/loginStatus.js +56 -0
  100. package/dist/bridge/realPty.js +6 -2
  101. package/dist/bridge/relayClient.d.ts +166 -0
  102. package/dist/bridge/relayClient.js +517 -0
  103. package/dist/bridge/repoCheckout.d.ts +31 -0
  104. package/dist/bridge/repoCheckout.js +18 -0
  105. package/dist/bridge/server.d.ts +33 -0
  106. package/dist/bridge/server.js +98 -10
  107. package/dist/bridge/sessionManager.d.ts +18 -0
  108. package/dist/bridge/sessionManager.js +15 -1
  109. package/dist/commands/bridge.d.ts +21 -0
  110. package/dist/commands/bridge.js +194 -1
  111. package/dist/commands/devices.d.ts +9 -0
  112. package/dist/commands/devices.js +53 -0
  113. package/dist/commands/local.d.ts +9 -0
  114. package/dist/commands/local.js +141 -0
  115. package/dist/index.js +7 -1
  116. package/package.json +1 -1
@@ -0,0 +1,34 @@
1
+ /**
2
+ * lws-05 — OS-native folder picker (PRD D3b) behind POST /local/pick-folder.
3
+ * `pickerCommandFor` is a pure command table — easy to test per platform
4
+ * without spawning anything. `pickFolder` runs it with an injected runner
5
+ * (real implementation in commands/bridge.ts uses child_process.execFile);
6
+ * tests never shell out to osascript/PowerShell/zenity.
7
+ *
8
+ * "One picker at a time" (server-level, not spawn-level) is enforced by the
9
+ * HTTP route in server.ts, which gates concurrent /local/pick-folder calls —
10
+ * this module only knows how to run a single pick.
11
+ */
12
+ export interface PickerCommand {
13
+ cmd: string;
14
+ args: string[];
15
+ }
16
+ export declare function pickerCommandFor(platform: string, opts: {
17
+ hasZenity: boolean;
18
+ }): PickerCommand | null;
19
+ export type PickFolderRunner = (cmd: string, args: string[]) => Promise<{
20
+ stdout: string;
21
+ code: number;
22
+ }>;
23
+ export type PickFolderResult = {
24
+ path: string;
25
+ } | {
26
+ cancelled: true;
27
+ } | {
28
+ unsupported: true;
29
+ };
30
+ export declare function pickFolder(opts: {
31
+ platform: string;
32
+ hasZenity: boolean;
33
+ run: PickFolderRunner;
34
+ }): Promise<PickFolderResult>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pickerCommandFor = pickerCommandFor;
4
+ exports.pickFolder = pickFolder;
5
+ function pickerCommandFor(platform, opts) {
6
+ if (platform === 'darwin') {
7
+ return { cmd: 'osascript', args: ['-e', 'POSIX path of (choose folder)'] };
8
+ }
9
+ if (platform === 'win32') {
10
+ const script = "Add-Type -AssemblyName System.Windows.Forms; " +
11
+ "$f = New-Object System.Windows.Forms.FolderBrowserDialog; " +
12
+ "if ($f.ShowDialog() -eq 'OK') { Write-Output $f.SelectedPath }";
13
+ return { cmd: 'powershell.exe', args: ['-NoProfile', '-Command', script] };
14
+ }
15
+ if (platform === 'linux' && opts.hasZenity) {
16
+ return { cmd: 'zenity', args: ['--file-selection', '--directory'] };
17
+ }
18
+ return null;
19
+ }
20
+ async function pickFolder(opts) {
21
+ const command = pickerCommandFor(opts.platform, { hasZenity: opts.hasZenity });
22
+ if (!command)
23
+ return { unsupported: true };
24
+ try {
25
+ const { stdout } = await opts.run(command.cmd, command.args);
26
+ const path = stdout.trim();
27
+ if (!path)
28
+ return { cancelled: true };
29
+ return { path };
30
+ }
31
+ catch {
32
+ // Non-zero exit (user hit Cancel) surfaces as a rejected promise from
33
+ // every platform's runner — treated as a plain cancel, not an error.
34
+ return { cancelled: true };
35
+ }
36
+ }
@@ -0,0 +1,3 @@
1
+ import { PickFolderRunner } from './folderPicker';
2
+ export declare const execFileRunner: PickFolderRunner;
3
+ export declare function hasZenityInstalled(): Promise<boolean>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execFileRunner = void 0;
4
+ exports.hasZenityInstalled = hasZenityInstalled;
5
+ /**
6
+ * lws-05 — real execFile-backed PickFolderRunner + zenity presence probe,
7
+ * wired into commands/bridge.ts's POST /local/pick-folder handler. Kept
8
+ * separate from folderPicker.ts (the pure command table + result logic) so
9
+ * that module never touches child_process directly and stays test-friendly.
10
+ */
11
+ const child_process_1 = require("child_process");
12
+ const execFileRunner = (cmd, args) => new Promise((resolve, reject) => {
13
+ (0, child_process_1.execFile)(cmd, args, (err, stdout) => {
14
+ if (err)
15
+ reject(err);
16
+ else
17
+ resolve({ stdout: stdout ?? '', code: 0 });
18
+ });
19
+ });
20
+ exports.execFileRunner = execFileRunner;
21
+ function hasZenityInstalled() {
22
+ return new Promise((resolve) => {
23
+ (0, child_process_1.execFile)('which', ['zenity'], (err) => resolve(!err));
24
+ });
25
+ }
@@ -0,0 +1,19 @@
1
+ export interface FolderEntry {
2
+ workspaceId: string;
3
+ name: string;
4
+ addedAt: string;
5
+ }
6
+ /** Keyed by absolute path. */
7
+ export interface FolderRegistry {
8
+ v: 1;
9
+ entries: Record<string, FolderEntry>;
10
+ }
11
+ export declare function loadFolderRegistry(dir?: string): FolderRegistry;
12
+ export declare function saveFolderRegistry(registry: FolderRegistry, dir?: string): void;
13
+ export declare function upsertEntry(registry: FolderRegistry, absPath: string, entry: FolderEntry): FolderRegistry;
14
+ export declare function removeEntryByPath(registry: FolderRegistry, absPath: string): FolderRegistry;
15
+ export declare function removeEntryByWorkspaceId(registry: FolderRegistry, workspaceId: string): FolderRegistry;
16
+ export declare function findEntryByPath(registry: FolderRegistry, absPath: string): FolderEntry | null;
17
+ export declare function findEntryByWorkspaceId(registry: FolderRegistry, workspaceId: string): {
18
+ path: string;
19
+ } & FolderEntry | null;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadFolderRegistry = loadFolderRegistry;
37
+ exports.saveFolderRegistry = saveFolderRegistry;
38
+ exports.upsertEntry = upsertEntry;
39
+ exports.removeEntryByPath = removeEntryByPath;
40
+ exports.removeEntryByWorkspaceId = removeEntryByWorkspaceId;
41
+ exports.findEntryByPath = findEntryByPath;
42
+ exports.findEntryByWorkspaceId = findEntryByWorkspaceId;
43
+ /**
44
+ * lws-03 — folder→workspace registry. Backs `wen-market local add/list/
45
+ * remove` and the discovery advertisement (server.ts). Lives alongside the
46
+ * bridge's other dotfiles under ~/.wen-market/ rather than a second dotdir,
47
+ * matching config.ts's convention. File-backed so it survives daemon
48
+ * restart; pure ops below operate on an in-memory snapshot so add/remove
49
+ * logic is unit-testable without touching disk.
50
+ */
51
+ const fs = __importStar(require("fs"));
52
+ const os = __importStar(require("os"));
53
+ const path = __importStar(require("path"));
54
+ const DEFAULT_DIR = path.join(os.homedir(), '.wen-market');
55
+ const REGISTRY_NAME = 'local-workspaces.json';
56
+ function ensureDir(dir) {
57
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
58
+ }
59
+ function loadFolderRegistry(dir = DEFAULT_DIR) {
60
+ try {
61
+ const parsed = JSON.parse(fs.readFileSync(path.join(dir, REGISTRY_NAME), 'utf-8'));
62
+ if (parsed && typeof parsed === 'object' && parsed.entries)
63
+ return parsed;
64
+ return { v: 1, entries: {} };
65
+ }
66
+ catch {
67
+ return { v: 1, entries: {} };
68
+ }
69
+ }
70
+ function saveFolderRegistry(registry, dir = DEFAULT_DIR) {
71
+ ensureDir(dir);
72
+ fs.writeFileSync(path.join(dir, REGISTRY_NAME), JSON.stringify(registry, null, 2) + '\n', { mode: 0o600 });
73
+ }
74
+ // ── Pure in-memory ops (no I/O — easy to unit test, easy to compose) ──────
75
+ function upsertEntry(registry, absPath, entry) {
76
+ return { ...registry, entries: { ...registry.entries, [absPath]: entry } };
77
+ }
78
+ function removeEntryByPath(registry, absPath) {
79
+ const entries = { ...registry.entries };
80
+ delete entries[absPath];
81
+ return { ...registry, entries };
82
+ }
83
+ function removeEntryByWorkspaceId(registry, workspaceId) {
84
+ const entries = {};
85
+ for (const [p, e] of Object.entries(registry.entries)) {
86
+ if (e.workspaceId !== workspaceId)
87
+ entries[p] = e;
88
+ }
89
+ return { ...registry, entries };
90
+ }
91
+ function findEntryByPath(registry, absPath) {
92
+ return registry.entries[absPath] ?? null;
93
+ }
94
+ function findEntryByWorkspaceId(registry, workspaceId) {
95
+ for (const [p, e] of Object.entries(registry.entries)) {
96
+ if (e.workspaceId === workspaceId)
97
+ return { path: p, ...e };
98
+ }
99
+ return null;
100
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * cbs-01 — keep-newest frame pacer (pure, no I/O).
3
+ *
4
+ * Backpressure policy for the cloud browser session's `browser-frame`
5
+ * stream (docs/prd/cloud-browser-session.md, "Backpressure" decision): the
6
+ * relay has no message-size limit and no built-in backpressure, so an
7
+ * unbounded queue in front of a slow consumer (bad wifi, a stalled pane) is
8
+ * how memory balloons. This pacer enforces "at most ONE undelivered frame
9
+ * per session, newest wins":
10
+ *
11
+ * - The first pushed frame (nothing in flight) is delivered immediately.
12
+ * - A frame pushed while one is already in flight (awaiting ack) is held
13
+ * in a SINGLE queue slot, not appended to a list.
14
+ * - A frame pushed while the queue slot is already occupied REPLACES it —
15
+ * the replaced (older, now-stale) frame is counted as dropped and
16
+ * discarded. The queue can never hold more than one frame, so memory
17
+ * use is O(1) regardless of producer rate or consumer slowness.
18
+ * - The queued frame (if any) is delivered exactly when the consumer
19
+ * acks the in-flight one — delivery is entirely ack-gated, never
20
+ * time-gated.
21
+ *
22
+ * This module owns NO timers and no cadence — target ~10fps (per the PRD)
23
+ * is the PRODUCER's concern (e.g. the CDP screencast ack loop calling
24
+ * push() at the capture rate). The pacer only decides deliver-now vs.
25
+ * queue-and-maybe-drop; it never sleeps, polls, or schedules anything.
26
+ *
27
+ * Input events (`browser-input`) do NOT go through this pacer — see the
28
+ * drop rule in browserFrames.ts. This module is frame-type-agnostic (`F`
29
+ * is generic) precisely so it can't accidentally be reused for input.
30
+ */
31
+ export interface FramePacerStats {
32
+ /** Frames replaced in the queue slot before ever being delivered. */
33
+ droppedCount: number;
34
+ /** Frames actually handed to onDeliver. */
35
+ deliveredCount: number;
36
+ }
37
+ export interface FramePacerOptions<F> {
38
+ /**
39
+ * Called synchronously the moment the pacer decides to deliver a frame —
40
+ * either immediately inside push() (nothing in flight) or immediately
41
+ * inside ack() (a queued frame was waiting). Must not throw for frames
42
+ * the pacer already validated as deliverable; any error propagates to
43
+ * the caller of push()/ack() (the pacer does no error swallowing of its
44
+ * own — that's the producer/consumer's call).
45
+ */
46
+ onDeliver: (frame: F) => void;
47
+ }
48
+ export interface FramePacer<F> extends FramePacerStats {
49
+ /** Producer calls this once per captured frame. */
50
+ push(frame: F): void;
51
+ /** Consumer calls this once it has finished handling the last delivered
52
+ * frame. If a frame is queued, it is delivered now (synchronously,
53
+ * awaiting the NEXT ack) and the queue slot empties. A no-op if nothing
54
+ * is queued, and a safe no-op after close(). */
55
+ ack(): void;
56
+ /** Ends the session: discards any queued frame (uncounted — it's not a
57
+ * "replacement", the session is simply over) and makes every further
58
+ * push()/ack() call a safe no-op. Idempotent. */
59
+ close(): void;
60
+ }
61
+ export declare function createFramePacer<F>(opts: FramePacerOptions<F>): FramePacer<F>;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * cbs-01 — keep-newest frame pacer (pure, no I/O).
4
+ *
5
+ * Backpressure policy for the cloud browser session's `browser-frame`
6
+ * stream (docs/prd/cloud-browser-session.md, "Backpressure" decision): the
7
+ * relay has no message-size limit and no built-in backpressure, so an
8
+ * unbounded queue in front of a slow consumer (bad wifi, a stalled pane) is
9
+ * how memory balloons. This pacer enforces "at most ONE undelivered frame
10
+ * per session, newest wins":
11
+ *
12
+ * - The first pushed frame (nothing in flight) is delivered immediately.
13
+ * - A frame pushed while one is already in flight (awaiting ack) is held
14
+ * in a SINGLE queue slot, not appended to a list.
15
+ * - A frame pushed while the queue slot is already occupied REPLACES it —
16
+ * the replaced (older, now-stale) frame is counted as dropped and
17
+ * discarded. The queue can never hold more than one frame, so memory
18
+ * use is O(1) regardless of producer rate or consumer slowness.
19
+ * - The queued frame (if any) is delivered exactly when the consumer
20
+ * acks the in-flight one — delivery is entirely ack-gated, never
21
+ * time-gated.
22
+ *
23
+ * This module owns NO timers and no cadence — target ~10fps (per the PRD)
24
+ * is the PRODUCER's concern (e.g. the CDP screencast ack loop calling
25
+ * push() at the capture rate). The pacer only decides deliver-now vs.
26
+ * queue-and-maybe-drop; it never sleeps, polls, or schedules anything.
27
+ *
28
+ * Input events (`browser-input`) do NOT go through this pacer — see the
29
+ * drop rule in browserFrames.ts. This module is frame-type-agnostic (`F`
30
+ * is generic) precisely so it can't accidentally be reused for input.
31
+ */
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.createFramePacer = createFramePacer;
34
+ function createFramePacer(opts) {
35
+ let awaitingAck = false;
36
+ let queued = null;
37
+ let closed = false;
38
+ let droppedCount = 0;
39
+ let deliveredCount = 0;
40
+ const deliver = (frame) => {
41
+ awaitingAck = true;
42
+ deliveredCount += 1;
43
+ opts.onDeliver(frame);
44
+ };
45
+ return {
46
+ get droppedCount() {
47
+ return droppedCount;
48
+ },
49
+ get deliveredCount() {
50
+ return deliveredCount;
51
+ },
52
+ push(frame) {
53
+ if (closed)
54
+ return;
55
+ if (!awaitingAck) {
56
+ deliver(frame);
57
+ return;
58
+ }
59
+ if (queued !== null)
60
+ droppedCount += 1; // replacing an already-queued frame
61
+ queued = { frame };
62
+ },
63
+ ack() {
64
+ if (closed)
65
+ return;
66
+ awaitingAck = false;
67
+ if (queued !== null) {
68
+ const next = queued.frame;
69
+ queued = null;
70
+ deliver(next);
71
+ }
72
+ },
73
+ close() {
74
+ closed = true;
75
+ queued = null;
76
+ awaitingAck = false;
77
+ },
78
+ };
79
+ }
@@ -0,0 +1,28 @@
1
+ import { FolderRegistry } from './folderRegistry';
2
+ export interface LocalAddCoreDeps {
3
+ loadRegistry: () => FolderRegistry;
4
+ saveRegistry: (registry: FolderRegistry) => void;
5
+ createWorkspace: (name: string) => Promise<{
6
+ workspace: {
7
+ id: string;
8
+ name: string;
9
+ };
10
+ }>;
11
+ }
12
+ export type LocalAddCoreResult = {
13
+ status: 'created';
14
+ workspace: {
15
+ id: string;
16
+ name: string;
17
+ };
18
+ } | {
19
+ status: 'existing';
20
+ workspace: {
21
+ id: string;
22
+ name: string;
23
+ };
24
+ } | {
25
+ status: 'error';
26
+ message: string;
27
+ };
28
+ export declare function addLocalWorkspaceCore(absPath: string, nameOverride: string | undefined, deps: LocalAddCoreDeps): Promise<LocalAddCoreResult>;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.addLocalWorkspaceCore = addLocalWorkspaceCore;
37
+ /**
38
+ * lws-05 — bridge-side idempotent add core, behind POST /local/add. Mirrors
39
+ * `wen-market local add`'s behavior (commands/local.ts) so the arena
40
+ * create-modal's Browse/typed-path attach mechanisms (PRD D3b/D3c) end at the
41
+ * exact same registered-workspace outcome as the CLI mechanism (D3a) —
42
+ * deps-injected so server.ts can wire it without this module knowing about
43
+ * fs, network, or the WorkspaceRecord shape server.ts already has.
44
+ */
45
+ const path = __importStar(require("path"));
46
+ const folderRegistry_1 = require("./folderRegistry");
47
+ async function addLocalWorkspaceCore(absPath, nameOverride, deps) {
48
+ const registry = deps.loadRegistry();
49
+ const existing = (0, folderRegistry_1.findEntryByPath)(registry, absPath);
50
+ if (existing) {
51
+ return { status: 'existing', workspace: { id: existing.workspaceId, name: existing.name } };
52
+ }
53
+ const name = nameOverride || path.basename(absPath);
54
+ try {
55
+ const result = await deps.createWorkspace(name);
56
+ const updated = (0, folderRegistry_1.upsertEntry)(registry, absPath, {
57
+ workspaceId: result.workspace.id,
58
+ name: result.workspace.name,
59
+ addedAt: new Date().toISOString(),
60
+ });
61
+ deps.saveRegistry(updated);
62
+ return { status: 'created', workspace: result.workspace };
63
+ }
64
+ catch (err) {
65
+ return { status: 'error', message: err?.message || String(err) };
66
+ }
67
+ }
@@ -0,0 +1,20 @@
1
+ export interface LocalPathDeps {
2
+ exists: (absPath: string) => boolean;
3
+ isDirectory: (absPath: string) => boolean;
4
+ /** Best-effort readability probe (e.g. fs.accessSync with R_OK). */
5
+ readable: (absPath: string) => boolean;
6
+ /** Counts files under the folder (implementation may cap/skip heavy dirs
7
+ * like node_modules/.git — this is a quick echo, not the D5 tree scan). */
8
+ countFiles: (absPath: string) => number;
9
+ isGitRepo: (absPath: string) => boolean;
10
+ }
11
+ export type ValidatePathResult = {
12
+ ok: true;
13
+ name: string;
14
+ fileCount: number;
15
+ isGitRepo: boolean;
16
+ } | {
17
+ ok: false;
18
+ error: 'not-exists' | 'not-dir' | 'unreadable';
19
+ };
20
+ export declare function validateLocalPath(absPath: string, deps: LocalPathDeps): Promise<ValidatePathResult>;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateLocalPath = validateLocalPath;
37
+ /**
38
+ * lws-05 — POST /local/validate-path. Backs the create-modal's typed-path
39
+ * fallback (PRD D3c): echoes "my-app — 214 files, git repo detected" (or a
40
+ * typed error) before the user confirms. Pure logic, fs fully injected so
41
+ * the endpoint handler (server.ts) is unit-testable without real disk I/O —
42
+ * the real implementation (wired in commands/bridge.ts) uses node:fs/path.
43
+ */
44
+ const path = __importStar(require("path"));
45
+ async function validateLocalPath(absPath, deps) {
46
+ if (!deps.exists(absPath))
47
+ return { ok: false, error: 'not-exists' };
48
+ if (!deps.isDirectory(absPath))
49
+ return { ok: false, error: 'not-dir' };
50
+ if (!deps.readable(absPath))
51
+ return { ok: false, error: 'unreadable' };
52
+ return {
53
+ ok: true,
54
+ name: path.basename(absPath),
55
+ fileCount: deps.countFiles(absPath),
56
+ isGitRepo: deps.isGitRepo(absPath),
57
+ };
58
+ }
@@ -0,0 +1,2 @@
1
+ import { LocalPathDeps } from './localPathValidation';
2
+ export declare function realLocalPathDeps(): LocalPathDeps;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.realLocalPathDeps = realLocalPathDeps;
37
+ /**
38
+ * lws-05 — real fs-backed LocalPathDeps for POST /local/validate-path
39
+ * (commands/bridge.ts wiring). File count is a quick echo for the
40
+ * create-modal ("my-app — 214 files, git repo detected"), NOT the D5 tree
41
+ * scan — it skips node_modules/.git and hard-caps to keep the endpoint fast
42
+ * on huge folders.
43
+ */
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const SKIP_DIRS = new Set(['node_modules', '.git']);
47
+ const COUNT_CAP = 20000;
48
+ function countFiles(root) {
49
+ let count = 0;
50
+ const stack = [root];
51
+ while (stack.length > 0 && count < COUNT_CAP) {
52
+ const dir = stack.pop();
53
+ let entries;
54
+ try {
55
+ entries = fs.readdirSync(dir, { withFileTypes: true });
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ for (const entry of entries) {
61
+ if (count >= COUNT_CAP)
62
+ break;
63
+ if (entry.isDirectory()) {
64
+ if (SKIP_DIRS.has(entry.name))
65
+ continue;
66
+ stack.push(path.join(dir, entry.name));
67
+ }
68
+ else {
69
+ count++;
70
+ }
71
+ }
72
+ }
73
+ return count;
74
+ }
75
+ function realLocalPathDeps() {
76
+ return {
77
+ exists: (p) => fs.existsSync(p),
78
+ isDirectory: (p) => {
79
+ try {
80
+ return fs.statSync(p).isDirectory();
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ },
86
+ readable: (p) => {
87
+ try {
88
+ fs.accessSync(p, fs.constants.R_OK);
89
+ return true;
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ },
95
+ countFiles,
96
+ isGitRepo: (p) => fs.existsSync(path.join(p, '.git')),
97
+ };
98
+ }