climemo 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/dist/commands/ask.d.ts +2 -0
- package/dist/commands/ask.js +37 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/compare.d.ts +2 -0
- package/dist/commands/compare.js +57 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +37 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/daemon.d.ts +2 -0
- package/dist/commands/daemon.js +69 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/invite.d.ts +2 -0
- package/dist/commands/invite.js +49 -0
- package/dist/commands/invite.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +94 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +9 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/members.d.ts +2 -0
- package/dist/commands/members.js +98 -0
- package/dist/commands/members.js.map +1 -0
- package/dist/commands/merge.d.ts +2 -0
- package/dist/commands/merge.js +120 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +32 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +200 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/token.d.ts +2 -0
- package/dist/commands/token.js +60 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +20 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.js +27 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/install.d.ts +13 -0
- package/dist/daemon/install.js +143 -0
- package/dist/daemon/install.js.map +1 -0
- package/dist/daemon/logger.d.ts +8 -0
- package/dist/daemon/logger.js +41 -0
- package/dist/daemon/logger.js.map +1 -0
- package/dist/daemon/token-refresher.d.ts +14 -0
- package/dist/daemon/token-refresher.js +88 -0
- package/dist/daemon/token-refresher.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +13 -0
- package/dist/lib/api.js +18 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/auth.d.ts +24 -0
- package/dist/lib/auth.js +72 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.js +36 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/git.d.ts +24 -0
- package/dist/lib/git.js +63 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/keychain.d.ts +4 -0
- package/dist/lib/keychain.js +39 -0
- package/dist/lib/keychain.js.map +1 -0
- package/dist/lib/project.d.ts +13 -0
- package/dist/lib/project.js +133 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/server.d.ts +9 -0
- package/dist/lib/server.js +80 -0
- package/dist/lib/server.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiRequest } from "../lib/api.js";
|
|
3
|
+
import { resolveProject } from "../lib/project.js";
|
|
4
|
+
export const askCommand = new Command("ask")
|
|
5
|
+
.description("Ask AI about your project documents")
|
|
6
|
+
.argument("<question>", "Question to ask")
|
|
7
|
+
.option("--project <projectId>", "Project ID (auto-detected from git repo if omitted)")
|
|
8
|
+
.option("--model <model>", "AI model to use (e.g., google/gemini-2.0-flash-001)")
|
|
9
|
+
.action(async (question, options, cmd) => {
|
|
10
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
11
|
+
try {
|
|
12
|
+
const projectId = options.project || await resolveProject(globalOpts.token);
|
|
13
|
+
const body = {
|
|
14
|
+
question,
|
|
15
|
+
project_ids: [projectId],
|
|
16
|
+
};
|
|
17
|
+
if (options.model) {
|
|
18
|
+
body.model = options.model;
|
|
19
|
+
}
|
|
20
|
+
const { ok, data } = await apiRequest("/api/ask", {
|
|
21
|
+
method: "POST",
|
|
22
|
+
body,
|
|
23
|
+
token: globalOpts.token,
|
|
24
|
+
});
|
|
25
|
+
if (!ok) {
|
|
26
|
+
console.error(JSON.stringify({ error: "Failed to get answer", details: data }));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
console.log(JSON.stringify(data, null, 2));
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
console.error(JSON.stringify({ error: message }));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,qCAAqC,CAAC;KAClD,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;KACzC,MAAM,CAAC,uBAAuB,EAAE,qDAAqD,CAAC;KACtF,MAAM,CAAC,iBAAiB,EAAE,qDAAqD,CAAC;KAChF,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;IAC/C,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;IAEzC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAE5E,MAAM,IAAI,GAA4B;YACpC,QAAQ;YACR,WAAW,EAAE,CAAC,SAAS,CAAC;SACzB,CAAC;QACF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC7B,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE;YAChD,MAAM,EAAE,MAAM;YACd,IAAI;YACJ,KAAK,EAAE,UAAU,CAAC,KAAK;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiRequest } from "../lib/api.js";
|
|
3
|
+
export const compareCommand = new Command("compare")
|
|
4
|
+
.description("Compare projects on a specific topic")
|
|
5
|
+
.argument("<projectA>", "First project ID")
|
|
6
|
+
.argument("[projectB]", "Second project ID (optional if --all)")
|
|
7
|
+
.option("--all", "Compare all projects")
|
|
8
|
+
.option("--topic <topic>", "Comparison topic", "overview")
|
|
9
|
+
.option("--model <model>", "AI model to use")
|
|
10
|
+
.action(async (projectA, projectB, options, cmd) => {
|
|
11
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12
|
+
try {
|
|
13
|
+
let projectIds;
|
|
14
|
+
if (options.all) {
|
|
15
|
+
// Fetch all projects first
|
|
16
|
+
const { ok, data } = await apiRequest("/api/projects", {
|
|
17
|
+
token: globalOpts.token,
|
|
18
|
+
});
|
|
19
|
+
if (!ok || !data) {
|
|
20
|
+
console.error(JSON.stringify({ error: "Failed to fetch projects" }));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
projectIds = (data.data || []).map((p) => p.id);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
projectIds = [projectA];
|
|
27
|
+
if (projectB)
|
|
28
|
+
projectIds.push(projectB);
|
|
29
|
+
if (projectIds.length < 2) {
|
|
30
|
+
console.error(JSON.stringify({ error: "Need at least 2 projects to compare. Use --all or provide two project IDs." }));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const body = {
|
|
35
|
+
project_ids: projectIds,
|
|
36
|
+
topic: options.topic,
|
|
37
|
+
};
|
|
38
|
+
if (options.model)
|
|
39
|
+
body.model = options.model;
|
|
40
|
+
const { ok, data } = await apiRequest("/api/compare", {
|
|
41
|
+
method: "POST",
|
|
42
|
+
body,
|
|
43
|
+
token: globalOpts.token,
|
|
44
|
+
});
|
|
45
|
+
if (!ok) {
|
|
46
|
+
console.error(JSON.stringify({ error: "Comparison failed", details: data }));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
console.log(JSON.stringify(data, null, 2));
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
53
|
+
console.error(JSON.stringify({ error: message }));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/commands/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getConfig, setConfig } from "../lib/config.js";
|
|
3
|
+
export const configCommand = new Command("config")
|
|
4
|
+
.description("CLI configuration");
|
|
5
|
+
configCommand
|
|
6
|
+
.command("set <key> <value>")
|
|
7
|
+
.description("Set a config value")
|
|
8
|
+
.action((key, value) => {
|
|
9
|
+
const validKeys = ["apiUrl", "defaultProject", "defaultModel", "slackWebhook"];
|
|
10
|
+
if (!validKeys.includes(key)) {
|
|
11
|
+
console.error(`Unknown config key: ${key}`);
|
|
12
|
+
console.error(`Valid keys: ${validKeys.join(", ")}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
setConfig({ [key]: value });
|
|
16
|
+
console.log(JSON.stringify({ [key]: value }));
|
|
17
|
+
});
|
|
18
|
+
configCommand
|
|
19
|
+
.command("list")
|
|
20
|
+
.description("Show current configuration")
|
|
21
|
+
.action(() => {
|
|
22
|
+
const config = getConfig();
|
|
23
|
+
console.log(JSON.stringify(config, null, 2));
|
|
24
|
+
});
|
|
25
|
+
configCommand
|
|
26
|
+
.command("get <key>")
|
|
27
|
+
.description("Get a config value")
|
|
28
|
+
.action((key) => {
|
|
29
|
+
const config = getConfig();
|
|
30
|
+
const value = config[key];
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
console.error(`Config key not set: ${key}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
console.log(JSON.stringify({ [key]: value }));
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAkB,MAAM,kBAAkB,CAAC;AAExE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,mBAAmB,CAAC,CAAC;AAEpC,aAAa;KACV,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE;IACrC,MAAM,SAAS,GAAwB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;IAEpG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAsB,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,KAAK,CAAC,eAAe,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAwB,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEL,aAAa;KACV,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEL,aAAa;KACV,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,CAAC,GAAW,EAAE,EAAE;IACtB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAsB,CAAC,CAAC;IAC7C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { installDaemon, uninstallDaemon, isDaemonRunning } from "../daemon/install.js";
|
|
3
|
+
import { DaemonLogger } from "../daemon/logger.js";
|
|
4
|
+
export const daemonCommand = new Command("daemon")
|
|
5
|
+
.description("Daemon management");
|
|
6
|
+
daemonCommand
|
|
7
|
+
.command("start")
|
|
8
|
+
.description("Install and start the daemon")
|
|
9
|
+
.action(() => {
|
|
10
|
+
const logger = new DaemonLogger();
|
|
11
|
+
try {
|
|
12
|
+
installDaemon(logger);
|
|
13
|
+
console.log(JSON.stringify({ status: "started", message: "Daemon installed and started." }));
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17
|
+
console.error(JSON.stringify({ status: "error", message }));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
daemonCommand
|
|
22
|
+
.command("stop")
|
|
23
|
+
.description("Stop and uninstall the daemon")
|
|
24
|
+
.action(() => {
|
|
25
|
+
const logger = new DaemonLogger();
|
|
26
|
+
try {
|
|
27
|
+
uninstallDaemon(logger);
|
|
28
|
+
console.log(JSON.stringify({ status: "stopped", message: "Daemon stopped and uninstalled." }));
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32
|
+
console.error(JSON.stringify({ status: "error", message }));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
daemonCommand
|
|
37
|
+
.command("status")
|
|
38
|
+
.description("Check daemon status")
|
|
39
|
+
.action(() => {
|
|
40
|
+
const running = isDaemonRunning();
|
|
41
|
+
console.log(JSON.stringify({
|
|
42
|
+
status: running ? "running" : "stopped",
|
|
43
|
+
platform: process.platform,
|
|
44
|
+
}));
|
|
45
|
+
});
|
|
46
|
+
daemonCommand
|
|
47
|
+
.command("run")
|
|
48
|
+
.description("Run daemon in foreground (used by launchd/systemd)")
|
|
49
|
+
.action(async () => {
|
|
50
|
+
// Dynamic import to avoid loading daemon code for other commands
|
|
51
|
+
const { TokenRefresher } = await import("../daemon/token-refresher.js");
|
|
52
|
+
const logger = new DaemonLogger();
|
|
53
|
+
logger.info("Climemo daemon starting in foreground...");
|
|
54
|
+
const tokenRefresher = new TokenRefresher(logger);
|
|
55
|
+
tokenRefresher.start(30 * 60 * 1000);
|
|
56
|
+
process.on("SIGINT", () => {
|
|
57
|
+
logger.info("Daemon shutting down...");
|
|
58
|
+
tokenRefresher.stop();
|
|
59
|
+
process.exit(0);
|
|
60
|
+
});
|
|
61
|
+
process.on("SIGTERM", () => {
|
|
62
|
+
logger.info("Daemon shutting down...");
|
|
63
|
+
tokenRefresher.stop();
|
|
64
|
+
process.exit(0);
|
|
65
|
+
});
|
|
66
|
+
// Keep process alive
|
|
67
|
+
setInterval(() => { }, 1 << 30);
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiRequest } from "../lib/api.js";
|
|
3
|
+
export const inviteCommand = new Command("invite")
|
|
4
|
+
.description("Invite a team member to your workspace")
|
|
5
|
+
.argument("<email>", "Email of the person to invite")
|
|
6
|
+
.option("--role <role>", "Role: member or admin", "member")
|
|
7
|
+
.option("--org <orgId>", "Organization ID (uses default workspace if omitted)")
|
|
8
|
+
.action(async (email, options, cmd) => {
|
|
9
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
10
|
+
try {
|
|
11
|
+
let orgId = options.org;
|
|
12
|
+
// If no org specified, get user's default (first) org
|
|
13
|
+
if (!orgId) {
|
|
14
|
+
const { ok, data } = await apiRequest("/api/organizations", {
|
|
15
|
+
token: globalOpts.token,
|
|
16
|
+
});
|
|
17
|
+
if (!ok || !data) {
|
|
18
|
+
console.error(JSON.stringify({ error: "Failed to fetch organizations" }));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const orgs = data.data || [];
|
|
22
|
+
if (orgs.length === 0) {
|
|
23
|
+
console.error(JSON.stringify({ error: "No workspace found" }));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
orgId = orgs[0].id;
|
|
27
|
+
}
|
|
28
|
+
const { ok, data } = await apiRequest("/api/organizations/invite", {
|
|
29
|
+
method: "POST",
|
|
30
|
+
body: {
|
|
31
|
+
email,
|
|
32
|
+
organization_id: orgId,
|
|
33
|
+
role: options.role,
|
|
34
|
+
},
|
|
35
|
+
token: globalOpts.token,
|
|
36
|
+
});
|
|
37
|
+
if (!ok) {
|
|
38
|
+
console.error(JSON.stringify({ error: "Invite failed", details: data }));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
console.log(JSON.stringify(data, null, 2));
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
+
console.error(JSON.stringify({ error: message }));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=invite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invite.js","sourceRoot":"","sources":["../../src/commands/invite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,wCAAwC,CAAC;KACrD,QAAQ,CAAC,SAAS,EAAE,+BAA+B,CAAC;KACpD,MAAM,CAAC,eAAe,EAAE,uBAAuB,EAAE,QAAQ,CAAC;KAC1D,MAAM,CAAC,eAAe,EAAE,qDAAqD,CAAC;KAC9E,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;IAC5C,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;IAEzC,IAAI,CAAC;QACH,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;QAExB,sDAAsD;QACtD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,oBAAoB,EAAE;gBAC1D,KAAK,EAAE,UAAU,CAAC,KAAK;aACxB,CAAC,CAAC;YACH,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC,CAAC;gBAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,IAAI,GAAI,IAAmC,CAAC,IAAI,IAAI,EAAE,CAAC;YAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,2BAA2B,EAAE;YACjE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE;gBACJ,KAAK;gBACL,eAAe,EAAE,KAAK;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB;YACD,KAAK,EAAE,UAAU,CAAC,KAAK;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { saveTokens, getAccessToken } from "../lib/keychain.js";
|
|
3
|
+
import { validateToken } from "../lib/auth.js";
|
|
4
|
+
import { getConfig } from "../lib/config.js";
|
|
5
|
+
export const loginCommand = new Command("login")
|
|
6
|
+
.description("Log in to Climemo via browser OAuth")
|
|
7
|
+
.option("--provider <provider>", "OAuth provider: github | google", "github")
|
|
8
|
+
.action(async (options) => {
|
|
9
|
+
const config = getConfig();
|
|
10
|
+
const apiBase = config.apiUrl;
|
|
11
|
+
// Check if already logged in
|
|
12
|
+
const existingToken = await getAccessToken();
|
|
13
|
+
if (existingToken) {
|
|
14
|
+
const { valid, email } = await validateToken(existingToken);
|
|
15
|
+
if (valid) {
|
|
16
|
+
console.log(`Already logged in as ${email}`);
|
|
17
|
+
console.log('Run "climemo logout" first to switch accounts.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
console.log("Opening browser for login...");
|
|
22
|
+
try {
|
|
23
|
+
// Start local callback server and open browser
|
|
24
|
+
const { code, port, close } = await startCallbackServerAndOpenBrowser(apiBase, options.provider);
|
|
25
|
+
// Exchange code for tokens via API
|
|
26
|
+
console.log("Authenticating...");
|
|
27
|
+
const res = await fetch(`${apiBase}/api/cli/token/exchange`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify({ code, redirect_port: port }),
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
const err = await res.text();
|
|
34
|
+
console.error("Login failed:", err);
|
|
35
|
+
close();
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const { access_token, refresh_token, user } = await res.json();
|
|
39
|
+
// Save to OS Keychain
|
|
40
|
+
await saveTokens(access_token, refresh_token);
|
|
41
|
+
close();
|
|
42
|
+
console.log("");
|
|
43
|
+
console.log(`Logged in as ${user.email}`);
|
|
44
|
+
console.log(" Token stored in OS Keychain (no plaintext files)");
|
|
45
|
+
console.log("");
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
49
|
+
console.error("Login failed:", message);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
async function startCallbackServerAndOpenBrowser(apiBase, provider) {
|
|
54
|
+
const { createServer } = await import("http");
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const server = createServer(async (req, res) => {
|
|
57
|
+
const url = new URL(req.url || "/", `http://localhost`);
|
|
58
|
+
const code = url.searchParams.get("code");
|
|
59
|
+
const error = url.searchParams.get("error");
|
|
60
|
+
if (error) {
|
|
61
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
62
|
+
res.end("<html><body><h1>Login failed</h1><p>Please try again.</p></body></html>");
|
|
63
|
+
reject(new Error(`Auth failed: ${error}`));
|
|
64
|
+
server.close();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (code) {
|
|
68
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
69
|
+
res.end(`<html><body style="font-family:sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0">
|
|
70
|
+
<div style="text-align:center"><h1 style="color:#18181b">Login successful!</h1><p style="color:#71717a">You can close this window.</p></div></body></html>`);
|
|
71
|
+
resolve({
|
|
72
|
+
code,
|
|
73
|
+
port: server.address().port,
|
|
74
|
+
close: () => server.close(),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
server.listen(0, "127.0.0.1", async () => {
|
|
79
|
+
const addr = server.address();
|
|
80
|
+
const callbackUrl = `http://127.0.0.1:${addr.port}`;
|
|
81
|
+
const loginUrl = `${apiBase}/api/cli/auth?provider=${provider}&redirect_uri=${encodeURIComponent(callbackUrl)}`;
|
|
82
|
+
// Open browser
|
|
83
|
+
const open = (await import("open")).default;
|
|
84
|
+
await open(loginUrl);
|
|
85
|
+
console.log(`Waiting for browser login... (port ${addr.port})`);
|
|
86
|
+
});
|
|
87
|
+
// Timeout after 5 minutes
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
server.close();
|
|
90
|
+
reject(new Error("Login timed out after 5 minutes."));
|
|
91
|
+
}, 5 * 60 * 1000);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { clearTokens } from "../lib/keychain.js";
|
|
3
|
+
export const logoutCommand = new Command("logout")
|
|
4
|
+
.description("Log out and remove stored tokens")
|
|
5
|
+
.action(async () => {
|
|
6
|
+
await clearTokens();
|
|
7
|
+
console.log("Logged out. Tokens removed from OS Keychain.");
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,WAAW,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiRequest } from "../lib/api.js";
|
|
3
|
+
function getOrgOption(cmd) {
|
|
4
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
5
|
+
return { token: globalOpts.token, org: globalOpts.org };
|
|
6
|
+
}
|
|
7
|
+
async function resolveOrgId(token, orgOption) {
|
|
8
|
+
if (orgOption)
|
|
9
|
+
return orgOption;
|
|
10
|
+
const { ok, data } = await apiRequest("/api/organizations", { token });
|
|
11
|
+
if (!ok || !data) {
|
|
12
|
+
console.error(JSON.stringify({ error: "Failed to fetch organizations" }));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const orgs = data.data || [];
|
|
16
|
+
if (orgs.length === 0) {
|
|
17
|
+
console.error(JSON.stringify({ error: "No workspace found" }));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
return orgs[0].id;
|
|
21
|
+
}
|
|
22
|
+
const listSubcommand = new Command("list")
|
|
23
|
+
.description("List workspace members")
|
|
24
|
+
.option("--org <orgId>", "Organization ID (uses default workspace if omitted)")
|
|
25
|
+
.action(async (_options, cmd) => {
|
|
26
|
+
const { token, org } = getOrgOption(cmd);
|
|
27
|
+
try {
|
|
28
|
+
const orgId = await resolveOrgId(token, org);
|
|
29
|
+
const { ok, data } = await apiRequest(`/api/organizations/members?organization_id=${orgId}`, { token });
|
|
30
|
+
if (!ok) {
|
|
31
|
+
console.error(JSON.stringify({ error: "Failed to list members", details: data }));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
console.log(JSON.stringify(data, null, 2));
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
38
|
+
console.error(JSON.stringify({ error: message }));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const removeSubcommand = new Command("remove")
|
|
43
|
+
.description("Remove a member from the workspace")
|
|
44
|
+
.argument("<email>", "Email of the member to remove")
|
|
45
|
+
.option("--org <orgId>", "Organization ID (uses default workspace if omitted)")
|
|
46
|
+
.action(async (email, _options, cmd) => {
|
|
47
|
+
const { token, org } = getOrgOption(cmd);
|
|
48
|
+
try {
|
|
49
|
+
const orgId = await resolveOrgId(token, org);
|
|
50
|
+
const { ok, data } = await apiRequest("/api/organizations/members", {
|
|
51
|
+
method: "DELETE",
|
|
52
|
+
body: { email, organization_id: orgId },
|
|
53
|
+
token,
|
|
54
|
+
});
|
|
55
|
+
if (!ok) {
|
|
56
|
+
console.error(JSON.stringify({ error: "Failed to remove member", details: data }));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.log(JSON.stringify(data, null, 2));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
console.error(JSON.stringify({ error: message }));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const roleSubcommand = new Command("role")
|
|
68
|
+
.description("Change a member's role")
|
|
69
|
+
.argument("<email>", "Email of the member")
|
|
70
|
+
.argument("<role>", "New role: member, admin, or owner")
|
|
71
|
+
.option("--org <orgId>", "Organization ID (uses default workspace if omitted)")
|
|
72
|
+
.action(async (email, role, _options, cmd) => {
|
|
73
|
+
const { token, org } = getOrgOption(cmd);
|
|
74
|
+
try {
|
|
75
|
+
const orgId = await resolveOrgId(token, org);
|
|
76
|
+
const { ok, data } = await apiRequest("/api/organizations/members", {
|
|
77
|
+
method: "PATCH",
|
|
78
|
+
body: { email, organization_id: orgId, role },
|
|
79
|
+
token,
|
|
80
|
+
});
|
|
81
|
+
if (!ok) {
|
|
82
|
+
console.error(JSON.stringify({ error: "Failed to update role", details: data }));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
console.log(JSON.stringify(data, null, 2));
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
console.error(JSON.stringify({ error: message }));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
export const membersCommand = new Command("members")
|
|
94
|
+
.description("Manage workspace members")
|
|
95
|
+
.addCommand(listSubcommand)
|
|
96
|
+
.addCommand(removeSubcommand)
|
|
97
|
+
.addCommand(roleSubcommand);
|
|
98
|
+
//# sourceMappingURL=members.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../src/commands/members.ts"],"names":[],"mappings":"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"}
|