unotoken 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.
Files changed (122) hide show
  1. package/README.md +360 -0
  2. package/dist/cli.d.ts +17 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +1207 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/client.d.ts +15 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +15 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/db.d.ts +52 -0
  11. package/dist/db.d.ts.map +1 -0
  12. package/dist/db.js +97 -0
  13. package/dist/db.js.map +1 -0
  14. package/dist/dotenv.d.ts +69 -0
  15. package/dist/dotenv.d.ts.map +1 -0
  16. package/dist/dotenv.js +115 -0
  17. package/dist/dotenv.js.map +1 -0
  18. package/dist/env-mapper.d.ts +55 -0
  19. package/dist/env-mapper.d.ts.map +1 -0
  20. package/dist/env-mapper.js +97 -0
  21. package/dist/env-mapper.js.map +1 -0
  22. package/dist/exec.d.ts +80 -0
  23. package/dist/exec.d.ts.map +1 -0
  24. package/dist/exec.js +214 -0
  25. package/dist/exec.js.map +1 -0
  26. package/dist/index.d.ts +12 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +43 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/oauth/commands.d.ts +151 -0
  31. package/dist/oauth/commands.d.ts.map +1 -0
  32. package/dist/oauth/commands.js +322 -0
  33. package/dist/oauth/commands.js.map +1 -0
  34. package/dist/oauth/config.d.ts +84 -0
  35. package/dist/oauth/config.d.ts.map +1 -0
  36. package/dist/oauth/config.js +156 -0
  37. package/dist/oauth/config.js.map +1 -0
  38. package/dist/oauth/crypto-helpers.d.ts +44 -0
  39. package/dist/oauth/crypto-helpers.d.ts.map +1 -0
  40. package/dist/oauth/crypto-helpers.js +94 -0
  41. package/dist/oauth/crypto-helpers.js.map +1 -0
  42. package/dist/oauth/device-secret.d.ts +57 -0
  43. package/dist/oauth/device-secret.d.ts.map +1 -0
  44. package/dist/oauth/device-secret.js +106 -0
  45. package/dist/oauth/device-secret.js.map +1 -0
  46. package/dist/oauth/flow.d.ts +112 -0
  47. package/dist/oauth/flow.d.ts.map +1 -0
  48. package/dist/oauth/flow.js +255 -0
  49. package/dist/oauth/flow.js.map +1 -0
  50. package/dist/oauth/index.d.ts +18 -0
  51. package/dist/oauth/index.d.ts.map +1 -0
  52. package/dist/oauth/index.js +24 -0
  53. package/dist/oauth/index.js.map +1 -0
  54. package/dist/oauth/key-wrap.d.ts +146 -0
  55. package/dist/oauth/key-wrap.d.ts.map +1 -0
  56. package/dist/oauth/key-wrap.js +275 -0
  57. package/dist/oauth/key-wrap.js.map +1 -0
  58. package/dist/oauth/pkce.d.ts +29 -0
  59. package/dist/oauth/pkce.d.ts.map +1 -0
  60. package/dist/oauth/pkce.js +34 -0
  61. package/dist/oauth/pkce.js.map +1 -0
  62. package/dist/oauth/provider.d.ts +79 -0
  63. package/dist/oauth/provider.d.ts.map +1 -0
  64. package/dist/oauth/provider.js +10 -0
  65. package/dist/oauth/provider.js.map +1 -0
  66. package/dist/oauth/providers/github.d.ts +75 -0
  67. package/dist/oauth/providers/github.d.ts.map +1 -0
  68. package/dist/oauth/providers/github.js +119 -0
  69. package/dist/oauth/providers/github.js.map +1 -0
  70. package/dist/oauth/providers/google.d.ts +115 -0
  71. package/dist/oauth/providers/google.d.ts.map +1 -0
  72. package/dist/oauth/providers/google.js +285 -0
  73. package/dist/oauth/providers/google.js.map +1 -0
  74. package/dist/sdk.d.ts +8 -0
  75. package/dist/sdk.d.ts.map +1 -0
  76. package/dist/sdk.js +8 -0
  77. package/dist/sdk.js.map +1 -0
  78. package/dist/server.d.ts +33 -0
  79. package/dist/server.d.ts.map +1 -0
  80. package/dist/server.js +287 -0
  81. package/dist/server.js.map +1 -0
  82. package/dist/signatures/approval-codes.d.ts +192 -0
  83. package/dist/signatures/approval-codes.d.ts.map +1 -0
  84. package/dist/signatures/approval-codes.js +407 -0
  85. package/dist/signatures/approval-codes.js.map +1 -0
  86. package/dist/signatures/commands.d.ts +108 -0
  87. package/dist/signatures/commands.d.ts.map +1 -0
  88. package/dist/signatures/commands.js +270 -0
  89. package/dist/signatures/commands.js.map +1 -0
  90. package/dist/signatures/devices.d.ts +165 -0
  91. package/dist/signatures/devices.d.ts.map +1 -0
  92. package/dist/signatures/devices.js +344 -0
  93. package/dist/signatures/devices.js.map +1 -0
  94. package/dist/signatures/email-config.d.ts +102 -0
  95. package/dist/signatures/email-config.d.ts.map +1 -0
  96. package/dist/signatures/email-config.js +188 -0
  97. package/dist/signatures/email-config.js.map +1 -0
  98. package/dist/signatures/email.d.ts +106 -0
  99. package/dist/signatures/email.d.ts.map +1 -0
  100. package/dist/signatures/email.js +180 -0
  101. package/dist/signatures/email.js.map +1 -0
  102. package/dist/signatures/fingerprint.d.ts +70 -0
  103. package/dist/signatures/fingerprint.d.ts.map +1 -0
  104. package/dist/signatures/fingerprint.js +123 -0
  105. package/dist/signatures/fingerprint.js.map +1 -0
  106. package/dist/signatures/guard.d.ts +118 -0
  107. package/dist/signatures/guard.d.ts.map +1 -0
  108. package/dist/signatures/guard.js +310 -0
  109. package/dist/signatures/guard.js.map +1 -0
  110. package/dist/signatures/resend.d.ts +84 -0
  111. package/dist/signatures/resend.d.ts.map +1 -0
  112. package/dist/signatures/resend.js +248 -0
  113. package/dist/signatures/resend.js.map +1 -0
  114. package/dist/token-requests.d.ts +80 -0
  115. package/dist/token-requests.d.ts.map +1 -0
  116. package/dist/token-requests.js +201 -0
  117. package/dist/token-requests.js.map +1 -0
  118. package/dist/tokens.d.ts +80 -0
  119. package/dist/tokens.d.ts.map +1 -0
  120. package/dist/tokens.js +150 -0
  121. package/dist/tokens.js.map +1 -0
  122. package/package.json +62 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Token approval workflow for unotoken -- US-004.
3
+ *
4
+ * Provides a request-and-approve flow for prefix-scoped tokens.
5
+ * Apps request access to specific prefixes, and vault owners approve or deny.
6
+ *
7
+ * Storage: JSON file (token-requests.json) alongside the vault,
8
+ * consistent with the existing token-scopes.json pattern from db.ts.
9
+ *
10
+ * Workflow:
11
+ * 1. App requests: `unotoken token request --name ci-levelfit --prefix levelfit/ --reason '...'`
12
+ * 2. Owner reviews: `unotoken token requests`
13
+ * 3. Owner acts: `unotoken token approve <id>` or `unotoken token deny <id> --reason '...'`
14
+ * 4. On approval, a scoped token is created and displayed once.
15
+ */
16
+ export type RequestStatus = 'pending' | 'approved' | 'denied';
17
+ export interface TokenRequest {
18
+ id: string;
19
+ name: string;
20
+ prefixes: string[];
21
+ reason: string;
22
+ status: RequestStatus;
23
+ requested_at: string;
24
+ reviewed_at: string | null;
25
+ reviewed_by: string | null;
26
+ deny_reason: string | null;
27
+ }
28
+ export type TokenRequestStore = Record<string, TokenRequest>;
29
+ export interface CreateRequestInput {
30
+ name: string;
31
+ prefixes: string[];
32
+ reason: string;
33
+ }
34
+ export interface ApproveResult {
35
+ request: TokenRequest;
36
+ /** The raw token value (displayed once) */
37
+ token: string;
38
+ }
39
+ export interface EmailNotification {
40
+ to: string;
41
+ subject: string;
42
+ body: string;
43
+ }
44
+ export declare function setNotifyFn(fn: (notification: EmailNotification) => void): void;
45
+ export declare function getNotifyFn(): (notification: EmailNotification) => void;
46
+ export declare function getDefaultRequestsPath(baseDir?: string): string;
47
+ export declare function loadRequests(requestsPath?: string): TokenRequestStore;
48
+ export declare function saveRequests(store: TokenRequestStore, requestsPath?: string): void;
49
+ /**
50
+ * Create a new pending token request.
51
+ */
52
+ export declare function createRequest(input: CreateRequestInput, ownerEmail?: string, requestsPath?: string): TokenRequest;
53
+ /**
54
+ * List token requests, optionally filtered by status.
55
+ */
56
+ export declare function listRequests(statusFilter?: RequestStatus, requestsPath?: string): TokenRequest[];
57
+ /**
58
+ * Get a single request by ID.
59
+ */
60
+ export declare function getRequest(id: string, requestsPath?: string): TokenRequest | null;
61
+ /**
62
+ * Get a request by name (for status polling by the requester).
63
+ */
64
+ export declare function getRequestByName(name: string, requestsPath?: string): TokenRequest | null;
65
+ /**
66
+ * Approve a token request. Returns the request and the generated token value.
67
+ *
68
+ * The caller is responsible for actually creating the token via yokotoken's
69
+ * token API and storing the scopes. This function only updates the request status.
70
+ */
71
+ export declare function approveRequest(id: string, reviewedBy: string, requestsPath?: string): TokenRequest;
72
+ /**
73
+ * Deny a token request with an optional reason.
74
+ */
75
+ export declare function denyRequest(id: string, reviewedBy: string, denyReason?: string, requestsPath?: string): TokenRequest;
76
+ /**
77
+ * Format a request for display.
78
+ */
79
+ export declare function formatRequest(req: TokenRequest): string;
80
+ //# sourceMappingURL=token-requests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-requests.d.ts","sourceRoot":"","sources":["../src/token-requests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAUD,wBAAgB,WAAW,CAAC,EAAE,EAAE,CAAC,YAAY,EAAE,iBAAiB,KAAK,IAAI,GAAG,IAAI,CAE/E;AAED,wBAAgB,WAAW,IAAI,CAAC,YAAY,EAAE,iBAAiB,KAAK,IAAI,CAEvE;AAID,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED,wBAAgB,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAWrE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAOlF;AAWD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,CAwCd;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,YAAY,CAAC,EAAE,aAAa,EAC5B,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,EAAE,CAShB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAGjF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAMzF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,CAmBd;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,CAoBd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAoBvD"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Token approval workflow for unotoken -- US-004.
3
+ *
4
+ * Provides a request-and-approve flow for prefix-scoped tokens.
5
+ * Apps request access to specific prefixes, and vault owners approve or deny.
6
+ *
7
+ * Storage: JSON file (token-requests.json) alongside the vault,
8
+ * consistent with the existing token-scopes.json pattern from db.ts.
9
+ *
10
+ * Workflow:
11
+ * 1. App requests: `unotoken token request --name ci-levelfit --prefix levelfit/ --reason '...'`
12
+ * 2. Owner reviews: `unotoken token requests`
13
+ * 3. Owner acts: `unotoken token approve <id>` or `unotoken token deny <id> --reason '...'`
14
+ * 4. On approval, a scoped token is created and displayed once.
15
+ */
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import crypto from 'node:crypto';
19
+ import { getVaultDir } from 'yokotoken';
20
+ // ─── Notification callback ────────────────────────────────────────────
21
+ /**
22
+ * Optional notification callback. Set via setNotifyFn() for email alerts.
23
+ * Default is a no-op. Tests can inject a mock.
24
+ */
25
+ let notifyFn = () => { };
26
+ export function setNotifyFn(fn) {
27
+ notifyFn = fn;
28
+ }
29
+ export function getNotifyFn() {
30
+ return notifyFn;
31
+ }
32
+ // ─── Storage ──────────────────────────────────────────────────────────
33
+ export function getDefaultRequestsPath(baseDir) {
34
+ const dir = baseDir ?? getVaultDir();
35
+ return path.join(dir, 'token-requests.json');
36
+ }
37
+ export function loadRequests(requestsPath) {
38
+ const filePath = requestsPath ?? getDefaultRequestsPath();
39
+ try {
40
+ if (fs.existsSync(filePath)) {
41
+ const content = fs.readFileSync(filePath, 'utf-8');
42
+ return JSON.parse(content);
43
+ }
44
+ }
45
+ catch {
46
+ // Corrupt file -- start fresh
47
+ }
48
+ return {};
49
+ }
50
+ export function saveRequests(store, requestsPath) {
51
+ const filePath = requestsPath ?? getDefaultRequestsPath();
52
+ const dir = path.dirname(filePath);
53
+ if (!fs.existsSync(dir)) {
54
+ fs.mkdirSync(dir, { recursive: true });
55
+ }
56
+ fs.writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');
57
+ }
58
+ // ─── Operations ───────────────────────────────────────────────────────
59
+ /**
60
+ * Generate a short unique request ID (8 hex chars).
61
+ */
62
+ function generateRequestId() {
63
+ return crypto.randomBytes(4).toString('hex');
64
+ }
65
+ /**
66
+ * Create a new pending token request.
67
+ */
68
+ export function createRequest(input, ownerEmail, requestsPath) {
69
+ const store = loadRequests(requestsPath);
70
+ const id = generateRequestId();
71
+ const request = {
72
+ id,
73
+ name: input.name,
74
+ prefixes: input.prefixes,
75
+ reason: input.reason,
76
+ status: 'pending',
77
+ requested_at: new Date().toISOString(),
78
+ reviewed_at: null,
79
+ reviewed_by: null,
80
+ deny_reason: null,
81
+ };
82
+ store[id] = request;
83
+ saveRequests(store, requestsPath);
84
+ // Send email notification if configured
85
+ if (ownerEmail) {
86
+ const prefixDisplay = input.prefixes.map((p) => `${p}*`).join(', ');
87
+ notifyFn({
88
+ to: ownerEmail,
89
+ subject: `New token request: ${input.name} wants access to ${prefixDisplay}`,
90
+ body: [
91
+ `A new token request has been submitted:`,
92
+ ``,
93
+ `Name: ${input.name}`,
94
+ `Prefixes: ${prefixDisplay}`,
95
+ `Reason: ${input.reason}`,
96
+ `Request ID: ${id}`,
97
+ ``,
98
+ `To approve: unotoken token approve ${id}`,
99
+ `To deny: unotoken token deny ${id} --reason "..."`,
100
+ ].join('\n'),
101
+ });
102
+ }
103
+ return request;
104
+ }
105
+ /**
106
+ * List token requests, optionally filtered by status.
107
+ */
108
+ export function listRequests(statusFilter, requestsPath) {
109
+ const store = loadRequests(requestsPath);
110
+ let requests = Object.values(store);
111
+ if (statusFilter) {
112
+ requests = requests.filter((r) => r.status === statusFilter);
113
+ }
114
+ // Sort by requested_at descending (newest first)
115
+ requests.sort((a, b) => b.requested_at.localeCompare(a.requested_at));
116
+ return requests;
117
+ }
118
+ /**
119
+ * Get a single request by ID.
120
+ */
121
+ export function getRequest(id, requestsPath) {
122
+ const store = loadRequests(requestsPath);
123
+ return store[id] ?? null;
124
+ }
125
+ /**
126
+ * Get a request by name (for status polling by the requester).
127
+ */
128
+ export function getRequestByName(name, requestsPath) {
129
+ const store = loadRequests(requestsPath);
130
+ const requests = Object.values(store)
131
+ .filter((r) => r.name === name)
132
+ .sort((a, b) => b.requested_at.localeCompare(a.requested_at));
133
+ return requests[0] ?? null;
134
+ }
135
+ /**
136
+ * Approve a token request. Returns the request and the generated token value.
137
+ *
138
+ * The caller is responsible for actually creating the token via yokotoken's
139
+ * token API and storing the scopes. This function only updates the request status.
140
+ */
141
+ export function approveRequest(id, reviewedBy, requestsPath) {
142
+ const store = loadRequests(requestsPath);
143
+ const request = store[id];
144
+ if (!request) {
145
+ throw new Error(`Token request '${id}' not found`);
146
+ }
147
+ if (request.status !== 'pending') {
148
+ throw new Error(`Token request '${id}' is already ${request.status}`);
149
+ }
150
+ request.status = 'approved';
151
+ request.reviewed_at = new Date().toISOString();
152
+ request.reviewed_by = reviewedBy;
153
+ store[id] = request;
154
+ saveRequests(store, requestsPath);
155
+ return request;
156
+ }
157
+ /**
158
+ * Deny a token request with an optional reason.
159
+ */
160
+ export function denyRequest(id, reviewedBy, denyReason, requestsPath) {
161
+ const store = loadRequests(requestsPath);
162
+ const request = store[id];
163
+ if (!request) {
164
+ throw new Error(`Token request '${id}' not found`);
165
+ }
166
+ if (request.status !== 'pending') {
167
+ throw new Error(`Token request '${id}' is already ${request.status}`);
168
+ }
169
+ request.status = 'denied';
170
+ request.reviewed_at = new Date().toISOString();
171
+ request.reviewed_by = reviewedBy;
172
+ request.deny_reason = denyReason ?? null;
173
+ store[id] = request;
174
+ saveRequests(store, requestsPath);
175
+ return request;
176
+ }
177
+ /**
178
+ * Format a request for display.
179
+ */
180
+ export function formatRequest(req) {
181
+ const prefixDisplay = req.prefixes.map((p) => `${p}*`).join(', ');
182
+ const lines = [
183
+ `ID: ${req.id}`,
184
+ `Name: ${req.name}`,
185
+ `Prefixes: ${prefixDisplay}`,
186
+ `Reason: ${req.reason}`,
187
+ `Status: ${req.status}`,
188
+ `Requested: ${req.requested_at}`,
189
+ ];
190
+ if (req.reviewed_at) {
191
+ lines.push(`Reviewed: ${req.reviewed_at}`);
192
+ }
193
+ if (req.reviewed_by) {
194
+ lines.push(`Reviewed by: ${req.reviewed_by}`);
195
+ }
196
+ if (req.deny_reason) {
197
+ lines.push(`Deny reason: ${req.deny_reason}`);
198
+ }
199
+ return lines.join('\n');
200
+ }
201
+ //# sourceMappingURL=token-requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-requests.js","sourceRoot":"","sources":["../src/token-requests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAsCxC,yEAAyE;AAEzE;;;GAGG;AACH,IAAI,QAAQ,GAA8C,GAAG,EAAE,GAAE,CAAC,CAAC;AAEnE,MAAM,UAAU,WAAW,CAAC,EAA6C;IACvE,QAAQ,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AAEzE,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,MAAM,GAAG,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,YAAqB;IAChD,MAAM,QAAQ,GAAG,YAAY,IAAI,sBAAsB,EAAE,CAAC;IAC1D,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC;QAClD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAwB,EAAE,YAAqB;IAC1E,MAAM,QAAQ,GAAG,YAAY,IAAI,sBAAsB,EAAE,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,yEAAyE;AAEzE;;GAEG;AACH,SAAS,iBAAiB;IACxB,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAyB,EACzB,UAAmB,EACnB,YAAqB;IAErB,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE/B,MAAM,OAAO,GAAiB;QAC5B,EAAE;QACF,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,IAAI;KAClB,CAAC;IAEF,KAAK,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IACpB,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAElC,wCAAwC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,QAAQ,CAAC;YACP,EAAE,EAAE,UAAU;YACd,OAAO,EAAE,sBAAsB,KAAK,CAAC,IAAI,oBAAoB,aAAa,EAAE;YAC5E,IAAI,EAAE;gBACJ,yCAAyC;gBACzC,EAAE;gBACF,SAAS,KAAK,CAAC,IAAI,EAAE;gBACrB,aAAa,aAAa,EAAE;gBAC5B,WAAW,KAAK,CAAC,MAAM,EAAE;gBACzB,eAAe,EAAE,EAAE;gBACnB,EAAE;gBACF,sCAAsC,EAAE,EAAE;gBAC1C,gCAAgC,EAAE,iBAAiB;aACpD,CAAC,IAAI,CAAC,IAAI,CAAC;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,YAA4B,EAC5B,YAAqB;IAErB,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,YAAY,EAAE,CAAC;QACjB,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;IAC/D,CAAC;IACD,iDAAiD;IACjD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IACtE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,EAAU,EAAE,YAAqB;IAC1D,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,YAAqB;IAClE,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;SAC9B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,EAAU,EACV,UAAkB,EAClB,YAAqB;IAErB,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IAE1B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,gBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC;IAC5B,OAAO,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC;IACjC,KAAK,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IACpB,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAElC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,EAAU,EACV,UAAkB,EAClB,UAAmB,EACnB,YAAqB;IAErB,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IAE1B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,gBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC;IAC1B,OAAO,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC;IACjC,OAAO,CAAC,WAAW,GAAG,UAAU,IAAI,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IACpB,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAElC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAiB;IAC7C,MAAM,aAAa,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG;QACZ,OAAO,GAAG,CAAC,EAAE,EAAE;QACf,SAAS,GAAG,CAAC,IAAI,EAAE;QACnB,aAAa,aAAa,EAAE;QAC5B,WAAW,GAAG,CAAC,MAAM,EAAE;QACvB,WAAW,GAAG,CAAC,MAAM,EAAE;QACvB,cAAc,GAAG,CAAC,YAAY,EAAE;KACjC,CAAC;IACF,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Prefix-scoped token management for unotoken — US-001.
3
+ *
4
+ * Extends yokotoken's TokenManager with prefix-based scope enforcement.
5
+ * Scopes restrict which secret paths a token can access.
6
+ *
7
+ * Scope matching rules:
8
+ * - A scope "levelfit/" matches any path starting with "levelfit/"
9
+ * - Multiple scopes: token can access paths matching ANY of its scopes
10
+ * - No scopes (null): token has full access (backward compat)
11
+ * - Empty scopes array ([]): token has no access (effectively revoked)
12
+ */
13
+ export interface ScopeCheckResult {
14
+ allowed: boolean;
15
+ reason?: string;
16
+ }
17
+ /**
18
+ * Check if a secret path matches any of the allowed prefix scopes.
19
+ *
20
+ * @param secretPath - The secret path being accessed (e.g., "levelfit/stripe/key")
21
+ * @param scopes - Array of allowed prefixes (e.g., ["levelfit/"])
22
+ * @returns true if the path matches at least one scope
23
+ */
24
+ export declare function matchesPrefix(secretPath: string, scopes: string[]): boolean;
25
+ /**
26
+ * Check if a token (by raw value) is allowed to access a given path.
27
+ *
28
+ * @param rawToken - The raw bearer token
29
+ * @param secretPath - The secret path being accessed
30
+ * @param scopesPath - Optional path to the scopes file
31
+ * @returns ScopeCheckResult with allowed/denied and reason
32
+ */
33
+ export declare function checkTokenScope(rawToken: string, secretPath: string, scopesPath?: string): ScopeCheckResult;
34
+ /**
35
+ * Filter a list of secret paths to only those matching a token's scopes.
36
+ *
37
+ * @param rawToken - The raw bearer token
38
+ * @param paths - List of secret paths to filter
39
+ * @param scopesPath - Optional path to the scopes file
40
+ * @returns Filtered paths that the token can access
41
+ */
42
+ export declare function filterByTokenScope(rawToken: string, paths: string[], scopesPath?: string): string[];
43
+ /**
44
+ * Store scopes for a newly created token.
45
+ *
46
+ * @param rawToken - The raw token value (will be hashed for storage)
47
+ * @param name - The token name
48
+ * @param prefixes - Array of prefix scopes, or null for full access
49
+ * @param scopesPath - Optional path to the scopes file
50
+ */
51
+ export declare function storeTokenScopes(rawToken: string, name: string, prefixes: string[] | null, scopesPath?: string): void;
52
+ /**
53
+ * Remove scopes when a token is revoked.
54
+ */
55
+ export declare function revokeTokenScopes(tokenName: string, scopesPath?: string): boolean;
56
+ /**
57
+ * Get scope display string for a token.
58
+ * Examples: "levelfit/*", "hq-cloud/* + synesis/*", "*" (full access)
59
+ */
60
+ export declare function formatScopes(scopes: string[] | null): string;
61
+ /**
62
+ * Build a scope map from the store keyed by token name.
63
+ * Used by the CLI to display scopes alongside token metadata.
64
+ */
65
+ export declare function buildScopesByName(scopesPath?: string): Map<string, string[] | null>;
66
+ /**
67
+ * Intersect a query prefix with token scopes.
68
+ * Used for LIST operations: if the user requests prefix "levelfit/"
69
+ * and the token has scope ["levelfit/"], return "levelfit/".
70
+ * If the token has scope ["synesis/"], return null (no intersection).
71
+ *
72
+ * @param queryPrefix - The prefix from the list query (may be undefined for all)
73
+ * @param scopes - The token's allowed prefixes
74
+ * @returns The effective prefix to query, or null if no intersection
75
+ */
76
+ export declare function intersectPrefixWithScopes(queryPrefix: string | undefined, scopes: string[] | null): {
77
+ effectivePrefixes: string[] | null;
78
+ restricted: boolean;
79
+ };
80
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAG3E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,gBAAgB,CAuBlB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,EAAE,CAWV;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EACzB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CAGN;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,MAAM,CAI5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,CAOnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,GACtB;IAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CA8B7D"}
package/dist/tokens.js ADDED
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Prefix-scoped token management for unotoken — US-001.
3
+ *
4
+ * Extends yokotoken's TokenManager with prefix-based scope enforcement.
5
+ * Scopes restrict which secret paths a token can access.
6
+ *
7
+ * Scope matching rules:
8
+ * - A scope "levelfit/" matches any path starting with "levelfit/"
9
+ * - Multiple scopes: token can access paths matching ANY of its scopes
10
+ * - No scopes (null): token has full access (backward compat)
11
+ * - Empty scopes array ([]): token has no access (effectively revoked)
12
+ */
13
+ import { hashToken } from 'yokotoken';
14
+ import { getTokenScopes, setTokenScopes, removeTokenScopesByName, loadTokenScopes, } from './db.js';
15
+ /**
16
+ * Check if a secret path matches any of the allowed prefix scopes.
17
+ *
18
+ * @param secretPath - The secret path being accessed (e.g., "levelfit/stripe/key")
19
+ * @param scopes - Array of allowed prefixes (e.g., ["levelfit/"])
20
+ * @returns true if the path matches at least one scope
21
+ */
22
+ export function matchesPrefix(secretPath, scopes) {
23
+ if (scopes.length === 0)
24
+ return false;
25
+ return scopes.some((scope) => secretPath.startsWith(scope));
26
+ }
27
+ /**
28
+ * Check if a token (by raw value) is allowed to access a given path.
29
+ *
30
+ * @param rawToken - The raw bearer token
31
+ * @param secretPath - The secret path being accessed
32
+ * @param scopesPath - Optional path to the scopes file
33
+ * @returns ScopeCheckResult with allowed/denied and reason
34
+ */
35
+ export function checkTokenScope(rawToken, secretPath, scopesPath) {
36
+ const tokenHash = hashToken(rawToken);
37
+ const scopes = getTokenScopes(tokenHash, scopesPath);
38
+ // No scope entry = full access (backward compat)
39
+ if (scopes === null) {
40
+ return { allowed: true };
41
+ }
42
+ // Empty scopes = no access
43
+ if (scopes.length === 0) {
44
+ return { allowed: false, reason: 'Token has no prefix scopes configured' };
45
+ }
46
+ // Check if path matches any scope
47
+ if (matchesPrefix(secretPath, scopes)) {
48
+ return { allowed: true };
49
+ }
50
+ return {
51
+ allowed: false,
52
+ reason: `Access denied: path '${secretPath}' is outside token's allowed prefixes: ${scopes.map((s) => `${s}*`).join(', ')}`,
53
+ };
54
+ }
55
+ /**
56
+ * Filter a list of secret paths to only those matching a token's scopes.
57
+ *
58
+ * @param rawToken - The raw bearer token
59
+ * @param paths - List of secret paths to filter
60
+ * @param scopesPath - Optional path to the scopes file
61
+ * @returns Filtered paths that the token can access
62
+ */
63
+ export function filterByTokenScope(rawToken, paths, scopesPath) {
64
+ const tokenHash = hashToken(rawToken);
65
+ const scopes = getTokenScopes(tokenHash, scopesPath);
66
+ // No scope entry = full access
67
+ if (scopes === null) {
68
+ return paths;
69
+ }
70
+ // Filter to matching paths
71
+ return paths.filter((p) => matchesPrefix(p, scopes));
72
+ }
73
+ /**
74
+ * Store scopes for a newly created token.
75
+ *
76
+ * @param rawToken - The raw token value (will be hashed for storage)
77
+ * @param name - The token name
78
+ * @param prefixes - Array of prefix scopes, or null for full access
79
+ * @param scopesPath - Optional path to the scopes file
80
+ */
81
+ export function storeTokenScopes(rawToken, name, prefixes, scopesPath) {
82
+ const tokenHash = hashToken(rawToken);
83
+ setTokenScopes(tokenHash, name, prefixes, scopesPath);
84
+ }
85
+ /**
86
+ * Remove scopes when a token is revoked.
87
+ */
88
+ export function revokeTokenScopes(tokenName, scopesPath) {
89
+ return removeTokenScopesByName(tokenName, scopesPath);
90
+ }
91
+ /**
92
+ * Get scope display string for a token.
93
+ * Examples: "levelfit/*", "hq-cloud/* + synesis/*", "*" (full access)
94
+ */
95
+ export function formatScopes(scopes) {
96
+ if (scopes === null)
97
+ return '*';
98
+ if (scopes.length === 0)
99
+ return '(none)';
100
+ return scopes.map((s) => `${s}*`).join(' + ');
101
+ }
102
+ /**
103
+ * Build a scope map from the store keyed by token name.
104
+ * Used by the CLI to display scopes alongside token metadata.
105
+ */
106
+ export function buildScopesByName(scopesPath) {
107
+ const store = loadTokenScopes(scopesPath);
108
+ const map = new Map();
109
+ for (const entry of Object.values(store)) {
110
+ map.set(entry.name, entry.scopes);
111
+ }
112
+ return map;
113
+ }
114
+ /**
115
+ * Intersect a query prefix with token scopes.
116
+ * Used for LIST operations: if the user requests prefix "levelfit/"
117
+ * and the token has scope ["levelfit/"], return "levelfit/".
118
+ * If the token has scope ["synesis/"], return null (no intersection).
119
+ *
120
+ * @param queryPrefix - The prefix from the list query (may be undefined for all)
121
+ * @param scopes - The token's allowed prefixes
122
+ * @returns The effective prefix to query, or null if no intersection
123
+ */
124
+ export function intersectPrefixWithScopes(queryPrefix, scopes) {
125
+ // No scopes = full access
126
+ if (scopes === null) {
127
+ return { effectivePrefixes: null, restricted: false };
128
+ }
129
+ // No query prefix = return all scope prefixes
130
+ if (!queryPrefix) {
131
+ return { effectivePrefixes: scopes, restricted: true };
132
+ }
133
+ // Find scopes that overlap with the query prefix
134
+ const matching = scopes.filter((scope) => {
135
+ // Query is more specific than scope: "levelfit/stripe/" starts with "levelfit/"
136
+ if (queryPrefix.startsWith(scope))
137
+ return true;
138
+ // Scope is more specific than query: "levelfit/" starts with "levelfit"
139
+ if (scope.startsWith(queryPrefix))
140
+ return true;
141
+ return false;
142
+ });
143
+ if (matching.length === 0) {
144
+ return { effectivePrefixes: [], restricted: true };
145
+ }
146
+ // Use the more specific prefix in each case
147
+ const effective = matching.map((scope) => queryPrefix.startsWith(scope) ? queryPrefix : scope);
148
+ return { effectivePrefixes: effective, restricted: true };
149
+ }
150
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "unotoken",
3
+ "version": "0.1.0",
4
+ "description": "unotoken — yokotoken + cloud features (OAuth unlock, team vaults, key escrow, device signatures)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "unotoken": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist/",
13
+ "README.md"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./sdk": {
21
+ "types": "./dist/sdk.d.ts",
22
+ "import": "./dist/sdk.js"
23
+ },
24
+ "./client": {
25
+ "types": "./dist/client.d.ts",
26
+ "import": "./dist/client.js"
27
+ }
28
+ },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "typecheck": "tsc --noEmit"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/indigoai-us/unotoken.git"
38
+ },
39
+ "license": "MIT",
40
+ "keywords": [
41
+ "vault",
42
+ "secrets",
43
+ "encryption",
44
+ "oauth",
45
+ "cloud",
46
+ "agent",
47
+ "ai"
48
+ ],
49
+ "author": "Indigo AI <engineering@getindigo.ai> (https://getindigo.ai)",
50
+ "dependencies": {
51
+ "commander": "^14.0.3",
52
+ "yokotoken": "^0.1.1"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.13.4",
56
+ "typescript": "^5.7.3",
57
+ "vitest": "^3.0.5"
58
+ },
59
+ "engines": {
60
+ "node": ">=20.0.0"
61
+ }
62
+ }