insta 0.0.57 → 0.0.58

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
@@ -233,7 +233,7 @@ build never reaches a production installer.
233
233
  | `INSTA_PROJECT_ID` · `INSTA_ORG_ID` · `INSTA_BRANCH` | Target a project, org or branch without linking |
234
234
  | `INSTA_PASSWORD` | Password for non-interactive login |
235
235
  | `INSTA_NO_AUTOUPDATE` | Disable self-update |
236
- | `INSTA_NO_TELEMETRY` · `DO_NOT_TRACK` | Disable usage analytics. Each command sends one event (command, flags, outcome, version, OS) to the same PostHog project as the console. Only ids, enums and numbers among the arguments are kept — names, branches, keys, paths, secret values, free text and error messages never leave the machine; custom API hosts report nothing |
236
+ | `INSTA_NO_TELEMETRY` · `DO_NOT_TRACK` | Disable usage analytics. Each command sends one event (command, flags, outcome, version, OS) to the same PostHog project as the console. Only ids, enums and numbers among the arguments are kept — names, branches, keys, paths, secret values, free text and error messages never leave the machine; custom API hosts report nothing. Before you sign in, events carry a random id stored in `~/.insta/telemetry.json`; the first command after you sign in sends one extra event merging that id into your account, and the first command after the session ends (logout, `env use`) retires it |
237
237
 
238
238
  ## Agent skills
239
239
 
package/dist/telemetry.js CHANGED
@@ -18,6 +18,7 @@ const PROJECT_KEYS = {
18
18
  };
19
19
  // The send is awaited before the process exits, so it gets one bounded attempt and no retry.
20
20
  const SEND_TIMEOUT_MS = 1500;
21
+ const ID_FILE = join(os.homedir(), '.insta', 'telemetry.json');
21
22
  const REDACTED = '[REDACTED]';
22
23
  const oneOf = (values) => (v) => values.includes(v);
23
24
  const ID = (v) => /^(?:[0-9a-f]{8}-[0-9a-f-]{27}|[a-z]+_[\w-]{1,64}|(?=.*\d)[\w-]{1,64})$/i.test(v);
@@ -144,6 +145,7 @@ export function buildCommandEvent(command, args, options, outcome, ctx) {
144
145
  auth_kind: token ? (token.startsWith('insta_') ? 'api_key' : 'session') : null,
145
146
  project_id: ctx.project?.projectId ?? null,
146
147
  org_id: ctx.project?.orgId ?? null,
148
+ ...(ctx.project ? { $groups: { project: ctx.project.projectId, ...(ID(ctx.project.orgId) ? { org: ctx.project.orgId } : {}) } } : {}),
147
149
  tty: ctx.tty,
148
150
  ci: !!ctx.env.CI,
149
151
  agent: detectAgent(ctx.env),
@@ -154,20 +156,22 @@ export function buildCommandEvent(command, args, options, outcome, ctx) {
154
156
  },
155
157
  };
156
158
  }
157
- export async function anonymousId(file = join(os.homedir(), '.insta', 'telemetry.json')) {
159
+ export async function readState(file = ID_FILE) {
158
160
  try {
159
161
  const parsed = JSON.parse(await readFile(file, 'utf8'));
160
162
  if (typeof parsed.anonymousId === 'string' && parsed.anonymousId)
161
- return parsed.anonymousId;
163
+ return parsed;
162
164
  }
163
165
  catch { }
164
- const id = randomUUID();
166
+ return writeState({ anonymousId: randomUUID() }, file);
167
+ }
168
+ async function writeState(state, file = ID_FILE) {
165
169
  try {
166
170
  await mkdir(dirname(file), { recursive: true });
167
- await writeFile(file, JSON.stringify({ anonymousId: id }, null, 2));
171
+ await writeFile(file, JSON.stringify(state, null, 2));
168
172
  }
169
173
  catch { }
170
- return id;
174
+ return state;
171
175
  }
172
176
  export async function sendBatch(key, batch, fetchImpl = fetch) {
173
177
  try {
@@ -201,16 +205,27 @@ export async function trackCommand(cmd, args, outcome, cliVersion, deps = {}) {
201
205
  if (!key)
202
206
  return;
203
207
  const project = await (deps.loadProject ?? readProject)();
208
+ const userId = config.user?.id;
209
+ let state = await readState(deps.idFile);
210
+ // Session gone or switched: the id belongs to the account that left, so later commands get a fresh one.
211
+ if (state.identifiedAs && state.identifiedAs !== userId)
212
+ state = await writeState({ anonymousId: randomUUID() }, deps.idFile);
204
213
  const event = buildCommandEvent(command, args.flat(), cmd.opts(), outcome, {
205
214
  cliVersion,
206
215
  channel: deps.channel ?? detectChannel(),
207
216
  config,
208
217
  project,
209
- anonymousId: await anonymousId(deps.idFile),
218
+ anonymousId: state.anonymousId,
210
219
  env,
211
220
  tty: deps.tty ?? !!process.stdout.isTTY,
212
221
  });
213
- await sendBatch(key, [event], deps.fetchImpl);
222
+ const batch = [event];
223
+ const merge = userId !== undefined && state.identifiedAs !== userId;
224
+ if (merge)
225
+ batch.push({ event: '$identify', distinct_id: userId, timestamp: event.timestamp, properties: { $anon_distinct_id: state.anonymousId } });
226
+ const sent = await sendBatch(key, batch, deps.fetchImpl);
227
+ if (merge && sent)
228
+ await writeState({ ...state, identifiedAs: userId }, deps.idFile);
214
229
  }
215
230
  catch { }
216
231
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.0.57",
3
+ "version": "0.0.58",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [