@frontera-sdk/cli 0.1.0 → 1.43.5
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.
- package/package.json +4 -2
- package/src/api/apps-api.ts +13 -1
- package/src/api/automation-api.ts +129 -1
- package/src/api/blueprint-authoring-api.ts +574 -0
- package/src/api/dataset-api.ts +199 -0
- package/src/api/platform-api.ts +300 -0
- package/src/automation-template.ts +224 -0
- package/src/blueprint/compile.ts +371 -0
- package/src/blueprint/dataset-revision.ts +33 -0
- package/src/blueprint/diff.ts +223 -0
- package/src/blueprint/model.ts +227 -0
- package/src/blueprint/projection.ts +254 -0
- package/src/blueprint/render.ts +73 -0
- package/src/blueprint/scaffold.ts +79 -0
- package/src/blueprint/tree.ts +121 -0
- package/src/commands/agent/index-commands.ts +87 -1
- package/src/commands/app/deploy.ts +43 -3
- package/src/commands/app/init.ts +23 -1
- package/src/commands/app/pull.ts +12 -35
- package/src/commands/automation/index-commands.ts +42 -1
- package/src/commands/automation/init.ts +52 -0
- package/src/commands/automation/project-root.ts +58 -0
- package/src/commands/automation/pull.ts +124 -0
- package/src/commands/automation/run.ts +271 -0
- package/src/commands/blueprint/authoring.ts +410 -0
- package/src/commands/blueprint/bind.ts +228 -0
- package/src/commands/blueprint/declarative.ts +1052 -0
- package/src/commands/blueprint/grants.ts +164 -0
- package/src/commands/dataset/index-commands.ts +431 -0
- package/src/commands/knowledge/index-commands.ts +278 -27
- package/src/commands/knowledge/upload-batch.ts +146 -0
- package/src/commands/knowledge/upload-plan.ts +127 -0
- package/src/commands/login.ts +49 -11
- package/src/commands/pack/index-commands.ts +373 -0
- package/src/commands/registry.ts +19 -2
- package/src/commands/secret/index-commands.ts +195 -0
- package/src/commands/skill/bundle-commands.ts +327 -0
- package/src/commands/skill/index-commands.ts +36 -42
- package/src/commands/skill/resolve.ts +34 -0
- package/src/dev-env.ts +114 -0
- package/src/flag-help.ts +34 -0
- package/src/harness.ts +30 -3
- package/src/main.ts +10 -3
- package/src/render-evidence.ts +152 -0
- package/src/template.ts +4 -0
- package/src/untar.ts +44 -0
- package/src/vendor/sdk-sources.json +13 -11
- package/src/commands/blueprint/reserved.ts +0 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontera-sdk/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.43.5",
|
|
4
4
|
"description": "The frontera CLI — scaffold, pull, save and deploy Frontera apps and automations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontera",
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@frontera-sdk/automation": "0.1.0",
|
|
41
|
-
"@frontera-sdk/core": "0.1.0"
|
|
41
|
+
"@frontera-sdk/core": "0.1.0",
|
|
42
|
+
"gray-matter": "^4.0.3",
|
|
43
|
+
"yaml": "^2.9.0"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@types/bun": "latest",
|
package/src/api/apps-api.ts
CHANGED
|
@@ -105,6 +105,8 @@ export class AppsApi {
|
|
|
105
105
|
source: Uint8Array
|
|
106
106
|
manifest?: unknown
|
|
107
107
|
promote: boolean
|
|
108
|
+
/** §4.4 — whether this tree had been rendered since its last source change. */
|
|
109
|
+
renderEvidence?: unknown
|
|
108
110
|
}) {
|
|
109
111
|
const form = new FormData()
|
|
110
112
|
form.set('version', args.version)
|
|
@@ -113,7 +115,17 @@ export class AppsApi {
|
|
|
113
115
|
form.set('source', new Blob([args.source], { type: 'application/gzip' }), 'src.tgz')
|
|
114
116
|
if (args.manifest) form.set('manifest', JSON.stringify(args.manifest))
|
|
115
117
|
form.set('promote', args.promote ? 'true' : 'false')
|
|
116
|
-
|
|
118
|
+
if (args.renderEvidence) form.set('renderEvidence', JSON.stringify(args.renderEvidence))
|
|
119
|
+
return this.multipart<{
|
|
120
|
+
version: string
|
|
121
|
+
promoted: boolean
|
|
122
|
+
fileCount: number
|
|
123
|
+
totalBytes: number
|
|
124
|
+
/** Where a person opens this exact build — composed by the service,
|
|
125
|
+
* which is the only side that knows the platform origin. */
|
|
126
|
+
previewUrl?: string
|
|
127
|
+
liveUrl?: string
|
|
128
|
+
}>(
|
|
117
129
|
`/v1/platform-apps/${appId}/versions`,
|
|
118
130
|
form,
|
|
119
131
|
)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { FronteraClient } from '@frontera-sdk/core/client'
|
|
2
2
|
import { FronteraError } from '@frontera-sdk/core/errors'
|
|
3
3
|
|
|
4
|
+
import { CliError } from '../errors'
|
|
5
|
+
|
|
4
6
|
/** A row of `GET /v1/automations/` — the shell, not any one version of it. */
|
|
5
7
|
export interface AutomationSummary {
|
|
6
8
|
id: string
|
|
@@ -40,6 +42,49 @@ export interface AutomationDeployResult {
|
|
|
40
42
|
runnerLastSeenAt?: string | null
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
/** A row of `GET /v1/automations/<slug>/runs`. */
|
|
46
|
+
export interface AutomationRunSummary {
|
|
47
|
+
id: string
|
|
48
|
+
status: string
|
|
49
|
+
triggerSource: string
|
|
50
|
+
triggeredBy: string | null
|
|
51
|
+
errorMessage: string | null
|
|
52
|
+
/** What the handler RETURNED. Null when it returned nothing, or has not finished. */
|
|
53
|
+
result: unknown
|
|
54
|
+
/** Billable ctx calls the run made. */
|
|
55
|
+
ctxCalls: number
|
|
56
|
+
startedAt: string
|
|
57
|
+
finishedAt: string | null
|
|
58
|
+
version: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A row of `GET /v1/automations/runs/<id>/steps`. */
|
|
62
|
+
export interface AutomationRunStep {
|
|
63
|
+
id: string
|
|
64
|
+
seq: number
|
|
65
|
+
kind: string
|
|
66
|
+
label: string
|
|
67
|
+
status: string
|
|
68
|
+
durationMs: number | null
|
|
69
|
+
/** The author's step name, or null when the row is a `ctx` call. */
|
|
70
|
+
stepName: string | null
|
|
71
|
+
/** The step this row ran inside, or null at the top level. */
|
|
72
|
+
parentStepId: string | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `GET /v1/automations/registry-status`. */
|
|
76
|
+
export interface RegistryStatus {
|
|
77
|
+
/** The generation the service has. */
|
|
78
|
+
generation: number
|
|
79
|
+
/**
|
|
80
|
+
* The generation a runner has REGISTERED with the broker, or null if none ever
|
|
81
|
+
* has. While this lags `generation`, a version deployed in between is not yet
|
|
82
|
+
* invocable.
|
|
83
|
+
*/
|
|
84
|
+
registeredGeneration: number | null
|
|
85
|
+
lastPolledAt: string | null
|
|
86
|
+
}
|
|
87
|
+
|
|
43
88
|
/**
|
|
44
89
|
* Client for the automation routes.
|
|
45
90
|
*
|
|
@@ -80,6 +125,41 @@ export class AutomationApi {
|
|
|
80
125
|
)
|
|
81
126
|
}
|
|
82
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Queue a run of the live version.
|
|
130
|
+
*
|
|
131
|
+
* Returns as soon as the invoke event is accepted — the run itself is
|
|
132
|
+
* asynchronous, which is why the caller polls `runs()` rather than being
|
|
133
|
+
* handed a result here. Ignores `enabled` on purpose: disable is a schedule
|
|
134
|
+
* kill switch, not a lock, so an operator can test a fix without re-arming the
|
|
135
|
+
* cron.
|
|
136
|
+
*/
|
|
137
|
+
run(slug: string): Promise<{ slug: string; version: number; queued: boolean }> {
|
|
138
|
+
return this.client.request<{ slug: string; version: number; queued: boolean }>(
|
|
139
|
+
`/v1/automations/${encodeURIComponent(slug)}/run`,
|
|
140
|
+
{ method: 'POST' },
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
registryStatus(): Promise<RegistryStatus> {
|
|
145
|
+
return this.client.request<RegistryStatus>('/v1/automations/registry-status')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
runs(slug: string, limit?: number): Promise<AutomationRunSummary[]> {
|
|
149
|
+
const query = limit === undefined ? '' : `?limit=${limit}`
|
|
150
|
+
return this.client.request<AutomationRunSummary[]>(
|
|
151
|
+
`/v1/automations/${encodeURIComponent(slug)}/runs${query}`,
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** One run's trail. Workspace-scoped server-side, so a guessed id reads
|
|
156
|
+
* nothing. */
|
|
157
|
+
runSteps(runId: string): Promise<AutomationRunStep[]> {
|
|
158
|
+
return this.client.request<AutomationRunStep[]>(
|
|
159
|
+
`/v1/automations/runs/${encodeURIComponent(runId)}/steps`,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
83
163
|
setEnabled(slug: string, enabled: boolean): Promise<{ slug: string; enabled: boolean }> {
|
|
84
164
|
return this.client.request<{ slug: string; enabled: boolean }>(
|
|
85
165
|
`/v1/automations/${encodeURIComponent(slug)}/enabled`,
|
|
@@ -95,13 +175,61 @@ export class AutomationApi {
|
|
|
95
175
|
* way — its schema is `t.Unknown()` for exactly that reason — and sending the
|
|
96
176
|
* string keeps this end honest about what a form field is.
|
|
97
177
|
*/
|
|
178
|
+
/** The stored source archive for a version, as gzip bytes. */
|
|
179
|
+
async downloadSource(slug: string, version: number): Promise<Uint8Array> {
|
|
180
|
+
const credential = this.client.config.credential
|
|
181
|
+
const res = await fetch(
|
|
182
|
+
`${this.client.config.apiBaseUrl}/v1/automations/${encodeURIComponent(slug)}`
|
|
183
|
+
+ `/versions/${version}/source`,
|
|
184
|
+
{
|
|
185
|
+
headers: {
|
|
186
|
+
authorization: `Bearer ${credential.kind === 'apiKey' ? credential.key : credential.token}`,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
)
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
// The service's message is the useful one — it distinguishes "no version"
|
|
192
|
+
// from "deployed before source retention", and the second names its remedy.
|
|
193
|
+
// Unwrapped from the envelope rather than passed through raw: a bare
|
|
194
|
+
// `throw new Error` renders as `[INTERNAL_ERROR] … {"error":true,…}`, which
|
|
195
|
+
// buries an actionable remedy inside JSON and mislabels a 404 the author
|
|
196
|
+
// caused as a platform fault. They would file a bug instead of reading the
|
|
197
|
+
// fix.
|
|
198
|
+
const body = await res.text()
|
|
199
|
+
let message = body
|
|
200
|
+
try {
|
|
201
|
+
const parsed = JSON.parse(body) as { message?: unknown }
|
|
202
|
+
if (typeof parsed.message === 'string') message = parsed.message
|
|
203
|
+
} catch {
|
|
204
|
+
// Not an envelope — keep the raw body, which is better than nothing.
|
|
205
|
+
}
|
|
206
|
+
// A hint is mandatory here — `error-hints.test.ts` scans for it, on the
|
|
207
|
+
// principle that every CLI-raised error names the next command. For a
|
|
208
|
+
// missing archive the next command is a redeploy from a local copy; for
|
|
209
|
+
// anything else, listing the versions tells the author what exists.
|
|
210
|
+
throw new CliError(message, {
|
|
211
|
+
code: res.status === 404 ? 'NOT_FOUND' : 'REQUEST_FAILED',
|
|
212
|
+
hint: res.status === 404
|
|
213
|
+
? `frontera automation deploy <file> (from a local copy of ${slug})`
|
|
214
|
+
: `frontera automation versions ${slug}`,
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
return new Uint8Array(await res.arrayBuffer())
|
|
218
|
+
}
|
|
219
|
+
|
|
98
220
|
async deploy(
|
|
99
221
|
slug: string,
|
|
100
|
-
args: { manifest: unknown; bundle: Uint8Array; promote: boolean },
|
|
222
|
+
args: { manifest: unknown; bundle: Uint8Array; source?: Uint8Array; promote: boolean },
|
|
101
223
|
): Promise<AutomationDeployResult> {
|
|
102
224
|
const form = new FormData()
|
|
103
225
|
form.set('manifest', JSON.stringify(args.manifest))
|
|
104
226
|
form.set('bundle', new Blob([args.bundle], { type: 'application/javascript' }), 'bundle.js')
|
|
227
|
+
// The project the bundle was built FROM. Without it a deployed version can
|
|
228
|
+
// never be edited again — only the built output would survive. Absent when
|
|
229
|
+
// no project root was found; the caller has already warned about that.
|
|
230
|
+
if (args.source) {
|
|
231
|
+
form.set('source', new Blob([args.source], { type: 'application/gzip' }), 'src.tgz')
|
|
232
|
+
}
|
|
105
233
|
// The service promotes unless the field is the literal "false".
|
|
106
234
|
form.set('promote', args.promote ? 'true' : 'false')
|
|
107
235
|
|