@wenrwa/marketplace-cli 0.8.7 → 0.8.8

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,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * A session anchor must not need a git identity.
5
+ *
6
+ * Production 2026-10-19 (owner: "the Changes button doesn't seem to work"):
7
+ * a hosted box has NO git identity — no user.name/user.email in any config;
8
+ * the agent's identity rides only in the PTY env (GIT_AUTHOR_*). The bridge's
9
+ * own `git commit-tree` therefore died with "Author identity unknown" (exit
10
+ * 128, probed on the live machine), no refs/wenrwa/session-* ref was ever
11
+ * created, and no session-scoped Changes report has ever been persisted (zero
12
+ * `scope = 'session'` rows in terminal_changes).
13
+ *
14
+ * This runs REAL git with `user.useConfigOnly = true` and no identity, which
15
+ * is the container's situation made deterministic (a laptop would otherwise
16
+ * auto-detect user@host and hide the bug).
17
+ */
18
+ const fs_1 = require("fs");
19
+ const os_1 = require("os");
20
+ const path_1 = require("path");
21
+ const child_process_1 = require("child_process");
22
+ const sessionAnchor_1 = require("../bridge/sessionAnchor");
23
+ const changesReporter_1 = require("../bridge/changesReporter");
24
+ // Jest's sandboxed process.env does not reach child_process on its own, so
25
+ // every git call here passes the (identity-less) env explicitly — the same
26
+ // merge execGit does in production.
27
+ const sandboxEnv = () => ({ ...process.env });
28
+ const run = (cmd, args, opts) => (0, changesReporter_1.execGit)(cmd, args, { env: { ...sandboxEnv(), ...(opts?.env ?? {}) } });
29
+ const git = (args) => (0, child_process_1.execFileSync)('git', args, { env: sandboxEnv(), stdio: 'pipe' }).toString().trim();
30
+ const IDENTITY_KEYS = ['GIT_AUTHOR_NAME', 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_NAME', 'GIT_COMMITTER_EMAIL', 'EMAIL'];
31
+ describe('createSessionAnchor on a box with no git identity', () => {
32
+ let dir;
33
+ let repo;
34
+ const saved = {};
35
+ beforeEach(() => {
36
+ dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'anchor-noid-'));
37
+ repo = (0, path_1.join)(dir, 'repo');
38
+ const globalCfg = (0, path_1.join)(dir, 'gitconfig');
39
+ (0, fs_1.writeFileSync)(globalCfg, '[user]\n\tuseConfigOnly = true\n');
40
+ for (const k of [...IDENTITY_KEYS, 'GIT_CONFIG_GLOBAL', 'GIT_CONFIG_NOSYSTEM', 'HOME'])
41
+ saved[k] = process.env[k];
42
+ for (const k of IDENTITY_KEYS)
43
+ delete process.env[k];
44
+ process.env.GIT_CONFIG_GLOBAL = globalCfg;
45
+ process.env.GIT_CONFIG_NOSYSTEM = '1';
46
+ process.env.HOME = dir;
47
+ git(['init', '-q', repo]);
48
+ (0, fs_1.writeFileSync)((0, path_1.join)(repo, 'a.txt'), 'hello\n');
49
+ });
50
+ afterEach(() => {
51
+ for (const [k, v] of Object.entries(saved)) {
52
+ if (v === undefined)
53
+ delete process.env[k];
54
+ else
55
+ process.env[k] = v;
56
+ }
57
+ (0, fs_1.rmSync)(dir, { recursive: true, force: true });
58
+ });
59
+ it('git itself refuses a commit here (the precondition is real)', () => {
60
+ const tree = git(['-C', repo, 'write-tree']);
61
+ expect(() => git(['-C', repo, 'commit-tree', tree, '-m', 'x'])).toThrow(/identity unknown|auto-detection is disabled/i);
62
+ });
63
+ it('still creates the anchor commit and its ref', async () => {
64
+ const res = await (0, sessionAnchor_1.createSessionAnchor)(repo, 'sess-noid', { run });
65
+ expect(res.ok).toBe(true);
66
+ if (!res.ok)
67
+ return;
68
+ const refSha = git(['-C', repo, 'rev-parse', (0, sessionAnchor_1.anchorRefName)('sess-noid')]);
69
+ expect(refSha).toBe(res.sha);
70
+ // The anchor snapshots the untracked file, so a later diff is relative to it.
71
+ const files = git(['-C', repo, 'ls-tree', '--name-only', res.sha]);
72
+ expect(files).toBe('a.txt');
73
+ });
74
+ });
@@ -68,9 +68,17 @@ describe('createSessionAnchor', () => {
68
68
  expect(calls[1].args).toEqual(['-C', CWD, 'write-tree']);
69
69
  expect(calls[1].opts?.env?.GIT_INDEX_FILE).toBe(indexFile);
70
70
  // 3. commit-tree — no parent (a fresh anchor, not a chain), no index
71
- // involved at all (commit-tree never reads one) — no env override.
71
+ // involved at all (commit-tree never reads one). Its only env is a
72
+ // fixed identity: a hosted box has none (see
73
+ // bridge-session-anchor-no-identity.test.ts).
72
74
  expect(calls[2].args).toEqual(['-C', CWD, 'commit-tree', 'treesha0000', '-m', 'wenrwa session anchor']);
73
- expect(calls[2].opts?.env).toBeUndefined();
75
+ expect(calls[2].opts?.env).toEqual({
76
+ GIT_AUTHOR_NAME: 'HiveOp',
77
+ GIT_AUTHOR_EMAIL: 'session-anchor@hiveop.local',
78
+ GIT_COMMITTER_NAME: 'HiveOp',
79
+ GIT_COMMITTER_EMAIL: 'session-anchor@hiveop.local',
80
+ });
81
+ expect(calls[2].opts?.env?.GIT_INDEX_FILE).toBeUndefined();
74
82
  // 4. update-ref — points the session's ref at the new commit, no env.
75
83
  expect(calls[3].args).toEqual(['-C', CWD, 'update-ref', 'refs/wenrwa/session-sess-1', 'commitsha1111']);
76
84
  expect(calls[3].opts?.env).toBeUndefined();
@@ -22,6 +22,15 @@ export type SweepAnchorsResult = {
22
22
  ok: false;
23
23
  error: string;
24
24
  };
25
+ /** The fixed author/committer of every anchor commit — see createSessionAnchor. */
26
+ export declare const ANCHOR_IDENTITY: {
27
+ env: {
28
+ GIT_AUTHOR_NAME: string;
29
+ GIT_AUTHOR_EMAIL: string;
30
+ GIT_COMMITTER_NAME: string;
31
+ GIT_COMMITTER_EMAIL: string;
32
+ };
33
+ };
25
34
  /** Every session anchor ref lives under this namespace — sweepStaleAnchors's glob and this function must agree. */
26
35
  export declare function anchorRefName(sessionId: string): string;
27
36
  /**
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ANCHOR_IDENTITY = void 0;
36
37
  exports.anchorRefName = anchorRefName;
37
38
  exports.createSessionAnchor = createSessionAnchor;
38
39
  exports.releaseSessionAnchor = releaseSessionAnchor;
@@ -80,6 +81,15 @@ exports.sweepStaleAnchors = sweepStaleAnchors;
80
81
  const os = __importStar(require("os"));
81
82
  const path = __importStar(require("path"));
82
83
  const crypto_1 = require("crypto");
84
+ /** The fixed author/committer of every anchor commit — see createSessionAnchor. */
85
+ exports.ANCHOR_IDENTITY = {
86
+ env: {
87
+ GIT_AUTHOR_NAME: 'HiveOp',
88
+ GIT_AUTHOR_EMAIL: 'session-anchor@hiveop.local',
89
+ GIT_COMMITTER_NAME: 'HiveOp',
90
+ GIT_COMMITTER_EMAIL: 'session-anchor@hiveop.local',
91
+ },
92
+ };
83
93
  /** Every session anchor ref lives under this namespace — sweepStaleAnchors's glob and this function must agree. */
84
94
  function anchorRefName(sessionId) {
85
95
  return `refs/wenrwa/session-${sessionId}`;
@@ -114,7 +124,12 @@ async function createSessionAnchor(cwd, sessionId, deps) {
114
124
  const tree = treeRes.stdout.trim();
115
125
  // commit-tree never reads an index — no env override, and deliberately
116
126
  // no `-p <parent>`: this commit is a floating snapshot, not history.
117
- const commitRes = await deps.run('git', ['-C', cwd, 'commit-tree', tree, '-m', 'wenrwa session anchor']);
127
+ // The identity is set HERE, never inherited: a hosted box has no git
128
+ // identity at all (the agent's rides only in its PTY env), and without
129
+ // one commit-tree dies "Author identity unknown" — which is why no hosted
130
+ // session report had ever been produced (prod 2026-10-19). The anchor is
131
+ // a hidden ref nobody pushes, so a fixed identity costs nothing.
132
+ const commitRes = await deps.run('git', ['-C', cwd, 'commit-tree', tree, '-m', 'wenrwa session anchor'], exports.ANCHOR_IDENTITY);
118
133
  if (commitRes.code !== 0) {
119
134
  return { ok: false, error: `anchor-commit-failed: ${commitRes.stdout.trim() || 'git commit-tree failed'}` };
120
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenrwa/marketplace-cli",
3
- "version": "0.8.7",
3
+ "version": "0.8.8",
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",