codiedev 0.9.79 → 0.9.81

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 (39) hide show
  1. package/assets/opencode-plugin.mjs +6 -0
  2. package/assets/session-key.mjs +48 -0
  3. package/dist/cli.js +113 -9
  4. package/dist/cliSession.d.ts +108 -0
  5. package/dist/cliSession.js +513 -0
  6. package/dist/codie.js +21 -1
  7. package/dist/commands/agentTemplates.generated.d.ts +11 -0
  8. package/dist/commands/agentTemplates.generated.js +139 -0
  9. package/dist/commands/agents.d.ts +7 -0
  10. package/dist/commands/agents.js +1130 -0
  11. package/dist/commands/agentsSdk.generated.d.ts +4 -0
  12. package/dist/commands/agentsSdk.generated.js +10 -0
  13. package/dist/commands/scrub.d.ts +11 -0
  14. package/dist/commands/scrub.js +102 -0
  15. package/dist/configDir.d.ts +9 -0
  16. package/dist/configDir.js +25 -1
  17. package/dist/connect.js +45 -2
  18. package/dist/connectFlow.d.ts +21 -0
  19. package/dist/connectFlow.js +41 -0
  20. package/dist/hook.js +25 -0
  21. package/dist/knownSecrets.d.ts +43 -0
  22. package/dist/knownSecrets.js +241 -0
  23. package/dist/launcher.d.ts +7 -2
  24. package/dist/launcher.js +36 -2
  25. package/dist/localScrub.d.ts +172 -0
  26. package/dist/localScrub.js +820 -0
  27. package/dist/localScrubReport.d.ts +83 -0
  28. package/dist/localScrubReport.js +268 -0
  29. package/dist/managedPi.js +18 -2
  30. package/dist/mcp.js +3 -0
  31. package/dist/scrub/extraRules.d.ts +4 -0
  32. package/dist/scrub/extraRules.js +17 -0
  33. package/dist/scrub/rules.d.ts +86 -0
  34. package/dist/scrub/rules.js +645 -0
  35. package/dist/scrub/scrubProviderRules.d.ts +14 -0
  36. package/dist/scrub/scrubProviderRules.js +149 -0
  37. package/dist/utils.d.ts +2 -0
  38. package/dist/utils.js +9 -7
  39. package/package.json +5 -4
@@ -10,6 +10,7 @@ import {
10
10
  trackWorkPath,
11
11
  workDirBySession,
12
12
  } from "./git-refs.mjs";
13
+ import { freshGatewayKey } from "./session-key.mjs";
13
14
 
14
15
  const skillUseBySession = new Map();
15
16
 
@@ -60,6 +61,11 @@ export const CodiedevMetadata = async () => ({
60
61
  });
61
62
  },
62
63
  "chat.headers": async (input, output) => {
64
+ // Browser sign-in: the current 24h token, refreshed as it nears expiry,
65
+ // so a session open for days keeps working (see session-key.mjs). Exact
66
+ // "Authorization" key: per-call headers win over the apiKey-derived one.
67
+ const key = await freshGatewayKey();
68
+ if (key) output.headers.Authorization = `Bearer ${key}`;
63
69
  Object.assign(
64
70
  output.headers,
65
71
  buildAttributionHeaders({
@@ -0,0 +1,48 @@
1
+ // The gateway key for one model request, kept fresh for the whole session.
2
+ //
3
+ // With browser sign-in (`codiedev connect`), the key is a 24h access token.
4
+ // The engine (OpenCode, pi) would otherwise hold the one it was launched
5
+ // with until the session ends, so a session open for more than a day would
6
+ // start failing. `codie` sets these variables only for a browser sign-in:
7
+ // CODIEDEV_SESSION_CONFIG path to ~/.codiedev/config.json
8
+ // CODIEDEV_CLI_JS the CLI entry (dist/cli.js)
9
+ // CODIEDEV_NODE the node binary that runs it
10
+ // Each request reads the current token from config (other codiedev
11
+ // processes may have refreshed it). When it has under 30 minutes left, this
12
+ // runs `codiedev session-refresh`, which refreshes under the shared lock
13
+ // (cliSession.ts, specs/tla/CliSession.tla), then reads again.
14
+ // No variables (pasted API token, CI): returns null and the launch key stands.
15
+
16
+ import { execFile } from "node:child_process";
17
+ import { readFileSync } from "node:fs";
18
+
19
+ export const REFRESH_WHEN_LEFT_MS = 30 * 60_000;
20
+
21
+ function readSession(path) {
22
+ try {
23
+ const c = JSON.parse(readFileSync(path, "utf8"));
24
+ return c && c.auth && typeof c.token === "string" && c.token ? c : null;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ function runRefresh(env) {
31
+ return new Promise((resolve) => {
32
+ execFile(env.CODIEDEV_NODE, [env.CODIEDEV_CLI_JS, "session-refresh"], { timeout: 30_000, env }, () => resolve());
33
+ });
34
+ }
35
+
36
+ /** Fresh gateway key, or null when the session isn't a browser sign-in. */
37
+ export async function freshGatewayKey(env = process.env, now = Date.now, refresh = runRefresh) {
38
+ const path = env.CODIEDEV_SESSION_CONFIG;
39
+ if (!path || !env.CODIEDEV_CLI_JS || !env.CODIEDEV_NODE) return null;
40
+ let session = readSession(path);
41
+ if (!session) return null;
42
+ const left = Number(session.auth.accessTokenExpiresAt) - now();
43
+ if (!(left > REFRESH_WHEN_LEFT_MS)) {
44
+ await refresh(env);
45
+ session = readSession(path) ?? session;
46
+ }
47
+ return session.token;
48
+ }
package/dist/cli.js CHANGED
@@ -13,8 +13,43 @@
13
13
  * codiedev note "<text>" — capture a thought
14
14
  * codiedev help — show this help
15
15
  */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
16
49
  Object.defineProperty(exports, "__esModule", { value: true });
17
50
  const connect_1 = require("./connect");
51
+ const cliSession_1 = require("./cliSession");
52
+ const utils_1 = require("./utils");
18
53
  const push_1 = require("./commands/push");
19
54
  const delete_1 = require("./commands/delete");
20
55
  const pull_1 = require("./commands/pull");
@@ -32,6 +67,7 @@ const capture_1 = require("./commands/capture");
32
67
  const pack_1 = require("./commands/pack");
33
68
  const traces_1 = require("./commands/traces");
34
69
  const evals_1 = require("./commands/evals");
70
+ const agents_1 = require("./commands/agents");
35
71
  // --- starter packs phase 2 ---
36
72
  const hookGate_1 = require("./packs/hookGate");
37
73
  const sync_1 = require("./packs/sync");
@@ -45,8 +81,10 @@ Codiedev CLI
45
81
  someone left off across sessions and providers.
46
82
 
47
83
  Connect:
48
- codiedev connect Link Claude Code / Codex / Cursor /
84
+ codiedev connect Sign in with your browser and link
85
+ Claude Code / Codex / Cursor /
49
86
  GitHub Copilot / pi to your org
87
+ codiedev logout Sign this machine out
50
88
  codiedev doctor Verify your setup after connecting
51
89
 
52
90
  Harness:
@@ -94,6 +132,9 @@ Starter packs:
94
132
  codiedev pack status Installed packs, up to date or not
95
133
 
96
134
  Traces (agents on the gateway):
135
+ codiedev agents init | deploy | run | runs | show | secrets | webhook
136
+ Deploy agents and run them in fresh sandboxes
137
+ (codiedev agents help)
97
138
  codiedev traces agents Agents you maintain, with saved test cases
98
139
  codiedev traces cases <agent> [--dataset name [--version n]] [--model id]
99
140
  Pull an agent's test cases (or one dataset)
@@ -117,6 +158,7 @@ Privacy:
117
158
  codiedev ignore <path> Never capture this folder or its children
118
159
  codiedev unignore <path> Stop ignoring a folder
119
160
  codiedev ignored [path] List ignored folders, or check one path
161
+ codiedev scrub [--dry-run] Remove keys from past agent sessions on this machine
120
162
 
121
163
  Capture:
122
164
  codiedev note "<text>" Capture a passing thought
@@ -168,12 +210,14 @@ status sync never weakens it.
168
210
  const COMMAND_HELP = {
169
211
  pack: pack_1.PACK_HELP,
170
212
  traces: traces_1.TRACES_HELP,
213
+ agents: agents_1.AGENTS_HELP,
171
214
  evals: evals_1.EVALS_HELP,
172
215
  connect: `
173
216
  codiedev connect — link your agent CLI to Codiedev
174
217
 
175
- Interactive setup. Prompts for an API token, then installs:
176
- - ~/.codiedev/config.json (token + org + tracked repos)
218
+ Opens your browser to sign in (approve once, no API key to copy), then
219
+ installs:
220
+ - ~/.codiedev/config.json (sign-in + org + tracked repos)
177
221
  - ~/.claude/settings.json hook (captures session transcripts)
178
222
  - ~/.claude/CLAUDE.md instructions (teaches Claude the commands)
179
223
  - ~/.codex/hooks.json hook (same, for Codex)
@@ -186,7 +230,27 @@ codiedev connect — link your agent CLI to Codiedev
186
230
 
187
231
  Re-run anytime to swap accounts or refresh the instruction block.
188
232
 
189
- Get your token: https://codiedev.com/portal/integrations/claude-code
233
+ Over SSH or on a machine with no display it shows a link and a short code
234
+ instead: open the link on your laptop or phone, check the code, approve.
235
+ Detected automatically.
236
+
237
+ Options:
238
+ --token Paste an API token instead of signing in with a browser
239
+ (CI, or a machine with no browser anywhere). Create one at
240
+ https://codiedev.com/portal/integrations/claude-code
241
+ --force, -f Sign in again even if this machine is already connected.
242
+
243
+ Browser sign-in stores a 24-hour access token and a refresh token that
244
+ rotates on every use; nothing long-lived is saved. Revoke a machine from
245
+ Integrations in the portal, or run 'codiedev logout' on it.
246
+ `.trim(),
247
+ logout: `
248
+ codiedev logout — sign this machine out
249
+
250
+ Ends this machine's browser sign-in on the server and removes it from
251
+ ~/.codiedev/config.json. Device settings (ignored folders) are kept.
252
+ For a pasted API token, it's only forgotten locally: revoke it in the
253
+ portal under Integrations.
190
254
  `.trim(),
191
255
  push: `
192
256
  codiedev push — author or update an artifact
@@ -405,13 +469,15 @@ Run 'codiedev help <command>' for each, or see the quick list:
405
469
 
406
470
  codiedev help
407
471
 
408
- ## Getting a token
472
+ ## Signing in
409
473
 
410
- 1. Log in to https://codiedev.com/portal/integrations/claude-code
411
- 2. Click "Create token"
412
- 3. Copy the cdv_... string and paste it into 'codiedev connect'
474
+ 'codiedev connect' opens your browser; approve, and you're done. Nothing
475
+ long-lived is stored: a 24-hour access token plus a rotating refresh token.
413
476
 
414
- Tokens are per-user. Rotate any time by revoking + creating a new one.
477
+ Over SSH it shows a link and code to approve from your laptop or phone,
478
+ automatically. For CI, create a token at
479
+ https://codiedev.com/portal/integrations/claude-code and run
480
+ 'codiedev connect --token'.
415
481
  `.trim();
416
482
  // Retired commands: say so instead of "Unknown command", since agent
417
483
  // instructions written by older installs may still reference them.
@@ -474,11 +540,41 @@ async function main() {
474
540
  console.log(`codiedev v${version_1.CLI_VERSION}`);
475
541
  return;
476
542
  }
543
+ // Browser sign-in: refresh the access token (under the shared lock) before
544
+ // the command reads config. No-op for pasted API tokens.
545
+ if (command !== "connect" && command !== "logout") {
546
+ const fresh = await (0, cliSession_1.ensureFreshToken)({ configPath: utils_1.CONFIG_PATH }).catch(() => null);
547
+ if (fresh?.status === "signed_out") {
548
+ console.error("\nYour Codiedev sign-in has ended (revoked or expired). Run `codiedev connect` to sign in again.\n");
549
+ }
550
+ }
477
551
  try {
478
552
  switch (command) {
479
553
  case "connect":
480
554
  await (0, connect_1.runConnect)(rest);
481
555
  return;
556
+ case "session-refresh": {
557
+ // Internal: the Codie engine's per-request hook asks for a fresh
558
+ // token when the current one has under 30 minutes left. Refreshes
559
+ // under the shared lock; prints nothing.
560
+ await (0, cliSession_1.ensureFreshToken)({ configPath: utils_1.CONFIG_PATH }, 60 * 60_000).catch(() => null);
561
+ return;
562
+ }
563
+ case "logout": {
564
+ const out = await (0, cliSession_1.logout)({ configPath: utils_1.CONFIG_PATH });
565
+ if (!out.wasConnected) {
566
+ console.log("This machine isn't signed in.");
567
+ }
568
+ else if (out.hadBrowserAuth) {
569
+ console.log(out.serverRevoked
570
+ ? "Signed out. This machine's sign-in is revoked."
571
+ : "Signed out on this machine. Couldn't reach Codiedev to revoke the sign-in; revoke it under Integrations in the portal.");
572
+ }
573
+ else {
574
+ console.log("Removed the API token from this machine. It still works elsewhere until you revoke it under Integrations in the portal.");
575
+ }
576
+ return;
577
+ }
482
578
  case "doctor":
483
579
  case "verify":
484
580
  await (0, doctor_1.runDoctor)(rest);
@@ -512,9 +608,17 @@ async function main() {
512
608
  case "read":
513
609
  await (0, inbox_1.runRead)(rest);
514
610
  return;
611
+ case "scrub": {
612
+ const { runScrubCommand } = await Promise.resolve().then(() => __importStar(require("./commands/scrub")));
613
+ process.exitCode = await runScrubCommand(rest);
614
+ return;
615
+ }
515
616
  case "note":
516
617
  await (0, note_1.runNote)(rest);
517
618
  return;
619
+ case "agents":
620
+ await (0, agents_1.runAgents)(rest);
621
+ return;
518
622
  case "promote":
519
623
  case "reverse-ticket":
520
624
  case "reverseTicket":
@@ -0,0 +1,108 @@
1
+ import type { CodiedevConfig } from "./utils";
2
+ export interface BrowserAuth {
3
+ kind: "browser";
4
+ clientId: string;
5
+ refreshToken: string;
6
+ /** epoch ms when `token` (the access token) stops working */
7
+ accessTokenExpiresAt: number;
8
+ }
9
+ export interface TokenSet {
10
+ accessToken: string;
11
+ refreshToken: string;
12
+ expiresAt: number;
13
+ clientId: string;
14
+ }
15
+ /** Default: refresh when less than 10 minutes remain. */
16
+ export declare const DEFAULT_MIN_VALIDITY_MS: number;
17
+ /** A lock older than this was left by a crashed process. Refresh calls time
18
+ * out well before it (REFRESH_TIMEOUT_MS), so a live holder never looks stale. */
19
+ export declare const LOCK_STALE_MS = 30000;
20
+ export interface SessionDeps {
21
+ configPath: string;
22
+ fetchImpl?: typeof fetch;
23
+ now?: () => number;
24
+ sleep?: (ms: number) => Promise<void>;
25
+ }
26
+ export declare function readConfigAt(configPath: string): CodiedevConfig | null;
27
+ /** rename(), retrying the transient Windows sharing errors. Exported for tests. */
28
+ export declare function renameWithRetry(from: string, to: string, rename?: (a: string, b: string) => void, sleep?: (ms: number) => void): void;
29
+ /**
30
+ * Write via temp file + rename so a concurrent reader never sees a torn file.
31
+ * If the rename still fails after ~1.6s of retries (a handle held that long),
32
+ * fall back to writing in place: a rare torn read beats failing `connect`.
33
+ */
34
+ export declare function writeConfigAtomic(configPath: string, config: CodiedevConfig): void;
35
+ export type FreshResult = {
36
+ status: "fresh";
37
+ config: CodiedevConfig;
38
+ } | {
39
+ status: "refreshed";
40
+ config: CodiedevConfig;
41
+ } | {
42
+ status: "adopted";
43
+ config: CodiedevConfig;
44
+ } | {
45
+ status: "unavailable";
46
+ config: CodiedevConfig;
47
+ } | {
48
+ status: "signed_out";
49
+ config: CodiedevConfig;
50
+ } | {
51
+ status: "none";
52
+ config: CodiedevConfig | null;
53
+ };
54
+ /**
55
+ * Make sure config.token is a usable access token for at least
56
+ * `minValidityMs`. Never throws: auth problems surface as the next request's
57
+ * 401, which every command already explains.
58
+ */
59
+ export declare function ensureFreshToken(deps: SessionDeps, minValidityMs?: number): Promise<FreshResult>;
60
+ export declare function pkcePair(): {
61
+ verifier: string;
62
+ challenge: string;
63
+ };
64
+ export declare function openBrowser(url: string): boolean;
65
+ export interface LoginOptions {
66
+ backendUrl: string;
67
+ clientName?: string;
68
+ fetchImpl?: typeof fetch;
69
+ /** Test hook: called with the authorize URL instead of opening a browser. */
70
+ onAuthorizeUrl?: (url: string) => void;
71
+ log?: (line: string) => void;
72
+ now?: () => number;
73
+ }
74
+ /** Parse `code`/`state` (or `error`) out of a loopback callback URL. */
75
+ export declare function parseCallback(input: string): {
76
+ code?: string;
77
+ state?: string;
78
+ error?: string;
79
+ };
80
+ export declare function browserLogin(opts: LoginOptions): Promise<TokenSet>;
81
+ export declare const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
82
+ /**
83
+ * True when a browser opened here can't reach the user: an SSH session, or a
84
+ * Linux box with no display. Then `connect` uses the device flow (a code the
85
+ * user approves on any device) instead of a loopback redirect, with no flag.
86
+ */
87
+ export declare function isHeadless(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
88
+ export interface DeviceLoginOptions {
89
+ backendUrl: string;
90
+ clientName?: string;
91
+ fetchImpl?: typeof fetch;
92
+ log?: (line: string) => void;
93
+ now?: () => number;
94
+ sleep?: (ms: number) => Promise<void>;
95
+ /** Test hook: receives the code + link instead of only printing them. */
96
+ onCode?: (info: {
97
+ userCode: string;
98
+ verificationUri: string;
99
+ verificationUriComplete: string;
100
+ }) => void;
101
+ }
102
+ export declare function deviceLogin(opts: DeviceLoginOptions): Promise<TokenSet>;
103
+ /** `codiedev logout`: end a browser sign-in server-side, forget it locally. */
104
+ export declare function logout(deps: SessionDeps): Promise<{
105
+ wasConnected: boolean;
106
+ hadBrowserAuth: boolean;
107
+ serverRevoked: boolean;
108
+ }>;