@straiffi/archon 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +224 -0
- package/dist/cli.js +216 -0
- package/dist/client/assets/index-8_-boBBA.css +2 -0
- package/dist/client/assets/index-s_jjeqha.js +176 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/client/favicon.svg +62 -0
- package/dist/client/icons.svg +24 -0
- package/dist/client/index.html +14 -0
- package/dist/server/db.js +764 -0
- package/dist/server/db.js.map +1 -0
- package/dist/server/index.js +5134 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lib/agent.js +1302 -0
- package/dist/server/lib/agent.js.map +1 -0
- package/dist/server/lib/buildChains.js +2 -0
- package/dist/server/lib/buildChains.js.map +1 -0
- package/dist/server/lib/buildFlow.js +59 -0
- package/dist/server/lib/buildFlow.js.map +1 -0
- package/dist/server/lib/buildSequences.js +599 -0
- package/dist/server/lib/buildSequences.js.map +1 -0
- package/dist/server/lib/bundleActivity.js +95 -0
- package/dist/server/lib/bundleActivity.js.map +1 -0
- package/dist/server/lib/bundlePullRequests.js +126 -0
- package/dist/server/lib/bundlePullRequests.js.map +1 -0
- package/dist/server/lib/chatMessages.js +60 -0
- package/dist/server/lib/chatMessages.js.map +1 -0
- package/dist/server/lib/chatTargets.js +123 -0
- package/dist/server/lib/chatTargets.js.map +1 -0
- package/dist/server/lib/chatTicketProposals.js +180 -0
- package/dist/server/lib/chatTicketProposals.js.map +1 -0
- package/dist/server/lib/chats.js +279 -0
- package/dist/server/lib/chats.js.map +1 -0
- package/dist/server/lib/config.js +3 -0
- package/dist/server/lib/config.js.map +1 -0
- package/dist/server/lib/cors.js +30 -0
- package/dist/server/lib/cors.js.map +1 -0
- package/dist/server/lib/directoryPicker.js +174 -0
- package/dist/server/lib/directoryPicker.js.map +1 -0
- package/dist/server/lib/git.js +1284 -0
- package/dist/server/lib/git.js.map +1 -0
- package/dist/server/lib/integrations/github.js +511 -0
- package/dist/server/lib/integrations/github.js.map +1 -0
- package/dist/server/lib/integrations/index.js +162 -0
- package/dist/server/lib/integrations/index.js.map +1 -0
- package/dist/server/lib/integrations/jira.js +283 -0
- package/dist/server/lib/integrations/jira.js.map +1 -0
- package/dist/server/lib/integrations/planning.js +27 -0
- package/dist/server/lib/integrations/planning.js.map +1 -0
- package/dist/server/lib/integrations/types.js +2 -0
- package/dist/server/lib/integrations/types.js.map +1 -0
- package/dist/server/lib/lightweightPrompt.js +88 -0
- package/dist/server/lib/lightweightPrompt.js.map +1 -0
- package/dist/server/lib/models.js +219 -0
- package/dist/server/lib/models.js.map +1 -0
- package/dist/server/lib/preview.js +377 -0
- package/dist/server/lib/preview.js.map +1 -0
- package/dist/server/lib/previewProxy.js +659 -0
- package/dist/server/lib/previewProxy.js.map +1 -0
- package/dist/server/lib/projectAutoConfig.js +682 -0
- package/dist/server/lib/projectAutoConfig.js.map +1 -0
- package/dist/server/lib/projectFileSuggestions.js +133 -0
- package/dist/server/lib/projectFileSuggestions.js.map +1 -0
- package/dist/server/lib/projectMemory.js +1519 -0
- package/dist/server/lib/projectMemory.js.map +1 -0
- package/dist/server/lib/projectMemoryPrompt.js +390 -0
- package/dist/server/lib/projectMemoryPrompt.js.map +1 -0
- package/dist/server/lib/projectMemoryScan.js +681 -0
- package/dist/server/lib/projectMemoryScan.js.map +1 -0
- package/dist/server/lib/projectMemorySuggestions.js +166 -0
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -0
- package/dist/server/lib/projectMemoryTransfer.js +958 -0
- package/dist/server/lib/projectMemoryTransfer.js.map +1 -0
- package/dist/server/lib/projects.js +569 -0
- package/dist/server/lib/projects.js.map +1 -0
- package/dist/server/lib/promptSkills.js +28 -0
- package/dist/server/lib/promptSkills.js.map +1 -0
- package/dist/server/lib/queue.js +15 -0
- package/dist/server/lib/queue.js.map +1 -0
- package/dist/server/lib/reviewFindings.js +390 -0
- package/dist/server/lib/reviewFindings.js.map +1 -0
- package/dist/server/lib/run.js +416 -0
- package/dist/server/lib/run.js.map +1 -0
- package/dist/server/lib/runtimePaths.js +93 -0
- package/dist/server/lib/runtimePaths.js.map +1 -0
- package/dist/server/lib/shell.js +27 -0
- package/dist/server/lib/shell.js.map +1 -0
- package/dist/server/lib/skills.js +124 -0
- package/dist/server/lib/skills.js.map +1 -0
- package/dist/server/lib/startDev.js +18 -0
- package/dist/server/lib/startDev.js.map +1 -0
- package/dist/server/lib/staticClient.js +80 -0
- package/dist/server/lib/staticClient.js.map +1 -0
- package/dist/server/lib/terminal.js +366 -0
- package/dist/server/lib/terminal.js.map +1 -0
- package/dist/server/lib/ticketDependencies.js +174 -0
- package/dist/server/lib/ticketDependencies.js.map +1 -0
- package/dist/server/lib/ticketMessages.js +65 -0
- package/dist/server/lib/ticketMessages.js.map +1 -0
- package/dist/server/lib/ticketOpenQuestions.js +128 -0
- package/dist/server/lib/ticketOpenQuestions.js.map +1 -0
- package/dist/server/lib/ticketUndo.js +549 -0
- package/dist/server/lib/ticketUndo.js.map +1 -0
- package/dist/server/lib/tickets.js +981 -0
- package/dist/server/lib/tickets.js.map +1 -0
- package/dist/server/lib/types.js +2 -0
- package/dist/server/lib/types.js.map +1 -0
- package/dist/server/package.json +3 -0
- package/dist/server/workers/build.js +229 -0
- package/dist/server/workers/build.js.map +1 -0
- package/dist/server/workers/chat.js +190 -0
- package/dist/server/workers/chat.js.map +1 -0
- package/dist/server/workers/followUp.js +204 -0
- package/dist/server/workers/followUp.js.map +1 -0
- package/dist/server/workers/plan.js +1130 -0
- package/dist/server/workers/plan.js.map +1 -0
- package/dist/server/workers/planFollowUp.js +360 -0
- package/dist/server/workers/planFollowUp.js.map +1 -0
- package/dist/server/workers/review.js +167 -0
- package/dist/server/workers/review.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Archon
|
|
2
|
+
|
|
3
|
+
A ticket-driven interface for AI-assisted code automation. Create tickets, plan features, then let an AI agent (Claude or OpenCode) implement them in isolated git worktrees.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Archon ships as a global npm package:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @straiffi/archon
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then start it with:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
archon
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Archon will:
|
|
20
|
+
|
|
21
|
+
1. start the backend and frontend as one local process
|
|
22
|
+
2. serve the UI and API from the same origin
|
|
23
|
+
3. open your default browser automatically
|
|
24
|
+
4. stay attached until you press `Ctrl+C`
|
|
25
|
+
|
|
26
|
+
Typical startup output:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
Archon running at http://localhost:43110
|
|
30
|
+
Press Ctrl+C to stop.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Archon starts on port `43110` by default and scans upward if that port is already in use.
|
|
34
|
+
|
|
35
|
+
### Update
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
archon update
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Supported platforms
|
|
42
|
+
|
|
43
|
+
V1 targets macOS and Linux first.
|
|
44
|
+
|
|
45
|
+
## External tool prerequisites
|
|
46
|
+
|
|
47
|
+
Archon does not bundle external developer tools.
|
|
48
|
+
|
|
49
|
+
Required or commonly expected tools:
|
|
50
|
+
|
|
51
|
+
| Tool | Why it matters |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `git` | Required for project repositories, worktrees, diffing, and commit operations |
|
|
54
|
+
| `opencode` | Required for OpenCode plan/build/chat/review workflows |
|
|
55
|
+
| `claude` | Required for Claude-backed workflows |
|
|
56
|
+
| `gh` | Required for GitHub PR and review integration |
|
|
57
|
+
|
|
58
|
+
If one of the optional AI tools is missing, Archon should still start, but features that depend on that binary will return actionable errors.
|
|
59
|
+
|
|
60
|
+
## How it works
|
|
61
|
+
|
|
62
|
+
Tickets move through three states:
|
|
63
|
+
|
|
64
|
+
1. **Plan** — write a title and description for the feature or task
|
|
65
|
+
2. **Build** — the server spawns your configured AI tool in a new git worktree on a dedicated branch; real-time logs stream back to the UI
|
|
66
|
+
3. **Review** — an agent conducts a code review and fixes any issues found
|
|
67
|
+
|
|
68
|
+
The frontend is a React kanban board. The backend is an Express server with a SQLite database and Socket.io for live status updates.
|
|
69
|
+
|
|
70
|
+
## Development
|
|
71
|
+
|
|
72
|
+
For normal source development, use the repo checkout workflow instead of the packaged CLI.
|
|
73
|
+
|
|
74
|
+
Install dependencies from the repository root:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Start dev mode:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run start:dev
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`npm run start:dev` is the primary local development command. It keeps the Express server and Vite frontend as separate processes and configures the dev database plus API base URL overrides for you.
|
|
87
|
+
|
|
88
|
+
Other useful commands:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm run build
|
|
92
|
+
npm test
|
|
93
|
+
npm run smoke:package
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`npm run smoke:package` builds the package, installs the packed tarball into a temporary npm prefix, runs `archon --version`, starts `archon --no-open`, fetches the app shell and an API route, then shuts it down.
|
|
97
|
+
|
|
98
|
+
## Project Setup
|
|
99
|
+
|
|
100
|
+
After Archon starts, create a project from the UI:
|
|
101
|
+
|
|
102
|
+
1. Click `Create project`
|
|
103
|
+
2. Enter a project name and repository path
|
|
104
|
+
3. Optionally click `Auto-config` to inspect top-level repo files and fill any empty setup, service, or worktree sync settings
|
|
105
|
+
4. Optionally open `Advanced settings` to review or edit setup, service, worktree sync, and IDE settings
|
|
106
|
+
5. Save the project, select it in the project switcher, and create tickets inside that project
|
|
107
|
+
|
|
108
|
+
## Project configuration
|
|
109
|
+
|
|
110
|
+
Archon stores repository and run settings per project in its database. Use the project form in the UI to configure each project.
|
|
111
|
+
|
|
112
|
+
### Required fields
|
|
113
|
+
|
|
114
|
+
| Field | What to enter |
|
|
115
|
+
|-------|---------------|
|
|
116
|
+
| `Project name` | The label shown in the project switcher, such as `Platform API` or `Marketing Site` |
|
|
117
|
+
| `Repository path` | Any path inside the target git repository; Archon resolves it to the repo root before saving |
|
|
118
|
+
|
|
119
|
+
`Auto-config` is available in both the create and edit project modals. It uses the currently selected global tool, reads only top-level README/config files, and fills only empty `Setup commands`, `Services`, and `Worktree sync` sections. It never changes `IDE command`.
|
|
120
|
+
|
|
121
|
+
### Advanced fields
|
|
122
|
+
|
|
123
|
+
| Field | Guidance |
|
|
124
|
+
|-------|----------|
|
|
125
|
+
| `Setup commands` | Optional newline-separated commands that run inside the new worktree before any services start. Use this for dependency install, code generation, or other one-time setup such as `pnpm install` or `pnpm db:generate`. |
|
|
126
|
+
| `Worktree sync` | Optional file sync rules that run only when Archon creates a worktree for the first time. Each entry can either copy a file or directory into the new worktree, or symlink back to the project repo. |
|
|
127
|
+
| `Services` | Optional background processes to launch after setup completes. Each service needs a command, and can also include a `cwd`. |
|
|
128
|
+
| `cwd` | Optional working directory for a service, relative to the worktree root. For example, use `client` for a frontend dev server or `server` for an API process. Leave it blank to run from the worktree root. |
|
|
129
|
+
| `IDE command` | Optional command to open the finished worktree in your editor after services start, such as `code --reuse-window` or `idea`. |
|
|
130
|
+
|
|
131
|
+
Worktree sync entries are stored as `{ source, target?, mode }` objects. `source` is required, `target` is optional and defaults to the same path, and `mode` is either `copy` or `symlink`.
|
|
132
|
+
|
|
133
|
+
Service entries are stored as `{ cmd, cwd }` objects. `cmd` is required, while `cwd` is optional.
|
|
134
|
+
|
|
135
|
+
### Example project setup
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
Project name: My App
|
|
139
|
+
Repository path: /Users/you/projects/my-app
|
|
140
|
+
Setup commands:
|
|
141
|
+
pnpm install
|
|
142
|
+
pnpm db:generate
|
|
143
|
+
|
|
144
|
+
Worktree sync:
|
|
145
|
+
- source: node_modules
|
|
146
|
+
mode: symlink
|
|
147
|
+
- source: .env
|
|
148
|
+
mode: copy
|
|
149
|
+
|
|
150
|
+
Services:
|
|
151
|
+
- cmd: pnpm dev
|
|
152
|
+
cwd: client
|
|
153
|
+
- cmd: pnpm dev
|
|
154
|
+
cwd: server
|
|
155
|
+
|
|
156
|
+
IDE command: code --reuse-window
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Each ticket gets its own worktree and branch derived from the ticket title, using the active project's repository settings. Archon reuses an existing local branch when one already exists; otherwise it creates a new branch for the worktree.
|
|
160
|
+
|
|
161
|
+
## Data And Migration
|
|
162
|
+
|
|
163
|
+
Archon keeps runtime state out of the installed package tree.
|
|
164
|
+
|
|
165
|
+
Default database locations:
|
|
166
|
+
|
|
167
|
+
| Platform | Default path |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| macOS | `~/Library/Application Support/Archon/archon.db` |
|
|
170
|
+
| Linux | `~/.local/share/archon/archon.db` |
|
|
171
|
+
|
|
172
|
+
You can override the database location with `DB_PATH`.
|
|
173
|
+
|
|
174
|
+
### Migrating from an existing dev database
|
|
175
|
+
|
|
176
|
+
Older local development runs may already have data in one of these files:
|
|
177
|
+
|
|
178
|
+
- `server/archon-dev.db`
|
|
179
|
+
- `server/archon.db`
|
|
180
|
+
|
|
181
|
+
Recommended manual migration flow:
|
|
182
|
+
|
|
183
|
+
1. Stop all Archon dev and production processes.
|
|
184
|
+
2. Back up the source database and any current production database.
|
|
185
|
+
3. Create the production app-data directory if it does not exist.
|
|
186
|
+
4. Copy the chosen development database to the production database path.
|
|
187
|
+
5. If SQLite sidecar files such as `-wal` or `-shm` exist, only copy them while Archon is fully stopped.
|
|
188
|
+
6. Start Archon normally and let its startup schema checks apply any missing migrations.
|
|
189
|
+
|
|
190
|
+
If you want to run production Archon against an existing database temporarily, launch it with:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
DB_PATH=/path/to/archon-dev.db archon
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Release Process
|
|
197
|
+
|
|
198
|
+
Archon uses a manual GitHub Actions release workflow.
|
|
199
|
+
|
|
200
|
+
### GitHub setup
|
|
201
|
+
|
|
202
|
+
1. Publish the npm package scope or package name from the npm account or organization that owns `@straiffi/archon`.
|
|
203
|
+
2. Add an `NPM_TOKEN` repository secret in GitHub with publish access to that package.
|
|
204
|
+
3. Run the `Release` workflow from the `main` branch.
|
|
205
|
+
4. Choose one of `patch`, `minor`, or `major` for `release_type`.
|
|
206
|
+
|
|
207
|
+
The workflow will:
|
|
208
|
+
|
|
209
|
+
1. install dependencies
|
|
210
|
+
2. run type checks, lint, tests, build, tarball validation, and packaged-install smoke checks
|
|
211
|
+
3. run `npm version <release_type>`
|
|
212
|
+
4. publish with `npm publish --access public`
|
|
213
|
+
5. push the version commit and git tag back to GitHub
|
|
214
|
+
|
|
215
|
+
### Local release dry run
|
|
216
|
+
|
|
217
|
+
Before running the workflow, you can validate the release path locally with:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
npm install
|
|
221
|
+
npm run build
|
|
222
|
+
npm run smoke:package
|
|
223
|
+
npm pack --dry-run
|
|
224
|
+
```
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createServer } from 'node:net'
|
|
4
|
+
import { spawn } from 'node:child_process'
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
6
|
+
import { dirname, resolve } from 'node:path'
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
|
+
|
|
9
|
+
const CURRENT_FILE_PATH = fileURLToPath(import.meta.url)
|
|
10
|
+
const CURRENT_DIRECTORY = dirname(CURRENT_FILE_PATH)
|
|
11
|
+
const PACKAGE_JSON_PATH = resolve(CURRENT_DIRECTORY, '../package.json')
|
|
12
|
+
const DIST_SERVER_ENTRY_PATH = resolve(CURRENT_DIRECTORY, './server/index.js')
|
|
13
|
+
const DEFAULT_PORT = 43110
|
|
14
|
+
const PACKAGE_NAME = '@straiffi/archon'
|
|
15
|
+
|
|
16
|
+
const readPackageVersion = () => {
|
|
17
|
+
try {
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8'))
|
|
19
|
+
return typeof packageJson?.version === 'string' ? packageJson.version : '0.0.0'
|
|
20
|
+
} catch {
|
|
21
|
+
return '0.0.0'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const printUsage = () => {
|
|
26
|
+
console.log(`Usage: archon [--port <port>] [--no-open]\n archon update\n archon --version`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const parsePort = (value) => {
|
|
30
|
+
const parsed = Number(value)
|
|
31
|
+
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
32
|
+
throw new Error(`Invalid port: ${value}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return parsed
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const parseArgs = (argv) => {
|
|
39
|
+
let command = 'start'
|
|
40
|
+
let port = DEFAULT_PORT
|
|
41
|
+
let shouldOpenBrowser = true
|
|
42
|
+
|
|
43
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
44
|
+
const argument = argv[index]
|
|
45
|
+
|
|
46
|
+
if (argument === 'update') {
|
|
47
|
+
command = 'update'
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (argument === '--version' || argument === '-v') {
|
|
52
|
+
command = 'version'
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (argument === '--help' || argument === '-h') {
|
|
57
|
+
command = 'help'
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (argument === '--no-open') {
|
|
62
|
+
shouldOpenBrowser = false
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (argument === '--port') {
|
|
67
|
+
const nextValue = argv[index + 1]
|
|
68
|
+
if (!nextValue) {
|
|
69
|
+
throw new Error('--port requires a value')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
port = parsePort(nextValue)
|
|
73
|
+
index += 1
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new Error(`Unknown argument: ${argument}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
command,
|
|
82
|
+
port,
|
|
83
|
+
shouldOpenBrowser,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const canUsePort = (port) => {
|
|
88
|
+
return new Promise(resolvePromise => {
|
|
89
|
+
const probeServer = createServer()
|
|
90
|
+
|
|
91
|
+
probeServer.once('error', () => {
|
|
92
|
+
resolvePromise(false)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
probeServer.once('listening', () => {
|
|
96
|
+
probeServer.close(() => {
|
|
97
|
+
resolvePromise(true)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
probeServer.listen(port, '127.0.0.1')
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const findAvailablePort = async (startingPort) => {
|
|
106
|
+
let port = startingPort
|
|
107
|
+
|
|
108
|
+
while (!(await canUsePort(port))) {
|
|
109
|
+
port += 1
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return port
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const openBrowser = (url) => {
|
|
116
|
+
const openCommand = process.platform === 'darwin'
|
|
117
|
+
? ['open', [url]]
|
|
118
|
+
: process.platform === 'win32'
|
|
119
|
+
? ['cmd', ['/c', 'start', '', url]]
|
|
120
|
+
: ['xdg-open', [url]]
|
|
121
|
+
|
|
122
|
+
const [command, args] = openCommand
|
|
123
|
+
|
|
124
|
+
return new Promise(resolvePromise => {
|
|
125
|
+
const child = spawn(command, args, {
|
|
126
|
+
stdio: 'ignore',
|
|
127
|
+
detached: true,
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
child.on('error', () => resolvePromise(false))
|
|
131
|
+
child.unref()
|
|
132
|
+
resolvePromise(true)
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const runUpdate = () => {
|
|
137
|
+
return new Promise(resolvePromise => {
|
|
138
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
139
|
+
const child = spawn(npmCommand, ['install', '-g', `${PACKAGE_NAME}@latest`], {
|
|
140
|
+
stdio: 'inherit',
|
|
141
|
+
env: process.env,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
child.on('error', () => {
|
|
145
|
+
console.error(`Update failed. Run: npm install -g ${PACKAGE_NAME}@latest`)
|
|
146
|
+
resolvePromise(1)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
child.on('exit', code => {
|
|
150
|
+
if (code && code !== 0) {
|
|
151
|
+
console.error(`Update failed. Run: npm install -g ${PACKAGE_NAME}@latest`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
resolvePromise(code ?? 0)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const startArchon = async ({ port, shouldOpenBrowser }) => {
|
|
160
|
+
if (!existsSync(DIST_SERVER_ENTRY_PATH)) {
|
|
161
|
+
throw new Error('Archon is not built yet. Run `npm run build` before starting the packaged runtime from a source checkout.')
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
process.env.PORT = String(await findAvailablePort(port))
|
|
165
|
+
|
|
166
|
+
const serverModule = await import(pathToFileURL(DIST_SERVER_ENTRY_PATH).href)
|
|
167
|
+
if (typeof serverModule.registerShutdownHandlers === 'function') {
|
|
168
|
+
serverModule.registerShutdownHandlers()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const result = await serverModule.startServer({ port: Number(process.env.PORT) })
|
|
172
|
+
|
|
173
|
+
console.log(`Archon running at ${result.url}`)
|
|
174
|
+
console.log('Press Ctrl+C to stop.')
|
|
175
|
+
|
|
176
|
+
if (shouldOpenBrowser) {
|
|
177
|
+
const opened = await openBrowser(result.url)
|
|
178
|
+
if (!opened) {
|
|
179
|
+
console.warn(`Unable to open a browser automatically. Open ${result.url} manually.`)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const main = async () => {
|
|
185
|
+
let parsedArgs
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
parsedArgs = parseArgs(process.argv.slice(2))
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
191
|
+
printUsage()
|
|
192
|
+
process.exit(1)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (parsedArgs.command === 'help') {
|
|
196
|
+
printUsage()
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (parsedArgs.command === 'version') {
|
|
201
|
+
console.log(readPackageVersion())
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (parsedArgs.command === 'update') {
|
|
206
|
+
process.exitCode = await runUpdate()
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await startArchon(parsedArgs)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void main().catch(error => {
|
|
214
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
215
|
+
process.exit(1)
|
|
216
|
+
})
|