@wenrwa/marketplace-cli 0.8.14 → 0.8.15

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.
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * lr-03 — the managed region tells an agent which linked repos it has, where
5
+ * they are on the box, and whether it may push to each. Without this an agent
6
+ * never learns the backend sits at /data/linked/<ws>/backend.
7
+ */
8
+ const contextWriter_1 = require("../bridge/contextWriter");
9
+ const workGraphPointer_1 = require("../bridge/workGraphPointer");
10
+ const WS = '11111111-1111-4111-8111-111111111111';
11
+ const LINKED = [
12
+ { alias: 'backend', fullName: 'acme/backend', writable: true, path: `/data/linked/${WS}/backend` },
13
+ { alias: 'cli', fullName: 'acme/cli', writable: false, path: `/data/linked/${WS}/cli` },
14
+ ];
15
+ describe('linkedReposSection', () => {
16
+ it('one line per repo: alias, repo, path, and whether pushing is allowed', () => {
17
+ expect((0, workGraphPointer_1.linkedReposSection)({ linkedRepos: LINKED })).toBe([
18
+ '## Linked repos',
19
+ 'This workspace also has these repos, each its own checkout:',
20
+ `- \`backend\` (acme/backend) at /data/linked/${WS}/backend — you can push`,
21
+ `- \`cli\` (acme/cli) at /data/linked/${WS}/cli — read-only, pushes are refused`,
22
+ ].join('\n'));
23
+ });
24
+ it('nothing linked → no section', () => {
25
+ expect((0, workGraphPointer_1.linkedReposSection)({ linkedRepos: [] })).toBeNull();
26
+ expect((0, workGraphPointer_1.linkedReposSection)({})).toBeNull();
27
+ });
28
+ });
29
+ describe('writeContext carries it inside the pointer block', () => {
30
+ it('a pointers-only refresh writes the section between the pointer markers', () => {
31
+ const files = new Map();
32
+ (0, contextWriter_1.writeContext)({ workspaceId: WS, pointersOnly: true, linkedRepos: LINKED }, {
33
+ reposBaseDir: '/data/repos',
34
+ exists: (p) => files.has(p),
35
+ readFile: (p) => files.get(p) ?? '',
36
+ writeFile: (p, c) => { files.set(p, c); },
37
+ mkdirp: () => { },
38
+ });
39
+ const written = [...files.values()].join('\n');
40
+ const block = written.slice(written.indexOf(contextWriter_1.POINTERS_START_MARKER), written.indexOf(contextWriter_1.POINTERS_END_MARKER));
41
+ expect(block).toContain('## Linked repos');
42
+ expect(block).toContain(`/data/linked/${WS}/cli — read-only`);
43
+ });
44
+ });
@@ -35,6 +35,13 @@ export declare function writeContext(input: {
35
35
  /** Absent only for a pointers-only refresh (wmr-07). */
36
36
  content?: string;
37
37
  workGraphAvailable?: boolean;
38
+ /** lr-03 — this workspace's linked repos on the box (linkedReposSection). */
39
+ linkedRepos?: Array<{
40
+ alias: string;
41
+ fullName: string;
42
+ writable: boolean;
43
+ path: string;
44
+ }>;
38
45
  /** 2026-10-02 — a match workspace: MATCH_POINTER replaces the work-queue pointer (workSection). */
39
46
  match?: boolean;
40
47
  /**
@@ -173,6 +173,9 @@ function writeContextFile(input, claudeMdPath, seedPath, workspaceRoot, deps) {
173
173
  pointers.push(work);
174
174
  if (input.memoryAvailable)
175
175
  pointers.push((0, memoryPointer_1.memorySection)(input.memoryBrief));
176
+ const linked = (0, workGraphPointer_1.linkedReposSection)(input);
177
+ if (linked)
178
+ pointers.push(linked);
176
179
  const browser = (0, workGraphPointer_1.browserSection)(input);
177
180
  if (browser)
178
181
  pointers.push(browser);
@@ -3,7 +3,11 @@ export interface LinkedRepoRef {
3
3
  alias: string;
4
4
  fullName: string;
5
5
  cloneUrl: string;
6
+ /** Shown to the agent (context pointer); GitHub enforces it via the token. */
7
+ writable?: boolean;
6
8
  }
9
+ /** /data/linked/<workspaceId>/<alias> — the ONE place a linked clone lives. */
10
+ export declare function linkedRepoPath(linkedBaseDir: string, workspaceId: string, alias: string): string;
7
11
  export interface LinkedRepoResult {
8
12
  alias: string;
9
13
  action: 'existing' | 'cloned' | 'failed';
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.linkedRepoPath = linkedRepoPath;
6
7
  exports.ensureLinkedRepos = ensureLinkedRepos;
7
8
  /**
8
9
  * lr-03 — clone a workspace's LINKED repos on the box
@@ -22,6 +23,10 @@ exports.ensureLinkedRepos = ensureLinkedRepos;
22
23
  * - Never throws: a failed clone must not break the terminal that opened.
23
24
  */
24
25
  const path_1 = __importDefault(require("path"));
26
+ /** /data/linked/<workspaceId>/<alias> — the ONE place a linked clone lives. */
27
+ function linkedRepoPath(linkedBaseDir, workspaceId, alias) {
28
+ return path_1.default.join(linkedBaseDir, workspaceId, alias);
29
+ }
25
30
  const ALIAS = /^[a-z0-9][a-z0-9-]{0,31}$/;
26
31
  const WORKSPACE_ID = /^[A-Za-z0-9-]{1,64}$/;
27
32
  const FULL_NAME = /^[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}$/;
@@ -291,6 +291,7 @@ export interface RelayClientOpts {
291
291
  refreshContextPointers?: (args: {
292
292
  workspaceId: string;
293
293
  engine?: string;
294
+ linkedRepos?: LinkedRepoRef[];
294
295
  }) => void | Promise<void>;
295
296
  writeContext?: (manifest: {
296
297
  workspaceId: string;
@@ -1231,6 +1231,8 @@ function startRelayClient(opts) {
1231
1231
  if (workspaceId && opts.refreshContextPointers) {
1232
1232
  void Promise.resolve(opts.refreshContextPointers({
1233
1233
  workspaceId,
1234
+ // lr-03: absent on the frame = none linked (or an older relay).
1235
+ linkedRepos: Array.isArray(linkedRepos) ? linkedRepos : [],
1234
1236
  // oc-23: the relay-injected env names the agent's engine; absent = Claude.
1235
1237
  ...(injectedEnv?.WENRWA_AGENT_ENGINE ? { engine: injectedEnv.WENRWA_AGENT_ENGINE } : {}),
1236
1238
  })).catch((err) => {
@@ -51,6 +51,19 @@ export declare function workSection(input: {
51
51
  workGraphAvailable?: boolean;
52
52
  match?: boolean;
53
53
  }): string | null;
54
+ /**
55
+ * lr-03 — which linked repos this workspace has, where each is on the box, and
56
+ * whether the agent may push there (a read-only link's token is contents:read,
57
+ * so a push is refused by GitHub). Silent when nothing is linked.
58
+ */
59
+ export declare function linkedReposSection(input: {
60
+ linkedRepos?: Array<{
61
+ alias: string;
62
+ fullName: string;
63
+ writable: boolean;
64
+ path: string;
65
+ }>;
66
+ }): string | null;
54
67
  /**
55
68
  * cvt-09 (amended by the 2026-10-04 show-in-browser-tabs change) — tells the
56
69
  * agent its pilot watches a Browser pane and WHEN to call `show_in_browser`:
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SECRETS_POINTER = exports.KEYS_POINTER = exports.BROWSER_POINTER = exports.MATCH_POINTER = exports.WORK_GRAPH_POINTER = void 0;
4
4
  exports.workSection = workSection;
5
+ exports.linkedReposSection = linkedReposSection;
5
6
  exports.browserSection = browserSection;
6
7
  exports.keysSection = keysSection;
7
8
  exports.withSecretsSection = withSecretsSection;
@@ -67,6 +68,21 @@ function workSection(input) {
67
68
  return exports.MATCH_POINTER;
68
69
  return input.workGraphAvailable ? exports.WORK_GRAPH_POINTER : null;
69
70
  }
71
+ /**
72
+ * lr-03 — which linked repos this workspace has, where each is on the box, and
73
+ * whether the agent may push there (a read-only link's token is contents:read,
74
+ * so a push is refused by GitHub). Silent when nothing is linked.
75
+ */
76
+ function linkedReposSection(input) {
77
+ const repos = input.linkedRepos ?? [];
78
+ if (repos.length === 0)
79
+ return null;
80
+ return [
81
+ '## Linked repos',
82
+ 'This workspace also has these repos, each its own checkout:',
83
+ ...repos.map((r) => `- \`${r.alias}\` (${r.fullName}) at ${r.path} — ${r.writable ? 'you can push' : 'read-only, pushes are refused'}`),
84
+ ].join('\n');
85
+ }
70
86
  /**
71
87
  * cvt-09 (amended by the 2026-10-04 show-in-browser-tabs change) — tells the
72
88
  * agent its pilot watches a Browser pane and WHEN to call `show_in_browser`:
@@ -700,6 +700,15 @@ exports.bridgeCommand = new commander_1.Command('bridge')
700
700
  // lr-03 — a workspace's linked repos, cloned beside (never inside)
701
701
  // /data/repos, each with its own credential from the broker.
702
702
  const linkedBaseDir = process.env.WENRWA_LINKED_DIR || path.join(path.dirname(reposBaseDir), 'linked');
703
+ // The latest list each workspace's open frame carried — so a file-selection
704
+ // write (which has no frame of its own) keeps the agent's linked-repo lines.
705
+ const linkedReposByWorkspace = new Map();
706
+ const linkedPointerFor = (workspaceId) => (linkedReposByWorkspace.get(workspaceId) ?? []).map((r) => ({
707
+ alias: r.alias,
708
+ fullName: r.fullName,
709
+ writable: r.writable === true,
710
+ path: (0, linkedRepos_1.linkedRepoPath)(linkedBaseDir, workspaceId, r.alias),
711
+ }));
703
712
  const ensureLinked = (args) => (0, linkedRepos_1.ensureLinkedRepos)(args, {
704
713
  linkedBaseDir,
705
714
  credentialFor: (workspaceId, fullName) => gitTokenBroker.get(workspaceId, fullName),
@@ -911,7 +920,9 @@ exports.bridgeCommand = new commander_1.Command('bridge')
911
920
  only sends one when the pilot has files selected, so most workspaces
912
921
  never got the pointers at all.
913
922
  */
914
- refreshContextPointers: async ({ workspaceId, engine }) => {
923
+ refreshContextPointers: async ({ workspaceId, engine, linkedRepos }) => {
924
+ if (linkedRepos)
925
+ linkedReposByWorkspace.set(workspaceId, linkedRepos);
915
926
  const memoryAvailable = Boolean(process.env.WENRWA_CONNECT_TOKEN);
916
927
  // 2026-10-02 — the same reply says whether this is a MATCH workspace.
917
928
  const briefInfo = memoryAvailable ? await (0, memoryBrief_1.fetchWorkspaceBrief)(workspaceId) : null;
@@ -920,6 +931,7 @@ exports.bridgeCommand = new commander_1.Command('bridge')
920
931
  workspaceId,
921
932
  pointersOnly: true,
922
933
  engine,
934
+ linkedRepos: linkedPointerFor(workspaceId),
923
935
  workGraphAvailable: Boolean(process.env.WENRWA_CONNECT_TOKEN),
924
936
  memoryAvailable,
925
937
  memoryBrief,
@@ -949,6 +961,7 @@ exports.bridgeCommand = new commander_1.Command('bridge')
949
961
  workspaceId,
950
962
  content,
951
963
  engine,
964
+ linkedRepos: linkedPointerFor(workspaceId),
952
965
  // wgcp-01 — same gate as checkStorageQuota above: WENRWA_CONNECT_TOKEN
953
966
  // is only injected at container boot when this workspace actually has
954
967
  // MCP world-wiring (hosted-container.service.ts's machineEnv). Absent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenrwa/marketplace-cli",
3
- "version": "0.8.14",
3
+ "version": "0.8.15",
4
4
  "description": "CLI tool for the Wenrwa Agent Marketplace — monitor bounties, agents, workspaces, and treasury from your terminal",
5
5
  "author": "Wenrwa <dev@wenrwa.com>",
6
6
  "license": "MIT",