@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.
Files changed (41) hide show
  1. package/README.md +128 -69
  2. package/dist/base-command.d.ts +1 -1
  3. package/dist/base-command.d.ts.map +1 -1
  4. package/dist/base-command.js +2 -2
  5. package/dist/codegen.d.ts.map +1 -1
  6. package/dist/codegen.js +22 -5
  7. package/dist/codegen.test.js +3 -2
  8. package/dist/commands/build/delete.d.ts.map +1 -1
  9. package/dist/commands/build/delete.js +0 -11
  10. package/dist/commands/build/deploy.d.ts +0 -1
  11. package/dist/commands/build/deploy.d.ts.map +1 -1
  12. package/dist/commands/build/deploy.js +3 -16
  13. package/dist/commands/build/find.js +2 -2
  14. package/dist/commands/build/list.d.ts +1 -0
  15. package/dist/commands/build/list.d.ts.map +1 -1
  16. package/dist/commands/build/list.js +27 -8
  17. package/dist/commands/logs/query.d.ts +30 -0
  18. package/dist/commands/logs/query.d.ts.map +1 -0
  19. package/dist/commands/logs/query.js +229 -0
  20. package/dist/commands/logs/tail.d.ts +22 -0
  21. package/dist/commands/logs/tail.d.ts.map +1 -0
  22. package/dist/commands/logs/tail.js +144 -0
  23. package/dist/commands/tail.d.ts +1 -6
  24. package/dist/commands/tail.d.ts.map +1 -1
  25. package/dist/commands/tail.js +9 -203
  26. package/dist/deploy.d.ts.map +1 -1
  27. package/dist/deploy.js +1 -4
  28. package/dist/log-helpers.d.ts +9 -0
  29. package/dist/log-helpers.d.ts.map +1 -0
  30. package/dist/log-helpers.js +205 -0
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/oclif.manifest.json +2096 -1687
  33. package/package.json +3 -3
  34. package/templates/db/node_modules/.bin/prisma +4 -4
  35. package/templates/db/node_modules/.bin/prisma-kysely +2 -2
  36. package/templates/db/node_modules/.bin/tsc +4 -4
  37. package/templates/db/node_modules/.bin/tsserver +4 -4
  38. package/templates/db/node_modules/.bin/zx +2 -2
  39. package/templates/db/package.json +1 -1
  40. package/templates/handlers/mcp-service/index.ts.hbs +53 -0
  41. 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
- plan.push({
196
- templateName: path.join(TEMPLATES_DIR, TEMPLATE_DB_PRISMA),
197
- outPath: path.join(PRISMA_DIR, valueOf(db.name)),
198
- context: { dbName: valueOf(db.name) },
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');
@@ -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;AAGpD,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;CAsD3B"}
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;;;;;;;;;;;;;;;;;;MA2CV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuD3B"}
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.resume ? (this.flags.lock ?? config.lock) : undefined;
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.resume || this.flags.amend;
57
+ const amend = config.sandbox || this.flags.amend;
71
58
  if (config.sandbox) {
72
- this.log(`🔔 Branch is in Sandbox mode, running deploy in amend mode`);
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;IAuIlE,uBAAuB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAwBxE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IA+DlE,cAAc,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAiDzD,gBAAgB;IA8DhB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3B"}
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 [appName, appVersions] of groups) {
109
+ for (const [_appName, appVersions] of groups) {
110
110
  result.push('');
111
- result.push(chalk.bold.underline(`=== ${appName} ===`));
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.name)}`);
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 [appName, versions] of sortedGroups) {
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
- result.push(`\n${chalk.bold(appName)} ${chalk.dim(`(${versions.length} version${versions.length !== 1 ? 's' : ''})`)}`);
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 [appName, versions] of groups) {
304
- result.push(`\n${chalk.bold.underline(`=== ${appName} ===`)}`);
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"}