@iacmp/cli 1.1.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.
- package/LICENSE +21 -0
- package/bin/run.js +73 -0
- package/dist/chat.js +4068 -0
- package/dist/commands/ai.js +3985 -0
- package/dist/commands/audit-all.js +1039 -0
- package/dist/commands/audit-dr.js +351 -0
- package/dist/commands/audit-ha.js +375 -0
- package/dist/commands/audit-improvements.js +373 -0
- package/dist/commands/audit-security.js +351 -0
- package/dist/commands/dashboard.js +417 -0
- package/dist/commands/deploy.js +188 -0
- package/dist/commands/destroy.js +194 -0
- package/dist/commands/diagram.js +896 -0
- package/dist/commands/diff.js +4420 -0
- package/dist/commands/doctor.js +191 -0
- package/dist/commands/init.js +507 -0
- package/dist/commands/ls.js +75 -0
- package/dist/commands/registry.js +170 -0
- package/dist/commands/registry.json +29 -0
- package/dist/commands/synth.js +4458 -0
- package/dist/commands/watch.js +133 -0
- package/dist/index.js +30 -0
- package/oclif.manifest.json +727 -0
- package/package.json +95 -0
package/dist/chat.js
ADDED
|
@@ -0,0 +1,4068 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
|
|
29
|
+
// ../ai/dist/prompts/system-prompt.js
|
|
30
|
+
var require_system_prompt = __commonJS({
|
|
31
|
+
"../ai/dist/prompts/system-prompt.js"(exports2) {
|
|
32
|
+
"use strict";
|
|
33
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
34
|
+
exports2.SYSTEM_PROMPT = exports2.SYSTEM_PROMPT_TEMPLATE = void 0;
|
|
35
|
+
exports2.buildSystemPrompt = buildSystemPrompt2;
|
|
36
|
+
exports2.SYSTEM_PROMPT_TEMPLATE = `Voc\xEA \xE9 um especialista em infraestrutura como c\xF3digo (IaC) integrado ao iacmp CLI.
|
|
37
|
+
Seu papel \xE9 gerar stacks de infraestrutura em TypeScript usando os constructs do @iacmp/core. Prefira sempre os constructs tipados quando existirem. Quando o servi\xE7o pedido pelo usu\xE1rio N\xC3O tiver construct tipado no cat\xE1logo abaixo, N\xC3O diga apenas "n\xE3o existe" \u2014 use o \`Custom.Resource\` (ver se\xE7\xE3o dedicada mais abaixo) para gerar o recurso nativo real do provider (CloudFormation/ARM/Deployment Manager/Terraform) com sua pr\xF3pria sintaxe, formatado nesse construct de escape hatch. Voc\xEA conhece a sintaxe nativa de cada formato; use esse conhecimento em vez de bloquear o pedido do usu\xE1rio.
|
|
38
|
+
|
|
39
|
+
## REGRA ABSOLUTA \u2014 imports
|
|
40
|
+
NUNCA use aws-cdk-lib, iacmp-core, constructs, @aws-cdk ou qualquer outro pacote externo.
|
|
41
|
+
O \xDANICO import permitido \xE9: import { Stack, ... } from '@iacmp/core';
|
|
42
|
+
|
|
43
|
+
## API completa do @iacmp/core
|
|
44
|
+
|
|
45
|
+
### Stack
|
|
46
|
+
\`\`\`typescript
|
|
47
|
+
import { Stack } from '@iacmp/core';
|
|
48
|
+
const stack = new Stack('nome-da-stack');
|
|
49
|
+
export default stack;
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
## COMPUTE
|
|
54
|
+
|
|
55
|
+
### Compute.Instance \u2014 EC2, Azure VM, Compute Engine
|
|
56
|
+
\`\`\`typescript
|
|
57
|
+
import { Stack, Compute } from '@iacmp/core';
|
|
58
|
+
const stack = new Stack('nome');
|
|
59
|
+
new Compute.Instance(stack, 'LogicalId', {
|
|
60
|
+
instanceType: 'small' | 'medium' | 'large',
|
|
61
|
+
image: string, // ver valores suportados abaixo
|
|
62
|
+
region?: string,
|
|
63
|
+
});
|
|
64
|
+
export default stack;
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
### Compute.AutoScaling \u2014 Auto Scaling Group / VMSS
|
|
68
|
+
\`\`\`typescript
|
|
69
|
+
new Compute.AutoScaling(stack, 'LogicalId', {
|
|
70
|
+
instanceType: 'small' | 'medium' | 'large',
|
|
71
|
+
image: string, // ver valores suportados abaixo
|
|
72
|
+
minCapacity: number, // obrigat\xF3rio
|
|
73
|
+
maxCapacity: number, // obrigat\xF3rio
|
|
74
|
+
desiredCapacity?: number,
|
|
75
|
+
targetCpuUtilization?: number, // ex: 70 para 70%
|
|
76
|
+
subnetIds?: string[],
|
|
77
|
+
});
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
**Valores de \`image\` suportados (atalhos autom\xE1ticos por provider):**
|
|
81
|
+
| image | AWS | Azure | GCP |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| \`ubuntu\` / \`ubuntu-22.04\` | SSM \u2192 Ubuntu 22.04 AMI | Canonical UbuntuServer 22_04-lts | ubuntu-os-cloud/ubuntu-2204-lts |
|
|
84
|
+
| \`ubuntu-20.04\` | SSM \u2192 Ubuntu 20.04 AMI | Canonical UbuntuServer 20_04-lts | ubuntu-os-cloud/ubuntu-2004-lts |
|
|
85
|
+
| \`amazon-linux-2\` | SSM \u2192 Amazon Linux 2 AMI | \u2014 | \u2014 |
|
|
86
|
+
| \`amazon-linux-2023\` | SSM \u2192 AL2023 AMI | \u2014 | \u2014 |
|
|
87
|
+
| \`windows-2022\` | SSM \u2192 Windows Server 2022 AMI | MicrosoftWindowsServer 2022-Datacenter | windows-cloud/windows-2022 |
|
|
88
|
+
| \`windows-2019\` | SSM \u2192 Windows Server 2019 AMI | MicrosoftWindowsServer 2019-Datacenter | windows-cloud/windows-2019 |
|
|
89
|
+
| \`windows-2016\` | SSM \u2192 Windows Server 2016 AMI | MicrosoftWindowsServer 2016-Datacenter | windows-cloud/windows-2016 |
|
|
90
|
+
|
|
91
|
+
Para Windows, o Azure configura automaticamente \`adminUsername: 'adminuser'\` e \`windowsConfiguration\` em vez de \`linuxConfiguration\`.
|
|
92
|
+
|
|
93
|
+
### Compute.Container \u2014 ECS/Fargate, ACI, Cloud Run
|
|
94
|
+
\`\`\`typescript
|
|
95
|
+
new Compute.Container(stack, 'LogicalId', {
|
|
96
|
+
image: string, // obrigat\xF3rio: ex: 'nginx:latest'
|
|
97
|
+
cpu?: number, // unidades de CPU (padr\xE3o: 256)
|
|
98
|
+
memory?: number, // MB (padr\xE3o: 512)
|
|
99
|
+
port?: number,
|
|
100
|
+
desiredCount?: number,
|
|
101
|
+
publicIp?: boolean,
|
|
102
|
+
environment?: Record<string, string>,
|
|
103
|
+
});
|
|
104
|
+
\`\`\`
|
|
105
|
+
|
|
106
|
+
### Compute.Kubernetes \u2014 EKS, AKS, GKE
|
|
107
|
+
\`\`\`typescript
|
|
108
|
+
new Compute.Kubernetes(stack, 'LogicalId', {
|
|
109
|
+
version?: string, // ex: '1.29'
|
|
110
|
+
nodeInstanceType?: 'small' | 'medium' | 'large',
|
|
111
|
+
minNodes?: number,
|
|
112
|
+
maxNodes?: number,
|
|
113
|
+
desiredNodes?: number,
|
|
114
|
+
privateCluster?: boolean,
|
|
115
|
+
});
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
## STORAGE
|
|
120
|
+
|
|
121
|
+
### Storage.Bucket \u2014 S3, Blob Storage, Cloud Storage
|
|
122
|
+
\`\`\`typescript
|
|
123
|
+
import { Stack, Storage } from '@iacmp/core';
|
|
124
|
+
const stack = new Stack('nome');
|
|
125
|
+
new Storage.Bucket(stack, 'LogicalId', {
|
|
126
|
+
versioning?: boolean,
|
|
127
|
+
publicAccess?: boolean,
|
|
128
|
+
lifecycleRules?: [
|
|
129
|
+
{
|
|
130
|
+
prefix?: string,
|
|
131
|
+
expireAfterDays?: number,
|
|
132
|
+
transitionToGlacierDays?: number,
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
export default stack;
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
### Storage.FileSystem \u2014 EFS, Azure Files, Filestore
|
|
140
|
+
\`\`\`typescript
|
|
141
|
+
new Storage.FileSystem(stack, 'LogicalId', {
|
|
142
|
+
performanceMode?: 'generalPurpose' | 'maxIO',
|
|
143
|
+
throughputMode?: 'bursting' | 'provisioned',
|
|
144
|
+
encrypted?: boolean,
|
|
145
|
+
accessPoints?: [
|
|
146
|
+
{ name: string, path: string, uid?: number, gid?: number }
|
|
147
|
+
],
|
|
148
|
+
});
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
### Storage.Archive \u2014 S3 Glacier Deep Archive, Cool Blob, Coldline
|
|
152
|
+
\`\`\`typescript
|
|
153
|
+
new Storage.Archive(stack, 'LogicalId', {
|
|
154
|
+
retentionDays?: number,
|
|
155
|
+
lockEnabled?: boolean,
|
|
156
|
+
});
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
## NETWORK
|
|
161
|
+
|
|
162
|
+
### Network.VPC \u2014 VPC, VNet
|
|
163
|
+
\`\`\`typescript
|
|
164
|
+
import { Stack, Network } from '@iacmp/core';
|
|
165
|
+
const stack = new Stack('nome');
|
|
166
|
+
new Network.VPC(stack, 'LogicalId', {
|
|
167
|
+
cidr?: string, // ex: '10.0.0.0/16'
|
|
168
|
+
maxAzs?: number,
|
|
169
|
+
});
|
|
170
|
+
export default stack;
|
|
171
|
+
\`\`\`
|
|
172
|
+
|
|
173
|
+
### Network.Subnet \u2014 Subnet expl\xEDcita
|
|
174
|
+
\`\`\`typescript
|
|
175
|
+
new Network.Subnet(stack, 'LogicalId', {
|
|
176
|
+
vpcId: string, // obrigat\xF3rio
|
|
177
|
+
cidr: string, // obrigat\xF3rio ex '10.0.1.0/24'
|
|
178
|
+
availabilityZone?: string,
|
|
179
|
+
public?: boolean,
|
|
180
|
+
});
|
|
181
|
+
\`\`\`
|
|
182
|
+
|
|
183
|
+
### Network.SecurityGroup \u2014 Security Group / NSG / Firewall Rules
|
|
184
|
+
\`\`\`typescript
|
|
185
|
+
new Network.SecurityGroup(stack, 'LogicalId', {
|
|
186
|
+
vpcId: string, // obrigat\xF3rio
|
|
187
|
+
description?: string,
|
|
188
|
+
ingressRules?: [
|
|
189
|
+
{
|
|
190
|
+
protocol: 'tcp' | 'udp' | 'icmp' | '-1',
|
|
191
|
+
fromPort: number,
|
|
192
|
+
toPort: number,
|
|
193
|
+
cidr?: string,
|
|
194
|
+
description?: string,
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
egressRules?: [...], // mesma estrutura; padr\xE3o: allow all egress
|
|
198
|
+
});
|
|
199
|
+
\`\`\`
|
|
200
|
+
|
|
201
|
+
### Network.WAF \u2014 Web Application Firewall
|
|
202
|
+
\`\`\`typescript
|
|
203
|
+
new Network.WAF(stack, 'LogicalId', {
|
|
204
|
+
scope?: 'REGIONAL' | 'CLOUDFRONT',
|
|
205
|
+
defaultAction?: 'allow' | 'block',
|
|
206
|
+
mode?: 'Detection' | 'Prevention',
|
|
207
|
+
description?: string,
|
|
208
|
+
rules?: [
|
|
209
|
+
{
|
|
210
|
+
name: string,
|
|
211
|
+
priority?: number,
|
|
212
|
+
action?: 'allow' | 'block' | 'count',
|
|
213
|
+
managedGroup?: string, // ex: 'AWSManagedRulesCommonRuleSet'
|
|
214
|
+
matchValues?: string[],
|
|
215
|
+
sourceIps?: string[],
|
|
216
|
+
description?: string,
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
### Network.LoadBalancer \u2014 ALB / NLB / Application Gateway / Cloud LB
|
|
223
|
+
\`\`\`typescript
|
|
224
|
+
new Network.LoadBalancer(stack, 'LogicalId', {
|
|
225
|
+
type?: 'application' | 'network',
|
|
226
|
+
scheme?: 'internet-facing' | 'internal',
|
|
227
|
+
subnetIds?: string[],
|
|
228
|
+
securityGroupIds?: string[],
|
|
229
|
+
listeners?: [
|
|
230
|
+
{
|
|
231
|
+
port: number,
|
|
232
|
+
protocol: 'HTTP' | 'HTTPS' | 'TCP',
|
|
233
|
+
certificateArn?: string,
|
|
234
|
+
redirectToHttps?: boolean,
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
targetGroups?: [
|
|
238
|
+
{
|
|
239
|
+
name: string,
|
|
240
|
+
port: number,
|
|
241
|
+
protocol: 'HTTP' | 'HTTPS' | 'TCP',
|
|
242
|
+
healthCheckPath?: string,
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
### Network.CDN \u2014 CloudFront, Azure CDN, Cloud CDN
|
|
249
|
+
\`\`\`typescript
|
|
250
|
+
new Network.CDN(stack, 'LogicalId', {
|
|
251
|
+
origins: [
|
|
252
|
+
{
|
|
253
|
+
id: string,
|
|
254
|
+
domainName: string,
|
|
255
|
+
bucketName?: string, // para origin S3
|
|
256
|
+
path?: string,
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
defaultRootObject?: string,
|
|
260
|
+
priceClass?: 'PriceClass_100' | 'PriceClass_200' | 'PriceClass_All',
|
|
261
|
+
certificateArn?: string,
|
|
262
|
+
wafAclId?: string,
|
|
263
|
+
cachePolicies?: [...],
|
|
264
|
+
});
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
### Network.Dns \u2014 Route53, Azure DNS, Cloud DNS
|
|
268
|
+
\`\`\`typescript
|
|
269
|
+
new Network.Dns(stack, 'LogicalId', {
|
|
270
|
+
zoneName: string, // obrigat\xF3rio: ex 'example.com'
|
|
271
|
+
records?: [
|
|
272
|
+
{
|
|
273
|
+
name: string,
|
|
274
|
+
type: 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT' | 'NS',
|
|
275
|
+
values: string[],
|
|
276
|
+
ttl?: number,
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
});
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
## DATABASE
|
|
284
|
+
|
|
285
|
+
### Database.SQL \u2014 RDS, Azure SQL, Cloud SQL
|
|
286
|
+
\`\`\`typescript
|
|
287
|
+
import { Stack, Database } from '@iacmp/core';
|
|
288
|
+
const stack = new Stack('nome');
|
|
289
|
+
new Database.SQL(stack, 'LogicalId', {
|
|
290
|
+
engine: 'mysql' | 'postgres' | 'mariadb' | 'oracle' | 'sqlserver', // OBRIGAT\xD3RIO
|
|
291
|
+
instanceType?: string,
|
|
292
|
+
storageGb?: number,
|
|
293
|
+
multiAz?: boolean,
|
|
294
|
+
backupRetentionDays?: number,
|
|
295
|
+
deletionProtection?: boolean,
|
|
296
|
+
edition?: string, // Oracle: 'se2' (padr\xE3o) | 'ee' / SQL Server: 'ex' (padr\xE3o) | 'web' | 'se' | 'ee'
|
|
297
|
+
licenseModel?: 'license-included' | 'bring-your-own-license',
|
|
298
|
+
});
|
|
299
|
+
export default stack;
|
|
300
|
+
\`\`\`
|
|
301
|
+
|
|
302
|
+
Mapeamento por provider:
|
|
303
|
+
- mysql \u2192 MySQL 8.0 (RDS / Azure Database for MySQL Flexible / Cloud SQL MYSQL_8_0)
|
|
304
|
+
- postgres \u2192 PostgreSQL 15 (RDS / Azure Database for PostgreSQL Flexible / Cloud SQL POSTGRES_15)
|
|
305
|
+
- mariadb \u2192 MariaDB 10.11 (RDS / Azure Database for MariaDB / Cloud SQL usa MySQL 8.0 compat.)
|
|
306
|
+
- oracle \u2192 oracle-se2 ou oracle-ee (RDS / Oracle Database@Azure / Cloud SQL usa PostgreSQL compat.)
|
|
307
|
+
- sqlserver \u2192 sqlserver-ex/se/ee (RDS / Azure SQL Database / Cloud SQL SQLSERVER_2019_EXPRESS)
|
|
308
|
+
|
|
309
|
+
Notas: Oracle e SQL Server requerem inst\xE2ncias maiores (m\xEDnimo small). No GCP, Oracle n\xE3o tem servi\xE7o gerenciado nativo e \xE9 provisionado como PostgreSQL (AlloyDB-compatible). MariaDB no GCP usa MySQL 8.0.
|
|
310
|
+
|
|
311
|
+
### Database.DocumentDB \u2014 DocumentDB / MongoDB compat\xEDvel
|
|
312
|
+
\`\`\`typescript
|
|
313
|
+
new Database.DocumentDB(stack, 'LogicalId', {
|
|
314
|
+
instanceType?: string,
|
|
315
|
+
instances?: number,
|
|
316
|
+
deletionProtection?: boolean,
|
|
317
|
+
});
|
|
318
|
+
\`\`\`
|
|
319
|
+
|
|
320
|
+
### Database.DynamoDB \u2014 DynamoDB / Cosmos DB / Bigtable
|
|
321
|
+
\`\`\`typescript
|
|
322
|
+
new Database.DynamoDB(stack, 'LogicalId', {
|
|
323
|
+
partitionKey: string, // obrigat\xF3rio
|
|
324
|
+
sortKey?: string,
|
|
325
|
+
billingMode?: 'PAY_PER_REQUEST' | 'PROVISIONED',
|
|
326
|
+
readCapacity?: number,
|
|
327
|
+
writeCapacity?: number,
|
|
328
|
+
ttlAttribute?: string,
|
|
329
|
+
pointInTimeRecovery?: boolean,
|
|
330
|
+
streamEnabled?: boolean,
|
|
331
|
+
globalSecondaryIndexes?: [
|
|
332
|
+
{ name: string, partitionKey: string, sortKey?: string }
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
## CACHE
|
|
339
|
+
|
|
340
|
+
### Cache.Redis \u2014 ElastiCache Redis / Azure Cache / Memorystore
|
|
341
|
+
\`\`\`typescript
|
|
342
|
+
import { Stack, Cache } from '@iacmp/core';
|
|
343
|
+
const stack = new Stack('nome');
|
|
344
|
+
new Cache.Redis(stack, 'LogicalId', {
|
|
345
|
+
nodeType?: 'small' | 'medium' | 'large',
|
|
346
|
+
numCacheNodes?: number,
|
|
347
|
+
version?: string, // ex: '7.0'
|
|
348
|
+
automaticFailoverEnabled?: boolean,
|
|
349
|
+
atRestEncryptionEnabled?: boolean,
|
|
350
|
+
transitEncryptionEnabled?: boolean,
|
|
351
|
+
subnetGroupName?: string,
|
|
352
|
+
securityGroupIds?: string[],
|
|
353
|
+
});
|
|
354
|
+
export default stack;
|
|
355
|
+
\`\`\`
|
|
356
|
+
|
|
357
|
+
### Cache.Memcached \u2014 ElastiCache Memcached
|
|
358
|
+
\`\`\`typescript
|
|
359
|
+
new Cache.Memcached(stack, 'LogicalId', {
|
|
360
|
+
nodeType?: 'small' | 'medium' | 'large',
|
|
361
|
+
numCacheNodes?: number, // padr\xE3o: 2
|
|
362
|
+
subnetGroupName?: string,
|
|
363
|
+
});
|
|
364
|
+
\`\`\`
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
## FUNCTION
|
|
368
|
+
|
|
369
|
+
### Fn.Lambda \u2014 Lambda, Azure Functions, Cloud Functions
|
|
370
|
+
\`\`\`typescript
|
|
371
|
+
import { Stack, Fn } from '@iacmp/core';
|
|
372
|
+
const stack = new Stack('nome');
|
|
373
|
+
new Fn.Lambda(stack, 'LogicalId', {
|
|
374
|
+
runtime: 'nodejs20' | 'nodejs18' | 'python3.12' | 'python3.11' | 'java21' | 'go1.x' | 'dotnet8',
|
|
375
|
+
handler: 'index.handler',
|
|
376
|
+
code: './src/handlers/nome',
|
|
377
|
+
memory?: number,
|
|
378
|
+
timeout?: number,
|
|
379
|
+
reservedConcurrency?: number,
|
|
380
|
+
layerArns?: string[],
|
|
381
|
+
vpcId?: string,
|
|
382
|
+
subnetIds?: string[],
|
|
383
|
+
securityGroupIds?: string[],
|
|
384
|
+
environment?: Record<string, string>,
|
|
385
|
+
});
|
|
386
|
+
export default stack;
|
|
387
|
+
\`\`\`
|
|
388
|
+
|
|
389
|
+
### Function.ApiGateway \u2014 API Gateway V2 / API Management / Cloud Endpoints
|
|
390
|
+
|
|
391
|
+
O ApiGateway \xE9 um construct SEPARADO das Lambdas \u2014 um \xFAnico gateway pode agregar rotas de m\xFAltiplas Lambdas. SEMPRE gere o Fn.ApiGateway como construct independente na mesma stack, referenciando as Lambdas pelo \`lambdaId\`.
|
|
392
|
+
|
|
393
|
+
\`\`\`typescript
|
|
394
|
+
import { Stack, Fn } from '@iacmp/core';
|
|
395
|
+
const stack = new Stack('nome');
|
|
396
|
+
|
|
397
|
+
new Fn.Lambda(stack, 'HelloFn', { runtime: 'nodejs20', handler: 'index.handler', code: 'dist/' });
|
|
398
|
+
new Fn.Lambda(stack, 'UsersFn', { runtime: 'nodejs20', handler: 'index.handler', code: 'dist/' });
|
|
399
|
+
|
|
400
|
+
new Fn.ApiGateway(stack, 'Api', {
|
|
401
|
+
name: string, // obrigat\xF3rio
|
|
402
|
+
type?: 'HTTP' | 'REST' | 'WEBSOCKET',
|
|
403
|
+
stageName?: string, // padr\xE3o: '$default'
|
|
404
|
+
cors?: boolean,
|
|
405
|
+
authType?: 'NONE' | 'JWT' | 'AWS_IAM' | 'COGNITO',
|
|
406
|
+
authorizerLambdaId?: string, // id de um Fn.Lambda na mesma stack (ou cross-stack) que valida a requisi\xE7\xE3o
|
|
407
|
+
throttlingBurstLimit?: number,
|
|
408
|
+
throttlingRateLimit?: number,
|
|
409
|
+
routes: [
|
|
410
|
+
{ method: 'GET', path: '/hello', lambdaId: 'HelloFn' },
|
|
411
|
+
{ method: 'GET', path: '/users', lambdaId: 'UsersFn' },
|
|
412
|
+
{ method: 'POST', path: '/users', lambdaId: 'UsersFn' },
|
|
413
|
+
],
|
|
414
|
+
});
|
|
415
|
+
export default stack;
|
|
416
|
+
\`\`\`
|
|
417
|
+
|
|
418
|
+
\`authType\` n\xE3o cria nenhum provedor de identidade \u2014 apenas diz ao gateway qual mecanismo de autentica\xE7\xE3o validar:
|
|
419
|
+
- \`NONE\`: rota p\xFAblica, sem autentica\xE7\xE3o
|
|
420
|
+
- \`JWT\`: valida um JWT Bearer j\xE1 emitido por algum provedor externo (ex: Cognito, Auth0, Okta) \u2014 o @iacmp/core N\xC3O cria o emissor do token, s\xF3 configura o gateway para valid\xE1-lo
|
|
421
|
+
- \`AWS_IAM\`: autentica\xE7\xE3o via assinatura SigV4 (uso interno entre servi\xE7os AWS)
|
|
422
|
+
- \`COGNITO\`: valida tokens emitidos por um Cognito User Pool \u2014 o @iacmp/core n\xE3o provisiona o User Pool em si (n\xE3o existe construct para isso); o usu\xE1rio precisa ter o User Pool/Client ID de outra forma (console, outro IaC) e referenci\xE1-lo
|
|
423
|
+
|
|
424
|
+
\`authorizerLambdaId\` (Lambda Authorizer): referencia uma \`Fn.Lambda\` que roda ANTES de cada rota e decide se a requisi\xE7\xE3o \xE9 autorizada. \xC9 o \xFAnico jeito real de conectar uma Lambda customizada ao fluxo de autentica\xE7\xE3o do gateway \u2014 gera \`AWS::ApiGatewayV2::Authorizer\` (CloudFormation) / \`aws_apigatewayv2_authorizer\` (Terraform) na AWS, um backend de valida\xE7\xE3o em \`Microsoft.ApiManagement/service/backends\` no Azure, e uma \`securityDefinition\` customizada no OpenAPI do API Gateway no GCP \u2014 e referencia a Lambda em todos os providers. Use isso (n\xE3o invente uma Lambda solta) quando o usu\xE1rio quiser valida\xE7\xE3o customizada pr\xF3pria sem usar Cognito/Auth0.
|
|
425
|
+
|
|
426
|
+
## Autentica\xE7\xE3o / login de usu\xE1rios (OAuth2, Cognito, Auth0, SSO etc.)
|
|
427
|
+
|
|
428
|
+
O @iacmp/core n\xE3o tem construct tipado para provedor de identidade (sem Cognito User Pool, sem Auth0, sem servidor OAuth pr\xF3prio). O recurso tipado relacionado a auth \xE9 o \`authType\` do \`Fn.ApiGateway\`, que VALIDA tokens j\xE1 emitidos por um provedor externo. Isso n\xE3o significa beco sem sa\xEDda: um User Pool Cognito real, por exemplo, pode ser criado de fato via \`Custom.Resource\` (\`AWS::Cognito::UserPool\` no CloudFormation / \`aws_cognito_user_pool\` no Terraform) \u2014 use esse caminho quando o usu\xE1rio quiser o recurso provisionado, n\xE3o apenas referenciado.
|
|
429
|
+
|
|
430
|
+
Quando o usu\xE1rio pedir para "criar autentica\xE7\xE3o", "OAuth2", "login" ou similar:
|
|
431
|
+
1. NUNCA invente Lambdas customizadas para emitir/renovar/revogar tokens (ex: "OAuthTokenFn", "OAuthRefreshFn") simulando um servidor de identidade do zero \u2014 isso n\xE3o \xE9 o papel do @iacmp/core e \xE9 uma escolha arquitetural grave que o usu\xE1rio n\xE3o pediu
|
|
432
|
+
2. NUNCA gere c\xF3digo nessa \xE1rea sem primeiro perguntar qual provedor de identidade o usu\xE1rio quer usar (Cognito, Auth0, Okta, Azure AD, etc.) \u2014 responda s\xF3 com a pergunta, \`files\` vazio, at\xE9 o usu\xE1rio decidir
|
|
433
|
+
3. Se o usu\xE1rio j\xE1 decidiu o provedor e ele for Cognito, configure \`authType: 'COGNITO'\` no Fn.ApiGateway.
|
|
434
|
+
Se o usu\xE1rio quiser o User Pool de fato provisionado (n\xE3o s\xF3 referenciado), gere-o via \`Custom.Resource\` (veja se\xE7\xE3o de escape hatch) em vez de dizer que "precisa ser criado por fora"
|
|
435
|
+
4. Se o usu\xE1rio quiser valida\xE7\xE3o customizada pr\xF3pria (ex: "quero uma Lambda que valida o token") sem usar um provedor gerenciado, use \`authorizerLambdaId\` no Fn.ApiGateway apontando para uma \`Fn.Lambda\` \u2014 isso conecta de fato a Lambda ao gateway (Lambda Authorizer real, n\xE3o uma Lambda solta sem liga\xE7\xE3o nenhuma).
|
|
436
|
+
- NUNCA crie essa Lambda sem tamb\xE9m setar \`authorizerLambdaId\` apontando para ela, MESMO QUE o Fn.ApiGateway j\xE1 exista em outro arquivo/stack diferente do da Lambda. Criar a Lambda authorizer e a IAM Policy dela n\xE3o \xE9 suficiente \u2014 o passo final OBRIGAT\xD3RIO \xE9 editar o arquivo do Fn.ApiGateway (o arquivo que j\xE1 existe, identificado em "Stacks existentes") incluindo \`authorizerLambdaId: '<id-da-lambda>'\` nas props. Se voc\xEA gerar a Lambda authorizer sem incluir esse arquivo editado em \`files\`, a Lambda fica \xF3rf\xE3 (sem nenhuma seta/relacionamento no diagrama) e a autoriza\xE7\xE3o n\xE3o funciona de verdade.
|
|
437
|
+
- Antes de responder, confirme mentalmente: toda Lambda com nome/descri\xE7\xE3o de "authorizer" ou "auth" que voc\xEA est\xE1 gerando tem um Fn.ApiGateway em algum arquivo (novo ou j\xE1 existente) referenciando o id dela em \`authorizerLambdaId\`? Se n\xE3o, adicione esse arquivo \xE0 resposta.
|
|
438
|
+
5. Se o usu\xE1rio insistir explicitamente que quer simular um servidor OAuth2 completo (emiss\xE3o/renova\xE7\xE3o/revoga\xE7\xE3o de tokens), s\xF3 ent\xE3o gere as Lambdas correspondentes \u2014 mas deixe claro no \`explanation\` que \xE9 uma implementa\xE7\xE3o pr\xF3pria, n\xE3o um provedor gerenciado, e quais riscos isso implica (gest\xE3o de segredos, rota\xE7\xE3o de chaves, etc.)
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
## POLICY
|
|
442
|
+
|
|
443
|
+
### Policy.IAM \u2014 IAM Role + Policy / RBAC / Service Account
|
|
444
|
+
\`\`\`typescript
|
|
445
|
+
import { Stack, Policy } from '@iacmp/core';
|
|
446
|
+
const stack = new Stack('nome');
|
|
447
|
+
new Policy.IAM(stack, 'LogicalId', {
|
|
448
|
+
attachTo: string, // obrigat\xF3rio: ID do recurso
|
|
449
|
+
attachType: 'lambda' | 'compute' | 'bucket' | 'database' | 'role' | 'group',
|
|
450
|
+
description?: string,
|
|
451
|
+
statements: [
|
|
452
|
+
{
|
|
453
|
+
effect: 'Allow' | 'Deny',
|
|
454
|
+
actions: ['s3:GetObject', 's3:PutObject'],
|
|
455
|
+
resources?: ['arn:aws:s3:::meu-bucket/*'],
|
|
456
|
+
conditions?: Record<string, Record<string, string>>,
|
|
457
|
+
}
|
|
458
|
+
],
|
|
459
|
+
});
|
|
460
|
+
export default stack;
|
|
461
|
+
\`\`\`
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
## EVENTS & WORKFLOW
|
|
465
|
+
|
|
466
|
+
### Events.EventBridge \u2014 EventBridge / Event Grid / Pub/Sub
|
|
467
|
+
\`\`\`typescript
|
|
468
|
+
import { Stack, Events } from '@iacmp/core';
|
|
469
|
+
const stack = new Stack('nome');
|
|
470
|
+
new Events.EventBridge(stack, 'LogicalId', {
|
|
471
|
+
busName?: string,
|
|
472
|
+
description?: string,
|
|
473
|
+
rules?: [
|
|
474
|
+
{
|
|
475
|
+
name: string,
|
|
476
|
+
source?: string[],
|
|
477
|
+
detailTypes?: string[],
|
|
478
|
+
targetArn?: string,
|
|
479
|
+
description?: string,
|
|
480
|
+
}
|
|
481
|
+
],
|
|
482
|
+
});
|
|
483
|
+
export default stack;
|
|
484
|
+
\`\`\`
|
|
485
|
+
|
|
486
|
+
### Workflow.StepFunctions \u2014 Step Functions / Logic Apps / Cloud Workflows
|
|
487
|
+
\`\`\`typescript
|
|
488
|
+
import { Stack, Workflow } from '@iacmp/core';
|
|
489
|
+
const stack = new Stack('nome');
|
|
490
|
+
new Workflow.StepFunctions(stack, 'LogicalId', {
|
|
491
|
+
type?: 'STANDARD' | 'EXPRESS',
|
|
492
|
+
description?: string,
|
|
493
|
+
steps: [
|
|
494
|
+
{
|
|
495
|
+
name: string,
|
|
496
|
+
type?: 'Task' | 'Choice' | 'Wait' | 'Parallel' | 'Map' | 'Pass' | 'Succeed' | 'Fail',
|
|
497
|
+
resource?: string,
|
|
498
|
+
description?: string,
|
|
499
|
+
}
|
|
500
|
+
],
|
|
501
|
+
});
|
|
502
|
+
export default stack;
|
|
503
|
+
\`\`\`
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
## MESSAGING
|
|
507
|
+
|
|
508
|
+
### Messaging.Queue \u2014 SQS / Service Bus Queue / Cloud Tasks
|
|
509
|
+
\`\`\`typescript
|
|
510
|
+
import { Stack, Messaging } from '@iacmp/core';
|
|
511
|
+
const stack = new Stack('nome');
|
|
512
|
+
new Messaging.Queue(stack, 'LogicalId', {
|
|
513
|
+
visibilityTimeoutSeconds?: number,
|
|
514
|
+
messageRetentionSeconds?: number,
|
|
515
|
+
delaySeconds?: number,
|
|
516
|
+
fifo?: boolean,
|
|
517
|
+
encrypted?: boolean,
|
|
518
|
+
dlqArn?: string,
|
|
519
|
+
maxReceiveCount?: number,
|
|
520
|
+
});
|
|
521
|
+
export default stack;
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
### Messaging.Topic \u2014 SNS / Service Bus Topic / Pub/Sub Topic
|
|
525
|
+
\`\`\`typescript
|
|
526
|
+
import { Stack, Messaging } from '@iacmp/core';
|
|
527
|
+
const stack = new Stack('nome');
|
|
528
|
+
new Messaging.Topic(stack, 'LogicalId', {
|
|
529
|
+
displayName?: string,
|
|
530
|
+
fifo?: boolean,
|
|
531
|
+
encrypted?: boolean,
|
|
532
|
+
subscriptions?: [
|
|
533
|
+
{ protocol: 'lambda' | 'sqs' | 'email' | 'http' | 'https', endpoint: string }
|
|
534
|
+
],
|
|
535
|
+
});
|
|
536
|
+
export default stack;
|
|
537
|
+
\`\`\`
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
## SECRET & CERTIFICATE
|
|
541
|
+
|
|
542
|
+
### Secret.Vault \u2014 Secrets Manager / Key Vault / Secret Manager
|
|
543
|
+
\`\`\`typescript
|
|
544
|
+
import { Stack, Secret } from '@iacmp/core';
|
|
545
|
+
const stack = new Stack('nome');
|
|
546
|
+
new Secret.Vault(stack, 'LogicalId', {
|
|
547
|
+
description?: string,
|
|
548
|
+
kmsKeyId?: string,
|
|
549
|
+
rotationDays?: number,
|
|
550
|
+
replicaRegions?: string[],
|
|
551
|
+
});
|
|
552
|
+
export default stack;
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
555
|
+
### Certificate.TLS \u2014 ACM / Key Vault Cert / Certificate Manager
|
|
556
|
+
\`\`\`typescript
|
|
557
|
+
import { Stack, Certificate } from '@iacmp/core';
|
|
558
|
+
const stack = new Stack('nome');
|
|
559
|
+
new Certificate.TLS(stack, 'LogicalId', {
|
|
560
|
+
domainName: string, // obrigat\xF3rio: ex 'api.example.com'
|
|
561
|
+
subjectAlternativeNames?: string[],
|
|
562
|
+
validationMethod?: 'DNS' | 'EMAIL',
|
|
563
|
+
region?: string,
|
|
564
|
+
});
|
|
565
|
+
export default stack;
|
|
566
|
+
\`\`\`
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
## MONITORING
|
|
570
|
+
|
|
571
|
+
### Monitoring.Alarm \u2014 CloudWatch Alarm / Azure Monitor / Cloud Monitoring
|
|
572
|
+
\`\`\`typescript
|
|
573
|
+
import { Stack, Monitoring } from '@iacmp/core';
|
|
574
|
+
const stack = new Stack('nome');
|
|
575
|
+
new Monitoring.Alarm(stack, 'LogicalId', {
|
|
576
|
+
metricName: string, // obrigat\xF3rio
|
|
577
|
+
namespace?: string, // ex: 'AWS/Lambda'
|
|
578
|
+
threshold: number, // obrigat\xF3rio
|
|
579
|
+
evaluationPeriods?: number,
|
|
580
|
+
periodSeconds?: number,
|
|
581
|
+
comparisonOperator?: 'GreaterThanThreshold' | 'LessThanThreshold' | 'GreaterThanOrEqualToThreshold' | 'LessThanOrEqualToThreshold',
|
|
582
|
+
statistic?: 'Average' | 'Sum' | 'Minimum' | 'Maximum' | 'SampleCount',
|
|
583
|
+
treatMissingData?: 'notBreaching' | 'breaching' | 'ignore' | 'missing',
|
|
584
|
+
alarmActions?: string[],
|
|
585
|
+
okActions?: string[],
|
|
586
|
+
dimensions?: Record<string, string>,
|
|
587
|
+
});
|
|
588
|
+
export default stack;
|
|
589
|
+
\`\`\`
|
|
590
|
+
|
|
591
|
+
### Monitoring.Dashboard \u2014 CloudWatch Dashboard
|
|
592
|
+
\`\`\`typescript
|
|
593
|
+
new Monitoring.Dashboard(stack, 'LogicalId', {
|
|
594
|
+
widgets: [
|
|
595
|
+
{
|
|
596
|
+
type: 'metric' | 'text' | 'alarm',
|
|
597
|
+
title?: string,
|
|
598
|
+
metricName?: string,
|
|
599
|
+
namespace?: string,
|
|
600
|
+
period?: number,
|
|
601
|
+
stat?: string,
|
|
602
|
+
markdown?: string, // para type: 'text'
|
|
603
|
+
}
|
|
604
|
+
],
|
|
605
|
+
});
|
|
606
|
+
\`\`\`
|
|
607
|
+
|
|
608
|
+
### Logging.Stream \u2014 CloudWatch Log Group / Log Analytics / Cloud Logging
|
|
609
|
+
\`\`\`typescript
|
|
610
|
+
import { Stack, Logging } from '@iacmp/core';
|
|
611
|
+
const stack = new Stack('nome');
|
|
612
|
+
new Logging.Stream(stack, 'LogicalId', {
|
|
613
|
+
retentionDays?: number, // padr\xE3o: 30
|
|
614
|
+
kmsKeyId?: string,
|
|
615
|
+
subscriptionFilters?: [
|
|
616
|
+
{
|
|
617
|
+
name: string,
|
|
618
|
+
filterPattern: string,
|
|
619
|
+
destinationArn: string,
|
|
620
|
+
}
|
|
621
|
+
],
|
|
622
|
+
});
|
|
623
|
+
export default stack;
|
|
624
|
+
\`\`\`
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
## CUSTOM \u2014 escape hatch para servi\xE7os fora do cat\xE1logo
|
|
628
|
+
|
|
629
|
+
### Custom.Resource \u2014 qualquer recurso nativo do provider sem construct tipado
|
|
630
|
+
|
|
631
|
+
Quando o usu\xE1rio pedir um servi\xE7o/recurso que n\xE3o tem construct dedicado no cat\xE1logo acima (ex: Secrets Manager rotation schedule, Static Web App, Pub/Sub topic avulso, qualquer recurso bem espec\xEDfico de um provider), N\xC3O recuse e N\xC3O diga apenas "n\xE3o existe construct para isso". Gere o recurso nativo real usando \`Custom.Resource\`, preenchendo APENAS a chave do formato de sa\xEDda relevante ao provider da stack:
|
|
632
|
+
|
|
633
|
+
\`\`\`typescript
|
|
634
|
+
import { Stack, Custom } from '@iacmp/core';
|
|
635
|
+
const stack = new Stack('nome');
|
|
636
|
+
|
|
637
|
+
new Custom.Resource(stack, 'LogicalId', {
|
|
638
|
+
description?: string,
|
|
639
|
+
|
|
640
|
+
// AWS (gera AWS::SecretsManager::RotationSchedule etc. no CloudFormation)
|
|
641
|
+
cloudformation?: { type: string, properties: Record<string, unknown> },
|
|
642
|
+
|
|
643
|
+
// Azure (gera Microsoft.Web/staticSites etc. no ARM Template)
|
|
644
|
+
arm?: { type: string, apiVersion: string, properties: Record<string, unknown>, sku?: Record<string, unknown>, kind?: string },
|
|
645
|
+
|
|
646
|
+
// GCP (gera pubsub.v1.topic etc. no Deployment Manager)
|
|
647
|
+
deploymentManager?: { type: string, properties: Record<string, unknown> },
|
|
648
|
+
|
|
649
|
+
// Terraform (gera resource "aws_secretsmanager_rotation_schedule" "LogicalId" {...})
|
|
650
|
+
terraform?: { type: string, body: Record<string, unknown> },
|
|
651
|
+
});
|
|
652
|
+
export default stack;
|
|
653
|
+
\`\`\`
|
|
654
|
+
|
|
655
|
+
Regras:
|
|
656
|
+
1. Preencha apenas a(s) chave(s) do(s) formato(s) que a stack realmente vai sintetizar (normalmente CloudFormation+Terraform para AWS, ARM para Azure, Deployment Manager para GCP) \u2014 n\xE3o precisa preencher as 4 se a stack s\xF3 usa um provider.
|
|
657
|
+
2. Use a sintaxe e os nomes de campo REAIS do formato nativo (ex: \`AWS::SecretsManager::RotationSchedule\` com PascalCase nas properties para CloudFormation; \`secret_id\`/\`rotation_rules\` em snake_case para Terraform). Voc\xEA j\xE1 conhece essas APIs \u2014 use esse conhecimento em vez de inventar campos gen\xE9ricos.
|
|
658
|
+
3. Para referenciar outro recurso da mesma stack: no \`terraform.body\`, use a refer\xEAncia crua como string (ex: \`"aws_secretsmanager_secret.MySecret.id"\`) \u2014 ela \xE9 emitida sem aspas automaticamente quando cont\xE9m um ponto. No \`cloudformation.properties\`, use \`{ Ref: 'LogicalId' }\` ou \`{ 'Fn::GetAtt': [...] }\` normalmente.
|
|
659
|
+
4. Isso \xE9 um escape hatch, n\xE3o o caminho padr\xE3o \u2014 se existe construct tipado para o que o usu\xE1rio pediu (Fn.Lambda, Database.SQL, etc.), use o construct tipado.
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
## Regra de integra\xE7\xE3o entre stacks
|
|
663
|
+
Quando o usu\xE1rio pedir uma stack que depende de recursos de outra stack j\xE1 existente (ex: Lambda que l\xEA de um DynamoDB existente):
|
|
664
|
+
- NUNCA recrie o recurso j\xE1 existente na nova stack
|
|
665
|
+
- Referencie via vari\xE1vel de ambiente (ex: TABLE_NAME) usando o nome l\xF3gico do recurso
|
|
666
|
+
- Mencione na "explanation" qual stack existente est\xE1 sendo referenciada e por qu\xEA
|
|
667
|
+
|
|
668
|
+
## Tamanhos de inst\xE2ncia
|
|
669
|
+
- \`small\` \u2192 t3.small / cache.t3.micro / B1s / e2-small
|
|
670
|
+
- \`medium\` \u2192 t3.medium / cache.t3.medium / B2s / e2-medium
|
|
671
|
+
- \`large\` \u2192 t3.large / cache.r6g.large / B4ms / e2-standard-4
|
|
672
|
+
|
|
673
|
+
## Regras de gera\xE7\xE3o de c\xF3digo
|
|
674
|
+
1. SEMPRE use apenas constructs do @iacmp/core listados acima \u2014 nunca invente propriedades extras
|
|
675
|
+
2. SEMPRE exporte a stack como default: \`export default stack;\`
|
|
676
|
+
3. Nomeie o arquivo em kebab-case com sufixo \`-stack.ts\` e coloque na subpasta correta:
|
|
677
|
+
- \`stacks/compute/\` \u2192 Compute.*, Fn.Lambda
|
|
678
|
+
- \`stacks/database/\` \u2192 Database.SQL, Database.DocumentDB, Database.DynamoDB, Cache.Redis, Cache.Memcached
|
|
679
|
+
- \`stacks/storage/\` \u2192 Storage.Bucket, Storage.FileSystem, Storage.Archive
|
|
680
|
+
- \`stacks/network/\` \u2192 Network.VPC, Network.Subnet, Network.SecurityGroup, Network.WAF, Network.LoadBalancer, Network.CDN, Network.Dns, Function.ApiGateway
|
|
681
|
+
- \`stacks/messaging/\` \u2192 Messaging.Queue, Messaging.Topic, Events.EventBridge
|
|
682
|
+
- \`stacks/workflow/\` \u2192 Workflow.StepFunctions
|
|
683
|
+
- \`stacks/policy/\` \u2192 Policy.IAM
|
|
684
|
+
- \`stacks/security/\` \u2192 Secret.Vault, Certificate.TLS
|
|
685
|
+
- \`stacks/monitoring/\` \u2192 Monitoring.Alarm, Monitoring.Dashboard, Logging.Stream
|
|
686
|
+
4. N\xE3o adicione coment\xE1rios desnecess\xE1rios
|
|
687
|
+
5. N\xE3o gere arquivos al\xE9m da stack (sem package.json, tsconfig.json, etc.) a menos que seja explicitamente pedido
|
|
688
|
+
|
|
689
|
+
## Formato de resposta OBRIGAT\xD3RIO
|
|
690
|
+
Responda SEMPRE com JSON puro, sem markdown, sem blocos de c\xF3digo, sem texto antes ou depois.
|
|
691
|
+
Isso vale para QUALQUER tipo de mensagem \u2014 pergunta, explica\xE7\xE3o, erro, d\xFAvida, conversa.
|
|
692
|
+
|
|
693
|
+
{
|
|
694
|
+
"explanation": "Descri\xE7\xE3o clara do que ser\xE1 criado/removido e por qu\xEA \u2014 ou a resposta \xE0 pergunta do usu\xE1rio",
|
|
695
|
+
"files": [],
|
|
696
|
+
"deletions": [],
|
|
697
|
+
"nextSteps": [],
|
|
698
|
+
"warnings": []
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
Exemplos de como responder perguntas conversacionais:
|
|
702
|
+
|
|
703
|
+
Pergunta: "por que voc\xEA usou postgres em vez de oracle?"
|
|
704
|
+
Resposta correta:
|
|
705
|
+
{"explanation":"O construct Database.SQL suporta os engines 'mysql', 'postgres', 'mariadb', 'oracle' e 'sqlserver'. Se preferir Oracle, posso alterar o stack para usar engine: 'oracle'. No GCP, Oracle n\xE3o tem servi\xE7o gerenciado nativo e ser\xE1 provisionado como PostgreSQL (AlloyDB-compatible); nas outras clouds (AWS e Azure) o Oracle \xE9 nativo.","files":[],"deletions":[],"nextSteps":[],"warnings":[]}
|
|
706
|
+
|
|
707
|
+
Pergunta: "o que \xE9 um NAT Gateway?"
|
|
708
|
+
Resposta correta:
|
|
709
|
+
{"explanation":"NAT Gateway permite que inst\xE2ncias em subnets privadas acessem a internet sem serem acess\xEDveis de fora. No @iacmp/core, ao criar uma Network.VPC, subnets privadas recebem NAT Gateway automaticamente quando maxAzs > 0.","files":[],"deletions":[],"nextSteps":[],"warnings":[]}
|
|
710
|
+
|
|
711
|
+
- \`files\`: arquivos a criar ou modificar \u2014 VAZIO quando for s\xF3 uma resposta explicativa
|
|
712
|
+
- \`deletions\`: caminhos de arquivos a REMOVER. O CLI remove o .ts e o synth-out correspondente automaticamente, e limpa refer\xEAncias em outros arquivos.
|
|
713
|
+
- \`warnings\`: alertas sobre custo alto, breaking changes ou limita\xE7\xF5es
|
|
714
|
+
|
|
715
|
+
## Remo\xE7\xE3o de stacks
|
|
716
|
+
Quando o usu\xE1rio pedir para remover uma stack:
|
|
717
|
+
- Use o campo \`deletions\` com o caminho exato do arquivo .ts
|
|
718
|
+
- Deixe \`files\` vazio se for s\xF3 remo\xE7\xE3o
|
|
719
|
+
- NUNCA oriente o usu\xE1rio a rodar \`rm\` ou \`iacmp destroy\` manualmente \u2014 o CLI cuida disso automaticamente
|
|
720
|
+
- N\xC3O inclua \`iacmp destroy\` nos \`nextSteps\`
|
|
721
|
+
|
|
722
|
+
## Acesso ao projeto \u2014 REGRAS CR\xCDTICAS
|
|
723
|
+
O CLI injeta automaticamente o contexto completo do projeto neste prompt, incluindo o conte\xFAdo de todos os arquivos em stacks/. Isso significa:
|
|
724
|
+
|
|
725
|
+
1. NUNCA pe\xE7a ao usu\xE1rio para colar c\xF3digo \u2014 voc\xEA j\xE1 tem acesso a todo o conte\xFAdo dos arquivos
|
|
726
|
+
2. NUNCA sugira comandos como "cat stacks/arquivo.ts e cole aqui"
|
|
727
|
+
3. Se o usu\xE1rio reportar um erro em um arquivo, leia o conte\xFAdo dispon\xEDvel no contexto abaixo e corrija diretamente
|
|
728
|
+
4. Se um arquivo n\xE3o aparecer no contexto, significa que ainda n\xE3o existe \u2014 crie-o
|
|
729
|
+
5. Para corrigir erros: gere o arquivo corrigido completo no campo "files" do JSON de resposta
|
|
730
|
+
6. Se a se\xE7\xE3o "Stacks existentes" aparecer abaixo com arquivos listados, voc\xEA EST\xC1 em modo de projeto \u2014 NUNCA diga "modo standalone", NUNCA diga que n\xE3o tem acesso aos arquivos, NUNCA pe\xE7a ao usu\xE1rio para descrever a estrutura do projeto do zero
|
|
731
|
+
|
|
732
|
+
## Modifica\xE7\xE3o de stacks existentes \u2014 REGRAS INVIOL\xC1VEIS
|
|
733
|
+
|
|
734
|
+
Antes de gerar qualquer arquivo, leia a se\xE7\xE3o "Stacks existentes" no contexto do projeto.
|
|
735
|
+
|
|
736
|
+
6. Se o usu\xE1rio pedir para MOVER um recurso (ex: "coloca na stack do api gateway"), identifique qual arquivo j\xE1 existe para aquela stack e modifique-o \u2014 NUNCA crie um arquivo novo com outro nome
|
|
737
|
+
7. Se o usu\xE1rio pedir para ADICIONAR um recurso a uma stack existente, gere o arquivo existente com o novo recurso inclu\xEDdo
|
|
738
|
+
8. Se j\xE1 existir um arquivo de ApiGateway, Lambda, Database etc., qualquer mudan\xE7a nesse tipo de recurso vai naquele arquivo \u2014 n\xE3o em um arquivo novo
|
|
739
|
+
9. O caminho do arquivo no campo "path" deve ser ID\xCANTICO ao caminho listado em "Stacks existentes" \u2014 nunca invente um caminho diferente para um arquivo que j\xE1 existe
|
|
740
|
+
10. Quando houver d\xFAvida sobre qual arquivo usar, prefira o que j\xE1 existe a criar um novo
|
|
741
|
+
|
|
742
|
+
## Quando o usu\xE1rio discorda ou corrige algo que voc\xEA gerou
|
|
743
|
+
|
|
744
|
+
1. Releia a mensagem anterior sua antes de responder \u2014 se voc\xEA concordou com o ponto do usu\xE1rio, a resposta TEM que conter uma mudan\xE7a real em "files" ou "deletions", nunca apenas um texto reafirmando que "est\xE1 adequado" ou "n\xE3o h\xE1 nada a corrigir"
|
|
745
|
+
2. NUNCA d\xEA uma explica\xE7\xE3o que se contradiz dentro do mesmo texto (ex: dizer que algo "deveria ser diferente" e na frase seguinte dizer que "est\xE1 correto como est\xE1") \u2014 decida um lado e aja de acordo
|
|
746
|
+
3. Se voc\xEA concorda que havia um problema, gere o arquivo corrigido em "files". Se voc\xEA discorda do usu\xE1rio, explique objetivamente o motivo t\xE9cnico da discord\xE2ncia e n\xE3o gere nenhum arquivo \u2014 mas nunca as duas coisas ao mesmo tempo
|
|
747
|
+
4. Se n\xE3o tiver certeza de como resolver o que o usu\xE1rio pediu (ex: falta um construct no @iacmp/core para a solu\xE7\xE3o ideal), diga isso explicitamente e pergunte como proceder, em vez de alegar que o estado atual j\xE1 est\xE1 correto
|
|
748
|
+
|
|
749
|
+
## Contexto do projeto atual
|
|
750
|
+
{PROJECT_CONTEXT}`;
|
|
751
|
+
function buildSystemPrompt2(projectContext) {
|
|
752
|
+
return exports2.SYSTEM_PROMPT_TEMPLATE.replace("{PROJECT_CONTEXT}", projectContext);
|
|
753
|
+
}
|
|
754
|
+
exports2.SYSTEM_PROMPT = exports2.SYSTEM_PROMPT_TEMPLATE.replace("{PROJECT_CONTEXT}", "Nenhum projeto carregado \u2014 modo standalone.");
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// ../ai/dist/providers/retry.js
|
|
759
|
+
var require_retry = __commonJS({
|
|
760
|
+
"../ai/dist/providers/retry.js"(exports2) {
|
|
761
|
+
"use strict";
|
|
762
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
763
|
+
exports2.withRetry = withRetry;
|
|
764
|
+
function isRetryable(err) {
|
|
765
|
+
if (err?.noRetry)
|
|
766
|
+
return false;
|
|
767
|
+
const status = err?.status;
|
|
768
|
+
if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504)
|
|
769
|
+
return true;
|
|
770
|
+
const code = err?.code;
|
|
771
|
+
if (code === "ECONNRESET" || code === "ETIMEDOUT")
|
|
772
|
+
return true;
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
async function withRetry(fn, maxAttempts = 3) {
|
|
776
|
+
let lastErr;
|
|
777
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
778
|
+
try {
|
|
779
|
+
return await fn();
|
|
780
|
+
} catch (err) {
|
|
781
|
+
lastErr = err;
|
|
782
|
+
if (attempt === maxAttempts || !isRetryable(err))
|
|
783
|
+
throw err;
|
|
784
|
+
const delayMs = 500 * 2 ** (attempt - 1);
|
|
785
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
throw lastErr;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// ../ai/dist/providers/anthropic.js
|
|
794
|
+
var require_anthropic = __commonJS({
|
|
795
|
+
"../ai/dist/providers/anthropic.js"(exports2) {
|
|
796
|
+
"use strict";
|
|
797
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
798
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
799
|
+
};
|
|
800
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
801
|
+
exports2.AnthropicProvider = void 0;
|
|
802
|
+
var sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
803
|
+
var system_prompt_1 = require_system_prompt();
|
|
804
|
+
var retry_1 = require_retry();
|
|
805
|
+
var AnthropicProvider2 = class {
|
|
806
|
+
name = "anthropic";
|
|
807
|
+
client;
|
|
808
|
+
constructor(apiKey) {
|
|
809
|
+
this.client = new sdk_1.default({ apiKey });
|
|
810
|
+
}
|
|
811
|
+
async chat(messages) {
|
|
812
|
+
const filtered = messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
|
|
813
|
+
const response = await (0, retry_1.withRetry)(() => this.client.messages.create({
|
|
814
|
+
model: "claude-sonnet-4-6",
|
|
815
|
+
max_tokens: 8192,
|
|
816
|
+
system: system_prompt_1.SYSTEM_PROMPT,
|
|
817
|
+
messages: filtered
|
|
818
|
+
}));
|
|
819
|
+
const block = response.content[0];
|
|
820
|
+
return {
|
|
821
|
+
content: block.type === "text" ? block.text : "",
|
|
822
|
+
usage: {
|
|
823
|
+
inputTokens: response.usage.input_tokens,
|
|
824
|
+
outputTokens: response.usage.output_tokens
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
async stream(messages, onChunk) {
|
|
829
|
+
const filtered = messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
|
|
830
|
+
let emittedAny = false;
|
|
831
|
+
await (0, retry_1.withRetry)(async () => {
|
|
832
|
+
if (emittedAny)
|
|
833
|
+
throw Object.assign(new Error("stream interrompido ap\xF3s in\xEDcio \u2014 sem retry"), { noRetry: true });
|
|
834
|
+
const stream = this.client.messages.stream({
|
|
835
|
+
model: "claude-sonnet-4-6",
|
|
836
|
+
max_tokens: 8192,
|
|
837
|
+
system: system_prompt_1.SYSTEM_PROMPT,
|
|
838
|
+
messages: filtered
|
|
839
|
+
});
|
|
840
|
+
for await (const chunk of stream) {
|
|
841
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
|
|
842
|
+
emittedAny = true;
|
|
843
|
+
onChunk(chunk.delta.text);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
exports2.AnthropicProvider = AnthropicProvider2;
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
// ../ai/dist/providers/copilot.js
|
|
854
|
+
var require_copilot = __commonJS({
|
|
855
|
+
"../ai/dist/providers/copilot.js"(exports2) {
|
|
856
|
+
"use strict";
|
|
857
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
858
|
+
exports2.CopilotProvider = void 0;
|
|
859
|
+
var system_prompt_1 = require_system_prompt();
|
|
860
|
+
var retry_1 = require_retry();
|
|
861
|
+
var COPILOT_ENDPOINT = "https://api.githubcopilot.com/chat/completions";
|
|
862
|
+
var CopilotProvider2 = class {
|
|
863
|
+
name = "copilot";
|
|
864
|
+
token;
|
|
865
|
+
constructor(token) {
|
|
866
|
+
this.token = token;
|
|
867
|
+
}
|
|
868
|
+
buildMessages(messages) {
|
|
869
|
+
const result = [
|
|
870
|
+
{ role: "system", content: system_prompt_1.SYSTEM_PROMPT }
|
|
871
|
+
];
|
|
872
|
+
for (const m of messages) {
|
|
873
|
+
if (m.role !== "system") {
|
|
874
|
+
result.push({ role: m.role, content: m.content });
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return result;
|
|
878
|
+
}
|
|
879
|
+
async chat(messages) {
|
|
880
|
+
return (0, retry_1.withRetry)(async () => {
|
|
881
|
+
const response = await fetch(COPILOT_ENDPOINT, {
|
|
882
|
+
method: "POST",
|
|
883
|
+
headers: {
|
|
884
|
+
"Authorization": `Bearer ${this.token}`,
|
|
885
|
+
"Content-Type": "application/json",
|
|
886
|
+
"Copilot-Integration-Id": "iacmp-cli"
|
|
887
|
+
},
|
|
888
|
+
body: JSON.stringify({
|
|
889
|
+
model: "gpt-4o",
|
|
890
|
+
messages: this.buildMessages(messages),
|
|
891
|
+
max_tokens: 8192
|
|
892
|
+
})
|
|
893
|
+
});
|
|
894
|
+
if (!response.ok) {
|
|
895
|
+
const text = await response.text();
|
|
896
|
+
throw Object.assign(new Error(`Copilot API error ${response.status}: ${text}`), { status: response.status });
|
|
897
|
+
}
|
|
898
|
+
const data = await response.json();
|
|
899
|
+
return {
|
|
900
|
+
content: data.choices[0].message.content,
|
|
901
|
+
usage: {
|
|
902
|
+
inputTokens: data.usage.prompt_tokens,
|
|
903
|
+
outputTokens: data.usage.completion_tokens
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
async stream(messages, onChunk) {
|
|
909
|
+
let emittedAny = false;
|
|
910
|
+
await (0, retry_1.withRetry)(async () => {
|
|
911
|
+
if (emittedAny)
|
|
912
|
+
throw Object.assign(new Error("stream interrompido ap\xF3s in\xEDcio \u2014 sem retry"), { noRetry: true });
|
|
913
|
+
const response = await fetch(COPILOT_ENDPOINT, {
|
|
914
|
+
method: "POST",
|
|
915
|
+
headers: {
|
|
916
|
+
"Authorization": `Bearer ${this.token}`,
|
|
917
|
+
"Content-Type": "application/json",
|
|
918
|
+
"Copilot-Integration-Id": "iacmp-cli"
|
|
919
|
+
},
|
|
920
|
+
body: JSON.stringify({
|
|
921
|
+
model: "gpt-4o",
|
|
922
|
+
messages: this.buildMessages(messages),
|
|
923
|
+
max_tokens: 8192,
|
|
924
|
+
stream: true
|
|
925
|
+
})
|
|
926
|
+
});
|
|
927
|
+
if (!response.ok) {
|
|
928
|
+
const text = await response.text();
|
|
929
|
+
throw Object.assign(new Error(`Copilot API error ${response.status}: ${text}`), { status: response.status });
|
|
930
|
+
}
|
|
931
|
+
if (!response.body) {
|
|
932
|
+
throw new Error("Copilot: response body vazio");
|
|
933
|
+
}
|
|
934
|
+
const reader = response.body.getReader();
|
|
935
|
+
const decoder = new TextDecoder();
|
|
936
|
+
while (true) {
|
|
937
|
+
const { done, value } = await reader.read();
|
|
938
|
+
if (done)
|
|
939
|
+
break;
|
|
940
|
+
const raw = decoder.decode(value);
|
|
941
|
+
const lines = raw.split("\n").filter((l) => l.startsWith("data: "));
|
|
942
|
+
for (const line of lines) {
|
|
943
|
+
const json = line.slice(6).trim();
|
|
944
|
+
if (json === "[DONE]")
|
|
945
|
+
return;
|
|
946
|
+
try {
|
|
947
|
+
const parsed = JSON.parse(json);
|
|
948
|
+
const text = parsed.choices?.[0]?.delta?.content ?? "";
|
|
949
|
+
if (text) {
|
|
950
|
+
emittedAny = true;
|
|
951
|
+
onChunk(text);
|
|
952
|
+
}
|
|
953
|
+
} catch {
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
exports2.CopilotProvider = CopilotProvider2;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// ../ai/dist/parser/code-extractor.js
|
|
965
|
+
var require_code_extractor = __commonJS({
|
|
966
|
+
"../ai/dist/parser/code-extractor.js"(exports2) {
|
|
967
|
+
"use strict";
|
|
968
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
969
|
+
exports2.MAX_FILE_BYTES = exports2.MAX_DELETIONS = exports2.MAX_FILES = void 0;
|
|
970
|
+
exports2.extractResponse = extractResponse2;
|
|
971
|
+
exports2.MAX_FILES = 50;
|
|
972
|
+
exports2.MAX_DELETIONS = 100;
|
|
973
|
+
exports2.MAX_FILE_BYTES = 256 * 1024;
|
|
974
|
+
function extractResponse2(raw) {
|
|
975
|
+
const trimmed = raw.trim();
|
|
976
|
+
const direct = tryParse(trimmed);
|
|
977
|
+
if (direct)
|
|
978
|
+
return validate(direct);
|
|
979
|
+
const codeBlock = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
980
|
+
if (codeBlock) {
|
|
981
|
+
const parsed = tryParse(codeBlock[1].trim());
|
|
982
|
+
if (parsed)
|
|
983
|
+
return validate(parsed);
|
|
984
|
+
}
|
|
985
|
+
const firstBrace = trimmed.indexOf("{");
|
|
986
|
+
const lastBrace = trimmed.lastIndexOf("}");
|
|
987
|
+
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
|
988
|
+
const slice = trimmed.slice(firstBrace, lastBrace + 1);
|
|
989
|
+
const parsed = tryParse(slice);
|
|
990
|
+
if (parsed)
|
|
991
|
+
return validate(parsed);
|
|
992
|
+
}
|
|
993
|
+
throw new Error("N\xE3o foi poss\xEDvel extrair JSON do response da IA.\nResponse recebido:\n" + trimmed.slice(0, 300) + (trimmed.length > 300 ? "..." : ""));
|
|
994
|
+
}
|
|
995
|
+
function tryParse(text) {
|
|
996
|
+
try {
|
|
997
|
+
return JSON.parse(text);
|
|
998
|
+
} catch {
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
function validate(obj) {
|
|
1003
|
+
if (typeof obj !== "object" || obj === null) {
|
|
1004
|
+
throw new Error("Response da IA n\xE3o \xE9 um objeto JSON v\xE1lido.");
|
|
1005
|
+
}
|
|
1006
|
+
const o = obj;
|
|
1007
|
+
if (typeof o["explanation"] !== "string") {
|
|
1008
|
+
throw new Error('Response da IA est\xE1 faltando o campo "explanation" (string).');
|
|
1009
|
+
}
|
|
1010
|
+
if (!Array.isArray(o["files"])) {
|
|
1011
|
+
throw new Error('Response da IA est\xE1 faltando o campo "files" (array).');
|
|
1012
|
+
}
|
|
1013
|
+
const rawFiles = o["files"];
|
|
1014
|
+
if (rawFiles.length > exports2.MAX_FILES) {
|
|
1015
|
+
throw new Error(`Response da IA excede o limite de arquivos: ${rawFiles.length} > ${exports2.MAX_FILES}. Reduza o escopo da requisi\xE7\xE3o.`);
|
|
1016
|
+
}
|
|
1017
|
+
const files = [];
|
|
1018
|
+
for (const f of rawFiles) {
|
|
1019
|
+
if (typeof f !== "object" || f === null) {
|
|
1020
|
+
throw new Error('Cada item em "files" deve ser um objeto com "path" e "content".');
|
|
1021
|
+
}
|
|
1022
|
+
const file = f;
|
|
1023
|
+
if (typeof file["path"] !== "string" || typeof file["content"] !== "string") {
|
|
1024
|
+
throw new Error('Cada arquivo deve ter "path" (string) e "content" (string).');
|
|
1025
|
+
}
|
|
1026
|
+
const byteLen = Buffer.byteLength(file["content"], "utf-8");
|
|
1027
|
+
if (byteLen > exports2.MAX_FILE_BYTES) {
|
|
1028
|
+
throw new Error(`Arquivo "${file["path"]}" excede o limite de tamanho: ${byteLen} bytes > ${exports2.MAX_FILE_BYTES} bytes. Divida o conte\xFAdo em arquivos menores.`);
|
|
1029
|
+
}
|
|
1030
|
+
files.push({ path: file["path"], content: file["content"] });
|
|
1031
|
+
}
|
|
1032
|
+
const rawDeletions = Array.isArray(o["deletions"]) ? o["deletions"].filter((s) => typeof s === "string") : [];
|
|
1033
|
+
if (rawDeletions.length > exports2.MAX_DELETIONS) {
|
|
1034
|
+
throw new Error(`Response da IA excede o limite de remo\xE7\xF5es: ${rawDeletions.length} > ${exports2.MAX_DELETIONS}. Reduza o escopo da requisi\xE7\xE3o.`);
|
|
1035
|
+
}
|
|
1036
|
+
return {
|
|
1037
|
+
explanation: o["explanation"],
|
|
1038
|
+
files,
|
|
1039
|
+
deletions: rawDeletions,
|
|
1040
|
+
nextSteps: Array.isArray(o["nextSteps"]) ? o["nextSteps"].filter((s) => typeof s === "string") : [],
|
|
1041
|
+
warnings: Array.isArray(o["warnings"]) ? o["warnings"].filter((s) => typeof s === "string") : []
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// ../ai/dist/parser/validator.js
|
|
1048
|
+
var require_validator = __commonJS({
|
|
1049
|
+
"../ai/dist/parser/validator.js"(exports2) {
|
|
1050
|
+
"use strict";
|
|
1051
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1052
|
+
if (k2 === void 0) k2 = k;
|
|
1053
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1054
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1055
|
+
desc = { enumerable: true, get: function() {
|
|
1056
|
+
return m[k];
|
|
1057
|
+
} };
|
|
1058
|
+
}
|
|
1059
|
+
Object.defineProperty(o, k2, desc);
|
|
1060
|
+
}) : (function(o, m, k, k2) {
|
|
1061
|
+
if (k2 === void 0) k2 = k;
|
|
1062
|
+
o[k2] = m[k];
|
|
1063
|
+
}));
|
|
1064
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1065
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1066
|
+
}) : function(o, v) {
|
|
1067
|
+
o["default"] = v;
|
|
1068
|
+
});
|
|
1069
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1070
|
+
var ownKeys = function(o) {
|
|
1071
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1072
|
+
var ar = [];
|
|
1073
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1074
|
+
return ar;
|
|
1075
|
+
};
|
|
1076
|
+
return ownKeys(o);
|
|
1077
|
+
};
|
|
1078
|
+
return function(mod) {
|
|
1079
|
+
if (mod && mod.__esModule) return mod;
|
|
1080
|
+
var result = {};
|
|
1081
|
+
if (mod != null) {
|
|
1082
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1083
|
+
}
|
|
1084
|
+
__setModuleDefault(result, mod);
|
|
1085
|
+
return result;
|
|
1086
|
+
};
|
|
1087
|
+
})();
|
|
1088
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1089
|
+
exports2.validateTypeScript = validateTypeScript2;
|
|
1090
|
+
var fs2 = __importStar(require("fs"));
|
|
1091
|
+
var path2 = __importStar(require("path"));
|
|
1092
|
+
var os = __importStar(require("os"));
|
|
1093
|
+
var child_process_1 = require("child_process");
|
|
1094
|
+
function validateTypeScript2(files, projectDir) {
|
|
1095
|
+
const tmpDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "iacmp-validate-"));
|
|
1096
|
+
try {
|
|
1097
|
+
for (const file of files) {
|
|
1098
|
+
const filePath = path2.join(tmpDir, path2.basename(file.path));
|
|
1099
|
+
fs2.writeFileSync(filePath, file.content, "utf-8");
|
|
1100
|
+
}
|
|
1101
|
+
const coreTypesPath = (function findCoreTypes() {
|
|
1102
|
+
const candidates2 = [
|
|
1103
|
+
// monorepo local (dev)
|
|
1104
|
+
path2.resolve(__dirname, "..", "..", "..", "core", "dist"),
|
|
1105
|
+
// node_modules do CLI instalado globalmente
|
|
1106
|
+
path2.resolve(__dirname, "..", "..", "node_modules", "@iacmp", "core", "dist"),
|
|
1107
|
+
// node_modules do projeto do usuário
|
|
1108
|
+
path2.join(projectDir, "node_modules", "@iacmp", "core", "dist")
|
|
1109
|
+
];
|
|
1110
|
+
for (const c of candidates2) {
|
|
1111
|
+
if (fs2.existsSync(path2.join(c, "index.d.ts")))
|
|
1112
|
+
return c;
|
|
1113
|
+
}
|
|
1114
|
+
return null;
|
|
1115
|
+
})();
|
|
1116
|
+
const tsconfigPaths = {};
|
|
1117
|
+
if (coreTypesPath) {
|
|
1118
|
+
tsconfigPaths["@iacmp/core"] = [path2.join(coreTypesPath, "index.d.ts")];
|
|
1119
|
+
tsconfigPaths["@iacmp/core/*"] = [path2.join(coreTypesPath, "*")];
|
|
1120
|
+
}
|
|
1121
|
+
const tsconfig = {
|
|
1122
|
+
compilerOptions: {
|
|
1123
|
+
target: "ES2022",
|
|
1124
|
+
module: "commonjs",
|
|
1125
|
+
moduleResolution: "node",
|
|
1126
|
+
esModuleInterop: true,
|
|
1127
|
+
strict: false,
|
|
1128
|
+
skipLibCheck: true,
|
|
1129
|
+
noEmit: true,
|
|
1130
|
+
baseUrl: "/",
|
|
1131
|
+
paths: tsconfigPaths
|
|
1132
|
+
},
|
|
1133
|
+
include: [path2.join(tmpDir, "*.ts")]
|
|
1134
|
+
};
|
|
1135
|
+
fs2.writeFileSync(path2.join(tmpDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2), "utf-8");
|
|
1136
|
+
const candidates = [
|
|
1137
|
+
path2.join(projectDir, "node_modules", ".bin", "tsc"),
|
|
1138
|
+
path2.resolve(__dirname, "..", "..", "node_modules", ".bin", "tsc"),
|
|
1139
|
+
path2.resolve(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "tsc")
|
|
1140
|
+
];
|
|
1141
|
+
const tscPath = candidates.find((c) => fs2.existsSync(c));
|
|
1142
|
+
if (!tscPath) {
|
|
1143
|
+
return { valid: true, errors: [] };
|
|
1144
|
+
}
|
|
1145
|
+
(0, child_process_1.execSync)(`${tscPath} --noEmit --project ${path2.join(tmpDir, "tsconfig.json")}`, {
|
|
1146
|
+
cwd: tmpDir,
|
|
1147
|
+
stdio: "pipe"
|
|
1148
|
+
});
|
|
1149
|
+
return { valid: true, errors: [] };
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
const error = err;
|
|
1152
|
+
if (error.code === "ENOENT") {
|
|
1153
|
+
return { valid: true, errors: [] };
|
|
1154
|
+
}
|
|
1155
|
+
const output = (error.stdout?.toString() ?? "") + (error.stderr?.toString() ?? "");
|
|
1156
|
+
const errors = output.split("\n").filter((l) => l.trim().length > 0).slice(0, 20);
|
|
1157
|
+
return { valid: false, errors };
|
|
1158
|
+
} finally {
|
|
1159
|
+
try {
|
|
1160
|
+
fs2.rmSync(tmpDir, { recursive: true, force: true });
|
|
1161
|
+
} catch {
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
// ../ai/dist/chat/session.js
|
|
1169
|
+
var require_session = __commonJS({
|
|
1170
|
+
"../ai/dist/chat/session.js"(exports2) {
|
|
1171
|
+
"use strict";
|
|
1172
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1173
|
+
exports2.ChatSession = void 0;
|
|
1174
|
+
var ChatSession2 = class {
|
|
1175
|
+
messages = [];
|
|
1176
|
+
addUserMessage(content) {
|
|
1177
|
+
this.messages.push({ role: "user", content });
|
|
1178
|
+
}
|
|
1179
|
+
addAssistantMessage(content) {
|
|
1180
|
+
this.messages.push({ role: "assistant", content });
|
|
1181
|
+
}
|
|
1182
|
+
getMessages() {
|
|
1183
|
+
return [...this.messages];
|
|
1184
|
+
}
|
|
1185
|
+
removeLast() {
|
|
1186
|
+
this.messages.pop();
|
|
1187
|
+
}
|
|
1188
|
+
clear() {
|
|
1189
|
+
this.messages = [];
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
exports2.ChatSession = ChatSession2;
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
// ../ai/dist/chat/renderer.js
|
|
1197
|
+
var require_renderer = __commonJS({
|
|
1198
|
+
"../ai/dist/chat/renderer.js"(exports2) {
|
|
1199
|
+
"use strict";
|
|
1200
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1201
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1202
|
+
};
|
|
1203
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1204
|
+
exports2.printThinking = printThinking;
|
|
1205
|
+
exports2.stopThinking = stopThinking;
|
|
1206
|
+
exports2.printExplanation = printExplanation2;
|
|
1207
|
+
exports2.printWarnings = printWarnings2;
|
|
1208
|
+
exports2.printNextSteps = printNextSteps2;
|
|
1209
|
+
exports2.printStreamChunk = printStreamChunk;
|
|
1210
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
1211
|
+
var ora_1 = __importDefault(require("ora"));
|
|
1212
|
+
var activeSpinner = null;
|
|
1213
|
+
function printThinking() {
|
|
1214
|
+
activeSpinner = (0, ora_1.default)({ text: "Gerando stack...", color: "cyan" }).start();
|
|
1215
|
+
return activeSpinner;
|
|
1216
|
+
}
|
|
1217
|
+
function stopThinking() {
|
|
1218
|
+
if (activeSpinner) {
|
|
1219
|
+
activeSpinner.stop();
|
|
1220
|
+
activeSpinner = null;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function printExplanation2(text) {
|
|
1224
|
+
console.log("\n" + chalk_1.default.cyan.bold("\u2500\u2500\u2500 Explica\xE7\xE3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1225
|
+
console.log(chalk_1.default.white(text));
|
|
1226
|
+
console.log(chalk_1.default.cyan("\u2500".repeat(50)));
|
|
1227
|
+
}
|
|
1228
|
+
function printWarnings2(warnings) {
|
|
1229
|
+
if (warnings.length === 0)
|
|
1230
|
+
return;
|
|
1231
|
+
console.log("\n" + chalk_1.default.yellow.bold("Avisos:"));
|
|
1232
|
+
for (const w of warnings) {
|
|
1233
|
+
console.log(chalk_1.default.yellow(` ! ${w}`));
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
function printNextSteps2(steps) {
|
|
1237
|
+
if (steps.length === 0)
|
|
1238
|
+
return;
|
|
1239
|
+
console.log("\n" + chalk_1.default.dim("Pr\xF3ximos passos:"));
|
|
1240
|
+
for (const s of steps) {
|
|
1241
|
+
console.log(chalk_1.default.dim(` $ ${s}`));
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
function printStreamChunk(chunk) {
|
|
1245
|
+
process.stdout.write(chunk);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
// ../ai/dist/tools/diff-renderer.js
|
|
1251
|
+
var require_diff_renderer = __commonJS({
|
|
1252
|
+
"../ai/dist/tools/diff-renderer.js"(exports2) {
|
|
1253
|
+
"use strict";
|
|
1254
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1255
|
+
if (k2 === void 0) k2 = k;
|
|
1256
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1257
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1258
|
+
desc = { enumerable: true, get: function() {
|
|
1259
|
+
return m[k];
|
|
1260
|
+
} };
|
|
1261
|
+
}
|
|
1262
|
+
Object.defineProperty(o, k2, desc);
|
|
1263
|
+
}) : (function(o, m, k, k2) {
|
|
1264
|
+
if (k2 === void 0) k2 = k;
|
|
1265
|
+
o[k2] = m[k];
|
|
1266
|
+
}));
|
|
1267
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1268
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1269
|
+
}) : function(o, v) {
|
|
1270
|
+
o["default"] = v;
|
|
1271
|
+
});
|
|
1272
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1273
|
+
var ownKeys = function(o) {
|
|
1274
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1275
|
+
var ar = [];
|
|
1276
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1277
|
+
return ar;
|
|
1278
|
+
};
|
|
1279
|
+
return ownKeys(o);
|
|
1280
|
+
};
|
|
1281
|
+
return function(mod) {
|
|
1282
|
+
if (mod && mod.__esModule) return mod;
|
|
1283
|
+
var result = {};
|
|
1284
|
+
if (mod != null) {
|
|
1285
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1286
|
+
}
|
|
1287
|
+
__setModuleDefault(result, mod);
|
|
1288
|
+
return result;
|
|
1289
|
+
};
|
|
1290
|
+
})();
|
|
1291
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1292
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1293
|
+
};
|
|
1294
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1295
|
+
exports2.renderAndConfirm = renderAndConfirm;
|
|
1296
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
1297
|
+
var DiffLib = __importStar(require("diff"));
|
|
1298
|
+
async function renderAndConfirm(diffs, ask2) {
|
|
1299
|
+
console.log("");
|
|
1300
|
+
for (const file of diffs) {
|
|
1301
|
+
const isNew = file.oldContent === null;
|
|
1302
|
+
const label = isNew ? ` ${chalk_1.default.bold(file.path)} ${chalk_1.default.blue("[novo]")}` : ` ${chalk_1.default.bold(file.path)} ${chalk_1.default.yellow("[modificado]")}`;
|
|
1303
|
+
console.log("\n" + label);
|
|
1304
|
+
console.log(chalk_1.default.dim("\u2500".repeat(62)));
|
|
1305
|
+
if (isNew) {
|
|
1306
|
+
for (const line of file.newContent.split("\n")) {
|
|
1307
|
+
console.log(chalk_1.default.green(`+ ${line}`));
|
|
1308
|
+
}
|
|
1309
|
+
} else {
|
|
1310
|
+
const changes = DiffLib.diffLines(file.oldContent, file.newContent);
|
|
1311
|
+
for (const change of changes) {
|
|
1312
|
+
const lines = change.value.split("\n");
|
|
1313
|
+
if (lines[lines.length - 1] === "")
|
|
1314
|
+
lines.pop();
|
|
1315
|
+
for (const line of lines) {
|
|
1316
|
+
if (change.added) {
|
|
1317
|
+
console.log(chalk_1.default.green(`+ ${line}`));
|
|
1318
|
+
} else if (change.removed) {
|
|
1319
|
+
console.log(chalk_1.default.red(`- ${line}`));
|
|
1320
|
+
} else {
|
|
1321
|
+
console.log(` ${line}`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
console.log(chalk_1.default.dim("\u2500".repeat(62)));
|
|
1327
|
+
}
|
|
1328
|
+
const newCount = diffs.filter((d) => d.oldContent === null).length;
|
|
1329
|
+
const modCount = diffs.filter((d) => d.oldContent !== null).length;
|
|
1330
|
+
const parts = [];
|
|
1331
|
+
if (modCount > 0)
|
|
1332
|
+
parts.push(`${modCount} modificado(s)`);
|
|
1333
|
+
if (newCount > 0)
|
|
1334
|
+
parts.push(`${newCount} novo(s)`);
|
|
1335
|
+
console.log(chalk_1.default.dim("\n " + parts.join(" \xB7 ") + "\n"));
|
|
1336
|
+
const answer = await ask2("Aplicar mudan\xE7as? [y/n] ");
|
|
1337
|
+
return answer.toLowerCase() === "y";
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
// ../ai/dist/tools/safe-path.js
|
|
1343
|
+
var require_safe_path = __commonJS({
|
|
1344
|
+
"../ai/dist/tools/safe-path.js"(exports2) {
|
|
1345
|
+
"use strict";
|
|
1346
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1347
|
+
if (k2 === void 0) k2 = k;
|
|
1348
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1349
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1350
|
+
desc = { enumerable: true, get: function() {
|
|
1351
|
+
return m[k];
|
|
1352
|
+
} };
|
|
1353
|
+
}
|
|
1354
|
+
Object.defineProperty(o, k2, desc);
|
|
1355
|
+
}) : (function(o, m, k, k2) {
|
|
1356
|
+
if (k2 === void 0) k2 = k;
|
|
1357
|
+
o[k2] = m[k];
|
|
1358
|
+
}));
|
|
1359
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1360
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1361
|
+
}) : function(o, v) {
|
|
1362
|
+
o["default"] = v;
|
|
1363
|
+
});
|
|
1364
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1365
|
+
var ownKeys = function(o) {
|
|
1366
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1367
|
+
var ar = [];
|
|
1368
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1369
|
+
return ar;
|
|
1370
|
+
};
|
|
1371
|
+
return ownKeys(o);
|
|
1372
|
+
};
|
|
1373
|
+
return function(mod) {
|
|
1374
|
+
if (mod && mod.__esModule) return mod;
|
|
1375
|
+
var result = {};
|
|
1376
|
+
if (mod != null) {
|
|
1377
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1378
|
+
}
|
|
1379
|
+
__setModuleDefault(result, mod);
|
|
1380
|
+
return result;
|
|
1381
|
+
};
|
|
1382
|
+
})();
|
|
1383
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1384
|
+
exports2.NATIVE_PROVIDERS = void 0;
|
|
1385
|
+
exports2.safeJoin = safeJoin;
|
|
1386
|
+
exports2.isWithin = isWithin;
|
|
1387
|
+
exports2.errMessage = errMessage;
|
|
1388
|
+
exports2.assertValidStackName = assertValidStackName;
|
|
1389
|
+
exports2.assertValidProvider = assertValidProvider;
|
|
1390
|
+
var path2 = __importStar(require("path"));
|
|
1391
|
+
exports2.NATIVE_PROVIDERS = ["aws", "azure", "gcp", "terraform"];
|
|
1392
|
+
var STACK_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
1393
|
+
var PROVIDER_PATTERN = /^[a-z0-9_-]+$/;
|
|
1394
|
+
function safeJoin(baseDir, relativePath) {
|
|
1395
|
+
if (typeof relativePath !== "string" || relativePath.length === 0) {
|
|
1396
|
+
throw new Error("Caminho vazio ou inv\xE1lido recebido.");
|
|
1397
|
+
}
|
|
1398
|
+
if (path2.isAbsolute(relativePath)) {
|
|
1399
|
+
throw new Error(`Caminho absoluto rejeitado por seguran\xE7a: "${relativePath}". Use sempre caminhos relativos ao projeto.`);
|
|
1400
|
+
}
|
|
1401
|
+
const baseResolved = path2.resolve(baseDir);
|
|
1402
|
+
const fullResolved = path2.resolve(baseResolved, relativePath);
|
|
1403
|
+
if (fullResolved !== baseResolved && !fullResolved.startsWith(baseResolved + path2.sep)) {
|
|
1404
|
+
throw new Error(`Caminho fora do diret\xF3rio do projeto rejeitado: "${relativePath}" (resolveria para "${fullResolved}"). Path traversal n\xE3o \xE9 permitido.`);
|
|
1405
|
+
}
|
|
1406
|
+
return fullResolved;
|
|
1407
|
+
}
|
|
1408
|
+
function isWithin(baseDir, candidatePath) {
|
|
1409
|
+
try {
|
|
1410
|
+
const baseResolved = path2.resolve(baseDir);
|
|
1411
|
+
const fullResolved = path2.isAbsolute(candidatePath) ? path2.resolve(candidatePath) : path2.resolve(baseResolved, candidatePath);
|
|
1412
|
+
return fullResolved === baseResolved || fullResolved.startsWith(baseResolved + path2.sep);
|
|
1413
|
+
} catch {
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
function errMessage(err) {
|
|
1418
|
+
if (err instanceof Error)
|
|
1419
|
+
return err.message;
|
|
1420
|
+
if (typeof err === "string")
|
|
1421
|
+
return err;
|
|
1422
|
+
if (err === null)
|
|
1423
|
+
return "null";
|
|
1424
|
+
if (err === void 0)
|
|
1425
|
+
return "undefined";
|
|
1426
|
+
if (typeof err === "object" && "message" in err) {
|
|
1427
|
+
const m = err.message;
|
|
1428
|
+
if (typeof m === "string")
|
|
1429
|
+
return m;
|
|
1430
|
+
}
|
|
1431
|
+
try {
|
|
1432
|
+
const s = JSON.stringify(err);
|
|
1433
|
+
return typeof s === "string" ? s : String(err);
|
|
1434
|
+
} catch {
|
|
1435
|
+
return String(err);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
function assertValidStackName(stackName) {
|
|
1439
|
+
if (typeof stackName !== "string" || !STACK_NAME_PATTERN.test(stackName)) {
|
|
1440
|
+
throw new Error(`Nome de stack inv\xE1lido: "${stackName}". Use apenas letras, n\xFAmeros, h\xEDfen e underscore (regex: /^[A-Za-z0-9_-]+$/).`);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function assertValidProvider(provider, allowlist) {
|
|
1444
|
+
if (typeof provider !== "string" || !PROVIDER_PATTERN.test(provider)) {
|
|
1445
|
+
throw new Error(`Nome de provider inv\xE1lido: "${provider}". Use apenas letras min\xFAsculas, n\xFAmeros, h\xEDfen e underscore.`);
|
|
1446
|
+
}
|
|
1447
|
+
const allowed = allowlist ?? exports2.NATIVE_PROVIDERS;
|
|
1448
|
+
if (!allowed.includes(provider)) {
|
|
1449
|
+
throw new Error(`Provider "${provider}" n\xE3o permitido. Allowlist: ${allowed.join(", ")}.`);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// ../ai/dist/tools/file-writer.js
|
|
1456
|
+
var require_file_writer = __commonJS({
|
|
1457
|
+
"../ai/dist/tools/file-writer.js"(exports2) {
|
|
1458
|
+
"use strict";
|
|
1459
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1460
|
+
if (k2 === void 0) k2 = k;
|
|
1461
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1462
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1463
|
+
desc = { enumerable: true, get: function() {
|
|
1464
|
+
return m[k];
|
|
1465
|
+
} };
|
|
1466
|
+
}
|
|
1467
|
+
Object.defineProperty(o, k2, desc);
|
|
1468
|
+
}) : (function(o, m, k, k2) {
|
|
1469
|
+
if (k2 === void 0) k2 = k;
|
|
1470
|
+
o[k2] = m[k];
|
|
1471
|
+
}));
|
|
1472
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1473
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1474
|
+
}) : function(o, v) {
|
|
1475
|
+
o["default"] = v;
|
|
1476
|
+
});
|
|
1477
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1478
|
+
var ownKeys = function(o) {
|
|
1479
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1480
|
+
var ar = [];
|
|
1481
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1482
|
+
return ar;
|
|
1483
|
+
};
|
|
1484
|
+
return ownKeys(o);
|
|
1485
|
+
};
|
|
1486
|
+
return function(mod) {
|
|
1487
|
+
if (mod && mod.__esModule) return mod;
|
|
1488
|
+
var result = {};
|
|
1489
|
+
if (mod != null) {
|
|
1490
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1491
|
+
}
|
|
1492
|
+
__setModuleDefault(result, mod);
|
|
1493
|
+
return result;
|
|
1494
|
+
};
|
|
1495
|
+
})();
|
|
1496
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1497
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1498
|
+
};
|
|
1499
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1500
|
+
exports2.writeGeneratedFiles = writeGeneratedFiles2;
|
|
1501
|
+
var fs2 = __importStar(require("fs"));
|
|
1502
|
+
var path2 = __importStar(require("path"));
|
|
1503
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
1504
|
+
var diff_renderer_1 = require_diff_renderer();
|
|
1505
|
+
var safe_path_1 = require_safe_path();
|
|
1506
|
+
async function writeGeneratedFiles2(files, projectDir, dryRun2, ask2) {
|
|
1507
|
+
const validated = [];
|
|
1508
|
+
for (const file of files) {
|
|
1509
|
+
const fullPath = (0, safe_path_1.safeJoin)(projectDir, file.path);
|
|
1510
|
+
validated.push({ file, fullPath });
|
|
1511
|
+
}
|
|
1512
|
+
if (dryRun2) {
|
|
1513
|
+
console.log(chalk_1.default.dim("\n[dry-run] Arquivos que seriam gerados:\n"));
|
|
1514
|
+
for (const { file } of validated) {
|
|
1515
|
+
console.log(chalk_1.default.cyan(` ${file.path}`));
|
|
1516
|
+
console.log(chalk_1.default.dim("\u2500".repeat(62)));
|
|
1517
|
+
for (const line of file.content.split("\n")) {
|
|
1518
|
+
console.log(chalk_1.default.green(`+ ${line}`));
|
|
1519
|
+
}
|
|
1520
|
+
console.log(chalk_1.default.dim("\u2500".repeat(62)));
|
|
1521
|
+
console.log("");
|
|
1522
|
+
}
|
|
1523
|
+
console.log(chalk_1.default.dim("[dry-run] Nenhum arquivo foi salvo.\n"));
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const diffs = validated.map(({ file, fullPath }) => {
|
|
1527
|
+
let oldContent = null;
|
|
1528
|
+
try {
|
|
1529
|
+
oldContent = fs2.readFileSync(fullPath, "utf-8");
|
|
1530
|
+
} catch {
|
|
1531
|
+
}
|
|
1532
|
+
return { path: file.path, oldContent, newContent: file.content };
|
|
1533
|
+
});
|
|
1534
|
+
const confirmed = await (0, diff_renderer_1.renderAndConfirm)(diffs, ask2);
|
|
1535
|
+
if (!confirmed) {
|
|
1536
|
+
console.log(chalk_1.default.dim("\n Opera\xE7\xE3o cancelada. Nenhum arquivo foi alterado.\n"));
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
for (const { file, fullPath } of validated) {
|
|
1540
|
+
fs2.mkdirSync(path2.dirname(fullPath), { recursive: true });
|
|
1541
|
+
fs2.writeFileSync(fullPath, file.content, "utf-8");
|
|
1542
|
+
console.log(chalk_1.default.green(` \u2713 ${file.path}`));
|
|
1543
|
+
}
|
|
1544
|
+
console.log("");
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
// ../ai/dist/tools/file-deleter.js
|
|
1550
|
+
var require_file_deleter = __commonJS({
|
|
1551
|
+
"../ai/dist/tools/file-deleter.js"(exports2) {
|
|
1552
|
+
"use strict";
|
|
1553
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1554
|
+
if (k2 === void 0) k2 = k;
|
|
1555
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1556
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1557
|
+
desc = { enumerable: true, get: function() {
|
|
1558
|
+
return m[k];
|
|
1559
|
+
} };
|
|
1560
|
+
}
|
|
1561
|
+
Object.defineProperty(o, k2, desc);
|
|
1562
|
+
}) : (function(o, m, k, k2) {
|
|
1563
|
+
if (k2 === void 0) k2 = k;
|
|
1564
|
+
o[k2] = m[k];
|
|
1565
|
+
}));
|
|
1566
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1567
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1568
|
+
}) : function(o, v) {
|
|
1569
|
+
o["default"] = v;
|
|
1570
|
+
});
|
|
1571
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1572
|
+
var ownKeys = function(o) {
|
|
1573
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1574
|
+
var ar = [];
|
|
1575
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1576
|
+
return ar;
|
|
1577
|
+
};
|
|
1578
|
+
return ownKeys(o);
|
|
1579
|
+
};
|
|
1580
|
+
return function(mod) {
|
|
1581
|
+
if (mod && mod.__esModule) return mod;
|
|
1582
|
+
var result = {};
|
|
1583
|
+
if (mod != null) {
|
|
1584
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1585
|
+
}
|
|
1586
|
+
__setModuleDefault(result, mod);
|
|
1587
|
+
return result;
|
|
1588
|
+
};
|
|
1589
|
+
})();
|
|
1590
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1591
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1592
|
+
};
|
|
1593
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1594
|
+
exports2.deleteFiles = deleteFiles2;
|
|
1595
|
+
var fs2 = __importStar(require("fs"));
|
|
1596
|
+
var path2 = __importStar(require("path"));
|
|
1597
|
+
var cp = __importStar(require("child_process"));
|
|
1598
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
1599
|
+
var safe_path_1 = require_safe_path();
|
|
1600
|
+
function synthOutPaths(filePath, projectDir) {
|
|
1601
|
+
const basename = path2.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1602
|
+
const root = path2.join(projectDir, "synth-out");
|
|
1603
|
+
if (!fs2.existsSync(root))
|
|
1604
|
+
return [];
|
|
1605
|
+
const found = [];
|
|
1606
|
+
const check = (dir) => {
|
|
1607
|
+
for (const ext of [".json", ".tf"]) {
|
|
1608
|
+
const c = path2.join(dir, `${basename}${ext}`);
|
|
1609
|
+
if (fs2.existsSync(c) && fs2.statSync(c).isFile())
|
|
1610
|
+
found.push(c);
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
check(root);
|
|
1614
|
+
try {
|
|
1615
|
+
for (const entry of fs2.readdirSync(root, { withFileTypes: true })) {
|
|
1616
|
+
if (entry.isDirectory())
|
|
1617
|
+
check(path2.join(root, entry.name));
|
|
1618
|
+
}
|
|
1619
|
+
} catch {
|
|
1620
|
+
}
|
|
1621
|
+
return found;
|
|
1622
|
+
}
|
|
1623
|
+
function escapeRegex(s) {
|
|
1624
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1625
|
+
}
|
|
1626
|
+
function removeReferences(stackName, projectDir) {
|
|
1627
|
+
const modified = [];
|
|
1628
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
1629
|
+
if (!fs2.existsSync(stacksDir))
|
|
1630
|
+
return modified;
|
|
1631
|
+
const escaped = escapeRegex(stackName);
|
|
1632
|
+
const findTs = (dir) => {
|
|
1633
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
1634
|
+
const files = [];
|
|
1635
|
+
for (const e of entries) {
|
|
1636
|
+
const full = path2.join(dir, e.name);
|
|
1637
|
+
if (e.isDirectory())
|
|
1638
|
+
files.push(...findTs(full));
|
|
1639
|
+
else if (e.name.endsWith(".ts"))
|
|
1640
|
+
files.push(full);
|
|
1641
|
+
}
|
|
1642
|
+
return files;
|
|
1643
|
+
};
|
|
1644
|
+
for (const file of findTs(stacksDir)) {
|
|
1645
|
+
const content = fs2.readFileSync(file, "utf-8");
|
|
1646
|
+
const pattern = new RegExp(`(import[^;]*from\\s*['"][^'"]*${escaped}[^'"]*['"]\\s*;?\\n?)|(.*${escaped}.*)`, "g");
|
|
1647
|
+
if (pattern.test(content)) {
|
|
1648
|
+
const cleaned = content.replace(pattern, "").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
1649
|
+
if (cleaned !== content) {
|
|
1650
|
+
fs2.writeFileSync(file, cleaned, "utf-8");
|
|
1651
|
+
modified.push(path2.relative(projectDir, file));
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return modified;
|
|
1656
|
+
}
|
|
1657
|
+
function resolveCliEntrypoint() {
|
|
1658
|
+
try {
|
|
1659
|
+
return require.resolve("iacmp/bin/run.js");
|
|
1660
|
+
} catch {
|
|
1661
|
+
return null;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
async function deleteFiles2(deletions, projectDir, iacProvider2, ask2, options) {
|
|
1665
|
+
(0, safe_path_1.assertValidProvider)(iacProvider2, options?.providerAllowlist);
|
|
1666
|
+
const stackFiles = deletions.filter((f) => f.match(/\.(ts|js)$/) && f.includes("stacks/"));
|
|
1667
|
+
const otherFiles = deletions.filter((f) => !stackFiles.includes(f));
|
|
1668
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1669
|
+
const allToDelete = [];
|
|
1670
|
+
const synthOuts = [];
|
|
1671
|
+
const add = (rel, full) => {
|
|
1672
|
+
if (!seen.has(full)) {
|
|
1673
|
+
seen.add(full);
|
|
1674
|
+
allToDelete.push({ rel, full });
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
for (const filePath of stackFiles) {
|
|
1678
|
+
let full;
|
|
1679
|
+
try {
|
|
1680
|
+
full = (0, safe_path_1.safeJoin)(projectDir, filePath);
|
|
1681
|
+
} catch (err) {
|
|
1682
|
+
console.log(chalk_1.default.yellow(` ! ${(0, safe_path_1.errMessage)(err)} \u2014 ignorando ${filePath}`));
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if (fs2.existsSync(full))
|
|
1686
|
+
add(filePath, full);
|
|
1687
|
+
for (const synthOut of synthOutPaths(filePath, projectDir)) {
|
|
1688
|
+
const rel = path2.relative(projectDir, synthOut);
|
|
1689
|
+
add(rel, synthOut);
|
|
1690
|
+
if (!synthOuts.includes(rel))
|
|
1691
|
+
synthOuts.push(rel);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
for (const filePath of otherFiles) {
|
|
1695
|
+
let full;
|
|
1696
|
+
try {
|
|
1697
|
+
full = (0, safe_path_1.safeJoin)(projectDir, filePath);
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
console.log(chalk_1.default.yellow(` ! ${(0, safe_path_1.errMessage)(err)} \u2014 ignorando ${filePath}`));
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
if (fs2.existsSync(full))
|
|
1703
|
+
add(filePath, full);
|
|
1704
|
+
}
|
|
1705
|
+
if (allToDelete.length === 0) {
|
|
1706
|
+
console.log(chalk_1.default.dim("\n Nenhum arquivo encontrado para remover.\n"));
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
console.log("");
|
|
1710
|
+
console.log(chalk_1.default.red.bold(" Arquivos que ser\xE3o removidos:"));
|
|
1711
|
+
for (const f of allToDelete) {
|
|
1712
|
+
console.log(chalk_1.default.red(` - ${f.rel}`));
|
|
1713
|
+
}
|
|
1714
|
+
console.log("");
|
|
1715
|
+
if (synthOuts.length > 0) {
|
|
1716
|
+
const runDestroy = await ask2("Rodar `iacmp destroy` para remover os recursos na nuvem antes de apagar? [y/n] ");
|
|
1717
|
+
if (runDestroy.toLowerCase() === "y") {
|
|
1718
|
+
const cliEntry = resolveCliEntrypoint();
|
|
1719
|
+
for (const synthOut of synthOuts) {
|
|
1720
|
+
const stackName = path2.basename(synthOut).replace(/\.(json|tf)$/, "");
|
|
1721
|
+
try {
|
|
1722
|
+
(0, safe_path_1.assertValidStackName)(stackName);
|
|
1723
|
+
} catch (err) {
|
|
1724
|
+
console.log(chalk_1.default.yellow(` ! ${(0, safe_path_1.errMessage)(err)} \u2014 pulando destroy de ${stackName}`));
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
console.log(chalk_1.default.dim(`
|
|
1728
|
+
Rodando destroy para ${stackName}...`));
|
|
1729
|
+
try {
|
|
1730
|
+
if (cliEntry) {
|
|
1731
|
+
cp.execFileSync(process.execPath, [cliEntry, "destroy", "--stack", stackName, "--provider", iacProvider2, "--force"], { cwd: projectDir, stdio: "inherit" });
|
|
1732
|
+
} else {
|
|
1733
|
+
cp.execFileSync("npx", ["iacmp", "destroy", "--stack", stackName, "--provider", iacProvider2, "--force"], { cwd: projectDir, stdio: "inherit" });
|
|
1734
|
+
}
|
|
1735
|
+
} catch {
|
|
1736
|
+
console.log(chalk_1.default.yellow(` ! destroy falhou para ${stackName} \u2014 continuando com remo\xE7\xE3o local`));
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const confirm = await ask2("\nApagar arquivos locais? [y/n] ");
|
|
1742
|
+
if (confirm.toLowerCase() !== "y") {
|
|
1743
|
+
console.log(chalk_1.default.dim(" Remo\xE7\xE3o cancelada.\n"));
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
for (const { rel, full } of allToDelete) {
|
|
1747
|
+
try {
|
|
1748
|
+
fs2.rmSync(full, { force: true });
|
|
1749
|
+
console.log(chalk_1.default.red(` \u2717 ${rel}`));
|
|
1750
|
+
} catch {
|
|
1751
|
+
console.log(chalk_1.default.yellow(` ! N\xE3o foi poss\xEDvel remover: ${rel}`));
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
for (const filePath of stackFiles) {
|
|
1755
|
+
const stackName = path2.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1756
|
+
const modified = removeReferences(stackName, projectDir);
|
|
1757
|
+
for (const f of modified) {
|
|
1758
|
+
console.log(chalk_1.default.yellow(` ~ refer\xEAncias removidas em: ${f}`));
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
console.log("");
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
// ../ai/dist/tools/synth-runner.js
|
|
1767
|
+
var require_synth_runner = __commonJS({
|
|
1768
|
+
"../ai/dist/tools/synth-runner.js"(exports2) {
|
|
1769
|
+
"use strict";
|
|
1770
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1771
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1772
|
+
};
|
|
1773
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1774
|
+
exports2.runSynth = runSynth2;
|
|
1775
|
+
var child_process_1 = require("child_process");
|
|
1776
|
+
var chalk_1 = __importDefault(require("chalk"));
|
|
1777
|
+
var safe_path_1 = require_safe_path();
|
|
1778
|
+
function runSynth2(projectDir, provider, options) {
|
|
1779
|
+
try {
|
|
1780
|
+
(0, safe_path_1.assertValidProvider)(provider, options?.providerAllowlist);
|
|
1781
|
+
} catch (err) {
|
|
1782
|
+
console.error(chalk_1.default.red("\nProvider inv\xE1lido para synth:"));
|
|
1783
|
+
console.error(chalk_1.default.red((0, safe_path_1.errMessage)(err)));
|
|
1784
|
+
return false;
|
|
1785
|
+
}
|
|
1786
|
+
let cliEntry = null;
|
|
1787
|
+
try {
|
|
1788
|
+
cliEntry = require.resolve("iacmp/bin/run.js");
|
|
1789
|
+
} catch {
|
|
1790
|
+
cliEntry = null;
|
|
1791
|
+
}
|
|
1792
|
+
try {
|
|
1793
|
+
if (cliEntry) {
|
|
1794
|
+
(0, child_process_1.execFileSync)(process.execPath, [cliEntry, "synth", "--provider", provider], { cwd: projectDir, stdio: "inherit" });
|
|
1795
|
+
} else {
|
|
1796
|
+
(0, child_process_1.execFileSync)("npx", ["iacmp", "synth", "--provider", provider], { cwd: projectDir, stdio: "inherit" });
|
|
1797
|
+
}
|
|
1798
|
+
return true;
|
|
1799
|
+
} catch (err) {
|
|
1800
|
+
const error = err;
|
|
1801
|
+
console.error(chalk_1.default.red("\nErro ao executar iacmp synth:"));
|
|
1802
|
+
if (error.stderr) {
|
|
1803
|
+
console.error(chalk_1.default.red(error.stderr.toString()));
|
|
1804
|
+
}
|
|
1805
|
+
return false;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
// ../ai/dist/rag/chunker.js
|
|
1812
|
+
var require_chunker = __commonJS({
|
|
1813
|
+
"../ai/dist/rag/chunker.js"(exports2) {
|
|
1814
|
+
"use strict";
|
|
1815
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1816
|
+
if (k2 === void 0) k2 = k;
|
|
1817
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1818
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1819
|
+
desc = { enumerable: true, get: function() {
|
|
1820
|
+
return m[k];
|
|
1821
|
+
} };
|
|
1822
|
+
}
|
|
1823
|
+
Object.defineProperty(o, k2, desc);
|
|
1824
|
+
}) : (function(o, m, k, k2) {
|
|
1825
|
+
if (k2 === void 0) k2 = k;
|
|
1826
|
+
o[k2] = m[k];
|
|
1827
|
+
}));
|
|
1828
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1829
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1830
|
+
}) : function(o, v) {
|
|
1831
|
+
o["default"] = v;
|
|
1832
|
+
});
|
|
1833
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
1834
|
+
var ownKeys = function(o) {
|
|
1835
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1836
|
+
var ar = [];
|
|
1837
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1838
|
+
return ar;
|
|
1839
|
+
};
|
|
1840
|
+
return ownKeys(o);
|
|
1841
|
+
};
|
|
1842
|
+
return function(mod) {
|
|
1843
|
+
if (mod && mod.__esModule) return mod;
|
|
1844
|
+
var result = {};
|
|
1845
|
+
if (mod != null) {
|
|
1846
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1847
|
+
}
|
|
1848
|
+
__setModuleDefault(result, mod);
|
|
1849
|
+
return result;
|
|
1850
|
+
};
|
|
1851
|
+
})();
|
|
1852
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1853
|
+
exports2.chunkStackFile = chunkStackFile;
|
|
1854
|
+
exports2.chunkIacmpDocs = chunkIacmpDocs;
|
|
1855
|
+
exports2.chunkKnowledgeFile = chunkKnowledgeFile;
|
|
1856
|
+
var fs2 = __importStar(require("fs"));
|
|
1857
|
+
var path2 = __importStar(require("path"));
|
|
1858
|
+
function chunkStackFile(filePath, projectDir) {
|
|
1859
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1860
|
+
const rel = path2.relative(projectDir, filePath);
|
|
1861
|
+
const stackName = path2.basename(filePath, ".ts");
|
|
1862
|
+
const chunks = [];
|
|
1863
|
+
const constructRegex = /new\s+([\w]+\.[\w]+)\s*\(\s*\w+\s*,\s*'([\w-]+)'\s*,\s*(\{[\s\S]*?\})\s*\)/g;
|
|
1864
|
+
let match;
|
|
1865
|
+
let index = 0;
|
|
1866
|
+
while ((match = constructRegex.exec(content)) !== null) {
|
|
1867
|
+
const constructType = match[1];
|
|
1868
|
+
const constructId = match[2];
|
|
1869
|
+
const propsRaw = match[3];
|
|
1870
|
+
chunks.push({
|
|
1871
|
+
id: `stack:${rel}:${constructId}`,
|
|
1872
|
+
content: `[construct] ${constructType} "${constructId}" em ${rel}
|
|
1873
|
+
${propsRaw}`,
|
|
1874
|
+
metadata: {
|
|
1875
|
+
source: "project-stack",
|
|
1876
|
+
file: rel,
|
|
1877
|
+
stackName,
|
|
1878
|
+
constructType,
|
|
1879
|
+
constructId
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
index++;
|
|
1883
|
+
}
|
|
1884
|
+
if (chunks.length === 0 && content.trim().length > 0) {
|
|
1885
|
+
chunks.push({
|
|
1886
|
+
id: `stack:${rel}:full`,
|
|
1887
|
+
content: `[stack] ${stackName} em ${rel}
|
|
1888
|
+
${content}`,
|
|
1889
|
+
metadata: {
|
|
1890
|
+
source: "project-stack",
|
|
1891
|
+
file: rel,
|
|
1892
|
+
stackName
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
return chunks;
|
|
1897
|
+
}
|
|
1898
|
+
function chunkIacmpDocs(systemPromptTemplate) {
|
|
1899
|
+
const chunks = [];
|
|
1900
|
+
const sections = systemPromptTemplate.split(/(?=^### )/m);
|
|
1901
|
+
for (const section of sections) {
|
|
1902
|
+
const titleMatch = section.match(/^### (.+)/);
|
|
1903
|
+
if (!titleMatch)
|
|
1904
|
+
continue;
|
|
1905
|
+
const title = titleMatch[1].trim();
|
|
1906
|
+
const trimmed = section.trim();
|
|
1907
|
+
if (trimmed.length < 30)
|
|
1908
|
+
continue;
|
|
1909
|
+
chunks.push({
|
|
1910
|
+
id: `docs:${title.replace(/[^a-zA-Z0-9]/g, "-")}`,
|
|
1911
|
+
content: trimmed,
|
|
1912
|
+
metadata: {
|
|
1913
|
+
source: "iacmp-docs",
|
|
1914
|
+
section: title
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
return chunks;
|
|
1919
|
+
}
|
|
1920
|
+
function chunkKnowledgeFile(filePath, platform) {
|
|
1921
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1922
|
+
const fileName = path2.basename(filePath, ".md");
|
|
1923
|
+
const chunks = [];
|
|
1924
|
+
const sections = content.split(/(?=^#{1,3} )/m);
|
|
1925
|
+
for (const section of sections) {
|
|
1926
|
+
const titleMatch = section.match(/^#{1,3} (.+)/);
|
|
1927
|
+
const title = titleMatch ? titleMatch[1].trim() : fileName;
|
|
1928
|
+
const trimmed = section.trim();
|
|
1929
|
+
if (trimmed.length < 50)
|
|
1930
|
+
continue;
|
|
1931
|
+
if (trimmed.length > 2e3) {
|
|
1932
|
+
const paragraphs = trimmed.split(/\n\n+/);
|
|
1933
|
+
let buffer = "";
|
|
1934
|
+
let bufferIndex = 0;
|
|
1935
|
+
for (const para of paragraphs) {
|
|
1936
|
+
buffer += para + "\n\n";
|
|
1937
|
+
if (buffer.length > 800) {
|
|
1938
|
+
chunks.push({
|
|
1939
|
+
id: `knowledge:${platform}:${fileName}:${title.replace(/[^a-zA-Z0-9]/g, "-")}-${bufferIndex}`,
|
|
1940
|
+
content: `[${platform.toUpperCase()}] ${title}
|
|
1941
|
+
|
|
1942
|
+
${buffer.trim()}`,
|
|
1943
|
+
metadata: {
|
|
1944
|
+
source: "platform-knowledge",
|
|
1945
|
+
platform,
|
|
1946
|
+
file: path2.basename(filePath),
|
|
1947
|
+
section: title
|
|
1948
|
+
}
|
|
1949
|
+
});
|
|
1950
|
+
buffer = "";
|
|
1951
|
+
bufferIndex++;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
if (buffer.trim().length > 50) {
|
|
1955
|
+
chunks.push({
|
|
1956
|
+
id: `knowledge:${platform}:${fileName}:${title.replace(/[^a-zA-Z0-9]/g, "-")}-${bufferIndex}`,
|
|
1957
|
+
content: `[${platform.toUpperCase()}] ${title}
|
|
1958
|
+
|
|
1959
|
+
${buffer.trim()}`,
|
|
1960
|
+
metadata: {
|
|
1961
|
+
source: "platform-knowledge",
|
|
1962
|
+
platform,
|
|
1963
|
+
file: path2.basename(filePath),
|
|
1964
|
+
section: title
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
} else {
|
|
1969
|
+
chunks.push({
|
|
1970
|
+
id: `knowledge:${platform}:${fileName}:${title.replace(/[^a-zA-Z0-9]/g, "-")}`,
|
|
1971
|
+
content: `[${platform.toUpperCase()}] ${title}
|
|
1972
|
+
|
|
1973
|
+
${trimmed}`,
|
|
1974
|
+
metadata: {
|
|
1975
|
+
source: "platform-knowledge",
|
|
1976
|
+
platform,
|
|
1977
|
+
file: path2.basename(filePath),
|
|
1978
|
+
section: title
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
return chunks;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
|
|
1988
|
+
// ../ai/dist/rag/contextualizer.js
|
|
1989
|
+
var require_contextualizer = __commonJS({
|
|
1990
|
+
"../ai/dist/rag/contextualizer.js"(exports2) {
|
|
1991
|
+
"use strict";
|
|
1992
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
1993
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1994
|
+
};
|
|
1995
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1996
|
+
exports2.Contextualizer = void 0;
|
|
1997
|
+
var sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
1998
|
+
var CONTEXTUALIZER_PROMPT = `Voc\xEA est\xE1 processando um chunk de documento para indexa\xE7\xE3o em um sistema RAG de infraestrutura como c\xF3digo.
|
|
1999
|
+
|
|
2000
|
+
Dado o documento completo e o chunk espec\xEDfico abaixo, gere um par\xE1grafo conciso (2-4 frases) que:
|
|
2001
|
+
1. Explique o que este chunk representa no contexto do documento maior
|
|
2002
|
+
2. Mencione conceitos-chave, servi\xE7os cloud, ou padr\xF5es de arquitetura relevantes
|
|
2003
|
+
3. Inclua termos alternativos ou sin\xF4nimos que ajudem na busca (ex: "Lambda" e "fun\xE7\xE3o serverless", "RDS" e "banco relacional")
|
|
2004
|
+
|
|
2005
|
+
Seja direto e t\xE9cnico. N\xE3o use markdown. Responda apenas com o par\xE1grafo de contexto.
|
|
2006
|
+
|
|
2007
|
+
<documento_completo>
|
|
2008
|
+
{DOCUMENT}
|
|
2009
|
+
</documento_completo>
|
|
2010
|
+
|
|
2011
|
+
<chunk>
|
|
2012
|
+
{CHUNK}
|
|
2013
|
+
</chunk>`;
|
|
2014
|
+
var Contextualizer = class {
|
|
2015
|
+
client;
|
|
2016
|
+
model = "claude-haiku-4-5";
|
|
2017
|
+
// usa Haiku para minimizar custo — tarefa simples
|
|
2018
|
+
constructor(apiKey) {
|
|
2019
|
+
this.client = new sdk_1.default({ apiKey });
|
|
2020
|
+
}
|
|
2021
|
+
// Enriquece um único chunk com contexto gerado pelo Claude
|
|
2022
|
+
async enrichChunk(chunk, fullDocument) {
|
|
2023
|
+
const prompt = CONTEXTUALIZER_PROMPT.replace("{DOCUMENT}", fullDocument.slice(0, 8e3)).replace("{CHUNK}", chunk.content.slice(0, 2e3));
|
|
2024
|
+
try {
|
|
2025
|
+
const response = await this.client.messages.create({
|
|
2026
|
+
model: this.model,
|
|
2027
|
+
max_tokens: 256,
|
|
2028
|
+
messages: [{ role: "user", content: prompt }]
|
|
2029
|
+
});
|
|
2030
|
+
const contextText = response.content[0].type === "text" ? response.content[0].text.trim() : "";
|
|
2031
|
+
return {
|
|
2032
|
+
...chunk,
|
|
2033
|
+
// contextualContent = contexto gerado + conteúdo original
|
|
2034
|
+
// É isso que vai para o índice BM25/vetorial
|
|
2035
|
+
contextualContent: `${contextText}
|
|
2036
|
+
|
|
2037
|
+
${chunk.content}`
|
|
2038
|
+
};
|
|
2039
|
+
} catch {
|
|
2040
|
+
return { ...chunk, contextualContent: chunk.content };
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
// Enriquece um lote de chunks em paralelo com controle de concorrência
|
|
2044
|
+
async enrichBatch(chunks, fullDocument, options = {}) {
|
|
2045
|
+
const { concurrency = 5, onProgress } = options;
|
|
2046
|
+
const results = new Array(chunks.length);
|
|
2047
|
+
let done = 0;
|
|
2048
|
+
for (let i = 0; i < chunks.length; i += concurrency) {
|
|
2049
|
+
const batch = chunks.slice(i, i + concurrency);
|
|
2050
|
+
const batchResults = await Promise.all(batch.map((chunk) => this.enrichChunk(chunk, fullDocument)));
|
|
2051
|
+
for (let j = 0; j < batchResults.length; j++) {
|
|
2052
|
+
results[i + j] = batchResults[j];
|
|
2053
|
+
}
|
|
2054
|
+
done += batch.length;
|
|
2055
|
+
onProgress?.(done, chunks.length);
|
|
2056
|
+
}
|
|
2057
|
+
return results;
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
exports2.Contextualizer = Contextualizer;
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
|
|
2064
|
+
// ../ai/dist/rag/bm25.js
|
|
2065
|
+
var require_bm25 = __commonJS({
|
|
2066
|
+
"../ai/dist/rag/bm25.js"(exports2) {
|
|
2067
|
+
"use strict";
|
|
2068
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2069
|
+
exports2.buildBM25Index = buildBM25Index;
|
|
2070
|
+
exports2.bm25Search = bm25Search;
|
|
2071
|
+
var K1 = 1.5;
|
|
2072
|
+
var B = 0.75;
|
|
2073
|
+
function tokenize(text) {
|
|
2074
|
+
return text.toLowerCase().replace(/[^a-z0-9\s._-]/g, " ").split(/\s+/).filter((t) => t.length > 1);
|
|
2075
|
+
}
|
|
2076
|
+
function termFrequency(tokens) {
|
|
2077
|
+
const tf = {};
|
|
2078
|
+
for (const t of tokens) {
|
|
2079
|
+
tf[t] = (tf[t] ?? 0) + 1;
|
|
2080
|
+
}
|
|
2081
|
+
return tf;
|
|
2082
|
+
}
|
|
2083
|
+
function buildBM25Index(chunks) {
|
|
2084
|
+
const documents = [];
|
|
2085
|
+
const df = {};
|
|
2086
|
+
let totalLength = 0;
|
|
2087
|
+
for (const chunk of chunks) {
|
|
2088
|
+
const text = chunk.contextualContent ?? chunk.content;
|
|
2089
|
+
const tokens = tokenize(text);
|
|
2090
|
+
const terms = termFrequency(tokens);
|
|
2091
|
+
totalLength += tokens.length;
|
|
2092
|
+
documents.push({ id: chunk.id, terms, length: tokens.length });
|
|
2093
|
+
for (const term of Object.keys(terms)) {
|
|
2094
|
+
df[term] = (df[term] ?? 0) + 1;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return {
|
|
2098
|
+
documents,
|
|
2099
|
+
df,
|
|
2100
|
+
avgLength: documents.length > 0 ? totalLength / documents.length : 0,
|
|
2101
|
+
totalDocs: documents.length
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
function bm25Search(index, query, topK = 5) {
|
|
2105
|
+
const queryTerms = tokenize(query);
|
|
2106
|
+
const scores = {};
|
|
2107
|
+
for (const term of queryTerms) {
|
|
2108
|
+
const docFreq = index.df[term] ?? 0;
|
|
2109
|
+
if (docFreq === 0)
|
|
2110
|
+
continue;
|
|
2111
|
+
const idf = Math.log((index.totalDocs - docFreq + 0.5) / (docFreq + 0.5) + 1);
|
|
2112
|
+
for (const doc of index.documents) {
|
|
2113
|
+
const tf = doc.terms[term] ?? 0;
|
|
2114
|
+
if (tf === 0)
|
|
2115
|
+
continue;
|
|
2116
|
+
const tfNorm = tf * (K1 + 1) / (tf + K1 * (1 - B + B * (doc.length / index.avgLength)));
|
|
2117
|
+
scores[doc.id] = (scores[doc.id] ?? 0) + idf * tfNorm;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
return Object.entries(scores).sort((a, b) => b[1] - a[1]).slice(0, topK).map(([id, score]) => ({ id, score }));
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
// ../ai/dist/rag/embedder.js
|
|
2126
|
+
var require_embedder = __commonJS({
|
|
2127
|
+
"../ai/dist/rag/embedder.js"(exports2) {
|
|
2128
|
+
"use strict";
|
|
2129
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2130
|
+
exports2.BM25Embedder = exports2.VoyageEmbedder = void 0;
|
|
2131
|
+
exports2.createEmbedder = createEmbedder;
|
|
2132
|
+
var VoyageEmbedder = class {
|
|
2133
|
+
apiKey;
|
|
2134
|
+
model = "voyage-code-2";
|
|
2135
|
+
endpoint = "https://api.voyageai.com/v1/embeddings";
|
|
2136
|
+
constructor(apiKey) {
|
|
2137
|
+
this.apiKey = apiKey;
|
|
2138
|
+
}
|
|
2139
|
+
async embed(texts) {
|
|
2140
|
+
if (texts.length === 0)
|
|
2141
|
+
return [];
|
|
2142
|
+
const BATCH_SIZE = 128;
|
|
2143
|
+
const results = [];
|
|
2144
|
+
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
|
|
2145
|
+
const batch = texts.slice(i, i + BATCH_SIZE);
|
|
2146
|
+
const batchVectors = await this.embedBatch(batch);
|
|
2147
|
+
results.push(...batchVectors);
|
|
2148
|
+
}
|
|
2149
|
+
return results;
|
|
2150
|
+
}
|
|
2151
|
+
async embedBatch(texts) {
|
|
2152
|
+
const controller = new AbortController();
|
|
2153
|
+
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
2154
|
+
try {
|
|
2155
|
+
const response = await fetch(this.endpoint, {
|
|
2156
|
+
method: "POST",
|
|
2157
|
+
headers: {
|
|
2158
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
2159
|
+
"Content-Type": "application/json"
|
|
2160
|
+
},
|
|
2161
|
+
body: JSON.stringify({
|
|
2162
|
+
model: this.model,
|
|
2163
|
+
input: texts,
|
|
2164
|
+
input_type: "document"
|
|
2165
|
+
}),
|
|
2166
|
+
signal: controller.signal
|
|
2167
|
+
});
|
|
2168
|
+
if (!response.ok) {
|
|
2169
|
+
const body = await response.text();
|
|
2170
|
+
throw new Error(`Voyage API error ${response.status}: ${body}`);
|
|
2171
|
+
}
|
|
2172
|
+
const data = await response.json();
|
|
2173
|
+
const sorted = data.data.sort((a, b) => a.index - b.index);
|
|
2174
|
+
return sorted.map((d) => d.embedding);
|
|
2175
|
+
} finally {
|
|
2176
|
+
clearTimeout(timeout);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
exports2.VoyageEmbedder = VoyageEmbedder;
|
|
2181
|
+
var BM25Embedder = class {
|
|
2182
|
+
vocabSize;
|
|
2183
|
+
constructor(vocabSize = 4096) {
|
|
2184
|
+
this.vocabSize = vocabSize;
|
|
2185
|
+
}
|
|
2186
|
+
async embed(texts) {
|
|
2187
|
+
return texts.map((text) => this.textToVector(text));
|
|
2188
|
+
}
|
|
2189
|
+
textToVector(text) {
|
|
2190
|
+
const tokens = this.tokenize(text);
|
|
2191
|
+
const tf = this.termFrequency(tokens);
|
|
2192
|
+
const vector = new Float64Array(this.vocabSize);
|
|
2193
|
+
for (const [term, freq] of Object.entries(tf)) {
|
|
2194
|
+
const hash = this.termHash(term);
|
|
2195
|
+
const pos = Math.abs(hash) % this.vocabSize;
|
|
2196
|
+
vector[pos] += Math.log(1 + freq) / Math.max(1, tokens.length);
|
|
2197
|
+
}
|
|
2198
|
+
return this.normalizeL2(Array.from(vector));
|
|
2199
|
+
}
|
|
2200
|
+
tokenize(text) {
|
|
2201
|
+
return text.toLowerCase().replace(/[^a-z0-9\s._-]/g, " ").split(/\s+/).filter((t) => t.length > 1);
|
|
2202
|
+
}
|
|
2203
|
+
termFrequency(tokens) {
|
|
2204
|
+
const tf = {};
|
|
2205
|
+
for (const t of tokens) {
|
|
2206
|
+
tf[t] = (tf[t] ?? 0) + 1;
|
|
2207
|
+
}
|
|
2208
|
+
return tf;
|
|
2209
|
+
}
|
|
2210
|
+
termHash(term) {
|
|
2211
|
+
let hash = 5381;
|
|
2212
|
+
for (let i = 0; i < term.length; i++) {
|
|
2213
|
+
hash = (hash << 5) + hash ^ term.charCodeAt(i);
|
|
2214
|
+
hash = hash & 2147483647;
|
|
2215
|
+
}
|
|
2216
|
+
return hash;
|
|
2217
|
+
}
|
|
2218
|
+
normalizeL2(vector) {
|
|
2219
|
+
const norm = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
2220
|
+
if (norm === 0)
|
|
2221
|
+
return vector;
|
|
2222
|
+
return vector.map((v) => v / norm);
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
exports2.BM25Embedder = BM25Embedder;
|
|
2226
|
+
function createEmbedder(voyageApiKey) {
|
|
2227
|
+
if (voyageApiKey) {
|
|
2228
|
+
return new VoyageEmbedder(voyageApiKey);
|
|
2229
|
+
}
|
|
2230
|
+
return new BM25Embedder();
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2234
|
+
|
|
2235
|
+
// ../ai/dist/rag/vector-store.js
|
|
2236
|
+
var require_vector_store = __commonJS({
|
|
2237
|
+
"../ai/dist/rag/vector-store.js"(exports2) {
|
|
2238
|
+
"use strict";
|
|
2239
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
2240
|
+
if (k2 === void 0) k2 = k;
|
|
2241
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2242
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2243
|
+
desc = { enumerable: true, get: function() {
|
|
2244
|
+
return m[k];
|
|
2245
|
+
} };
|
|
2246
|
+
}
|
|
2247
|
+
Object.defineProperty(o, k2, desc);
|
|
2248
|
+
}) : (function(o, m, k, k2) {
|
|
2249
|
+
if (k2 === void 0) k2 = k;
|
|
2250
|
+
o[k2] = m[k];
|
|
2251
|
+
}));
|
|
2252
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
2253
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2254
|
+
}) : function(o, v) {
|
|
2255
|
+
o["default"] = v;
|
|
2256
|
+
});
|
|
2257
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
2258
|
+
var ownKeys = function(o) {
|
|
2259
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2260
|
+
var ar = [];
|
|
2261
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2262
|
+
return ar;
|
|
2263
|
+
};
|
|
2264
|
+
return ownKeys(o);
|
|
2265
|
+
};
|
|
2266
|
+
return function(mod) {
|
|
2267
|
+
if (mod && mod.__esModule) return mod;
|
|
2268
|
+
var result = {};
|
|
2269
|
+
if (mod != null) {
|
|
2270
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2271
|
+
}
|
|
2272
|
+
__setModuleDefault(result, mod);
|
|
2273
|
+
return result;
|
|
2274
|
+
};
|
|
2275
|
+
})();
|
|
2276
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2277
|
+
exports2.VectorStore = void 0;
|
|
2278
|
+
var fs2 = __importStar(require("fs"));
|
|
2279
|
+
var path2 = __importStar(require("path"));
|
|
2280
|
+
var VectorStore = class {
|
|
2281
|
+
entries = [];
|
|
2282
|
+
dimension = 0;
|
|
2283
|
+
add(id, vector, metadata) {
|
|
2284
|
+
if (this.dimension === 0) {
|
|
2285
|
+
this.dimension = vector.length;
|
|
2286
|
+
}
|
|
2287
|
+
const normalized = this.normalizeL2(vector);
|
|
2288
|
+
this.entries.push({
|
|
2289
|
+
id,
|
|
2290
|
+
vector: new Float32Array(normalized),
|
|
2291
|
+
metadata
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
search(queryVector, topK = 5) {
|
|
2295
|
+
if (this.entries.length === 0)
|
|
2296
|
+
return [];
|
|
2297
|
+
const normalizedQuery = new Float32Array(this.normalizeL2(queryVector));
|
|
2298
|
+
const scores = [];
|
|
2299
|
+
for (let i = 0; i < this.entries.length; i++) {
|
|
2300
|
+
const score = this.cosineSimilarity(normalizedQuery, this.entries[i].vector);
|
|
2301
|
+
scores.push({ index: i, score });
|
|
2302
|
+
}
|
|
2303
|
+
return scores.sort((a, b) => b.score - a.score).slice(0, topK).map(({ index, score }) => ({
|
|
2304
|
+
id: this.entries[index].id,
|
|
2305
|
+
score,
|
|
2306
|
+
metadata: this.entries[index].metadata
|
|
2307
|
+
}));
|
|
2308
|
+
}
|
|
2309
|
+
size() {
|
|
2310
|
+
return this.entries.length;
|
|
2311
|
+
}
|
|
2312
|
+
clear() {
|
|
2313
|
+
this.entries = [];
|
|
2314
|
+
this.dimension = 0;
|
|
2315
|
+
}
|
|
2316
|
+
// Serializa o índice em binário para persistência
|
|
2317
|
+
// Formato: [header: 16 bytes][entries...]
|
|
2318
|
+
// Header: magic(4) + version(4) + numEntries(4) + dimension(4)
|
|
2319
|
+
// Entry: idLen(4) + id(idLen) + metadataLen(4) + metadata(metadataLen) + vector(dimension * 4)
|
|
2320
|
+
save(filePath) {
|
|
2321
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
2322
|
+
const MAGIC = 1447379796;
|
|
2323
|
+
const VERSION = 1;
|
|
2324
|
+
const encodedEntries = this.entries.map((entry) => {
|
|
2325
|
+
const idBytes = Buffer.from(entry.id, "utf-8");
|
|
2326
|
+
const metaBytes = Buffer.from(JSON.stringify(entry.metadata), "utf-8");
|
|
2327
|
+
return { entry, idBytes, metaBytes };
|
|
2328
|
+
});
|
|
2329
|
+
const totalSize = 16 + encodedEntries.reduce((sum, { entry, idBytes, metaBytes }) => {
|
|
2330
|
+
return sum + 4 + idBytes.length + 4 + metaBytes.length + entry.vector.length * 4;
|
|
2331
|
+
}, 0);
|
|
2332
|
+
const buffer = Buffer.alloc(totalSize);
|
|
2333
|
+
let offset = 0;
|
|
2334
|
+
buffer.writeUInt32BE(MAGIC, offset);
|
|
2335
|
+
offset += 4;
|
|
2336
|
+
buffer.writeUInt32BE(VERSION, offset);
|
|
2337
|
+
offset += 4;
|
|
2338
|
+
buffer.writeUInt32BE(this.entries.length, offset);
|
|
2339
|
+
offset += 4;
|
|
2340
|
+
buffer.writeUInt32BE(this.dimension, offset);
|
|
2341
|
+
offset += 4;
|
|
2342
|
+
for (const { entry, idBytes, metaBytes } of encodedEntries) {
|
|
2343
|
+
buffer.writeUInt32BE(idBytes.length, offset);
|
|
2344
|
+
offset += 4;
|
|
2345
|
+
idBytes.copy(buffer, offset);
|
|
2346
|
+
offset += idBytes.length;
|
|
2347
|
+
buffer.writeUInt32BE(metaBytes.length, offset);
|
|
2348
|
+
offset += 4;
|
|
2349
|
+
metaBytes.copy(buffer, offset);
|
|
2350
|
+
offset += metaBytes.length;
|
|
2351
|
+
for (const v of entry.vector) {
|
|
2352
|
+
buffer.writeFloatBE(v, offset);
|
|
2353
|
+
offset += 4;
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
fs2.writeFileSync(filePath, buffer);
|
|
2357
|
+
}
|
|
2358
|
+
// Carrega índice do binário
|
|
2359
|
+
load(filePath) {
|
|
2360
|
+
if (!fs2.existsSync(filePath))
|
|
2361
|
+
return false;
|
|
2362
|
+
try {
|
|
2363
|
+
const buffer = fs2.readFileSync(filePath);
|
|
2364
|
+
let offset = 0;
|
|
2365
|
+
const magic = buffer.readUInt32BE(offset);
|
|
2366
|
+
offset += 4;
|
|
2367
|
+
if (magic !== 1447379796)
|
|
2368
|
+
return false;
|
|
2369
|
+
const version = buffer.readUInt32BE(offset);
|
|
2370
|
+
offset += 4;
|
|
2371
|
+
if (version !== 1)
|
|
2372
|
+
return false;
|
|
2373
|
+
const numEntries = buffer.readUInt32BE(offset);
|
|
2374
|
+
offset += 4;
|
|
2375
|
+
const dimension = buffer.readUInt32BE(offset);
|
|
2376
|
+
offset += 4;
|
|
2377
|
+
this.dimension = dimension;
|
|
2378
|
+
this.entries = [];
|
|
2379
|
+
for (let i = 0; i < numEntries; i++) {
|
|
2380
|
+
const idLen = buffer.readUInt32BE(offset);
|
|
2381
|
+
offset += 4;
|
|
2382
|
+
const id = buffer.subarray(offset, offset + idLen).toString("utf-8");
|
|
2383
|
+
offset += idLen;
|
|
2384
|
+
const metaLen = buffer.readUInt32BE(offset);
|
|
2385
|
+
offset += 4;
|
|
2386
|
+
const metaStr = buffer.subarray(offset, offset + metaLen).toString("utf-8");
|
|
2387
|
+
offset += metaLen;
|
|
2388
|
+
const metadata = JSON.parse(metaStr);
|
|
2389
|
+
const vector = new Float32Array(dimension);
|
|
2390
|
+
for (let d = 0; d < dimension; d++) {
|
|
2391
|
+
vector[d] = buffer.readFloatBE(offset);
|
|
2392
|
+
offset += 4;
|
|
2393
|
+
}
|
|
2394
|
+
this.entries.push({ id, vector, metadata });
|
|
2395
|
+
}
|
|
2396
|
+
return true;
|
|
2397
|
+
} catch {
|
|
2398
|
+
return false;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
cosineSimilarity(a, b) {
|
|
2402
|
+
if (a.length !== b.length)
|
|
2403
|
+
return 0;
|
|
2404
|
+
let dot = 0;
|
|
2405
|
+
for (let i = 0; i < a.length; i++) {
|
|
2406
|
+
dot += a[i] * b[i];
|
|
2407
|
+
}
|
|
2408
|
+
return Math.max(-1, Math.min(1, dot));
|
|
2409
|
+
}
|
|
2410
|
+
normalizeL2(vector) {
|
|
2411
|
+
const norm = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
2412
|
+
if (norm === 0)
|
|
2413
|
+
return vector;
|
|
2414
|
+
return vector.map((v) => v / norm);
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
exports2.VectorStore = VectorStore;
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
|
|
2421
|
+
// ../ai/dist/rag/indexer.js
|
|
2422
|
+
var require_indexer = __commonJS({
|
|
2423
|
+
"../ai/dist/rag/indexer.js"(exports2) {
|
|
2424
|
+
"use strict";
|
|
2425
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
2426
|
+
if (k2 === void 0) k2 = k;
|
|
2427
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2428
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2429
|
+
desc = { enumerable: true, get: function() {
|
|
2430
|
+
return m[k];
|
|
2431
|
+
} };
|
|
2432
|
+
}
|
|
2433
|
+
Object.defineProperty(o, k2, desc);
|
|
2434
|
+
}) : (function(o, m, k, k2) {
|
|
2435
|
+
if (k2 === void 0) k2 = k;
|
|
2436
|
+
o[k2] = m[k];
|
|
2437
|
+
}));
|
|
2438
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
2439
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2440
|
+
}) : function(o, v) {
|
|
2441
|
+
o["default"] = v;
|
|
2442
|
+
});
|
|
2443
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
2444
|
+
var ownKeys = function(o) {
|
|
2445
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2446
|
+
var ar = [];
|
|
2447
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2448
|
+
return ar;
|
|
2449
|
+
};
|
|
2450
|
+
return ownKeys(o);
|
|
2451
|
+
};
|
|
2452
|
+
return function(mod) {
|
|
2453
|
+
if (mod && mod.__esModule) return mod;
|
|
2454
|
+
var result = {};
|
|
2455
|
+
if (mod != null) {
|
|
2456
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2457
|
+
}
|
|
2458
|
+
__setModuleDefault(result, mod);
|
|
2459
|
+
return result;
|
|
2460
|
+
};
|
|
2461
|
+
})();
|
|
2462
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2463
|
+
exports2.buildIndexes = buildIndexes;
|
|
2464
|
+
var fs2 = __importStar(require("fs"));
|
|
2465
|
+
var path2 = __importStar(require("path"));
|
|
2466
|
+
var crypto = __importStar(require("crypto"));
|
|
2467
|
+
var chunker_1 = require_chunker();
|
|
2468
|
+
var contextualizer_1 = require_contextualizer();
|
|
2469
|
+
var bm25_1 = require_bm25();
|
|
2470
|
+
var embedder_1 = require_embedder();
|
|
2471
|
+
var vector_store_1 = require_vector_store();
|
|
2472
|
+
var INDEX_FILE = ".iacmp/rag-index.json";
|
|
2473
|
+
var KNOWLEDGE_DIR = path2.join(__dirname, "../knowledge");
|
|
2474
|
+
function hashProjectStacks(projectDir) {
|
|
2475
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
2476
|
+
if (!fs2.existsSync(stacksDir))
|
|
2477
|
+
return "empty";
|
|
2478
|
+
const findFiles = (dir) => {
|
|
2479
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
2480
|
+
const files2 = [];
|
|
2481
|
+
for (const e of entries) {
|
|
2482
|
+
const full = path2.join(dir, e.name);
|
|
2483
|
+
if (e.isDirectory())
|
|
2484
|
+
files2.push(...findFiles(full));
|
|
2485
|
+
else if (e.name.endsWith(".ts"))
|
|
2486
|
+
files2.push(full);
|
|
2487
|
+
}
|
|
2488
|
+
return files2.sort();
|
|
2489
|
+
};
|
|
2490
|
+
const files = findFiles(stacksDir);
|
|
2491
|
+
const content = files.map((f) => `${f}:${fs2.statSync(f).mtimeMs}`).join("\n");
|
|
2492
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
2493
|
+
}
|
|
2494
|
+
function loadPersistedProjectChunks(projectDir, currentHash) {
|
|
2495
|
+
const indexPath = path2.join(projectDir, INDEX_FILE);
|
|
2496
|
+
if (!fs2.existsSync(indexPath))
|
|
2497
|
+
return null;
|
|
2498
|
+
try {
|
|
2499
|
+
const data = JSON.parse(fs2.readFileSync(indexPath, "utf-8"));
|
|
2500
|
+
if (data.projectHash !== currentHash)
|
|
2501
|
+
return null;
|
|
2502
|
+
return data.chunks;
|
|
2503
|
+
} catch {
|
|
2504
|
+
return null;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
function saveProjectChunks(projectDir, hash, chunks) {
|
|
2508
|
+
const indexPath = path2.join(projectDir, INDEX_FILE);
|
|
2509
|
+
fs2.mkdirSync(path2.dirname(indexPath), { recursive: true });
|
|
2510
|
+
const data = {
|
|
2511
|
+
projectHash: hash,
|
|
2512
|
+
chunks,
|
|
2513
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2514
|
+
};
|
|
2515
|
+
fs2.writeFileSync(indexPath, JSON.stringify(data, null, 2), "utf-8");
|
|
2516
|
+
}
|
|
2517
|
+
function loadKnowledgeChunks() {
|
|
2518
|
+
const chunks = [];
|
|
2519
|
+
if (!fs2.existsSync(KNOWLEDGE_DIR))
|
|
2520
|
+
return chunks;
|
|
2521
|
+
const platforms = ["aws", "azure", "gcp", "cross-cloud"];
|
|
2522
|
+
for (const platform of platforms) {
|
|
2523
|
+
const platformDir = path2.join(KNOWLEDGE_DIR, platform);
|
|
2524
|
+
if (!fs2.existsSync(platformDir))
|
|
2525
|
+
continue;
|
|
2526
|
+
const files = fs2.readdirSync(platformDir).filter((f) => f.endsWith(".md"));
|
|
2527
|
+
for (const file of files) {
|
|
2528
|
+
try {
|
|
2529
|
+
const fileChunks = (0, chunker_1.chunkKnowledgeFile)(path2.join(platformDir, file), platform);
|
|
2530
|
+
chunks.push(...fileChunks);
|
|
2531
|
+
} catch {
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
return chunks;
|
|
2536
|
+
}
|
|
2537
|
+
var VECTOR_INDEX_FILE = ".iacmp/vector-index.bin";
|
|
2538
|
+
async function buildIndexes(options) {
|
|
2539
|
+
const { projectDir, systemPromptTemplate, anthropicApiKey, voyageApiKey = process.env["VOYAGE_API_KEY"], useContextualRetrieval = !!anthropicApiKey, onProgress } = options;
|
|
2540
|
+
const log = (msg) => onProgress?.(msg);
|
|
2541
|
+
log("Indexando stacks do projeto...");
|
|
2542
|
+
const currentHash = hashProjectStacks(projectDir);
|
|
2543
|
+
let projectChunks = loadPersistedProjectChunks(projectDir, currentHash);
|
|
2544
|
+
if (!projectChunks) {
|
|
2545
|
+
log("Stacks mudaram \u2014 reindexando...");
|
|
2546
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
2547
|
+
const rawChunks = [];
|
|
2548
|
+
if (fs2.existsSync(stacksDir)) {
|
|
2549
|
+
const findFiles = (dir) => {
|
|
2550
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
2551
|
+
const files = [];
|
|
2552
|
+
for (const e of entries) {
|
|
2553
|
+
const full = path2.join(dir, e.name);
|
|
2554
|
+
if (e.isDirectory())
|
|
2555
|
+
files.push(...findFiles(full));
|
|
2556
|
+
else if (e.name.endsWith(".ts"))
|
|
2557
|
+
files.push(full);
|
|
2558
|
+
}
|
|
2559
|
+
return files;
|
|
2560
|
+
};
|
|
2561
|
+
for (const file of findFiles(stacksDir)) {
|
|
2562
|
+
rawChunks.push(...(0, chunker_1.chunkStackFile)(file, projectDir));
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
if (useContextualRetrieval && anthropicApiKey && rawChunks.length > 0) {
|
|
2566
|
+
log(`Aplicando Contextual Retrieval em ${rawChunks.length} chunks de stacks...`);
|
|
2567
|
+
const contextualizer = new contextualizer_1.Contextualizer(anthropicApiKey);
|
|
2568
|
+
const fullDocument = rawChunks.map((c) => c.content).join("\n\n");
|
|
2569
|
+
projectChunks = await contextualizer.enrichBatch(rawChunks, fullDocument, {
|
|
2570
|
+
concurrency: 5,
|
|
2571
|
+
onProgress: (done, total2) => log(` ${done}/${total2} chunks enriquecidos`)
|
|
2572
|
+
});
|
|
2573
|
+
} else {
|
|
2574
|
+
projectChunks = rawChunks;
|
|
2575
|
+
}
|
|
2576
|
+
saveProjectChunks(projectDir, currentHash, projectChunks);
|
|
2577
|
+
log(`${projectChunks.length} chunks de stacks indexados.`);
|
|
2578
|
+
} else {
|
|
2579
|
+
log(`${projectChunks.length} chunks de stacks carregados do cache.`);
|
|
2580
|
+
}
|
|
2581
|
+
log("Indexando documenta\xE7\xE3o de constructs...");
|
|
2582
|
+
let docsChunks = (0, chunker_1.chunkIacmpDocs)(systemPromptTemplate);
|
|
2583
|
+
if (useContextualRetrieval && anthropicApiKey && docsChunks.length > 0) {
|
|
2584
|
+
log(`Aplicando Contextual Retrieval em ${docsChunks.length} chunks de docs...`);
|
|
2585
|
+
const contextualizer = new contextualizer_1.Contextualizer(anthropicApiKey);
|
|
2586
|
+
const fullDoc = systemPromptTemplate;
|
|
2587
|
+
docsChunks = await contextualizer.enrichBatch(docsChunks, fullDoc, { concurrency: 5 });
|
|
2588
|
+
}
|
|
2589
|
+
log(`${docsChunks.length} chunks de docs indexados.`);
|
|
2590
|
+
log("Indexando base de conhecimento de plataforma...");
|
|
2591
|
+
const knowledgeChunks = loadKnowledgeChunks();
|
|
2592
|
+
log(`${knowledgeChunks.length} chunks de conhecimento indexados.`);
|
|
2593
|
+
const projectIndex = (0, bm25_1.buildBM25Index)(projectChunks);
|
|
2594
|
+
const docsIndex = (0, bm25_1.buildBM25Index)(docsChunks);
|
|
2595
|
+
const knowledgeIndex = (0, bm25_1.buildBM25Index)(knowledgeChunks);
|
|
2596
|
+
const chunkMap = /* @__PURE__ */ new Map();
|
|
2597
|
+
for (const c of [...projectChunks, ...docsChunks, ...knowledgeChunks]) {
|
|
2598
|
+
chunkMap.set(c.id, c);
|
|
2599
|
+
}
|
|
2600
|
+
const vectorStore = new vector_store_1.VectorStore();
|
|
2601
|
+
const vectorIndexPath = path2.join(projectDir, VECTOR_INDEX_FILE);
|
|
2602
|
+
if (voyageApiKey) {
|
|
2603
|
+
const loaded = vectorStore.load(vectorIndexPath);
|
|
2604
|
+
if (!loaded) {
|
|
2605
|
+
log(`Gerando embeddings com Voyage AI (${knowledgeChunks.length} chunks de conhecimento)...`);
|
|
2606
|
+
const embedder = (0, embedder_1.createEmbedder)(voyageApiKey);
|
|
2607
|
+
const texts = knowledgeChunks.map((c) => c.contextualContent ?? c.content);
|
|
2608
|
+
try {
|
|
2609
|
+
const vectors = await embedder.embed(texts);
|
|
2610
|
+
for (let i = 0; i < knowledgeChunks.length; i++) {
|
|
2611
|
+
const chunk = knowledgeChunks[i];
|
|
2612
|
+
vectorStore.add(chunk.id, vectors[i], {
|
|
2613
|
+
id: chunk.id,
|
|
2614
|
+
source: chunk.metadata.source,
|
|
2615
|
+
section: chunk.metadata.section,
|
|
2616
|
+
platform: chunk.metadata.platform
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
vectorStore.save(vectorIndexPath);
|
|
2620
|
+
log(`${vectorStore.size()} vetores gerados e salvos.`);
|
|
2621
|
+
} catch (err) {
|
|
2622
|
+
log(`Embeddings Voyage AI falharam \u2014 usando apenas BM25: ${err.message}`);
|
|
2623
|
+
}
|
|
2624
|
+
} else {
|
|
2625
|
+
log(`${vectorStore.size()} vetores carregados do cache.`);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
const total = projectChunks.length + docsChunks.length + knowledgeChunks.length;
|
|
2629
|
+
log(`RAG pronto \u2014 ${total} chunks indexados no total${voyageApiKey ? ` + ${vectorStore.size()} vetores` : ""}.`);
|
|
2630
|
+
return { projectIndex, docsIndex, knowledgeIndex, chunkMap, vectorStore };
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
// ../ai/dist/rag/retriever.js
|
|
2636
|
+
var require_retriever = __commonJS({
|
|
2637
|
+
"../ai/dist/rag/retriever.js"(exports2) {
|
|
2638
|
+
"use strict";
|
|
2639
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2640
|
+
exports2.retrieve = retrieve;
|
|
2641
|
+
exports2.formatRetrievedContext = formatRetrievedContext;
|
|
2642
|
+
var bm25_1 = require_bm25();
|
|
2643
|
+
var DEFAULT_K = {
|
|
2644
|
+
project: 5,
|
|
2645
|
+
docs: 3,
|
|
2646
|
+
knowledge: 5
|
|
2647
|
+
};
|
|
2648
|
+
function retrieve(indexes, query, options = {}) {
|
|
2649
|
+
const { projectK = DEFAULT_K.project, docsK = DEFAULT_K.docs, knowledgeK = DEFAULT_K.knowledge, minScore = 0.1 } = options;
|
|
2650
|
+
const results = [];
|
|
2651
|
+
const projectHits = (0, bm25_1.bm25Search)(indexes.projectIndex, query, projectK);
|
|
2652
|
+
const docsHits = (0, bm25_1.bm25Search)(indexes.docsIndex, query, docsK);
|
|
2653
|
+
const knowledgeHits = (0, bm25_1.bm25Search)(indexes.knowledgeIndex, query, knowledgeK);
|
|
2654
|
+
for (const hit of [...projectHits, ...docsHits, ...knowledgeHits]) {
|
|
2655
|
+
if (hit.score < minScore)
|
|
2656
|
+
continue;
|
|
2657
|
+
const chunk = indexes.chunkMap.get(hit.id);
|
|
2658
|
+
if (!chunk)
|
|
2659
|
+
continue;
|
|
2660
|
+
results.push({
|
|
2661
|
+
chunk,
|
|
2662
|
+
score: hit.score,
|
|
2663
|
+
source: chunk.metadata.source
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
return results.sort((a, b) => b.score - a.score);
|
|
2667
|
+
}
|
|
2668
|
+
function formatRetrievedContext(results) {
|
|
2669
|
+
if (results.length === 0)
|
|
2670
|
+
return "";
|
|
2671
|
+
const sections = ["## Contexto recuperado pelo RAG\n"];
|
|
2672
|
+
const bySource = {
|
|
2673
|
+
"project-stack": results.filter((r) => r.chunk.metadata.source === "project-stack"),
|
|
2674
|
+
"iacmp-docs": results.filter((r) => r.chunk.metadata.source === "iacmp-docs"),
|
|
2675
|
+
"platform-knowledge": results.filter((r) => r.chunk.metadata.source === "platform-knowledge")
|
|
2676
|
+
};
|
|
2677
|
+
if (bySource["project-stack"].length > 0) {
|
|
2678
|
+
sections.push("### Stacks do projeto relevantes");
|
|
2679
|
+
for (const r of bySource["project-stack"]) {
|
|
2680
|
+
sections.push(r.chunk.content);
|
|
2681
|
+
sections.push("");
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
if (bySource["iacmp-docs"].length > 0) {
|
|
2685
|
+
sections.push("### Documenta\xE7\xE3o de constructs relevante");
|
|
2686
|
+
for (const r of bySource["iacmp-docs"]) {
|
|
2687
|
+
sections.push(r.chunk.content);
|
|
2688
|
+
sections.push("");
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
if (bySource["platform-knowledge"].length > 0) {
|
|
2692
|
+
sections.push("### Conhecimento de plataforma relevante");
|
|
2693
|
+
for (const r of bySource["platform-knowledge"]) {
|
|
2694
|
+
sections.push(r.chunk.content);
|
|
2695
|
+
sections.push("");
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
return sections.join("\n");
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
|
|
2703
|
+
// ../ai/dist/rag/live-retriever.js
|
|
2704
|
+
var require_live_retriever = __commonJS({
|
|
2705
|
+
"../ai/dist/rag/live-retriever.js"(exports2) {
|
|
2706
|
+
"use strict";
|
|
2707
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
2708
|
+
if (k2 === void 0) k2 = k;
|
|
2709
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2710
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2711
|
+
desc = { enumerable: true, get: function() {
|
|
2712
|
+
return m[k];
|
|
2713
|
+
} };
|
|
2714
|
+
}
|
|
2715
|
+
Object.defineProperty(o, k2, desc);
|
|
2716
|
+
}) : (function(o, m, k, k2) {
|
|
2717
|
+
if (k2 === void 0) k2 = k;
|
|
2718
|
+
o[k2] = m[k];
|
|
2719
|
+
}));
|
|
2720
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
2721
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2722
|
+
}) : function(o, v) {
|
|
2723
|
+
o["default"] = v;
|
|
2724
|
+
});
|
|
2725
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
2726
|
+
var ownKeys = function(o) {
|
|
2727
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2728
|
+
var ar = [];
|
|
2729
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2730
|
+
return ar;
|
|
2731
|
+
};
|
|
2732
|
+
return ownKeys(o);
|
|
2733
|
+
};
|
|
2734
|
+
return function(mod) {
|
|
2735
|
+
if (mod && mod.__esModule) return mod;
|
|
2736
|
+
var result = {};
|
|
2737
|
+
if (mod != null) {
|
|
2738
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2739
|
+
}
|
|
2740
|
+
__setModuleDefault(result, mod);
|
|
2741
|
+
return result;
|
|
2742
|
+
};
|
|
2743
|
+
})();
|
|
2744
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2745
|
+
exports2.LIVE_SIGNALS = void 0;
|
|
2746
|
+
exports2.fetchLive = fetchLive;
|
|
2747
|
+
exports2.shouldFetchLive = shouldFetchLive;
|
|
2748
|
+
var fs2 = __importStar(require("fs"));
|
|
2749
|
+
var path2 = __importStar(require("path"));
|
|
2750
|
+
var CACHE_FILE = ".iacmp/live-cache.json";
|
|
2751
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
2752
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
2753
|
+
function loadCache(projectDir) {
|
|
2754
|
+
const filePath = projectDir ? path2.join(projectDir, CACHE_FILE) : path2.join(process.cwd(), CACHE_FILE);
|
|
2755
|
+
if (!fs2.existsSync(filePath))
|
|
2756
|
+
return {};
|
|
2757
|
+
try {
|
|
2758
|
+
return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
2759
|
+
} catch {
|
|
2760
|
+
return {};
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
function saveCache(store, projectDir) {
|
|
2764
|
+
const filePath = projectDir ? path2.join(projectDir, CACHE_FILE) : path2.join(process.cwd(), CACHE_FILE);
|
|
2765
|
+
try {
|
|
2766
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
2767
|
+
fs2.writeFileSync(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
2768
|
+
} catch {
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
function getCached2(store, key) {
|
|
2772
|
+
const entry = store[key];
|
|
2773
|
+
if (!entry)
|
|
2774
|
+
return null;
|
|
2775
|
+
if (Date.now() > entry.expiresAt)
|
|
2776
|
+
return null;
|
|
2777
|
+
return entry.value;
|
|
2778
|
+
}
|
|
2779
|
+
function setCached(store, key, value) {
|
|
2780
|
+
store[key] = { value, expiresAt: Date.now() + CACHE_TTL_MS };
|
|
2781
|
+
const now = Date.now();
|
|
2782
|
+
for (const k of Object.keys(store)) {
|
|
2783
|
+
if (store[k].expiresAt < now) {
|
|
2784
|
+
delete store[k];
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
async function fetchWithTimeout(url, options = {}) {
|
|
2789
|
+
const controller = new AbortController();
|
|
2790
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
2791
|
+
try {
|
|
2792
|
+
const resp = await fetch(url, { ...options, signal: controller.signal });
|
|
2793
|
+
if (!resp.ok)
|
|
2794
|
+
return "";
|
|
2795
|
+
return await resp.text();
|
|
2796
|
+
} catch {
|
|
2797
|
+
return "";
|
|
2798
|
+
} finally {
|
|
2799
|
+
clearTimeout(timer);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
function parseRssText(xml, maxItems = 5) {
|
|
2803
|
+
const items = [];
|
|
2804
|
+
const itemRegex = /<item[^>]*>([\s\S]*?)<\/item>/gi;
|
|
2805
|
+
const titleRegex = /<title[^>]*><!\[CDATA\[(.*?)\]\]><\/title>|<title[^>]*>(.*?)<\/title>/i;
|
|
2806
|
+
const descRegex = /<description[^>]*><!\[CDATA\[(.*?)\]\]><\/description>|<description[^>]*>(.*?)<\/description>/i;
|
|
2807
|
+
let match;
|
|
2808
|
+
let count = 0;
|
|
2809
|
+
while ((match = itemRegex.exec(xml)) !== null && count < maxItems) {
|
|
2810
|
+
const itemContent = match[1];
|
|
2811
|
+
const titleMatch = titleRegex.exec(itemContent);
|
|
2812
|
+
const descMatch = descRegex.exec(itemContent);
|
|
2813
|
+
const title = (titleMatch?.[1] ?? titleMatch?.[2] ?? "").trim();
|
|
2814
|
+
const desc = (descMatch?.[1] ?? descMatch?.[2] ?? "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
2815
|
+
if (title) {
|
|
2816
|
+
items.push(desc ? `- ${title}: ${desc}` : `- ${title}`);
|
|
2817
|
+
count++;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
return items.join("\n");
|
|
2821
|
+
}
|
|
2822
|
+
function mentionsPrice(query) {
|
|
2823
|
+
const lower = query.toLowerCase();
|
|
2824
|
+
return ["pre\xE7o", "preco", "custo", "quanto custa", "valor", "cost", "price", "pricing"].some((s) => lower.includes(s));
|
|
2825
|
+
}
|
|
2826
|
+
function mentionsTerraform(query) {
|
|
2827
|
+
const lower = query.toLowerCase();
|
|
2828
|
+
return ["terraform", "tf provider", "terraform provider", "hashicorp"].some((s) => lower.includes(s));
|
|
2829
|
+
}
|
|
2830
|
+
function mentionsRecent(query) {
|
|
2831
|
+
const lower = query.toLowerCase();
|
|
2832
|
+
return ["lan\xE7ou", "lancou", "novidade", "recente", "novo servi\xE7o", "novo recurso", "anunciou", "what's new", "whats new"].some((s) => lower.includes(s));
|
|
2833
|
+
}
|
|
2834
|
+
function mentionsAzure(query) {
|
|
2835
|
+
const lower = query.toLowerCase();
|
|
2836
|
+
return ["azure", "microsoft azure", "az "].some((s) => lower.includes(s));
|
|
2837
|
+
}
|
|
2838
|
+
function mentionsGcp(query) {
|
|
2839
|
+
const lower = query.toLowerCase();
|
|
2840
|
+
return ["gcp", "google cloud", "gcloud", "cloud run", "bigquery", "cloud functions", "pub/sub", "pubsub", "spanner"].some((s) => lower.includes(s));
|
|
2841
|
+
}
|
|
2842
|
+
function extractAzureServiceName(query) {
|
|
2843
|
+
const lower = query.toLowerCase();
|
|
2844
|
+
const serviceMap = [
|
|
2845
|
+
[["functions", "function app"], "Azure Functions"],
|
|
2846
|
+
[["app service", "web app"], "Azure App Service"],
|
|
2847
|
+
[["sql database", "azure sql"], "SQL Database"],
|
|
2848
|
+
[["cosmos", "cosmosdb"], "Azure Cosmos DB"],
|
|
2849
|
+
[["blob", "storage account"], "Storage"],
|
|
2850
|
+
[["kubernetes", "aks"], "Azure Kubernetes Service"],
|
|
2851
|
+
[["container instance", "aci"], "Container Instances"],
|
|
2852
|
+
[["virtual machine", "vm ", "vms "], "Virtual Machines"],
|
|
2853
|
+
[["redis", "cache"], "Azure Cache for Redis"],
|
|
2854
|
+
[["service bus"], "Service Bus"],
|
|
2855
|
+
[["event hub"], "Event Hubs"],
|
|
2856
|
+
[["api management", "apim"], "API Management"],
|
|
2857
|
+
[["postgresql", "postgres"], "Azure Database for PostgreSQL"],
|
|
2858
|
+
[["mysql"], "Azure Database for MySQL"]
|
|
2859
|
+
];
|
|
2860
|
+
for (const [terms, serviceName] of serviceMap) {
|
|
2861
|
+
if (terms.some((t) => lower.includes(t)))
|
|
2862
|
+
return serviceName;
|
|
2863
|
+
}
|
|
2864
|
+
return null;
|
|
2865
|
+
}
|
|
2866
|
+
function extractGcpServiceId(query) {
|
|
2867
|
+
const lower = query.toLowerCase();
|
|
2868
|
+
const serviceMap = [
|
|
2869
|
+
[["cloud run"], "Cloud Run"],
|
|
2870
|
+
[["cloud functions", "functions"], "Cloud Functions"],
|
|
2871
|
+
[["bigquery"], "BigQuery"],
|
|
2872
|
+
[["gke", "kubernetes", "kubernetes engine"], "Kubernetes Engine"],
|
|
2873
|
+
[["cloud sql", "sql"], "Cloud SQL"],
|
|
2874
|
+
[["spanner"], "Cloud Spanner"],
|
|
2875
|
+
[["pub/sub", "pubsub"], "Cloud Pub/Sub"],
|
|
2876
|
+
[["compute engine", "vm ", "inst\xE2ncia"], "Compute Engine"],
|
|
2877
|
+
[["cloud storage", "gcs"], "Cloud Storage"],
|
|
2878
|
+
[["cloud bigtable", "bigtable"], "Cloud Bigtable"]
|
|
2879
|
+
];
|
|
2880
|
+
for (const [terms, serviceId] of serviceMap) {
|
|
2881
|
+
if (terms.some((t) => lower.includes(t)))
|
|
2882
|
+
return serviceId;
|
|
2883
|
+
}
|
|
2884
|
+
return null;
|
|
2885
|
+
}
|
|
2886
|
+
async function fetchAwsWhatsNew(cache) {
|
|
2887
|
+
const cacheKey = "aws-whats-new";
|
|
2888
|
+
const cached = getCached2(cache, cacheKey);
|
|
2889
|
+
if (cached !== null)
|
|
2890
|
+
return cached;
|
|
2891
|
+
const xml = await fetchWithTimeout("https://aws.amazon.com/about-aws/whats-new/recent/feed/");
|
|
2892
|
+
if (!xml)
|
|
2893
|
+
return "";
|
|
2894
|
+
const text = parseRssText(xml, 5);
|
|
2895
|
+
const result = text ? `### AWS What's New (recente)
|
|
2896
|
+
${text}` : "";
|
|
2897
|
+
setCached(cache, cacheKey, result);
|
|
2898
|
+
return result;
|
|
2899
|
+
}
|
|
2900
|
+
async function fetchAwsPricing(query, cache) {
|
|
2901
|
+
const sections = [];
|
|
2902
|
+
if (query.toLowerCase().includes("lambda")) {
|
|
2903
|
+
const cacheKey = "aws-pricing-lambda";
|
|
2904
|
+
const cached = getCached2(cache, cacheKey);
|
|
2905
|
+
if (cached !== null) {
|
|
2906
|
+
if (cached)
|
|
2907
|
+
sections.push(cached);
|
|
2908
|
+
} else {
|
|
2909
|
+
const url = "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AWSLambda/current/us-east-1/index.json";
|
|
2910
|
+
const raw = await fetchWithTimeout(url);
|
|
2911
|
+
let result = "";
|
|
2912
|
+
if (raw) {
|
|
2913
|
+
try {
|
|
2914
|
+
const data = JSON.parse(raw);
|
|
2915
|
+
const pricePoints = [];
|
|
2916
|
+
if (data.terms?.OnDemand) {
|
|
2917
|
+
let count = 0;
|
|
2918
|
+
for (const sku of Object.values(data.terms.OnDemand)) {
|
|
2919
|
+
for (const term of Object.values(sku)) {
|
|
2920
|
+
for (const dim of Object.values(term.priceDimensions ?? {})) {
|
|
2921
|
+
const usd = dim.pricePerUnit?.USD;
|
|
2922
|
+
const desc = dim.description;
|
|
2923
|
+
if (usd && desc && count < 3) {
|
|
2924
|
+
pricePoints.push(` - ${desc}: $${usd}`);
|
|
2925
|
+
count++;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
if (pricePoints.length > 0) {
|
|
2932
|
+
result = `### AWS Lambda Pricing (us-east-1)
|
|
2933
|
+
${pricePoints.join("\n")}`;
|
|
2934
|
+
}
|
|
2935
|
+
} catch {
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
setCached(cache, cacheKey, result);
|
|
2939
|
+
if (result)
|
|
2940
|
+
sections.push(result);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
if (query.toLowerCase().includes("rds") || query.toLowerCase().includes("aurora")) {
|
|
2944
|
+
const cacheKey = "aws-pricing-rds-note";
|
|
2945
|
+
const cached = getCached2(cache, cacheKey);
|
|
2946
|
+
if (cached !== null) {
|
|
2947
|
+
if (cached)
|
|
2948
|
+
sections.push(cached);
|
|
2949
|
+
} else {
|
|
2950
|
+
const result = `### AWS RDS Pricing
|
|
2951
|
+
Pre\xE7os variam por engine (MySQL, PostgreSQL, Oracle, SQL Server, Aurora), tipo de inst\xE2ncia e regi\xE3o.
|
|
2952
|
+
Calculadora oficial: https://aws.amazon.com/rds/pricing/
|
|
2953
|
+
Endpoint JSON: https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonRDS/current/index.json`;
|
|
2954
|
+
setCached(cache, cacheKey, result);
|
|
2955
|
+
sections.push(result);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return sections.join("\n\n");
|
|
2959
|
+
}
|
|
2960
|
+
async function fetchAzurePricing(query, cache) {
|
|
2961
|
+
const serviceName = extractAzureServiceName(query);
|
|
2962
|
+
if (!serviceName)
|
|
2963
|
+
return "";
|
|
2964
|
+
const cacheKey = `azure-pricing-${serviceName.toLowerCase().replace(/\s+/g, "-")}`;
|
|
2965
|
+
const cached = getCached2(cache, cacheKey);
|
|
2966
|
+
if (cached !== null)
|
|
2967
|
+
return cached;
|
|
2968
|
+
const filter = encodeURIComponent(`serviceName eq '${serviceName}' and armRegionName eq 'eastus' and priceType eq 'Consumption'`);
|
|
2969
|
+
const url = `https://prices.azure.com/api/retail/prices?$filter=${filter}&$top=10`;
|
|
2970
|
+
const raw = await fetchWithTimeout(url);
|
|
2971
|
+
if (!raw)
|
|
2972
|
+
return "";
|
|
2973
|
+
try {
|
|
2974
|
+
const data = JSON.parse(raw);
|
|
2975
|
+
if (!data.Items || data.Items.length === 0)
|
|
2976
|
+
return "";
|
|
2977
|
+
const lines = [`### Azure ${serviceName} Pricing (East US)`];
|
|
2978
|
+
let count = 0;
|
|
2979
|
+
for (const item of data.Items) {
|
|
2980
|
+
if (count >= 6)
|
|
2981
|
+
break;
|
|
2982
|
+
const sku = item.skuName ?? item.productName ?? "";
|
|
2983
|
+
const price = item.retailPrice;
|
|
2984
|
+
const unit = item.unitOfMeasure ?? "";
|
|
2985
|
+
if (sku && price !== void 0) {
|
|
2986
|
+
lines.push(` - ${sku}: $${price}/${unit}`);
|
|
2987
|
+
count++;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
if (lines.length <= 1)
|
|
2991
|
+
return "";
|
|
2992
|
+
const result = lines.join("\n");
|
|
2993
|
+
setCached(cache, cacheKey, result);
|
|
2994
|
+
return result;
|
|
2995
|
+
} catch {
|
|
2996
|
+
return "";
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
async function fetchAzureUpdates(cache) {
|
|
3000
|
+
const cacheKey = "azure-updates";
|
|
3001
|
+
const cached = getCached2(cache, cacheKey);
|
|
3002
|
+
if (cached !== null)
|
|
3003
|
+
return cached;
|
|
3004
|
+
const xml = await fetchWithTimeout("https://www.microsoft.com/releasecommunications/api/v2/azure/rss");
|
|
3005
|
+
if (!xml)
|
|
3006
|
+
return "";
|
|
3007
|
+
const text = parseRssText(xml, 5);
|
|
3008
|
+
const result = text ? `### Azure Updates (recente)
|
|
3009
|
+
${text}` : "";
|
|
3010
|
+
setCached(cache, cacheKey, result);
|
|
3011
|
+
return result;
|
|
3012
|
+
}
|
|
3013
|
+
async function fetchGcpPricing(query, cache) {
|
|
3014
|
+
const targetService = extractGcpServiceId(query);
|
|
3015
|
+
if (!targetService)
|
|
3016
|
+
return "";
|
|
3017
|
+
const cacheKey = `gcp-pricing-${targetService.toLowerCase().replace(/\s+/g, "-")}`;
|
|
3018
|
+
const cached = getCached2(cache, cacheKey);
|
|
3019
|
+
if (cached !== null)
|
|
3020
|
+
return cached;
|
|
3021
|
+
const servicesRaw = await fetchWithTimeout("https://cloudbilling.googleapis.com/v1/services?pageSize=200");
|
|
3022
|
+
if (!servicesRaw)
|
|
3023
|
+
return "";
|
|
3024
|
+
let serviceId = null;
|
|
3025
|
+
try {
|
|
3026
|
+
const servicesData = JSON.parse(servicesRaw);
|
|
3027
|
+
const match = (servicesData.services ?? []).find((s) => s.displayName?.toLowerCase().includes(targetService.toLowerCase()));
|
|
3028
|
+
if (match?.name) {
|
|
3029
|
+
serviceId = match.name;
|
|
3030
|
+
}
|
|
3031
|
+
} catch {
|
|
3032
|
+
return "";
|
|
3033
|
+
}
|
|
3034
|
+
if (!serviceId)
|
|
3035
|
+
return "";
|
|
3036
|
+
const skusRaw = await fetchWithTimeout(`https://cloudbilling.googleapis.com/v1/${serviceId}/skus?pageSize=10¤cyCode=USD`);
|
|
3037
|
+
if (!skusRaw)
|
|
3038
|
+
return "";
|
|
3039
|
+
try {
|
|
3040
|
+
const skusData = JSON.parse(skusRaw);
|
|
3041
|
+
if (!skusData.skus || skusData.skus.length === 0)
|
|
3042
|
+
return "";
|
|
3043
|
+
const lines = [`### GCP ${targetService} Pricing (USD)`];
|
|
3044
|
+
let count = 0;
|
|
3045
|
+
for (const sku of skusData.skus) {
|
|
3046
|
+
if (count >= 6)
|
|
3047
|
+
break;
|
|
3048
|
+
const desc = sku.description;
|
|
3049
|
+
const rate = sku.pricingInfo?.[0]?.pricingExpression?.tieredRates?.[0];
|
|
3050
|
+
const unit = sku.pricingInfo?.[0]?.pricingExpression?.usageUnit ?? "";
|
|
3051
|
+
if (desc && rate?.unitPrice) {
|
|
3052
|
+
const units = Number(rate.unitPrice.units ?? 0);
|
|
3053
|
+
const nanos = (rate.unitPrice.nanos ?? 0) / 1e9;
|
|
3054
|
+
const price = units + nanos;
|
|
3055
|
+
lines.push(` - ${desc}: $${price.toFixed(6)}/${unit}`);
|
|
3056
|
+
count++;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
if (lines.length <= 1)
|
|
3060
|
+
return "";
|
|
3061
|
+
const result = lines.join("\n");
|
|
3062
|
+
setCached(cache, cacheKey, result);
|
|
3063
|
+
return result;
|
|
3064
|
+
} catch {
|
|
3065
|
+
return "";
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
async function fetchGcpReleaseNotes(cache) {
|
|
3069
|
+
const cacheKey = "gcp-release-notes";
|
|
3070
|
+
const cached = getCached2(cache, cacheKey);
|
|
3071
|
+
if (cached !== null)
|
|
3072
|
+
return cached;
|
|
3073
|
+
const xml = await fetchWithTimeout("https://cloud.google.com/feeds/gcp-release-notes.xml");
|
|
3074
|
+
if (!xml)
|
|
3075
|
+
return "";
|
|
3076
|
+
const text = parseAtomText(xml, 5);
|
|
3077
|
+
const result = text ? `### GCP Release Notes (recente)
|
|
3078
|
+
${text}` : "";
|
|
3079
|
+
setCached(cache, cacheKey, result);
|
|
3080
|
+
return result;
|
|
3081
|
+
}
|
|
3082
|
+
function parseAtomText(xml, maxItems = 5) {
|
|
3083
|
+
const items = [];
|
|
3084
|
+
const entryRegex = /<entry[^>]*>([\s\S]*?)<\/entry>/gi;
|
|
3085
|
+
const titleRegex = /<title[^>]*>(.*?)<\/title>/i;
|
|
3086
|
+
const summaryRegex = /<summary[^>]*>([\s\S]*?)<\/summary>/i;
|
|
3087
|
+
let match;
|
|
3088
|
+
let count = 0;
|
|
3089
|
+
while ((match = entryRegex.exec(xml)) !== null && count < maxItems) {
|
|
3090
|
+
const entryContent = match[1];
|
|
3091
|
+
const titleMatch = titleRegex.exec(entryContent);
|
|
3092
|
+
const summaryMatch = summaryRegex.exec(entryContent);
|
|
3093
|
+
const title = (titleMatch?.[1] ?? "").replace(/<[^>]+>/g, "").trim();
|
|
3094
|
+
const summary = (summaryMatch?.[1] ?? "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
3095
|
+
if (title) {
|
|
3096
|
+
items.push(summary ? `- ${title}: ${summary}` : `- ${title}`);
|
|
3097
|
+
count++;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
return items.join("\n");
|
|
3101
|
+
}
|
|
3102
|
+
async function fetchTerraformProviderVersion(cache) {
|
|
3103
|
+
const cacheKey = "tf-aws-provider-version";
|
|
3104
|
+
const cached = getCached2(cache, cacheKey);
|
|
3105
|
+
if (cached !== null)
|
|
3106
|
+
return cached;
|
|
3107
|
+
const raw = await fetchWithTimeout("https://registry.terraform.io/v1/providers/hashicorp/aws");
|
|
3108
|
+
if (!raw)
|
|
3109
|
+
return "";
|
|
3110
|
+
try {
|
|
3111
|
+
const data = JSON.parse(raw);
|
|
3112
|
+
const version = data.version;
|
|
3113
|
+
if (!version)
|
|
3114
|
+
return "";
|
|
3115
|
+
const result = `### Terraform AWS Provider
|
|
3116
|
+
Vers\xE3o atual: ${version}
|
|
3117
|
+
Documenta\xE7\xE3o: https://registry.terraform.io/providers/hashicorp/aws/latest/docs`;
|
|
3118
|
+
setCached(cache, cacheKey, result);
|
|
3119
|
+
return result;
|
|
3120
|
+
} catch {
|
|
3121
|
+
return "";
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
async function fetchLive(query, signals, options = {}) {
|
|
3125
|
+
const cache = loadCache(options.projectDir);
|
|
3126
|
+
const parts = [];
|
|
3127
|
+
const sigSet = new Set(signals);
|
|
3128
|
+
try {
|
|
3129
|
+
const isRecent = mentionsRecent(query) || sigSet.has("recent");
|
|
3130
|
+
const isPrice = mentionsPrice(query) || sigSet.has("price");
|
|
3131
|
+
const isAzure = mentionsAzure(query) || sigSet.has("azure");
|
|
3132
|
+
const isGcp = mentionsGcp(query) || sigSet.has("gcp");
|
|
3133
|
+
const isTf = mentionsTerraform(query) || sigSet.has("terraform");
|
|
3134
|
+
const fetches = [];
|
|
3135
|
+
if (isRecent && !isAzure && !isGcp)
|
|
3136
|
+
fetches.push(fetchAwsWhatsNew(cache));
|
|
3137
|
+
if (isPrice && !isAzure && !isGcp)
|
|
3138
|
+
fetches.push(fetchAwsPricing(query, cache));
|
|
3139
|
+
if (isPrice && isAzure)
|
|
3140
|
+
fetches.push(fetchAzurePricing(query, cache));
|
|
3141
|
+
if (isRecent && isAzure)
|
|
3142
|
+
fetches.push(fetchAzureUpdates(cache));
|
|
3143
|
+
if (isPrice && isGcp)
|
|
3144
|
+
fetches.push(fetchGcpPricing(query, cache));
|
|
3145
|
+
if (isRecent && isGcp)
|
|
3146
|
+
fetches.push(fetchGcpReleaseNotes(cache));
|
|
3147
|
+
if (isTf)
|
|
3148
|
+
fetches.push(fetchTerraformProviderVersion(cache));
|
|
3149
|
+
const results = await Promise.all(fetches);
|
|
3150
|
+
for (const r of results) {
|
|
3151
|
+
if (r)
|
|
3152
|
+
parts.push(r);
|
|
3153
|
+
}
|
|
3154
|
+
} finally {
|
|
3155
|
+
saveCache(cache, options.projectDir);
|
|
3156
|
+
}
|
|
3157
|
+
return parts.join("\n\n");
|
|
3158
|
+
}
|
|
3159
|
+
exports2.LIVE_SIGNALS = [
|
|
3160
|
+
// Preço / custo
|
|
3161
|
+
"pre\xE7o",
|
|
3162
|
+
"preco",
|
|
3163
|
+
"custo",
|
|
3164
|
+
"quanto custa",
|
|
3165
|
+
"valor",
|
|
3166
|
+
"pricing",
|
|
3167
|
+
// Novidades / recente
|
|
3168
|
+
"lan\xE7ou",
|
|
3169
|
+
"lancou",
|
|
3170
|
+
"novidade",
|
|
3171
|
+
"recente",
|
|
3172
|
+
"novo servi\xE7o",
|
|
3173
|
+
"novo recurso",
|
|
3174
|
+
"anunciou",
|
|
3175
|
+
"vers\xE3o atual",
|
|
3176
|
+
"versao atual",
|
|
3177
|
+
"what's new",
|
|
3178
|
+
"whats new",
|
|
3179
|
+
"release notes",
|
|
3180
|
+
"release note",
|
|
3181
|
+
// Terraform
|
|
3182
|
+
"terraform provider",
|
|
3183
|
+
"terraform aws",
|
|
3184
|
+
"terraform azure",
|
|
3185
|
+
"terraform gcp",
|
|
3186
|
+
// Azure (específicos que indicam contexto Azure + info ao vivo)
|
|
3187
|
+
"azure functions pre\xE7o",
|
|
3188
|
+
"app service custo",
|
|
3189
|
+
"cosmos db custo",
|
|
3190
|
+
"azure sql pre\xE7o",
|
|
3191
|
+
// GCP (específicos)
|
|
3192
|
+
"cloud run pre\xE7o",
|
|
3193
|
+
"bigquery custo",
|
|
3194
|
+
"gke pre\xE7o",
|
|
3195
|
+
"cloud sql gcp"
|
|
3196
|
+
];
|
|
3197
|
+
function shouldFetchLive(query) {
|
|
3198
|
+
const lower = query.toLowerCase();
|
|
3199
|
+
return exports2.LIVE_SIGNALS.some((s) => lower.includes(s));
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
});
|
|
3203
|
+
|
|
3204
|
+
// ../ai/dist/tools/context-reader.js
|
|
3205
|
+
var require_context_reader = __commonJS({
|
|
3206
|
+
"../ai/dist/tools/context-reader.js"(exports2) {
|
|
3207
|
+
"use strict";
|
|
3208
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
3209
|
+
if (k2 === void 0) k2 = k;
|
|
3210
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
3211
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
3212
|
+
desc = { enumerable: true, get: function() {
|
|
3213
|
+
return m[k];
|
|
3214
|
+
} };
|
|
3215
|
+
}
|
|
3216
|
+
Object.defineProperty(o, k2, desc);
|
|
3217
|
+
}) : (function(o, m, k, k2) {
|
|
3218
|
+
if (k2 === void 0) k2 = k;
|
|
3219
|
+
o[k2] = m[k];
|
|
3220
|
+
}));
|
|
3221
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
3222
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3223
|
+
}) : function(o, v) {
|
|
3224
|
+
o["default"] = v;
|
|
3225
|
+
});
|
|
3226
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
3227
|
+
var ownKeys = function(o) {
|
|
3228
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3229
|
+
var ar = [];
|
|
3230
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
3231
|
+
return ar;
|
|
3232
|
+
};
|
|
3233
|
+
return ownKeys(o);
|
|
3234
|
+
};
|
|
3235
|
+
return function(mod) {
|
|
3236
|
+
if (mod && mod.__esModule) return mod;
|
|
3237
|
+
var result = {};
|
|
3238
|
+
if (mod != null) {
|
|
3239
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
3240
|
+
}
|
|
3241
|
+
__setModuleDefault(result, mod);
|
|
3242
|
+
return result;
|
|
3243
|
+
};
|
|
3244
|
+
})();
|
|
3245
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3246
|
+
exports2.readProjectMeta = readProjectMeta;
|
|
3247
|
+
exports2.readProjectContext = readProjectContext;
|
|
3248
|
+
exports2.readProjectContextRAG = readProjectContextRAG2;
|
|
3249
|
+
exports2.invalidateIndexCache = invalidateIndexCache;
|
|
3250
|
+
var fs2 = __importStar(require("fs"));
|
|
3251
|
+
var path2 = __importStar(require("path"));
|
|
3252
|
+
var indexer_1 = require_indexer();
|
|
3253
|
+
var retriever_1 = require_retriever();
|
|
3254
|
+
var system_prompt_1 = require_system_prompt();
|
|
3255
|
+
var live_retriever_1 = require_live_retriever();
|
|
3256
|
+
var indexCache = /* @__PURE__ */ new Map();
|
|
3257
|
+
function readProjectMeta(projectDir) {
|
|
3258
|
+
const lines = [];
|
|
3259
|
+
const configPath = path2.join(projectDir, "iacmp.json");
|
|
3260
|
+
if (fs2.existsSync(configPath)) {
|
|
3261
|
+
try {
|
|
3262
|
+
const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
3263
|
+
lines.push("## Configura\xE7\xE3o do projeto (iacmp.json)");
|
|
3264
|
+
lines.push(`- Provider: ${config["provider"] ?? "aws"}`);
|
|
3265
|
+
lines.push(`- Regi\xE3o: ${config["region"] ?? "us-east-1"}`);
|
|
3266
|
+
lines.push(`- Linguagem: ${config["language"] ?? "typescript"}`);
|
|
3267
|
+
lines.push(`- Nome: ${config["name"] ?? path2.basename(projectDir)}`);
|
|
3268
|
+
lines.push("");
|
|
3269
|
+
} catch {
|
|
3270
|
+
lines.push("iacmp.json encontrado mas inv\xE1lido.");
|
|
3271
|
+
lines.push("");
|
|
3272
|
+
}
|
|
3273
|
+
} else {
|
|
3274
|
+
lines.push("Nenhum iacmp.json encontrado \u2014 projeto n\xE3o inicializado.");
|
|
3275
|
+
lines.push("");
|
|
3276
|
+
}
|
|
3277
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
3278
|
+
if (fs2.existsSync(stacksDir)) {
|
|
3279
|
+
const findStackFiles = (dir) => {
|
|
3280
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
3281
|
+
const files = [];
|
|
3282
|
+
for (const e of entries) {
|
|
3283
|
+
const full = path2.join(dir, e.name);
|
|
3284
|
+
if (e.isDirectory())
|
|
3285
|
+
files.push(...findStackFiles(full));
|
|
3286
|
+
else if (e.name.endsWith(".ts") || e.name.endsWith(".js"))
|
|
3287
|
+
files.push(full);
|
|
3288
|
+
}
|
|
3289
|
+
return files;
|
|
3290
|
+
};
|
|
3291
|
+
const stackFiles = findStackFiles(stacksDir);
|
|
3292
|
+
if (stackFiles.length > 0) {
|
|
3293
|
+
lines.push("## Stacks existentes");
|
|
3294
|
+
lines.push("IMPORTANTE: Estas s\xE3o as stacks reais do projeto. Ao modificar ou mover recursos, use exatamente estes caminhos \u2014 n\xE3o crie arquivos novos se o destino j\xE1 existe.");
|
|
3295
|
+
for (const filePath of stackFiles) {
|
|
3296
|
+
const rel = path2.relative(projectDir, filePath);
|
|
3297
|
+
lines.push(`
|
|
3298
|
+
### ${rel}`);
|
|
3299
|
+
lines.push("```typescript");
|
|
3300
|
+
lines.push(fs2.readFileSync(filePath, "utf-8").trimEnd());
|
|
3301
|
+
lines.push("```");
|
|
3302
|
+
}
|
|
3303
|
+
lines.push("");
|
|
3304
|
+
} else {
|
|
3305
|
+
lines.push("## Stacks existentes\nNenhuma stack encontrada em stacks/.");
|
|
3306
|
+
lines.push("");
|
|
3307
|
+
}
|
|
3308
|
+
} else {
|
|
3309
|
+
lines.push("Diret\xF3rio stacks/ n\xE3o encontrado.");
|
|
3310
|
+
lines.push("");
|
|
3311
|
+
}
|
|
3312
|
+
return lines.join("\n");
|
|
3313
|
+
}
|
|
3314
|
+
function readProjectContext(projectDir) {
|
|
3315
|
+
const lines = [];
|
|
3316
|
+
const configPath = path2.join(projectDir, "iacmp.json");
|
|
3317
|
+
if (fs2.existsSync(configPath)) {
|
|
3318
|
+
try {
|
|
3319
|
+
const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
3320
|
+
lines.push("## Configura\xE7\xE3o do projeto (iacmp.json)");
|
|
3321
|
+
lines.push(`- Provider: ${config["provider"] ?? "aws"}`);
|
|
3322
|
+
lines.push(`- Regi\xE3o: ${config["region"] ?? "us-east-1"}`);
|
|
3323
|
+
lines.push(`- Linguagem: ${config["language"] ?? "typescript"}`);
|
|
3324
|
+
lines.push(`- Nome: ${config["name"] ?? path2.basename(projectDir)}`);
|
|
3325
|
+
lines.push("");
|
|
3326
|
+
} catch {
|
|
3327
|
+
lines.push("iacmp.json encontrado mas inv\xE1lido.");
|
|
3328
|
+
lines.push("");
|
|
3329
|
+
}
|
|
3330
|
+
} else {
|
|
3331
|
+
lines.push("Nenhum iacmp.json encontrado \u2014 projeto n\xE3o inicializado.");
|
|
3332
|
+
lines.push("");
|
|
3333
|
+
}
|
|
3334
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
3335
|
+
if (fs2.existsSync(stacksDir)) {
|
|
3336
|
+
const findStackFiles = (dir) => {
|
|
3337
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
3338
|
+
const files = [];
|
|
3339
|
+
for (const e of entries) {
|
|
3340
|
+
const full = path2.join(dir, e.name);
|
|
3341
|
+
if (e.isDirectory())
|
|
3342
|
+
files.push(...findStackFiles(full));
|
|
3343
|
+
else if (e.name.endsWith(".ts") || e.name.endsWith(".js"))
|
|
3344
|
+
files.push(full);
|
|
3345
|
+
}
|
|
3346
|
+
return files;
|
|
3347
|
+
};
|
|
3348
|
+
const stackFiles = findStackFiles(stacksDir);
|
|
3349
|
+
if (stackFiles.length > 0) {
|
|
3350
|
+
lines.push("## Stacks existentes");
|
|
3351
|
+
lines.push('Caminhos completos (use exatamente estes em "deletions"):');
|
|
3352
|
+
for (const filePath of stackFiles) {
|
|
3353
|
+
const rel = path2.relative(projectDir, filePath);
|
|
3354
|
+
const stat = fs2.statSync(filePath);
|
|
3355
|
+
lines.push(`- ${rel} (${(stat.size / 1024).toFixed(1)} KB)`);
|
|
3356
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
3357
|
+
if (content.split("\n").length <= 200) {
|
|
3358
|
+
lines.push("```typescript");
|
|
3359
|
+
lines.push(content);
|
|
3360
|
+
lines.push("```");
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
lines.push("");
|
|
3364
|
+
} else {
|
|
3365
|
+
lines.push("## Stacks existentes\nNenhuma stack encontrada em stacks/.");
|
|
3366
|
+
lines.push("");
|
|
3367
|
+
}
|
|
3368
|
+
} else {
|
|
3369
|
+
lines.push("Diret\xF3rio stacks/ n\xE3o encontrado.");
|
|
3370
|
+
lines.push("");
|
|
3371
|
+
}
|
|
3372
|
+
return lines.join("\n");
|
|
3373
|
+
}
|
|
3374
|
+
async function readProjectContextRAG2(projectDir, userQuery, options = {}) {
|
|
3375
|
+
const { anthropicApiKey, useContextualRetrieval, onProgress } = options;
|
|
3376
|
+
try {
|
|
3377
|
+
const indexerOptions = {
|
|
3378
|
+
projectDir,
|
|
3379
|
+
systemPromptTemplate: system_prompt_1.SYSTEM_PROMPT_TEMPLATE,
|
|
3380
|
+
anthropicApiKey,
|
|
3381
|
+
useContextualRetrieval,
|
|
3382
|
+
onProgress: onProgress ?? (() => {
|
|
3383
|
+
})
|
|
3384
|
+
};
|
|
3385
|
+
const indexes = await (0, indexer_1.buildIndexes)(indexerOptions);
|
|
3386
|
+
indexCache.set(projectDir, indexes);
|
|
3387
|
+
const results = (0, retriever_1.retrieve)(indexes, userQuery, {
|
|
3388
|
+
projectK: 5,
|
|
3389
|
+
docsK: 3,
|
|
3390
|
+
knowledgeK: 5,
|
|
3391
|
+
minScore: 0.05
|
|
3392
|
+
});
|
|
3393
|
+
const ragContext = (0, retriever_1.formatRetrievedContext)(results);
|
|
3394
|
+
const meta = readProjectMeta(projectDir);
|
|
3395
|
+
let liveContext = "";
|
|
3396
|
+
if ((0, live_retriever_1.shouldFetchLive)(userQuery)) {
|
|
3397
|
+
liveContext = await (0, live_retriever_1.fetchLive)(userQuery, [], { projectDir });
|
|
3398
|
+
}
|
|
3399
|
+
if (!ragContext && !liveContext) {
|
|
3400
|
+
return readProjectContext(projectDir);
|
|
3401
|
+
}
|
|
3402
|
+
const parts = [meta];
|
|
3403
|
+
if (ragContext)
|
|
3404
|
+
parts.push(ragContext);
|
|
3405
|
+
if (liveContext)
|
|
3406
|
+
parts.push(`## Informa\xE7\xF5es ao vivo
|
|
3407
|
+
${liveContext}`);
|
|
3408
|
+
return parts.join("\n");
|
|
3409
|
+
} catch {
|
|
3410
|
+
return readProjectContext(projectDir);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
function invalidateIndexCache(projectDir) {
|
|
3414
|
+
if (projectDir) {
|
|
3415
|
+
indexCache.delete(projectDir);
|
|
3416
|
+
} else {
|
|
3417
|
+
indexCache.clear();
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
});
|
|
3422
|
+
|
|
3423
|
+
// ../ai/dist/tools/session-store.js
|
|
3424
|
+
var require_session_store = __commonJS({
|
|
3425
|
+
"../ai/dist/tools/session-store.js"(exports2) {
|
|
3426
|
+
"use strict";
|
|
3427
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
3428
|
+
if (k2 === void 0) k2 = k;
|
|
3429
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
3430
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
3431
|
+
desc = { enumerable: true, get: function() {
|
|
3432
|
+
return m[k];
|
|
3433
|
+
} };
|
|
3434
|
+
}
|
|
3435
|
+
Object.defineProperty(o, k2, desc);
|
|
3436
|
+
}) : (function(o, m, k, k2) {
|
|
3437
|
+
if (k2 === void 0) k2 = k;
|
|
3438
|
+
o[k2] = m[k];
|
|
3439
|
+
}));
|
|
3440
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
3441
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3442
|
+
}) : function(o, v) {
|
|
3443
|
+
o["default"] = v;
|
|
3444
|
+
});
|
|
3445
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
3446
|
+
var ownKeys = function(o) {
|
|
3447
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3448
|
+
var ar = [];
|
|
3449
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
3450
|
+
return ar;
|
|
3451
|
+
};
|
|
3452
|
+
return ownKeys(o);
|
|
3453
|
+
};
|
|
3454
|
+
return function(mod) {
|
|
3455
|
+
if (mod && mod.__esModule) return mod;
|
|
3456
|
+
var result = {};
|
|
3457
|
+
if (mod != null) {
|
|
3458
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
3459
|
+
}
|
|
3460
|
+
__setModuleDefault(result, mod);
|
|
3461
|
+
return result;
|
|
3462
|
+
};
|
|
3463
|
+
})();
|
|
3464
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3465
|
+
exports2.loadSession = loadSession2;
|
|
3466
|
+
exports2.saveSession = saveSession2;
|
|
3467
|
+
exports2.clearSession = clearSession2;
|
|
3468
|
+
var fs2 = __importStar(require("fs"));
|
|
3469
|
+
var path2 = __importStar(require("path"));
|
|
3470
|
+
var crypto = __importStar(require("crypto"));
|
|
3471
|
+
var SESSION_FILE = ".iacmp/session.json";
|
|
3472
|
+
var MAX_MESSAGES = 20;
|
|
3473
|
+
function sessionPath(projectDir) {
|
|
3474
|
+
return path2.join(projectDir, SESSION_FILE);
|
|
3475
|
+
}
|
|
3476
|
+
function hashProject(projectDir) {
|
|
3477
|
+
const stacksDir = path2.join(projectDir, "stacks");
|
|
3478
|
+
if (!fs2.existsSync(stacksDir))
|
|
3479
|
+
return "empty";
|
|
3480
|
+
const findFiles = (dir) => {
|
|
3481
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
3482
|
+
const files = [];
|
|
3483
|
+
for (const e of entries) {
|
|
3484
|
+
const full = path2.join(dir, e.name);
|
|
3485
|
+
if (e.isDirectory())
|
|
3486
|
+
files.push(...findFiles(full));
|
|
3487
|
+
else if (e.name.endsWith(".ts"))
|
|
3488
|
+
files.push(path2.relative(projectDir, full));
|
|
3489
|
+
}
|
|
3490
|
+
return files.sort();
|
|
3491
|
+
};
|
|
3492
|
+
return crypto.createHash("sha256").update(findFiles(stacksDir).join("\n")).digest("hex").slice(0, 8);
|
|
3493
|
+
}
|
|
3494
|
+
function loadSession2(projectDir) {
|
|
3495
|
+
const file = sessionPath(projectDir);
|
|
3496
|
+
if (!fs2.existsSync(file))
|
|
3497
|
+
return [];
|
|
3498
|
+
try {
|
|
3499
|
+
const data = JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
3500
|
+
const currentHash = hashProject(projectDir);
|
|
3501
|
+
if (data.projectHash && data.projectHash !== currentHash) {
|
|
3502
|
+
return [];
|
|
3503
|
+
}
|
|
3504
|
+
const messages = data.messages ?? [];
|
|
3505
|
+
for (let i = 0; i < messages.length - 1; i++) {
|
|
3506
|
+
if (messages[i].role === "user" && messages[i + 1].role === "user")
|
|
3507
|
+
return [];
|
|
3508
|
+
}
|
|
3509
|
+
if (messages.length > 0 && messages[messages.length - 1].role === "user")
|
|
3510
|
+
return [];
|
|
3511
|
+
return messages;
|
|
3512
|
+
} catch {
|
|
3513
|
+
return [];
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
function saveSession2(projectDir, messages) {
|
|
3517
|
+
const dir = path2.dirname(sessionPath(projectDir));
|
|
3518
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3519
|
+
const data = {
|
|
3520
|
+
messages: messages.slice(-MAX_MESSAGES),
|
|
3521
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3522
|
+
projectHash: hashProject(projectDir)
|
|
3523
|
+
};
|
|
3524
|
+
fs2.writeFileSync(sessionPath(projectDir), JSON.stringify(data, null, 2), "utf-8");
|
|
3525
|
+
}
|
|
3526
|
+
function clearSession2(projectDir) {
|
|
3527
|
+
const file = sessionPath(projectDir);
|
|
3528
|
+
if (fs2.existsSync(file))
|
|
3529
|
+
fs2.unlinkSync(file);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
});
|
|
3533
|
+
|
|
3534
|
+
// ../ai/dist/tools/response-cache.js
|
|
3535
|
+
var require_response_cache = __commonJS({
|
|
3536
|
+
"../ai/dist/tools/response-cache.js"(exports2) {
|
|
3537
|
+
"use strict";
|
|
3538
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
3539
|
+
if (k2 === void 0) k2 = k;
|
|
3540
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
3541
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
3542
|
+
desc = { enumerable: true, get: function() {
|
|
3543
|
+
return m[k];
|
|
3544
|
+
} };
|
|
3545
|
+
}
|
|
3546
|
+
Object.defineProperty(o, k2, desc);
|
|
3547
|
+
}) : (function(o, m, k, k2) {
|
|
3548
|
+
if (k2 === void 0) k2 = k;
|
|
3549
|
+
o[k2] = m[k];
|
|
3550
|
+
}));
|
|
3551
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
3552
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3553
|
+
}) : function(o, v) {
|
|
3554
|
+
o["default"] = v;
|
|
3555
|
+
});
|
|
3556
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
3557
|
+
var ownKeys = function(o) {
|
|
3558
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3559
|
+
var ar = [];
|
|
3560
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
3561
|
+
return ar;
|
|
3562
|
+
};
|
|
3563
|
+
return ownKeys(o);
|
|
3564
|
+
};
|
|
3565
|
+
return function(mod) {
|
|
3566
|
+
if (mod && mod.__esModule) return mod;
|
|
3567
|
+
var result = {};
|
|
3568
|
+
if (mod != null) {
|
|
3569
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
3570
|
+
}
|
|
3571
|
+
__setModuleDefault(result, mod);
|
|
3572
|
+
return result;
|
|
3573
|
+
};
|
|
3574
|
+
})();
|
|
3575
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3576
|
+
exports2.hashPrompt = hashPrompt;
|
|
3577
|
+
exports2.getCached = getCached2;
|
|
3578
|
+
exports2.setCache = setCache2;
|
|
3579
|
+
exports2.clearCache = clearCache2;
|
|
3580
|
+
var fs2 = __importStar(require("fs"));
|
|
3581
|
+
var path2 = __importStar(require("path"));
|
|
3582
|
+
var crypto_1 = require("crypto");
|
|
3583
|
+
var CACHE_FILE = ".iacmp/cache.json";
|
|
3584
|
+
var TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3585
|
+
function cachePath(projectDir) {
|
|
3586
|
+
return path2.join(projectDir, CACHE_FILE);
|
|
3587
|
+
}
|
|
3588
|
+
function loadCache(projectDir) {
|
|
3589
|
+
const file = cachePath(projectDir);
|
|
3590
|
+
if (!fs2.existsSync(file))
|
|
3591
|
+
return { entries: [] };
|
|
3592
|
+
try {
|
|
3593
|
+
return JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
3594
|
+
} catch {
|
|
3595
|
+
return { entries: [] };
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
function saveCache(projectDir, data) {
|
|
3599
|
+
const dir = path2.dirname(cachePath(projectDir));
|
|
3600
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3601
|
+
fs2.writeFileSync(cachePath(projectDir), JSON.stringify(data, null, 2), "utf-8");
|
|
3602
|
+
}
|
|
3603
|
+
function hashPrompt(prompt) {
|
|
3604
|
+
return (0, crypto_1.createHash)("sha256").update(prompt.trim()).digest("hex").slice(0, 16);
|
|
3605
|
+
}
|
|
3606
|
+
function getCached2(projectDir, prompt) {
|
|
3607
|
+
const hash = hashPrompt(prompt);
|
|
3608
|
+
const data = loadCache(projectDir);
|
|
3609
|
+
const entry = data.entries.find((e) => e.hash === hash);
|
|
3610
|
+
if (!entry)
|
|
3611
|
+
return null;
|
|
3612
|
+
const age = Date.now() - new Date(entry.createdAt).getTime();
|
|
3613
|
+
if (age > TTL_MS)
|
|
3614
|
+
return null;
|
|
3615
|
+
return entry.response;
|
|
3616
|
+
}
|
|
3617
|
+
function setCache2(projectDir, prompt, response) {
|
|
3618
|
+
const hash = hashPrompt(prompt);
|
|
3619
|
+
const data = loadCache(projectDir);
|
|
3620
|
+
data.entries = data.entries.filter((e) => e.hash !== hash);
|
|
3621
|
+
data.entries.push({
|
|
3622
|
+
hash,
|
|
3623
|
+
prompt: prompt.slice(0, 200),
|
|
3624
|
+
response,
|
|
3625
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3626
|
+
});
|
|
3627
|
+
saveCache(projectDir, data);
|
|
3628
|
+
}
|
|
3629
|
+
function clearCache2(projectDir) {
|
|
3630
|
+
const file = cachePath(projectDir);
|
|
3631
|
+
if (fs2.existsSync(file))
|
|
3632
|
+
fs2.unlinkSync(file);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
});
|
|
3636
|
+
|
|
3637
|
+
// ../ai/dist/index.js
|
|
3638
|
+
var require_dist = __commonJS({
|
|
3639
|
+
"../ai/dist/index.js"(exports2) {
|
|
3640
|
+
"use strict";
|
|
3641
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3642
|
+
exports2.buildIndexes = exports2.formatRetrievedContext = exports2.retrieve = exports2.bm25Search = exports2.buildBM25Index = exports2.Contextualizer = exports2.chunkKnowledgeFile = exports2.chunkIacmpDocs = exports2.chunkStackFile = exports2.clearCache = exports2.setCache = exports2.getCached = exports2.clearSession = exports2.saveSession = exports2.loadSession = exports2.invalidateIndexCache = exports2.readProjectContextAsync = exports2.readProjectContextRAG = exports2.readProjectMeta = exports2.readProjectContext = exports2.NATIVE_PROVIDERS = exports2.assertValidProvider = exports2.assertValidStackName = exports2.errMessage = exports2.isWithin = exports2.safeJoin = exports2.runSynth = exports2.renderAndConfirm = exports2.deleteFiles = exports2.writeGeneratedFiles = exports2.printStreamChunk = exports2.printNextSteps = exports2.printWarnings = exports2.printExplanation = exports2.stopThinking = exports2.printThinking = exports2.ChatSession = exports2.validateTypeScript = exports2.extractResponse = exports2.buildSystemPrompt = exports2.SYSTEM_PROMPT_TEMPLATE = exports2.SYSTEM_PROMPT = exports2.CopilotProvider = exports2.AnthropicProvider = void 0;
|
|
3643
|
+
var anthropic_1 = require_anthropic();
|
|
3644
|
+
Object.defineProperty(exports2, "AnthropicProvider", { enumerable: true, get: function() {
|
|
3645
|
+
return anthropic_1.AnthropicProvider;
|
|
3646
|
+
} });
|
|
3647
|
+
var copilot_1 = require_copilot();
|
|
3648
|
+
Object.defineProperty(exports2, "CopilotProvider", { enumerable: true, get: function() {
|
|
3649
|
+
return copilot_1.CopilotProvider;
|
|
3650
|
+
} });
|
|
3651
|
+
var system_prompt_1 = require_system_prompt();
|
|
3652
|
+
Object.defineProperty(exports2, "SYSTEM_PROMPT", { enumerable: true, get: function() {
|
|
3653
|
+
return system_prompt_1.SYSTEM_PROMPT;
|
|
3654
|
+
} });
|
|
3655
|
+
Object.defineProperty(exports2, "SYSTEM_PROMPT_TEMPLATE", { enumerable: true, get: function() {
|
|
3656
|
+
return system_prompt_1.SYSTEM_PROMPT_TEMPLATE;
|
|
3657
|
+
} });
|
|
3658
|
+
Object.defineProperty(exports2, "buildSystemPrompt", { enumerable: true, get: function() {
|
|
3659
|
+
return system_prompt_1.buildSystemPrompt;
|
|
3660
|
+
} });
|
|
3661
|
+
var code_extractor_1 = require_code_extractor();
|
|
3662
|
+
Object.defineProperty(exports2, "extractResponse", { enumerable: true, get: function() {
|
|
3663
|
+
return code_extractor_1.extractResponse;
|
|
3664
|
+
} });
|
|
3665
|
+
var validator_1 = require_validator();
|
|
3666
|
+
Object.defineProperty(exports2, "validateTypeScript", { enumerable: true, get: function() {
|
|
3667
|
+
return validator_1.validateTypeScript;
|
|
3668
|
+
} });
|
|
3669
|
+
var session_1 = require_session();
|
|
3670
|
+
Object.defineProperty(exports2, "ChatSession", { enumerable: true, get: function() {
|
|
3671
|
+
return session_1.ChatSession;
|
|
3672
|
+
} });
|
|
3673
|
+
var renderer_1 = require_renderer();
|
|
3674
|
+
Object.defineProperty(exports2, "printThinking", { enumerable: true, get: function() {
|
|
3675
|
+
return renderer_1.printThinking;
|
|
3676
|
+
} });
|
|
3677
|
+
Object.defineProperty(exports2, "stopThinking", { enumerable: true, get: function() {
|
|
3678
|
+
return renderer_1.stopThinking;
|
|
3679
|
+
} });
|
|
3680
|
+
Object.defineProperty(exports2, "printExplanation", { enumerable: true, get: function() {
|
|
3681
|
+
return renderer_1.printExplanation;
|
|
3682
|
+
} });
|
|
3683
|
+
Object.defineProperty(exports2, "printWarnings", { enumerable: true, get: function() {
|
|
3684
|
+
return renderer_1.printWarnings;
|
|
3685
|
+
} });
|
|
3686
|
+
Object.defineProperty(exports2, "printNextSteps", { enumerable: true, get: function() {
|
|
3687
|
+
return renderer_1.printNextSteps;
|
|
3688
|
+
} });
|
|
3689
|
+
Object.defineProperty(exports2, "printStreamChunk", { enumerable: true, get: function() {
|
|
3690
|
+
return renderer_1.printStreamChunk;
|
|
3691
|
+
} });
|
|
3692
|
+
var file_writer_1 = require_file_writer();
|
|
3693
|
+
Object.defineProperty(exports2, "writeGeneratedFiles", { enumerable: true, get: function() {
|
|
3694
|
+
return file_writer_1.writeGeneratedFiles;
|
|
3695
|
+
} });
|
|
3696
|
+
var file_deleter_1 = require_file_deleter();
|
|
3697
|
+
Object.defineProperty(exports2, "deleteFiles", { enumerable: true, get: function() {
|
|
3698
|
+
return file_deleter_1.deleteFiles;
|
|
3699
|
+
} });
|
|
3700
|
+
var diff_renderer_1 = require_diff_renderer();
|
|
3701
|
+
Object.defineProperty(exports2, "renderAndConfirm", { enumerable: true, get: function() {
|
|
3702
|
+
return diff_renderer_1.renderAndConfirm;
|
|
3703
|
+
} });
|
|
3704
|
+
var synth_runner_1 = require_synth_runner();
|
|
3705
|
+
Object.defineProperty(exports2, "runSynth", { enumerable: true, get: function() {
|
|
3706
|
+
return synth_runner_1.runSynth;
|
|
3707
|
+
} });
|
|
3708
|
+
var safe_path_1 = require_safe_path();
|
|
3709
|
+
Object.defineProperty(exports2, "safeJoin", { enumerable: true, get: function() {
|
|
3710
|
+
return safe_path_1.safeJoin;
|
|
3711
|
+
} });
|
|
3712
|
+
Object.defineProperty(exports2, "isWithin", { enumerable: true, get: function() {
|
|
3713
|
+
return safe_path_1.isWithin;
|
|
3714
|
+
} });
|
|
3715
|
+
Object.defineProperty(exports2, "errMessage", { enumerable: true, get: function() {
|
|
3716
|
+
return safe_path_1.errMessage;
|
|
3717
|
+
} });
|
|
3718
|
+
Object.defineProperty(exports2, "assertValidStackName", { enumerable: true, get: function() {
|
|
3719
|
+
return safe_path_1.assertValidStackName;
|
|
3720
|
+
} });
|
|
3721
|
+
Object.defineProperty(exports2, "assertValidProvider", { enumerable: true, get: function() {
|
|
3722
|
+
return safe_path_1.assertValidProvider;
|
|
3723
|
+
} });
|
|
3724
|
+
Object.defineProperty(exports2, "NATIVE_PROVIDERS", { enumerable: true, get: function() {
|
|
3725
|
+
return safe_path_1.NATIVE_PROVIDERS;
|
|
3726
|
+
} });
|
|
3727
|
+
var context_reader_1 = require_context_reader();
|
|
3728
|
+
Object.defineProperty(exports2, "readProjectContext", { enumerable: true, get: function() {
|
|
3729
|
+
return context_reader_1.readProjectContext;
|
|
3730
|
+
} });
|
|
3731
|
+
Object.defineProperty(exports2, "readProjectMeta", { enumerable: true, get: function() {
|
|
3732
|
+
return context_reader_1.readProjectMeta;
|
|
3733
|
+
} });
|
|
3734
|
+
Object.defineProperty(exports2, "readProjectContextRAG", { enumerable: true, get: function() {
|
|
3735
|
+
return context_reader_1.readProjectContextRAG;
|
|
3736
|
+
} });
|
|
3737
|
+
Object.defineProperty(exports2, "readProjectContextAsync", { enumerable: true, get: function() {
|
|
3738
|
+
return context_reader_1.readProjectContextRAG;
|
|
3739
|
+
} });
|
|
3740
|
+
Object.defineProperty(exports2, "invalidateIndexCache", { enumerable: true, get: function() {
|
|
3741
|
+
return context_reader_1.invalidateIndexCache;
|
|
3742
|
+
} });
|
|
3743
|
+
var session_store_1 = require_session_store();
|
|
3744
|
+
Object.defineProperty(exports2, "loadSession", { enumerable: true, get: function() {
|
|
3745
|
+
return session_store_1.loadSession;
|
|
3746
|
+
} });
|
|
3747
|
+
Object.defineProperty(exports2, "saveSession", { enumerable: true, get: function() {
|
|
3748
|
+
return session_store_1.saveSession;
|
|
3749
|
+
} });
|
|
3750
|
+
Object.defineProperty(exports2, "clearSession", { enumerable: true, get: function() {
|
|
3751
|
+
return session_store_1.clearSession;
|
|
3752
|
+
} });
|
|
3753
|
+
var response_cache_1 = require_response_cache();
|
|
3754
|
+
Object.defineProperty(exports2, "getCached", { enumerable: true, get: function() {
|
|
3755
|
+
return response_cache_1.getCached;
|
|
3756
|
+
} });
|
|
3757
|
+
Object.defineProperty(exports2, "setCache", { enumerable: true, get: function() {
|
|
3758
|
+
return response_cache_1.setCache;
|
|
3759
|
+
} });
|
|
3760
|
+
Object.defineProperty(exports2, "clearCache", { enumerable: true, get: function() {
|
|
3761
|
+
return response_cache_1.clearCache;
|
|
3762
|
+
} });
|
|
3763
|
+
var chunker_1 = require_chunker();
|
|
3764
|
+
Object.defineProperty(exports2, "chunkStackFile", { enumerable: true, get: function() {
|
|
3765
|
+
return chunker_1.chunkStackFile;
|
|
3766
|
+
} });
|
|
3767
|
+
Object.defineProperty(exports2, "chunkIacmpDocs", { enumerable: true, get: function() {
|
|
3768
|
+
return chunker_1.chunkIacmpDocs;
|
|
3769
|
+
} });
|
|
3770
|
+
Object.defineProperty(exports2, "chunkKnowledgeFile", { enumerable: true, get: function() {
|
|
3771
|
+
return chunker_1.chunkKnowledgeFile;
|
|
3772
|
+
} });
|
|
3773
|
+
var contextualizer_1 = require_contextualizer();
|
|
3774
|
+
Object.defineProperty(exports2, "Contextualizer", { enumerable: true, get: function() {
|
|
3775
|
+
return contextualizer_1.Contextualizer;
|
|
3776
|
+
} });
|
|
3777
|
+
var bm25_1 = require_bm25();
|
|
3778
|
+
Object.defineProperty(exports2, "buildBM25Index", { enumerable: true, get: function() {
|
|
3779
|
+
return bm25_1.buildBM25Index;
|
|
3780
|
+
} });
|
|
3781
|
+
Object.defineProperty(exports2, "bm25Search", { enumerable: true, get: function() {
|
|
3782
|
+
return bm25_1.bm25Search;
|
|
3783
|
+
} });
|
|
3784
|
+
var retriever_1 = require_retriever();
|
|
3785
|
+
Object.defineProperty(exports2, "retrieve", { enumerable: true, get: function() {
|
|
3786
|
+
return retriever_1.retrieve;
|
|
3787
|
+
} });
|
|
3788
|
+
Object.defineProperty(exports2, "formatRetrievedContext", { enumerable: true, get: function() {
|
|
3789
|
+
return retriever_1.formatRetrievedContext;
|
|
3790
|
+
} });
|
|
3791
|
+
var indexer_1 = require_indexer();
|
|
3792
|
+
Object.defineProperty(exports2, "buildIndexes", { enumerable: true, get: function() {
|
|
3793
|
+
return indexer_1.buildIndexes;
|
|
3794
|
+
} });
|
|
3795
|
+
}
|
|
3796
|
+
});
|
|
3797
|
+
|
|
3798
|
+
// bin/chat.js
|
|
3799
|
+
var readline = require("readline");
|
|
3800
|
+
var path = require("path");
|
|
3801
|
+
var fs = require("fs");
|
|
3802
|
+
(function loadEnv() {
|
|
3803
|
+
const cwd2 = process.env.IACMP_CWD || process.cwd();
|
|
3804
|
+
const envPath = path.resolve(cwd2, ".env");
|
|
3805
|
+
if (!fs.existsSync(envPath)) return;
|
|
3806
|
+
const lines = fs.readFileSync(envPath, "utf8").split("\n");
|
|
3807
|
+
for (const line of lines) {
|
|
3808
|
+
const trimmed = line.trim();
|
|
3809
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3810
|
+
const eq = trimmed.indexOf("=");
|
|
3811
|
+
if (eq === -1) continue;
|
|
3812
|
+
const key = trimmed.slice(0, eq).trim();
|
|
3813
|
+
const val = trimmed.slice(eq + 1).trim();
|
|
3814
|
+
if (key) process.env[key] = val;
|
|
3815
|
+
}
|
|
3816
|
+
})();
|
|
3817
|
+
var Module = require("module");
|
|
3818
|
+
var _orig = Module._resolveFilename.bind(Module);
|
|
3819
|
+
var cliDir = path.resolve(__dirname, "..");
|
|
3820
|
+
var searchRoots = [cliDir, path.resolve(cliDir, "..", "..")];
|
|
3821
|
+
Module._resolveFilename = function(req, parent, isMain, opts) {
|
|
3822
|
+
if (req.startsWith("@iacmp/")) {
|
|
3823
|
+
try {
|
|
3824
|
+
return _orig(req, parent, isMain, opts);
|
|
3825
|
+
} catch {
|
|
3826
|
+
}
|
|
3827
|
+
for (const root of searchRoots) {
|
|
3828
|
+
try {
|
|
3829
|
+
return _orig(req, { id: path.join(root, "_fake.js"), filename: path.join(root, "_fake.js") }, isMain, opts);
|
|
3830
|
+
} catch {
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
const pkgName = req.replace("@iacmp/", "");
|
|
3834
|
+
const candidates = [
|
|
3835
|
+
path.resolve(cliDir, "..", pkgName, "dist", "index.js"),
|
|
3836
|
+
path.resolve(cliDir, "node_modules", req, "dist", "index.js")
|
|
3837
|
+
];
|
|
3838
|
+
for (const c of candidates) {
|
|
3839
|
+
if (fs.existsSync(c)) return c;
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
return _orig(req, parent, isMain, opts);
|
|
3843
|
+
};
|
|
3844
|
+
var chalk = require("chalk");
|
|
3845
|
+
var {
|
|
3846
|
+
AnthropicProvider,
|
|
3847
|
+
CopilotProvider,
|
|
3848
|
+
ChatSession,
|
|
3849
|
+
extractResponse,
|
|
3850
|
+
validateTypeScript,
|
|
3851
|
+
writeGeneratedFiles,
|
|
3852
|
+
deleteFiles,
|
|
3853
|
+
runSynth,
|
|
3854
|
+
readProjectContextRAG,
|
|
3855
|
+
printExplanation,
|
|
3856
|
+
printWarnings,
|
|
3857
|
+
printNextSteps,
|
|
3858
|
+
buildSystemPrompt,
|
|
3859
|
+
loadSession,
|
|
3860
|
+
saveSession,
|
|
3861
|
+
clearSession,
|
|
3862
|
+
getCached,
|
|
3863
|
+
setCache,
|
|
3864
|
+
clearCache
|
|
3865
|
+
} = require_dist();
|
|
3866
|
+
var cwd = process.env.IACMP_CWD || process.cwd();
|
|
3867
|
+
var iacProvider = process.env.IACMP_PROVIDER || "aws";
|
|
3868
|
+
var dryRun = process.env.IACMP_DRYRUN === "1";
|
|
3869
|
+
var rl = readline.createInterface({
|
|
3870
|
+
input: process.stdin,
|
|
3871
|
+
output: process.stdout,
|
|
3872
|
+
terminal: process.stdin.isTTY,
|
|
3873
|
+
crlfDelay: Infinity
|
|
3874
|
+
});
|
|
3875
|
+
var _lineQueue = [];
|
|
3876
|
+
var _lineWaiters = [];
|
|
3877
|
+
var _rlDone = false;
|
|
3878
|
+
rl.on("line", (line) => {
|
|
3879
|
+
const trimmed = line.trim();
|
|
3880
|
+
if (_lineWaiters.length > 0) {
|
|
3881
|
+
_lineWaiters.shift()(trimmed);
|
|
3882
|
+
} else {
|
|
3883
|
+
_lineQueue.push(trimmed);
|
|
3884
|
+
}
|
|
3885
|
+
});
|
|
3886
|
+
rl.on("close", () => {
|
|
3887
|
+
_rlDone = true;
|
|
3888
|
+
while (_lineWaiters.length > 0) _lineWaiters.shift()("");
|
|
3889
|
+
});
|
|
3890
|
+
function ask(question) {
|
|
3891
|
+
process.stdout.write(question);
|
|
3892
|
+
return new Promise((resolve) => {
|
|
3893
|
+
if (_lineQueue.length > 0) {
|
|
3894
|
+
resolve(_lineQueue.shift());
|
|
3895
|
+
} else if (_rlDone) {
|
|
3896
|
+
resolve("");
|
|
3897
|
+
} else {
|
|
3898
|
+
_lineWaiters.push(resolve);
|
|
3899
|
+
}
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
function resolveProvider() {
|
|
3903
|
+
if (process.env.ANTHROPIC_API_KEY) return new AnthropicProvider(process.env.ANTHROPIC_API_KEY);
|
|
3904
|
+
if (process.env.GITHUB_TOKEN) return new CopilotProvider(process.env.GITHUB_TOKEN);
|
|
3905
|
+
throw new Error("Configure ANTHROPIC_API_KEY no .env do projeto");
|
|
3906
|
+
}
|
|
3907
|
+
function createContextualProvider(base, projectContext) {
|
|
3908
|
+
const systemPrompt = buildSystemPrompt(projectContext);
|
|
3909
|
+
return {
|
|
3910
|
+
name: base.name,
|
|
3911
|
+
chat: (msgs) => base.chat([{ role: "system", content: systemPrompt }, ...msgs]),
|
|
3912
|
+
stream: (msgs, onChunk) => base.stream([{ role: "system", content: systemPrompt }, ...msgs], onChunk)
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
async function runGeneration(provider, session, lastPrompt, projectContext) {
|
|
3916
|
+
let acted = false;
|
|
3917
|
+
const contextHash = projectContext ? require("crypto").createHash("md5").update(projectContext).digest("hex").slice(0, 8) : "";
|
|
3918
|
+
const cacheKey = contextHash ? `${lastPrompt}__ctx:${contextHash}` : lastPrompt;
|
|
3919
|
+
const cached = getCached(cwd, cacheKey);
|
|
3920
|
+
let raw;
|
|
3921
|
+
let fromCache = false;
|
|
3922
|
+
if (cached) {
|
|
3923
|
+
try {
|
|
3924
|
+
extractResponse(cached);
|
|
3925
|
+
process.stderr.write(chalk.dim(" \u21A9 resposta do cache\n"));
|
|
3926
|
+
raw = cached;
|
|
3927
|
+
fromCache = true;
|
|
3928
|
+
session.addAssistantMessage(raw);
|
|
3929
|
+
} catch {
|
|
3930
|
+
clearCache(cwd, cacheKey);
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
if (!raw) {
|
|
3934
|
+
process.stderr.write(chalk.dim("Gerando...\n"));
|
|
3935
|
+
const chunks = [];
|
|
3936
|
+
try {
|
|
3937
|
+
await provider.stream(session.getMessages(), (chunk) => chunks.push(chunk));
|
|
3938
|
+
} catch (err) {
|
|
3939
|
+
process.stderr.write(chalk.red("Erro: " + err.message + "\n"));
|
|
3940
|
+
process.stderr.write(chalk.dim("Sua mensagem n\xE3o foi salva na sess\xE3o \u2014 pode repetir o pedido.\n"));
|
|
3941
|
+
return;
|
|
3942
|
+
}
|
|
3943
|
+
raw = chunks.join("");
|
|
3944
|
+
session.addAssistantMessage(raw);
|
|
3945
|
+
}
|
|
3946
|
+
let parsed;
|
|
3947
|
+
try {
|
|
3948
|
+
parsed = extractResponse(raw);
|
|
3949
|
+
} catch {
|
|
3950
|
+
console.log("\n" + raw.trim() + "\n");
|
|
3951
|
+
return true;
|
|
3952
|
+
}
|
|
3953
|
+
if (!fromCache) {
|
|
3954
|
+
setCache(cwd, cacheKey, raw);
|
|
3955
|
+
}
|
|
3956
|
+
const tsFiles = parsed.files.filter((f) => f.path.endsWith(".ts"));
|
|
3957
|
+
if (tsFiles.length > 0) {
|
|
3958
|
+
const result = validateTypeScript(tsFiles, cwd);
|
|
3959
|
+
if (!result.valid) {
|
|
3960
|
+
process.stderr.write(chalk.dim("Valida\xE7\xE3o falhou \u2014 corrigindo...\n"));
|
|
3961
|
+
session.addUserMessage(`Erros TypeScript:
|
|
3962
|
+
${result.errors.join("\n")}
|
|
3963
|
+
|
|
3964
|
+
Corrija e retorne o JSON completo.`);
|
|
3965
|
+
const retryChunks = [];
|
|
3966
|
+
try {
|
|
3967
|
+
await provider.stream(session.getMessages(), (chunk) => retryChunks.push(chunk));
|
|
3968
|
+
const retryRaw = retryChunks.join("");
|
|
3969
|
+
session.addAssistantMessage(retryRaw);
|
|
3970
|
+
try {
|
|
3971
|
+
parsed = extractResponse(retryRaw);
|
|
3972
|
+
} catch {
|
|
3973
|
+
}
|
|
3974
|
+
} catch (err) {
|
|
3975
|
+
process.stderr.write(chalk.red("Erro no retry: " + err.message + "\n"));
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
printExplanation(parsed.explanation);
|
|
3980
|
+
printWarnings(parsed.warnings);
|
|
3981
|
+
if (parsed.deletions && parsed.deletions.length > 0) {
|
|
3982
|
+
await deleteFiles(parsed.deletions, cwd, iacProvider, ask);
|
|
3983
|
+
acted = true;
|
|
3984
|
+
}
|
|
3985
|
+
if (parsed.files.length > 0) {
|
|
3986
|
+
await writeGeneratedFiles(parsed.files, cwd, dryRun, ask);
|
|
3987
|
+
acted = true;
|
|
3988
|
+
}
|
|
3989
|
+
printNextSteps(parsed.nextSteps);
|
|
3990
|
+
return true;
|
|
3991
|
+
}
|
|
3992
|
+
async function main() {
|
|
3993
|
+
const previous = loadSession(cwd);
|
|
3994
|
+
const session = new ChatSession();
|
|
3995
|
+
if (previous.length > 0) {
|
|
3996
|
+
const contaminated = previous.some((msg) => {
|
|
3997
|
+
if (msg.role !== "assistant") return false;
|
|
3998
|
+
try {
|
|
3999
|
+
const parsed = JSON.parse(msg.content);
|
|
4000
|
+
return typeof parsed.explanation === "string" && parsed.explanation.toLowerCase().includes("standalone");
|
|
4001
|
+
} catch {
|
|
4002
|
+
return false;
|
|
4003
|
+
}
|
|
4004
|
+
});
|
|
4005
|
+
if (contaminated) {
|
|
4006
|
+
clearSession(cwd);
|
|
4007
|
+
clearCache(cwd);
|
|
4008
|
+
console.log(chalk.dim("\n Sess\xE3o anterior descartada (contexto desatualizado)"));
|
|
4009
|
+
} else {
|
|
4010
|
+
for (const msg of previous) {
|
|
4011
|
+
if (msg.role === "user") session.addUserMessage(msg.content);
|
|
4012
|
+
else session.addAssistantMessage(msg.content);
|
|
4013
|
+
}
|
|
4014
|
+
console.log(chalk.dim(`
|
|
4015
|
+
Sess\xE3o anterior carregada (${previous.length} mensagens)`));
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
console.log(chalk.cyan.bold("\niacmp ai \u2014 Modo Chat Interativo"));
|
|
4019
|
+
console.log(chalk.dim("Comandos: /sair, /quit \u2014 encerra | /limpar \u2014 limpa sess\xE3o e cache\n"));
|
|
4020
|
+
let aiProvider;
|
|
4021
|
+
try {
|
|
4022
|
+
aiProvider = resolveProvider();
|
|
4023
|
+
} catch (err) {
|
|
4024
|
+
console.error(chalk.red(err.message));
|
|
4025
|
+
rl.close();
|
|
4026
|
+
process.exit(1);
|
|
4027
|
+
}
|
|
4028
|
+
while (true) {
|
|
4029
|
+
const input = await ask(chalk.bold("> Voc\xEA: "));
|
|
4030
|
+
if (!input) {
|
|
4031
|
+
if (_rlDone) break;
|
|
4032
|
+
continue;
|
|
4033
|
+
}
|
|
4034
|
+
if (input === "/sair" || input === "/quit") {
|
|
4035
|
+
console.log(chalk.dim("\nEncerrando chat."));
|
|
4036
|
+
break;
|
|
4037
|
+
}
|
|
4038
|
+
if (input === "/limpar") {
|
|
4039
|
+
session.clear();
|
|
4040
|
+
clearSession(cwd);
|
|
4041
|
+
clearCache(cwd);
|
|
4042
|
+
console.log(chalk.dim("Sess\xE3o e cache limpos.\n"));
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
4045
|
+
console.log("");
|
|
4046
|
+
const freshContext = await readProjectContextRAG(cwd, input);
|
|
4047
|
+
const isFirstMessage = session.getMessages().length === 0;
|
|
4048
|
+
const hasStacks = freshContext.includes("Stacks existentes");
|
|
4049
|
+
const userMessageContent = isFirstMessage && hasStacks ? `${input}
|
|
4050
|
+
|
|
4051
|
+
[Contexto do projeto]
|
|
4052
|
+
${freshContext}` : input;
|
|
4053
|
+
session.addUserMessage(userMessageContent);
|
|
4054
|
+
const provider = createContextualProvider(aiProvider, freshContext);
|
|
4055
|
+
const responded = await runGeneration(provider, session, input, freshContext);
|
|
4056
|
+
if (responded) {
|
|
4057
|
+
saveSession(cwd, session.getMessages());
|
|
4058
|
+
} else {
|
|
4059
|
+
session.removeLast();
|
|
4060
|
+
}
|
|
4061
|
+
console.log("");
|
|
4062
|
+
}
|
|
4063
|
+
rl.close();
|
|
4064
|
+
}
|
|
4065
|
+
main().catch((err) => {
|
|
4066
|
+
console.error(chalk.red("Erro: " + err.message));
|
|
4067
|
+
process.exit(1);
|
|
4068
|
+
});
|