anyray-connect 0.2.0 → 0.5.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/README.md +5 -0
- package/dist/cli.js +242 -65
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.js +86 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/hook.js +233 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/shared.js +83 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/status.js +71 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.js +87 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/target.js +11 -1
- package/dist/target.js.map +1 -1
- package/dist/tools/claudeCode.js +88 -2
- package/dist/tools/claudeCode.js.map +1 -1
- package/dist/tools/codex.js +3 -3
- package/dist/tools/codex.js.map +1 -1
- package/dist/tools/shellEnv.js +40 -8
- package/dist/tools/shellEnv.js.map +1 -1
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/dist/util/persist.js +32 -7
- package/dist/util/persist.js.map +1 -1
- package/dist/util/policySync.js +143 -0
- package/dist/util/policySync.js.map +1 -0
- package/dist/util/savings.js +71 -0
- package/dist/util/savings.js.map +1 -0
- package/dist/util/seatState.js +79 -0
- package/dist/util/seatState.js.map +1 -0
- package/dist/util/setupLink.js +5 -1
- package/dist/util/setupLink.js.map +1 -1
- package/dist/util/trimOutput.js +120 -0
- package/dist/util/trimOutput.js.map +1 -0
- package/dist/util/verify.js +192 -0
- package/dist/util/verify.js.map +1 -0
- package/package.json +1 -1
package/dist/tools/claudeCode.js
CHANGED
|
@@ -28,13 +28,69 @@
|
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
29
|
import { join } from 'node:path';
|
|
30
30
|
import { readJson, writeJson, fileExists } from '../util/jsonFile.js';
|
|
31
|
-
import { metadataHeaderValue } from '../types.js';
|
|
31
|
+
import { effectiveSubscription, metadataHeaderValue } from '../types.js';
|
|
32
32
|
const settingsPath = () => join(homedir(), '.claude', 'settings.json');
|
|
33
33
|
const OWNED_KEYS = [
|
|
34
34
|
'ANTHROPIC_BASE_URL',
|
|
35
35
|
'ANTHROPIC_AUTH_TOKEN',
|
|
36
36
|
'ANTHROPIC_CUSTOM_HEADERS',
|
|
37
37
|
];
|
|
38
|
+
/** Unique flag that identifies the Anyray PostToolUse hook command (so re-apply
|
|
39
|
+
* and revert can find/replace exactly our entry, never the user's own hooks). */
|
|
40
|
+
const HOOK_FLAG = '__anyray-hook';
|
|
41
|
+
/**
|
|
42
|
+
* The command Claude Code runs for the PostToolUse hook: re-invoke this very
|
|
43
|
+
* `anyray-connect` in hidden hook mode. The standalone (Bun-compiled) binary —
|
|
44
|
+
* the zero-install distribution — IS `process.execPath`, so we invoke it
|
|
45
|
+
* directly; under Node/npx (`node dist/index.js`) we re-invoke the entry script
|
|
46
|
+
* under node. Quoted for paths with spaces.
|
|
47
|
+
*/
|
|
48
|
+
const hookCommand = () => {
|
|
49
|
+
const quote = (s) => (/\s/.test(s) ? JSON.stringify(s) : s);
|
|
50
|
+
const exe = process.execPath;
|
|
51
|
+
const runsUnderNode = /[\\/](node|node\.exe)$/i.test(exe);
|
|
52
|
+
const script = process.argv[1];
|
|
53
|
+
const base = runsUnderNode && script ? `${quote(exe)} ${quote(script)}` : quote(exe);
|
|
54
|
+
return `${base} ${HOOK_FLAG}`;
|
|
55
|
+
};
|
|
56
|
+
const entryIsOurs = (entry) => {
|
|
57
|
+
const e = entry;
|
|
58
|
+
return (Array.isArray(e?.hooks) &&
|
|
59
|
+
e.hooks.some((h) => typeof h?.command === 'string' && h.command.includes(HOOK_FLAG)));
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Install (idempotently) our PostToolUse hook into the settings object. Omitting
|
|
63
|
+
* `matcher` matches every tool; a size gate in the hook itself means small
|
|
64
|
+
* outputs pass through untouched. Drops any prior Anyray entry first so a
|
|
65
|
+
* re-apply (or a changed binary path) replaces rather than duplicates it.
|
|
66
|
+
*/
|
|
67
|
+
const installHook = (settings) => {
|
|
68
|
+
const hooks = settings.hooks ?? {};
|
|
69
|
+
const list = Array.isArray(hooks.PostToolUse)
|
|
70
|
+
? hooks.PostToolUse
|
|
71
|
+
: [];
|
|
72
|
+
const cleaned = list.filter((e) => !entryIsOurs(e));
|
|
73
|
+
cleaned.push({ hooks: [{ type: 'command', command: hookCommand() }] });
|
|
74
|
+
hooks.PostToolUse = cleaned;
|
|
75
|
+
settings.hooks = hooks;
|
|
76
|
+
};
|
|
77
|
+
/** Remove our PostToolUse hook, pruning empty containers. Returns true if found. */
|
|
78
|
+
const removeHook = (settings) => {
|
|
79
|
+
const hooks = settings.hooks;
|
|
80
|
+
if (!hooks || !Array.isArray(hooks.PostToolUse))
|
|
81
|
+
return false;
|
|
82
|
+
const list = hooks.PostToolUse;
|
|
83
|
+
const cleaned = list.filter((e) => !entryIsOurs(e));
|
|
84
|
+
if (cleaned.length === list.length)
|
|
85
|
+
return false;
|
|
86
|
+
if (cleaned.length === 0)
|
|
87
|
+
delete hooks.PostToolUse;
|
|
88
|
+
else
|
|
89
|
+
hooks.PostToolUse = cleaned;
|
|
90
|
+
if (Object.keys(hooks).length === 0)
|
|
91
|
+
delete settings.hooks;
|
|
92
|
+
return true;
|
|
93
|
+
};
|
|
38
94
|
const customHeaders = (meta, subscription, clientKey) => {
|
|
39
95
|
// No provider pin in placeholder mode: the gateway's routing config owns
|
|
40
96
|
// that decision (see the module comment).
|
|
@@ -68,7 +124,9 @@ export const claudeCode = {
|
|
|
68
124
|
},
|
|
69
125
|
async apply(target, opts) {
|
|
70
126
|
const path = settingsPath();
|
|
71
|
-
|
|
127
|
+
// Anthropic family: subscription when forced, or (in auto mode) when a
|
|
128
|
+
// Claude seat was detected on this machine; else the org key.
|
|
129
|
+
const subscription = effectiveSubscription(target, 'anthropic');
|
|
72
130
|
const meta = metadataHeaderValue(target.metadata);
|
|
73
131
|
const headers = customHeaders(meta, subscription, target.clientKey);
|
|
74
132
|
const env = {
|
|
@@ -113,9 +171,32 @@ export const claudeCode = {
|
|
|
113
171
|
}
|
|
114
172
|
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
115
173
|
}
|
|
174
|
+
// Install the cache-safe PostToolUse hook: it trims oversized tool outputs
|
|
175
|
+
// locally (before they enter the cached prefix) and beacons content-free
|
|
176
|
+
// savings to the gateway. Applies in both auth modes — trimming helps
|
|
177
|
+
// subscription seats (usage caps/overage) and org-key billing alike.
|
|
178
|
+
installHook(settings);
|
|
179
|
+
messages.push('Installed a PostToolUse hook: trims oversized tool outputs locally (cache-safe) and reports content-free savings.');
|
|
116
180
|
await writeJson(path, settings);
|
|
117
181
|
return { status: 'configured', messages, changedFiles: [path] };
|
|
118
182
|
},
|
|
183
|
+
skillsDir() {
|
|
184
|
+
return join(homedir(), '.claude', 'skills');
|
|
185
|
+
},
|
|
186
|
+
async pointsAt(origin) {
|
|
187
|
+
const path = settingsPath();
|
|
188
|
+
if (!(await fileExists(path)))
|
|
189
|
+
return false;
|
|
190
|
+
try {
|
|
191
|
+
const settings = await readJson(path);
|
|
192
|
+
const env = settings.env;
|
|
193
|
+
const base = env?.ANTHROPIC_BASE_URL;
|
|
194
|
+
return typeof base === 'string' && base.replace(/\/+$/, '') === origin;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
},
|
|
119
200
|
async revert(opts) {
|
|
120
201
|
const path = settingsPath();
|
|
121
202
|
if (!(await fileExists(path))) {
|
|
@@ -129,14 +210,19 @@ export const claudeCode = {
|
|
|
129
210
|
}
|
|
130
211
|
const settings = await readJson(path);
|
|
131
212
|
const env = settings.env;
|
|
213
|
+
const hookRemoved = removeHook(settings);
|
|
132
214
|
if (env) {
|
|
133
215
|
for (const key of OWNED_KEYS)
|
|
134
216
|
delete env[key];
|
|
135
217
|
if (Object.keys(env).length === 0)
|
|
136
218
|
delete settings.env;
|
|
219
|
+
}
|
|
220
|
+
if (env || hookRemoved) {
|
|
137
221
|
await writeJson(path, settings);
|
|
138
222
|
}
|
|
139
223
|
const messages = [`removed Anyray keys from ${path}`];
|
|
224
|
+
if (hookRemoved)
|
|
225
|
+
messages.push('Removed the Anyray PostToolUse hook.');
|
|
140
226
|
// Revert only deletes our keys; if apply overwrote a pre-existing value
|
|
141
227
|
// (e.g. you already set ANTHROPIC_BASE_URL), point at the backup.
|
|
142
228
|
if (await fileExists(`${path}.anyray-bak`)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claudeCode.js","sourceRoot":"","sources":["../../src/tools/claudeCode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"claudeCode.js","sourceRoot":"","sources":["../../src/tools/claudeCode.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/codex.js
CHANGED
|
@@ -40,7 +40,7 @@ import { join } from 'node:path';
|
|
|
40
40
|
import { writeFile, rm } from 'node:fs/promises';
|
|
41
41
|
import { fileExists } from '../util/jsonFile.js';
|
|
42
42
|
import { removeProfileBlock } from '../util/profile.js';
|
|
43
|
-
import { metadataHeaderValue } from '../types.js';
|
|
43
|
+
import { effectiveSubscription, metadataHeaderValue } from '../types.js';
|
|
44
44
|
/** Model Codex sends when ANYRAY_CODEX_MODEL is unset; the gateway's default
|
|
45
45
|
* routing decides the real model/provider, so this is just a required token. */
|
|
46
46
|
const DEFAULT_MODEL = 'anyray-default';
|
|
@@ -92,7 +92,7 @@ const resolveModel = (subscription) => {
|
|
|
92
92
|
/** Full contents of the standalone profile file (flat top-level keys). */
|
|
93
93
|
const profileFile = (target) => {
|
|
94
94
|
const meta = codexMetadata(target);
|
|
95
|
-
const subscription = target
|
|
95
|
+
const subscription = effectiveSubscription(target, 'openai');
|
|
96
96
|
// Subscription traffic is ChatGPT-backend-only — the provider pin is not
|
|
97
97
|
// the operator's call there, so ANYRAY_CODEX_PROVIDER applies only to
|
|
98
98
|
// placeholder mode.
|
|
@@ -152,7 +152,7 @@ export const codex = {
|
|
|
152
152
|
async apply(target, opts) {
|
|
153
153
|
const path = profilePath();
|
|
154
154
|
const meta = codexMetadata(target);
|
|
155
|
-
const subscription = target
|
|
155
|
+
const subscription = effectiveSubscription(target, 'openai');
|
|
156
156
|
const model = resolveModel(subscription);
|
|
157
157
|
const messages = [
|
|
158
158
|
`${path} ← standalone Codex profile (model_provider = anyray)`,
|
package/dist/tools/codex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/tools/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAQxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/tools/codex.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/shellEnv.js
CHANGED
|
@@ -7,20 +7,34 @@
|
|
|
7
7
|
* headers, so per-request `x-anyray-metadata` attribution isn't set here —
|
|
8
8
|
* attribution for these falls back to the gateway's defaults. Tools that DO
|
|
9
9
|
* support headers (Claude Code) have their own adapter.
|
|
10
|
+
*
|
|
11
|
+
* SEATS — export keys PER FAMILY: a placeholder `OPENAI_API_KEY` /
|
|
12
|
+
* `ANTHROPIC_AUTH_TOKEN` exported here is not just useless against a seat, it is
|
|
13
|
+
* actively harmful — the seat-based tools (Claude Code, Codex) read these
|
|
14
|
+
* process-env vars with HIGHER precedence than their own config, so the
|
|
15
|
+
* placeholder silently overrides the seat sign-in and forces the request off the
|
|
16
|
+
* subscription onto the org's default provider. So we export a family's key only
|
|
17
|
+
* when that family is NOT on a seat (resolved via `effectiveSubscription`). Base
|
|
18
|
+
* URLs are always safe (they only redirect to the gateway; a seat tool still
|
|
19
|
+
* supplies its own token). Raw SDK/script callers on a seat must bring their own
|
|
20
|
+
* credential for that family.
|
|
10
21
|
*/
|
|
11
22
|
import { detectProfilePath, removeProfileBlock, upsertProfileBlock } from '../util/profile.js';
|
|
12
23
|
import { fileExists } from '../util/jsonFile.js';
|
|
13
24
|
import { maskKey } from '../util/redeem.js';
|
|
25
|
+
import { effectiveSubscription } from '../types.js';
|
|
14
26
|
// Single-quote the values: shells treat single-quoted strings literally, so a
|
|
15
27
|
// `$`, backtick, or double-quote in a URL/key can't trigger expansion or break
|
|
16
28
|
// out of the assignment. (Single quotes themselves can't appear in these.)
|
|
17
29
|
// The exported key is the dev's personal gateway key (from an invite) when
|
|
18
|
-
// present, else the shared placeholder.
|
|
19
|
-
|
|
30
|
+
// present, else the shared placeholder. A family on a seat exports its base URL
|
|
31
|
+
// only and NO key (see the module comment — a placeholder token here hijacks
|
|
32
|
+
// that tool's own sign-in).
|
|
33
|
+
const envLines = (target, key, openaiSeat, anthropicSeat) => [
|
|
20
34
|
`export OPENAI_BASE_URL='${target.openaiBaseUrl}'`,
|
|
21
|
-
`export OPENAI_API_KEY='${key}'
|
|
35
|
+
...(openaiSeat ? [] : [`export OPENAI_API_KEY='${key}'`]),
|
|
22
36
|
`export ANTHROPIC_BASE_URL='${target.anthropicBaseUrl}'`,
|
|
23
|
-
`export ANTHROPIC_AUTH_TOKEN='${key}'
|
|
37
|
+
...(anthropicSeat ? [] : [`export ANTHROPIC_AUTH_TOKEN='${key}'`]),
|
|
24
38
|
];
|
|
25
39
|
export const shellEnv = {
|
|
26
40
|
id: 'shell-env',
|
|
@@ -35,19 +49,37 @@ export const shellEnv = {
|
|
|
35
49
|
},
|
|
36
50
|
async apply(target, opts) {
|
|
37
51
|
const path = detectProfilePath();
|
|
52
|
+
// Per family: a detected/forced seat means the tool owns its own auth, so we
|
|
53
|
+
// must NOT export that family's key (it would override the seat sign-in).
|
|
54
|
+
const openaiSeat = effectiveSubscription(target, 'openai');
|
|
55
|
+
const anthropicSeat = effectiveSubscription(target, 'anthropic');
|
|
56
|
+
const anySeat = openaiSeat || anthropicSeat;
|
|
38
57
|
const key = target.clientKey ?? target.placeholderKey;
|
|
39
|
-
const lines = envLines(target, key);
|
|
58
|
+
const lines = envLines(target, key, openaiSeat, anthropicSeat);
|
|
40
59
|
// Console output masks a personal key (the file gets the real one) — a
|
|
41
60
|
// terminal/scrollback should never show the full credential.
|
|
42
61
|
const shownLines = target.clientKey
|
|
43
|
-
? envLines(target, maskKey(target.clientKey))
|
|
62
|
+
? envLines(target, maskKey(target.clientKey), openaiSeat, anthropicSeat)
|
|
44
63
|
: lines;
|
|
64
|
+
const seatLabel = openaiSeat && anthropicSeat
|
|
65
|
+
? 'either family'
|
|
66
|
+
: openaiSeat
|
|
67
|
+
? 'OpenAI'
|
|
68
|
+
: 'Anthropic';
|
|
45
69
|
const messages = [
|
|
46
70
|
`${path} ← managed Anyray block`,
|
|
47
71
|
...shownLines.map((l) => ` ${l}`),
|
|
48
72
|
`Run \`source ${path}\` or open a new terminal to apply.`,
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
...(anySeat
|
|
74
|
+
? [
|
|
75
|
+
`No auth token exported for ${seatLabel} — your seat-based tool keeps`,
|
|
76
|
+
'its own sign-in (a placeholder here would override it). Raw SDK/script',
|
|
77
|
+
'calls on a seat need your own key.',
|
|
78
|
+
]
|
|
79
|
+
: [
|
|
80
|
+
'Note: standard SDK env vars carry no per-user header, so attribution',
|
|
81
|
+
'for these falls back to the gateway default.',
|
|
82
|
+
]),
|
|
51
83
|
];
|
|
52
84
|
if (opts.dryRun)
|
|
53
85
|
return { status: 'configured', messages };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellEnv.js","sourceRoot":"","sources":["../../src/tools/shellEnv.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"shellEnv.js","sourceRoot":"","sources":["../../src/tools/shellEnv.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -12,6 +12,19 @@
|
|
|
12
12
|
* grouping only (user/team/intent/session), matching the gateway's
|
|
13
13
|
* `x-anyray-metadata` contract.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Resolve whether a given provider family should use subscription pass-through
|
|
17
|
+
* for this target. The single place that interprets `authMode` so adapters
|
|
18
|
+
* don't each re-derive it: forced modes win; `auto` defers to the detected
|
|
19
|
+
* `seat` state (absent → org key, the safe fallback).
|
|
20
|
+
*/
|
|
21
|
+
export const effectiveSubscription = (target, family) => {
|
|
22
|
+
if (target.authMode === 'subscription')
|
|
23
|
+
return true;
|
|
24
|
+
if (target.authMode === 'placeholder')
|
|
25
|
+
return false;
|
|
26
|
+
return target.seat?.[family] ?? false;
|
|
27
|
+
};
|
|
15
28
|
/** Build the `x-anyray-metadata` header value, or undefined when empty. */
|
|
16
29
|
export const metadataHeaderValue = (metadata) => {
|
|
17
30
|
const entries = Object.entries(metadata).filter(([, v]) => v);
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqFH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAAqB,EACrB,MAAuB,EACd,EAAE;IACX,IAAI,MAAM,CAAC,QAAQ,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,MAAM,CAAC,QAAQ,KAAK,aAAa;QAAE,OAAO,KAAK,CAAC;IACpD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;AACxC,CAAC,CAAC;AAoDF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,QAA6B,EACT,EAAE;IACtB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACrD,CAAC,CAAC"}
|
package/dist/util/persist.js
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
|
-
import { writeFile, readFile, chmod } from 'node:fs/promises';
|
|
1
|
+
import { writeFile, readFile, chmod, stat } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Resolved per-call (not cached) so a changed $HOME is always honored.
|
|
5
|
+
const stateDir = () => join(homedir(), '.anyray');
|
|
6
|
+
/**
|
|
7
|
+
* Where the connect profile lives.
|
|
8
|
+
*
|
|
9
|
+
* Historically this is the file `~/.anyray`. But a locally-run Anyray gateway
|
|
10
|
+
* uses `~/.anyray/` as its DATA DIRECTORY (audit logs etc.), so on a machine
|
|
11
|
+
* that also runs the gateway, `~/.anyray` is a directory and writing it as a
|
|
12
|
+
* file fails with EISDIR. Resolve the collision: when `~/.anyray` is a
|
|
13
|
+
* directory, store the profile as a file inside it; otherwise use the simple
|
|
14
|
+
* `~/.anyray` file. The two states are mutually exclusive (a path is a file or
|
|
15
|
+
* a directory, never both), so no migration is needed.
|
|
16
|
+
*/
|
|
17
|
+
const profilePath = async () => {
|
|
18
|
+
const dir = stateDir();
|
|
19
|
+
try {
|
|
20
|
+
if ((await stat(dir)).isDirectory()) {
|
|
21
|
+
return join(dir, 'connect.json');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* missing → use the simple ~/.anyray file path */
|
|
26
|
+
}
|
|
27
|
+
return dir;
|
|
28
|
+
};
|
|
29
|
+
/** Read the connect profile; returns {} on missing or corrupt file. */
|
|
6
30
|
export const loadProfile = async () => {
|
|
7
31
|
try {
|
|
8
|
-
const raw = await readFile(profilePath(), 'utf-8');
|
|
32
|
+
const raw = await readFile(await profilePath(), 'utf-8');
|
|
9
33
|
const parsed = JSON.parse(raw);
|
|
10
34
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
11
35
|
return {};
|
|
@@ -15,13 +39,14 @@ export const loadProfile = async () => {
|
|
|
15
39
|
return {};
|
|
16
40
|
}
|
|
17
41
|
};
|
|
18
|
-
/** Write
|
|
42
|
+
/** Write the connect profile (mode 600) — replaced, not merged, so cleared values stay cleared. */
|
|
19
43
|
export const saveProfile = async (profile) => {
|
|
20
44
|
const clean = Object.fromEntries(Object.entries(profile).filter(([, v]) => v !== undefined));
|
|
21
|
-
await
|
|
45
|
+
const path = await profilePath();
|
|
46
|
+
await writeFile(path, `${JSON.stringify(clean, null, 2)}\n`, {
|
|
22
47
|
mode: 0o600,
|
|
23
48
|
});
|
|
24
49
|
// mode only applies on creation — tighten a pre-existing file too.
|
|
25
|
-
await chmod(
|
|
50
|
+
await chmod(path, 0o600);
|
|
26
51
|
};
|
|
27
52
|
//# sourceMappingURL=persist.js.map
|
package/dist/util/persist.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/util/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/util/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA0BjC,uEAAuE;AACvE,MAAM,QAAQ,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,GAAG,KAAK,IAAqB,EAAE;IAC9C,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAA4B,EAAE;IAC5D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtF,OAAO,MAAuB,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,OAAsB,EAAiB,EAAE;IACzE,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAC9B,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAC1C,CAAC;IACnB,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;IACjC,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAC3D,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IACH,mEAAmE;IACnE,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team skills/policy sync (client side).
|
|
3
|
+
*
|
|
4
|
+
* `anyray-connect sync` pulls the org's shared skills/instructions from the
|
|
5
|
+
* gateway and writes them as `SKILL.md` files into each supported coding tool's
|
|
6
|
+
* skills directory (e.g. Claude Code `~/.claude/skills/<id>/SKILL.md`). This is
|
|
7
|
+
* content-free TEAM POLICY text (instructions the org wants its tools to
|
|
8
|
+
* follow) — it is NOT user prompt/response content, so writing it to disk is
|
|
9
|
+
* fine and does not touch the privacy invariant.
|
|
10
|
+
*
|
|
11
|
+
* Contract (gateway-side): `GET <gatewayUrl>/connect/policy` with header
|
|
12
|
+
* `x-anyray-api-key: <personal key or placeholder>` → 200
|
|
13
|
+
* { "skills": [ { "id", "name", "version", "body" } ], "updatedAt": "ISO" }
|
|
14
|
+
*
|
|
15
|
+
* On a non-200 or a network error we fail gracefully with a clear message
|
|
16
|
+
* (never crash). The last-synced version per skill is tracked in ~/.anyray so
|
|
17
|
+
* re-runs only rewrite files whose `version` changed.
|
|
18
|
+
*/
|
|
19
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
const POLICY_TIMEOUT_MS = 10_000;
|
|
22
|
+
const optionalString = (value) => typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
23
|
+
/**
|
|
24
|
+
* A skill `id` becomes a directory name (`<skillsDir>/<id>/SKILL.md`). The
|
|
25
|
+
* gateway only validates it as non-empty, so a malicious or mistyped id like
|
|
26
|
+
* `../../evil` would escape the skills dir and write SKILL.md anywhere. Restrict
|
|
27
|
+
* it to a single safe path segment: a leading alphanumeric, then alphanumerics,
|
|
28
|
+
* `.`, `_`, `-` — which excludes `/`, `\`, `..`, and absolute paths.
|
|
29
|
+
*/
|
|
30
|
+
const isSafeSkillId = (id) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(id) && id !== '.' && id !== '..';
|
|
31
|
+
/**
|
|
32
|
+
* Fetch the team policy from the gateway. Throws an Error with a clear,
|
|
33
|
+
* content-free message on non-200 or network failure — callers catch it and
|
|
34
|
+
* print it (no crash).
|
|
35
|
+
*/
|
|
36
|
+
export const fetchTeamPolicy = async (gatewayUrl, apiKey, opts = {}) => {
|
|
37
|
+
const timeoutMs = opts.timeoutMs ?? POLICY_TIMEOUT_MS;
|
|
38
|
+
const url = `${gatewayUrl}/connect/policy`;
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await fetch(url, {
|
|
44
|
+
method: 'GET',
|
|
45
|
+
headers: { 'x-anyray-api-key': apiKey },
|
|
46
|
+
signal: controller.signal,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const reason = error instanceof Error && error.name === 'AbortError'
|
|
51
|
+
? `no response within ${timeoutMs}ms`
|
|
52
|
+
: error instanceof Error
|
|
53
|
+
? error.message
|
|
54
|
+
: String(error);
|
|
55
|
+
throw new Error(`could not reach the gateway at ${url}: ${reason}`);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
}
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
throw new Error(`team policy fetch failed: the gateway responded ${res.status} at ${url}`);
|
|
62
|
+
}
|
|
63
|
+
let body;
|
|
64
|
+
try {
|
|
65
|
+
body = (await res.json());
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error('team policy fetch failed: the gateway returned a malformed response');
|
|
69
|
+
}
|
|
70
|
+
const rawSkills = Array.isArray(body.skills) ? body.skills : [];
|
|
71
|
+
const skills = [];
|
|
72
|
+
for (const raw of rawSkills) {
|
|
73
|
+
const entry = raw;
|
|
74
|
+
const id = optionalString(entry?.id);
|
|
75
|
+
const version = optionalString(entry?.version);
|
|
76
|
+
// Skip entries that can't be keyed/diffed; an id+version are required for
|
|
77
|
+
// the SKILL.md path and the version diff. Reject ids that aren't a safe path
|
|
78
|
+
// segment — they'd let a hostile policy write outside the skills dir.
|
|
79
|
+
if (!id || !version || !isSafeSkillId(id))
|
|
80
|
+
continue;
|
|
81
|
+
skills.push({
|
|
82
|
+
id,
|
|
83
|
+
name: optionalString(entry?.name) ?? id,
|
|
84
|
+
version,
|
|
85
|
+
body: typeof entry?.body === 'string' ? entry.body : '',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { skills, updatedAt: optionalString(body.updatedAt) };
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Diff the fetched policy against the previously synced versions and write
|
|
92
|
+
* `SKILL.md` for every skill whose version changed into each provided skills
|
|
93
|
+
* dir. Returns a content-free summary plus the new version baseline to persist.
|
|
94
|
+
*
|
|
95
|
+
* Pure I/O over the inputs — fetching and persistence are the caller's job, so
|
|
96
|
+
* this stays unit-testable with a real temp dir and no network.
|
|
97
|
+
*/
|
|
98
|
+
export const syncPolicyToDirs = async (policy, skillsDirs, previousVersions) => {
|
|
99
|
+
const results = [];
|
|
100
|
+
const versions = {};
|
|
101
|
+
let changed = 0;
|
|
102
|
+
let unchanged = 0;
|
|
103
|
+
for (const skill of policy.skills) {
|
|
104
|
+
// Defense-in-depth: never turn an unsafe id into a write path, even if a
|
|
105
|
+
// caller passed skills that didn't go through fetchTeamPolicy's filter.
|
|
106
|
+
if (!isSafeSkillId(skill.id))
|
|
107
|
+
continue;
|
|
108
|
+
versions[skill.id] = skill.version;
|
|
109
|
+
const isUpToDate = previousVersions[skill.id] === skill.version;
|
|
110
|
+
if (isUpToDate) {
|
|
111
|
+
unchanged += 1;
|
|
112
|
+
results.push({
|
|
113
|
+
id: skill.id,
|
|
114
|
+
name: skill.name,
|
|
115
|
+
version: skill.version,
|
|
116
|
+
written: false,
|
|
117
|
+
files: [],
|
|
118
|
+
});
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const files = [];
|
|
122
|
+
for (const dir of skillsDirs) {
|
|
123
|
+
const skillDir = join(dir, skill.id);
|
|
124
|
+
const file = join(skillDir, 'SKILL.md');
|
|
125
|
+
await mkdir(skillDir, { recursive: true });
|
|
126
|
+
// Skill bodies are team policy text (content-free); persisting them is
|
|
127
|
+
// intended. A trailing newline keeps the files diff-friendly.
|
|
128
|
+
await writeFile(file, ensureTrailingNewline(skill.body), 'utf-8');
|
|
129
|
+
files.push(file);
|
|
130
|
+
}
|
|
131
|
+
changed += 1;
|
|
132
|
+
results.push({
|
|
133
|
+
id: skill.id,
|
|
134
|
+
name: skill.name,
|
|
135
|
+
version: skill.version,
|
|
136
|
+
written: true,
|
|
137
|
+
files,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return { results, changed, unchanged, versions, updatedAt: policy.updatedAt };
|
|
141
|
+
};
|
|
142
|
+
const ensureTrailingNewline = (body) => body.endsWith('\n') ? body : `${body}\n`;
|
|
143
|
+
//# sourceMappingURL=policySync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policySync.js","sourceRoot":"","sources":["../../src/util/policySync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Personal savings readout — the tangible number that ends onboarding.
|
|
3
|
+
*
|
|
4
|
+
* Contract (gateway-side): `GET <origin>/connect/savings` with header
|
|
5
|
+
* `x-anyray-api-key: <personal key or placeholder>` → 200
|
|
6
|
+
* { "tokensSaved": number, "savingsUsd": number,
|
|
7
|
+
* "requestCount": number, "windowDays": number }
|
|
8
|
+
*
|
|
9
|
+
* Best-effort: an older gateway returns 404 (or the network fails); callers
|
|
10
|
+
* silently skip the savings line — it must NEVER break the apply flow. Brand-new
|
|
11
|
+
* users see all-zero numbers, which we render as an encouraging "start sending
|
|
12
|
+
* traffic" line rather than "$0.00".
|
|
13
|
+
*
|
|
14
|
+
* PRIVACY: the endpoint returns content-free aggregates only (token counts, a
|
|
15
|
+
* dollar figure, a request count). Nothing here touches prompt/response content.
|
|
16
|
+
*/
|
|
17
|
+
const SAVINGS_TIMEOUT_MS = 5000;
|
|
18
|
+
const finiteNumber = (value) => typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the dev's content-free savings, or `undefined` on any error / non-200
|
|
21
|
+
* (older gateway, network hiccup). Never throws — onboarding must not break.
|
|
22
|
+
*/
|
|
23
|
+
export const fetchSavings = async (origin, apiKey, opts = {}) => {
|
|
24
|
+
const timeoutMs = opts.timeoutMs ?? SAVINGS_TIMEOUT_MS;
|
|
25
|
+
const url = `${origin}/connect/savings`;
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
28
|
+
try {
|
|
29
|
+
const res = await fetch(url, {
|
|
30
|
+
method: 'GET',
|
|
31
|
+
headers: { 'x-anyray-api-key': apiKey },
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok)
|
|
35
|
+
return undefined;
|
|
36
|
+
const body = (await res.json());
|
|
37
|
+
return {
|
|
38
|
+
tokensSaved: finiteNumber(body.tokensSaved),
|
|
39
|
+
savingsUsd: finiteNumber(body.savingsUsd),
|
|
40
|
+
requestCount: finiteNumber(body.requestCount),
|
|
41
|
+
windowDays: finiteNumber(body.windowDays),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/** Round to whole tokens and group with thousands separators. */
|
|
52
|
+
const formatTokens = (tokens) => Math.round(tokens).toLocaleString('en-US');
|
|
53
|
+
/** Two-decimal USD, e.g. `$12.30`. */
|
|
54
|
+
const formatUsd = (usd) => `$${usd.toLocaleString('en-US', {
|
|
55
|
+
minimumFractionDigits: 2,
|
|
56
|
+
maximumFractionDigits: 2,
|
|
57
|
+
})}`;
|
|
58
|
+
/**
|
|
59
|
+
* Render the savings line for onboarding/status. Returns the encouraging
|
|
60
|
+
* zero-state line when there's no traffic yet (no requests / nothing saved).
|
|
61
|
+
*/
|
|
62
|
+
export const formatSavingsLine = (savings) => {
|
|
63
|
+
const noTraffic = savings.requestCount <= 0 ||
|
|
64
|
+
(savings.tokensSaved <= 0 && savings.savingsUsd <= 0);
|
|
65
|
+
if (noTraffic) {
|
|
66
|
+
return 'No savings yet — start sending traffic through Anyray to see them add up.';
|
|
67
|
+
}
|
|
68
|
+
const window = savings.windowDays > 0 ? ` (last ${Math.round(savings.windowDays)}d)` : '';
|
|
69
|
+
return `You've saved ~${formatUsd(savings.savingsUsd)} / ~${formatTokens(savings.tokensSaved)} tokens via Anyray so far${window}.`;
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=savings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savings.js","sourceRoot":"","sources":["../../src/util/savings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAkBhC,MAAM,YAAY,GAAG,CAAC,KAAc,EAAU,EAAE,CAC9C,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,MAAc,EACd,MAAc,EACd,OAAuB,EAAE,EACK,EAAE;IAChC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,MAAM,GAAG,GAAG,GAAG,MAAM,kBAAkB,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE;YACvC,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC3D,OAAO;YACL,WAAW,EAAE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC;YAC3C,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YACzC,YAAY,EAAE,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC;YAC7C,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;SAC1C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC,CAAC;AAEF,iEAAiE;AACjE,MAAM,YAAY,GAAG,CAAC,MAAc,EAAU,EAAE,CAC9C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AAE7C,sCAAsC;AACtC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE,CACxC,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE;IAC9B,qBAAqB,EAAE,CAAC;IACxB,qBAAqB,EAAE,CAAC;CACzB,CAAC,EAAE,CAAC;AAEP;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAgB,EAAU,EAAE;IAC5D,MAAM,SAAS,GACb,OAAO,CAAC,YAAY,IAAI,CAAC;QACzB,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;IACxD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,2EAA2E,CAAC;IACrF,CAAC;IACD,MAAM,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,OAAO,iBAAiB,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CACtE,OAAO,CAAC,WAAW,CACpB,4BAA4B,MAAM,GAAG,CAAC;AACzC,CAAC,CAAC"}
|