Repository navigation
Expand file tree
/
Copy pathbuild.mill
More file actions
125 lines (117 loc) · 5.73 KB
/
Copy pathbuild.mill
File metadata and controls
125 lines (117 loc) · 5.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//| mill-version: 1.3.0
//| mill-jvm-version: system
package build
import mill.*, scalalib.*
import mill.scalalib.scalafmt.ScalafmtModule
object Versions:
val atc = "0.2.2"
val scala = "3.10.2-RC1-bin-20261015-88be2f5-NIGHTLY"
val jline = "4.4.8"
val upickle = "4.4.3"
val openai = "4.81.0"
val anthropic = "2.72.0"
val okhttp = "4.12.0" // Matches the provider SDKs; call cancellation needs its public API.
val munit = "1.3.6"
val scalaNightlies = "https://repo.scala-lang.org/artifactory/maven-nightlies/"
/** Shared settings; `ScalafmtModule` gives `./mill __.reformat` and `./mill __.checkFormat` (see `.scalafmt.conf`). */
trait AtcScalaModule extends ScalaModule, ScalafmtModule:
def scalaVersion = Versions.scala
def repositories = Seq(scalaNightlies)
def scalacOptions = Seq(
"-deprecation", "-feature", "-unchecked",
"-release:17",
"-language:experimental.modularity",
"-Yexplicit-nulls",
"-Wsafe-init",
)
trait AtcTests extends ScalaTests, TestModule.Munit, ScalafmtModule:
def mvnDeps = Seq(mvn"org.scalameta::munit:${Versions.munit}")
/** The capability-safe API visible to agent code (`atc.lib.Interface`).
* Compiled with capture checking; every agent-facing definition is
* `@assumeSafe`. The app implements the interface; the sandbox shares these
* classes with the app through a filtering class loader. */
object lib extends AtcScalaModule:
def scalacOptions = super.scalacOptions() ++ Seq(
"-language:experimental.captureChecking",
)
/** The agent-facing API source; the app bundles it for the system prompt. */
def interfaceSource = Task.Source("src/atc/lib/Interface.scala")
object test extends AtcTests
/** The agent application: TUI, LLM providers, permission policy, sandbox host. */
object app extends AtcScalaModule:
def moduleDeps = Seq(lib)
def mainClass = Some("atc.Main")
/** No self-executing shell prefix on `atc.jar`: every launcher runs `java -jar`, and the
* assembly has more zip entries (> 65535) than Mill allows a prefixed jar to carry. */
def prependShellScript = ""
def mvnDeps = Seq(
mvn"org.scala-lang::scala3-compiler:${Versions.scala}",
mvn"org.scala-lang::scala3-repl:${Versions.scala}",
mvn"org.jline:jline-reader:${Versions.jline}",
mvn"org.jline:jline-terminal-jni:${Versions.jline}",
mvn"com.lihaoyi::upickle:${Versions.upickle}",
mvn"com.openai:openai-java:${Versions.openai}",
mvn"com.anthropic:anthropic-java:${Versions.anthropic}",
mvn"com.squareup.okhttp3:okhttp:${Versions.okhttp}",
)
/** The REPL compiler needs the library and the Scala stdlib as jars/dirs on
* its `-classpath` (the same artifacts the app runs with). */
def libClasspathArg = Task {
val cp = lib.runClasspath().map(_.path).filter(os.exists(_)).map(_.toString).mkString(java.io.File.pathSeparator)
s"-Datc.lib.classpath=$cp"
}
/** Bundle the agent-facing API source (for the system prompt) and the version
* (for the banner and `--version`). The `--init` config template lives in
* `app/resources/atc/config-template.json`. */
def resources = Task {
os.copy(lib.interfaceSource().path, Task.dest / "atc" / "Interface.scala.txt", createFolders = true)
os.write(Task.dest / "atc" / "version.txt", Versions.atc)
super.resources() ++ Seq(PathRef(Task.dest))
}
def forkArgs = Task { Seq(libClasspathArg(), "-Dfile.encoding=UTF-8") }
object test extends AtcTests:
def forkArgs = Task { Seq(app.libClasspathArg(), "-Dfile.encoding=UTF-8") }
// Mill's dynamic test queue can let several Windows workers claim the same
// suite. Besides duplicating work, that starts several compiler-heavy REPLs.
def testParallelism = !scala.util.Properties.isWin
// Avoid Windows' command-line length limit for the forked test JVM.
def testUseArgsFile = scala.util.Properties.isWin
def windowsPowerShellSource = Task.Source("windows/atc.ps1")
/** Build a self-contained distribution under `out/dist.dest/`. */
def dist = Task {
val libJar = lib.assembly().path
val appJar = app.assembly().path
os.copy(libJar, Task.dest / "atc-lib.jar", replaceExisting = true)
os.copy(appJar, Task.dest / "atc.jar", replaceExisting = true)
os.write.over(Task.dest / "version.txt", Versions.atc)
val script =
s"""#!/usr/bin/env sh
|DIR=$$(cd "$$(dirname "$$0")" && pwd)
|# shellcheck disable=SC2086
|# JVM defaults as in the `atc` wrapper (the same list is repeated in atc, start.sh, start.ps1
|# and windows/atc.ps1); ATC_JAVA_OPTS comes later and wins.
|exec java -Xms256m -Xmx2g -Xss4m -XX:-UsePerfData $${ATC_JAVA_OPTS:-} -Dfile.encoding=UTF-8 -Datc.version=${Versions.atc} -Datc.lib.classpath="$$DIR/atc-lib.jar" -jar "$$DIR/atc.jar" "$$@"
|""".stripMargin
if scala.util.Properties.isWin then os.write.over(Task.dest / "atc", script)
else os.write.over(Task.dest / "atc", script, perms = "rwxr-xr-x")
val windowsPowerShell =
os.read(windowsPowerShellSource().path).replace("@ATC_VERSION@", Versions.atc)
os.write.over(Task.dest / "atc.ps1", windowsPowerShell)
val windowsScript =
"""@echo off
|setlocal DisableDelayedExpansion
|set "ATC_POWERSHELL=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"
|if not exist "%ATC_POWERSHELL%" (
| echo atc: Windows PowerShell was not found at "%ATC_POWERSHELL%". 1>&2
| exit /b 1
|)
|if not exist "%~dp0atc.ps1" (
| echo atc: atc.ps1 is missing beside "%~f0". Download atc.ps1, atc.cmd, atc.jar, and atc-lib.jar into the same directory. 1>&2
| exit /b 1
|)
|"%ATC_POWERSHELL%" -NoProfile -ExecutionPolicy Bypass -File "%~dp0atc.ps1" %*
|exit /b %errorlevel%
|""".stripMargin.replace("\n", "\r\n")
os.write.over(Task.dest / "atc.cmd", windowsScript)
PathRef(Task.dest)
}