@triangllabs/otis 0.1.7

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 (3) hide show
  1. package/README.md +80 -0
  2. package/bin/otis +81 -0
  3. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Otis
2
+
3
+ Otis is an interactive terminal AI agent. The MVP runs locally as an OpenTUI CLI, uses a local TypeScript agent runtime, and calls an Otis-hosted Go gateway for inference and hosted web tools.
4
+
5
+ ## MVP Architecture
6
+
7
+ ```txt
8
+ OpenTUI CLI
9
+ -> local TypeScript agent runtime on Node 22+
10
+ -> Otis Go gateway on GCP Cloud Run
11
+ -> hosted model and web providers
12
+ ```
13
+
14
+ The local runtime owns the agent loop, local tool execution, session files, permissions, and UI events. The cloud gateway owns authentication, provider keys, system prompts, model routing, provider calls, hosted web tools, and future quota enforcement.
15
+
16
+ ## Current Decisions
17
+
18
+ - Interactive CLI only for MVP.
19
+ - OpenTUI frontend.
20
+ - TypeScript local runtime on Node 22+.
21
+ - Go cloud gateway.
22
+ - GCP infrastructure managed with Terraform.
23
+ - GitHub Actions for CI/CD.
24
+ - Provider access behind Otis's backend keys.
25
+ - One configured primary model with fallback handling. No user-visible model picker.
26
+ - Local JSONL session files.
27
+ - Single-use invite code exchanged for a bearer token.
28
+ - Quota hooks implemented but disabled initially.
29
+ - Provider-native structured tool calls streamed through the gateway.
30
+ - Shell tool included.
31
+
32
+ ## Initial User Flow
33
+
34
+ ```txt
35
+ 1. Invited user installs Otis.
36
+ 2. User runs `otis`.
37
+ 3. User selects Log in and enters a single-use invite code.
38
+ 4. CLI stores a bearer token locally.
39
+ 5. User starts chatting.
40
+ 6. Otis streams responses through the Otis gateway.
41
+ 7. Otis can read/search files, edit files, and run shell commands.
42
+ 8. Session history is saved locally as JSONL.
43
+ ```
44
+
45
+ ## Login
46
+
47
+ Run `otis` and press Enter on Log in, or run `otis login`, then enter a single-use invite code. Otis stores the issued bearer token locally under the user's Otis config directory.
48
+
49
+ The CLI defaults to the Otis dev gateway: `https://otis-gateway-dev-774436916534.us-central1.run.app`. Use `--gateway-url <gateway-url>` or `OTIS_GATEWAY_URL` to override it.
50
+
51
+ For development, a full `OTIS_GATEWAY_URL` and `OTIS_AUTH_TOKEN` pair still overrides saved auth.
52
+
53
+ ## Repository Layout
54
+
55
+ ```txt
56
+ apps/cli/ OpenTUI CLI app
57
+ packages/core/ Agent loop, events, context assembly
58
+ packages/gateway/ Gateway API client and protocol types
59
+ packages/tools/ Local tool implementations
60
+ packages/storage/ JSONL session storage
61
+ .github/workflows/ GitHub Actions workflows
62
+ docs/ Architecture and implementation notes
63
+ ```
64
+
65
+ ## Non-Goals For MVP
66
+
67
+ - Electron desktop app.
68
+ - Local inference.
69
+ - Print, JSON, or RPC modes.
70
+ - User-visible model selection.
71
+ - BYOK.
72
+ - OAuth.
73
+ - Cloud session sync.
74
+ - Extension marketplace.
75
+ - MCP.
76
+ - Subagents.
77
+
78
+ ## Status
79
+
80
+ Interactive CLI runtime is in progress. Hosted gateway and GCP infrastructure live in separate private repos.
package/bin/otis ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ const childProcess = require("child_process")
4
+ const fs = require("fs")
5
+ const path = require("path")
6
+ const os = require("os")
7
+
8
+ const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
9
+
10
+ const platformMap = {
11
+ darwin: "darwin",
12
+ linux: "linux",
13
+ win32: "windows",
14
+ }
15
+
16
+ const archMap = {
17
+ x64: "x64",
18
+ arm64: "arm64",
19
+ }
20
+
21
+ const platform = platformMap[os.platform()] ?? os.platform()
22
+ const arch = archMap[os.arch()] ?? os.arch()
23
+ const binary = platform === "windows" ? "otis.exe" : "otis"
24
+ const packageName = `@triangllabs/otis-${platform}-${arch}`
25
+
26
+ function findBinary(startDir) {
27
+ let current = startDir
28
+ for (;;) {
29
+ const modules = path.join(current, "node_modules")
30
+ if (fs.existsSync(modules)) {
31
+ const candidate = path.join(modules, packageName, "bin", binary)
32
+ if (fs.existsSync(candidate)) return candidate
33
+ }
34
+ const parent = path.dirname(current)
35
+ if (parent === current) return
36
+ current = parent
37
+ }
38
+ }
39
+
40
+ const scriptDir = path.dirname(fs.realpathSync(__filename))
41
+ const args = process.argv.slice(2)
42
+
43
+ if (args[0] === "update") {
44
+ console.log("Updating Otis...")
45
+ childProcess.execSync("npm install -g @triangllabs/otis@latest", { stdio: "inherit" })
46
+ console.log("Otis updated successfully.")
47
+ process.exit(0)
48
+ }
49
+
50
+ const resolved = process.env.OTIS_BIN_PATH || findBinary(scriptDir)
51
+
52
+ if (!resolved) {
53
+ console.error(
54
+ `Could not find the Otis binary for ${platform}-${arch}. ` +
55
+ `Try installing "${packageName}" manually, or set OTIS_BIN_PATH to the binary location.`,
56
+ )
57
+ process.exit(1)
58
+ }
59
+
60
+ const child = childProcess.spawn(resolved, process.argv.slice(2), { stdio: "inherit" })
61
+
62
+ child.on("error", (error) => {
63
+ console.error(error.message)
64
+ process.exit(1)
65
+ })
66
+
67
+ for (const signal of forwardedSignals) {
68
+ process.on(signal, () => {
69
+ try {
70
+ child.kill(signal)
71
+ } catch {}
72
+ })
73
+ }
74
+
75
+ child.on("exit", (code, signal) => {
76
+ if (signal) {
77
+ process.kill(process.pid, signal)
78
+ return
79
+ }
80
+ process.exit(typeof code === "number" ? code : 0)
81
+ })
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@triangllabs/otis",
3
+ "version": "0.1.7",
4
+ "description": "Interactive terminal AI agent by Triangl Labs",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "bin": {
8
+ "otis": "bin/otis"
9
+ },
10
+ "scripts": {
11
+ "dev": "bun run apps/cli/src/index.ts",
12
+ "build": "bun run scripts/build.ts",
13
+ "format": "biome format --write .",
14
+ "lint": "biome lint .",
15
+ "check": "biome check .",
16
+ "test": "vitest run",
17
+ "test:coverage": "vitest run --coverage",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "bun run typecheck && bun run check && bun run test"
20
+ },
21
+ "files": [
22
+ "bin/"
23
+ ],
24
+ "keywords": [
25
+ "ai",
26
+ "agent",
27
+ "cli",
28
+ "terminal",
29
+ "coding",
30
+ "assistant"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/triangllabs/otis.git"
35
+ },
36
+ "homepage": "https://github.com/triangllabs/otis",
37
+ "bugs": {
38
+ "url": "https://github.com/triangllabs/otis/issues"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "optionalDependencies": {
47
+ "@triangllabs/otis-darwin-arm64": "0.1.7",
48
+ "@triangllabs/otis-darwin-x64": "0.1.7",
49
+ "@triangllabs/otis-linux-arm64": "0.1.7",
50
+ "@triangllabs/otis-linux-x64": "0.1.7"
51
+ },
52
+ "devDependencies": {
53
+ "@biomejs/biome": "^2.5.0",
54
+ "@opentui/core": "^0.4.1",
55
+ "@types/node": "^24.0.0",
56
+ "@vitest/coverage-v8": "^4.1.9",
57
+ "diff": "^9.0.0",
58
+ "typescript": "^5.8.0",
59
+ "vitest": "^4.1.9"
60
+ }
61
+ }