@frontera-sdk/cli 1.50.79 → 1.50.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 (37) hide show
  1. package/README.md +6 -0
  2. package/package.json +4 -4
  3. package/src/api/blueprint-authoring-api.ts +52 -0
  4. package/src/api/credential-failure.ts +34 -4
  5. package/src/api/dataset-api.ts +1 -1
  6. package/src/api/governed-action-api.ts +75 -7
  7. package/src/api/media-api.ts +181 -0
  8. package/src/api/platform-api.ts +59 -0
  9. package/src/api/workflow-api.ts +1 -1
  10. package/src/auth-verify.ts +1 -1
  11. package/src/commands/action/available.ts +59 -0
  12. package/src/commands/action/cancel.ts +34 -0
  13. package/src/commands/action/decide.ts +54 -0
  14. package/src/commands/action/deploy.ts +4 -1
  15. package/src/commands/action/index-commands.ts +16 -6
  16. package/src/commands/action/prepare.ts +3 -1
  17. package/src/commands/action/request-summary.ts +13 -0
  18. package/src/commands/action/requests.ts +6 -5
  19. package/src/commands/action/submit.ts +183 -0
  20. package/src/commands/blueprint/get.ts +139 -29
  21. package/src/commands/chat-app/index-commands.ts +475 -0
  22. package/src/commands/knowledge/upload-plan.ts +14 -8
  23. package/src/commands/marking/index-commands.ts +171 -0
  24. package/src/commands/media/index-commands.ts +592 -0
  25. package/src/commands/registry.ts +28 -6
  26. package/src/commands/types.ts +6 -0
  27. package/src/commands/workflow/list.ts +1 -1
  28. package/src/errors.ts +3 -2
  29. package/src/exit.ts +126 -1
  30. package/src/flag-help.ts +24 -2
  31. package/src/forge/touches.ts +2 -0
  32. package/src/harness.ts +5 -3
  33. package/src/main.ts +23 -38
  34. package/src/scopes.ts +43 -0
  35. package/src/template.ts +6 -4
  36. package/src/templates/next-app-files.ts +8 -4
  37. package/src/vendor/sdk-sources.json +16 -15
@@ -0,0 +1,171 @@
1
+ import { BlueprintAuthoringApi, type MarkingPrincipal } from '../../api/blueprint-authoring-api'
2
+ import { UsageError } from '../../errors'
3
+ import { table } from '../../table'
4
+ import { flagString, type Command, type CommandContext } from '../types'
5
+
6
+ /**
7
+ * Security markings — the audiences a marked Blueprint row is visible to.
8
+ *
9
+ * Its own noun rather than verbs under `blueprint`, because `blueprint create`,
10
+ * `grant` and `delete` already mean object types there. Declarative Blueprint
11
+ * files say WHICH column carries a row's markings (`markingsColumn`); nothing
12
+ * there registers a marking or grants one, so this does not duplicate them.
13
+ *
14
+ * Every verb needs a person's session. The service gates reads on `role:read`
15
+ * and writes on `role:create` / `role:update`, at organization scope. `role` is
16
+ * non-delegable, so no organization key can ever hold it; the router is also
17
+ * closed to organization-key writes, because granting a marking grants an
18
+ * audience; and a workspace key never satisfies an organization-scoped check.
19
+ * So all five verbs declare the session lane — the dispatcher refuses a key
20
+ * before any request — and name where a person does this in the platform.
21
+ * They also declare organization scope themselves: the registry has no
22
+ * workspace, so `--workspace` is refused rather than silently ignored.
23
+ */
24
+
25
+ const KINDS = ['workspace', 'role', 'user', 'agent'] as const
26
+
27
+ function api(ctx: CommandContext): BlueprintAuthoringApi {
28
+ return new BlueprintAuthoringApi(ctx.apiUrl, ctx.token)
29
+ }
30
+
31
+ /** `role:analyst` → `{ kind: 'role', id: 'analyst' }`. Split on the first colon only. */
32
+ export function parsePrincipal(raw: string | undefined, example: string): MarkingPrincipal {
33
+ const at = raw?.indexOf(':') ?? -1
34
+ const kind = at > 0 ? raw!.slice(0, at) : ''
35
+ const id = at > 0 ? raw!.slice(at + 1) : ''
36
+ if (!(KINDS as readonly string[]).includes(kind) || !id) {
37
+ throw new UsageError(
38
+ raw ? `"${raw}" is not a principal` : 'missing <principal>',
39
+ `write it as <kind>:<id>, kind one of ${KINDS.join(', ')} — e.g. ${example}`,
40
+ )
41
+ }
42
+ return { kind: kind as MarkingPrincipal['kind'], id }
43
+ }
44
+
45
+ const list: Command = {
46
+ meta: {
47
+ noun: 'marking',
48
+ verb: 'list',
49
+ authLane: 'session',
50
+ scope: 'organization',
51
+ sessionAlternative: 'Blueprint → Access → Markings',
52
+ args: [],
53
+ flags: {},
54
+ summary: 'List the security markings registered in this organization',
55
+ examples: ['frontera marking list', 'frontera marking list --json'],
56
+ },
57
+ async run(ctx) {
58
+ const { markings } = await api(ctx).markings()
59
+ return {
60
+ data: markings,
61
+ text:
62
+ markings.length === 0
63
+ ? 'No security markings in this organization.'
64
+ : table(
65
+ ['marking', 'display name', 'description'],
66
+ markings.map((m) => [m.marking, m.displayName ?? '', m.description ?? '']),
67
+ [undefined, 40, 60],
68
+ ),
69
+ }
70
+ },
71
+ }
72
+
73
+ const create: Command = {
74
+ meta: {
75
+ noun: 'marking',
76
+ verb: 'create',
77
+ authLane: 'session',
78
+ scope: 'organization',
79
+ sessionAlternative: 'Blueprint → Access → Markings',
80
+ args: [{ name: 'marking', required: true, description: '1-64 letters, digits and _ : . -' }],
81
+ flags: { 'display-name': 'string', description: 'string' },
82
+ summary: 'Register a security marking, or restore a retired one',
83
+ examples: [
84
+ 'frontera marking create pii --display-name "Personal data"',
85
+ 'frontera marking create finance:restricted --description "Board finance only"',
86
+ ],
87
+ },
88
+ async run(ctx) {
89
+ const marking = ctx.positional[0]
90
+ if (!marking) throw new UsageError('missing <marking>', 'frontera marking create pii')
91
+ const displayName = flagString(ctx, 'display-name')
92
+ const description = flagString(ctx, 'description')
93
+
94
+ const result = await api(ctx).createMarking({
95
+ marking,
96
+ ...(displayName === undefined ? {} : { displayName }),
97
+ ...(description === undefined ? {} : { description }),
98
+ })
99
+ return {
100
+ data: result,
101
+ text: `Registered marking "${result.marking}".\n`
102
+ + ` grant it: frontera marking grant ${result.marking} role:<role>`,
103
+ }
104
+ },
105
+ }
106
+
107
+ const grants: Command = {
108
+ meta: {
109
+ noun: 'marking',
110
+ verb: 'grants',
111
+ authLane: 'session',
112
+ scope: 'organization',
113
+ sessionAlternative: 'Blueprint → Access → Markings',
114
+ args: [{ name: 'principal', required: true, description: '<kind>:<id> — workspace, role, user or agent' }],
115
+ flags: {},
116
+ summary: 'List the markings granted directly to one principal',
117
+ examples: [
118
+ 'frontera marking grants role:analyst',
119
+ 'frontera marking grants workspace:<workspace-id> --json',
120
+ ],
121
+ },
122
+ async run(ctx) {
123
+ const principal = parsePrincipal(ctx.positional[0], 'role:analyst')
124
+ const { markings } = await api(ctx).principalMarkings(principal)
125
+ const who = `${principal.kind} "${principal.id}"`
126
+ return {
127
+ data: { principalKind: principal.kind, principalId: principal.id, markings },
128
+ text: markings.length === 0
129
+ ? `No markings granted to ${who}.`
130
+ : `${who}: ${markings.join(', ')}`,
131
+ }
132
+ },
133
+ }
134
+
135
+ function grantCommand(verb: 'grant' | 'revoke'): Command {
136
+ const revoke = verb === 'revoke'
137
+ return {
138
+ meta: {
139
+ noun: 'marking',
140
+ verb,
141
+ authLane: 'session',
142
+ scope: 'organization',
143
+ sessionAlternative: 'Blueprint → Access → Markings',
144
+ args: [
145
+ { name: 'marking', required: true, description: 'A registered marking, from `frontera marking list`' },
146
+ { name: 'principal', required: true, description: '<kind>:<id> — workspace, role, user or agent' },
147
+ ],
148
+ flags: {},
149
+ summary: revoke
150
+ ? 'Revoke a marking from a principal, effective on its next request'
151
+ : 'Grant a marking to a workspace, role, user or agent',
152
+ examples: revoke
153
+ ? ['frontera marking revoke pii role:analyst']
154
+ : ['frontera marking grant pii role:analyst', 'frontera marking grant pii workspace:<workspace-id>'],
155
+ },
156
+ async run(ctx) {
157
+ const [marking, raw] = ctx.positional
158
+ if (!marking) throw new UsageError('missing <marking>', `frontera marking ${verb} pii role:analyst`)
159
+ const principal = parsePrincipal(raw, `frontera marking ${verb} ${marking} role:analyst`)
160
+
161
+ if (revoke) await api(ctx).revokeMarking(marking, principal)
162
+ else await api(ctx).grantMarking(marking, principal)
163
+ return {
164
+ data: { marking, principalKind: principal.kind, principalId: principal.id, [revoke ? 'revoked' : 'granted']: true },
165
+ text: `${revoke ? 'Revoked' : 'Granted'} "${marking}" ${revoke ? 'from' : 'to'} ${principal.kind} "${principal.id}".`,
166
+ }
167
+ },
168
+ }
169
+ }
170
+
171
+ export const markingCommands: Command[] = [list, create, grants, grantCommand('grant'), grantCommand('revoke')]