@pikku/cli 0.10.1 → 0.10.2

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 (150) hide show
  1. package/.pikku/channel/pikku-channel-types.gen.ts +4 -3
  2. package/.pikku/channel/pikku-channels-map.gen.d.ts +2 -2
  3. package/.pikku/channel/pikku-channels-meta.gen.ts +1 -1
  4. package/.pikku/channel/pikku-channels.gen.ts +1 -1
  5. package/.pikku/cli/pikku-cli-types.gen.ts +23 -1
  6. package/.pikku/cli/pikku-cli-wirings-meta.gen.ts +4 -115
  7. package/.pikku/cli/pikku-cli-wirings.gen.ts +2 -3
  8. package/.pikku/function/pikku-function-types.gen.ts +1 -1
  9. package/.pikku/function/pikku-functions-meta.gen.ts +156 -138
  10. package/.pikku/function/pikku-functions-meta.min.gen.ts +37 -32
  11. package/.pikku/function/pikku-functions.gen.ts +1 -1
  12. package/.pikku/http/pikku-http-types.gen.ts +1 -1
  13. package/.pikku/http/pikku-http-wirings-map.gen.d.ts +2 -2
  14. package/.pikku/http/pikku-http-wirings-meta.gen.ts +1 -1
  15. package/.pikku/http/pikku-http-wirings.gen.ts +1 -1
  16. package/.pikku/mcp/pikku-mcp-types.gen.ts +1 -1
  17. package/.pikku/mcp/pikku-mcp-wirings-meta.gen.ts +1 -1
  18. package/.pikku/mcp/pikku-mcp-wirings.gen.ts +1 -1
  19. package/.pikku/pikku-bootstrap.gen.ts +1 -1
  20. package/.pikku/pikku-services.gen.ts +16 -12
  21. package/.pikku/pikku-types.gen.ts +1 -1
  22. package/.pikku/pikku-websocket.gen.ts +15 -1
  23. package/.pikku/queue/pikku-queue-types.gen.ts +1 -1
  24. package/.pikku/queue/pikku-queue-workers-wirings-map.gen.d.ts +2 -2
  25. package/.pikku/queue/pikku-queue-workers-wirings-meta.gen.ts +1 -1
  26. package/.pikku/queue/pikku-queue-workers-wirings.gen.ts +1 -1
  27. package/.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts +2 -2
  28. package/.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts +10 -9
  29. package/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.ts +9 -8
  30. package/.pikku/scheduler/pikku-scheduler-types.gen.ts +1 -1
  31. package/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.ts +1 -1
  32. package/.pikku/scheduler/pikku-schedulers-wirings.gen.ts +1 -1
  33. package/.pikku/schemas/register.gen.ts +5 -5
  34. package/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  35. package/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
  36. package/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
  37. package/CHANGELOG.md +49 -0
  38. package/bin/pikku.ts +30 -21
  39. package/cli.schema.json +1 -1
  40. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +4 -3
  41. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  42. package/dist/.pikku/channel/pikku-channels-meta.gen.js +1 -1
  43. package/dist/.pikku/channel/pikku-channels.gen.d.ts +1 -1
  44. package/dist/.pikku/channel/pikku-channels.gen.js +1 -1
  45. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +18 -1
  46. package/dist/.pikku/cli/pikku-cli-types.gen.js +20 -1
  47. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +4 -115
  48. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -2
  49. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -2
  50. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  51. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  52. package/dist/.pikku/function/pikku-functions-meta.gen.js +156 -138
  53. package/dist/.pikku/function/pikku-functions-meta.min.gen.js +37 -32
  54. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  55. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  56. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  57. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  58. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  59. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  60. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  61. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  62. package/dist/.pikku/mcp/pikku-mcp-wirings-meta.gen.js +1 -1
  63. package/dist/.pikku/mcp/pikku-mcp-wirings.gen.d.ts +1 -1
  64. package/dist/.pikku/mcp/pikku-mcp-wirings.gen.js +1 -1
  65. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  66. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  67. package/dist/.pikku/pikku-services.gen.d.ts +9 -6
  68. package/dist/.pikku/pikku-services.gen.js +8 -2
  69. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  70. package/dist/.pikku/pikku-types.gen.js +1 -1
  71. package/dist/.pikku/pikku-websocket.gen.d.ts +15 -1
  72. package/dist/.pikku/pikku-websocket.gen.js +15 -1
  73. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  74. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  75. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  76. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  77. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  78. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +9 -8
  79. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  80. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  81. package/dist/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.js +1 -1
  82. package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.d.ts +1 -1
  83. package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.js +1 -1
  84. package/dist/.pikku/schemas/register.gen.js +3 -3
  85. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  86. package/dist/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
  87. package/dist/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
  88. package/dist/bin/pikku.js +24 -19
  89. package/dist/src/cli.wiring.js +107 -99
  90. package/dist/src/functions/commands/all.js +31 -2
  91. package/dist/src/functions/commands/bootstrap.d.ts +1 -0
  92. package/dist/src/functions/commands/bootstrap.js +23 -0
  93. package/dist/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.js +46 -2
  94. package/dist/src/functions/wirings/channels/serialize-channel-types.js +3 -2
  95. package/dist/src/functions/wirings/channels/serialize-websocket-wrapper.js +14 -0
  96. package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +4 -4
  97. package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +24 -4
  98. package/dist/src/functions/wirings/cli/serialize-channel-cli.js +32 -7
  99. package/dist/src/functions/wirings/cli/serialize-cli-types.js +22 -0
  100. package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
  101. package/dist/src/functions/wirings/functions/pikku-command-services.js +54 -26
  102. package/dist/src/functions/wirings/functions/schemas.js +2 -2
  103. package/dist/src/functions/wirings/http/pikku-command-openapi.js +1 -1
  104. package/dist/src/functions/wirings/middleware/pikku-command-middleware.js +3 -10
  105. package/dist/src/middleware/log-command-info-and-time.d.ts +1 -1
  106. package/dist/src/middleware/log-command-info-and-time.js +8 -5
  107. package/dist/src/services/cli-logger.service.d.ts +7 -2
  108. package/dist/src/services/cli-logger.service.js +16 -4
  109. package/dist/src/services.js +77 -12
  110. package/dist/src/utils/command-summary.d.ts +43 -0
  111. package/dist/src/utils/command-summary.js +73 -0
  112. package/dist/src/utils/file-writer.js +2 -2
  113. package/dist/src/utils/pikku-cli-config.js +28 -0
  114. package/dist/src/utils/schema-generator.d.ts +2 -2
  115. package/dist/src/utils/schema-generator.js +3 -3
  116. package/dist/tsconfig.tsbuildinfo +1 -1
  117. package/package.json +3 -4
  118. package/pikku.config.json +5 -2
  119. package/src/cli.wiring.ts +106 -101
  120. package/src/functions/commands/all.ts +38 -2
  121. package/src/functions/commands/bootstrap.ts +27 -0
  122. package/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts +46 -2
  123. package/src/functions/wirings/channels/serialize-channel-types.ts +3 -2
  124. package/src/functions/wirings/channels/serialize-websocket-wrapper.ts +14 -0
  125. package/src/functions/wirings/cli/pikku-command-cli-entry.ts +4 -4
  126. package/src/functions/wirings/cli/serialize-channel-cli-client.ts +24 -4
  127. package/src/functions/wirings/cli/serialize-channel-cli.ts +40 -8
  128. package/src/functions/wirings/cli/serialize-cli-types.ts +22 -0
  129. package/src/functions/wirings/functions/pikku-command-services.ts +57 -28
  130. package/src/functions/wirings/functions/schemas.ts +4 -3
  131. package/src/functions/wirings/http/pikku-command-openapi.ts +2 -1
  132. package/src/functions/wirings/middleware/pikku-command-middleware.ts +11 -22
  133. package/src/middleware/log-command-info-and-time.ts +8 -5
  134. package/src/services/cli-logger.service.ts +20 -5
  135. package/src/services.ts +86 -11
  136. package/src/utils/command-summary.ts +101 -0
  137. package/src/utils/file-writer.ts +2 -2
  138. package/src/utils/pikku-cli-config.ts +28 -0
  139. package/src/utils/schema-generator.ts +5 -4
  140. package/types/application-types.d.ts +5 -1
  141. package/types/config.d.ts +16 -6
  142. package/.pikku/cli/pikku-cli-channel.gen.ts +0 -34
  143. package/.pikku/cli/pikku-cli-client.gen.ts +0 -43
  144. package/.pikku/cli/pikku-cli.gen.ts +0 -41
  145. package/dist/.pikku/cli/pikku-cli-channel.gen.d.ts +0 -1
  146. package/dist/.pikku/cli/pikku-cli-channel.gen.js +0 -33
  147. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +0 -10
  148. package/dist/.pikku/cli/pikku-cli-client.gen.js +0 -34
  149. package/dist/.pikku/cli/pikku-cli.gen.d.ts +0 -10
  150. package/dist/.pikku/cli/pikku-cli.gen.js +0 -38
@@ -5,6 +5,8 @@ import { writeFileInDir } from '../../../utils/file-writer.js'
5
5
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js'
6
6
 
7
7
  export const serializeServicesMap = (
8
+ allSingletonServices: string[],
9
+ allSessionServices: string[],
8
10
  requiredServices: Set<string>,
9
11
  forceRequiredServices: string[] = [],
10
12
  servicesImport: string,
@@ -18,8 +20,18 @@ export const serializeServicesMap = (
18
20
  // - Session factories
19
21
  const usedServices = new Set(requiredServices)
20
22
 
21
- // Internal services that are created internally and not via the create service script
22
- const internalServices = new Set(['rpc', 'mcp', 'channel', 'userSession'])
23
+ // Internal services that are created internally by the framework (PikkuInteraction)
24
+ // These should not appear in the services maps
25
+ const internalServices = new Set([
26
+ 'rpc',
27
+ 'mcp',
28
+ 'channel',
29
+ 'userSession',
30
+ 'cli',
31
+ 'http',
32
+ 'queue',
33
+ 'scheduledTask',
34
+ ])
23
35
 
24
36
  // Add force-required services that might not be detected from function inspection
25
37
  forceRequiredServices.forEach((service) => {
@@ -28,44 +40,59 @@ export const serializeServicesMap = (
28
40
  }
29
41
  })
30
42
 
31
- // Create a map of services with true for all needed services
32
- const servicesMap = Object.fromEntries(
33
- Array.from(usedServices)
34
- .sort()
35
- .map((service) => [service, true])
36
- )
37
-
38
- // Generate the TypeScript code
39
- const serviceKeys = Object.keys(servicesMap).sort()
40
-
41
43
  // Services that are always required internally by the framework
42
44
  const defaultServices = ['config', 'logger', 'variables', 'schema']
45
+ defaultServices.forEach((service) => usedServices.add(service))
43
46
 
44
- // Combine default services with detected services
45
- const allRequiredServices = [
46
- ...new Set([...defaultServices, ...serviceKeys]),
47
- ].sort()
47
+ // Create singleton services map: all singleton services with true/false based on usage
48
+ const singletonServicesMap: Record<string, boolean> = {}
49
+ allSingletonServices.forEach((service) => {
50
+ singletonServicesMap[service] = usedServices.has(service)
51
+ })
52
+
53
+ // Create session services map: all session services with true/false based on usage
54
+ // Exclude internal framework services (PikkuInteraction)
55
+ const sessionServicesMap: Record<string, boolean> = {}
56
+ allSessionServices.forEach((service) => {
57
+ if (!internalServices.has(service)) {
58
+ sessionServicesMap[service] = usedServices.has(service)
59
+ }
60
+ })
48
61
 
49
- // For RequiredSingletonServices, we need to pick from the actual SingletonServices interface
50
- // This will be resolved at compile time based on what's actually in the SingletonServices interface
51
- // We don't need to hardcode which services are singletons beyond the core framework ones
62
+ // Get all required service names (those marked as true)
63
+ const requiredSingletonServiceNames = Object.keys(singletonServicesMap)
64
+ .filter((key) => singletonServicesMap[key])
65
+ .sort()
66
+ const requiredSessionServiceNames = Object.keys(sessionServicesMap)
67
+ .filter((key) => sessionServicesMap[key])
68
+ .sort()
52
69
 
53
70
  const code = [
54
71
  servicesImport,
55
72
  sessionServicesImport,
56
- "import type { PikkuInteraction } from '@pikku/core'",
57
73
  '',
58
- 'export const singletonServices = {',
59
- ...Object.keys(servicesMap).map((service) => ` '${service}': true,`),
74
+ '// Singleton services map: true if required, false if available but unused',
75
+ 'export const requiredSingletonServices = {',
76
+ ...Object.keys(singletonServicesMap)
77
+ .sort()
78
+ .map((service) => ` '${service}': ${singletonServicesMap[service]},`),
79
+ '} as const',
80
+ '',
81
+ '// Session services map: true if required, false if available but unused',
82
+ 'export const requiredSessionServices = {',
83
+ ...Object.keys(sessionServicesMap)
84
+ .sort()
85
+ .map((service) => ` '${service}': ${sessionServicesMap[service]},`),
60
86
  '} as const',
61
87
  '',
62
- '// Singleton services (created once at startup)',
63
- '// Only includes services that are both required and available in SingletonServices',
64
- `export type RequiredSingletonServices = Pick<SingletonServices, Extract<keyof SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>> & Partial<Omit<SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>>`,
88
+ '// Type exports',
89
+ requiredSingletonServiceNames.length > 0
90
+ ? `export type RequiredSingletonServices = Pick<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
91
+ : 'export type RequiredSingletonServices = Partial<SingletonServices>',
65
92
  '',
66
- '// Session services (created per request, can access singleton services)',
67
- '// Omits singleton services and PikkuInteraction (mcp, rpc, http, channel)',
68
- `export type RequiredSessionServices = Omit<Services, keyof SingletonServices | keyof PikkuInteraction>`,
93
+ requiredSessionServiceNames.length > 0
94
+ ? `export type RequiredSessionServices = Pick<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
95
+ : 'export type RequiredSessionServices = Partial<Services>',
69
96
  '',
70
97
  ].join('\n')
71
98
 
@@ -95,6 +122,8 @@ export const pikkuServices: any = pikkuSessionlessFunc<void, void>({
95
122
  const sessionServicesImport = `import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(config.typesDeclarationFile, sessionServicesType.typePath, config.packageMappings)}'`
96
123
 
97
124
  const servicesCode = serializeServicesMap(
125
+ visitState.serviceAggregation.allSingletonServices,
126
+ visitState.serviceAggregation.allSessionServices,
98
127
  visitState.serviceAggregation.requiredServices,
99
128
  config.forceRequiredServices,
100
129
  servicesImport,
@@ -21,7 +21,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
21
21
  visitState.functions.typesMap,
22
22
  visitState.functions.meta,
23
23
  visitState.http.meta,
24
- config.schemasFromTypes
24
+ config.schemasFromTypes,
25
+ config.schema?.additionalProperties
25
26
  )
26
27
 
27
28
  await saveSchemas(
@@ -30,8 +31,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
30
31
  schemas,
31
32
  visitState.functions.typesMap,
32
33
  visitState.functions.meta,
33
- config.supportsImportAttributes,
34
- config.schemasFromTypes
34
+ config.schemasFromTypes,
35
+ config.schema?.supportsImportAttributes
35
36
  )
36
37
 
37
38
  return true
@@ -27,7 +27,8 @@ export const pikkuOpenAPI: any = pikkuSessionlessFunc<void, void>({
27
27
  functions.typesMap,
28
28
  functions.meta,
29
29
  http.meta,
30
- schemasFromTypes
30
+ schemasFromTypes,
31
+ config.schema?.additionalProperties
31
32
  )
32
33
  const openAPISpec = await generateOpenAPISpec(
33
34
  logger,
@@ -15,17 +15,18 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
15
15
 
16
16
  let filesGenerated = false
17
17
 
18
- // Check if there are any middleware group factories
19
- const hasHTTPFactories = Array.from(
20
- state.http.routeMiddleware.values()
21
- ).some((meta) => meta.exportName && meta.isFactory)
22
- const hasTagFactories = Array.from(
23
- state.middleware.tagMiddleware.values()
24
- ).some((meta) => meta.exportName && meta.isFactory)
25
- const hasFactories = hasHTTPFactories || hasTagFactories
18
+ // Check if there are any middleware groups
19
+ const hasHTTPGroups = state.http.routeMiddleware.size > 0
20
+ const hasTagGroups = state.middleware.tagMiddleware.size > 0
21
+
22
+ if (hasHTTPGroups || hasTagGroups) {
23
+ await writeFileInDir(
24
+ logger,
25
+ config.middlewareGroupsMetaFile,
26
+ serializeMiddlewareGroupsMeta(state)
27
+ )
26
28
 
27
- // Generate middleware imports file if there are factories
28
- if (hasFactories) {
29
+ // Always generate middleware imports file when groups exist (even if empty)
29
30
  await writeFileInDir(
30
31
  logger,
31
32
  middlewareFile,
@@ -36,19 +37,7 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
36
37
  packageMappings
37
38
  )
38
39
  )
39
- filesGenerated = true
40
- }
41
40
 
42
- // Generate middleware groups metadata file
43
- const hasHTTPGroups = state.http.routeMiddleware.size > 0
44
- const hasTagGroups = state.middleware.tagMiddleware.size > 0
45
-
46
- if (hasHTTPGroups || hasTagGroups) {
47
- await writeFileInDir(
48
- logger,
49
- config.middlewareGroupsMetaFile,
50
- serializeMiddlewareGroupsMeta(state)
51
- )
52
41
  filesGenerated = true
53
42
  }
54
43
 
@@ -15,21 +15,24 @@ export interface LogCommandInfoOptions {
15
15
 
16
16
  /**
17
17
  * Middleware to log command execution timing and status
18
- * Replaces the logCommandInfoAndTime wrapper function
18
+ * Uses debug level so it only shows with --verbose flag
19
19
  */
20
20
  export const logCommandInfoAndTime = ({
21
21
  commandStart,
22
22
  commandEnd,
23
23
  }: LogCommandInfoOptions): PikkuMiddleware => {
24
24
  return async ({ logger }, _interaction, next) => {
25
- // Log start
25
+ // Log start (debug level - only shows with --verbose)
26
26
  const start = Date.now()
27
- logger.info(`• ${commandStart}...`)
27
+ logger.debug(`• ${commandStart}...`)
28
28
 
29
29
  // Execute the function
30
30
  await next()
31
31
 
32
- // Log completion
33
- logger.info(`✓ ${commandEnd} in ${Date.now() - start}ms.`)
32
+ // Log completion (debug level - only shows with --verbose)
33
+ logger.debug({
34
+ type: 'success',
35
+ message: `✓ ${commandEnd} in ${Date.now() - start}ms.`,
36
+ })
34
37
  }
35
38
  }
@@ -15,7 +15,7 @@ const BASE_ERROR_URL = 'https://pikku.dev/errors'
15
15
 
16
16
  export class CLILogger implements Logger {
17
17
  private silent: boolean
18
- private level: LogLevel = LogLevel.info
18
+ private level: LogLevel = LogLevel.warn // default to warn level
19
19
  private criticalErrors: string[] = []
20
20
 
21
21
  constructor({
@@ -27,7 +27,7 @@ export class CLILogger implements Logger {
27
27
  }) {
28
28
  this.silent = silent
29
29
  if (logLogo && !silent) {
30
- this.logPikkuLogo()
30
+ this.logLogo()
31
31
  }
32
32
  }
33
33
 
@@ -35,6 +35,14 @@ export class CLILogger implements Logger {
35
35
  this.level = level
36
36
  }
37
37
 
38
+ setSilent(silent: boolean): void {
39
+ this.silent = silent
40
+ }
41
+
42
+ isSilent(): boolean {
43
+ return this.silent
44
+ }
45
+
38
46
  info(message: string | { message: string; type?: string }) {
39
47
  if (this.level > LogLevel.info || this.silent) return
40
48
 
@@ -59,9 +67,16 @@ export class CLILogger implements Logger {
59
67
  console.error(chalk.yellow(message))
60
68
  }
61
69
 
62
- debug(message: string) {
70
+ debug(message: string | { message: string; type?: string }) {
63
71
  if (this.level > LogLevel.debug || this.silent) return
64
- console.log(chalk.gray(message))
72
+
73
+ let c = chalk.gray
74
+ if (typeof message === 'object') {
75
+ if (message.type === 'success') {
76
+ c = chalk.green
77
+ }
78
+ }
79
+ console.log(c(typeof message === 'string' ? message : message.message))
65
80
  }
66
81
 
67
82
  critical(code: ErrorCode, message: string) {
@@ -75,7 +90,7 @@ export class CLILogger implements Logger {
75
90
  return this.criticalErrors.length > 0
76
91
  }
77
92
 
78
- private logPikkuLogo() {
93
+ logLogo() {
79
94
  this.primary(logo)
80
95
  // // When running from dist/, __filename is dist/src/services/cli-logger.service.js
81
96
  // // So we need to go up 3 levels: dist/src/services -> dist/src -> dist -> package.json
package/src/services.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  CreateSingletonServices,
11
11
  } from '@pikku/core'
12
12
  import { pikkuCLIRender } from '@pikku/core/cli'
13
- import { LocalVariablesService } from '@pikku/core/services'
13
+ import { LocalVariablesService, LogLevel } from '@pikku/core/services'
14
14
  import { CLILogger } from './services/cli-logger.service.js'
15
15
  import { getPikkuCLIConfig } from './utils/pikku-cli-config.js'
16
16
  import {
@@ -20,6 +20,7 @@ import {
20
20
  serializeInspectorState,
21
21
  deserializeInspectorState,
22
22
  filterInspectorState,
23
+ getInitialInspectorState,
23
24
  } from '@pikku/inspector'
24
25
  import { glob } from 'tinyglobby'
25
26
  import path from 'path'
@@ -30,7 +31,9 @@ import {
30
31
  } from './services/cli-logger-forwarder.service.js'
31
32
  import { readFile, writeFile } from 'fs/promises'
32
33
 
33
- const logger = new CLILogger({ logLogo: true, silent: false })
34
+ // Logger instance will be configured with log level from CLI flags in createConfig
35
+ // Logo will be displayed conditionally in createConfig based on --silent flag
36
+ const logger = new CLILogger({ logLogo: false, silent: false })
34
37
 
35
38
  /**
36
39
  * Parse a comma-separated string or array into an array of trimmed, non-empty strings
@@ -94,7 +97,7 @@ function parseCLIFilters(data: any): InspectorFilters {
94
97
  export const defaultCLIRenderer = pikkuCLIRender<
95
98
  ForwardedLogMessage,
96
99
  SingletonServices
97
- >((_services, data) => {
100
+ >(({ logger }, data) => {
98
101
  if (data) {
99
102
  logger[data.level]({ message: data.message, type: data.type })
100
103
  }
@@ -105,11 +108,9 @@ export const defaultCLIRenderer = pikkuCLIRender<
105
108
  * This renderer can be used in CLI-over-channel clients
106
109
  */
107
110
  export const clientCLIRenderer = pikkuCLIRender<ForwardedLogMessage>(
108
- (_services, data) => {
111
+ ({ logger }, data) => {
109
112
  if (data) {
110
- // Simple console output without service dependencies
111
- const prefix = data.type ? `[${data.type}] ` : ''
112
- console.log(`${prefix}${data.message}`)
113
+ logger[data.level]?.({ message: data.message, type: data.type } as any)
113
114
  }
114
115
  }
115
116
  )
@@ -118,6 +119,37 @@ export const createConfig: CreateConfig<Config, [PikkuCLIConfig]> = async (
118
119
  _variablesService,
119
120
  data
120
121
  ) => {
122
+ // Determine log level based on CLI flags with precedence:
123
+ // --silent > --loglevel > --verbose > --info > default (warn)
124
+ let logLevel: LogLevel = LogLevel.warn // default
125
+ let isSilent = false
126
+
127
+ if ((data as any).silent) {
128
+ logLevel = LogLevel.critical
129
+ isSilent = true
130
+ } else if ((data as any).loglevel) {
131
+ const levelStr = (data as any).loglevel
132
+ if (LogLevel[levelStr] !== undefined) {
133
+ logLevel = LogLevel[levelStr as keyof typeof LogLevel]
134
+ } else {
135
+ logger.warn(
136
+ `Invalid log level "${levelStr}". Valid levels: trace, debug, info, warn, error, critical. Using default (warn).`
137
+ )
138
+ }
139
+ } else if ((data as any).verbose) {
140
+ logLevel = LogLevel.debug
141
+ } else if ((data as any).info) {
142
+ logLevel = LogLevel.info
143
+ }
144
+
145
+ logger.setLevel(logLevel)
146
+ logger.setSilent(isSilent)
147
+
148
+ // Display logo unless in silent mode
149
+ if (!isSilent) {
150
+ logger.logLogo()
151
+ }
152
+
121
153
  const cliConfig = await getPikkuCLIConfig(logger, data.configFile, [], true)
122
154
 
123
155
  // Load inspector state from file if stateInput is provided
@@ -170,19 +202,62 @@ export const createSingletonServices: CreateSingletonServices<
170
202
  | Omit<InspectorState, 'typesLookup'>
171
203
  | undefined = preloadedInspectorState
172
204
 
173
- const getInspectorState = async (refresh: boolean = false) => {
205
+ const getInspectorState = async (
206
+ refresh: boolean = false,
207
+ setupOnly: boolean = false,
208
+ bootstrapMode: boolean = false
209
+ ) => {
210
+ // In bootstrap mode, return a minimal "zero state" with core types
211
+ // This allows bootstrap to run immediately without inspecting the codebase
212
+ if (bootstrapMode) {
213
+ const corePackagePath = '@pikku/core'
214
+ const initialState = getInitialInspectorState(rootDir)
215
+
216
+ // Populate filesAndMethods with core types from @pikku/core
217
+ initialState.filesAndMethods = {
218
+ userSessionType: {
219
+ file: corePackagePath,
220
+ variable: 'CoreUserSession',
221
+ type: 'CoreUserSession',
222
+ typePath: corePackagePath,
223
+ },
224
+ sessionServicesType: {
225
+ file: corePackagePath,
226
+ variable: 'CoreServices',
227
+ type: 'CoreServices',
228
+ typePath: corePackagePath,
229
+ },
230
+ singletonServicesType: {
231
+ file: corePackagePath,
232
+ variable: 'CoreSingletonServices',
233
+ type: 'CoreSingletonServices',
234
+ typePath: corePackagePath,
235
+ },
236
+ pikkuConfigType: {
237
+ file: corePackagePath,
238
+ variable: 'CoreConfig',
239
+ type: 'CoreConfig',
240
+ typePath: corePackagePath,
241
+ },
242
+ }
243
+
244
+ return initialState
245
+ }
246
+
174
247
  // Get or refresh the unfiltered state
175
248
  if (!unfilteredState || refresh) {
176
249
  // Run inspector WITHOUT filters to get full state
177
250
  const wiringFiles = (
178
251
  await Promise.all(
179
252
  srcDirectories.map((dir) =>
180
- glob(`${path.join(rootDir, dir)}/**/*.ts`)
253
+ glob(`${path.join(rootDir, dir)}/**/*.ts`, {
254
+ ignore: config.ignoreFiles || [],
255
+ })
181
256
  )
182
257
  )
183
258
  ).flat()
184
- unfilteredState = await inspect(logger, wiringFiles, {
185
- // NO filters here - inspector returns full unfiltered state
259
+ unfilteredState = inspect(logger, wiringFiles, {
260
+ setupOnly,
186
261
  types: {
187
262
  configFileType: config.configFile,
188
263
  userSessionType: config.userSessionType,
@@ -0,0 +1,101 @@
1
+ import chalk from 'chalk'
2
+
3
+ export interface CommandSummaryStats {
4
+ httpRoutes?: number
5
+ channels?: number
6
+ functions?: number
7
+ scheduledTasks?: number
8
+ queueWorkers?: number
9
+ mcpEndpoints?: number
10
+ cliCommands?: number
11
+ [key: string]: number | undefined
12
+ }
13
+
14
+ /**
15
+ * Utility class to collect and display command execution summaries
16
+ */
17
+ export class CommandSummary {
18
+ private stats: CommandSummaryStats = {}
19
+ private startTime: number
20
+ private commandName: string
21
+
22
+ constructor(commandName: string) {
23
+ this.commandName = commandName
24
+ this.startTime = Date.now()
25
+ }
26
+
27
+ /**
28
+ * Increment a stat counter
29
+ */
30
+ increment(key: keyof CommandSummaryStats, count: number = 1): void {
31
+ this.stats[key] = (this.stats[key] || 0) + count
32
+ }
33
+
34
+ /**
35
+ * Set a stat value directly
36
+ */
37
+ set(key: keyof CommandSummaryStats, value: number): void {
38
+ this.stats[key] = value
39
+ }
40
+
41
+ /**
42
+ * Get current stats
43
+ */
44
+ getStats(): CommandSummaryStats {
45
+ return { ...this.stats }
46
+ }
47
+
48
+ /**
49
+ * Get elapsed time in milliseconds
50
+ */
51
+ getElapsedTime(): number {
52
+ return Date.now() - this.startTime
53
+ }
54
+
55
+ /**
56
+ * Format the summary for display
57
+ */
58
+ format(): string {
59
+ const elapsed = this.getElapsedTime()
60
+ const lines: string[] = []
61
+
62
+ // Header with timing
63
+ lines.push(
64
+ chalk.green(`\npikku ${this.commandName} (completed in ${elapsed}ms)`)
65
+ )
66
+
67
+ // Stats
68
+ const statLabels: Record<keyof CommandSummaryStats, string> = {
69
+ httpRoutes: 'HTTP routes',
70
+ channels: 'WebSocket channels',
71
+ functions: 'functions',
72
+ scheduledTasks: 'scheduled tasks',
73
+ queueWorkers: 'queue workers',
74
+ mcpEndpoints: 'MCP endpoints',
75
+ cliCommands: 'CLI commands',
76
+ }
77
+
78
+ for (const [key, label] of Object.entries(statLabels)) {
79
+ const value = this.stats[key]
80
+ if (value !== undefined && value > 0) {
81
+ lines.push(chalk.gray(` • ${value} ${label}`))
82
+ }
83
+ }
84
+
85
+ // If no stats, show a simple completion message
86
+ if (Object.values(this.stats).every((v) => !v || v === 0)) {
87
+ return chalk.green(
88
+ `\npikku ${this.commandName} (completed in ${elapsed}ms)\n`
89
+ )
90
+ }
91
+
92
+ return lines.join('\n') + '\n'
93
+ }
94
+
95
+ /**
96
+ * Check if there are any stats to display
97
+ */
98
+ hasStats(): boolean {
99
+ return Object.values(this.stats).some((v) => v && v > 0)
100
+ }
101
+ }
@@ -51,7 +51,7 @@ export const writeFileInDir = async (
51
51
  await writeFile(path, content, 'utf-8')
52
52
 
53
53
  if (logWrite) {
54
- logger.info({ message: `✓ File written to ${path}`, type: 'success' })
54
+ logger.debug({ message: `✓ File written to ${path}`, type: 'success' })
55
55
  }
56
56
  }
57
57
  }
@@ -66,7 +66,7 @@ export const removeFileInDir = async (
66
66
  await rm(path, { force: true })
67
67
 
68
68
  if (logRemove) {
69
- logger.info({ message: `✓ File removed at ${path}`, type: 'success' })
69
+ logger.debug({ message: `✓ File removed at ${path}`, type: 'success' })
70
70
  }
71
71
  }
72
72
  }
@@ -64,6 +64,21 @@ const _getPikkuCLIConfig = async (
64
64
  ...extendedConfig.packageMappings,
65
65
  ...config.packageMappings,
66
66
  },
67
+ ignoreFiles: config.ignoreFiles ??
68
+ extendedConfig.ignoreFiles ?? [
69
+ '**/*.gen.ts',
70
+ '**/*.test.ts',
71
+ '**/*.spec.ts',
72
+ '**/node_modules/**',
73
+ '**/.pikku/**',
74
+ '**/dist/**',
75
+ ],
76
+ schema: {
77
+ additionalProperties: false,
78
+ supportsImportAttributes: false,
79
+ ...extendedConfig.schema,
80
+ ...config.schema,
81
+ },
67
82
  }
68
83
  } else {
69
84
  result = {
@@ -73,6 +88,19 @@ const _getPikkuCLIConfig = async (
73
88
  rootDir: config.rootDir
74
89
  ? resolve(configDir, config.rootDir)
75
90
  : configDir,
91
+ ignoreFiles: config.ignoreFiles ?? [
92
+ '**/*.gen.ts',
93
+ '**/*.test.ts',
94
+ '**/*.spec.ts',
95
+ '**/node_modules/**',
96
+ '**/.pikku/**',
97
+ '**/dist/**',
98
+ ],
99
+ schema: {
100
+ additionalProperties: false,
101
+ supportsImportAttributes: false,
102
+ ...config.schema,
103
+ },
76
104
  }
77
105
  }
78
106
 
@@ -12,7 +12,8 @@ export async function generateSchemas(
12
12
  typesMap: TypesMap,
13
13
  functionMeta: FunctionsMeta,
14
14
  httpWiringsMeta: HTTPWiringsMeta,
15
- additionalTypes?: string[]
15
+ additionalTypes?: string[],
16
+ additionalProperties: boolean = false
16
17
  ): Promise<Record<string, JSONValue>> {
17
18
  const schemasSet = new Set(typesMap.customTypes.keys())
18
19
  for (const { inputs, outputs } of Object.values(functionMeta)) {
@@ -56,7 +57,7 @@ export async function generateSchemas(
56
57
  sortProps: true,
57
58
  strictTuples: false,
58
59
  encodeRefs: false,
59
- additionalProperties: false,
60
+ additionalProperties,
60
61
  })
61
62
  const schemas: Record<string, JSONValue> = {}
62
63
 
@@ -86,8 +87,8 @@ export async function saveSchemas(
86
87
  schemas: Record<string, JSONValue>,
87
88
  typesMap: TypesMap,
88
89
  functionsMeta: FunctionsMeta,
89
- supportsImportAttributes: boolean,
90
- additionalTypes?: string[]
90
+ additionalTypes?: string[],
91
+ supportsImportAttributes: boolean = false
91
92
  ) {
92
93
  await writeFileInDir(
93
94
  logger,
@@ -15,7 +15,11 @@ export interface Config extends CoreConfig<PikkuCLIConfig> {
15
15
 
16
16
  export interface SingletonServices extends CoreSingletonServices<Config> {
17
17
  logger: CLILogger
18
- getInspectorState: (refresh?: boolean = false) => Promise<InspectorState>
18
+ getInspectorState: (
19
+ refresh?: boolean,
20
+ setupOnly?: boolean,
21
+ bootstrapMode?: boolean
22
+ ) => Promise<InspectorState>
19
23
  }
20
24
 
21
25
  export interface Services extends CoreServices<SingletonServices, {}> {}