workweave 0.2.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/README.md +254 -0
- package/dist/ai/local.d.ts +9 -0
- package/dist/ai/local.d.ts.map +1 -0
- package/dist/ai/local.js +148 -0
- package/dist/ai/local.js.map +1 -0
- package/dist/ai/provider.d.ts +43 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +191 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +193 -0
- package/dist/cli.js.map +1 -0
- package/dist/connectors/github.d.ts +13 -0
- package/dist/connectors/github.d.ts.map +1 -0
- package/dist/connectors/github.js +212 -0
- package/dist/connectors/github.js.map +1 -0
- package/dist/connectors/linear.d.ts +12 -0
- package/dist/connectors/linear.d.ts.map +1 -0
- package/dist/connectors/linear.js +160 -0
- package/dist/connectors/linear.js.map +1 -0
- package/dist/connectors/registry.d.ts +10 -0
- package/dist/connectors/registry.d.ts.map +1 -0
- package/dist/connectors/registry.js +36 -0
- package/dist/connectors/registry.js.map +1 -0
- package/dist/connectors/slack.d.ts +11 -0
- package/dist/connectors/slack.d.ts.map +1 -0
- package/dist/connectors/slack.js +212 -0
- package/dist/connectors/slack.js.map +1 -0
- package/dist/connectors/types.d.ts +2 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +3 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/display.d.ts +9 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +216 -0
- package/dist/display.js.map +1 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +64 -0
- package/dist/env.js.map +1 -0
- package/dist/pipeline/ai-synthesize.d.ts +11 -0
- package/dist/pipeline/ai-synthesize.d.ts.map +1 -0
- package/dist/pipeline/ai-synthesize.js +264 -0
- package/dist/pipeline/ai-synthesize.js.map +1 -0
- package/dist/pipeline/cluster-id.d.ts +5 -0
- package/dist/pipeline/cluster-id.d.ts.map +1 -0
- package/dist/pipeline/cluster-id.js +13 -0
- package/dist/pipeline/cluster-id.js.map +1 -0
- package/dist/pipeline/correlate.d.ts +7 -0
- package/dist/pipeline/correlate.d.ts.map +1 -0
- package/dist/pipeline/correlate.js +105 -0
- package/dist/pipeline/correlate.js.map +1 -0
- package/dist/pipeline/ingest.d.ts +17 -0
- package/dist/pipeline/ingest.d.ts.map +1 -0
- package/dist/pipeline/ingest.js +56 -0
- package/dist/pipeline/ingest.js.map +1 -0
- package/dist/pipeline/merge-plan.d.ts +14 -0
- package/dist/pipeline/merge-plan.d.ts.map +1 -0
- package/dist/pipeline/merge-plan.js +70 -0
- package/dist/pipeline/merge-plan.js.map +1 -0
- package/dist/pipeline/normalize.d.ts +3 -0
- package/dist/pipeline/normalize.d.ts.map +1 -0
- package/dist/pipeline/normalize.js +169 -0
- package/dist/pipeline/normalize.js.map +1 -0
- package/dist/pipeline/prioritize.d.ts +6 -0
- package/dist/pipeline/prioritize.d.ts.map +1 -0
- package/dist/pipeline/prioritize.js +453 -0
- package/dist/pipeline/prioritize.js.map +1 -0
- package/dist/pipeline/schedule.d.ts +11 -0
- package/dist/pipeline/schedule.d.ts.map +1 -0
- package/dist/pipeline/schedule.js +98 -0
- package/dist/pipeline/schedule.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +276 -0
- package/dist/setup.js.map +1 -0
- package/dist/types/index.d.ts +90 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Emin
|
|
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,254 @@
|
|
|
1
|
+
# Workweave
|
|
2
|
+
|
|
3
|
+
> Turn scattered developer signals into a focused, prioritized plan for your day.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](package.json)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
Workweave is a CLI tool that ingests activity from **GitHub**, **Linear**, and **Slack**, then synthesizes a time-boxed workday plan — either using deterministic prioritization rules or an AI model of your choice (local, Anthropic, or OpenAI).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
GitHub ─┐
|
|
17
|
+
Linear ─┼──▶ ingest ──▶ normalize ──▶ correlate ──▶ prioritize ──▶ schedule ──▶ plan
|
|
18
|
+
Slack ─┘ ▲
|
|
19
|
+
(optional AI)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
1. **Ingest** — pull open PRs, assigned issues, mentions, and messages
|
|
23
|
+
2. **Normalize** — map everything to a common `Artifact` model
|
|
24
|
+
3. **Correlate** — link related items across sources
|
|
25
|
+
4. **Prioritize** — score by urgency, importance, social pressure, staleness, and blocking factors
|
|
26
|
+
5. **Schedule** — fit clusters into your available workday budget
|
|
27
|
+
6. **Display** — rich terminal output or machine-readable JSON
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Three AI providers** — local model (no key required), Anthropic Claude, or OpenAI
|
|
34
|
+
- **Rules mode** — works fully offline, no API key needed
|
|
35
|
+
- **JSON output** — pipe into scripts, dashboards, or other tools with `--json`
|
|
36
|
+
- **Interactive setup** — guided `workweave setup` wizard configures everything
|
|
37
|
+
- **Connector detection** — `workweave detect` checks which sources are ready before you run
|
|
38
|
+
- **Configurable workday budget** — schedule work to fit your actual available hours
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/emin93/workweave.git
|
|
46
|
+
cd workweave
|
|
47
|
+
npm install
|
|
48
|
+
npm run build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To use the `workweave` command globally:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm link
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or run directly without linking:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node dist/cli.js <command>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Quick start
|
|
66
|
+
|
|
67
|
+
**1. Run the interactive setup wizard:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
workweave setup
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The wizard walks you through:
|
|
74
|
+
- GitHub personal access token (repo scope)
|
|
75
|
+
- AI provider — downloads a local model (~4 GB, runs on CPU) or configures an API key
|
|
76
|
+
- Workday hours (default: 8 h)
|
|
77
|
+
- Linear API key (optional)
|
|
78
|
+
- Slack user token (optional)
|
|
79
|
+
|
|
80
|
+
All credentials are stored in a local `.env` file that is never committed.
|
|
81
|
+
|
|
82
|
+
**2. Check that your connectors are ready:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
workweave detect --connectors github,linear,slack
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**3. Synthesize your day:**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
workweave synth --connectors github,linear,slack
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Commands
|
|
97
|
+
|
|
98
|
+
### `workweave setup`
|
|
99
|
+
|
|
100
|
+
Interactive wizard to configure connectors and AI.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
workweave setup
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `workweave detect`
|
|
107
|
+
|
|
108
|
+
Check which connectors are configured and reachable.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
workweave detect --connectors github,linear,slack
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
github ✓ authenticated as emin93
|
|
116
|
+
linear ✓ workspace: Acme Corp
|
|
117
|
+
slack ✗ SLACK_USER_TOKEN not set
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `workweave synth`
|
|
121
|
+
|
|
122
|
+
Fetch signals and produce a workday plan.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Rules-based (no AI, works offline)
|
|
126
|
+
workweave synth --connectors github,linear
|
|
127
|
+
|
|
128
|
+
# With AI — auto-detects provider (local → Anthropic → OpenAI)
|
|
129
|
+
workweave synth --connectors github,linear,slack --ai
|
|
130
|
+
|
|
131
|
+
# Force a specific provider
|
|
132
|
+
workweave synth --connectors github --ai --provider anthropic
|
|
133
|
+
|
|
134
|
+
# Adjust available time
|
|
135
|
+
workweave synth --connectors github --workday-minutes 360
|
|
136
|
+
|
|
137
|
+
# Machine-readable output
|
|
138
|
+
workweave synth --connectors github --ai --json
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## AI providers
|
|
144
|
+
|
|
145
|
+
Workweave supports three AI backends. When `--ai` is set and no `--provider` is specified, it auto-detects in this order:
|
|
146
|
+
|
|
147
|
+
| Priority | Provider | How to configure |
|
|
148
|
+
|----------|----------|-----------------|
|
|
149
|
+
| 1 | **Local model** (Llama, via `node-llama-cpp`) | Run `workweave setup` to download (~4 GB, CPU-only, no key) |
|
|
150
|
+
| 2 | **Anthropic** (`claude-haiku-4-5`) | Set `ANTHROPIC_API_KEY` |
|
|
151
|
+
| 3 | **OpenAI** (`gpt-4o-mini`) | Set `OPENAI_API_KEY` |
|
|
152
|
+
|
|
153
|
+
You can force a provider with `--provider local|anthropic|openai`.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Environment variables
|
|
158
|
+
|
|
159
|
+
| Variable | Description |
|
|
160
|
+
|----------|-------------|
|
|
161
|
+
| `GITHUB_TOKEN` | GitHub personal access token (repo scope) |
|
|
162
|
+
| `LINEAR_API_KEY` | Linear personal API key |
|
|
163
|
+
| `SLACK_USER_TOKEN` | Slack user token (`xoxp-…`) |
|
|
164
|
+
| `ANTHROPIC_API_KEY` | Anthropic API key |
|
|
165
|
+
| `ANTHROPIC_MODEL` | Model override (default: `claude-haiku-4-5`) |
|
|
166
|
+
| `OPENAI_API_KEY` | OpenAI API key |
|
|
167
|
+
| `OPENAI_MODEL` | Model override (default: `gpt-4o-mini`) |
|
|
168
|
+
| `WORKDAY_MINUTES` | Available minutes per day (default: `480`) |
|
|
169
|
+
|
|
170
|
+
Variables can be set in a local `.env` file at the project root (created by `workweave setup`) or exported in your shell.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## JSON output
|
|
175
|
+
|
|
176
|
+
Add `--json` to any command for machine-readable output. Progress logs are suppressed and only the result is written to stdout.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
workweave synth --connectors github --json | jq '.plan.blocks[].title'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**`synth` output shape:**
|
|
183
|
+
|
|
184
|
+
```jsonc
|
|
185
|
+
{
|
|
186
|
+
"plan": {
|
|
187
|
+
"blocks": [...], // scheduled work blocks
|
|
188
|
+
"synthesisMode": "ai", // "ai" | "rules"
|
|
189
|
+
"synthesisProvider": "local"
|
|
190
|
+
},
|
|
191
|
+
"meta": {
|
|
192
|
+
"connectors": ["github"],
|
|
193
|
+
"rawEvents": 42, // raw events ingested
|
|
194
|
+
"artifacts": 18, // normalized artifacts
|
|
195
|
+
"connectorErrors": [] // non-fatal errors
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**`detect` output shape:**
|
|
201
|
+
|
|
202
|
+
```jsonc
|
|
203
|
+
{
|
|
204
|
+
"connectors": [
|
|
205
|
+
{ "id": "github", "ready": true, "detail": "authenticated as emin93" },
|
|
206
|
+
{ "id": "linear", "ready": false, "detail": "LINEAR_API_KEY not set" }
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Project structure
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
src/
|
|
217
|
+
cli.ts # CLI entry point, argument parsing
|
|
218
|
+
setup.ts # Interactive setup wizard
|
|
219
|
+
display.ts # Terminal rendering (plan, detect, spinner)
|
|
220
|
+
env.ts # .env file loading and writing
|
|
221
|
+
connectors/
|
|
222
|
+
github.ts # GitHub REST API connector
|
|
223
|
+
linear.ts # Linear GraphQL connector
|
|
224
|
+
slack.ts # Slack Web API connector
|
|
225
|
+
registry.ts # Connector registry + detect-all
|
|
226
|
+
pipeline/
|
|
227
|
+
ingest.ts # Orchestrate connector fetching
|
|
228
|
+
normalize.ts # Map raw events → Artifact
|
|
229
|
+
correlate.ts # Link related artifacts across sources
|
|
230
|
+
prioritize.ts # Score and rank clusters
|
|
231
|
+
schedule.ts # Fit clusters into workday budget
|
|
232
|
+
ai-synthesize.ts # AI-powered clustering and titling
|
|
233
|
+
ai/
|
|
234
|
+
provider.ts # LLMProvider interface + OpenAI/Anthropic/LlamaCpp impls
|
|
235
|
+
local.ts # Local model path helpers and download
|
|
236
|
+
types/ # Shared TypeScript models
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Contributing
|
|
242
|
+
|
|
243
|
+
Contributions are welcome. Please open an issue to discuss significant changes before submitting a PR.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npm run build # compile TypeScript
|
|
247
|
+
npm run lint # ESLint
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const MODEL_NAME = "Qwen2.5-1.5B-Instruct";
|
|
2
|
+
export declare const MODEL_FILE = "qwen2.5-1.5b-instruct-q4_k_m.gguf";
|
|
3
|
+
export declare const MODEL_SIZE_MB = 986;
|
|
4
|
+
export declare function modelsDir(): string;
|
|
5
|
+
export declare function modelPath(): string;
|
|
6
|
+
export declare function modelExists(): boolean;
|
|
7
|
+
export type ProgressCallback = (downloadedMB: number, totalMB: number, pct: number) => void;
|
|
8
|
+
export declare function downloadModel(onProgress: ProgressCallback): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/ai/local.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,UAAU,0BAA0B,CAAC;AAClD,eAAO,MAAM,UAAU,sCAAsC,CAAC;AAC9D,eAAO,MAAM,aAAa,MAAM,CAAC;AAMjC,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,KACR,IAAI,CAAC;AAEV,wBAAgB,aAAa,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmGzE"}
|
package/dist/ai/local.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MODEL_SIZE_MB = exports.MODEL_FILE = exports.MODEL_NAME = void 0;
|
|
37
|
+
exports.modelsDir = modelsDir;
|
|
38
|
+
exports.modelPath = modelPath;
|
|
39
|
+
exports.modelExists = modelExists;
|
|
40
|
+
exports.downloadModel = downloadModel;
|
|
41
|
+
const os_1 = require("os");
|
|
42
|
+
const path_1 = require("path");
|
|
43
|
+
const fs_1 = require("fs");
|
|
44
|
+
const https = __importStar(require("https"));
|
|
45
|
+
const http = __importStar(require("http"));
|
|
46
|
+
exports.MODEL_NAME = "Qwen2.5-1.5B-Instruct";
|
|
47
|
+
exports.MODEL_FILE = "qwen2.5-1.5b-instruct-q4_k_m.gguf";
|
|
48
|
+
exports.MODEL_SIZE_MB = 986;
|
|
49
|
+
const HF_URL = "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/" +
|
|
50
|
+
exports.MODEL_FILE;
|
|
51
|
+
function modelsDir() {
|
|
52
|
+
return (0, path_1.join)((0, os_1.homedir)(), ".workweave", "models");
|
|
53
|
+
}
|
|
54
|
+
function modelPath() {
|
|
55
|
+
return (0, path_1.join)(modelsDir(), exports.MODEL_FILE);
|
|
56
|
+
}
|
|
57
|
+
function modelExists() {
|
|
58
|
+
return (0, fs_1.existsSync)(modelPath());
|
|
59
|
+
}
|
|
60
|
+
function downloadModel(onProgress) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const dir = modelsDir();
|
|
63
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
64
|
+
const dest = modelPath();
|
|
65
|
+
const tmp = dest + ".part";
|
|
66
|
+
function doRequest(url, redirects = 0) {
|
|
67
|
+
if (redirects > 10) {
|
|
68
|
+
reject(new Error("Too many redirects"));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const parsed = new URL(url);
|
|
72
|
+
const transport = parsed.protocol === "https:" ? https : http;
|
|
73
|
+
transport
|
|
74
|
+
.get(url, { headers: { "User-Agent": "workweave/1.0" } }, (res) => {
|
|
75
|
+
const { statusCode, headers: resHeaders } = res;
|
|
76
|
+
if (statusCode === 301 ||
|
|
77
|
+
statusCode === 302 ||
|
|
78
|
+
statusCode === 307 ||
|
|
79
|
+
statusCode === 308) {
|
|
80
|
+
res.resume();
|
|
81
|
+
doRequest(resHeaders.location, redirects + 1);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (statusCode !== 200) {
|
|
85
|
+
res.resume();
|
|
86
|
+
reject(new Error(`Download failed: HTTP ${statusCode}`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const totalBytes = parseInt(resHeaders["content-length"] ?? "0", 10);
|
|
90
|
+
const totalMB = totalBytes
|
|
91
|
+
? Math.round(totalBytes / 1024 / 1024)
|
|
92
|
+
: exports.MODEL_SIZE_MB;
|
|
93
|
+
let downloaded = 0;
|
|
94
|
+
const ws = (0, fs_1.createWriteStream)(tmp);
|
|
95
|
+
res.on("data", (chunk) => {
|
|
96
|
+
downloaded += chunk.length;
|
|
97
|
+
ws.write(chunk);
|
|
98
|
+
const downloadedMB = Math.round(downloaded / 1024 / 1024);
|
|
99
|
+
const pct = totalBytes
|
|
100
|
+
? Math.round((downloaded / totalBytes) * 100)
|
|
101
|
+
: 0;
|
|
102
|
+
onProgress(downloadedMB, totalMB, pct);
|
|
103
|
+
});
|
|
104
|
+
res.on("end", () => {
|
|
105
|
+
ws.end(() => {
|
|
106
|
+
// rename .part → final
|
|
107
|
+
const { renameSync } = require("fs");
|
|
108
|
+
try {
|
|
109
|
+
renameSync(tmp, dest);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// cross-device fallback: copy then delete
|
|
113
|
+
const { copyFileSync } = require("fs");
|
|
114
|
+
copyFileSync(tmp, dest);
|
|
115
|
+
(0, fs_1.unlinkSync)(tmp);
|
|
116
|
+
}
|
|
117
|
+
resolve();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
res.on("error", (err) => {
|
|
121
|
+
ws.destroy();
|
|
122
|
+
try {
|
|
123
|
+
(0, fs_1.unlinkSync)(tmp);
|
|
124
|
+
}
|
|
125
|
+
catch { /* ignore */ }
|
|
126
|
+
reject(err);
|
|
127
|
+
});
|
|
128
|
+
ws.on("error", (err) => {
|
|
129
|
+
res.destroy();
|
|
130
|
+
try {
|
|
131
|
+
(0, fs_1.unlinkSync)(tmp);
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignore */ }
|
|
134
|
+
reject(err);
|
|
135
|
+
});
|
|
136
|
+
})
|
|
137
|
+
.on("error", (err) => {
|
|
138
|
+
try {
|
|
139
|
+
(0, fs_1.unlinkSync)(tmp);
|
|
140
|
+
}
|
|
141
|
+
catch { /* ignore */ }
|
|
142
|
+
reject(err);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
doRequest(HF_URL);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/ai/local.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface LLMProvider {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
complete(prompt: string): Promise<string>;
|
|
5
|
+
isAvailable(): Promise<boolean>;
|
|
6
|
+
}
|
|
7
|
+
export interface OpenAIConfig {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AnthropicConfig {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
model?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class OpenAIProvider implements LLMProvider {
|
|
16
|
+
private config;
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
constructor(config: OpenAIConfig);
|
|
20
|
+
isAvailable(): Promise<boolean>;
|
|
21
|
+
complete(prompt: string): Promise<string>;
|
|
22
|
+
}
|
|
23
|
+
export declare class AnthropicProvider implements LLMProvider {
|
|
24
|
+
private config;
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
constructor(config: AnthropicConfig);
|
|
28
|
+
isAvailable(): Promise<boolean>;
|
|
29
|
+
complete(prompt: string): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
export declare class LlamaCppProvider implements LLMProvider {
|
|
32
|
+
private readonly _modelPath;
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
private _llama;
|
|
36
|
+
private _model;
|
|
37
|
+
constructor(_modelPath: string);
|
|
38
|
+
isAvailable(): Promise<boolean>;
|
|
39
|
+
private static _esmImport;
|
|
40
|
+
private _load;
|
|
41
|
+
complete(prompt: string): Promise<string>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,cAAe,YAAW,WAAW;IAIpC,OAAO,CAAC,MAAM;IAH1B,EAAE,SAAY;IACd,IAAI,SAAgB;gBAEA,MAAM,EAAE,YAAY;IAElC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA4BhD;AAED,qBAAa,iBAAkB,YAAW,WAAW;IAIvC,OAAO,CAAC,MAAM;IAH1B,EAAE,SAAe;IACjB,IAAI,SAAmB;gBAEH,MAAM,EAAE,eAAe;IAErC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA8BhD;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAStC,OAAO,CAAC,QAAQ,CAAC,UAAU;IARvC,EAAE,SAAW;IACb,IAAI,SAAgC;IAGpC,OAAO,CAAC,MAAM,CAAa;IAE3B,OAAO,CAAC,MAAM,CAAa;gBAEE,UAAU,EAAE,MAAM;IAEzC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAQrC,OAAO,CAAC,MAAM,CAAC,UAAU,CACiC;YAE5C,KAAK;IAOb,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAWhD"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LlamaCppProvider = exports.AnthropicProvider = exports.OpenAIProvider = void 0;
|
|
37
|
+
const https = __importStar(require("https"));
|
|
38
|
+
const http = __importStar(require("http"));
|
|
39
|
+
const fs_1 = require("fs");
|
|
40
|
+
class OpenAIProvider {
|
|
41
|
+
config;
|
|
42
|
+
id = "openai";
|
|
43
|
+
name = "OpenAI API";
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
}
|
|
47
|
+
async isAvailable() {
|
|
48
|
+
return !!this.config.apiKey;
|
|
49
|
+
}
|
|
50
|
+
async complete(prompt) {
|
|
51
|
+
const model = this.config.model || "gpt-4o-mini";
|
|
52
|
+
const url = new URL("/v1/chat/completions", "https://api.openai.com");
|
|
53
|
+
const body = JSON.stringify({
|
|
54
|
+
model,
|
|
55
|
+
messages: [{ role: "user", content: prompt }],
|
|
56
|
+
temperature: 0.3,
|
|
57
|
+
response_format: { type: "json_object" },
|
|
58
|
+
});
|
|
59
|
+
return httpRequest({
|
|
60
|
+
hostname: url.hostname,
|
|
61
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
62
|
+
path: url.pathname,
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
67
|
+
},
|
|
68
|
+
protocol: url.protocol,
|
|
69
|
+
body,
|
|
70
|
+
timeoutMs: 30_000,
|
|
71
|
+
}).then((raw) => {
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
return parsed.choices?.[0]?.message?.content ?? "";
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.OpenAIProvider = OpenAIProvider;
|
|
78
|
+
class AnthropicProvider {
|
|
79
|
+
config;
|
|
80
|
+
id = "anthropic";
|
|
81
|
+
name = "Anthropic API";
|
|
82
|
+
constructor(config) {
|
|
83
|
+
this.config = config;
|
|
84
|
+
}
|
|
85
|
+
async isAvailable() {
|
|
86
|
+
return !!this.config.apiKey;
|
|
87
|
+
}
|
|
88
|
+
async complete(prompt) {
|
|
89
|
+
const model = this.config.model || "claude-haiku-4-5";
|
|
90
|
+
const url = new URL("/v1/messages", "https://api.anthropic.com");
|
|
91
|
+
const body = JSON.stringify({
|
|
92
|
+
model,
|
|
93
|
+
max_tokens: 4096,
|
|
94
|
+
messages: [{ role: "user", content: prompt }],
|
|
95
|
+
});
|
|
96
|
+
return httpRequest({
|
|
97
|
+
hostname: url.hostname,
|
|
98
|
+
port: 443,
|
|
99
|
+
path: url.pathname,
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
"x-api-key": this.config.apiKey,
|
|
104
|
+
"anthropic-version": "2023-06-01",
|
|
105
|
+
},
|
|
106
|
+
protocol: "https:",
|
|
107
|
+
body,
|
|
108
|
+
timeoutMs: 60_000,
|
|
109
|
+
}).then((raw) => {
|
|
110
|
+
const parsed = JSON.parse(raw);
|
|
111
|
+
const textBlock = parsed.content
|
|
112
|
+
?.find((b) => b.type === "text");
|
|
113
|
+
return textBlock?.text ?? "";
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
118
|
+
class LlamaCppProvider {
|
|
119
|
+
_modelPath;
|
|
120
|
+
id = "local";
|
|
121
|
+
name = "Local model (Qwen2.5-1.5B)";
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
|
+
_llama = null;
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
_model = null;
|
|
126
|
+
constructor(_modelPath) {
|
|
127
|
+
this._modelPath = _modelPath;
|
|
128
|
+
}
|
|
129
|
+
async isAvailable() {
|
|
130
|
+
return (0, fs_1.existsSync)(this._modelPath);
|
|
131
|
+
}
|
|
132
|
+
// node-llama-cpp is ESM-only with top-level await, so require() cannot load
|
|
133
|
+
// it. This function bypasses TypeScript's import()->require() transform and
|
|
134
|
+
// calls the real ESM dynamic import loader instead.
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
136
|
+
static _esmImport = new Function("m", "return import(m)");
|
|
137
|
+
async _load() {
|
|
138
|
+
if (this._model)
|
|
139
|
+
return;
|
|
140
|
+
const { getLlama } = await LlamaCppProvider._esmImport("node-llama-cpp");
|
|
141
|
+
this._llama = await getLlama({ gpu: false });
|
|
142
|
+
this._model = await this._llama.loadModel({ modelPath: this._modelPath });
|
|
143
|
+
}
|
|
144
|
+
async complete(prompt) {
|
|
145
|
+
await this._load();
|
|
146
|
+
const { LlamaChatSession } = await LlamaCppProvider._esmImport("node-llama-cpp");
|
|
147
|
+
const context = await this._model.createContext({ contextSize: 4096 });
|
|
148
|
+
const session = new LlamaChatSession({
|
|
149
|
+
contextSequence: context.getSequence(),
|
|
150
|
+
});
|
|
151
|
+
const response = await session.prompt(prompt);
|
|
152
|
+
await context.dispose();
|
|
153
|
+
return response;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.LlamaCppProvider = LlamaCppProvider;
|
|
157
|
+
function httpRequest(opts) {
|
|
158
|
+
const transport = opts.protocol === "https:" ? https : http;
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
const req = transport.request({
|
|
161
|
+
hostname: opts.hostname,
|
|
162
|
+
port: opts.port,
|
|
163
|
+
path: opts.path,
|
|
164
|
+
method: opts.method,
|
|
165
|
+
headers: {
|
|
166
|
+
...opts.headers,
|
|
167
|
+
...(opts.body ? { "Content-Length": Buffer.byteLength(opts.body) } : {}),
|
|
168
|
+
},
|
|
169
|
+
}, (res) => {
|
|
170
|
+
let data = "";
|
|
171
|
+
res.on("data", (chunk) => (data += chunk));
|
|
172
|
+
res.on("end", () => {
|
|
173
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
174
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
resolve(data);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
req.on("error", reject);
|
|
182
|
+
req.setTimeout(opts.timeoutMs, () => {
|
|
183
|
+
req.destroy();
|
|
184
|
+
reject(new Error(`Request timed out after ${opts.timeoutMs}ms`));
|
|
185
|
+
});
|
|
186
|
+
if (opts.body)
|
|
187
|
+
req.write(opts.body);
|
|
188
|
+
req.end();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=provider.js.map
|