@deerdesign/deertools 1.0.2 → 1.0.3

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 CHANGED
@@ -6,13 +6,15 @@ Log in once from your computer, and grant your AI coding agent (Antigravity, Cur
6
6
 
7
7
  ### Supported Products
8
8
 
9
- | Product Key | Name | Production URL | Description |
10
- | :--- | :--- | :--- | :--- |
11
- | `kosztorys` | DeerTools Kosztorys | `https://connect.deerdesign.pl/kosztorys/` | Floor plan PDF analysis, kitchen area detection, interior cost estimation |
12
- | `katalog` | DeerTools Katalog | `https://connect.deerdesign.pl/katalog/` | Material catalog (Smart/Modern/High Life), category specs, products |
13
- | `dostawy` | DeerTools Dostawy | `https://ship.deergroup.pl/` | Logistics, site delivery schedules, supplier shipments, tracking |
14
- | `supplier` | Supplier Hub | `https://supplier.deergroup.pl/` | Supplier orders, line items, claims, delivery status |
15
- | `rendermind` | RenderMind | `https://rendermind.deergroup.pl/` | pCon 3D render pipelines, AI image generation, floor plan proxies |
9
+ | Product Command Key | Polish Alias | Name | Production URL | Description |
10
+ | :--- | :--- | :--- | :--- | :--- |
11
+ | `estimate` | `kosztorys` | DeerTools Estimate | `https://connect.deerdesign.pl/kosztorys/` | Floor plan PDF analysis, kitchen area detection, interior cost estimation |
12
+ | `catalog` | `katalog` | DeerTools Catalog | `https://connect.deerdesign.pl/katalog/` | Material catalog (Smart/Modern/High Life), category specs, products |
13
+ | `deliveries` | `dostawy` | DeerTools Deliveries | `https://ship.deergroup.pl/` | Logistics, site delivery schedules, supplier shipments, tracking |
14
+ | `supplier` | — | Supplier Hub | `https://supplier.deergroup.pl/` | Supplier orders, line items, claims, delivery status |
15
+ | `rendermind` | — | RenderMind | `https://rendermind.deergroup.pl/` | pCon 3D render pipelines, AI image generation, floor plan proxies |
16
+
17
+ *(Both English names like `catalog`, `estimate`, `deliveries` and legacy Polish names like `katalog`, `kosztorys`, `dostawy` are supported interchangeably).*
16
18
 
17
19
  ---
18
20
 
@@ -23,7 +25,7 @@ Requires **Node.js ≥ 18**. Zero runtime dependencies.
23
25
  ### Option 1: Run instantly with `npx` (No installation needed)
24
26
 
25
27
  ```bash
26
- npx @deerdesign/deertools login rendermind
28
+ npx @deerdesign/deertools login catalog
27
29
  ```
28
30
 
29
31
  ### Option 2: Install globally
@@ -48,10 +50,16 @@ DeerTools CLI implements the official **OAuth 2.1 Authorization Code grant with
48
50
 
49
51
  ```bash
50
52
  # Log in to a single product with read-only access (default):
53
+ deertools login catalog
54
+
55
+ # Log in to other products (in English or Polish):
56
+ deertools login estimate # or: deertools login kosztorys
57
+ deertools login deliveries # or: deertools login dostawy
58
+ deertools login supplier
51
59
  deertools login rendermind
52
60
 
53
61
  # Request read + write scopes:
54
- deertools login katalog --write
62
+ deertools login catalog --write
55
63
 
56
64
  # Interactive login across all 5 products sequentially:
57
65
  deertools login all
@@ -63,14 +71,14 @@ deertools whoami
63
71
  deertools whoami --json
64
72
 
65
73
  # Revoke local credentials:
66
- deertools logout rendermind
74
+ deertools logout catalog
67
75
  ```
68
76
 
69
77
  ### How it works under the hood
70
78
 
71
79
  1. **RFC 8414 Discovery**: The CLI queries `/.well-known/oauth-authorization-server` on the product host to discover authorization and token endpoints.
72
80
  2. **Loopback Server**: The CLI starts an ephemeral HTTP server on `127.0.0.1:<random-port>` and generates a cryptographically secure PKCE code verifier and S256 challenge.
73
- 3. **Browser Consent**: The user's default browser opens to the product's `/oauth/authorize` page. The user authenticates (SSO / Microsoft / Magic Link) and approves the requested scopes.
81
+ 3. **Browser Consent**: The user's default browser opens to the product's `/oauth/authorize` page. The user authenticates (Microsoft SSO / SSO / Magic Link) and approves the requested scopes.
74
82
  4. **Single-Use Authorization Code**: The browser redirects to the loopback server with a short-lived authorization code (60-second TTL). No keys or tokens ever appear in query strings.
75
83
  5. **Direct Exchange**: The CLI sends a direct `POST` to the product's token endpoint (`/oauth-token`) with the code verifier.
76
84
  6. **Local Storage**: The product issues a scoped API key (`deer_<product>_live_...`) valid for 90 days. The CLI persists it in `~/.deertools/credentials.json` with strict POSIX file permissions (`0600`).
@@ -83,13 +91,14 @@ deertools logout rendermind
83
91
 
84
92
  ```bash
85
93
  # Read high-level llms.txt (public overview and authentication instructions):
94
+ deertools docs catalog
86
95
  deertools docs rendermind
87
96
 
88
97
  # Fetch full OpenAPI 3.0 JSON specification:
89
- deertools docs rendermind openapi
98
+ deertools docs catalog openapi
90
99
 
91
100
  # List all available endpoints with required scopes:
92
- deertools endpoints rendermind
101
+ deertools endpoints catalog
93
102
  ```
94
103
 
95
104
  ### 2. Invoke Product APIs
@@ -98,7 +107,7 @@ The CLI handles authorization headers automatically, rejecting any requests to n
98
107
 
99
108
  ```bash
100
109
  # Call GET endpoint:
101
- deertools api katalog GET /api/v1/products --query category=flooring
110
+ deertools api catalog GET /api/v1/products --query category=flooring
102
111
 
103
112
  # Call POST endpoint with JSON payload:
104
113
  deertools api rendermind POST /api/v1/visualizations --data '{"prompt":"Modern Scandinavian kitchen"}'
@@ -110,10 +119,11 @@ deertools api rendermind POST /api/v1/visualizations --data-file request.json
110
119
  ### 3. Specialized Helpers
111
120
 
112
121
  ```bash
113
- # Analyze architectural floor plan PDF and calculate cost estimate (Kosztorys):
122
+ # Analyze architectural floor plan PDF and calculate cost estimate (Estimate / Kosztorys):
114
123
  deertools estimate --file floorplan.pdf --kitchen 6
115
124
 
116
125
  # Install product agent skills into ~/.gemini/config/skills:
126
+ deertools skill install catalog
117
127
  deertools skill install rendermind
118
128
  ```
119
129
 
@@ -124,9 +134,9 @@ deertools skill install rendermind
124
134
  In CI pipelines or containerized agents where browser interaction is unavailable, provide API keys via environment variables:
125
135
 
126
136
  ```bash
127
- export DEERTOOLS_KOSZTORYS_KEY="deer_kosztorys_live_..."
128
- export DEERTOOLS_KATALOG_KEY="deer_katalog_live_..."
129
- export DEERTOOLS_DOSTAWY_KEY="deer_dostawy_live_..."
137
+ export DEERTOOLS_CATALOG_KEY="deer_catalog_live_..." # or DEERTOOLS_KATALOG_KEY
138
+ export DEERTOOLS_ESTIMATE_KEY="deer_kosztorys_live_..." # or DEERTOOLS_KOSZTORYS_KEY
139
+ export DEERTOOLS_DELIVERIES_KEY="deer_dostawy_live_..." # or DEERTOOLS_DOSTAWY_KEY
130
140
  export DEERTOOLS_SUPPLIER_KEY="deer_supplier_live_..."
131
141
  export DEERTOOLS_RENDERMIND_KEY="deer_rendermind_live_..."
132
142
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deerdesign/deertools",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -37,20 +37,39 @@ function saveProduct(productId, entry) {
37
37
 
38
38
  function removeProduct(productId) {
39
39
  const data = readAll();
40
- const existed = Boolean(data.products[productId]);
41
- delete data.products[productId];
40
+ const ids = [productId];
41
+ try {
42
+ const { getProduct } = require('./registry');
43
+ const p = getProduct(productId);
44
+ if (p.aliases) ids.push(...p.aliases);
45
+ } catch (_) {}
46
+ let existed = false;
47
+ for (const id of ids) {
48
+ if (data.products[id]) {
49
+ existed = true;
50
+ delete data.products[id];
51
+ }
52
+ }
42
53
  writeAll(data);
43
54
  return existed;
44
55
  }
45
56
 
46
57
  /** Klucz z zmiennej środowiskowej ma pierwszeństwo (CI, agenty); zwraca też źródło. */
47
58
  function getKey(product) {
48
- const envName = `DEERTOOLS_${product.id.toUpperCase()}_KEY`;
49
- const fromEnv = process.env[envName] || (process.env.DEER_API_KEY && process.env.DEER_API_KEY.startsWith(product.keyPrefix) ? process.env.DEER_API_KEY : '');
50
- if (fromEnv) return { key: fromEnv, source: `env:${envName}`, entry: null };
51
- const entry = readAll().products[product.id];
52
- if (!entry || !entry.key) return null;
53
- return { key: entry.key, source: 'file', entry };
59
+ const idsToCheck = [product.id, ...(product.aliases || [])];
60
+ for (const pid of idsToCheck) {
61
+ const envName = `DEERTOOLS_${pid.toUpperCase()}_KEY`;
62
+ if (process.env[envName]) return { key: process.env[envName], source: `env:${envName}`, entry: null };
63
+ }
64
+ if (process.env.DEER_API_KEY && process.env.DEER_API_KEY.startsWith(product.keyPrefix)) {
65
+ return { key: process.env.DEER_API_KEY, source: 'env:DEER_API_KEY', entry: null };
66
+ }
67
+ const products = readAll().products;
68
+ for (const pid of idsToCheck) {
69
+ const entry = products[pid];
70
+ if (entry && entry.key) return { key: entry.key, source: 'file', entry };
71
+ }
72
+ return null;
54
73
  }
55
74
 
56
75
  function expiryInfo(entry) {
package/src/registry.js CHANGED
@@ -1,53 +1,85 @@
1
1
  'use strict';
2
2
  /**
3
- * Rejestr produktów DeerTools. Każdy produkt ma własny adres autoryzacji, własny prefiks klucza
4
- * i własne API. Adres bazowy API i lista endpointów pochodzą z publicznego openapi.json produktu
5
- * (wykrywanego z llms.txt), więc CLI nie zna endpointów na sztywno.
3
+ * DeerTools Product Registry.
4
+ *
5
+ * Primary CLI product identifiers are in English:
6
+ * - estimate (Polish alias: kosztorys)
7
+ * - catalog (Polish alias: katalog)
8
+ * - deliveries (Polish alias: dostawy)
9
+ * - supplier
10
+ * - rendermind
6
11
  */
7
12
  const PRODUCTS = {
8
- kosztorys: {
9
- name: 'Kosztorys',
13
+ estimate: {
14
+ name: 'Estimate',
15
+ localName: 'Kosztorys',
10
16
  site: 'https://connect.deerdesign.pl/kosztorys',
11
17
  authorizeUrl: 'https://connect.deerdesign.pl/kosztorys/oauth/authorize',
18
+ tokenUrl: 'https://vlemuxlubrowuuxpgllk.supabase.co/functions/v1/oauth-token',
12
19
  keyPrefix: 'deer_kosztorys_live_',
13
20
  fallbackApiBase: 'https://vlemuxlubrowuuxpgllk.supabase.co/functions/v1',
21
+ aliases: ['kosztorys', 'costs', 'cost'],
14
22
  },
15
- katalog: {
16
- name: 'Katalog Connect',
23
+ catalog: {
24
+ name: 'Catalog',
25
+ localName: 'Katalog Connect',
17
26
  site: 'https://connect.deerdesign.pl/katalog',
18
27
  authorizeUrl: 'https://connect.deerdesign.pl/katalog/oauth/authorize',
28
+ tokenUrl: 'https://ftzaobeixglnssemugjf.supabase.co/functions/v1/oauth-token',
19
29
  keyPrefix: 'deer_catalog_live_',
20
- fallbackApiBase: null,
30
+ fallbackApiBase: 'https://ftzaobeixglnssemugjf.supabase.co/functions/v1/api-gateway',
31
+ aliases: ['katalog'],
21
32
  },
22
- dostawy: {
23
- name: 'Dostawy',
33
+ deliveries: {
34
+ name: 'Deliveries',
35
+ localName: 'Dostawy',
24
36
  site: 'https://ship.deergroup.pl',
25
37
  authorizeUrl: 'https://ship.deergroup.pl/oauth/authorize',
38
+ tokenUrl: 'https://ddyfbvogkumvscckotvb.supabase.co/functions/v1/oauth-token',
26
39
  keyPrefix: 'deer_dostawy_live_',
27
- fallbackApiBase: null,
40
+ fallbackApiBase: 'https://ddyfbvogkumvscckotvb.supabase.co/functions/v1',
41
+ aliases: ['dostawy', 'shipping', 'delivery', 'ship'],
28
42
  },
29
43
  supplier: {
30
44
  name: 'Supplier Hub',
45
+ localName: 'Supplier Hub',
31
46
  site: 'https://supplier.deergroup.pl',
32
47
  authorizeUrl: 'https://supplier.deergroup.pl/oauth/authorize',
48
+ tokenUrl: 'https://hubnxvzxdgocbtmkfdsz.supabase.co/functions/v1/oauth-token',
33
49
  keyPrefix: 'deer_supplier_live_',
34
- fallbackApiBase: null,
50
+ fallbackApiBase: 'https://hubnxvzxdgocbtmkfdsz.supabase.co/functions/v1/public-api',
51
+ aliases: [],
35
52
  },
36
53
  rendermind: {
37
54
  name: 'RenderMind',
55
+ localName: 'RenderMind',
38
56
  site: 'https://rendermind.deergroup.pl',
39
57
  authorizeUrl: 'https://rendermind.deergroup.pl/oauth/authorize',
58
+ tokenUrl: 'https://lkljsuptgyktkwojnwbu.supabase.co/functions/v1/oauth-token',
40
59
  keyPrefix: 'deer_rendermind_live_',
41
- fallbackApiBase: null,
60
+ fallbackApiBase: 'https://lkljsuptgyktkwojnwbu.supabase.co/functions/v1/rendermind-api',
61
+ aliases: [],
42
62
  },
43
63
  };
44
64
 
65
+ const ALIASES = {
66
+ kosztorys: 'estimate',
67
+ costs: 'estimate',
68
+ cost: 'estimate',
69
+ katalog: 'catalog',
70
+ dostawy: 'deliveries',
71
+ shipping: 'deliveries',
72
+ delivery: 'deliveries',
73
+ ship: 'deliveries',
74
+ };
75
+
45
76
  const ALL_IDS = Object.keys(PRODUCTS);
46
77
 
47
78
  function getProduct(id) {
48
- const key = String(id || '').toLowerCase();
79
+ let key = String(id || '').toLowerCase().trim();
80
+ if (ALIASES[key]) key = ALIASES[key];
49
81
  if (!PRODUCTS[key]) {
50
- const err = new Error(`Nieznany produkt „${id}”. Dostępne: ${ALL_IDS.join(', ')}.`);
82
+ const err = new Error(`Nieznany produkt „${id}”. Dostępne: ${ALL_IDS.join(', ')} (lub po polsku: kosztorys, katalog, dostawy).`);
51
83
  err.exitCode = 2;
52
84
  throw err;
53
85
  }
@@ -59,4 +91,4 @@ function resolveProducts(arg) {
59
91
  return String(arg).split(',').map((s) => getProduct(s.trim()));
60
92
  }
61
93
 
62
- module.exports = { PRODUCTS, ALL_IDS, getProduct, resolveProducts };
94
+ module.exports = { PRODUCTS, ALIASES, ALL_IDS, getProduct, resolveProducts };