@telora/daemon 0.14.23 → 0.14.25

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
@@ -124,7 +124,64 @@ Instead of environment variables, you can use a JSON config file. The daemon loo
124
124
  2. Config file values
125
125
  3. Default values
126
126
 
127
- **Security note:** The `trackerId` must always be set via the `TELORA_TRACKER_ID` environment variable. It cannot be set in the config file (which is safe to commit to git).
127
+ **Security note:** The `trackerId` may live in `~/.telora/daemon.json` (mode 0600, written by the connect flow) or in the `TELORA_TRACKER_ID` environment variable. Env vars take priority. The home-scoped daemon.json is the same posture as `~/.aws/credentials` -- not committable.
128
+
129
+ ### Workstation tracking install model
130
+
131
+ Connect time, the daemon detects `telora-ai` (a Python CLI shipped under
132
+ `scripts/`) on `PATH`. If present, connect:
133
+
134
+ 1. Pre-populates the user's local tracking DB with the redeem-derived
135
+ credentials (`telora-ai config tracker_id ...`, etc.) -- this skips
136
+ the registration-code round-trip that Settings > AI Trackers normally
137
+ requires
138
+ 2. Merges `PostToolUse` and `UserPromptSubmit` hook entries into
139
+ `~/.claude/settings.json` so a fresh Claude Code session is tracked
140
+ end-to-end without manual `telora-ai setup-hooks`
141
+
142
+ If `telora-ai` is absent, connect prints a remediation hint and
143
+ continues -- the daemon, MCP, and scaffolding parts of connect must
144
+ succeed even when tracking can't be wired automatically.
145
+
146
+ We picked detect-only (option c) over the alternatives:
147
+
148
+ - **(a) pipx/uv install**: `telora-ai` is not on PyPI; no upstream
149
+ install target exists yet. A pipx install of a path-shipped script
150
+ doesn't generalise across machines that haven't checked out the repo.
151
+ - **(b) Single-binary build / fold into daemon**: cleanest long-term but
152
+ out of scope here; it requires either a PyInstaller pipeline that
153
+ doesn't exist yet, or a TS rewrite of the tracking instrumentation.
154
+
155
+ The remediation hint points users at the documented bring-up so they can
156
+ opt into tracking manually when they want it.
157
+
158
+ ### `.mcp.json` resolution model (option C)
159
+
160
+ The connect flow writes a credential-free `.mcp.json` so it can be committed and shared across collaborators:
161
+
162
+ ```json
163
+ {
164
+ "mcpServers": {
165
+ "telora-products": {
166
+ "command": "npx",
167
+ "args": ["-p", "@telora/mcp-products@latest", "telora-mcp-products"]
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ The MCP server resolves credentials at startup in this order:
174
+
175
+ 1. `TELORA_TRACKER_ID` + `TELORA_URL` env vars (explicit override)
176
+ 2. `~/.telora/daemon.json` (written by `telora-daemon connect`)
177
+
178
+ We picked option (c) -- read from daemon.json -- over the alternatives:
179
+
180
+ - **(a) Export from shell rc**: works only when the user's shell rc was sourced before `claude` was launched. Breaks under IDE-spawned Claude Code, `.envrc`-only setups, or any path where the rc didn't run.
181
+ - **(b) Sourced `~/.telora/env`**: same brittleness as (a), plus a new file we'd have to teach the user about.
182
+ - **(c) Read from `~/.telora/daemon.json`**: single source of truth, no shell-state coupling, same file the daemon already reads.
183
+
184
+ The trade-off: the MCP server has to do one filesystem read per startup. That's negligible compared to npx's cold-cache cost on the same path.
128
185
 
129
186
  Available config file fields:
130
187
  - `teloraUrl` - Telora API URL
package/build-info.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "commitSha": "2b9ce606",
3
- "builtAt": "2026-04-27T16:34:26.256Z"
2
+ "commitSha": "fcf1d2e5",
3
+ "builtAt": "2026-04-29T12:17:15.904Z"
4
4
  }
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * `telora-daemon connect <token>` subcommand.
3
3
  *
4
- * Redeems a Telora connect token, writes the daemon config + .mcp.json,
5
- * installs @telora/daemon as a dev dep, and starts the daemon in the
6
- * background. Designed to be invoked at the tail end of the bash bootstrap
7
- * (`scripts/connect.sh`) so the user lands in `claude` with everything wired.
4
+ * Redeems a Telora connect token, writes the daemon config + a committable
5
+ * `.mcp.json`, then starts the daemon in the background via `npx -p
6
+ * @telora/daemon@latest telora-daemon run`. Designed to be invoked at the
7
+ * tail end of the bash bootstrap (`scripts/connect.sh`) so the user lands
8
+ * in `claude` with everything wired -- and importantly leaves no node-side
9
+ * footprint (no package.json, no node_modules, no dev dependency) in the
10
+ * user's repo. Rust/Python/Go/empty repos stay untouched.
8
11
  */
9
12
  /**
10
13
  * Production Telora API URL embedded in the published @telora/daemon package.
@@ -17,6 +20,8 @@ export interface RedeemResponse {
17
20
  organization_id?: string;
18
21
  product_id?: string;
19
22
  telora_url?: string;
23
+ /** True when redeem created the product on the fly (unbound flow). */
24
+ product_was_created?: boolean;
20
25
  error?: string;
21
26
  code?: string;
22
27
  }
@@ -33,11 +38,14 @@ export interface ConnectOptions {
33
38
  token: string;
34
39
  /** Override the Telora URL (default: TELORA_URL env or production). */
35
40
  teloraUrl?: string;
36
- /** Repo path to install the daemon dep into (default: cwd). */
41
+ /** Repo path to wire the daemon into (default: cwd). */
37
42
  repoPath?: string;
38
43
  /** Skip starting the daemon (used by tests). */
39
44
  skipDaemonStart?: boolean;
40
- /** Skip running `npm install` (used by tests). */
45
+ /**
46
+ * @deprecated The connect flow no longer runs `npm install`. This flag is
47
+ * retained as a no-op so existing callers / tests continue to compile.
48
+ */
41
49
  skipInstall?: boolean;
42
50
  /** Override IO (used by tests). */
43
51
  io?: Partial<ConnectIO>;
@@ -51,6 +59,11 @@ export interface ConnectResult {
51
59
  mcpConfigPath: string | null;
52
60
  starterPromptPath: string | null;
53
61
  daemonPid: number | null;
62
+ /** True when the redeem step bootstrapped the product (unbound flow). */
63
+ productWasCreated: boolean;
64
+ /** True when this connect found an existing product entry for repoPath in
65
+ * daemon.json and short-circuited the proposed-name path. */
66
+ reusedExistingBinding: boolean;
54
67
  }
55
68
  /**
56
69
  * Map a redeem error code to a one-line remediation hint.
@@ -94,29 +107,103 @@ export declare function writeConnectDaemonConfig(params: {
94
107
  */
95
108
  export declare function writeStarterPromptArtifacts(repoPath: string, io: ConnectIO): string | null;
96
109
  /**
97
- * Append .telora/ and .mcp.json to .gitignore in the repo root if missing.
110
+ * Append `.telora/` to .gitignore in the repo root if missing.
111
+ *
112
+ * Earlier connect runs added `.mcp.json` here too, because the file embedded
113
+ * a per-user secret (TELORA_TRACKER_ID). The new `.mcp.json` shape is
114
+ * credential-free and meant to be committed; new runs no longer add it.
115
+ *
116
+ * If a previous run added `.mcp.json` to .gitignore, leave that line in
117
+ * place -- removing it would surface as a noisy migration. The user can
118
+ * delete the line on their own when they realize they want to commit the
119
+ * file.
98
120
  */
99
121
  export declare function ensureGitignoreEntries(repoPath: string, io: ConnectIO): void;
100
122
  /**
101
- * Ensure a package.json exists. If missing, write a minimal stub so we can
102
- * `npm install --save-dev` without npm complaining.
123
+ * Spawn the daemon as a detached background process via `npx`.
124
+ *
125
+ * Deliberately uses `npx -p @telora/daemon@latest telora-daemon run` (and
126
+ * NOT a node_modules path) so the connect flow does not require a
127
+ * `package.json` or `node_modules/` in the user's repo. This keeps the
128
+ * connect flow language-agnostic: a Rust/Python/Go/empty repo never gets
129
+ * a Node footprint.
130
+ *
131
+ * Bin name is explicit (`telora-daemon`) because the package's bins are
132
+ * `telora-daemon` and `telora-daemon-wrapper`; neither matches the unscoped
133
+ * package name `daemon`, so `npx @telora/daemon` cannot resolve a bin and
134
+ * aborts with a misleading error (lesson from commit 0368f4b6).
135
+ */
136
+ export declare function startDaemonViaNpx(repoPath: string): number;
137
+ /**
138
+ * Look up an existing product entry in `~/.telora/daemon.json` whose
139
+ * repoPath matches the given path. Returns the product id when found,
140
+ * else null. Used by runConnect for re-run idempotency: a second run on
141
+ * the same dir reuses the existing binding instead of creating a duplicate
142
+ * product on the unbound path.
143
+ */
144
+ export declare function findExistingProductForRepo(repoPath: string): string | null;
145
+ /**
146
+ * Write or merge a seeded `CLAUDE.md` at the repo root.
147
+ *
148
+ * - Fresh repo: writes the seeded block straight in.
149
+ * - Existing CLAUDE.md without the Telora delimiters: appends a delimited
150
+ * Telora block at the end. The user's content is untouched.
151
+ * - Existing CLAUDE.md WITH the delimiters: replaces only the delimited
152
+ * block. Re-runs are byte-identical (no churn, no duplication).
153
+ */
154
+ export declare function writeOrMergeClaudeMd(repoPath: string, productName: string | null, io: ConnectIO): void;
155
+ /**
156
+ * Write or deep-merge `<repo>/.claude/settings.json`.
157
+ *
158
+ * Fresh: writes the template directly.
159
+ * Existing: deep-merges our `permissions.allow` entries into the user's
160
+ * settings, deduplicating. All other top-level keys are untouched.
161
+ * Re-runs produce no diff.
103
162
  */
104
- export declare function ensurePackageJson(repoPath: string, io: ConnectIO): void;
163
+ export declare function writeOrMergeClaudeSettings(repoPath: string, io: ConnectIO): void;
105
164
  /**
106
- * Install @telora/daemon as a dev dependency in the project.
107
- * Skipped if the package is already declared in devDependencies/dependencies.
165
+ * Locate the `telora-ai` Python CLI on PATH. Returns the absolute path if
166
+ * found, else null. Detection is the entire install model -- if the user
167
+ * doesn't have it, connect prints a remediation hint and continues
168
+ * (tracking is opt-in).
108
169
  */
109
- export declare function installDaemonIfNeeded(repoPath: string, io: ConnectIO): void;
170
+ export declare function locateTeloraAi(): string | null;
110
171
  /**
111
- * Spawn the locally-installed daemon as a detached background process.
172
+ * Pre-populate the user's `~/.syntelyos/ai_tracking.db` config table with
173
+ * the redeem-derived credentials. Skips the
174
+ * `telora-ai register <code>` round-trip that Settings > AI Trackers
175
+ * normally requires -- the tracker row already exists in Telora (the
176
+ * redeem RPC inserted it), so all we need is to point the local DB at
177
+ * it.
178
+ *
179
+ * Each `telora-ai config <key> <value>` call is a single sqlite UPSERT;
180
+ * idempotent on re-runs.
112
181
  *
113
- * We deliberately do not reuse `startDaemonBackground` from daemon-process.ts
114
- * because that resolves the entry point relative to *this* file -- which,
115
- * during a fresh `npx --yes -p @telora/daemon telora-daemon connect ...` invocation, lives in
116
- * a transient npx cache. Spawning from the local install gives the daemon
117
- * a stable home for its lifetime.
182
+ * Returns true on success, false if any sub-call failed (the failure is
183
+ * surfaced via io.err but never aborts the connect flow).
184
+ */
185
+ export declare function registerWorkstationLocally(params: {
186
+ teloraAiPath: string;
187
+ trackerId: string;
188
+ organizationId: string;
189
+ teloraUrl: string;
190
+ io: ConnectIO;
191
+ }): boolean;
192
+ /**
193
+ * Idempotent merge of `PostToolUse` and `UserPromptSubmit` hook entries
194
+ * into `~/.claude/settings.json`. Mirrors the shape of `telora-ai
195
+ * setup-hooks` in scripts/telora-ai but runs from the daemon connect flow
196
+ * so the user doesn't have to invoke it manually.
197
+ *
198
+ * Existing hook entries are preserved. Re-runs do not duplicate.
199
+ */
200
+ export declare function mergeUserClaudeSettingsHooks(io: ConnectIO, homeOverride?: string): boolean;
201
+ /**
202
+ * Write `.telora/.product-bootstrapped` -- a marker the starter prompt
203
+ * detects on first turn to offer a rename of the basename-derived product
204
+ * name. The starter prompt removes the marker after acting on it.
118
205
  */
119
- export declare function startInstalledDaemonBackground(repoPath: string): number;
206
+ export declare function writeBootstrapMarker(repoPath: string, io: ConnectIO): void;
120
207
  /**
121
208
  * Run the connect flow.
122
209
  *
@@ -1 +1 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"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"}