@playdrop/playdrop-cli 0.12.18 → 0.12.20

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.12.8",
3
- "build": 11,
3
+ "build": 13,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
6
6
  "clients": {
@@ -96,6 +96,7 @@ export declare function buildWorkerChildEnv(input: {
96
96
  homeDir?: string;
97
97
  codexHomeDir?: string;
98
98
  claudeConfigDir?: string;
99
+ playdropConfigPath?: string;
99
100
  playwrightBrowsersPath?: string;
100
101
  baseEnv?: NodeJS.ProcessEnv;
101
102
  }): NodeJS.ProcessEnv;
@@ -712,6 +712,17 @@ function buildWorkerChildEnv(input) {
712
712
  }
713
713
  child.CLAUDE_CONFIG_DIR = claudeConfigDir;
714
714
  }
715
+ // Claude sessions inherit the host's real HOME and Claude config on purpose: the machine is
716
+ // logged in with the Claude CLI and we call it exactly as a human does. The worker never
717
+ // stages, copies, or refreshes Claude credentials. The agent's `playdrop` CLI is still scoped
718
+ // to a task session through PLAYDROP_CONFIG_PATH rather than by hijacking HOME.
719
+ if (input.playdropConfigPath !== undefined) {
720
+ const playdropConfigPath = input.playdropConfigPath.trim();
721
+ if (!playdropConfigPath || !node_path_1.default.isAbsolute(playdropConfigPath)) {
722
+ throw new Error('invalid_worker_child_playdrop_config_path');
723
+ }
724
+ child.PLAYDROP_CONFIG_PATH = playdropConfigPath;
725
+ }
715
726
  if (input.playwrightBrowsersPath !== undefined) {
716
727
  const playwrightBrowsersPath = input.playwrightBrowsersPath.trim();
717
728
  if (!playwrightBrowsersPath || !node_path_1.default.isAbsolute(playwrightBrowsersPath)) {
@@ -249,11 +249,9 @@ export declare function prepareClaudeRunConfig(input: {
249
249
  workspaceDir: string;
250
250
  workerUsername: string;
251
251
  envName: string;
252
- sourceCredentialsPath?: string;
253
252
  playdropConfig?: ReturnType<typeof loadConfig>;
254
253
  }): Promise<{
255
- homeDir: string;
256
- claudeConfigDir: string;
254
+ playdropConfigPath: string;
257
255
  }>;
258
256
  export declare function removeAgentRunCredentials(workspaceDir: string): Promise<void>;
259
257
  export declare function removeStaleAgentRunCredentials(workerHomeDir?: string): Promise<void>;
@@ -1958,78 +1958,32 @@ async function stageWorkerPlaydropSession(input) {
1958
1958
  },
1959
1959
  }, null, 2), { mode: 0o600 });
1960
1960
  await (0, promises_1.chmod)(playdropConfigPath, 0o600);
1961
+ return playdropConfigPath;
1961
1962
  }
1962
1963
  async function prepareClaudeRunConfig(input) {
1963
1964
  const runRoot = node_path_1.default.join(input.workspaceDir, '.playdrop', 'claude-run');
1964
1965
  const homeDir = node_path_1.default.join(runRoot, 'home');
1965
- const claudeConfigDir = node_path_1.default.join(runRoot, 'config');
1966
1966
  await (0, promises_1.rm)(runRoot, { recursive: true, force: true });
1967
1967
  await (0, promises_1.mkdir)(runRoot, { recursive: true, mode: 0o700 });
1968
1968
  await (0, promises_1.chmod)(runRoot, 0o700);
1969
1969
  await (0, promises_1.mkdir)(homeDir, { mode: 0o700 });
1970
- await (0, promises_1.mkdir)(claudeConfigDir, { mode: 0o700 });
1971
- const configuredCredentialsPath = input.sourceCredentialsPath?.trim()
1972
- || node_process_1.default.env.PLAYDROP_WORKER_CLAUDE_CREDENTIALS_FILE?.trim();
1973
- const defaultCredentialsPath = node_path_1.default.join(node_os_1.default.homedir(), '.claude', '.credentials.json');
1974
- let rawCredentials;
1975
- if (configuredCredentialsPath) {
1976
- const sourceCredentialsPath = node_path_1.default.resolve(configuredCredentialsPath);
1977
- if (!isFile(sourceCredentialsPath)) {
1978
- throw new Error(`worker_claude_credentials_missing:${sourceCredentialsPath}`);
1979
- }
1980
- rawCredentials = (0, node_fs_1.readFileSync)(sourceCredentialsPath, 'utf8');
1981
- }
1982
- else if (isFile(defaultCredentialsPath)) {
1983
- rawCredentials = (0, node_fs_1.readFileSync)(defaultCredentialsPath, 'utf8');
1984
- }
1985
- else if (node_process_1.default.platform === 'darwin') {
1986
- try {
1987
- const result = await execFileAsync('security', [
1988
- 'find-generic-password',
1989
- '-s',
1990
- 'Claude Code-credentials',
1991
- '-w',
1992
- ], { maxBuffer: 1024 * 1024 });
1993
- rawCredentials = result.stdout;
1994
- }
1995
- catch {
1996
- throw new Error('worker_claude_credentials_missing:macos_keychain');
1997
- }
1998
- }
1999
- else {
2000
- throw new Error(`worker_claude_credentials_missing:${defaultCredentialsPath}`);
2001
- }
2002
- let credentials;
2003
- try {
2004
- credentials = JSON.parse(rawCredentials);
2005
- }
2006
- catch {
2007
- throw new Error('worker_claude_credentials_invalid_json');
2008
- }
2009
- const credentialObject = typeof credentials === 'object' && credentials !== null && !Array.isArray(credentials)
2010
- ? credentials
2011
- : null;
2012
- const oauthValue = credentialObject?.claudeAiOauth;
2013
- const claudeAiOauth = typeof oauthValue === 'object' && oauthValue !== null && !Array.isArray(oauthValue)
2014
- ? oauthValue
2015
- : null;
2016
- if (!claudeAiOauth
2017
- || typeof claudeAiOauth.accessToken !== 'string'
2018
- || !claudeAiOauth.accessToken.trim()
2019
- || typeof claudeAiOauth.refreshToken !== 'string'
2020
- || !claudeAiOauth.refreshToken.trim()) {
2021
- throw new Error('worker_claude_oauth_credentials_missing');
2022
- }
2023
- const stagedCredentialsPath = node_path_1.default.join(claudeConfigDir, '.credentials.json');
2024
- await (0, promises_1.writeFile)(stagedCredentialsPath, `${JSON.stringify({ claudeAiOauth })}\n`, { mode: 0o600 });
2025
- await (0, promises_1.chmod)(stagedCredentialsPath, 0o600);
2026
- await stageWorkerPlaydropSession({
2027
- homeDir,
2028
- workerUsername: input.workerUsername,
2029
- envName: input.envName,
2030
- playdropConfig: input.playdropConfig,
2031
- });
2032
- return { homeDir, claudeConfigDir };
1970
+ // The worker does NOT manage Claude authentication. The host is logged in with the Claude CLI;
1971
+ // we call `claude` and let it use the machine's own login exactly as a human would.
1972
+ //
1973
+ // We used to copy the OAuth credential into this per-task config dir. That is what logged the
1974
+ // machine out: the copy refreshed on first use after the 8h access-token expiry, Anthropic
1975
+ // rotated the refresh token (invalidating the one still in the keychain), and the refreshed
1976
+ // credential was thrown away with the task workspace. The host was then permanently logged out.
1977
+ // Observed twice in production (2026-07-13 19:50, 2026-07-14 13:00:23, the latter three seconds
1978
+ // after task 449's Claude exited). Never stage, copy, or write Claude credentials here.
1979
+ return {
1980
+ playdropConfigPath: await stageWorkerPlaydropSession({
1981
+ homeDir,
1982
+ workerUsername: input.workerUsername,
1983
+ envName: input.envName,
1984
+ playdropConfig: input.playdropConfig,
1985
+ }),
1986
+ };
2033
1987
  }
2034
1988
  async function removeAgentRunCredentials(workspaceDir) {
2035
1989
  await Promise.all([
@@ -2124,7 +2078,7 @@ async function runCodex(input) {
2124
2078
  };
2125
2079
  }
2126
2080
  async function runClaude(input) {
2127
- const { homeDir, claudeConfigDir } = await prepareClaudeRunConfig({
2081
+ const { playdropConfigPath } = await prepareClaudeRunConfig({
2128
2082
  workspaceDir: input.workspaceDir,
2129
2083
  workerUsername: input.workerUsername,
2130
2084
  envName: input.envName,
@@ -2135,7 +2089,8 @@ async function runClaude(input) {
2135
2089
  : null;
2136
2090
  let playwrightMcpConfigPath = null;
2137
2091
  if (input.enablePlaywrightMcp) {
2138
- playwrightMcpConfigPath = node_path_1.default.join(claudeConfigDir, 'playwright-mcp.json');
2092
+ playwrightMcpConfigPath = node_path_1.default.join(input.workspaceDir, '.playdrop', 'claude-run', 'playwright-mcp.json');
2093
+ (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(playwrightMcpConfigPath), { recursive: true });
2139
2094
  (0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(), null, 2)}\n`, 'utf8');
2140
2095
  }
2141
2096
  const result = await (0, runtime_1.runLoggedProcess)({
@@ -2157,8 +2112,7 @@ async function runClaude(input) {
2157
2112
  envName: input.envName,
2158
2113
  eventDir: input.eventDir,
2159
2114
  devPort: input.devPort,
2160
- homeDir,
2161
- claudeConfigDir,
2115
+ playdropConfigPath,
2162
2116
  playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
2163
2117
  }),
2164
2118
  stdin: input.prompt,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.12.8",
3
- "build": 11,
3
+ "build": 13,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
6
6
  "clients": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playdrop/playdrop-cli",
3
- "version": "0.12.18",
3
+ "version": "0.12.20",
4
4
  "description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
5
5
  "homepage": "https://www.playdrop.ai",
6
6
  "repository": {