@detergent-software/conductor 3.0.0-dev.3 → 3.0.0-dev.30

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 (171) hide show
  1. package/CHANGELOG.md +241 -0
  2. package/README.md +201 -63
  3. package/build/brief-cli.js +1 -1
  4. package/build/brief-cli.js.map +1 -1
  5. package/build/broker-cli.js +24 -25
  6. package/build/broker-cli.js.map +1 -1
  7. package/build/broker.js +66 -92
  8. package/build/broker.js.map +1 -1
  9. package/build/claude.js +50 -7
  10. package/build/claude.js.map +1 -1
  11. package/build/cli.js +143 -63
  12. package/build/cli.js.map +1 -1
  13. package/build/clones.js +92 -0
  14. package/build/clones.js.map +1 -0
  15. package/build/code-host-cli.js +116 -32
  16. package/build/code-host-cli.js.map +1 -1
  17. package/build/code-host-service.js +8 -0
  18. package/build/code-host-service.js.map +1 -1
  19. package/build/code-host.js +22 -11
  20. package/build/code-host.js.map +1 -1
  21. package/build/config.js +58 -14
  22. package/build/config.js.map +1 -1
  23. package/build/console.js +40 -24
  24. package/build/console.js.map +1 -1
  25. package/build/credentials.js +42 -29
  26. package/build/credentials.js.map +1 -1
  27. package/build/daemon.js +38 -22
  28. package/build/daemon.js.map +1 -1
  29. package/build/help.js +179 -42
  30. package/build/help.js.map +1 -1
  31. package/build/history.js +4 -2
  32. package/build/history.js.map +1 -1
  33. package/build/host/credential-manager.js +109 -0
  34. package/build/host/credential-manager.js.map +1 -0
  35. package/build/host/keychain.js +44 -0
  36. package/build/host/keychain.js.map +1 -0
  37. package/build/host/launchd.js +118 -0
  38. package/build/host/launchd.js.map +1 -0
  39. package/build/host/libsecret.js +40 -0
  40. package/build/host/libsecret.js.map +1 -0
  41. package/build/host/macos-terminal.js +63 -0
  42. package/build/host/macos-terminal.js.map +1 -0
  43. package/build/host/platform.js +4 -0
  44. package/build/host/platform.js.map +1 -0
  45. package/build/host/secrets.js +46 -0
  46. package/build/host/secrets.js.map +1 -0
  47. package/build/host/shell.js +26 -0
  48. package/build/host/shell.js.map +1 -0
  49. package/build/host/startup.js +26 -0
  50. package/build/host/startup.js.map +1 -0
  51. package/build/host/systemd.js +91 -0
  52. package/build/host/systemd.js.map +1 -0
  53. package/build/host/task-scheduler.js +78 -0
  54. package/build/host/task-scheduler.js.map +1 -0
  55. package/build/host/terminal.js +46 -0
  56. package/build/host/terminal.js.map +1 -0
  57. package/build/host/tmux.js +39 -0
  58. package/build/host/tmux.js.map +1 -0
  59. package/build/host/windows-terminal.js +46 -0
  60. package/build/host/windows-terminal.js.map +1 -0
  61. package/build/in-process.js +6 -5
  62. package/build/in-process.js.map +1 -1
  63. package/build/inbox.js +63 -17
  64. package/build/inbox.js.map +1 -1
  65. package/build/init.js +508 -0
  66. package/build/init.js.map +1 -0
  67. package/build/landing.js +86 -7
  68. package/build/landing.js.map +1 -1
  69. package/build/lib/providers/ado/code-host.js +5 -1
  70. package/build/lib/providers/ado/code-host.js.map +1 -1
  71. package/build/lib/providers/ado/pipelines.js +130 -22
  72. package/build/lib/providers/ado/pipelines.js.map +1 -1
  73. package/build/lib/providers/ado/trigger.js +5 -0
  74. package/build/lib/providers/ado/trigger.js.map +1 -1
  75. package/build/lib/providers/ado/writes.js +10 -1
  76. package/build/lib/providers/ado/writes.js.map +1 -1
  77. package/build/lib/providers/github/actions.js +90 -3
  78. package/build/lib/providers/github/actions.js.map +1 -1
  79. package/build/lib/providers/github/code-host.js +2 -0
  80. package/build/lib/providers/github/code-host.js.map +1 -1
  81. package/build/lib/providers/github/writes.js +24 -2
  82. package/build/lib/providers/github/writes.js.map +1 -1
  83. package/build/lib/providers/index.js +4 -3
  84. package/build/lib/providers/index.js.map +1 -1
  85. package/build/lib/providers/npm/stage.js +28 -5
  86. package/build/lib/providers/npm/stage.js.map +1 -1
  87. package/build/lib/providers/timing.js +10 -0
  88. package/build/lib/providers/timing.js.map +1 -0
  89. package/build/lib/providers/verdicts/pr.js +47 -6
  90. package/build/lib/providers/verdicts/pr.js.map +1 -1
  91. package/build/lib/providers/verdicts/runs.js +12 -2
  92. package/build/lib/providers/verdicts/runs.js.map +1 -1
  93. package/build/local-files.js +95 -0
  94. package/build/local-files.js.map +1 -0
  95. package/build/manager.js +171 -47
  96. package/build/manager.js.map +1 -1
  97. package/build/note.js +1 -1
  98. package/build/note.js.map +1 -1
  99. package/build/notify.js +101 -39
  100. package/build/notify.js.map +1 -1
  101. package/build/npm-reads.js +323 -0
  102. package/build/npm-reads.js.map +1 -0
  103. package/build/paths.js +44 -1
  104. package/build/paths.js.map +1 -1
  105. package/build/plugin.js +10 -0
  106. package/build/plugin.js.map +1 -1
  107. package/build/profile-fields.js +149 -0
  108. package/build/profile-fields.js.map +1 -0
  109. package/build/profile-layers.js +264 -0
  110. package/build/profile-layers.js.map +1 -0
  111. package/build/profile.js +70 -44
  112. package/build/profile.js.map +1 -1
  113. package/build/project-check-cli.js +18 -10
  114. package/build/project-check-cli.js.map +1 -1
  115. package/build/project-check.js +187 -98
  116. package/build/project-check.js.map +1 -1
  117. package/build/pushover.js +5 -5
  118. package/build/pushover.js.map +1 -1
  119. package/build/registry.js +195 -36
  120. package/build/registry.js.map +1 -1
  121. package/build/release-kinds.js +34 -0
  122. package/build/release-kinds.js.map +1 -0
  123. package/build/rule-actions.js +4 -2
  124. package/build/rule-actions.js.map +1 -1
  125. package/build/rules-service.js +101 -44
  126. package/build/rules-service.js.map +1 -1
  127. package/build/rules.js +15 -15
  128. package/build/rules.js.map +1 -1
  129. package/build/run.js +20 -7
  130. package/build/run.js.map +1 -1
  131. package/build/runs.js +200 -45
  132. package/build/runs.js.map +1 -1
  133. package/build/scheduler.js +11 -1
  134. package/build/scheduler.js.map +1 -1
  135. package/build/server.js +6 -25
  136. package/build/server.js.map +1 -1
  137. package/build/spend.js +4 -0
  138. package/build/spend.js.map +1 -1
  139. package/build/store.js +82 -25
  140. package/build/store.js.map +1 -1
  141. package/build/timeline.js +104 -0
  142. package/build/timeline.js.map +1 -0
  143. package/build/tracker-reads-cli.js +4 -5
  144. package/build/tracker-reads-cli.js.map +1 -1
  145. package/build/train-cli.js +6 -5
  146. package/build/train-cli.js.map +1 -1
  147. package/build/upgrade.js +8 -9
  148. package/build/upgrade.js.map +1 -1
  149. package/build/wait.js +6 -6
  150. package/build/wait.js.map +1 -1
  151. package/build/watchers/ado.js +11 -3
  152. package/build/watchers/ado.js.map +1 -1
  153. package/build/watchers/github.js +15 -4
  154. package/build/watchers/github.js.map +1 -1
  155. package/build/watchers/npm.js +6 -4
  156. package/build/watchers/npm.js.map +1 -1
  157. package/build/webhook.js +80 -0
  158. package/build/webhook.js.map +1 -0
  159. package/build/worktrees.js +199 -92
  160. package/build/worktrees.js.map +1 -1
  161. package/package.json +61 -61
  162. package/plugin/skills/conductor-console/SKILL.md +4 -4
  163. package/plugin/skills/work-item/SKILL.md +13 -11
  164. package/build/checkout-sync.js +0 -249
  165. package/build/checkout-sync.js.map +0 -1
  166. package/build/credential.js +0 -66
  167. package/build/credential.js.map +0 -1
  168. package/build/startup.js +0 -92
  169. package/build/startup.js.map +0 -1
  170. package/build/tab.js +0 -32
  171. package/build/tab.js.map +0 -1
@@ -0,0 +1,63 @@
1
+ import { outputOf } from "../run.js";
2
+ import { shLine, shQuote, viaSh } from "./shell.js";
3
+ // A string as AppleScript reads it, in double quotes.
4
+ export function appleString(text) {
5
+ return `"${text.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
6
+ }
7
+ // Terminal.app: a new window per tab (Terminal has no named windows to add a tab to), titled.
8
+ export function appleTerminal(run) {
9
+ return macTerminal(run, 'Terminal', (tab) => [
10
+ 'tell application "Terminal"',
11
+ 'activate',
12
+ `set t to do script ${appleString(macLine(tab))}`,
13
+ `set custom title of t to ${appleString(cleanTitle(tab.title))}`,
14
+ 'end tell',
15
+ ]);
16
+ }
17
+ // iTerm: a tab in its front window, or a new window when none is open, named after the tab. An
18
+ // iTerm that wasn't running opens a window of its own as it starts: the tab takes that window's
19
+ // session instead of adding one beside it.
20
+ export function iterm(run) {
21
+ return macTerminal(run, 'iTerm', (tab) => [
22
+ 'set wasRunning to application "iTerm" is running',
23
+ 'tell application "iTerm"',
24
+ 'activate',
25
+ 'if (count of windows) is 0 then',
26
+ 'set w to (create window with default profile)',
27
+ 'else',
28
+ 'set w to current window',
29
+ 'if wasRunning then tell w to create tab with default profile',
30
+ 'end if',
31
+ 'tell current session of w',
32
+ `set name to ${appleString(cleanTitle(tab.title))}`,
33
+ `write text ${appleString(macLine(tab))}`,
34
+ 'end tell',
35
+ 'end tell',
36
+ ]);
37
+ }
38
+ // What a macOS tab types into its shell: the folder and the command through sh, then `exit`, so the
39
+ // shell ends with the command as a Windows Terminal or tmux tab does (Terminal closes the window
40
+ // then if its profile says to). The shell is the user's login shell, in its own environment, so
41
+ // `env` goes on the line.
42
+ export function macLine(tab) {
43
+ return `${viaSh(`cd ${shQuote(tab.cwd)} && ${shLine(tab)}`)}; exit`;
44
+ }
45
+ function cleanTitle(title) {
46
+ return title.replace(/\s+/g, ' ').trim().slice(0, 80);
47
+ }
48
+ // Runs the AppleScript, one `-e` per line: osascript takes it as arguments, never through a shell.
49
+ function macTerminal(run, name, script) {
50
+ return {
51
+ attachHint: () => null,
52
+ name,
53
+ open: async (tab) => {
54
+ const result = await run('osascript', script(tab).flatMap((line) => ['-e', line]), { timeoutMs: 60_000 });
55
+ if (result.code === 0)
56
+ return null;
57
+ return result.error !== null && /ENOENT/.test(result.error)
58
+ ? 'no osascript to open it with'
59
+ : outputOf(result, 500);
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=macos-terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos-terminal.js","sourceRoot":"","sources":["../../src/host/macos-terminal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEpD,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AACjE,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,GAAQ;IACpC,OAAO,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;QAC3C,6BAA6B;QAC7B,UAAU;QACV,sBAAsB,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE;QACjD,4BAA4B,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE;QAChE,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED,+FAA+F;AAC/F,gGAAgG;AAChG,2CAA2C;AAC3C,MAAM,UAAU,KAAK,CAAC,GAAQ;IAC5B,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;QACxC,kDAAkD;QAClD,0BAA0B;QAC1B,UAAU;QACV,iCAAiC;QACjC,+CAA+C;QAC/C,MAAM;QACN,yBAAyB;QACzB,8DAA8D;QAC9D,QAAQ;QACR,2BAA2B;QAC3B,eAAe,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE;QACnD,cAAc,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE;QACzC,UAAU;QACV,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED,oGAAoG;AACpG,iGAAiG;AACjG,gGAAgG;AAChG,0BAA0B;AAC1B,MAAM,UAAU,OAAO,CAAC,GAAyD;IAC/E,OAAO,GAAG,KAAK,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;AACtE,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,mGAAmG;AACnG,SAAS,WAAW,CAAC,GAAQ,EAAE,IAAY,EAAE,MAAqC;IAChF,OAAO;QACL,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI;QACtB,IAAI;QACJ,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAClB,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,WAAW,EACX,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAC3C,EAAE,SAAS,EAAE,MAAM,EAAE,CACtB,CAAC;YACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACnC,OAAO,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACzD,CAAC,CAAC,8BAA8B;gBAChC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export function hostPlatform(platform = process.platform) {
2
+ return platform === 'win32' || platform === 'darwin' ? platform : 'linux';
3
+ }
4
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/host/platform.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,YAAY,CAAC,WAA4B,OAAO,CAAC,QAAQ;IACvE,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { run as runCommand } from "../run.js";
2
+ import { credentialManager } from "./credential-manager.js";
3
+ import { keychain } from "./keychain.js";
4
+ import { libsecret } from "./libsecret.js";
5
+ import { hostPlatform } from "./platform.js";
6
+ // The env var that stands in for the credential `target` where the store has none, or can't be
7
+ // read (a headless Linux box with no keyring): CONDUCTOR_SECRET_ and the target in upper snake
8
+ // case, with its user name, when one is needed, in the same name plus _USER.
9
+ export function secretEnvName(target) {
10
+ return `CONDUCTOR_SECRET_${target
11
+ .toUpperCase()
12
+ .replace(/[^A-Z0-9]+/g, '_')
13
+ .replace(/^_+|_+$/g, '')}`;
14
+ }
15
+ // This platform's store, with the env fallback.
16
+ export function secretStore({ platform = hostPlatform(), run = runCommand, } = {}) {
17
+ const stores = {
18
+ darwin: () => keychain(run),
19
+ linux: () => libsecret(run),
20
+ win32: () => credentialManager(run),
21
+ };
22
+ return withEnvFallback(stores[platform]());
23
+ }
24
+ // The store, else secretEnvName(target) from the environment.
25
+ export function withEnvFallback(store) {
26
+ const fromEnv = (target, env) => {
27
+ const name = secretEnvName(target);
28
+ const secret = env[name]?.trim();
29
+ return secret ? { secret, user: env[`${name}_USER`]?.trim() ?? '' } : null;
30
+ };
31
+ return {
32
+ name: store.name,
33
+ read: async (target, env) => {
34
+ const got = await store.read(target, env);
35
+ if (got.found)
36
+ return got;
37
+ const credential = fromEnv(target, env);
38
+ return credential
39
+ ? { credential, found: true }
40
+ : { ...got, reason: `${got.reason}, and no ${secretEnvName(target)}` };
41
+ },
42
+ saveCommand: (target, user) => store.saveCommand(target, user),
43
+ saved: async (target, env) => (fromEnv(target, env) ? 'saved' : store.saved(target, env)),
44
+ };
45
+ }
46
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/host/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,UAAU,EAAmC,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAqB,YAAY,EAAE,MAAM,eAAe,CAAC;AA8BhE,+FAA+F;AAC/F,+FAA+F;AAC/F,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,oBAAoB,MAAM;SAC9B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,WAAW,CAAC,EAC1B,QAAQ,GAAG,YAAY,EAAE,EACzB,GAAG,GAAG,UAAU,MAC0B,EAAE;IAC5C,MAAM,MAAM,GAA4C;QACtD,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC3B,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC;QAC3B,KAAK,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC;KACpC,CAAC;IACF,OAAO,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,GAAsB,EAAqB,EAAE;QAC5E,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAC1C,IAAI,GAAG,CAAC,KAAK;gBAAE,OAAO,GAAG,CAAC;YAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACxC,OAAO,UAAU;gBACf,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE;gBAC7B,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,YAAY,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAC3E,CAAC;QACD,WAAW,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC;QAC9D,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC1F,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { win32 } from 'node:path';
2
+ // What a held tab says when its command fails, such as "claude attach failed, press Enter to close
3
+ // this tab". win32's basename splits on both / and \, so a Windows path reads the same on any host.
4
+ export function failedText(command) {
5
+ const [program = '', verb] = command;
6
+ const name = win32.basename(program).replace(/\.(exe|cmd)$/i, '');
7
+ return `${verb === undefined ? name : `${name} ${verb}`} failed, press Enter to close this tab`;
8
+ }
9
+ // The tab's command as one sh line: its env first, and with `hold`, a failure that waits for Enter
10
+ // before the tab closes.
11
+ export function shLine(tab) {
12
+ const env = Object.entries(tab.env).map(([name, value]) => `${name}=${shQuote(value)}`);
13
+ const line = [...(env.length > 0 ? ['env', ...env] : []), ...tab.command.map(shQuote)].join(' ');
14
+ return tab.hold ? `${line} || { echo ${shQuote(failedText(tab.command))}; read _; }` : line;
15
+ }
16
+ // A word as sh reads it: bare when it can't be taken for anything else, else in single quotes.
17
+ export function shQuote(word) {
18
+ return /^[\w%+,./:=@-]+$/.test(word) ? word : `'${word.replace(/'/g, `'\\''`)}'`;
19
+ }
20
+ // A line run through sh, as one word for the shell that runs it. tmux's default-shell and a macOS
21
+ // login shell are the user's, which may not be sh (fish has no `{ …; }`); `sh -c '…'` reads the same
22
+ // in each.
23
+ export function viaSh(line) {
24
+ return `sh -c ${shQuote(line)}`;
25
+ }
26
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/host/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAIlC,mGAAmG;AACnG,oGAAoG;AACpG,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,MAAM,CAAC,OAAO,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,wCAAwC,CAAC;AAClG,CAAC;AAED,mGAAmG;AACnG,yBAAyB;AACzB,MAAM,UAAU,MAAM,CAAC,GAAiD;IACtE,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjG,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,cAAc,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9F,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACnF,CAAC;AAED,kGAAkG;AAClG,qGAAqG;AACrG,WAAW;AACX,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,SAAS,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { homedir, userInfo } from 'node:os';
2
+ import { run as runCommand } from "../run.js";
3
+ import { launchd } from "./launchd.js";
4
+ import { hostPlatform } from "./platform.js";
5
+ import {} from "./secrets.js";
6
+ import { systemd } from "./systemd.js";
7
+ import { taskScheduler } from "./task-scheduler.js";
8
+ // This platform's startup.
9
+ export function startup({ env = process.env, home = homedir(), platform = hostPlatform(), run = runCommand, uid = process.getuid?.() ?? 0, user = userName(), } = {}) {
10
+ const startups = {
11
+ darwin: () => launchd({ home, run, uid }),
12
+ linux: () => systemd({ env, home, run, user }),
13
+ win32: () => taskScheduler(run),
14
+ };
15
+ return startups[platform]();
16
+ }
17
+ // userInfo throws for a uid with no passwd entry, such as a container's.
18
+ function userName() {
19
+ try {
20
+ return userInfo().username;
21
+ }
22
+ catch {
23
+ return process.env.USER ?? '';
24
+ }
25
+ }
26
+ //# sourceMappingURL=startup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup.js","sourceRoot":"","sources":["../../src/host/startup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAqB,YAAY,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAY,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAuCpD,2BAA2B;AAC3B,MAAM,UAAU,OAAO,CAAC,EACtB,GAAG,GAAG,OAAO,CAAC,GAAG,EACjB,IAAI,GAAG,OAAO,EAAE,EAChB,QAAQ,GAAG,YAAY,EAAE,EACzB,GAAG,GAAG,UAAU,EAChB,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EAC7B,IAAI,GAAG,QAAQ,EAAE,MACF,EAAE;IACjB,MAAM,QAAQ,GAAwC;QACpD,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACzC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC;KAChC,CAAC;IACF,OAAO,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED,yEAAyE;AACzE,SAAS,QAAQ;IACf,IAAI,CAAC;QACH,OAAO,QAAQ,EAAE,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAChC,CAAC;AACH,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { ConductorError } from "../errors.js";
4
+ import { outputOf } from "../run.js";
5
+ export const SYSTEMD_UNIT = 'conductor.service';
6
+ // A systemd user unit that runs `conduct supervise` when the user's service manager starts: at
7
+ // login, or at boot with lingering on. supervise restarts the daemon and conduct stop must stick,
8
+ // so no Restart=; and KillMode=process, or the unit ending (a stop, an upgrade) would kill its whole
9
+ // cgroup, the workers and tmux the daemon started included. Its output goes to the journal
10
+ // (journalctl --user -u conductor).
11
+ export function systemd({ env, home, run, user, }) {
12
+ // systemd reads user units from $XDG_CONFIG_HOME/systemd/user, ~/.config without it.
13
+ const unit = join(env.XDG_CONFIG_HOME || join(home, '.config'), 'systemd', 'user', SYSTEMD_UNIT);
14
+ const systemctl = async (what, args) => {
15
+ const result = await run('systemctl', ['--user', ...args], { timeoutMs: 60_000 });
16
+ if (result.code !== 0)
17
+ throw systemctlFailed(what, result);
18
+ };
19
+ const text = () => (existsSync(unit) ? readFileSync(unit, 'utf8') : undefined);
20
+ return {
21
+ install: async (spec) => {
22
+ mkdirSync(dirname(unit), { recursive: true });
23
+ writeFileSync(unit, systemdUnit(spec));
24
+ await systemctl('could not load the systemd unit', ['daemon-reload']);
25
+ await systemctl('could not enable the systemd unit', ['enable', SYSTEMD_UNIT]);
26
+ // A running one keeps the command it started with until it restarts.
27
+ await systemctl('could not start the systemd unit', ['restart', SYSTEMD_UNIT]);
28
+ const linger = await run('loginctl', ['show-user', user, '--property=Linger', '--value'], { timeoutMs: 30_000 });
29
+ return [
30
+ 'systemd started it now',
31
+ ...(linger.stdout.trim() === 'yes'
32
+ ? []
33
+ : [
34
+ `it starts at your login; to start it at boot on a machine nobody logs in to, run \`loginctl enable-linger ${user || '$USER'}\``,
35
+ ]),
36
+ ];
37
+ },
38
+ name: `the systemd user unit ${SYSTEMD_UNIT}`,
39
+ // Disabled and gone, but not stopped: a daemon it started keeps running, as on Windows.
40
+ remove: async () => {
41
+ if (!existsSync(unit))
42
+ return false;
43
+ await systemctl('could not disable the systemd unit', ['disable', SYSTEMD_UNIT]);
44
+ rmSync(unit);
45
+ await run('systemctl', ['--user', 'daemon-reload'], { timeoutMs: 60_000 });
46
+ return true;
47
+ },
48
+ run: () => systemctl('could not start the systemd unit', ['start', SYSTEMD_UNIT]),
49
+ runsCli: async (cliPath) => execStart(text())?.includes(` ${unitWord(cliPath)} `) ?? false,
50
+ supervises: async () => / supervise$/.test(execStart(text()) ?? ''),
51
+ };
52
+ }
53
+ // The unit file.
54
+ export function systemdUnit({ cliPath, nodePath, path }) {
55
+ return [
56
+ '[Unit]',
57
+ 'Description=The conductor daemon (conduct supervise)',
58
+ '',
59
+ '[Service]',
60
+ `ExecStart=${[nodePath, cliPath].map((word) => unitWord(word)).join(' ')} supervise`,
61
+ `Environment=${unitWord(`PATH=${path}`, false)}`,
62
+ 'KillMode=process',
63
+ '',
64
+ '[Install]',
65
+ 'WantedBy=default.target',
66
+ '',
67
+ ].join('\n');
68
+ }
69
+ function execStart(unit) {
70
+ return /^ExecStart=(.*)$/m.exec(unit ?? '')?.[1];
71
+ }
72
+ function systemctlFailed(what, result) {
73
+ if (/ENOENT/.test(result.error ?? '')) {
74
+ return new ConductorError(`${what}: no systemctl`, ['start `conduct supervise` from your own login items']);
75
+ }
76
+ return new ConductorError(what, [
77
+ outputOf(result, 1000),
78
+ "systemctl --user needs your login session's service manager: run it in a session you logged in to, not after su",
79
+ ]);
80
+ }
81
+ // A word in quotes, as ExecStart= and Environment= read it: `%` starts a specifier, and in
82
+ // ExecStart= `$` an environment variable.
83
+ function unitWord(text, command = true) {
84
+ const escaped = text
85
+ .replaceAll('\\', '\\\\')
86
+ .replaceAll('"', '\\"')
87
+ .replaceAll('%', '%%')
88
+ .replaceAll('$', () => (command ? '$$' : '$'));
89
+ return `"${escaped}"`;
90
+ }
91
+ //# sourceMappingURL=systemd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemd.js","sourceRoot":"","sources":["../../src/host/systemd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { ConductorError } from "../errors.js";
2
+ import { outputOf } from "../run.js";
3
+ export const TASK_NAME = 'Conductor';
4
+ // The task's <Arguments> quote the cli.js path (install); XML escapes it, and Windows paths compare
5
+ // without case.
6
+ export function taskRunsCli(xml, cliPath) {
7
+ const args = taskArguments(xml);
8
+ return args !== undefined && args.toLowerCase().includes(`"${cliPath.toLowerCase()}"`);
9
+ }
10
+ // A per-user Task Scheduler task that runs `conduct supervise` at logon, which keeps `conduct serve`
11
+ // running (supervise.ts). No elevation is needed for a task that runs as the current user.
12
+ // conhost --headless keeps a console window (or a Windows Terminal tab) from opening, and the zero
13
+ // time limit stops Task Scheduler from ending the daemon after its default 72 hours. A second start
14
+ // while one runs exits on the pid lock, and its supervisor with it. Installing doesn't start it.
15
+ export function taskScheduler(run) {
16
+ const schtasks = (args) => run('schtasks', args, { timeoutMs: 60_000 });
17
+ const taskXml = async () => {
18
+ const result = await schtasks(['/Query', '/TN', TASK_NAME, '/XML']);
19
+ return result.code === 0 ? result.stdout : undefined;
20
+ };
21
+ const installed = async () => (await schtasks(['/Query', '/TN', TASK_NAME])).code === 0;
22
+ return {
23
+ install: async ({ cliPath, nodePath }) => {
24
+ const script = [
25
+ "$ErrorActionPreference = 'Stop'",
26
+ '$user = "$env:USERDOMAIN\\$env:USERNAME"',
27
+ '$action = New-ScheduledTaskAction -Execute "$env:SystemRoot\\System32\\conhost.exe" -Argument $env:CONDUCTOR_TASK_ARGS',
28
+ '$trigger = New-ScheduledTaskTrigger -AtLogOn -User $user',
29
+ '$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew -ExecutionTimeLimit ([TimeSpan]::Zero)',
30
+ '$principal = New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive -RunLevel Limited',
31
+ `Register-ScheduledTask -TaskName '${TASK_NAME}' -Description 'Starts the conductor daemon (conduct supervise) at logon.' -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force | Out-Null`,
32
+ ].join('\n');
33
+ const result = await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {
34
+ env: { ...process.env, CONDUCTOR_TASK_ARGS: `--headless "${nodePath}" "${cliPath}" supervise` },
35
+ timeoutMs: 60_000,
36
+ });
37
+ if (result.code !== 0)
38
+ throw new ConductorError('could not register the logon task', [outputOf(result, 1000)]);
39
+ return [];
40
+ },
41
+ name: `the "${TASK_NAME}" logon task`,
42
+ remove: async () => {
43
+ if (!(await installed()))
44
+ return false;
45
+ const result = await schtasks(['/Delete', '/TN', TASK_NAME, '/F']);
46
+ if (result.code !== 0)
47
+ throw new ConductorError('could not remove the logon task', [outputOf(result, 1000)]);
48
+ return true;
49
+ },
50
+ run: async () => {
51
+ const result = await schtasks(['/Run', '/TN', TASK_NAME]);
52
+ if (result.code !== 0)
53
+ throw new ConductorError(`could not run the "${TASK_NAME}" logon task`, [outputOf(result, 1000)]);
54
+ },
55
+ runsCli: async (cliPath) => {
56
+ const xml = await taskXml();
57
+ return xml !== undefined && taskRunsCli(xml, cliPath);
58
+ },
59
+ supervises: async () => {
60
+ const xml = await taskXml();
61
+ return xml !== undefined && taskSupervises(xml);
62
+ },
63
+ };
64
+ }
65
+ export function taskSupervises(xml) {
66
+ return /\ssupervise\s*$/.test(taskArguments(xml) ?? '');
67
+ }
68
+ // The task action's <Arguments>, XML unescaped.
69
+ function taskArguments(xml) {
70
+ return /<Arguments>([^<]*)<\/Arguments>/
71
+ .exec(xml)?.[1]
72
+ ?.replaceAll('&quot;', '"')
73
+ .replaceAll('&apos;', "'")
74
+ .replaceAll('&lt;', '<')
75
+ .replaceAll('&gt;', '>')
76
+ .replaceAll('&amp;', '&');
77
+ }
78
+ //# sourceMappingURL=task-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-scheduler.js","sourceRoot":"","sources":["../../src/host/task-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { run as runCommand } from "../run.js";
5
+ import { appleTerminal, iterm } from "./macos-terminal.js";
6
+ import { hostPlatform } from "./platform.js";
7
+ import { tmux } from "./tmux.js";
8
+ import { windowsTerminal } from "./windows-terminal.js";
9
+ // config.json's workers.terminal: auto is Windows Terminal on Windows, iTerm on macOS where it is
10
+ // installed and Terminal where it isn't, and tmux elsewhere.
11
+ export const TERMINALS = ['auto', 'windows-terminal', 'terminal', 'iterm', 'tmux'];
12
+ const ITERM_APPS = ['/Applications/iTerm.app', join(homedir(), 'Applications', 'iTerm.app')];
13
+ // This platform's terminal, or the one config.json names.
14
+ export function terminal({ choice = 'auto', installed = existsSync, platform = hostPlatform(), run = runCommand, } = {}) {
15
+ const picked = choice !== 'auto'
16
+ ? choice
17
+ : platform === 'win32'
18
+ ? 'windows-terminal'
19
+ : platform === 'darwin'
20
+ ? ITERM_APPS.some((app) => installed(app))
21
+ ? 'iterm'
22
+ : 'terminal'
23
+ : 'tmux';
24
+ const terminals = {
25
+ iterm: () => iterm(run),
26
+ terminal: () => appleTerminal(run),
27
+ tmux: () => tmux(run),
28
+ 'windows-terminal': () => windowsTerminal(run),
29
+ };
30
+ return terminals[picked]();
31
+ }
32
+ export function workerTabs(where, onLaunch) {
33
+ return {
34
+ hint: (window) => where.attachHint(window),
35
+ onLaunch,
36
+ open: (tab) => where.open({
37
+ command: ['claude', 'attach', tab.handle],
38
+ cwd: tab.cwd,
39
+ env: {},
40
+ hold: true,
41
+ title: tab.title,
42
+ window: tab.window,
43
+ }),
44
+ };
45
+ }
46
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/host/terminal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAqB,YAAY,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,kGAAkG;AAClG,6DAA6D;AAC7D,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AA4C5F,MAAM,UAAU,GAAG,CAAC,yBAAyB,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AAE7F,0DAA0D;AAC1D,MAAM,UAAU,QAAQ,CAAC,EACvB,MAAM,GAAG,MAAM,EACf,SAAS,GAAG,UAAU,EACtB,QAAQ,GAAG,YAAY,EAAE,EACzB,GAAG,GAAG,UAAU,MAOd,EAAE;IACJ,MAAM,MAAM,GACV,MAAM,KAAK,MAAM;QACf,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,QAAQ,KAAK,OAAO;YACpB,CAAC,CAAC,kBAAkB;YACpB,CAAC,CAAC,QAAQ,KAAK,QAAQ;gBACrB,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACxC,CAAC,CAAC,OAAO;oBACT,CAAC,CAAC,UAAU;gBACd,CAAC,CAAC,MAAM,CAAC;IACjB,MAAM,SAAS,GAA0C;QACvD,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;QACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC;QAClC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACrB,kBAAkB,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;KAC/C,CAAC;IACF,OAAO,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAe,EAAE,QAAiB;IAC3D,OAAO;QACL,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;QAC1C,QAAQ;QACR,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CACZ,KAAK,CAAC,IAAI,CAAC;YACT,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC;YACzC,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,GAAG,EAAE,EAAE;YACP,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { outputOf } from "../run.js";
2
+ import { shLine, viaSh } from "./shell.js";
3
+ // tmux: a detached session per window, made by its first tab, and a tmux window per tab. It runs
4
+ // where there is no desktop (a server over ssh), so a person reaches it with `tmux attach`. tmux
5
+ // starts a tab in the tmux server's environment, so `env` goes on the command line, and runs it in
6
+ // its default-shell, so the line goes through sh.
7
+ export function tmux(run) {
8
+ return {
9
+ attachHint: (window) => `tmux attach -t ${tmuxSession(window)}`,
10
+ name: 'tmux',
11
+ open: async (tab) => {
12
+ const session = tmuxSession(tab.window);
13
+ // `=` matches the name exactly, not as a prefix.
14
+ const has = await run('tmux', ['has-session', '-t', `=${session}`], { timeoutMs: 30_000 });
15
+ if (has.error !== null && /ENOENT/.test(has.error))
16
+ return 'no tmux to open it in';
17
+ // tmux expands formats in -n and -c, and `#(…)` in one runs a command: `##` is a plain `#`.
18
+ const literal = (text) => text.replace(/#/g, '##');
19
+ const title = literal(tab.title.replace(/\s+/g, ' ').trim().slice(0, 80));
20
+ const open = (where) => run('tmux', [...where, '-n', title, '-c', literal(tab.cwd), viaSh(shLine(tab))], { timeoutMs: 30_000 });
21
+ const inSession = ['new-window', '-t', `=${session}:`];
22
+ let result = await open(has.code === 0 ? inSession : ['new-session', '-d', '-s', session]);
23
+ // Another tab made the session since has-session asked.
24
+ if (has.code !== 0 && /duplicate session/.test(result.stderr))
25
+ result = await open(inSession);
26
+ return result.code === 0 ? null : outputOf(result, 500);
27
+ },
28
+ };
29
+ }
30
+ // The tmux session a window's tabs open in: its name in lower case, with a dash for anything tmux
31
+ // reads in a target (`.` and `:`) and anything a person would have to quote. "Conductor · atk"
32
+ // becomes conductor-atk.
33
+ export function tmuxSession(window) {
34
+ return (window
35
+ .toLowerCase()
36
+ .replace(/[^a-z0-9_-]+/g, '-')
37
+ .replace(/^-+|-+$/g, '') || 'conductor');
38
+ }
39
+ //# sourceMappingURL=tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../src/host/tmux.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE3C,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,kDAAkD;AAClD,MAAM,UAAU,IAAI,CAAC,GAAQ;IAC3B,OAAO;QACL,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,WAAW,CAAC,MAAM,CAAC,EAAE;QAC/D,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAClB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,iDAAiD;YACjD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3F,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO,uBAAuB,CAAC;YACnF,4FAA4F;YAC5F,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACnE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,CAAC,KAAwB,EAAE,EAAE,CACxC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC1G,MAAM,SAAS,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,OAAO,GAAG,CAAC,CAAC;YACvD,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YAC3F,wDAAwD;YACxD,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;gBAAE,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9F,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,+FAA+F;AAC/F,yBAAyB;AACzB,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,CACL,MAAM;SACH,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,WAAW,CAC1C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { outputOf } from "../run.js";
2
+ import { failedText } from "./shell.js";
3
+ // Windows Terminal (wt.exe). Not hidden: when the named window doesn't exist yet, Windows Terminal
4
+ // creates it with the spawn's SW_HIDE, and the tab runs where nobody sees it (ATK-469, ATK-336).
5
+ export function windowsTerminal(run) {
6
+ return {
7
+ attachHint: () => null,
8
+ name: 'Windows Terminal',
9
+ open: async (tab) => {
10
+ const args = windowsTerminalArgs(tab);
11
+ if (typeof args === 'string')
12
+ return args;
13
+ const result = await run('wt.exe', args, { cwd: tab.cwd, timeoutMs: 60_000, windowsHide: false });
14
+ if (result.code === 0)
15
+ return null;
16
+ return result.error !== null && /ENOENT/.test(result.error)
17
+ ? 'no Windows Terminal (wt.exe) to open it in'
18
+ : outputOf(result, 500);
19
+ },
20
+ };
21
+ }
22
+ // wt.exe's arguments for a tab in the named window. wt.exe splits its own arguments on `;`, so the
23
+ // window and title lose it, a folder with one is left out (the tab starts in its profile's, as
24
+ // tabs did before ATK-529), and a command with one can't open (a string says why). A held command
25
+ // runs in pwsh and chains its prompt with `||`. The tab gets wt.exe's environment, which is the
26
+ // caller's even in a window already open, so `env` needs no setting.
27
+ export function windowsTerminalArgs(tab) {
28
+ const split = tab.command.find((part) => part.includes(';'));
29
+ if (split !== undefined)
30
+ return `a Windows Terminal tab can't take ";": ${split}`;
31
+ const clean = (text) => text.replace(/;/g, ',').replace(/\s+/g, ' ').trim();
32
+ const command = tab.hold
33
+ ? ['pwsh', '-Command', `${psLine(tab.command)} || Read-Host ${psQuote(failedText(tab.command))}`]
34
+ : tab.command;
35
+ const folder = tab.cwd.includes(';') ? [] : ['-d', tab.cwd];
36
+ return ['-w', clean(tab.window), 'new-tab', '--title', clean(tab.title).slice(0, 80), ...folder, ...command];
37
+ }
38
+ // A command as pwsh runs it: words bare where they can be, and a quoted program called with `&`.
39
+ function psLine(command) {
40
+ const words = command.map((word) => (/^[\w.:/\\-]+$/.test(word) ? word : psQuote(word)));
41
+ return words[0]?.startsWith("'") ? `& ${words.join(' ')}` : words.join(' ');
42
+ }
43
+ function psQuote(text) {
44
+ return `'${text.replace(/'/g, "''")}'`;
45
+ }
46
+ //# sourceMappingURL=windows-terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-terminal.js","sourceRoot":"","sources":["../../src/host/windows-terminal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,mGAAmG;AACnG,iGAAiG;AACjG,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,OAAO;QACL,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI;QACtB,IAAI,EAAE,kBAAkB;QACxB,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAClB,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAClG,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACnC,OAAO,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACzD,CAAC,CAAC,4CAA4C;gBAC9C,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,mGAAmG;AACnG,+FAA+F;AAC/F,kGAAkG;AAClG,gGAAgG;AAChG,qEAAqE;AACrE,MAAM,UAAU,mBAAmB,CAAC,GAAe;IACjD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,0CAA0C,KAAK,EAAE,CAAC;IAClF,MAAM,KAAK,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5F,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI;QACtB,CAAC,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACjG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;IAChB,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;AAC/G,CAAC;AAED,iGAAiG;AACjG,SAAS,MAAM,CAAC,OAA0B;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzF,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AACzC,CAAC"}
@@ -1,22 +1,23 @@
1
1
  import { loadConfig } from "./config.js";
2
2
  import { providerCredentials } from "./credentials.js";
3
- import { adoOrganization, PROFILE_FILE, readProfile, trackerKey } from "./profile.js";
3
+ import { profileHere } from "./profile-layers.js";
4
+ import { adoOrganization, profileProblem, trackerKey } from "./profile.js";
4
5
  import { gitToplevel } from "./wait.js";
5
6
  // What a tracker verb run in-process (no daemon) needs: the project of the checkout it runs in, and
6
7
  // the machine's accounts. `conduct note` and `conduct brief` share it.
7
8
  // The profile of the checkout around `cwd`, when `key` (an item key, checked already) is one of its
8
9
  // items; else why not, after `why` (why it runs here), for the verb's refusal. `purpose` ends the
9
10
  // sentence: "post it from", "read it from".
10
- export async function checkoutProject(cwd, key, why, purpose) {
11
+ export async function checkoutProject(paths, cwd, key, why, purpose) {
11
12
  const checkout = await gitToplevel(cwd);
12
13
  if (checkout === null)
13
14
  return { refusal: `${why}, and this isn't a project checkout to ${purpose}` };
14
15
  let profile;
15
16
  try {
16
- profile = readProfile(checkout).profile;
17
+ profile = profileHere(checkout, paths);
17
18
  }
18
- catch {
19
- return { refusal: `${why}, and ${checkout} has no readable ${PROFILE_FILE}` };
19
+ catch (error) {
20
+ return { refusal: `${why}, and ${checkout} has no readable profile: ${profileProblem(error)}` };
20
21
  }
21
22
  if (key.split('-')[0] !== trackerKey(profile).toUpperCase())
22
23
  return { refusal: `${why}, and ${key} isn't an item of ${profile.name}, this checkout's project` };
@@ -1 +1 @@
1
- {"version":3,"file":"in-process.js","sourceRoot":"","sources":["../src/in-process.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAoB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAgB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,oGAAoG;AACpG,uEAAuE;AAEvE,oGAAoG;AACpG,kGAAkG;AAClG,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,GAAW,EACX,GAAW,EACX,OAAe;IAEf,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,0CAA0C,OAAO,EAAE,EAAE,CAAC;IACrG,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,SAAS,QAAQ,oBAAoB,YAAY,EAAE,EAAE,CAAC;IAChF,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;QACzD,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,SAAS,GAAG,qBAAqB,OAAO,CAAC,IAAI,2BAA2B,EAAE,CAAC;IACrG,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,8FAA8F;AAC9F,kGAAkG;AAClG,4BAA4B;AAC5B,MAAM,UAAU,gBAAgB,CAAC,KAAqB,EAAE,KAAmC;IACzF,OAAO,mBAAmB,CAAC;QACzB,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9E,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ;KAC5C,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAAC,KAAkC;IACnE,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC;QACtF,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"in-process.js","sourceRoot":"","sources":["../src/in-process.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAoB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAgB,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,oGAAoG;AACpG,uEAAuE;AAEvE,oGAAoG;AACpG,kGAAkG;AAClG,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAuC,EACvC,GAAW,EACX,GAAW,EACX,GAAW,EACX,OAAe;IAEf,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,0CAA0C,OAAO,EAAE,EAAE,CAAC;IACrG,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,SAAS,QAAQ,6BAA6B,cAAc,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IAClG,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;QACzD,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,SAAS,GAAG,qBAAqB,OAAO,CAAC,IAAI,2BAA2B,EAAE,CAAC;IACrG,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,8FAA8F;AAC9F,kGAAkG;AAClG,4BAA4B;AAC5B,MAAM,UAAU,gBAAgB,CAAC,KAAqB,EAAE,KAAmC;IACzF,OAAO,mBAAmB,CAAC;QACzB,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9E,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ;KAC5C,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAAC,KAAkC;IACnE,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC;QACtF,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC;AACJ,CAAC"}