@digitalpresence/cliclaw-auth 0.1.1 → 0.3.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/agent-store.d.ts +6 -9
- package/dist/agent-store.d.ts.map +1 -1
- package/dist/agent-store.js +26 -38
- package/dist/agent-store.js.map +1 -1
- package/dist/claude-md-generator.d.ts +15 -1
- package/dist/claude-md-generator.d.ts.map +1 -1
- package/dist/claude-md-generator.js +61 -22
- package/dist/claude-md-generator.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/instance-store.d.ts +25 -0
- package/dist/instance-store.d.ts.map +1 -0
- package/dist/instance-store.js +106 -0
- package/dist/instance-store.js.map +1 -0
- package/dist/integration-registry.d.ts +11 -0
- package/dist/integration-registry.d.ts.map +1 -1
- package/dist/integration-registry.js +32 -0
- package/dist/integration-registry.js.map +1 -1
- package/dist/scoped-client-manager.d.ts +2 -3
- package/dist/scoped-client-manager.d.ts.map +1 -1
- package/dist/scoped-client-manager.js +9 -9
- package/dist/scoped-client-manager.js.map +1 -1
- package/dist/token-store.d.ts +4 -3
- package/dist/token-store.d.ts.map +1 -1
- package/dist/token-store.js.map +1 -1
- package/package.json +7 -3
- package/src/agent-store.ts +26 -48
- package/src/claude-md-generator.ts +74 -26
- package/src/config.ts +4 -0
- package/src/index.ts +5 -3
- package/src/instance-store.ts +136 -0
- package/src/integration-registry.ts +44 -0
- package/src/scoped-client-manager.ts +7 -10
- package/src/token-store.ts +5 -4
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
writeFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
statSync,
|
|
9
|
+
} from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { AgentStore } from "./agent-store.js";
|
|
12
|
+
import { generateUniversalClaudeMd, generateContextMd } from "./claude-md-generator.js";
|
|
13
|
+
import { MemoryStore } from "./memory-store.js";
|
|
14
|
+
|
|
15
|
+
export class InstanceStore {
|
|
16
|
+
constructor(
|
|
17
|
+
private instancesDir: string,
|
|
18
|
+
private agentStore: AgentStore,
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
private ensureDir(path: string): void {
|
|
22
|
+
if (!existsSync(path)) {
|
|
23
|
+
mkdirSync(path, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private readFileOrEmpty(path: string): string {
|
|
28
|
+
try {
|
|
29
|
+
return readFileSync(path, "utf-8");
|
|
30
|
+
} catch {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Get the root path for an instance */
|
|
36
|
+
getInstancePath(agentName: string, userId: string): string {
|
|
37
|
+
return join(this.instancesDir, agentName, userId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Get the workspace subdir (what gets mounted into Docker) */
|
|
41
|
+
getWorkspacePath(agentName: string, userId: string): string {
|
|
42
|
+
return join(this.getInstancePath(agentName, userId), "workspace");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Create a new instance by copying template files and generating CONTEXT.md */
|
|
46
|
+
createInstance(agentName: string, userId: string): string {
|
|
47
|
+
const config = this.agentStore.get(agentName);
|
|
48
|
+
if (!config) throw new Error(`Agent "${agentName}" not found`);
|
|
49
|
+
|
|
50
|
+
const instancePath = this.getInstancePath(agentName, userId);
|
|
51
|
+
this.ensureDir(instancePath);
|
|
52
|
+
this.ensureDir(join(instancePath, "workspace"));
|
|
53
|
+
this.ensureDir(join(instancePath, "memory"));
|
|
54
|
+
this.ensureDir(join(instancePath, "cron"));
|
|
55
|
+
|
|
56
|
+
// Read agent template files
|
|
57
|
+
const agentDir = this.agentStore.workspacePath(agentName);
|
|
58
|
+
const soul = this.readFileOrEmpty(join(agentDir, "SOUL.md"));
|
|
59
|
+
const role = this.readFileOrEmpty(join(agentDir, "ROLE.md"));
|
|
60
|
+
|
|
61
|
+
// Generate CONTEXT.md content
|
|
62
|
+
const memoryStore = new MemoryStore(join(instancePath, "memory"));
|
|
63
|
+
const memories = memoryStore.list();
|
|
64
|
+
const context = generateContextMd(config, memories);
|
|
65
|
+
|
|
66
|
+
// Generate unified CLAUDE.md with soul/role/context inlined
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(instancePath, "CLAUDE.md"),
|
|
69
|
+
generateUniversalClaudeMd({ soul, role, context }),
|
|
70
|
+
"utf-8",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return instancePath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Re-copy template files and regenerate CONTEXT.md after agent config changes */
|
|
77
|
+
syncInstance(agentName: string, userId: string): void {
|
|
78
|
+
const config = this.agentStore.get(agentName);
|
|
79
|
+
if (!config) throw new Error(`Agent "${agentName}" not found`);
|
|
80
|
+
|
|
81
|
+
const instancePath = this.getInstancePath(agentName, userId);
|
|
82
|
+
if (!existsSync(instancePath)) {
|
|
83
|
+
this.createInstance(agentName, userId);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Read agent template files
|
|
88
|
+
const agentDir = this.agentStore.workspacePath(agentName);
|
|
89
|
+
const soul = this.readFileOrEmpty(join(agentDir, "SOUL.md"));
|
|
90
|
+
const role = this.readFileOrEmpty(join(agentDir, "ROLE.md"));
|
|
91
|
+
|
|
92
|
+
// Regenerate unified CLAUDE.md with soul/role/context inlined
|
|
93
|
+
const memoryStore = new MemoryStore(join(instancePath, "memory"));
|
|
94
|
+
const memories = memoryStore.list();
|
|
95
|
+
const context = generateContextMd(config, memories);
|
|
96
|
+
writeFileSync(
|
|
97
|
+
join(instancePath, "CLAUDE.md"),
|
|
98
|
+
generateUniversalClaudeMd({ soul, role, context }),
|
|
99
|
+
"utf-8",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Delete an instance */
|
|
104
|
+
deleteInstance(agentName: string, userId: string): boolean {
|
|
105
|
+
const instancePath = this.getInstancePath(agentName, userId);
|
|
106
|
+
if (!existsSync(instancePath)) return false;
|
|
107
|
+
rmSync(instancePath, { recursive: true });
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** List all instances, optionally filtered by agent name */
|
|
112
|
+
listInstances(agentName?: string): Array<{ agentName: string; userId: string; path: string }> {
|
|
113
|
+
const results: Array<{ agentName: string; userId: string; path: string }> = [];
|
|
114
|
+
|
|
115
|
+
if (!existsSync(this.instancesDir)) return results;
|
|
116
|
+
|
|
117
|
+
const agentDirs = agentName ? [agentName] : readdirSync(this.instancesDir).filter((entry) => {
|
|
118
|
+
const entryPath = join(this.instancesDir, entry);
|
|
119
|
+
return statSync(entryPath).isDirectory();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
for (const agent of agentDirs) {
|
|
123
|
+
const agentPath = join(this.instancesDir, agent);
|
|
124
|
+
if (!existsSync(agentPath)) continue;
|
|
125
|
+
|
|
126
|
+
for (const userId of readdirSync(agentPath)) {
|
|
127
|
+
const userPath = join(agentPath, userId);
|
|
128
|
+
if (statSync(userPath).isDirectory()) {
|
|
129
|
+
results.push({ agentName: agent, userId, path: userPath });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -1,23 +1,58 @@
|
|
|
1
1
|
export interface IntegrationDef {
|
|
2
2
|
id: string;
|
|
3
3
|
displayName: string;
|
|
4
|
+
provider: string;
|
|
4
5
|
scopes: string[];
|
|
5
6
|
}
|
|
6
7
|
|
|
8
|
+
export interface ProviderConfig {
|
|
9
|
+
authUrl: string;
|
|
10
|
+
tokenUrl: string;
|
|
11
|
+
userInfoUrl: string;
|
|
12
|
+
clientIdEnv: string;
|
|
13
|
+
clientSecretEnv: string;
|
|
14
|
+
extraScopes: string[];
|
|
15
|
+
extraAuthParams: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const PROVIDERS: Record<string, ProviderConfig> = {
|
|
19
|
+
google: {
|
|
20
|
+
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
21
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
22
|
+
userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
23
|
+
clientIdEnv: "GOOGLE_CLIENT_ID",
|
|
24
|
+
clientSecretEnv: "GOOGLE_CLIENT_SECRET",
|
|
25
|
+
extraScopes: ["openid", "email", "profile"],
|
|
26
|
+
extraAuthParams: { access_type: "offline", prompt: "consent" },
|
|
27
|
+
},
|
|
28
|
+
github: {
|
|
29
|
+
authUrl: "https://github.com/login/oauth/authorize",
|
|
30
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
31
|
+
userInfoUrl: "https://api.github.com/user",
|
|
32
|
+
clientIdEnv: "GITHUB_CLIENT_ID",
|
|
33
|
+
clientSecretEnv: "GITHUB_CLIENT_SECRET",
|
|
34
|
+
extraScopes: [],
|
|
35
|
+
extraAuthParams: {},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
7
39
|
export const INTEGRATIONS: Record<string, IntegrationDef> = {
|
|
8
40
|
gmail: {
|
|
9
41
|
id: "gmail",
|
|
10
42
|
displayName: "Gmail",
|
|
43
|
+
provider: "google",
|
|
11
44
|
scopes: ["https://www.googleapis.com/auth/gmail.modify"],
|
|
12
45
|
},
|
|
13
46
|
gdrive: {
|
|
14
47
|
id: "gdrive",
|
|
15
48
|
displayName: "Google Drive",
|
|
49
|
+
provider: "google",
|
|
16
50
|
scopes: ["https://www.googleapis.com/auth/drive"],
|
|
17
51
|
},
|
|
18
52
|
gsheets: {
|
|
19
53
|
id: "gsheets",
|
|
20
54
|
displayName: "Google Sheets",
|
|
55
|
+
provider: "google",
|
|
21
56
|
scopes: [
|
|
22
57
|
"https://www.googleapis.com/auth/spreadsheets",
|
|
23
58
|
"https://www.googleapis.com/auth/drive",
|
|
@@ -26,6 +61,7 @@ export const INTEGRATIONS: Record<string, IntegrationDef> = {
|
|
|
26
61
|
gslides: {
|
|
27
62
|
id: "gslides",
|
|
28
63
|
displayName: "Google Slides",
|
|
64
|
+
provider: "google",
|
|
29
65
|
scopes: [
|
|
30
66
|
"https://www.googleapis.com/auth/presentations",
|
|
31
67
|
"https://www.googleapis.com/auth/drive.file",
|
|
@@ -34,15 +70,23 @@ export const INTEGRATIONS: Record<string, IntegrationDef> = {
|
|
|
34
70
|
calendar: {
|
|
35
71
|
id: "calendar",
|
|
36
72
|
displayName: "Google Calendar",
|
|
73
|
+
provider: "google",
|
|
37
74
|
scopes: ["https://www.googleapis.com/auth/calendar"],
|
|
38
75
|
},
|
|
39
76
|
forms: {
|
|
40
77
|
id: "forms",
|
|
41
78
|
displayName: "Google Forms",
|
|
79
|
+
provider: "google",
|
|
42
80
|
scopes: [
|
|
43
81
|
"https://www.googleapis.com/auth/forms.body",
|
|
44
82
|
"https://www.googleapis.com/auth/forms.responses.readonly",
|
|
45
83
|
"https://www.googleapis.com/auth/drive.metadata.readonly",
|
|
46
84
|
],
|
|
47
85
|
},
|
|
86
|
+
github: {
|
|
87
|
+
id: "github",
|
|
88
|
+
displayName: "GitHub",
|
|
89
|
+
provider: "github",
|
|
90
|
+
scopes: ["repo", "read:user", "user:email"],
|
|
91
|
+
},
|
|
48
92
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { google } from "googleapis";
|
|
2
2
|
import { OAuthClientManager } from "./oauth-client-manager.js";
|
|
3
3
|
import { TokenStore } from "./token-store.js";
|
|
4
|
-
import type { AgentPermission } from "./agent-store.js";
|
|
5
4
|
|
|
6
5
|
type OAuth2Client = InstanceType<typeof google.auth.OAuth2>;
|
|
7
6
|
|
|
@@ -23,18 +22,16 @@ function parseAccountKey(key: string): { integration: string; account: string }
|
|
|
23
22
|
export class ScopedClientManager {
|
|
24
23
|
constructor(
|
|
25
24
|
private inner: OAuthClientManager,
|
|
26
|
-
private
|
|
25
|
+
private integrations: string[],
|
|
27
26
|
) {}
|
|
28
27
|
|
|
29
|
-
private hasPermission(integration: string
|
|
30
|
-
return this.
|
|
31
|
-
(p) => p.integration === integration,
|
|
32
|
-
);
|
|
28
|
+
private hasPermission(integration: string): boolean {
|
|
29
|
+
return this.integrations.includes(integration);
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
getClient(accountKey: string): OAuth2Client {
|
|
36
33
|
const { integration, account } = parseAccountKey(accountKey);
|
|
37
|
-
if (!this.hasPermission(integration
|
|
34
|
+
if (!this.hasPermission(integration)) {
|
|
38
35
|
throw new PermissionDeniedError(integration, account);
|
|
39
36
|
}
|
|
40
37
|
return this.inner.getClient(accountKey);
|
|
@@ -42,8 +39,8 @@ export class ScopedClientManager {
|
|
|
42
39
|
|
|
43
40
|
listAccounts(): string[] {
|
|
44
41
|
return this.inner.listAccounts().filter((key) => {
|
|
45
|
-
const { integration
|
|
46
|
-
return this.hasPermission(integration
|
|
42
|
+
const { integration } = parseAccountKey(key);
|
|
43
|
+
return this.hasPermission(integration);
|
|
47
44
|
});
|
|
48
45
|
}
|
|
49
46
|
|
|
@@ -57,7 +54,7 @@ export class ScopedClientManager {
|
|
|
57
54
|
|
|
58
55
|
setCredentials(account: string, tokens: Parameters<OAuth2Client["setCredentials"]>[0]): OAuth2Client {
|
|
59
56
|
const { integration, account: acct } = parseAccountKey(account);
|
|
60
|
-
if (!this.hasPermission(integration
|
|
57
|
+
if (!this.hasPermission(integration)) {
|
|
61
58
|
throw new PermissionDeniedError(integration, acct);
|
|
62
59
|
}
|
|
63
60
|
return this.inner.setCredentials(account, tokens);
|
package/src/token-store.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
2
|
import { dirname } from "path";
|
|
3
|
-
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
type OAuthCredentials = Record<string, any>;
|
|
4
5
|
|
|
5
6
|
interface TokenFile {
|
|
6
|
-
[account: string]:
|
|
7
|
+
[account: string]: OAuthCredentials;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export class TokenStore {
|
|
@@ -28,7 +29,7 @@ export class TokenStore {
|
|
|
28
29
|
writeFileSync(this.tokensPath, JSON.stringify(data, null, 2), "utf-8");
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
get(account: string):
|
|
32
|
+
get(account: string): OAuthCredentials | null {
|
|
32
33
|
const data = this.load();
|
|
33
34
|
if (data[account]) return data[account];
|
|
34
35
|
// Fall back to integration:account key format (portal token injection)
|
|
@@ -41,7 +42,7 @@ export class TokenStore {
|
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
set(account: string, tokens:
|
|
45
|
+
set(account: string, tokens: OAuthCredentials): void {
|
|
45
46
|
const data = this.load();
|
|
46
47
|
data[account] = tokens;
|
|
47
48
|
this.save(data);
|