@vextlabs/theron-cli 0.1.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/LICENSE +15 -0
- package/LICENSE.txt +190 -0
- package/README.md +98 -0
- package/bin/theron +22 -0
- package/bin/theron.js +25 -0
- package/dist/api.d.ts +111 -0
- package/dist/api.js +328 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +15 -0
- package/dist/auth.js +92 -0
- package/dist/auth.js.map +1 -0
- package/dist/banner.d.ts +29 -0
- package/dist/banner.js +191 -0
- package/dist/banner.js.map +1 -0
- package/dist/cap_config.d.ts +28 -0
- package/dist/cap_config.js +83 -0
- package/dist/cap_config.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/connections.d.ts +3 -0
- package/dist/connections.js +105 -0
- package/dist/connections.js.map +1 -0
- package/dist/import_claude.d.ts +3 -0
- package/dist/import_claude.js +268 -0
- package/dist/import_claude.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +237 -0
- package/dist/index.js.map +1 -0
- package/dist/onboard.d.ts +3 -0
- package/dist/onboard.js +234 -0
- package/dist/onboard.js.map +1 -0
- package/dist/profile_match.d.ts +15 -0
- package/dist/profile_match.js +107 -0
- package/dist/profile_match.js.map +1 -0
- package/dist/profiles/index.d.ts +20 -0
- package/dist/profiles/index.js +56 -0
- package/dist/profiles/index.js.map +1 -0
- package/dist/profiles/seeds.d.ts +4 -0
- package/dist/profiles/seeds.js +500 -0
- package/dist/profiles/seeds.js.map +1 -0
- package/dist/profiles/types.d.ts +35 -0
- package/dist/profiles/types.js +18 -0
- package/dist/profiles/types.js.map +1 -0
- package/dist/render.d.ts +18 -0
- package/dist/render.js +82 -0
- package/dist/render.js.map +1 -0
- package/dist/repl.d.ts +13 -0
- package/dist/repl.js +821 -0
- package/dist/repl.js.map +1 -0
- package/dist/streaming.d.ts +28 -0
- package/dist/streaming.js +118 -0
- package/dist/streaming.js.map +1 -0
- package/dist/tools/bash.d.ts +8 -0
- package/dist/tools/bash.js +57 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +9 -0
- package/dist/tools/edit.js +42 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +7 -0
- package/dist/tools/glob.js +40 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +9 -0
- package/dist/tools/grep.js +73 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +31 -0
- package/dist/tools/index.js +180 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/ls.d.ts +6 -0
- package/dist/tools/ls.js +25 -0
- package/dist/tools/ls.js.map +1 -0
- package/dist/tools/read.d.ts +8 -0
- package/dist/tools/read.js +43 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/stoa.d.ts +34 -0
- package/dist/tools/stoa.js +103 -0
- package/dist/tools/stoa.js.map +1 -0
- package/dist/tools/write.d.ts +7 -0
- package/dist/tools/write.js +15 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/verifiers/ai_ism_check.d.ts +2 -0
- package/dist/verifiers/ai_ism_check.js +48 -0
- package/dist/verifiers/ai_ism_check.js.map +1 -0
- package/dist/verifiers/arithmetic_recheck.d.ts +2 -0
- package/dist/verifiers/arithmetic_recheck.js +74 -0
- package/dist/verifiers/arithmetic_recheck.js.map +1 -0
- package/dist/verifiers/citation_presence.d.ts +2 -0
- package/dist/verifiers/citation_presence.js +42 -0
- package/dist/verifiers/citation_presence.js.map +1 -0
- package/dist/verifiers/em_dash_check.d.ts +2 -0
- package/dist/verifiers/em_dash_check.js +23 -0
- package/dist/verifiers/em_dash_check.js.map +1 -0
- package/dist/verifiers/index.d.ts +16 -0
- package/dist/verifiers/index.js +123 -0
- package/dist/verifiers/index.js.map +1 -0
- package/dist/verifiers/lint.d.ts +2 -0
- package/dist/verifiers/lint.js +90 -0
- package/dist/verifiers/lint.js.map +1 -0
- package/dist/verifiers/style_lint.d.ts +2 -0
- package/dist/verifiers/style_lint.js +115 -0
- package/dist/verifiers/style_lint.js.map +1 -0
- package/dist/verifiers/test_smoke.d.ts +2 -0
- package/dist/verifiers/test_smoke.js +94 -0
- package/dist/verifiers/test_smoke.js.map +1 -0
- package/dist/verifiers/typecheck.d.ts +2 -0
- package/dist/verifiers/typecheck.js +98 -0
- package/dist/verifiers/typecheck.js.map +1 -0
- package/dist/verifiers/types.d.ts +33 -0
- package/dist/verifiers/types.js +23 -0
- package/dist/verifiers/types.js.map +1 -0
- package/package.json +60 -0
package/dist/banner.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// THERON ASCII banner + branded welcome card.
|
|
2
|
+
//
|
|
3
|
+
// Anna 2026-05-10: 'get the CLI to be super interactive like Claude
|
|
4
|
+
// CLI is. BUT with VEXT BRANDING and THERON.' Block-letter banner,
|
|
5
|
+
// boxed welcome cards, numbered security notes — same visual rhythm
|
|
6
|
+
// as Claude Code's research-preview welcome but with our amber +
|
|
7
|
+
// paper-on-ink palette.
|
|
8
|
+
//
|
|
9
|
+
// All output goes through chalk so it adapts to NO_COLOR and
|
|
10
|
+
// dumb-terminal envs gracefully.
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
const AMBER = "#FFAE00";
|
|
13
|
+
const INK = "#0B0B0B";
|
|
14
|
+
// ─── Block letters ────────────────────────────────────────────────────
|
|
15
|
+
// Each letter is a 5-row × 7-col grid. Drawing in the same blocky
|
|
16
|
+
// retro-arcade style as Claude Code's CLAUDE banner. Generated by
|
|
17
|
+
// hand from the same Slick-style template, kerned tight. Renders
|
|
18
|
+
// identically in monospace at 14-16pt.
|
|
19
|
+
const LETTERS = {
|
|
20
|
+
T: [
|
|
21
|
+
"███████",
|
|
22
|
+
"█ █ █",
|
|
23
|
+
" █ ",
|
|
24
|
+
" █ ",
|
|
25
|
+
" █ ",
|
|
26
|
+
],
|
|
27
|
+
H: [
|
|
28
|
+
"█ █",
|
|
29
|
+
"█ █",
|
|
30
|
+
"███████",
|
|
31
|
+
"█ █",
|
|
32
|
+
"█ █",
|
|
33
|
+
],
|
|
34
|
+
E: [
|
|
35
|
+
"███████",
|
|
36
|
+
"█ ",
|
|
37
|
+
"██████ ",
|
|
38
|
+
"█ ",
|
|
39
|
+
"███████",
|
|
40
|
+
],
|
|
41
|
+
R: [
|
|
42
|
+
"██████ ",
|
|
43
|
+
"█ █",
|
|
44
|
+
"██████ ",
|
|
45
|
+
"█ █ ",
|
|
46
|
+
"█ █",
|
|
47
|
+
],
|
|
48
|
+
O: [
|
|
49
|
+
" █████ ",
|
|
50
|
+
"█ █",
|
|
51
|
+
"█ █",
|
|
52
|
+
"█ █",
|
|
53
|
+
" █████ ",
|
|
54
|
+
],
|
|
55
|
+
N: [
|
|
56
|
+
"█ █",
|
|
57
|
+
"██ █",
|
|
58
|
+
"█ █ █",
|
|
59
|
+
"█ █ █",
|
|
60
|
+
"█ ███",
|
|
61
|
+
],
|
|
62
|
+
" ": [
|
|
63
|
+
" ",
|
|
64
|
+
" ",
|
|
65
|
+
" ",
|
|
66
|
+
" ",
|
|
67
|
+
" ",
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
/** Render the THERON banner as a string of ANSI-colored block letters. */
|
|
71
|
+
export function bannerTheron() {
|
|
72
|
+
const word = "THERON";
|
|
73
|
+
const rows = 5;
|
|
74
|
+
const lines = [];
|
|
75
|
+
for (let r = 0; r < rows; r++) {
|
|
76
|
+
const segs = [];
|
|
77
|
+
for (const ch of word) {
|
|
78
|
+
const grid = LETTERS[ch] ?? LETTERS[" "];
|
|
79
|
+
segs.push(grid[r]);
|
|
80
|
+
}
|
|
81
|
+
lines.push(segs.join(" "));
|
|
82
|
+
}
|
|
83
|
+
return lines.map((line) => chalk.bold.hex(AMBER)(line)).join("\n");
|
|
84
|
+
}
|
|
85
|
+
// ─── Boxed welcome card ───────────────────────────────────────────────
|
|
86
|
+
/** Tiny rounded-box drawing — same shape as Claude Code's "Welcome to
|
|
87
|
+
* Claude Code research preview!" pill. Single-line content, amber
|
|
88
|
+
* border, leading * marker. */
|
|
89
|
+
export function welcomePill(text) {
|
|
90
|
+
const inner = ` ${chalk.hex(AMBER)("✱")} ${text} `;
|
|
91
|
+
const visibleLen = stripAnsi(inner).length;
|
|
92
|
+
const top = chalk.hex(AMBER)("╭" + "─".repeat(visibleLen) + "╮");
|
|
93
|
+
const mid = chalk.hex(AMBER)("│") + inner + chalk.hex(AMBER)("│");
|
|
94
|
+
const bot = chalk.hex(AMBER)("╰" + "─".repeat(visibleLen) + "╯");
|
|
95
|
+
return [top, mid, bot].join("\n");
|
|
96
|
+
}
|
|
97
|
+
/** Strip ANSI escape sequences so we can measure visible width. */
|
|
98
|
+
function stripAnsi(s) {
|
|
99
|
+
// eslint-disable-next-line no-control-regex
|
|
100
|
+
return s.replace(/\[[0-9;]*m/g, "");
|
|
101
|
+
}
|
|
102
|
+
export function renderNotes(notes) {
|
|
103
|
+
const out = [];
|
|
104
|
+
out.push(chalk.bold("Security notes:"));
|
|
105
|
+
out.push("");
|
|
106
|
+
notes.forEach((n, i) => {
|
|
107
|
+
out.push(` ${chalk.dim(`${i + 1}.`)} ${chalk.bold(n.title)}`);
|
|
108
|
+
for (const line of n.body) {
|
|
109
|
+
out.push(` ${chalk.dim(line)}`);
|
|
110
|
+
}
|
|
111
|
+
if (i < notes.length - 1)
|
|
112
|
+
out.push("");
|
|
113
|
+
});
|
|
114
|
+
return out.join("\n");
|
|
115
|
+
}
|
|
116
|
+
// ─── Sample prompts to inspire the user's first turn ──────────────────
|
|
117
|
+
const SAMPLE_PROMPTS = [
|
|
118
|
+
"What's broken in this repo?",
|
|
119
|
+
"Review the latest commit and flag anything risky.",
|
|
120
|
+
"Find every place we call OpenRouter without a timeout.",
|
|
121
|
+
"Audit src/ for unused exports.",
|
|
122
|
+
"Run the type-checker and explain the first 3 errors.",
|
|
123
|
+
"Walk me through how the council fan-out works in marketing/api/_lib/.",
|
|
124
|
+
"Summarize what changed in the last 5 commits.",
|
|
125
|
+
"Look at the largest file in this repo and tell me how to split it.",
|
|
126
|
+
];
|
|
127
|
+
export function sampleSuggestion() {
|
|
128
|
+
const pick = SAMPLE_PROMPTS[Math.floor(Math.random() * SAMPLE_PROMPTS.length)];
|
|
129
|
+
return chalk.dim("try:") + " " + chalk.italic.hex(AMBER)(`"${pick}"`);
|
|
130
|
+
}
|
|
131
|
+
export function renderStatus(s) {
|
|
132
|
+
const home = process.env.HOME || "";
|
|
133
|
+
const shortCwd = home && s.cwd.startsWith(home) ? "~" + s.cwd.slice(home.length) : s.cwd;
|
|
134
|
+
const parts = [];
|
|
135
|
+
parts.push(chalk.dim("cwd ") + chalk.hex(AMBER)(shortCwd));
|
|
136
|
+
if (s.profileLabel)
|
|
137
|
+
parts.push(chalk.dim("mode ") + chalk.bold.hex(AMBER)(s.profileLabel));
|
|
138
|
+
parts.push(chalk.dim("api ") + s.apiUrl.replace(/^https?:\/\//, ""));
|
|
139
|
+
parts.push(chalk.dim("auth ") + (s.loggedIn ? chalk.hex(AMBER)("signed in") : chalk.dim("anonymous")));
|
|
140
|
+
if (s.yolo)
|
|
141
|
+
parts.push(chalk.yellow("yolo · tools auto-approve"));
|
|
142
|
+
if (s.model)
|
|
143
|
+
parts.push(chalk.dim("model ") + s.model);
|
|
144
|
+
// Right-align onto two lines if too wide for the terminal.
|
|
145
|
+
const width = process.stdout.columns ?? 100;
|
|
146
|
+
const flat = parts.join(chalk.dim(" · "));
|
|
147
|
+
if (stripAnsi(flat).length <= width - 2)
|
|
148
|
+
return flat;
|
|
149
|
+
return parts.map((p) => " " + p).join("\n");
|
|
150
|
+
}
|
|
151
|
+
// ─── /help slash-command menu ─────────────────────────────────────────
|
|
152
|
+
export const SLASH_COMMANDS = [
|
|
153
|
+
{ trigger: "/help", desc: "Show this menu" },
|
|
154
|
+
{ trigger: "/login", desc: "Sign in (saves API key in ~/.theron/credentials)" },
|
|
155
|
+
{ trigger: "/logout", desc: "Clear stored credentials" },
|
|
156
|
+
{ trigger: "/connections", desc: "List connected SaaS accounts + cap URNs (alias: /integrations)" },
|
|
157
|
+
{ trigger: "/clear", desc: "Reset the current conversation" },
|
|
158
|
+
{ trigger: "/yolo", desc: "Toggle auto-approve for tool calls" },
|
|
159
|
+
{ trigger: "/cwd", desc: "Show current working directory" },
|
|
160
|
+
{ trigger: "/cd <path>", desc: "Change working directory" },
|
|
161
|
+
{ trigger: "/model", desc: "Show / pick the model used for this session" },
|
|
162
|
+
{ trigger: "/tools", desc: "List available tools (Read, Write, Edit, Bash, …)" },
|
|
163
|
+
{ trigger: "/pin <spec>", desc: "Pin a council specialist for the next turn (@legal, @code, …)" },
|
|
164
|
+
{ trigger: "/unpin", desc: "Clear pinned specialists" },
|
|
165
|
+
{ trigger: "/mode <slug>", desc: "Switch domain profile (code, legal, finance, design, research, writing, +27)" },
|
|
166
|
+
{ trigger: "/mode list", desc: "Show all 33 profiles + descriptions" },
|
|
167
|
+
{ trigger: "/cap list", desc: "Show registered Stoa caps + per-cap policy (~/.theron/caps.json overlaid by ./.theron/caps.json)" },
|
|
168
|
+
{ trigger: "/readiness", desc: "Probe prod substrate health (council, db, Stoa, RAG, OAuth, driver loop)" },
|
|
169
|
+
{ trigger: "/innovate", desc: "Force one Theron-Meta driver tick — paradigm 3 (state-driven, never cron)" },
|
|
170
|
+
{ trigger: "/suggest <text>", desc: "Rank profiles by similarity to your prompt; pick the best /mode for the task" },
|
|
171
|
+
{ trigger: "/design <prompt>", desc: "Hand off a design brief to AE OS (opens browser, fans Creative+Code+Lang)" },
|
|
172
|
+
{ trigger: "/deck <prompt>", desc: "Hand off a pitch/sales/board deck brief to AE OS" },
|
|
173
|
+
{ trigger: "/draft <prompt>", desc: "Hand off a doc/memo/proposal brief to AE OS" },
|
|
174
|
+
{ trigger: "/research <prompt>", desc: "Hand off a deep-research brief to AE OS" },
|
|
175
|
+
{ trigger: "/status", desc: "Show cwd / mode / api / auth / yolo state" },
|
|
176
|
+
{ trigger: "/bug", desc: "File an issue against the CLI (opens the issue tracker)" },
|
|
177
|
+
{ trigger: "/quit", desc: "Exit (or Ctrl-C)" },
|
|
178
|
+
];
|
|
179
|
+
export function renderSlashHelp() {
|
|
180
|
+
const out = [];
|
|
181
|
+
out.push(chalk.bold.hex(AMBER)("theron") + chalk.dim(" — slash commands:"));
|
|
182
|
+
out.push("");
|
|
183
|
+
for (const c of SLASH_COMMANDS) {
|
|
184
|
+
const pad = c.trigger.padEnd(16, " ");
|
|
185
|
+
out.push(" " + chalk.hex(AMBER)(pad) + " " + chalk.dim(c.desc));
|
|
186
|
+
}
|
|
187
|
+
out.push("");
|
|
188
|
+
out.push(chalk.dim("type any message to chat · ") + chalk.dim("@") + chalk.dim("specialist to pin · ") + chalk.dim("Shift+Enter for newline"));
|
|
189
|
+
return out.join("\n");
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CapPolicy = "auto" | "ask" | "deny";
|
|
2
|
+
export interface CapConfig {
|
|
3
|
+
/** Default policy when no specific rule matches. */
|
|
4
|
+
default_policy: CapPolicy;
|
|
5
|
+
/** URN-prefix → policy. Longer prefixes win on match. */
|
|
6
|
+
policies: Record<string, CapPolicy>;
|
|
7
|
+
}
|
|
8
|
+
export interface LoadedCapConfig {
|
|
9
|
+
config: CapConfig;
|
|
10
|
+
/** Paths that contributed to the merged config — surfaced in `/cap`
|
|
11
|
+
* so the user knows what's in play. */
|
|
12
|
+
sources: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load the merged cap config. Global first, then project-local overlay.
|
|
16
|
+
* Returns the merged config plus the source paths that were read.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadCapConfig(cwd?: string): LoadedCapConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the policy for a specific cap URN. Longest matching prefix
|
|
21
|
+
* wins; falls back to default_policy.
|
|
22
|
+
*
|
|
23
|
+
* resolve("urn:stoa:cap:github.repo.read@1.0.0", { policies: {
|
|
24
|
+
* "urn:stoa:cap:github.": "auto",
|
|
25
|
+
* "urn:stoa:cap:github.repo.delete": "ask",
|
|
26
|
+
* }}) → "auto"
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCapPolicy(urn: string, config: CapConfig): CapPolicy;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// cap_config — two-tier Stoa-cap config loader.
|
|
2
|
+
//
|
|
3
|
+
// Pattern lifted from jcode (MIT, github.com/1jehuang/jcode): global
|
|
4
|
+
// config at `~/.theron/caps.json` overlaid by project-local config at
|
|
5
|
+
// `<cwd>/.theron/caps.json`. Local wins on key conflicts so a repo can
|
|
6
|
+
// override a global allow with a deny (or vice-versa).
|
|
7
|
+
//
|
|
8
|
+
// Use case: you want `urn:stoa:cap:github.*` auto-approved globally
|
|
9
|
+
// but `urn:stoa:cap:github.repo.delete@1.0.0` always-ask in the repo
|
|
10
|
+
// where it'd be catastrophic. Two-tier config makes that one-line.
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import process from "node:process";
|
|
14
|
+
const EMPTY = { default_policy: "ask", policies: {} };
|
|
15
|
+
function readJsonSafe(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
if (!fs.existsSync(filePath))
|
|
18
|
+
return null;
|
|
19
|
+
const txt = fs.readFileSync(filePath, "utf8");
|
|
20
|
+
const parsed = JSON.parse(txt);
|
|
21
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
22
|
+
return null;
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load the merged cap config. Global first, then project-local overlay.
|
|
31
|
+
* Returns the merged config plus the source paths that were read.
|
|
32
|
+
*/
|
|
33
|
+
export function loadCapConfig(cwd = process.cwd()) {
|
|
34
|
+
const sources = [];
|
|
35
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
36
|
+
const globalPath = home ? path.join(home, ".theron", "caps.json") : "";
|
|
37
|
+
const localPath = path.join(cwd, ".theron", "caps.json");
|
|
38
|
+
let merged = { ...EMPTY, policies: { ...EMPTY.policies } };
|
|
39
|
+
if (globalPath) {
|
|
40
|
+
const g = readJsonSafe(globalPath);
|
|
41
|
+
if (g) {
|
|
42
|
+
sources.push(globalPath);
|
|
43
|
+
if (g.default_policy)
|
|
44
|
+
merged.default_policy = g.default_policy;
|
|
45
|
+
if (g.policies && typeof g.policies === "object") {
|
|
46
|
+
Object.assign(merged.policies, g.policies);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const l = readJsonSafe(localPath);
|
|
51
|
+
if (l) {
|
|
52
|
+
sources.push(localPath);
|
|
53
|
+
if (l.default_policy)
|
|
54
|
+
merged.default_policy = l.default_policy;
|
|
55
|
+
if (l.policies && typeof l.policies === "object") {
|
|
56
|
+
Object.assign(merged.policies, l.policies);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { config: merged, sources };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the policy for a specific cap URN. Longest matching prefix
|
|
63
|
+
* wins; falls back to default_policy.
|
|
64
|
+
*
|
|
65
|
+
* resolve("urn:stoa:cap:github.repo.read@1.0.0", { policies: {
|
|
66
|
+
* "urn:stoa:cap:github.": "auto",
|
|
67
|
+
* "urn:stoa:cap:github.repo.delete": "ask",
|
|
68
|
+
* }}) → "auto"
|
|
69
|
+
*/
|
|
70
|
+
export function resolveCapPolicy(urn, config) {
|
|
71
|
+
let bestMatch = null;
|
|
72
|
+
for (const prefix of Object.keys(config.policies)) {
|
|
73
|
+
if (urn.startsWith(prefix)) {
|
|
74
|
+
if (bestMatch === null || prefix.length > bestMatch.length) {
|
|
75
|
+
bestMatch = prefix;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (bestMatch !== null)
|
|
80
|
+
return config.policies[bestMatch];
|
|
81
|
+
return config.default_policy;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=cap_config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cap_config.js","sourceRoot":"","sources":["../src/cap_config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const DEFAULT_API_URL = "https://theron.tryvext.com";
|
|
2
|
+
export declare const CONFIG_DIR: string;
|
|
3
|
+
export declare const CONFIG_FILE: string;
|
|
4
|
+
export interface TheronConfig {
|
|
5
|
+
api_key?: string;
|
|
6
|
+
api_url?: string;
|
|
7
|
+
default_workspace_id?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function loadConfig(): Promise<TheronConfig>;
|
|
11
|
+
export declare function saveConfig(cfg: TheronConfig): Promise<void>;
|
|
12
|
+
export declare function clearConfig(): Promise<boolean>;
|
|
13
|
+
export interface ResolvedAuth {
|
|
14
|
+
apiKey: string | undefined;
|
|
15
|
+
apiUrl: string;
|
|
16
|
+
defaultWorkspaceId: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveAuth(flagApiKey?: string, flagApiUrl?: string): Promise<ResolvedAuth>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Persistent CLI config. Single file at ~/.theron/config.json, mode 0600.
|
|
2
|
+
//
|
|
3
|
+
// Resolution order for the API key (first wins):
|
|
4
|
+
// 1. --api-key flag on the command (handled by each command)
|
|
5
|
+
// 2. THERON_API_KEY environment variable
|
|
6
|
+
// 3. ~/.theron/config.json {api_key}
|
|
7
|
+
//
|
|
8
|
+
// Resolution order for the API URL:
|
|
9
|
+
// 1. --api-url flag
|
|
10
|
+
// 2. THERON_API_URL env var
|
|
11
|
+
// 3. ~/.theron/config.json {api_url}
|
|
12
|
+
// 4. https://theron.tryvext.com
|
|
13
|
+
import { promises as fs } from "node:fs";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
export const DEFAULT_API_URL = "https://theron.tryvext.com";
|
|
17
|
+
export const CONFIG_DIR = path.join(os.homedir(), ".theron");
|
|
18
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
19
|
+
export async function loadConfig() {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await fs.readFile(CONFIG_FILE, "utf-8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (parsed && typeof parsed === "object")
|
|
24
|
+
return parsed;
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// ENOENT or malformed file. Quiet — first-run users have no file yet.
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function saveConfig(cfg) {
|
|
33
|
+
// 0700 dir + 0600 file so no other user on the box can read the key.
|
|
34
|
+
// chmod runs after writeFile because writeFile only honors `mode` on
|
|
35
|
+
// create — an existing looser file would otherwise stay world-readable.
|
|
36
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
37
|
+
await fs.writeFile(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
38
|
+
try {
|
|
39
|
+
await fs.chmod(CONFIG_FILE, 0o600);
|
|
40
|
+
}
|
|
41
|
+
catch { /* best effort */ }
|
|
42
|
+
try {
|
|
43
|
+
await fs.chmod(CONFIG_DIR, 0o700);
|
|
44
|
+
}
|
|
45
|
+
catch { /* best effort */ }
|
|
46
|
+
}
|
|
47
|
+
export async function clearConfig() {
|
|
48
|
+
try {
|
|
49
|
+
await fs.unlink(CONFIG_FILE);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const code = err.code;
|
|
54
|
+
if (code === "ENOENT")
|
|
55
|
+
return false;
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function resolveAuth(flagApiKey, flagApiUrl) {
|
|
60
|
+
const cfg = await loadConfig();
|
|
61
|
+
const apiKey = flagApiKey ?? process.env.THERON_API_KEY ?? cfg.api_key;
|
|
62
|
+
const apiUrl = flagApiUrl ?? process.env.THERON_API_URL ?? cfg.api_url ?? DEFAULT_API_URL;
|
|
63
|
+
return { apiKey, apiUrl, defaultWorkspaceId: cfg.default_workspace_id };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,iDAAiD;AACjD,+DAA+D;AAC/D,2CAA2C;AAC3C,uCAAuC;AACvC,EAAE;AACF,oCAAoC;AACpC,sBAAsB;AACtB,8BAA8B;AAC9B,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,eAAe,GAAG,4BAA4B,CAAC;AAE5D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAShE,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAsB,CAAC;QACxE,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAiB;IAChD,qEAAqE;IACrE,qEAAqE;IACrE,wEAAwE;IACxE,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACvE,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACpC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAAmB,EACnB,UAAmB;IAEnB,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,OAAO,CAAC;IACvE,MAAM,MAAM,GAAG,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,OAAO,IAAI,eAAe,CAAC;IAC1F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,CAAC,oBAAoB,EAAE,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// `theron connections` — list what the user has connected, status per
|
|
2
|
+
// provider, and the cap URNs each connection unlocks.
|
|
3
|
+
//
|
|
4
|
+
// Anna 2026-05-10: "We want to be able to connect to any app — or any
|
|
5
|
+
// single SaaS can connect Stoa." This CLI command is the terminal-
|
|
6
|
+
// side mirror of /integrations on the web: shows the connected
|
|
7
|
+
// accounts (Microsoft, Google, GitHub, GitLab, Slack, etc.), per-
|
|
8
|
+
// account status (connected | expired | not connected), and a quick
|
|
9
|
+
// hint for how to connect anything that isn't.
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import { loadCredentials } from "./auth.js";
|
|
12
|
+
const AMBER = "#FFAE00";
|
|
13
|
+
// The catalog we surface in the CLI. Stays in sync with the web
|
|
14
|
+
// /integrations CATALOG. Each row maps to a Stoa cap family + the
|
|
15
|
+
// /api/oauth/<provider>/start endpoint that lights it up.
|
|
16
|
+
const CATALOG = [
|
|
17
|
+
{ slug: 'microsoft', label: 'Microsoft 365', caps: [
|
|
18
|
+
'urn:stoa:cap:ms365.excel.write_range@1.0.0',
|
|
19
|
+
'urn:stoa:cap:ms365.powerpoint.add_slide@1.0.0',
|
|
20
|
+
'urn:stoa:cap:ms365.word.append_text@1.0.0',
|
|
21
|
+
'urn:stoa:cap:ms365.outlook.send_email@1.0.0',
|
|
22
|
+
] },
|
|
23
|
+
{ slug: 'google', label: 'Google Workspace', caps: [
|
|
24
|
+
'urn:stoa:cap:gws.sheets.write_range@1.0.0',
|
|
25
|
+
'urn:stoa:cap:gws.docs.append_text@1.0.0',
|
|
26
|
+
'urn:stoa:cap:gws.slides.add_slide@1.0.0',
|
|
27
|
+
'urn:stoa:cap:gws.gmail.send@1.0.0',
|
|
28
|
+
'urn:stoa:cap:gws.calendar.create_event@1.0.0',
|
|
29
|
+
'urn:stoa:cap:gws.drive.list@1.0.0',
|
|
30
|
+
] },
|
|
31
|
+
{ slug: 'github', label: 'GitHub', caps: ['urn:stoa:cap:github.issues.create@1.0.0'] },
|
|
32
|
+
{ slug: 'gitlab', label: 'GitLab', caps: [] },
|
|
33
|
+
{ slug: 'slack', label: 'Slack', caps: ['urn:stoa:cap:slack.chat.postMessage@1.0.0'] },
|
|
34
|
+
];
|
|
35
|
+
export async function connectionsCommand(opts = {}) {
|
|
36
|
+
const creds = await loadCredentials();
|
|
37
|
+
const apiUrl = (opts.apiUrl ?? creds.api_url ?? 'https://tryvext.com').replace(/\/$/, '');
|
|
38
|
+
if (!creds.api_key) {
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(chalk.dim(" not signed in — connections are per-user."));
|
|
41
|
+
console.log(" Run " + chalk.hex(AMBER)("theron login") + " first, then re-run this command.");
|
|
42
|
+
console.log();
|
|
43
|
+
return 2;
|
|
44
|
+
}
|
|
45
|
+
// Hit /api/oauth/list to see what the user has connected. The
|
|
46
|
+
// endpoint returns a small JSON object {connections: [{provider,
|
|
47
|
+
// account_label, connected_at, ...}]} — same shape /integrations
|
|
48
|
+
// uses.
|
|
49
|
+
let connections = [];
|
|
50
|
+
try {
|
|
51
|
+
const r = await fetch(`${apiUrl}/api/oauth/list`, {
|
|
52
|
+
headers: { authorization: `Bearer ${creds.api_key}` },
|
|
53
|
+
});
|
|
54
|
+
if (r.ok) {
|
|
55
|
+
const data = (await r.json());
|
|
56
|
+
connections = data.connections ?? [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// If the connections endpoint isn't reachable, fall through —
|
|
61
|
+
// we still print the catalog so the user sees what's available
|
|
62
|
+
// and how to connect.
|
|
63
|
+
}
|
|
64
|
+
const connectedSlugs = new Set(connections.map((c) => c.provider));
|
|
65
|
+
const rows = CATALOG.map((entry) => {
|
|
66
|
+
const found = connections.find((c) => c.provider === entry.slug);
|
|
67
|
+
return {
|
|
68
|
+
slug: entry.slug,
|
|
69
|
+
label: entry.label,
|
|
70
|
+
status: found ? 'connected' : 'not_connected',
|
|
71
|
+
caps: entry.caps,
|
|
72
|
+
account_email: found?.account_label,
|
|
73
|
+
connect_url: `${apiUrl}/api/oauth/${entry.slug}/start?apikey=${encodeURIComponent(creds.api_key)}`,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
// Render.
|
|
77
|
+
console.log();
|
|
78
|
+
console.log(chalk.hex(AMBER).bold(" Theron · connections"));
|
|
79
|
+
console.log(chalk.dim(" Connect once — every surface uses it (chat, CLI, voice, Office, agents)."));
|
|
80
|
+
console.log();
|
|
81
|
+
const widthSlug = Math.max(...rows.map((r) => r.label.length)) + 2;
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
const dot = row.status === 'connected' ? chalk.hex(AMBER)('●') : chalk.dim('○');
|
|
84
|
+
const label = chalk.bold(row.label.padEnd(widthSlug));
|
|
85
|
+
const status = row.status === 'connected'
|
|
86
|
+
? chalk.hex(AMBER)(`connected${row.account_email ? ' · ' + row.account_email : ''}`)
|
|
87
|
+
: chalk.dim('not connected');
|
|
88
|
+
console.log(` ${dot} ${label} ${status}`);
|
|
89
|
+
if (row.caps.length > 0) {
|
|
90
|
+
console.log(chalk.dim(` caps: ${row.caps.slice(0, 3).map((c) => c.replace(/^urn:stoa:cap:/, '')).join(', ')}${row.caps.length > 3 ? ` +${row.caps.length - 3} more` : ''}`));
|
|
91
|
+
}
|
|
92
|
+
if (row.status !== 'connected' && row.connect_url) {
|
|
93
|
+
console.log(chalk.dim(` connect: ${row.connect_url}`));
|
|
94
|
+
}
|
|
95
|
+
console.log();
|
|
96
|
+
}
|
|
97
|
+
// Footer.
|
|
98
|
+
const connectedCount = rows.filter((r) => r.status === 'connected').length;
|
|
99
|
+
console.log(chalk.dim(` ${connectedCount} of ${rows.length} providers connected.`));
|
|
100
|
+
console.log();
|
|
101
|
+
console.log(` Full catalog + register-your-SaaS: ${chalk.hex(AMBER)(apiUrl + '/integrations')}`);
|
|
102
|
+
console.log();
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=connections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connections.js","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"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"}
|