@construct-space/cli 1.1.12 → 1.3.1
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/dist/index.js +415 -1052
- package/dist/templates/space/full/space.manifest.json.tmpl +1 -1
- package/dist/templates/space/space.manifest.json.tmpl +2 -2
- package/dist/templates/space/widgets/2x1.vue.tmpl +7 -0
- package/dist/templates/space/widgets/4x1.vue.tmpl +7 -0
- package/package.json +2 -2
- package/templates/space/full/space.manifest.json.tmpl +1 -1
- package/templates/space/space.manifest.json.tmpl +2 -2
- package/templates/space/widgets/2x1.vue.tmpl +7 -0
- package/templates/space/widgets/4x1.vue.tmpl +7 -0
package/dist/index.js
CHANGED
|
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
function __accessProp(key) {
|
|
9
|
-
return this[key];
|
|
10
|
-
}
|
|
11
|
-
var __toESMCache_node;
|
|
12
|
-
var __toESMCache_esm;
|
|
13
8
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
-
var canCache = mod != null && typeof mod === "object";
|
|
15
|
-
if (canCache) {
|
|
16
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
-
var cached = cache.get(mod);
|
|
18
|
-
if (cached)
|
|
19
|
-
return cached;
|
|
20
|
-
}
|
|
21
9
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
10
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
11
|
for (let key of __getOwnPropNames(mod))
|
|
24
12
|
if (!__hasOwnProp.call(to, key))
|
|
25
13
|
__defProp(to, key, {
|
|
26
|
-
get:
|
|
14
|
+
get: () => mod[key],
|
|
27
15
|
enumerable: true
|
|
28
16
|
});
|
|
29
|
-
if (canCache)
|
|
30
|
-
cache.set(mod, to);
|
|
31
17
|
return to;
|
|
32
18
|
};
|
|
33
19
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
-
var __returnValue = (v) => v;
|
|
35
|
-
function __exportSetter(name, newValue) {
|
|
36
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
-
}
|
|
38
20
|
var __export = (target, all) => {
|
|
39
21
|
for (var name in all)
|
|
40
22
|
__defProp(target, name, {
|
|
41
23
|
get: all[name],
|
|
42
24
|
enumerable: true,
|
|
43
25
|
configurable: true,
|
|
44
|
-
set:
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
45
27
|
});
|
|
46
28
|
};
|
|
47
29
|
var __require = import.meta.require;
|
|
@@ -2807,6 +2789,7 @@ Object.defineProperties(createChalk.prototype, styles2);
|
|
|
2807
2789
|
var chalk = createChalk();
|
|
2808
2790
|
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
2809
2791
|
var source_default = chalk;
|
|
2792
|
+
|
|
2810
2793
|
// node_modules/@inquirer/core/dist/lib/key.js
|
|
2811
2794
|
var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
|
|
2812
2795
|
var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
|
|
@@ -2951,7 +2934,7 @@ var effectScheduler = {
|
|
|
2951
2934
|
// node_modules/@inquirer/core/dist/lib/use-state.js
|
|
2952
2935
|
function useState(defaultValue) {
|
|
2953
2936
|
return withPointer((pointer) => {
|
|
2954
|
-
const setState = AsyncResource2.bind(function
|
|
2937
|
+
const setState = AsyncResource2.bind(function setState(newValue) {
|
|
2955
2938
|
if (pointer.get() !== newValue) {
|
|
2956
2939
|
pointer.set(newValue);
|
|
2957
2940
|
handleChange();
|
|
@@ -4524,22 +4507,6 @@ var dist_default6 = createPrompt((config, done) => {
|
|
|
4524
4507
|
});
|
|
4525
4508
|
// src/lib/utils.ts
|
|
4526
4509
|
import { execSync } from "child_process";
|
|
4527
|
-
import { platform } from "os";
|
|
4528
|
-
function openBrowser(url) {
|
|
4529
|
-
try {
|
|
4530
|
-
switch (platform()) {
|
|
4531
|
-
case "darwin":
|
|
4532
|
-
execSync(`open "${url}"`);
|
|
4533
|
-
break;
|
|
4534
|
-
case "linux":
|
|
4535
|
-
execSync(`xdg-open "${url}"`);
|
|
4536
|
-
break;
|
|
4537
|
-
case "win32":
|
|
4538
|
-
execSync(`rundll32 url.dll,FileProtocolHandler "${url}"`);
|
|
4539
|
-
break;
|
|
4540
|
-
}
|
|
4541
|
-
} catch {}
|
|
4542
|
-
}
|
|
4543
4510
|
function git(dir, ...args) {
|
|
4544
4511
|
const quoted = args.map((a) => a.includes(" ") || a.includes(":") ? `"${a}"` : a);
|
|
4545
4512
|
return execSync(`git ${quoted.join(" ")}`, { cwd: dir, encoding: "utf-8" }).trim();
|
|
@@ -4613,616 +4580,18 @@ function runHook(hooks, hookName, root) {
|
|
|
4613
4580
|
execSync2(cmd, { cwd: root, stdio: "inherit" });
|
|
4614
4581
|
}
|
|
4615
4582
|
|
|
4616
|
-
// src/lib/embedded-templates.ts
|
|
4617
|
-
var EMBEDDED_TEMPLATES = {
|
|
4618
|
-
"package.json.tmpl": `{
|
|
4619
|
-
"name": "@construct-spaces/{{.Name}}",
|
|
4620
|
-
"version": "0.1.0",
|
|
4621
|
-
"private": true,
|
|
4622
|
-
"type": "module",
|
|
4623
|
-
"scripts": {
|
|
4624
|
-
"generate-entry": "construct build --entry-only",
|
|
4625
|
-
"build": "construct build",
|
|
4626
|
-
"dev": "construct dev",
|
|
4627
|
-
"check": "construct check",
|
|
4628
|
-
"validate": "construct validate"
|
|
4629
|
-
},
|
|
4630
|
-
"peerDependencies": {
|
|
4631
|
-
"vue": "^3.5.0",
|
|
4632
|
-
"pinia": "^3.0.0",
|
|
4633
|
-
"@vueuse/core": "^14.0.0"
|
|
4634
|
-
},
|
|
4635
|
-
"devDependencies": {
|
|
4636
|
-
"@construct-space/cli": "latest",
|
|
4637
|
-
"@construct-space/sdk": "latest",
|
|
4638
|
-
"@vitejs/plugin-vue": "^5.2.3",
|
|
4639
|
-
"lucide-vue-next": "^1.0.0",
|
|
4640
|
-
"typescript": "^5.9.3",
|
|
4641
|
-
"vite": "^6.3.5",
|
|
4642
|
-
"vue-tsc": "^3.2.5"
|
|
4643
|
-
}
|
|
4644
|
-
}
|
|
4645
|
-
`,
|
|
4646
|
-
"config.md.tmpl": `---
|
|
4647
|
-
id: {{.ID}}
|
|
4648
|
-
name: {{.DisplayName}}
|
|
4649
|
-
category: space
|
|
4650
|
-
description: AI agent for the {{.DisplayName}} space
|
|
4651
|
-
maxIterations: 15
|
|
4652
|
-
tools: []
|
|
4653
|
-
canInvokeAgents: []
|
|
4654
|
-
---
|
|
4655
|
-
|
|
4656
|
-
You are Construct's {{.DisplayName}} agent. You help users work within the {{.DisplayName}} space.
|
|
4657
|
-
|
|
4658
|
-
## Context
|
|
4659
|
-
|
|
4660
|
-
Use space_list_actions to discover available actions for this space.
|
|
4661
|
-
Use space_run_action to execute actions.
|
|
4662
|
-
Do NOT call get_project_context \u2014 you work with space content, not project files.
|
|
4663
|
-
|
|
4664
|
-
## Behavior
|
|
4665
|
-
|
|
4666
|
-
- Start by listing available actions to understand what you can do
|
|
4667
|
-
- Be concise and action-oriented
|
|
4668
|
-
- Focus on tasks relevant to this space
|
|
4669
|
-
`,
|
|
4670
|
-
"full/skill-data.md.tmpl": `---
|
|
4671
|
-
id: {{.ID}}-data
|
|
4672
|
-
name: {{.DisplayName}} Data Management
|
|
4673
|
-
description: Skill for managing {{.DisplayName}} data and content
|
|
4674
|
-
trigger: {{.ID}} data|content|manage
|
|
4675
|
-
category: space
|
|
4676
|
-
tools: [read_file, list_dir, glob, grep, write_file]
|
|
4677
|
-
---
|
|
4678
|
-
|
|
4679
|
-
# {{.DisplayName}} Data Management Skill
|
|
4680
|
-
|
|
4681
|
-
This skill handles data operations for the {{.DisplayName}} space.
|
|
4682
|
-
|
|
4683
|
-
## Instructions
|
|
4684
|
-
- Help users create, read, update, and delete content
|
|
4685
|
-
- Validate data before writing
|
|
4686
|
-
- Use the space actions API when available
|
|
4687
|
-
- Prefer batch operations for bulk changes
|
|
4688
|
-
`,
|
|
4689
|
-
"full/entry.ts.tmpl": `// Space entry \u2014 exports pages, widgets, and actions for the host loader.
|
|
4690
|
-
// \`construct dev\` regenerates this from space.manifest.json on changes.
|
|
4691
|
-
import IndexPage from './pages/index.vue'
|
|
4692
|
-
import SettingsPage from './pages/settings.vue'
|
|
4693
|
-
import { actions } from './actions'
|
|
4694
|
-
|
|
4695
|
-
const spaceExport = {
|
|
4696
|
-
pages: {
|
|
4697
|
-
'': IndexPage,
|
|
4698
|
-
'settings': SettingsPage,
|
|
4699
|
-
},
|
|
4700
|
-
widgets: {},
|
|
4701
|
-
actions,
|
|
4702
|
-
}
|
|
4703
|
-
|
|
4704
|
-
export default spaceExport
|
|
4705
|
-
`,
|
|
4706
|
-
"full/skill-ui.md.tmpl": `---
|
|
4707
|
-
id: {{.ID}}-ui
|
|
4708
|
-
name: {{.DisplayName}} UI Customization
|
|
4709
|
-
description: Skill for customizing the {{.DisplayName}} user interface
|
|
4710
|
-
trigger: {{.ID}} ui|layout|design|style
|
|
4711
|
-
category: space
|
|
4712
|
-
tools: [read_file, write_file, glob]
|
|
4713
|
-
---
|
|
4714
|
-
|
|
4715
|
-
# {{.DisplayName}} UI Customization Skill
|
|
4716
|
-
|
|
4717
|
-
This skill helps customize the {{.DisplayName}} space interface.
|
|
4718
|
-
|
|
4719
|
-
## Instructions
|
|
4720
|
-
- Help users adjust layout and styling
|
|
4721
|
-
- Use Construct CSS variables (--app-foreground, --app-background, --app-accent, etc.)
|
|
4722
|
-
- Follow the host design system conventions
|
|
4723
|
-
- Suggest accessible color combinations
|
|
4724
|
-
`,
|
|
4725
|
-
"full/space.manifest.json.tmpl": `{
|
|
4726
|
-
"id": "{{.ID}}",
|
|
4727
|
-
"name": "{{.DisplayName}}",
|
|
4728
|
-
"version": "0.1.0",
|
|
4729
|
-
"description": "{{.DisplayName}} space for Construct",
|
|
4730
|
-
"author": {
|
|
4731
|
-
"name": "Your Name"
|
|
4732
|
-
},
|
|
4733
|
-
"icon": "i-lucide-box",
|
|
4734
|
-
"scope": "both",
|
|
4735
|
-
"minConstructVersion": "0.7.0",
|
|
4736
|
-
"navigation": {
|
|
4737
|
-
"label": "{{.DisplayName}}",
|
|
4738
|
-
"icon": "i-lucide-box",
|
|
4739
|
-
"to": "{{.ID}}",
|
|
4740
|
-
"order": 100
|
|
4741
|
-
},
|
|
4742
|
-
"pages": [
|
|
4743
|
-
{
|
|
4744
|
-
"path": "",
|
|
4745
|
-
"label": "Home",
|
|
4746
|
-
"icon": "i-lucide-home",
|
|
4747
|
-
"default": true
|
|
4748
|
-
},
|
|
4749
|
-
{
|
|
4750
|
-
"path": "settings",
|
|
4751
|
-
"label": "Settings",
|
|
4752
|
-
"icon": "i-lucide-settings"
|
|
4753
|
-
}
|
|
4754
|
-
],
|
|
4755
|
-
"toolbar": [
|
|
4756
|
-
{
|
|
4757
|
-
"id": "{{.ID}}-new",
|
|
4758
|
-
"icon": "i-lucide-plus",
|
|
4759
|
-
"label": "New {{.DisplayName}}",
|
|
4760
|
-
"action": "create{{.DisplayNameNoSpace}}"
|
|
4761
|
-
}
|
|
4762
|
-
],
|
|
4763
|
-
"keywords": ["{{.ID}}"],
|
|
4764
|
-
"agent": "agent/config.md",
|
|
4765
|
-
"skills": [
|
|
4766
|
-
"agent/skills/default.md",
|
|
4767
|
-
"agent/skills/data.md",
|
|
4768
|
-
"agent/skills/ui.md"
|
|
4769
|
-
],
|
|
4770
|
-
"actions": "src/actions.ts",
|
|
4771
|
-
"widgets": [
|
|
4772
|
-
{
|
|
4773
|
-
"id": "summary",
|
|
4774
|
-
"name": "{{.DisplayName}} Summary",
|
|
4775
|
-
"description": "Quick summary widget for {{.DisplayName}}",
|
|
4776
|
-
"icon": "i-lucide-box",
|
|
4777
|
-
"defaultSize": "4x1",
|
|
4778
|
-
"sizes": {
|
|
4779
|
-
"2x1": "widgets/summary/2x1.vue",
|
|
4780
|
-
"4x1": "widgets/summary/4x1.vue"
|
|
4781
|
-
}
|
|
4782
|
-
}
|
|
4783
|
-
]
|
|
4784
|
-
}
|
|
4785
|
-
`,
|
|
4786
|
-
"full/settings.vue.tmpl": `<script setup lang="ts">
|
|
4787
|
-
/**
|
|
4788
|
-
* {{.DisplayName}} \u2014 Settings page
|
|
4789
|
-
*/
|
|
4790
|
-
import { ref } from 'vue'
|
|
4791
|
-
|
|
4792
|
-
const autoSave = ref(true)
|
|
4793
|
-
const theme = ref<'system' | 'light' | 'dark'>('system')
|
|
4794
|
-
</script>
|
|
4795
|
-
|
|
4796
|
-
<template>
|
|
4797
|
-
<div class="h-full overflow-auto p-6">
|
|
4798
|
-
<h1 class="text-xl font-bold text-[var(--app-foreground)] mb-6">Settings</h1>
|
|
4799
|
-
|
|
4800
|
-
<div class="space-y-4 max-w-md">
|
|
4801
|
-
<label class="flex items-center justify-between">
|
|
4802
|
-
<span class="text-sm text-[var(--app-foreground)]">Auto-save</span>
|
|
4803
|
-
<input v-model="autoSave" type="checkbox" class="rounded" />
|
|
4804
|
-
</label>
|
|
4805
|
-
|
|
4806
|
-
<label class="block">
|
|
4807
|
-
<span class="text-sm text-[var(--app-foreground)] block mb-1">Theme</span>
|
|
4808
|
-
<select v-model="theme" class="w-full rounded border border-[var(--app-border)] bg-[var(--app-background)] px-3 py-1.5 text-sm">
|
|
4809
|
-
<option value="system">System</option>
|
|
4810
|
-
<option value="light">Light</option>
|
|
4811
|
-
<option value="dark">Dark</option>
|
|
4812
|
-
</select>
|
|
4813
|
-
</label>
|
|
4814
|
-
</div>
|
|
4815
|
-
</div>
|
|
4816
|
-
</template>
|
|
4817
|
-
`,
|
|
4818
|
-
"vite.config.ts.tmpl": `import { defineConfig } from 'vite'
|
|
4819
|
-
import vue from '@vitejs/plugin-vue'
|
|
4820
|
-
import { resolve } from 'path'
|
|
4821
|
-
|
|
4822
|
-
/**
|
|
4823
|
-
* Host-provided externals \u2014 these are supplied by Construct at runtime
|
|
4824
|
-
* via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
|
|
4825
|
-
*
|
|
4826
|
-
* Full list lives in: construct-app/frontend/lib/spaceHost.ts
|
|
4827
|
-
*/
|
|
4828
|
-
const hostExternals = [
|
|
4829
|
-
'vue',
|
|
4830
|
-
'vue-router',
|
|
4831
|
-
'pinia',
|
|
4832
|
-
'@vueuse/core',
|
|
4833
|
-
'@vueuse/integrations',
|
|
4834
|
-
'@tauri-apps/api',
|
|
4835
|
-
'@tauri-apps/api/core',
|
|
4836
|
-
'@tauri-apps/api/path',
|
|
4837
|
-
'@tauri-apps/api/event',
|
|
4838
|
-
'@tauri-apps/api/webview',
|
|
4839
|
-
'@tauri-apps/plugin-fs',
|
|
4840
|
-
'@tauri-apps/plugin-shell',
|
|
4841
|
-
'@tauri-apps/plugin-dialog',
|
|
4842
|
-
'@tauri-apps/plugin-process',
|
|
4843
|
-
'lucide-vue-next',
|
|
4844
|
-
'date-fns',
|
|
4845
|
-
'dexie',
|
|
4846
|
-
'zod',
|
|
4847
|
-
'@construct-space/ui',
|
|
4848
|
-
'@construct/sdk',
|
|
4849
|
-
'@construct-space/sdk',
|
|
4850
|
-
]
|
|
4851
|
-
|
|
4852
|
-
function makeGlobals(externals: string[]): Record<string, string> {
|
|
4853
|
-
const globals: Record<string, string> = {}
|
|
4854
|
-
for (const ext of externals) {
|
|
4855
|
-
// Simple ids use dot access, scoped/slashed ids use bracket access
|
|
4856
|
-
if (/^[a-z]+$/.test(ext)) {
|
|
4857
|
-
globals[ext] = \`window.__CONSTRUCT__.\${ext}\`
|
|
4858
|
-
} else {
|
|
4859
|
-
globals[ext] = \`window.__CONSTRUCT__["\${ext}"]\`
|
|
4860
|
-
}
|
|
4861
|
-
}
|
|
4862
|
-
return globals
|
|
4863
|
-
}
|
|
4864
|
-
|
|
4865
|
-
export default defineConfig({
|
|
4866
|
-
plugins: [vue()],
|
|
4867
|
-
build: {
|
|
4868
|
-
lib: {
|
|
4869
|
-
entry: resolve(__dirname, 'src/entry.ts'),
|
|
4870
|
-
name: '__CONSTRUCT_SPACE_{{.IDUpper}}',
|
|
4871
|
-
fileName: 'space-{{.ID}}',
|
|
4872
|
-
formats: ['iife'],
|
|
4873
|
-
},
|
|
4874
|
-
rollupOptions: {
|
|
4875
|
-
external: hostExternals,
|
|
4876
|
-
output: {
|
|
4877
|
-
globals: makeGlobals(hostExternals),
|
|
4878
|
-
},
|
|
4879
|
-
},
|
|
4880
|
-
},
|
|
4881
|
-
resolve: {
|
|
4882
|
-
alias: {
|
|
4883
|
-
'~': resolve(__dirname),
|
|
4884
|
-
'@': resolve(__dirname, 'src'),
|
|
4885
|
-
},
|
|
4886
|
-
},
|
|
4887
|
-
})
|
|
4888
|
-
`,
|
|
4889
|
-
"index.vue.tmpl": `<script setup lang="ts">
|
|
4890
|
-
/**
|
|
4891
|
-
* {{.DisplayName}} \u2014 Home page
|
|
4892
|
-
*
|
|
4893
|
-
* Host-provided packages (vue, pinia, @vueuse/core, @construct/sdk, etc.)
|
|
4894
|
-
* are available as imports \u2014 they resolve to the host at runtime.
|
|
4895
|
-
*/
|
|
4896
|
-
import { ref } from 'vue'
|
|
4897
|
-
|
|
4898
|
-
const greeting = ref('Your space is ready. Start building!')
|
|
4899
|
-
</script>
|
|
4900
|
-
|
|
4901
|
-
<template>
|
|
4902
|
-
<div class="h-full flex items-center justify-center">
|
|
4903
|
-
<div class="text-center">
|
|
4904
|
-
<h1 class="text-2xl font-bold text-[var(--app-foreground)] mb-2">{{.DisplayName}}</h1>
|
|
4905
|
-
<p class="text-sm text-[var(--app-muted)]">{{ greeting }}</p>
|
|
4906
|
-
</div>
|
|
4907
|
-
</div>
|
|
4908
|
-
</template>
|
|
4909
|
-
`,
|
|
4910
|
-
"entry.ts.tmpl": `// Space entry \u2014 exports pages, widgets, and actions for the host loader.
|
|
4911
|
-
// \`construct dev\` regenerates this from space.manifest.json on changes.
|
|
4912
|
-
import IndexPage from './pages/index.vue'
|
|
4913
|
-
import { actions } from './actions'
|
|
4914
|
-
|
|
4915
|
-
const spaceExport = {
|
|
4916
|
-
pages: {
|
|
4917
|
-
'': IndexPage,
|
|
4918
|
-
},
|
|
4919
|
-
widgets: {},
|
|
4920
|
-
actions,
|
|
4921
|
-
}
|
|
4922
|
-
|
|
4923
|
-
export default spaceExport
|
|
4924
|
-
`,
|
|
4925
|
-
"space.manifest.json.tmpl": `{
|
|
4926
|
-
"id": "{{.ID}}",
|
|
4927
|
-
"name": "{{.DisplayName}}",
|
|
4928
|
-
"version": "0.1.0",
|
|
4929
|
-
"description": "{{.DisplayName}} space for Construct",
|
|
4930
|
-
"author": {
|
|
4931
|
-
"name": "Your Name"
|
|
4932
|
-
},
|
|
4933
|
-
"icon": "i-lucide-box",
|
|
4934
|
-
"scope": "both",
|
|
4935
|
-
"minConstructVersion": "0.7.0",
|
|
4936
|
-
"navigation": {
|
|
4937
|
-
"label": "{{.DisplayName}}",
|
|
4938
|
-
"icon": "i-lucide-box",
|
|
4939
|
-
"to": "{{.ID}}",
|
|
4940
|
-
"order": 100
|
|
4941
|
-
},
|
|
4942
|
-
"pages": [
|
|
4943
|
-
{
|
|
4944
|
-
"path": "",
|
|
4945
|
-
"label": "Home",
|
|
4946
|
-
"icon": "i-lucide-home",
|
|
4947
|
-
"default": true
|
|
4948
|
-
}
|
|
4949
|
-
],
|
|
4950
|
-
"toolbar": [
|
|
4951
|
-
{
|
|
4952
|
-
"id": "{{.ID}}-new",
|
|
4953
|
-
"icon": "i-lucide-plus",
|
|
4954
|
-
"label": "New {{.DisplayName}}",
|
|
4955
|
-
"action": "create{{.DisplayNameNoSpace}}"
|
|
4956
|
-
}
|
|
4957
|
-
],
|
|
4958
|
-
"keywords": ["{{.ID}}"],
|
|
4959
|
-
"agent": "agent/config.md",
|
|
4960
|
-
"skills": ["agent/skills/default.md"],
|
|
4961
|
-
"actions": {},
|
|
4962
|
-
"widgets": [
|
|
4963
|
-
{
|
|
4964
|
-
"id": "summary",
|
|
4965
|
-
"name": "{{.DisplayName}} Summary",
|
|
4966
|
-
"description": "Quick summary widget for {{.DisplayName}}",
|
|
4967
|
-
"icon": "i-lucide-box",
|
|
4968
|
-
"defaultSize": "4x1",
|
|
4969
|
-
"sizes": {
|
|
4970
|
-
"2x1": "widgets/summary/2x1.vue",
|
|
4971
|
-
"4x1": "widgets/summary/4x1.vue"
|
|
4972
|
-
}
|
|
4973
|
-
}
|
|
4974
|
-
]
|
|
4975
|
-
}
|
|
4976
|
-
`,
|
|
4977
|
-
"skill.md.tmpl": `---
|
|
4978
|
-
id: {{.ID}}-default
|
|
4979
|
-
name: {{.DisplayName}} Basics
|
|
4980
|
-
description: Default skill for {{.DisplayName}} space
|
|
4981
|
-
trigger: {{.ID}}|help
|
|
4982
|
-
category: space
|
|
4983
|
-
tools: [read_file, list_dir, glob, grep]
|
|
4984
|
-
---
|
|
4985
|
-
|
|
4986
|
-
# {{.DisplayName}} Space Skill
|
|
4987
|
-
|
|
4988
|
-
This skill provides default behavior for the {{.DisplayName}} space.
|
|
4989
|
-
|
|
4990
|
-
## Instructions
|
|
4991
|
-
- Assist with {{.DisplayName}}-related tasks
|
|
4992
|
-
- Follow project conventions
|
|
4993
|
-
- Read relevant files before making suggestions
|
|
4994
|
-
`,
|
|
4995
|
-
"gitignore.tmpl": `node_modules/
|
|
4996
|
-
dist/
|
|
4997
|
-
src/entry.ts
|
|
4998
|
-
*.local
|
|
4999
|
-
.DS_Store
|
|
5000
|
-
`,
|
|
5001
|
-
"actions.ts.tmpl": `/**
|
|
5002
|
-
* Space Actions \u2014 exposed to the AI agent via space_run_action
|
|
5003
|
-
*
|
|
5004
|
-
* Define actions here and they'll be automatically available as agent tools.
|
|
5005
|
-
* The agent calls: space_run_action(action: "action_id", payload: {...})
|
|
5006
|
-
*/
|
|
5007
|
-
|
|
5008
|
-
// --- Graph SDK (data layer) ---
|
|
5009
|
-
// Uncomment after running \`construct graph init\` and defining models:
|
|
5010
|
-
//
|
|
5011
|
-
// import { useGraph } from '@construct-space/graph'
|
|
5012
|
-
// const graph = useGraph()
|
|
5013
|
-
//
|
|
5014
|
-
// Example action using Graph:
|
|
5015
|
-
// fetchItems: {
|
|
5016
|
-
// description: 'Fetch items from the graph',
|
|
5017
|
-
// params: {
|
|
5018
|
-
// limit: { type: 'number', description: 'Max items to return', required: false },
|
|
5019
|
-
// },
|
|
5020
|
-
// run: async (p: any) => {
|
|
5021
|
-
// const items = await graph.query('Item').limit(p.limit ?? 10).exec()
|
|
5022
|
-
// return { items }
|
|
5023
|
-
// },
|
|
5024
|
-
// },
|
|
5025
|
-
|
|
5026
|
-
export const actions = {
|
|
5027
|
-
// Example action:
|
|
5028
|
-
// hello: {
|
|
5029
|
-
// description: 'Say hello',
|
|
5030
|
-
// params: {
|
|
5031
|
-
// name: { type: 'string', description: 'Name to greet', required: true },
|
|
5032
|
-
// },
|
|
5033
|
-
// run: (p: any) => ({ message: \`Hello \${p.name}!\` }),
|
|
5034
|
-
// },
|
|
5035
|
-
}
|
|
5036
|
-
`,
|
|
5037
|
-
"readme.md.tmpl": `# {{.DisplayName}}
|
|
5038
|
-
|
|
5039
|
-
A Construct space.
|
|
5040
|
-
|
|
5041
|
-
## Development
|
|
5042
|
-
|
|
5043
|
-
\`\`\`bash
|
|
5044
|
-
bun install
|
|
5045
|
-
construct space dev
|
|
5046
|
-
\`\`\`
|
|
5047
|
-
|
|
5048
|
-
## Build
|
|
5049
|
-
|
|
5050
|
-
\`\`\`bash
|
|
5051
|
-
construct space build
|
|
5052
|
-
\`\`\`
|
|
5053
|
-
|
|
5054
|
-
Output goes to \`dist/\`.
|
|
5055
|
-
`,
|
|
5056
|
-
"safety.json.tmpl": `{
|
|
5057
|
-
"hooks": [
|
|
5058
|
-
{
|
|
5059
|
-
"id": "{{.ID}}-no-rm-rf",
|
|
5060
|
-
"type": "pre_tool",
|
|
5061
|
-
"tools": ["bash"],
|
|
5062
|
-
"command": "if echo \\"$TOOL_INPUT\\" | grep -qE 'rm\\\\s+-rf\\\\s+/'; then\\n echo '{\\"block\\":true,\\"message\\":\\"Blocked: rm -rf with absolute root path\\"}'\\nfi",
|
|
5063
|
-
"source": "space:{{.ID}}"
|
|
5064
|
-
}
|
|
5065
|
-
]
|
|
5066
|
-
}
|
|
5067
|
-
`,
|
|
5068
|
-
"e2e/playwright.config.ts.tmpl": `import { defineConfig } from '@playwright/test'
|
|
5069
|
-
|
|
5070
|
-
export default defineConfig({
|
|
5071
|
-
testDir: './e2e',
|
|
5072
|
-
timeout: 30_000,
|
|
5073
|
-
retries: 0,
|
|
5074
|
-
use: {
|
|
5075
|
-
baseURL: 'http://localhost:5173',
|
|
5076
|
-
trace: 'on-first-retry',
|
|
5077
|
-
},
|
|
5078
|
-
webServer: {
|
|
5079
|
-
command: 'construct dev',
|
|
5080
|
-
port: 5173,
|
|
5081
|
-
reuseExistingServer: true,
|
|
5082
|
-
},
|
|
5083
|
-
})
|
|
5084
|
-
`,
|
|
5085
|
-
"e2e/space.spec.ts.tmpl": `import { test, expect } from '@playwright/test'
|
|
5086
|
-
|
|
5087
|
-
test.describe('{{.DisplayName}} space', () => {
|
|
5088
|
-
test('renders the default page', async ({ page }) => {
|
|
5089
|
-
await page.goto('/')
|
|
5090
|
-
await expect(page.locator('[data-testid="space-root"]')).toBeVisible()
|
|
5091
|
-
})
|
|
5092
|
-
|
|
5093
|
-
test('displays the space title', async ({ page }) => {
|
|
5094
|
-
await page.goto('/')
|
|
5095
|
-
await expect(page.locator('text={{.DisplayName}}')).toBeVisible()
|
|
5096
|
-
})
|
|
5097
|
-
})
|
|
5098
|
-
`,
|
|
5099
|
-
"build.yml.tmpl": `name: Build Space
|
|
5100
|
-
|
|
5101
|
-
on:
|
|
5102
|
-
push:
|
|
5103
|
-
tags:
|
|
5104
|
-
- 'v*'
|
|
5105
|
-
pull_request:
|
|
5106
|
-
branches: [main]
|
|
5107
|
-
|
|
5108
|
-
jobs:
|
|
5109
|
-
build:
|
|
5110
|
-
runs-on: ubuntu-latest
|
|
5111
|
-
steps:
|
|
5112
|
-
- uses: actions/checkout@v4
|
|
5113
|
-
- uses: oven-sh/setup-bun@v2
|
|
5114
|
-
with:
|
|
5115
|
-
bun-version: latest
|
|
5116
|
-
- run: bun install
|
|
5117
|
-
- run: bun run build
|
|
5118
|
-
- name: Upload artifacts
|
|
5119
|
-
if: startsWith(github.ref, 'refs/tags/v')
|
|
5120
|
-
uses: actions/upload-artifact@v4
|
|
5121
|
-
with:
|
|
5122
|
-
name: space-dist
|
|
5123
|
-
path: dist/
|
|
5124
|
-
|
|
5125
|
-
release:
|
|
5126
|
-
needs: build
|
|
5127
|
-
if: startsWith(github.ref, 'refs/tags/v')
|
|
5128
|
-
runs-on: ubuntu-latest
|
|
5129
|
-
permissions:
|
|
5130
|
-
contents: write
|
|
5131
|
-
steps:
|
|
5132
|
-
- uses: actions/download-artifact@v4
|
|
5133
|
-
with:
|
|
5134
|
-
name: space-dist
|
|
5135
|
-
path: dist/
|
|
5136
|
-
- run: tar -czf space-dist.tar.gz -C dist .
|
|
5137
|
-
- uses: softprops/action-gh-release@v2
|
|
5138
|
-
with:
|
|
5139
|
-
files: |
|
|
5140
|
-
space-dist.tar.gz
|
|
5141
|
-
dist/manifest.json
|
|
5142
|
-
generate_release_notes: true
|
|
5143
|
-
`,
|
|
5144
|
-
"widgets/2x1.vue.tmpl": `<script setup lang="ts">
|
|
5145
|
-
/**
|
|
5146
|
-
* {{.DisplayName}} Summary Widget \u2014 2\xD71 compact view
|
|
5147
|
-
*/
|
|
5148
|
-
</script>
|
|
5149
|
-
|
|
5150
|
-
<template>
|
|
5151
|
-
<div class="h-full flex items-center gap-3 px-3">
|
|
5152
|
-
<div class="size-8 rounded-lg bg-[var(--app-accent)]/10 flex items-center justify-center">
|
|
5153
|
-
<Icon name="i-lucide-box" class="size-4 text-[var(--app-accent)]" />
|
|
5154
|
-
</div>
|
|
5155
|
-
<div class="min-w-0">
|
|
5156
|
-
<p class="text-sm font-medium text-[var(--app-foreground)] truncate">{{.DisplayName}}</p>
|
|
5157
|
-
<p class="text-xs text-[var(--app-muted)]">Ready</p>
|
|
5158
|
-
</div>
|
|
5159
|
-
</div>
|
|
5160
|
-
</template>
|
|
5161
|
-
`,
|
|
5162
|
-
"widgets/4x1.vue.tmpl": `<script setup lang="ts">
|
|
5163
|
-
/**
|
|
5164
|
-
* {{.DisplayName}} Summary Widget \u2014 4\xD71 wide view
|
|
5165
|
-
*/
|
|
5166
|
-
</script>
|
|
5167
|
-
|
|
5168
|
-
<template>
|
|
5169
|
-
<div class="h-full flex items-center justify-between px-4">
|
|
5170
|
-
<div class="flex items-center gap-3">
|
|
5171
|
-
<div class="size-8 rounded-lg bg-[var(--app-accent)]/10 flex items-center justify-center">
|
|
5172
|
-
<Icon name="i-lucide-box" class="size-4 text-[var(--app-accent)]" />
|
|
5173
|
-
</div>
|
|
5174
|
-
<div>
|
|
5175
|
-
<p class="text-sm font-medium text-[var(--app-foreground)]">{{.DisplayName}}</p>
|
|
5176
|
-
<p class="text-xs text-[var(--app-muted)]">Your space is ready</p>
|
|
5177
|
-
</div>
|
|
5178
|
-
</div>
|
|
5179
|
-
<div class="text-right">
|
|
5180
|
-
<p class="text-lg font-bold text-[var(--app-foreground)]">0</p>
|
|
5181
|
-
<p class="text-[10px] text-[var(--app-muted)] uppercase">Items</p>
|
|
5182
|
-
</div>
|
|
5183
|
-
</div>
|
|
5184
|
-
</template>
|
|
5185
|
-
`,
|
|
5186
|
-
"tsconfig.json.tmpl": `{
|
|
5187
|
-
"compilerOptions": {
|
|
5188
|
-
"target": "ESNext",
|
|
5189
|
-
"module": "ESNext",
|
|
5190
|
-
"moduleResolution": "bundler",
|
|
5191
|
-
"strict": true,
|
|
5192
|
-
"jsx": "preserve",
|
|
5193
|
-
"noEmit": true,
|
|
5194
|
-
"esModuleInterop": true,
|
|
5195
|
-
"skipLibCheck": true,
|
|
5196
|
-
"paths": {
|
|
5197
|
-
"@/*": ["./src/*"],
|
|
5198
|
-
"@construct/sdk": ["./node_modules/@construct-space/sdk/src/index.ts"]
|
|
5199
|
-
}
|
|
5200
|
-
},
|
|
5201
|
-
"include": [
|
|
5202
|
-
"src/**/*.ts",
|
|
5203
|
-
"src/**/*.vue",
|
|
5204
|
-
"widgets/**/*.vue"
|
|
5205
|
-
]
|
|
5206
|
-
}
|
|
5207
|
-
`
|
|
5208
|
-
};
|
|
5209
|
-
|
|
5210
4583
|
// src/commands/scaffold.ts
|
|
5211
4584
|
var nameRegex = /^[a-z][a-z0-9-]*$/;
|
|
5212
4585
|
function render(template, data) {
|
|
5213
4586
|
return template.replace(/\{\{\.Name\}\}/g, data.name).replace(/\{\{\.ID\}\}/g, data.id).replace(/\{\{\.IDUpper\}\}/g, data.idUpper).replace(/\{\{\.DisplayName\}\}/g, data.displayName).replace(/\{\{\.DisplayNameNoSpace\}\}/g, data.displayNameNoSpace);
|
|
5214
4587
|
}
|
|
5215
4588
|
function writeTemplate(templateDir, tmplName, outPath, data) {
|
|
5216
|
-
let content;
|
|
5217
4589
|
const tmplPath = join2(templateDir, tmplName);
|
|
5218
|
-
if (existsSync2(tmplPath)) {
|
|
5219
|
-
content = readFileSync(tmplPath, "utf-8");
|
|
5220
|
-
} else if (EMBEDDED_TEMPLATES[tmplName] !== undefined) {
|
|
5221
|
-
content = EMBEDDED_TEMPLATES[tmplName];
|
|
5222
|
-
} else {
|
|
4590
|
+
if (!existsSync2(tmplPath)) {
|
|
5223
4591
|
console.warn(source_default.yellow(`Template not found: ${tmplName}`));
|
|
5224
4592
|
return;
|
|
5225
4593
|
}
|
|
4594
|
+
const content = readFileSync(tmplPath, "utf-8");
|
|
5226
4595
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
5227
4596
|
writeFileSync(outPath, render(content, data));
|
|
5228
4597
|
}
|
|
@@ -5325,7 +4694,7 @@ async function scaffold(nameArg, options) {
|
|
|
5325
4694
|
}
|
|
5326
4695
|
|
|
5327
4696
|
// src/commands/build.ts
|
|
5328
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as
|
|
4697
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync, statSync as statSync3 } from "fs";
|
|
5329
4698
|
import { join as join6 } from "path";
|
|
5330
4699
|
import { createHash } from "crypto";
|
|
5331
4700
|
|
|
@@ -7978,8 +7347,8 @@ function validate2(m) {
|
|
|
7978
7347
|
errors2.push("author: must be an object with a name");
|
|
7979
7348
|
if (!m.icon)
|
|
7980
7349
|
errors2.push("icon: must be a string");
|
|
7981
|
-
if (!["
|
|
7982
|
-
errors2.push('scope: must be "
|
|
7350
|
+
if (!["app", "project", "org", "any"].includes(m.scope))
|
|
7351
|
+
errors2.push('scope: must be "app", "project", "org", or "any"');
|
|
7983
7352
|
if (!m.pages?.length)
|
|
7984
7353
|
errors2.push("pages: must be a non-empty array");
|
|
7985
7354
|
if (!m.navigation?.label)
|
|
@@ -8006,30 +7375,84 @@ function exists(dir) {
|
|
|
8006
7375
|
}
|
|
8007
7376
|
|
|
8008
7377
|
// src/lib/entry.ts
|
|
8009
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
8010
|
-
import { join as join4, basename, extname } from "path";
|
|
7378
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4, readdirSync, statSync } from "fs";
|
|
7379
|
+
import { join as join4, basename, extname, relative } from "path";
|
|
8011
7380
|
function capitalize(s) {
|
|
8012
7381
|
if (!s)
|
|
8013
7382
|
return s;
|
|
8014
7383
|
return s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
8015
7384
|
}
|
|
8016
|
-
function
|
|
7385
|
+
function scanPagesDir(pagesDir) {
|
|
7386
|
+
const results = [];
|
|
7387
|
+
function walk(dir, routeSegments) {
|
|
7388
|
+
if (!existsSync4(dir))
|
|
7389
|
+
return;
|
|
7390
|
+
const entries = readdirSync(dir).sort();
|
|
7391
|
+
for (const entry of entries) {
|
|
7392
|
+
const fullPath = join4(dir, entry);
|
|
7393
|
+
const stat = statSync(fullPath);
|
|
7394
|
+
if (stat.isDirectory()) {
|
|
7395
|
+
const segment = entry.replace(/^\[(.+)\]$/, ":$1");
|
|
7396
|
+
walk(fullPath, [...routeSegments, segment]);
|
|
7397
|
+
} else if (stat.isFile() && entry.endsWith(".vue")) {
|
|
7398
|
+
const nameWithoutExt = entry.replace(/\.vue$/, "");
|
|
7399
|
+
const relFile = relative(join4(pagesDir, ".."), fullPath).replace(/\\/g, "/");
|
|
7400
|
+
let routePath;
|
|
7401
|
+
if (nameWithoutExt === "index") {
|
|
7402
|
+
routePath = routeSegments.join("/");
|
|
7403
|
+
} else {
|
|
7404
|
+
const segment = nameWithoutExt.replace(/^\[(.+)\]$/, ":$1");
|
|
7405
|
+
routePath = [...routeSegments, segment].join("/");
|
|
7406
|
+
}
|
|
7407
|
+
results.push({ filePath: relFile, routePath });
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
walk(pagesDir, []);
|
|
7412
|
+
return results;
|
|
7413
|
+
}
|
|
7414
|
+
function varNameFromFile(filePath) {
|
|
7415
|
+
let cleaned = filePath.replace(/^pages\//, "").replace(/\.vue$/, "");
|
|
7416
|
+
cleaned = cleaned.replace(/\[([^\]]+)\]/g, "$1");
|
|
7417
|
+
const segments = cleaned.split(/[\/-]/).filter(Boolean);
|
|
7418
|
+
const name = segments.map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
7419
|
+
return (name || "Index") + "Page";
|
|
7420
|
+
}
|
|
7421
|
+
function resolvePages(m, root, prefix) {
|
|
7422
|
+
const pagesDir = join4(root, "src", "pages");
|
|
7423
|
+
const fsPages = scanPagesDir(pagesDir);
|
|
7424
|
+
const fsMap = new Map;
|
|
7425
|
+
for (const fp of fsPages) {
|
|
7426
|
+
fsMap.set(fp.routePath, fp);
|
|
7427
|
+
}
|
|
8017
7428
|
return m.pages.map((p) => {
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
7429
|
+
if (p.component) {
|
|
7430
|
+
const base = basename(p.component);
|
|
7431
|
+
const nameWithoutExt = base.replace(extname(base), "").replace(/[\[\]]/g, "");
|
|
7432
|
+
const dir = p.component.replace(/^pages\//, "").replace(/\/[^/]+$/, "");
|
|
7433
|
+
let varName;
|
|
7434
|
+
if (dir && dir !== p.component.replace(/^pages\//, "")) {
|
|
7435
|
+
varName = capitalize(dir.split("/").map((s) => s.replace(/[\[\]]/g, "")).join("-")) + capitalize(nameWithoutExt) + "Page";
|
|
7436
|
+
} else {
|
|
7437
|
+
varName = capitalize(nameWithoutExt) + "Page";
|
|
7438
|
+
}
|
|
7439
|
+
return { varName, importPath: prefix + p.component, path: p.path };
|
|
8021
7440
|
}
|
|
8022
|
-
|
|
8023
|
-
if (
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
7441
|
+
const fsPage = fsMap.get(p.path);
|
|
7442
|
+
if (fsPage) {
|
|
7443
|
+
const varName = varNameFromFile(fsPage.filePath);
|
|
7444
|
+
return { varName, importPath: prefix + fsPage.filePath, path: p.path };
|
|
7445
|
+
}
|
|
7446
|
+
const legacyComponent = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
|
|
7447
|
+
const legacyFullPath = join4(root, "src", legacyComponent);
|
|
7448
|
+
if (existsSync4(legacyFullPath)) {
|
|
7449
|
+
let varName = "IndexPage";
|
|
7450
|
+
if (p.path) {
|
|
7451
|
+
varName = capitalize(p.path.replace(/[\/:]/g, "-").replace(/-+/g, "-")) + "Page";
|
|
8029
7452
|
}
|
|
8030
|
-
varName
|
|
7453
|
+
return { varName, importPath: prefix + legacyComponent, path: p.path };
|
|
8031
7454
|
}
|
|
8032
|
-
|
|
7455
|
+
throw new Error(`[entry] Could not resolve component for page "${p.path}". ` + `Checked: manifest component field, filesystem scan of src/pages/, ` + `and legacy fallback at ${legacyComponent}. ` + `Ensure a .vue file exists for this route.`);
|
|
8033
7456
|
});
|
|
8034
7457
|
}
|
|
8035
7458
|
function resolveWidgets(m, prefix) {
|
|
@@ -8048,7 +7471,7 @@ function resolveWidgets(m, prefix) {
|
|
|
8048
7471
|
}
|
|
8049
7472
|
function generate(root, m) {
|
|
8050
7473
|
const pagePrefix = existsSync4(join4(root, "src", "pages")) ? "./" : "../";
|
|
8051
|
-
const pages = resolvePages(m, pagePrefix);
|
|
7474
|
+
const pages = resolvePages(m, root, pagePrefix);
|
|
8052
7475
|
const widgets = resolveWidgets(m, "../");
|
|
8053
7476
|
const actionsPath = join4(root, "src", "actions.ts");
|
|
8054
7477
|
const hasActions = existsSync4(actionsPath);
|
|
@@ -8104,7 +7527,7 @@ function writeEntry(root, m) {
|
|
|
8104
7527
|
}
|
|
8105
7528
|
|
|
8106
7529
|
// src/lib/agent.ts
|
|
8107
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync, existsSync as existsSync5 } from "fs";
|
|
7530
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2, existsSync as existsSync5 } from "fs";
|
|
8108
7531
|
import { join as join5, extname as extname2, basename as basename2 } from "path";
|
|
8109
7532
|
var AGENT_KEY = "construct-agent-obfuscate-v1";
|
|
8110
7533
|
function encode(content) {
|
|
@@ -8120,7 +7543,7 @@ function readMdFiles(dir) {
|
|
|
8120
7543
|
const result = {};
|
|
8121
7544
|
if (!existsSync5(dir))
|
|
8122
7545
|
return result;
|
|
8123
|
-
for (const f of
|
|
7546
|
+
for (const f of readdirSync2(dir)) {
|
|
8124
7547
|
if (extname2(f) !== ".md")
|
|
8125
7548
|
continue;
|
|
8126
7549
|
result[basename2(f, ".md")] = readFileSync3(join5(dir, f), "utf-8");
|
|
@@ -8131,7 +7554,7 @@ function readJsonFiles(dir) {
|
|
|
8131
7554
|
const result = {};
|
|
8132
7555
|
if (!existsSync5(dir))
|
|
8133
7556
|
return result;
|
|
8134
|
-
for (const f of
|
|
7557
|
+
for (const f of readdirSync2(dir)) {
|
|
8135
7558
|
if (extname2(f) !== ".json")
|
|
8136
7559
|
continue;
|
|
8137
7560
|
result[basename2(f, ".json")] = readFileSync3(join5(dir, f), "utf-8");
|
|
@@ -8221,7 +7644,7 @@ async function build(options) {
|
|
|
8221
7644
|
}
|
|
8222
7645
|
runHook(m.hooks, "postBuild", root);
|
|
8223
7646
|
const agentDir = join6(root, "agent");
|
|
8224
|
-
if (existsSync6(agentDir) &&
|
|
7647
|
+
if (existsSync6(agentDir) && statSync3(agentDir).isDirectory()) {
|
|
8225
7648
|
const distDir2 = join6(root, "dist");
|
|
8226
7649
|
bundleAgentDir(agentDir, distDir2);
|
|
8227
7650
|
bundleAgentDir(agentDir, root);
|
|
@@ -8230,7 +7653,7 @@ async function build(options) {
|
|
|
8230
7653
|
const expectedBundle = `space-${m.id}.iife.js`;
|
|
8231
7654
|
let bundlePath = join6(distDir, expectedBundle);
|
|
8232
7655
|
if (!existsSync6(bundlePath)) {
|
|
8233
|
-
const matches =
|
|
7656
|
+
const matches = readdirSync3(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
|
|
8234
7657
|
if (matches.length === 1) {
|
|
8235
7658
|
renameSync(join6(distDir, matches[0]), bundlePath);
|
|
8236
7659
|
const oldCSS = join6(distDir, matches[0].replace(".iife.js", ".css"));
|
|
@@ -8260,20 +7683,20 @@ async function build(options) {
|
|
|
8260
7683
|
}
|
|
8261
7684
|
|
|
8262
7685
|
// src/commands/dev.ts
|
|
8263
|
-
import { existsSync as
|
|
8264
|
-
import { join as
|
|
7686
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
7687
|
+
import { join as join9 } from "path";
|
|
8265
7688
|
import { createHash as createHash2 } from "crypto";
|
|
8266
7689
|
|
|
8267
|
-
// node_modules/chokidar/
|
|
8268
|
-
import { stat as statcb } from "fs";
|
|
8269
|
-
import { stat as stat3, readdir as readdir2 } from "fs/promises";
|
|
7690
|
+
// node_modules/chokidar/index.js
|
|
8270
7691
|
import { EventEmitter } from "events";
|
|
8271
|
-
import
|
|
7692
|
+
import { stat as statcb, Stats } from "fs";
|
|
7693
|
+
import { readdir as readdir2, stat as stat3 } from "fs/promises";
|
|
7694
|
+
import * as sp2 from "path";
|
|
8272
7695
|
|
|
8273
|
-
// node_modules/readdirp/
|
|
8274
|
-
import {
|
|
7696
|
+
// node_modules/readdirp/index.js
|
|
7697
|
+
import { lstat, readdir, realpath, stat } from "fs/promises";
|
|
7698
|
+
import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
|
|
8275
7699
|
import { Readable } from "stream";
|
|
8276
|
-
import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "path";
|
|
8277
7700
|
var EntryTypes = {
|
|
8278
7701
|
FILE_TYPE: "files",
|
|
8279
7702
|
DIR_TYPE: "directories",
|
|
@@ -8329,6 +7752,20 @@ var normalizeFilter = (filter) => {
|
|
|
8329
7752
|
};
|
|
8330
7753
|
|
|
8331
7754
|
class ReaddirpStream extends Readable {
|
|
7755
|
+
parents;
|
|
7756
|
+
reading;
|
|
7757
|
+
parent;
|
|
7758
|
+
_stat;
|
|
7759
|
+
_maxDepth;
|
|
7760
|
+
_wantsDir;
|
|
7761
|
+
_wantsFile;
|
|
7762
|
+
_wantsEverything;
|
|
7763
|
+
_root;
|
|
7764
|
+
_isDirent;
|
|
7765
|
+
_statsProp;
|
|
7766
|
+
_rdOptions;
|
|
7767
|
+
_fileFilter;
|
|
7768
|
+
_directoryFilter;
|
|
8332
7769
|
constructor(options = {}) {
|
|
8333
7770
|
super({
|
|
8334
7771
|
objectMode: true,
|
|
@@ -8345,7 +7782,7 @@ class ReaddirpStream extends Readable {
|
|
|
8345
7782
|
} else {
|
|
8346
7783
|
this._stat = statMethod;
|
|
8347
7784
|
}
|
|
8348
|
-
this._maxDepth = opts.depth
|
|
7785
|
+
this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
|
|
8349
7786
|
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
8350
7787
|
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
8351
7788
|
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
@@ -8490,11 +7927,11 @@ function readdirp(root, options = {}) {
|
|
|
8490
7927
|
return new ReaddirpStream(options);
|
|
8491
7928
|
}
|
|
8492
7929
|
|
|
8493
|
-
// node_modules/chokidar/
|
|
8494
|
-
import {
|
|
8495
|
-
import {
|
|
8496
|
-
import * as sysPath from "path";
|
|
7930
|
+
// node_modules/chokidar/handler.js
|
|
7931
|
+
import { watch as fs_watch, unwatchFile, watchFile } from "fs";
|
|
7932
|
+
import { realpath as fsrealpath, lstat as lstat2, open, stat as stat2 } from "fs/promises";
|
|
8497
7933
|
import { type as osType } from "os";
|
|
7934
|
+
import * as sp from "path";
|
|
8498
7935
|
var STR_DATA = "data";
|
|
8499
7936
|
var STR_END = "end";
|
|
8500
7937
|
var STR_CLOSE = "close";
|
|
@@ -8786,7 +8223,7 @@ var binaryExtensions = new Set([
|
|
|
8786
8223
|
"zip",
|
|
8787
8224
|
"zipx"
|
|
8788
8225
|
]);
|
|
8789
|
-
var isBinaryPath = (filePath) => binaryExtensions.has(
|
|
8226
|
+
var isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase());
|
|
8790
8227
|
var foreach = (val, fn) => {
|
|
8791
8228
|
if (val instanceof Set) {
|
|
8792
8229
|
val.forEach(fn);
|
|
@@ -8824,7 +8261,7 @@ function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
|
|
|
8824
8261
|
listener(path);
|
|
8825
8262
|
emitRaw(rawEvent, evPath, { watchedPath: path });
|
|
8826
8263
|
if (evPath && path !== evPath) {
|
|
8827
|
-
fsWatchBroadcast(
|
|
8264
|
+
fsWatchBroadcast(sp.resolve(path, evPath), KEY_LISTENERS, sp.join(path, evPath));
|
|
8828
8265
|
}
|
|
8829
8266
|
};
|
|
8830
8267
|
try {
|
|
@@ -8939,17 +8376,19 @@ var setFsWatchFileListener = (path, fullPath, options, handlers) => {
|
|
|
8939
8376
|
};
|
|
8940
8377
|
|
|
8941
8378
|
class NodeFsHandler {
|
|
8379
|
+
fsw;
|
|
8380
|
+
_boundHandleError;
|
|
8942
8381
|
constructor(fsW) {
|
|
8943
8382
|
this.fsw = fsW;
|
|
8944
8383
|
this._boundHandleError = (error2) => fsW._handleError(error2);
|
|
8945
8384
|
}
|
|
8946
8385
|
_watchWithNodeFs(path, listener) {
|
|
8947
8386
|
const opts = this.fsw.options;
|
|
8948
|
-
const directory =
|
|
8949
|
-
const basename4 =
|
|
8387
|
+
const directory = sp.dirname(path);
|
|
8388
|
+
const basename4 = sp.basename(path);
|
|
8950
8389
|
const parent = this.fsw._getWatchedDir(directory);
|
|
8951
8390
|
parent.add(basename4);
|
|
8952
|
-
const absolutePath =
|
|
8391
|
+
const absolutePath = sp.resolve(path);
|
|
8953
8392
|
const options = {
|
|
8954
8393
|
persistent: opts.persistent
|
|
8955
8394
|
};
|
|
@@ -8976,8 +8415,8 @@ class NodeFsHandler {
|
|
|
8976
8415
|
if (this.fsw.closed) {
|
|
8977
8416
|
return;
|
|
8978
8417
|
}
|
|
8979
|
-
const dirname3 =
|
|
8980
|
-
const basename4 =
|
|
8418
|
+
const dirname3 = sp.dirname(file);
|
|
8419
|
+
const basename4 = sp.basename(file);
|
|
8981
8420
|
const parent = this.fsw._getWatchedDir(dirname3);
|
|
8982
8421
|
let prevStats = stats;
|
|
8983
8422
|
if (parent.has(basename4))
|
|
@@ -9060,8 +8499,9 @@ class NodeFsHandler {
|
|
|
9060
8499
|
this.fsw._symlinkPaths.set(full, true);
|
|
9061
8500
|
}
|
|
9062
8501
|
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
|
9063
|
-
directory =
|
|
9064
|
-
|
|
8502
|
+
directory = sp.join(directory, "");
|
|
8503
|
+
const throttleKey = target ? `${directory}:${target}` : directory;
|
|
8504
|
+
throttler = this.fsw._throttle("readdir", throttleKey, 1000);
|
|
9065
8505
|
if (!throttler)
|
|
9066
8506
|
return;
|
|
9067
8507
|
const previous = this.fsw._getWatchedDir(wh.path);
|
|
@@ -9078,7 +8518,7 @@ class NodeFsHandler {
|
|
|
9078
8518
|
return;
|
|
9079
8519
|
}
|
|
9080
8520
|
const item = entry.path;
|
|
9081
|
-
let path =
|
|
8521
|
+
let path = sp.join(directory, item);
|
|
9082
8522
|
current.add(item);
|
|
9083
8523
|
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
|
|
9084
8524
|
return;
|
|
@@ -9089,7 +8529,7 @@ class NodeFsHandler {
|
|
|
9089
8529
|
}
|
|
9090
8530
|
if (item === target || !target && !previous.has(item)) {
|
|
9091
8531
|
this.fsw._incrReadyCount();
|
|
9092
|
-
path =
|
|
8532
|
+
path = sp.join(dir, sp.relative(dir, path));
|
|
9093
8533
|
this._addToNodeFs(path, initialAdd, wh, depth + 1);
|
|
9094
8534
|
}
|
|
9095
8535
|
}).on(EV.ERROR, this._boundHandleError);
|
|
@@ -9115,12 +8555,12 @@ class NodeFsHandler {
|
|
|
9115
8555
|
});
|
|
9116
8556
|
}
|
|
9117
8557
|
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
|
|
9118
|
-
const parentDir = this.fsw._getWatchedDir(
|
|
9119
|
-
const tracked = parentDir.has(
|
|
8558
|
+
const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
|
|
8559
|
+
const tracked = parentDir.has(sp.basename(dir));
|
|
9120
8560
|
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
9121
8561
|
this.fsw._emit(EV.ADD_DIR, dir, stats);
|
|
9122
8562
|
}
|
|
9123
|
-
parentDir.add(
|
|
8563
|
+
parentDir.add(sp.basename(dir));
|
|
9124
8564
|
this.fsw._getWatchedDir(dir);
|
|
9125
8565
|
let throttler;
|
|
9126
8566
|
let closer;
|
|
@@ -9161,7 +8601,7 @@ class NodeFsHandler {
|
|
|
9161
8601
|
const follow = this.fsw.options.followSymlinks;
|
|
9162
8602
|
let closer;
|
|
9163
8603
|
if (stats.isDirectory()) {
|
|
9164
|
-
const absPath =
|
|
8604
|
+
const absPath = sp.resolve(path);
|
|
9165
8605
|
const targetPath = follow ? await fsrealpath(path) : path;
|
|
9166
8606
|
if (this.fsw.closed)
|
|
9167
8607
|
return;
|
|
@@ -9175,14 +8615,14 @@ class NodeFsHandler {
|
|
|
9175
8615
|
const targetPath = follow ? await fsrealpath(path) : path;
|
|
9176
8616
|
if (this.fsw.closed)
|
|
9177
8617
|
return;
|
|
9178
|
-
const parent =
|
|
8618
|
+
const parent = sp.dirname(wh.watchPath);
|
|
9179
8619
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
9180
8620
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
9181
8621
|
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
|
|
9182
8622
|
if (this.fsw.closed)
|
|
9183
8623
|
return;
|
|
9184
8624
|
if (targetPath !== undefined) {
|
|
9185
|
-
this.fsw._symlinkPaths.set(
|
|
8625
|
+
this.fsw._symlinkPaths.set(sp.resolve(path), targetPath);
|
|
9186
8626
|
}
|
|
9187
8627
|
} else {
|
|
9188
8628
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
@@ -9200,7 +8640,7 @@ class NodeFsHandler {
|
|
|
9200
8640
|
}
|
|
9201
8641
|
}
|
|
9202
8642
|
|
|
9203
|
-
// node_modules/chokidar/
|
|
8643
|
+
// node_modules/chokidar/index.js
|
|
9204
8644
|
/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
|
|
9205
8645
|
var SLASH = "/";
|
|
9206
8646
|
var SLASH_SLASH = "//";
|
|
@@ -9208,7 +8648,7 @@ var ONE_DOT = ".";
|
|
|
9208
8648
|
var TWO_DOTS = "..";
|
|
9209
8649
|
var STRING_TYPE = "string";
|
|
9210
8650
|
var BACK_SLASH_RE = /\\/g;
|
|
9211
|
-
var DOUBLE_SLASH_RE =
|
|
8651
|
+
var DOUBLE_SLASH_RE = /\/\//g;
|
|
9212
8652
|
var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
|
|
9213
8653
|
var REPLACER_RE = /^\.[/\\]/;
|
|
9214
8654
|
function arrify(item) {
|
|
@@ -9227,11 +8667,11 @@ function createPattern(matcher) {
|
|
|
9227
8667
|
if (matcher.path === string)
|
|
9228
8668
|
return true;
|
|
9229
8669
|
if (matcher.recursive) {
|
|
9230
|
-
const
|
|
9231
|
-
if (!
|
|
8670
|
+
const relative4 = sp2.relative(matcher.path, string);
|
|
8671
|
+
if (!relative4) {
|
|
9232
8672
|
return false;
|
|
9233
8673
|
}
|
|
9234
|
-
return !
|
|
8674
|
+
return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
|
|
9235
8675
|
}
|
|
9236
8676
|
return false;
|
|
9237
8677
|
};
|
|
@@ -9241,14 +8681,12 @@ function createPattern(matcher) {
|
|
|
9241
8681
|
function normalizePath(path) {
|
|
9242
8682
|
if (typeof path !== "string")
|
|
9243
8683
|
throw new Error("string expected");
|
|
9244
|
-
path =
|
|
8684
|
+
path = sp2.normalize(path);
|
|
9245
8685
|
path = path.replace(/\\/g, "/");
|
|
9246
8686
|
let prepend = false;
|
|
9247
8687
|
if (path.startsWith("//"))
|
|
9248
8688
|
prepend = true;
|
|
9249
|
-
|
|
9250
|
-
while (path.match(DOUBLE_SLASH_RE2))
|
|
9251
|
-
path = path.replace(DOUBLE_SLASH_RE2, "/");
|
|
8689
|
+
path = path.replace(DOUBLE_SLASH_RE, "/");
|
|
9252
8690
|
if (prepend)
|
|
9253
8691
|
path = "/" + path;
|
|
9254
8692
|
return path;
|
|
@@ -9289,31 +8727,32 @@ var toUnix = (string) => {
|
|
|
9289
8727
|
if (str.startsWith(SLASH_SLASH)) {
|
|
9290
8728
|
prepend = true;
|
|
9291
8729
|
}
|
|
9292
|
-
|
|
9293
|
-
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
|
9294
|
-
}
|
|
8730
|
+
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
|
9295
8731
|
if (prepend) {
|
|
9296
8732
|
str = SLASH + str;
|
|
9297
8733
|
}
|
|
9298
8734
|
return str;
|
|
9299
8735
|
};
|
|
9300
|
-
var normalizePathToUnix = (path) => toUnix(
|
|
8736
|
+
var normalizePathToUnix = (path) => toUnix(sp2.normalize(toUnix(path)));
|
|
9301
8737
|
var normalizeIgnored = (cwd = "") => (path) => {
|
|
9302
8738
|
if (typeof path === "string") {
|
|
9303
|
-
return normalizePathToUnix(
|
|
8739
|
+
return normalizePathToUnix(sp2.isAbsolute(path) ? path : sp2.join(cwd, path));
|
|
9304
8740
|
} else {
|
|
9305
8741
|
return path;
|
|
9306
8742
|
}
|
|
9307
8743
|
};
|
|
9308
8744
|
var getAbsolutePath = (path, cwd) => {
|
|
9309
|
-
if (
|
|
8745
|
+
if (sp2.isAbsolute(path)) {
|
|
9310
8746
|
return path;
|
|
9311
8747
|
}
|
|
9312
|
-
return
|
|
8748
|
+
return sp2.join(cwd, path);
|
|
9313
8749
|
};
|
|
9314
8750
|
var EMPTY_SET = Object.freeze(new Set);
|
|
9315
8751
|
|
|
9316
8752
|
class DirEntry {
|
|
8753
|
+
path;
|
|
8754
|
+
_removeWatcher;
|
|
8755
|
+
items;
|
|
9317
8756
|
constructor(dir, removeWatcher) {
|
|
9318
8757
|
this.path = dir;
|
|
9319
8758
|
this._removeWatcher = removeWatcher;
|
|
@@ -9338,7 +8777,7 @@ class DirEntry {
|
|
|
9338
8777
|
await readdir2(dir);
|
|
9339
8778
|
} catch (err) {
|
|
9340
8779
|
if (this._removeWatcher) {
|
|
9341
|
-
this._removeWatcher(
|
|
8780
|
+
this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
|
|
9342
8781
|
}
|
|
9343
8782
|
}
|
|
9344
8783
|
}
|
|
@@ -9366,12 +8805,19 @@ var STAT_METHOD_F = "stat";
|
|
|
9366
8805
|
var STAT_METHOD_L = "lstat";
|
|
9367
8806
|
|
|
9368
8807
|
class WatchHelper {
|
|
8808
|
+
fsw;
|
|
8809
|
+
path;
|
|
8810
|
+
watchPath;
|
|
8811
|
+
fullWatchPath;
|
|
8812
|
+
dirParts;
|
|
8813
|
+
followSymlinks;
|
|
8814
|
+
statMethod;
|
|
9369
8815
|
constructor(path, follow, fsw) {
|
|
9370
8816
|
this.fsw = fsw;
|
|
9371
8817
|
const watchPath = path;
|
|
9372
8818
|
this.path = path = path.replace(REPLACER_RE, "");
|
|
9373
8819
|
this.watchPath = watchPath;
|
|
9374
|
-
this.fullWatchPath =
|
|
8820
|
+
this.fullWatchPath = sp2.resolve(watchPath);
|
|
9375
8821
|
this.dirParts = [];
|
|
9376
8822
|
this.dirParts.forEach((parts) => {
|
|
9377
8823
|
if (parts.length > 1)
|
|
@@ -9381,7 +8827,7 @@ class WatchHelper {
|
|
|
9381
8827
|
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
|
9382
8828
|
}
|
|
9383
8829
|
entryPath(entry) {
|
|
9384
|
-
return
|
|
8830
|
+
return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
|
|
9385
8831
|
}
|
|
9386
8832
|
filterPath(entry) {
|
|
9387
8833
|
const { stats } = entry;
|
|
@@ -9396,6 +8842,24 @@ class WatchHelper {
|
|
|
9396
8842
|
}
|
|
9397
8843
|
|
|
9398
8844
|
class FSWatcher extends EventEmitter {
|
|
8845
|
+
closed;
|
|
8846
|
+
options;
|
|
8847
|
+
_closers;
|
|
8848
|
+
_ignoredPaths;
|
|
8849
|
+
_throttled;
|
|
8850
|
+
_streams;
|
|
8851
|
+
_symlinkPaths;
|
|
8852
|
+
_watched;
|
|
8853
|
+
_pendingWrites;
|
|
8854
|
+
_pendingUnlinks;
|
|
8855
|
+
_readyCount;
|
|
8856
|
+
_emitReady;
|
|
8857
|
+
_closePromise;
|
|
8858
|
+
_userIgnored;
|
|
8859
|
+
_readyEmitted;
|
|
8860
|
+
_emitRaw;
|
|
8861
|
+
_boundRemove;
|
|
8862
|
+
_nodeFsHandler;
|
|
9399
8863
|
constructor(_opts = {}) {
|
|
9400
8864
|
super();
|
|
9401
8865
|
this.closed = false;
|
|
@@ -9504,7 +8968,7 @@ class FSWatcher extends EventEmitter {
|
|
|
9504
8968
|
return;
|
|
9505
8969
|
results.forEach((item) => {
|
|
9506
8970
|
if (item)
|
|
9507
|
-
this.add(
|
|
8971
|
+
this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
|
|
9508
8972
|
});
|
|
9509
8973
|
});
|
|
9510
8974
|
return this;
|
|
@@ -9515,10 +8979,10 @@ class FSWatcher extends EventEmitter {
|
|
|
9515
8979
|
const paths = unifyPaths(paths_);
|
|
9516
8980
|
const { cwd } = this.options;
|
|
9517
8981
|
paths.forEach((path) => {
|
|
9518
|
-
if (!
|
|
8982
|
+
if (!sp2.isAbsolute(path) && !this._closers.has(path)) {
|
|
9519
8983
|
if (cwd)
|
|
9520
|
-
path =
|
|
9521
|
-
path =
|
|
8984
|
+
path = sp2.join(cwd, path);
|
|
8985
|
+
path = sp2.resolve(path);
|
|
9522
8986
|
}
|
|
9523
8987
|
this._closePath(path);
|
|
9524
8988
|
this._addIgnoredPath(path);
|
|
@@ -9562,7 +9026,7 @@ class FSWatcher extends EventEmitter {
|
|
|
9562
9026
|
getWatched() {
|
|
9563
9027
|
const watchList = {};
|
|
9564
9028
|
this._watched.forEach((entry, dir) => {
|
|
9565
|
-
const key = this.options.cwd ?
|
|
9029
|
+
const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
|
|
9566
9030
|
const index = key || ONE_DOT;
|
|
9567
9031
|
watchList[index] = entry.getChildren().sort();
|
|
9568
9032
|
});
|
|
@@ -9578,9 +9042,9 @@ class FSWatcher extends EventEmitter {
|
|
|
9578
9042
|
return;
|
|
9579
9043
|
const opts = this.options;
|
|
9580
9044
|
if (isWindows)
|
|
9581
|
-
path =
|
|
9045
|
+
path = sp2.normalize(path);
|
|
9582
9046
|
if (opts.cwd)
|
|
9583
|
-
path =
|
|
9047
|
+
path = sp2.relative(opts.cwd, path);
|
|
9584
9048
|
const args = [path];
|
|
9585
9049
|
if (stats != null)
|
|
9586
9050
|
args.push(stats);
|
|
@@ -9631,7 +9095,7 @@ class FSWatcher extends EventEmitter {
|
|
|
9631
9095
|
return this;
|
|
9632
9096
|
}
|
|
9633
9097
|
if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
9634
|
-
const fullPath = opts.cwd ?
|
|
9098
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path) : path;
|
|
9635
9099
|
let stats2;
|
|
9636
9100
|
try {
|
|
9637
9101
|
stats2 = await stat3(fullPath);
|
|
@@ -9687,8 +9151,8 @@ class FSWatcher extends EventEmitter {
|
|
|
9687
9151
|
const pollInterval = awf.pollInterval;
|
|
9688
9152
|
let timeoutHandler;
|
|
9689
9153
|
let fullPath = path;
|
|
9690
|
-
if (this.options.cwd && !
|
|
9691
|
-
fullPath =
|
|
9154
|
+
if (this.options.cwd && !sp2.isAbsolute(path)) {
|
|
9155
|
+
fullPath = sp2.join(this.options.cwd, path);
|
|
9692
9156
|
}
|
|
9693
9157
|
const now = new Date;
|
|
9694
9158
|
const writes = this._pendingWrites;
|
|
@@ -9745,7 +9209,7 @@ class FSWatcher extends EventEmitter {
|
|
|
9745
9209
|
return new WatchHelper(path, this.options.followSymlinks, this);
|
|
9746
9210
|
}
|
|
9747
9211
|
_getWatchedDir(directory) {
|
|
9748
|
-
const dir =
|
|
9212
|
+
const dir = sp2.resolve(directory);
|
|
9749
9213
|
if (!this._watched.has(dir))
|
|
9750
9214
|
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
|
9751
9215
|
return this._watched.get(dir);
|
|
@@ -9756,8 +9220,8 @@ class FSWatcher extends EventEmitter {
|
|
|
9756
9220
|
return Boolean(Number(stats.mode) & 256);
|
|
9757
9221
|
}
|
|
9758
9222
|
_remove(directory, item, isDirectory) {
|
|
9759
|
-
const path =
|
|
9760
|
-
const fullPath =
|
|
9223
|
+
const path = sp2.join(directory, item);
|
|
9224
|
+
const fullPath = sp2.resolve(path);
|
|
9761
9225
|
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
|
|
9762
9226
|
if (!this._throttle("remove", path, 100))
|
|
9763
9227
|
return;
|
|
@@ -9775,7 +9239,7 @@ class FSWatcher extends EventEmitter {
|
|
|
9775
9239
|
}
|
|
9776
9240
|
let relPath = path;
|
|
9777
9241
|
if (this.options.cwd)
|
|
9778
|
-
relPath =
|
|
9242
|
+
relPath = sp2.relative(this.options.cwd, path);
|
|
9779
9243
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
9780
9244
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
9781
9245
|
if (event === EVENTS.ADD)
|
|
@@ -9790,8 +9254,8 @@ class FSWatcher extends EventEmitter {
|
|
|
9790
9254
|
}
|
|
9791
9255
|
_closePath(path) {
|
|
9792
9256
|
this._closeFile(path);
|
|
9793
|
-
const dir =
|
|
9794
|
-
this._getWatchedDir(dir).remove(
|
|
9257
|
+
const dir = sp2.dirname(path);
|
|
9258
|
+
this._getWatchedDir(dir).remove(sp2.basename(path));
|
|
9795
9259
|
}
|
|
9796
9260
|
_closeFile(path) {
|
|
9797
9261
|
const closers = this._closers.get(path);
|
|
@@ -9834,55 +9298,11 @@ function watch(paths, options = {}) {
|
|
|
9834
9298
|
return watcher;
|
|
9835
9299
|
}
|
|
9836
9300
|
|
|
9837
|
-
// src/lib/appdir.ts
|
|
9838
|
-
import { join as join9 } from "path";
|
|
9839
|
-
import { homedir } from "os";
|
|
9840
|
-
import { platform as platform2 } from "process";
|
|
9841
|
-
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
9842
|
-
function dataDir() {
|
|
9843
|
-
if (process.env.CONSTRUCT_DATA_DIR)
|
|
9844
|
-
return process.env.CONSTRUCT_DATA_DIR;
|
|
9845
|
-
const home = homedir();
|
|
9846
|
-
switch (platform2) {
|
|
9847
|
-
case "darwin":
|
|
9848
|
-
return join9(home, "Library", "Application Support", "Construct");
|
|
9849
|
-
case "win32": {
|
|
9850
|
-
const appData = process.env.APPDATA || join9(home, "AppData", "Roaming");
|
|
9851
|
-
return join9(appData, "Construct");
|
|
9852
|
-
}
|
|
9853
|
-
default: {
|
|
9854
|
-
const xdg = process.env.XDG_DATA_HOME || join9(home, ".local", "share");
|
|
9855
|
-
return join9(xdg, "construct");
|
|
9856
|
-
}
|
|
9857
|
-
}
|
|
9858
|
-
}
|
|
9859
|
-
function activeProfileId() {
|
|
9860
|
-
try {
|
|
9861
|
-
const profilesPath = join9(dataDir(), "profiles.json");
|
|
9862
|
-
if (!existsSync7(profilesPath))
|
|
9863
|
-
return null;
|
|
9864
|
-
const data = JSON.parse(readFileSync5(profilesPath, "utf-8"));
|
|
9865
|
-
return data?.active_profile || null;
|
|
9866
|
-
} catch {
|
|
9867
|
-
return null;
|
|
9868
|
-
}
|
|
9869
|
-
}
|
|
9870
|
-
function spacesDir() {
|
|
9871
|
-
const profileId = activeProfileId();
|
|
9872
|
-
if (profileId) {
|
|
9873
|
-
return join9(dataDir(), "profiles", profileId, "spaces");
|
|
9874
|
-
}
|
|
9875
|
-
return join9(dataDir(), "spaces");
|
|
9876
|
-
}
|
|
9877
|
-
function spaceDir(spaceId) {
|
|
9878
|
-
return join9(spacesDir(), spaceId);
|
|
9879
|
-
}
|
|
9880
|
-
|
|
9881
9301
|
// src/commands/dev.ts
|
|
9882
9302
|
function getEntryWatchPaths(root) {
|
|
9883
9303
|
return [
|
|
9884
|
-
|
|
9885
|
-
|
|
9304
|
+
join9(root, MANIFEST_FILE),
|
|
9305
|
+
join9(root, "src", "actions.ts")
|
|
9886
9306
|
];
|
|
9887
9307
|
}
|
|
9888
9308
|
async function dev() {
|
|
@@ -9921,14 +9341,14 @@ async function dev() {
|
|
|
9921
9341
|
}
|
|
9922
9342
|
console.log(source_default.blue("Actions changed \u2014 entry regenerated"));
|
|
9923
9343
|
});
|
|
9924
|
-
const distDir =
|
|
9925
|
-
const bundleFile =
|
|
9344
|
+
const distDir = join9(root, "dist");
|
|
9345
|
+
const bundleFile = join9(distDir, `space-${m.id}.iife.js`);
|
|
9926
9346
|
let lastChecksum = "";
|
|
9927
9347
|
const distWatcher = watch(bundleFile, { ignoreInitial: false });
|
|
9928
9348
|
distWatcher.on("all", () => {
|
|
9929
|
-
if (!
|
|
9349
|
+
if (!existsSync7(bundleFile))
|
|
9930
9350
|
return;
|
|
9931
|
-
const bundleData =
|
|
9351
|
+
const bundleData = readFileSync5(bundleFile);
|
|
9932
9352
|
const checksum = createHash2("sha256").update(bundleData).digest("hex");
|
|
9933
9353
|
if (checksum === lastChecksum)
|
|
9934
9354
|
return;
|
|
@@ -9940,15 +9360,7 @@ async function dev() {
|
|
|
9940
9360
|
hostApiVersion: "0.2.0",
|
|
9941
9361
|
builtAt: new Date().toISOString()
|
|
9942
9362
|
});
|
|
9943
|
-
|
|
9944
|
-
const installDir = spaceDir(m.id);
|
|
9945
|
-
mkdirSync3(installDir, { recursive: true });
|
|
9946
|
-
cpSync(distDir, installDir, { recursive: true });
|
|
9947
|
-
console.log(source_default.green(`Built + installed \u2192 ${installDir} (${(bundleData.length / 1024).toFixed(1)} KB)`));
|
|
9948
|
-
} catch (err) {
|
|
9949
|
-
console.log(source_default.green(`Built \u2192 dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
|
|
9950
|
-
console.log(source_default.yellow(` Install failed: ${err.message}`));
|
|
9951
|
-
}
|
|
9363
|
+
console.log(source_default.green(`Built \u2192 dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
|
|
9952
9364
|
});
|
|
9953
9365
|
console.log(source_default.green("Watching for changes... (Ctrl+C to stop)"));
|
|
9954
9366
|
console.log(source_default.dim("Use the Preview button in Construct to open the Space Runner"));
|
|
@@ -9956,8 +9368,41 @@ async function dev() {
|
|
|
9956
9368
|
}
|
|
9957
9369
|
|
|
9958
9370
|
// src/commands/run.ts
|
|
9959
|
-
import { existsSync as
|
|
9371
|
+
import { existsSync as existsSync8, cpSync, mkdirSync as mkdirSync3 } from "fs";
|
|
9960
9372
|
import { join as join11 } from "path";
|
|
9373
|
+
|
|
9374
|
+
// src/lib/appdir.ts
|
|
9375
|
+
import { join as join10 } from "path";
|
|
9376
|
+
import { homedir } from "os";
|
|
9377
|
+
import { platform } from "process";
|
|
9378
|
+
function dataDir() {
|
|
9379
|
+
if (process.env.CONSTRUCT_DATA_DIR)
|
|
9380
|
+
return process.env.CONSTRUCT_DATA_DIR;
|
|
9381
|
+
const home = homedir();
|
|
9382
|
+
switch (platform) {
|
|
9383
|
+
case "darwin":
|
|
9384
|
+
return join10(home, "Library", "Application Support", "Construct");
|
|
9385
|
+
case "win32": {
|
|
9386
|
+
const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
|
|
9387
|
+
return join10(appData, "Construct");
|
|
9388
|
+
}
|
|
9389
|
+
default: {
|
|
9390
|
+
const xdg = process.env.XDG_DATA_HOME || join10(home, ".local", "share");
|
|
9391
|
+
return join10(xdg, "construct");
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
}
|
|
9395
|
+
function spacesDir() {
|
|
9396
|
+
return join10(dataDir(), "spaces");
|
|
9397
|
+
}
|
|
9398
|
+
function profilesDir() {
|
|
9399
|
+
return join10(dataDir(), "profiles");
|
|
9400
|
+
}
|
|
9401
|
+
function spaceDir(spaceId) {
|
|
9402
|
+
return join10(spacesDir(), spaceId);
|
|
9403
|
+
}
|
|
9404
|
+
|
|
9405
|
+
// src/commands/run.ts
|
|
9961
9406
|
function install() {
|
|
9962
9407
|
const root = process.cwd();
|
|
9963
9408
|
if (!exists(root)) {
|
|
@@ -9965,83 +9410,75 @@ function install() {
|
|
|
9965
9410
|
process.exit(1);
|
|
9966
9411
|
}
|
|
9967
9412
|
const distDir = join11(root, "dist");
|
|
9968
|
-
if (!
|
|
9413
|
+
if (!existsSync8(distDir)) {
|
|
9969
9414
|
console.error(source_default.red("No dist/ directory found. Run 'construct build' first."));
|
|
9970
9415
|
process.exit(1);
|
|
9971
9416
|
}
|
|
9972
9417
|
const m = read(root);
|
|
9973
9418
|
const agentDir = join11(root, "agent");
|
|
9974
|
-
if (
|
|
9419
|
+
if (existsSync8(agentDir)) {
|
|
9975
9420
|
bundleAgentDir(agentDir, distDir);
|
|
9976
9421
|
}
|
|
9977
9422
|
const installDir = spaceDir(m.id);
|
|
9978
|
-
|
|
9979
|
-
|
|
9423
|
+
mkdirSync3(installDir, { recursive: true });
|
|
9424
|
+
cpSync(distDir, installDir, { recursive: true });
|
|
9980
9425
|
console.log(source_default.green(`Installed ${m.name} \u2192 ${installDir}`));
|
|
9981
9426
|
console.log(source_default.dim(" Restart Construct to load the updated space."));
|
|
9982
9427
|
}
|
|
9983
9428
|
|
|
9984
9429
|
// src/commands/publish.ts
|
|
9985
|
-
import { readFileSync as
|
|
9430
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
9986
9431
|
import { join as join14, basename as basename6 } from "path";
|
|
9987
9432
|
|
|
9988
9433
|
// src/lib/auth.ts
|
|
9989
|
-
import { readFileSync as
|
|
9434
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync, existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
9990
9435
|
import { join as join12, dirname as dirname4 } from "path";
|
|
9991
9436
|
var CREDENTIALS_FILE = "credentials.json";
|
|
9992
|
-
var
|
|
9993
|
-
|
|
9437
|
+
var DEFAULT_PORTAL = "https://my.construct.space/api/developer";
|
|
9438
|
+
function listDesktopProfiles() {
|
|
9439
|
+
const dir = profilesDir();
|
|
9440
|
+
if (!existsSync9(dir))
|
|
9441
|
+
return [];
|
|
9442
|
+
const results = [];
|
|
9443
|
+
for (const entry of readdirSync4(dir)) {
|
|
9444
|
+
const full = join12(dir, entry);
|
|
9445
|
+
try {
|
|
9446
|
+
if (!statSync4(full).isDirectory())
|
|
9447
|
+
continue;
|
|
9448
|
+
const authPath = join12(full, "auth.json");
|
|
9449
|
+
if (!existsSync9(authPath))
|
|
9450
|
+
continue;
|
|
9451
|
+
const data = JSON.parse(readFileSync6(authPath, "utf-8"));
|
|
9452
|
+
if (!data.token)
|
|
9453
|
+
continue;
|
|
9454
|
+
if (data.authenticated !== undefined && !data.authenticated)
|
|
9455
|
+
continue;
|
|
9456
|
+
results.push({
|
|
9457
|
+
id: entry,
|
|
9458
|
+
token: data.token,
|
|
9459
|
+
user: data.user,
|
|
9460
|
+
updatedAt: data.updated_at,
|
|
9461
|
+
authenticated: true
|
|
9462
|
+
});
|
|
9463
|
+
} catch {}
|
|
9464
|
+
}
|
|
9465
|
+
return results;
|
|
9466
|
+
}
|
|
9994
9467
|
function credentialsPath() {
|
|
9995
9468
|
return join12(dataDir(), CREDENTIALS_FILE);
|
|
9996
9469
|
}
|
|
9997
|
-
function appProfilesPath() {
|
|
9998
|
-
return join12(dataDir(), APP_PROFILES_FILE);
|
|
9999
|
-
}
|
|
10000
|
-
function appProfileAuthPath(profileId) {
|
|
10001
|
-
return join12(dataDir(), "profiles", profileId, "auth.json");
|
|
10002
|
-
}
|
|
10003
|
-
function loadFromApp() {
|
|
10004
|
-
try {
|
|
10005
|
-
const profilesPath = appProfilesPath();
|
|
10006
|
-
if (!existsSync10(profilesPath))
|
|
10007
|
-
return null;
|
|
10008
|
-
const profiles = JSON.parse(readFileSync7(profilesPath, "utf-8"));
|
|
10009
|
-
if (!profiles?.active_profile)
|
|
10010
|
-
return null;
|
|
10011
|
-
const authPath = appProfileAuthPath(profiles.active_profile);
|
|
10012
|
-
if (!existsSync10(authPath))
|
|
10013
|
-
return null;
|
|
10014
|
-
const auth = JSON.parse(readFileSync7(authPath, "utf-8"));
|
|
10015
|
-
if (!auth?.authenticated || !auth?.token)
|
|
10016
|
-
return null;
|
|
10017
|
-
return {
|
|
10018
|
-
token: auth.token,
|
|
10019
|
-
portal: DEFAULT_PORTAL,
|
|
10020
|
-
user: {
|
|
10021
|
-
id: auth.user.id,
|
|
10022
|
-
name: auth.user.name || [auth.user.first_name, auth.user.last_name].filter(Boolean).join(" ") || auth.user.email,
|
|
10023
|
-
email: auth.user.email
|
|
10024
|
-
}
|
|
10025
|
-
};
|
|
10026
|
-
} catch {
|
|
10027
|
-
return null;
|
|
10028
|
-
}
|
|
10029
|
-
}
|
|
10030
9470
|
function store(creds) {
|
|
10031
9471
|
const path = credentialsPath();
|
|
10032
|
-
|
|
9472
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
10033
9473
|
writeFileSync5(path, JSON.stringify(creds, null, 2) + `
|
|
10034
9474
|
`, { mode: 384 });
|
|
10035
9475
|
}
|
|
10036
9476
|
function load2() {
|
|
10037
|
-
const fromApp = loadFromApp();
|
|
10038
|
-
if (fromApp)
|
|
10039
|
-
return fromApp;
|
|
10040
9477
|
const path = credentialsPath();
|
|
10041
|
-
if (!
|
|
10042
|
-
throw new Error("not logged in \u2014 run 'construct login' first
|
|
9478
|
+
if (!existsSync9(path)) {
|
|
9479
|
+
throw new Error("not logged in \u2014 run 'construct login' first");
|
|
10043
9480
|
}
|
|
10044
|
-
const data = JSON.parse(
|
|
9481
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
10045
9482
|
if (!data.token) {
|
|
10046
9483
|
throw new Error("not logged in \u2014 run 'construct login' first");
|
|
10047
9484
|
}
|
|
@@ -10057,12 +9494,12 @@ function isAuthenticated() {
|
|
|
10057
9494
|
}
|
|
10058
9495
|
function clear() {
|
|
10059
9496
|
const path = credentialsPath();
|
|
10060
|
-
if (
|
|
9497
|
+
if (existsSync9(path))
|
|
10061
9498
|
unlinkSync(path);
|
|
10062
9499
|
}
|
|
10063
9500
|
|
|
10064
9501
|
// src/lib/pack.ts
|
|
10065
|
-
import { readdirSync as
|
|
9502
|
+
import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
|
|
10066
9503
|
import { join as join13 } from "path";
|
|
10067
9504
|
import { tmpdir } from "os";
|
|
10068
9505
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -10101,11 +9538,11 @@ async function packSource(root) {
|
|
|
10101
9538
|
const tarballPath = join13(tmpdir(), `space-source-${Date.now()}.tar.gz`);
|
|
10102
9539
|
const entries = [];
|
|
10103
9540
|
for (const name of allowedRootFiles) {
|
|
10104
|
-
if (
|
|
9541
|
+
if (existsSync10(join13(root, name)))
|
|
10105
9542
|
entries.push(name);
|
|
10106
9543
|
}
|
|
10107
|
-
for (const entry of
|
|
10108
|
-
if (
|
|
9544
|
+
for (const entry of readdirSync5(root)) {
|
|
9545
|
+
if (statSync5(join13(root, entry)).isDirectory())
|
|
10109
9546
|
continue;
|
|
10110
9547
|
if (allowedRootFiles.includes(entry))
|
|
10111
9548
|
continue;
|
|
@@ -10115,17 +9552,17 @@ async function packSource(root) {
|
|
|
10115
9552
|
entries.push(entry);
|
|
10116
9553
|
}
|
|
10117
9554
|
for (const dir of allowedDirs) {
|
|
10118
|
-
if (
|
|
9555
|
+
if (existsSync10(join13(root, dir)))
|
|
10119
9556
|
entries.push(dir);
|
|
10120
9557
|
}
|
|
10121
|
-
const validEntries = entries.filter((e) =>
|
|
9558
|
+
const validEntries = entries.filter((e) => existsSync10(join13(root, e)));
|
|
10122
9559
|
if (validEntries.length === 0) {
|
|
10123
9560
|
throw new Error("No files to pack");
|
|
10124
9561
|
}
|
|
10125
9562
|
const excludes = "--exclude=node_modules --exclude=dist --exclude=.git --exclude=*.env --exclude=*.log --exclude=*.lock --exclude=*.lockb";
|
|
10126
9563
|
const cmd = `tar czf "${tarballPath}" ${excludes} ${validEntries.join(" ")}`;
|
|
10127
9564
|
execSync3(cmd, { cwd: root });
|
|
10128
|
-
const size =
|
|
9565
|
+
const size = statSync5(tarballPath).size;
|
|
10129
9566
|
if (size > MAX_SIZE) {
|
|
10130
9567
|
throw new Error(`Source exceeds maximum size of ${MAX_SIZE / 1024 / 1024}MB`);
|
|
10131
9568
|
}
|
|
@@ -10136,10 +9573,10 @@ async function packSource(root) {
|
|
|
10136
9573
|
async function uploadSource(portalURL, token, tarballPath, m) {
|
|
10137
9574
|
const formData = new FormData;
|
|
10138
9575
|
formData.append("manifest", JSON.stringify(m));
|
|
10139
|
-
const fileData =
|
|
9576
|
+
const fileData = readFileSync7(tarballPath);
|
|
10140
9577
|
const blob = new Blob([fileData]);
|
|
10141
9578
|
formData.append("source", blob, basename6(tarballPath));
|
|
10142
|
-
const resp = await fetch(`${portalURL}/
|
|
9579
|
+
const resp = await fetch(`${portalURL}/publish`, {
|
|
10143
9580
|
method: "POST",
|
|
10144
9581
|
headers: { Authorization: `Bearer ${token}` },
|
|
10145
9582
|
body: formData
|
|
@@ -10148,6 +9585,16 @@ async function uploadSource(portalURL, token, tarballPath, m) {
|
|
|
10148
9585
|
if (resp.status === 401) {
|
|
10149
9586
|
throw new Error("authentication failed \u2014 run 'construct login' to re-authenticate");
|
|
10150
9587
|
}
|
|
9588
|
+
if (resp.status === 403) {
|
|
9589
|
+
let msg = result.error || "You are not the owner of this space";
|
|
9590
|
+
if (result.owner_user_id) {
|
|
9591
|
+
msg += `
|
|
9592
|
+
Current owner: ${result.owner_user_id}`;
|
|
9593
|
+
}
|
|
9594
|
+
msg += `
|
|
9595
|
+
Fork to a new space_id to publish your own version.`;
|
|
9596
|
+
throw new Error(msg);
|
|
9597
|
+
}
|
|
10151
9598
|
if (resp.status >= 400) {
|
|
10152
9599
|
const msg = result.error || result.errors?.join("; ") || `server returned ${resp.status}`;
|
|
10153
9600
|
throw new Error(msg);
|
|
@@ -10160,7 +9607,7 @@ function setVersionInFiles(root, oldVer, newVer) {
|
|
|
10160
9607
|
for (const file of ["package.json", "space.manifest.json"]) {
|
|
10161
9608
|
const path = join14(root, file);
|
|
10162
9609
|
try {
|
|
10163
|
-
const data =
|
|
9610
|
+
const data = readFileSync7(path, "utf-8");
|
|
10164
9611
|
writeFileSync6(path, data.replace(oldStr, newStr));
|
|
10165
9612
|
} catch {}
|
|
10166
9613
|
}
|
|
@@ -10182,24 +9629,6 @@ async function publish(options) {
|
|
|
10182
9629
|
console.log(source_default.dim(" Run 'construct login' to authenticate."));
|
|
10183
9630
|
process.exit(1);
|
|
10184
9631
|
}
|
|
10185
|
-
const publishers = creds.publishers || [];
|
|
10186
|
-
if (publishers.length === 0) {
|
|
10187
|
-
console.log(source_default.yellow(" No publisher linked to this session. The upload will likely be rejected."));
|
|
10188
|
-
console.log(source_default.dim(" Run 'construct login' after enrolling at developer.construct.space."));
|
|
10189
|
-
} else {
|
|
10190
|
-
const primary = publishers[0];
|
|
10191
|
-
const kindLabel = primary.kind === "org" ? source_default.cyan("org") : primary.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
|
|
10192
|
-
console.log(source_default.dim(` Publishing as ${primary.name} (${kindLabel})`));
|
|
10193
|
-
if (publishers.length > 1 && !yes) {
|
|
10194
|
-
const proceed = await dist_default4({
|
|
10195
|
-
message: `You have ${publishers.length} publisher identities. Publish under ${primary.name}?`
|
|
10196
|
-
});
|
|
10197
|
-
if (!proceed) {
|
|
10198
|
-
console.log("Cancelled.");
|
|
10199
|
-
return;
|
|
10200
|
-
}
|
|
10201
|
-
}
|
|
10202
|
-
}
|
|
10203
9632
|
const status = gitSafe(root, "status", "--porcelain");
|
|
10204
9633
|
if (status) {
|
|
10205
9634
|
console.log(source_default.yellow("You have uncommitted changes."));
|
|
@@ -10283,7 +9712,7 @@ async function publish(options) {
|
|
|
10283
9712
|
let tarballPath;
|
|
10284
9713
|
try {
|
|
10285
9714
|
tarballPath = await packSource(root);
|
|
10286
|
-
const size =
|
|
9715
|
+
const size = statSync6(tarballPath).size;
|
|
10287
9716
|
spinner.succeed(`Source packed (${formatBytes(size)})`);
|
|
10288
9717
|
} catch (err) {
|
|
10289
9718
|
spinner.fail("Pack failed");
|
|
@@ -10322,7 +9751,7 @@ async function publish(options) {
|
|
|
10322
9751
|
}
|
|
10323
9752
|
|
|
10324
9753
|
// src/commands/validate.ts
|
|
10325
|
-
import { existsSync as
|
|
9754
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
10326
9755
|
import { join as join15 } from "path";
|
|
10327
9756
|
function validate3() {
|
|
10328
9757
|
const root = process.cwd();
|
|
@@ -10342,21 +9771,21 @@ function validate3() {
|
|
|
10342
9771
|
for (const page of m.pages) {
|
|
10343
9772
|
const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
|
|
10344
9773
|
const fullPath = join15(root, "src", component);
|
|
10345
|
-
if (!
|
|
9774
|
+
if (!existsSync11(fullPath)) {
|
|
10346
9775
|
console.log(source_default.yellow(` \u26A0 Page component not found: src/${component}`));
|
|
10347
9776
|
warnings++;
|
|
10348
9777
|
}
|
|
10349
9778
|
}
|
|
10350
9779
|
if (m.agent) {
|
|
10351
9780
|
const agentPath = join15(root, m.agent);
|
|
10352
|
-
if (!
|
|
9781
|
+
if (!existsSync11(agentPath)) {
|
|
10353
9782
|
console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
|
|
10354
9783
|
warnings++;
|
|
10355
9784
|
}
|
|
10356
9785
|
}
|
|
10357
9786
|
const pkgPath = join15(root, "package.json");
|
|
10358
|
-
if (
|
|
10359
|
-
const pkg = JSON.parse(
|
|
9787
|
+
if (existsSync11(pkgPath)) {
|
|
9788
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
10360
9789
|
if (pkg.version && pkg.version !== m.version) {
|
|
10361
9790
|
console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
|
|
10362
9791
|
warnings++;
|
|
@@ -10371,7 +9800,7 @@ function validate3() {
|
|
|
10371
9800
|
|
|
10372
9801
|
// src/commands/check.ts
|
|
10373
9802
|
import { execSync as execSync4 } from "child_process";
|
|
10374
|
-
import { existsSync as
|
|
9803
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
10375
9804
|
import { join as join16 } from "path";
|
|
10376
9805
|
function check() {
|
|
10377
9806
|
const root = process.cwd();
|
|
@@ -10391,18 +9820,18 @@ function check() {
|
|
|
10391
9820
|
let warnings = 0;
|
|
10392
9821
|
for (const page of m.pages) {
|
|
10393
9822
|
const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
|
|
10394
|
-
if (!
|
|
9823
|
+
if (!existsSync12(join16(root, "src", component))) {
|
|
10395
9824
|
console.log(source_default.yellow(` \u26A0 Page not found: src/${component}`));
|
|
10396
9825
|
warnings++;
|
|
10397
9826
|
}
|
|
10398
9827
|
}
|
|
10399
|
-
if (m.agent && !
|
|
9828
|
+
if (m.agent && !existsSync12(join16(root, m.agent))) {
|
|
10400
9829
|
console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
|
|
10401
9830
|
warnings++;
|
|
10402
9831
|
}
|
|
10403
9832
|
const pkgPath = join16(root, "package.json");
|
|
10404
|
-
if (
|
|
10405
|
-
const pkg = JSON.parse(
|
|
9833
|
+
if (existsSync12(pkgPath)) {
|
|
9834
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
10406
9835
|
if (pkg.version && pkg.version !== m.version) {
|
|
10407
9836
|
console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
|
|
10408
9837
|
warnings++;
|
|
@@ -10433,7 +9862,7 @@ function check() {
|
|
|
10433
9862
|
}
|
|
10434
9863
|
|
|
10435
9864
|
// src/commands/clean.ts
|
|
10436
|
-
import { rmSync, existsSync as
|
|
9865
|
+
import { rmSync, existsSync as existsSync13 } from "fs";
|
|
10437
9866
|
import { join as join17 } from "path";
|
|
10438
9867
|
function clean(options) {
|
|
10439
9868
|
const root = process.cwd();
|
|
@@ -10444,7 +9873,7 @@ function clean(options) {
|
|
|
10444
9873
|
const lockfiles = ["bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
|
|
10445
9874
|
for (const dir of dirs) {
|
|
10446
9875
|
const path = join17(root, dir);
|
|
10447
|
-
if (
|
|
9876
|
+
if (existsSync13(path)) {
|
|
10448
9877
|
rmSync(path, { recursive: true });
|
|
10449
9878
|
console.log(source_default.dim(` Removed ${dir}/`));
|
|
10450
9879
|
}
|
|
@@ -10452,7 +9881,7 @@ function clean(options) {
|
|
|
10452
9881
|
if (options?.all) {
|
|
10453
9882
|
for (const file of lockfiles) {
|
|
10454
9883
|
const path = join17(root, file);
|
|
10455
|
-
if (
|
|
9884
|
+
if (existsSync13(path)) {
|
|
10456
9885
|
rmSync(path);
|
|
10457
9886
|
console.log(source_default.dim(` Removed ${file}`));
|
|
10458
9887
|
}
|
|
@@ -10462,15 +9891,8 @@ function clean(options) {
|
|
|
10462
9891
|
}
|
|
10463
9892
|
|
|
10464
9893
|
// src/commands/login.ts
|
|
10465
|
-
|
|
9894
|
+
var ACCOUNTS_SCOPE_URL = "https://my.construct.space/api/accounts/me/scope";
|
|
10466
9895
|
async function login(options) {
|
|
10467
|
-
const portalURL = options?.portal || DEFAULT_PORTAL;
|
|
10468
|
-
const fromApp = loadFromApp();
|
|
10469
|
-
if (fromApp) {
|
|
10470
|
-
console.log(source_default.green(`Using Construct app profile: ${fromApp.user?.name || fromApp.user?.email}`));
|
|
10471
|
-
console.log(source_default.dim(" To use a different identity, sign out of the app or run `construct logout` after login."));
|
|
10472
|
-
return;
|
|
10473
|
-
}
|
|
10474
9896
|
if (isAuthenticated()) {
|
|
10475
9897
|
const creds = load2();
|
|
10476
9898
|
const name = creds.user?.name || "unknown";
|
|
@@ -10478,159 +9900,85 @@ async function login(options) {
|
|
|
10478
9900
|
console.log(source_default.dim(" Run 'construct logout' to sign out first."));
|
|
10479
9901
|
return;
|
|
10480
9902
|
}
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
const
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
}, 5 * 60 * 1000);
|
|
10492
|
-
const srv = createServer((req, res) => {
|
|
10493
|
-
const url = new URL(req.url, `http://localhost:${port}`);
|
|
10494
|
-
if (url.pathname !== "/callback") {
|
|
10495
|
-
res.end();
|
|
10496
|
-
return;
|
|
10497
|
-
}
|
|
10498
|
-
const error2 = url.searchParams.get("error");
|
|
10499
|
-
const token = url.searchParams.get("token");
|
|
10500
|
-
res.setHeader("Content-Type", "text/html");
|
|
10501
|
-
if (error2) {
|
|
10502
|
-
res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
10503
|
-
<h2 style="color:#EF4444">Login Failed</h2><p>${error2}</p>
|
|
10504
|
-
<p style="color:#6B7280">You can close this window.</p></body></html>`);
|
|
10505
|
-
clearTimeout(timeout);
|
|
10506
|
-
srv.close();
|
|
10507
|
-
reject(new Error(error2));
|
|
10508
|
-
return;
|
|
10509
|
-
}
|
|
10510
|
-
if (!token) {
|
|
10511
|
-
res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
10512
|
-
<h2 style="color:#EF4444">Login Failed</h2><p>No token received.</p>
|
|
10513
|
-
<p style="color:#6B7280">You can close this window.</p></body></html>`);
|
|
10514
|
-
clearTimeout(timeout);
|
|
10515
|
-
srv.close();
|
|
10516
|
-
reject(new Error("No token received"));
|
|
10517
|
-
return;
|
|
10518
|
-
}
|
|
10519
|
-
res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
10520
|
-
<h2 style="color:#10B981">Logged In!</h2>
|
|
10521
|
-
<p>You can close this window and return to your terminal.</p></body></html>`);
|
|
10522
|
-
clearTimeout(timeout);
|
|
10523
|
-
srv.close();
|
|
10524
|
-
resolve3(token);
|
|
10525
|
-
});
|
|
10526
|
-
srv.listen(port, "127.0.0.1");
|
|
10527
|
-
});
|
|
10528
|
-
console.log(source_default.blue("Opening browser to log in..."));
|
|
10529
|
-
console.log(source_default.dim(" If the browser doesn't open, visit:"));
|
|
10530
|
-
console.log(source_default.dim(` ${loginURL}`));
|
|
9903
|
+
if (options?.token) {
|
|
9904
|
+
await loginWithToken(options.token);
|
|
9905
|
+
return;
|
|
9906
|
+
}
|
|
9907
|
+
const profiles = listDesktopProfiles();
|
|
9908
|
+
if (profiles.length > 0) {
|
|
9909
|
+
await loginFromProfile(profiles);
|
|
9910
|
+
return;
|
|
9911
|
+
}
|
|
9912
|
+
console.log(source_default.yellow("No signed-in profiles found on this machine."));
|
|
10531
9913
|
console.log();
|
|
10532
|
-
|
|
10533
|
-
console.log(
|
|
9914
|
+
console.log(" Two options:");
|
|
9915
|
+
console.log(" 1. Sign in with the Construct desktop app and re-run this command, or");
|
|
9916
|
+
console.log(` 2. Paste a token from ${source_default.cyan("https://my.construct.space/settings/tokens")} below.`);
|
|
9917
|
+
console.log();
|
|
9918
|
+
const token = await dist_default5({
|
|
9919
|
+
message: "Token (or press Ctrl+C to cancel):",
|
|
9920
|
+
validate: (s) => s.trim().length > 0 || "Token required"
|
|
9921
|
+
});
|
|
9922
|
+
await loginWithToken(token.trim());
|
|
9923
|
+
}
|
|
9924
|
+
async function loginFromProfile(profiles) {
|
|
9925
|
+
let picked = profiles[0];
|
|
9926
|
+
if (profiles.length > 1) {
|
|
9927
|
+
picked = await dist_default6({
|
|
9928
|
+
message: "Choose a profile to use:",
|
|
9929
|
+
choices: profiles.map((p) => ({ name: formatLabel(p), value: p }))
|
|
9930
|
+
});
|
|
9931
|
+
}
|
|
9932
|
+
const user = {
|
|
9933
|
+
id: picked.user?.id || picked.id,
|
|
9934
|
+
name: picked.user?.name || picked.user?.username || picked.id,
|
|
9935
|
+
email: picked.user?.email || ""
|
|
9936
|
+
};
|
|
9937
|
+
store({ token: picked.token, portal: DEFAULT_PORTAL, user });
|
|
9938
|
+
console.log(source_default.green(`Logged in as ${user.name}`));
|
|
9939
|
+
if (user.email)
|
|
9940
|
+
console.log(source_default.dim(` ${user.email}`));
|
|
9941
|
+
console.log(source_default.dim(` profile: ${picked.id}`));
|
|
9942
|
+
}
|
|
9943
|
+
async function loginWithToken(token) {
|
|
9944
|
+
let resp;
|
|
10534
9945
|
try {
|
|
10535
|
-
|
|
10536
|
-
const resp = await fetch(`${portalURL}/api/auth/cli-verify`, {
|
|
9946
|
+
resp = await fetch(ACCOUNTS_SCOPE_URL, {
|
|
10537
9947
|
headers: { Authorization: `Bearer ${token}` }
|
|
10538
9948
|
});
|
|
10539
|
-
const { user, publishers } = await resp.json();
|
|
10540
|
-
store({ token, portal: portalURL, user, publishers });
|
|
10541
|
-
console.log();
|
|
10542
|
-
console.log(source_default.green(`Logged in as ${user?.name || "there"}`));
|
|
10543
|
-
const list = publishers || [];
|
|
10544
|
-
if (list.length === 0) {
|
|
10545
|
-
console.log(source_default.yellow(" No publisher yet. Run developer enrollment to publish spaces."));
|
|
10546
|
-
} else {
|
|
10547
|
-
for (const p of list) {
|
|
10548
|
-
const label = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
|
|
10549
|
-
console.log(source_default.dim(` Publisher: ${p.name} (${label})`));
|
|
10550
|
-
}
|
|
10551
|
-
}
|
|
10552
9949
|
} catch (err) {
|
|
10553
|
-
console.error(source_default.red(`
|
|
9950
|
+
console.error(source_default.red(`Failed to reach accounts service: ${err.message}`));
|
|
10554
9951
|
process.exit(1);
|
|
10555
9952
|
}
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
clear();
|
|
10560
|
-
if (wasCli) {
|
|
10561
|
-
console.log(source_default.green("Logged out of CLI credentials."));
|
|
10562
|
-
} else {
|
|
10563
|
-
console.log(source_default.dim("No CLI credentials to clear."));
|
|
9953
|
+
if (resp.status === 401) {
|
|
9954
|
+
console.error(source_default.red("Token rejected by accounts service (invalid or expired)."));
|
|
9955
|
+
process.exit(1);
|
|
10564
9956
|
}
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
console.log(source_default.yellow("Note: the Construct app is still signed in as ") + source_default.white(appStill.user?.name || appStill.user?.email || ""));
|
|
10569
|
-
console.log(source_default.dim(" The CLI will continue to use the app profile. Sign out of the app to fully disconnect."));
|
|
9957
|
+
if (!resp.ok) {
|
|
9958
|
+
console.error(source_default.red(`Token verification failed (HTTP ${resp.status}).`));
|
|
9959
|
+
process.exit(1);
|
|
10570
9960
|
}
|
|
9961
|
+
const body = await resp.json();
|
|
9962
|
+
const u = body.user || {};
|
|
9963
|
+
const user = {
|
|
9964
|
+
id: u.id || u.uuid || "",
|
|
9965
|
+
name: u.name || u.username || "",
|
|
9966
|
+
email: u.email || ""
|
|
9967
|
+
};
|
|
9968
|
+
store({ token, portal: DEFAULT_PORTAL, user });
|
|
9969
|
+
console.log(source_default.green(`Logged in as ${user.name || "unknown"}`));
|
|
9970
|
+
if (user.email)
|
|
9971
|
+
console.log(source_default.dim(` ${user.email}`));
|
|
10571
9972
|
}
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
const
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
} else {
|
|
10582
|
-
try {
|
|
10583
|
-
creds = load2();
|
|
10584
|
-
source = "cli";
|
|
10585
|
-
} catch {
|
|
10586
|
-
source = "none";
|
|
10587
|
-
}
|
|
10588
|
-
}
|
|
10589
|
-
if (source === "none" || !creds) {
|
|
10590
|
-
console.log(source_default.yellow("Not signed in."));
|
|
10591
|
-
console.log(source_default.dim(" Run ") + source_default.white("construct login") + source_default.dim(" or sign in to the Construct app."));
|
|
10592
|
-
return;
|
|
10593
|
-
}
|
|
10594
|
-
const user = creds.user;
|
|
10595
|
-
const sourceLabel = source === "app" ? source_default.cyan("Construct app profile") : source_default.blue("CLI login");
|
|
10596
|
-
console.log();
|
|
10597
|
-
console.log(source_default.bold(user?.name || "Signed in"));
|
|
10598
|
-
console.log(source_default.dim(" " + (user?.email || "")));
|
|
10599
|
-
console.log(source_default.dim(" id: ") + source_default.dim(user?.id || "\u2014"));
|
|
10600
|
-
console.log(source_default.dim(" source: ") + sourceLabel);
|
|
10601
|
-
console.log(source_default.dim(" portal: ") + (creds.portal || DEFAULT_PORTAL));
|
|
10602
|
-
try {
|
|
10603
|
-
const resp = await fetch(`${creds.portal || DEFAULT_PORTAL}/api/auth/cli-verify`, {
|
|
10604
|
-
headers: { Authorization: `Bearer ${creds.token}` }
|
|
10605
|
-
});
|
|
10606
|
-
if (!resp.ok) {
|
|
10607
|
-
console.log(source_default.dim(" publishers: ") + source_default.red(`error (${resp.status})`));
|
|
10608
|
-
return;
|
|
10609
|
-
}
|
|
10610
|
-
const body = await resp.json();
|
|
10611
|
-
const list = body.publishers || [];
|
|
10612
|
-
if (list.length === 0) {
|
|
10613
|
-
console.log(source_default.dim(" publishers: ") + source_default.yellow("none yet \u2014 enroll at developer.construct.space"));
|
|
10614
|
-
return;
|
|
10615
|
-
}
|
|
10616
|
-
console.log();
|
|
10617
|
-
console.log(source_default.bold("Publishers"));
|
|
10618
|
-
for (const p of list) {
|
|
10619
|
-
const kindLabel = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
|
|
10620
|
-
const verified = p.verified ? source_default.green(" \u2713") : "";
|
|
10621
|
-
console.log(` ${source_default.white(p.name)} (${kindLabel})${verified}`);
|
|
10622
|
-
if (p.orgId)
|
|
10623
|
-
console.log(source_default.dim(` org: ${p.orgId}`));
|
|
10624
|
-
}
|
|
10625
|
-
} catch (err) {
|
|
10626
|
-
console.log(source_default.dim(" publishers: ") + source_default.red("could not reach portal"));
|
|
10627
|
-
}
|
|
10628
|
-
console.log();
|
|
10629
|
-
if (source === "app") {
|
|
10630
|
-
console.log(source_default.dim("To switch identities, change the active profile in the Construct app."));
|
|
10631
|
-
} else {
|
|
10632
|
-
console.log(source_default.dim("To switch identities, run 'construct logout' then 'construct login'."));
|
|
10633
|
-
}
|
|
9973
|
+
function formatLabel(p) {
|
|
9974
|
+
const name = p.user?.name || p.user?.username || p.id;
|
|
9975
|
+
const email = p.user?.email;
|
|
9976
|
+
const kind = p.id.startsWith("org:") ? " [org]" : "";
|
|
9977
|
+
return email ? `${name}${kind} ${source_default.dim(email)}` : `${name}${kind} ${source_default.dim(p.id)}`;
|
|
9978
|
+
}
|
|
9979
|
+
function logout() {
|
|
9980
|
+
clear();
|
|
9981
|
+
console.log(source_default.green("Logged out."));
|
|
10634
9982
|
}
|
|
10635
9983
|
|
|
10636
9984
|
// src/commands/update.ts
|
|
@@ -10662,7 +10010,7 @@ function update() {
|
|
|
10662
10010
|
}
|
|
10663
10011
|
|
|
10664
10012
|
// src/commands/graph/init.ts
|
|
10665
|
-
import { existsSync as
|
|
10013
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
10666
10014
|
import { join as join18 } from "path";
|
|
10667
10015
|
import { execSync as execSync6 } from "child_process";
|
|
10668
10016
|
function graphInit() {
|
|
@@ -10674,9 +10022,9 @@ function graphInit() {
|
|
|
10674
10022
|
}
|
|
10675
10023
|
const m = read(root);
|
|
10676
10024
|
const modelsDir = join18(root, "src", "models");
|
|
10677
|
-
|
|
10025
|
+
mkdirSync5(modelsDir, { recursive: true });
|
|
10678
10026
|
const indexPath = join18(modelsDir, "index.ts");
|
|
10679
|
-
if (!
|
|
10027
|
+
if (!existsSync14(indexPath)) {
|
|
10680
10028
|
writeFileSync7(indexPath, `// Data models for ${m.name}
|
|
10681
10029
|
// Generated by construct graph init
|
|
10682
10030
|
|
|
@@ -10685,7 +10033,7 @@ function graphInit() {
|
|
|
10685
10033
|
`);
|
|
10686
10034
|
}
|
|
10687
10035
|
const pkgPath = join18(root, "package.json");
|
|
10688
|
-
const pkg = JSON.parse(
|
|
10036
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
10689
10037
|
if (!pkg.dependencies)
|
|
10690
10038
|
pkg.dependencies = {};
|
|
10691
10039
|
if (!pkg.dependencies["@construct-space/graph"]) {
|
|
@@ -10715,7 +10063,7 @@ function graphInit() {
|
|
|
10715
10063
|
}
|
|
10716
10064
|
|
|
10717
10065
|
// src/commands/graph/generate.ts
|
|
10718
|
-
import { existsSync as
|
|
10066
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
10719
10067
|
import { join as join19 } from "path";
|
|
10720
10068
|
var FIELD_TYPES = {
|
|
10721
10069
|
string: "field.string()",
|
|
@@ -10869,9 +10217,9 @@ function generate2(modelName, fieldSpecs, options) {
|
|
|
10869
10217
|
const content = lines.join(`
|
|
10870
10218
|
`);
|
|
10871
10219
|
const modelsDir = join19(root, "src", "models");
|
|
10872
|
-
|
|
10220
|
+
mkdirSync6(modelsDir, { recursive: true });
|
|
10873
10221
|
const filePath = join19(modelsDir, `${name}.ts`);
|
|
10874
|
-
if (
|
|
10222
|
+
if (existsSync15(filePath)) {
|
|
10875
10223
|
console.log(source_default.yellow(` Model file already exists: src/models/${name}.ts`));
|
|
10876
10224
|
console.log(source_default.dim(" Overwriting..."));
|
|
10877
10225
|
}
|
|
@@ -10890,8 +10238,8 @@ function generate2(modelName, fieldSpecs, options) {
|
|
|
10890
10238
|
function updateBarrel(modelsDir, modelName) {
|
|
10891
10239
|
const indexPath = join19(modelsDir, "index.ts");
|
|
10892
10240
|
const exportLine = `export { ${modelName} } from './${modelName}'`;
|
|
10893
|
-
if (
|
|
10894
|
-
const content =
|
|
10241
|
+
if (existsSync15(indexPath)) {
|
|
10242
|
+
const content = readFileSync11(indexPath, "utf-8");
|
|
10895
10243
|
if (content.includes(exportLine))
|
|
10896
10244
|
return;
|
|
10897
10245
|
writeFileSync8(indexPath, content.trimEnd() + `
|
|
@@ -10905,7 +10253,7 @@ function updateBarrel(modelsDir, modelName) {
|
|
|
10905
10253
|
}
|
|
10906
10254
|
|
|
10907
10255
|
// src/commands/graph/push.ts
|
|
10908
|
-
import { existsSync as
|
|
10256
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync12 } from "fs";
|
|
10909
10257
|
import { join as join20, basename as basename7 } from "path";
|
|
10910
10258
|
async function graphPush() {
|
|
10911
10259
|
const root = process.cwd();
|
|
@@ -10915,11 +10263,11 @@ async function graphPush() {
|
|
|
10915
10263
|
}
|
|
10916
10264
|
const m = read(root);
|
|
10917
10265
|
const modelsDir = join20(root, "src", "models");
|
|
10918
|
-
if (!
|
|
10266
|
+
if (!existsSync16(modelsDir)) {
|
|
10919
10267
|
console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
|
|
10920
10268
|
process.exit(1);
|
|
10921
10269
|
}
|
|
10922
|
-
const modelFiles =
|
|
10270
|
+
const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
|
|
10923
10271
|
if (modelFiles.length === 0) {
|
|
10924
10272
|
console.error(source_default.red("No model files found in src/models/"));
|
|
10925
10273
|
console.log(source_default.dim(" Generate one: construct graph g User name:string email:string"));
|
|
@@ -10928,7 +10276,7 @@ async function graphPush() {
|
|
|
10928
10276
|
console.log(source_default.blue(`Pushing ${modelFiles.length} model(s) for space: ${m.id}`));
|
|
10929
10277
|
const models = [];
|
|
10930
10278
|
for (const file of modelFiles) {
|
|
10931
|
-
const content =
|
|
10279
|
+
const content = readFileSync12(join20(modelsDir, file), "utf-8");
|
|
10932
10280
|
const model = parseModelFile(content, basename7(file, ".ts"));
|
|
10933
10281
|
if (model)
|
|
10934
10282
|
models.push(model);
|
|
@@ -10944,15 +10292,17 @@ async function graphPush() {
|
|
|
10944
10292
|
console.error(source_default.red(err.message));
|
|
10945
10293
|
process.exit(1);
|
|
10946
10294
|
}
|
|
10947
|
-
const graphURL = process.env.GRAPH_URL || "https://
|
|
10295
|
+
const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
|
|
10948
10296
|
const spinner = ora("Registering models...").start();
|
|
10949
10297
|
try {
|
|
10298
|
+
const userID = creds.user?.id || "";
|
|
10950
10299
|
const resp = await fetch(`${graphURL}/api/schemas/register`, {
|
|
10951
10300
|
method: "POST",
|
|
10952
10301
|
headers: {
|
|
10953
10302
|
"Content-Type": "application/json",
|
|
10954
10303
|
Authorization: `Bearer ${creds.token}`,
|
|
10955
|
-
"X-Space-ID": m.id
|
|
10304
|
+
"X-Space-ID": m.id,
|
|
10305
|
+
"X-Auth-User-ID": userID
|
|
10956
10306
|
},
|
|
10957
10307
|
body: JSON.stringify({
|
|
10958
10308
|
space_id: m.id,
|
|
@@ -10962,6 +10312,20 @@ async function graphPush() {
|
|
|
10962
10312
|
manifest: { version: 1, models }
|
|
10963
10313
|
})
|
|
10964
10314
|
});
|
|
10315
|
+
if (resp.status === 403) {
|
|
10316
|
+
spinner.fail("Ownership check failed");
|
|
10317
|
+
try {
|
|
10318
|
+
const errBody = await resp.json();
|
|
10319
|
+
console.error(source_default.red(` ${errBody.error || "You are not the owner of this space"}`));
|
|
10320
|
+
if (errBody.owner_user_id) {
|
|
10321
|
+
console.error(source_default.dim(` Current owner: ${errBody.owner_user_id}`));
|
|
10322
|
+
}
|
|
10323
|
+
console.error(source_default.dim(" Fork to a new space_id to publish your own version."));
|
|
10324
|
+
} catch {
|
|
10325
|
+
console.error(source_default.red(` 403: Forbidden \u2014 ownership check failed`));
|
|
10326
|
+
}
|
|
10327
|
+
process.exit(1);
|
|
10328
|
+
}
|
|
10965
10329
|
if (!resp.ok) {
|
|
10966
10330
|
const body = await resp.text();
|
|
10967
10331
|
spinner.fail("Registration failed");
|
|
@@ -11044,7 +10408,7 @@ function parseModelFile(content, fileName) {
|
|
|
11044
10408
|
}
|
|
11045
10409
|
|
|
11046
10410
|
// src/commands/graph/migrate.ts
|
|
11047
|
-
import { existsSync as
|
|
10411
|
+
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync13 } from "fs";
|
|
11048
10412
|
import { join as join21, basename as basename8 } from "path";
|
|
11049
10413
|
async function graphMigrate(options) {
|
|
11050
10414
|
const root = process.cwd();
|
|
@@ -11054,7 +10418,7 @@ async function graphMigrate(options) {
|
|
|
11054
10418
|
}
|
|
11055
10419
|
const m = read(root);
|
|
11056
10420
|
const modelsDir = join21(root, "src", "models");
|
|
11057
|
-
if (!
|
|
10421
|
+
if (!existsSync17(modelsDir)) {
|
|
11058
10422
|
console.error(source_default.red("No src/models/ directory. Run 'construct graph init' first."));
|
|
11059
10423
|
process.exit(1);
|
|
11060
10424
|
}
|
|
@@ -11065,7 +10429,7 @@ async function graphMigrate(options) {
|
|
|
11065
10429
|
console.error(source_default.red(err.message));
|
|
11066
10430
|
process.exit(1);
|
|
11067
10431
|
}
|
|
11068
|
-
const graphURL = process.env.GRAPH_URL || "https://
|
|
10432
|
+
const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
|
|
11069
10433
|
const spinner = ora("Fetching current schema...").start();
|
|
11070
10434
|
let serverModels = [];
|
|
11071
10435
|
try {
|
|
@@ -11081,10 +10445,10 @@ async function graphMigrate(options) {
|
|
|
11081
10445
|
spinner.fail("Could not fetch schema");
|
|
11082
10446
|
process.exit(1);
|
|
11083
10447
|
}
|
|
11084
|
-
const modelFiles =
|
|
10448
|
+
const modelFiles = readdirSync7(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
|
|
11085
10449
|
const localModels = [];
|
|
11086
10450
|
for (const file of modelFiles) {
|
|
11087
|
-
const content =
|
|
10451
|
+
const content = readFileSync13(join21(modelsDir, file), "utf-8");
|
|
11088
10452
|
const model = parseModelFields(content, basename8(file, ".ts"));
|
|
11089
10453
|
if (model)
|
|
11090
10454
|
localModels.push(model);
|
|
@@ -11188,7 +10552,7 @@ function parseModelFields(content, fileName) {
|
|
|
11188
10552
|
}
|
|
11189
10553
|
|
|
11190
10554
|
// src/index.ts
|
|
11191
|
-
var VERSION = "1.1
|
|
10555
|
+
var VERSION = "1.3.1";
|
|
11192
10556
|
var program2 = new Command;
|
|
11193
10557
|
program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
|
|
11194
10558
|
program2.command("scaffold [name]").alias("new").alias("create").description("Create a new Construct space project").option("--with-tests", "Include E2E testing boilerplate").option("--full", "Full preset: multiple pages, extra skills, widget templates").action(async (name, opts) => scaffold(name, opts));
|
|
@@ -11199,9 +10563,8 @@ program2.command("publish").description("Publish a space to the Construct regist
|
|
|
11199
10563
|
program2.command("validate").description("Validate space.manifest.json").action(() => validate3());
|
|
11200
10564
|
program2.command("check").description("Run type-check (vue-tsc) and linter (eslint)").action(() => check());
|
|
11201
10565
|
program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
|
|
11202
|
-
program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").action(async (opts) => login(opts));
|
|
10566
|
+
program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").option("--token <token>", "Authenticate with a token directly (skip desktop profile picker)").action(async (opts) => login(opts));
|
|
11203
10567
|
program2.command("logout").description("Sign out").action(() => logout());
|
|
11204
|
-
program2.command("whoami").alias("status").description("Show the active profile, its source (app or CLI), and linked publishers").action(() => whoami());
|
|
11205
10568
|
program2.command("update").description("Update the CLI to the latest version").action(() => update());
|
|
11206
10569
|
var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
|
|
11207
10570
|
graph.command("init").description("Initialize Graph in a space project").action(() => graphInit());
|