intentdocs 0.8.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.
@@ -0,0 +1,123 @@
1
+ import {
2
+ writeConfig
3
+ } from "./chunk-ICOMMHKG.js";
4
+ import {
5
+ getAuth
6
+ } from "./chunk-PCNDP5SX.js";
7
+
8
+ // src/lib/api.ts
9
+ var AuthError = class extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "AuthError";
13
+ }
14
+ };
15
+ async function refreshSyncToken(config) {
16
+ const auth = getAuth();
17
+ if (!auth || !config.projectId || !config.mapId) return null;
18
+ try {
19
+ const res = await fetch(
20
+ `${auth.apiBase}/api/cli/projects/${config.projectId}/maps`,
21
+ { headers: { Authorization: `Bearer ${auth.token}`, "Content-Type": "application/json" } }
22
+ );
23
+ if (!res.ok) return null;
24
+ const { maps } = await res.json();
25
+ const map = maps.find((m) => m.id === config.mapId);
26
+ if (!map) return null;
27
+ const updated = { ...config, syncToken: map.syncToken };
28
+ writeConfig(updated);
29
+ return map.syncToken;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ async function apiFetch(config, path, init) {
35
+ const res = await fetch(apiUrl(config, path), init);
36
+ if (res.status === 401) {
37
+ const newToken = await refreshSyncToken(config);
38
+ if (newToken) {
39
+ const refreshedConfig = { ...config, syncToken: newToken };
40
+ const retry = await fetch(apiUrl(refreshedConfig, path), init);
41
+ if (retry.ok) return retry;
42
+ }
43
+ throw new AuthError(
44
+ "Session expired. Run `intent login` then `intent init` to re-authenticate."
45
+ );
46
+ }
47
+ if (res.status === 403) {
48
+ throw new AuthError(
49
+ "Access denied. You may not have permission for this project. Run `intent login`."
50
+ );
51
+ }
52
+ if (!res.ok) throw new Error(`API error: ${res.status}`);
53
+ return res;
54
+ }
55
+ function apiUrl(config, path) {
56
+ return `${config.apiBase}/api/sync/${config.syncToken}${path}`;
57
+ }
58
+ async function listStories(config) {
59
+ const res = await apiFetch(config, "/stories");
60
+ return res.json();
61
+ }
62
+ async function getContext(config) {
63
+ const res = await apiFetch(config, "/context");
64
+ return res.text();
65
+ }
66
+ async function updateStories(config, stories) {
67
+ const res = await apiFetch(config, "/stories", {
68
+ method: "PATCH",
69
+ headers: { "Content-Type": "application/json" },
70
+ body: JSON.stringify({ stories })
71
+ });
72
+ return res.json();
73
+ }
74
+ async function uploadScreenshot(config, storyId, screenshot) {
75
+ try {
76
+ const form = new FormData();
77
+ form.append("story_id", storyId);
78
+ form.append("image", new Blob([screenshot.buffer], { type: screenshot.mimeType }), screenshot.filename);
79
+ const res = await apiFetch(config, "/screenshots", {
80
+ method: "POST",
81
+ body: form
82
+ });
83
+ return await res.json();
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ async function createStories(config, stories) {
89
+ const res = await apiFetch(config, "/stories", {
90
+ method: "POST",
91
+ headers: { "Content-Type": "application/json" },
92
+ body: JSON.stringify({ stories })
93
+ });
94
+ return res.json();
95
+ }
96
+ async function reportCriteria(config, storyId, results) {
97
+ const res = await apiFetch(config, "/criteria", {
98
+ method: "PATCH",
99
+ headers: { "Content-Type": "application/json" },
100
+ body: JSON.stringify({ story_id: storyId, results })
101
+ });
102
+ return res.json();
103
+ }
104
+ async function raiseBlocker(config, payload) {
105
+ const res = await apiFetch(config, "/blocker", {
106
+ method: "POST",
107
+ headers: { "Content-Type": "application/json" },
108
+ body: JSON.stringify(payload)
109
+ });
110
+ return res.json();
111
+ }
112
+
113
+ export {
114
+ apiFetch,
115
+ apiUrl,
116
+ listStories,
117
+ getContext,
118
+ updateStories,
119
+ uploadScreenshot,
120
+ createStories,
121
+ reportCriteria,
122
+ raiseBlocker
123
+ };
@@ -0,0 +1,48 @@
1
+ // src/lib/config.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ var CONFIG_DIR = ".intent";
5
+ var CONFIG_FILE = "config.json";
6
+ function configPath() {
7
+ let dir = process.cwd();
8
+ while (true) {
9
+ const candidate = join(dir, CONFIG_DIR, CONFIG_FILE);
10
+ if (existsSync(candidate)) return candidate;
11
+ const parent = dirname(dir);
12
+ if (parent === dir) break;
13
+ dir = parent;
14
+ }
15
+ return join(process.cwd(), CONFIG_DIR, CONFIG_FILE);
16
+ }
17
+ function hasConfig() {
18
+ return existsSync(configPath());
19
+ }
20
+ function readConfig() {
21
+ try {
22
+ const raw = readFileSync(configPath(), "utf-8");
23
+ return JSON.parse(raw);
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ function writeConfig(config) {
29
+ const existing = existsSync(configPath()) ? configPath() : null;
30
+ const dir = existing ? dirname(existing) : join(process.cwd(), CONFIG_DIR);
31
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
32
+ writeFileSync(join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n");
33
+ }
34
+ function requireConfig() {
35
+ const config = readConfig();
36
+ if (!config) {
37
+ console.error("Not initialized. Run: intent init");
38
+ process.exit(1);
39
+ }
40
+ return config;
41
+ }
42
+
43
+ export {
44
+ hasConfig,
45
+ readConfig,
46
+ writeConfig,
47
+ requireConfig
48
+ };