@shogo-ai/worker 1.7.4
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/LICENSE +21 -0
- package/README.md +179 -0
- package/bin/shogo.mjs +38 -0
- package/package.json +56 -0
- package/src/cli.ts +127 -0
- package/src/commands/config.ts +29 -0
- package/src/commands/login.ts +126 -0
- package/src/commands/logs.ts +24 -0
- package/src/commands/runtime.ts +104 -0
- package/src/commands/start.ts +252 -0
- package/src/commands/status.ts +22 -0
- package/src/commands/stop.ts +13 -0
- package/src/lib/__tests__/cloud-login.test.ts +218 -0
- package/src/lib/__tests__/config.test.ts +136 -0
- package/src/lib/__tests__/runtime-resolver.test.ts +112 -0
- package/src/lib/api-discovery.ts +36 -0
- package/src/lib/cloud-login.ts +321 -0
- package/src/lib/config.ts +63 -0
- package/src/lib/device-id.ts +27 -0
- package/src/lib/paths.ts +35 -0
- package/src/lib/preflight.ts +158 -0
- package/src/lib/process-manager.ts +123 -0
- package/src/lib/runtime-install.ts +371 -0
- package/src/lib/runtime-manager.ts +645 -0
- package/src/lib/runtime-resolver.ts +136 -0
- package/src/lib/transport.ts +202 -0
- package/src/lib/tunnel.ts +664 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shogo Technologies, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# @shogo-ai/worker
|
|
2
|
+
|
|
3
|
+
Run Shogo Cloud Agents on a machine you already own — a laptop, devbox, or CI runner.
|
|
4
|
+
Outbound-only: the worker dials Shogo Cloud over HTTPS, never the other way around.
|
|
5
|
+
|
|
6
|
+
> Licensed **MIT**. The agent runtime it spawns is licensed AGPL-3.0-or-later
|
|
7
|
+
> and ships as a separate binary — see [Architecture & licensing](#architecture--licensing) below.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Requires node >= 20 (or bun >= 1.3)
|
|
13
|
+
npm i -g @shogo-ai/worker
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
> Prefer a single self-contained binary? Grab the prebuilt tarball for your
|
|
17
|
+
> OS / arch from the [latest worker release](https://github.com/shogo-ai/shogo-ai/releases?q=worker-v)
|
|
18
|
+
> — it has no Node / Bun dependency.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 1. Sign in (opens your browser; falls back to --api-key for CI)
|
|
24
|
+
shogo login
|
|
25
|
+
|
|
26
|
+
# 2. Download the AGPL agent-runtime binary into ~/.shogo/runtime/
|
|
27
|
+
shogo runtime install
|
|
28
|
+
|
|
29
|
+
# 3. Start the worker (detached by default)
|
|
30
|
+
shogo worker start --name my-devbox
|
|
31
|
+
|
|
32
|
+
# 4. Confirm it's online
|
|
33
|
+
shogo worker status
|
|
34
|
+
|
|
35
|
+
# 5. Open https://studio.shogo.ai — your machine appears in the
|
|
36
|
+
# environment dropdown next to the desktop entries.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Headless / CI
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Skip the browser flow with a personal API key from
|
|
43
|
+
# https://studio.shogo.ai/api-keys
|
|
44
|
+
shogo login --api-key "shogo_sk_..."
|
|
45
|
+
|
|
46
|
+
# Or skip `login` entirely
|
|
47
|
+
SHOGO_API_KEY=shogo_sk_... shogo runtime install
|
|
48
|
+
SHOGO_API_KEY=shogo_sk_... shogo worker start --foreground
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
| Command | What it does |
|
|
54
|
+
|---------|--------------|
|
|
55
|
+
| `shogo login` | Browser device-code flow against `studio.shogo.ai`. `--api-key` / `SHOGO_API_KEY` for headless. `--workspace <id>` to pre-select. `--no-browser` to print the URL only. |
|
|
56
|
+
| `shogo runtime install` | Download + verify + extract the AGPL agent-runtime tarball into `~/.shogo/runtime/`. Flags: `--channel stable\|beta\|nightly`, `--version`, `--force`, `--base-url`. |
|
|
57
|
+
| `shogo runtime version` | Print the installed agent-runtime version. |
|
|
58
|
+
| `shogo runtime where` | Print the resolved binary path (priority order: `--runtime-bin` → `$SHOGO_AGENT_RUNTIME_BIN` → `~/.shogo/runtime/agent-runtime` → `$PATH`). |
|
|
59
|
+
| `shogo runtime update` | Reinstall the latest in-channel build. |
|
|
60
|
+
| `shogo worker start` | Pair this machine with Shogo Cloud. `--foreground` to attach to stdout (e.g. `systemd --user`); default detaches and writes `~/.shogo/worker.pid`. |
|
|
61
|
+
| `shogo worker stop` | Kill the running worker via PID file. |
|
|
62
|
+
| `shogo worker status` | Online / offline + uptime. |
|
|
63
|
+
| `shogo worker logs [-f]` | Tail `~/.shogo/logs/worker.log`. |
|
|
64
|
+
| `shogo config show` | Print config (API key masked). |
|
|
65
|
+
| `shogo config set <key> <value>` | Edit a single key. |
|
|
66
|
+
|
|
67
|
+
## Files & layout
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
~/.shogo/
|
|
71
|
+
├── config.json # API key + cloud URL (mode 0600)
|
|
72
|
+
├── device-id # stable per-machine UUID; dedupes re-logins
|
|
73
|
+
├── worker.pid # lifecycle file for `worker start/stop/status`
|
|
74
|
+
├── logs/
|
|
75
|
+
│ ├── worker.log
|
|
76
|
+
│ └── worker.err.log
|
|
77
|
+
└── runtime/
|
|
78
|
+
├── agent-runtime # AGPL binary, downloaded by `shogo runtime install`
|
|
79
|
+
└── version.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Networking
|
|
83
|
+
|
|
84
|
+
The worker needs outbound HTTPS (TCP 443) to 3 hosts. **No inbound ports required.**
|
|
85
|
+
|
|
86
|
+
| Host | Purpose | If blocked |
|
|
87
|
+
|------|---------|-----------|
|
|
88
|
+
| `studio.shogo.ai` | Sign-in, session, heartbeat, on-demand tunnel WS | **FATAL** — worker can't run |
|
|
89
|
+
| `api-direct.shogo.ai` | Direct tunnel fallback (when used) | Graceful — edge routing takes over |
|
|
90
|
+
| `github.com` / `objects.githubusercontent.com` | Runtime binary downloads (`shogo runtime install`) | Only `runtime install/update` affected; can be self-mirrored via `--base-url` |
|
|
91
|
+
|
|
92
|
+
### Corporate proxy
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# HTTPS_PROXY / https_proxy / HTTP_PROXY / http_proxy all honoured
|
|
96
|
+
HTTPS_PROXY=http://proxy.corp:3128 shogo worker start
|
|
97
|
+
|
|
98
|
+
# Or pass explicitly (overrides env)
|
|
99
|
+
shogo worker start --proxy http://proxy.corp:3128
|
|
100
|
+
|
|
101
|
+
# TLS-inspecting proxy needs your corp root CA
|
|
102
|
+
NODE_EXTRA_CA_CERTS=/etc/ssl/corp-root.pem \
|
|
103
|
+
HTTPS_PROXY=http://proxy.corp:3128 \
|
|
104
|
+
shogo worker start --debug
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`shogo worker start --debug` runs a preflight that includes a proxy-reachability
|
|
108
|
+
check when `HTTPS_PROXY` is set.
|
|
109
|
+
|
|
110
|
+
## Architecture & licensing
|
|
111
|
+
|
|
112
|
+
The worker is split into two processes that talk over localhost only:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
|
|
116
|
+
│ shogo (this package) │ │ agent-runtime │
|
|
117
|
+
│ ── MIT ────────────────────────── │ │ ── AGPL-3.0-or-later ──────────── │
|
|
118
|
+
│ │ │ │
|
|
119
|
+
│ • CLI (login / start / stop) │ │ • Runs your agents │
|
|
120
|
+
│ • WorkerTunnel: HTTP heartbeat + ├────► • Tools, LLM proxy, plans │
|
|
121
|
+
│ on-demand WS to Shogo Cloud │ │ │
|
|
122
|
+
│ • WorkerRuntimeManager: spawns │ │ │
|
|
123
|
+
│ one runtime per project │ │ │
|
|
124
|
+
│ │ │ │
|
|
125
|
+
│ Spawned by you / systemd / etc. │ │ Spawned by the worker (process │
|
|
126
|
+
│ │ │ boundary; not linked as library) │
|
|
127
|
+
└────────────────────────────────────┘ └────────────────────────────────────┘
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The MIT worker discovers and spawns the AGPL runtime as a separate OS process —
|
|
131
|
+
no library link, no dynamic import, no embed. This keeps the licenses cleanly
|
|
132
|
+
separated: you may consume `@shogo-ai/worker` from MIT / Apache-2.0 / proprietary
|
|
133
|
+
code without AGPL infecting it.
|
|
134
|
+
|
|
135
|
+
The runtime spawns themselves are managed by `WorkerRuntimeManager`: per-project
|
|
136
|
+
port allocation, env injection, restart-with-backoff, idle eviction, and health
|
|
137
|
+
checks. The same code path is consumed by Shogo Desktop (`apps/api`) for its own
|
|
138
|
+
agent runtimes — so any improvement made here ships to both.
|
|
139
|
+
|
|
140
|
+
## Programmatic use
|
|
141
|
+
|
|
142
|
+
Both core classes are exposed for direct embedding (e.g. building your own
|
|
143
|
+
desktop wrapper):
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { WorkerTunnel, WorkerRuntimeManager } from '@shogo-ai/worker'
|
|
147
|
+
|
|
148
|
+
const runtimeManager = new WorkerRuntimeManager({
|
|
149
|
+
defaultSpawnConfig: { cloudUrl: 'https://studio.shogo.ai', apiKey: process.env.SHOGO_API_KEY! },
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const tunnel = new WorkerTunnel({
|
|
153
|
+
apiKey: process.env.SHOGO_API_KEY!,
|
|
154
|
+
cloudUrl: 'https://studio.shogo.ai',
|
|
155
|
+
resolver: runtimeManager, // implements RuntimeResolver
|
|
156
|
+
kind: 'cli-worker',
|
|
157
|
+
onAuthRevoked: (reason) => { /* re-login UX */ },
|
|
158
|
+
})
|
|
159
|
+
tunnel.start()
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Troubleshooting
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
shogo worker start --debug # run preflight checks
|
|
166
|
+
shogo worker logs --follow # tail live logs
|
|
167
|
+
SHOGO_DEBUG=1 shogo worker status # verbose errors
|
|
168
|
+
|
|
169
|
+
# Runtime missing?
|
|
170
|
+
shogo runtime where # see what's resolved
|
|
171
|
+
shogo runtime install # (re)download the latest stable binary
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Links
|
|
175
|
+
|
|
176
|
+
- [Cloud Agent: My Machines guide](../../docs/cloud-agent-my-machines.md) — full
|
|
177
|
+
walk-through, security model, deploy patterns
|
|
178
|
+
- [Networking & firewall guide](../../docs/my-machines-networking.md)
|
|
179
|
+
- Source: [github.com/shogo-ai/shogo-ai](https://github.com/shogo-ai/shogo-ai)
|
package/bin/shogo.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (C) 2026 Shogo Technologies, Inc.
|
|
4
|
+
//
|
|
5
|
+
// `shogo` CLI entry shim. Picks an execution mode in this order:
|
|
6
|
+
//
|
|
7
|
+
// 1. Compiled per-platform binary at $PREFIX/dist/shogo (in the
|
|
8
|
+
// tarball release — Bun bundled, no runtime deps).
|
|
9
|
+
// 2. Bundled JS at ../dist/cli.mjs (npm install
|
|
10
|
+
// with no Bun on PATH — Node ESM-loadable).
|
|
11
|
+
// 3. Source TS at ../src/cli.ts (monorepo
|
|
12
|
+
// / `bun link`ed dev, requires Bun or tsx).
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
|
|
21
|
+
const compiledBin = join(__dirname, '..', 'dist', process.platform === 'win32' ? 'shogo.exe' : 'shogo');
|
|
22
|
+
const distEntry = join(__dirname, '..', 'dist', 'cli.mjs');
|
|
23
|
+
const srcEntry = join(__dirname, '..', 'src', 'cli.ts');
|
|
24
|
+
|
|
25
|
+
if (existsSync(compiledBin)) {
|
|
26
|
+
const child = spawn(compiledBin, args, { stdio: 'inherit' });
|
|
27
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
28
|
+
} else if (existsSync(distEntry)) {
|
|
29
|
+
await import(distEntry);
|
|
30
|
+
} else if (existsSync(srcEntry)) {
|
|
31
|
+
const runner = process.versions.bun ? 'bun' : 'tsx';
|
|
32
|
+
const child = spawn(runner, [srcEntry, ...args], { stdio: 'inherit' });
|
|
33
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
34
|
+
} else {
|
|
35
|
+
console.error('shogo: no executable entry found (looked for dist/shogo, dist/cli.mjs, src/cli.ts)');
|
|
36
|
+
console.error(' Reinstall with `npm i -g @shogo-ai/worker` or rebuild via `bun run build`.');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shogo-ai/worker",
|
|
3
|
+
"version": "1.7.4",
|
|
4
|
+
"description": "Shogo Cloud Agent Worker — run Shogo agents on your own machine (laptop, devbox, CI).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Shogo Technologies, Inc.",
|
|
7
|
+
"homepage": "https://shogo.ai/docs/cloud-agent/my-machines",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/shogo-labs/shogo-ai.git",
|
|
11
|
+
"directory": "packages/shogo-worker"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"shogo": "./bin/shogo.mjs"
|
|
15
|
+
},
|
|
16
|
+
"main": "./src/cli.ts",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/cli.ts",
|
|
20
|
+
"./tunnel": "./src/lib/tunnel.ts",
|
|
21
|
+
"./runtime-manager": "./src/lib/runtime-manager.ts",
|
|
22
|
+
"./runtime-resolver": "./src/lib/runtime-resolver.ts",
|
|
23
|
+
"./runtime-install": "./src/lib/runtime-install.ts",
|
|
24
|
+
"./cloud-login": "./src/lib/cloud-login.ts",
|
|
25
|
+
"./paths": "./src/lib/paths.ts"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin",
|
|
29
|
+
"src",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"start": "bun src/cli.ts",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"build": "bun run build:js",
|
|
36
|
+
"build:js": "bun build --target=node --format=esm --minify --external bun --external @shogo-ai/sdk --external commander --external picocolors --outdir dist src/cli.ts && mv dist/cli.js dist/cli.mjs",
|
|
37
|
+
"build:bin:darwin-arm64": "bun build --compile --minify --target=bun-darwin-arm64 src/cli.ts --outfile dist/shogo-darwin-arm64",
|
|
38
|
+
"build:bin:darwin-x64": "bun build --compile --minify --target=bun-darwin-x64 src/cli.ts --outfile dist/shogo-darwin-x64",
|
|
39
|
+
"build:bin:linux-x64": "bun build --compile --minify --target=bun-linux-x64 src/cli.ts --outfile dist/shogo-linux-x64",
|
|
40
|
+
"build:bin:linux-arm64": "bun build --compile --minify --target=bun-linux-arm64 src/cli.ts --outfile dist/shogo-linux-arm64",
|
|
41
|
+
"build:bin:all": "bun run build:bin:darwin-arm64 && bun run build:bin:darwin-x64 && bun run build:bin:linux-x64 && bun run build:bin:linux-arm64",
|
|
42
|
+
"prepublishOnly": "bun run typecheck"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@shogo-ai/sdk": "workspace:*",
|
|
46
|
+
"commander": "^12.1.0",
|
|
47
|
+
"picocolors": "^1.1.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.0.0",
|
|
51
|
+
"typescript": "^5.3.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=20"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (C) 2026 Shogo Technologies, Inc.
|
|
3
|
+
/**
|
|
4
|
+
* Shogo Worker CLI — entry.
|
|
5
|
+
*
|
|
6
|
+
* `shogo login` — save an API key to ~/.shogo/config.json
|
|
7
|
+
* `shogo worker start [flags]` — pair this machine with Shogo Cloud
|
|
8
|
+
* `shogo worker stop` — stop the local worker
|
|
9
|
+
* `shogo worker status` — show running/stopped
|
|
10
|
+
* `shogo worker logs [--follow]` — tail worker logs
|
|
11
|
+
* `shogo config show | set <k> <v>` — inspect/modify config
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import pc from 'picocolors';
|
|
15
|
+
import { runStart } from './commands/start.ts';
|
|
16
|
+
import { runStop } from './commands/stop.ts';
|
|
17
|
+
import { runStatus } from './commands/status.ts';
|
|
18
|
+
import { runLogs } from './commands/logs.ts';
|
|
19
|
+
import { runLogin } from './commands/login.ts';
|
|
20
|
+
import { runConfigShow, runConfigSet } from './commands/config.ts';
|
|
21
|
+
import {
|
|
22
|
+
runRuntimeInstall,
|
|
23
|
+
runRuntimeUpdate,
|
|
24
|
+
runRuntimeVersion,
|
|
25
|
+
runRuntimeWhere,
|
|
26
|
+
} from './commands/runtime.ts';
|
|
27
|
+
|
|
28
|
+
const VERSION = '0.1.0';
|
|
29
|
+
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program
|
|
32
|
+
.name('shogo')
|
|
33
|
+
.description('Shogo Cloud Agent Worker — run Shogo agents on your own machine.')
|
|
34
|
+
.version(VERSION);
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.command('login')
|
|
38
|
+
.description('Pair this machine with Shogo Cloud (browser device flow, or --api-key for CI)')
|
|
39
|
+
.option('--api-key <key>', 'CI escape hatch — skip the browser flow and use a key directly')
|
|
40
|
+
.option('--cloud-url <url>', 'Shogo Cloud URL (default: https://studio.shogo.ai)')
|
|
41
|
+
.option('--name <name>', 'Device label shown in the dashboard (default: hostname)')
|
|
42
|
+
.option('--workspace <id>', 'Pre-select a workspace on the bridge picker')
|
|
43
|
+
.option('--no-browser', 'Do not auto-open the browser; print the URL instead')
|
|
44
|
+
.action((flags) => handle(() => runLogin(flags)));
|
|
45
|
+
|
|
46
|
+
const worker = program.command('worker').description('Manage the local worker process');
|
|
47
|
+
|
|
48
|
+
worker
|
|
49
|
+
.command('start')
|
|
50
|
+
.description('Start the worker and pair with Shogo Cloud')
|
|
51
|
+
.option('--name <name>', 'Instance name shown in the dashboard (default: hostname)')
|
|
52
|
+
.option('--worker-dir <path>', 'Working directory for the worker (default: $PWD)')
|
|
53
|
+
.option('--api-key <key>', 'API key (overrides config/env)')
|
|
54
|
+
.option('--cloud-url <url>', 'Shogo Cloud URL')
|
|
55
|
+
.option('--port <port>', 'Local HTTP port for the embedded API')
|
|
56
|
+
.option('--proxy <url>', 'HTTPS proxy (overrides HTTPS_PROXY env)')
|
|
57
|
+
.option('--project <id>', 'Pin to a single project (default: multi-project on demand)')
|
|
58
|
+
.option('--runtime-bin <path>', 'Override the agent-runtime binary path')
|
|
59
|
+
.option('--debug', 'Run preflight checks before starting')
|
|
60
|
+
.option('--foreground', 'Run in foreground (don\'t detach)')
|
|
61
|
+
.action((flags) => handle(() => runStart(flags)));
|
|
62
|
+
|
|
63
|
+
worker
|
|
64
|
+
.command('stop')
|
|
65
|
+
.description('Stop the running worker')
|
|
66
|
+
.action(() => handle(runStop));
|
|
67
|
+
|
|
68
|
+
worker
|
|
69
|
+
.command('status')
|
|
70
|
+
.description('Show worker status')
|
|
71
|
+
.action(() => handle(runStatus));
|
|
72
|
+
|
|
73
|
+
worker
|
|
74
|
+
.command('logs')
|
|
75
|
+
.description('Tail worker logs from ~/.shogo/logs/worker.log')
|
|
76
|
+
.option('-f, --follow', 'Follow the log (tail -F)')
|
|
77
|
+
.option('--err', 'Show stderr log instead')
|
|
78
|
+
.action((flags) => handle(() => runLogs(flags)));
|
|
79
|
+
|
|
80
|
+
const runtime = program
|
|
81
|
+
.command('runtime')
|
|
82
|
+
.description('Install / inspect the local agent-runtime binary');
|
|
83
|
+
runtime
|
|
84
|
+
.command('install')
|
|
85
|
+
.description('Download + verify the agent-runtime tarball into ~/.shogo/runtime/')
|
|
86
|
+
.option('--channel <channel>', 'stable | beta | nightly (default: stable)')
|
|
87
|
+
.option('--version <version>', 'install a specific version (e.g. 0.1.0)')
|
|
88
|
+
.option('--base-url <url>', 'override release base URL (default: GitHub Releases)')
|
|
89
|
+
.option('--force', 'reinstall even if the same version is already on disk')
|
|
90
|
+
.action((flags) => handle(() => runRuntimeInstall(flags)));
|
|
91
|
+
runtime
|
|
92
|
+
.command('version')
|
|
93
|
+
.description('Print the installed agent-runtime version')
|
|
94
|
+
.action(() => handle(() => runRuntimeVersion()));
|
|
95
|
+
runtime
|
|
96
|
+
.command('where')
|
|
97
|
+
.description('Print the resolved agent-runtime binary path')
|
|
98
|
+
.action(() => handle(() => runRuntimeWhere()));
|
|
99
|
+
runtime
|
|
100
|
+
.command('update')
|
|
101
|
+
.description('Update agent-runtime to the latest in its channel')
|
|
102
|
+
.option('--channel <channel>', 'override channel (defaults to whatever is installed)')
|
|
103
|
+
.option('--base-url <url>', 'override release base URL')
|
|
104
|
+
.action((flags) => handle(() => runRuntimeUpdate(flags)));
|
|
105
|
+
|
|
106
|
+
const config = program.command('config').description('Inspect or modify ~/.shogo/config.json');
|
|
107
|
+
config
|
|
108
|
+
.command('show')
|
|
109
|
+
.description('Print current config (API key masked)')
|
|
110
|
+
.action(() => handle(runConfigShow));
|
|
111
|
+
config
|
|
112
|
+
.command('set <key> <value>')
|
|
113
|
+
.description('Set apiKey / cloudUrl / name / workerDir / port')
|
|
114
|
+
.action((k: string, v: string) => handle(() => runConfigSet(k, v)));
|
|
115
|
+
|
|
116
|
+
program.showHelpAfterError(pc.dim('\n(use --help for usage)'));
|
|
117
|
+
program.parseAsync(process.argv);
|
|
118
|
+
|
|
119
|
+
async function handle(fn: () => Promise<void> | void): Promise<void> {
|
|
120
|
+
try {
|
|
121
|
+
await fn();
|
|
122
|
+
} catch (err: any) {
|
|
123
|
+
console.error(pc.red(`✗ ${err?.message ?? err}`));
|
|
124
|
+
if (process.env.SHOGO_DEBUG) console.error(err?.stack);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (C) 2026 Shogo Technologies, Inc.
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { loadConfig, saveConfig } from '../lib/config.ts';
|
|
5
|
+
import { CONFIG_FILE } from '../lib/paths.ts';
|
|
6
|
+
|
|
7
|
+
export async function runConfigShow(): Promise<void> {
|
|
8
|
+
const cfg = loadConfig();
|
|
9
|
+
if (Object.keys(cfg).length === 0) {
|
|
10
|
+
console.log(pc.dim('(no config — run `shogo login` or `shogo config set`)'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const masked = { ...cfg, apiKey: cfg.apiKey ? `***${cfg.apiKey.slice(-4)}` : undefined };
|
|
14
|
+
console.log(pc.dim(`file: ${CONFIG_FILE}`));
|
|
15
|
+
console.log(JSON.stringify(masked, null, 2));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function runConfigSet(key: string, value: string): Promise<void> {
|
|
19
|
+
const allowed = ['apiKey', 'cloudUrl', 'name', 'workerDir', 'port'] as const;
|
|
20
|
+
if (!allowed.includes(key as any)) {
|
|
21
|
+
console.error(pc.red(`Unknown key: ${key}`));
|
|
22
|
+
console.error(pc.dim(`Allowed: ${allowed.join(', ')}`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const cfg = loadConfig();
|
|
26
|
+
(cfg as any)[key] = key === 'port' ? parseInt(value, 10) : value;
|
|
27
|
+
saveConfig(cfg);
|
|
28
|
+
console.log(pc.green(`✓ ${key} set`));
|
|
29
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (C) 2026 Shogo Technologies, Inc.
|
|
3
|
+
/**
|
|
4
|
+
* `shogo login` — pair this machine with Shogo Cloud.
|
|
5
|
+
*
|
|
6
|
+
* Two modes, in priority order:
|
|
7
|
+
*
|
|
8
|
+
* 1. `--api-key <key>` flag, or `SHOGO_API_KEY` env var
|
|
9
|
+
* → CI / headless mode. Validate against cloud, save, done.
|
|
10
|
+
*
|
|
11
|
+
* 2. Interactive (default)
|
|
12
|
+
* → Poll-based device flow (see lib/cloud-login.ts):
|
|
13
|
+
* POST /api/cli/login/start → state + authUrl
|
|
14
|
+
* open authUrl in browser → user approves on cloud
|
|
15
|
+
* GET /api/cli/login/poll → key once approved
|
|
16
|
+
*
|
|
17
|
+
* The minted key is written to ~/.shogo/config.json with mode 0600 so
|
|
18
|
+
* other local users can't read it. The same file is what
|
|
19
|
+
* `shogo worker start` reads on boot.
|
|
20
|
+
*
|
|
21
|
+
* The legacy "paste a shogo_sk_ key at the prompt" flow is still
|
|
22
|
+
* supported via `--api-key`; it just isn't the default any more.
|
|
23
|
+
*/
|
|
24
|
+
import pc from 'picocolors';
|
|
25
|
+
import { loadConfig, saveConfig } from '../lib/config.ts';
|
|
26
|
+
import { runCloudLogin, CloudLoginError } from '../lib/cloud-login.ts';
|
|
27
|
+
import { getOrCreateDeviceId } from '../lib/device-id.ts';
|
|
28
|
+
|
|
29
|
+
export interface LoginFlags {
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
cloudUrl?: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
workspace?: string;
|
|
34
|
+
noBrowser?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DEFAULT_CLOUD_URL = 'https://studio.shogo.ai';
|
|
38
|
+
|
|
39
|
+
export async function runLogin(flags: LoginFlags): Promise<void> {
|
|
40
|
+
const cfg = loadConfig();
|
|
41
|
+
const cloudUrl = (flags.cloudUrl || cfg.cloudUrl || DEFAULT_CLOUD_URL).replace(/\/$/, '');
|
|
42
|
+
|
|
43
|
+
const escapeKey = flags.apiKey || process.env.SHOGO_API_KEY;
|
|
44
|
+
if (escapeKey) {
|
|
45
|
+
await loginWithApiKey({ key: escapeKey, cloudUrl, cfg, name: flags.name });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const deviceId = getOrCreateDeviceId();
|
|
50
|
+
let result;
|
|
51
|
+
try {
|
|
52
|
+
result = await runCloudLogin({
|
|
53
|
+
cloudUrl,
|
|
54
|
+
deviceId,
|
|
55
|
+
deviceName: flags.name,
|
|
56
|
+
workspaceId: flags.workspace,
|
|
57
|
+
openBrowser: !flags.noBrowser,
|
|
58
|
+
});
|
|
59
|
+
} catch (err: any) {
|
|
60
|
+
if (err instanceof CloudLoginError) {
|
|
61
|
+
console.error(pc.red(`✗ ${err.message}`));
|
|
62
|
+
if (err.kind === 'transport') {
|
|
63
|
+
console.error(pc.dim(` If your network blocks browsers, run with --api-key <key> instead.`));
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
cfg.apiKey = result.key;
|
|
71
|
+
cfg.cloudUrl = cloudUrl;
|
|
72
|
+
if (flags.name) cfg.name = flags.name;
|
|
73
|
+
saveConfig(cfg);
|
|
74
|
+
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log(pc.green('✓ Signed in to Shogo Cloud'));
|
|
77
|
+
if (result.workspace) console.log(pc.dim(' workspace: ') + result.workspace);
|
|
78
|
+
if (result.email) console.log(pc.dim(' email: ') + result.email);
|
|
79
|
+
console.log(pc.dim(' saved to: ') + '~/.shogo/config.json');
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(pc.dim(' next: ') + 'shogo worker start');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface ApiKeyOpts {
|
|
85
|
+
key: string;
|
|
86
|
+
cloudUrl: string;
|
|
87
|
+
cfg: ReturnType<typeof loadConfig>;
|
|
88
|
+
name?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function loginWithApiKey({ key, cloudUrl, cfg, name }: ApiKeyOpts): Promise<void> {
|
|
92
|
+
if (!/^shogo_sk_/.test(key)) {
|
|
93
|
+
console.error(pc.red('✗ API key should start with "shogo_sk_". Copy it verbatim from the API Keys page.'));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Mirror apps/api/src/routes/local-auth.ts: re-validate against cloud
|
|
98
|
+
// before persisting so the user gets immediate feedback on a bad key.
|
|
99
|
+
let validation: { valid?: boolean; error?: string; workspace?: { name?: string } | null; user?: { email?: string } | null };
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetch(`${cloudUrl}/api/api-keys/validate`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { 'content-type': 'application/json' },
|
|
104
|
+
body: JSON.stringify({ key }),
|
|
105
|
+
signal: AbortSignal.timeout(10_000),
|
|
106
|
+
});
|
|
107
|
+
validation = await res.json().catch(() => ({} as any)) as typeof validation;
|
|
108
|
+
if (!res.ok || !validation?.valid) {
|
|
109
|
+
console.error(pc.red(`✗ ${validation?.error || `Cloud rejected the key (HTTP ${res.status}).`}`));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
} catch (err: any) {
|
|
113
|
+
console.error(pc.red(`✗ Cannot reach Shogo Cloud at ${cloudUrl}: ${err?.message ?? err}`));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
cfg.apiKey = key;
|
|
118
|
+
cfg.cloudUrl = cloudUrl;
|
|
119
|
+
if (name) cfg.name = name;
|
|
120
|
+
saveConfig(cfg);
|
|
121
|
+
|
|
122
|
+
console.log(pc.green('✓ API key saved to ~/.shogo/config.json'));
|
|
123
|
+
if (validation.workspace?.name) console.log(pc.dim(' workspace: ') + validation.workspace.name);
|
|
124
|
+
if (validation.user?.email) console.log(pc.dim(' email: ') + validation.user.email);
|
|
125
|
+
console.log(pc.dim(' next: ') + 'shogo worker start');
|
|
126
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (C) 2026 Shogo Technologies, Inc.
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { existsSync, statSync } from 'node:fs';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { WORKER_LOG, WORKER_ERR } from '../lib/paths.ts';
|
|
7
|
+
|
|
8
|
+
export interface LogsFlags { follow?: boolean; err?: boolean }
|
|
9
|
+
|
|
10
|
+
export async function runLogs(flags: LogsFlags): Promise<void> {
|
|
11
|
+
const file = flags.err ? WORKER_ERR : WORKER_LOG;
|
|
12
|
+
if (!existsSync(file)) {
|
|
13
|
+
console.log(pc.dim('No logs yet.'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const args = flags.follow ? ['-F', '-n', '200', file] : ['-n', '200', file];
|
|
17
|
+
const child = spawn('tail', args, { stdio: 'inherit' });
|
|
18
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
19
|
+
|
|
20
|
+
if (!flags.follow) {
|
|
21
|
+
const size = statSync(file).size;
|
|
22
|
+
if (size === 0) console.log(pc.dim('(log file is empty)'));
|
|
23
|
+
}
|
|
24
|
+
}
|