@factorialco/fcode-cli 3.2.0 → 3.4.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.
- package/README.md +1 -1
- package/dist/commands/i18n/status.js +9 -0
- package/dist/commands/modules/pull.js +5 -4
- package/dist/commands/modules/push.js +1 -1
- package/dist/commands/modules/status.js +9 -0
- package/dist/commands/processes/pull.js +5 -4
- package/dist/commands/processes/push.js +1 -1
- package/dist/commands/processes/status.js +9 -0
- package/dist/commands/runtime-env.js +2 -2
- package/dist/core/commands/resource-command.d.ts +4 -1
- package/dist/core/commands/resource-command.js +9 -2
- package/dist/core/dependencies/javascript-env-dependencies.js +4 -4
- package/dist/core/dependencies/python-env-dependencies.js +5 -4
- package/dist/core/executors/execution-task.js +1 -1
- package/dist/core/executors/javascript-execution-task.js +21 -17
- package/dist/core/executors/python-execution-task.js +11 -10
- package/dist/core/resource-pusher.js +13 -7
- package/dist/core/types.d.ts +76 -3
- package/dist/core/types.js +33 -6
- package/dist/repositories/locale.js +5 -3
- package/dist/repositories/process.js +20 -2
- package/dist/repositories/script-library.js +3 -2
- package/dist/runtime/libs/python/fcode/datastore.py +3 -1
- package/dist/services/fcode-graphql-client.d.ts +45 -7
- package/dist/services/fcode-graphql-client.js +264 -46
- package/dist/services/skills-installer.js +2 -2
- package/dist/services/version-pull-reporter.d.ts +20 -0
- package/dist/services/version-pull-reporter.js +33 -0
- package/dist/utils/child-process.d.ts +26 -0
- package/dist/utils/child-process.js +60 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/core/types.d.ts
CHANGED
|
@@ -72,14 +72,27 @@ export interface WorkspaceConfig {
|
|
|
72
72
|
export type ResourceRepositoryOptions = {
|
|
73
73
|
name: string;
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Why a deleted resource is still in the cloud: a workspace version contains one of its published
|
|
77
|
+
* snapshots. The row is read-only there and goes for good once the last of those versions does.
|
|
78
|
+
*/
|
|
79
|
+
export type Deletion = {
|
|
80
|
+
deletedAt: string;
|
|
81
|
+
retainingVersionTags: string[];
|
|
82
|
+
};
|
|
75
83
|
export declare abstract class Resource {
|
|
76
84
|
abstract readonly contentHash: string;
|
|
77
85
|
id: string | undefined;
|
|
78
86
|
slug: string;
|
|
79
87
|
errorPushing: boolean | undefined;
|
|
80
88
|
resolvingTeamSlug?: string;
|
|
89
|
+
deletion?: Deletion;
|
|
81
90
|
sameAs(other: Resource): boolean;
|
|
82
91
|
}
|
|
92
|
+
/** Whether the workspace may edit the resource: neither a parent's nor a deleted one is its to change. */
|
|
93
|
+
export declare const isReadOnly: (resource: Resource) => boolean;
|
|
94
|
+
/** The sentence `push` and `diff` use to say why a resource is not the workspace's to change. */
|
|
95
|
+
export declare const readOnlyReason: (resource: Resource) => string | undefined;
|
|
83
96
|
export declare enum OnConflictResolution {
|
|
84
97
|
KEEP_LOCAL = 0,
|
|
85
98
|
KEEP_REMOTE = 1
|
|
@@ -148,6 +161,62 @@ export interface ProcessUiTrigger {
|
|
|
148
161
|
*/
|
|
149
162
|
awaitResult?: boolean;
|
|
150
163
|
}
|
|
164
|
+
export interface ProcessFactorial {
|
|
165
|
+
/** Master switch: nothing below is exposed while false. */
|
|
166
|
+
enabled: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Whether Factorial waits for the execution and receives its result. Defaults to true, and is
|
|
169
|
+
* omitted from metadata.json when it does.
|
|
170
|
+
*/
|
|
171
|
+
awaitResult?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Monolith policy keys the caller must hold: the outer list is an OR of alternatives, each
|
|
174
|
+
* inner list an AND. Free text as far as fcode is concerned. Omitted when empty.
|
|
175
|
+
*/
|
|
176
|
+
requiredPolicies?: string[][];
|
|
177
|
+
uiTrigger?: ProcessFactorialUiTrigger;
|
|
178
|
+
form?: ProcessFactorialForm;
|
|
179
|
+
agentTool?: ProcessFactorialAgentTool;
|
|
180
|
+
backend?: ProcessFactorialBackend;
|
|
181
|
+
}
|
|
182
|
+
export interface ProcessFactorialUiTrigger {
|
|
183
|
+
enabled: boolean;
|
|
184
|
+
/** Identifier of the UI location the button is rendered in. Required while enabled. */
|
|
185
|
+
locationId?: string;
|
|
186
|
+
/** Button text; the only field of the block that accepts fcode.i18n("key") tokens. */
|
|
187
|
+
label?: string;
|
|
188
|
+
/** f0 icon name, e.g. "Globe". */
|
|
189
|
+
icon?: string;
|
|
190
|
+
}
|
|
191
|
+
export interface ProcessFactorialForm {
|
|
192
|
+
enabled: boolean;
|
|
193
|
+
/** Whether the form may be embedded anonymously. Omitted from metadata.json when false. */
|
|
194
|
+
public?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Whether the app offers the form to its users from its own page in Factorial. The `factorial`
|
|
197
|
+
* block's name for `form.appRole: "USER_FACING_FORM"`. Omitted from metadata.json when false.
|
|
198
|
+
*/
|
|
199
|
+
appTool?: boolean;
|
|
200
|
+
}
|
|
201
|
+
export type AgentToolEffect = 'READ' | 'WRITE' | 'DESTRUCTIVE';
|
|
202
|
+
export interface ProcessFactorialAgentTool {
|
|
203
|
+
enabled: boolean;
|
|
204
|
+
/** What the tool does, written for the model. */
|
|
205
|
+
description?: string;
|
|
206
|
+
effect?: AgentToolEffect;
|
|
207
|
+
whenToUse?: string;
|
|
208
|
+
whenNotToUse?: string[];
|
|
209
|
+
preconditions?: string[];
|
|
210
|
+
doesNotDo?: string[];
|
|
211
|
+
/** What the agent should do when the tool is unavailable or fails. */
|
|
212
|
+
degradation?: string;
|
|
213
|
+
/** Slug of the process this one previews the outcome of, when it is a dry run. */
|
|
214
|
+
simulatesFor?: string;
|
|
215
|
+
}
|
|
216
|
+
export interface ProcessFactorialBackend {
|
|
217
|
+
enabled: boolean;
|
|
218
|
+
}
|
|
219
|
+
export type LifecycleRole = 'INSTALL' | 'SETTINGS' | 'UNINSTALL' | 'SYNC';
|
|
151
220
|
export interface ProcessMetadataFields {
|
|
152
221
|
name: string;
|
|
153
222
|
description?: string;
|
|
@@ -155,6 +224,7 @@ export interface ProcessMetadataFields {
|
|
|
155
224
|
webhook?: ProcessWebhook;
|
|
156
225
|
form?: ProcessForm;
|
|
157
226
|
uiTrigger?: ProcessUiTrigger;
|
|
227
|
+
factorial?: ProcessFactorial;
|
|
158
228
|
}
|
|
159
229
|
export declare class Process extends Resource {
|
|
160
230
|
id: string;
|
|
@@ -166,6 +236,9 @@ export declare class Process extends Resource {
|
|
|
166
236
|
webhook?: ProcessWebhook;
|
|
167
237
|
form?: ProcessForm;
|
|
168
238
|
uiTrigger?: ProcessUiTrigger;
|
|
239
|
+
factorial?: ProcessFactorial;
|
|
240
|
+
/** Set by the platform for the reserved lifecycle slugs; not part of the content hash. */
|
|
241
|
+
lifecycleRole?: LifecycleRole;
|
|
169
242
|
script: {
|
|
170
243
|
parametersSchema: any;
|
|
171
244
|
programmingLanguage: ProgrammingLanguage;
|
|
@@ -185,7 +258,7 @@ export declare class Process extends Resource {
|
|
|
185
258
|
dependencies: {
|
|
186
259
|
scopedToProcess: boolean;
|
|
187
260
|
};
|
|
188
|
-
}, metadata: ProcessMetadataFields, resolvingTeamSlug?: string);
|
|
261
|
+
}, metadata: ProcessMetadataFields, resolvingTeamSlug?: string, deletion?: Deletion);
|
|
189
262
|
get contentHash(): string;
|
|
190
263
|
}
|
|
191
264
|
export declare class VersionedProcess extends Resource {
|
|
@@ -207,7 +280,7 @@ export declare class ScriptLibrary extends Resource {
|
|
|
207
280
|
name: string;
|
|
208
281
|
programmingLanguage: ProgrammingLanguage;
|
|
209
282
|
sourceCode: string;
|
|
210
|
-
constructor(id: string, name: string, sourceCode: string, programmingLanguage: ProgrammingLanguage, resolvingTeamSlug?: string);
|
|
283
|
+
constructor(id: string, name: string, sourceCode: string, programmingLanguage: ProgrammingLanguage, resolvingTeamSlug?: string, deletion?: Deletion);
|
|
211
284
|
get contentHash(): string;
|
|
212
285
|
}
|
|
213
286
|
export declare class Locale extends Resource {
|
|
@@ -217,7 +290,7 @@ export declare class Locale extends Resource {
|
|
|
217
290
|
slug: string;
|
|
218
291
|
locale: string;
|
|
219
292
|
content: string;
|
|
220
|
-
constructor(id: string | undefined, locale: string, content: string, resolvingTeamSlug?: string);
|
|
293
|
+
constructor(id: string | undefined, locale: string, content: string, resolvingTeamSlug?: string, deletion?: Deletion);
|
|
221
294
|
/**
|
|
222
295
|
* The name a locale is tracked and stored under. Unlike every other resource, the identifier is
|
|
223
296
|
* not unique: a workspace sees its own `en` and each parent workspace's `en`, and all of them are
|
package/dist/core/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServiceAccount = exports.Dependency = exports.DependenciesFlags = exports.TeamNameArg = exports.ShowInheritedFlag = exports.ResourceSlugArg = exports.ForceFlag = exports.ScriptLibraryNotFound = exports.ProcessNotFound = exports.WorkspaceSettings = exports.ProgrammingLanguageManifest = exports.Variable = exports.VersionedProgrammingLanguageManifest = exports.VersionedScriptLibrary = exports.Locale = exports.ScriptLibrary = exports.VersionedProcess = exports.Process = exports.DEFAULT_WEBHOOK_AUTH_HEADER = exports.programmingLanguageDisplayName = exports.PROGRAMMING_LANGUAGES = exports.ResourceStatus = exports.OnConflictResolution = exports.Resource = void 0;
|
|
3
|
+
exports.ServiceAccount = exports.Dependency = exports.DependenciesFlags = exports.TeamNameArg = exports.ShowInheritedFlag = exports.ResourceSlugArg = exports.ForceFlag = exports.ScriptLibraryNotFound = exports.ProcessNotFound = exports.WorkspaceSettings = exports.ProgrammingLanguageManifest = exports.Variable = exports.VersionedProgrammingLanguageManifest = exports.VersionedScriptLibrary = exports.Locale = exports.ScriptLibrary = exports.VersionedProcess = exports.Process = exports.DEFAULT_WEBHOOK_AUTH_HEADER = exports.programmingLanguageDisplayName = exports.PROGRAMMING_LANGUAGES = exports.ResourceStatus = exports.OnConflictResolution = exports.readOnlyReason = exports.isReadOnly = exports.Resource = void 0;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const crypto = require("node:crypto");
|
|
6
6
|
const canonical_hash_1 = require("../utils/canonical-hash");
|
|
@@ -10,6 +10,27 @@ class Resource {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
exports.Resource = Resource;
|
|
13
|
+
/** Whether the workspace may edit the resource: neither a parent's nor a deleted one is its to change. */
|
|
14
|
+
const isReadOnly = (resource) => Boolean(resource.resolvingTeamSlug || resource.deletion);
|
|
15
|
+
exports.isReadOnly = isReadOnly;
|
|
16
|
+
/** The sentence `push` and `diff` use to say why a resource is not the workspace's to change. */
|
|
17
|
+
const readOnlyReason = (resource) => {
|
|
18
|
+
if (resource.resolvingTeamSlug) {
|
|
19
|
+
return `inherited from team '${resource.resolvingTeamSlug}'`;
|
|
20
|
+
}
|
|
21
|
+
if (resource.deletion) {
|
|
22
|
+
return `deleted in the cloud and kept by workspace version(s) ${resource.deletion.retainingVersionTags.join(', ')}`;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
};
|
|
26
|
+
exports.readOnlyReason = readOnlyReason;
|
|
27
|
+
/**
|
|
28
|
+
* The deletion as it enters a content hash. A deleted resource must read as changed the moment the
|
|
29
|
+
* cloud deletes it — or brings it back — so a pull marks its files read-only (or writable again)
|
|
30
|
+
* even though their content did not move; a live resource contributes nothing, so no stored hash
|
|
31
|
+
* changes for it.
|
|
32
|
+
*/
|
|
33
|
+
const deletionHashPart = (deletion) => deletion ? JSON.stringify({ deletedAt: deletion.deletedAt, retainingVersionTags: [...deletion.retainingVersionTags].sort() }) : '';
|
|
13
34
|
var OnConflictResolution;
|
|
14
35
|
(function (OnConflictResolution) {
|
|
15
36
|
OnConflictResolution[OnConflictResolution["KEEP_LOCAL"] = 0] = "KEEP_LOCAL";
|
|
@@ -41,16 +62,18 @@ exports.programmingLanguageDisplayName = programmingLanguageDisplayName;
|
|
|
41
62
|
// The header assumed when none is configured; its value carries a Bearer scheme.
|
|
42
63
|
exports.DEFAULT_WEBHOOK_AUTH_HEADER = 'Authorization';
|
|
43
64
|
class Process extends Resource {
|
|
44
|
-
constructor(id, slug, script, readme, manifest, settings, metadata, resolvingTeamSlug) {
|
|
65
|
+
constructor(id, slug, script, readme, manifest, settings, metadata, resolvingTeamSlug, deletion) {
|
|
45
66
|
super();
|
|
46
67
|
this.id = id;
|
|
47
68
|
this.slug = slug;
|
|
69
|
+
this.deletion = deletion;
|
|
48
70
|
this.name = metadata.name;
|
|
49
71
|
this.description = metadata.description;
|
|
50
72
|
this.tags = metadata.tags;
|
|
51
73
|
this.webhook = metadata.webhook;
|
|
52
74
|
this.form = metadata.form;
|
|
53
75
|
this.uiTrigger = metadata.uiTrigger;
|
|
76
|
+
this.factorial = metadata.factorial;
|
|
54
77
|
this.script = script;
|
|
55
78
|
this.readme = readme;
|
|
56
79
|
this.manifest = settings.dependencies.scopedToProcess && manifest !== null ? manifest : { dependencies: [] };
|
|
@@ -70,8 +93,10 @@ class Process extends Resource {
|
|
|
70
93
|
// Inserted only when set, so every process that predates UI triggers keeps the
|
|
71
94
|
// content hash already stored in .fcode (mirrors WorkspaceSettings.contentHash).
|
|
72
95
|
...(this.uiTrigger && { uiTrigger: this.uiTrigger }),
|
|
96
|
+
...(this.factorial && { factorial: this.factorial }),
|
|
73
97
|
manifestDependencies: this.manifest.dependencies,
|
|
74
98
|
dependenciesScopedToProcess: this.settings.dependencies.scopedToProcess,
|
|
99
|
+
...(this.deletion && { deletion: deletionHashPart(this.deletion) }),
|
|
75
100
|
});
|
|
76
101
|
}
|
|
77
102
|
}
|
|
@@ -94,7 +119,7 @@ class VersionedProcess extends Resource {
|
|
|
94
119
|
}
|
|
95
120
|
exports.VersionedProcess = VersionedProcess;
|
|
96
121
|
class ScriptLibrary extends Resource {
|
|
97
|
-
constructor(id, name, sourceCode, programmingLanguage, resolvingTeamSlug) {
|
|
122
|
+
constructor(id, name, sourceCode, programmingLanguage, resolvingTeamSlug, deletion) {
|
|
98
123
|
super();
|
|
99
124
|
this.id = id;
|
|
100
125
|
this.slug = name;
|
|
@@ -102,20 +127,22 @@ class ScriptLibrary extends Resource {
|
|
|
102
127
|
this.sourceCode = sourceCode;
|
|
103
128
|
this.programmingLanguage = programmingLanguage;
|
|
104
129
|
this.resolvingTeamSlug = resolvingTeamSlug;
|
|
130
|
+
this.deletion = deletion;
|
|
105
131
|
}
|
|
106
132
|
get contentHash() {
|
|
107
|
-
return crypto.createHash('md5').update(this.sourceCode).digest('hex');
|
|
133
|
+
return crypto.createHash('md5').update(this.sourceCode).update(deletionHashPart(this.deletion)).digest('hex');
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
exports.ScriptLibrary = ScriptLibrary;
|
|
111
137
|
class Locale extends Resource {
|
|
112
|
-
constructor(id, locale, content, resolvingTeamSlug) {
|
|
138
|
+
constructor(id, locale, content, resolvingTeamSlug, deletion) {
|
|
113
139
|
super();
|
|
114
140
|
this.id = id;
|
|
115
141
|
this.slug = Locale.slugFor(locale, resolvingTeamSlug);
|
|
116
142
|
this.locale = locale;
|
|
117
143
|
this.content = content;
|
|
118
144
|
this.resolvingTeamSlug = resolvingTeamSlug;
|
|
145
|
+
this.deletion = deletion;
|
|
119
146
|
}
|
|
120
147
|
/**
|
|
121
148
|
* The name a locale is tracked and stored under. Unlike every other resource, the identifier is
|
|
@@ -133,7 +160,7 @@ class Locale extends Resource {
|
|
|
133
160
|
// Hashed over the raw YAML: the file is the source of truth and round-trips comments and
|
|
134
161
|
// ordering, so any edit — even one that parses to the same translations — is a change to push.
|
|
135
162
|
get contentHash() {
|
|
136
|
-
return crypto.createHash('md5').update(this.content).digest('hex');
|
|
163
|
+
return crypto.createHash('md5').update(this.content).update(deletionHashPart(this.deletion)).digest('hex');
|
|
137
164
|
}
|
|
138
165
|
}
|
|
139
166
|
exports.Locale = Locale;
|
|
@@ -32,7 +32,7 @@ class LocaleRepository extends resource_1.default {
|
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
34
34
|
const content = fs.readFileSync(this.getResourcePath(localeMetadata)).toString();
|
|
35
|
-
return new types_1.Locale(localeMetadata.id, localeMetadata.locale ?? slug, content, localeMetadata.resolvingTeamSlug);
|
|
35
|
+
return new types_1.Locale(localeMetadata.id, localeMetadata.locale ?? slug, content, localeMetadata.resolvingTeamSlug, localeMetadata.deletion);
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
38
|
throw new errors_1.LocalLocaleNotFound(error, slug);
|
|
@@ -78,12 +78,14 @@ class LocaleRepository extends resource_1.default {
|
|
|
78
78
|
locale: locale.locale,
|
|
79
79
|
slug: locale.slug,
|
|
80
80
|
...(locale.resolvingTeamSlug && { resolvingTeamSlug: locale.resolvingTeamSlug }),
|
|
81
|
+
...(locale.deletion && { deletion: locale.deletion }),
|
|
81
82
|
};
|
|
82
83
|
}
|
|
83
84
|
async saveFiles(locale) {
|
|
84
85
|
// Inherited locales are written read-only, like inherited modules: they belong to a parent
|
|
85
|
-
// workspace, and editing one here would push an override rather than change the parent.
|
|
86
|
-
|
|
86
|
+
// workspace, and editing one here would push an override rather than change the parent. A
|
|
87
|
+
// deleted locale is read-only too: only its versions are still resolved.
|
|
88
|
+
(0, file_utils_1.outputFileSyncIfChanged)(this.getResourcePath(locale), locale.content || '', { readOnly: (0, types_1.isReadOnly)(locale) });
|
|
87
89
|
}
|
|
88
90
|
async metadataFromFiles(slug) {
|
|
89
91
|
return {
|
|
@@ -37,8 +37,23 @@ function buildProcessMetadata(process) {
|
|
|
37
37
|
if (process.uiTrigger !== undefined) {
|
|
38
38
|
metadata.uiTrigger = process.uiTrigger;
|
|
39
39
|
}
|
|
40
|
+
if (process.factorial !== undefined) {
|
|
41
|
+
metadata.factorial = process.factorial;
|
|
42
|
+
}
|
|
40
43
|
return metadata;
|
|
41
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The `factorial` block as the file has it, minus the `title` and `description` keys an earlier
|
|
47
|
+
* draft of the block carried: the process's own name and description took their place, so a file
|
|
48
|
+
* that still has them is read as if it did not, and the next save drops them.
|
|
49
|
+
*/
|
|
50
|
+
function readFactorialBlock(raw) {
|
|
51
|
+
if (raw === undefined || raw === null || typeof raw !== 'object') {
|
|
52
|
+
return raw ?? undefined;
|
|
53
|
+
}
|
|
54
|
+
const { title: _title, description: _description, ...factorial } = raw;
|
|
55
|
+
return factorial;
|
|
56
|
+
}
|
|
42
57
|
const PROCESS_SOURCE_CODE_FILE_NAME = (programmingLanguage) => (programmingLanguage === 'PYTHON' ? 'main.py' : 'index.js');
|
|
43
58
|
exports.PROCESS_SOURCE_CODE_FILE_NAME = PROCESS_SOURCE_CODE_FILE_NAME;
|
|
44
59
|
const PROCESS_DEPENDENCIES_FILE_NAME = (programmingLanguage) => PROGRAMMING_LANGUAGE_DEPENDENCIES_FILE_NAME[programmingLanguage];
|
|
@@ -147,7 +162,7 @@ class ProcessRepository extends resource_1.default {
|
|
|
147
162
|
const manifest = isDependenciesFilePresent
|
|
148
163
|
? { dependencies: (0, dependencies_1.dependenciesFromFile)(processMetadata.script.programmingLanguage, fs.readFileSync(dependenciesFile).toString()) }
|
|
149
164
|
: null;
|
|
150
|
-
return new types_1.Process(processMetadata.id, processMetadata.slug, { parametersSchema, programmingLanguage: processMetadata.script.programmingLanguage, sourceCode }, readme, manifest, { dependencies: { scopedToProcess: isDependenciesFilePresent } }, metadata, processMetadata.resolvingTeamSlug);
|
|
165
|
+
return new types_1.Process(processMetadata.id, processMetadata.slug, { parametersSchema, programmingLanguage: processMetadata.script.programmingLanguage, sourceCode }, readme, manifest, { dependencies: { scopedToProcess: isDependenciesFilePresent } }, metadata, processMetadata.resolvingTeamSlug, processMetadata.deletion);
|
|
151
166
|
}
|
|
152
167
|
catch {
|
|
153
168
|
throw new Error(`Process ${processSlug} not found!`);
|
|
@@ -163,6 +178,7 @@ class ProcessRepository extends resource_1.default {
|
|
|
163
178
|
webhook: raw.webhook,
|
|
164
179
|
form: raw.form,
|
|
165
180
|
uiTrigger: raw.uiTrigger,
|
|
181
|
+
factorial: readFactorialBlock(raw.factorial),
|
|
166
182
|
};
|
|
167
183
|
}
|
|
168
184
|
catch (error) {
|
|
@@ -229,6 +245,7 @@ class ProcessRepository extends resource_1.default {
|
|
|
229
245
|
programmingLanguage: process.script.programmingLanguage,
|
|
230
246
|
},
|
|
231
247
|
...(process.resolvingTeamSlug && { resolvingTeamSlug: process.resolvingTeamSlug }),
|
|
248
|
+
...(process.deletion && { deletion: process.deletion }),
|
|
232
249
|
};
|
|
233
250
|
}
|
|
234
251
|
async metadataFromFiles(slug) {
|
|
@@ -243,7 +260,8 @@ class ProcessRepository extends resource_1.default {
|
|
|
243
260
|
};
|
|
244
261
|
}
|
|
245
262
|
async saveFiles(process) {
|
|
246
|
-
|
|
263
|
+
// A deleted process is kept exactly as its versions left it, like an inherited one: read-only.
|
|
264
|
+
const readOnly = (0, types_1.isReadOnly)(process);
|
|
247
265
|
(0, file_utils_1.outputFileSyncIfChanged)(this.buildResourcePath(process, this.getSourceCodeFileName(process.script.programmingLanguage)), process.script.sourceCode || '', {
|
|
248
266
|
readOnly,
|
|
249
267
|
});
|
|
@@ -25,7 +25,7 @@ class ScriptLibraryRepository extends resource_1.default {
|
|
|
25
25
|
const sourceCodeFile = fs.readFileSync(this.getResourcePath(scriptLibraryMetadata));
|
|
26
26
|
try {
|
|
27
27
|
const sourceCode = sourceCodeFile.toString();
|
|
28
|
-
return new types_1.ScriptLibrary(scriptLibraryMetadata.id, scriptLibraryMetadata.name, sourceCode, scriptLibraryMetadata.programmingLanguage, scriptLibraryMetadata.resolvingTeamSlug);
|
|
28
|
+
return new types_1.ScriptLibrary(scriptLibraryMetadata.id, scriptLibraryMetadata.name, sourceCode, scriptLibraryMetadata.programmingLanguage, scriptLibraryMetadata.resolvingTeamSlug, scriptLibraryMetadata.deletion);
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
31
|
throw new errors_1.LocalScriptLibraryNotFound(error, slug);
|
|
@@ -83,10 +83,11 @@ class ScriptLibraryRepository extends resource_1.default {
|
|
|
83
83
|
slug: scriptLibrary.slug,
|
|
84
84
|
programmingLanguage: scriptLibrary.programmingLanguage,
|
|
85
85
|
...(scriptLibrary.resolvingTeamSlug && { resolvingTeamSlug: scriptLibrary.resolvingTeamSlug }),
|
|
86
|
+
...(scriptLibrary.deletion && { deletion: scriptLibrary.deletion }),
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
89
|
async saveFiles(scriptLibrary) {
|
|
89
|
-
(0, file_utils_1.outputFileSyncIfChanged)(this.getResourcePath(scriptLibrary), scriptLibrary.sourceCode || '', { readOnly:
|
|
90
|
+
(0, file_utils_1.outputFileSyncIfChanged)(this.getResourcePath(scriptLibrary), scriptLibrary.sourceCode || '', { readOnly: (0, types_1.isReadOnly)(scriptLibrary) });
|
|
90
91
|
}
|
|
91
92
|
versionsDirectory(scriptLibrary) {
|
|
92
93
|
return path.join(this._repositoryName, this.resourceDirectoryName(scriptLibrary), 'versions');
|
|
@@ -21,7 +21,9 @@ class Datastore:
|
|
|
21
21
|
return self.getAll().get(key, None)
|
|
22
22
|
raise TypeError("Invalid argument type")
|
|
23
23
|
|
|
24
|
-
def set(self, key, value):
|
|
24
|
+
def set(self, key, value, encrypted=False):
|
|
25
|
+
if not isinstance(encrypted, bool):
|
|
26
|
+
raise TypeError("Invalid encrypted flag. Encrypted must be a boolean.")
|
|
25
27
|
if isinstance(key, (str, int)) and isinstance(value, (str, int)):
|
|
26
28
|
datastore = self.getAll()
|
|
27
29
|
datastore[key] = value
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { GraphQLClient } from 'graphql-request';
|
|
2
2
|
import { Locale, Process, ProgrammingLanguage, ProgrammingLanguageManifest, RawTeamSettings, ScriptLibrary, TeamUpdateInput, TeamVersion, TeamVersionAlias, Variable, VersionedProcess, VersionedProgrammingLanguageManifest, VersionedScriptLibrary, ServiceAccount } from '../core/types';
|
|
3
|
+
/**
|
|
4
|
+
* What a delete mutation answers. `deletedAt` is set when the entity was kept as deleted because a
|
|
5
|
+
* workspace version still contains it; `retainingVersionTags` names those versions.
|
|
6
|
+
*/
|
|
7
|
+
export interface RemovedResource {
|
|
8
|
+
id: string;
|
|
9
|
+
deletedAt: string | null;
|
|
10
|
+
retainingVersionTags: string[];
|
|
11
|
+
}
|
|
3
12
|
export type FcodeGraphQLClientOptions = {
|
|
4
13
|
url: string;
|
|
5
14
|
accessToken: string;
|
|
@@ -16,10 +25,17 @@ declare class FcodeGraphQLClient {
|
|
|
16
25
|
cleanError(error: any): any;
|
|
17
26
|
private cloudUnavailableMessage;
|
|
18
27
|
private isNetworkError;
|
|
19
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Every process of the workspace, the deleted ones a workspace version still contains included:
|
|
30
|
+
* they are pulled read-only with their versions, so the local copy keeps what the versions run.
|
|
31
|
+
* An api that predates deleted-marked processes rejects the query over the unknown field, and
|
|
32
|
+
* the fallback lists the live ones as it always did.
|
|
33
|
+
*/
|
|
34
|
+
allProcesses(includeDeleted?: boolean): Promise<Array<Process>>;
|
|
20
35
|
allVersionedProcesses(processId: string): Promise<Array<VersionedProcess>>;
|
|
21
36
|
createVersionedProcesses(versionedProcess: VersionedProcess): Promise<void>;
|
|
22
|
-
|
|
37
|
+
/** Same deleted-inclusive listing, and the same fallback, as {@link allProcesses}. */
|
|
38
|
+
allScriptLibraries(includeDeleted?: boolean): Promise<Array<ScriptLibrary>>;
|
|
23
39
|
allVersionedScriptLibraries(scriptLibraryId: string): Promise<Array<VersionedScriptLibrary>>;
|
|
24
40
|
createVersionedScriptLibrary(versionedScriptLibrary: VersionedScriptLibrary): Promise<void>;
|
|
25
41
|
allVariables(): Promise<Array<Variable>>;
|
|
@@ -29,26 +45,48 @@ declare class FcodeGraphQLClient {
|
|
|
29
45
|
* kept twice here — an override is layered over the parent's file key by key, so the parent's
|
|
30
46
|
* keys are still what the ones it does not mention resolve to.
|
|
31
47
|
*/
|
|
32
|
-
allLocales(): Promise<Array<Locale>>;
|
|
48
|
+
allLocales(includeDeleted?: boolean): Promise<Array<Locale>>;
|
|
49
|
+
private fetchLocales;
|
|
33
50
|
pushLocale(locale: Locale): Promise<Locale>;
|
|
34
51
|
updateLocale(locale: Locale): Promise<Locale>;
|
|
35
|
-
removeLocale(locale: string | undefined): Promise<
|
|
52
|
+
removeLocale(locale: string | undefined): Promise<RemovedResource>;
|
|
36
53
|
private toProcessInput;
|
|
37
54
|
pushProcess(process: Process): Promise<Process>;
|
|
38
|
-
removeProcess(id: String | undefined): Promise<
|
|
55
|
+
removeProcess(id: String | undefined): Promise<RemovedResource>;
|
|
39
56
|
pushScriptLibrary(scriptLibrary: ScriptLibrary): Promise<ScriptLibrary>;
|
|
40
|
-
removeScriptLibrary(id: String | undefined): Promise<
|
|
57
|
+
removeScriptLibrary(id: String | undefined): Promise<RemovedResource>;
|
|
41
58
|
pushVariable(variable: Variable): Promise<Variable>;
|
|
42
59
|
updateVariable(variable: Variable): Promise<Variable>;
|
|
43
60
|
removeVariable(id: String | undefined): Promise<Process>;
|
|
44
61
|
programmingLanguageManifest(programmingLanguage: ProgrammingLanguage): Promise<ProgrammingLanguageManifest>;
|
|
45
62
|
private inheritedTeam;
|
|
63
|
+
private deletionOf;
|
|
64
|
+
private isUndefinedDeletionFieldError;
|
|
46
65
|
/**
|
|
47
66
|
* The public mode and the default header name are left out, so a public webhook — or one
|
|
48
67
|
* using the default header — keeps the metadata.json it already had on disk.
|
|
49
68
|
*/
|
|
50
69
|
private mapWebhook;
|
|
51
|
-
|
|
70
|
+
/**
|
|
71
|
+
* metadata.json omits every defaulted field of the `factorial` block, so the input restores
|
|
72
|
+
* them explicitly: every input field left out means "leave unchanged" on the platform, and the
|
|
73
|
+
* file is the source of truth. That includes the agent tool's clearable texts, sent as "" when
|
|
74
|
+
* the file has none so deleting the line clears the value. The platform reads a null or empty
|
|
75
|
+
* `uiTrigger.locationId` / `label` / `icon` and `agentTool.effect` as unchanged, though, so
|
|
76
|
+
* those are sent only when present (like the legacy `uiTrigger.label`).
|
|
77
|
+
*/
|
|
78
|
+
private toFactorialInput;
|
|
79
|
+
/**
|
|
80
|
+
* The `factorial` block as metadata.json writes it: defaulted fields omitted, and a block or
|
|
81
|
+
* sub-block omitted only when disabled AND all defaults — a disabled sub-block that still carries
|
|
82
|
+
* settings (a tool contract, a trigger location, a public form) is kept, or a pull followed by a
|
|
83
|
+
* push would clear them on the platform. The platform always returns the block, so a process
|
|
84
|
+
* that never configured one comes back as all defaults and is mapped to no block at all, or every
|
|
85
|
+
* process in every workspace would gain a `factorial` key (and a new content hash) on its next
|
|
86
|
+
* pull.
|
|
87
|
+
*/
|
|
88
|
+
private mapFactorialSettings;
|
|
89
|
+
mapProcess({ id, slug, name, description, tags, script, readme, manifest, settings, triggers, resolvingTeamSlug, lifecycleRole, deletedAt, retainingVersionTags, }: any): Process;
|
|
52
90
|
/**
|
|
53
91
|
* GraphQL nests the webhook configuration under `settings`, while settings.json and the REST API
|
|
54
92
|
* keep it flat. Mapping it explicitly is what keeps that mismatch from silently reading as
|