conductor-remote 1.55.1 → 1.56.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.
package/dist/index.html CHANGED
@@ -13,8 +13,8 @@
13
13
  <title>Conductor Remote</title>
14
14
  <!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
15
15
  <script src="/self-heal.js"></script>
16
- <script type="module" crossorigin src="/assets/index-DVIyfOd7.js"></script>
17
- <link rel="stylesheet" crossorigin href="/assets/index-DJ35BMTb.css">
16
+ <script type="module" crossorigin src="/assets/index-CxgN1roC.js"></script>
17
+ <link rel="stylesheet" crossorigin href="/assets/index-C2BviSOi.css">
18
18
  <link rel="manifest" href="/manifest.webmanifest"></head>
19
19
  <body>
20
20
  <div id="root"></div>
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const c=e=>i(e,o),t={module:{uri:o},exports:l,require:c};s[o]=Promise.all(n.map(e=>t[e]||c(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"33aaf20ef5c7f33c262674c4a3e3e838"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DVIyfOd7.js",revision:null},{url:"assets/index-DJ35BMTb.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
1
+ if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"45b48aad06a99f1da7ee2c21dbaf2c2e"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CxgN1roC.js",revision:null},{url:"assets/index-C2BviSOi.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
@@ -1,46 +1,5 @@
1
1
  import path from 'node:path';
2
- const SOURCE_EXTENSIONS = new Set([
3
- '.bash',
4
- '.c',
5
- '.cc',
6
- '.cpp',
7
- '.css',
8
- '.go',
9
- '.h',
10
- '.hpp',
11
- '.html',
12
- '.java',
13
- '.js',
14
- '.json',
15
- '.jsx',
16
- '.md',
17
- '.mjs',
18
- '.mts',
19
- '.php',
20
- '.py',
21
- '.rb',
22
- '.rs',
23
- '.scss',
24
- '.sh',
25
- '.sql',
26
- '.svg',
27
- '.swift',
28
- '.toml',
29
- '.ts',
30
- '.tsx',
31
- '.txt',
32
- '.yaml',
33
- '.yml'
34
- ]);
35
- function sourceExtension(filePath) {
36
- const name = filePath.slice(filePath.lastIndexOf('/') + 1);
37
- const dot = name.lastIndexOf('.');
38
- return dot === -1 ? '' : name.slice(dot).toLowerCase();
39
- }
40
- /** True when a file is a text source format this viewer is allowed to expose. */
41
- export function isPreviewableSource(filePath) {
42
- return SOURCE_EXTENSIONS.has(sourceExtension(filePath));
43
- }
2
+ import { isPreviewableSource } from "./shared.js";
44
3
  /** True only for a descendant. A prefix test would let `/workspaces-old` escape `/workspaces`. */
45
4
  function insideDirectory(filePath, root) {
46
5
  const relative = path.relative(root, filePath);
@@ -54,8 +13,11 @@ function insideDirectory(filePath, root) {
54
13
  * Call this only with real paths. That ensures a symlink cannot point outside
55
14
  * an allowed directory after this check.
56
15
  */
57
- export function isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, exposeMode) {
58
- return insideDirectory(filePath, workspaceRoot) || (exposeMode === 'tailnet' && insideDirectory(filePath, homeRoot));
16
+ export function isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, exposeMode, bundledSkillsRoot = null) {
17
+ return (insideDirectory(filePath, workspaceRoot) ||
18
+ (exposeMode === 'tailnet' &&
19
+ (insideDirectory(filePath, homeRoot) ||
20
+ (bundledSkillsRoot !== null && insideDirectory(filePath, bundledSkillsRoot)))));
59
21
  }
60
22
  /**
61
23
  * Parse a source link without treating ordinary PWA routes as file references.
@@ -37,7 +37,7 @@ export function attachPrStatus(workspaces) {
37
37
  else {
38
38
  const c = w.worktree ? conflictCache.get(w.worktree) : undefined;
39
39
  // Optimistically green until the local merge check says otherwise (self-corrects within CONFLICT_TTL).
40
- w.pr_status = c?.conflicts ? 'conflicts' : 'mergeable';
40
+ w.pr_status = c?.conflicts ? 'conflicts' : hasFailedChecks(pr.statusCheckRollup) ? 'checks_failed' : 'mergeable';
41
41
  if (w.worktree && (!c || now - c.at > CONFLICT_TTL))
42
42
  refreshConflict(w.worktree, w.baseBranch);
43
43
  }
@@ -61,7 +61,16 @@ function refreshRepo(root) {
61
61
  /** One `gh` call per repo → its PRs keyed by head branch. Never rejects. */
62
62
  async function fetchRepoPRs(root) {
63
63
  try {
64
- const { stdout } = await exec('gh', ['pr', 'list', '--state', 'all', '--limit', '100', '--json', 'headRefName,number,url,state,isDraft,updatedAt'], { cwd: root, encoding: 'utf8', timeout: 15_000 });
64
+ const { stdout } = await exec('gh', [
65
+ 'pr',
66
+ 'list',
67
+ '--state',
68
+ 'all',
69
+ '--limit',
70
+ '100',
71
+ '--json',
72
+ 'headRefName,number,url,state,isDraft,updatedAt,statusCheckRollup'
73
+ ], { cwd: root, encoding: 'utf8', timeout: 15_000 });
65
74
  const list = JSON.parse(stdout);
66
75
  // Oldest first so the newest PR wins when a branch has been reused across PRs.
67
76
  list.sort((a, b) => a.updatedAt.localeCompare(b.updatedAt));
@@ -74,6 +83,23 @@ async function fetchRepoPRs(root) {
74
83
  return null;
75
84
  }
76
85
  }
86
+ const FAILED_CHECK_CONCLUSIONS = new Set([
87
+ 'ACTION_REQUIRED',
88
+ 'CANCELLED',
89
+ 'FAILURE',
90
+ 'STALE',
91
+ 'STARTUP_FAILURE',
92
+ 'TIMED_OUT'
93
+ ]);
94
+ const FAILED_CHECK_STATES = new Set(['ERROR', 'FAILURE']);
95
+ /**
96
+ * Check runs carry their terminal result in `conclusion`; legacy status contexts
97
+ * use `state`. Pending checks remain green because this marker calls out failures.
98
+ */
99
+ export function hasFailedChecks(checks) {
100
+ return !!checks?.some(check => (check.conclusion && FAILED_CHECK_CONCLUSIONS.has(check.conclusion)) ||
101
+ (check.state && FAILED_CHECK_STATES.has(check.state)));
102
+ }
77
103
  function refreshConflict(worktree, base) {
78
104
  if (conflictInflight.has(worktree))
79
105
  return;
@@ -468,6 +468,7 @@ async function serveLocalImage(req, res, requestedPath) {
468
468
  const FILE_PREVIEW_MAX_BYTES = 512 * 1024;
469
469
  const FILE_PREVIEW_CONTEXT_LINES = 100;
470
470
  const FILE_PREVIEW_FIRST_LINES = 500;
471
+ const BUNDLED_SKILLS_ROOT = '/Applications/Conductor.app/Contents/Resources/conductor-skill/skills';
471
472
  /**
472
473
  * Serve source that an agent linked in its Markdown. The link format comes from
473
474
  * coding-agent file references, but its path still arrives from a remote client.
@@ -481,15 +482,17 @@ async function serveFilePreview(req, res, reference) {
481
482
  let filePath;
482
483
  let workspaceRoot;
483
484
  let homeRoot;
485
+ let bundledSkillsRoot;
484
486
  let size;
485
487
  try {
486
488
  ;
487
- [filePath, workspaceRoot, homeRoot] = await Promise.all([
489
+ [filePath, workspaceRoot, homeRoot, bundledSkillsRoot] = await Promise.all([
488
490
  fs.promises.realpath(target.path),
489
491
  fs.promises.realpath(cfg.workspacesRoot),
490
- fs.promises.realpath(os.homedir())
492
+ fs.promises.realpath(os.homedir()),
493
+ fs.promises.realpath(BUNDLED_SKILLS_ROOT).catch(() => null)
491
494
  ]);
492
- if (!isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, readExposeMode())) {
495
+ if (!isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, readExposeMode(), bundledSkillsRoot)) {
493
496
  return json(req, res, 404, { error: 'source file not found' });
494
497
  }
495
498
  const info = await fs.promises.stat(filePath);
@@ -162,3 +162,43 @@ export function attachmentTokens(text) {
162
162
  }
163
163
  return tokens;
164
164
  }
165
+ /** Text source formats the relay may return to the phone's source preview. */
166
+ const SOURCE_EXTENSIONS = new Set([
167
+ '.bash',
168
+ '.c',
169
+ '.cc',
170
+ '.cpp',
171
+ '.css',
172
+ '.go',
173
+ '.h',
174
+ '.hpp',
175
+ '.html',
176
+ '.java',
177
+ '.js',
178
+ '.json',
179
+ '.jsx',
180
+ '.md',
181
+ '.mjs',
182
+ '.mts',
183
+ '.php',
184
+ '.py',
185
+ '.rb',
186
+ '.rs',
187
+ '.scss',
188
+ '.sh',
189
+ '.sql',
190
+ '.svg',
191
+ '.swift',
192
+ '.toml',
193
+ '.ts',
194
+ '.tsx',
195
+ '.txt',
196
+ '.yaml',
197
+ '.yml'
198
+ ]);
199
+ /** True for a file extension the relay's source preview accepts. */
200
+ export function isPreviewableSource(filePath) {
201
+ const name = filePath.slice(filePath.lastIndexOf('/') + 1);
202
+ const dot = name.lastIndexOf('.');
203
+ return dot !== -1 && SOURCE_EXTENSIONS.has(name.slice(dot).toLowerCase());
204
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.55.1",
3
+ "version": "1.56.1",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",
@@ -50,7 +50,7 @@
50
50
  "typecheck": "tsc -p tsconfig.json",
51
51
  "lint": "biome check .",
52
52
  "fix": "biome check . --fix",
53
- "verify": "yarn typecheck && yarn lint && yarn check:imports && yarn check:routes && yarn check:attachments && yarn check:applescript && yarn check:config && yarn check:utf8-target && yarn check:file-links && yarn check:model-cache && yarn check:model-labels && yarn check:nosleep && yarn check:uilock && yarn check:firstprompt && yarn check:sendonce && yarn check:notify && yarn check:online",
53
+ "verify": "yarn typecheck && yarn lint && yarn check:pr && yarn check:imports && yarn check:routes && yarn check:attachments && yarn check:applescript && yarn check:config && yarn check:utf8-target && yarn check:file-links && yarn check:model-cache && yarn check:model-labels && yarn check:transcript-actions && yarn check:nosleep && yarn check:uilock && yarn check:firstprompt && yarn check:sendonce && yarn check:notify && yarn check:online",
54
54
  "release": "semantic-release",
55
55
  "prepack": "yarn build && yarn build:node",
56
56
  "postinstall": "husky || true",
@@ -62,9 +62,11 @@
62
62
  "check:firstprompt": "node scripts/check-firstprompt.ts",
63
63
  "check:model-cache": "node scripts/check-model-cache.ts",
64
64
  "check:model-labels": "node scripts/check-model-labels.ts",
65
+ "check:transcript-actions": "node scripts/check-transcript-actions.ts",
65
66
  "check:sendonce": "node scripts/check-sendonce.ts",
66
67
  "check:notify": "node scripts/check-notify.ts",
67
68
  "check:online": "node scripts/check-online.ts",
69
+ "check:pr": "node scripts/check-pr.ts",
68
70
  "check:imports": "node scripts/check-imports.ts",
69
71
  "check:nosleep": "node scripts/check-nosleep.ts",
70
72
  "check:routes": "node scripts/check-routes.ts",