teamshare-bridge 0.26.25 → 0.27.1

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 (63) hide show
  1. package/dist/cli/commands/build.js +48 -22
  2. package/dist/cli/commands/build.js.map +1 -1
  3. package/dist/cli/commands/plan.js +40 -2
  4. package/dist/cli/commands/plan.js.map +1 -1
  5. package/dist/cli/commands/planner.js +3 -1
  6. package/dist/cli/commands/planner.js.map +1 -1
  7. package/dist/cli/commands/research.js +68 -4
  8. package/dist/cli/commands/research.js.map +1 -1
  9. package/dist/cli/commands/run.js +54 -3
  10. package/dist/cli/commands/run.js.map +1 -1
  11. package/dist/cli/commands/worker.js +52 -59
  12. package/dist/cli/commands/worker.js.map +1 -1
  13. package/dist/cli/commands/workflow.js +36 -2
  14. package/dist/cli/commands/workflow.js.map +1 -1
  15. package/dist/lib/api-content.d.ts +38 -0
  16. package/dist/lib/api-content.js +14 -0
  17. package/dist/lib/api-content.js.map +1 -1
  18. package/dist/lib/api.d.ts +4 -0
  19. package/dist/lib/api.js.map +1 -1
  20. package/dist/lib/brief.d.ts +12 -0
  21. package/dist/lib/brief.js +10 -7
  22. package/dist/lib/brief.js.map +1 -1
  23. package/dist/lib/browser/batch.d.ts +5 -0
  24. package/dist/lib/browser/batch.js +29 -0
  25. package/dist/lib/browser/batch.js.map +1 -1
  26. package/dist/lib/browser/challenge.d.ts +85 -0
  27. package/dist/lib/browser/challenge.js +164 -0
  28. package/dist/lib/browser/challenge.js.map +1 -0
  29. package/dist/lib/browser/launch.d.ts +9 -0
  30. package/dist/lib/browser/launch.js +11 -9
  31. package/dist/lib/browser/launch.js.map +1 -1
  32. package/dist/lib/browser/session.d.ts +50 -1
  33. package/dist/lib/browser/session.js +162 -2
  34. package/dist/lib/browser/session.js.map +1 -1
  35. package/dist/lib/browser/stealth.d.ts +20 -0
  36. package/dist/lib/browser/stealth.js +86 -0
  37. package/dist/lib/browser/stealth.js.map +1 -0
  38. package/dist/lib/git/git-finalize.d.ts +2 -0
  39. package/dist/lib/git/git-finalize.js +32 -5
  40. package/dist/lib/git/git-finalize.js.map +1 -1
  41. package/dist/lib/git/git-pipeline.d.ts +3 -2
  42. package/dist/lib/git/git-pipeline.js.map +1 -1
  43. package/dist/lib/git/git-presence.js +1 -1
  44. package/dist/lib/git/git-presence.js.map +1 -1
  45. package/dist/lib/git/index.d.ts +2 -2
  46. package/dist/lib/git/index.js +4 -1
  47. package/dist/lib/git/index.js.map +1 -1
  48. package/dist/lib/git/pr-token.d.ts +74 -16
  49. package/dist/lib/git/pr-token.js +160 -16
  50. package/dist/lib/git/pr-token.js.map +1 -1
  51. package/dist/lib/orchestrator/handlers/browser.js +92 -3
  52. package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
  53. package/dist/lib/run-report.d.ts +38 -11
  54. package/dist/lib/run-report.js +137 -88
  55. package/dist/lib/run-report.js.map +1 -1
  56. package/dist/lib/tools/docx-source.d.ts +6 -4
  57. package/dist/lib/tools/docx-source.js +27 -4
  58. package/dist/lib/tools/docx-source.js.map +1 -1
  59. package/dist/lib/tools/pptx-deck.d.ts +90 -0
  60. package/dist/lib/tools/pptx-deck.js +1302 -147
  61. package/dist/lib/tools/pptx-deck.js.map +1 -1
  62. package/package.json +1 -1
  63. package/skills/teamshare-cdd/SKILL.md +219 -215
@@ -7,6 +7,6 @@ export { formatLocalGitState } from './stash-context';
7
7
  export { detectIndexBlocker, recoverBlockedIndex, preflightRecoverIndexes, postIndexRecoveryComment, blockedRecordFor, markBlockedIndexRepo, isBlockedIndexRepo, clearBlockedIndexRepo, clearAllBlockedIndexRepos, } from './index-recovery';
8
8
  export type { IndexBlocker, IndexBlockerKind, IndexRecoveryApi, IndexRecoveryRecord, } from './index-recovery';
9
9
  export { createPullRequest, isGitHubUrl } from './pr-create';
10
- export { resolvePrToken, backendRunToken, ghAuthToken } from './pr-token';
11
- export type { ResolvedPrToken, PrTokenSource } from './pr-token';
10
+ export { resolvePrToken, backendRunToken, ghAuthToken, credentialFillToken, looksLikeGitHubToken, prRefusalLine, } from './pr-token';
11
+ export type { ResolvedPrToken, PrTokenSource, PrTokenRefusal, BackendRunTokenOpts, ResolvePrTokenOpts, ResolvePrTokenResult, } from './pr-token';
12
12
  export { slugifySegment } from './slug';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.slugifySegment = exports.ghAuthToken = exports.backendRunToken = exports.resolvePrToken = exports.isGitHubUrl = exports.createPullRequest = exports.clearAllBlockedIndexRepos = exports.clearBlockedIndexRepo = exports.isBlockedIndexRepo = exports.markBlockedIndexRepo = exports.blockedRecordFor = exports.postIndexRecoveryComment = exports.preflightRecoverIndexes = exports.recoverBlockedIndex = exports.detectIndexBlocker = exports.formatLocalGitState = exports.restoreStash = exports.listStashes = exports.checkOrphanedStashes = exports.formatStashSummary = exports.resolveGitPolicy = exports.commitRemainingChanges = exports.finalizeRunGit = exports.prepareRunGitAsync = exports.prepareRunGit = exports.snapshotGit = exports.ensureRepo = exports.originUrlFor = exports.urlKey = exports.rememberGitToken = exports.tokenForUrl = exports.authArgsForRepo = exports.authArgsForUrl = void 0;
3
+ exports.slugifySegment = exports.prRefusalLine = exports.looksLikeGitHubToken = exports.credentialFillToken = exports.ghAuthToken = exports.backendRunToken = exports.resolvePrToken = exports.isGitHubUrl = exports.createPullRequest = exports.clearAllBlockedIndexRepos = exports.clearBlockedIndexRepo = exports.isBlockedIndexRepo = exports.markBlockedIndexRepo = exports.blockedRecordFor = exports.postIndexRecoveryComment = exports.preflightRecoverIndexes = exports.recoverBlockedIndex = exports.detectIndexBlocker = exports.formatLocalGitState = exports.restoreStash = exports.listStashes = exports.checkOrphanedStashes = exports.formatStashSummary = exports.resolveGitPolicy = exports.commitRemainingChanges = exports.finalizeRunGit = exports.prepareRunGitAsync = exports.prepareRunGit = exports.snapshotGit = exports.ensureRepo = exports.originUrlFor = exports.urlKey = exports.rememberGitToken = exports.tokenForUrl = exports.authArgsForRepo = exports.authArgsForUrl = void 0;
4
4
  var git_auth_1 = require("./git-auth");
5
5
  Object.defineProperty(exports, "authArgsForUrl", { enumerable: true, get: function () { return git_auth_1.authArgsForUrl; } });
6
6
  Object.defineProperty(exports, "authArgsForRepo", { enumerable: true, get: function () { return git_auth_1.authArgsForRepo; } });
@@ -41,6 +41,9 @@ var pr_token_1 = require("./pr-token");
41
41
  Object.defineProperty(exports, "resolvePrToken", { enumerable: true, get: function () { return pr_token_1.resolvePrToken; } });
42
42
  Object.defineProperty(exports, "backendRunToken", { enumerable: true, get: function () { return pr_token_1.backendRunToken; } });
43
43
  Object.defineProperty(exports, "ghAuthToken", { enumerable: true, get: function () { return pr_token_1.ghAuthToken; } });
44
+ Object.defineProperty(exports, "credentialFillToken", { enumerable: true, get: function () { return pr_token_1.credentialFillToken; } });
45
+ Object.defineProperty(exports, "looksLikeGitHubToken", { enumerable: true, get: function () { return pr_token_1.looksLikeGitHubToken; } });
46
+ Object.defineProperty(exports, "prRefusalLine", { enumerable: true, get: function () { return pr_token_1.prRefusalLine; } });
44
47
  var slug_1 = require("./slug");
45
48
  Object.defineProperty(exports, "slugifySegment", { enumerable: true, get: function () { return slug_1.slugifySegment; } });
46
49
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/git/index.ts"],"names":[],"mappings":";;;AAAA,uCAOoB;AANlB,0GAAA,cAAc,OAAA;AACd,2GAAA,eAAe,OAAA;AACf,uGAAA,WAAW,OAAA;AACX,4GAAA,gBAAgB,OAAA;AAChB,kGAAA,MAAM,OAAA;AACN,wGAAA,YAAY,OAAA;AAEd,uCAAqD;AAA5C,sGAAA,UAAU,OAAA;AAAE,uGAAA,WAAW,OAAA;AAChC,+CAMwB;AALtB,6GAAA,aAAa,OAAA;AACb,kHAAA,kBAAkB,OAAA;AAClB,8GAAA,cAAc,OAAA;AACd,sHAAA,sBAAsB,OAAA;AACtB,gHAAA,gBAAgB,OAAA;AAElB,2CAKsB;AAJpB,gHAAA,kBAAkB,OAAA;AAClB,kHAAA,oBAAoB,OAAA;AACpB,yGAAA,WAAW,OAAA;AACX,0GAAA,YAAY,OAAA;AAGd,iDAAsD;AAA7C,oHAAA,mBAAmB,OAAA;AAC5B,mDAU0B;AATxB,oHAAA,kBAAkB,OAAA;AAClB,qHAAA,mBAAmB,OAAA;AACnB,yHAAA,uBAAuB,OAAA;AACvB,0HAAA,wBAAwB,OAAA;AACxB,kHAAA,gBAAgB,OAAA;AAChB,sHAAA,oBAAoB,OAAA;AACpB,oHAAA,kBAAkB,OAAA;AAClB,uHAAA,qBAAqB,OAAA;AACrB,2HAAA,yBAAyB,OAAA;AAQ3B,yCAA6D;AAApD,8GAAA,iBAAiB,OAAA;AAAE,wGAAA,WAAW,OAAA;AACvC,uCAA0E;AAAjE,0GAAA,cAAc,OAAA;AAAE,2GAAA,eAAe,OAAA;AAAE,uGAAA,WAAW,OAAA;AAErD,+BAAwC;AAA/B,sGAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/git/index.ts"],"names":[],"mappings":";;;AAAA,uCAOoB;AANlB,0GAAA,cAAc,OAAA;AACd,2GAAA,eAAe,OAAA;AACf,uGAAA,WAAW,OAAA;AACX,4GAAA,gBAAgB,OAAA;AAChB,kGAAA,MAAM,OAAA;AACN,wGAAA,YAAY,OAAA;AAEd,uCAAqD;AAA5C,sGAAA,UAAU,OAAA;AAAE,uGAAA,WAAW,OAAA;AAChC,+CAMwB;AALtB,6GAAA,aAAa,OAAA;AACb,kHAAA,kBAAkB,OAAA;AAClB,8GAAA,cAAc,OAAA;AACd,sHAAA,sBAAsB,OAAA;AACtB,gHAAA,gBAAgB,OAAA;AAElB,2CAKsB;AAJpB,gHAAA,kBAAkB,OAAA;AAClB,kHAAA,oBAAoB,OAAA;AACpB,yGAAA,WAAW,OAAA;AACX,0GAAA,YAAY,OAAA;AAGd,iDAAsD;AAA7C,oHAAA,mBAAmB,OAAA;AAC5B,mDAU0B;AATxB,oHAAA,kBAAkB,OAAA;AAClB,qHAAA,mBAAmB,OAAA;AACnB,yHAAA,uBAAuB,OAAA;AACvB,0HAAA,wBAAwB,OAAA;AACxB,kHAAA,gBAAgB,OAAA;AAChB,sHAAA,oBAAoB,OAAA;AACpB,oHAAA,kBAAkB,OAAA;AAClB,uHAAA,qBAAqB,OAAA;AACrB,2HAAA,yBAAyB,OAAA;AAQ3B,yCAA6D;AAApD,8GAAA,iBAAiB,OAAA;AAAE,wGAAA,WAAW,OAAA;AACvC,uCAOoB;AANlB,0GAAA,cAAc,OAAA;AACd,2GAAA,eAAe,OAAA;AACf,uGAAA,WAAW,OAAA;AACX,+GAAA,mBAAmB,OAAA;AACnB,gHAAA,oBAAoB,OAAA;AACpB,yGAAA,aAAa,OAAA;AAUf,+BAAwC;AAA/B,sGAAA,cAAc,OAAA"}
@@ -1,29 +1,87 @@
1
- export type PrTokenSource = 'project' | 'env' | 'gh-cli' | 'github_app';
1
+ export type PrTokenSource = 'project' | 'env' | 'gh-cli' | 'github_app' | 'credential-helper';
2
2
  export interface ResolvedPrToken {
3
3
  token: string;
4
4
  source: PrTokenSource;
5
+ /**
6
+ * Set only on a credential-helper rescue: the backend refused first with
7
+ * this code (e.g. REPO_NOT_INSTALLED) and the helper supplied a token
8
+ * anyway. Lets the caller log WHY the fallback was used without ever
9
+ * seeing the token.
10
+ */
11
+ note?: string;
5
12
  }
6
- /** Best-effort ambient GitHub CLI token (null when gh is absent/logged-out). */
7
- export declare function ghAuthToken(): string | null;
8
- /**
9
- * Ask the hub for a one-shot token for this run (step 4). Returns null when
10
- * there is no project, no run identity, no App connection — every one of
11
- * those is a normal "not connected" state, never an error.
12
- */
13
- export declare function backendRunToken(opts: {
13
+ /** A typed backend refusal: the UPPER_SNAKE code names the fix. */
14
+ export interface PrTokenRefusal {
15
+ error: {
16
+ code: string;
17
+ message: string;
18
+ };
19
+ }
20
+ export interface BackendRunTokenOpts {
14
21
  remoteUrl: string;
15
22
  projectId?: string | null;
16
23
  apiKey?: string | null;
17
24
  apiBaseUrl?: string | null;
18
- }): Promise<ResolvedPrToken | null>;
25
+ /** Passed in the POST body when present (lets the hub audit per task). */
26
+ taskId?: string | null;
27
+ }
28
+ /** Best-effort ambient GitHub CLI token (null when gh is absent/logged-out). */
29
+ export declare function ghAuthToken(): string | null;
30
+ /**
31
+ * GitHub-shaped token check for the credential-helper rescue.
32
+ *
33
+ * Some helpers answer `git credential fill` with a username or some other
34
+ * non-token secret; pushing with that burns the attempt on a 401 and the
35
+ * report then blames the wrong thing. So on a github.com host ONLY a
36
+ * GitHub token shape is accepted (classic `ghp_*` family + fine-grained
37
+ * `github_pat_*` + the legacy 40-hex PAT). Non-GitHub forges (Gitea accepts
38
+ * any non-empty password) take whatever the helper stored.
39
+ */
40
+ export declare function looksLikeGitHubToken(value: string): boolean;
41
+ /**
42
+ * Best-effort machine credential (step 5). Runs `git credential fill` for the
43
+ * remote's host and returns the filled password when it is usable. Returns
44
+ * null on every failure — absent git, timeout, non-zero exit, empty password,
45
+ * or a non-token-shaped secret on a github.com host. Never throws, never
46
+ * logs the secret (callers log only the source name).
47
+ *
48
+ * NOTE on where the password can come from: `fill` consults the configured
49
+ * helpers AND, when they miss, the prompt fallback (`GIT_ASKPASS` /
50
+ * `core.askPass`) — on a VS Code-integrated shell that fallback silently
51
+ * returns the editor's stored GitHub session. That is ordinary `fill`
52
+ * semantics and a session token is a fine credential (same trust boundary as
53
+ * manager-core), so it is accepted, not scrubbed. `GIT_TERMINAL_PROMPT=0`
54
+ * keeps a truly-interactive prompt from hanging the run past the timeout.
55
+ */
56
+ export declare function credentialFillToken(remoteUrl: string): ResolvedPrToken | null;
57
+ /**
58
+ * Ask the hub for a one-shot token for this run (step 4).
59
+ *
60
+ * Returns null ONLY when there is no run identity (no project, no key) — a
61
+ * normal "not connected" state, never an error. Every answered request
62
+ * resolves to either a token or a TYPED refusal carrying the backend's
63
+ * UPPER_SNAKE code and message (truncated to 300 chars); a request that never
64
+ * got an answer (network/timeout) is `RUN_TOKEN_UNREACHABLE`. Callers must
65
+ * NOT treat a typed refusal as "try nothing else" — see `resolvePrToken`.
66
+ */
67
+ export declare function backendRunToken(opts: BackendRunTokenOpts): Promise<ResolvedPrToken | PrTokenRefusal | null>;
68
+ export type ResolvePrTokenOpts = BackendRunTokenOpts;
69
+ export type ResolvePrTokenResult = ResolvedPrToken | PrTokenRefusal | null;
19
70
  /**
20
71
  * Full precedence chain. Pure except for the env/process reads and the
21
72
  * optional backend call — `gh` and the network are only touched when every
22
73
  * earlier step missed, so the common PAT/env path stays side-effect free.
74
+ *
75
+ * The backend leg NEVER short-circuits to a refusal: a typed error is
76
+ * remembered, the credential helper is still tried (it may hold a fine PAT
77
+ * while the App installation is merely missing the repo), and a helper hit
78
+ * carries the backend code in `note` so the caller can log why the fallback
79
+ * was used. Only when the helper also misses is the typed error surfaced.
23
80
  */
24
- export declare function resolvePrToken(opts: {
25
- remoteUrl: string;
26
- projectId?: string | null;
27
- apiKey?: string | null;
28
- apiBaseUrl?: string | null;
29
- }): Promise<ResolvedPrToken | null>;
81
+ export declare function resolvePrToken(opts: ResolvePrTokenOpts): Promise<ResolvePrTokenResult>;
82
+ /**
83
+ * User-facing PR refusal for a typed run-token error. Every branch ends with
84
+ * "The branch IS pushed" — the push already succeeded, so the refusal is
85
+ * never a data-loss scare, only a next step.
86
+ */
87
+ export declare function prRefusalLine(code: string, message: string): string;
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ghAuthToken = ghAuthToken;
4
+ exports.looksLikeGitHubToken = looksLikeGitHubToken;
5
+ exports.credentialFillToken = credentialFillToken;
4
6
  exports.backendRunToken = backendRunToken;
5
7
  exports.resolvePrToken = resolvePrToken;
8
+ exports.prRefusalLine = prRefusalLine;
6
9
  /**
7
10
  * PR/push token resolution for the task-run git pipeline (2026-10).
8
11
  *
@@ -18,11 +21,22 @@ exports.resolvePrToken = resolvePrToken;
18
21
  * 2. `GITHUB_TOKEN` / `GITEA_TOKEN` env — ambient machine credential;
19
22
  * 3. `gh auth token` — ambient GitHub CLI credential (best-effort);
20
23
  * 4. the backend run-token endpoint — the run caller's own App connection,
21
- * verified against the exact repo before it is handed out.
24
+ * verified against the exact repo before it is handed out;
25
+ * 5. `git credential fill` — the OS / manager-core credential helper
26
+ * (best-effort rescue when the backend refuses but the machine holds
27
+ * a usable credential of its own).
28
+ *
29
+ * A backend REPO_NOT_INSTALLED must NOT block the credential-helper rescue:
30
+ * the helper may hold a fine PAT while the App installation is merely
31
+ * missing the repo. So a typed backend refusal is REMEMBERED, the helper is
32
+ * still tried, and only when the helper also misses is the typed error
33
+ * surfaced (with a message that names the fix). Env/gh-cli/file hits still
34
+ * win immediately as today.
22
35
  *
23
36
  * An installation token is MEMORY-ONLY: it expires in ~1h, is repo-scoped,
24
37
  * and must never be written to `git-tokens.json` (that file is for durable
25
- * PATs). Callers use the returned token transiently and drop it.
38
+ * PATs). Callers use the returned token transiently and drop it. The helper
39
+ * password is likewise memory-only — only the source name is ever logged.
26
40
  */
27
41
  const node_child_process_1 = require("node:child_process");
28
42
  const shims_1 = require("../workspace/shims");
@@ -54,38 +68,137 @@ function ghAuthToken() {
54
68
  }
55
69
  }
56
70
  /**
57
- * Ask the hub for a one-shot token for this run (step 4). Returns null when
58
- * there is no project, no run identity, no App connection — every one of
59
- * those is a normal "not connected" state, never an error.
71
+ * GitHub-shaped token check for the credential-helper rescue.
72
+ *
73
+ * Some helpers answer `git credential fill` with a username or some other
74
+ * non-token secret; pushing with that burns the attempt on a 401 and the
75
+ * report then blames the wrong thing. So on a github.com host ONLY a
76
+ * GitHub token shape is accepted (classic `ghp_*` family + fine-grained
77
+ * `github_pat_*` + the legacy 40-hex PAT). Non-GitHub forges (Gitea accepts
78
+ * any non-empty password) take whatever the helper stored.
79
+ */
80
+ function looksLikeGitHubToken(value) {
81
+ const s = value.trim();
82
+ if (/^(ghp_|gho_|ghu_|ghs_|github_pat_)/.test(s))
83
+ return true;
84
+ if (/^[0-9a-f]{40}$/i.test(s))
85
+ return true;
86
+ return false;
87
+ }
88
+ /**
89
+ * Best-effort machine credential (step 5). Runs `git credential fill` for the
90
+ * remote's host and returns the filled password when it is usable. Returns
91
+ * null on every failure — absent git, timeout, non-zero exit, empty password,
92
+ * or a non-token-shaped secret on a github.com host. Never throws, never
93
+ * logs the secret (callers log only the source name).
94
+ *
95
+ * NOTE on where the password can come from: `fill` consults the configured
96
+ * helpers AND, when they miss, the prompt fallback (`GIT_ASKPASS` /
97
+ * `core.askPass`) — on a VS Code-integrated shell that fallback silently
98
+ * returns the editor's stored GitHub session. That is ordinary `fill`
99
+ * semantics and a session token is a fine credential (same trust boundary as
100
+ * manager-core), so it is accepted, not scrubbed. `GIT_TERMINAL_PROMPT=0`
101
+ * keeps a truly-interactive prompt from hanging the run past the timeout.
102
+ */
103
+ function credentialFillToken(remoteUrl) {
104
+ let host = '';
105
+ try {
106
+ host = new URL(remoteUrl).host.toLowerCase();
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ if (!host)
112
+ return null;
113
+ const github = host === 'github.com' || host.endsWith('.github.com');
114
+ let res;
115
+ try {
116
+ res = (0, node_child_process_1.spawnSync)('git', ['credential', 'fill'], {
117
+ input: `protocol=https\nhost=${host}\n\n`,
118
+ encoding: 'utf-8',
119
+ timeout: 10_000,
120
+ windowsHide: true,
121
+ env: { ...(0, shims_1.envWithoutShims)(), GIT_TERMINAL_PROMPT: '0' },
122
+ });
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ if (res.error || res.status !== 0)
128
+ return null;
129
+ const password = String(res.stdout ?? '')
130
+ .split(/\r?\n/)
131
+ .map((line) => line.trim())
132
+ .find((line) => line.startsWith('password='))
133
+ ?.slice('password='.length)
134
+ .trim() ?? '';
135
+ if (!password)
136
+ return null;
137
+ if (github && !looksLikeGitHubToken(password))
138
+ return null;
139
+ return { token: password, source: 'credential-helper' };
140
+ }
141
+ /**
142
+ * Ask the hub for a one-shot token for this run (step 4).
143
+ *
144
+ * Returns null ONLY when there is no run identity (no project, no key) — a
145
+ * normal "not connected" state, never an error. Every answered request
146
+ * resolves to either a token or a TYPED refusal carrying the backend's
147
+ * UPPER_SNAKE code and message (truncated to 300 chars); a request that never
148
+ * got an answer (network/timeout) is `RUN_TOKEN_UNREACHABLE`. Callers must
149
+ * NOT treat a typed refusal as "try nothing else" — see `resolvePrToken`.
60
150
  */
61
151
  async function backendRunToken(opts) {
62
- const { remoteUrl, projectId, apiKey, apiBaseUrl } = opts;
152
+ const { remoteUrl, projectId, apiKey, apiBaseUrl, taskId } = opts;
63
153
  if (!remoteUrl || !projectId || !apiKey)
64
154
  return null;
155
+ const body = { projectId, repoUrl: remoteUrl };
156
+ if (taskId)
157
+ body.taskId = taskId;
158
+ let res;
65
159
  try {
66
- const res = await fetch(`${(apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/+$/, '')}/git-connections/run-token`, {
160
+ res = await fetch(`${(apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/+$/, '')}/git-connections/run-token`, {
67
161
  method: 'POST',
68
162
  headers: {
69
163
  'Content-Type': 'application/json',
70
164
  'X-Api-Key': apiKey,
71
165
  },
72
- body: JSON.stringify({ projectId, repoUrl: remoteUrl }),
166
+ body: JSON.stringify(body),
73
167
  signal: AbortSignal.timeout(15_000),
74
168
  });
75
- const raw = (await res.json().catch(() => null));
76
- const token = raw?.data?.token;
77
- if (!res.ok || raw?.success !== true || !token)
78
- return null;
79
- return { token, source: 'github_app' };
80
169
  }
81
- catch {
82
- return null;
170
+ catch (err) {
171
+ const reason = err instanceof Error ? err.message : String(err);
172
+ return {
173
+ error: {
174
+ code: 'RUN_TOKEN_UNREACHABLE',
175
+ message: `run-token request failed (${reason})`.slice(0, 300),
176
+ },
177
+ };
83
178
  }
179
+ const raw = (await res.json().catch(() => null));
180
+ const token = raw?.data?.token;
181
+ if (res.ok && raw?.success === true && token) {
182
+ return { token, source: 'github_app' };
183
+ }
184
+ const code = typeof raw?.error?.code === 'string' && raw.error.code
185
+ ? raw.error.code
186
+ : 'GITHUB_APP_MINT_FAILED';
187
+ const message = (typeof raw?.error?.message === 'string' && raw.error.message
188
+ ? raw.error.message
189
+ : `run-token request failed (HTTP ${res.status})`).slice(0, 300);
190
+ return { error: { code, message } };
84
191
  }
85
192
  /**
86
193
  * Full precedence chain. Pure except for the env/process reads and the
87
194
  * optional backend call — `gh` and the network are only touched when every
88
195
  * earlier step missed, so the common PAT/env path stays side-effect free.
196
+ *
197
+ * The backend leg NEVER short-circuits to a refusal: a typed error is
198
+ * remembered, the credential helper is still tried (it may hold a fine PAT
199
+ * while the App installation is merely missing the repo), and a helper hit
200
+ * carries the backend code in `note` so the caller can log why the fallback
201
+ * was used. Only when the helper also misses is the typed error surfaced.
89
202
  */
90
203
  async function resolvePrToken(opts) {
91
204
  const local = (0, git_auth_1.tokenForUrl)(opts.remoteUrl);
@@ -97,6 +210,37 @@ async function resolvePrToken(opts) {
97
210
  const cli = ghAuthToken();
98
211
  if (cli)
99
212
  return { token: cli, source: 'gh-cli' };
100
- return backendRunToken(opts);
213
+ const backend = await backendRunToken(opts);
214
+ if (backend && 'token' in backend)
215
+ return backend;
216
+ const remembered = backend && 'error' in backend ? backend.error : null;
217
+ const helper = credentialFillToken(opts.remoteUrl);
218
+ if (helper) {
219
+ return remembered
220
+ ? { ...helper, note: `backend ${remembered.code}` }
221
+ : helper;
222
+ }
223
+ if (remembered)
224
+ return { error: remembered };
225
+ return null;
226
+ }
227
+ /**
228
+ * User-facing PR refusal for a typed run-token error. Every branch ends with
229
+ * "The branch IS pushed" — the push already succeeded, so the refusal is
230
+ * never a data-loss scare, only a next step.
231
+ */
232
+ function prRefusalLine(code, message) {
233
+ switch (code) {
234
+ case 'REPO_NOT_INSTALLED':
235
+ return 'PR: not opened — the GitHub installation does not include owner/repo. Add it under Settings → Connected accounts → Manage repositories (company: ask an admin to share or install), then re-run. The branch IS pushed.';
236
+ case 'GITHUB_APP_NOT_CONNECTED':
237
+ return 'PR: not opened — no GitHub installation is linked for this project (tried company + personal fallbacks). Link GitHub in Settings → Connected accounts, share it with the company, or add a project PAT; then re-run. The branch IS pushed.';
238
+ case 'GITHUB_APP_NOT_CONFIGURED':
239
+ case 'GITHUB_APP_MINT_FAILED':
240
+ case 'RUN_TOKEN_UNREACHABLE':
241
+ return `PR: not opened — ${message} (server-side; not something linking can fix). The branch IS pushed.`;
242
+ default:
243
+ return `PR: not opened — ${code}: ${message}. The branch IS pushed.`;
244
+ }
101
245
  }
102
246
  //# sourceMappingURL=pr-token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-token.js","sourceRoot":"","sources":["../../../src/lib/git/pr-token.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pr-token.js","sourceRoot":"","sources":["../../../src/lib/git/pr-token.ts"],"names":[],"mappings":"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"}
@@ -100,6 +100,24 @@ async function emitWithVision(emit, session, cmdId, payload, opts) {
100
100
  /* keep the pre-settle screen */
101
101
  }
102
102
  }
103
+ if (session?.connected && typeof session.detectChallenge === 'function') {
104
+ if (!payload.challenge) {
105
+ try {
106
+ const challenge = await session.detectChallenge();
107
+ if (challenge) {
108
+ payload.challenge = {
109
+ ...challenge,
110
+ attempted: false,
111
+ passed: false,
112
+ nextStep: challenge.kind === 'block' ? 'report_blocked' : 'ask_human',
113
+ };
114
+ }
115
+ }
116
+ catch {
117
+ /* a challenge probe never fails the action */
118
+ }
119
+ }
120
+ }
103
121
  if (session?.connected && session.qaVision === true) {
104
122
  try {
105
123
  const shot = await session.screenshot('jpeg', 62);
@@ -269,6 +287,8 @@ async function handleBrowserCommand(data, emit) {
269
287
  ? data.actor.trim().slice(0, 80)
270
288
  : null;
271
289
  const isolated = data.isolated === true;
290
+ // OPT-IN fingerprint reduction (2026-10): see BrowserSessionOptions.
291
+ const stealth = data.stealth === true;
272
292
  // A tagged window never changes hands.
273
293
  const tagged = manager.get(sessionId);
274
294
  if (tagged?.actor && callActor) {
@@ -293,12 +313,19 @@ async function handleBrowserCommand(data, emit) {
293
313
  existing.setAllowCredentials(allowCredentials);
294
314
  existing.setQaVision(qaVision);
295
315
  existing.setActor(callActor);
316
+ if (stealth)
317
+ await existing.enableStealth();
296
318
  // reset:true drops a crashed run's leftovers (open modal, corrupted
297
319
  // field values, stale validation errors) before the new run starts.
298
320
  // A resumed run omits it and keeps its page.
299
321
  let pageState;
300
322
  if (data.url) {
301
- pageState = await existing.navigate(String(data.url));
323
+ pageState = await existing.navigate(String(data.url), {
324
+ autoChallenge: data.autoChallenge !== false,
325
+ challengeTimeoutMs: typeof data.challengeTimeoutMs === 'number'
326
+ ? data.challengeTimeoutMs
327
+ : undefined,
328
+ });
302
329
  }
303
330
  else if (data.reset === true) {
304
331
  pageState = await existing.navigate('about:blank');
@@ -314,6 +341,7 @@ async function handleBrowserCommand(data, emit) {
314
341
  port: existing.port,
315
342
  ...(existing.actor ? { actor: existing.actor } : {}),
316
343
  qaVision: existing.qaVision,
344
+ stealth: existing.stealth,
317
345
  ...pageState,
318
346
  ...(screen ? { screen } : {}),
319
347
  });
@@ -357,6 +385,7 @@ async function handleBrowserCommand(data, emit) {
357
385
  profileDir: profile.dir,
358
386
  allowCredentials,
359
387
  qaVision,
388
+ stealth,
360
389
  ...(callActor ? { actor: callActor } : {}),
361
390
  });
362
391
  // A reused session keeps its flag from a previous connect; honour the
@@ -369,7 +398,12 @@ async function handleBrowserCommand(data, emit) {
369
398
  log.info(`Browser session "${sessionId}" on port ${actualPort} ` +
370
399
  `(${profile.shared ? 'shared debug profile' : `isolated profile ${profile.dir}`})`);
371
400
  const pageState = data.url
372
- ? await session.navigate(String(data.url))
401
+ ? await session.navigate(String(data.url), {
402
+ autoChallenge: data.autoChallenge !== false,
403
+ challengeTimeoutMs: typeof data.challengeTimeoutMs === 'number'
404
+ ? data.challengeTimeoutMs
405
+ : undefined,
406
+ })
373
407
  : info;
374
408
  const screen = await screenOf(session);
375
409
  emit('orchestrator:browser-result', data.cmdId, {
@@ -379,6 +413,7 @@ async function handleBrowserCommand(data, emit) {
379
413
  profile: profile.shared ? 'shared' : 'isolated',
380
414
  ...(callActor ? { actor: callActor } : {}),
381
415
  qaVision: session.qaVision,
416
+ stealth: session.stealth,
382
417
  ...pageState,
383
418
  ...(screen ? { screen } : {}),
384
419
  });
@@ -430,7 +465,12 @@ async function handleBrowserCommand(data, emit) {
430
465
  .then((info) => info.url ?? null)
431
466
  .catch(() => null);
432
467
  const navSince = Date.now();
433
- const info = await session.navigate(url);
468
+ const info = await session.navigate(url, {
469
+ autoChallenge: data.autoChallenge !== false,
470
+ challengeTimeoutMs: typeof data.challengeTimeoutMs === 'number'
471
+ ? data.challengeTimeoutMs
472
+ : undefined,
473
+ });
434
474
  const screen = await screenOf(session);
435
475
  await emitWithVision(emit, session, data.cmdId, {
436
476
  success: true,
@@ -1093,9 +1133,22 @@ async function handleBrowserCommand(data, emit) {
1093
1133
  : {}),
1094
1134
  ...(data.refs === true ? { refs: true } : {}),
1095
1135
  });
1136
+ const challenge = data.includeChallenge === false
1137
+ ? null
1138
+ : await session.detectChallenge().catch(() => null);
1096
1139
  const payload = {
1097
1140
  success: true,
1098
1141
  ...state,
1142
+ ...(challenge
1143
+ ? {
1144
+ challenge: {
1145
+ ...challenge,
1146
+ attempted: false,
1147
+ passed: false,
1148
+ nextStep: challenge.kind === 'block' ? 'report_blocked' : 'ask_human',
1149
+ },
1150
+ }
1151
+ : {}),
1099
1152
  };
1100
1153
  const wantsShot = data.screenshot === true ||
1101
1154
  (session.qaVision && data.screenshot !== false);
@@ -1174,6 +1227,42 @@ async function handleBrowserCommand(data, emit) {
1174
1227
  });
1175
1228
  break;
1176
1229
  }
1230
+ // 2026-10: bounded Cloudflare/CAPTCHA resolution. `attempt` defaults to
1231
+ // none here - navigate already makes the one allowed click; this action
1232
+ // is for a resume after a human answer, so it only polls.
1233
+ case 'waitForChallenge': {
1234
+ const session = manager.get(sessionId);
1235
+ if (!session?.connected) {
1236
+ emit('orchestrator:browser-result', data.cmdId, {
1237
+ success: false,
1238
+ error: `No active browser session "${sessionId}". ` +
1239
+ `Call browser_open({ sessionId: "${sessionId}" }) first.`,
1240
+ });
1241
+ return;
1242
+ }
1243
+ const result = await session.waitForChallenge({
1244
+ attempt: data.attempt === true,
1245
+ timeoutMs: typeof data.timeoutMs === 'number'
1246
+ ? data.timeoutMs
1247
+ : typeof data.challengeTimeoutMs === 'number'
1248
+ ? data.challengeTimeoutMs
1249
+ : undefined,
1250
+ });
1251
+ const screen = await screenOf(session);
1252
+ await emitWithVision(emit, session, data.cmdId, {
1253
+ success: true,
1254
+ ...result,
1255
+ ...(screen ? { screen } : {}),
1256
+ ...(result.passed
1257
+ ? {}
1258
+ : {
1259
+ nextStep: result.challenge?.kind === 'block'
1260
+ ? 'report_blocked'
1261
+ : 'ask_human',
1262
+ }),
1263
+ }, { settle: false });
1264
+ break;
1265
+ }
1177
1266
  // 2026-10: recent toast / alert / dialog events. A toast that disappeared
1178
1267
  // before the next tool call is still in the page's ring buffer.
1179
1268
  case 'events': {