@liquidmetal-ai/raindrop 0.9.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +86 -86
  2. package/bundle/{chunk-AWVSG5HH.js → chunk-2GD7Q3YS.js} +1 -1
  3. package/bundle/{chunk-FYMUM7KM.js → chunk-2QH4PO44.js} +1 -1
  4. package/bundle/{chunk-2KEFV5U5.js → chunk-3DZWEZ2R.js} +1 -1
  5. package/bundle/{chunk-B4MQISI4.js → chunk-45V47YNS.js} +4 -4
  6. package/bundle/{chunk-ILG4QJPW.js → chunk-4FXE4I6S.js} +2 -2
  7. package/bundle/{chunk-43UGYZQA.js → chunk-4RN4TIJI.js} +1 -1
  8. package/bundle/{chunk-OHP3SQGY.js → chunk-4U2CSHCN.js} +1 -1
  9. package/bundle/{chunk-T3VHOMDJ.js → chunk-4UUTGMDZ.js} +1 -1
  10. package/bundle/{chunk-Z2VVSBIX.js → chunk-4WNBHWJP.js} +4 -4
  11. package/bundle/{chunk-CEFC4JXX.js → chunk-5KMI72AS.js} +1 -1
  12. package/bundle/{chunk-GBFY2PMX.js → chunk-5REFCZTG.js} +2 -2
  13. package/bundle/chunk-6BCRCAR4.js +22452 -0
  14. package/bundle/{chunk-YWKX37S2.js → chunk-7GDFE7HE.js} +3 -1
  15. package/bundle/{chunk-4NTUYQ5R.js → chunk-AI5PKHR7.js} +1 -1
  16. package/bundle/chunk-D5MXH3QV.js +133 -0
  17. package/bundle/chunk-ETDAUGBG.js +74 -0
  18. package/bundle/chunk-GBUNP7OT.js +4539 -0
  19. package/bundle/chunk-GRUPCK5H.js +384 -0
  20. package/bundle/chunk-JENN4EVA.js +75 -0
  21. package/bundle/chunk-JZYHHRGL.js +292 -0
  22. package/bundle/chunk-LJKAU7FY.js +4486 -0
  23. package/bundle/{chunk-E7J2LWQ5.js → chunk-MJZG4ABT.js} +1 -1
  24. package/bundle/chunk-MZZHXHB4.js +805 -0
  25. package/bundle/{chunk-B6JLI47W.js → chunk-ORUMKXQZ.js} +2 -2
  26. package/bundle/chunk-PMHLHYMI.js +231 -0
  27. package/bundle/chunk-QRJUX37T.js +48 -0
  28. package/bundle/chunk-QTCJ6YYG.js +147 -0
  29. package/bundle/chunk-RAPSKVRO.js +12148 -0
  30. package/bundle/chunk-U7NHRBYD.js +502 -0
  31. package/bundle/chunk-WDR5M2SS.js +238746 -0
  32. package/bundle/{chunk-SPBJ5BNI.js → chunk-YXJU5KSB.js} +1 -1
  33. package/bundle/chunk-ZQIPU6IX.js +44 -0
  34. package/bundle/commands/annotation/get.js +3 -3
  35. package/bundle/commands/annotation/list.js +3 -3
  36. package/bundle/commands/annotation/put.js +3 -3
  37. package/bundle/commands/auth/list.js +2 -2
  38. package/bundle/commands/auth/login.js +3 -3
  39. package/bundle/commands/auth/logout.js +2 -2
  40. package/bundle/commands/auth/select.js +3 -3
  41. package/bundle/commands/bucket/create-credential.js +2 -2
  42. package/bundle/commands/bucket/delete-credential.js +2 -2
  43. package/bundle/commands/bucket/get-credential.js +2 -2
  44. package/bundle/commands/bucket/list-credentials.js +2 -2
  45. package/bundle/commands/build/branch.js +9 -9
  46. package/bundle/commands/build/checkout.js +6 -6
  47. package/bundle/commands/build/clone.js +7 -7
  48. package/bundle/commands/build/delete.js +6 -6
  49. package/bundle/commands/build/deploy.js +9 -9
  50. package/bundle/commands/build/env/get.js +2 -2
  51. package/bundle/commands/build/env/set.js +2 -2
  52. package/bundle/commands/build/find.js +4 -4
  53. package/bundle/commands/build/generate.js +3 -3
  54. package/bundle/commands/build/init-workspace.js +3 -3
  55. package/bundle/commands/build/init.js +3 -3
  56. package/bundle/commands/build/list.js +5 -5
  57. package/bundle/commands/build/sandbox.js +7 -7
  58. package/bundle/commands/build/start.js +2 -2
  59. package/bundle/commands/build/status.js +5 -5
  60. package/bundle/commands/build/stop.js +2 -2
  61. package/bundle/commands/build/tools/check.js +2 -2
  62. package/bundle/commands/build/tools/fmt.js +2 -2
  63. package/bundle/commands/build/unsandbox.js +7 -7
  64. package/bundle/commands/build/upload.js +5 -5
  65. package/bundle/commands/build/validate.js +4 -4
  66. package/bundle/commands/build/workos/delete.js +4 -4
  67. package/bundle/commands/build/workos/env/attach.js +3 -3
  68. package/bundle/commands/build/workos/env/create.js +3 -3
  69. package/bundle/commands/build/workos/env/delete.js +3 -3
  70. package/bundle/commands/build/workos/env/detach.js +3 -3
  71. package/bundle/commands/build/workos/env/get.js +3 -3
  72. package/bundle/commands/build/workos/env/list.js +3 -3
  73. package/bundle/commands/build/workos/env/set.js +3 -3
  74. package/bundle/commands/build/workos/invite.js +3 -3
  75. package/bundle/commands/build/workos/setup.js +3 -3
  76. package/bundle/commands/build/workos/status.js +3 -3
  77. package/bundle/commands/dns/create.js +2 -2
  78. package/bundle/commands/dns/delete.js +4 -4
  79. package/bundle/commands/dns/get.js +4 -4
  80. package/bundle/commands/dns/list.js +3 -3
  81. package/bundle/commands/dns/records/create.js +2 -2
  82. package/bundle/commands/dns/records/delete.js +3 -3
  83. package/bundle/commands/dns/records/get.js +2 -2
  84. package/bundle/commands/dns/records/list.js +2 -2
  85. package/bundle/commands/dns/records/update.js +2 -2
  86. package/bundle/commands/logs/query.js +3 -3
  87. package/bundle/commands/logs/tail.js +3 -3
  88. package/bundle/commands/mcp/install-claude.js +2 -2
  89. package/bundle/commands/mcp/install-gemini.js +2 -2
  90. package/bundle/commands/mcp/install-goose.js +2 -2
  91. package/bundle/commands/mcp/status.js +2 -2
  92. package/bundle/commands/object/delete.js +2 -2
  93. package/bundle/commands/object/get.js +2 -2
  94. package/bundle/commands/object/list.js +73 -7
  95. package/bundle/commands/object/put.js +2 -2
  96. package/bundle/commands/query/chunk-search.js +3 -3
  97. package/bundle/commands/query/document.js +3 -3
  98. package/bundle/commands/query/events.js +2 -2
  99. package/bundle/commands/query/reindex.js +2 -2
  100. package/bundle/commands/query/search.js +3 -3
  101. package/bundle/commands/tail.js +2 -2
  102. package/bundle/index.js +1 -1
  103. package/dist/build.test.d.ts +2 -0
  104. package/dist/build.test.d.ts.map +1 -0
  105. package/dist/build.test.js +46 -0
  106. package/dist/codegen.test.d.ts +2 -0
  107. package/dist/codegen.test.d.ts.map +1 -0
  108. package/dist/codegen.test.js +223 -0
  109. package/dist/commands/logs/tail.test.d.ts +2 -0
  110. package/dist/commands/logs/tail.test.d.ts.map +1 -0
  111. package/dist/commands/logs/tail.test.js +366 -0
  112. package/dist/commands/object/list.d.ts.map +1 -1
  113. package/dist/commands/object/list.js +91 -4
  114. package/dist/config.test.d.ts +2 -0
  115. package/dist/config.test.d.ts.map +1 -0
  116. package/dist/config.test.js +27 -0
  117. package/dist/index.test.d.ts +2 -0
  118. package/dist/index.test.d.ts.map +1 -0
  119. package/dist/index.test.js +56 -0
  120. package/oclif.manifest.json +2569 -2569
  121. package/package.json +3 -3
  122. package/templates/db/node_modules/.bin/prisma +2 -2
  123. package/templates/db/node_modules/.bin/prisma-kysely +2 -2
  124. package/templates/db/node_modules/.bin/tsc +3 -7
  125. package/templates/db/node_modules/.bin/tsserver +3 -7
  126. package/templates/db/node_modules/.bin/zx +2 -2
  127. package/templates/init/RAINDROP.md.hbs +3 -3
@@ -0,0 +1,366 @@
1
+ import { create } from '@bufbuild/protobuf';
2
+ import { QueryLogsResponseSchema, StreamLogsResponseSchema } from '@liquidmetal-ai/drizzle/liquidmetal/v1alpha1/riverjack_pb';
3
+ import { expect, test, vi, describe, beforeEach, afterEach } from 'vitest';
4
+ import Tail from './tail.js';
5
+ // Mock valueOf function
6
+ vi.mock('@liquidmetal-ai/drizzle/appify/build', () => ({
7
+ valueOf: (obj) => obj?.value || obj,
8
+ }));
9
+ // Mock the base command dependencies
10
+ vi.mock('../../base-command.js', () => {
11
+ return {
12
+ BaseCommand: class {
13
+ static HIDDEN_FLAGS = {};
14
+ flags = {};
15
+ log = vi.fn();
16
+ error = vi.fn((msg, options) => {
17
+ if (options?.exit) {
18
+ throw new Error(msg);
19
+ }
20
+ });
21
+ async loadManifest() {
22
+ return [{ name: { value: 'test-app' } }];
23
+ }
24
+ async loadConfig() {
25
+ return { versionId: 'test-version-123' };
26
+ }
27
+ async tenantRiverjackService() {
28
+ return {
29
+ userId: 'test-user',
30
+ organizationId: 'test-org',
31
+ client: this.mockRiverjackClient,
32
+ };
33
+ }
34
+ mockRiverjackClient = {
35
+ queryLogs: vi.fn(),
36
+ streamLogs: vi.fn(),
37
+ };
38
+ }
39
+ };
40
+ });
41
+ // Mock the log helpers
42
+ vi.mock('../../log-helpers.js', () => ({
43
+ displayTraceGroupedEvents: vi.fn(),
44
+ }));
45
+ describe('Tail command', () => {
46
+ let tail;
47
+ let mockQueryLogs;
48
+ let mockStreamLogs;
49
+ let logSpy;
50
+ beforeEach(() => {
51
+ tail = new Tail([], {});
52
+ logSpy = vi.spyOn(tail, 'log');
53
+ // Access the mock client through the instance
54
+ const mockClient = tail.mockRiverjackClient;
55
+ mockQueryLogs = mockClient.queryLogs;
56
+ mockStreamLogs = mockClient.streamLogs;
57
+ // Reset mocks
58
+ vi.clearAllMocks();
59
+ });
60
+ afterEach(() => {
61
+ vi.restoreAllMocks();
62
+ });
63
+ describe('GNU tail behavior', () => {
64
+ test('default behavior: shows 10 logs and exits (no streaming)', async () => {
65
+ // Setup mock response
66
+ const mockEvents = [
67
+ { traceId: '1', message: 'log 1' },
68
+ { traceId: '2', message: 'log 2' },
69
+ ];
70
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
71
+ events: mockEvents,
72
+ totalCount: BigInt(2),
73
+ hasMore: false,
74
+ }));
75
+ // Set no flags (default behavior)
76
+ tail.flags = {
77
+ application: 'test-app',
78
+ output: 'text',
79
+ };
80
+ await tail.run();
81
+ // Should query for 10 logs (default)
82
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
83
+ limit: 10,
84
+ organizationId: 'test-org',
85
+ userId: 'test-user',
86
+ applicationName: 'test-app',
87
+ applicationVersionId: 'test-version-123',
88
+ }));
89
+ // Should NOT start streaming
90
+ expect(mockStreamLogs).not.toHaveBeenCalled();
91
+ // Should show appropriate messages
92
+ expect(logSpy).toHaveBeenCalledWith('Using organization: test-org');
93
+ expect(logSpy).toHaveBeenCalledWith('Using user: test-user');
94
+ expect(logSpy).toHaveBeenCalledWith('Showing last 10 logs for test-app@test-version-123...');
95
+ expect(logSpy).toHaveBeenCalledWith('📜 Showing last 2 log events:');
96
+ // Should NOT show streaming messages
97
+ expect(logSpy).not.toHaveBeenCalledWith(expect.stringContaining('Press Ctrl+C'));
98
+ expect(logSpy).not.toHaveBeenCalledWith(expect.stringContaining('Live stream starting'));
99
+ });
100
+ test('with -n flag: shows N logs and exits', async () => {
101
+ const mockEvents = Array(5).fill(null).map((_, i) => ({
102
+ traceId: `${i}`,
103
+ message: `log ${i}`,
104
+ }));
105
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
106
+ events: mockEvents,
107
+ totalCount: BigInt(5),
108
+ hasMore: false,
109
+ }));
110
+ tail.flags = {
111
+ application: 'test-app',
112
+ output: 'text',
113
+ lines: 25,
114
+ };
115
+ await tail.run();
116
+ // Should query for 25 logs
117
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
118
+ limit: 25,
119
+ }));
120
+ // Should NOT stream
121
+ expect(mockStreamLogs).not.toHaveBeenCalled();
122
+ // Should show correct message
123
+ expect(logSpy).toHaveBeenCalledWith('Showing last 25 logs for test-app@test-version-123...');
124
+ });
125
+ test('with -f flag: shows 10 logs then streams', async () => {
126
+ const mockEvents = [{ traceId: '1', message: 'log 1' }];
127
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
128
+ events: mockEvents,
129
+ totalCount: BigInt(1),
130
+ hasMore: false,
131
+ }));
132
+ // Mock async generator for streaming
133
+ const mockStream = async function* () {
134
+ yield create(StreamLogsResponseSchema, {
135
+ responseType: 'stream_started',
136
+ message: 'Stream started',
137
+ });
138
+ };
139
+ mockStreamLogs.mockReturnValue(mockStream());
140
+ tail.flags = {
141
+ application: 'test-app',
142
+ output: 'text',
143
+ follow: true,
144
+ };
145
+ await tail.run();
146
+ // Should query for 10 logs (default)
147
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
148
+ limit: 10,
149
+ }));
150
+ // Should start streaming
151
+ expect(mockStreamLogs).toHaveBeenCalled();
152
+ // Should show appropriate messages
153
+ expect(logSpy).toHaveBeenCalledWith('Following logs for test-app@test-version-123...');
154
+ expect(logSpy).toHaveBeenCalledWith('Press Ctrl+C to stop following\n');
155
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Live stream starting'));
156
+ });
157
+ test('with -n and -f flags: shows N logs then streams', async () => {
158
+ const mockEvents = Array(30).fill(null).map((_, i) => ({
159
+ traceId: `${i}`,
160
+ message: `log ${i}`,
161
+ }));
162
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
163
+ events: mockEvents,
164
+ totalCount: BigInt(30),
165
+ hasMore: false,
166
+ }));
167
+ const mockStream = async function* () {
168
+ yield create(StreamLogsResponseSchema, {
169
+ responseType: 'stream_started',
170
+ });
171
+ };
172
+ mockStreamLogs.mockReturnValue(mockStream());
173
+ tail.flags = {
174
+ application: 'test-app',
175
+ output: 'text',
176
+ lines: 30,
177
+ follow: true,
178
+ };
179
+ await tail.run();
180
+ // Should query for 30 logs
181
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
182
+ limit: 30,
183
+ }));
184
+ // Should start streaming
185
+ expect(mockStreamLogs).toHaveBeenCalled();
186
+ // Should show correct messages
187
+ expect(logSpy).toHaveBeenCalledWith('Following logs for test-app@test-version-123...');
188
+ expect(logSpy).toHaveBeenCalledWith('📜 Showing last 30 log events:');
189
+ });
190
+ });
191
+ describe('edge cases', () => {
192
+ test('handles no logs found gracefully', async () => {
193
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
194
+ events: [],
195
+ totalCount: BigInt(0),
196
+ hasMore: false,
197
+ }));
198
+ tail.flags = {
199
+ application: 'test-app',
200
+ output: 'text',
201
+ lines: 10,
202
+ };
203
+ await tail.run();
204
+ expect(logSpy).toHaveBeenCalledWith('ℹ️ No recent logs found in the last hour');
205
+ expect(mockStreamLogs).not.toHaveBeenCalled();
206
+ });
207
+ test('handles query error gracefully when following', async () => {
208
+ mockQueryLogs.mockRejectedValue(new Error('Query failed'));
209
+ const mockStream = async function* () {
210
+ yield create(StreamLogsResponseSchema, {
211
+ responseType: 'stream_started',
212
+ });
213
+ };
214
+ mockStreamLogs.mockReturnValue(mockStream());
215
+ tail.flags = {
216
+ application: 'test-app',
217
+ output: 'text',
218
+ follow: true,
219
+ };
220
+ await tail.run();
221
+ // Should show error but continue to stream
222
+ expect(logSpy).toHaveBeenCalledWith('⚠️ Could not fetch recent logs: Query failed');
223
+ expect(logSpy).toHaveBeenCalledWith('🔴 Starting live stream anyway...');
224
+ expect(mockStreamLogs).toHaveBeenCalled();
225
+ });
226
+ test('handles query error gracefully when not following', async () => {
227
+ mockQueryLogs.mockRejectedValue(new Error('Query failed'));
228
+ tail.flags = {
229
+ application: 'test-app',
230
+ output: 'text',
231
+ lines: 10,
232
+ };
233
+ await tail.run();
234
+ // Should show error and exit
235
+ expect(logSpy).toHaveBeenCalledWith('⚠️ Could not fetch recent logs: Query failed');
236
+ expect(mockStreamLogs).not.toHaveBeenCalled();
237
+ });
238
+ test('handles -n 0 correctly (no recent logs)', async () => {
239
+ tail.flags = {
240
+ application: 'test-app',
241
+ output: 'text',
242
+ lines: 0,
243
+ };
244
+ await tail.run();
245
+ // Should not query for logs when lines is 0
246
+ expect(mockQueryLogs).not.toHaveBeenCalled();
247
+ expect(mockStreamLogs).not.toHaveBeenCalled();
248
+ expect(logSpy).toHaveBeenCalledWith('Showing last 0 logs for test-app@test-version-123...');
249
+ });
250
+ test('handles JSON output format', async () => {
251
+ const mockEvents = [
252
+ { traceId: '1', message: 'log 1' },
253
+ ];
254
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
255
+ events: mockEvents,
256
+ totalCount: BigInt(1),
257
+ hasMore: false,
258
+ }));
259
+ tail.flags = {
260
+ application: 'test-app',
261
+ output: 'json',
262
+ lines: 1,
263
+ };
264
+ await tail.run();
265
+ // Should output JSON format
266
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('['));
267
+ });
268
+ });
269
+ describe('streaming behavior', () => {
270
+ test('handles different stream response types', async () => {
271
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
272
+ events: [],
273
+ totalCount: BigInt(0),
274
+ hasMore: false,
275
+ }));
276
+ const mockStream = async function* () {
277
+ yield create(StreamLogsResponseSchema, {
278
+ responseType: 'stream_started',
279
+ message: 'Started',
280
+ timestamp: new Date().toISOString(),
281
+ });
282
+ yield create(StreamLogsResponseSchema, {
283
+ responseType: 'heartbeat',
284
+ });
285
+ yield create(StreamLogsResponseSchema, {
286
+ responseType: 'events',
287
+ events: [{ traceId: '1', message: 'streamed log' }],
288
+ });
289
+ yield create(StreamLogsResponseSchema, {
290
+ responseType: 'stream_ended',
291
+ message: 'Ended',
292
+ });
293
+ };
294
+ mockStreamLogs.mockReturnValue(mockStream());
295
+ tail.flags = {
296
+ application: 'test-app',
297
+ output: 'text',
298
+ follow: true,
299
+ };
300
+ await tail.run();
301
+ // Check that different response types are handled
302
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Started streaming logs'));
303
+ expect(logSpy).toHaveBeenCalledWith('✅ Stream ended');
304
+ });
305
+ test('handles stream errors', async () => {
306
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
307
+ events: [],
308
+ totalCount: BigInt(0),
309
+ hasMore: false,
310
+ }));
311
+ // Mock error method to capture the error
312
+ const errorSpy = vi.spyOn(tail, 'error').mockImplementation((msg) => {
313
+ throw new Error(msg);
314
+ });
315
+ const mockStream = async function* () {
316
+ yield create(StreamLogsResponseSchema, {
317
+ responseType: 'service_error',
318
+ message: 'Stream connection failed',
319
+ });
320
+ throw new Error('Stream connection failed');
321
+ };
322
+ mockStreamLogs.mockReturnValue(mockStream());
323
+ tail.flags = {
324
+ application: 'test-app',
325
+ output: 'text',
326
+ follow: true,
327
+ };
328
+ // Should handle the error
329
+ await expect(tail.run()).rejects.toThrow('Failed to tail logs');
330
+ // Restore the mock
331
+ errorSpy.mockRestore();
332
+ });
333
+ });
334
+ describe('configuration', () => {
335
+ test('uses application from manifest when not provided', async () => {
336
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
337
+ events: [],
338
+ totalCount: BigInt(0),
339
+ hasMore: false,
340
+ }));
341
+ tail.flags = {
342
+ output: 'text',
343
+ // No application flag set
344
+ };
345
+ await tail.run();
346
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
347
+ applicationName: 'test-app', // From manifest
348
+ }));
349
+ });
350
+ test('uses provided application over manifest', async () => {
351
+ mockQueryLogs.mockResolvedValue(create(QueryLogsResponseSchema, {
352
+ events: [],
353
+ totalCount: BigInt(0),
354
+ hasMore: false,
355
+ }));
356
+ tail.flags = {
357
+ output: 'text',
358
+ application: 'custom-app',
359
+ };
360
+ await tail.run();
361
+ expect(mockQueryLogs).toHaveBeenCalledWith(expect.objectContaining({
362
+ applicationName: 'custom-app',
363
+ }));
364
+ });
365
+ });
366
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/object/list.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW,CAAC,OAAO,UAAU,CAAC;IACpE,MAAM,CAAC,WAAW,SAA8B;IAEhD,MAAM,CAAC,QAAQ,WAUb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MAgDV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAqK3B"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/object/list.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW,CAAC,OAAO,UAAU,CAAC;IACpE,MAAM,CAAC,WAAW,SAA8B;IAEhD,MAAM,CAAC,QAAQ,WAUb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MAgDV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA0P3B"}
@@ -193,25 +193,112 @@ List objects in my-bucket within my-app
193
193
  console.log('No objects found in bucket');
194
194
  return;
195
195
  }
196
+ // Get search agent service for status queries (used by all output formats)
197
+ const { client: searchAgent } = await this.searchAgentService();
198
+ // Query status for all objects using bulk API (significantly more efficient)
199
+ const statusMap = new Map();
200
+ try {
201
+ const bulkStatusResponse = await searchAgent.getDocumentStatusBulk({
202
+ userId,
203
+ organizationId,
204
+ bucketLocation,
205
+ objectIds: response.objects.map(obj => obj.key),
206
+ });
207
+ // Build a map of objectId -> formatted status
208
+ for (const docStatus of bulkStatusResponse.documents) {
209
+ let indexingStatus = 'unknown';
210
+ // Format status based on document state
211
+ if (docStatus.status === 'not_found') {
212
+ indexingStatus = 'not started';
213
+ }
214
+ else if (docStatus.status === 'completed') {
215
+ indexingStatus = 'completed';
216
+ }
217
+ else if (docStatus.status === 'failed') {
218
+ const errorMsg = docStatus.errors.length > 0 ? ` (${docStatus.errors[0]})` : '';
219
+ indexingStatus = `failed${errorMsg}`;
220
+ }
221
+ else if (docStatus.status === 'partial') {
222
+ const errorMsg = docStatus.errors.length > 0 ? ` (${docStatus.errors[0]})` : '';
223
+ indexingStatus = `partial${errorMsg}`;
224
+ }
225
+ else if (docStatus.status === 'ingesting') {
226
+ const chunks = docStatus.ingest?.totalChunksCreated || 0;
227
+ indexingStatus = `ingesting (${chunks} chunks)`;
228
+ }
229
+ else if (docStatus.status === 'processing') {
230
+ // Show progress for most active stage
231
+ const stages = [
232
+ { name: 'embedding', info: docStatus.embedding },
233
+ { name: 'vector index', info: docStatus.vectorIndex },
234
+ { name: 'pii', info: docStatus.pii },
235
+ { name: 'relationships', info: docStatus.relationships },
236
+ ];
237
+ // Find first stage with items remaining
238
+ const activeStage = stages.find(s => s.info && s.info.itemsRemaining > 0);
239
+ if (activeStage && activeStage.info) {
240
+ const progress = activeStage.info.totalExpected - activeStage.info.itemsRemaining;
241
+ const total = activeStage.info.totalExpected;
242
+ indexingStatus = `processing ${activeStage.name} (${progress}/${total})`;
243
+ }
244
+ else {
245
+ // No active stage found - check if stages have been initialized
246
+ const hasInitializedStages = stages.some(s => s.info && s.info.totalExpected > 0);
247
+ if (!hasInitializedStages) {
248
+ // Stages not initialized yet - show ingest info
249
+ const chunks = docStatus.ingest?.totalChunksCreated || 0;
250
+ const queued = docStatus.ingest?.chunksQueued || 0;
251
+ indexingStatus = `processing (${chunks} chunks created, ${queued} queued)`;
252
+ }
253
+ else {
254
+ // Stages initialized but all complete - shouldn't happen in 'processing' state
255
+ indexingStatus = 'processing';
256
+ }
257
+ }
258
+ }
259
+ else {
260
+ indexingStatus = docStatus.status;
261
+ }
262
+ statusMap.set(docStatus.objectId, indexingStatus);
263
+ }
264
+ }
265
+ catch (error) {
266
+ // If bulk query fails, fall back to marking all as unknown
267
+ console.error('Failed to fetch bulk status:', error);
268
+ }
269
+ // Attach status to each object
270
+ const objectsWithStatus = response.objects.map(obj => ({
271
+ ...obj,
272
+ indexingStatus: statusMap.get(obj.key) || 'unknown',
273
+ }));
196
274
  switch (this.flags.output) {
197
- case 'json':
198
- console.log(toJsonString(ListObjectsResponseSchema, response, { prettySpaces: 2 }));
275
+ case 'json': {
276
+ // Create enhanced JSON response with indexing status
277
+ const jsonResponse = JSON.parse(toJsonString(ListObjectsResponseSchema, response));
278
+ jsonResponse.objects = objectsWithStatus.map((obj, index) => ({
279
+ ...jsonResponse.objects[index],
280
+ indexingStatus: obj.indexingStatus,
281
+ }));
282
+ console.log(JSON.stringify(jsonResponse, null, 2));
199
283
  break;
284
+ }
200
285
  case 'table':
201
- console.table(response.objects.map((obj) => ({
286
+ console.table(objectsWithStatus.map((obj) => ({
202
287
  key: obj.key,
203
288
  size: obj.size,
204
289
  contentType: obj.contentType,
205
290
  lastModified: obj.lastModified ? timestampDate(obj.lastModified).toUTCString() : 'N/A',
291
+ indexing: obj.indexingStatus,
206
292
  })));
207
293
  break;
208
294
  default:
209
- for (const obj of response.objects) {
295
+ for (const obj of objectsWithStatus) {
210
296
  const lastModified = obj.lastModified ? timestampDate(obj.lastModified).toUTCString() : 'N/A';
211
297
  console.log(`Key: ${obj.key}`);
212
298
  console.log(` Size: ${obj.size} bytes`);
213
299
  console.log(` Content-Type: ${obj.contentType || 'N/A'}`);
214
300
  console.log(` Last Modified: ${lastModified}`);
301
+ console.log(` Indexing: ${obj.indexingStatus}`);
215
302
  console.log('---');
216
303
  }
217
304
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { expect, test } from 'vitest';
5
+ import { loadConfig, saveConfig } from './config.js';
6
+ test('loading missing config returns default/base config', async () => {
7
+ const configPath = '/tmp/this-never-exists-config.json';
8
+ const config = await loadConfig(configPath);
9
+ expect(config).toEqual({
10
+ versionId: undefined,
11
+ sandbox: false,
12
+ });
13
+ });
14
+ test('save/load config', async () => {
15
+ // Make a temp directory and write a config file to it.
16
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'config-test'));
17
+ const configPath = path.join(tempDir, 'config.json');
18
+ await saveConfig(configPath, {
19
+ versionId: 'test-version-id',
20
+ sandbox: false,
21
+ });
22
+ const config = await loadConfig(configPath);
23
+ expect(config).toEqual({
24
+ versionId: 'test-version-id',
25
+ sandbox: false,
26
+ });
27
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import { expect, test } from 'vitest';
2
+ import semver from 'semver';
3
+ // Import the function for testing (we need to export it first)
4
+ // For now, we'll test the logic by creating a local version
5
+ function shouldUpdateCli(cliVersion, serverVersion) {
6
+ try {
7
+ // Use tilde range (~) - allows patch-level changes but requires same major.minor
8
+ const clientSatisfies = semver.satisfies(cliVersion, `~${serverVersion}`);
9
+ return !clientSatisfies;
10
+ }
11
+ catch {
12
+ // If version parsing fails, fall back to simple comparison
13
+ try {
14
+ return semver.lt(cliVersion, serverVersion);
15
+ }
16
+ catch {
17
+ // If all else fails, assume update is needed
18
+ return true;
19
+ }
20
+ }
21
+ }
22
+ test('shouldUpdateCli - same major.minor, CLI patch higher', () => {
23
+ expect(shouldUpdateCli('1.2.5', '1.2.3')).toBe(false);
24
+ });
25
+ test('shouldUpdateCli - same major.minor, server patch higher', () => {
26
+ expect(shouldUpdateCli('1.2.3', '1.2.5')).toBe(true);
27
+ });
28
+ test('shouldUpdateCli - same major.minor.patch', () => {
29
+ expect(shouldUpdateCli('1.2.3', '1.2.3')).toBe(false);
30
+ });
31
+ test('shouldUpdateCli - different major versions, server higher', () => {
32
+ expect(shouldUpdateCli('1.2.3', '2.0.0')).toBe(true);
33
+ });
34
+ test('shouldUpdateCli - different major versions, CLI higher', () => {
35
+ expect(shouldUpdateCli('2.0.0', '1.5.10')).toBe(true);
36
+ });
37
+ test('shouldUpdateCli - different minor versions, server higher', () => {
38
+ expect(shouldUpdateCli('1.2.3', '1.5.0')).toBe(true);
39
+ });
40
+ test('shouldUpdateCli - different minor versions, CLI higher', () => {
41
+ expect(shouldUpdateCli('1.5.0', '1.2.10')).toBe(true);
42
+ });
43
+ test('shouldUpdateCli - tilde range compatibility', () => {
44
+ // Within tilde range - should not require update
45
+ expect(shouldUpdateCli('1.2.5', '1.2.3')).toBe(false); // CLI 1.2.5 satisfies ~1.2.3 (>=1.2.3 <1.3.0)
46
+ expect(shouldUpdateCli('1.2.3', '1.2.5')).toBe(true); // CLI 1.2.3 does NOT satisfy ~1.2.5 (>=1.2.5 <1.3.0)
47
+ expect(shouldUpdateCli('1.2.3', '1.2.3')).toBe(false); // Same version
48
+ // Outside tilde range - should require update
49
+ expect(shouldUpdateCli('1.2.3', '1.3.0')).toBe(true); // Different minor
50
+ expect(shouldUpdateCli('1.2.3', '2.0.0')).toBe(true); // Different major
51
+ });
52
+ test('shouldUpdateCli - invalid version strings', () => {
53
+ expect(shouldUpdateCli('invalid', '1.2.3')).toBe(true);
54
+ expect(shouldUpdateCli('1.2.3', 'invalid')).toBe(true);
55
+ expect(shouldUpdateCli('invalid', 'invalid')).toBe(true);
56
+ });