anyclaude-sdk 0.6.2 → 0.7.1
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 +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/query.d.ts +6 -0
- package/dist/query.js +25 -0
- package/dist/telemetry.d.ts +17 -0
- package/dist/telemetry.js +155 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -390,6 +390,10 @@ Runnable Vite projects in [`examples/`](examples/): **`browser-ide`** (WebContai
|
|
|
390
390
|
| MCP / slash commands / background tasks / sub-agents | Built-in | Built-in |
|
|
391
391
|
| Serverless survivor + prompt projection | — | Built-in |
|
|
392
392
|
|
|
393
|
+
## Telemetry
|
|
394
|
+
|
|
395
|
+
The SDK emits **anonymous, opt-out** usage telemetry (SDK version, runtime, a coarse model-family bucket, and which features are used) — never code, prompts, repo identity, paths, or keys, and a no-op unless you configure a collector. Disable with `ANYCLAUDE_TELEMETRY=0`, `DO_NOT_TRACK=1`, or `query({ disableTelemetry: true })`. Full disclosure: [TELEMETRY.md](TELEMETRY.md).
|
|
396
|
+
|
|
393
397
|
## License
|
|
394
398
|
|
|
395
399
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -25,4 +25,5 @@ export { uuid } from './util/ids.js';
|
|
|
25
25
|
export * as paths from './util/paths.js';
|
|
26
26
|
export { priceFor, computeCostUSD, contextWindowFor, type Pricing } from './util/pricing.js';
|
|
27
27
|
export { estimateTokens, summarizeHistory, compactWithWindow } from './compact.js';
|
|
28
|
+
export { track, telemetryEnabled, detectRuntime, TELEMETRY_SDK_VERSION, type TelemetryOptions } from './telemetry.js';
|
|
28
29
|
export { runToolLoop, type RunToolLoopOptions } from './loop.js';
|
package/dist/index.js
CHANGED
|
@@ -27,5 +27,6 @@ export { uuid } from './util/ids.js';
|
|
|
27
27
|
export * as paths from './util/paths.js';
|
|
28
28
|
export { priceFor, computeCostUSD, contextWindowFor } from './util/pricing.js';
|
|
29
29
|
export { estimateTokens, summarizeHistory, compactWithWindow } from './compact.js';
|
|
30
|
+
export { track, telemetryEnabled, detectRuntime, TELEMETRY_SDK_VERSION } from './telemetry.js';
|
|
30
31
|
export { runToolLoop } from './loop.js';
|
|
31
32
|
// (createResponsesClient is exported via ./llm/index.js)
|
package/dist/query.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { SlashCommand } from './commands/index.js';
|
|
|
5
5
|
import type { SessionStoreLike } from './session/index.js';
|
|
6
6
|
import type { MemoryStore } from './memory/index.js';
|
|
7
7
|
import { type Workspace } from './agent.js';
|
|
8
|
+
import { type TelemetryOptions } from './telemetry.js';
|
|
8
9
|
export interface QueryOptions {
|
|
9
10
|
/** A plain string (single turn) or a stream of user messages (multi-turn). */
|
|
10
11
|
prompt: string | AsyncIterable<SDKUserMessage>;
|
|
@@ -109,6 +110,11 @@ export interface QueryOptions {
|
|
|
109
110
|
settings?: boolean | import('./settings/index.js').Settings;
|
|
110
111
|
/** Load `.claude/skills/*.md` as slash commands + a skill registry, or pass a Skill[]. */
|
|
111
112
|
skills?: boolean | import('./skills/index.js').Skill[];
|
|
113
|
+
/** Anonymous, opt-out usage telemetry (version/runtime/which-features only — never
|
|
114
|
+
* code, prompts, repo, or keys). Configure or disable via `telemetry`; see TELEMETRY.md. */
|
|
115
|
+
telemetry?: TelemetryOptions;
|
|
116
|
+
/** Convenience to force telemetry off for this run (same as `telemetry: { disabled: true }`). */
|
|
117
|
+
disableTelemetry?: boolean;
|
|
112
118
|
}
|
|
113
119
|
/** An async iterator of SDK messages, augmented with session controls. */
|
|
114
120
|
export interface Query extends AsyncGenerator<SDKMessage, void, void> {
|
package/dist/query.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// Returns an AsyncGenerator<SDKMessage>. Accepts either a single string prompt
|
|
4
4
|
// or an async iterable of SDKUserMessage (for multi-turn / interactive use).
|
|
5
5
|
import { runAgent } from './agent.js';
|
|
6
|
+
import { track, telemetryEnabled } from './telemetry.js';
|
|
7
|
+
import { profileForModel } from './llm/profiles.js';
|
|
6
8
|
export function query(options) {
|
|
7
9
|
const prompt = typeof options.prompt === 'string'
|
|
8
10
|
? singlePrompt(options.prompt)
|
|
@@ -58,6 +60,29 @@ export function query(options) {
|
|
|
58
60
|
skills: options.skills,
|
|
59
61
|
});
|
|
60
62
|
gen.interrupt = () => abortController.abort();
|
|
63
|
+
// Anonymous, aggregate adoption signal — one event per public run. Fire-and-forget,
|
|
64
|
+
// never blocks the generator, no-ops unless enabled + a collector is configured.
|
|
65
|
+
// Only booleans + a coarse model-family bucket leave the process (see telemetry.ts).
|
|
66
|
+
const telemetry = {
|
|
67
|
+
disabled: options.disableTelemetry,
|
|
68
|
+
...options.telemetry,
|
|
69
|
+
};
|
|
70
|
+
if (telemetryEnabled(telemetry)) {
|
|
71
|
+
track('run', {
|
|
72
|
+
model_family: profileForModel(options.model).name,
|
|
73
|
+
client_workspace_tools: !!options.clientWorkspaceTools,
|
|
74
|
+
client_tools: !!options.clientTools?.length,
|
|
75
|
+
survivor: options.maxDurationMs != null,
|
|
76
|
+
mcp: !!options.mcpServers,
|
|
77
|
+
team: !!options.team,
|
|
78
|
+
background: !!options.background,
|
|
79
|
+
auto_compact: !!options.autoCompact,
|
|
80
|
+
skills: !!options.skills,
|
|
81
|
+
sessions: !!options.sessionStore,
|
|
82
|
+
partial_messages: !!options.includePartialMessages,
|
|
83
|
+
resumed: !!options.continueRun || !!options.resume,
|
|
84
|
+
}, telemetry);
|
|
85
|
+
}
|
|
61
86
|
return gen;
|
|
62
87
|
}
|
|
63
88
|
/** Wrap a single text prompt into the async-iterable form runAgent expects. */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Bump on release so adoption can be bucketed by version. */
|
|
2
|
+
export declare const TELEMETRY_SDK_VERSION = "0.7.1";
|
|
3
|
+
export interface TelemetryOptions {
|
|
4
|
+
/** Force-disable for this call (highest precedence besides the global opt-outs). */
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
/** Collector URL. Defaults to `ANYCLAUDE_TELEMETRY_URL` then the built-in default. */
|
|
7
|
+
url?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Resolve whether telemetry may run, honoring every documented opt-out. */
|
|
10
|
+
export declare function telemetryEnabled(opts?: TelemetryOptions): boolean;
|
|
11
|
+
/** Coarse runtime bucket — never anything machine-identifying. */
|
|
12
|
+
export declare function detectRuntime(): 'browser' | 'webcontainer' | 'bun' | 'node' | 'unknown';
|
|
13
|
+
/**
|
|
14
|
+
* Record an anonymous, aggregate event. No-op unless telemetry is enabled AND a
|
|
15
|
+
* collector URL is configured. Never blocks, never throws.
|
|
16
|
+
*/
|
|
17
|
+
export declare function track(event: string, props?: Record<string, unknown>, opts?: TelemetryOptions): void;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Anonymous, opt-out usage telemetry. The goal is a single, honest question:
|
|
2
|
+
// "are people adopting the SDK, and which parts?" — answered in AGGREGATE, never
|
|
3
|
+
// per-user. This module is deliberately conservative about what it can ever send.
|
|
4
|
+
//
|
|
5
|
+
// HARD GUARANTEES (enforced here, not just by convention):
|
|
6
|
+
// • It NEVER sends: repo/remote URLs, project/package names, file paths, source
|
|
7
|
+
// code, prompts, messages, tool arguments, LLM responses, API keys, or
|
|
8
|
+
// endpoints/base URLs. `track()` whitelists prop keys and keeps only booleans
|
|
9
|
+
// + a few coarse string buckets — anything else is dropped.
|
|
10
|
+
// • Off with one switch: `ANYCLAUDE_TELEMETRY=0`, `DO_NOT_TRACK=1`, any `CI`,
|
|
11
|
+
// a `disableTelemetry` option, browser `localStorage['anyclaude_telemetry']='0'`,
|
|
12
|
+
// or `globalThis.__ANYCLAUDE_NO_TELEMETRY__ = true`.
|
|
13
|
+
// • No endpoint configured ⇒ no-op (it can't send anywhere by default).
|
|
14
|
+
// • Fire-and-forget: never blocks, never throws, swallows all errors.
|
|
15
|
+
//
|
|
16
|
+
// See TELEMETRY.md for the full disclosure.
|
|
17
|
+
import { uuid } from './util/ids.js';
|
|
18
|
+
/** Bump on release so adoption can be bucketed by version. */
|
|
19
|
+
export const TELEMETRY_SDK_VERSION = '0.7.1';
|
|
20
|
+
// Aggregate-only collector (Puter Worker; see examples/telemetry-collector).
|
|
21
|
+
// Override with `ANYCLAUDE_TELEMETRY_URL` / `telemetry: { url }`, or disable
|
|
22
|
+
// entirely with the opt-outs above. Set to '' to make telemetry a no-op.
|
|
23
|
+
const DEFAULT_TELEMETRY_URL = 'https://anyclaude-telemetry.puter.work';
|
|
24
|
+
// Only these prop keys are ever transmitted, and only with safe value types.
|
|
25
|
+
// Booleans pass through; these specific string keys pass through as-is (they are
|
|
26
|
+
// coarse buckets we set ourselves — never free-form / user data).
|
|
27
|
+
const ALLOWED_STRING_KEYS = new Set(['model_family', 'event_detail']);
|
|
28
|
+
function readEnv(name) {
|
|
29
|
+
const p = globalThis.process;
|
|
30
|
+
return p?.env?.[name];
|
|
31
|
+
}
|
|
32
|
+
/** Resolve whether telemetry may run, honoring every documented opt-out. */
|
|
33
|
+
export function telemetryEnabled(opts) {
|
|
34
|
+
if (opts?.disabled)
|
|
35
|
+
return false;
|
|
36
|
+
const flag = (readEnv('ANYCLAUDE_TELEMETRY') ?? '').toLowerCase();
|
|
37
|
+
if (flag === '0' || flag === 'false' || flag === 'off' || flag === 'no')
|
|
38
|
+
return false;
|
|
39
|
+
const dnt = (readEnv('DO_NOT_TRACK') ?? '').toLowerCase();
|
|
40
|
+
if (dnt === '1' || dnt === 'true')
|
|
41
|
+
return false;
|
|
42
|
+
if (readEnv('CI'))
|
|
43
|
+
return false;
|
|
44
|
+
try {
|
|
45
|
+
const g = globalThis;
|
|
46
|
+
if (g.__ANYCLAUDE_NO_TELEMETRY__ === true)
|
|
47
|
+
return false;
|
|
48
|
+
const v = g.localStorage?.getItem('anyclaude_telemetry');
|
|
49
|
+
if (v === '0' || v === 'off' || v === 'false')
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
/* localStorage may throw in sandboxed contexts */
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function telemetryUrl(opts) {
|
|
58
|
+
return opts?.url || readEnv('ANYCLAUDE_TELEMETRY_URL') || DEFAULT_TELEMETRY_URL;
|
|
59
|
+
}
|
|
60
|
+
/** Coarse runtime bucket — never anything machine-identifying. */
|
|
61
|
+
export function detectRuntime() {
|
|
62
|
+
const g = globalThis;
|
|
63
|
+
// WebContainer sets process.versions.webcontainer.
|
|
64
|
+
if (g.process?.versions?.webcontainer)
|
|
65
|
+
return 'webcontainer';
|
|
66
|
+
if (typeof g.window !== 'undefined' && typeof g.document !== 'undefined')
|
|
67
|
+
return 'browser';
|
|
68
|
+
if (g.Bun || g.process?.versions?.bun)
|
|
69
|
+
return 'bun';
|
|
70
|
+
if (g.process?.versions?.node)
|
|
71
|
+
return 'node';
|
|
72
|
+
return 'unknown';
|
|
73
|
+
}
|
|
74
|
+
// A random, NON-identifying id. Persisted in browser localStorage so repeated
|
|
75
|
+
// runs on one origin coalesce; per-process otherwise. Not tied to machine/user/IP.
|
|
76
|
+
let cachedInstallId = null;
|
|
77
|
+
function installId() {
|
|
78
|
+
if (cachedInstallId)
|
|
79
|
+
return cachedInstallId;
|
|
80
|
+
try {
|
|
81
|
+
const ls = globalThis.localStorage;
|
|
82
|
+
if (ls) {
|
|
83
|
+
const existing = ls.getItem('anyclaude_install_id');
|
|
84
|
+
if (existing)
|
|
85
|
+
return (cachedInstallId = existing);
|
|
86
|
+
const fresh = uuid();
|
|
87
|
+
ls.setItem('anyclaude_install_id', fresh);
|
|
88
|
+
return (cachedInstallId = fresh);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* ignore */
|
|
93
|
+
}
|
|
94
|
+
return (cachedInstallId = uuid());
|
|
95
|
+
}
|
|
96
|
+
/** Keep only booleans + the allowlisted coarse string buckets. Everything else is dropped. */
|
|
97
|
+
function safeProps(props) {
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [k, v] of Object.entries(props)) {
|
|
100
|
+
if (typeof v === 'boolean')
|
|
101
|
+
out[k] = v;
|
|
102
|
+
else if (typeof v === 'number' && Number.isFinite(v))
|
|
103
|
+
out[k] = v;
|
|
104
|
+
else if (typeof v === 'string' && ALLOWED_STRING_KEYS.has(k))
|
|
105
|
+
out[k] = v.slice(0, 40);
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
let noticeShown = false;
|
|
110
|
+
function showNoticeOnce() {
|
|
111
|
+
if (noticeShown)
|
|
112
|
+
return;
|
|
113
|
+
noticeShown = true;
|
|
114
|
+
try {
|
|
115
|
+
const log = globalThis.console;
|
|
116
|
+
log?.error?.('[anyclaude-sdk] Anonymous usage telemetry is on (version + runtime + which features, no code/prompts/repo/keys). ' +
|
|
117
|
+
'Opt out: ANYCLAUDE_TELEMETRY=0 (or DO_NOT_TRACK=1). See TELEMETRY.md.');
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
/* ignore */
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Record an anonymous, aggregate event. No-op unless telemetry is enabled AND a
|
|
125
|
+
* collector URL is configured. Never blocks, never throws.
|
|
126
|
+
*/
|
|
127
|
+
export function track(event, props = {}, opts) {
|
|
128
|
+
try {
|
|
129
|
+
if (!telemetryEnabled(opts))
|
|
130
|
+
return;
|
|
131
|
+
const url = telemetryUrl(opts);
|
|
132
|
+
if (!url)
|
|
133
|
+
return;
|
|
134
|
+
const f = globalThis.fetch;
|
|
135
|
+
if (!f)
|
|
136
|
+
return;
|
|
137
|
+
showNoticeOnce();
|
|
138
|
+
const body = JSON.stringify({
|
|
139
|
+
event,
|
|
140
|
+
sdk_version: TELEMETRY_SDK_VERSION,
|
|
141
|
+
runtime: detectRuntime(),
|
|
142
|
+
install: installId(),
|
|
143
|
+
...safeProps(props),
|
|
144
|
+
});
|
|
145
|
+
void f(url, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: { 'content-type': 'application/json' },
|
|
148
|
+
body,
|
|
149
|
+
keepalive: true,
|
|
150
|
+
}).catch(() => { });
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
/* telemetry must never affect the host app */
|
|
154
|
+
}
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anyclaude-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Standalone, browser-compatible SDK providing Claude Code agent capabilities (tools, tool loop, multi-turn, MCP, sub-agents, sessions) against any OpenAI/Anthropic-compatible LLM endpoint. Runs in the browser (WebContainer), Node, and Bun — no backend required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,6 +71,10 @@
|
|
|
71
71
|
"./anthropic-endpoint": {
|
|
72
72
|
"types": "./dist/anthropic-endpoint.d.ts",
|
|
73
73
|
"import": "./dist/anthropic-endpoint.js"
|
|
74
|
+
},
|
|
75
|
+
"./telemetry": {
|
|
76
|
+
"types": "./dist/telemetry.d.ts",
|
|
77
|
+
"import": "./dist/telemetry.js"
|
|
74
78
|
}
|
|
75
79
|
},
|
|
76
80
|
"sideEffects": false,
|