lastlight 0.15.0 → 0.16.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 (382) hide show
  1. package/README.md +71 -692
  2. package/dist/cli-config.js.map +1 -0
  3. package/dist/cli-format.js.map +1 -0
  4. package/dist/{cli/cli-server.d.ts → cli-server.d.ts} +26 -1
  5. package/dist/{cli/cli-server.js → cli-server.js} +40 -5
  6. package/dist/cli-server.js.map +1 -0
  7. package/dist/cli-timeline.js.map +1 -0
  8. package/dist/{cli/cli.js → cli.js} +4 -3
  9. package/dist/cli.js.map +1 -0
  10. package/dist/{cli/fork-cli.d.ts → fork-cli.d.ts} +3 -3
  11. package/dist/{cli/fork-cli.js → fork-cli.js} +28 -27
  12. package/dist/fork-cli.js.map +1 -0
  13. package/dist/{cli/oauth-cli.js → oauth-cli.js} +1 -1
  14. package/dist/oauth-cli.js.map +1 -0
  15. package/dist/{cli/setup.js → setup.js} +10 -4
  16. package/dist/setup.js.map +1 -0
  17. package/dist/{cli/skills-install.d.ts → skills-install.d.ts} +6 -0
  18. package/dist/{cli/skills-install.js → skills-install.js} +6 -2
  19. package/dist/skills-install.js.map +1 -0
  20. package/package.json +20 -65
  21. package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
  22. package/agent-context/rules.md +0 -80
  23. package/agent-context/security.md +0 -69
  24. package/agent-context/soul.md +0 -21
  25. package/config/default.yaml +0 -119
  26. package/deploy/.env.production.example +0 -100
  27. package/deploy/entrypoint.sh +0 -82
  28. package/deploy/native/README.md +0 -160
  29. package/deploy/native/install.sh +0 -110
  30. package/deploy/native/lastlight.env.example +0 -95
  31. package/deploy/native/lastlight.service +0 -50
  32. package/deploy/sandbox-entrypoint.sh +0 -93
  33. package/dist/admin/auth.d.ts +0 -51
  34. package/dist/admin/auth.js +0 -121
  35. package/dist/admin/auth.js.map +0 -1
  36. package/dist/admin/chat-session-reader.d.ts +0 -39
  37. package/dist/admin/chat-session-reader.js +0 -84
  38. package/dist/admin/chat-session-reader.js.map +0 -1
  39. package/dist/admin/docker.d.ts +0 -61
  40. package/dist/admin/docker.js +0 -187
  41. package/dist/admin/docker.js.map +0 -1
  42. package/dist/admin/index.d.ts +0 -9
  43. package/dist/admin/index.js +0 -59
  44. package/dist/admin/index.js.map +0 -1
  45. package/dist/admin/routes.d.ts +0 -81
  46. package/dist/admin/routes.js +0 -1381
  47. package/dist/admin/routes.js.map +0 -1
  48. package/dist/admin/sessions.d.ts +0 -89
  49. package/dist/admin/sessions.js +0 -204
  50. package/dist/admin/sessions.js.map +0 -1
  51. package/dist/admin/tail.d.ts +0 -12
  52. package/dist/admin/tail.js +0 -145
  53. package/dist/admin/tail.js.map +0 -1
  54. package/dist/admin/version.d.ts +0 -22
  55. package/dist/admin/version.js +0 -92
  56. package/dist/admin/version.js.map +0 -1
  57. package/dist/cli/cli-config.js.map +0 -1
  58. package/dist/cli/cli-format.js.map +0 -1
  59. package/dist/cli/cli-server.js.map +0 -1
  60. package/dist/cli/cli-timeline.js.map +0 -1
  61. package/dist/cli/cli.js.map +0 -1
  62. package/dist/cli/fork-cli.js.map +0 -1
  63. package/dist/cli/oauth-cli.js.map +0 -1
  64. package/dist/cli/setup.js.map +0 -1
  65. package/dist/cli/skills-install.js.map +0 -1
  66. package/dist/config/config-resolve.d.ts +0 -28
  67. package/dist/config/config-resolve.js +0 -47
  68. package/dist/config/config-resolve.js.map +0 -1
  69. package/dist/config/config.d.ts +0 -157
  70. package/dist/config/config.js +0 -581
  71. package/dist/config/config.js.map +0 -1
  72. package/dist/config/core-pin.d.ts +0 -16
  73. package/dist/config/core-pin.js +0 -85
  74. package/dist/config/core-pin.js.map +0 -1
  75. package/dist/config/overlay-assets.d.ts +0 -20
  76. package/dist/config/overlay-assets.js +0 -114
  77. package/dist/config/overlay-assets.js.map +0 -1
  78. package/dist/config/overlay-bootstrap.d.ts +0 -44
  79. package/dist/config/overlay-bootstrap.js +0 -304
  80. package/dist/config/overlay-bootstrap.js.map +0 -1
  81. package/dist/connectors/github-webhook.d.ts +0 -45
  82. package/dist/connectors/github-webhook.js +0 -321
  83. package/dist/connectors/github-webhook.js.map +0 -1
  84. package/dist/connectors/index.d.ts +0 -18
  85. package/dist/connectors/index.js +0 -41
  86. package/dist/connectors/index.js.map +0 -1
  87. package/dist/connectors/messaging/base.d.ts +0 -42
  88. package/dist/connectors/messaging/base.js +0 -139
  89. package/dist/connectors/messaging/base.js.map +0 -1
  90. package/dist/connectors/messaging/delivery.d.ts +0 -14
  91. package/dist/connectors/messaging/delivery.js +0 -38
  92. package/dist/connectors/messaging/delivery.js.map +0 -1
  93. package/dist/connectors/messaging/index.d.ts +0 -4
  94. package/dist/connectors/messaging/index.js +0 -4
  95. package/dist/connectors/messaging/index.js.map +0 -1
  96. package/dist/connectors/messaging/session-manager.d.ts +0 -53
  97. package/dist/connectors/messaging/session-manager.js +0 -255
  98. package/dist/connectors/messaging/session-manager.js.map +0 -1
  99. package/dist/connectors/messaging/types.d.ts +0 -58
  100. package/dist/connectors/messaging/types.js +0 -7
  101. package/dist/connectors/messaging/types.js.map +0 -1
  102. package/dist/connectors/slack/connector.d.ts +0 -100
  103. package/dist/connectors/slack/connector.js +0 -341
  104. package/dist/connectors/slack/connector.js.map +0 -1
  105. package/dist/connectors/slack/index.d.ts +0 -2
  106. package/dist/connectors/slack/index.js +0 -3
  107. package/dist/connectors/slack/index.js.map +0 -1
  108. package/dist/connectors/slack/mrkdwn.d.ts +0 -8
  109. package/dist/connectors/slack/mrkdwn.js +0 -123
  110. package/dist/connectors/slack/mrkdwn.js.map +0 -1
  111. package/dist/connectors/types.d.ts +0 -61
  112. package/dist/connectors/types.js +0 -2
  113. package/dist/connectors/types.js.map +0 -1
  114. package/dist/cron/fanout.d.ts +0 -33
  115. package/dist/cron/fanout.js +0 -55
  116. package/dist/cron/fanout.js.map +0 -1
  117. package/dist/cron/jobs.d.ts +0 -18
  118. package/dist/cron/jobs.js +0 -37
  119. package/dist/cron/jobs.js.map +0 -1
  120. package/dist/cron/scheduler.d.ts +0 -45
  121. package/dist/cron/scheduler.js +0 -105
  122. package/dist/cron/scheduler.js.map +0 -1
  123. package/dist/engine/agent-executor.d.ts +0 -34
  124. package/dist/engine/agent-executor.js +0 -246
  125. package/dist/engine/agent-executor.js.map +0 -1
  126. package/dist/engine/chat/chat-runner.d.ts +0 -132
  127. package/dist/engine/chat/chat-runner.js +0 -424
  128. package/dist/engine/chat/chat-runner.js.map +0 -1
  129. package/dist/engine/chat/chat-skills.d.ts +0 -45
  130. package/dist/engine/chat/chat-skills.js +0 -184
  131. package/dist/engine/chat/chat-skills.js.map +0 -1
  132. package/dist/engine/chat/chat.d.ts +0 -54
  133. package/dist/engine/chat/chat.js +0 -316
  134. package/dist/engine/chat/chat.js.map +0 -1
  135. package/dist/engine/chat/message-batcher.d.ts +0 -32
  136. package/dist/engine/chat/message-batcher.js +0 -109
  137. package/dist/engine/chat/message-batcher.js.map +0 -1
  138. package/dist/engine/dispatcher.d.ts +0 -73
  139. package/dist/engine/dispatcher.js +0 -663
  140. package/dist/engine/dispatcher.js.map +0 -1
  141. package/dist/engine/event-shim.d.ts +0 -117
  142. package/dist/engine/event-shim.js +0 -435
  143. package/dist/engine/event-shim.js.map +0 -1
  144. package/dist/engine/executors/orchestrator.d.ts +0 -81
  145. package/dist/engine/executors/orchestrator.js +0 -365
  146. package/dist/engine/executors/orchestrator.js.map +0 -1
  147. package/dist/engine/executors/shared.d.ts +0 -189
  148. package/dist/engine/executors/shared.js +0 -616
  149. package/dist/engine/executors/shared.js.map +0 -1
  150. package/dist/engine/github/git-auth.d.ts +0 -62
  151. package/dist/engine/github/git-auth.js +0 -161
  152. package/dist/engine/github/git-auth.js.map +0 -1
  153. package/dist/engine/github/github-app-client.d.ts +0 -23
  154. package/dist/engine/github/github-app-client.js +0 -33
  155. package/dist/engine/github/github-app-client.js.map +0 -1
  156. package/dist/engine/github/github-tools.d.ts +0 -18
  157. package/dist/engine/github/github-tools.js +0 -263
  158. package/dist/engine/github/github-tools.js.map +0 -1
  159. package/dist/engine/github/github.d.ts +0 -1193
  160. package/dist/engine/github/github.js +0 -357
  161. package/dist/engine/github/github.js.map +0 -1
  162. package/dist/engine/github/profiles.d.ts +0 -279
  163. package/dist/engine/github/profiles.js +0 -42
  164. package/dist/engine/github/profiles.js.map +0 -1
  165. package/dist/engine/github/review-poster.d.ts +0 -98
  166. package/dist/engine/github/review-poster.js +0 -177
  167. package/dist/engine/github/review-poster.js.map +0 -1
  168. package/dist/engine/llm.d.ts +0 -79
  169. package/dist/engine/llm.js +0 -269
  170. package/dist/engine/llm.js.map +0 -1
  171. package/dist/engine/oauth.d.ts +0 -43
  172. package/dist/engine/oauth.js +0 -103
  173. package/dist/engine/oauth.js.map +0 -1
  174. package/dist/engine/router.d.ts +0 -29
  175. package/dist/engine/router.js +0 -680
  176. package/dist/engine/router.js.map +0 -1
  177. package/dist/engine/screen/classifier.d.ts +0 -93
  178. package/dist/engine/screen/classifier.js +0 -215
  179. package/dist/engine/screen/classifier.js.map +0 -1
  180. package/dist/engine/screen/screen.d.ts +0 -41
  181. package/dist/engine/screen/screen.js +0 -93
  182. package/dist/engine/screen/screen.js.map +0 -1
  183. package/dist/evals-api.d.ts +0 -24
  184. package/dist/evals-api.js +0 -22
  185. package/dist/evals-api.js.map +0 -1
  186. package/dist/index.d.ts +0 -1
  187. package/dist/index.js +0 -923
  188. package/dist/index.js.map +0 -1
  189. package/dist/managed-repos.d.ts +0 -30
  190. package/dist/managed-repos.js +0 -72
  191. package/dist/managed-repos.js.map +0 -1
  192. package/dist/notify/index.d.ts +0 -15
  193. package/dist/notify/index.js +0 -6
  194. package/dist/notify/index.js.map +0 -1
  195. package/dist/notify/model.d.ts +0 -57
  196. package/dist/notify/model.js +0 -89
  197. package/dist/notify/model.js.map +0 -1
  198. package/dist/notify/notifier.d.ts +0 -17
  199. package/dist/notify/notifier.js +0 -87
  200. package/dist/notify/notifier.js.map +0 -1
  201. package/dist/notify/render.d.ts +0 -21
  202. package/dist/notify/render.js +0 -59
  203. package/dist/notify/render.js.map +0 -1
  204. package/dist/notify/transports/github.d.ts +0 -26
  205. package/dist/notify/transports/github.js +0 -26
  206. package/dist/notify/transports/github.js.map +0 -1
  207. package/dist/notify/transports/slack.d.ts +0 -26
  208. package/dist/notify/transports/slack.js +0 -28
  209. package/dist/notify/transports/slack.js.map +0 -1
  210. package/dist/notify/types.d.ts +0 -95
  211. package/dist/notify/types.js +0 -16
  212. package/dist/notify/types.js.map +0 -1
  213. package/dist/providers.d.ts +0 -125
  214. package/dist/providers.js +0 -279
  215. package/dist/providers.js.map +0 -1
  216. package/dist/sandbox/docker.d.ts +0 -180
  217. package/dist/sandbox/docker.js +0 -517
  218. package/dist/sandbox/docker.js.map +0 -1
  219. package/dist/sandbox/egress-allowlist.d.ts +0 -74
  220. package/dist/sandbox/egress-allowlist.js +0 -164
  221. package/dist/sandbox/egress-allowlist.js.map +0 -1
  222. package/dist/sandbox/egress-firewall-config.d.ts +0 -127
  223. package/dist/sandbox/egress-firewall-config.js +0 -434
  224. package/dist/sandbox/egress-firewall-config.js.map +0 -1
  225. package/dist/sandbox/images.d.ts +0 -23
  226. package/dist/sandbox/images.js +0 -55
  227. package/dist/sandbox/images.js.map +0 -1
  228. package/dist/sandbox/index.d.ts +0 -112
  229. package/dist/sandbox/index.js +0 -332
  230. package/dist/sandbox/index.js.map +0 -1
  231. package/dist/sandbox/sandbox.d.ts +0 -214
  232. package/dist/sandbox/sandbox.js +0 -453
  233. package/dist/sandbox/sandbox.js.map +0 -1
  234. package/dist/sandbox/smol.d.ts +0 -162
  235. package/dist/sandbox/smol.js +0 -485
  236. package/dist/sandbox/smol.js.map +0 -1
  237. package/dist/session-log.d.ts +0 -63
  238. package/dist/session-log.js +0 -290
  239. package/dist/session-log.js.map +0 -1
  240. package/dist/state/approval-store.d.ts +0 -88
  241. package/dist/state/approval-store.js +0 -149
  242. package/dist/state/approval-store.js.map +0 -1
  243. package/dist/state/build-assets.d.ts +0 -50
  244. package/dist/state/build-assets.js +0 -154
  245. package/dist/state/build-assets.js.map +0 -1
  246. package/dist/state/db.d.ts +0 -81
  247. package/dist/state/db.js +0 -155
  248. package/dist/state/db.js.map +0 -1
  249. package/dist/state/execution-store.d.ts +0 -257
  250. package/dist/state/execution-store.js +0 -527
  251. package/dist/state/execution-store.js.map +0 -1
  252. package/dist/state/migrate.d.ts +0 -15
  253. package/dist/state/migrate.js +0 -175
  254. package/dist/state/migrate.js.map +0 -1
  255. package/dist/state/workflow-run-store.d.ts +0 -216
  256. package/dist/state/workflow-run-store.js +0 -403
  257. package/dist/state/workflow-run-store.js.map +0 -1
  258. package/dist/telemetry/index.d.ts +0 -38
  259. package/dist/telemetry/index.js +0 -253
  260. package/dist/telemetry/index.js.map +0 -1
  261. package/dist/telemetry/pi-events.d.ts +0 -12
  262. package/dist/telemetry/pi-events.js +0 -134
  263. package/dist/telemetry/pi-events.js.map +0 -1
  264. package/dist/workflows/dag.d.ts +0 -37
  265. package/dist/workflows/dag.js +0 -118
  266. package/dist/workflows/dag.js.map +0 -1
  267. package/dist/workflows/loader.d.ts +0 -53
  268. package/dist/workflows/loader.js +0 -421
  269. package/dist/workflows/loader.js.map +0 -1
  270. package/dist/workflows/loop-eval.d.ts +0 -28
  271. package/dist/workflows/loop-eval.js +0 -74
  272. package/dist/workflows/loop-eval.js.map +0 -1
  273. package/dist/workflows/phase-executor.d.ts +0 -215
  274. package/dist/workflows/phase-executor.js +0 -1144
  275. package/dist/workflows/phase-executor.js.map +0 -1
  276. package/dist/workflows/phase-ref.d.ts +0 -50
  277. package/dist/workflows/phase-ref.js +0 -91
  278. package/dist/workflows/phase-ref.js.map +0 -1
  279. package/dist/workflows/resume.d.ts +0 -58
  280. package/dist/workflows/resume.js +0 -313
  281. package/dist/workflows/resume.js.map +0 -1
  282. package/dist/workflows/runner.d.ts +0 -63
  283. package/dist/workflows/runner.js +0 -364
  284. package/dist/workflows/runner.js.map +0 -1
  285. package/dist/workflows/schema.d.ts +0 -344
  286. package/dist/workflows/schema.js +0 -460
  287. package/dist/workflows/schema.js.map +0 -1
  288. package/dist/workflows/simple.d.ts +0 -90
  289. package/dist/workflows/simple.js +0 -393
  290. package/dist/workflows/simple.js.map +0 -1
  291. package/dist/workflows/target-policy.d.ts +0 -74
  292. package/dist/workflows/target-policy.js +0 -80
  293. package/dist/workflows/target-policy.js.map +0 -1
  294. package/dist/workflows/templates.d.ts +0 -88
  295. package/dist/workflows/templates.js +0 -127
  296. package/dist/workflows/templates.js.map +0 -1
  297. package/dist/workflows/triggers.d.ts +0 -21
  298. package/dist/workflows/triggers.js +0 -45
  299. package/dist/workflows/triggers.js.map +0 -1
  300. package/dist/workflows/verdict.d.ts +0 -19
  301. package/dist/workflows/verdict.js +0 -30
  302. package/dist/workflows/verdict.js.map +0 -1
  303. package/dist/worktree/manager.d.ts +0 -43
  304. package/dist/worktree/manager.js +0 -127
  305. package/dist/worktree/manager.js.map +0 -1
  306. package/docker-compose.yml +0 -274
  307. package/sandbox.Dockerfile +0 -68
  308. package/skills/README.md +0 -42
  309. package/skills/browser-qa/SKILL.md +0 -208
  310. package/skills/browser-qa/scripts/agent-browser.mjs +0 -531
  311. package/skills/building/SKILL.md +0 -95
  312. package/skills/chat/SKILL.md +0 -36
  313. package/skills/code-review/SKILL.md +0 -82
  314. package/skills/debug-production/SKILL.md +0 -108
  315. package/skills/demo/SKILL.md +0 -228
  316. package/skills/demo/scripts/compose-demo.sh +0 -243
  317. package/skills/issue-answer/SKILL.md +0 -91
  318. package/skills/issue-comment/SKILL.md +0 -49
  319. package/skills/issue-triage/SKILL.md +0 -149
  320. package/skills/issue-triage/references/AGENT-BRIEF.md +0 -65
  321. package/skills/pr-comment/SKILL.md +0 -64
  322. package/skills/pr-review/SKILL.md +0 -148
  323. package/skills/pr-review/references/findings-schema.md +0 -89
  324. package/skills/qa-test/SKILL.md +0 -97
  325. package/skills/repo-health/SKILL.md +0 -67
  326. package/skills/security-feedback/SKILL.md +0 -173
  327. package/skills/security-feedback/references/templates.md +0 -77
  328. package/skills/security-review/SKILL.md +0 -215
  329. package/skills/security-review/references/issue-format.md +0 -302
  330. package/skills/verify/SKILL.md +0 -118
  331. package/workflows/answer.yaml +0 -44
  332. package/workflows/build.yaml +0 -129
  333. package/workflows/cron-health.yaml +0 -6
  334. package/workflows/cron-review.yaml +0 -8
  335. package/workflows/cron-security.yaml +0 -6
  336. package/workflows/cron-triage.yaml +0 -8
  337. package/workflows/demo.yaml +0 -62
  338. package/workflows/examples/bash-smoke.yaml +0 -41
  339. package/workflows/examples/parallel-review.yaml +0 -36
  340. package/workflows/examples/tdd-loop.yaml +0 -17
  341. package/workflows/explore.yaml +0 -80
  342. package/workflows/issue-comment.yaml +0 -15
  343. package/workflows/issue-triage.yaml +0 -22
  344. package/workflows/pr-comment.yaml +0 -16
  345. package/workflows/pr-fix.yaml +0 -17
  346. package/workflows/pr-review.yaml +0 -50
  347. package/workflows/prompts/answer.md +0 -68
  348. package/workflows/prompts/architect.md +0 -44
  349. package/workflows/prompts/classifier.md +0 -60
  350. package/workflows/prompts/classify-adds-info.md +0 -17
  351. package/workflows/prompts/demo.md +0 -125
  352. package/workflows/prompts/executor.md +0 -42
  353. package/workflows/prompts/explore-ask.md +0 -99
  354. package/workflows/prompts/explore-publish.md +0 -31
  355. package/workflows/prompts/explore-read.md +0 -120
  356. package/workflows/prompts/explore-synthesize.md +0 -88
  357. package/workflows/prompts/fix.md +0 -18
  358. package/workflows/prompts/guardrails.md +0 -96
  359. package/workflows/prompts/pr-fix.md +0 -26
  360. package/workflows/prompts/pr.md +0 -39
  361. package/workflows/prompts/qa-browser.md +0 -95
  362. package/workflows/prompts/qa-synth.md +0 -44
  363. package/workflows/prompts/qa-test.md +0 -60
  364. package/workflows/prompts/re-reviewer.md +0 -33
  365. package/workflows/prompts/reviewer.md +0 -60
  366. package/workflows/prompts/verify-browser.md +0 -93
  367. package/workflows/prompts/verify-synth.md +0 -44
  368. package/workflows/prompts/verify.md +0 -63
  369. package/workflows/qa-test.yaml +0 -94
  370. package/workflows/repo-health.yaml +0 -15
  371. package/workflows/security-feedback.yaml +0 -25
  372. package/workflows/security-review.yaml +0 -37
  373. package/workflows/verify.yaml +0 -93
  374. /package/dist/{cli/cli-config.d.ts → cli-config.d.ts} +0 -0
  375. /package/dist/{cli/cli-config.js → cli-config.js} +0 -0
  376. /package/dist/{cli/cli-format.d.ts → cli-format.d.ts} +0 -0
  377. /package/dist/{cli/cli-format.js → cli-format.js} +0 -0
  378. /package/dist/{cli/cli-timeline.d.ts → cli-timeline.d.ts} +0 -0
  379. /package/dist/{cli/cli-timeline.js → cli-timeline.js} +0 -0
  380. /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
  381. /package/dist/{cli/oauth-cli.d.ts → oauth-cli.d.ts} +0 -0
  382. /package/dist/{cli/setup.d.ts → setup.d.ts} +0 -0
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Last Light Docker entrypoint
3
- # Links secrets, ensures state dirs, then starts the harness.
4
- set -euo pipefail
5
-
6
- # Secrets live in the consolidated deployment folder (instance/secrets). Fall
7
- # back to the legacy standalone secrets/ mount for pre-migration deployments.
8
- SECRETS="/app/instance/secrets"
9
- [ -d "$SECRETS" ] || SECRETS="/app/secrets"
10
- APP_DIR="/app"
11
- STATE_DIR="${STATE_DIR:-/app/data}"
12
-
13
- # Materialize secrets into the writable /app layer, owned by `lastlight` (mode
14
- # 600). We COPY rather than symlink: the source lives on a read-only mount owned
15
- # by whatever uid created it on the host (often 1000), while the harness runs as
16
- # `lastlight` (uid-pinned 10001) via gosu below. A symlink would leave the
17
- # unreadable 600 source in place and the harness would EACCES on it. root (this
18
- # entrypoint, pre-gosu) bypasses the source perms to read+copy; the chown then
19
- # hands the copy to lastlight. Same pattern as the PEM→state-volume copy below.
20
- # rm -f first so a stale symlink from an older image isn't written through.
21
- for f in .env; do
22
- if [ -f "$SECRETS/$f" ]; then
23
- rm -f "$APP_DIR/$f"
24
- cp "$SECRETS/$f" "$APP_DIR/$f"
25
- chown lastlight:lastlight "$APP_DIR/$f"
26
- chmod 600 "$APP_DIR/$f"
27
- echo "Copied $f from secrets volume"
28
- fi
29
- done
30
-
31
- # GitHub App private key(s) — same copy+chown so the in-process Octokit (which
32
- # also runs as lastlight) can read them.
33
- for pem in "$SECRETS"/*.pem; do
34
- if [ -f "$pem" ]; then
35
- dest="$APP_DIR/$(basename "$pem")"
36
- rm -f "$dest"
37
- cp "$pem" "$dest"
38
- chown lastlight:lastlight "$dest"
39
- chmod 600 "$dest"
40
- echo "Copied $(basename "$pem") from secrets volume"
41
- fi
42
- done
43
-
44
- # Ensure state directory structure exists and is owned by lastlight.
45
- # agent-sessions/ is where agentic-pi's event shim writes JSONL envelopes
46
- # the dashboard reads (path is set by LASTLIGHT_SESSIONS_DIR).
47
- mkdir -p "$STATE_DIR"/{logs,sandboxes,secrets,agent-sessions}
48
- chown -R lastlight:lastlight "$STATE_DIR"
49
-
50
- # Copy PEM to the data volume so sandbox containers can access it via shared
51
- # volume (LASTLIGHT_SANDBOX=docker fallback path only — gondolin doesn't go
52
- # through this). Owner is `lastlight` so the host harness (which exec's via
53
- # gosu lastlight below) can read it for in-process GitHub API calls.
54
- # Sandbox-entrypoint runs as root before switching to `agent`, so it can
55
- # still read this 600 file and materialize an agent-readable copy when
56
- # ALLOW_APP_PEM=1.
57
- for pem in "$SECRETS"/*.pem; do
58
- if [ -f "$pem" ]; then
59
- cp "$pem" "$STATE_DIR/secrets/app.pem"
60
- chown lastlight:lastlight "$STATE_DIR/secrets/app.pem"
61
- chmod 600 "$STATE_DIR/secrets/app.pem"
62
- echo "Copied PEM to data volume for sandbox access"
63
- break
64
- fi
65
- done
66
-
67
- # Source .env if available
68
- if [ -f "$APP_DIR/.env" ]; then
69
- set -a
70
- # shellcheck disable=SC1091
71
- source "$APP_DIR/.env"
72
- set +a
73
- fi
74
-
75
- # Fix Docker socket permissions — host GID may differ from container docker group
76
- if [ -S /var/run/docker.sock ]; then
77
- chmod 666 /var/run/docker.sock
78
- fi
79
-
80
- echo "Starting Last Light (state: $STATE_DIR)..."
81
- # Drop to non-root before exec'ing the harness.
82
- exec gosu lastlight "$@"
@@ -1,160 +0,0 @@
1
- # Native deploy (systemd + gondolin)
2
-
3
- Production deploy for `lastlight` as a systemd service on a KVM-capable
4
- Linux host. Replaces the Docker compose deploy; the Dockerfiles in the
5
- repo root are kept only for local prod-like smoke testing.
6
-
7
- ## Why native
8
-
9
- `agentic-pi`'s gondolin sandbox needs `/dev/kvm`. Running lastlight inside
10
- a container loses or complicates KVM access (Docker-in-Docker, nested
11
- virt, capability juggling). On a bare-metal Linux box you get
12
- near-native VM performance with no extra moving parts.
13
-
14
- See `agentic-pi/SPIKE-gondolin.md` for the full analysis.
15
-
16
- ## First-time install
17
-
18
- On the prod host as root:
19
-
20
- ```bash
21
- # 1. Clone the repo to the canonical path.
22
- git clone https://github.com/nearform/lastlight.git /opt/lastlight
23
- cd /opt/lastlight
24
-
25
- # 2. (Optional) Drop the GitHub App PEM in place before the install runs,
26
- # so install.sh locks it down. Otherwise place it later and re-run.
27
- install -m 0600 -o root /path/to/your-app.pem /etc/lastlight/app.pem
28
-
29
- # 3. Provision system + install systemd unit.
30
- sudo bash deploy/native/install.sh
31
-
32
- # 4. install.sh scaffolds /etc/lastlight/lastlight.env from the example
33
- # and refuses to (re)start the service while any CHANGE_ME placeholder
34
- # remains. Edit it:
35
- sudo $EDITOR /etc/lastlight/lastlight.env
36
-
37
- # 5. Re-run install.sh to start the service.
38
- sudo bash deploy/native/install.sh
39
- ```
40
-
41
- ## Re-deploying a code change
42
-
43
- ```bash
44
- cd /opt/lastlight
45
- sudo -u lastlight git pull
46
- sudo bash deploy/native/install.sh # rebuilds + restarts the service
47
- ```
48
-
49
- The existing `/home/lastlight/deploy.sh` on the prod host should be
50
- replaced with the two lines above (or wrap them in a script).
51
-
52
- ## What's where
53
-
54
- | Path | Purpose |
55
- | --- | --- |
56
- | `/opt/lastlight` | Source checkout. `node dist/index.js` runs from here. |
57
- | `/etc/lastlight/lastlight.env` | systemd EnvironmentFile (mode 0640). Holds all secrets. |
58
- | `/etc/lastlight/app.pem` | GitHub App private key (mode 0640). Optional but typical. |
59
- | `/var/lib/lastlight/` | Persistent state — SQLite DB, session JSONLs, gondolin image cache. |
60
- | `/var/lib/lastlight/lastlight.db` | Workflow runs, executions, messaging sessions. |
61
- | `/var/lib/lastlight/agent-sessions/` | Dashboard's JSONL envelope store. |
62
- | `/var/lib/lastlight/.cache/agentic-pi/images/` | Downloaded gondolin guest images (~89 MB each). |
63
- | `/var/log/lastlight/` | LogsDirectory if anything writes to it. |
64
- | `/etc/systemd/system/lastlight.service` | The unit file (installed by install.sh from `deploy/native/lastlight.service`). |
65
-
66
- ## Operations
67
-
68
- ```bash
69
- # Status / logs
70
- systemctl status lastlight
71
- journalctl -u lastlight -f # live tail
72
- journalctl -u lastlight --since '10 min ago' # recent
73
-
74
- # Restart (e.g. after an env edit that doesn't need a rebuild)
75
- sudo systemctl restart lastlight
76
-
77
- # Stop / disable
78
- sudo systemctl stop lastlight
79
- sudo systemctl disable lastlight
80
- ```
81
-
82
- ## TLS / reverse proxy
83
-
84
- The harness binds `127.0.0.1:8644` by default (PORT in the env file).
85
- Front it with Caddy or nginx for TLS.
86
-
87
- Minimal Caddy setup:
88
-
89
- ```bash
90
- sudo apt-get install -y caddy
91
- sudo tee /etc/caddy/Caddyfile <<'EOF'
92
- your-domain.example.com {
93
- reverse_proxy 127.0.0.1:8644
94
- }
95
- EOF
96
- sudo systemctl reload caddy
97
- ```
98
-
99
- ## Migrating from the Docker compose deploy
100
-
101
- On the old (Docker compose) box:
102
-
103
- ```bash
104
- # Snapshot the persistent volume.
105
- docker run --rm -v lastlight_agent-data:/data -v /tmp:/backup busybox \
106
- tar czf /backup/lastlight-data.tgz -C /data .
107
- scp /tmp/lastlight-data.tgz root@new-host:/tmp/
108
- docker compose down
109
- ```
110
-
111
- On the new (native) host, after running install.sh and editing the env file:
112
-
113
- ```bash
114
- sudo systemctl stop lastlight
115
- sudo tar xzf /tmp/lastlight-data.tgz -C /var/lib/lastlight \
116
- lastlight.db lastlight.db-shm lastlight.db-wal \
117
- agent-sessions secrets || true
118
- # The old volume used these directory names that the new code doesn't read.
119
- # Safe to skip restoring them: opencode-home/, opencode-serve/, sandboxes/,
120
- # sandbox-data/, sessions/, claude-home/.
121
- sudo chown -R lastlight:lastlight /var/lib/lastlight
122
- sudo systemctl start lastlight
123
- ```
124
-
125
- The DB schema is unchanged across the migration, so workflow_runs +
126
- executions history carry over.
127
-
128
- ## Rollback
129
-
130
- If the new service doesn't come up cleanly:
131
-
132
- ```bash
133
- sudo systemctl stop lastlight
134
- sudo systemctl disable lastlight
135
- # Restore the docker stack on the same host (or fail over to the old host)
136
- docker compose up -d
137
- ```
138
-
139
- State at `/var/lib/lastlight/` and the docker `agent-data` volume are
140
- independent — neither rollback step touches the other.
141
-
142
- ## Verification
143
-
144
- After the service is up:
145
-
146
- ```bash
147
- # 1. It started cleanly.
148
- systemctl status lastlight # → active (running)
149
-
150
- # 2. KVM is reachable from the service's user context.
151
- sudo -u lastlight ls -l /dev/kvm
152
-
153
- # 3. The harness is listening.
154
- curl -sS http://127.0.0.1:8644/admin/ | head -5
155
-
156
- # 4. (Optional) Trigger a real workflow via a PR comment and watch:
157
- journalctl -u lastlight -f
158
- # Then look in the dashboard for the executions row + a JSONL under
159
- # /var/lib/lastlight/agent-sessions/projects/<slug>/
160
- ```
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Idempotent provision + (re)deploy of lastlight as a systemd service on a
3
- # KVM-capable Linux host. Run as root from the repo root:
4
- #
5
- # sudo bash deploy/native/install.sh
6
- #
7
- # On first run: installs system deps, creates the lastlight user, scaffolds
8
- # /etc/lastlight/lastlight.env from the example, installs and starts the
9
- # systemd unit. On subsequent runs: re-installs the deps that changed,
10
- # rebuilds, restarts the service.
11
-
12
- set -euo pipefail
13
-
14
- REPO_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
15
- SERVICE_NAME=lastlight
16
- LASTLIGHT_USER=lastlight
17
- LASTLIGHT_HOME=/var/lib/lastlight
18
- ENV_DIR=/etc/lastlight
19
- ENV_FILE="${ENV_DIR}/lastlight.env"
20
- PEM_FILE="${ENV_DIR}/app.pem"
21
-
22
- if [ "$(id -u)" -ne 0 ]; then
23
- echo "install.sh must run as root (try: sudo $0)" >&2
24
- exit 1
25
- fi
26
-
27
- # ── 1. KVM check — gondolin silently hangs without it ──────────────────────
28
- if [ ! -e /dev/kvm ]; then
29
- echo "ERROR: /dev/kvm not present on this host." >&2
30
- echo " gondolin (lastlight's default sandbox) requires KVM. The host must" >&2
31
- echo " be bare-metal Linux or a VM with nested virtualization enabled." >&2
32
- echo " See agentic-pi/SPIKE-gondolin.md for the full constraint." >&2
33
- exit 1
34
- fi
35
-
36
- # ── 2. System dependencies (Debian/Ubuntu) ─────────────────────────────────
37
- export DEBIAN_FRONTEND=noninteractive
38
- apt-get update
39
- apt-get install -y --no-install-recommends \
40
- curl ca-certificates git \
41
- qemu-system-x86 qemu-utils \
42
- python3 build-essential
43
- # Use NodeSource for a current Node 22 LTS (Debian's nodejs is too old).
44
- if ! command -v node >/dev/null 2>&1 || ! node -e 'process.exit(process.versions.node.split(".")[0] >= 20 ? 0 : 1)'; then
45
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
46
- apt-get install -y --no-install-recommends nodejs
47
- fi
48
-
49
- # ── 3. User + group ────────────────────────────────────────────────────────
50
- if ! id "$LASTLIGHT_USER" >/dev/null 2>&1; then
51
- useradd --system --create-home --home-dir "$LASTLIGHT_HOME" \
52
- --shell /usr/sbin/nologin "$LASTLIGHT_USER"
53
- fi
54
- # `kvm` group exists once qemu is installed; gate the membership add to
55
- # avoid a usermod warning on hosts where it doesn't.
56
- if getent group kvm >/dev/null 2>&1; then
57
- usermod -aG kvm "$LASTLIGHT_USER"
58
- fi
59
-
60
- # ── 4. Repo install + build (as the lastlight user) ────────────────────────
61
- chown -R "$LASTLIGHT_USER:$LASTLIGHT_USER" "$REPO_DIR"
62
- sudo -u "$LASTLIGHT_USER" -H bash -c "cd '$REPO_DIR' && npm ci --no-audit --no-fund"
63
- sudo -u "$LASTLIGHT_USER" -H bash -c "cd '$REPO_DIR' && npm run build && npm run build:dashboard"
64
- # Prune dev deps to shrink the on-disk footprint of the deployed checkout.
65
- sudo -u "$LASTLIGHT_USER" -H bash -c "cd '$REPO_DIR' && npm prune --omit=dev"
66
-
67
- # ── 5. Env file scaffold (don't overwrite an existing config) ──────────────
68
- mkdir -p "$ENV_DIR"
69
- chmod 0750 "$ENV_DIR"
70
- chgrp "$LASTLIGHT_USER" "$ENV_DIR"
71
- if [ ! -f "$ENV_FILE" ]; then
72
- install -m 0640 -o root -g "$LASTLIGHT_USER" \
73
- "$REPO_DIR/deploy/native/lastlight.env.example" "$ENV_FILE"
74
- echo "" >&2
75
- echo " NOTE: $ENV_FILE was just created from the example template." >&2
76
- echo " Edit it to fill in your GitHub App, model API keys, Slack tokens, etc." >&2
77
- echo " Then re-run this script to start the service." >&2
78
- echo "" >&2
79
- fi
80
-
81
- # ── 6. App PEM — referenced by GITHUB_APP_PRIVATE_KEY_PATH in the env file ─
82
- # If you placed the PEM at /etc/lastlight/app.pem before running install.sh,
83
- # this section locks down its permissions; otherwise it's a no-op.
84
- if [ -f "$PEM_FILE" ]; then
85
- chown root:"$LASTLIGHT_USER" "$PEM_FILE"
86
- chmod 0640 "$PEM_FILE"
87
- fi
88
-
89
- # ── 7. systemd unit ────────────────────────────────────────────────────────
90
- install -m 0644 \
91
- "$REPO_DIR/deploy/native/lastlight.service" \
92
- "/etc/systemd/system/${SERVICE_NAME}.service"
93
- systemctl daemon-reload
94
-
95
- # ── 8. Enable + start ──────────────────────────────────────────────────────
96
- systemctl enable "${SERVICE_NAME}"
97
- # Only start/restart if the env file looks configured (the example has the
98
- # string CHANGE_ME everywhere a value is required).
99
- if grep -q '^[A-Z].*=CHANGE_ME' "$ENV_FILE"; then
100
- echo " $ENV_FILE still has CHANGE_ME placeholders — not (re)starting the service." >&2
101
- echo " Fill in the values and re-run install.sh." >&2
102
- exit 0
103
- fi
104
- systemctl restart "${SERVICE_NAME}"
105
-
106
- # Brief status read-out so the operator sees if it started cleanly.
107
- sleep 2
108
- systemctl status "${SERVICE_NAME}" --no-pager -l | head -20 || true
109
- echo ""
110
- echo " Live logs: journalctl -u ${SERVICE_NAME} -f"
@@ -1,95 +0,0 @@
1
- # ── Last Light — Native deploy env (systemd EnvironmentFile) ────────────
2
- # Path on the host: /etc/lastlight/lastlight.env
3
- # Mode 0640, owned by root:lastlight.
4
- #
5
- # Anything marked CHANGE_ME must be replaced before the service will start
6
- # (install.sh refuses to start the unit while placeholders are present).
7
-
8
- # ── GitHub App (required) ─────────────────────────────────────────────────
9
- GITHUB_APP_ID=CHANGE_ME
10
- GITHUB_APP_INSTALLATION_ID=CHANGE_ME
11
- # PEM is read by the harness at boot. install.sh locks down permissions if
12
- # you place it at /etc/lastlight/app.pem before running.
13
- GITHUB_APP_PRIVATE_KEY_PATH=/etc/lastlight/app.pem
14
-
15
- # Webhook receiver
16
- WEBHOOK_SECRET=CHANGE_ME
17
- # Listener port — Caddy/Nginx should reverse-proxy 443 → 127.0.0.1:PORT.
18
- PORT=8644
19
-
20
- # ── Model + provider API key ─────────────────────────────────────────────
21
- # pi-ai supports anthropic/…, openai/…, openrouter/<vendor>/<model>.
22
- LASTLIGHT_MODEL=anthropic/claude-sonnet-4-6
23
- # Set whichever matches LASTLIGHT_MODEL (or several if LASTLIGHT_MODELS
24
- # routes different phases to different providers).
25
- ANTHROPIC_API_KEY=CHANGE_ME
26
- # OPENAI_API_KEY=
27
- # OPENROUTER_API_KEY=
28
-
29
- # ── OpenTelemetry (optional, disabled by default) ────────────────────────
30
- # LASTLIGHT_OTEL_ENABLED=false
31
- # LASTLIGHT_OTEL_SERVICE_NAME=lastlight
32
- # LASTLIGHT_OTEL_INCLUDE_CONTENT=false
33
- # LASTLIGHT_OTEL_FORWARD_TO_SANDBOX=true
34
- # LASTLIGHT_OTEL_STRICT=false
35
- # OTEL_EXPORTER_OTLP_ENDPOINT=https://CHANGE_ME
36
- # OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer CHANGE_ME
37
- # docker backend routes sandbox telemetry through an in-network otel-collector
38
- # (re-exports to the endpoint above; headers stay host-side, any port works).
39
- # LASTLIGHT_OTEL_COLLECTOR_HOSTS is only used by the gondolin egress allowlist.
40
- # LASTLIGHT_OTEL_COLLECTOR_HOSTS=CHANGE_ME
41
-
42
- # Per-task model overrides (optional). JSON keyed by phase/skill name.
43
- # LASTLIGHT_MODELS={"architect":"openai/gpt-5.5","triage":"anthropic/claude-haiku-4-5-20251001"}
44
-
45
- # ── Web search (optional, explore workflow only) ─────────────────────────
46
- # Set any one to enable agentic-pi's `web_search` / `web_fetch` tools for
47
- # the explore workflow. Provider auto-detected from whichever key is set
48
- # (Tavily > Exa > Brave). Other workflows ignore these keys.
49
- # TAVILY_API_KEY=tvly-...
50
- # BRAVE_SEARCH_API_KEY=BSA...
51
- # EXA_API_KEY=...
52
-
53
- # Reasoning-effort default + per-task overrides (optional).
54
- # Values: off | minimal | low | medium | high | xhigh
55
- # LASTLIGHT_THINKING=medium
56
- # LASTLIGHT_THINKINGS={"architect":"high","reviewer":"high"}
57
-
58
- # ── Sandbox backend ───────────────────────────────────────────────────────
59
- # gondolin = agentic-pi's QEMU micro-VM (default, requires /dev/kvm).
60
- # docker = sibling docker containers (requires docker socket access).
61
- # none = unsandboxed in-process (dev only — agent reads/writes host fs).
62
- LASTLIGHT_SANDBOX=gondolin
63
-
64
- # ── State paths ───────────────────────────────────────────────────────────
65
- # systemd's StateDirectory= grants /var/lib/lastlight with the right mode.
66
- STATE_DIR=/var/lib/lastlight
67
- DB_PATH=/var/lib/lastlight/lastlight.db
68
- LASTLIGHT_SESSIONS_DIR=/var/lib/lastlight/agent-sessions
69
-
70
- # ── Admin dashboard ───────────────────────────────────────────────────────
71
- # HMAC secret for session tokens. Generate with: openssl rand -hex 32
72
- ADMIN_SECRET=CHANGE_ME
73
- # Optional: require a password for the dashboard. Leave unset to allow
74
- # anyone who can reach the listener (i.e. anyone past Caddy).
75
- # ADMIN_PASSWORD=
76
-
77
- # ── Slack (optional) ──────────────────────────────────────────────────────
78
- # Setting SLACK_BOT_TOKEN enables the chat skill and the Slack connector.
79
- # SLACK_BOT_TOKEN=xoxb-…
80
- # SLACK_APP_TOKEN=xapp-…
81
- # SLACK_DELIVERY_CHANNEL=C0123456789
82
- # SLACK_ALLOWED_USERS=U0123,U0456
83
-
84
- # Slack OAuth for dashboard login (optional)
85
- # SLACK_OAUTH_CLIENT_ID=
86
- # SLACK_OAUTH_CLIENT_SECRET=
87
- # SLACK_OAUTH_REDIRECT_URI=https://your-domain/admin/auth/slack/callback
88
- # SLACK_ALLOWED_WORKSPACE=
89
-
90
- # ── Approval gates (optional) ─────────────────────────────────────────────
91
- # Comma-separated list of gate names that should pause the runner.
92
- # APPROVAL_GATES=post_architect,post_reviewer
93
-
94
- # ── Bootstrap label (optional) ────────────────────────────────────────────
95
- # BOOTSTRAP_LABEL=lastlight:bootstrap
@@ -1,50 +0,0 @@
1
- [Unit]
2
- Description=Last Light — GitHub repository maintenance agent
3
- Documentation=https://github.com/nearform/lastlight
4
- After=network-online.target
5
- Wants=network-online.target
6
-
7
- [Service]
8
- Type=simple
9
- User=lastlight
10
- Group=lastlight
11
- # gondolin needs /dev/kvm — group `kvm` is the standard accessor on Debian/Ubuntu.
12
- SupplementaryGroups=kvm
13
-
14
- WorkingDirectory=/opt/lastlight
15
- ExecStart=/usr/bin/node /opt/lastlight/dist/index.js
16
- EnvironmentFile=/etc/lastlight/lastlight.env
17
-
18
- # HOME drives where agentic-pi caches the gondolin guest image
19
- # (~/.cache/agentic-pi/images/). Pin it to the StateDirectory so the cache
20
- # survives redeploys and isn't recomputed on every restart.
21
- Environment=HOME=/var/lib/lastlight
22
- Environment=NODE_ENV=production
23
-
24
- Restart=on-failure
25
- RestartSec=5s
26
- LimitNOFILE=65536
27
- # 2 GB matches the docker-compose limit. Bursts during webhook fan-out +
28
- # in-process pi-ai chat; per-task gondolin VMs have their own memory budget.
29
- MemoryMax=2G
30
-
31
- # /var/lib/lastlight created with mode 0750, owned by lastlight:lastlight
32
- StateDirectory=lastlight
33
- StateDirectoryMode=0750
34
- LogsDirectory=lastlight
35
- LogsDirectoryMode=0750
36
-
37
- # Security hardening — the harness only writes to its state dir and logs.
38
- NoNewPrivileges=true
39
- PrivateTmp=true
40
- ProtectSystem=strict
41
- ProtectHome=true
42
- ProtectKernelTunables=true
43
- ProtectKernelModules=true
44
- ProtectControlGroups=true
45
- ReadWritePaths=/var/lib/lastlight /var/log/lastlight
46
- # Explicit /dev/kvm rule — ProtectSystem=strict alone doesn't gate /dev.
47
- DeviceAllow=/dev/kvm rw
48
-
49
- [Install]
50
- WantedBy=multi-user.target
@@ -1,93 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Sandbox container entrypoint — runs as root after volumes are mounted.
3
- # Used only when LASTLIGHT_SANDBOX=docker (the gondolin sandbox runs in
4
- # process and doesn't go through this script). Fixes permissions, writes
5
- # AGENTS.md, sets up git identity, then drops to the agent user via gosu.
6
- set -euo pipefail
7
-
8
- AGENT_HOME="/home/agent"
9
- WORKSPACE="$AGENT_HOME/workspace"
10
- # LASTLIGHT_WORKSPACE and LASTLIGHT_GIT_CREDENTIALS are set as image-level
11
- # ENV in sandbox.Dockerfile so they're visible to every `docker exec` call
12
- # the harness makes (not just the entrypoint's PID-1 tree). They show up in
13
- # this shell's env via that mechanism; no export needed here.
14
-
15
- # ── Fix workspace ownership (bind-mounts may be root-owned on macOS) ──
16
- chown -R agent:agent "$WORKSPACE" 2>/dev/null || true
17
- chown agent:agent "$AGENT_HOME"
18
-
19
- # Ensure ~/.config exists and is agent-owned before any root-side mkdir
20
- # below claims it (some tools write here at startup).
21
- mkdir -p "$AGENT_HOME/.config"
22
- chown agent:agent "$AGENT_HOME/.config"
23
-
24
- # ── Shared package-manager cache (issue #107) ──
25
- # /cache is a Docker named volume shared across every sandbox so npm / pnpm /
26
- # yarn reuse already-downloaded tarballs. A freshly-created volume is root-owned,
27
- # and a cache seeded by an older image (when `agent` had a different UID) leaves
28
- # children owned by the wrong UID — which blocks npm/pnpm/yarn writes (e.g.
29
- # /cache/npm/_logs). Self-heal: if the subdir OR any immediate child isn't
30
- # agent-owned, chown -R it. The recursive pass runs only when something is
31
- # actually wrong, so a warm, correctly-owned cache stays cheap.
32
- if [ -d /cache ]; then
33
- for sub in npm pnpm yarn uv; do
34
- mkdir -p "/cache/$sub"
35
- if [ -n "$(find "/cache/$sub" -maxdepth 1 ! -user agent -print -quit 2>/dev/null)" ]; then
36
- chown -R agent:agent "/cache/$sub" 2>/dev/null || true
37
- fi
38
- done
39
- fi
40
-
41
- # ── App PEM: keep shared copy unreadable by the agent by default ──
42
- if [ -f /data/secrets/app.pem ]; then
43
- chmod 600 /data/secrets/app.pem 2>/dev/null || true
44
- fi
45
-
46
- # Optionally materialize an agent-readable PEM for high-trust runs only.
47
- # The harness forwards GITHUB_APP_PRIVATE_KEY_PATH into the sandbox env so
48
- # any tool inside that needs the App key can find it.
49
- if [ "${ALLOW_APP_PEM:-0}" = "1" ] && [ -f /data/secrets/app.pem ]; then
50
- cp /data/secrets/app.pem "$AGENT_HOME/.config/app.pem"
51
- chown agent:agent "$AGENT_HOME/.config/app.pem"
52
- chmod 600 "$AGENT_HOME/.config/app.pem"
53
- export GITHUB_APP_PRIVATE_KEY_PATH="$AGENT_HOME/.config/app.pem"
54
- else
55
- export GITHUB_APP_PRIVATE_KEY_PATH=""
56
- fi
57
-
58
- # ── AGENTS.md — agentic-pi auto-loads this from cwd as the agent's
59
- # system context (same convention as CLAUDE.md). ──
60
- if [ ! -f "$WORKSPACE/AGENTS.md" ]; then
61
- cat /app/agent-context/*.md > "$WORKSPACE/AGENTS.md" 2>/dev/null || true
62
- fi
63
- chown agent:agent "$WORKSPACE/AGENTS.md" 2>/dev/null || true
64
-
65
- # ── Git identity and auth (system-wide so it applies regardless of exec user) ──
66
- git config --system user.name "last-light[bot]"
67
- git config --system user.email "last-light[bot]@users.noreply.github.com"
68
-
69
- if [ -n "${GIT_TOKEN:-}" ]; then
70
- # Reject tokens containing characters that would break the URL line in
71
- # the credentials file (newline, '@', ':', '/', whitespace). Real GitHub
72
- # tokens are alphanumeric + underscore — the wider charset here is
73
- # defensive against future format changes.
74
- if ! printf %s "$GIT_TOKEN" | grep -Eq '^[A-Za-z0-9_-]+$'; then
75
- echo "ERROR: GIT_TOKEN contains characters outside [A-Za-z0-9_-]; refusing to write credentials file" >&2
76
- exit 1
77
- fi
78
- # Write the file as the agent user (mode 600) so the helper can read it
79
- # after the entrypoint drops privileges. Path is set by us above; the
80
- # `store --file=<path>` value goes into git's config as argv-split (no
81
- # shell), so the only constraint is no-whitespace in the path.
82
- install -m 600 -o agent -g agent /dev/null "$LASTLIGHT_GIT_CREDENTIALS"
83
- printf 'https://x-access-token:%s@github.com\n' "$GIT_TOKEN" > "$LASTLIGHT_GIT_CREDENTIALS"
84
- chown agent:agent "$LASTLIGHT_GIT_CREDENTIALS"
85
- git config --system credential.helper "store --file=$LASTLIGHT_GIT_CREDENTIALS"
86
- fi
87
-
88
- # ── Sentinel for harness waitForReady() ──
89
- touch "$WORKSPACE/.ready"
90
- chown agent:agent "$WORKSPACE/.ready"
91
-
92
- # ── Drop to agent user ──
93
- exec gosu agent "$@"
@@ -1,51 +0,0 @@
1
- import type { Context, Next } from "hono";
2
- /**
3
- * Grace window past `exp` in which a token can still be *refreshed* (but not
4
- * used to authenticate a normal request). Lets an active user whose token
5
- * lapsed briefly slide their session forward via the refresh route instead of
6
- * being bounced back through a full login. Beyond this, they must re-login.
7
- */
8
- export declare const REFRESH_GRACE_SECONDS: number;
9
- export declare function createToken(secret: string, method?: "password" | "slack" | "github"): string;
10
- export declare function verifyToken(token: string, secret: string): boolean;
11
- /**
12
- * Like `verifyToken`, but also accepts a token that expired within the last
13
- * `REFRESH_GRACE_SECONDS`. Used ONLY by the refresh route so a briefly-lapsed
14
- * session can be renewed. The signature must still be valid (we minted it), so
15
- * this can't be forged.
16
- */
17
- export declare function verifyTokenForRefresh(token: string, secret: string): boolean;
18
- export interface TokenPayload {
19
- exp: number;
20
- method?: "password" | "slack" | "github";
21
- }
22
- /**
23
- * Decode a token's payload WITHOUT verifying its signature or expiry — used to
24
- * carry the login `method` across a refresh. Callers that need authenticity
25
- * must still gate on `verifyToken` (the refresh route runs behind
26
- * `authMiddleware`, which does exactly that). Returns null if unparseable.
27
- */
28
- export declare function decodeToken(token: string): TokenPayload | null;
29
- /**
30
- * Whether any login method is configured — an admin password OR a working OAuth
31
- * provider. The single source of truth for "is auth on", shared by the admin
32
- * dashboard and the `/api/*` trigger routes so they can't drift. The GitHub
33
- * gate, like the OAuth route, also requires an allowed-org to be set.
34
- */
35
- export declare function authIsEnabled(opts: {
36
- adminPassword?: string;
37
- slackOAuthClientId?: string;
38
- slackOAuthClientSecret?: string;
39
- githubOAuthClientId?: string;
40
- githubOAuthClientSecret?: string;
41
- githubAllowedOrg?: string;
42
- }): boolean;
43
- /**
44
- * Gate the admin API. `enabled` should be true when ANY login method is
45
- * configured — an admin password OR a working OAuth provider. Gating only on
46
- * the password (the old behaviour) left the dashboard fully open whenever the
47
- * password was cleared, even with Slack/GitHub OAuth set up.
48
- */
49
- export declare function authMiddleware(enabled: boolean, secret: string): (c: Context, next: Next) => Promise<void | (Response & import("hono").TypedResponse<{
50
- error: string;
51
- }, 401, "json">)>;