runwork 0.13.3 → 0.14.0

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 (37) hide show
  1. package/bundled-types/core-endpoints.d.ts +16 -4
  2. package/bundled-types/core-workflow-instance.d.ts +6 -0
  3. package/bundled-types/workflows.d.ts +1 -0
  4. package/dist/agents/claude-desktop-plugin-tree.js +23 -8
  5. package/dist/agents/claude-desktop.js +6 -3
  6. package/dist/agents/codex.js +15 -7
  7. package/dist/api/client.d.ts +5 -0
  8. package/dist/api/client.js +8 -0
  9. package/dist/commands/__tests__/mcp-entries.test.d.ts +1 -0
  10. package/dist/commands/__tests__/mcp-entries.test.js +48 -0
  11. package/dist/commands/deploy.js +101 -22
  12. package/dist/commands/dev.d.ts +1 -0
  13. package/dist/commands/dev.js +145 -155
  14. package/dist/commands/logs.js +10 -3
  15. package/dist/commands/mcp-entries.d.ts +13 -0
  16. package/dist/commands/mcp-entries.js +26 -0
  17. package/dist/commands/sync.js +6 -10
  18. package/dist/dev/__tests__/session.test.js +60 -0
  19. package/dist/dev/session.d.ts +8 -0
  20. package/dist/dev/session.js +4 -1
  21. package/dist/generated/bundled-types.js +3 -3
  22. package/dist/generated/version.d.ts +1 -1
  23. package/dist/generated/version.js +1 -1
  24. package/dist/git/__tests__/auto-commit.test.js +59 -1
  25. package/dist/git/__tests__/deploy-guard.test.d.ts +1 -0
  26. package/dist/git/__tests__/deploy-guard.test.js +61 -0
  27. package/dist/git/auto-commit.d.ts +22 -0
  28. package/dist/git/auto-commit.js +72 -11
  29. package/dist/git/critical-files.d.ts +20 -0
  30. package/dist/git/critical-files.js +68 -0
  31. package/dist/git/deploy-guard.d.ts +53 -0
  32. package/dist/git/deploy-guard.js +78 -0
  33. package/dist/types.d.ts +3 -0
  34. package/dist/utils/agent-guidance.d.ts +2 -0
  35. package/dist/utils/ignore-matcher.d.ts +9 -0
  36. package/dist/utils/ignore-matcher.js +16 -0
  37. package/package.json +1 -1
@@ -10,9 +10,16 @@ import { z } from 'zod';
10
10
  import type { Hono } from 'hono';
11
11
  type ZodSchema = z.ZodType<any, any, any>;
12
12
  /**
13
- * Authentication type for public endpoints
13
+ * Authentication requirement declared by an endpoint definition.
14
14
  */
15
15
  export type EndpointAuthType = 'apiKey' | 'public';
16
+ /**
17
+ * Caller principal type resolved by the platform and passed via the
18
+ * `X-Public-Endpoint-Auth-Type` header. `user` and `workspace_key` are
19
+ * authenticated principals the platform has already authorized; `api_key`
20
+ * additionally carries a validated key id; `none` is unauthenticated.
21
+ */
22
+ export type EndpointPrincipalType = 'none' | 'api_key' | 'workspace_key' | 'user';
16
23
  /**
17
24
  * HTTP methods supported by endpoints
18
25
  */
@@ -53,10 +60,14 @@ export interface EndpointContext<TQuery = Record<string, unknown>, TBody = unkno
53
60
  * Authentication information passed to handlers
54
61
  */
55
62
  export interface EndpointAuthInfo {
56
- /** Authentication type used */
63
+ /** Authentication requirement declared by the endpoint */
57
64
  type: EndpointAuthType;
58
- /** API key ID (if authenticated with API key) */
65
+ /** Resolved caller principal type (api_key, workspace_key, user, none) */
66
+ principalType?: EndpointPrincipalType;
67
+ /** API key ID (if authenticated with an API key) */
59
68
  apiKeyId?: string;
69
+ /** User ID (if authenticated as a workspace member) */
70
+ userId?: string;
60
71
  /** Scopes granted to the API key */
61
72
  scopes?: string[];
62
73
  /** Whether request is authenticated */
@@ -112,8 +123,9 @@ export declare function isEndpointDefinition(entry: unknown): entry is EndpointD
112
123
  export interface ParsedEndpointRequest {
113
124
  endpointId: string;
114
125
  appId: string;
115
- authType: EndpointAuthType;
126
+ authType: EndpointPrincipalType;
116
127
  apiKeyId?: string;
128
+ userId?: string;
117
129
  scopes?: string[];
118
130
  }
119
131
  /**
@@ -9,6 +9,12 @@ import { DurableObject } from 'cloudflare:workers';
9
9
  import type { NativeWorkflowInstance, NativeWorkflowConfig } from './core-workflow-types';
10
10
  import type { StepOptions, WaitEventOptions } from './core-workflows';
11
11
  import { type Env } from './core-utils';
12
+ export declare class WorkflowPausedError extends Error {
13
+ reason: 'sleep' | 'waitEvent';
14
+ resumeAt?: number | undefined;
15
+ constructor(reason: 'sleep' | 'waitEvent', resumeAt?: number | undefined);
16
+ }
17
+ export declare function isWorkflowControlSignal(error: unknown): boolean;
12
18
  /**
13
19
  * WorkflowInstance Durable Object
14
20
  *
@@ -12,3 +12,4 @@ export { DEFAULT_WORKFLOW_CONFIG } from './core-workflow-types';
12
12
  export { WORKFLOW_INFRA_MODE } from './core-workflow-config';
13
13
  export { WorkflowInstanceDO } from './core-workflow-instance';
14
14
  export { WorkflowCoordinator } from './core-workflow-coordinator';
15
+ export { WorkflowPausedError, isWorkflowControlSignal } from './core-workflow-instance';
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, mkdirSync, rmSync, existsSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { appendTokenToUrl, buildSkillMd } from './types.js';
3
+ import { appendTokenToUrl, buildSkillMd, RUNWORK_WORKSPACE_MCP_NAME } from './types.js';
4
4
  import { SESSION_START_HOOK_SCRIPT } from './session-start-hook.js';
5
5
  /**
6
6
  * Write <destDir>/.claude-plugin/plugin.json.
@@ -24,13 +24,28 @@ export function writePluginMcpConfig(destDir, mcpServers) {
24
24
  mkdirSync(destDir, { recursive: true });
25
25
  const mcpEntries = {};
26
26
  for (const server of mcpServers) {
27
- const token = server.headers?.Authorization?.replace(/^Bearer\s+/i, '');
28
- const connectorUrl = token ? appendTokenToUrl(server.url, token) : server.url;
29
- mcpEntries[server.name] = {
30
- type: 'http',
31
- url: connectorUrl,
32
- ...(server.description ? { description: server.description } : {}),
33
- };
27
+ if (server.name === RUNWORK_WORKSPACE_MCP_NAME) {
28
+ // Preserve the exact, known-working behavior for the Runwork connector: embed
29
+ // the workspace key in the URL, no headers field. Claude Desktop's native
30
+ // connector did not reliably honor request headers here, which is why the token
31
+ // lives in the URL — do NOT change this.
32
+ const token = server.headers?.Authorization?.replace(/^Bearer\s+/i, '');
33
+ mcpEntries[server.name] = {
34
+ type: 'http',
35
+ url: token ? appendTokenToUrl(server.url, token) : server.url,
36
+ ...(server.description ? { description: server.description } : {}),
37
+ };
38
+ }
39
+ else {
40
+ // Third-party servers: forward the auth header map (same .mcp.json schema
41
+ // claude-code uses). Their secrets must NEVER be placed in the URL.
42
+ mcpEntries[server.name] = {
43
+ type: 'http',
44
+ url: server.url,
45
+ ...(server.headers ? { headers: server.headers } : {}),
46
+ ...(server.description ? { description: server.description } : {}),
47
+ };
48
+ }
34
49
  }
35
50
  writeFileSync(join(destDir, '.mcp.json'), JSON.stringify({ mcpServers: mcpEntries }, null, 2));
36
51
  }
@@ -189,12 +189,15 @@ export class ClaudeDesktopAdapter {
189
189
  // For cowork connectors, embed the auth token in the URL query string.
190
190
  // This lets Claude Desktop connect without requiring the user to go through
191
191
  // an OAuth flow or manually click "Connect" in the Connectors UI.
192
- // Write to claude_desktop_config.json via mcp-remote (chat mode fallback)
192
+ // Write to claude_desktop_config.json via mcp-remote (chat mode fallback).
193
+ // Forward every auth header (Authorization, X-Api-Key, etc.) as a --header arg.
193
194
  const entries = {};
194
195
  for (const s of servers) {
195
196
  const args = ['mcp-remote', s.url];
196
- if (s.headers?.Authorization) {
197
- args.push('--header', `Authorization:${s.headers.Authorization}`);
197
+ if (s.headers) {
198
+ for (const [name, value] of Object.entries(s.headers)) {
199
+ args.push('--header', `${name}:${value}`);
200
+ }
198
201
  }
199
202
  entries[s.name] = {
200
203
  command: 'npx',
@@ -45,15 +45,23 @@ export class CodexAdapter {
45
45
  delete mcpServers[key];
46
46
  }
47
47
  }
48
- // Add new entries (sanitize names to match Codex's ^[a-zA-Z0-9_-]+$ requirement)
48
+ // Add new entries (sanitize names to match Codex's ^[a-zA-Z0-9_-]+$ requirement).
49
49
  for (const s of servers) {
50
50
  const safeName = s.name.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
51
- // Embed auth token in URL so Codex doesn't need a separate env var
52
- const token = s.headers?.Authorization?.replace(/^Bearer\s+/i, '');
53
- const serverUrl = token ? appendTokenToUrl(s.url, token) : s.url;
54
- mcpServers[safeName] = {
55
- url: serverUrl,
56
- };
51
+ const entry = { url: s.url };
52
+ if (s.name === RUNWORK_WORKSPACE_MCP_NAME) {
53
+ // Preserve the known-working behavior: embed the workspace key in the URL.
54
+ // Codex's header-based auth was historically unreliable here, so do NOT change it.
55
+ const token = s.headers?.Authorization?.replace(/^Bearer\s+/i, '');
56
+ if (token)
57
+ entry.url = appendTokenToUrl(s.url, token);
58
+ }
59
+ else if (s.headers && Object.keys(s.headers).length > 0) {
60
+ // External servers: forward headers via Codex's documented http_headers field
61
+ // (static literal values). These had no prior working path, so this is additive.
62
+ entry.http_headers = s.headers;
63
+ }
64
+ mcpServers[safeName] = entry;
57
65
  }
58
66
  mkdirSync(join(configPath, '..'), { recursive: true });
59
67
  writeFileSync(configPath, stringify(parsed));
@@ -141,6 +141,11 @@ export declare class ApiClient {
141
141
  transport: 'sse' | 'streamable-http';
142
142
  }): Promise<McpServerConfig>;
143
143
  removeMcpServer(workspaceId: string, serverId: string): Promise<void>;
144
+ /**
145
+ * Resolve decrypted auth headers for header-auth MCP servers, scoped to the
146
+ * authenticated user (their own + workspace-wide credentials). Keyed by server id.
147
+ */
148
+ resolveMcpCredentials(workspaceId: string): Promise<Record<string, Record<string, string>>>;
144
149
  searchCommunitySkills(query: string, limit?: number, includeContent?: boolean): Promise<{
145
150
  query: string;
146
151
  count: number;
@@ -194,6 +194,14 @@ export class ApiClient {
194
194
  async removeMcpServer(workspaceId, serverId) {
195
195
  await this.request(`/api/workspaces/${workspaceId}/mcp-servers/${serverId}`, { method: 'DELETE' });
196
196
  }
197
+ /**
198
+ * Resolve decrypted auth headers for header-auth MCP servers, scoped to the
199
+ * authenticated user (their own + workspace-wide credentials). Keyed by server id.
200
+ */
201
+ async resolveMcpCredentials(workspaceId) {
202
+ const res = await this.request(`/api/workspaces/${workspaceId}/mcp-credentials/resolved`);
203
+ return res.data.servers;
204
+ }
197
205
  // --- Community Marketplace ---
198
206
  async searchCommunitySkills(query, limit = 10, includeContent = false) {
199
207
  const res = await this.request(`/api/community/skills/search?q=${encodeURIComponent(query)}&limit=${limit}&include_content=${includeContent}`);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildExternalMcpEntries } from '../mcp-entries.js';
3
+ const PLATFORM_KEY = 'rk_ws_live_supersecretplatformkey';
4
+ function server(overrides) {
5
+ return {
6
+ id: 'srv-1',
7
+ name: 'example',
8
+ url: 'https://mcp.example.com',
9
+ transport: 'streamable-http',
10
+ enabled: true,
11
+ addedAt: 0,
12
+ addedBy: 'user-1',
13
+ ...overrides,
14
+ };
15
+ }
16
+ describe('buildExternalMcpEntries', () => {
17
+ it('never injects the Runwork platform key onto external entries', () => {
18
+ const entries = buildExternalMcpEntries([
19
+ server({ id: 'a', authType: 'header' }),
20
+ server({ id: 'b', authType: 'oauth' }),
21
+ server({ id: 'c', authType: 'none' }),
22
+ server({ id: 'd' }), // authType unset
23
+ ], { a: { Authorization: 'Bearer user-token' } });
24
+ // Regression: the platform key must not appear anywhere in the output.
25
+ expect(JSON.stringify(entries)).not.toContain(PLATFORM_KEY);
26
+ });
27
+ it('injects resolved per-server headers for header-auth servers', () => {
28
+ const entries = buildExternalMcpEntries([server({ id: 'a', authType: 'header' })], { a: { Authorization: 'Bearer tok', 'X-Api-Key': 'key123' } });
29
+ expect(entries[0].headers).toEqual({ Authorization: 'Bearer tok', 'X-Api-Key': 'key123' });
30
+ });
31
+ it('emits no auth header for oauth servers (local agent runs its own flow)', () => {
32
+ const entries = buildExternalMcpEntries([server({ id: 'a', authType: 'oauth' })], { a: { Authorization: 'Bearer should-not-be-used' } });
33
+ expect(entries[0].headers).toBeUndefined();
34
+ });
35
+ it('emits no auth header for none/unset servers', () => {
36
+ const entries = buildExternalMcpEntries([server({ id: 'a', authType: 'none' }), server({ id: 'b' })], {});
37
+ expect(entries[0].headers).toBeUndefined();
38
+ expect(entries[1].headers).toBeUndefined();
39
+ });
40
+ it('omits headers when a header-auth server has no resolved credentials', () => {
41
+ const entries = buildExternalMcpEntries([server({ id: 'a', authType: 'header' })], {});
42
+ expect(entries[0].headers).toBeUndefined();
43
+ });
44
+ it('skips disabled servers', () => {
45
+ const entries = buildExternalMcpEntries([server({ id: 'a', enabled: false })], {});
46
+ expect(entries).toHaveLength(0);
47
+ });
48
+ });
@@ -1,13 +1,20 @@
1
1
  import { Command } from 'commander';
2
- import { execFileSync } from '../utils/subprocess.js';
3
2
  import { readFileSync, existsSync } from 'fs';
4
3
  import { requireAuth } from '../auth/store.js';
5
4
  import { ApiClient } from '../api/client.js';
6
5
  import { shouldOutputJson, jsonOut } from '../utils/output.js';
7
6
  import { buildDeployGuide, buildErrorResponse } from '../utils/agent-guidance.js';
7
+ import { requireGit } from '../git/preflight.js';
8
+ import { ensureGitIdentity } from '../git/identity.js';
9
+ import { commitWorkingTree } from '../git/auto-commit.js';
10
+ import { syncWithRemote, hasCommits } from '../git/sync.js';
11
+ import { snapshotCriticalFiles, restoreMissingCriticalFiles, commitAndPushRestoredFiles } from '../git/critical-files.js';
12
+ import { evaluateDeployGuard } from '../git/deploy-guard.js';
13
+ import { promptConfirm } from '../utils/prompt.js';
8
14
  export const deployCommand = new Command('deploy')
9
15
  .description('Deploy the current app to production')
10
- .action(async (_opts, command) => {
16
+ .option('-y, --yes', 'Skip the confirmation prompt when the working tree differs from the preview')
17
+ .action(async (opts, command) => {
11
18
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
12
19
  if (!existsSync('.runwork.json')) {
13
20
  if (useJson) {
@@ -17,36 +24,93 @@ export const deployCommand = new Command('deploy')
17
24
  console.error('No .runwork.json found. Run `runwork init` first.');
18
25
  process.exit(1);
19
26
  }
27
+ requireGit('deploy');
20
28
  const config = JSON.parse(readFileSync('.runwork.json', 'utf-8'));
21
29
  const creds = requireAuth();
22
30
  const client = new ApiClient(creds);
23
- // Sync: pull + push (skip if no commits yet)
24
- let repoHasCommits = false;
31
+ const cwd = process.cwd();
32
+ // Deploy guard: the preview reflects the last *pushed* commit. If the
33
+ // working tree is dirty or ahead of `runwork/main` and no auto-syncing
34
+ // dev session is keeping the preview in lockstep, `runwork deploy` will
35
+ // commit and ship code the user never saw in the preview. Warn first.
36
+ // Runs BEFORE the commit/sync/deploy steps below, which are unchanged.
37
+ const guard = evaluateDeployGuard(cwd, config.appId);
38
+ let deployGuardWarning;
39
+ if (guard.warn) {
40
+ deployGuardWarning = 'You have uncommitted or unpushed changes. `runwork deploy` will commit and deploy them, but they are not reflected in the preview.';
41
+ if (useJson) {
42
+ // Agents must never be blocked: the warning rides along in the
43
+ // final success response below so we proceed without prompting.
44
+ }
45
+ else if (opts.yes || !process.stdin.isTTY) {
46
+ // Non-interactive (no TTY) or explicit --yes: warn and proceed.
47
+ console.warn(deployGuardWarning);
48
+ }
49
+ else {
50
+ console.warn(deployGuardWarning);
51
+ const proceed = await promptConfirm('Deploy these changes anyway?');
52
+ if (!proceed) {
53
+ console.log('Deploy cancelled.');
54
+ process.exit(0);
55
+ }
56
+ }
57
+ }
58
+ // Deploy must work even when no `runwork dev` session is (or ever was)
59
+ // running. Reuse the exact sync path dev uses: commit any pending
60
+ // working-tree changes, then fetch -> rebase (fallback merge) -> push.
61
+ // `ensureGitIdentity` seeds a local identity so the commit cannot fail
62
+ // on a fresh machine where git user.email/.name were never configured.
63
+ ensureGitIdentity(cwd, creds);
64
+ if (!useJson)
65
+ console.log('Syncing...');
25
66
  try {
26
- execFileSync('git', ['rev-parse', 'HEAD'], { stdio: 'pipe' });
27
- repoHasCommits = true;
67
+ commitWorkingTree(cwd, `deploy: ${new Date().toISOString().replace('T', ' ').slice(0, 19)}`);
28
68
  }
29
69
  catch {
30
- // No commits yet
70
+ // No commit created (clean tree) or git not initialized yet. The
71
+ // sync/push step below surfaces the actionable failure.
72
+ }
73
+ if (!hasCommits(cwd)) {
74
+ if (useJson) {
75
+ jsonOut(buildErrorResponse('deploy', 'Nothing to deploy', 'No commits exist and the working tree has no changes to commit, so there is nothing to sync or deploy.', ['Make changes to your app first', 'Run runwork dev to develop and verify changes', 'Then run runwork deploy']));
76
+ process.exit(1);
77
+ }
78
+ console.error('Nothing to deploy. Make changes first, then run `runwork deploy`.');
79
+ process.exit(1);
31
80
  }
32
- if (repoHasCommits) {
33
- if (!useJson)
34
- console.log('Syncing...');
35
- try {
36
- execFileSync('git', ['pull', '--rebase', 'runwork', 'main'], { stdio: useJson ? 'pipe' : 'inherit' });
81
+ // Protect critical files across the sync exactly as `runwork dev` does:
82
+ // syncWithRemote may merge with `-X theirs` (remote wins) to reconcile
83
+ // diverged histories, which can delete `.runwork.json`/`blueprint.json`/
84
+ // `.gitignore` if the remote tip lacks them. Snapshot before, restore after.
85
+ const criticalSnapshot = snapshotCriticalFiles(cwd);
86
+ const syncResult = syncWithRemote(cwd);
87
+ const restoredCriticalFiles = restoreMissingCriticalFiles(cwd, criticalSnapshot);
88
+ if (restoredCriticalFiles.length > 0) {
89
+ const pushed = commitAndPushRestoredFiles(cwd, restoredCriticalFiles);
90
+ if (!useJson) {
91
+ console.warn(`Sync removed critical file(s); restored: ${restoredCriticalFiles.join(', ')}`);
92
+ if (!pushed)
93
+ console.warn('Restoration committed locally but not pushed; will retry on next sync.');
37
94
  }
38
- catch {
39
- if (!useJson)
40
- console.warn('Pull failed. Continuing...');
95
+ }
96
+ if (syncResult.status === 'sync-failed') {
97
+ const reason = syncResult.error && syncResult.error !== 'stash-conflict'
98
+ ? syncResult.error
99
+ : 'Local history could not be reconciled with the remote.';
100
+ if (useJson) {
101
+ jsonOut(buildErrorResponse('deploy', 'Sync failed before deployment', reason, ['Run git fetch runwork && git rebase runwork/main and resolve any conflicts', 'Or run runwork dev to sync interactively', 'Then retry runwork deploy']));
102
+ process.exit(1);
41
103
  }
42
- execFileSync('git', ['push', 'runwork', 'main'], { stdio: useJson ? 'pipe' : 'inherit' });
104
+ console.error(`Sync failed: ${reason}`);
105
+ console.error('Resolve conflicts (git status), or run `runwork dev`, then retry `runwork deploy`.');
106
+ process.exit(1);
43
107
  }
44
- else {
108
+ if (!syncResult.pushed) {
45
109
  if (useJson) {
46
- jsonOut(buildErrorResponse('deploy', 'Sync failed before deployment', 'Files could not be synced to the remote.', ['Run runwork dev first to ensure files sync correctly', 'Check your network connection', 'Retry runwork deploy']));
110
+ jsonOut(buildErrorResponse('deploy', 'Failed to push before deployment', 'Local commits were synced but could not be pushed to the remote.', ['Check your network connection', 'Run runwork dev to retry syncing', 'Then retry runwork deploy']));
47
111
  process.exit(1);
48
112
  }
49
- console.error('No commits to deploy. Commit your changes first.');
113
+ console.error('Push failed. Check your connection and retry `runwork deploy`.');
50
114
  process.exit(1);
51
115
  }
52
116
  // Trigger deploy
@@ -65,19 +129,34 @@ export const deployCommand = new Command('deploy')
65
129
  console.error(`Deploy failed: ${message}`);
66
130
  process.exit(1);
67
131
  }
132
+ // The server returns a deployment URL only when a deploy actually
133
+ // happened. An empty/missing URL means the deploy was a no-op or did
134
+ // not produce a live deployment, so we must not report success.
135
+ const deploymentUrl = result.deploymentUrl?.trim() ?? '';
136
+ const deployed = deploymentUrl.length > 0;
137
+ if (!deployed) {
138
+ if (useJson) {
139
+ jsonOut(buildErrorResponse('deploy', 'Deployment did not produce a URL', 'The deploy API returned no deployment URL. The deploy may have been a no-op (no changes) or failed server-side.', ['Confirm your changes were committed and pushed (the sync step above succeeded)', 'Run runwork info to check the deployed state', 'Run runwork logs --production to inspect server-side errors', 'Retry runwork deploy']));
140
+ process.exit(1);
141
+ }
142
+ console.error('Deploy did not return a URL. The deploy may have been a no-op or failed server-side.');
143
+ console.error('Run `runwork info` to check state, or `runwork logs --production` to inspect errors.');
144
+ process.exit(1);
145
+ }
68
146
  if (useJson) {
69
147
  const response = {
70
148
  success: true,
71
149
  command: 'deploy',
72
150
  result: {
73
- deployed: true,
74
- url: result.deploymentUrl,
151
+ deployed,
152
+ url: deploymentUrl,
75
153
  appName: config.appName,
76
154
  },
77
155
  guide: buildDeployGuide(),
156
+ ...(deployGuardWarning ? { warning: deployGuardWarning } : {}),
78
157
  };
79
158
  jsonOut(response);
80
159
  return;
81
160
  }
82
- console.log(`Deployed: ${result.deploymentUrl}`);
161
+ console.log(`Deployed: ${deploymentUrl}`);
83
162
  });
@@ -6,5 +6,6 @@ export declare function execDev(options?: {
6
6
  json?: boolean;
7
7
  mode?: SessionMode;
8
8
  restart?: boolean;
9
+ noSync?: boolean;
9
10
  }): Promise<void>;
10
11
  export declare const devCommand: Command;