openshell-ai 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +354 -0
  3. package/dist/analytics/client.d.ts +2 -0
  4. package/dist/analytics/client.js +95 -0
  5. package/dist/analytics/client.js.map +1 -0
  6. package/dist/analytics/session.d.ts +15 -0
  7. package/dist/analytics/session.js +70 -0
  8. package/dist/analytics/session.js.map +1 -0
  9. package/dist/cli/commands.d.ts +14 -0
  10. package/dist/cli/commands.js +265 -0
  11. package/dist/cli/commands.js.map +1 -0
  12. package/dist/cli/index.d.ts +2 -0
  13. package/dist/cli/index.js +4 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/prompts.d.ts +2 -0
  16. package/dist/cli/prompts.js +32 -0
  17. package/dist/cli/prompts.js.map +1 -0
  18. package/dist/cli/repl.d.ts +12 -0
  19. package/dist/cli/repl.js +184 -0
  20. package/dist/cli/repl.js.map +1 -0
  21. package/dist/config/configurator.d.ts +18 -0
  22. package/dist/config/configurator.js +133 -0
  23. package/dist/config/configurator.js.map +1 -0
  24. package/dist/config/env.d.ts +2 -0
  25. package/dist/config/env.js +8 -0
  26. package/dist/config/env.js.map +1 -0
  27. package/dist/config/providerCatalog.d.ts +8 -0
  28. package/dist/config/providerCatalog.js +33 -0
  29. package/dist/config/providerCatalog.js.map +1 -0
  30. package/dist/config/userConfig.d.ts +12 -0
  31. package/dist/config/userConfig.js +141 -0
  32. package/dist/config/userConfig.js.map +1 -0
  33. package/dist/core/explainCommand.d.ts +2 -0
  34. package/dist/core/explainCommand.js +4 -0
  35. package/dist/core/explainCommand.js.map +1 -0
  36. package/dist/core/generateCommand.d.ts +2 -0
  37. package/dist/core/generateCommand.js +34 -0
  38. package/dist/core/generateCommand.js.map +1 -0
  39. package/dist/core/output.d.ts +4 -0
  40. package/dist/core/output.js +67 -0
  41. package/dist/core/output.js.map +1 -0
  42. package/dist/core/prompts.d.ts +8 -0
  43. package/dist/core/prompts.js +75 -0
  44. package/dist/core/prompts.js.map +1 -0
  45. package/dist/core/response.d.ts +19 -0
  46. package/dist/core/response.js +44 -0
  47. package/dist/core/response.js.map +1 -0
  48. package/dist/core/session.d.ts +9 -0
  49. package/dist/core/session.js +23 -0
  50. package/dist/core/session.js.map +1 -0
  51. package/dist/exec/runCommand.d.ts +11 -0
  52. package/dist/exec/runCommand.js +57 -0
  53. package/dist/exec/runCommand.js.map +1 -0
  54. package/dist/index.d.ts +5 -0
  55. package/dist/index.js +6 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/platform/detectPlatform.d.ts +9 -0
  58. package/dist/platform/detectPlatform.js +49 -0
  59. package/dist/platform/detectPlatform.js.map +1 -0
  60. package/dist/platform/detectServiceManager.d.ts +3 -0
  61. package/dist/platform/detectServiceManager.js +38 -0
  62. package/dist/platform/detectServiceManager.js.map +1 -0
  63. package/dist/platform/detectShell.d.ts +3 -0
  64. package/dist/platform/detectShell.js +21 -0
  65. package/dist/platform/detectShell.js.map +1 -0
  66. package/dist/providers/anthropic.d.ts +8 -0
  67. package/dist/providers/anthropic.js +67 -0
  68. package/dist/providers/anthropic.js.map +1 -0
  69. package/dist/providers/factory.d.ts +2 -0
  70. package/dist/providers/factory.js +24 -0
  71. package/dist/providers/factory.js.map +1 -0
  72. package/dist/providers/google.d.ts +8 -0
  73. package/dist/providers/google.js +76 -0
  74. package/dist/providers/google.js.map +1 -0
  75. package/dist/providers/ollama.d.ts +8 -0
  76. package/dist/providers/ollama.js +67 -0
  77. package/dist/providers/ollama.js.map +1 -0
  78. package/dist/providers/openai.d.ts +8 -0
  79. package/dist/providers/openai.js +77 -0
  80. package/dist/providers/openai.js.map +1 -0
  81. package/dist/providers/types.d.ts +1 -0
  82. package/dist/providers/types.js +2 -0
  83. package/dist/providers/types.js.map +1 -0
  84. package/dist/safety/classifyRisk.d.ts +7 -0
  85. package/dist/safety/classifyRisk.js +115 -0
  86. package/dist/safety/classifyRisk.js.map +1 -0
  87. package/dist/safety/dangerousPatterns.d.ts +8 -0
  88. package/dist/safety/dangerousPatterns.js +70 -0
  89. package/dist/safety/dangerousPatterns.js.map +1 -0
  90. package/dist/safety/executionPolicy.d.ts +9 -0
  91. package/dist/safety/executionPolicy.js +21 -0
  92. package/dist/safety/executionPolicy.js.map +1 -0
  93. package/dist/types/index.d.ts +105 -0
  94. package/dist/types/index.js +2 -0
  95. package/dist/types/index.js.map +1 -0
  96. package/dist/utils/branding.d.ts +6 -0
  97. package/dist/utils/branding.js +16 -0
  98. package/dist/utils/branding.js.map +1 -0
  99. package/dist/utils/errors.d.ts +26 -0
  100. package/dist/utils/errors.js +59 -0
  101. package/dist/utils/errors.js.map +1 -0
  102. package/dist/utils/logger.d.ts +5 -0
  103. package/dist/utils/logger.js +14 -0
  104. package/dist/utils/logger.js.map +1 -0
  105. package/dist/utils/strings.d.ts +5 -0
  106. package/dist/utils/strings.js +30 -0
  107. package/dist/utils/strings.js.map +1 -0
  108. package/dist/workspace/inspectWorkspace.d.ts +1 -0
  109. package/dist/workspace/inspectWorkspace.js +174 -0
  110. package/dist/workspace/inspectWorkspace.js.map +1 -0
  111. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ottili ONE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # `ai-cmd`
2
+
3
+ ![GitHub stars](https://img.shields.io/github/stars/Ottili-ONE/ai-cmd)
4
+ ![License](https://img.shields.io/github/license/Ottili-ONE/ai-cmd)
5
+ ![npm version](https://img.shields.io/npm/v/ai-cmd)
6
+ ![Downloads](https://img.shields.io/npm/dm/ai-cmd)
7
+
8
+ `ai-cmd` is a focused CLI for turning natural-language terminal questions into a single shell command you can inspect, copy, explain, and optionally run with confirmation.
9
+
10
+ It is designed for the fast path:
11
+
12
+ ```bash
13
+ ai "how do I restart nginx"
14
+ ```
15
+
16
+ Instead of a chatty assistant or a full autonomous agent, `ai-cmd` aims to be the small utility you keep installed because it is quick, predictable, and useful.
17
+
18
+ ## Why it exists
19
+
20
+ Developers regularly know what they want to do in the terminal, but not the exact command for the current machine, shell, or init system.
21
+
22
+ `ai-cmd` narrows that gap by:
23
+
24
+ - generating one best command instead of a long essay
25
+ - adapting to Linux, macOS, WSL, and generic Unix-like environments
26
+ - explaining what the command does
27
+ - classifying command risk before execution
28
+ - keeping interactive follow-ups lightweight
29
+
30
+ ## Powered by Ottili ONE
31
+
32
+ This project is part of the **Ottili ONE ecosystem** — a modular AI system for automating development, business workflows, and operations.
33
+
34
+ ### Website: https://ottili.one
35
+
36
+ ---
37
+
38
+ ## Features
39
+
40
+ - One-shot command generation: `ai "<question>"`
41
+ - Interactive REPL mode: `ai`
42
+ - Session-aware follow-up questions in REPL mode
43
+ - Command explanations and platform notes
44
+ - Clipboard copy support
45
+ - Optional execution with confirmation
46
+ - Heuristic risk classification for destructive commands
47
+ - First-run configurator for provider, API key, and analytics consent
48
+ - Configurable provider abstraction for OpenAI, Anthropic, Google, Ollama, and vLLM
49
+ - Workspace-aware command generation based on the current folder
50
+ - Opt-in anonymous analytics with separate error reporting
51
+ - JSON mode for scripting
52
+
53
+ ## Installation
54
+
55
+ ### Global install from npm
56
+
57
+ ```bash
58
+ npm install -g ai-cmd
59
+ ```
60
+
61
+ ### Local development install
62
+
63
+ ```bash
64
+ npm install
65
+ npm run build
66
+ npm link
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ `ai-cmd` reads configuration from environment variables and optionally from `~/.ai-cmd/config.json`.
72
+
73
+ Environment variables take precedence over the config file.
74
+
75
+ If required configuration is missing and you launch `ai-cmd` interactively, a first-run configurator opens and asks for:
76
+
77
+ - AI provider
78
+ - API key
79
+ - analytics consent
80
+
81
+ If you opt in, `config.json` stores `"analytics": true` and a random anonymous install id. If you opt out, it stores `"analytics": false`.
82
+
83
+ ### OpenAI
84
+
85
+ OpenAI remains the default provider:
86
+
87
+ ```bash
88
+ export AI_API_KEY="your-api-key"
89
+ export AI_PROVIDER="openai"
90
+ export AI_MODEL="gpt-5.4-mini"
91
+ export AI_BASE_URL="https://api.openai.com/v1"
92
+ export AI_TIMEOUT_MS="30000"
93
+ ```
94
+
95
+ ### Anthropic
96
+
97
+ ```bash
98
+ export AI_PROVIDER="anthropic"
99
+ export AI_API_KEY="your-anthropic-key"
100
+ export AI_MODEL="claude-sonnet-4-20250514"
101
+ export AI_BASE_URL="https://api.anthropic.com/v1"
102
+ ```
103
+
104
+ ### Google
105
+
106
+ ```bash
107
+ export AI_PROVIDER="google"
108
+ export AI_API_KEY="your-google-ai-key"
109
+ export AI_MODEL="gemini-2.5-flash"
110
+ export AI_BASE_URL="https://generativelanguage.googleapis.com/v1beta"
111
+ ```
112
+
113
+ ### Ollama
114
+
115
+ Use a local Ollama model such as Gemma:
116
+
117
+ ```bash
118
+ export AI_PROVIDER="ollama"
119
+ export AI_MODEL="gemma3:4b"
120
+ export AI_BASE_URL="http://localhost:11434/api"
121
+ ```
122
+
123
+ No API key is required for the default local Ollama setup.
124
+
125
+ ### vLLM
126
+
127
+ Use a local or self-hosted OpenAI-compatible vLLM server:
128
+
129
+ ```bash
130
+ export AI_PROVIDER="vllm"
131
+ export AI_MODEL="google/gemma-3-4b-it"
132
+ export AI_BASE_URL="http://localhost:8000/v1"
133
+ ```
134
+
135
+ No API key is required for a local vLLM server unless you configured auth yourself.
136
+
137
+ ### Example config file
138
+
139
+ `~/.ai-cmd/config.json`
140
+
141
+ ```json
142
+ {
143
+ "provider": "openai",
144
+ "model": "gpt-5.4-mini",
145
+ "apiKey": "your-api-key",
146
+ "baseUrl": "https://api.openai.com/v1",
147
+ "timeoutMs": 30000,
148
+ "analytics": false
149
+ }
150
+ ```
151
+
152
+ ### Analytics
153
+
154
+ Analytics are opt-in only.
155
+
156
+ When enabled, `ai-cmd` sends anonymous usage events to:
157
+
158
+ ```text
159
+ https://tracking.ottili.one/api/aicmd
160
+ ```
161
+
162
+ Usage events include:
163
+
164
+ - anonymous install count
165
+ - CLI starts
166
+ - prompt count
167
+
168
+ Error reports can include:
169
+
170
+ - prompt
171
+ - OS
172
+ - version
173
+ - timestamp
174
+
175
+ Regular usage analytics do not store generated command content.
176
+
177
+ Analytics requests now use a short-lived server-issued session plus proof-of-work. This does not create perfect authentication for a public open-source client, but it makes automated spam substantially harder and gives the server something real to validate.
178
+
179
+ Server setup notes live in [docs/analytics-server.md](docs/analytics-server.md).
180
+
181
+ If OpenAI configuration is missing, `ai-cmd` fails clearly:
182
+
183
+ ```text
184
+ Missing AI_API_KEY. Set it in your environment or edit ~/.ai-cmd/config.json. A starter config has been created if it did not already exist.
185
+ ```
186
+
187
+ ## Usage
188
+
189
+ ### One-shot mode
190
+
191
+ ```bash
192
+ ai "how do I restart nginx"
193
+ ai "find all files bigger than 100MB"
194
+ ai "show disk usage" --json
195
+ ai "remove node_modules and reinstall packages" --exec
196
+ ai "find all jpg files" --copy
197
+ ```
198
+
199
+ `ai-cmd` now inspects the current folder structure and common project files like `package.json`, `Makefile`, `Cargo.toml`, `go.mod`, and compose files so it can suggest commands that fit the active workspace better.
200
+
201
+ ### Interactive mode
202
+
203
+ ```bash
204
+ ai
205
+ ```
206
+
207
+ Commands available inside the REPL:
208
+
209
+ - `help`
210
+ - `last`
211
+ - `explain`
212
+ - `run`
213
+ - `copy`
214
+ - `clear`
215
+ - `exit`
216
+
217
+ Example session:
218
+
219
+ ```text
220
+ ai-cmd > how do I restart nginx
221
+ ai-cmd > explain
222
+ ai-cmd > run
223
+ ```
224
+
225
+ ## CLI flags
226
+
227
+ - `--version` Show the branded version banner
228
+ - `--exec` Execute the generated command after confirmation
229
+ - `--yes` Skip the normal confirmation step for low and medium risk commands
230
+ - `--explain` Explicitly request explanation details from the provider
231
+ - `--json` Output machine-readable JSON
232
+ - `--shell <bash|zsh|sh>` Hint the active shell
233
+ - `--copy` Copy the generated command to the clipboard
234
+ - `--no-color` Disable colored output
235
+ - `--debug` Print diagnostic context
236
+
237
+ ## Safety model
238
+
239
+ `ai-cmd` is allowed to execute commands, so the MVP includes a practical safety layer:
240
+
241
+ - `low` risk: read-only or status-style commands such as `ls -la`
242
+ - `medium` risk: service restarts, dependency installs, project-scoped cleanup
243
+ - `high` risk: destructive recursive deletes, disk tools, remote shell pipes, system package removal
244
+
245
+ Execution rules:
246
+
247
+ - low and medium risk commands require confirmation unless `--yes` is used
248
+ - high risk commands always require an explicit confirmation phrase
249
+ - `--yes` never bypasses the high-risk confirmation path
250
+ - unsupported host OSes can still generate best-effort Unix-style commands, but execution is disabled
251
+
252
+ Example high-risk confirmation:
253
+
254
+ ```text
255
+ Risk: HIGH
256
+ This command can delete files recursively outside the current project.
257
+ Type EXECUTE HIGH RISK COMMAND to continue:
258
+ ```
259
+
260
+ ## Supported platforms
261
+
262
+ - Linux
263
+ - macOS
264
+ - WSL
265
+ - Generic Unix-like systems
266
+
267
+ Notes:
268
+
269
+ - Linux service-manager detection prefers `systemctl`, then `service`, then `rc-service`
270
+ - macOS service handling prefers launchd-oriented commands
271
+ - Native Windows command generation is intentionally limited in this MVP
272
+
273
+ ## Architecture
274
+
275
+ ```text
276
+ src/
277
+ cli/
278
+ config/
279
+ core/
280
+ exec/
281
+ platform/
282
+ providers/
283
+ safety/
284
+ types/
285
+ utils/
286
+ tests/
287
+ integration/
288
+ unit/
289
+ ```
290
+
291
+ Key design choices:
292
+
293
+ - thin Commander-based CLI layer
294
+ - business logic isolated under `core/`
295
+ - provider abstraction isolated under `providers/`
296
+ - platform and service-manager detection isolated under `platform/`
297
+ - execution safety and confirmation logic isolated under `safety/`
298
+ - strict JSON normalization and validation before rendering or execution
299
+
300
+ ## Development
301
+
302
+ ```bash
303
+ npm install
304
+ npm run typecheck
305
+ npm run lint
306
+ npm test
307
+ npm run build
308
+ ```
309
+
310
+ Run in development mode:
311
+
312
+ ```bash
313
+ npm run dev -- "how do I list listening ports"
314
+ ```
315
+
316
+ ## JSON output
317
+
318
+ `--json` prints a structured payload suitable for scripts:
319
+
320
+ ```bash
321
+ ai "show disk usage" --json
322
+ ```
323
+
324
+ Example shape:
325
+
326
+ ```json
327
+ {
328
+ "question": "show disk usage",
329
+ "command": "du -sh .",
330
+ "explanation": "Shows total disk usage for the current directory.",
331
+ "risk": "low",
332
+ "platformNotes": [],
333
+ "assumptions": [],
334
+ "platform": {
335
+ "os": "linux",
336
+ "shell": "bash",
337
+ "serviceManager": "systemctl",
338
+ "cwd": "/work/project",
339
+ "cwdName": "project"
340
+ }
341
+ }
342
+ ```
343
+
344
+ ## Roadmap
345
+
346
+ - richer shell support
347
+ - more precise filesystem-aware risk detection
348
+ - Homebrew distribution
349
+ - optional standalone binaries
350
+ - improved follow-up context handling
351
+
352
+ ## License
353
+
354
+ MIT
@@ -0,0 +1,2 @@
1
+ import type { AnalyticsClient, AppConfig } from "../types/index.js";
2
+ export declare function createAnalyticsClient(config: AppConfig): AnalyticsClient;
@@ -0,0 +1,95 @@
1
+ import { APP_NAME, APP_VERSION } from "../utils/branding.js";
2
+ import { createAnalyticsProof, createAnalyticsSession, isAnalyticsSessionFresh } from "./session.js";
3
+ const TRACKING_BASE_URL = "https://tracking.ottili.one/api/aicmd";
4
+ function createSessionGetter(config) {
5
+ let cachedSession;
6
+ let sessionPromise;
7
+ return async () => {
8
+ if (isAnalyticsSessionFresh(cachedSession)) {
9
+ return cachedSession;
10
+ }
11
+ if (!sessionPromise) {
12
+ sessionPromise = createAnalyticsSession(config).then((session) => {
13
+ cachedSession = session;
14
+ sessionPromise = undefined;
15
+ return session;
16
+ });
17
+ }
18
+ return sessionPromise;
19
+ };
20
+ }
21
+ async function postJson(path, payload, config, getSession) {
22
+ const controller = new AbortController();
23
+ const timeout = setTimeout(() => controller.abort(), 1_500);
24
+ try {
25
+ const session = await getSession();
26
+ if (!session) {
27
+ return;
28
+ }
29
+ await fetch(`${TRACKING_BASE_URL}${path}`, {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ "User-Agent": `${APP_NAME}/${APP_VERSION}`,
34
+ "X-AI-CMD-Install-Id": config.analyticsId ?? "",
35
+ "X-AI-CMD-Session-Id": session.sessionId,
36
+ "X-AI-CMD-Session-Expires": session.expiresAt,
37
+ "X-AI-CMD-Session-Signature": session.signature
38
+ },
39
+ body: JSON.stringify({
40
+ payload,
41
+ auth: createAnalyticsProof(session, payload)
42
+ }),
43
+ signal: controller.signal
44
+ });
45
+ }
46
+ catch {
47
+ // Analytics should never block or break the CLI.
48
+ }
49
+ finally {
50
+ clearTimeout(timeout);
51
+ }
52
+ }
53
+ function createNoopAnalyticsClient() {
54
+ return {
55
+ async trackCliStart() { },
56
+ async trackPromptSent() { },
57
+ async trackError() { }
58
+ };
59
+ }
60
+ export function createAnalyticsClient(config) {
61
+ if (!config.analytics || !config.analyticsId) {
62
+ return createNoopAnalyticsClient();
63
+ }
64
+ const getSession = createSessionGetter(config);
65
+ const basePayload = () => ({
66
+ installId: config.analyticsId,
67
+ app: APP_NAME.toLowerCase(),
68
+ version: APP_VERSION,
69
+ time: new Date().toISOString()
70
+ });
71
+ return {
72
+ async trackCliStart(payload) {
73
+ await postJson("/events", {
74
+ ...basePayload(),
75
+ event: "cli_started",
76
+ ...payload
77
+ }, config, getSession);
78
+ },
79
+ async trackPromptSent(payload) {
80
+ await postJson("/events", {
81
+ ...basePayload(),
82
+ event: "prompt_sent",
83
+ ...payload
84
+ }, config, getSession);
85
+ },
86
+ async trackError(payload) {
87
+ await postJson("/errors", {
88
+ ...basePayload(),
89
+ event: "error_reported",
90
+ ...payload
91
+ }, config, getSession);
92
+ }
93
+ };
94
+ }
95
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/analytics/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { AppConfig } from "../types/index.js";
2
+ export interface AnalyticsSession {
3
+ sessionId: string;
4
+ nonce: string;
5
+ difficulty: number;
6
+ expiresAt: string;
7
+ signature: string;
8
+ }
9
+ export declare function createAnalyticsSession(config: AppConfig): Promise<AnalyticsSession | undefined>;
10
+ export declare function isAnalyticsSessionFresh(session: AnalyticsSession | undefined): session is AnalyticsSession;
11
+ export declare function createAnalyticsProof(session: AnalyticsSession, payload: Record<string, unknown>): {
12
+ payloadHash: string;
13
+ counter: number;
14
+ proof: string;
15
+ };
@@ -0,0 +1,70 @@
1
+ import { createHash } from "node:crypto";
2
+ import { APP_NAME, APP_VERSION } from "../utils/branding.js";
3
+ const TRACKING_BASE_URL = "https://tracking.ottili.one/api/aicmd";
4
+ const SESSION_REFRESH_BUFFER_MS = 60_000;
5
+ function sha256(input) {
6
+ return createHash("sha256").update(input).digest("hex");
7
+ }
8
+ function hasLeadingZeroes(hex, zeroCount) {
9
+ return hex.startsWith("0".repeat(Math.max(0, zeroCount)));
10
+ }
11
+ export async function createAnalyticsSession(config) {
12
+ if (!config.analyticsId) {
13
+ return undefined;
14
+ }
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), 1_500);
17
+ try {
18
+ const response = await fetch(`${TRACKING_BASE_URL}/session`, {
19
+ method: "POST",
20
+ headers: {
21
+ "Content-Type": "application/json",
22
+ "User-Agent": `${APP_NAME}/${APP_VERSION}`
23
+ },
24
+ body: JSON.stringify({
25
+ installId: config.analyticsId,
26
+ app: APP_NAME.toLowerCase(),
27
+ version: APP_VERSION
28
+ }),
29
+ signal: controller.signal
30
+ });
31
+ if (!response.ok) {
32
+ return undefined;
33
+ }
34
+ return (await response.json());
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ finally {
40
+ clearTimeout(timeout);
41
+ }
42
+ }
43
+ export function isAnalyticsSessionFresh(session) {
44
+ if (!session) {
45
+ return false;
46
+ }
47
+ return (new Date(session.expiresAt).getTime() - Date.now() >
48
+ SESSION_REFRESH_BUFFER_MS);
49
+ }
50
+ export function createAnalyticsProof(session, payload) {
51
+ const payloadHash = sha256(JSON.stringify(payload));
52
+ let counter = 0;
53
+ while (counter < 250_000) {
54
+ const proof = sha256(`${session.sessionId}:${session.nonce}:${payloadHash}:${counter}`);
55
+ if (hasLeadingZeroes(proof, session.difficulty)) {
56
+ return {
57
+ payloadHash,
58
+ counter,
59
+ proof
60
+ };
61
+ }
62
+ counter += 1;
63
+ }
64
+ return {
65
+ payloadHash,
66
+ counter,
67
+ proof: sha256(`${session.sessionId}:${session.nonce}:${payloadHash}:${counter}`)
68
+ };
69
+ }
70
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/analytics/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { runCommand } from "../exec/runCommand.js";
2
+ import type { AIProvider, AnalyticsClient, AppConfig, PlatformContext, PromptAdapter } from "../types/index.js";
3
+ export interface CliDependencies {
4
+ loadConfig: () => Promise<AppConfig>;
5
+ detectPlatformContext: () => Promise<PlatformContext>;
6
+ createProvider: (config: AppConfig) => AIProvider;
7
+ createAnalyticsClient: (config: AppConfig) => AnalyticsClient;
8
+ createPromptAdapter: () => PromptAdapter;
9
+ copyToClipboard: (command: string) => Promise<void>;
10
+ commandRunner: typeof runCommand;
11
+ }
12
+ export declare function createDefaultDependencies(): CliDependencies;
13
+ export declare function runCli(argv?: string[], dependencies?: CliDependencies): Promise<void>;
14
+ export declare function runCliAndHandleErrors(argv?: string[], dependencies?: CliDependencies): Promise<void>;