rudel 0.1.14 → 0.1.16

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +257 -80
  3. package/package.json +8 -7
package/README.md CHANGED
@@ -83,4 +83,4 @@ Each uploaded session includes:
83
83
  ## Links
84
84
 
85
85
  - **Web App**: [app.rudel.ai](https://app.rudel.ai)
86
- - **Issues**: [GitHub Issues](https://github.com/obsessiondb/rudel/issues)
86
+ - **Issues**: [GitHub Issues](https://github.com/evrendom/rudel/issues)
package/dist/cli.js CHANGED
@@ -1889,18 +1889,18 @@ async function run(app, inputs, context) {
1889
1889
  // package.json
1890
1890
  var package_default = {
1891
1891
  name: "rudel",
1892
- version: "0.1.14",
1892
+ version: "0.1.16",
1893
1893
  type: "module",
1894
1894
  description: "CLI for the Coding Agent Analytics Platform rudel.ai",
1895
1895
  license: "MIT",
1896
1896
  homepage: "https://app.rudel.ai",
1897
1897
  repository: {
1898
1898
  type: "git",
1899
- url: "https://github.com/obsessiondb/rudel.git",
1899
+ url: "https://github.com/evrendom/rudel.git",
1900
1900
  directory: "apps/cli"
1901
1901
  },
1902
1902
  bugs: {
1903
- url: "https://github.com/obsessiondb/rudel/issues"
1903
+ url: "https://github.com/evrendom/rudel/issues"
1904
1904
  },
1905
1905
  keywords: [
1906
1906
  "claude",
@@ -1923,7 +1923,8 @@ var package_default = {
1923
1923
  "check-types": "tsc --noEmit",
1924
1924
  build: "bun build src/bin/cli.ts --outdir dist --target node",
1925
1925
  prepack: "bun run build",
1926
- test: "bun test"
1926
+ test: "bun test",
1927
+ "test:integration": "bun test ./src/__tests__/api-upload.integration.ts ./src/__tests__/auth-verify.integration.ts ./src/__tests__/auth.e2e.integration.ts"
1927
1928
  },
1928
1929
  dependencies: {
1929
1930
  "@clack/prompts": "^1.0.1",
@@ -11002,12 +11003,12 @@ import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
11002
11003
  import { homedir as homedir8 } from "node:os";
11003
11004
  import { dirname as dirname4, join as join9 } from "node:path";
11004
11005
 
11005
- // ../../node_modules/@orpc/contract/node_modules/@orpc/shared/dist/index.mjs
11006
+ // ../../node_modules/@orpc/client/node_modules/@orpc/shared/dist/index.mjs
11006
11007
  function resolveMaybeOptionalOptions2(rest) {
11007
11008
  return rest[0] ?? {};
11008
11009
  }
11009
11010
  var ORPC_SHARED_PACKAGE_NAME2 = "@orpc/shared";
11010
- var ORPC_SHARED_PACKAGE_VERSION2 = "1.14.1";
11011
+ var ORPC_SHARED_PACKAGE_VERSION2 = "1.13.14";
11011
11012
  function once2(fn) {
11012
11013
  let cached;
11013
11014
  return () => {
@@ -11197,9 +11198,9 @@ function isObject2(value2) {
11197
11198
  function isTypescriptObject2(value2) {
11198
11199
  return !!value2 && (typeof value2 === "object" || typeof value2 === "function");
11199
11200
  }
11200
- // ../../node_modules/@orpc/contract/node_modules/@orpc/client/dist/shared/client.B2S3uk-N.mjs
11201
+ // ../../node_modules/@orpc/client/dist/shared/client.CCotFXgP.mjs
11201
11202
  var ORPC_CLIENT_PACKAGE_NAME2 = "@orpc/client";
11202
- var ORPC_CLIENT_PACKAGE_VERSION2 = "1.14.1";
11203
+ var ORPC_CLIENT_PACKAGE_VERSION2 = "1.13.14";
11203
11204
  var COMMON_ORPC_ERROR_DEFS2 = {
11204
11205
  BAD_REQUEST: {
11205
11206
  status: 400,
@@ -11350,7 +11351,7 @@ function createORPCErrorFromJson2(json, options = {}) {
11350
11351
  ...json
11351
11352
  });
11352
11353
  }
11353
- // ../../node_modules/@orpc/contract/node_modules/@orpc/client/node_modules/@orpc/standard-server/dist/index.mjs
11354
+ // ../../node_modules/@orpc/client/node_modules/@orpc/standard-server/dist/index.mjs
11354
11355
  class EventEncoderError2 extends TypeError {
11355
11356
  }
11356
11357
 
@@ -11495,7 +11496,7 @@ function getEventMeta2(container) {
11495
11496
  return isTypescriptObject2(container) ? Reflect.get(container, EVENT_SOURCE_META_SYMBOL2) : undefined;
11496
11497
  }
11497
11498
 
11498
- // ../../node_modules/@orpc/contract/node_modules/@orpc/client/dist/shared/client.BLtwTQUg.mjs
11499
+ // ../../node_modules/@orpc/client/dist/shared/client.BLtwTQUg.mjs
11499
11500
  function mapEventIterator2(iterator, maps) {
11500
11501
  const mapError = async (error) => {
11501
11502
  let mappedError = await maps.error(error);
@@ -11553,7 +11554,7 @@ function isContractProcedure(item) {
11553
11554
  }
11554
11555
  return (typeof item === "object" || typeof item === "function") && item !== null && "~orpc" in item && typeof item["~orpc"] === "object" && item["~orpc"] !== null && "errorMap" in item["~orpc"] && "route" in item["~orpc"] && "meta" in item["~orpc"];
11555
11556
  }
11556
- // ../../node_modules/@orpc/contract/node_modules/@orpc/client/dist/shared/client.BUWX4iqI.mjs
11557
+ // ../../node_modules/@orpc/client/dist/shared/client.DqXOyGul.mjs
11557
11558
  class StandardRPCSerializer2 {
11558
11559
  constructor(jsonSerializer) {
11559
11560
  this.jsonSerializer = jsonSerializer;
@@ -11620,6 +11621,173 @@ class StandardRPCSerializer2 {
11620
11621
  return this.jsonSerializer.deserialize(serialized.json, serialized.meta ?? [], serialized.maps, (i) => data.get(i.toString()));
11621
11622
  }
11622
11623
  }
11624
+ // ../../node_modules/@orpc/contract/node_modules/@orpc/shared/dist/index.mjs
11625
+ var ORPC_SHARED_PACKAGE_NAME3 = "@orpc/shared";
11626
+ var ORPC_SHARED_PACKAGE_VERSION3 = "1.13.14";
11627
+ function once3(fn) {
11628
+ let cached;
11629
+ return () => {
11630
+ if (cached) {
11631
+ return cached.result;
11632
+ }
11633
+ const result = fn();
11634
+ cached = { result };
11635
+ return result;
11636
+ };
11637
+ }
11638
+ function sequential3(fn) {
11639
+ let lastOperationPromise = Promise.resolve();
11640
+ return (...args) => {
11641
+ return lastOperationPromise = lastOperationPromise.catch(() => {}).then(() => {
11642
+ return fn(...args);
11643
+ });
11644
+ };
11645
+ }
11646
+ var GLOBAL_OTEL_CONFIG_KEY3 = `__${ORPC_SHARED_PACKAGE_NAME3}@${ORPC_SHARED_PACKAGE_VERSION3}/otel/config__`;
11647
+ var fallbackAsyncDisposeSymbol3 = Symbol.for("asyncDispose");
11648
+ var asyncDisposeSymbol3 = Symbol.asyncDispose ?? fallbackAsyncDisposeSymbol3;
11649
+
11650
+ class AsyncIteratorClass3 {
11651
+ #isDone = false;
11652
+ #isExecuteComplete = false;
11653
+ #cleanup;
11654
+ #next;
11655
+ constructor(next, cleanup) {
11656
+ this.#cleanup = cleanup;
11657
+ this.#next = sequential3(async () => {
11658
+ if (this.#isDone) {
11659
+ return { done: true, value: undefined };
11660
+ }
11661
+ try {
11662
+ const result = await next();
11663
+ if (result.done) {
11664
+ this.#isDone = true;
11665
+ }
11666
+ return result;
11667
+ } catch (err) {
11668
+ this.#isDone = true;
11669
+ throw err;
11670
+ } finally {
11671
+ if (this.#isDone && !this.#isExecuteComplete) {
11672
+ this.#isExecuteComplete = true;
11673
+ await this.#cleanup("next");
11674
+ }
11675
+ }
11676
+ });
11677
+ }
11678
+ next() {
11679
+ return this.#next();
11680
+ }
11681
+ async return(value3) {
11682
+ this.#isDone = true;
11683
+ if (!this.#isExecuteComplete) {
11684
+ this.#isExecuteComplete = true;
11685
+ await this.#cleanup("return");
11686
+ }
11687
+ return { done: true, value: value3 };
11688
+ }
11689
+ async throw(err) {
11690
+ this.#isDone = true;
11691
+ if (!this.#isExecuteComplete) {
11692
+ this.#isExecuteComplete = true;
11693
+ await this.#cleanup("throw");
11694
+ }
11695
+ throw err;
11696
+ }
11697
+ async[asyncDisposeSymbol3]() {
11698
+ this.#isDone = true;
11699
+ if (!this.#isExecuteComplete) {
11700
+ this.#isExecuteComplete = true;
11701
+ await this.#cleanup("dispose");
11702
+ }
11703
+ }
11704
+ [Symbol.asyncIterator]() {
11705
+ return this;
11706
+ }
11707
+ }
11708
+ class EventPublisher3 {
11709
+ #listenersMap = /* @__PURE__ */ new Map;
11710
+ #maxBufferedEvents;
11711
+ constructor(options = {}) {
11712
+ this.#maxBufferedEvents = options.maxBufferedEvents ?? 100;
11713
+ }
11714
+ get size() {
11715
+ return this.#listenersMap.size;
11716
+ }
11717
+ publish(event, payload) {
11718
+ const listeners = this.#listenersMap.get(event);
11719
+ if (!listeners) {
11720
+ return;
11721
+ }
11722
+ for (const listener of listeners) {
11723
+ listener(payload);
11724
+ }
11725
+ }
11726
+ subscribe(event, listenerOrOptions) {
11727
+ if (typeof listenerOrOptions === "function") {
11728
+ let listeners = this.#listenersMap.get(event);
11729
+ if (!listeners) {
11730
+ this.#listenersMap.set(event, listeners = []);
11731
+ }
11732
+ listeners.push(listenerOrOptions);
11733
+ return once3(() => {
11734
+ listeners.splice(listeners.indexOf(listenerOrOptions), 1);
11735
+ if (listeners.length === 0) {
11736
+ this.#listenersMap.delete(event);
11737
+ }
11738
+ });
11739
+ }
11740
+ const signal = listenerOrOptions?.signal;
11741
+ const maxBufferedEvents = listenerOrOptions?.maxBufferedEvents ?? this.#maxBufferedEvents;
11742
+ signal?.throwIfAborted();
11743
+ const bufferedEvents = [];
11744
+ const pullResolvers = [];
11745
+ const unsubscribe = this.subscribe(event, (payload) => {
11746
+ const resolver = pullResolvers.shift();
11747
+ if (resolver) {
11748
+ resolver[0]({ done: false, value: payload });
11749
+ } else {
11750
+ bufferedEvents.push(payload);
11751
+ if (bufferedEvents.length > maxBufferedEvents) {
11752
+ bufferedEvents.shift();
11753
+ }
11754
+ }
11755
+ });
11756
+ const abortListener = (event2) => {
11757
+ unsubscribe();
11758
+ pullResolvers.forEach((resolver) => resolver[1](event2.target.reason));
11759
+ pullResolvers.length = 0;
11760
+ bufferedEvents.length = 0;
11761
+ };
11762
+ signal?.addEventListener("abort", abortListener, { once: true });
11763
+ return new AsyncIteratorClass3(async () => {
11764
+ if (signal?.aborted) {
11765
+ throw signal.reason;
11766
+ }
11767
+ if (bufferedEvents.length > 0) {
11768
+ return { done: false, value: bufferedEvents.shift() };
11769
+ }
11770
+ return new Promise((resolve2, reject) => {
11771
+ pullResolvers.push([resolve2, reject]);
11772
+ });
11773
+ }, async () => {
11774
+ unsubscribe();
11775
+ signal?.removeEventListener("abort", abortListener);
11776
+ pullResolvers.forEach((resolver) => resolver[0]({ done: true, value: undefined }));
11777
+ pullResolvers.length = 0;
11778
+ bufferedEvents.length = 0;
11779
+ });
11780
+ }
11781
+ }
11782
+
11783
+ class SequentialIdGenerator3 {
11784
+ index = BigInt(1);
11785
+ generate() {
11786
+ const id = this.index.toString(36);
11787
+ this.index++;
11788
+ return id;
11789
+ }
11790
+ }
11623
11791
  // ../../node_modules/@orpc/contract/dist/index.mjs
11624
11792
  function mergeMeta(meta1, meta2) {
11625
11793
  return { ...meta1, ...meta2 };
@@ -12827,8 +12995,23 @@ var WrappedV1Schema = exports_external.object({
12827
12995
  archetype_gate: WrappedV1ArchetypeGateSchema
12828
12996
  });
12829
12997
 
12998
+ // ../../packages/api-routes/src/schemas/wrapped-decimal-claim.ts
12999
+ var WrappedDecimalClaimTokenSchema = exports_external.string().trim().min(8).max(64).regex(/^wct_[A-Za-z0-9_-]+$/u);
13000
+ var RedeemWrappedDecimalClaimInputSchema = exports_external.object({
13001
+ token: exports_external.string().trim().min(1).max(128)
13002
+ });
13003
+ var RedeemWrappedDecimalClaimResultSchema = exports_external.discriminatedUnion("status", [
13004
+ exports_external.object({ status: exports_external.literal("granted") }),
13005
+ exports_external.object({ status: exports_external.literal("already_entitled") }),
13006
+ exports_external.object({ status: exports_external.literal("invalid_or_used") })
13007
+ ]);
13008
+ var WrappedDecimalClaimEntitlementSchema = exports_external.object({
13009
+ entitled: exports_external.boolean()
13010
+ });
13011
+
12830
13012
  // ../../packages/api-routes/src/schemas/wrapped-share.ts
12831
13013
  var WrappedShareThemeSchema = exports_external.enum(["dark", "light", "muted"]);
13014
+ var WrappedShareVariantSchema = exports_external.enum(["normal", "decimal"]);
12832
13015
  var WrappedShareLayoutModeSchema = exports_external.enum(["front", "front_back"]);
12833
13016
  var WrappedShareIdSchema = exports_external.string().trim().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u);
12834
13017
  var WrappedShareHeaderMetricSchema = exports_external.object({
@@ -12860,6 +13043,7 @@ var WrappedShareAppearanceSchema = exports_external.object({
12860
13043
  layoutMode: WrappedShareLayoutModeSchema,
12861
13044
  showArchetypeLabel: exports_external.boolean()
12862
13045
  });
13046
+ var WrappedShareSocialImageDataUrlSchema = exports_external.string().max(7000000).regex(/^data:image\/png;base64,[A-Za-z0-9+/]+={0,2}$/u);
12863
13047
  var WrappedShareRowSchema = exports_external.object({
12864
13048
  activeDays: exports_external.number().nonnegative(),
12865
13049
  cost: exports_external.number().nonnegative(),
@@ -12883,11 +13067,16 @@ var WrappedShareSnapshotSchema = exports_external.object({
12883
13067
  revealMetrics: WrappedShareRevealMetricsSchema.optional(),
12884
13068
  row: WrappedShareRowSchema,
12885
13069
  shellClassName: exports_external.string().min(1),
13070
+ socialImageDataUrl: WrappedShareSocialImageDataUrlSchema.optional(),
12886
13071
  statItems: exports_external.array(WrappedShareStatItemSchema),
12887
13072
  theme: WrappedShareThemeSchema
12888
13073
  });
13074
+ var PublicWrappedShareSnapshotSchema = WrappedShareSnapshotSchema.omit({
13075
+ socialImageDataUrl: true
13076
+ });
12889
13077
  var CreateWrappedShareInputSchema = exports_external.object({
12890
- snapshot: WrappedShareSnapshotSchema
13078
+ snapshot: WrappedShareSnapshotSchema,
13079
+ variant: WrappedShareVariantSchema.default("normal")
12891
13080
  });
12892
13081
  var GetPublicWrappedShareInputSchema = exports_external.object({
12893
13082
  shareId: WrappedShareIdSchema
@@ -12895,10 +13084,11 @@ var GetPublicWrappedShareInputSchema = exports_external.object({
12895
13084
  var WrappedShareRecordSchema = exports_external.object({
12896
13085
  created_at: exports_external.string(),
12897
13086
  expires_at: exports_external.string(),
12898
- id: WrappedShareIdSchema
13087
+ id: WrappedShareIdSchema,
13088
+ variant: WrappedShareVariantSchema
12899
13089
  });
12900
13090
  var PublicWrappedShareSchema = WrappedShareRecordSchema.extend({
12901
- snapshot: WrappedShareSnapshotSchema
13091
+ snapshot: PublicWrappedShareSnapshotSchema
12902
13092
  });
12903
13093
 
12904
13094
  // ../../packages/api-routes/src/schemas/wrapped-resume.ts
@@ -12993,6 +13183,8 @@ var IngestSessionOutputSchema = exports_external.object({
12993
13183
  success: exports_external.literal(true),
12994
13184
  sessionId: exports_external.string()
12995
13185
  });
13186
+ var SESSION_OWNERSHIP_CONFLICT_CODE = "SESSION_OWNERSHIP_CONFLICT";
13187
+ var SESSION_OWNERSHIP_CONFLICT_MESSAGE = "This session belongs to another organization member and cannot be replaced.";
12996
13188
  var AdminUserSchema = exports_external.object({
12997
13189
  id: exports_external.string(),
12998
13190
  name: exports_external.string(),
@@ -13009,7 +13201,8 @@ var contract = {
13009
13201
  health: oc.output(HealthSchema),
13010
13202
  me: oc.output(UserSchema),
13011
13203
  profile: {
13012
- updateMine: oc.input(UpdateProfileInputSchema).output(UserSchema)
13204
+ updateMine: oc.input(UpdateProfileInputSchema).output(UserSchema),
13205
+ deleteMine: oc.output(exports_external.object({ success: exports_external.literal(true) }))
13013
13206
  },
13014
13207
  cli: {
13015
13208
  authStatus: oc.output(CliUserSchema),
@@ -13017,7 +13210,12 @@ var contract = {
13017
13210
  setupStatus: oc.output(CliSetupStatusSchema)
13018
13211
  },
13019
13212
  listMyOrganizations: oc.output(exports_external.array(OrganizationSchema)),
13020
- ingestSession: oc.input(IngestSessionInputSchema).output(IngestSessionOutputSchema),
13213
+ ingestSession: oc.input(IngestSessionInputSchema).output(IngestSessionOutputSchema).errors({
13214
+ [SESSION_OWNERSHIP_CONFLICT_CODE]: {
13215
+ status: 409,
13216
+ message: SESSION_OWNERSHIP_CONFLICT_MESSAGE
13217
+ }
13218
+ }),
13021
13219
  getOrganizationSessionCount: oc.input(exports_external.object({ organizationId: exports_external.string(), userId: exports_external.string().optional() })).output(exports_external.object({ count: exports_external.number() })),
13022
13220
  deleteOrganization: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ success: exports_external.literal(true) })),
13023
13221
  teamInviteLink: {
@@ -13032,6 +13230,10 @@ var contract = {
13032
13230
  create: oc.input(CreateWrappedResumeInputSchema).output(WrappedResumeRecordSchema),
13033
13231
  consume: oc.input(ConsumeWrappedResumeInputSchema).output(WrappedResumeConsumeResultSchema)
13034
13232
  },
13233
+ wrappedDecimalClaim: {
13234
+ redeem: oc.input(RedeemWrappedDecimalClaimInputSchema).output(RedeemWrappedDecimalClaimResultSchema),
13235
+ getMine: oc.output(WrappedDecimalClaimEntitlementSchema)
13236
+ },
13035
13237
  admin: {
13036
13238
  listUsers: oc.input(exports_external.object({
13037
13239
  search: exports_external.string().optional(),
@@ -18016,6 +18218,9 @@ function formatUploadError(error) {
18016
18218
  const limit = data?.limit ?? "unknown";
18017
18219
  return `Rate limit reached (${limit} sessions per ${windowMin} min). Wait and retry with: rudel upload --retry`;
18018
18220
  }
18221
+ if (error instanceof ORPCError && error.code === SESSION_OWNERSHIP_CONFLICT_CODE) {
18222
+ return "This session ID is already owned by another organization member. Upload it from the original member account or use a different session ID.";
18223
+ }
18019
18224
  if (isPayloadTooLarge(error)) {
18020
18225
  return formatPayloadTooLargeError(error);
18021
18226
  }
@@ -18151,10 +18356,8 @@ async function runEnable() {
18151
18356
  failureStage: "auth_verify",
18152
18357
  error: auth.reason
18153
18358
  });
18154
- R2.error(auth.message);
18155
18359
  Gt("Run `rudel login` to authenticate.");
18156
- process.exitCode = 1;
18157
- return;
18360
+ return new Error(auth.message);
18158
18361
  }
18159
18362
  const { credentials } = auth;
18160
18363
  let orgs;
@@ -18171,9 +18374,7 @@ async function runEnable() {
18171
18374
  error,
18172
18375
  userId: auth.user.id
18173
18376
  });
18174
- R2.error("Failed to fetch organizations. Check your connection.");
18175
- process.exitCode = 1;
18176
- return;
18377
+ return new Error("Failed to fetch organizations. Check your connection.");
18177
18378
  }
18178
18379
  }
18179
18380
  if (orgs.length === 0) {
@@ -18183,10 +18384,8 @@ async function runEnable() {
18183
18384
  error: new Error("No organizations found"),
18184
18385
  userId: auth.user.id
18185
18386
  });
18186
- R2.error("No organizations found.");
18187
18387
  Gt("Create one at app.rudel.ai first.");
18188
- process.exitCode = 1;
18189
- return;
18388
+ return new Error("No organizations found.");
18190
18389
  }
18191
18390
  const cwd = process.cwd();
18192
18391
  const existingOrgId = await getProjectOrgId(cwd);
@@ -18333,7 +18532,7 @@ async function runEnable() {
18333
18532
  }
18334
18533
  Gt("Done!");
18335
18534
  if (totalFailed > 0 || hookInstallFailures > 0) {
18336
- process.exitCode = 1;
18535
+ return new Error(`Enable completed with ${hookInstallFailures} hook installation failure(s) and ${totalFailed} upload failure(s).`);
18337
18536
  }
18338
18537
  }
18339
18538
  var enableCommand = buildCommand({
@@ -20584,9 +20783,7 @@ async function runLogin(flags) {
20584
20783
  deviceCode = await requestDeviceCode(flags.apiBase);
20585
20784
  } catch (error) {
20586
20785
  captureLoginFailure("device_code_request", error);
20587
- R2.error(error instanceof Error ? error.message : String(error));
20588
- process.exitCode = 1;
20589
- return;
20786
+ return error instanceof Error ? error : new Error(String(error));
20590
20787
  }
20591
20788
  const verifyUrl = deviceCode.verification_uri_complete ?? `${deviceCode.verification_uri}?user_code=${encodeURIComponent(deviceCode.user_code)}`;
20592
20789
  captureCliProductAnalyticsEvent({
@@ -20616,9 +20813,7 @@ ${verifyUrl}`);
20616
20813
  const failureReason = normalizeFailureReason(error);
20617
20814
  captureLoginFailure(failureReason === "timeout" ? "browser_approval_timeout" : "token_exchange", error);
20618
20815
  spin.stop("Authentication failed");
20619
- R2.error(error instanceof Error ? error.message : String(error));
20620
- process.exitCode = 1;
20621
- return;
20816
+ return error instanceof Error ? error : new Error(String(error));
20622
20817
  }
20623
20818
  spin.message("Creating ingest token...");
20624
20819
  let ingestKey;
@@ -20627,9 +20822,7 @@ ${verifyUrl}`);
20627
20822
  } catch (error) {
20628
20823
  captureLoginFailure("api_key_create", error);
20629
20824
  spin.stop("Authentication failed");
20630
- R2.error(error instanceof Error ? error.message : String(error));
20631
- process.exitCode = 1;
20632
- return;
20825
+ return error instanceof Error ? error : new Error(String(error));
20633
20826
  }
20634
20827
  const client2 = createApiClient({
20635
20828
  apiBaseUrl: flags.apiBase,
@@ -20652,9 +20845,7 @@ ${verifyUrl}`);
20652
20845
  } catch (error) {
20653
20846
  captureLoginFailure("account_fetch", error);
20654
20847
  spin.stop("Authentication failed");
20655
- R2.error("Login failed: unable to fetch account details");
20656
- process.exitCode = 1;
20657
- return;
20848
+ return new Error("Login failed: unable to fetch account details");
20658
20849
  }
20659
20850
  try {
20660
20851
  saveCredentials({
@@ -20668,9 +20859,7 @@ ${verifyUrl}`);
20668
20859
  } catch (error) {
20669
20860
  captureLoginFailure("account_fetch", error);
20670
20861
  spin.stop("Authentication failed");
20671
- R2.error("Login failed: unable to persist credentials");
20672
- process.exitCode = 1;
20673
- return;
20862
+ return new Error("Login failed: unable to persist credentials");
20674
20863
  }
20675
20864
  captureCliProductAnalyticsEvent({
20676
20865
  distinctId: user.id,
@@ -20716,26 +20905,35 @@ var loginCommand = buildCommand({
20716
20905
  });
20717
20906
 
20718
20907
  // src/commands/logout.ts
20719
- async function runLogout() {
20908
+ async function runLogout(flags) {
20720
20909
  const credentials = loadCredentials();
20721
20910
  if (!credentials) {
20722
20911
  R2.info("Not logged in.");
20723
20912
  return;
20724
20913
  }
20725
- if (credentials.authType === "api-key") {
20914
+ if (credentials.authType === "api-key" && !flags.localOnly) {
20726
20915
  try {
20727
20916
  const client2 = createApiClient(credentials);
20728
20917
  await client2.cli.revokeToken();
20729
- } catch {
20730
- R2.warn("Failed to revoke token on server. Local credentials were cleared.");
20918
+ } catch (error) {
20919
+ const message = error instanceof Error ? error.message : String(error);
20920
+ return new Error(`Failed to revoke token on server: ${message}. Credentials were kept; retry or run \`rudel logout --local-only\`.`);
20731
20921
  }
20732
20922
  }
20733
20923
  clearCredentials();
20734
- R2.success("Logged out successfully.");
20924
+ R2.success(flags.localOnly ? "Logged out locally. Server token was not revoked." : "Logged out successfully.");
20735
20925
  }
20736
20926
  var logoutCommand = buildCommand({
20737
20927
  loader: async () => ({ default: runLogout }),
20738
- parameters: {},
20928
+ parameters: {
20929
+ flags: {
20930
+ localOnly: {
20931
+ kind: "boolean",
20932
+ brief: "Clear local credentials without revoking the server token",
20933
+ default: false
20934
+ }
20935
+ }
20936
+ },
20739
20937
  docs: {
20740
20938
  brief: "Log out and remove stored credentials"
20741
20939
  }
@@ -20746,10 +20944,8 @@ async function runSetOrg() {
20746
20944
  Wt2("rudel set-org");
20747
20945
  const credentials = loadCredentials();
20748
20946
  if (!credentials) {
20749
- R2.error("Not authenticated.");
20750
20947
  Gt("Run `rudel login` first.");
20751
- process.exitCode = 1;
20752
- return;
20948
+ return new Error("Not authenticated.");
20753
20949
  }
20754
20950
  let orgs;
20755
20951
  if (credentials.authType === "api-key") {
@@ -20759,16 +20955,12 @@ async function runSetOrg() {
20759
20955
  try {
20760
20956
  orgs = await client2.listMyOrganizations();
20761
20957
  } catch {
20762
- R2.error("Failed to fetch organizations. Check your connection.");
20763
- process.exitCode = 1;
20764
- return;
20958
+ return new Error("Failed to fetch organizations. Check your connection.");
20765
20959
  }
20766
20960
  }
20767
20961
  if (orgs.length === 0) {
20768
- R2.error("No organizations found.");
20769
20962
  Gt("Create one at app.rudel.ai first.");
20770
- process.exitCode = 1;
20771
- return;
20963
+ return new Error("No organizations found.");
20772
20964
  }
20773
20965
  const cwd = process.cwd();
20774
20966
  const currentOrgId = await getProjectOrgId(cwd);
@@ -20942,9 +21134,7 @@ function validateNotSubagent(filename) {
20942
21134
  async function runInteractiveUpload(flags) {
20943
21135
  const credentials = loadCredentials();
20944
21136
  if (!credentials && !flags.dryRun) {
20945
- R2.error("Not authenticated. Run `rudel login` first.");
20946
- process.exitCode = 1;
20947
- return;
21137
+ return new Error("Not authenticated. Run `rudel login` first.");
20948
21138
  }
20949
21139
  Wt2("rudel upload");
20950
21140
  const spin = be();
@@ -21037,7 +21227,7 @@ async function runInteractiveUpload(flags) {
21037
21227
  Gt("Done!");
21038
21228
  }
21039
21229
  if (summary.failed > 0) {
21040
- process.exitCode = 1;
21230
+ return new Error(`${summary.failed} upload(s) failed.`);
21041
21231
  }
21042
21232
  }
21043
21233
  function getAdapterName(source) {
@@ -21049,26 +21239,18 @@ function sessionCountHint(count) {
21049
21239
  async function runSingleUpload(flags, session) {
21050
21240
  const write = (msg) => {
21051
21241
  process.stdout.write(`${msg}
21052
- `);
21053
- };
21054
- const writeError = (msg) => {
21055
- process.stderr.write(`${msg}
21056
21242
  `);
21057
21243
  };
21058
21244
  const credentials = loadCredentials();
21059
21245
  if (!credentials && !flags.dryRun) {
21060
- writeError("Error: Not authenticated. Run `rudel login` first.");
21061
- process.exitCode = 1;
21062
- return;
21246
+ return new Error("Not authenticated. Run `rudel login` first.");
21063
21247
  }
21064
21248
  write(`Resolving session: ${session}`);
21065
21249
  let sessionInfo;
21066
21250
  try {
21067
21251
  sessionInfo = await resolveSession(session);
21068
21252
  } catch (error) {
21069
- writeError(`Error: ${error instanceof Error ? error.message : String(error)}`);
21070
- process.exitCode = 1;
21071
- return;
21253
+ return error instanceof Error ? error : new Error(String(error));
21072
21254
  }
21073
21255
  write(`Found session at: ${sessionInfo.transcriptPath}`);
21074
21256
  const gitInfo = await getGitInfo(sessionInfo.projectPath);
@@ -21126,16 +21308,13 @@ async function runSingleUpload(flags, session) {
21126
21308
  if (result.success) {
21127
21309
  write("Upload successful!");
21128
21310
  } else {
21129
- writeError(`Upload failed: ${result.error}`);
21130
- process.exitCode = 1;
21311
+ return new Error(`Upload failed: ${result.error}`);
21131
21312
  }
21132
21313
  }
21133
21314
  async function runRetryUpload(flags) {
21134
21315
  const credentials = loadCredentials();
21135
21316
  if (!credentials) {
21136
- R2.error("Not authenticated. Run `rudel login` first.");
21137
- process.exitCode = 1;
21138
- return;
21317
+ return new Error("Not authenticated. Run `rudel login` first.");
21139
21318
  }
21140
21319
  Wt2("rudel upload --retry");
21141
21320
  const failures = await loadFailedUploads();
@@ -21198,7 +21377,7 @@ async function runRetryUpload(flags) {
21198
21377
  renderBatchSummary(summary);
21199
21378
  Gt("Done!");
21200
21379
  if (summary.failed > 0) {
21201
- process.exitCode = 1;
21380
+ return new Error(`${summary.failed} upload(s) failed.`);
21202
21381
  }
21203
21382
  }
21204
21383
  async function runUpload(flags, ...sessions) {
@@ -21282,11 +21461,9 @@ async function runWhoami() {
21282
21461
  if (!result.authenticated) {
21283
21462
  if (result.reason === "no_credentials") {
21284
21463
  R2.info("Not logged in. Run `rudel login` to authenticate.");
21285
- } else {
21286
- R2.error(result.message);
21287
- process.exitCode = 1;
21464
+ return;
21288
21465
  }
21289
- return;
21466
+ return new Error(result.message);
21290
21467
  }
21291
21468
  R2.info(`Logged in as ${result.user.name} (${result.user.email})`);
21292
21469
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "rudel",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "description": "CLI for the Coding Agent Analytics Platform rudel.ai",
6
6
  "license": "MIT",
7
7
  "homepage": "https://app.rudel.ai",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/obsessiondb/rudel.git",
10
+ "url": "https://github.com/evrendom/rudel.git",
11
11
  "directory": "apps/cli"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/obsessiondb/rudel/issues"
14
+ "url": "https://github.com/evrendom/rudel/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "claude",
@@ -34,7 +34,8 @@
34
34
  "check-types": "tsc --noEmit",
35
35
  "build": "bun build src/bin/cli.ts --outdir dist --target node",
36
36
  "prepack": "bun run build",
37
- "test": "bun test"
37
+ "test": "bun test",
38
+ "test:integration": "bun test ./src/__tests__/api-upload.integration.ts ./src/__tests__/auth-verify.integration.ts ./src/__tests__/auth.e2e.integration.ts"
38
39
  },
39
40
  "dependencies": {
40
41
  "@clack/prompts": "^1.0.1",
@@ -47,9 +48,9 @@
47
48
  "p-map": "^7.0.4"
48
49
  },
49
50
  "devDependencies": {
50
- "@rudel/agent-adapters": "0.0.0",
51
- "@rudel/api-routes": "0.0.0",
52
- "@rudel/typescript-config": "0.0.0",
51
+ "@rudel/agent-adapters": "workspace:*",
52
+ "@rudel/api-routes": "workspace:*",
53
+ "@rudel/typescript-config": "workspace:*",
53
54
  "@types/node": "^22",
54
55
  "bun-types": "latest",
55
56
  "typescript": "5.9.2"