daystrom 0.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/README.md +98 -0
- package/dist/airs/management.d.ts +21 -0
- package/dist/airs/management.d.ts.map +1 -0
- package/dist/airs/management.js +78 -0
- package/dist/airs/management.js.map +1 -0
- package/dist/airs/scanner.d.ts +8 -0
- package/dist/airs/scanner.d.ts.map +1 -0
- package/dist/airs/scanner.js +28 -0
- package/dist/airs/scanner.js.map +1 -0
- package/dist/airs/types.d.ts +26 -0
- package/dist/airs/types.d.ts.map +1 -0
- package/dist/airs/types.js +6 -0
- package/dist/airs/types.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +3 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +127 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +22 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/report.d.ts +3 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +57 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +3 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +95 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +6 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +72 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/cli/renderer.d.ts +15 -0
- package/dist/cli/renderer.d.ts.map +1 -0
- package/dist/cli/renderer.js +103 -0
- package/dist/cli/renderer.js.map +1 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +63 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +72 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +42 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/constraints.d.ts +16 -0
- package/dist/core/constraints.d.ts.map +1 -0
- package/dist/core/constraints.js +77 -0
- package/dist/core/constraints.js.map +1 -0
- package/dist/core/loop.d.ts +23 -0
- package/dist/core/loop.d.ts.map +1 -0
- package/dist/core/loop.js +146 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/core/metrics.d.ts +3 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +38 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/prompts/analyze-results.d.ts +3 -0
- package/dist/llm/prompts/analyze-results.d.ts.map +1 -0
- package/dist/llm/prompts/analyze-results.js +38 -0
- package/dist/llm/prompts/analyze-results.js.map +1 -0
- package/dist/llm/prompts/generate-tests.d.ts +3 -0
- package/dist/llm/prompts/generate-tests.d.ts.map +1 -0
- package/dist/llm/prompts/generate-tests.js +33 -0
- package/dist/llm/prompts/generate-tests.js.map +1 -0
- package/dist/llm/prompts/generate-topic.d.ts +4 -0
- package/dist/llm/prompts/generate-topic.d.ts.map +1 -0
- package/dist/llm/prompts/generate-topic.js +32 -0
- package/dist/llm/prompts/generate-topic.js.map +1 -0
- package/dist/llm/prompts/improve-topic.d.ts +3 -0
- package/dist/llm/prompts/improve-topic.d.ts.map +1 -0
- package/dist/llm/prompts/improve-topic.js +50 -0
- package/dist/llm/prompts/improve-topic.js.map +1 -0
- package/dist/llm/provider.d.ts +15 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +71 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/llm/schemas.d.ts +97 -0
- package/dist/llm/schemas.d.ts.map +1 -0
- package/dist/llm/schemas.js +22 -0
- package/dist/llm/schemas.js.map +1 -0
- package/dist/llm/service.d.ts +19 -0
- package/dist/llm/service.d.ts.map +1 -0
- package/dist/llm/service.js +177 -0
- package/dist/llm/service.js.map +1 -0
- package/dist/memory/diff.d.ts +4 -0
- package/dist/memory/diff.d.ts.map +1 -0
- package/dist/memory/diff.js +24 -0
- package/dist/memory/diff.js.map +1 -0
- package/dist/memory/extractor.d.ts +15 -0
- package/dist/memory/extractor.d.ts.map +1 -0
- package/dist/memory/extractor.js +129 -0
- package/dist/memory/extractor.js.map +1 -0
- package/dist/memory/injector.d.ts +8 -0
- package/dist/memory/injector.d.ts.map +1 -0
- package/dist/memory/injector.js +76 -0
- package/dist/memory/injector.js.map +1 -0
- package/dist/memory/prompts/extract-learnings.d.ts +3 -0
- package/dist/memory/prompts/extract-learnings.d.ts.map +1 -0
- package/dist/memory/prompts/extract-learnings.js +34 -0
- package/dist/memory/prompts/extract-learnings.js.map +1 -0
- package/dist/memory/schemas.d.ts +63 -0
- package/dist/memory/schemas.d.ts.map +1 -0
- package/dist/memory/schemas.js +13 -0
- package/dist/memory/schemas.js.map +1 -0
- package/dist/memory/store.d.ts +13 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +88 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.d.ts +55 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/persistence/store.d.ts +12 -0
- package/dist/persistence/store.d.ts.map +1 -0
- package/dist/persistence/store.js +68 -0
- package/dist/persistence/store.js.map +1 -0
- package/dist/persistence/types.d.ts +17 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +2 -0
- package/dist/persistence/types.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Daystrom
|
|
2
|
+
|
|
3
|
+
**Automated Prisma AIRS custom topic guardrail generator with iterative, self-improving refinement.**
|
|
4
|
+
|
|
5
|
+
Named after Dr. Richard Daystrom's self-learning M-5 multitronic unit from Star Trek TOS — a system designed to improve itself through experience. Daystrom generates, tests, evaluates, and refines Palo Alto Prisma AIRS custom topic guardrails in an autonomous loop, learning from each run to produce better results over time.
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
Daystrom automates the creation and optimization of [Prisma AIRS](https://docs.paloaltonetworks.com/ai-runtime-security) custom topic guardrails — content detection rules that tell the AIRS scanner what prompts to block or allow. Instead of manually crafting topic definitions and testing them by hand, Daystrom:
|
|
10
|
+
|
|
11
|
+
1. **Generates** a custom topic definition (name, description, up to 5 examples) using an LLM, informed by any prior learnings from previous runs
|
|
12
|
+
2. **Deploys** the topic to a live Prisma AIRS security profile via the Management API (OAuth2)
|
|
13
|
+
3. **Generates test cases** — balanced positive prompts (should trigger detection) and negative prompts (should not trigger)
|
|
14
|
+
4. **Scans** all test prompts against the live AIRS Scan API with configurable concurrency
|
|
15
|
+
5. **Evaluates** efficacy: true positive rate, true negative rate, accuracy, coverage (`min(TPR, TNR)`), and F1 score
|
|
16
|
+
6. **Analyzes** false positives and false negatives using the LLM to identify patterns
|
|
17
|
+
7. **Improves** the topic definition iteratively — refining description and examples while keeping the topic name locked
|
|
18
|
+
8. **Learns** — after the loop completes, extracts actionable insights and persists them for future runs on similar topics
|
|
19
|
+
|
|
20
|
+
The loop runs until coverage reaches a target threshold (default 90%) or max iterations (default 20) are exhausted.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install
|
|
26
|
+
pnpm install
|
|
27
|
+
|
|
28
|
+
# Configure credentials
|
|
29
|
+
cp .env.example .env
|
|
30
|
+
# Edit .env — you need: ANTHROPIC_API_KEY (or other LLM provider),
|
|
31
|
+
# PANW_AI_SEC_API_KEY, PANW_MGMT_CLIENT_ID, PANW_MGMT_CLIENT_SECRET, PANW_MGMT_TSG_ID
|
|
32
|
+
|
|
33
|
+
# Run interactively (prompts for topic, profile, intent, etc.)
|
|
34
|
+
pnpm run generate
|
|
35
|
+
|
|
36
|
+
# Run non-interactively
|
|
37
|
+
pnpm run generate \
|
|
38
|
+
--provider claude-api \
|
|
39
|
+
--profile my-security-profile \
|
|
40
|
+
--topic "Block discussions about building explosives" \
|
|
41
|
+
--intent block \
|
|
42
|
+
--target-coverage 90
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
All commands use `daystrom` as the binary name (or `pnpm run dev` in development):
|
|
48
|
+
|
|
49
|
+
| Command | Description |
|
|
50
|
+
|---------|-------------|
|
|
51
|
+
| `daystrom generate` | Start a new guardrail generation loop |
|
|
52
|
+
| `daystrom resume <runId>` | Resume a paused or failed run with additional iterations |
|
|
53
|
+
| `daystrom report <runId>` | View detailed results for a saved run (best or specific iteration) |
|
|
54
|
+
| `daystrom list` | List all saved runs with status and coverage |
|
|
55
|
+
|
|
56
|
+
### Generate Flags
|
|
57
|
+
|
|
58
|
+
| Flag | Default | Description |
|
|
59
|
+
|------|---------|-------------|
|
|
60
|
+
| `--provider <name>` | `claude-api` | LLM provider (`claude-api`, `claude-vertex`, `claude-bedrock`, `gemini-api`, `gemini-vertex`, `gemini-bedrock`) |
|
|
61
|
+
| `--model <name>` | per-provider | Override the default model |
|
|
62
|
+
| `--profile <name>` | (prompted) | AIRS security profile name to attach the topic to |
|
|
63
|
+
| `--topic <desc>` | (prompted) | Natural language description of what to detect |
|
|
64
|
+
| `--intent <block\|allow>` | `block` | Whether matching prompts should be blocked or allowed |
|
|
65
|
+
| `--max-iterations <n>` | `20` | Maximum refinement iterations |
|
|
66
|
+
| `--target-coverage <n>` | `90` | Coverage percentage to stop at |
|
|
67
|
+
| `--no-memory` | memory on | Disable cross-run learning for this run |
|
|
68
|
+
|
|
69
|
+
## Documentation
|
|
70
|
+
|
|
71
|
+
Full documentation: **[cdot65.github.io/daystrom](https://cdot65.github.io/daystrom/)**
|
|
72
|
+
|
|
73
|
+
## Tech Stack
|
|
74
|
+
|
|
75
|
+
- **TypeScript ESM** on Node.js 20+ with strict mode
|
|
76
|
+
- **LangChain.js** — Claude (Anthropic API, Vertex, Bedrock) and Gemini (API, Vertex, Bedrock) with structured output via Zod schemas
|
|
77
|
+
- **Prisma AIRS SDK** (`@cdot65/prisma-airs-sdk@^0.2.0`) — scan API + management API (OAuth2 client credentials)
|
|
78
|
+
- **Commander.js** — CLI framework with 4 subcommands
|
|
79
|
+
- **Vitest** + **MSW** — 165 tests across 17 files (~98% stmt coverage)
|
|
80
|
+
- **Biome** — linting and formatting
|
|
81
|
+
- **Zod** — config validation, LLM output parsing, learning extraction schemas
|
|
82
|
+
|
|
83
|
+
## Project Structure
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
src/
|
|
87
|
+
├── cli/ CLI entry, commands (generate/resume/report/list), prompts, renderer
|
|
88
|
+
├── config/ Zod-validated config schema + env/file/CLI cascade loader
|
|
89
|
+
├── core/ Async generator loop, efficacy metrics, AIRS topic constraints
|
|
90
|
+
├── llm/ LangChain provider factory, structured output service, prompt templates
|
|
91
|
+
├── airs/ Scanner (sync scan + batch) and Management (CRUD + profile linking) services
|
|
92
|
+
├── memory/ Learning store, extractor, budget-aware injector, iteration diff
|
|
93
|
+
└── persistence/ JSON file store for run state
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CreateCustomTopicRequest, type ManagementClientOptions, type CustomTopic as SdkCustomTopic } from '@cdot65/prisma-airs-sdk';
|
|
2
|
+
import type { ManagementService } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the SDK's ManagementClient to implement our ManagementService interface.
|
|
5
|
+
* OAuth2 token management, caching, and retry are handled by the SDK.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SdkManagementService implements ManagementService {
|
|
8
|
+
private client;
|
|
9
|
+
constructor(opts?: ManagementClientOptions);
|
|
10
|
+
createTopic(request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
|
|
11
|
+
updateTopic(topicId: string, request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
|
|
12
|
+
deleteTopic(topicId: string): Promise<void>;
|
|
13
|
+
listTopics(): Promise<SdkCustomTopic[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Sets a single custom topic on a profile's topic-guardrails config.
|
|
16
|
+
* Replaces any existing topics so only the current topic is evaluated.
|
|
17
|
+
* Previous runs may have left stale topics — this clears them.
|
|
18
|
+
*/
|
|
19
|
+
assignTopicToProfile(profileName: string, topicId: string, topicName: string, action: 'allow' | 'block'): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=management.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/airs/management.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAE7B,KAAK,uBAAuB,EAC5B,KAAK,WAAW,IAAI,cAAc,EACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IAC5D,OAAO,CAAC,MAAM,CAAmB;gBAErB,IAAI,CAAC,EAAE,uBAAuB;IAIpC,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIvE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK7C;;;;OAIG;IACG,oBAAoB,CACxB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC;CAsDjB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ManagementClient, } from '@cdot65/prisma-airs-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps the SDK's ManagementClient to implement our ManagementService interface.
|
|
4
|
+
* OAuth2 token management, caching, and retry are handled by the SDK.
|
|
5
|
+
*/
|
|
6
|
+
export class SdkManagementService {
|
|
7
|
+
client;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
this.client = new ManagementClient(opts);
|
|
10
|
+
}
|
|
11
|
+
async createTopic(request) {
|
|
12
|
+
return this.client.topics.create(request);
|
|
13
|
+
}
|
|
14
|
+
async updateTopic(topicId, request) {
|
|
15
|
+
return this.client.topics.update(topicId, request);
|
|
16
|
+
}
|
|
17
|
+
async deleteTopic(topicId) {
|
|
18
|
+
await this.client.topics.delete(topicId);
|
|
19
|
+
}
|
|
20
|
+
async listTopics() {
|
|
21
|
+
const response = await this.client.topics.list();
|
|
22
|
+
return response.custom_topics;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sets a single custom topic on a profile's topic-guardrails config.
|
|
26
|
+
* Replaces any existing topics so only the current topic is evaluated.
|
|
27
|
+
* Previous runs may have left stale topics — this clears them.
|
|
28
|
+
*/
|
|
29
|
+
async assignTopicToProfile(profileName, topicId, topicName, action) {
|
|
30
|
+
// Find profile by name
|
|
31
|
+
const { ai_profiles } = await this.client.profiles.list();
|
|
32
|
+
const profile = ai_profiles.find((p) => p.profile_name === profileName);
|
|
33
|
+
if (!profile?.profile_id) {
|
|
34
|
+
throw new Error(`Profile "${profileName}" not found`);
|
|
35
|
+
}
|
|
36
|
+
// Deep clone the policy to mutate
|
|
37
|
+
const policy = JSON.parse(JSON.stringify(profile.policy ?? {}));
|
|
38
|
+
const aiProfiles = policy['ai-security-profiles'] ?? [
|
|
39
|
+
{ 'model-type': 'default', 'model-configuration': {} },
|
|
40
|
+
];
|
|
41
|
+
const modelConfig = aiProfiles[0]?.['model-configuration'] ?? {};
|
|
42
|
+
// Find or create model-protection with topic-guardrails
|
|
43
|
+
const modelProtection = modelConfig['model-protection'] ?? [];
|
|
44
|
+
let topicGuardrails = modelProtection.find((mp) => mp.name === 'topic-guardrails');
|
|
45
|
+
if (!topicGuardrails) {
|
|
46
|
+
topicGuardrails = {
|
|
47
|
+
action: 'allow',
|
|
48
|
+
name: 'topic-guardrails',
|
|
49
|
+
options: [],
|
|
50
|
+
'topic-list': [],
|
|
51
|
+
};
|
|
52
|
+
modelProtection.push(topicGuardrails);
|
|
53
|
+
}
|
|
54
|
+
// Replace the entire topic-list with only the current topic under the given action.
|
|
55
|
+
// The opposite action gets an empty list to ensure no stale topics remain.
|
|
56
|
+
const oppositeAction = action === 'block' ? 'allow' : 'block';
|
|
57
|
+
topicGuardrails['topic-list'] = [
|
|
58
|
+
{
|
|
59
|
+
action,
|
|
60
|
+
topic: [{ topic_id: topicId, topic_name: topicName, revision: 1 }],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
action: oppositeAction,
|
|
64
|
+
topic: [],
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
// Write back
|
|
68
|
+
modelConfig['model-protection'] = modelProtection;
|
|
69
|
+
aiProfiles[0]['model-configuration'] = modelConfig;
|
|
70
|
+
policy['ai-security-profiles'] = aiProfiles;
|
|
71
|
+
await this.client.profiles.update(profile.profile_id, {
|
|
72
|
+
profile_name: profile.profile_name,
|
|
73
|
+
active: profile.active,
|
|
74
|
+
policy,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=management.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"management.js","sourceRoot":"","sources":["../../src/airs/management.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ScanResult, ScanService } from './types.js';
|
|
2
|
+
export declare class AirsScanService implements ScanService {
|
|
3
|
+
private scanner;
|
|
4
|
+
constructor(apiKey: string);
|
|
5
|
+
scan(profileName: string, prompt: string, sessionId?: string): Promise<ScanResult>;
|
|
6
|
+
scanBatch(profileName: string, prompts: string[], concurrency?: number, sessionId?: string): Promise<ScanResult[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/airs/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,OAAO,CAA+B;gBAElC,MAAM,EAAE,MAAM;IAKpB,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBlF,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,SAAI,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC;CAMzB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Content, init, Scanner } from '@cdot65/prisma-airs-sdk';
|
|
2
|
+
import pLimit from 'p-limit';
|
|
3
|
+
export class AirsScanService {
|
|
4
|
+
scanner;
|
|
5
|
+
constructor(apiKey) {
|
|
6
|
+
init({ apiKey });
|
|
7
|
+
this.scanner = new Scanner();
|
|
8
|
+
}
|
|
9
|
+
async scan(profileName, prompt, sessionId) {
|
|
10
|
+
const content = new Content({ prompt });
|
|
11
|
+
const response = await this.scanner.syncScan({ profile_name: profileName }, content, sessionId ? { sessionId } : undefined);
|
|
12
|
+
const action = response.action === 'block' ? 'block' : 'allow';
|
|
13
|
+
const detected = response.prompt_detected;
|
|
14
|
+
const triggered = !!(detected?.topic_guardrails_details || detected?.topic_violation);
|
|
15
|
+
return {
|
|
16
|
+
scanId: response.scan_id ?? '',
|
|
17
|
+
reportId: response.report_id ?? '',
|
|
18
|
+
action: action,
|
|
19
|
+
triggered,
|
|
20
|
+
raw: response,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async scanBatch(profileName, prompts, concurrency = 5, sessionId) {
|
|
24
|
+
const limit = pLimit(concurrency);
|
|
25
|
+
return Promise.all(prompts.map((prompt) => limit(() => this.scan(profileName, prompt, sessionId))));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/airs/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,MAAM,MAAM,SAAS,CAAC;AAG7B,MAAM,OAAO,eAAe;IAClB,OAAO,CAA+B;IAE9C,YAAY,MAAc;QACxB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAmB,EAAE,MAAc,EAAE,SAAkB;QAChE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAC1C,EAAE,YAAY,EAAE,WAAW,EAAE,EAC7B,OAAO,EACP,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CACtC,CAAC;QAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,eAAsD,CAAC;QACjF,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,wBAAwB,IAAI,QAAQ,EAAE,eAAe,CAAC,CAAC;QAEtF,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,OAAO,IAAI,EAAE;YAC9B,QAAQ,EAAE,QAAQ,CAAC,SAAS,IAAI,EAAE;YAClC,MAAM,EAAE,MAA2B;YACnC,SAAS;YACT,GAAG,EAAE,QAAQ;SACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CACb,WAAmB,EACnB,OAAiB,EACjB,WAAW,GAAG,CAAC,EACf,SAAkB;QAElB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAClC,OAAO,OAAO,CAAC,GAAG,CAChB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAChF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIRS integration types — scan results and service interfaces for the
|
|
3
|
+
* Prisma AIRS scanner and topic management APIs.
|
|
4
|
+
*/
|
|
5
|
+
import type { CreateCustomTopicRequest, CustomTopic as SdkCustomTopic } from '@cdot65/prisma-airs-sdk';
|
|
6
|
+
export type { CreateCustomTopicRequest, SdkCustomTopic };
|
|
7
|
+
export interface ScanResult {
|
|
8
|
+
scanId: string;
|
|
9
|
+
reportId: string;
|
|
10
|
+
action: 'allow' | 'block';
|
|
11
|
+
triggered: boolean;
|
|
12
|
+
category?: string;
|
|
13
|
+
raw?: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface ScanService {
|
|
16
|
+
scan(profileName: string, prompt: string, sessionId?: string): Promise<ScanResult>;
|
|
17
|
+
scanBatch(profileName: string, prompts: string[], concurrency?: number, sessionId?: string): Promise<ScanResult[]>;
|
|
18
|
+
}
|
|
19
|
+
export interface ManagementService {
|
|
20
|
+
createTopic(request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
|
|
21
|
+
updateTopic(topicId: string, request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
|
|
22
|
+
deleteTopic(topicId: string): Promise<void>;
|
|
23
|
+
listTopics(): Promise<SdkCustomTopic[]>;
|
|
24
|
+
assignTopicToProfile(profileName: string, topicId: string, topicName: string, action: 'allow' | 'block'): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/airs/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,WAAW,IAAI,cAAc,EAC9B,MAAM,yBAAyB,CAAC;AAKjC,YAAY,EAAE,wBAAwB,EAAE,cAAc,EAAE,CAAC;AAKzD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAKD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnF,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACzF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACxC,oBAAoB,CAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/airs/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuH9D"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { SdkManagementService } from '../../airs/management.js';
|
|
2
|
+
import { AirsScanService } from '../../airs/scanner.js';
|
|
3
|
+
import { loadConfig } from '../../config/loader.js';
|
|
4
|
+
import { runLoop } from '../../core/loop.js';
|
|
5
|
+
import { createLlmProvider } from '../../llm/provider.js';
|
|
6
|
+
import { LangChainLlmService } from '../../llm/service.js';
|
|
7
|
+
import { LearningExtractor } from '../../memory/extractor.js';
|
|
8
|
+
import { MemoryInjector } from '../../memory/injector.js';
|
|
9
|
+
import { MemoryStore } from '../../memory/store.js';
|
|
10
|
+
import { JsonFileStore } from '../../persistence/store.js';
|
|
11
|
+
import { collectUserInput } from '../prompts.js';
|
|
12
|
+
import { renderAnalysis, renderError, renderHeader, renderIterationStart, renderIterationSummary, renderLoopComplete, renderMemoryExtracted, renderMemoryLoaded, renderMetrics, renderTestProgress, renderTopic, } from '../renderer.js';
|
|
13
|
+
export function registerGenerateCommand(program) {
|
|
14
|
+
program
|
|
15
|
+
.command('generate')
|
|
16
|
+
.description('Start a new guardrail generation loop')
|
|
17
|
+
.option('--provider <provider>', 'LLM provider')
|
|
18
|
+
.option('--model <model>', 'LLM model name')
|
|
19
|
+
.option('--profile <name>', 'AIRS security profile name')
|
|
20
|
+
.option('--topic <description>', 'Topic description')
|
|
21
|
+
.option('--intent <intent>', 'Intent: block or allow')
|
|
22
|
+
.option('--max-iterations <n>', 'Max iterations', '20')
|
|
23
|
+
.option('--target-coverage <n>', 'Target coverage %', '90')
|
|
24
|
+
.option('--memory', 'Enable learning memory (default)')
|
|
25
|
+
.option('--no-memory', 'Disable learning memory')
|
|
26
|
+
.action(async (opts) => {
|
|
27
|
+
try {
|
|
28
|
+
renderHeader();
|
|
29
|
+
const config = await loadConfig({
|
|
30
|
+
llmProvider: opts.provider,
|
|
31
|
+
llmModel: opts.model,
|
|
32
|
+
memoryEnabled: opts.memory !== undefined ? String(opts.memory) : undefined,
|
|
33
|
+
});
|
|
34
|
+
// Collect user input (interactive or from CLI flags)
|
|
35
|
+
let userInput;
|
|
36
|
+
if (opts.topic && opts.profile) {
|
|
37
|
+
userInput = {
|
|
38
|
+
topicDescription: opts.topic,
|
|
39
|
+
intent: (opts.intent ?? 'block'),
|
|
40
|
+
profileName: opts.profile,
|
|
41
|
+
maxIterations: Number.parseInt(opts.maxIterations, 10),
|
|
42
|
+
targetCoverage: Number.parseInt(opts.targetCoverage, 10) / 100,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
userInput = await collectUserInput();
|
|
47
|
+
}
|
|
48
|
+
// Initialize services
|
|
49
|
+
const model = await createLlmProvider({
|
|
50
|
+
provider: config.llmProvider,
|
|
51
|
+
model: config.llmModel,
|
|
52
|
+
anthropicApiKey: config.anthropicApiKey,
|
|
53
|
+
googleApiKey: config.googleApiKey,
|
|
54
|
+
googleCloudProject: config.googleCloudProject,
|
|
55
|
+
googleCloudLocation: config.googleCloudLocation,
|
|
56
|
+
awsRegion: config.awsRegion,
|
|
57
|
+
awsAccessKeyId: config.awsAccessKeyId,
|
|
58
|
+
awsSecretAccessKey: config.awsSecretAccessKey,
|
|
59
|
+
});
|
|
60
|
+
// Set up memory system
|
|
61
|
+
const memoryEnabled = config.memoryEnabled;
|
|
62
|
+
const memoryStore = memoryEnabled ? new MemoryStore(config.memoryDir) : undefined;
|
|
63
|
+
const memoryInjector = memoryStore
|
|
64
|
+
? new MemoryInjector(memoryStore, config.maxMemoryChars)
|
|
65
|
+
: undefined;
|
|
66
|
+
const llm = new LangChainLlmService(model, memoryInjector);
|
|
67
|
+
if (!config.airsApiKey)
|
|
68
|
+
throw new Error('PANW_AI_SEC_API_KEY is required');
|
|
69
|
+
const scanner = new AirsScanService(config.airsApiKey);
|
|
70
|
+
const management = new SdkManagementService({
|
|
71
|
+
clientId: config.mgmtClientId,
|
|
72
|
+
clientSecret: config.mgmtClientSecret,
|
|
73
|
+
tsgId: config.mgmtTsgId,
|
|
74
|
+
apiEndpoint: config.mgmtEndpoint,
|
|
75
|
+
tokenEndpoint: config.mgmtTokenEndpoint,
|
|
76
|
+
});
|
|
77
|
+
const store = new JsonFileStore(config.dataDir);
|
|
78
|
+
// Load memory before loop
|
|
79
|
+
if (memoryEnabled) {
|
|
80
|
+
const learningCount = await llm.loadMemory(userInput.topicDescription);
|
|
81
|
+
renderMemoryLoaded(learningCount);
|
|
82
|
+
}
|
|
83
|
+
const memoryExtractor = memoryStore ? new LearningExtractor(model, memoryStore) : undefined;
|
|
84
|
+
// Run the loop
|
|
85
|
+
for await (const event of runLoop(userInput, {
|
|
86
|
+
llm,
|
|
87
|
+
management,
|
|
88
|
+
scanner,
|
|
89
|
+
propagationDelayMs: config.propagationDelayMs,
|
|
90
|
+
memory: memoryExtractor ? { extractor: memoryExtractor } : undefined,
|
|
91
|
+
})) {
|
|
92
|
+
switch (event.type) {
|
|
93
|
+
case 'iteration:start':
|
|
94
|
+
renderIterationStart(event.iteration);
|
|
95
|
+
break;
|
|
96
|
+
case 'generate:complete':
|
|
97
|
+
renderTopic(event.topic);
|
|
98
|
+
break;
|
|
99
|
+
case 'test:progress':
|
|
100
|
+
renderTestProgress(event.completed, event.total);
|
|
101
|
+
break;
|
|
102
|
+
case 'evaluate:complete':
|
|
103
|
+
renderMetrics(event.metrics);
|
|
104
|
+
break;
|
|
105
|
+
case 'analyze:complete':
|
|
106
|
+
renderAnalysis(event.analysis);
|
|
107
|
+
break;
|
|
108
|
+
case 'iteration:complete':
|
|
109
|
+
renderIterationSummary(event.result);
|
|
110
|
+
break;
|
|
111
|
+
case 'memory:extracted':
|
|
112
|
+
renderMemoryExtracted(event.learningCount);
|
|
113
|
+
break;
|
|
114
|
+
case 'loop:complete':
|
|
115
|
+
await store.save(event.runState);
|
|
116
|
+
renderLoopComplete(event.runState);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
renderError(err instanceof Error ? err.message : String(err));
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB1D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { loadConfig } from '../../config/loader.js';
|
|
2
|
+
import { JsonFileStore } from '../../persistence/store.js';
|
|
3
|
+
import { renderError, renderHeader, renderRunList } from '../renderer.js';
|
|
4
|
+
export function registerListCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('list')
|
|
7
|
+
.description('List all saved runs')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
try {
|
|
10
|
+
renderHeader();
|
|
11
|
+
const config = await loadConfig();
|
|
12
|
+
const store = new JsonFileStore(config.dataDir);
|
|
13
|
+
const runs = await store.list();
|
|
14
|
+
renderRunList(runs);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
renderError(err instanceof Error ? err.message : String(err));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE1E,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,qBAAqB,CAAC;SAClC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,YAAY,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YAChC,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsD5D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadConfig } from '../../config/loader.js';
|
|
3
|
+
import { JsonFileStore } from '../../persistence/store.js';
|
|
4
|
+
import { renderAnalysis, renderError, renderHeader, renderMetrics, renderTopic, } from '../renderer.js';
|
|
5
|
+
export function registerReportCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('report <runId>')
|
|
8
|
+
.description('View detailed report for a run')
|
|
9
|
+
.option('--iteration <n>', 'Show specific iteration')
|
|
10
|
+
.action(async (runId, opts) => {
|
|
11
|
+
try {
|
|
12
|
+
renderHeader();
|
|
13
|
+
const config = await loadConfig();
|
|
14
|
+
const store = new JsonFileStore(config.dataDir);
|
|
15
|
+
const run = await store.load(runId);
|
|
16
|
+
if (!run) {
|
|
17
|
+
renderError(`Run ${runId} not found`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
console.log(chalk.bold(`\n Run: ${run.id}`));
|
|
21
|
+
console.log(` Status: ${run.status}`);
|
|
22
|
+
console.log(` Created: ${run.createdAt}`);
|
|
23
|
+
console.log(` Topic: ${run.userInput.topicDescription}`);
|
|
24
|
+
console.log(` Intent: ${run.userInput.intent}`);
|
|
25
|
+
console.log(` Best coverage: ${(run.bestCoverage * 100).toFixed(1)}% (iteration ${run.bestIteration})`);
|
|
26
|
+
console.log(` Total iterations: ${run.iterations.length}`);
|
|
27
|
+
if (opts.iteration) {
|
|
28
|
+
const idx = Number.parseInt(opts.iteration, 10) - 1;
|
|
29
|
+
const iter = run.iterations[idx];
|
|
30
|
+
if (!iter) {
|
|
31
|
+
renderError(`Iteration ${opts.iteration} not found`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
console.log(chalk.bold(`\n Iteration ${iter.iteration}:`));
|
|
35
|
+
renderTopic(iter.topic);
|
|
36
|
+
renderMetrics(iter.metrics);
|
|
37
|
+
renderAnalysis(iter.analysis);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Show best iteration
|
|
41
|
+
const best = run.iterations[run.bestIteration - 1];
|
|
42
|
+
if (best) {
|
|
43
|
+
console.log(chalk.bold(`\n Best Iteration (${best.iteration}):`));
|
|
44
|
+
renderTopic(best.topic);
|
|
45
|
+
renderMetrics(best.metrics);
|
|
46
|
+
renderAnalysis(best.analysis);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
renderError(err instanceof Error ? err.message : String(err));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2F5D"}
|