@liquidmetal-ai/raindrop 0.4.13 → 0.5.1
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 +128 -69
- package/dist/base-command.d.ts +1 -1
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +2 -2
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +22 -5
- package/dist/codegen.test.js +3 -2
- package/dist/commands/build/delete.d.ts.map +1 -1
- package/dist/commands/build/delete.js +0 -11
- package/dist/commands/build/deploy.d.ts +0 -1
- package/dist/commands/build/deploy.d.ts.map +1 -1
- package/dist/commands/build/deploy.js +3 -16
- package/dist/commands/build/find.js +2 -2
- package/dist/commands/build/list.d.ts +1 -0
- package/dist/commands/build/list.d.ts.map +1 -1
- package/dist/commands/build/list.js +27 -8
- package/dist/commands/logs/query.d.ts +30 -0
- package/dist/commands/logs/query.d.ts.map +1 -0
- package/dist/commands/logs/query.js +229 -0
- package/dist/commands/logs/tail.d.ts +22 -0
- package/dist/commands/logs/tail.d.ts.map +1 -0
- package/dist/commands/logs/tail.js +144 -0
- package/dist/commands/tail.d.ts +1 -6
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +9 -203
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +1 -4
- package/dist/log-helpers.d.ts +9 -0
- package/dist/log-helpers.d.ts.map +1 -0
- package/dist/log-helpers.js +205 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/oclif.manifest.json +2096 -1687
- package/package.json +3 -3
- package/templates/db/node_modules/.bin/prisma +4 -4
- package/templates/db/node_modules/.bin/prisma-kysely +2 -2
- package/templates/db/node_modules/.bin/tsc +4 -4
- package/templates/db/node_modules/.bin/tsserver +4 -4
- package/templates/db/node_modules/.bin/zx +2 -2
- package/templates/db/package.json +1 -1
- package/templates/handlers/mcp-service/index.ts.hbs +53 -0
- package/templates/init/package.json.hbs +3 -1
package/dist/codegen.js
CHANGED
|
@@ -16,6 +16,7 @@ export const RAINDROP_TYPES_FILENAME = 'raindrop.gen.ts';
|
|
|
16
16
|
const TEMPLATE_BUCKET_EVENT_NOTIFICATION = path.join('handlers', 'bucket-event-notification');
|
|
17
17
|
const TEMPLATE_QUEUE_CONSUMER = path.join('handlers', 'queue-consumer');
|
|
18
18
|
const TEMPLATE_HTTP_SERVICE = path.join('handlers', 'http-service');
|
|
19
|
+
const TEMPLATE_MCP_SERVICE = path.join('handlers', 'mcp-service');
|
|
19
20
|
const TEMPLATE_ACTOR = path.join('handlers', 'actor');
|
|
20
21
|
const TEMPLATE_TASK = path.join('handlers', 'task');
|
|
21
22
|
const TEMPLATE_DB = 'db';
|
|
@@ -163,6 +164,13 @@ export function codegenPlan(apps) {
|
|
|
163
164
|
context: { serviceName: valueOf(service.name) },
|
|
164
165
|
});
|
|
165
166
|
}
|
|
167
|
+
for (const service of app.mcpService) {
|
|
168
|
+
plan.push({
|
|
169
|
+
templateName: path.join(TEMPLATES_DIR, TEMPLATE_MCP_SERVICE),
|
|
170
|
+
outPath: path.join(HANDLERS_DIR, valueOf(service.name)),
|
|
171
|
+
context: { serviceName: valueOf(service.name) },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
166
174
|
for (const actor of app.actor) {
|
|
167
175
|
plan.push({
|
|
168
176
|
templateName: path.join(TEMPLATES_DIR, TEMPLATE_ACTOR),
|
|
@@ -192,11 +200,13 @@ export function codegenPlan(apps) {
|
|
|
192
200
|
outPath: path.join(HANDLERS_DIR, COMMON_DIR),
|
|
193
201
|
context: { dbName: valueOf(db.name) },
|
|
194
202
|
});
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
if (db.disable_prisma === undefined || !valueOf(db.disable_prisma)) {
|
|
204
|
+
plan.push({
|
|
205
|
+
templateName: path.join(TEMPLATES_DIR, TEMPLATE_DB_PRISMA),
|
|
206
|
+
outPath: path.join(PRISMA_DIR, valueOf(db.name)),
|
|
207
|
+
context: { dbName: kebabCaseToCamelCase(valueOf(db.name)) },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
200
210
|
plan.push({
|
|
201
211
|
templateName: path.join(TEMPLATES_DIR, TEMPLATE_DB_SCRIPT),
|
|
202
212
|
outPath: path.join(DB_SCRIPT_DIR),
|
|
@@ -281,6 +291,10 @@ export function gatherEnvForHandler(handler, app) {
|
|
|
281
291
|
bindings[kebabCaseToConstantCase(valueOf(appSmartMemory.name))] = 'SmartMemory';
|
|
282
292
|
types.add('SmartMemory');
|
|
283
293
|
}
|
|
294
|
+
for (const appSmartSql of app.smartSql) {
|
|
295
|
+
bindings[kebabCaseToConstantCase(valueOf(appSmartSql.name))] = 'SmartSql';
|
|
296
|
+
types.add('SmartSql');
|
|
297
|
+
}
|
|
284
298
|
for (const env of app.env) {
|
|
285
299
|
bindings[kebabCaseToConstantCase(valueOf(env.name))] = 'string';
|
|
286
300
|
}
|
|
@@ -301,6 +315,9 @@ export function gatherEnvForHandler(handler, app) {
|
|
|
301
315
|
// Add logger binding.
|
|
302
316
|
bindings['logger'] = 'Logger';
|
|
303
317
|
types.add('Logger');
|
|
318
|
+
// Add tracer binding.
|
|
319
|
+
bindings['tracer'] = 'Tracer';
|
|
320
|
+
types.add('Tracer');
|
|
304
321
|
// Define the App type (uniquely identifying the Raindrop app)
|
|
305
322
|
// which will be provided in the template
|
|
306
323
|
types.add('App');
|
package/dist/codegen.test.js
CHANGED
|
@@ -113,6 +113,7 @@ test('codegens template files', async () => {
|
|
|
113
113
|
context: {
|
|
114
114
|
toolName: 'raindrop',
|
|
115
115
|
applicationName: 'test-app',
|
|
116
|
+
raindropFrameworkVersion: '0.4.9',
|
|
116
117
|
},
|
|
117
118
|
opts: {
|
|
118
119
|
renderScaffoldingCode: true,
|
|
@@ -134,7 +135,7 @@ test('generates SmartMemory type bindings', async () => {
|
|
|
134
135
|
const apps = await mustManifestFromString(`
|
|
135
136
|
application "test-app" {
|
|
136
137
|
smartmemory "user-memory" {}
|
|
137
|
-
|
|
138
|
+
|
|
138
139
|
service "test-service" {}
|
|
139
140
|
}
|
|
140
141
|
`);
|
|
@@ -150,7 +151,7 @@ test('generates SmartBucket type bindings', async () => {
|
|
|
150
151
|
const apps = await mustManifestFromString(`
|
|
151
152
|
application "test-app" {
|
|
152
153
|
smartbucket "data-bucket" {}
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
service "test-service" {}
|
|
155
156
|
}
|
|
156
157
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/build/delete.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/build/delete.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI;;MAET;IAEF,MAAM,CAAC,WAAW,SAAkC;IAEpD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;MAiCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2C3B"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { valueOf } from '@liquidmetal-ai/drizzle/appify/build';
|
|
2
2
|
import { Args, Flags } from '@oclif/core';
|
|
3
3
|
import { BaseCommand } from '../../base-command.js';
|
|
4
|
-
import { defaultConfig } from '../../config.js';
|
|
5
4
|
export default class Delete extends BaseCommand {
|
|
6
5
|
static args = {
|
|
7
6
|
application: Args.string({ char: 'a', description: 'application name', required: false }),
|
|
@@ -59,10 +58,6 @@ Deleted (application) at version (version)
|
|
|
59
58
|
const config = await this.loadConfig();
|
|
60
59
|
versionId = config.versionId;
|
|
61
60
|
}
|
|
62
|
-
// Don't allow deleting the epoch version.
|
|
63
|
-
if (versionId === defaultConfig.versionId) {
|
|
64
|
-
this.error('There are no applications at the epoch version', { exit: 1 });
|
|
65
|
-
}
|
|
66
61
|
if (this.args.application === undefined) {
|
|
67
62
|
const apps = await this.loadManifest();
|
|
68
63
|
const app = apps[0];
|
|
@@ -78,12 +73,6 @@ Deleted (application) at version (version)
|
|
|
78
73
|
// Omitting versionId will delete all versions of the application
|
|
79
74
|
applications: [{ applicationName: this.args.application, currentVersionId: versionId || '' }],
|
|
80
75
|
});
|
|
81
|
-
// TODO [ian] Failures?
|
|
82
|
-
// if (resp.failure.length) {
|
|
83
|
-
// this.error(`Failed to delete applications: ${resp.failure.map((f) => `${f.name}@${f.versionId}`).join(', ')}`, {
|
|
84
|
-
// exit: 1,
|
|
85
|
-
// });
|
|
86
|
-
// }
|
|
87
76
|
}
|
|
88
77
|
catch (e) {
|
|
89
78
|
if (e instanceof Error) {
|
|
@@ -11,7 +11,6 @@ export default class Deploy extends BaseCommand<typeof Deploy> {
|
|
|
11
11
|
impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
start: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
'no-watch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
resume: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
14
|
lock: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
15
|
amend: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
16
|
config: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/build/deploy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/build/deploy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;MAsCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA0C3B"}
|
|
@@ -43,11 +43,6 @@ Deploy a Raindrop application version.
|
|
|
43
43
|
required: false,
|
|
44
44
|
default: false,
|
|
45
45
|
}),
|
|
46
|
-
resume: Flags.boolean({
|
|
47
|
-
description: 'resume a deployment',
|
|
48
|
-
required: false,
|
|
49
|
-
default: false,
|
|
50
|
-
}),
|
|
51
46
|
lock: Flags.string({
|
|
52
47
|
description: 'override lock ID to resume deployment',
|
|
53
48
|
required: false,
|
|
@@ -57,19 +52,11 @@ Deploy a Raindrop application version.
|
|
|
57
52
|
async run() {
|
|
58
53
|
const config = await this.loadConfig();
|
|
59
54
|
const versionId = this.flags.versionId ?? config.versionId;
|
|
60
|
-
const unlock = this.flags.
|
|
61
|
-
if (this.flags.resume) {
|
|
62
|
-
if (versionId === undefined) {
|
|
63
|
-
this.error(`Cannot resume deployment without a versionId. Please specify a versionId or run without --resume to start a new deployment.`);
|
|
64
|
-
}
|
|
65
|
-
if (unlock === undefined) {
|
|
66
|
-
this.error(`Cannot resume deployment without a lock. Please specify --lock or run without --resume to start a new deployment.`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
55
|
+
const unlock = this.flags.lock ?? config.lock ?? 'user';
|
|
69
56
|
// Resuming and Sandbox is _always_ amending
|
|
70
|
-
const amend = config.sandbox || this.flags.
|
|
57
|
+
const amend = config.sandbox || this.flags.amend;
|
|
71
58
|
if (config.sandbox) {
|
|
72
|
-
this.log(`🔔
|
|
59
|
+
this.log(`🔔 Application is in Sandbox mode, running deploy in amend mode`);
|
|
73
60
|
}
|
|
74
61
|
await deploy({
|
|
75
62
|
command: this,
|
|
@@ -159,8 +159,8 @@ Find modules for a specific application and version.
|
|
|
159
159
|
console.log(` └─ ${chalk.cyan(module.name)} ${chalk.dim(`(${module.moduleId})`)} ${chalk.yellow(module.type)}`);
|
|
160
160
|
console.log(` Status: ${convergedStatus} at ${chalk.dim(date)}`);
|
|
161
161
|
// Display routes for service and actor modules
|
|
162
|
-
if (module.service || module.actor) {
|
|
163
|
-
const moduleData = module.service || module.actor;
|
|
162
|
+
if (module.service || module.actor || module.mcpService) {
|
|
163
|
+
const moduleData = module.service || module.actor || module.mcpService;
|
|
164
164
|
if (moduleData?.routes && moduleData.routes.length > 0) {
|
|
165
165
|
console.log(` ${chalk.bold('Routes:')}`);
|
|
166
166
|
for (const route of moduleData.routes) {
|
|
@@ -33,6 +33,7 @@ export default class List extends BaseCommand<typeof List> {
|
|
|
33
33
|
nodesMap: Map<string, VersionNode>;
|
|
34
34
|
};
|
|
35
35
|
renderGitLogStyle(applications: ApplicationsResponse_Application[]): string;
|
|
36
|
+
formatAppName(app: ApplicationsResponse_Application): string;
|
|
36
37
|
groupApplicationsByName(applications: ApplicationsResponse_Application[]): Map<string, ApplicationsResponse_Application[]>;
|
|
37
38
|
renderCompactView(applications: ApplicationsResponse_Application[]): string;
|
|
38
39
|
renderTreeView(applications: ApplicationsResponse_Application[]): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/build/list.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAEhC,SAAS,EACV,MAAM,yDAAyD,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,gCAAgC,CAAC;IAC9C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CASzC,CAAC;AAEX,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAqC;IAEvD,MAAM,CAAC,QAAQ,WAgBb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MAmCV;IAEF,gBAAgB,CAAC,YAAY,EAAE,gCAAgC,EAAE;;;;IAkCjE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/build/list.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAEhC,SAAS,EACV,MAAM,yDAAyD,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,gCAAgC,CAAC;IAC9C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CASzC,CAAC;AAEX,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAqC;IAEvD,MAAM,CAAC,QAAQ,WAgBb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MAmCV;IAEF,gBAAgB,CAAC,YAAY,EAAE,gCAAgC,EAAE;;;;IAkCjE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IA0IlE,aAAa,CAAC,GAAG,EAAE,gCAAgC,GAAG,MAAM;IAM5D,uBAAuB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAwBxE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAmElE,cAAc,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAoDzD,gBAAgB;IA+DhB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3B"}
|
|
@@ -106,9 +106,13 @@ Show only running/active versions.
|
|
|
106
106
|
const groups = this.groupApplicationsByName(applications);
|
|
107
107
|
const result = [];
|
|
108
108
|
// Process each application group separately
|
|
109
|
-
for (const [
|
|
109
|
+
for (const [_appName, appVersions] of groups) {
|
|
110
110
|
result.push('');
|
|
111
|
-
|
|
111
|
+
const firstApp = appVersions[0];
|
|
112
|
+
if (!firstApp)
|
|
113
|
+
continue;
|
|
114
|
+
const formattedAppName = this.formatAppName(firstApp);
|
|
115
|
+
result.push(chalk.bold.underline(`=== ${formattedAppName} ===`));
|
|
112
116
|
result.push('');
|
|
113
117
|
// Build the version tree for this app
|
|
114
118
|
const { rootNodes } = this.buildVersionTree(appVersions);
|
|
@@ -215,12 +219,17 @@ Show only running/active versions.
|
|
|
215
219
|
result.push(`${graphLine} ${chalk.bold('Status:')} ${statusColor(statusString[app.state])}`);
|
|
216
220
|
result.push(`${graphLine} ${chalk.bold('Date:')} ${chalk.dim(timestamp)}`);
|
|
217
221
|
result.push(`${graphLine} `);
|
|
218
|
-
result.push(`${graphLine} ${chalk.bold(app
|
|
222
|
+
result.push(`${graphLine} ${chalk.bold(this.formatAppName(app))}`);
|
|
219
223
|
result.push(`${graphLine}`);
|
|
220
224
|
}
|
|
221
225
|
}
|
|
222
226
|
return result.join('\n');
|
|
223
227
|
}
|
|
228
|
+
// Helper function to format app name with privileged indicator
|
|
229
|
+
formatAppName(app) {
|
|
230
|
+
const privilegedIcon = app.privileged ? chalk.magenta('🔒 ') : '';
|
|
231
|
+
return `${privilegedIcon}${app.name}`;
|
|
232
|
+
}
|
|
224
233
|
// Group applications by name
|
|
225
234
|
groupApplicationsByName(applications) {
|
|
226
235
|
const groups = new Map();
|
|
@@ -255,10 +264,15 @@ Show only running/active versions.
|
|
|
255
264
|
const bTime = timestampDate(bLatest.createdAt || EPOCH_TS).getTime();
|
|
256
265
|
return bTime - aTime;
|
|
257
266
|
});
|
|
258
|
-
for (const [
|
|
267
|
+
for (const [_appName, versions] of sortedGroups) {
|
|
259
268
|
const activeVersions = versions.filter(v => v.state === UnitState.RUNNING);
|
|
260
269
|
const deletedVersions = versions.filter(v => v.state === UnitState.DELETED || v.state === UnitState.DELETING);
|
|
261
|
-
|
|
270
|
+
// Use the first version to check if app is privileged (all versions of same app have same privileged status)
|
|
271
|
+
const firstVersion = versions[0];
|
|
272
|
+
if (!firstVersion)
|
|
273
|
+
continue;
|
|
274
|
+
const formattedAppName = this.formatAppName(firstVersion);
|
|
275
|
+
result.push(`\n${chalk.bold(formattedAppName)} ${chalk.dim(`(${versions.length} version${versions.length !== 1 ? 's' : ''})`)}`);
|
|
262
276
|
// Show active versions first
|
|
263
277
|
let shown = 0;
|
|
264
278
|
for (const version of activeVersions) {
|
|
@@ -300,8 +314,12 @@ Show only running/active versions.
|
|
|
300
314
|
renderTreeView(applications) {
|
|
301
315
|
const groups = this.groupApplicationsByName(applications);
|
|
302
316
|
const result = [];
|
|
303
|
-
for (const [
|
|
304
|
-
|
|
317
|
+
for (const [_appName, versions] of groups) {
|
|
318
|
+
const firstVersion = versions[0];
|
|
319
|
+
if (!firstVersion)
|
|
320
|
+
continue;
|
|
321
|
+
const formattedAppName = this.formatAppName(firstVersion);
|
|
322
|
+
result.push(`\n${chalk.bold.underline(`=== ${formattedAppName} ===`)}`);
|
|
305
323
|
// Build version tree for this app
|
|
306
324
|
const { rootNodes } = this.buildVersionTree(versions);
|
|
307
325
|
// Render each root and its descendants
|
|
@@ -381,11 +399,12 @@ Show only running/active versions.
|
|
|
381
399
|
...a,
|
|
382
400
|
status: statusString[a.state],
|
|
383
401
|
locked: a.lock ? 'locked' : '',
|
|
402
|
+
privileged: a.privileged ? 'system' : 'user',
|
|
384
403
|
};
|
|
385
404
|
return acc;
|
|
386
405
|
},
|
|
387
406
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
388
|
-
{}), ['organizationId', 'name', 'versionId', 'branch', 'status', 'locked', ...(this.flags.all ? ['deletedAt'] : [])]);
|
|
407
|
+
{}), ['organizationId', 'name', 'versionId', 'branch', 'status', 'locked', 'privileged', ...(this.flags.all ? ['deletedAt'] : [])]);
|
|
389
408
|
}
|
|
390
409
|
else if (this.flags.output === 'json') {
|
|
391
410
|
console.log(toJsonString(ApplicationsResponseSchema, resp, { prettySpaces: 2 }));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class Query extends BaseCommand<typeof Query> {
|
|
3
|
+
static args: {};
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
manifest: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
application: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
version: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
startTime: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
endTime: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
last: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
traceId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
rainbowAuthService: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
raindropCatalogService: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
config: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
rainbowAuthToken: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
rainbowOrganizationId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
rainbowUserId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
sendVersionMetadata: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
};
|
|
26
|
+
private parseTimeToMillis;
|
|
27
|
+
private parseDurationToMillis;
|
|
28
|
+
run(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/commands/logs/query.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,WAAW,CAAC,OAAO,KAAK,CAAC;IAC1D,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAA0D;IAE5E,MAAM,CAAC,QAAQ,WAKb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;;;MAuEV;IAEF,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,qBAAqB;IAuBvB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAkH3B"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { valueOf } from '@liquidmetal-ai/drizzle/appify/build';
|
|
2
|
+
import { create } from '@bufbuild/protobuf';
|
|
3
|
+
import { timestampFromDate } from '@bufbuild/protobuf/wkt';
|
|
4
|
+
import { QueryLogsRequestSchema, TimeQuerySchema } from '@liquidmetal-ai/drizzle/liquidmetal/v1alpha1/riverjack_pb';
|
|
5
|
+
import { Flags } from '@oclif/core';
|
|
6
|
+
import { BaseCommand } from '../../base-command.js';
|
|
7
|
+
import { displayTraceGroupedEvents } from '../../log-helpers.js';
|
|
8
|
+
export default class Query extends BaseCommand {
|
|
9
|
+
static args = {};
|
|
10
|
+
static description = 'query logs of applications with time range filtering';
|
|
11
|
+
static examples = [
|
|
12
|
+
`<%= config.bin %> <%= command.id %>`,
|
|
13
|
+
`<%= config.bin %> <%= command.id %> --application my-app`,
|
|
14
|
+
`<%= config.bin %> <%= command.id %> --application my-app --version v1.2.3`,
|
|
15
|
+
`<%= config.bin %> <%= command.id %> --application my-app --start-time 1638360000000 --end-time 1638363600000`,
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
...BaseCommand.HIDDEN_FLAGS,
|
|
19
|
+
output: Flags.string({
|
|
20
|
+
char: 'o',
|
|
21
|
+
description: 'output format',
|
|
22
|
+
default: 'text',
|
|
23
|
+
options: ['text', 'json'],
|
|
24
|
+
}),
|
|
25
|
+
impersonate: Flags.string({
|
|
26
|
+
char: 'i',
|
|
27
|
+
description: 'impersonate organization',
|
|
28
|
+
required: false,
|
|
29
|
+
hidden: true,
|
|
30
|
+
}),
|
|
31
|
+
manifest: Flags.string({
|
|
32
|
+
char: 'm',
|
|
33
|
+
description: 'project manifest',
|
|
34
|
+
required: false,
|
|
35
|
+
default: 'raindrop.manifest',
|
|
36
|
+
hidden: true,
|
|
37
|
+
}),
|
|
38
|
+
application: Flags.string({
|
|
39
|
+
char: 'a',
|
|
40
|
+
description: 'application',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
version: Flags.string({
|
|
44
|
+
char: 'v',
|
|
45
|
+
description: 'application version',
|
|
46
|
+
required: false,
|
|
47
|
+
}),
|
|
48
|
+
startTime: Flags.string({
|
|
49
|
+
char: 's',
|
|
50
|
+
description: 'start time for query (Unix timestamp in milliseconds or ISO string)',
|
|
51
|
+
required: false,
|
|
52
|
+
}),
|
|
53
|
+
endTime: Flags.string({
|
|
54
|
+
char: 'e',
|
|
55
|
+
description: 'end time for query (Unix timestamp in milliseconds or ISO string)',
|
|
56
|
+
required: false,
|
|
57
|
+
}),
|
|
58
|
+
last: Flags.string({
|
|
59
|
+
char: 'l',
|
|
60
|
+
description: 'query logs from last duration (e.g., "1h", "30m", "2d")',
|
|
61
|
+
required: false,
|
|
62
|
+
}),
|
|
63
|
+
limit: Flags.integer({
|
|
64
|
+
char: 'n',
|
|
65
|
+
description: 'maximum number of log events to return',
|
|
66
|
+
required: false,
|
|
67
|
+
default: 100,
|
|
68
|
+
}),
|
|
69
|
+
traceId: Flags.string({
|
|
70
|
+
description: 'filter logs by trace ID',
|
|
71
|
+
required: false,
|
|
72
|
+
}),
|
|
73
|
+
status: Flags.string({
|
|
74
|
+
description: 'filter logs by status (ok, error)',
|
|
75
|
+
required: false,
|
|
76
|
+
options: ['ok', 'error'],
|
|
77
|
+
}),
|
|
78
|
+
rainbowAuthService: Flags.string({
|
|
79
|
+
default: 'https://liquidmetal.run/api/connect',
|
|
80
|
+
hidden: true,
|
|
81
|
+
env: 'LIQUIDMETAL_RAINBOW_AUTH_SERVICE',
|
|
82
|
+
}),
|
|
83
|
+
raindropCatalogService: Flags.string({
|
|
84
|
+
env: 'RAINDROP_CATALOG_SERVICE',
|
|
85
|
+
description: 'URL of the catalog service',
|
|
86
|
+
hidden: true,
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
parseTimeToMillis(timeStr) {
|
|
90
|
+
// Try parsing as Unix timestamp first
|
|
91
|
+
const timestamp = Number(timeStr);
|
|
92
|
+
if (!isNaN(timestamp)) {
|
|
93
|
+
// If it's a reasonable number, assume it's already in milliseconds
|
|
94
|
+
if (timestamp > 1000000000000) {
|
|
95
|
+
return timestamp;
|
|
96
|
+
}
|
|
97
|
+
// If it's smaller, assume it's in seconds and convert to milliseconds
|
|
98
|
+
return timestamp * 1000;
|
|
99
|
+
}
|
|
100
|
+
// Try parsing as ISO string
|
|
101
|
+
const date = new Date(timeStr);
|
|
102
|
+
if (!isNaN(date.getTime())) {
|
|
103
|
+
return date.getTime();
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`Invalid time format: ${timeStr}. Use Unix timestamp (ms) or ISO string.`);
|
|
106
|
+
}
|
|
107
|
+
parseDurationToMillis(duration) {
|
|
108
|
+
const match = duration.match(/^(\d+)([smhd])$/);
|
|
109
|
+
if (!match) {
|
|
110
|
+
throw new Error(`Invalid duration format: ${duration}. Use format like "1h", "30m", "2d".`);
|
|
111
|
+
}
|
|
112
|
+
const value = parseInt(match[1], 10);
|
|
113
|
+
const unit = match[2];
|
|
114
|
+
switch (unit) {
|
|
115
|
+
case 's':
|
|
116
|
+
return value * 1000;
|
|
117
|
+
case 'm':
|
|
118
|
+
return value * 60 * 1000;
|
|
119
|
+
case 'h':
|
|
120
|
+
return value * 60 * 60 * 1000;
|
|
121
|
+
case 'd':
|
|
122
|
+
return value * 24 * 60 * 60 * 1000;
|
|
123
|
+
default:
|
|
124
|
+
throw new Error(`Unsupported duration unit: ${unit}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async run() {
|
|
128
|
+
// Load version from config if not provided
|
|
129
|
+
const apps = await this.loadManifest();
|
|
130
|
+
const config = await this.loadConfig();
|
|
131
|
+
if (!this.flags.version) {
|
|
132
|
+
if (!config.versionId) {
|
|
133
|
+
this.error('No version provided or found in config', { exit: 1 });
|
|
134
|
+
}
|
|
135
|
+
this.flags.version = config.versionId;
|
|
136
|
+
}
|
|
137
|
+
// Load application from manifest if not provided
|
|
138
|
+
if (!this.flags.application) {
|
|
139
|
+
const app = apps[0];
|
|
140
|
+
if (app === undefined) {
|
|
141
|
+
this.error('No application provided or found in manifest', { exit: 1 });
|
|
142
|
+
}
|
|
143
|
+
this.flags.application = valueOf(app.name);
|
|
144
|
+
}
|
|
145
|
+
const { userId, client: riverjackService, organizationId: defaultOrganizationId, } = await this.tenantRiverjackService();
|
|
146
|
+
const organizationId = this.flags.impersonate ?? defaultOrganizationId;
|
|
147
|
+
// Calculate time range
|
|
148
|
+
let startTimeMs;
|
|
149
|
+
let endTimeMs;
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
if (this.flags.last) {
|
|
152
|
+
// Use "last" duration
|
|
153
|
+
try {
|
|
154
|
+
const durationMs = this.parseDurationToMillis(this.flags.last);
|
|
155
|
+
startTimeMs = now - durationMs;
|
|
156
|
+
endTimeMs = now;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.error(`Invalid duration: ${error instanceof Error ? error.message : String(error)}`, { exit: 1 });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (this.flags.startTime || this.flags.endTime) {
|
|
163
|
+
// Use explicit start/end times
|
|
164
|
+
try {
|
|
165
|
+
startTimeMs = this.flags.startTime ? this.parseTimeToMillis(this.flags.startTime) : now - 60 * 60 * 1000; // Default to 1 hour ago
|
|
166
|
+
endTimeMs = this.flags.endTime ? this.parseTimeToMillis(this.flags.endTime) : now;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
this.error(`Invalid time format: ${error instanceof Error ? error.message : String(error)}`, { exit: 1 });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Default to last hour
|
|
174
|
+
startTimeMs = now - 60 * 60 * 1000;
|
|
175
|
+
endTimeMs = now;
|
|
176
|
+
}
|
|
177
|
+
this.log(`Using organization: ${organizationId}`);
|
|
178
|
+
this.log(`Using user: ${userId}`);
|
|
179
|
+
this.log(`Querying logs for ${this.flags.application}@${this.flags.version}`);
|
|
180
|
+
this.log(`Time range: ${new Date(startTimeMs).toLocaleString()} - ${new Date(endTimeMs).toLocaleString()}`);
|
|
181
|
+
this.log('');
|
|
182
|
+
try {
|
|
183
|
+
const timeQuery = create(TimeQuerySchema, {
|
|
184
|
+
startTime: timestampFromDate(new Date(startTimeMs)),
|
|
185
|
+
endTime: timestampFromDate(new Date(endTimeMs)),
|
|
186
|
+
});
|
|
187
|
+
const request = create(QueryLogsRequestSchema, {
|
|
188
|
+
organizationId,
|
|
189
|
+
userId,
|
|
190
|
+
applicationName: this.flags.application,
|
|
191
|
+
applicationVersionId: this.flags.version,
|
|
192
|
+
timeQuery,
|
|
193
|
+
limit: this.flags.limit,
|
|
194
|
+
...(this.flags.traceId && { traceId: this.flags.traceId }),
|
|
195
|
+
...(this.flags.status && { status: this.flags.status }),
|
|
196
|
+
});
|
|
197
|
+
const response = await riverjackService.queryLogs(request);
|
|
198
|
+
if (this.flags.output === 'json') {
|
|
199
|
+
// Custom serializer to handle BigInt
|
|
200
|
+
this.log(JSON.stringify(response, (key, value) => {
|
|
201
|
+
if (typeof value === 'bigint') {
|
|
202
|
+
return value.toString();
|
|
203
|
+
}
|
|
204
|
+
return value;
|
|
205
|
+
}, 2));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
if (response.events.length === 0) {
|
|
209
|
+
this.log('No log events found for the specified criteria.');
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
this.log(`Found ${response.events.length} log events (total: ${response.totalCount}):\n`);
|
|
213
|
+
displayTraceGroupedEvents(response.events, { log: (message) => this.log(message) });
|
|
214
|
+
if (response.hasMore) {
|
|
215
|
+
this.log('ℹ️ More results available. Use --limit to increase the number of results.');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
if (error instanceof Error) {
|
|
222
|
+
this.error(`Failed to query logs: ${error.message}`, { exit: 1 });
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this.error('Failed to query logs: Unknown error', { exit: 1 });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class Tail extends BaseCommand<typeof Tail> {
|
|
3
|
+
static args: {};
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
manifest: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
application: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
version: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
rainbowAuthService: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
raindropCatalogService: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
rainbowAuthToken: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
rainbowOrganizationId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
rainbowUserId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
sendVersionMetadata: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
};
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=tail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tail.d.ts","sourceRoot":"","sources":["../../../src/commands/logs/tail.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAwC;IAE1D,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;MAyCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAkG3B"}
|