@frontera-sdk/cli 1.52.44 → 1.52.46

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 (110) hide show
  1. package/package.json +4 -4
  2. package/src/api/apps-api.ts +3 -1
  3. package/src/api/automation-api.ts +32 -0
  4. package/src/api/credential-failure.ts +1 -1
  5. package/src/api/governed-action-api.ts +24 -0
  6. package/src/api/media-api.ts +24 -0
  7. package/src/api/platform-api.ts +13 -0
  8. package/src/binding-registry.ts +9 -2
  9. package/src/blueprint/compile.ts +3 -1
  10. package/src/blueprint/ontology-edit-plan.ts +71 -4
  11. package/src/blueprint/org-name-check.ts +64 -0
  12. package/src/blueprint/tree.ts +28 -0
  13. package/src/commands/action/deploy.ts +22 -5
  14. package/src/commands/action/index-commands.ts +2 -0
  15. package/src/commands/action/retire.ts +60 -0
  16. package/src/commands/agent/compose.ts +1 -0
  17. package/src/commands/agent/index-commands.ts +42 -0
  18. package/src/commands/app/deploy.ts +1 -1
  19. package/src/commands/app/dev.ts +2 -2
  20. package/src/commands/app/save.ts +1 -1
  21. package/src/commands/auth/add.ts +5 -1
  22. package/src/commands/auth/current.ts +6 -0
  23. package/src/commands/auth/index-commands.ts +3 -0
  24. package/src/commands/auth/login.ts +84 -0
  25. package/src/commands/auth/use.ts +21 -2
  26. package/src/commands/automation/bindings.ts +54 -10
  27. package/src/commands/automation/index-commands.ts +2 -3
  28. package/src/commands/blueprint/bind.ts +12 -1
  29. package/src/commands/blueprint/declarative.ts +6 -2
  30. package/src/commands/conversation/index-commands.ts +66 -0
  31. package/src/commands/conversation/local-runtime.ts +18 -0
  32. package/src/commands/kit/vendor.ts +62 -48
  33. package/src/commands/local/check.ts +57 -11
  34. package/src/commands/local/data.ts +75 -7
  35. package/src/commands/local/dev.ts +77 -9
  36. package/src/commands/local/embeddings.ts +74 -0
  37. package/src/commands/local/index-commands.ts +133 -11
  38. package/src/commands/local/init.ts +22 -2
  39. package/src/commands/local/new.ts +78 -0
  40. package/src/commands/local/publish.ts +34 -0
  41. package/src/commands/local/steps.ts +105 -0
  42. package/src/commands/local/sync-request.ts +69 -23
  43. package/src/commands/local/sync.ts +2 -0
  44. package/src/commands/media/index-commands.ts +58 -3
  45. package/src/commands/registry.ts +11 -3
  46. package/src/commands/trace/index-commands.ts +56 -0
  47. package/src/commands/workspace/index-commands.ts +14 -7
  48. package/src/config.ts +4 -1
  49. package/src/dev-broker.ts +15 -1
  50. package/src/device-login.ts +143 -0
  51. package/src/exit.ts +36 -8
  52. package/src/flag-help.ts +31 -0
  53. package/src/forge/claude-adapter.ts +17 -4
  54. package/src/forge/permission-rules.ts +158 -0
  55. package/src/forge/touches.ts +7 -6
  56. package/src/function-outside-imports.ts +13 -2
  57. package/src/function-source.ts +90 -0
  58. package/src/harness.ts +4 -4
  59. package/src/key-reach.ts +64 -0
  60. package/src/local/local-actions.ts +15 -3
  61. package/src/local/local-apply-error.ts +26 -0
  62. package/src/local/local-apps.ts +9 -3
  63. package/src/local/local-blueprint-schemas.ts +15 -1
  64. package/src/local/local-confidential.ts +81 -0
  65. package/src/local/local-data-load.ts +130 -0
  66. package/src/local/local-dev-references.ts +24 -4
  67. package/src/local/local-embedding-setup.ts +62 -0
  68. package/src/local/local-jobs.ts +108 -0
  69. package/src/local/local-keys.ts +5 -3
  70. package/src/local/local-new.ts +148 -0
  71. package/src/local/local-org-settings.ts +1 -1
  72. package/src/local/local-person-steps.ts +340 -0
  73. package/src/local/local-personas.ts +16 -5
  74. package/src/local/local-portability.ts +22 -1
  75. package/src/local/local-publish.ts +26 -0
  76. package/src/local/local-request-evidence.ts +35 -6
  77. package/src/local/local-runtime-history.ts +4 -3
  78. package/src/local/local-said-once.ts +51 -0
  79. package/src/local/local-scaffold.ts +40 -2
  80. package/src/local/local-schemas.ts +60 -11
  81. package/src/local/local-seed.ts +1 -1
  82. package/src/local/local-stand-ins.ts +44 -0
  83. package/src/local/local-studio-server.ts +13 -7
  84. package/src/local/local-supervisor.ts +22 -0
  85. package/src/local/local-sync-models.ts +5 -2
  86. package/src/local/local-sync-names.ts +3 -0
  87. package/src/local/local-sync-portable.ts +64 -15
  88. package/src/local/local-sync-precheck.ts +30 -1
  89. package/src/local/local-sync-push.ts +81 -8
  90. package/src/local/local-sync-remote.ts +10 -7
  91. package/src/local/local-test-models.ts +173 -0
  92. package/src/local/local-test-runner.ts +232 -25
  93. package/src/local/local-test-scope.ts +174 -0
  94. package/src/local/local-ungranted-reads.ts +55 -0
  95. package/src/local/local-writers.ts +171 -0
  96. package/src/main.ts +75 -17
  97. package/src/mcp/browser/ax-snapshot.ts +13 -1
  98. package/src/mcp/browser/page-scripts.ts +1 -0
  99. package/src/mcp/mcp-conversations.ts +171 -0
  100. package/src/mcp/mcp-server.ts +4 -1
  101. package/src/mcp/mcp-traces.ts +7 -2
  102. package/src/mcp/tools/agent-tools.ts +94 -2
  103. package/src/mcp/tools/project-tools.ts +149 -13
  104. package/src/mcp/tools/test-tools.ts +64 -13
  105. package/src/output.ts +22 -7
  106. package/src/pack.ts +16 -0
  107. package/src/project-context.ts +18 -5
  108. package/src/project.ts +4 -0
  109. package/src/remote-guard.ts +52 -0
  110. package/src/vendor/kit-assets.json +67 -6
package/src/pack.ts CHANGED
@@ -2,6 +2,7 @@ import { collectSourceFiles, formatBytes, type SourceFile } from './packaging'
2
2
  import { createTarGz } from './tar'
3
3
  import type { Output } from './output'
4
4
  import { tsconfigAsKept } from './local/local-app-sdk-paths'
5
+ import { functionSourceFiles, IGNORE_FILE, sourceSizeWarning } from './function-source'
5
6
 
6
7
  /**
7
8
  * Package a directory for upload, reporting its size.
@@ -21,3 +22,18 @@ export function packDirectory(dir: string, output: Output, label: string, extra:
21
22
  output.note(` ${label} ${files.length} files, ${formatBytes(totalBytes)}`)
22
23
  return { tgz: createTarGz(files), fileCount: files.length, totalBytes }
23
24
  }
25
+
26
+ /**
27
+ * A Function's source (`function-source.ts`): what it reaches, less what
28
+ * `.fronteraignore` leaves out, with the size line and, past the limits, a
29
+ * warning naming the largest files.
30
+ */
31
+ export function packFunctionSource(packageRoot: string, entry: string, output: Output, label: string) {
32
+ const { files, reachedOnly, ignored } = functionSourceFiles(packageRoot, entry)
33
+ const totalBytes = files.reduce((n, f) => n + f.bytes.byteLength, 0)
34
+ const how = [reachedOnly ? 'the files it reaches' : null, ignored ? `${ignored} left out by ${IGNORE_FILE}` : null].filter(Boolean).join('; ')
35
+ output.note(` ${label} ${files.length} files, ${formatBytes(totalBytes)}${how ? ` (${how})` : ''}`)
36
+ const warning = sourceSizeWarning(label.replace(/^source of /, ''), files)
37
+ if (warning) output.note(` warning: ${warning}`)
38
+ return { tgz: createTarGz(files), fileCount: files.length, totalBytes, warning }
39
+ }
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs'
2
2
  import { dirname, join, resolve } from 'node:path'
3
3
 
4
- import { isTrustedBinding, recordBinding } from './binding-registry'
4
+ import { isTrustedBinding, isTrustedOrganizationBinding, recordBinding } from './binding-registry'
5
5
  import { findProjectRoot } from './context'
6
6
  import { CliError } from './errors'
7
7
  import { writeFileAtomic } from './fs-atomic'
@@ -28,6 +28,13 @@ export const CONTEXT_FILE = 'context.json'
28
28
  export interface ProjectContext {
29
29
  schemaVersion: 1
30
30
  profile: string
31
+ /**
32
+ * The organization profile organization-scoped commands use here, beside a
33
+ * workspace `profile` (X3: `auth use <profile> --org-profile <name>`). Named,
34
+ * never inferred: two customers can share one host, so an organization key
35
+ * found by its origin could be another customer's.
36
+ */
37
+ organizationProfile?: string
31
38
  }
32
39
 
33
40
  export interface FoundContext {
@@ -36,6 +43,10 @@ export interface FoundContext {
36
43
  path: string
37
44
  /** The directory that owns it — the binding root. */
38
45
  root: string
46
+ /** The organization profile the binding names for organization-scoped commands, when it names one. */
47
+ organizationProfile?: string
48
+ /** This machine bound `organizationProfile` here too. */
49
+ organizationTrusted?: boolean
39
50
  /**
40
51
  * Did THIS machine bind this root to this profile?
41
52
  *
@@ -65,7 +76,9 @@ export function readContextAt(root: string, env: Env): FoundContext | null {
65
76
  if (typeof profile !== 'string' || profile.length === 0) {
66
77
  throw corrupt(path, 'it names no profile')
67
78
  }
68
- return { profile, path, root, trusted: isTrustedBinding(root, profile, env) }
79
+ const org = (parsed as { organizationProfile?: unknown }).organizationProfile
80
+ const organization = typeof org === 'string' && org ? { organizationProfile: org, organizationTrusted: isTrustedOrganizationBinding(root, org, env) } : {}
81
+ return { profile, path, root, trusted: isTrustedBinding(root, profile, env), ...organization }
69
82
  }
70
83
 
71
84
  /**
@@ -136,9 +149,9 @@ export function resolveBindingRoot(
136
149
  * is a selection the resolver will refuse — a failure mode nobody would connect
137
150
  * to having called only half of the pair.
138
151
  */
139
- export function writeContext(root: string, profile: string, env: Env = process.env): string {
152
+ export function writeContext(root: string, profile: string, env: Env = process.env, organizationProfile?: string): string {
140
153
  const path = contextPath(root)
141
- const body: ProjectContext = { schemaVersion: 1, profile }
154
+ const body: ProjectContext = { schemaVersion: 1, profile, ...(organizationProfile ? { organizationProfile } : {}) }
142
155
  try {
143
156
  writeFileAtomic(path, `${JSON.stringify(body, null, 2)}\n`, 0o600)
144
157
  } catch (err) {
@@ -161,7 +174,7 @@ export function writeContext(root: string, profile: string, env: Env = process.e
161
174
  }
162
175
  throw err
163
176
  }
164
- recordBinding(root, profile, env)
177
+ recordBinding(root, profile, env, organizationProfile)
165
178
  return path
166
179
  }
167
180
 
package/src/project.ts CHANGED
@@ -11,6 +11,8 @@ export interface ProjectConfig {
11
11
  slug: string
12
12
  displayName: string
13
13
  description?: string
14
+ /** Bootstrap icon for the first deploy, from the platform's set by name (X6); the platform owns it after, as the name. */
15
+ icon?: string
14
16
  /**
15
17
  * The version a deploy names as its parent: the last deployment from this
16
18
  * folder, else the version the working tree was pulled from.
@@ -57,6 +59,7 @@ export function readProject(dir: string): ProjectConfig {
57
59
  let config: {
58
60
  displayName?: string
59
61
  description?: string
62
+ icon?: unknown
60
63
  connectDomains?: unknown
61
64
  resourceDomains?: unknown
62
65
  outputDirectory?: string
@@ -87,6 +90,7 @@ export function readProject(dir: string): ProjectConfig {
87
90
  slug,
88
91
  displayName: config.displayName ?? pkg.frontera?.displayName ?? slug,
89
92
  description: config.description ?? pkg.frontera?.description,
93
+ ...(typeof config.icon === 'string' && config.icon.trim() ? { icon: config.icon.trim() } : {}),
90
94
  // A folder never deployed from keeps its lineage: the version it was pulled from is the parent.
91
95
  parentVersion: state.parentVersion ?? state.pulledVersion ?? null,
92
96
  connectDomains: stringList(config.connectDomains ?? pkg.frontera?.connectDomains, 'connectDomains'),
@@ -0,0 +1,52 @@
1
+ import { FRONTERA_READ_ONLY_COMMANDS } from './forge/permission-rules'
2
+ import { findSolutionRoot, localRuntimeProfileOf } from './local/local-project'
3
+
4
+ /**
5
+ * A command from a local project that would change a remote deployment it
6
+ * was not told to use (Forge improvement plan X3, T7; P4 2.3: "in the project
7
+ * folder the CLI defaults to the production profile", and only the local-only
8
+ * `--as` flag stopped an `action submit` reaching it).
9
+ *
10
+ * In a project with its own local runtime, a mutating command whose profile
11
+ * reaches a deployment other than the local runtime, and was not chosen with
12
+ * `--profile` on this command, names where it is going and asks first. Where
13
+ * no one can answer (an agent's shell, CI) it is refused, naming the flag that
14
+ * says so. Reads, offline commands, `auth`, `kit` and `local` (which choose
15
+ * their own profiles) are never stopped.
16
+ */
17
+
18
+ /** Verbs that only read, beside the exact read-only paths the Forge permission rules list. */
19
+ const READ_VERBS = new Set(['list', 'get', 'status', 'show', 'diff', 'current', 'whoami', 'logs', 'query', 'search', 'versions', 'runs', 'plan', 'validate', 'catalog', 'available', 'installs', 'sources', 'requests', 'deployments', 'checks', 'lineage', 'bindings', 'object-sets', 'agents', 'help'])
20
+
21
+ /** Nouns that pick their own profile, or touch no deployment. */
22
+ const OWN_PROFILE_NOUNS = new Set(['auth', 'kit', 'local', 'help', 'setup', 'init', 'completion', 'mcp', 'forge'])
23
+
24
+ export function isReadOnlyCommand(meta: { noun: string; verb?: string }): boolean {
25
+ const path = `${meta.noun}${meta.verb ? ` ${meta.verb}` : ''}`
26
+ return FRONTERA_READ_ONLY_COMMANDS.includes(path) || (!!meta.verb && READ_VERBS.has(meta.verb))
27
+ }
28
+
29
+ const LOOPBACK = /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?(\/|$)/i
30
+
31
+ /**
32
+ * Where a command is going when the guard should ask, or null when it runs
33
+ * as it is: the profile and its origin.
34
+ */
35
+ export function remoteFromLocalProject(input: {
36
+ meta: { noun: string; verb?: string; offline?: boolean }
37
+ cwd: string
38
+ profile: string | null
39
+ profileSource: string
40
+ apiUrl: string
41
+ }): { profile: string; apiUrl: string; localProfile: string } | null {
42
+ if (input.meta.offline || OWN_PROFILE_NOUNS.has(input.meta.noun) || isReadOnlyCommand(input.meta)) return null
43
+ if (input.profileSource === 'flag' || !input.profile || LOOPBACK.test(input.apiUrl)) return null
44
+ const localProfile = localRuntimeProfileOf(findSolutionRoot(input.cwd))
45
+ if (!localProfile || input.profile === localProfile || input.profile.startsWith(`${localProfile}-`)) return null
46
+ return { profile: input.profile, apiUrl: input.apiUrl, localProfile }
47
+ }
48
+
49
+ /** The question, or the refusal's message: what runs where. */
50
+ export function remoteWords(label: string, target: { profile: string; apiUrl: string; localProfile: string }): string {
51
+ return `\`frontera ${label}\` would change ${target.apiUrl} (profile ${target.profile}), not this project's local runtime (${target.localProfile})`
52
+ }