nolo-cli 0.1.0 → 0.1.2
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 +8 -0
- package/authCommands.ts +66 -0
- package/client/profileConfig.ts +60 -0
- package/commandRegistry.ts +2 -0
- package/index.ts +21 -2
- package/package.json +3 -1
- package/tui/session.ts +3 -3
package/README.md
CHANGED
|
@@ -34,6 +34,14 @@ TypeScript script. For agent calls outside this repository, provide a token:
|
|
|
34
34
|
NOLO_SERVER=https://nolo.chat AUTH_TOKEN=<token> nolo
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Or save a local profile once:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
nolo login --server https://nolo.chat
|
|
41
|
+
nolo whoami
|
|
42
|
+
nolo
|
|
43
|
+
```
|
|
44
|
+
|
|
37
45
|
Local repo development can still use the script bridge without `AUTH_TOKEN`.
|
|
38
46
|
|
|
39
47
|
```bash
|
package/authCommands.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { rmSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getCurrentProfile,
|
|
7
|
+
getDefaultProfileConfigPath,
|
|
8
|
+
loadProfileConfig,
|
|
9
|
+
saveDefaultProfile,
|
|
10
|
+
} from "./client/profileConfig";
|
|
11
|
+
|
|
12
|
+
function getArg(args: string[], flag: string) {
|
|
13
|
+
const index = args.indexOf(flag);
|
|
14
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function runLoginCommand(args: string[]) {
|
|
18
|
+
const configPath = getDefaultProfileConfigPath();
|
|
19
|
+
const serverArg = getArg(args, "--server");
|
|
20
|
+
const tokenArg = getArg(args, "--token");
|
|
21
|
+
const rl = createInterface({ input, output });
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const serverUrl = (
|
|
25
|
+
serverArg ||
|
|
26
|
+
(await rl.question("server [https://nolo.chat]: ")) ||
|
|
27
|
+
"https://nolo.chat"
|
|
28
|
+
).replace(/\/+$/, "");
|
|
29
|
+
|
|
30
|
+
const authToken = tokenArg || (await rl.question("paste auth token: "));
|
|
31
|
+
if (!authToken.trim()) {
|
|
32
|
+
console.error("No auth token provided.");
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
saveDefaultProfile(configPath, {
|
|
37
|
+
serverUrl,
|
|
38
|
+
authToken: authToken.trim(),
|
|
39
|
+
});
|
|
40
|
+
console.log(`Saved profile default -> ${serverUrl}`);
|
|
41
|
+
return 0;
|
|
42
|
+
} finally {
|
|
43
|
+
rl.close();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function runWhoamiCommand() {
|
|
48
|
+
const config = loadProfileConfig();
|
|
49
|
+
const profile = getCurrentProfile(config);
|
|
50
|
+
if (!config || !profile) {
|
|
51
|
+
console.log("Not logged in. Run: nolo login");
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`profile: ${config.currentProfile}`);
|
|
56
|
+
console.log(`server: ${profile.serverUrl}`);
|
|
57
|
+
console.log(`token: ${profile.authToken.slice(0, 8)}...`);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function runLogoutCommand() {
|
|
62
|
+
const configPath = getDefaultProfileConfigPath();
|
|
63
|
+
rmSync(configPath, { force: true });
|
|
64
|
+
console.log("Logged out.");
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
|
|
5
|
+
export type NoloProfile = {
|
|
6
|
+
serverUrl: string;
|
|
7
|
+
authToken: string;
|
|
8
|
+
agentKey?: string;
|
|
9
|
+
agentName?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type NoloProfileConfig = {
|
|
13
|
+
currentProfile: string;
|
|
14
|
+
profiles: Record<string, NoloProfile>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function getDefaultProfileConfigPath() {
|
|
18
|
+
return join(homedir(), ".nolo", "config.json");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadProfileConfig(path = getDefaultProfileConfigPath()): NoloProfileConfig | null {
|
|
22
|
+
if (!existsSync(path)) return null;
|
|
23
|
+
const raw = readFileSync(path, "utf8");
|
|
24
|
+
const parsed = JSON.parse(raw) as NoloProfileConfig;
|
|
25
|
+
if (!parsed.currentProfile || !parsed.profiles?.[parsed.currentProfile]) return null;
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function saveDefaultProfile(
|
|
30
|
+
path: string,
|
|
31
|
+
profile: NoloProfile
|
|
32
|
+
): NoloProfileConfig {
|
|
33
|
+
const config: NoloProfileConfig = {
|
|
34
|
+
currentProfile: "default",
|
|
35
|
+
profiles: {
|
|
36
|
+
default: profile,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
40
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
41
|
+
return config;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildEnvFromProfile(config: NoloProfileConfig | null) {
|
|
45
|
+
if (!config) return {};
|
|
46
|
+
const profile = config.profiles[config.currentProfile];
|
|
47
|
+
if (!profile) return {};
|
|
48
|
+
return {
|
|
49
|
+
NOLO_PROFILE: config.currentProfile,
|
|
50
|
+
NOLO_SERVER: profile.serverUrl,
|
|
51
|
+
AUTH_TOKEN: profile.authToken,
|
|
52
|
+
...(profile.agentKey ? { NOLO_AGENT: profile.agentKey } : {}),
|
|
53
|
+
...(profile.agentName ? { NOLO_AGENT_NAME: profile.agentName } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getCurrentProfile(config: NoloProfileConfig | null) {
|
|
58
|
+
if (!config) return null;
|
|
59
|
+
return config.profiles[config.currentProfile] ?? null;
|
|
60
|
+
}
|
package/commandRegistry.ts
CHANGED
|
@@ -66,6 +66,8 @@ export function renderHelpText() {
|
|
|
66
66
|
"Examples:",
|
|
67
67
|
" nolo",
|
|
68
68
|
" nolo chat",
|
|
69
|
+
" nolo login",
|
|
70
|
+
" nolo whoami",
|
|
69
71
|
' nolo doc create --title "Trip Notes" --body "hello"',
|
|
70
72
|
' nolo skill-doc create --title "Agent Query Skill" --description "Inspect recent agent dialogs"',
|
|
71
73
|
" nolo agent list --json",
|
package/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
6
|
import { renderHelpText, resolveCommand } from "./commandRegistry";
|
|
7
|
+
import { runLoginCommand, runLogoutCommand, runWhoamiCommand } from "./authCommands";
|
|
8
|
+
import { buildEnvFromProfile, loadProfileConfig } from "./client/profileConfig";
|
|
7
9
|
import { startTuiWorkspace } from "./tui/readlineWorkspace";
|
|
8
10
|
|
|
9
11
|
const CLI_DIR = dirname(fileURLToPath(import.meta.url));
|
|
@@ -24,10 +26,15 @@ async function runScript(script: string, forwardedArgs: string[]) {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const args = process.argv.slice(2);
|
|
29
|
+
const profileEnv = buildEnvFromProfile(loadProfileConfig());
|
|
30
|
+
const runtimeEnv = {
|
|
31
|
+
...profileEnv,
|
|
32
|
+
...process.env,
|
|
33
|
+
};
|
|
27
34
|
|
|
28
35
|
if (args.length === 0) {
|
|
29
36
|
if (process.stdin.isTTY) {
|
|
30
|
-
await startTuiWorkspace({ scriptDir: SCRIPT_DIR });
|
|
37
|
+
await startTuiWorkspace({ scriptDir: SCRIPT_DIR, env: runtimeEnv });
|
|
31
38
|
} else {
|
|
32
39
|
console.log(renderHelpText());
|
|
33
40
|
}
|
|
@@ -35,10 +42,22 @@ if (args.length === 0) {
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
if (args.length === 1 && (args[0] === "tui" || args[0] === "chat")) {
|
|
38
|
-
await startTuiWorkspace({ scriptDir: SCRIPT_DIR });
|
|
45
|
+
await startTuiWorkspace({ scriptDir: SCRIPT_DIR, env: runtimeEnv });
|
|
39
46
|
process.exit(0);
|
|
40
47
|
}
|
|
41
48
|
|
|
49
|
+
if (args[0] === "login") {
|
|
50
|
+
process.exit(await runLoginCommand(args.slice(1)));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (args[0] === "whoami") {
|
|
54
|
+
process.exit(runWhoamiCommand());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (args[0] === "logout") {
|
|
58
|
+
process.exit(runLogoutCommand());
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
43
62
|
console.log(renderHelpText());
|
|
44
63
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nolo-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first terminal workspace for Nolo",
|
|
6
6
|
"bin": {
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
"module": "index.ts",
|
|
10
10
|
"files": [
|
|
11
11
|
"index.ts",
|
|
12
|
+
"authCommands.ts",
|
|
12
13
|
"commandRegistry.ts",
|
|
13
14
|
"client/agentRun.ts",
|
|
15
|
+
"client/profileConfig.ts",
|
|
14
16
|
"tui/readlineWorkspace.ts",
|
|
15
17
|
"tui/session.ts",
|
|
16
18
|
"README.md"
|
package/tui/session.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export const DEFAULT_TUI_AGENT_KEY = "agent-pub-
|
|
1
|
+
export const DEFAULT_TUI_AGENT_KEY = "agent-pub-01APPBUILDER00000001YAII3I";
|
|
2
2
|
export const DEFAULT_TUI_SERVER_URL = "http://127.0.0.1:38123";
|
|
3
3
|
|
|
4
4
|
const KNOWN_AGENT_ALIASES: Record<string, { name: string; key: string }> = {
|
|
5
5
|
nolo: {
|
|
6
6
|
name: "nolo",
|
|
7
|
-
key:
|
|
7
|
+
key: "agent-pub-01NOLOAPPBLD000000019KCKT0",
|
|
8
8
|
},
|
|
9
9
|
"app-builder": {
|
|
10
10
|
name: "app-builder",
|
|
@@ -41,7 +41,7 @@ type EnvLike = Record<string, string | undefined>;
|
|
|
41
41
|
|
|
42
42
|
export function createInitialTuiState(env: EnvLike = process.env): TuiState {
|
|
43
43
|
const agentKey = env.NOLO_AGENT?.trim() || DEFAULT_TUI_AGENT_KEY;
|
|
44
|
-
const agentName = env.NOLO_AGENT_NAME?.trim() || "
|
|
44
|
+
const agentName = env.NOLO_AGENT_NAME?.trim() || "app-builder";
|
|
45
45
|
|
|
46
46
|
return {
|
|
47
47
|
agentKey,
|