@kmlckj/licos-platform-sdk 0.6.4 → 0.6.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/README.md +9 -8
- package/package.json +1 -1
- package/src/database.js +9 -9
- package/src/runtime.js +81 -7
- package/src/storage.js +3 -3
package/README.md
CHANGED
|
@@ -5,14 +5,15 @@ LICOS platform SDK for server-side project runtime code.
|
|
|
5
5
|
## Runtime Configuration
|
|
6
6
|
|
|
7
7
|
Server-side helpers call platform Studio runtime APIs. Project code does not
|
|
8
|
-
pass API base URLs, project IDs, tokens, or environment scopes. The SDK loads
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- `
|
|
13
|
-
- `LICOS_PROJECT_ID` or `AGENT_PROJECT_ID`
|
|
14
|
-
- `LICOS_WORKSPACE_ID` or `AGENT_WORKSPACE_ID`
|
|
15
|
-
- `
|
|
8
|
+
pass API base URLs, project IDs, tokens, or environment scopes. The SDK loads
|
|
9
|
+
identity from runtime environment variables injected by LICOS. Authentication
|
|
10
|
+
is resolved automatically by the runtime:
|
|
11
|
+
|
|
12
|
+
- `LICOS_PLATFORM_API_BASE_URL`
|
|
13
|
+
- `LICOS_PROJECT_ID` or `AGENT_PROJECT_ID`
|
|
14
|
+
- `LICOS_WORKSPACE_ID` or `AGENT_WORKSPACE_ID`
|
|
15
|
+
- `LICOS_USER_ID` or `AGENT_USER_ID`
|
|
16
|
+
- `LICOS_PROJECT_ENV`, mapped internally to `dev` or `prod`
|
|
16
17
|
|
|
17
18
|
## Database
|
|
18
19
|
|
package/package.json
CHANGED
package/src/database.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ApiError } from './shared.js';
|
|
2
2
|
import { authHeaders, runtimeConfig } from './runtime.js';
|
|
3
3
|
|
|
4
|
-
function databaseConfig() {
|
|
5
|
-
return runtimeConfig();
|
|
6
|
-
}
|
|
4
|
+
async function databaseConfig() {
|
|
5
|
+
return runtimeConfig();
|
|
6
|
+
}
|
|
7
7
|
|
|
8
8
|
function databaseUrl(config, route) {
|
|
9
9
|
return `${config.baseUrl}/api/v1/studio/runtime/database/projects/${encodeURIComponent(config.projectId)}${route}`;
|
|
@@ -45,8 +45,8 @@ async function decodeResponse(response) {
|
|
|
45
45
|
return payload.data;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
async function requestJson(route, body) {
|
|
49
|
-
const config = databaseConfig();
|
|
48
|
+
async function requestJson(route, body) {
|
|
49
|
+
const config = await databaseConfig();
|
|
50
50
|
const payload = { environment: config.environment, ...compactObject(body) };
|
|
51
51
|
const response = await fetch(databaseUrl(config, route), {
|
|
52
52
|
method: 'POST',
|
|
@@ -56,8 +56,8 @@ async function requestJson(route, body) {
|
|
|
56
56
|
return decodeResponse(response);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async function requestControlJson(route, query = {}) {
|
|
60
|
-
const config = databaseConfig();
|
|
59
|
+
async function requestControlJson(route, query = {}) {
|
|
60
|
+
const config = await databaseConfig();
|
|
61
61
|
const url = new URL(controlDatabaseUrl(config, route));
|
|
62
62
|
for (const [key, value] of Object.entries({ environment: config.environment, ...query })) {
|
|
63
63
|
if (value !== undefined && value !== null) url.searchParams.set(key, String(value));
|
|
@@ -145,8 +145,8 @@ export async function upsert(table, options = {}) {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
export async function transaction(operations) {
|
|
149
|
-
const config = databaseConfig();
|
|
148
|
+
export async function transaction(operations) {
|
|
149
|
+
const config = await databaseConfig();
|
|
150
150
|
const normalized = operations.map((operation) => ({
|
|
151
151
|
type: operation.type,
|
|
152
152
|
request: {
|
package/src/runtime.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ConfigurationError } from './shared.js';
|
|
2
2
|
|
|
3
|
+
const AI_USER_TOKEN_PATH = '/api/v1/internal/auth/ai-user-token';
|
|
4
|
+
const tokenCache = new Map();
|
|
5
|
+
|
|
3
6
|
export function env(name) {
|
|
4
7
|
const value = process.env[name];
|
|
5
8
|
if (typeof value !== 'string') return undefined;
|
|
@@ -40,22 +43,93 @@ export function projectEnvironment(overrideEnvName) {
|
|
|
40
43
|
return ['prod', 'production', 'release'].includes(projectEnv) ? 'prod' : 'dev';
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
function decodeAiTokenPayload(payload) {
|
|
47
|
+
if (!payload || typeof payload !== 'object') {
|
|
48
|
+
throw new ConfigurationError('AI user token exchange response is not an object');
|
|
49
|
+
}
|
|
50
|
+
if ((payload.code !== undefined && payload.code !== 0) || payload.success === false) {
|
|
51
|
+
const error = new ConfigurationError(payload.message || 'AI user token exchange failed');
|
|
52
|
+
error.code = typeof payload.code === 'number' ? payload.code : undefined;
|
|
53
|
+
error.details = payload;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
const data = payload.data;
|
|
57
|
+
const token =
|
|
58
|
+
typeof data === 'string'
|
|
59
|
+
? data.trim()
|
|
60
|
+
: String(data?.accessToken || data?.access_token || data?.token || '').trim();
|
|
61
|
+
if (!token) {
|
|
62
|
+
const error = new ConfigurationError('AI user token exchange response missing accessToken');
|
|
63
|
+
error.details = payload;
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
return token;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function resolveUserToken(baseUrl, userId) {
|
|
70
|
+
const ownerUserId = String(userId || '').trim();
|
|
71
|
+
if (!ownerUserId) {
|
|
72
|
+
throw new ConfigurationError('project owner user ID is not configured');
|
|
73
|
+
}
|
|
74
|
+
const aiAgentToken = env('LICOS_AI_AGENT_TOKEN');
|
|
75
|
+
if (!aiAgentToken) {
|
|
76
|
+
throw new ConfigurationError('platform runtime identity is unavailable');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const cacheKey = `${baseUrl}\n${ownerUserId}\n${aiAgentToken}`;
|
|
80
|
+
if (tokenCache.has(cacheKey)) return tokenCache.get(cacheKey);
|
|
81
|
+
|
|
82
|
+
const promise = fetch(`${baseUrl}${AI_USER_TOKEN_PATH}`, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: {
|
|
85
|
+
Authorization: `Bearer ${aiAgentToken}`,
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({ userId: ownerUserId }),
|
|
89
|
+
}).then(async (response) => {
|
|
90
|
+
const text = await response.text();
|
|
91
|
+
let payload = null;
|
|
92
|
+
try {
|
|
93
|
+
payload = text ? JSON.parse(text) : null;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const wrapped = new ConfigurationError('parse AI user token exchange response failed');
|
|
96
|
+
wrapped.status = response.status;
|
|
97
|
+
wrapped.details = text;
|
|
98
|
+
throw wrapped;
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const message = payload?.message || text || `AI user token exchange returned ${response.status}`;
|
|
102
|
+
const wrapped = new ConfigurationError(message);
|
|
103
|
+
wrapped.status = response.status;
|
|
104
|
+
wrapped.details = payload;
|
|
105
|
+
throw wrapped;
|
|
106
|
+
}
|
|
107
|
+
return decodeAiTokenPayload(payload);
|
|
108
|
+
});
|
|
109
|
+
tokenCache.set(cacheKey, promise);
|
|
110
|
+
try {
|
|
111
|
+
return await promise;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
tokenCache.delete(cacheKey);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function runtimeConfig({ environmentOverrideEnv } = {}) {
|
|
44
119
|
const projectId = env('LICOS_PROJECT_ID') || env('AGENT_PROJECT_ID');
|
|
45
120
|
if (!projectId) {
|
|
46
121
|
throw new ConfigurationError('LICOS_PROJECT_ID or AGENT_PROJECT_ID is not configured');
|
|
47
122
|
}
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
123
|
+
const baseUrl = platformBaseUrl();
|
|
124
|
+
const userId = env('LICOS_USER_ID') || env('AGENT_USER_ID');
|
|
125
|
+
const token = await resolveUserToken(baseUrl, userId);
|
|
52
126
|
return {
|
|
53
|
-
baseUrl
|
|
127
|
+
baseUrl,
|
|
54
128
|
projectId,
|
|
55
129
|
environment: projectEnvironment(environmentOverrideEnv),
|
|
56
130
|
token,
|
|
57
131
|
workspaceId: env('LICOS_WORKSPACE_ID') || env('AGENT_WORKSPACE_ID'),
|
|
58
|
-
userId
|
|
132
|
+
userId,
|
|
59
133
|
};
|
|
60
134
|
}
|
|
61
135
|
|
package/src/storage.js
CHANGED
|
@@ -6,7 +6,7 @@ import { authHeaders, runtimeConfig } from './runtime.js';
|
|
|
6
6
|
|
|
7
7
|
export const MAX_UPLOAD_FILE_BYTES = 20 * 1024 * 1024;
|
|
8
8
|
|
|
9
|
-
function storageConfig() {
|
|
9
|
+
async function storageConfig() {
|
|
10
10
|
return runtimeConfig({ environmentOverrideEnv: 'LICOS_STORAGE_SCOPE' });
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -45,7 +45,7 @@ async function decodeResponse(response) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async function requestJson(method, route, { params, body } = {}) {
|
|
48
|
-
const config = storageConfig();
|
|
48
|
+
const config = await storageConfig();
|
|
49
49
|
const response = await fetch(storageUrl(config, route, params), {
|
|
50
50
|
method,
|
|
51
51
|
headers: authHeaders(config),
|
|
@@ -113,8 +113,8 @@ export async function deleteFolder(folderId) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export async function uploadFile(input, options = {}) {
|
|
116
|
-
const config = storageConfig();
|
|
117
116
|
const { blob, fileName } = await filePart(input, options);
|
|
117
|
+
const config = await storageConfig();
|
|
118
118
|
const form = new FormData();
|
|
119
119
|
form.set('projectId', config.projectId);
|
|
120
120
|
form.set('scope', config.environment);
|