agor-live 0.5.3 → 0.5.5
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/dist/cli/base-command.js +103 -9
- package/dist/cli/commands/auth/login.d.ts +18 -0
- package/dist/cli/commands/auth/login.js +133 -0
- package/dist/cli/commands/auth/logout.d.ts +15 -0
- package/dist/cli/commands/auth/logout.js +51 -0
- package/dist/cli/commands/auth/whoami.d.ts +15 -0
- package/dist/cli/commands/auth/whoami.js +67 -0
- package/dist/cli/commands/board/add-session.d.ts +4 -3
- package/dist/cli/commands/board/add-session.js +154 -33
- package/dist/cli/commands/board/list.d.ts +4 -3
- package/dist/cli/commands/board/list.js +148 -32
- package/dist/cli/commands/login.d.ts +16 -0
- package/dist/cli/commands/login.js +139 -0
- package/dist/cli/commands/logout.d.ts +15 -0
- package/dist/cli/commands/logout.js +57 -0
- package/dist/cli/commands/mcp/add.d.ts +4 -2
- package/dist/cli/commands/mcp/add.js +154 -36
- package/dist/cli/commands/mcp/list.d.ts +4 -3
- package/dist/cli/commands/mcp/list.js +154 -29
- package/dist/cli/commands/mcp/remove.d.ts +4 -3
- package/dist/cli/commands/mcp/remove.js +149 -36
- package/dist/cli/commands/mcp/show.d.ts +4 -3
- package/dist/cli/commands/mcp/show.js +169 -45
- package/dist/cli/commands/repo/add.d.ts +4 -2
- package/dist/cli/commands/repo/add.js +167 -41
- package/dist/cli/commands/repo/list.d.ts +4 -2
- package/dist/cli/commands/repo/list.js +154 -29
- package/dist/cli/commands/repo/rm.d.ts +4 -2
- package/dist/cli/commands/repo/rm.js +155 -29
- package/dist/cli/commands/session/list.js +103 -9
- package/dist/cli/commands/session/load-claude.d.ts +4 -2
- package/dist/cli/commands/session/load-claude.js +160 -40
- package/dist/cli/commands/user/create.d.ts +4 -2
- package/dist/cli/commands/user/create.js +153 -30
- package/dist/cli/commands/user/delete.d.ts +4 -2
- package/dist/cli/commands/user/delete.js +157 -25
- package/dist/cli/commands/user/update.d.ts +4 -2
- package/dist/cli/commands/user/update.js +162 -30
- package/dist/cli/commands/whoami.d.ts +15 -0
- package/dist/cli/commands/whoami.js +73 -0
- package/dist/cli/commands/worktree/add.d.ts +4 -2
- package/dist/cli/commands/worktree/add.js +150 -24
- package/dist/cli/commands/worktree/env/restart.js +103 -9
- package/dist/cli/commands/worktree/env/start.js +103 -9
- package/dist/cli/commands/worktree/env/status.js +103 -9
- package/dist/cli/commands/worktree/env/stop.js +103 -9
- package/dist/cli/commands/worktree/list.d.ts +4 -2
- package/dist/cli/commands/worktree/list.js +154 -27
- package/dist/cli/commands/worktree/rm.js +103 -9
- package/dist/cli/commands/worktree/show.js +103 -9
- package/dist/cli/commands/worktree/update.js +103 -9
- package/dist/cli/lib/auth.d.ts +29 -0
- package/dist/cli/lib/auth.js +37 -0
- package/dist/core/api/index.cjs +17 -0
- package/dist/core/api/index.d.cts +8 -1
- package/dist/core/api/index.d.ts +8 -1
- package/dist/core/api/index.js +16 -0
- package/dist/core/config/browser.d.cts +2 -0
- package/dist/core/config/browser.d.ts +2 -0
- package/dist/core/db/index.cjs +1 -1
- package/dist/core/db/index.js +1 -1
- package/dist/core/git/index.cjs +1 -14
- package/dist/core/git/index.d.cts +1 -14
- package/dist/core/git/index.d.ts +1 -14
- package/dist/core/git/index.js +1 -13
- package/dist/core/index.cjs +19 -15
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +18 -14
- package/dist/core/tools/index.cjs +31 -6
- package/dist/core/tools/index.js +31 -6
- package/dist/core/utils/cron.cjs +1 -1
- package/dist/core/utils/cron.js +1 -1
- package/dist/daemon/index.js +62 -106
- package/dist/daemon/services/context.js +3 -4
- package/dist/daemon/services/mcp-servers.d.ts +2 -7
- package/dist/daemon/services/scheduler.js +4 -84
- package/dist/daemon/services/worktrees.d.ts +2 -13
- package/dist/ui/assets/{index-B_3eFhE9.js → index-CDYLQXZ8.js} +127 -127
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
package/dist/cli/base-command.js
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/lib/auth.ts
|
|
12
|
+
var auth_exports = {};
|
|
13
|
+
__export(auth_exports, {
|
|
14
|
+
clearToken: () => clearToken,
|
|
15
|
+
loadToken: () => loadToken,
|
|
16
|
+
saveToken: () => saveToken
|
|
17
|
+
});
|
|
18
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
async function saveToken(auth) {
|
|
22
|
+
await mkdir(AGOR_DIR, { recursive: true });
|
|
23
|
+
await writeFile(TOKEN_FILE, JSON.stringify(auth, null, 2), {
|
|
24
|
+
mode: 384
|
|
25
|
+
// Owner read/write only
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function loadToken() {
|
|
29
|
+
try {
|
|
30
|
+
const data = await readFile(TOKEN_FILE, "utf-8");
|
|
31
|
+
const auth = JSON.parse(data);
|
|
32
|
+
if (auth.expiresAt && Date.now() > auth.expiresAt) {
|
|
33
|
+
await clearToken();
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return auth;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function clearToken() {
|
|
42
|
+
try {
|
|
43
|
+
await unlink(TOKEN_FILE);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var AGOR_DIR, TOKEN_FILE;
|
|
48
|
+
var init_auth = __esm({
|
|
49
|
+
"src/lib/auth.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
AGOR_DIR = join(homedir(), ".agor");
|
|
52
|
+
TOKEN_FILE = join(AGOR_DIR, "cli-token");
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
1
56
|
// src/base-command.ts
|
|
2
|
-
|
|
57
|
+
init_auth();
|
|
58
|
+
import { createRestClient, isDaemonRunning } from "@agor/core/api";
|
|
3
59
|
import { getDaemonUrl } from "@agor/core/config";
|
|
4
60
|
import { Command } from "@oclif/core";
|
|
5
61
|
import chalk from "chalk";
|
|
@@ -19,7 +75,48 @@ var BaseCommand = class extends Command {
|
|
|
19
75
|
);
|
|
20
76
|
this.exit(1);
|
|
21
77
|
}
|
|
22
|
-
|
|
78
|
+
const client = await createRestClient(this.daemonUrl);
|
|
79
|
+
const storedAuth = await loadToken();
|
|
80
|
+
if (storedAuth) {
|
|
81
|
+
try {
|
|
82
|
+
await client.authenticate({
|
|
83
|
+
strategy: "jwt",
|
|
84
|
+
accessToken: storedAuth.accessToken
|
|
85
|
+
});
|
|
86
|
+
} catch (_error) {
|
|
87
|
+
const { clearToken: clearToken2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
88
|
+
await clearToken2();
|
|
89
|
+
this.error(
|
|
90
|
+
chalk.red("\u2717 Authentication failed") + "\n\n" + chalk.dim("Your session has expired or is invalid.") + "\n" + chalk.dim("Please login again:") + "\n " + chalk.cyan("agor login")
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetch(`${this.daemonUrl}/health`);
|
|
96
|
+
const health = await response.json();
|
|
97
|
+
if (health.auth?.requireAuth) {
|
|
98
|
+
this.error(
|
|
99
|
+
chalk.red("\u2717 Not authenticated") + "\n\n" + chalk.dim("This Agor instance requires authentication.") + "\n" + chalk.dim("Please login:") + "\n " + chalk.cyan("agor login")
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await client.authenticate({ strategy: "anonymous" });
|
|
104
|
+
} catch (_authError) {
|
|
105
|
+
this.error(
|
|
106
|
+
chalk.red("\u2717 Authentication failed") + "\n\n" + chalk.dim("Please login:") + "\n " + chalk.cyan("agor login")
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
} catch (_error) {
|
|
110
|
+
try {
|
|
111
|
+
await client.authenticate({ strategy: "anonymous" });
|
|
112
|
+
} catch {
|
|
113
|
+
this.error(
|
|
114
|
+
chalk.red("\u2717 Not authenticated") + "\n\n" + chalk.dim("Please login to use the Agor CLI:") + "\n " + chalk.cyan("agor login")
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return client;
|
|
23
120
|
}
|
|
24
121
|
/**
|
|
25
122
|
* Cleanup client connection
|
|
@@ -27,13 +124,10 @@ var BaseCommand = class extends Command {
|
|
|
27
124
|
* Ensures socket is properly closed to prevent hanging processes
|
|
28
125
|
*/
|
|
29
126
|
async cleanupClient(client) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
client.io.close();
|
|
35
|
-
setTimeout(resolve, 1e3);
|
|
36
|
-
});
|
|
127
|
+
client.io.io.opts.reconnection = false;
|
|
128
|
+
client.io.removeAllListeners();
|
|
129
|
+
client.io.close();
|
|
130
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
37
131
|
}
|
|
38
132
|
};
|
|
39
133
|
export {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as _oclif_core_interfaces from '@oclif/core/interfaces';
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
|
+
|
|
4
|
+
declare class Login extends Command {
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
email: _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
|
|
9
|
+
password: _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Prompt helper with proper typing
|
|
14
|
+
*/
|
|
15
|
+
private prompt;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Login as default };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// src/commands/auth/login.ts
|
|
2
|
+
import * as readline from "readline";
|
|
3
|
+
import { createRestClient, isDaemonRunning } from "@agor/core/api";
|
|
4
|
+
import { getDaemonUrl } from "@agor/core/config";
|
|
5
|
+
import { Command, Flags } from "@oclif/core";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
|
|
8
|
+
// src/lib/auth.ts
|
|
9
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var AGOR_DIR = join(homedir(), ".agor");
|
|
13
|
+
var TOKEN_FILE = join(AGOR_DIR, "cli-token");
|
|
14
|
+
async function saveToken(auth) {
|
|
15
|
+
await mkdir(AGOR_DIR, { recursive: true });
|
|
16
|
+
await writeFile(TOKEN_FILE, JSON.stringify(auth, null, 2), {
|
|
17
|
+
mode: 384
|
|
18
|
+
// Owner read/write only
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/commands/auth/login.ts
|
|
23
|
+
var Login = class _Login extends Command {
|
|
24
|
+
static description = "Authenticate with Agor daemon";
|
|
25
|
+
static examples = [
|
|
26
|
+
"<%= config.bin %> <%= command.id %>",
|
|
27
|
+
"<%= config.bin %> <%= command.id %> --email user@example.com"
|
|
28
|
+
];
|
|
29
|
+
static flags = {
|
|
30
|
+
email: Flags.string({
|
|
31
|
+
char: "e",
|
|
32
|
+
description: "Email address"
|
|
33
|
+
}),
|
|
34
|
+
password: Flags.string({
|
|
35
|
+
char: "p",
|
|
36
|
+
description: "Password (will prompt if not provided)"
|
|
37
|
+
})
|
|
38
|
+
};
|
|
39
|
+
async run() {
|
|
40
|
+
const { flags } = await this.parse(_Login);
|
|
41
|
+
const daemonUrl = await getDaemonUrl();
|
|
42
|
+
const running = await isDaemonRunning(daemonUrl);
|
|
43
|
+
if (!running) {
|
|
44
|
+
this.error(
|
|
45
|
+
chalk.red("\u2717 Daemon not running") + "\n\n" + chalk.bold("To start the daemon:") + "\n " + chalk.cyan("cd apps/agor-daemon && pnpm dev")
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const email = flags.email || await this.prompt("Email", {
|
|
49
|
+
type: "input",
|
|
50
|
+
required: true
|
|
51
|
+
});
|
|
52
|
+
const password = flags.password || await this.prompt("Password", {
|
|
53
|
+
type: "hide",
|
|
54
|
+
required: true
|
|
55
|
+
});
|
|
56
|
+
const client = await createRestClient(daemonUrl);
|
|
57
|
+
try {
|
|
58
|
+
this.log(chalk.dim("Authenticating..."));
|
|
59
|
+
const authResult = await client.authenticate({
|
|
60
|
+
strategy: "local",
|
|
61
|
+
email,
|
|
62
|
+
password
|
|
63
|
+
});
|
|
64
|
+
if (!authResult.accessToken || !authResult.user) {
|
|
65
|
+
this.error("Authentication failed - no token returned");
|
|
66
|
+
}
|
|
67
|
+
const expiresAt = Date.now() + 7 * 24 * 60 * 60 * 1e3;
|
|
68
|
+
await saveToken({
|
|
69
|
+
accessToken: authResult.accessToken,
|
|
70
|
+
user: {
|
|
71
|
+
user_id: authResult.user.user_id,
|
|
72
|
+
email: authResult.user.email,
|
|
73
|
+
// biome-ignore lint/suspicious/noExplicitAny: AuthenticatedUser type doesn't include name, but it's returned
|
|
74
|
+
name: authResult.user.name,
|
|
75
|
+
role: authResult.user.role || "viewer"
|
|
76
|
+
},
|
|
77
|
+
expiresAt
|
|
78
|
+
});
|
|
79
|
+
this.log("");
|
|
80
|
+
this.log(chalk.green("\u2713 Logged in successfully"));
|
|
81
|
+
this.log("");
|
|
82
|
+
this.log(chalk.dim("User:"), chalk.cyan(authResult.user.email));
|
|
83
|
+
const userName = authResult.user.name;
|
|
84
|
+
if (userName) {
|
|
85
|
+
this.log(chalk.dim("Name:"), userName);
|
|
86
|
+
}
|
|
87
|
+
this.log(chalk.dim("Role:"), authResult.user.role || "viewer");
|
|
88
|
+
this.log("");
|
|
89
|
+
this.log(chalk.dim("Token saved to ~/.agor/cli-token"));
|
|
90
|
+
this.log(chalk.dim("Token expires in 7 days"));
|
|
91
|
+
this.log("");
|
|
92
|
+
client.io.io.opts.reconnection = false;
|
|
93
|
+
client.io.removeAllListeners();
|
|
94
|
+
client.io.close();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
client.io.io.opts.reconnection = false;
|
|
98
|
+
client.io.removeAllListeners();
|
|
99
|
+
client.io.close();
|
|
100
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
101
|
+
if (errorMessage.includes("Invalid login") || errorMessage.includes("NotFound")) {
|
|
102
|
+
this.error(chalk.red("\u2717 Invalid email or password"));
|
|
103
|
+
}
|
|
104
|
+
this.error(chalk.red(`\u2717 Authentication failed: ${errorMessage}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Prompt helper with proper typing
|
|
109
|
+
*/
|
|
110
|
+
async prompt(message, options) {
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
const rl = readline.createInterface({
|
|
113
|
+
input: process.stdin,
|
|
114
|
+
output: process.stdout
|
|
115
|
+
});
|
|
116
|
+
const hiddenOutput = options.type === "hide";
|
|
117
|
+
if (hiddenOutput && process.stdin.isTTY) {
|
|
118
|
+
process.stdin.setRawMode?.(true);
|
|
119
|
+
}
|
|
120
|
+
rl.question(`${message}: `, (answer) => {
|
|
121
|
+
if (hiddenOutput && process.stdin.isTTY) {
|
|
122
|
+
process.stdin.setRawMode?.(false);
|
|
123
|
+
console.log("");
|
|
124
|
+
}
|
|
125
|
+
rl.close();
|
|
126
|
+
resolve(answer.trim());
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
export {
|
|
132
|
+
Login as default
|
|
133
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `agor logout` - Clear stored authentication token
|
|
5
|
+
*
|
|
6
|
+
* Removes JWT token from disk
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare class Logout extends Command {
|
|
10
|
+
static description: string;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { Logout as default };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/commands/auth/logout.ts
|
|
2
|
+
import { Command } from "@oclif/core";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
// src/lib/auth.ts
|
|
6
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
var AGOR_DIR = join(homedir(), ".agor");
|
|
10
|
+
var TOKEN_FILE = join(AGOR_DIR, "cli-token");
|
|
11
|
+
async function loadToken() {
|
|
12
|
+
try {
|
|
13
|
+
const data = await readFile(TOKEN_FILE, "utf-8");
|
|
14
|
+
const auth = JSON.parse(data);
|
|
15
|
+
if (auth.expiresAt && Date.now() > auth.expiresAt) {
|
|
16
|
+
await clearToken();
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return auth;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function clearToken() {
|
|
25
|
+
try {
|
|
26
|
+
await unlink(TOKEN_FILE);
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/commands/auth/logout.ts
|
|
32
|
+
var Logout = class extends Command {
|
|
33
|
+
static description = "Logout and clear stored authentication token";
|
|
34
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
35
|
+
async run() {
|
|
36
|
+
const storedAuth = await loadToken();
|
|
37
|
+
if (!storedAuth) {
|
|
38
|
+
this.log(chalk.dim("Not currently logged in"));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await clearToken();
|
|
42
|
+
this.log("");
|
|
43
|
+
this.log(chalk.green("\u2713 Logged out successfully"));
|
|
44
|
+
this.log("");
|
|
45
|
+
this.log(chalk.dim("Token removed from ~/.agor/cli-token"));
|
|
46
|
+
this.log("");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
Logout as default
|
|
51
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `agor whoami` - Show current authenticated user
|
|
5
|
+
*
|
|
6
|
+
* Displays information about the currently authenticated user
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare class Whoami extends Command {
|
|
10
|
+
static description: string;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { Whoami as default };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/commands/auth/whoami.ts
|
|
2
|
+
import { Command } from "@oclif/core";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
// src/lib/auth.ts
|
|
6
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
var AGOR_DIR = join(homedir(), ".agor");
|
|
10
|
+
var TOKEN_FILE = join(AGOR_DIR, "cli-token");
|
|
11
|
+
async function loadToken() {
|
|
12
|
+
try {
|
|
13
|
+
const data = await readFile(TOKEN_FILE, "utf-8");
|
|
14
|
+
const auth = JSON.parse(data);
|
|
15
|
+
if (auth.expiresAt && Date.now() > auth.expiresAt) {
|
|
16
|
+
await clearToken();
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return auth;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function clearToken() {
|
|
25
|
+
try {
|
|
26
|
+
await unlink(TOKEN_FILE);
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/commands/auth/whoami.ts
|
|
32
|
+
var Whoami = class extends Command {
|
|
33
|
+
static description = "Show current authenticated user";
|
|
34
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
35
|
+
async run() {
|
|
36
|
+
const storedAuth = await loadToken();
|
|
37
|
+
if (!storedAuth) {
|
|
38
|
+
this.log(chalk.dim("Not currently logged in"));
|
|
39
|
+
this.log("");
|
|
40
|
+
this.log(chalk.dim("To login:"), chalk.cyan("agor login"));
|
|
41
|
+
this.log("");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.log("");
|
|
45
|
+
this.log(chalk.green("\u2713 Authenticated"));
|
|
46
|
+
this.log("");
|
|
47
|
+
this.log(chalk.dim("User ID:"), chalk.cyan(storedAuth.user.user_id));
|
|
48
|
+
this.log(chalk.dim("Email:"), chalk.cyan(storedAuth.user.email));
|
|
49
|
+
if (storedAuth.user.name) {
|
|
50
|
+
this.log(chalk.dim("Name:"), storedAuth.user.name);
|
|
51
|
+
}
|
|
52
|
+
this.log(chalk.dim("Role:"), storedAuth.user.role);
|
|
53
|
+
this.log("");
|
|
54
|
+
const expiresIn = storedAuth.expiresAt - Date.now();
|
|
55
|
+
const daysLeft = Math.floor(expiresIn / (24 * 60 * 60 * 1e3));
|
|
56
|
+
const hoursLeft = Math.floor(expiresIn % (24 * 60 * 60 * 1e3) / (60 * 60 * 1e3));
|
|
57
|
+
if (expiresIn > 0) {
|
|
58
|
+
this.log(chalk.dim("Token expires in:"), `${daysLeft}d ${hoursLeft}h`);
|
|
59
|
+
} else {
|
|
60
|
+
this.log(chalk.red("Token expired"), chalk.dim("- please login again"));
|
|
61
|
+
}
|
|
62
|
+
this.log("");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
export {
|
|
66
|
+
Whoami as default
|
|
67
|
+
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as _oclif_core_interfaces from '@oclif/core/interfaces';
|
|
2
|
-
import {
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import '@agor/core/api';
|
|
4
|
+
import '@oclif/core';
|
|
3
5
|
|
|
4
|
-
declare class BoardAddSession extends
|
|
6
|
+
declare class BoardAddSession extends BaseCommand {
|
|
5
7
|
static description: string;
|
|
6
8
|
static examples: string[];
|
|
7
9
|
static args: {
|
|
@@ -9,7 +11,6 @@ declare class BoardAddSession extends Command {
|
|
|
9
11
|
sessionId: _oclif_core_interfaces.Arg<string, Record<string, unknown>>;
|
|
10
12
|
};
|
|
11
13
|
run(): Promise<void>;
|
|
12
|
-
private cleanup;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export { BoardAddSession as default };
|
|
@@ -1,8 +1,142 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/lib/auth.ts
|
|
12
|
+
var auth_exports = {};
|
|
13
|
+
__export(auth_exports, {
|
|
14
|
+
clearToken: () => clearToken,
|
|
15
|
+
loadToken: () => loadToken,
|
|
16
|
+
saveToken: () => saveToken
|
|
17
|
+
});
|
|
18
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
async function saveToken(auth) {
|
|
22
|
+
await mkdir(AGOR_DIR, { recursive: true });
|
|
23
|
+
await writeFile(TOKEN_FILE, JSON.stringify(auth, null, 2), {
|
|
24
|
+
mode: 384
|
|
25
|
+
// Owner read/write only
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function loadToken() {
|
|
29
|
+
try {
|
|
30
|
+
const data = await readFile(TOKEN_FILE, "utf-8");
|
|
31
|
+
const auth = JSON.parse(data);
|
|
32
|
+
if (auth.expiresAt && Date.now() > auth.expiresAt) {
|
|
33
|
+
await clearToken();
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return auth;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function clearToken() {
|
|
42
|
+
try {
|
|
43
|
+
await unlink(TOKEN_FILE);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var AGOR_DIR, TOKEN_FILE;
|
|
48
|
+
var init_auth = __esm({
|
|
49
|
+
"src/lib/auth.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
AGOR_DIR = join(homedir(), ".agor");
|
|
52
|
+
TOKEN_FILE = join(AGOR_DIR, "cli-token");
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
1
56
|
// src/commands/board/add-session.ts
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
57
|
+
import { Args } from "@oclif/core";
|
|
58
|
+
import chalk2 from "chalk";
|
|
59
|
+
|
|
60
|
+
// src/base-command.ts
|
|
61
|
+
init_auth();
|
|
62
|
+
import { createRestClient, isDaemonRunning } from "@agor/core/api";
|
|
63
|
+
import { getDaemonUrl } from "@agor/core/config";
|
|
64
|
+
import { Command } from "@oclif/core";
|
|
4
65
|
import chalk from "chalk";
|
|
5
|
-
var
|
|
66
|
+
var BaseCommand = class extends Command {
|
|
67
|
+
daemonUrl = null;
|
|
68
|
+
/**
|
|
69
|
+
* Connect to daemon (checks if running first)
|
|
70
|
+
*
|
|
71
|
+
* @returns Feathers client instance
|
|
72
|
+
*/
|
|
73
|
+
async connectToDaemon() {
|
|
74
|
+
this.daemonUrl = await getDaemonUrl();
|
|
75
|
+
const running = await isDaemonRunning(this.daemonUrl);
|
|
76
|
+
if (!running) {
|
|
77
|
+
this.log(
|
|
78
|
+
chalk.red("\u2717 Daemon not running") + "\n\n" + chalk.bold("To start the daemon:") + "\n " + chalk.cyan("cd apps/agor-daemon && pnpm dev") + "\n\n" + chalk.bold("To configure daemon URL:") + "\n " + chalk.cyan("agor config set daemon.url <url>") + "\n " + chalk.gray(`Current: ${this.daemonUrl}`)
|
|
79
|
+
);
|
|
80
|
+
this.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const client = await createRestClient(this.daemonUrl);
|
|
83
|
+
const storedAuth = await loadToken();
|
|
84
|
+
if (storedAuth) {
|
|
85
|
+
try {
|
|
86
|
+
await client.authenticate({
|
|
87
|
+
strategy: "jwt",
|
|
88
|
+
accessToken: storedAuth.accessToken
|
|
89
|
+
});
|
|
90
|
+
} catch (_error) {
|
|
91
|
+
const { clearToken: clearToken2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
92
|
+
await clearToken2();
|
|
93
|
+
this.error(
|
|
94
|
+
chalk.red("\u2717 Authentication failed") + "\n\n" + chalk.dim("Your session has expired or is invalid.") + "\n" + chalk.dim("Please login again:") + "\n " + chalk.cyan("agor login")
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(`${this.daemonUrl}/health`);
|
|
100
|
+
const health = await response.json();
|
|
101
|
+
if (health.auth?.requireAuth) {
|
|
102
|
+
this.error(
|
|
103
|
+
chalk.red("\u2717 Not authenticated") + "\n\n" + chalk.dim("This Agor instance requires authentication.") + "\n" + chalk.dim("Please login:") + "\n " + chalk.cyan("agor login")
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
await client.authenticate({ strategy: "anonymous" });
|
|
108
|
+
} catch (_authError) {
|
|
109
|
+
this.error(
|
|
110
|
+
chalk.red("\u2717 Authentication failed") + "\n\n" + chalk.dim("Please login:") + "\n " + chalk.cyan("agor login")
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} catch (_error) {
|
|
114
|
+
try {
|
|
115
|
+
await client.authenticate({ strategy: "anonymous" });
|
|
116
|
+
} catch {
|
|
117
|
+
this.error(
|
|
118
|
+
chalk.red("\u2717 Not authenticated") + "\n\n" + chalk.dim("Please login to use the Agor CLI:") + "\n " + chalk.cyan("agor login")
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return client;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Cleanup client connection
|
|
127
|
+
*
|
|
128
|
+
* Ensures socket is properly closed to prevent hanging processes
|
|
129
|
+
*/
|
|
130
|
+
async cleanupClient(client) {
|
|
131
|
+
client.io.io.opts.reconnection = false;
|
|
132
|
+
client.io.removeAllListeners();
|
|
133
|
+
client.io.close();
|
|
134
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/commands/board/add-session.ts
|
|
139
|
+
var BoardAddSession = class _BoardAddSession extends BaseCommand {
|
|
6
140
|
static description = "Add a session's worktree to a board (sessions are organized through worktrees)";
|
|
7
141
|
static examples = [
|
|
8
142
|
"<%= config.bin %> <%= command.id %> default 0199b86c",
|
|
@@ -20,7 +154,7 @@ var BoardAddSession = class _BoardAddSession extends Command {
|
|
|
20
154
|
};
|
|
21
155
|
async run() {
|
|
22
156
|
const { args } = await this.parse(_BoardAddSession);
|
|
23
|
-
const client =
|
|
157
|
+
const client = await this.connectToDaemon();
|
|
24
158
|
try {
|
|
25
159
|
const boardsResult = await client.service("boards").find();
|
|
26
160
|
const boards = Array.isArray(boardsResult) ? boardsResult : boardsResult.data;
|
|
@@ -28,9 +162,8 @@ var BoardAddSession = class _BoardAddSession extends Command {
|
|
|
28
162
|
(b) => b.board_id === args.boardId || b.board_id.startsWith(args.boardId) || b.slug === args.boardId
|
|
29
163
|
);
|
|
30
164
|
if (!board) {
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
process.exit(1);
|
|
165
|
+
await this.cleanupClient(client);
|
|
166
|
+
this.error(`Board not found: ${args.boardId}`);
|
|
34
167
|
}
|
|
35
168
|
const sessionsResult = await client.service("sessions").find();
|
|
36
169
|
const sessions = Array.isArray(sessionsResult) ? sessionsResult : sessionsResult.data;
|
|
@@ -38,22 +171,19 @@ var BoardAddSession = class _BoardAddSession extends Command {
|
|
|
38
171
|
(s) => s.session_id === args.sessionId || s.session_id.startsWith(args.sessionId)
|
|
39
172
|
);
|
|
40
173
|
if (!session) {
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
process.exit(1);
|
|
174
|
+
await this.cleanupClient(client);
|
|
175
|
+
this.error(`Session not found: ${args.sessionId}`);
|
|
44
176
|
}
|
|
45
177
|
if (!session.worktree_id) {
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
process.exit(1);
|
|
178
|
+
await this.cleanupClient(client);
|
|
179
|
+
this.error("Session has no worktree associated");
|
|
49
180
|
}
|
|
50
181
|
const worktreesResult = await client.service("worktrees").find();
|
|
51
182
|
const worktrees = Array.isArray(worktreesResult) ? worktreesResult : worktreesResult.data;
|
|
52
183
|
const worktree = worktrees.find((w) => w.worktree_id === session.worktree_id);
|
|
53
184
|
if (!worktree) {
|
|
54
|
-
this.
|
|
55
|
-
|
|
56
|
-
process.exit(1);
|
|
185
|
+
await this.cleanupClient(client);
|
|
186
|
+
this.error("Worktree not found for session");
|
|
57
187
|
}
|
|
58
188
|
const boardObjectsResult = await client.service("board-objects").find({
|
|
59
189
|
query: {
|
|
@@ -65,8 +195,8 @@ var BoardAddSession = class _BoardAddSession extends Command {
|
|
|
65
195
|
(bo) => bo.worktree_id === worktree.worktree_id
|
|
66
196
|
);
|
|
67
197
|
if (existingObject) {
|
|
68
|
-
this.log(
|
|
69
|
-
await this.
|
|
198
|
+
this.log(chalk2.yellow(`\u26A0 Worktree "${worktree.name}" already on board "${board.name}"`));
|
|
199
|
+
await this.cleanupClient(client);
|
|
70
200
|
return;
|
|
71
201
|
}
|
|
72
202
|
await client.service("board-objects").create({
|
|
@@ -75,26 +205,17 @@ var BoardAddSession = class _BoardAddSession extends Command {
|
|
|
75
205
|
position: { x: 100, y: 100 }
|
|
76
206
|
});
|
|
77
207
|
this.log(
|
|
78
|
-
|
|
208
|
+
chalk2.green(
|
|
79
209
|
`\u2713 Added worktree "${worktree.name}" (containing session ${session.session_id.substring(0, 8)}) to board "${board.name}"`
|
|
80
210
|
)
|
|
81
211
|
);
|
|
82
212
|
} catch (error) {
|
|
83
|
-
this.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await this.cleanup(client);
|
|
88
|
-
process.exit(1);
|
|
213
|
+
await this.cleanupClient(client);
|
|
214
|
+
this.error(
|
|
215
|
+
`Failed to add session to board: ${error instanceof Error ? error.message : String(error)}`
|
|
216
|
+
);
|
|
89
217
|
}
|
|
90
|
-
await this.
|
|
91
|
-
}
|
|
92
|
-
async cleanup(client) {
|
|
93
|
-
await new Promise((resolve) => {
|
|
94
|
-
client.io.once("disconnect", () => resolve());
|
|
95
|
-
client.io.close();
|
|
96
|
-
setTimeout(() => resolve(), 1e3);
|
|
97
|
-
});
|
|
218
|
+
await this.cleanupClient(client);
|
|
98
219
|
}
|
|
99
220
|
};
|
|
100
221
|
export {
|