@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,38 @@
1
+ /**
2
+ * pucc-14 — in-box Claude Code login-validity check (ADR-0028 boundary).
3
+ *
4
+ * The shared container keeps the user's ONE Claude Code login on the volume at
5
+ * CLAUDE_CONFIG_DIR (/data/.claude). The platform needs to know whether that
6
+ * login is currently usable so `GET /me/claude-container` can render
7
+ * connected / needs_relogin — but the credential itself is the USER's and the
8
+ * platform must NEVER read, store, move, or transmit its value (ADR-0031
9
+ * decision 4 / ADR-0028). So the bridge reduces the whole thing to a single
10
+ * BOOLEAN: does a non-expired login exist? It reads ONLY the expiry timestamp
11
+ * out of the credentials file — never the accessToken / refreshToken — and
12
+ * returns true/false. Nothing else ever leaves this function.
13
+ */
14
+ /** Claude Code writes its OAuth login here under CLAUDE_CONFIG_DIR. */
15
+ export declare const CLAUDE_CREDENTIALS_FILE = ".credentials.json";
16
+ export interface LoginCheckDeps {
17
+ /** CLAUDE_CONFIG_DIR — /data/.claude in the container. */
18
+ claudeDir: string;
19
+ exists: (path: string) => boolean;
20
+ /** Reads + parses the credentials JSON. The caller extracts ONLY the expiry
21
+ * from the result and discards everything else, so the token value is never
22
+ * handled by this module beyond the fs read it cannot avoid. */
23
+ readJson: (path: string) => unknown;
24
+ /** Injectable clock for tests. */
25
+ now?: () => number;
26
+ }
27
+ /**
28
+ * Returns true iff a valid, non-expired `claude` login exists on the volume.
29
+ *
30
+ * Existence + expiry ONLY — the token value is never extracted, returned, or
31
+ * logged. Semantics:
32
+ * - credentials file absent → false (never logged in)
33
+ * - unparseable / not an object → false (corrupt → treat as no login)
34
+ * - has a numeric expiry in the past → false (expired / needs re-login)
35
+ * - has a numeric expiry in the future → true
36
+ * - exists but no expiry field → true (existence is the login signal)
37
+ */
38
+ export declare function checkClaudeLoginValid(deps: LoginCheckDeps): boolean;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * pucc-14 — in-box Claude Code login-validity check (ADR-0028 boundary).
4
+ *
5
+ * The shared container keeps the user's ONE Claude Code login on the volume at
6
+ * CLAUDE_CONFIG_DIR (/data/.claude). The platform needs to know whether that
7
+ * login is currently usable so `GET /me/claude-container` can render
8
+ * connected / needs_relogin — but the credential itself is the USER's and the
9
+ * platform must NEVER read, store, move, or transmit its value (ADR-0031
10
+ * decision 4 / ADR-0028). So the bridge reduces the whole thing to a single
11
+ * BOOLEAN: does a non-expired login exist? It reads ONLY the expiry timestamp
12
+ * out of the credentials file — never the accessToken / refreshToken — and
13
+ * returns true/false. Nothing else ever leaves this function.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.CLAUDE_CREDENTIALS_FILE = void 0;
17
+ exports.checkClaudeLoginValid = checkClaudeLoginValid;
18
+ /** Claude Code writes its OAuth login here under CLAUDE_CONFIG_DIR. */
19
+ exports.CLAUDE_CREDENTIALS_FILE = '.credentials.json';
20
+ function joinPath(dir, file) {
21
+ return dir.endsWith('/') ? `${dir}${file}` : `${dir}/${file}`;
22
+ }
23
+ /**
24
+ * Returns true iff a valid, non-expired `claude` login exists on the volume.
25
+ *
26
+ * Existence + expiry ONLY — the token value is never extracted, returned, or
27
+ * logged. Semantics:
28
+ * - credentials file absent → false (never logged in)
29
+ * - unparseable / not an object → false (corrupt → treat as no login)
30
+ * - has a numeric expiry in the past → false (expired / needs re-login)
31
+ * - has a numeric expiry in the future → true
32
+ * - exists but no expiry field → true (existence is the login signal)
33
+ */
34
+ function checkClaudeLoginValid(deps) {
35
+ const credPath = joinPath(deps.claudeDir, exports.CLAUDE_CREDENTIALS_FILE);
36
+ if (!deps.exists(credPath))
37
+ return false;
38
+ let parsed;
39
+ try {
40
+ parsed = deps.readJson(credPath);
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ if (!parsed || typeof parsed !== 'object')
46
+ return false;
47
+ // Claude Code nests the login under `claudeAiOauth`; tolerate a flat shape too.
48
+ const record = parsed;
49
+ const oauth = record.claudeAiOauth ?? record;
50
+ // Read ONLY the expiry. We deliberately never touch accessToken/refreshToken.
51
+ const expiresAt = oauth && typeof oauth.expiresAt === 'number' ? oauth.expiresAt : undefined;
52
+ if (expiresAt === undefined)
53
+ return true; // file present, no expiry → treat as valid
54
+ const now = (deps.now ?? Date.now)();
55
+ return expiresAt > now;
56
+ }
@@ -68,13 +68,17 @@ function realPtyFactory() {
68
68
  const pty = require('node-pty');
69
69
  const shell = process.env.SHELL || (os.platform() === 'win32' ? 'powershell.exe' : '/bin/bash');
70
70
  return {
71
- spawn: ({ cols, rows, cwd }) => {
71
+ spawn: ({ cols, rows, cwd, env }) => {
72
72
  const proc = pty.spawn(shell, [], {
73
73
  name: 'xterm-256color',
74
74
  cols,
75
75
  rows,
76
76
  cwd: cwd || os.homedir(),
77
- env: process.env,
77
+ // pucc-03: per-session env (WENRWA_WORKSPACE_ID / HT_AUTONOMOUS) is
78
+ // merged OVER the inherited process env, so one shared container can
79
+ // give each terminal its own workspace context without a machine-wide
80
+ // value. CLAUDE_CONFIG_DIR is inherited unchanged (shared ~/.claude).
81
+ env: { ...process.env, ...(env ?? {}) },
78
82
  });
79
83
  return {
80
84
  write: (data) => proc.write(data),
@@ -0,0 +1,166 @@
1
+ import { SessionManager } from './sessionManager';
2
+ import { BrowserDriver } from './browserDriver';
3
+ import { BrowserIdleTracker } from './browserIdle';
4
+ /** pucc-04 workspace-scoped git credential, resolved + injected by the relay. */
5
+ export interface InjectedGitCredential {
6
+ token: string;
7
+ repoFullName: string;
8
+ expiresAt?: string;
9
+ }
10
+ /**
11
+ * pucc-14 — assemble the per-session PTY env for a workspace-scoped `open`.
12
+ *
13
+ * Pure + exported so the env contract is unit-tested without a socket. Merges
14
+ * the relay-injected `env` (which already carries WENRWA_WORKSPACE_ID +
15
+ * WENRWA_CONNECT_TOKEN) and layers on:
16
+ * - WENRWA_WORKSPACE_ID — fallback if the relay didn't pre-set it in `env`.
17
+ * - WENRWA_CONNECT_TOKEN — the workspace-bound Connect Token (MCP world-wiring).
18
+ * - HT_AUTONOMOUS — per-session: `claude` vs `claude --dangerously-skip-permissions`.
19
+ * - WENRWA_GIT_TOKEN / WENRWA_GIT_REPO — the workspace git credential, read by
20
+ * git-credential-wenrwa for clone-on-demand + in-terminal git (per-workspace,
21
+ * no backend round-trip). The token rides the PTY env only; it is never
22
+ * logged here.
23
+ */
24
+ export declare function assembleSessionEnv(frame: {
25
+ workspaceId?: string;
26
+ autonomous?: boolean;
27
+ connectToken?: string;
28
+ gitCredential?: InjectedGitCredential;
29
+ env?: Record<string, string>;
30
+ }): Record<string, string>;
31
+ /** Minimal surface relayClient needs from a socket — lets tests inject fakes if ever needed. */
32
+ export interface WebSocketLike {
33
+ readyState: number;
34
+ send(data: string): void;
35
+ close(code?: number, reason?: string): void;
36
+ on(event: 'open' | 'message' | 'close' | 'error', cb: (...args: any[]) => void): void;
37
+ }
38
+ export interface BackoffOpts {
39
+ /** First retry delay in ms (before jitter). Default 250. */
40
+ initialMs?: number;
41
+ /** Cap on the backoff ceiling. Default 10_000. */
42
+ maxMs?: number;
43
+ /** Multiplier per attempt. Default 2. */
44
+ factor?: number;
45
+ /** Returns a value in [0, 1); overridable for deterministic tests. Default Math.random. */
46
+ jitter?: () => number;
47
+ }
48
+ export interface RelayClientOpts {
49
+ relayUrl: string;
50
+ relayToken: string;
51
+ sessions: SessionManager;
52
+ /**
53
+ * pucc-03 (ADR-0031 model B): resolve a workspace to the local checkout the
54
+ * terminal should open in, cloning on demand the first time (see
55
+ * ensureWorkspaceRepo). Returns the cwd, or null to fall back to the shell's
56
+ * default home dir. Absent → workspace-scoped opens still set
57
+ * WENRWA_WORKSPACE_ID but land in the default cwd.
58
+ */
59
+ resolveWorkspaceCwd?: (opts: {
60
+ workspaceId: string;
61
+ repoUrl?: string;
62
+ }) => Promise<string | null>;
63
+ /**
64
+ * pucc-int-04 (ADR-0032): install a forged crew `.claude` bundle (Forge
65
+ * format=map FileMap) into `/data/repos/<workspaceId>`. Path-safe + idempotent
66
+ * (see bundleInstaller.installBundle). Absent → an `install-bundle` frame is
67
+ * answered with an error (legacy / local bridge has no workspace checkouts).
68
+ * This does NOT run any crew engine — it only writes files.
69
+ */
70
+ installBundle?: (bundle: {
71
+ workspaceId: string;
72
+ files: Record<string, string>;
73
+ }) => {
74
+ written: string[];
75
+ skipped: Array<{
76
+ path: string;
77
+ reason: string;
78
+ }>;
79
+ } | void;
80
+ /**
81
+ * pucc-int-01 (ADR-0032 / ADR-0028): write the arena-pushed context manifest
82
+ * into a fenced managed region of `/data/repos/<workspaceId>/CLAUDE.md`
83
+ * (path-safe + idempotent — see contextWriter.writeContext); empty `content`
84
+ * removes the region. Absent → a `context-manifest` frame is answered with an
85
+ * error (legacy / local bridge has no workspace checkouts). This opens NO PTY
86
+ * and does NOT feed Claude Code via keystrokes — the context arrives as a FILE.
87
+ */
88
+ writeContext?: (manifest: {
89
+ workspaceId: string;
90
+ content: string;
91
+ }) => {
92
+ action: string;
93
+ claudeMdPath: string | null;
94
+ } | void;
95
+ /** Injectable for tests: defaults to `new WebSocket(url)` from `ws`. */
96
+ wsFactory?: (url: string) => WebSocketLike;
97
+ backoff?: BackoffOpts;
98
+ /** Injectable timer so backoff tests never wait on a real clock. */
99
+ setTimeoutFn?: (cb: () => void, ms: number) => unknown;
100
+ clearTimeoutFn?: (handle: unknown) => void;
101
+ /**
102
+ * pucc-14 login_valid report: returns whether a valid, non-expired `claude`
103
+ * login exists on the volume — a BOOLEAN only, never the token (see
104
+ * loginStatus.checkClaudeLoginValid). Absent → no login-status frame is ever
105
+ * emitted (legacy / local bridge). Present → the bridge emits `login-status`
106
+ * on connect and whenever the boolean changes.
107
+ */
108
+ checkLoginValid?: () => boolean;
109
+ /** Poll interval for re-checking login validity (default 30_000ms). */
110
+ loginPollMs?: number;
111
+ /**
112
+ * pucc-int-03 (ADR-0032): observe PTY output for Claude Code tool-use and
113
+ * emit a display-only `activity` frame ({ filePaths, thinking }) the relay
114
+ * broadcasts as `terminal:event` on the workspace room — driving the arena's
115
+ * drones/holograms from the agent's real work. This is a READ-ONLY tap that
116
+ * runs ALONGSIDE the browser forwarding: it never alters the `data` frames the
117
+ * pane receives (ADR-0028), and a parse miss/crash yields no frame — it can
118
+ * never break the terminal. Default ON. Set false for a plain (non-Claude)
119
+ * shell where activity has no meaning.
120
+ */
121
+ emitActivity?: boolean;
122
+ /** Min ms between activity frames per session (coalesces bursts). Default 1000. */
123
+ activityThrottleMs?: number;
124
+ /** Injectable clock for the activity throttle (tests). Default Date.now. */
125
+ nowFn?: () => number;
126
+ /** Injectable interval timer so the login poll is deterministic in tests. */
127
+ setIntervalFn?: (cb: () => void, ms: number) => unknown;
128
+ clearIntervalFn?: (handle: unknown) => void;
129
+ /**
130
+ * cbs-02: create the (single, session-keyed) browser driver for a
131
+ * `browser-open` frame's sessionId. Absent → `browser-open` is answered
132
+ * with an error (legacy / local bridge, or a bridge build without
133
+ * Chromium). This never opens or touches a PTY session — see the
134
+ * `browser-open`/`browser-input` cases below.
135
+ */
136
+ createBrowserDriver?: (args: {
137
+ sessionId: string;
138
+ }) => BrowserDriver;
139
+ /**
140
+ * cbs-06: create the per-session idle tracker (browserIdle.ts) alongside a
141
+ * freshly-created browser driver — mirrors createBrowserDriver's injection
142
+ * shape exactly. Absent → no idle auto-close is attempted (legacy / local
143
+ * bridge, or a caller that doesn't want it; browser-open/input/sleep still
144
+ * all work normally). `onIdle` fires once both liveness signals
145
+ * (browser-input, browser-frame-ack) have been stale past the tracker's
146
+ * own threshold; handled exactly like an incoming `browser-sleep` except
147
+ * the emitted browser-state's reason is 'idle-timeout' instead of 'sleep'.
148
+ */
149
+ createBrowserIdleTracker?: (args: {
150
+ sessionId: string;
151
+ onIdle: () => void;
152
+ }) => BrowserIdleTracker;
153
+ /** Test/observability hooks. */
154
+ onConnect?: () => void;
155
+ onDisconnect?: (info: {
156
+ code?: number;
157
+ reason?: string;
158
+ }) => void;
159
+ onReconnectScheduled?: (delayMs: number) => void;
160
+ }
161
+ export interface RelayClientHandle {
162
+ /** Stop reconnecting and close the current socket. Sessions are untouched — they live in SessionManager. */
163
+ close(): void;
164
+ isConnected(): boolean;
165
+ }
166
+ export declare function startRelayClient(opts: RelayClientOpts): RelayClientHandle;