@ai-content-space/loopx 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +343 -56
- package/README.zh-CN.md +392 -0
- package/package.json +4 -1
- package/plugins/loopx/.codex-plugin/plugin.json +1 -1
- package/plugins/loopx/scripts/plugin-install.test.mjs +1 -0
- package/plugins/loopx/skills/archive/SKILL.md +39 -0
- package/plugins/loopx/skills/build/SKILL.md +111 -9
- package/plugins/loopx/skills/clarify/SKILL.md +121 -1
- package/plugins/loopx/skills/debug/SKILL.md +296 -0
- package/plugins/loopx/skills/debug/condition-based-waiting.md +115 -0
- package/plugins/loopx/skills/debug/defense-in-depth.md +122 -0
- package/plugins/loopx/skills/debug/find-polluter.sh +63 -0
- package/plugins/loopx/skills/debug/root-cause-tracing.md +169 -0
- package/plugins/loopx/skills/go-style/SKILL.md +71 -0
- package/plugins/loopx/skills/kratos/SKILL.md +74 -0
- package/plugins/loopx/skills/kratos/references/advanced-features.md +314 -0
- package/plugins/loopx/skills/kratos/references/architecture.md +488 -0
- package/plugins/loopx/skills/kratos/references/configuration.md +399 -0
- package/plugins/loopx/skills/kratos/references/http-customization.md +512 -0
- package/plugins/loopx/skills/kratos/references/middleware-logging.md +400 -0
- package/plugins/loopx/skills/kratos/references/proto-api-design.md +432 -0
- package/plugins/loopx/skills/kratos/references/security-auth.md +411 -0
- package/plugins/loopx/skills/kratos/references/troubleshooting.md +385 -0
- package/plugins/loopx/skills/plan/SKILL.md +22 -2
- package/plugins/loopx/skills/review/SKILL.md +98 -1
- package/plugins/loopx/skills/tdd/SKILL.md +371 -0
- package/plugins/loopx/skills/tdd/testing-anti-patterns.md +299 -0
- package/plugins/loopx/skills/verify/SKILL.md +139 -0
- package/scripts/codex-stop-hook.mjs +71 -0
- package/scripts/codex-workflow-hook.mjs +153 -0
- package/skills/archive/SKILL.md +39 -0
- package/skills/build/SKILL.md +111 -9
- package/skills/clarify/SKILL.md +121 -1
- package/skills/debug/SKILL.md +296 -0
- package/skills/debug/condition-based-waiting.md +115 -0
- package/skills/debug/defense-in-depth.md +122 -0
- package/skills/debug/find-polluter.sh +63 -0
- package/skills/debug/root-cause-tracing.md +169 -0
- package/skills/go-style/SKILL.md +71 -0
- package/skills/kratos/SKILL.md +74 -0
- package/skills/kratos/references/advanced-features.md +314 -0
- package/skills/kratos/references/architecture.md +488 -0
- package/skills/kratos/references/configuration.md +399 -0
- package/skills/kratos/references/http-customization.md +512 -0
- package/skills/kratos/references/middleware-logging.md +400 -0
- package/skills/kratos/references/proto-api-design.md +432 -0
- package/skills/kratos/references/security-auth.md +411 -0
- package/skills/kratos/references/troubleshooting.md +385 -0
- package/skills/plan/SKILL.md +18 -2
- package/skills/review/SKILL.md +98 -1
- package/skills/tdd/SKILL.md +371 -0
- package/skills/tdd/testing-anti-patterns.md +299 -0
- package/skills/verify/SKILL.md +139 -0
- package/src/build-runtime.mjs +303 -26
- package/src/build-stop-gate.mjs +94 -0
- package/src/cli.mjs +47 -5
- package/src/codex-exec-runtime.mjs +105 -5
- package/src/context-manifest.mjs +172 -0
- package/src/install-discovery.mjs +352 -5
- package/src/next-skill.mjs +57 -5
- package/src/plan-runtime.mjs +79 -122
- package/src/review-runtime.mjs +378 -0
- package/src/runtime-maintenance.mjs +428 -14
- package/src/template-governance.mjs +223 -0
- package/src/workflow.mjs +1941 -117
- package/src/workspace-context.mjs +166 -0
- package/src/workspace-memory.mjs +69 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Guide for Kratos configuration management and startup hooks.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- Setting up Bootstrap configuration
|
|
8
|
+
- Validating config before startup
|
|
9
|
+
- Adding startup/shutdown hooks
|
|
10
|
+
- Extending config format support
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Bootstrap Configuration Pattern
|
|
15
|
+
|
|
16
|
+
Use protobuf to define configuration with validation:
|
|
17
|
+
|
|
18
|
+
### Proto Definition
|
|
19
|
+
|
|
20
|
+
```protobuf
|
|
21
|
+
// internal/conf/conf.proto
|
|
22
|
+
syntax = "proto3";
|
|
23
|
+
package conf;
|
|
24
|
+
|
|
25
|
+
import "buf/validate/validate.proto";
|
|
26
|
+
import "google/protobuf/duration.proto";
|
|
27
|
+
|
|
28
|
+
option go_package = "github.com/myorg/myproject/internal/conf;conf";
|
|
29
|
+
|
|
30
|
+
message Bootstrap {
|
|
31
|
+
Server server = 1;
|
|
32
|
+
Data data = 2;
|
|
33
|
+
Log log = 3;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
message Server {
|
|
37
|
+
message HTTP {
|
|
38
|
+
string network = 1;
|
|
39
|
+
string addr = 2;
|
|
40
|
+
google.protobuf.Duration timeout = 3 [
|
|
41
|
+
(buf.validate.field).required = true,
|
|
42
|
+
(buf.validate.field).duration = {
|
|
43
|
+
gt: {seconds: 1}
|
|
44
|
+
lte: {seconds: 600}
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
message GRPC {
|
|
49
|
+
string network = 1;
|
|
50
|
+
string addr = 2;
|
|
51
|
+
google.protobuf.Duration timeout = 3 [
|
|
52
|
+
(buf.validate.field).required = true,
|
|
53
|
+
(buf.validate.field).duration = {
|
|
54
|
+
gt: {seconds: 1}
|
|
55
|
+
lte: {seconds: 600}
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
HTTP http = 1;
|
|
60
|
+
GRPC grpc = 2;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
message Data {
|
|
64
|
+
message Database {
|
|
65
|
+
string driver = 1;
|
|
66
|
+
string source = 2;
|
|
67
|
+
}
|
|
68
|
+
message Redis {
|
|
69
|
+
string network = 1;
|
|
70
|
+
string addr = 2;
|
|
71
|
+
google.protobuf.Duration read_timeout = 3;
|
|
72
|
+
google.protobuf.Duration write_timeout = 4;
|
|
73
|
+
}
|
|
74
|
+
Database database = 1;
|
|
75
|
+
Redis redis = 2;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
enum LogLevel {
|
|
79
|
+
Debug = 0;
|
|
80
|
+
Info = 1;
|
|
81
|
+
Warn = 2;
|
|
82
|
+
Error = 3;
|
|
83
|
+
Fatal = 4;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message Log {
|
|
87
|
+
string log_path = 1;
|
|
88
|
+
LogLevel log_level = 2 [
|
|
89
|
+
(buf.validate.field).enum = {
|
|
90
|
+
defined_only: true
|
|
91
|
+
in: [0, 1, 2, 3, 4]
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
int32 max_size = 3;
|
|
95
|
+
int32 max_keep_days = 4;
|
|
96
|
+
int32 max_keep_files = 5;
|
|
97
|
+
bool compress = 6;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### YAML Config File
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
# configs/config.yaml
|
|
105
|
+
server:
|
|
106
|
+
http:
|
|
107
|
+
addr: 0.0.0.0:8000
|
|
108
|
+
timeout: 10s
|
|
109
|
+
grpc:
|
|
110
|
+
addr: 0.0.0.0:9000
|
|
111
|
+
timeout: 10s
|
|
112
|
+
|
|
113
|
+
data:
|
|
114
|
+
database:
|
|
115
|
+
driver: mysql
|
|
116
|
+
source: user:pass@tcp(localhost:3306)/db?charset=utf8mb4
|
|
117
|
+
redis:
|
|
118
|
+
addr: localhost:6379
|
|
119
|
+
read_timeout: 0.5s
|
|
120
|
+
write_timeout: 0.5s
|
|
121
|
+
|
|
122
|
+
log:
|
|
123
|
+
log_path: /var/log/app.log
|
|
124
|
+
log_level: Info
|
|
125
|
+
max_size: 100
|
|
126
|
+
max_keep_days: 7
|
|
127
|
+
max_keep_files: 10
|
|
128
|
+
compress: true
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Startup Validation
|
|
134
|
+
|
|
135
|
+
Validate configuration before application starts:
|
|
136
|
+
|
|
137
|
+
```go
|
|
138
|
+
// cmd/server/main.go
|
|
139
|
+
package main
|
|
140
|
+
|
|
141
|
+
import (
|
|
142
|
+
"buf.build/go/protovalidate"
|
|
143
|
+
"github.com/go-kratos/kratos/v2/config"
|
|
144
|
+
"github.com/go-kratos/kratos/v2/config/file"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
func provideConfigs(flagConf string) *conf.Bootstrap {
|
|
148
|
+
c := config.New(
|
|
149
|
+
config.WithSource(file.NewSource(flagConf)),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
var bc conf.Bootstrap
|
|
153
|
+
if err := c.Scan(&bc); err != nil {
|
|
154
|
+
panic(err) // Startup failure - program cannot run without config
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Create validator
|
|
158
|
+
validator, err := protovalidate.New()
|
|
159
|
+
if err != nil {
|
|
160
|
+
panic(err) // Startup failure - validator is required
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Validate config before boot
|
|
164
|
+
if err := validator.Validate(&bc); err != nil {
|
|
165
|
+
panic(err) // Startup failure - invalid config is fatal
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return &bc
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Why panic is acceptable here:** This is startup code. If config is invalid, the application cannot run. Panics at startup are appropriate because there's no graceful recovery possible. For library/utility functions, prefer returning errors instead.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Why this matters:** Invalid config is caught at startup, not during operation. Proto-based validation keeps rules with data definition.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Multi-Format Config Extension
|
|
180
|
+
|
|
181
|
+
Kratos supports json, yaml, proto, xml by default. Add other formats:
|
|
182
|
+
|
|
183
|
+
### TOML Example
|
|
184
|
+
|
|
185
|
+
```go
|
|
186
|
+
// internal/pkg/tomlencoding/toml.go
|
|
187
|
+
package tomlencoding
|
|
188
|
+
|
|
189
|
+
import (
|
|
190
|
+
"github.com/BurntSushi/toml"
|
|
191
|
+
"github.com/go-kratos/kratos/v2/encoding"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
const Name = "toml"
|
|
195
|
+
|
|
196
|
+
func init() {
|
|
197
|
+
encoding.RegisterCodec(codec{})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type codec struct{}
|
|
201
|
+
|
|
202
|
+
func (codec) Marshal(v interface{}) ([]byte, error) {
|
|
203
|
+
return toml.Marshal(v)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
func (codec) Unmarshal(data []byte, v interface{}) error {
|
|
207
|
+
return toml.Unmarshal(data, v)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func (codec) Name() string {
|
|
211
|
+
return Name
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Usage
|
|
216
|
+
|
|
217
|
+
```go
|
|
218
|
+
// cmd/server/main.go
|
|
219
|
+
import _ "github.com/myorg/myproject/internal/pkg/tomlencoding"
|
|
220
|
+
|
|
221
|
+
// Now .toml files are supported
|
|
222
|
+
c := config.New(
|
|
223
|
+
config.WithSource(file.NewSource("configs")),
|
|
224
|
+
)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Resolver for Key Normalization
|
|
228
|
+
|
|
229
|
+
Different formats use different key conventions (camelCase vs snake_case):
|
|
230
|
+
|
|
231
|
+
```go
|
|
232
|
+
func resolver(input map[string]interface{}) error {
|
|
233
|
+
// Normalize keys: MyTitle → my_title
|
|
234
|
+
for key, value := range input {
|
|
235
|
+
normalizedKey := toSnakeCase(key)
|
|
236
|
+
if normalizedKey != key {
|
|
237
|
+
input[normalizedKey] = value
|
|
238
|
+
delete(input, key)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return nil
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
c := config.New(
|
|
245
|
+
config.WithSource(file.NewSource("configs")),
|
|
246
|
+
config.WithResolver(resolver),
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Startup Hooks
|
|
253
|
+
|
|
254
|
+
Kratos v2.5.3+ provides lifecycle hooks:
|
|
255
|
+
|
|
256
|
+
### Hook Types
|
|
257
|
+
|
|
258
|
+
```go
|
|
259
|
+
// BeforeStart: Runs before server starts
|
|
260
|
+
// BeforeStop: Runs before server stops
|
|
261
|
+
// AfterStart: Runs after server starts
|
|
262
|
+
// AfterStop: Runs after server stops
|
|
263
|
+
|
|
264
|
+
app := kratos.New(
|
|
265
|
+
kratos.Name("my-app"),
|
|
266
|
+
kratos.Version("v1.0.0"),
|
|
267
|
+
|
|
268
|
+
kratos.BeforeStart(func(ctx context.Context) error {
|
|
269
|
+
log.Info("Initializing...")
|
|
270
|
+
// Initialize caches, warm up data
|
|
271
|
+
return nil
|
|
272
|
+
}),
|
|
273
|
+
|
|
274
|
+
kratos.AfterStart(func(ctx context.Context) error {
|
|
275
|
+
log.Info("Server started")
|
|
276
|
+
// Register with external systems
|
|
277
|
+
return nil
|
|
278
|
+
}),
|
|
279
|
+
|
|
280
|
+
kratos.BeforeStop(func(ctx context.Context) error {
|
|
281
|
+
log.Info("Shutting down...")
|
|
282
|
+
// Drain connections, save state
|
|
283
|
+
return nil
|
|
284
|
+
}),
|
|
285
|
+
|
|
286
|
+
kratos.AfterStop(func(ctx context.Context) error {
|
|
287
|
+
log.Info("Server stopped")
|
|
288
|
+
// Cleanup
|
|
289
|
+
return nil
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Use Cases
|
|
295
|
+
|
|
296
|
+
| Hook | Common Uses |
|
|
297
|
+
|------|-------------|
|
|
298
|
+
| BeforeStart | Database warmup, cache pre-fill, config validation |
|
|
299
|
+
| AfterStart | Health check registration, service discovery |
|
|
300
|
+
| BeforeStop | Connection drain, state persistence |
|
|
301
|
+
| AfterStop | Cleanup, metrics export |
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Task Dependency Handling
|
|
306
|
+
|
|
307
|
+
For initialization tasks with dependencies:
|
|
308
|
+
|
|
309
|
+
### Processor Interface
|
|
310
|
+
|
|
311
|
+
```go
|
|
312
|
+
type processor interface {
|
|
313
|
+
// IsInit: Check if initialization needed
|
|
314
|
+
IsInit() bool
|
|
315
|
+
|
|
316
|
+
// Apply: Execute initialization
|
|
317
|
+
Apply(seeds []interface{}) error
|
|
318
|
+
|
|
319
|
+
// LoadSeeds: Get initialization data
|
|
320
|
+
LoadSeeds() ([]interface{}, error)
|
|
321
|
+
|
|
322
|
+
// GetJobID: Task sequence number
|
|
323
|
+
GetJobID() int
|
|
324
|
+
|
|
325
|
+
// GetDepends: Dependencies (job IDs)
|
|
326
|
+
GetDepends() []int
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Example: Database Initialization
|
|
331
|
+
|
|
332
|
+
```go
|
|
333
|
+
type DatabaseInit struct {
|
|
334
|
+
jobID int
|
|
335
|
+
depends []int
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
func (d *DatabaseInit) GetJobID() int { return d.jobID }
|
|
339
|
+
func (d *DatabaseInit) GetDepends() []int { return d.depends }
|
|
340
|
+
func (d *DatabaseInit) IsInit() bool {
|
|
341
|
+
// Check if tables exist
|
|
342
|
+
return !d.tablesExist()
|
|
343
|
+
}
|
|
344
|
+
func (d *DatabaseInit) LoadSeeds() ([]interface{}, error) {
|
|
345
|
+
// Load seed data from files
|
|
346
|
+
return d.loadSeedData(), nil
|
|
347
|
+
}
|
|
348
|
+
func (d *DatabaseInit) Apply(seeds []interface{}) error {
|
|
349
|
+
// Create tables and insert seeds
|
|
350
|
+
return d.createTablesAndInsert(seeds)
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Task Ordering
|
|
355
|
+
|
|
356
|
+
Sort processors by dependencies:
|
|
357
|
+
|
|
358
|
+
```go
|
|
359
|
+
func runInitialization(processors []processor) error {
|
|
360
|
+
// Sort by dependencies (topological order)
|
|
361
|
+
sort.Slice(processors, func(i, j int) bool {
|
|
362
|
+
return processors[i].GetJobID() < processors[j].GetJobID()
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
for _, p := range processors {
|
|
366
|
+
if p.IsInit() {
|
|
367
|
+
seeds, err := p.LoadSeeds()
|
|
368
|
+
if err != nil {
|
|
369
|
+
return err
|
|
370
|
+
}
|
|
371
|
+
if err := p.Apply(seeds); err != nil {
|
|
372
|
+
return err
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return nil
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Environment Variable Integration
|
|
383
|
+
|
|
384
|
+
Combine file config with environment overrides:
|
|
385
|
+
|
|
386
|
+
```go
|
|
387
|
+
c := config.New(
|
|
388
|
+
config.WithSource(
|
|
389
|
+
file.NewSource("configs"),
|
|
390
|
+
// Add env source
|
|
391
|
+
env.NewSource("APP_"), // APP_SERVER_HTTP_ADDR → server.http.addr
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Environment variables override file values:
|
|
397
|
+
```bash
|
|
398
|
+
APP_SERVER_HTTP_ADDR=0.0.0.0:9000 # Overrides config.yaml
|
|
399
|
+
```
|