@treeseed/sdk 0.4.7 → 0.4.9
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 +1 -1
- package/dist/dispatch.d.ts +4 -0
- package/dist/dispatch.js +180 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +25 -3
- package/dist/operations/providers/default.js +1 -0
- package/dist/operations/services/config-runtime.d.ts +121 -26
- package/dist/operations/services/config-runtime.js +332 -198
- package/dist/operations/services/deploy.d.ts +0 -3
- package/dist/operations/services/deploy.js +1 -1
- package/dist/operations/services/export-runtime.d.ts +18 -0
- package/dist/operations/services/export-runtime.js +136 -0
- package/dist/operations/services/railway-deploy.js +2 -2
- package/dist/operations/services/runtime-tools.d.ts +0 -1
- package/dist/operations/services/runtime-tools.js +1 -2
- package/dist/operations-registry.js +1 -0
- package/dist/operations-types.d.ts +1 -1
- package/dist/platform/book-export.d.ts +78 -0
- package/dist/platform/book-export.js +449 -0
- package/dist/platform/contracts.d.ts +6 -2
- package/dist/platform/deploy-config.d.ts +2 -0
- package/dist/platform/deploy-config.js +30 -2
- package/dist/platform/env.yaml +5 -0
- package/dist/platform/environment.d.ts +10 -1
- package/dist/platform/environment.js +82 -6
- package/dist/remote.d.ts +65 -9
- package/dist/remote.js +104 -28
- package/dist/scripts/aggregate-book.js +13 -118
- package/dist/scripts/config-treeseed.js +18 -27
- package/dist/sdk-dispatch.d.ts +12 -0
- package/dist/sdk-dispatch.js +142 -0
- package/dist/sdk-types.d.ts +137 -4
- package/dist/sdk-types.js +16 -0
- package/dist/sdk.d.ts +7 -1
- package/dist/sdk.js +69 -0
- package/dist/workflow/operations.d.ts +59 -15
- package/dist/workflow/operations.js +61 -81
- package/dist/workflow-state.js +2 -1
- package/dist/workflow-support.d.ts +2 -1
- package/dist/workflow-support.js +14 -6
- package/dist/workflow.d.ts +11 -1
- package/dist/workflow.js +6 -0
- package/package.json +6 -1
|
@@ -21,6 +21,7 @@ const TREESEED_ENVIRONMENT_TARGETS = [
|
|
|
21
21
|
];
|
|
22
22
|
const TREESEED_ENVIRONMENT_PURPOSES = ["dev", "save", "deploy", "destroy", "config"];
|
|
23
23
|
const TREESEED_ENVIRONMENT_SENSITIVITY = ["secret", "plain", "derived"];
|
|
24
|
+
const TREESEED_ENVIRONMENT_STORAGE = ["scoped", "shared"];
|
|
24
25
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
25
26
|
const CORE_ENVIRONMENT_PATH = resolve(moduleDir, "env.yaml");
|
|
26
27
|
const TENANT_ENVIRONMENT_OVERLAY_PATH = "src/env.yaml";
|
|
@@ -45,9 +46,69 @@ function railwayManagedEnabled(context) {
|
|
|
45
46
|
function generatedSecret(bytes = 24) {
|
|
46
47
|
return randomBytes(bytes).toString("hex");
|
|
47
48
|
}
|
|
49
|
+
function normalizeUrl(value) {
|
|
50
|
+
return value.trim().replace(/\/$/u, "");
|
|
51
|
+
}
|
|
52
|
+
function primaryHostFromUrl(value) {
|
|
53
|
+
if (!value || value.trim().length === 0) {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return new URL(value).host;
|
|
58
|
+
} catch {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function parseDomainList(value) {
|
|
63
|
+
return String(value ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
64
|
+
}
|
|
65
|
+
function deriveApiDomainFromProjectDomain(domain) {
|
|
66
|
+
if (!domain) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
if (domain.startsWith("api.")) {
|
|
70
|
+
return domain;
|
|
71
|
+
}
|
|
72
|
+
const segments = domain.split(".").filter(Boolean);
|
|
73
|
+
if (segments.length <= 2) {
|
|
74
|
+
return `api.${domain}`;
|
|
75
|
+
}
|
|
76
|
+
return `api.${segments.slice(1).join(".")}`;
|
|
77
|
+
}
|
|
78
|
+
function resolveConfiguredApiBaseUrl(context, scope, values = {}) {
|
|
79
|
+
const localBaseUrl = context.deployConfig.services?.api?.environments?.local?.baseUrl ?? context.deployConfig.surfaces?.api?.localBaseUrl ?? "http://127.0.0.1:3000";
|
|
80
|
+
if (scope === "local") {
|
|
81
|
+
return normalizeUrl(localBaseUrl);
|
|
82
|
+
}
|
|
83
|
+
const scopedBaseUrl = context.deployConfig.services?.api?.environments?.[scope]?.baseUrl ?? context.deployConfig.services?.api?.publicBaseUrl ?? context.deployConfig.surfaces?.api?.publicBaseUrl;
|
|
84
|
+
if (scopedBaseUrl) {
|
|
85
|
+
return normalizeUrl(scopedBaseUrl);
|
|
86
|
+
}
|
|
87
|
+
const projectDomains = [
|
|
88
|
+
...parseDomainList(values.TREESEED_PROJECT_DOMAINS),
|
|
89
|
+
primaryHostFromUrl(context.deployConfig.siteUrl)
|
|
90
|
+
].filter(Boolean);
|
|
91
|
+
for (const domain of projectDomains) {
|
|
92
|
+
const apiDomain = deriveApiDomainFromProjectDomain(domain);
|
|
93
|
+
if (apiDomain) {
|
|
94
|
+
return `https://${apiDomain}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
function resolveWebServiceId(_values = {}) {
|
|
100
|
+
return "web";
|
|
101
|
+
}
|
|
102
|
+
function resolveApiWebServiceId(values = {}) {
|
|
103
|
+
return values.TREESEED_WEB_SERVICE_ID?.trim() || "web";
|
|
104
|
+
}
|
|
48
105
|
const VALUE_RESOLVERS = {
|
|
49
106
|
generatedSecret: () => generatedSecret(),
|
|
50
|
-
localFormsBypassDefault: () => "true"
|
|
107
|
+
localFormsBypassDefault: () => "true",
|
|
108
|
+
projectDomainsDefault: (context) => primaryHostFromUrl(context.deployConfig.siteUrl),
|
|
109
|
+
apiBaseUrlDefault: (context, scope, values) => resolveConfiguredApiBaseUrl(context, scope, values),
|
|
110
|
+
webServiceIdDefault: (_context, _scope, values) => resolveWebServiceId(values),
|
|
111
|
+
apiWebServiceIdDefault: (_context, _scope, values) => resolveApiWebServiceId(values)
|
|
51
112
|
};
|
|
52
113
|
const PREDICATES = {
|
|
53
114
|
turnstileEnabled: (context) => turnstileEnabled(context),
|
|
@@ -135,6 +196,7 @@ function materializeEntry(id, entry) {
|
|
|
135
196
|
return {
|
|
136
197
|
...entry,
|
|
137
198
|
id,
|
|
199
|
+
storage: entry.storage ?? "scoped",
|
|
138
200
|
defaultValue: resolveNamedValueResolver(entry.defaultValueRef),
|
|
139
201
|
localDefaultValue: resolveNamedValueResolver(entry.localDefaultValueRef),
|
|
140
202
|
isRelevant: resolveNamedPredicate(entry.relevanceRef),
|
|
@@ -233,18 +295,30 @@ function isEntryRequired(entry, context, scope, purpose) {
|
|
|
233
295
|
}
|
|
234
296
|
return false;
|
|
235
297
|
}
|
|
236
|
-
function materializeDefaultValue(entry, context, scope) {
|
|
298
|
+
function materializeDefaultValue(entry, context, scope, values = {}) {
|
|
237
299
|
const source = scope === "local" && entry.localDefaultValue !== void 0 ? entry.localDefaultValue : entry.defaultValue;
|
|
238
300
|
if (source === void 0) {
|
|
239
301
|
return void 0;
|
|
240
302
|
}
|
|
241
|
-
return typeof source === "function" ? source(context, scope) : source;
|
|
303
|
+
return typeof source === "function" ? source(context, scope, values) : source;
|
|
242
304
|
}
|
|
243
305
|
function getTreeseedEnvironmentSuggestedValues(options) {
|
|
244
306
|
const registry = resolveTreeseedEnvironmentRegistry(options);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
307
|
+
const suggestedValues = {};
|
|
308
|
+
const seedValues = { ...options.values ?? {} };
|
|
309
|
+
for (const entry of registry.entries.filter(
|
|
310
|
+
(candidate) => isTreeseedEnvironmentEntryRelevant(candidate, registry.context, options.scope, options.purpose)
|
|
311
|
+
)) {
|
|
312
|
+
const value = materializeDefaultValue(entry, registry.context, options.scope, { ...suggestedValues, ...seedValues });
|
|
313
|
+
if (value === void 0) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
suggestedValues[entry.id] = value;
|
|
317
|
+
}
|
|
318
|
+
return suggestedValues;
|
|
319
|
+
}
|
|
320
|
+
function isTreeseedEnvironmentEntryRequired(entry, context, scope, purpose) {
|
|
321
|
+
return isEntryRequired(entry, context, scope, purpose);
|
|
248
322
|
}
|
|
249
323
|
function valuePresent(value) {
|
|
250
324
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -322,9 +396,11 @@ export {
|
|
|
322
396
|
TREESEED_ENVIRONMENT_REQUIREMENTS,
|
|
323
397
|
TREESEED_ENVIRONMENT_SCOPES,
|
|
324
398
|
TREESEED_ENVIRONMENT_SENSITIVITY,
|
|
399
|
+
TREESEED_ENVIRONMENT_STORAGE,
|
|
325
400
|
TREESEED_ENVIRONMENT_TARGETS,
|
|
326
401
|
getTreeseedEnvironmentSuggestedValues,
|
|
327
402
|
isTreeseedEnvironmentEntryRelevant,
|
|
403
|
+
isTreeseedEnvironmentEntryRequired,
|
|
328
404
|
loadTreeseedEnvironmentOverlay,
|
|
329
405
|
resolveTreeseedEnvironmentContext,
|
|
330
406
|
resolveTreeseedEnvironmentRegistry,
|
package/dist/remote.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RemoteJob, RemoteJobEvent, SdkDispatchRequest, SdkDispatchResult } from './sdk-types.ts';
|
|
1
2
|
export declare const TREESEED_REMOTE_CONTRACT_VERSION = 1;
|
|
2
3
|
export declare const TREESEED_REMOTE_CONTRACT_HEADER = "x-treeseed-remote-contract-version";
|
|
3
4
|
export type ApiScope = string;
|
|
@@ -111,6 +112,25 @@ export interface RemoteWorkflowOperationResponse {
|
|
|
111
112
|
payload?: Record<string, unknown> | null;
|
|
112
113
|
nextSteps?: RemoteWorkflowNextStep[];
|
|
113
114
|
}
|
|
115
|
+
export interface RemoteJobPullRequest {
|
|
116
|
+
limit?: number;
|
|
117
|
+
runnerId?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface RemoteJobPullResponse {
|
|
120
|
+
ok: true;
|
|
121
|
+
payload: RemoteJob[];
|
|
122
|
+
}
|
|
123
|
+
export interface RemoteJobProgressRequest {
|
|
124
|
+
summary?: string;
|
|
125
|
+
data?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
export interface RemoteJobCompletionRequest {
|
|
128
|
+
output?: unknown;
|
|
129
|
+
}
|
|
130
|
+
export interface RemoteJobFailureRequest {
|
|
131
|
+
code?: string;
|
|
132
|
+
message: string;
|
|
133
|
+
}
|
|
114
134
|
export declare class RemoteTreeseedClient {
|
|
115
135
|
readonly config: RemoteTreeseedConfig;
|
|
116
136
|
private readonly fetchImpl;
|
|
@@ -141,15 +161,6 @@ export declare class RemoteTreeseedSdkClient {
|
|
|
141
161
|
constructor(client: RemoteTreeseedClient);
|
|
142
162
|
execute<T = unknown>(operation: string, request: RemoteSdkOperationRequest): Promise<T>;
|
|
143
163
|
}
|
|
144
|
-
export declare class TreeseedGatewayClient {
|
|
145
|
-
private readonly baseUrl;
|
|
146
|
-
private readonly token;
|
|
147
|
-
private readonly fetchImpl;
|
|
148
|
-
constructor(config: import('./sdk-types.ts').SdkGatewayClientConfig);
|
|
149
|
-
requestJson<T>(path: string, options?: RemoteGatewayRequest): Promise<T & {
|
|
150
|
-
error?: string;
|
|
151
|
-
}>;
|
|
152
|
-
}
|
|
153
164
|
export declare class CloudflareQueuePullClient {
|
|
154
165
|
private readonly baseUrl;
|
|
155
166
|
private readonly token;
|
|
@@ -170,8 +181,53 @@ export declare class CloudflareQueuePullClient {
|
|
|
170
181
|
delaySeconds?: number;
|
|
171
182
|
}>): Promise<unknown>;
|
|
172
183
|
}
|
|
184
|
+
export declare class CloudflareQueuePushClient {
|
|
185
|
+
private readonly baseUrl;
|
|
186
|
+
private readonly token;
|
|
187
|
+
private readonly fetchImpl;
|
|
188
|
+
constructor(config: import('./sdk-types.ts').SdkQueuePushClientConfig);
|
|
189
|
+
enqueue(request: import('./sdk-types.ts').SdkQueuePushRequest): Promise<void>;
|
|
190
|
+
}
|
|
173
191
|
export declare class RemoteTreeseedOperationsClient {
|
|
174
192
|
private readonly client;
|
|
175
193
|
constructor(client: RemoteTreeseedClient);
|
|
176
194
|
execute(operation: string, request: RemoteWorkflowOperationRequest): Promise<RemoteWorkflowOperationResponse>;
|
|
177
195
|
}
|
|
196
|
+
export declare class RemoteTreeseedDispatchClient {
|
|
197
|
+
private readonly client;
|
|
198
|
+
constructor(client: RemoteTreeseedClient);
|
|
199
|
+
dispatch(projectId: string, request: SdkDispatchRequest): Promise<SdkDispatchResult>;
|
|
200
|
+
}
|
|
201
|
+
export declare class RemoteTreeseedJobsClient {
|
|
202
|
+
private readonly client;
|
|
203
|
+
constructor(client: RemoteTreeseedClient);
|
|
204
|
+
get(jobId: string): Promise<{
|
|
205
|
+
ok: true;
|
|
206
|
+
payload: RemoteJob;
|
|
207
|
+
}>;
|
|
208
|
+
cancel(jobId: string): Promise<{
|
|
209
|
+
ok: true;
|
|
210
|
+
payload: RemoteJob;
|
|
211
|
+
}>;
|
|
212
|
+
events(jobId: string): Promise<{
|
|
213
|
+
ok: true;
|
|
214
|
+
payload: RemoteJobEvent[];
|
|
215
|
+
}>;
|
|
216
|
+
}
|
|
217
|
+
export declare class RemoteTreeseedRunnerClient {
|
|
218
|
+
private readonly client;
|
|
219
|
+
constructor(client: RemoteTreeseedClient);
|
|
220
|
+
pull(projectId: string, request?: RemoteJobPullRequest): Promise<RemoteJobPullResponse>;
|
|
221
|
+
progress(jobId: string, request?: RemoteJobProgressRequest): Promise<{
|
|
222
|
+
ok: true;
|
|
223
|
+
payload: RemoteJob;
|
|
224
|
+
}>;
|
|
225
|
+
complete(jobId: string, request?: RemoteJobCompletionRequest): Promise<{
|
|
226
|
+
ok: true;
|
|
227
|
+
payload: RemoteJob;
|
|
228
|
+
}>;
|
|
229
|
+
fail(jobId: string, request: RemoteJobFailureRequest): Promise<{
|
|
230
|
+
ok: true;
|
|
231
|
+
payload: RemoteJob;
|
|
232
|
+
}>;
|
|
233
|
+
}
|
package/dist/remote.js
CHANGED
|
@@ -100,32 +100,6 @@ class RemoteTreeseedSdkClient {
|
|
|
100
100
|
function normalizeExternalBaseUrl(baseUrl) {
|
|
101
101
|
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
102
102
|
}
|
|
103
|
-
class TreeseedGatewayClient {
|
|
104
|
-
baseUrl;
|
|
105
|
-
token;
|
|
106
|
-
fetchImpl;
|
|
107
|
-
constructor(config) {
|
|
108
|
-
this.baseUrl = normalizeExternalBaseUrl(config.baseUrl);
|
|
109
|
-
this.token = config.bearerToken;
|
|
110
|
-
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
111
|
-
}
|
|
112
|
-
async requestJson(path, options = {}) {
|
|
113
|
-
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
114
|
-
method: options.method ?? "POST",
|
|
115
|
-
headers: {
|
|
116
|
-
accept: "application/json",
|
|
117
|
-
authorization: `Bearer ${this.token}`,
|
|
118
|
-
...options.body === void 0 ? {} : { "content-type": "application/json" }
|
|
119
|
-
},
|
|
120
|
-
body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
|
|
121
|
-
});
|
|
122
|
-
const payload = await response.json().catch(() => ({}));
|
|
123
|
-
if (!response.ok) {
|
|
124
|
-
throw new Error(typeof payload.error === "string" ? payload.error : `Gateway request failed with ${response.status}.`);
|
|
125
|
-
}
|
|
126
|
-
return payload;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
103
|
class CloudflareQueuePullClient {
|
|
130
104
|
baseUrl;
|
|
131
105
|
token;
|
|
@@ -177,6 +151,36 @@ class CloudflareQueuePullClient {
|
|
|
177
151
|
});
|
|
178
152
|
}
|
|
179
153
|
}
|
|
154
|
+
class CloudflareQueuePushClient {
|
|
155
|
+
baseUrl;
|
|
156
|
+
token;
|
|
157
|
+
fetchImpl;
|
|
158
|
+
constructor(config) {
|
|
159
|
+
const apiBaseUrl = config.apiBaseUrl ?? "https://api.cloudflare.com/client/v4/accounts";
|
|
160
|
+
this.baseUrl = `${normalizeExternalBaseUrl(apiBaseUrl)}/${config.accountId}/queues/${config.queueId}`;
|
|
161
|
+
this.token = config.token;
|
|
162
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
163
|
+
}
|
|
164
|
+
async enqueue(request) {
|
|
165
|
+
const response = await this.fetchImpl(`${this.baseUrl}/messages`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: {
|
|
168
|
+
accept: "application/json",
|
|
169
|
+
authorization: `Bearer ${this.token}`,
|
|
170
|
+
"content-type": "application/json"
|
|
171
|
+
},
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
body: request.message,
|
|
174
|
+
content_type: "json",
|
|
175
|
+
delay_seconds: request.delaySeconds ?? 0
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
const payload = await response.json().catch(() => ({}));
|
|
179
|
+
if (!response.ok || payload.success === false) {
|
|
180
|
+
throw new Error(payload.errors?.[0]?.message ?? `Queue request failed with ${response.status}.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
180
184
|
class RemoteTreeseedOperationsClient {
|
|
181
185
|
constructor(client) {
|
|
182
186
|
this.client = client;
|
|
@@ -190,13 +194,85 @@ class RemoteTreeseedOperationsClient {
|
|
|
190
194
|
});
|
|
191
195
|
}
|
|
192
196
|
}
|
|
197
|
+
class RemoteTreeseedDispatchClient {
|
|
198
|
+
constructor(client) {
|
|
199
|
+
this.client = client;
|
|
200
|
+
}
|
|
201
|
+
client;
|
|
202
|
+
dispatch(projectId, request) {
|
|
203
|
+
return this.client.requestJson(`/v1/projects/${encodeURIComponent(projectId)}/dispatch`, {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: request,
|
|
206
|
+
requireAuth: true
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
class RemoteTreeseedJobsClient {
|
|
211
|
+
constructor(client) {
|
|
212
|
+
this.client = client;
|
|
213
|
+
}
|
|
214
|
+
client;
|
|
215
|
+
get(jobId) {
|
|
216
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}`, {
|
|
217
|
+
requireAuth: true
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
cancel(jobId) {
|
|
221
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}/cancel`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
requireAuth: true
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
events(jobId) {
|
|
227
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}/events`, {
|
|
228
|
+
requireAuth: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
class RemoteTreeseedRunnerClient {
|
|
233
|
+
constructor(client) {
|
|
234
|
+
this.client = client;
|
|
235
|
+
}
|
|
236
|
+
client;
|
|
237
|
+
pull(projectId, request = {}) {
|
|
238
|
+
return this.client.requestJson(`/v1/projects/${encodeURIComponent(projectId)}/runner/jobs/pull`, {
|
|
239
|
+
method: "POST",
|
|
240
|
+
body: request,
|
|
241
|
+
requireAuth: true
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
progress(jobId, request = {}) {
|
|
245
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}/progress`, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
body: request,
|
|
248
|
+
requireAuth: true
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
complete(jobId, request = {}) {
|
|
252
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}/complete`, {
|
|
253
|
+
method: "POST",
|
|
254
|
+
body: request,
|
|
255
|
+
requireAuth: true
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
fail(jobId, request) {
|
|
259
|
+
return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}/fail`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
body: request,
|
|
262
|
+
requireAuth: true
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
193
266
|
export {
|
|
194
267
|
CloudflareQueuePullClient,
|
|
268
|
+
CloudflareQueuePushClient,
|
|
195
269
|
RemoteTreeseedAuthClient,
|
|
196
270
|
RemoteTreeseedClient,
|
|
271
|
+
RemoteTreeseedDispatchClient,
|
|
272
|
+
RemoteTreeseedJobsClient,
|
|
197
273
|
RemoteTreeseedOperationsClient,
|
|
274
|
+
RemoteTreeseedRunnerClient,
|
|
198
275
|
RemoteTreeseedSdkClient,
|
|
199
276
|
TREESEED_REMOTE_CONTRACT_HEADER,
|
|
200
|
-
TREESEED_REMOTE_CONTRACT_VERSION
|
|
201
|
-
TreeseedGatewayClient
|
|
277
|
+
TREESEED_REMOTE_CONTRACT_VERSION
|
|
202
278
|
};
|
|
@@ -1,121 +1,16 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
downloadHref: '/books/karyon-knowledge.md',
|
|
11
|
-
downloadTitle: 'Karyon Knowledge Library',
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
const projectRoot = PROJECT_TENANT.__tenantRoot ?? process.cwd();
|
|
15
|
-
const outputDir = path.join(projectRoot, 'public', 'books');
|
|
16
|
-
const legacyOutputFile = path.join(projectRoot, 'public', 'book.md');
|
|
17
|
-
function sortPaths(paths) {
|
|
18
|
-
return [...paths].sort((left, right) => left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }));
|
|
19
|
-
}
|
|
20
|
-
function getSidebarOrder(filePath) {
|
|
21
|
-
const rawContent = fs.readFileSync(filePath, 'utf8');
|
|
22
|
-
const frontmatterMatch = rawContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
23
|
-
if (!frontmatterMatch)
|
|
24
|
-
return Number.POSITIVE_INFINITY;
|
|
25
|
-
const orderMatch = frontmatterMatch[1].match(/^\s{2}order:\s*(\d+)\s*$/m);
|
|
26
|
-
return orderMatch ? Number.parseInt(orderMatch[1], 10) : Number.POSITIVE_INFINITY;
|
|
27
|
-
}
|
|
28
|
-
function collectMarkdownFiles(rootPath) {
|
|
29
|
-
if (!fs.existsSync(rootPath)) {
|
|
30
|
-
throw new Error(`Book export root not found: ${rootPath}`);
|
|
31
|
-
}
|
|
32
|
-
const stats = fs.statSync(rootPath);
|
|
33
|
-
if (stats.isFile()) {
|
|
34
|
-
return [rootPath];
|
|
35
|
-
}
|
|
36
|
-
const entries = fs.readdirSync(rootPath, { withFileTypes: true });
|
|
37
|
-
return sortPaths(entries.flatMap((entry) => {
|
|
38
|
-
const fullPath = path.join(rootPath, entry.name);
|
|
39
|
-
if (entry.isDirectory()) {
|
|
40
|
-
return collectMarkdownFiles(fullPath);
|
|
41
|
-
}
|
|
42
|
-
if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) {
|
|
43
|
-
return [fullPath];
|
|
44
|
-
}
|
|
45
|
-
return [];
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
function stripFrontmatter(content) {
|
|
49
|
-
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '').trim();
|
|
50
|
-
}
|
|
51
|
-
function stripMdxOnlySyntax(content) {
|
|
52
|
-
return content
|
|
53
|
-
.replace(/^import\s.+$/gm, '')
|
|
54
|
-
.replace(/^\s*<\/?[A-Z][^>]*>\s*$/gm, '')
|
|
55
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
56
|
-
.trim();
|
|
57
|
-
}
|
|
58
|
-
function inferExportRootFromBasePath(book) {
|
|
59
|
-
const normalizedBasePath = String(book.basePath || '').trim();
|
|
60
|
-
const knowledgePrefix = '/knowledge/';
|
|
61
|
-
if (!normalizedBasePath.startsWith(knowledgePrefix)) {
|
|
62
|
-
throw new Error(`Book basePath must start with "${knowledgePrefix}" to infer exports: ${book.basePath}`);
|
|
63
|
-
}
|
|
64
|
-
const relativeKnowledgePath = normalizedBasePath
|
|
65
|
-
.slice(knowledgePrefix.length)
|
|
66
|
-
.replace(/^\/+|\/+$/g, '');
|
|
67
|
-
if (!relativeKnowledgePath) {
|
|
68
|
-
throw new Error(`Book basePath must identify a knowledge directory: ${book.basePath}`);
|
|
2
|
+
import { exportTenantBookPackages } from '../platform/book-export.js';
|
|
3
|
+
async function main() {
|
|
4
|
+
console.log('Generating Treeseed AI book packages...');
|
|
5
|
+
const result = await exportTenantBookPackages({ projectRoot: process.cwd() });
|
|
6
|
+
for (const entry of result.bookPackages) {
|
|
7
|
+
console.log(`Generated ${path.relative(result.projectRoot, entry.markdownPath)}`);
|
|
8
|
+
console.log(`Generated ${path.relative(result.projectRoot, entry.indexPath)}`);
|
|
69
9
|
}
|
|
70
|
-
|
|
10
|
+
console.log(`Generated ${path.relative(result.projectRoot, result.libraryPackage.markdownPath)}`);
|
|
11
|
+
console.log(`Generated ${path.relative(result.projectRoot, result.libraryPackage.indexPath)}`);
|
|
71
12
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return [inferExportRootFromBasePath(book)];
|
|
77
|
-
}
|
|
78
|
-
function resolveBookFiles(book) {
|
|
79
|
-
const files = resolveExportRoots(book).flatMap((root) => collectMarkdownFiles(path.resolve(projectRoot, root)).sort((left, right) => {
|
|
80
|
-
const orderDelta = getSidebarOrder(left) - getSidebarOrder(right);
|
|
81
|
-
if (orderDelta !== 0)
|
|
82
|
-
return orderDelta;
|
|
83
|
-
return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' });
|
|
84
|
-
}));
|
|
85
|
-
return Array.from(new Set(files));
|
|
86
|
-
}
|
|
87
|
-
function buildBookMarkdown(book) {
|
|
88
|
-
const sections = resolveBookFiles(book).map((filePath) => {
|
|
89
|
-
const rawContent = fs.readFileSync(filePath, 'utf8');
|
|
90
|
-
return stripMdxOnlySyntax(stripFrontmatter(rawContent));
|
|
91
|
-
});
|
|
92
|
-
return `# ${book.downloadTitle}\n\n> This document is auto-generated from the Karyon knowledge source.\n\n${sections.join('\n\n---\n\n')}\n`;
|
|
93
|
-
}
|
|
94
|
-
function ensureOutputDir() {
|
|
95
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
96
|
-
}
|
|
97
|
-
function writeBookOutput(fileName, content) {
|
|
98
|
-
const outputPath = path.join(outputDir, fileName);
|
|
99
|
-
fs.writeFileSync(outputPath, content);
|
|
100
|
-
return outputPath;
|
|
101
|
-
}
|
|
102
|
-
function main() {
|
|
103
|
-
console.log('Generating contextual Karyon knowledge exports...');
|
|
104
|
-
ensureOutputDir();
|
|
105
|
-
const bookOutputs = BOOKS.map((book) => {
|
|
106
|
-
const content = buildBookMarkdown(book);
|
|
107
|
-
const outputPath = writeBookOutput(book.downloadFileName, content);
|
|
108
|
-
console.log(`Generated ${path.relative(projectRoot, outputPath)}`);
|
|
109
|
-
return { book, content };
|
|
110
|
-
});
|
|
111
|
-
const compositeContent = `# ${TREESEED_LIBRARY_DOWNLOAD.downloadTitle}\n\n> This document is auto-generated from the Karyon knowledge source.\n\n${bookOutputs
|
|
112
|
-
.map(({ content }) => content.trim())
|
|
113
|
-
.join('\n\n---\n\n')}\n`;
|
|
114
|
-
const compositeOutputPath = writeBookOutput(TREESEED_LIBRARY_DOWNLOAD.downloadFileName, compositeContent);
|
|
115
|
-
console.log(`Generated ${path.relative(projectRoot, compositeOutputPath)}`);
|
|
116
|
-
if (fs.existsSync(legacyOutputFile)) {
|
|
117
|
-
fs.rmSync(legacyOutputFile);
|
|
118
|
-
console.log(`Removed legacy export ${path.relative(projectRoot, legacyOutputFile)}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
main();
|
|
13
|
+
main().catch((error) => {
|
|
14
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { stdin as input, stdout as output } from 'node:process';
|
|
4
|
-
import { collectCliPreflight } from '../operations/services/workspace-preflight.js';
|
|
5
|
-
import { applyTreeseedEnvironmentToProcess, ensureTreeseedGitignoreEntries, getTreeseedMachineConfigPaths, rotateTreeseedMachineKey, runTreeseedConfigWizard, writeTreeseedLocalEnvironmentFiles, } from '../operations/services/config-runtime.js';
|
|
2
|
+
import { applyTreeseedConfigValues, applyTreeseedSafeRepairs, collectTreeseedConfigContext, ensureTreeseedGitignoreEntries, finalizeTreeseedConfig, getTreeseedMachineConfigPaths, rotateTreeseedMachineKey, } from '../operations/services/config-runtime.js';
|
|
6
3
|
const tenantRoot = process.cwd();
|
|
7
4
|
function parseArgs(argv) {
|
|
8
5
|
const parsed = {
|
|
@@ -45,42 +42,37 @@ const scopes = options.scopes.length === 0 || options.scopes.includes('all')
|
|
|
45
42
|
? ['local', 'staging', 'prod']
|
|
46
43
|
: ['local', 'staging', 'prod'].filter((scope) => options.scopes.includes(scope));
|
|
47
44
|
ensureTreeseedGitignoreEntries(tenantRoot);
|
|
48
|
-
const preflight = collectCliPreflight({ cwd: tenantRoot, requireAuth: false });
|
|
49
|
-
const rl = readline.createInterface({ input, output });
|
|
50
45
|
try {
|
|
51
|
-
console.log('Treeseed configuration wizard');
|
|
52
|
-
console.log('This command writes a local machine config, generates .env.local and .dev.vars, and can sync GitHub or Cloudflare settings.');
|
|
53
|
-
console.log('Enter a value to set it, press Enter to keep the current/default value, or enter "-" to clear a value.\n');
|
|
54
46
|
if (options.rotateMachineKey) {
|
|
55
47
|
const result = rotateTreeseedMachineKey(tenantRoot);
|
|
56
48
|
console.log('Treeseed machine key rotated.');
|
|
57
49
|
console.log(`Machine key: ${result.keyPath}`);
|
|
58
50
|
}
|
|
59
51
|
else {
|
|
60
|
-
|
|
52
|
+
applyTreeseedSafeRepairs(tenantRoot);
|
|
53
|
+
const context = collectTreeseedConfigContext({
|
|
54
|
+
tenantRoot,
|
|
55
|
+
scopes,
|
|
56
|
+
env: process.env,
|
|
57
|
+
});
|
|
58
|
+
const updates = scopes.flatMap((scope) => context.entriesByScope[scope].map((entry) => ({
|
|
59
|
+
scope,
|
|
60
|
+
entryId: entry.id,
|
|
61
|
+
value: entry.effectiveValue,
|
|
62
|
+
reused: entry.currentValue.length > 0 || entry.suggestedValue.length > 0,
|
|
63
|
+
})));
|
|
64
|
+
const applyResult = applyTreeseedConfigValues({ tenantRoot, updates });
|
|
65
|
+
const result = finalizeTreeseedConfig({
|
|
61
66
|
tenantRoot,
|
|
62
67
|
scopes,
|
|
63
68
|
sync: options.sync,
|
|
64
|
-
|
|
65
|
-
prompt: async (message) => {
|
|
66
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
67
|
-
return '';
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
return await rl.question(message);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return '';
|
|
74
|
-
}
|
|
75
|
-
},
|
|
69
|
+
env: process.env,
|
|
76
70
|
});
|
|
77
|
-
writeTreeseedLocalEnvironmentFiles(tenantRoot);
|
|
78
|
-
applyTreeseedEnvironmentToProcess({ tenantRoot, scope: 'local', override: true });
|
|
79
71
|
const { configPath, keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
80
|
-
console.log('
|
|
72
|
+
console.log('Treeseed config completed.');
|
|
81
73
|
console.log(`Machine config: ${configPath}`);
|
|
82
74
|
console.log(`Machine key: ${keyPath}`);
|
|
83
|
-
console.log(`Updated values: ${
|
|
75
|
+
console.log(`Updated values: ${applyResult.updated.length}`);
|
|
84
76
|
console.log(`Initialized environments: ${result.initialized.length}`);
|
|
85
77
|
if (result.synced.github) {
|
|
86
78
|
console.log(`GitHub sync: ${result.synced.github.secrets.length} secrets, ${result.synced.github.variables.length} variables (${result.synced.github.repository})`);
|
|
@@ -91,5 +83,4 @@ try {
|
|
|
91
83
|
}
|
|
92
84
|
}
|
|
93
85
|
finally {
|
|
94
|
-
rl.close();
|
|
95
86
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentSdk } from './sdk.ts';
|
|
2
|
+
type JsonRecord = Record<string, unknown>;
|
|
3
|
+
type SdkOperationHandler = (sdk: AgentSdk, input: JsonRecord) => Promise<unknown> | unknown;
|
|
4
|
+
interface SdkOperationSpec {
|
|
5
|
+
name: string;
|
|
6
|
+
aliases?: string[];
|
|
7
|
+
handler: SdkOperationHandler;
|
|
8
|
+
}
|
|
9
|
+
export declare function listSdkOperationNames(): string[];
|
|
10
|
+
export declare function findSdkOperation(name: string): SdkOperationSpec | null;
|
|
11
|
+
export declare function executeSdkOperation(sdk: AgentSdk, operationName: string, input: JsonRecord): Promise<unknown>;
|
|
12
|
+
export {};
|